mirror of
https://github.com/0intro/wmii
synced 2024-12-22 03:22:46 +03:00
added initial files
This commit is contained in:
commit
caaad63c0d
92
CHANGES
Normal file
92
CHANGES
Normal file
@ -0,0 +1,92 @@
|
||||
3 - (20051101): Tini
|
||||
|
||||
2 - (20050701): Boyd
|
||||
* new sloppy focus
|
||||
* new ahwm-alike moving and resizing of frames
|
||||
* new tabbing through un-/locking frames
|
||||
* new builtin pager layer
|
||||
* new builtin icon layer
|
||||
* new layers per page:
|
||||
- floating layercontains floating frames
|
||||
- managed layer contains tiled frames
|
||||
* new native layout mechanism: C interface implements functions:
|
||||
init, deinit, manage, unmanage, resize, select
|
||||
* new default layouts: tiled, float, grid, vsplit
|
||||
* renamed wmiinput into wmimenu, several cleanups and added
|
||||
double-buffered rendering
|
||||
* changed wmikeys to allow grabbing the keyboard for special modes
|
||||
like resize mode, move mode
|
||||
* changed wmibar to calculate label sizes dynamically and added
|
||||
double-buffered rendering
|
||||
* removed limited generic layout mechanism
|
||||
* removed nesting from layout mechanism
|
||||
* fixed following issues:
|
||||
- http://wmi.modprobe.de/index.php/PITS/00001
|
||||
- http://wmi.modprobe.de/index.php/PITS/00002
|
||||
- http://wmi.modprobe.de/index.php/PITS/00003
|
||||
- http://wmi.modprobe.de/index.php/PITS/00004
|
||||
- http://wmi.modprobe.de/index.php/PITS/00005
|
||||
- http://wmi.modprobe.de/index.php/PITS/00006
|
||||
- http://wmi.modprobe.de/index.php/PITS/00007
|
||||
- http://wmi.modprobe.de/index.php/PITS/00008
|
||||
- http://wmi.modprobe.de/index.php/PITS/00009
|
||||
- http://wmi.modprobe.de/index.php/PITS/00010
|
||||
- http://wmi.modprobe.de/index.php/PITS/00011
|
||||
- http://wmi.modprobe.de/index.php/PITS/00012
|
||||
- http://wmi.modprobe.de/index.php/PITS/00013
|
||||
- http://wmi.modprobe.de/index.php/PITS/00014
|
||||
- http://wmi.modprobe.de/index.php/PITS/00015
|
||||
- http://wmi.modprobe.de/index.php/PITS/00016
|
||||
- http://wmi.modprobe.de/index.php/PITS/00017
|
||||
- http://wmi.modprobe.de/index.php/PITS/00018
|
||||
- http://wmi.modprobe.de/index.php/PITS/00019
|
||||
- http://wmi.modprobe.de/index.php/PITS/00020
|
||||
- http://wmi.modprobe.de/index.php/PITS/00021
|
||||
- http://wmi.modprobe.de/index.php/PITS/00022
|
||||
- http://wmi.modprobe.de/index.php/PITS/00023
|
||||
- http://wmi.modprobe.de/index.php/PITS/00024
|
||||
- http://wmi.modprobe.de/index.php/PITS/00025
|
||||
- http://wmi.modprobe.de/index.php/PITS/00026
|
||||
- http://wmi.modprobe.de/index.php/PITS/00027
|
||||
- http://wmi.modprobe.de/index.php/PITS/00028
|
||||
- http://wmi.modprobe.de/index.php/PITS/00029
|
||||
- http://wmi.modprobe.de/index.php/PITS/00030
|
||||
- http://wmi.modprobe.de/index.php/PITS/00031
|
||||
- http://wmi.modprobe.de/index.php/PITS/00032
|
||||
- http://wmi.modprobe.de/index.php/PITS/00033
|
||||
- http://wmi.modprobe.de/index.php/PITS/00034
|
||||
- http://wmi.modprobe.de/index.php/PITS/00035
|
||||
- http://wmi.modprobe.de/index.php/PITS/00036
|
||||
- http://wmi.modprobe.de/index.php/PITS/00037
|
||||
- http://wmi.modprobe.de/index.php/PITS/00038
|
||||
- http://wmi.modprobe.de/index.php/PITS/00039
|
||||
- http://wmi.modprobe.de/index.php/PITS/00040
|
||||
- http://wmi.modprobe.de/index.php/PITS/00041
|
||||
- http://wmi.modprobe.de/index.php/PITS/00042
|
||||
- http://wmi.modprobe.de/index.php/PITS/00043
|
||||
- http://wmi.modprobe.de/index.php/PITS/00044
|
||||
- http://wmi.modprobe.de/index.php/PITS/00045
|
||||
- http://wmi.modprobe.de/index.php/PITS/00046
|
||||
- http://wmi.modprobe.de/index.php/PITS/00047
|
||||
- http://wmi.modprobe.de/index.php/PITS/00048
|
||||
- http://wmi.modprobe.de/index.php/PITS/00049
|
||||
- http://wmi.modprobe.de/index.php/PITS/00050
|
||||
- http://wmi.modprobe.de/index.php/PITS/00051
|
||||
- http://wmi.modprobe.de/index.php/PITS/00052
|
||||
- http://wmi.modprobe.de/index.php/PITS/00053
|
||||
|
||||
1.1 - (20050613): Zadkiel
|
||||
* fixed following issues:
|
||||
- http://wmi.modprobe.de/index.php/PITS/00001
|
||||
- http://wmi.modprobe.de/index.php/PITS/00004
|
||||
- http://wmi.modprobe.de/index.php/PITS/00007
|
||||
- http://wmi.modprobe.de/index.php/PITS/00033
|
||||
- http://wmi.modprobe.de/index.php/PITS/00019
|
||||
- http://wmi.modprobe.de/index.php/PITS/00028
|
||||
- http://wmi.modprobe.de/index.php/PITS/00034
|
||||
- http://wmi.modprobe.de/index.php/PITS/00039
|
||||
- http://wmi.modprobe.de/index.php/PITS/00027
|
||||
- http://wmi.modprobe.de/index.php/PITS/00032
|
||||
|
||||
1 - (20050601): Uriel
|
||||
initial release
|
22
LICENSE
Normal file
22
LICENSE
Normal file
@ -0,0 +1,22 @@
|
||||
MIT/X Consortium License
|
||||
|
||||
(C)opyright MMIII-MMV Anselm R. Garbe <garbeam at wmii dot de>
|
||||
(C)opyright MMV Georg Neis <gn at oglaroon dot de>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
70
Makefile
Normal file
70
Makefile
Normal file
@ -0,0 +1,70 @@
|
||||
# wmii - window manager improved 2
|
||||
# (C)opyright MMIV-MMV Anselm R. Garbe
|
||||
|
||||
include config.mk
|
||||
|
||||
SUBDIRS = libcext liblitz libixp libixp2 libwmii cmd
|
||||
|
||||
BIN = cmd/wm/wmii cmd/wm/wmiiwm cmd/wmibar cmd/wmifs \
|
||||
cmd/wmikeys cmd/wmimenu cmd/wmiplumb cmd/wmir cmd/wmiwarp
|
||||
|
||||
MAN1 = cmd/wm/wmii.1 cmd/wm/wmiiwm.1 cmd/wmibar.1 cmd/wmifs.1 \
|
||||
cmd/wmikeys.1 cmd/wmimenu.1 cmd/wmir.1
|
||||
|
||||
all:
|
||||
@echo wmii build options:
|
||||
@echo "LIBS = ${LIBS}"
|
||||
@echo "INCLUDES = ${INCLUDES}"
|
||||
@echo "CFLAGS = ${CFLAGS}"
|
||||
@echo "LDFLAGS = ${LDFLAGS}"
|
||||
@echo "CC = ${CC}"
|
||||
@for i in ${SUBDIRS} cmd/wm; do \
|
||||
(cd $$i; ${MAKE}); \
|
||||
done
|
||||
|
||||
dist: clean
|
||||
@mkdir -p wmii-${VERSION}
|
||||
@cp -R Makefile README LICENSE config.mk rc ${SUBDIRS} extra doc wmii-${VERSION}
|
||||
@tar -cf wmii-${VERSION}.tar wmii-${VERSION}
|
||||
@gzip wmii-${VERSION}.tar
|
||||
@rm -rf wmii-${VERSION}
|
||||
@echo created distribution wmii-${VERSION}.tar.gz
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
@for i in ${SUBDIRS} cmd/wm; do \
|
||||
(cd $$i; ${MAKE} clean); \
|
||||
done
|
||||
rm -rf wmii-${VERSION}*
|
||||
|
||||
install: all
|
||||
@mkdir -p ${DESTDIR}${PREFIX}/bin
|
||||
@cp -f ${BIN} ${DESTDIR}${PREFIX}/bin
|
||||
@sed 's|CONFPREFIX|${CONFPREFIX}|; s|9PREFIX|${9PREFIX}|' <cmd/wm/wmii >${DESTDIR}${PREFIX}/bin/wmii
|
||||
@for i in ${BIN}; do \
|
||||
chmod 755 ${DESTDIR}${PREFIX}/bin/`basename $$i`; \
|
||||
done
|
||||
@echo installed executable files to ${DESTDIR}${PREFIX}/bin
|
||||
@mkdir -p ${DESTDIR}${CONFPREFIX}/wmii-3
|
||||
@cd rc; for i in *; do \
|
||||
sed 's|9PREFIX|${9PREFIX}|' <$$i >${DESTDIR}${CONFPREFIX}/wmii-3/$$i; \
|
||||
chmod 755 ${DESTDIR}${CONFPREFIX}/wmii-3/$$i; \
|
||||
done
|
||||
@echo installed rc scripts to ${DESTDIR}${CONFPREFIX}/wmii-3
|
||||
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
|
||||
@cp -f ${MAN1} ${DESTDIR}${MANPREFIX}/man1
|
||||
@sed 's|CONFPREFIX|${CONFPREFIX}|' <cmd/wm/wmii.1 >${DESTDIR}${MANPREFIX}/man1/wmii.1
|
||||
@for i in ${MAN1}; do \
|
||||
chmod 444 ${DESTDIR}${MANPREFIX}/man1/`basename $$i`; \
|
||||
done
|
||||
@echo installed manual pages to ${DESTDIR}${MANPREFIX}/man1
|
||||
|
||||
uninstall:
|
||||
@for i in ${BIN}; do \
|
||||
rm -f ${DESTDIR}${PREFIX}/bin/`basename $$i`; \
|
||||
done
|
||||
@for i in ${MAN1}; do \
|
||||
rm -f ${DESTDIR}${MANPREFIX}/man1/`basename $$i`; \
|
||||
done
|
||||
@rm -rf ${DESTDIR}${CONFPREFIX}/wmii-3
|
||||
@echo "uninstalled wmii"
|
86
README
Normal file
86
README
Normal file
@ -0,0 +1,86 @@
|
||||
Abstract
|
||||
--------
|
||||
window manager improved 2 (wmii) [1] is an improved, modularized and
|
||||
lightweight X11 window manager which supports tabbed, tiled and
|
||||
conventional window management through layouts.
|
||||
wmii consists of components that are independent processes and
|
||||
communicate via a socket-based virtual filesystem which is oriented
|
||||
on the "everything is file" paradigm of the Plan 9 [2] operating system.
|
||||
The core distribution of wmii contains the window manager itself,
|
||||
a master file system routing utility (wmifs), a generic bar (wmibar),
|
||||
a shortcut handler (wmikeys), and a generic interaction menu (wmimenu)
|
||||
beside several tiny utilities like wmir, wmiplumb, and wmiwarp.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
In order to build wmii you need the Xlib header files.
|
||||
Furthermore, the wmii configuration scripts rely on the 9rc package [3]
|
||||
which contains ports of various Plan 9 standard tools, in particular the
|
||||
rc shell.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
Edit config.mk to match your local setup. wmii is installed into
|
||||
the /usr/local hierarchy by default.
|
||||
|
||||
Afterwards enter the following command to build and install wmii (if
|
||||
necessary as root):
|
||||
|
||||
$ make clean install
|
||||
|
||||
|
||||
Running wmii
|
||||
------------
|
||||
Add the following line to your .xinitrc to start wmii using startx:
|
||||
|
||||
exec wmii
|
||||
|
||||
In order to connect wmii or wmir to a specific display, make sure that
|
||||
DISPLAY environment variable is set correctly, e.g.:
|
||||
|
||||
$ setenv DISPLAY foo.bar:1
|
||||
$ wmii
|
||||
|
||||
This will start wmii on display :1 of the host foo.bar if it is present
|
||||
in your .xinitrc.
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
The configuration of wmii is done by customizing the existing actions.
|
||||
Customizing an action means copying the appropriate file from the directory
|
||||
$WMII_CONFDIR (usually /usr/local/etc/wmii-3) to
|
||||
$HOME/.wmii-3 and editing it to fit your needs. The action of main
|
||||
interest is called 'wmirc': it is executed on startup.
|
||||
|
||||
|
||||
Credits
|
||||
-------
|
||||
Beside all thanks to the wmi contributors, the following people have
|
||||
contributed especially to wmii in various ways:
|
||||
|
||||
- Christoph Wegscheider <christoph (dot) wegscheider (at) wegi (dot) net>
|
||||
- Georg Neis <gn (at) oglaroon (dot) de>
|
||||
- Uwe Zeisberger <zeisberg (at) informatik (dot) uni-freiburg (dot) de>
|
||||
- Uriel <uriell (at) binarydream (dot) org>
|
||||
- Scot Doyle <scot (at) scotdoyle (dot) com>
|
||||
- Sebastian Hartmann <seb (dot) wmi (at) gmx (dot) de>
|
||||
- Bernhard Leiner <bleiner (at) gmail (dot) com>
|
||||
- Jonas Domeij <jonas (dot) domeij (at) gmail (dot) com>
|
||||
- Vincent <10 (dot) 50 (at) free (dot) fr>
|
||||
- Oliver Kopp <olly (at) flupp (dot) de>
|
||||
- Sebastian Roth <sebastian (dot) roth (at) gmail (dot) com>
|
||||
- Steve Hoffman <steveh (at) g2switchworks (dot) com>
|
||||
- Christof Musik <christof (at) senfdax (dot) de>
|
||||
- Steffen Liebergeld <perl (at) gmx (dot) org>
|
||||
|
||||
|
||||
References
|
||||
----------
|
||||
[1] http://wmii.de
|
||||
[2] http://www.cs.bell-labs.com/plan9dist/
|
||||
[3] http://wmii.de/repos/9base/
|
||||
|
||||
--Anselm R. Garbe
|
86
cmd/Makefile
Normal file
86
cmd/Makefile
Normal file
@ -0,0 +1,86 @@
|
||||
# window manager improved 2 utilities
|
||||
# (C)opyright MMIV-MMV Anselm R. Garbe
|
||||
|
||||
include ../config.mk
|
||||
|
||||
CFLAGS += -I../liblitz -I../libixp -I../libwmii -I../libcext
|
||||
LDFLAGS += -L../liblitz -llitz -L../libixp -lixp \
|
||||
-L../libwmii -lwmii -L../libcext -lcext
|
||||
LDFLAGS2 += ${LIBS} -L../liblitz -llitz -L../libixp2 -lixp \
|
||||
-L../libwmii -lwmii -L../libcext -lcext
|
||||
|
||||
SRC_bar = wmibar.c
|
||||
OBJ_bar = ${SRC_bar:.c=.o}
|
||||
|
||||
SRC_bar2 = wmibar2.c
|
||||
OBJ_bar2 = ${SRC_bar2:.c=.o}
|
||||
|
||||
SRC_menu = wmimenu.c
|
||||
OBJ_menu = ${SRC_menu:.c=.o}
|
||||
|
||||
SRC_r2 = wmir2.c
|
||||
OBJ_r2 = ${SRC_r2:.c=.o}
|
||||
|
||||
SRC_r = wmir.c
|
||||
OBJ_r = ${SRC_r:.c=.o}
|
||||
|
||||
SRC_fs = wmifs.c
|
||||
OBJ_fs = ${SRC_fs:.c=.o}
|
||||
|
||||
SRC_keys = wmikeys.c
|
||||
OBJ_keys = ${SRC_keys:.c=.o}
|
||||
|
||||
SRC_plumb = wmiplumb.c
|
||||
OBJ_plumb = ${SRC_plumb:.c=.o}
|
||||
|
||||
SRC_warp = wmiwarp.c
|
||||
OBJ_warp = ${SRC_warp:.c=.o}
|
||||
|
||||
all: wmibar wmibar2 wmimenu wmir wmir2 wmifs wmikeys wmiplumb wmiwarp
|
||||
@echo built wmi commands
|
||||
|
||||
.c.o:
|
||||
@echo CC $<
|
||||
@${CC} -c ${CFLAGS} $<
|
||||
|
||||
wmibar: ${OBJ_bar}
|
||||
@echo LD $@
|
||||
@${CC} -o $@ ${OBJ_bar} ${LDFLAGS}
|
||||
|
||||
wmibar2: ${OBJ_bar2}
|
||||
@echo LD $@
|
||||
@${CC} -o $@ ${OBJ_bar2} ${LDFLAGS2}
|
||||
|
||||
wmimenu: ${OBJ_menu}
|
||||
@echo LD $@
|
||||
@${CC} -o $@ ${OBJ_menu} ${LDFLAGS}
|
||||
|
||||
wmir2: ${OBJ_r2}
|
||||
@echo LD $@
|
||||
@${CC} -o $@ ${OBJ_r2} -g -static -L${LIBDIR} -L/usr/lib -lc \
|
||||
-L../libixp2 -lixp -L../libcext -lcext
|
||||
|
||||
wmir: ${OBJ_r}
|
||||
@echo LD $@
|
||||
@${CC} -o $@ ${OBJ_r} -g -static -L${LIBDIR} -L/usr/lib -lc \
|
||||
-L../libixp -lixp -L../libcext -lcext
|
||||
|
||||
wmifs: ${OBJ_fs}
|
||||
@echo LD $@
|
||||
@${CC} -o $@ ${OBJ_fs} ${LDFLAGS}
|
||||
|
||||
wmikeys: ${OBJ_keys}
|
||||
@echo LD $@
|
||||
@${CC} -o $@ ${OBJ_keys} ${LDFLAGS}
|
||||
|
||||
wmiplumb: ${OBJ_plumb}
|
||||
@echo LD $@
|
||||
@${CC} -o $@ ${OBJ_plumb} ${LDFLAGS}
|
||||
|
||||
wmiwarp: ${OBJ_warp}
|
||||
@echo LD $@
|
||||
@${CC} -o $@ ${OBJ_warp} ${LDFLAGS}
|
||||
|
||||
clean:
|
||||
rm -f wmibar wmibar2 wmimenu wmir wmir2 wmikeys wmiplumb wmifs\
|
||||
wmiwarp *.o
|
15
cmd/wm/Makefile
Normal file
15
cmd/wm/Makefile
Normal file
@ -0,0 +1,15 @@
|
||||
# wm - window manager improved 2
|
||||
# (C)opyright MMIV-MMV Anselm R. Garbe
|
||||
|
||||
include ../../config.mk
|
||||
|
||||
all: layout.c
|
||||
@${MAKE} -f Makefile.wm
|
||||
@echo built core wm
|
||||
|
||||
layout.c:
|
||||
@echo SH mklayout.sh
|
||||
@sh mklayout.sh
|
||||
|
||||
clean:
|
||||
rm -f wmiiwm *.o layout.*
|
25
cmd/wm/Makefile.wm
Normal file
25
cmd/wm/Makefile.wm
Normal file
@ -0,0 +1,25 @@
|
||||
# wm - window manager improved 2
|
||||
# (C)opyright MMIV-MMV Anselm R. Garbe
|
||||
|
||||
include ../../config.mk
|
||||
|
||||
CFLAGS += -I../../liblitz -I../../libixp -I../../libwmii \
|
||||
-I../../libcext
|
||||
LDFLAGS += -L../../liblitz -llitz -L../../libixp -lixp \
|
||||
-L../../libwmii -lwmii -L../../libcext -lcext
|
||||
|
||||
SRC = wm.c core.c client.c event.c mouse.c frame.c page.c layout.c
|
||||
|
||||
include layout.mk
|
||||
|
||||
OBJ = ${SRC:.c=.o}
|
||||
|
||||
all: wmiiwm
|
||||
|
||||
.c.o:
|
||||
@echo CC $<
|
||||
@${CC} -c ${CFLAGS} $<
|
||||
|
||||
wmiiwm: ${OBJ}
|
||||
@echo LD $@
|
||||
@${CC} -o $@ ${OBJ} ${LDFLAGS}
|
67
cmd/wm/area.c
Normal file
67
cmd/wm/area.c
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "wm.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
static Area zero_area = {0};
|
||||
|
||||
void
|
||||
free_area(Area* a)
|
||||
{
|
||||
ixp_remove_file(ixps, a->files[A_PREFIX]);
|
||||
free(f);
|
||||
}
|
||||
|
||||
void
|
||||
destroy_area(Area *a)
|
||||
{
|
||||
unsigned int i;
|
||||
a->layout->deinit(a);
|
||||
for(i = 0; a->frames && a->frames[i]; i++);
|
||||
destroy_frame(i);
|
||||
free_area(a);
|
||||
}
|
||||
|
||||
void
|
||||
focus_area(Area *a, int raise, int up, int down)
|
||||
{
|
||||
Page *p = f->page;
|
||||
Frame *old;
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
old = get_selected(p);
|
||||
if (down && f->clients)
|
||||
focus_client(f->clients[f->sel], raise, 0);
|
||||
|
||||
if (is_managed_frame(f)) {
|
||||
p->managed_stack = (Frame **)
|
||||
attach_item_begin(detach_item
|
||||
((void **) p->managed_stack, f,
|
||||
sizeof(Frame *)), f, sizeof(Frame *));
|
||||
p->files[P_MANAGED_SELECTED]->content =
|
||||
f->files[F_PREFIX]->content;
|
||||
p->files[P_MODE]->content = p->files[P_MANAGED_PREFIX]->content;
|
||||
} else {
|
||||
p->floating_stack = (Frame **)
|
||||
attach_item_begin(detach_item
|
||||
((void **) p->floating_stack, f,
|
||||
sizeof(Frame *)), f, sizeof(Frame *));
|
||||
p->files[P_FLOATING_SELECTED]->content =
|
||||
f->files[F_PREFIX]->content;
|
||||
p->files[P_MODE]->content = p->files[P_FLOATING_PREFIX]->content;
|
||||
if (raise)
|
||||
XRaiseWindow(dpy, f->win);
|
||||
}
|
||||
if (up)
|
||||
focus_page(p, raise, 0);
|
||||
}
|
||||
|
366
cmd/wm/client.c
Normal file
366
cmd/wm/client.c
Normal file
@ -0,0 +1,366 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
#include "wm.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
static Client zero_client = {0};
|
||||
|
||||
Client *
|
||||
alloc_client(Window w)
|
||||
{
|
||||
static int id = 0;
|
||||
char buf[MAX_BUF];
|
||||
char buf2[MAX_BUF];
|
||||
XClassHint ch;
|
||||
Client *c = (Client *) emalloc(sizeof(Client));
|
||||
|
||||
*c = zero_client;
|
||||
c->win = w;
|
||||
snprintf(buf, MAX_BUF, "/detached/client/%d", id);
|
||||
c->files[C_PREFIX] = ixp_create(ixps, buf);
|
||||
win_prop(dpy, c->win, XA_WM_NAME, buf2, MAX_BUF);
|
||||
snprintf(buf, MAX_BUF, "/detached/client/%d/name", id);
|
||||
c->files[C_NAME] = wmii_create_ixpfile(ixps, buf, buf2);
|
||||
if (XGetClassHint(dpy, c->win, &ch)) {
|
||||
snprintf(buf, MAX_BUF, "/detached/client/%d/class", id);
|
||||
c->files[C_CLASS] = wmii_create_ixpfile(ixps, buf, ch.res_class);
|
||||
snprintf(buf, MAX_BUF, "/detached/client/%d/instance", id);
|
||||
c->files[C_INSTANCE] = wmii_create_ixpfile(ixps, buf, ch.res_name);
|
||||
} else {
|
||||
snprintf(buf, MAX_BUF, "/detached/client/%d/class", id);
|
||||
c->files[C_CLASS] = ixp_create(ixps, buf);
|
||||
snprintf(buf, MAX_BUF, "/detached/client/%d/instance", id);
|
||||
c->files[C_INSTANCE] = ixp_create(ixps, buf);
|
||||
}
|
||||
id++;
|
||||
clients = (Client **) attach_item_end((void **) clients, c, sizeof(Client *));
|
||||
XSelectInput(dpy, c->win, CLIENT_MASK);
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
set_client_state(Client * c, int state)
|
||||
{
|
||||
long data[2];
|
||||
|
||||
data[0] = (long) state;
|
||||
data[1] = (long) None;
|
||||
XChangeProperty(dpy, c->win, wm_state, wm_state, 32,
|
||||
PropModeReplace, (unsigned char *) data, 2);
|
||||
}
|
||||
|
||||
void
|
||||
show_client(Client * c)
|
||||
{
|
||||
XSelectInput(dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
|
||||
XMapWindow(dpy, c->win);
|
||||
XSelectInput(dpy, c->win, CLIENT_MASK);
|
||||
set_client_state(c, NormalState);
|
||||
grab_client(c, Mod1Mask, Button1);
|
||||
grab_client(c, Mod1Mask, Button3);
|
||||
}
|
||||
|
||||
void
|
||||
hide_client(Client * c)
|
||||
{
|
||||
ungrab_client(c, AnyModifier, AnyButton);
|
||||
XSelectInput(dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
|
||||
XUnmapWindow(dpy, c->win);
|
||||
XSelectInput(dpy, c->win, CLIENT_MASK);
|
||||
set_client_state(c, WithdrawnState);
|
||||
}
|
||||
|
||||
void
|
||||
reparent_client(Client * c, Window w, int x, int y)
|
||||
{
|
||||
XSelectInput(dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
|
||||
XReparentWindow(dpy, c->win, w, x, y);
|
||||
XSelectInput(dpy, c->win, CLIENT_MASK);
|
||||
}
|
||||
|
||||
void
|
||||
grab_client(Client * c, unsigned long mod, unsigned int button)
|
||||
{
|
||||
XSelectInput(dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
|
||||
XGrabButton(dpy, button, mod, c->win, False,
|
||||
ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
|
||||
if ((mod != AnyModifier) && num_lock_mask) {
|
||||
XGrabButton(dpy, button, mod | num_lock_mask, c->win,
|
||||
False, ButtonPressMask, GrabModeAsync, GrabModeAsync,
|
||||
None, None);
|
||||
XGrabButton(dpy, button, mod | num_lock_mask | LockMask,
|
||||
c->win, False, ButtonPressMask, GrabModeAsync,
|
||||
GrabModeAsync, None, None);
|
||||
}
|
||||
XSelectInput(dpy, c->win, CLIENT_MASK);
|
||||
XSync(dpy, False);
|
||||
}
|
||||
|
||||
void
|
||||
ungrab_client(Client * c, unsigned long mod, unsigned int button)
|
||||
{
|
||||
XSelectInput(dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
|
||||
XUngrabButton(dpy, button, mod, c->win);
|
||||
if (mod != AnyModifier && num_lock_mask) {
|
||||
XUngrabButton(dpy, button, mod | num_lock_mask, c->win);
|
||||
XUngrabButton(dpy, button, mod | num_lock_mask | LockMask, c->win);
|
||||
}
|
||||
XSelectInput(dpy, c->win, CLIENT_MASK);
|
||||
XSync(dpy, False);
|
||||
}
|
||||
|
||||
void
|
||||
configure_client(Client * c)
|
||||
{
|
||||
XConfigureEvent e;
|
||||
e.type = ConfigureNotify;
|
||||
e.event = c->win;
|
||||
e.window = c->win;
|
||||
e.x = c->rect.x;
|
||||
e.y = c->rect.y;
|
||||
if (c->frame) {
|
||||
XRectangle *frect = rect_of_frame(c->frame);
|
||||
e.x += frect->x;
|
||||
e.y += frect->y;
|
||||
}
|
||||
e.width = c->rect.width;
|
||||
e.height = c->rect.height;
|
||||
e.border_width = c->border;
|
||||
e.above = None;
|
||||
e.override_redirect = False;
|
||||
|
||||
XSelectInput(dpy, c->win, CLIENT_MASK & ~StructureNotifyMask);
|
||||
XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *) & e);
|
||||
XSelectInput(dpy, c->win, CLIENT_MASK);
|
||||
XSync(dpy, False);
|
||||
}
|
||||
|
||||
void
|
||||
close_client(Client * c)
|
||||
{
|
||||
if (c->proto & PROTO_DEL)
|
||||
send_message(dpy, c->win, wm_protocols, wm_delete);
|
||||
else
|
||||
XKillClient(dpy, c->win);
|
||||
}
|
||||
|
||||
void
|
||||
_init_client(Client * c, XWindowAttributes * wa)
|
||||
{
|
||||
long msize;
|
||||
c->rect.x = wa->x;
|
||||
c->rect.y = wa->y;
|
||||
c->border = wa->border_width;
|
||||
c->rect.width = wa->width + 2 * c->border;
|
||||
c->rect.height = wa->height + 2 * c->border;
|
||||
XSetWindowBorderWidth(dpy, c->win, 0);
|
||||
XSelectInput(dpy, c->win, PropertyChangeMask);
|
||||
c->proto = win_proto(c->win);
|
||||
XGetTransientForHint(dpy, c->win, &c->trans);
|
||||
/* size hints */
|
||||
if (!XGetWMNormalHints(dpy, c->win, &c->size, &msize)
|
||||
|| !c->size.flags)
|
||||
c->size.flags = PSize;
|
||||
XAddToSaveSet(dpy, c->win);
|
||||
}
|
||||
|
||||
void
|
||||
handle_client_property(Client * c, XPropertyEvent * e)
|
||||
{
|
||||
char buf[1024];
|
||||
long msize;
|
||||
|
||||
buf[0] = '\0';
|
||||
if (e->state == PropertyDelete)
|
||||
return; /* ignore */
|
||||
|
||||
if (e->atom == wm_protocols) {
|
||||
/* update */
|
||||
c->proto = win_proto(c->win);
|
||||
return;
|
||||
}
|
||||
switch (e->atom) {
|
||||
case XA_WM_NAME:
|
||||
win_prop(dpy, c->win, XA_WM_NAME, buf, sizeof(buf));
|
||||
if (strlen(buf)) {
|
||||
if (c->files[C_NAME]->content)
|
||||
free(c->files[C_NAME]->content);
|
||||
c->files[C_NAME]->content = estrdup(buf);
|
||||
c->files[C_NAME]->size = strlen(buf);
|
||||
}
|
||||
if (c->frame)
|
||||
draw_client(c);
|
||||
invoke_core_event(defaults[WM_EVENT_CLIENT_UPDATE]);
|
||||
break;
|
||||
case XA_WM_TRANSIENT_FOR:
|
||||
XGetTransientForHint(dpy, c->win, &c->trans);
|
||||
break;
|
||||
case XA_WM_NORMAL_HINTS:
|
||||
if (!XGetWMNormalHints(dpy, c->win, &c->size, &msize)
|
||||
|| !c->size.flags) {
|
||||
c->size.flags = PSize;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
free_client(Client * c)
|
||||
{
|
||||
clients =
|
||||
(Client **) detach_item((void **) clients, c, sizeof(Client *));
|
||||
ixp_remove_file(ixps, c->files[C_PREFIX]);
|
||||
if (ixps->errstr)
|
||||
fprintf(stderr, "wmiiwm: free_client(): %s\n", ixps->errstr);
|
||||
free(c);
|
||||
}
|
||||
|
||||
/* speed reasoned function for client property change */
|
||||
void
|
||||
draw_client(Client * c)
|
||||
{
|
||||
Frame *f = c->frame;
|
||||
unsigned int tabh = tab_height(f);
|
||||
int i, size;
|
||||
XRectangle *frect;
|
||||
int tw;
|
||||
|
||||
if (!tabh)
|
||||
return;
|
||||
size = count_items((void **) f->clients);
|
||||
frect = rect_of_frame(f);
|
||||
tw = frect->width;
|
||||
if (size)
|
||||
tw /= size;
|
||||
for (i = 0; f->clients[i] && f->clients[i] != c; i++);
|
||||
|
||||
if (!f->clients[i + 1]) {
|
||||
int xoff = i * tw;
|
||||
draw_tab(f, c->files[C_NAME]->content, xoff, 0,
|
||||
frect->width - xoff, tabh, ISSELFRAME(f)
|
||||
&& f->clients[f->sel] == c);
|
||||
} else
|
||||
draw_tab(f, c->files[C_NAME]->content, i * tw, 0, tw, tabh,
|
||||
is_selected(f) && f->clients[f->sel] == c);
|
||||
}
|
||||
|
||||
void
|
||||
draw_clients(Frame * f)
|
||||
{
|
||||
unsigned int tabh = tab_height(f);
|
||||
int i, size = count_items((void **) f->clients);
|
||||
XRectangle *frect = rect_of_frame(f);
|
||||
int tw = frect->width;
|
||||
|
||||
if (!tabh || !size)
|
||||
return;
|
||||
if (size)
|
||||
tw /= size;
|
||||
for (i = 0; f->clients[i]; i++) {
|
||||
if (!f->clients[i + 1]) {
|
||||
int xoff = i * tw;
|
||||
draw_tab(f, f->clients[i]->files[C_NAME]->content,
|
||||
xoff, 0, frect->width - xoff, tabh, is_selected(f)
|
||||
&& f->clients[f->sel] == f->clients[i]);
|
||||
break;
|
||||
} else
|
||||
draw_tab(f, f->clients[i]->files[C_NAME]->content,
|
||||
i * tw, 0, tw, tabh, is_selected(f)
|
||||
&& f->clients[f->sel] == f->clients[i]);
|
||||
}
|
||||
XSync(dpy, False);
|
||||
}
|
||||
|
||||
int
|
||||
manage_class_instance(Client * c)
|
||||
{
|
||||
char buf[MAX_BUF];
|
||||
File *f;
|
||||
char *class = (char *) c->files[C_CLASS]->content;
|
||||
char *inst = (char *) c->files[C_INSTANCE]->content;
|
||||
|
||||
if (!c->files[C_CLASS]->content || !c->files[C_INSTANCE]->content)
|
||||
return 1;
|
||||
|
||||
snprintf(buf, sizeof(buf), "/default/client/%s:%s/manage",
|
||||
class ? class : "", inst ? inst : "");
|
||||
f = ixp_walk(ixps, buf);
|
||||
if (!f) {
|
||||
snprintf(buf, sizeof(buf), "/default/client/%s:%s/manage",
|
||||
class ? class : "", "*");
|
||||
f = ixp_walk(ixps, buf);
|
||||
}
|
||||
if (f && f->content)
|
||||
return _strtonum(f->content, 0, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
gravitate(Client * c, unsigned int tabh, unsigned int bw, int invert)
|
||||
{
|
||||
int dx = 0, dy = 0;
|
||||
int gravity = NorthWestGravity;
|
||||
|
||||
if (c->size.flags & PWinGravity) {
|
||||
gravity = c->size.win_gravity;
|
||||
}
|
||||
/* y */
|
||||
switch (gravity) {
|
||||
case StaticGravity:
|
||||
case NorthWestGravity:
|
||||
case NorthGravity:
|
||||
case NorthEastGravity:
|
||||
dy = tabh;
|
||||
break;
|
||||
case EastGravity:
|
||||
case CenterGravity:
|
||||
case WestGravity:
|
||||
dy = -(c->rect.height / 2) + tabh;
|
||||
break;
|
||||
case SouthEastGravity:
|
||||
case SouthGravity:
|
||||
case SouthWestGravity:
|
||||
dy = -c->rect.height;
|
||||
break;
|
||||
default: /* don't care */
|
||||
break;
|
||||
}
|
||||
|
||||
/* x */
|
||||
switch (gravity) {
|
||||
case StaticGravity:
|
||||
case NorthWestGravity:
|
||||
case WestGravity:
|
||||
case SouthWestGravity:
|
||||
dx = bw;
|
||||
break;
|
||||
case NorthGravity:
|
||||
case CenterGravity:
|
||||
case SouthGravity:
|
||||
dx = -(c->rect.width / 2) + bw;
|
||||
break;
|
||||
case NorthEastGravity:
|
||||
case EastGravity:
|
||||
case SouthEastGravity:
|
||||
dx = -(c->rect.width + bw);
|
||||
break;
|
||||
default: /* don't care */
|
||||
break;
|
||||
}
|
||||
|
||||
if (invert) {
|
||||
dx = -dx;
|
||||
dy = -dy;
|
||||
}
|
||||
c->rect.x += dx;
|
||||
c->rect.y += dy;
|
||||
}
|
582
cmd/wm/core.c
Normal file
582
cmd/wm/core.c
Normal file
@ -0,0 +1,582 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xproto.h>
|
||||
#include <X11/keysym.h>
|
||||
|
||||
#include "wm.h"
|
||||
|
||||
static void new_page(void *obj, char *cmd);
|
||||
static void _select_page(void *obj, char *cmd);
|
||||
static void _destroy_page(void *obj, char *cmd);
|
||||
static void quit(void *obj, char *cmd);
|
||||
static void _attach_client(void *obj, char *cmd);
|
||||
static void _detach_client(void *obj, char *cmd);
|
||||
static void _close_client(void *obj, char *cmd);
|
||||
static void pager(void *obj, char *cmd);
|
||||
static void icons(void *obj, char *cmd);
|
||||
|
||||
/* action table for /ctl namespace */
|
||||
Action core_acttbl[] = {
|
||||
{"new", new_page},
|
||||
{"destroy", _destroy_page},
|
||||
{"select", _select_page},
|
||||
{"attach", _attach_client},
|
||||
{"detach", _detach_client},
|
||||
{"close", _close_client},
|
||||
{"quit", quit},
|
||||
{"pager", pager},
|
||||
{"icons", icons},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
int
|
||||
comp_obj(void *c1, void *c2)
|
||||
{
|
||||
return c1 == c2;
|
||||
}
|
||||
|
||||
void
|
||||
run_action(File * f, void *obj, Action * acttbl)
|
||||
{
|
||||
int i;
|
||||
size_t len;
|
||||
|
||||
if (!f->content)
|
||||
return;
|
||||
for (i = 0; acttbl[i].name; i++) {
|
||||
len = strlen(acttbl[i].name);
|
||||
if (!strncmp(acttbl[i].name, (char *) f->content, len)) {
|
||||
if (f->size > len)
|
||||
acttbl[i].func(obj, &((char *) f->content)[len + 1]);
|
||||
else
|
||||
acttbl[i].func(obj, 0);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "wmiiwm: unknown action '%s'\n"
|
||||
" or invalid ctl device\n", (char *) f->content);
|
||||
}
|
||||
|
||||
static void
|
||||
quit(void *obj, char *cmd)
|
||||
{
|
||||
ixps->runlevel = SHUTDOWN;
|
||||
}
|
||||
|
||||
void
|
||||
invoke_core_event(File * f)
|
||||
{
|
||||
if (!f->content)
|
||||
return;
|
||||
spawn(dpy, f->content);
|
||||
}
|
||||
|
||||
void
|
||||
focus_page(Page * p, int raise, int down)
|
||||
{
|
||||
if (!pages)
|
||||
return;
|
||||
if (p != pages[sel]) {
|
||||
hide_page(pages[sel]);
|
||||
sel = index_item((void **) pages, p);
|
||||
show_page(pages[sel]);
|
||||
defaults[WM_SEL_PAGE]->content = p->files[P_PREFIX]->content;
|
||||
invoke_core_event(defaults[WM_EVENT_PAGE_UPDATE]);
|
||||
}
|
||||
if (down)
|
||||
focus_area(p->areas[p->sel], raise, 0, down);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
tab_height(Frame *f)
|
||||
{
|
||||
if (_strtonum(f->files[F_TAB]->content, 0, 1))
|
||||
return font->ascent + font->descent + 4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
border_width(Frame *f)
|
||||
{
|
||||
if(_strtonum(f->files[F_BORDER]->content, 0, 1))
|
||||
return BORDER_WIDTH;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
scale_rect(XRectangle * from_dim, XRectangle * to_dim,
|
||||
XRectangle * src, XRectangle * tgt)
|
||||
{
|
||||
double wfact = (double) to_dim->width / (double) from_dim->width;
|
||||
double hfact = (double) to_dim->height / (double) from_dim->height;
|
||||
|
||||
tgt->x = to_dim->x + (src->x * wfact);
|
||||
tgt->y = to_dim->y + (src->y * hfact);
|
||||
tgt->width = (src->width * wfact);
|
||||
tgt->height = (src->height * hfact);
|
||||
|
||||
if (tgt->width < 1)
|
||||
tgt->width = 1;
|
||||
if (tgt->height < 1)
|
||||
tgt->height = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
draw_pager_page(Page * p, Draw * d)
|
||||
{
|
||||
unsigned int i, j;
|
||||
XRectangle r = d->rect;
|
||||
char name[4];
|
||||
if (p == pages[sel]) {
|
||||
d->bg = blitz_loadcolor(dpy, screen_num, defaults[WM_SEL_BG_COLOR]->content);
|
||||
d->fg = blitz_loadcolor(dpy, screen_num, defaults[WM_SEL_FG_COLOR]->content);
|
||||
d->border = blitz_loadcolor(dpy, screen_num, defaults[WM_SEL_BORDER_COLOR]->content);
|
||||
d->font = font;
|
||||
} else {
|
||||
d->bg = blitz_loadcolor(dpy, screen_num, defaults[WM_NORM_BG_COLOR]->content);
|
||||
d->fg = blitz_loadcolor(dpy, screen_num, defaults[WM_NORM_FG_COLOR]->content);
|
||||
d->border = blitz_loadcolor(dpy, screen_num, defaults[WM_NORM_BORDER_COLOR]->content);
|
||||
d->font = font;
|
||||
}
|
||||
snprintf(name, sizeof(name), "%d", index_item((void **)pages, p));
|
||||
d->data = name;
|
||||
blitz_drawlabel(dpy, d);
|
||||
XSync(dpy, False);
|
||||
|
||||
for (i = 0; p->areas[i]; i++) {
|
||||
for(j = 0; p->areas[i]->frames[j]; j++) {
|
||||
if (i == p->sel && j == p->areas[i]->sel) {
|
||||
d->bg = blitz_loadcolor(dpy, screen_num, defaults[WM_SEL_BG_COLOR]->content);
|
||||
d->fg = blitz_loadcolor(dpy, screen_num, defaults[WM_SEL_FG_COLOR]->content);
|
||||
d->border = blitz_loadcolor(dpy, screen_num, defaults[WM_SEL_BORDER_COLOR]->content);
|
||||
d->font = font;
|
||||
} else {
|
||||
d->bg = blitz_loadcolor(dpy, screen_num, defaults[WM_NORM_BG_COLOR]->content);
|
||||
d->fg = blitz_loadcolor(dpy, screen_num, defaults[WM_NORM_FG_COLOR]->content);
|
||||
d->border = blitz_loadcolor(dpy, screen_num, defaults[WM_NORM_BORDER_COLOR]->content);
|
||||
d->font = font;
|
||||
}
|
||||
d->data = p->areas[i]->frames[j]->clients[p->areas[i]->frames[j]->sel]->files[C_NAME]->content;
|
||||
scale_rect(&rect, &r, &p->areas[i]->rect, &d->rect);
|
||||
blitz_drawlabel(dpy, d);
|
||||
XSync(dpy, False); /* do not clear upwards */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
draw_pager()
|
||||
{
|
||||
unsigned int ic, ir, tw, th, rows, cols, size;
|
||||
int i = 0;
|
||||
int dx;
|
||||
Draw d = {0};
|
||||
|
||||
blitz_getbasegeometry((void **) pages, &size, &cols, &rows);
|
||||
dx = (cols - 1) * GAP; /* GAPpx space */
|
||||
tw = (rect.width - dx) / cols;
|
||||
th = ((double) tw / rect.width) * rect.height;
|
||||
d.drawable = transient;
|
||||
d.gc = transient_gc;
|
||||
for (ir = 0; ir < rows; ir++) {
|
||||
for (ic = 0; ic < cols; ic++) {
|
||||
d.rect.x = ic * tw + (ic * GAP);
|
||||
d.rect.width = tw;
|
||||
if (rows == 1)
|
||||
d.rect.y = 0;
|
||||
else
|
||||
d.rect.y = ir * (rect.height - th) / (rows - 1);
|
||||
d.rect.height = th;
|
||||
if (!pages[i])
|
||||
return;
|
||||
draw_pager_page(pages[i], &d);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Page *
|
||||
xy_to_pager_page(int x, int y)
|
||||
{
|
||||
unsigned int ic, ir, tw, th, rows, cols, size;
|
||||
int i = 0;
|
||||
int dx;
|
||||
XRectangle r;
|
||||
|
||||
if (!pages)
|
||||
return 0;
|
||||
blitz_getbasegeometry((void **) pages, &size, &cols, &rows);
|
||||
dx = (cols - 1) * GAP; /* GAPpx space */
|
||||
tw = (rect.width - dx) / cols;
|
||||
th = ((double) tw / rect.width) * rect.height;
|
||||
|
||||
for (ir = 0; ir < rows; ir++) {
|
||||
for (ic = 0; ic < cols; ic++) {
|
||||
r.x = ic * tw + (ic * GAP);
|
||||
r.width = tw;
|
||||
if (rows == 1)
|
||||
r.y = 0;
|
||||
else
|
||||
r.y = ir * (rect.height - th) / (rows - 1);
|
||||
r.height = th;
|
||||
if (!pages[i])
|
||||
return 0;
|
||||
if (blitz_ispointinrect(x, y, &r))
|
||||
return pages[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_kpress(XKeyEvent * e)
|
||||
{
|
||||
KeySym ksym = XKeycodeToKeysym(dpy, e->keycode, 0);
|
||||
|
||||
if (ksym >= XK_1 && ksym <= XK_9)
|
||||
return ksym - XK_1;
|
||||
else if (ksym == XK_0)
|
||||
return 9;
|
||||
else if (ksym >= XK_a && ksym <= XK_z)
|
||||
return 10 + ksym - XK_a;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
pager(void *obj, char *cmd)
|
||||
{
|
||||
XEvent ev;
|
||||
int i;
|
||||
|
||||
if (!pages)
|
||||
return;
|
||||
|
||||
XClearWindow(dpy, transient);
|
||||
XMapRaised(dpy, transient);
|
||||
draw_pager();
|
||||
while (XGrabKeyboard(dpy, transient, True, GrabModeAsync, GrabModeAsync, CurrentTime) != GrabSuccess)
|
||||
usleep(1000);
|
||||
|
||||
for (;;) {
|
||||
while (!XCheckWindowEvent(dpy, transient, ButtonPressMask | KeyPressMask, &ev)) {
|
||||
usleep(20000);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (ev.type) {
|
||||
case KeyPress:
|
||||
XUnmapWindow(dpy, transient);
|
||||
if ((i = handle_kpress(&ev.xkey)) != -1)
|
||||
if (i < count_items((void **) pages))
|
||||
focus_page(pages[i], 0, 1);
|
||||
XUngrabKeyboard(dpy, CurrentTime);
|
||||
return;
|
||||
break;
|
||||
case ButtonPress:
|
||||
XUnmapWindow(dpy, transient);
|
||||
if (ev.xbutton.button == Button1) {
|
||||
Page *p = xy_to_pager_page(ev.xbutton.x, ev.xbutton.y);
|
||||
if (p)
|
||||
focus_page(p, 0, 1);
|
||||
}
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
draw_icons()
|
||||
{
|
||||
unsigned int i, ic, ir, tw, th, rows, cols, size;
|
||||
int dx, dy;
|
||||
|
||||
if (!detached)
|
||||
return;
|
||||
blitz_getbasegeometry((void **) detached, &size, &cols, &rows);
|
||||
dx = (cols - 1) * GAP; /* GAPpx space */
|
||||
dy = (rows - 1) * GAP; /* GAPpx space */
|
||||
tw = (rect.width - dx) / cols;
|
||||
th = (rect.height - dy) / rows;
|
||||
|
||||
XClearWindow(dpy, transient);
|
||||
XMapRaised(dpy, transient);
|
||||
i = 0;
|
||||
for (ir = 0; ir < rows; ir++) {
|
||||
for (ic = 0; ic < cols; ic++) {
|
||||
Client *c = detached[i++];
|
||||
XRectangle cr;
|
||||
if (!c)
|
||||
return;
|
||||
cr.x = ic * tw + (ic * GAP);
|
||||
cr.y = ir * th + (ir * GAP);
|
||||
cr.width = tw;
|
||||
cr.height = th;
|
||||
XMoveResizeWindow(dpy, c->win, cr.x, cr.y, cr.width,
|
||||
cr.height);
|
||||
configure_client(c);
|
||||
show_client(c);
|
||||
XRaiseWindow(dpy, c->win);
|
||||
grab_client(c, AnyModifier, AnyButton);
|
||||
XSync(dpy, False);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
icons(void *obj, char *cmd)
|
||||
{
|
||||
XEvent ev;
|
||||
int i, n;
|
||||
Client *c;
|
||||
|
||||
if (!detached)
|
||||
return;
|
||||
|
||||
XClearWindow(dpy, transient);
|
||||
XMapRaised(dpy, transient);
|
||||
draw_icons();
|
||||
while (XGrabKeyboard(dpy, transient, True, GrabModeAsync, GrabModeAsync, CurrentTime) != GrabSuccess)
|
||||
usleep(1000);
|
||||
|
||||
for (;;) {
|
||||
while (!XCheckMaskEvent(dpy, ButtonPressMask | KeyPressMask, &ev)) {
|
||||
usleep(20000);
|
||||
continue;
|
||||
}
|
||||
switch (ev.type) {
|
||||
case KeyPress:
|
||||
XUnmapWindow(dpy, transient);
|
||||
if ((n = handle_kpress(&ev.xkey)) != -1) {
|
||||
for (i = 0; detached && detached[i]; i++)
|
||||
hide_client(detached[i]);
|
||||
if (n - 1 < i) {
|
||||
c = detached[n];
|
||||
detached = (Client **) detach_item((void **) detached, c,
|
||||
sizeof(Client *));
|
||||
attach_client(c);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; detached && detached[i]; i++)
|
||||
hide_client(detached[i]);
|
||||
}
|
||||
XUngrabKeyboard(dpy, CurrentTime);
|
||||
return;
|
||||
break;
|
||||
case ButtonPress:
|
||||
if (ev.xbutton.button == Button1) {
|
||||
XUnmapWindow(dpy, transient);
|
||||
for (i = 0; detached && detached[i]; i++)
|
||||
hide_client(detached[i]);
|
||||
if ((c = win_to_client(ev.xbutton.window))) {
|
||||
detached =
|
||||
(Client **) detach_item((void **) detached, c,
|
||||
sizeof(Client *));
|
||||
attach_client(c);
|
||||
}
|
||||
XUngrabKeyboard(dpy, CurrentTime);
|
||||
}
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_close_client(void *obj, char *cmd)
|
||||
{
|
||||
if (clients && clients[sel_client])
|
||||
close_client(clients[sel_client]);
|
||||
}
|
||||
|
||||
static void
|
||||
_attach_client(void *obj, char *cmd)
|
||||
{
|
||||
if (detached) {
|
||||
Client *c = detached[0];
|
||||
detached =
|
||||
(Client **) detach_item((void **) detached, c,
|
||||
sizeof(Client *));
|
||||
attach_client(c);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_detach_client(void *obj, char *cmd)
|
||||
{
|
||||
Frame *f;
|
||||
if (!pages)
|
||||
return;
|
||||
f = SELFRAME(pages[sel]);
|
||||
if (!f)
|
||||
return;
|
||||
detach_client_from_frame(f->clients[f->sel], 0, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
_select_page(void *obj, char *cmd)
|
||||
{
|
||||
if (!pages || !cmd)
|
||||
return;
|
||||
if (!strncmp(cmd, "prev", 5))
|
||||
sel = index_prev_item((void **) pages, pages[sel]);
|
||||
else if (!strncmp(cmd, "next", 5))
|
||||
sel = index_next_item((void **) pages, pages[sel]);
|
||||
else
|
||||
sel = _strtonum(cmd, 0, count_items((void **)pages));
|
||||
focus_page(pages[sel], 0, 1);
|
||||
}
|
||||
|
||||
void
|
||||
destroy_page(Page * p)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; p->areas[i]; i++)
|
||||
destroy_area(p->areas[i]);
|
||||
free_page(p);
|
||||
if (pages) {
|
||||
show_page(pages[sel]);
|
||||
defaults[WM_SEL_PAGE]->content = pages[sel]->files[P_PREFIX]->content;
|
||||
focus_page(pages[sel], 0, 1);
|
||||
invoke_core_event(defaults[WM_EVENT_PAGE_UPDATE]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_destroy_page(void *obj, char *cmd)
|
||||
{
|
||||
if (!pages)
|
||||
return;
|
||||
destroy_page(pages[sel]);
|
||||
}
|
||||
|
||||
static void
|
||||
new_page(void *obj, char *cmd)
|
||||
{
|
||||
if (pages)
|
||||
hide_page(pages[sel]);
|
||||
alloc_page("0");
|
||||
}
|
||||
|
||||
Client *
|
||||
win_to_client(Window w)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; clients && clients[i]; i++)
|
||||
if (clients[i]->win == w)
|
||||
return clients[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
scan_wins()
|
||||
{
|
||||
int i;
|
||||
unsigned int num;
|
||||
Window *wins;
|
||||
XWindowAttributes wa;
|
||||
Window d1, d2;
|
||||
Client *c;
|
||||
|
||||
if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
|
||||
for (i = 0; i < num; i++) {
|
||||
if (!XGetWindowAttributes(dpy, wins[i], &wa))
|
||||
continue;
|
||||
if (wa.override_redirect
|
||||
|| XGetTransientForHint(dpy, wins[i], &d1))
|
||||
continue;
|
||||
if (wa.map_state == IsViewable) {
|
||||
c = alloc_client(wins[i]);
|
||||
_init_client(c, &wa);
|
||||
attach_client(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wins)
|
||||
XFree(wins);
|
||||
}
|
||||
|
||||
void *
|
||||
get_func(void *acttbl[][2], int rows, char *fname)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < rows; i++) {
|
||||
if (!strncmp((char *) acttbl[i][0], fname, MAX_BUF)) {
|
||||
return acttbl[i][1];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
win_proto(Window w)
|
||||
{
|
||||
Atom *protocols;
|
||||
long res;
|
||||
int protos = 0;
|
||||
int i;
|
||||
|
||||
res = property(dpy, w, wm_protocols, XA_ATOM,
|
||||
20L, ((unsigned char **) &protocols));
|
||||
if (res <= 0) {
|
||||
return protos;
|
||||
}
|
||||
for (i = 0; i < res; i++) {
|
||||
if (protocols[i] == wm_delete) {
|
||||
protos |= PROTO_DEL;
|
||||
}
|
||||
}
|
||||
free((char *) protocols);
|
||||
return protos;
|
||||
}
|
||||
|
||||
int
|
||||
win_state(Window w)
|
||||
{
|
||||
/* state hints */
|
||||
XWMHints *hints = XGetWMHints(dpy, w);
|
||||
int res;
|
||||
|
||||
long *prop = 0;
|
||||
if (property(dpy, w, wm_state, wm_state,
|
||||
2L, ((unsigned char **) &prop)) > 0) {
|
||||
res = (int) *prop;
|
||||
free((long *) prop);
|
||||
} else {
|
||||
res = hints ? hints->initial_state : NormalState;
|
||||
}
|
||||
|
||||
if (hints) {
|
||||
free(hints);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
handle_after_write(IXPServer * s, File * f)
|
||||
{
|
||||
if (f == defaults[WM_CTL])
|
||||
run_action(f, 0, core_acttbl);
|
||||
else if (f == defaults[WM_TRANS_COLOR]) {
|
||||
unsigned long col[1];
|
||||
col[0] = xorcolor.pixel;
|
||||
XFreeColors(dpy, DefaultColormap(dpy, screen_num), col, 1, 0);
|
||||
XAllocNamedColor(dpy, DefaultColormap(dpy, screen_num),
|
||||
defaults[WM_TRANS_COLOR]->content,
|
||||
&xorcolor, &xorcolor);
|
||||
XSetForeground(dpy, xorgc, xorcolor.pixel);
|
||||
}
|
||||
check_event(0);
|
||||
}
|
329
cmd/wm/event.c
Normal file
329
cmd/wm/event.c
Normal file
@ -0,0 +1,329 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <X11/keysym.h>
|
||||
|
||||
#include "wm.h"
|
||||
|
||||
/* local functions */
|
||||
static void handle_buttonpress(XEvent * e);
|
||||
static void handle_configurerequest(XEvent * e);
|
||||
static void handle_destroynotify(XEvent * e);
|
||||
static void handle_expose(XEvent * e);
|
||||
static void handle_maprequest(XEvent * e);
|
||||
static void handle_motionnotify(XEvent * e);
|
||||
static void handle_propertynotify(XEvent * e);
|
||||
static void handle_unmapnotify(XEvent * e);
|
||||
static void handle_enternotify(XEvent * e);
|
||||
static void update_ignore_enternotify_hack(XEvent * e);
|
||||
|
||||
static unsigned int ignore_enternotify_hack = 0;
|
||||
|
||||
void (*handler[LASTEvent]) (XEvent *);
|
||||
|
||||
void
|
||||
init_event_hander()
|
||||
{
|
||||
int i;
|
||||
/* init event handler */
|
||||
for (i = 0; i < LASTEvent; i++) {
|
||||
handler[i] = 0;
|
||||
}
|
||||
handler[ButtonPress] = handle_buttonpress;
|
||||
handler[CirculateNotify] = update_ignore_enternotify_hack;
|
||||
handler[ConfigureRequest] = handle_configurerequest;
|
||||
handler[DestroyNotify] = handle_destroynotify;
|
||||
handler[EnterNotify] = handle_enternotify;
|
||||
handler[Expose] = handle_expose;
|
||||
handler[GravityNotify] = update_ignore_enternotify_hack;
|
||||
handler[MapRequest] = handle_maprequest;
|
||||
handler[MapNotify] = update_ignore_enternotify_hack;
|
||||
handler[MotionNotify] = handle_motionnotify;
|
||||
handler[PropertyNotify] = handle_propertynotify;
|
||||
handler[UnmapNotify] = handle_unmapnotify;
|
||||
}
|
||||
|
||||
void
|
||||
check_event(Connection * c)
|
||||
{
|
||||
XEvent ev;
|
||||
while (XPending(dpy)) {
|
||||
XNextEvent(dpy, &ev);
|
||||
/* main evet loop */
|
||||
if (handler[ev.type]) {
|
||||
/* call handler */
|
||||
(handler[ev.type]) (&ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_buttonpress(XEvent * e)
|
||||
{
|
||||
Client *c;
|
||||
XButtonPressedEvent *ev = &e->xbutton;
|
||||
Frame *f = win_to_frame(ev->window);
|
||||
if (f) {
|
||||
handle_frame_buttonpress(ev, f);
|
||||
return;
|
||||
}
|
||||
if (ev->window == root) {
|
||||
XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
|
||||
XSync(dpy, False);
|
||||
}
|
||||
if ((c = win_to_client(ev->window))) {
|
||||
if (c->frame) {
|
||||
ev->state &= valid_mask;
|
||||
if (ev->state & Mod1Mask) {
|
||||
if (!is_managed_frame(c->frame))
|
||||
XRaiseWindow(dpy, c->frame->win);
|
||||
switch (ev->button) {
|
||||
case Button1:
|
||||
mouse_move(c->frame);
|
||||
break;
|
||||
case Button3:
|
||||
{
|
||||
Align align = xy_to_align(&c->rect, ev->x, ev->y);
|
||||
if (align == CENTER)
|
||||
mouse_move(c->frame);
|
||||
else
|
||||
mouse_resize(c->frame, align);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_configurerequest(XEvent * e)
|
||||
{
|
||||
XConfigureRequestEvent *ev = &e->xconfigurerequest;
|
||||
XWindowChanges wc;
|
||||
Client *c;
|
||||
unsigned int bw = 0, tabh = 0;
|
||||
Frame *f = 0;
|
||||
|
||||
update_ignore_enternotify_hack(e);
|
||||
/* fprintf(stderr, "%s", "configure request\n"); */
|
||||
c = win_to_client(ev->window);
|
||||
ev->value_mask &= ~CWSibling;
|
||||
|
||||
if (c) {
|
||||
/* fprintf(stderr, "%s", "configure request client\n"); */
|
||||
f = c->frame;
|
||||
|
||||
if (f) {
|
||||
bw = border_width(f);
|
||||
tabh = tab_height(f);
|
||||
}
|
||||
if (ev->value_mask & CWStackMode) {
|
||||
if (wc.stack_mode == Above)
|
||||
XRaiseWindow(dpy, c->win);
|
||||
else
|
||||
ev->value_mask &= ~CWStackMode;
|
||||
}
|
||||
gravitate(c, tabh ? tabh : bw, bw, 1);
|
||||
|
||||
if (ev->value_mask & CWX)
|
||||
c->rect.x = ev->x;
|
||||
if (ev->value_mask & CWY)
|
||||
c->rect.y = ev->y;
|
||||
if (ev->value_mask & CWWidth)
|
||||
c->rect.width = ev->width;
|
||||
if (ev->value_mask & CWHeight)
|
||||
c->rect.height = ev->height;
|
||||
if (ev->value_mask & CWBorderWidth)
|
||||
c->border = ev->border_width;
|
||||
|
||||
gravitate(c, tabh ? tabh : bw, bw, 0);
|
||||
|
||||
if (f) {
|
||||
XRectangle *frect = rect_of_frame(f);
|
||||
frect->x = wc.x = c->rect.x - bw;
|
||||
frect->y = wc.y = c->rect.y - (tabh ? tabh : bw);
|
||||
frect->width = wc.width = c->rect.width + 2 * bw;
|
||||
frect->height = wc.height =
|
||||
c->rect.height + bw + (tabh ? tabh : bw);
|
||||
wc.border_width = 1;
|
||||
wc.sibling = None;
|
||||
wc.stack_mode = ev->detail;
|
||||
XConfigureWindow(dpy, f->win, ev->value_mask, &wc);
|
||||
configure_client(c);
|
||||
}
|
||||
}
|
||||
wc.x = ev->x;
|
||||
wc.y = ev->y;
|
||||
|
||||
if (f) {
|
||||
/* if so, then bw and tabh are already initialized */
|
||||
wc.x = bw;
|
||||
wc.y = tabh ? tabh : bw;
|
||||
}
|
||||
wc.width = ev->width;
|
||||
wc.height = ev->height;
|
||||
wc.border_width = 0;
|
||||
wc.sibling = None;
|
||||
wc.stack_mode = Above;
|
||||
ev->value_mask &= ~CWStackMode;
|
||||
ev->value_mask |= CWBorderWidth;
|
||||
XConfigureWindow(dpy, e->xconfigurerequest.window, ev->value_mask,
|
||||
&wc);
|
||||
XSync(dpy, False);
|
||||
|
||||
/*
|
||||
fprintf(stderr, "%d,%d,%d,%d\n", wc.x, wc.y, wc.width, wc.height);
|
||||
*/
|
||||
}
|
||||
|
||||
static void
|
||||
handle_destroynotify(XEvent * e)
|
||||
{
|
||||
XDestroyWindowEvent *ev = &e->xdestroywindow;
|
||||
Client *c = win_to_client(ev->window);
|
||||
/* fprintf(stderr, "destroy: client 0x%x\n", (int)ev->window); */
|
||||
if (!c)
|
||||
return;
|
||||
if (c->frame)
|
||||
detach_client_from_frame(c, 0, 1);
|
||||
else if (detached && (index_item((void **) detached, c) >= 0))
|
||||
detached = (Client **) detach_item((void **) detached, c,
|
||||
sizeof(Client *));
|
||||
free_client(c);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_expose(XEvent * e)
|
||||
{
|
||||
static Frame *f;
|
||||
if (e->xexpose.count == 0) {
|
||||
f = win_to_frame(e->xbutton.window);
|
||||
if (f)
|
||||
draw_frame(f);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_maprequest(XEvent * e)
|
||||
{
|
||||
XMapRequestEvent *ev = &e->xmaprequest;
|
||||
static XWindowAttributes wa;
|
||||
static Client *c;
|
||||
|
||||
/* fprintf(stderr, "map: window 0x%x\n", (int)ev->window); */
|
||||
if (!XGetWindowAttributes(dpy, ev->window, &wa))
|
||||
return;
|
||||
if (wa.override_redirect)
|
||||
return;
|
||||
/* there're clients which send map requests twice */
|
||||
c = win_to_client(ev->window);
|
||||
if (!c)
|
||||
c = alloc_client(ev->window);
|
||||
if (!c->frame) {
|
||||
_init_client(c, &wa);
|
||||
attach_client(c);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_motionnotify(XEvent * e)
|
||||
{
|
||||
Frame *f = win_to_frame(e->xmotion.window);
|
||||
Cursor cursor;
|
||||
if (f) {
|
||||
Frame *old = SELFRAME(pages[sel]);
|
||||
if (old != f) {
|
||||
focus_frame(f, 0, 0, 1);
|
||||
draw_frame(old);
|
||||
draw_frame(f);
|
||||
} else if (f->clients) {
|
||||
/* multihead assumption */
|
||||
XSetInputFocus(dpy, f->clients[f->sel]->win,
|
||||
RevertToPointerRoot, CurrentTime);
|
||||
XSync(dpy, False);
|
||||
}
|
||||
cursor = cursor_for_motion(f, e->xmotion.x, e->xmotion.y);
|
||||
if (cursor != f->cursor) {
|
||||
f->cursor = cursor;
|
||||
XDefineCursor(dpy, f->win, cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_propertynotify(XEvent * e)
|
||||
{
|
||||
XPropertyEvent *ev = &e->xproperty;
|
||||
Client *c = win_to_client(ev->window);
|
||||
|
||||
if (c) {
|
||||
handle_client_property(c, ev);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_unmapnotify(XEvent * e)
|
||||
{
|
||||
XUnmapEvent *ev = &e->xunmap;
|
||||
Client *c;
|
||||
|
||||
update_ignore_enternotify_hack(e);
|
||||
if (ev->event == root)
|
||||
return;
|
||||
if ((c = win_to_client(ev->window))) {
|
||||
if (c->frame) {
|
||||
detach_client_from_frame(c, 1, 0);
|
||||
if (pages)
|
||||
draw_page(pages[sel]);
|
||||
free_client(c);
|
||||
} else if (detached) {
|
||||
if (index_item((void **) detached, c) == -1)
|
||||
free_client(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_enternotify(XEvent * e)
|
||||
{
|
||||
XCrossingEvent *ev = &e->xcrossing;
|
||||
Client *c;
|
||||
|
||||
if (ev->mode != NotifyNormal)
|
||||
return;
|
||||
|
||||
/* mouse is not in the focus window */
|
||||
if (ev->detail == NotifyInferior)
|
||||
return;
|
||||
|
||||
c = win_to_client(ev->window);
|
||||
if (c && c->frame && (ev->serial != ignore_enternotify_hack)) {
|
||||
Frame *old = SELFRAME(pages[sel]);
|
||||
XUndefineCursor(dpy, c->frame->win);
|
||||
if (old != c->frame) {
|
||||
focus_frame(c->frame, 0, 0, 1);
|
||||
draw_frame(old);
|
||||
draw_frame(c->frame);
|
||||
} else {
|
||||
/* multihead assumption */
|
||||
XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
|
||||
XSync(dpy, False);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
update_ignore_enternotify_hack(XEvent * e)
|
||||
{
|
||||
ignore_enternotify_hack = e->xany.serial;
|
||||
XSync(dpy, False);
|
||||
}
|
659
cmd/wm/frame.c
Normal file
659
cmd/wm/frame.c
Normal file
@ -0,0 +1,659 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "wm.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
static Frame zero_frame = {0};
|
||||
|
||||
static void mouse();
|
||||
static void select_client(void *obj, char *cmd);
|
||||
static void handle_after_write_frame(IXPServer * s, File * f);
|
||||
static void handle_before_read_frame(IXPServer * s, File * f);
|
||||
|
||||
/* action table for /frame/?/ namespace */
|
||||
Action frame_acttbl[] = {
|
||||
{"select", select_client},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
Frame *
|
||||
alloc_frame(XRectangle * r, int add_frame_border, int floating)
|
||||
{
|
||||
XSetWindowAttributes wa;
|
||||
static int id = 0;
|
||||
char buf[MAX_BUF];
|
||||
Frame *f = (Frame *) emalloc(sizeof(Frame));
|
||||
|
||||
*f = zero_frame;
|
||||
f->rect = *r;
|
||||
f->cursor = normal_cursor;
|
||||
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d", id);
|
||||
f->files[F_PREFIX] = ixp_create(ixps, buf);
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/client", id);
|
||||
f->files[F_CLIENT_PREFIX] = ixp_create(ixps, buf);
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/client/sel", id);
|
||||
f->files[F_SEL_CLIENT] = ixp_create(ixps, buf);
|
||||
f->files[F_SEL_CLIENT]->bind = 1;
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/ctl", id);
|
||||
f->files[F_CTL] = ixp_create(ixps, buf);
|
||||
f->files[F_CTL]->after_write = handle_after_write_frame;
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/size", id);
|
||||
f->files[F_SIZE] = ixp_create(ixps, buf);
|
||||
f->files[F_SIZE]->before_read = handle_before_read_frame;
|
||||
f->files[F_SIZE]->after_write = handle_after_write_frame;
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/border", id);
|
||||
f->files[F_BORDER] = wmii_create_ixpfile(ixps, buf, defaults[WM_BORDER]->content);
|
||||
f->files[F_BORDER]->after_write = handle_after_write_frame;
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/tab", id);
|
||||
f->files[F_TAB] = wmii_create_ixpfile(ixps, buf, defaults[WM_TAB]->content);
|
||||
f->files[F_TAB]->after_write = handle_after_write_frame;
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/handleinc", id);
|
||||
f->files[F_HANDLE_INC] = wmii_create_ixpfile(ixps, buf, defaults[WM_HANDLE_INC]->content);
|
||||
f->files[F_HANDLE_INC]->after_write = handle_after_write_frame;
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/locked", id);
|
||||
f->files[F_LOCKED] = wmii_create_ixpfile(ixps, buf, defaults[WM_LOCKED]->content);
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/selstyle/bgcolor", id);
|
||||
f->files[F_SEL_BG_COLOR] = wmii_create_ixpfile(ixps, buf, defaults[WM_SEL_BG_COLOR]->content);
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/selstyle/fgcolor", id);
|
||||
f->files[F_SEL_FG_COLOR] = wmii_create_ixpfile(ixps, buf, defaults[WM_SEL_FG_COLOR]->content);
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/selstyle/bordercolor", id);
|
||||
f->files[F_SEL_BORDER_COLOR] = wmii_create_ixpfile(ixps, buf, defaults[WM_SEL_BORDER_COLOR]->content);
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/normstyle/bgcolor", id);
|
||||
f->files[F_NORM_BG_COLOR] = wmii_create_ixpfile(ixps, buf, defaults[WM_NORM_BG_COLOR]->content);
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/normstyle/fgcolor", id);
|
||||
f->files[F_NORM_FG_COLOR] = wmii_create_ixpfile(ixps, buf, defaults[WM_NORM_FG_COLOR]->content);
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/normstyle/bordercolor", id);
|
||||
f->files[F_NORM_BORDER_COLOR] = wmii_create_ixpfile(ixps, buf, defaults[WM_NORM_BORDER_COLOR]->content);
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/event/b2press", id);
|
||||
f->files[F_EVENT_B2PRESS] = wmii_create_ixpfile(ixps, buf, defaults[WM_EVENT_B2PRESS]->content);
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/event/b3press", id);
|
||||
f->files[F_EVENT_B3PRESS] = wmii_create_ixpfile(ixps, buf, defaults[WM_EVENT_B3PRESS]->content);
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/event/b4press", id);
|
||||
f->files[F_EVENT_B4PRESS] = wmii_create_ixpfile(ixps, buf, defaults[WM_EVENT_B4PRESS]->content);
|
||||
snprintf(buf, MAX_BUF, "/detached/frame/%d/event/b5press", id);
|
||||
f->files[F_EVENT_B5PRESS] = wmii_create_ixpfile(ixps, buf, defaults[WM_EVENT_B5PRESS]->content);
|
||||
id++;
|
||||
|
||||
wa.override_redirect = 1;
|
||||
wa.background_pixmap = ParentRelative;
|
||||
wa.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask
|
||||
| PointerMotionMask | SubstructureRedirectMask
|
||||
| SubstructureNotifyMask;
|
||||
|
||||
if (add_frame_border) {
|
||||
int bw = border_width(f);
|
||||
int th = tab_height(f);
|
||||
f->rect.width += 2 * bw;
|
||||
f->rect.height += bw + (th ? th : bw);
|
||||
}
|
||||
f->win =
|
||||
XCreateWindow(dpy, root, f->rect.x, f->rect.y, f->rect.width, f->rect.height, 0,
|
||||
DefaultDepth(dpy, screen_num), CopyFromParent,
|
||||
DefaultVisual(dpy, screen_num),
|
||||
CWOverrideRedirect | CWBackPixmap | CWEventMask,
|
||||
&wa);
|
||||
|
||||
XDefineCursor(dpy, f->win, f->cursor);
|
||||
f->gc = XCreateGC(dpy, f->win, 0, 0);
|
||||
XSync(dpy, False);
|
||||
frames =
|
||||
(Frame **) attach_item_end((void **) frames, f, sizeof(Frame *));
|
||||
return f;
|
||||
}
|
||||
|
||||
void
|
||||
focus_frame(Frame * f, int raise, int up, int down)
|
||||
{
|
||||
Area *a = f->area;
|
||||
if (down && f->clients)
|
||||
focus_client(f->clients[f->sel], raise, 0);
|
||||
a->sel = index_item((void **)a->frames, f);
|
||||
a->files[A_SEL_FRAME]->content = f->files[F_PREFIX]->content;
|
||||
if (raise && a->page->sel == 0) /* only floating windows are raised */
|
||||
XRaiseWindow(dpy, f->win);
|
||||
if (up)
|
||||
focus_area(a, raise, up, 0);
|
||||
}
|
||||
|
||||
Frame *
|
||||
win_to_frame(Window w)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; frames && frames[i]; i++)
|
||||
if (frames[i]->win == w)
|
||||
return frames[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
toggle_frame(Frame * f)
|
||||
{
|
||||
Page *p = f->page;
|
||||
int was_managed = is_managed_frame(f);
|
||||
detach_frame_from_page(f, 1);
|
||||
attach_Frameo_page(p, f, !was_managed);
|
||||
resize_frame(f, rect_of_frame(f), 0, 0);
|
||||
draw_page(p);
|
||||
}
|
||||
|
||||
void
|
||||
free_frame(Frame * f)
|
||||
{
|
||||
frames = (Frame **) detach_item((void **) frames, f, sizeof(Frame *));
|
||||
XFreeGC(dpy, f->gc);
|
||||
XDestroyWindow(dpy, f->win);
|
||||
ixp_remove_file(ixps, f->files[F_PREFIX]);
|
||||
if (ixps->errstr)
|
||||
fprintf(stderr, "wmiiwm: free_frame(): %s\n", ixps->errstr);
|
||||
free(f);
|
||||
}
|
||||
|
||||
XRectangle *
|
||||
rect_of_frame(Frame * f)
|
||||
{
|
||||
return is_managed_frame(f) ? &f->managed_rect : &f->floating_rect;
|
||||
}
|
||||
|
||||
void
|
||||
focus_client(Client * c, int raise, int up)
|
||||
{
|
||||
Frame *f = 0;
|
||||
|
||||
sel = c;
|
||||
|
||||
/* focus client */
|
||||
if (c) {
|
||||
f = c->frame;
|
||||
for (f->sel = 0; f->clients && f->clients[f->sel] != c; f->sel++);
|
||||
f->files[F_CLIENT_SELECTED]->content = c->files[C_PREFIX]->content;
|
||||
if (raise)
|
||||
XRaiseWindow(dpy, c->win);
|
||||
XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
|
||||
} else
|
||||
XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
|
||||
invoke_core_event(core_files[CORE_EVENT_CLIENT_UPDATE]);
|
||||
if (up && f)
|
||||
focus_frame(f, raise, up, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
resize_clients(Frame * f, int tabh, int bw)
|
||||
{
|
||||
int i;
|
||||
XRectangle *frect = rect_of_frame(f);
|
||||
for (i = 0; f->clients && f->clients[i]; i++) {
|
||||
Client *c = f->clients[i];
|
||||
c->rect.x = bw;
|
||||
c->rect.y = tabh ? tabh : bw;
|
||||
c->rect.width = frect->width - 2 * bw;
|
||||
c->rect.height = frect->height - bw - (tabh ? tabh : bw);
|
||||
XMoveResizeWindow(dpy, c->win, c->rect.x, c->rect.y,
|
||||
c->rect.width, c->rect.height);
|
||||
configure_client(c);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
is_managed_frame(Frame * f)
|
||||
{
|
||||
Page *p = f->page;
|
||||
if (p->managed) {
|
||||
int i;
|
||||
for (i = 0; p->managed[i] && p->managed[i] != f; i++);
|
||||
if (p->managed[i])
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
check_dimensions(Frame * f, unsigned int tabh, unsigned int bw)
|
||||
{
|
||||
XRectangle *frect = rect_of_frame(f);
|
||||
Client *c = f->clients ? f->clients[f->sel] : 0;
|
||||
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
if (c->size.flags & PMinSize) {
|
||||
if (frect->width - 2 * bw < c->size.min_width) {
|
||||
frect->width = c->size.min_width + 2 * bw;
|
||||
}
|
||||
if (frect->height - bw - (tabh ? tabh : bw) < c->size.min_height) {
|
||||
frect->height = c->size.min_height + bw + (tabh ? tabh : bw);
|
||||
}
|
||||
}
|
||||
if (c->size.flags & PMaxSize) {
|
||||
if (frect->width - 2 * bw > c->size.max_width) {
|
||||
frect->width = c->size.max_width + 2 * bw;
|
||||
}
|
||||
if (frect->height - bw - (tabh ? tabh : bw) > c->size.max_height) {
|
||||
frect->height = c->size.max_height + bw + (tabh ? tabh : bw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
resize_incremental(Frame * f, unsigned int tabh, unsigned int bw)
|
||||
{
|
||||
XRectangle *frect = rect_of_frame(f);
|
||||
Client *c = f->clients ? f->clients[f->sel] : 0;
|
||||
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
/*
|
||||
* increment stuff, see chapter 4.1.2.3 of the Inter-Client
|
||||
* Communication Conventions Manual
|
||||
*/
|
||||
if (c->size.flags & PResizeInc) {
|
||||
int base_width = 0, base_height = 0;
|
||||
|
||||
if (c->size.flags & PBaseSize) {
|
||||
base_width = c->size.base_width;
|
||||
base_height = c->size.base_height;
|
||||
} else if (c->size.flags & PMinSize) {
|
||||
/* base_{width,height} defaults to min_{width,height} */
|
||||
base_width = c->size.min_width;
|
||||
base_height = c->size.min_height;
|
||||
}
|
||||
/*
|
||||
* client_width = base_width + i * c->size.width_inc for an
|
||||
* integer i
|
||||
*/
|
||||
frect->width -=
|
||||
(frect->width - 2 * bw - base_width) % c->size.width_inc;
|
||||
frect->height -=
|
||||
(frect->height - bw - (tabh ? tabh : bw) -
|
||||
base_height) % c->size.height_inc;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
resize_frame(Frame * f, XRectangle * r, XPoint * pt, int ignore_layout)
|
||||
{
|
||||
unsigned int tabh = _strtonum(f->files[F_TAB_H]->content, 0, 30);
|
||||
unsigned int bw = _strtonum(f->files[F_BORDER_W]->content, 0, 10);
|
||||
XRectangle *frect = rect_of_frame(f);
|
||||
|
||||
/* do layout stuff if necessary */
|
||||
if (!ignore_layout && is_managed_frame(f)) {
|
||||
Page *p = f->page;
|
||||
if (p && p->layout) {
|
||||
p->layout->resize(f, r, pt);
|
||||
} else
|
||||
*frect = *r;
|
||||
} else
|
||||
*frect = *r;
|
||||
|
||||
/* resize if client requests special size */
|
||||
check_dimensions(f, tabh, bw);
|
||||
|
||||
if (f->files[F_HANDLE_INC]->content
|
||||
&& ((char *) f->files[F_HANDLE_INC]->content)[0] == '1')
|
||||
resize_incremental(f, tabh, bw);
|
||||
|
||||
XMoveResizeWindow(dpy, f->win, frect->x, frect->y, frect->width,
|
||||
frect->height);
|
||||
resize_clients(f, (tabh ? tabh : bw), bw);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
draw_tab(Frame * f, char *text, int x, int y, int w, int h, int sel)
|
||||
{
|
||||
Draw d = {0};
|
||||
XFontStruct *font = blitz_getfont(dpy, f->files[F_SELECTED_TEXT_FONT]->content);
|
||||
|
||||
d.drawable = f->win;
|
||||
d.gc = f->gc;
|
||||
d.rect.x = x;
|
||||
d.rect.y = y;
|
||||
d.rect.width = w;
|
||||
d.rect.height = h;
|
||||
d.data = text;
|
||||
if (sel) {
|
||||
d.bg =
|
||||
blitz_loadcolor(dpy, screen_num,
|
||||
f->files[F_SELECTED_BG_COLOR]->content);
|
||||
d.fg =
|
||||
blitz_loadcolor(dpy, screen_num,
|
||||
f->files[F_SELECTED_FG_COLOR]->content);
|
||||
d.border =
|
||||
blitz_loadcolor(dpy, screen_num,
|
||||
f->files[F_SELECTED_BORDER_COLOR]->content);
|
||||
d.font = font;
|
||||
} else {
|
||||
d.bg =
|
||||
blitz_loadcolor(dpy, screen_num,
|
||||
f->files[F_NORMAL_BG_COLOR]->content);
|
||||
d.fg =
|
||||
blitz_loadcolor(dpy, screen_num,
|
||||
f->files[F_NORMAL_FG_COLOR]->content);
|
||||
d.border =
|
||||
blitz_loadcolor(dpy, screen_num,
|
||||
f->files[F_NORMAL_BORDER_COLOR]->content);
|
||||
d.font = font;
|
||||
}
|
||||
blitz_drawlabel(dpy, &d);
|
||||
XSync(dpy, False);
|
||||
XFreeFont(dpy, font);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Assumes following files:
|
||||
*
|
||||
* ./sel-style/text-font "<value>"
|
||||
* ./sel-style/text-size "<int>"
|
||||
* ./sel-style/text-color "#RRGGBBAA"
|
||||
* ./sel-style/bg-color "#RRGGBBAA"
|
||||
* ./sel-style/border-color "#RRGGBBAA [#RRGGBBAA [#RRGGBBAA [#RRGGBBAA]]]"
|
||||
* ./norm-style/text-font "<value>"
|
||||
* ./norm-style/text-size "<int>"
|
||||
* ./norm-style/text-color "#RRGGBBAA"
|
||||
* ./norm-style/bg-color "#RRGGBBAA"
|
||||
* ./norm-style/border-color "#RRGGBBAA [#RRGGBBAA [#RRGGBBAA [#RRGGBBAA]]]"
|
||||
*/
|
||||
void
|
||||
draw_frame(Frame * f)
|
||||
{
|
||||
Draw d = {0};
|
||||
int bw = _strtonum(f->files[F_BORDER_W]->content, 0, 10);
|
||||
XRectangle notch;
|
||||
XRectangle *frect = rect_of_frame(f);
|
||||
|
||||
if (bw) {
|
||||
notch.x = bw;
|
||||
notch.y = bw;
|
||||
notch.width = frect->width - 2 * bw;
|
||||
notch.height = frect->height - 2 * bw;
|
||||
d.drawable = f->win;
|
||||
d.gc = f->gc;
|
||||
|
||||
/* define ground plate (i = 0) */
|
||||
if (is_selected(f)) {
|
||||
d.bg =
|
||||
blitz_loadcolor(dpy, screen_num,
|
||||
f->files[F_SELECTED_BG_COLOR]->content);
|
||||
d.fg =
|
||||
blitz_loadcolor(dpy, screen_num,
|
||||
f->files[F_SELECTED_FG_COLOR]->content);
|
||||
d.border =
|
||||
blitz_loadcolor(dpy, screen_num,
|
||||
f->files[F_SELECTED_BORDER_COLOR]->
|
||||
content);
|
||||
} else {
|
||||
d.bg =
|
||||
blitz_loadcolor(dpy, screen_num,
|
||||
f->files[F_NORMAL_BG_COLOR]->content);
|
||||
d.fg =
|
||||
blitz_loadcolor(dpy, screen_num,
|
||||
f->files[F_NORMAL_FG_COLOR]->content);
|
||||
d.border =
|
||||
blitz_loadcolor(dpy, screen_num,
|
||||
f->files[F_NORMAL_BORDER_COLOR]->content);
|
||||
}
|
||||
d.rect = *frect;
|
||||
d.rect.x = d.rect.y = 0;
|
||||
d.notch = ¬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;
|
||||
}
|
||||
}
|
||||
}
|
393
cmd/wm/layout_column.c
Normal file
393
cmd/wm/layout_column.c
Normal file
@ -0,0 +1,393 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "wm.h"
|
||||
#include "layout.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
typedef struct Acme Acme;
|
||||
typedef struct Column Column;
|
||||
|
||||
struct Acme {
|
||||
int sel;
|
||||
Column **column;
|
||||
};
|
||||
|
||||
struct Column {
|
||||
int sel;
|
||||
int refresh;
|
||||
Frame **frames;
|
||||
XRectangle rect;
|
||||
};
|
||||
|
||||
static void init_col(Page * p, int argc, char **argv);
|
||||
static void deinit_col(Page * p);
|
||||
static void arrange_col(Page * p);
|
||||
static void attach_col(Page * p, Client * c);
|
||||
static void detach_col(Page *, Client * c, int unmapped, int destroyed);
|
||||
static void resize_col(Frame * f, XRectangle * new, XPoint * pt);
|
||||
static void aux_col(Frame * f, char *what);
|
||||
|
||||
static LayoutImpl lcol = {"col", init_col, deinit_col, arrange_col, attach_col,
|
||||
detach_col, resize_col, aux_col
|
||||
};
|
||||
|
||||
static Column zero_column = {0};
|
||||
static Acme zero_acme = {0};
|
||||
|
||||
void
|
||||
init_layout_col()
|
||||
{
|
||||
layouts =
|
||||
(Layout **) attach_item_end((void **) layouts, &lcol,
|
||||
sizeof(Layout *));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
arrange_column(Page * p, Column * col)
|
||||
{
|
||||
int i;
|
||||
int n = count_items((void **) col->frames);
|
||||
unsigned int height = p->managed_rect.height / n;
|
||||
for (i = 0; col->frames && col->frames[i]; i++) {
|
||||
if (col->refresh) {
|
||||
col->frames[i]->rect = col->rect;
|
||||
col->frames[i]->rect.height = height;
|
||||
col->frames[i]->rect.y = i * height;
|
||||
}
|
||||
resize_frame(col->frames[i], &col->frames[i]->rect, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
arrange_col(Page * p)
|
||||
{
|
||||
Acme *acme = p->aux;
|
||||
int i;
|
||||
|
||||
if (!acme) {
|
||||
fprintf(stderr, "%s", "wmiiwm: fatal, page has no layout\n");
|
||||
exit(1);
|
||||
}
|
||||
for (i = 0; acme->column && acme->column[i]; i++)
|
||||
arrange_column(p, acme->column[i]);
|
||||
}
|
||||
|
||||
static void
|
||||
init_col(Page * p, int argc, char **argv)
|
||||
{
|
||||
Acme *acme = emalloc(sizeof(Acme));
|
||||
int i, j, n, cols = 1;
|
||||
unsigned int width = 1;
|
||||
Column *col;
|
||||
|
||||
*acme = zero_acme;
|
||||
p->aux = acme;
|
||||
|
||||
/* processing argv */
|
||||
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
|
||||
switch (argv[i][1]) {
|
||||
case 'c':
|
||||
cols = _strtonum(argv[++i], 0, 32);
|
||||
if (cols < 1)
|
||||
cols = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
width = p->managed_rect.width / cols;
|
||||
acme->column = emalloc((cols + 1) * sizeof(Column *));
|
||||
for (i = 0; i < cols; i++) {
|
||||
acme->column[i] = emalloc(sizeof(Column));
|
||||
*acme->column[i] = zero_column;
|
||||
acme->column[i]->rect = p->managed_rect;
|
||||
acme->column[i]->rect.x = i * width;
|
||||
acme->column[i]->rect.width = width;
|
||||
acme->column[i]->refresh = 1;
|
||||
}
|
||||
acme->column[cols] = 0; /* null termination of array */
|
||||
|
||||
/*
|
||||
* Frame attaching strategy works as follows: 1. If more clients than
|
||||
* column exist, then each column gets one client, except eastmost
|
||||
* column, which gets all remaining clients. 2. If lesser clients
|
||||
* than column exist, than filling begins from eastmost to westmost
|
||||
* column until no more clients exist.
|
||||
*/
|
||||
n = count_items((void **) clients);
|
||||
if (n > cols) {
|
||||
/* 1st. case */
|
||||
j = 0;
|
||||
for (i = 0; i < (cols - 1); i++) {
|
||||
col = acme->column[i];
|
||||
col->frames = (Frame **) attach_item_end((void **) col->frames,
|
||||
alloc_frame(&clients[i]->rect, 1, 1), sizeof(Frame *));
|
||||
col->frames[0]->aux = col;
|
||||
attach_Frameo_page(p, col->frames[0], 1);
|
||||
attach_Cliento_frame(col->frames[0], clients[j]);
|
||||
j++;
|
||||
}
|
||||
col = acme->column[cols - 1];
|
||||
col->frames = emalloc((n - j + 1) * sizeof(Frame *));
|
||||
for (i = 0; i + j < n; i++) {
|
||||
col->frames[i] = alloc_frame(&clients[j + i]->rect, 1, 1);
|
||||
col->frames[i]->aux = col;
|
||||
attach_Frameo_page(p, col->frames[i], 1);
|
||||
attach_Cliento_frame(col->frames[i], clients[j + i]);
|
||||
}
|
||||
col->frames[i] = 0;
|
||||
} else {
|
||||
/* 2nd case */
|
||||
j = 0;
|
||||
for (i = cols - 1; j < n; i--) {
|
||||
col = acme->column[i];
|
||||
col->frames = (Frame **) attach_item_end((void **) col->frames,
|
||||
alloc_frame(&clients[i]->rect, 1, 1), sizeof(Frame *));
|
||||
col->frames[0]->aux = col;
|
||||
attach_Frameo_page(p, col->frames[0], 1);
|
||||
attach_Cliento_frame(col->frames[0], clients[j]);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
arrange_col(p);
|
||||
}
|
||||
|
||||
static void
|
||||
deinit_col(Page * p)
|
||||
{
|
||||
Acme *acme = p->aux;
|
||||
int i;
|
||||
|
||||
for (i = 0; acme->column && acme->column[i]; i++) {
|
||||
Column *col = acme->column[i];
|
||||
int j;
|
||||
for (j = 0; col->frames && col->frames[j]; j++) {
|
||||
Frame *f = col->frames[j];
|
||||
while (f->clients && f->clients[0])
|
||||
detach_client_from_frame(f->clients[0], 0, 0);
|
||||
detach_frame_from_page(f, 1);
|
||||
free_frame(f);
|
||||
}
|
||||
free(col->frames);
|
||||
}
|
||||
free(acme->column);
|
||||
free(acme);
|
||||
p->aux = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
attach_col(Page * p, Client * c)
|
||||
{
|
||||
Acme *acme = p->aux;
|
||||
Column *col;
|
||||
Frame *f;
|
||||
|
||||
col = acme->column[acme->sel];
|
||||
f = alloc_frame(&c->rect, 1, 1);
|
||||
col->frames = (Frame **) attach_item_end((void **) col->frames, f,
|
||||
sizeof(Frame *));
|
||||
f->aux = col;
|
||||
col->refresh = 1;
|
||||
attach_Frameo_page(p, f, 1);
|
||||
attach_Cliento_frame(f, c);
|
||||
|
||||
arrange_col(p);
|
||||
}
|
||||
|
||||
static void
|
||||
detach_col(Page * p, Client * c, int unmapped, int destroyed)
|
||||
{
|
||||
Frame *f = c->frame;
|
||||
Column *col = f->aux;
|
||||
|
||||
if (!col)
|
||||
return; /* client was not attached, maybe exit(1) in
|
||||
* such case */
|
||||
col->frames = (Frame **) detach_item((void **) col->frames, c->frame,
|
||||
sizeof(Frame *));
|
||||
col->refresh = 1;
|
||||
detach_client_from_frame(c, unmapped, destroyed);
|
||||
detach_frame_from_page(f, 1);
|
||||
free_frame(f);
|
||||
|
||||
arrange_col(p);
|
||||
}
|
||||
|
||||
static void
|
||||
drop_resize(Frame * f, XRectangle * new)
|
||||
{
|
||||
Column *col = f->aux;
|
||||
Acme *acme = f->page->aux;
|
||||
int i, idx, n = 0;
|
||||
|
||||
if (!col) {
|
||||
fprintf(stderr, "%s",
|
||||
"wmii: fatal: frame has no associated column\n");
|
||||
exit(1);
|
||||
}
|
||||
for (i = 0; acme->column && acme->column[i]; i++) {
|
||||
if (acme->column[i] == col)
|
||||
idx = i;
|
||||
n++;
|
||||
}
|
||||
|
||||
/* horizontal resizals are */
|
||||
if (new->x < f->rect.x) {
|
||||
if (idx && new->x > acme->column[idx - 1]->rect.x) {
|
||||
Column *west = acme->column[idx - 1];
|
||||
west->rect.width = new->x - west->rect.x;
|
||||
col->rect.width += f->rect.x - new->x;
|
||||
col->rect.x = new->x;
|
||||
|
||||
for (i = 0; west->frames && west->frames[i]; i++) {
|
||||
Frame *f = west->frames[i];
|
||||
f->rect.x = west->rect.x;
|
||||
f->rect.width = west->rect.width;
|
||||
resize_frame(f, &f->rect, 0, 1);
|
||||
}
|
||||
for (i = 0; col->frames && col->frames[i]; i++) {
|
||||
Frame *f = col->frames[i];
|
||||
f->rect.x = col->rect.x;
|
||||
f->rect.width = col->rect.width;
|
||||
resize_frame(f, &f->rect, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (new->x + new->width > f->rect.x + f->rect.width) {
|
||||
if ((idx + 1 < n) &&
|
||||
(new->x + new->width < acme->column[idx + 1]->rect.x
|
||||
+ acme->column[idx + 1]->rect.width)) {
|
||||
Column *east = acme->column[idx + 1];
|
||||
east->rect.width -= new->x + new->width - east->rect.x;
|
||||
east->rect.x = new->x + new->width;
|
||||
col->rect.x = new->x;
|
||||
col->rect.width = new->width;
|
||||
|
||||
for (i = 0; col->frames && col->frames[i]; i++) {
|
||||
Frame *f = col->frames[i];
|
||||
f->rect.x = col->rect.x;
|
||||
f->rect.width = col->rect.width;
|
||||
resize_frame(f, &f->rect, 0, 1);
|
||||
}
|
||||
for (i = 0; east->frames && east->frames[i]; i++) {
|
||||
Frame *f = east->frames[i];
|
||||
f->rect.x = east->rect.x;
|
||||
f->rect.width = east->rect.width;
|
||||
resize_frame(f, &f->rect, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* vertical stuff */
|
||||
n = 0;
|
||||
for (i = 0; col->frames && col->frames[i]; i++) {
|
||||
if (col->frames[i] == f)
|
||||
idx = i;
|
||||
n++;
|
||||
}
|
||||
|
||||
if (new->y < f->rect.y) {
|
||||
if (idx && new->y > col->frames[idx - 1]->rect.y) {
|
||||
Frame *north = col->frames[idx - 1];
|
||||
north->rect.height = new->y - north->rect.y;
|
||||
f->rect.width += new->y - north->rect.y;
|
||||
f->rect.y = new->y;
|
||||
resize_frame(north, &north->rect, 0, 1);
|
||||
resize_frame(f, &f->rect, 0, 1);
|
||||
}
|
||||
}
|
||||
if (new->y + new->height > f->rect.y + f->rect.height) {
|
||||
if ((idx + 1 < n) &&
|
||||
(new->y + new->height < col->frames[idx + 1]->rect.y
|
||||
+ col->frames[idx + 1]->rect.height)) {
|
||||
Frame *south = col->frames[idx + 1];
|
||||
south->rect.width -= new->x + new->width - south->rect.x;
|
||||
south->rect.x = new->x + new->width;
|
||||
f->rect.x = new->x;
|
||||
f->rect.width = new->width;
|
||||
resize_frame(f, &f->rect, 0, 1);
|
||||
resize_frame(south, &south->rect, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_drop_move(Frame * f, XRectangle * new, XPoint * pt)
|
||||
{
|
||||
Column *tgt = 0, *src = f->aux;
|
||||
Acme *acme = f->page->aux;
|
||||
int i;
|
||||
|
||||
if (!src) {
|
||||
fprintf(stderr, "%s",
|
||||
"wmii: fatal: frame has no associated column\n");
|
||||
exit(1);
|
||||
}
|
||||
for (i = 0; acme->column && acme->column[i]; i++) {
|
||||
Column *colp = acme->column[i];
|
||||
if (blitz_ispointinrect(pt->x, pt->y, &colp->rect)) {
|
||||
tgt = colp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* use pointer as fixpoint */
|
||||
if (tgt == src) {
|
||||
/* only change order within column */
|
||||
for (i = 0; tgt->frames && tgt->frames[i]; i++) {
|
||||
Frame *fp = tgt->frames[i];
|
||||
if (blitz_ispointinrect(pt->x, pt->y, &fp->rect)) {
|
||||
if (fp == f)
|
||||
return; /* just ignore */
|
||||
else {
|
||||
int idxf = index_item((void **) tgt->frames, f);
|
||||
int idxfp = index_item((void **) tgt->frames, fp);
|
||||
Frame *tmpf = f;
|
||||
XRectangle tmpr = f->rect;
|
||||
|
||||
f->rect = fp->rect;
|
||||
fp->rect = tmpr;
|
||||
tgt->frames[idxf] = tgt->frames[idxfp];
|
||||
tgt->frames[idxfp] = tmpf;
|
||||
resize_frame(f, &f->rect, 0, 1);
|
||||
resize_frame(fp, &fp->rect, 0, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* detach, attach and change order in target column */
|
||||
src->frames = (Frame **) detach_item((void **) src->frames,
|
||||
f, sizeof(Frame *));
|
||||
tgt->frames =
|
||||
(Frame **) attach_item_end((void **) tgt->frames, f,
|
||||
sizeof(Frame *));
|
||||
tgt->refresh = 1;
|
||||
arrange_column(f->page, tgt);
|
||||
|
||||
/* TODO: implement a better target placing strategy */
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
resize_col(Frame * f, XRectangle * new, XPoint * pt)
|
||||
{
|
||||
if ((f->rect.width == new->width)
|
||||
&& (f->rect.height == new->height))
|
||||
_drop_move(f, new, pt);
|
||||
else
|
||||
drop_resize(f, new);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
aux_col(Frame * f, char *what)
|
||||
{
|
||||
}
|
12
cmd/wm/mklayout.sh
Normal file
12
cmd/wm/mklayout.sh
Normal file
@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
rm -f layout.h layout.c layout.mk
|
||||
echo "#include \"layout.h\"" > layout.c
|
||||
echo "#include \"wm.h\"" >> layout.c
|
||||
echo "void init_layouts() {" >> layout.c
|
||||
for i in `ls layout_*.c`; do
|
||||
FUNC="`echo \`basename $i\` | sed 's/\.c//g'`"
|
||||
echo "void init_$FUNC();" >> layout.h
|
||||
echo " init_$FUNC();" >> layout.c;
|
||||
echo "SRC += $i" >>layout.mk
|
||||
done
|
||||
echo "}" >> layout.c
|
620
cmd/wm/mouse.c
Normal file
620
cmd/wm/mouse.c
Normal file
@ -0,0 +1,620 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "wm.h"
|
||||
|
||||
Cursor
|
||||
cursor_for_motion(Frame * f, int x, int y)
|
||||
{
|
||||
int n, e, w, s, tn, te, tw, ts;
|
||||
int tabh, bw;
|
||||
|
||||
bw = border_width(f);
|
||||
tabh = tab_height(f);
|
||||
|
||||
if (!f || !bw)
|
||||
return normal_cursor;
|
||||
|
||||
/* rectangle attributes of client are used */
|
||||
w = x < bw;
|
||||
e = x >= rect_of_frame(f)->width - bw;
|
||||
n = y < bw;
|
||||
s = y >= rect_of_frame(f)->height - bw;
|
||||
|
||||
tw = x < (tabh ? tabh : 2 * bw);
|
||||
te = x > rect_of_frame(f)->width - (tabh ? tabh : 2 * bw);
|
||||
tn = y < (tabh ? tabh : 2 * bw);
|
||||
ts = s > rect_of_frame(f)->height - (tabh ? tabh : 2 * bw);
|
||||
|
||||
if ((w && n) || (w && tn) || (n && tw))
|
||||
return nw_cursor;
|
||||
else if ((e && n) || (e && tn) || (n && te))
|
||||
return ne_cursor;
|
||||
else if ((w && s) || (w && ts) || (s && tw))
|
||||
return sw_cursor;
|
||||
else if ((e && s) || (e && ts) || (s && te))
|
||||
return se_cursor;
|
||||
else if (w)
|
||||
return w_cursor;
|
||||
else if (e)
|
||||
return e_cursor;
|
||||
else if (n)
|
||||
return n_cursor;
|
||||
else if (s)
|
||||
return s_cursor;
|
||||
|
||||
return normal_cursor;
|
||||
}
|
||||
|
||||
Align
|
||||
xy_to_align(XRectangle * rect, int x, int y)
|
||||
{
|
||||
|
||||
int w = x <= rect->x + rect->width / 2;
|
||||
int n = y <= rect->y + rect->height / 2;
|
||||
int e = x > rect->x + rect->width / 2;
|
||||
int s = y > rect->y + rect->height / 2;
|
||||
int nw = w && n;
|
||||
int ne = e && n;
|
||||
int sw = w && s;
|
||||
int se = e && s;
|
||||
|
||||
if (nw)
|
||||
return NWEST;
|
||||
else if (ne)
|
||||
return NEAST;
|
||||
else if (se)
|
||||
return SEAST;
|
||||
else if (sw)
|
||||
return SWEST;
|
||||
else if (w)
|
||||
return WEST;
|
||||
else if (e)
|
||||
return EAST;
|
||||
else if (n)
|
||||
return NORTH;
|
||||
else if (s)
|
||||
return SOUTH;
|
||||
return CENTER;
|
||||
}
|
||||
|
||||
Align
|
||||
cursor_to_align(Cursor cursor)
|
||||
{
|
||||
if (cursor == w_cursor)
|
||||
return WEST;
|
||||
else if (cursor == nw_cursor)
|
||||
return NWEST;
|
||||
else if (cursor == n_cursor)
|
||||
return NORTH;
|
||||
else if (cursor == ne_cursor)
|
||||
return NEAST;
|
||||
else if (cursor == e_cursor)
|
||||
return EAST;
|
||||
else if (cursor == se_cursor)
|
||||
return SEAST;
|
||||
else if (cursor == s_cursor)
|
||||
return SOUTH;
|
||||
else if (cursor == sw_cursor)
|
||||
return SWEST;
|
||||
return CENTER; /* should not happen */
|
||||
}
|
||||
|
||||
static int
|
||||
check_vert_match(XRectangle * r, XRectangle * neighbor)
|
||||
{
|
||||
/* check if neighbor matches edge */
|
||||
return (((neighbor->y <= r->y)
|
||||
&& (neighbor->y + neighbor->height >= r->y))
|
||||
|| ((neighbor->y >= r->y)
|
||||
&& (r->y + r->height >= neighbor->y)));
|
||||
}
|
||||
|
||||
static int
|
||||
check_horiz_match(XRectangle * r, XRectangle * neighbor)
|
||||
{
|
||||
/* check if neighbor matches edge */
|
||||
return (((neighbor->x <= r->x)
|
||||
&& (neighbor->x + neighbor->width >= r->x))
|
||||
|| ((neighbor->x >= r->x)
|
||||
&& (r->x + r->width >= neighbor->x)));
|
||||
}
|
||||
|
||||
static void
|
||||
snap_move(XRectangle * r, XRectangle * rects,
|
||||
unsigned int num, int snapw, int snaph)
|
||||
{
|
||||
|
||||
int i, j, w = 0, n = 0, e = 0, s = 0;
|
||||
|
||||
/* snap to other windows */
|
||||
for (i = 0; i <= snapw && !(w && e); i++) {
|
||||
|
||||
for (j = 0; j < num && !(w && e); j++) {
|
||||
|
||||
/* check west neighbors leftwards */
|
||||
if (!w) {
|
||||
if (r->x - i == (rects[j].x + rects[j].width)) {
|
||||
/*
|
||||
* west edge of neighbor found, check
|
||||
* vert match
|
||||
*/
|
||||
w = check_vert_match(r, &rects[j]);
|
||||
if (w)
|
||||
r->x = rects[j].x + rects[j].width;
|
||||
}
|
||||
}
|
||||
/* check west neighbors rightwards */
|
||||
if (!w) {
|
||||
if (r->x + i == (rects[j].x + rects[j].width)) {
|
||||
/*
|
||||
* west edge of neighbor found, check
|
||||
* vert match
|
||||
*/
|
||||
w = check_vert_match(r, &rects[j]);
|
||||
if (w)
|
||||
r->x = rects[j].x + rects[j].width;
|
||||
}
|
||||
}
|
||||
/* check east neighbors leftwards */
|
||||
if (!e) {
|
||||
if (r->x + r->width - i == rects[j].x) {
|
||||
/*
|
||||
* east edge of neighbor found, check
|
||||
* vert match
|
||||
*/
|
||||
e = check_vert_match(r, &rects[j]);
|
||||
if (e)
|
||||
r->x = rects[j].x - r->width;
|
||||
}
|
||||
}
|
||||
/* check east neighbors rightwards */
|
||||
if (!e) {
|
||||
if (r->x + r->width + i == rects[j].x) {
|
||||
/*
|
||||
* east edge of neighbor found, check
|
||||
* vert match
|
||||
*/
|
||||
e = check_vert_match(r, &rects[j]);
|
||||
if (e)
|
||||
r->x = rects[j].x - r->width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* snap to west screen border */
|
||||
if (!w && (r->x - i == rect.x)) {
|
||||
w = 1;
|
||||
r->x = rect.x;
|
||||
}
|
||||
/* snap to west screen border */
|
||||
if (!w && (r->x + i == rect.x)) {
|
||||
w = 1;
|
||||
r->x = rect.x;
|
||||
}
|
||||
/* snap to east screen border */
|
||||
if (!e && (r->x + r->width - i == rect.width)) {
|
||||
e = 1;
|
||||
r->x = rect.x + rect.width - r->width;
|
||||
}
|
||||
if (!e && (r->x + r->width + i == rect.width)) {
|
||||
e = 1;
|
||||
r->x = rect.x + rect.width - r->width;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i <= snaph && !(n && s); i++) {
|
||||
|
||||
for (j = 0; j < num && !(n && s); j++) {
|
||||
/* check north neighbors upwards */
|
||||
if (!n) {
|
||||
if (r->y - i == (rects[j].y + rects[j].height)) {
|
||||
/*
|
||||
* north edge of neighbor found,
|
||||
* check horiz match
|
||||
*/
|
||||
n = check_horiz_match(r, &rects[j]);
|
||||
if (n)
|
||||
r->y = rects[j].y + rects[j].height;
|
||||
}
|
||||
}
|
||||
/* check north neighbors downwards */
|
||||
if (!n) {
|
||||
if (r->y + i == (rects[j].y + rects[j].height)) {
|
||||
/*
|
||||
* north edge of neighbor found,
|
||||
* check horiz match
|
||||
*/
|
||||
n = check_horiz_match(r, &rects[j]);
|
||||
if (n)
|
||||
r->y = rects[j].y + rects[j].height;
|
||||
}
|
||||
}
|
||||
/* check south neighbors upwards */
|
||||
if (!s) {
|
||||
if (r->y + r->height - i == rects[j].y) {
|
||||
/*
|
||||
* south edge of neighbor found,
|
||||
* check horiz match
|
||||
*/
|
||||
s = check_horiz_match(r, &rects[j]);
|
||||
if (s)
|
||||
r->y = rects[j].y - r->height;
|
||||
}
|
||||
}
|
||||
/* check south neighbors downwards */
|
||||
if (!s) {
|
||||
if (r->y + r->height + i == rects[j].y) {
|
||||
/*
|
||||
* south edge of neighbor found,
|
||||
* check horiz match
|
||||
*/
|
||||
s = check_horiz_match(r, &rects[j]);
|
||||
if (s)
|
||||
r->y = rects[j].y - r->height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* snap to north screen border */
|
||||
if (!n && (r->y - i == rect.y)) {
|
||||
n = 1;
|
||||
r->y = rect.y;
|
||||
}
|
||||
if (!n && (r->y + i == rect.y)) {
|
||||
n = 1;
|
||||
r->y = rect.y;
|
||||
}
|
||||
/* snap to south screen border */
|
||||
if (!s && (r->y + r->height - i == rect.height)) {
|
||||
s = 1;
|
||||
r->y = rect.y + rect.height - r->height;
|
||||
}
|
||||
if (!s && (r->y + r->height + i == rect.height)) {
|
||||
s = 1;
|
||||
r->y = rect.y + rect.height - r->height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
draw_pseudo_border(XRectangle * r)
|
||||
{
|
||||
XRectangle pseudo = *r;
|
||||
|
||||
pseudo.x += 2;
|
||||
pseudo.y += 2;
|
||||
pseudo.width -= 4;
|
||||
pseudo.height -= 4;
|
||||
XDrawRectangles(dpy, root, xorgc, &pseudo, 1);
|
||||
XSync(dpy, False);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mouse_move(Frame * f)
|
||||
{
|
||||
int px = 0, py = 0, wex, wey, ex, ey, first = 1, i;
|
||||
Window dummy;
|
||||
XEvent ev;
|
||||
/* borders */
|
||||
int snapw = rect.width * _strtonum(defaults[WM_SNAP_VALUE]->content, 0, 1000) / 1000;
|
||||
int snaph = rect.height * _strtonum(defaults[WM_SNAP_VALUE]->content, 0, 1000) / 1000;
|
||||
unsigned int num;
|
||||
unsigned int dmask;
|
||||
XRectangle *rects = rectangles(&num);
|
||||
XRectangle frect = *rect_of_frame(f);
|
||||
XPoint pt;
|
||||
|
||||
XQueryPointer(dpy, f->win, &dummy, &dummy, &i, &i, &wex, &wey, &dmask);
|
||||
XTranslateCoordinates(dpy, f->win, root, wex, wey, &ex, &ey, &dummy);
|
||||
pt.x = ex;
|
||||
pt.y = ey;
|
||||
XSync(dpy, False);
|
||||
XGrabServer(dpy);
|
||||
XGrabPointer(dpy, root, False, ButtonMotionMask | ButtonReleaseMask,
|
||||
GrabModeAsync, GrabModeAsync, None, move_cursor,
|
||||
CurrentTime);
|
||||
for (;;) {
|
||||
while (!XCheckMaskEvent(dpy,
|
||||
ButtonReleaseMask | ButtonMotionMask, &ev)) {
|
||||
usleep(20000);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (ev.type) {
|
||||
case ButtonRelease:
|
||||
if (!first) {
|
||||
draw_pseudo_border(&frect);
|
||||
resize_frame(f, &frect, &pt, 0);
|
||||
}
|
||||
draw_page(pages[sel]);
|
||||
free(rects);
|
||||
XUngrabPointer(dpy, CurrentTime /* ev.xbutton.time */ );
|
||||
XUngrabServer(dpy);
|
||||
XSync(dpy, False);
|
||||
return;
|
||||
break;
|
||||
case MotionNotify:
|
||||
pt.x = ev.xmotion.x;
|
||||
pt.y = ev.xmotion.y;
|
||||
XTranslateCoordinates(dpy, f->win, root, ev.xmotion.x,
|
||||
ev.xmotion.y, &px, &py, &dummy);
|
||||
if (first)
|
||||
first = 0;
|
||||
else
|
||||
draw_pseudo_border(&frect);
|
||||
frect.x = px - ex;
|
||||
frect.y = py - ey;
|
||||
snap_move(&frect, rects, num, snapw, snaph);
|
||||
draw_pseudo_border(&frect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
snap_resize(XRectangle * r, XRectangle * o, Align align,
|
||||
XRectangle * rects, unsigned int num, int px, int ox, int py,
|
||||
int oy, int snapw, int snaph)
|
||||
{
|
||||
int i, j, pend = 0;
|
||||
int w, h;
|
||||
|
||||
/* x */
|
||||
switch (align) {
|
||||
case NEAST:
|
||||
case EAST:
|
||||
case SEAST:
|
||||
w = px - r->x + (o->width - ox);
|
||||
if (w < 10)
|
||||
break;
|
||||
r->width = w;
|
||||
if (w <= snapw)
|
||||
break;
|
||||
/* snap to border */
|
||||
for (i = 0; !pend && (i < snapw); i++) {
|
||||
if (r->x + r->width - i == rect.x + rect.width) {
|
||||
r->width -= i;
|
||||
break;
|
||||
}
|
||||
if (r->x + r->width + i == rect.x + rect.width) {
|
||||
r->width += i;
|
||||
break;
|
||||
}
|
||||
for (j = 0; j < num; j++) {
|
||||
if (r->x + r->width - i == rects[j].x) {
|
||||
pend = check_vert_match(r, &rects[j]);
|
||||
if (pend) {
|
||||
r->width -= i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (r->x + r->width + i == rects[j].x) {
|
||||
pend = check_vert_match(r, &rects[j]);
|
||||
if (pend) {
|
||||
r->width += i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NWEST:
|
||||
case WEST:
|
||||
case SWEST:
|
||||
w = r->width + r->x - px + ox;
|
||||
if (w < 10)
|
||||
break;
|
||||
r->width = w;
|
||||
r->x = px - ox;
|
||||
if (w <= snapw)
|
||||
break;
|
||||
/* snap to border */
|
||||
for (i = 0; !pend && (i < snapw); i++) {
|
||||
if (r->x - i == rect.x) {
|
||||
r->x -= i;
|
||||
r->width += i;
|
||||
break;
|
||||
}
|
||||
if (r->x + i == rect.x) {
|
||||
r->x += i;
|
||||
r->width -= i;
|
||||
break;
|
||||
}
|
||||
for (j = 0; j < num; j++) {
|
||||
if (r->x - i == rects[j].x + rects[j].width) {
|
||||
pend = check_vert_match(r, &rects[j]);
|
||||
if (pend) {
|
||||
r->x -= i;
|
||||
r->width += i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (r->x + i == rects[j].x + rects[j].width) {
|
||||
pend = check_vert_match(r, &rects[j]);
|
||||
if (pend) {
|
||||
r->x += i;
|
||||
r->width -= i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* y */
|
||||
pend = 0;
|
||||
switch (align) {
|
||||
case SWEST:
|
||||
case SOUTH:
|
||||
case SEAST:
|
||||
h = py - r->y + (o->height - oy);
|
||||
if (h < 10)
|
||||
break;
|
||||
r->height = h;
|
||||
if (h <= snaph)
|
||||
break;
|
||||
/* snap to border */
|
||||
for (i = 0; !pend && (i < snaph); i++) {
|
||||
if (r->y + r->height - i == rect.y + rect.height) {
|
||||
r->height -= i;
|
||||
break;
|
||||
}
|
||||
if (r->y + r->height + i == rect.y + rect.height) {
|
||||
r->height += i;
|
||||
break;
|
||||
}
|
||||
for (j = 0; j < num; j++) {
|
||||
if (r->y + r->height - i == rects[j].y) {
|
||||
pend = check_horiz_match(r, &rects[j]);
|
||||
if (pend) {
|
||||
r->height -= i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (r->y + r->height + i == rects[j].y) {
|
||||
pend = check_horiz_match(r, &rects[j]);
|
||||
if (pend) {
|
||||
r->height += i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NWEST:
|
||||
case NORTH:
|
||||
case NEAST:
|
||||
h = r->height + r->y - py + oy;
|
||||
if (h < 10)
|
||||
break;
|
||||
r->height = h;
|
||||
r->y = py - oy;
|
||||
if (h <= snaph)
|
||||
break;
|
||||
/* snap to border */
|
||||
for (i = 0; !pend && (i < snaph); i++) {
|
||||
if (r->y - i == rect.y) {
|
||||
r->y -= i;
|
||||
r->height += i;
|
||||
break;
|
||||
}
|
||||
if (r->y + i == rect.y) {
|
||||
r->y += i;
|
||||
r->height -= i;
|
||||
break;
|
||||
}
|
||||
for (j = 0; j < num; j++) {
|
||||
if (r->y - i == rects[j].y + rects[j].height) {
|
||||
pend = check_horiz_match(r, &rects[j]);
|
||||
if (pend) {
|
||||
r->y -= i;
|
||||
r->height += i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (r->y + i == rects[j].y + rects[j].height) {
|
||||
pend = check_horiz_match(r, &rects[j]);
|
||||
if (pend) {
|
||||
r->y += i;
|
||||
r->height -= i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mouse_resize(Frame * f, Align align)
|
||||
{
|
||||
int px = 0, py = 0, i, ox, oy, first = 1;
|
||||
Window dummy;
|
||||
XEvent ev;
|
||||
/* borders */
|
||||
int snapw = rect.width * _strtonum(defaults[WM_SNAP_VALUE]->content, 0, 1000) / 1000;
|
||||
int snaph = rect.height * _strtonum(defaults[WM_SNAP_VALUE]->content, 0, 1000) / 1000;
|
||||
unsigned int dmask;
|
||||
unsigned int num;
|
||||
XRectangle *rects = rectangles(&num);
|
||||
XRectangle frect = *rect_of_frame(f);
|
||||
XRectangle origin = frect;
|
||||
|
||||
XQueryPointer(dpy, f->win, &dummy, &dummy, &i, &i, &ox, &oy, &dmask);
|
||||
XSync(dpy, False);
|
||||
XGrabServer(dpy);
|
||||
XGrabPointer(dpy, f->win, False, ButtonMotionMask | ButtonReleaseMask,
|
||||
GrabModeAsync, GrabModeAsync, None, resize_cursor,
|
||||
CurrentTime);
|
||||
for (;;) {
|
||||
while (!XCheckMaskEvent(dpy,
|
||||
ButtonReleaseMask | ButtonMotionMask, &ev)) {
|
||||
usleep(20000);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (ev.type) {
|
||||
case ButtonRelease:
|
||||
if (!first) {
|
||||
XPoint pt;
|
||||
draw_pseudo_border(&frect);
|
||||
pt.x = px;
|
||||
pt.y = py;
|
||||
resize_frame(f, &frect, &pt, 0);
|
||||
}
|
||||
XUngrabPointer(dpy, CurrentTime /* ev.xbutton.time */ );
|
||||
XUngrabServer(dpy);
|
||||
XSync(dpy, False);
|
||||
return;
|
||||
break;
|
||||
case MotionNotify:
|
||||
XTranslateCoordinates(dpy, f->win, root, ev.xmotion.x,
|
||||
ev.xmotion.y, &px, &py, &dummy);
|
||||
|
||||
if (first)
|
||||
first = 0;
|
||||
else
|
||||
draw_pseudo_border(&frect);
|
||||
|
||||
snap_resize(&frect, &origin, align, rects, num, px,
|
||||
ox, py, oy, snapw, snaph);
|
||||
draw_pseudo_border(&frect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
drop_move(Frame * f, XRectangle * new, XPoint * pt)
|
||||
{
|
||||
Area *a = f->area;
|
||||
int cx, cy;
|
||||
unsigned int i, idx = index_item((void **) a->frames, f);
|
||||
|
||||
if ((f->rect.x == new->x) && (f->rect.y == new->y))
|
||||
return;
|
||||
cx = (pt ? pt->x : new->x + new->width / 2);
|
||||
cy = (pt ? pt->y : new->y + new->height / 2);
|
||||
for (i = 0; a->frames[i]; i++) {
|
||||
if ((a->frames[i] != f) && blitz_ispointinrect(cx, cy, &a->frames[i]->rect)) {
|
||||
swap((void **) &a->frames[i], (void **) &a->frames[idx]);
|
||||
a->sel = i;
|
||||
a->layout->arrange(a);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
316
cmd/wm/old/layout_grid.c
Normal file
316
cmd/wm/old/layout_grid.c
Normal file
@ -0,0 +1,316 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "wm.h"
|
||||
#include "layout.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
/* grid layout definition */
|
||||
static void arrange_grid(Page * p);
|
||||
static void init_grid(Page * p);
|
||||
static void deinit_grid(Page * p);
|
||||
static void manage_grid(Frame * f);
|
||||
static void unmanage_grid(Frame * f);
|
||||
static void resize_grid(Frame * f, XRectangle * new, XPoint * pt);
|
||||
static Frame *select_grid(Frame * f, char *what);
|
||||
|
||||
static Layout grid =
|
||||
{"grid", init_grid, deinit_grid, arrange_grid, manage_grid,
|
||||
unmanage_grid, resize_grid, select_grid
|
||||
};
|
||||
|
||||
void
|
||||
init_layout_grid()
|
||||
{
|
||||
layouts =
|
||||
(Layout **) attach_item_end((void **) layouts, &grid,
|
||||
sizeof(Layout *));
|
||||
}
|
||||
|
||||
static void
|
||||
arrange_grid(Page * p)
|
||||
{
|
||||
unsigned int i, ic, ir, tw, th, rows, cols;
|
||||
|
||||
if (!p->managed)
|
||||
return;
|
||||
|
||||
blitz_getbasegeometry((void **) p->managed, &ic, &cols, &rows);
|
||||
th = p->managed_rect.height / rows;
|
||||
tw = p->managed_rect.width / cols;
|
||||
|
||||
i = 0;
|
||||
for (ir = 0; ir < rows; ir++) {
|
||||
for (ic = 0; ic < cols; ic++) {
|
||||
if (p->managed[i]) {
|
||||
XRectangle *r = (XRectangle *) p->managed[i]->aux;
|
||||
r->x = p->managed_rect.x + ic * tw;
|
||||
r->y = p->managed_rect.y + ir * th;
|
||||
r->width = tw;
|
||||
r->height = th;
|
||||
p->managed[i]->managed_rect = *r;
|
||||
resize_frame(p->managed[i], &p->managed[i]->managed_rect,
|
||||
0, 1);
|
||||
} else
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
init_grid(Page * p)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; p->managed && p->managed[i]; i++)
|
||||
p->managed[i]->aux = emalloc(sizeof(XRectangle));
|
||||
}
|
||||
|
||||
static void
|
||||
deinit_grid(Page * p)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; p->managed && p->managed[i]; i++) {
|
||||
if (p->managed[i]->aux) {
|
||||
free(p->managed[i]->aux);
|
||||
p->managed[i]->aux = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
manage_grid(Frame * f)
|
||||
{
|
||||
f->aux = emalloc(sizeof(XRectangle));
|
||||
if (f->page)
|
||||
arrange_grid(f->page);
|
||||
}
|
||||
|
||||
static void
|
||||
unmanage_grid(Frame * f)
|
||||
{
|
||||
if (f->aux) {
|
||||
free(f->aux);
|
||||
f->aux = 0;
|
||||
}
|
||||
if (f->page)
|
||||
arrange_grid(f->page);
|
||||
}
|
||||
|
||||
#define THRESHOLD 30
|
||||
|
||||
static void
|
||||
drop_resize(Frame * f, XRectangle * new)
|
||||
{
|
||||
int diff;
|
||||
unsigned int i, rows, cols, cr, cc, num, idx;
|
||||
Page *p = f->page;
|
||||
XRectangle *r;
|
||||
|
||||
if (!p || !p->managed)
|
||||
return;
|
||||
blitz_getbasegeometry((void **) p->managed, &i, &cols, &rows);
|
||||
|
||||
num = index_item((void **) p->managed, f);
|
||||
|
||||
cr = num / cols;
|
||||
cc = num - cr * cols;
|
||||
|
||||
/* horizontal resize */
|
||||
if (f->managed_rect.x == new->x && f->managed_rect.width != new->width) {
|
||||
/* east direction resize */
|
||||
if (cc == cols - 1)
|
||||
return;
|
||||
if (p->managed[cc + 1]
|
||||
&& (new->x + new->width > p->managed[cc + 1]->managed_rect.x
|
||||
+ p->managed[cc + 1]->managed_rect.width - THRESHOLD))
|
||||
return;
|
||||
diff = new->width - ((XRectangle *) f->aux)->width;
|
||||
for (i = 0; i < rows; i++) {
|
||||
idx = (i * cols) + cc;
|
||||
if (p->managed[idx]) {
|
||||
r = (XRectangle *) p->managed[idx]->aux;
|
||||
r->width = new->width;
|
||||
p->managed[idx]->managed_rect = *r;
|
||||
resize_frame(p->managed[idx],
|
||||
&p->managed[idx]->managed_rect, 0, 1);
|
||||
}
|
||||
idx = (i * cols) + (cc + 1);
|
||||
if (p->managed[idx]) {
|
||||
r = (XRectangle *) p->managed[idx]->aux;
|
||||
r->x += diff;
|
||||
r->width -= diff;
|
||||
p->managed[idx]->managed_rect = *r;
|
||||
resize_frame(p->managed[idx],
|
||||
&p->managed[idx]->managed_rect, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (f->managed_rect.x != new->x) {
|
||||
/* west direction resize */
|
||||
if (!cc)
|
||||
return;
|
||||
if (new->x < p->managed[cc - 1]->managed_rect.x + THRESHOLD)
|
||||
return;
|
||||
diff = new->width - ((XRectangle *) f->aux)->width;
|
||||
for (i = 0; i < rows; i++) {
|
||||
idx = (i * cols) + cc;
|
||||
if (p->managed[idx]) {
|
||||
r = (XRectangle *) p->managed[idx]->aux;
|
||||
r->x -= diff;
|
||||
r->width += diff;
|
||||
p->managed[idx]->managed_rect = *r;
|
||||
resize_frame(p->managed[idx],
|
||||
&p->managed[idx]->managed_rect, 0, 1);
|
||||
}
|
||||
idx = (i * cols) + (cc - 1);
|
||||
if (p->managed[idx]) {
|
||||
r = (XRectangle *) p->managed[idx]->aux;
|
||||
r->width -= diff;
|
||||
p->managed[idx]->managed_rect = *r;
|
||||
resize_frame(p->managed[idx],
|
||||
&p->managed[idx]->managed_rect, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* vertical resize */
|
||||
if (f->managed_rect.y == new->y
|
||||
&& f->managed_rect.height != new->height) {
|
||||
/* south direction resize */
|
||||
if (cr == rows - 1)
|
||||
return;
|
||||
if (p->managed[cr + 1]
|
||||
&& (new->y + new->height >
|
||||
p->managed[(cr + 1) * rows]->managed_rect.y +
|
||||
p->managed[(cr + 1) * rows]->managed_rect.height -
|
||||
THRESHOLD))
|
||||
return;
|
||||
diff = new->height - ((XRectangle *) f->aux)->height;
|
||||
for (i = 0; i < cols; i++) {
|
||||
idx = (cr * rows) + i;
|
||||
if (p->managed[idx]) {
|
||||
r = (XRectangle *) p->managed[idx]->aux;
|
||||
r->height = new->height;
|
||||
p->managed[idx]->managed_rect = *r;
|
||||
resize_frame(p->managed[idx],
|
||||
&p->managed[idx]->managed_rect, 0, 1);
|
||||
}
|
||||
idx = (cr + 1) * rows + i;
|
||||
if (p->managed[idx]) {
|
||||
r = (XRectangle *) p->managed[idx]->aux;
|
||||
r->y += diff;
|
||||
r->height -= diff;
|
||||
p->managed[idx]->managed_rect = *r;
|
||||
resize_frame(p->managed[idx],
|
||||
&p->managed[idx]->managed_rect, 0, 1);
|
||||
}
|
||||
}
|
||||
} else if (f->managed_rect.y != new->y) {
|
||||
/* north direction resize */
|
||||
if (!cr)
|
||||
return;
|
||||
if (new->y <
|
||||
p->managed[(cr - 1) * rows]->managed_rect.y + THRESHOLD)
|
||||
return;
|
||||
diff = new->height - ((XRectangle *) f->aux)->height;
|
||||
for (i = 0; i < cols; i++) {
|
||||
idx = (cr * rows) + i;
|
||||
if (p->managed[idx]) {
|
||||
r = (XRectangle *) p->managed[idx]->aux;
|
||||
r->y -= diff;
|
||||
r->height += diff;
|
||||
p->managed[idx]->managed_rect = *r;
|
||||
resize_frame(p->managed[idx],
|
||||
&p->managed[idx]->managed_rect, 0, 1);
|
||||
}
|
||||
idx = (cr - 1) * rows + i;
|
||||
if (p->managed[idx]) {
|
||||
r = (XRectangle *) p->managed[idx]->aux;
|
||||
r->height -= diff;
|
||||
p->managed[idx]->managed_rect = *r;
|
||||
resize_frame(p->managed[idx],
|
||||
&p->managed[idx]->managed_rect, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
resize_grid(Frame * f, XRectangle * new, XPoint * pt)
|
||||
{
|
||||
if ((f->managed_rect.width == new->width)
|
||||
&& (f->managed_rect.height == new->height))
|
||||
drop_move(f, new, pt);
|
||||
else
|
||||
drop_resize(f, new);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
get_current_frame_position(Frame * f)
|
||||
{
|
||||
Page *p = f->page;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < count_items((void **) p->managed); ++i)
|
||||
if (p->managed[i] == f)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static Frame *
|
||||
select_grid(Frame * f, char *what)
|
||||
{
|
||||
Page *p = f->page;
|
||||
unsigned int ic, cols, rows, pos;
|
||||
int idx;
|
||||
|
||||
blitz_getbasegeometry((void **) p->managed, &ic, &cols, &rows);
|
||||
pos = get_current_frame_position(f);
|
||||
|
||||
if (!strncmp(what, "prev", 5)) {
|
||||
idx = index_prev_item((void **) p->managed, f);
|
||||
if (idx >= 0)
|
||||
return p->managed[idx];
|
||||
} else if (!strncmp(what, "next", 5)) {
|
||||
idx = index_next_item((void **) p->managed, f);
|
||||
if (idx >= 0)
|
||||
return p->managed[idx];
|
||||
} else if (!strncmp(what, "south", 6)) {
|
||||
if ((pos + cols) <= ic)
|
||||
pos += cols;
|
||||
else
|
||||
pos %= cols;
|
||||
return p->managed[pos];
|
||||
} else if (!strncmp(what, "north", 6)) {
|
||||
if (pos < cols)
|
||||
pos = (rows - 1) * cols + pos;
|
||||
else
|
||||
pos -= cols;
|
||||
return p->managed[pos];
|
||||
} else if (!strncmp(what, "west", 5)) {
|
||||
if (pos % cols == 0) {
|
||||
pos += (cols - 1);
|
||||
if (pos >= ic)
|
||||
pos = ic - 1;
|
||||
} else
|
||||
pos--;
|
||||
return p->managed[pos];
|
||||
} else if (!strncmp(what, "east", 5)) {
|
||||
if (pos % cols == (cols - 1))
|
||||
pos -= (cols - 1);
|
||||
else {
|
||||
pos++;
|
||||
if (pos >= ic)
|
||||
pos = ic - 1;
|
||||
}
|
||||
return p->managed[pos];
|
||||
}
|
||||
return 0;
|
||||
}
|
115
cmd/wm/old/layout_max.c
Normal file
115
cmd/wm/old/layout_max.c
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "wm.h"
|
||||
#include "layout.h"
|
||||
|
||||
static void arrange_max(Page * p);
|
||||
static void init_max(Page * p);
|
||||
static void deinit_max(Page * p);
|
||||
static void manage_max(Frame * f);
|
||||
static void unmanage_max(Frame * f);
|
||||
static void resize_max(Frame * f, XRectangle * new, XPoint * pt);
|
||||
static Frame *select_max(Frame * f, char *what);
|
||||
|
||||
static Layout max = {"max", init_max, deinit_max, arrange_max, manage_max,
|
||||
unmanage_max, resize_max, select_max
|
||||
};
|
||||
|
||||
void
|
||||
init_layout_max()
|
||||
{
|
||||
layouts =
|
||||
(Layout **) attach_item_end((void **) layouts, &max,
|
||||
sizeof(Layout *));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
arrange_max(Page * p)
|
||||
{
|
||||
int i = 0;
|
||||
if (!p->managed)
|
||||
return;
|
||||
|
||||
for (i = 0; p->managed[i]; i++) {
|
||||
p->managed[i]->managed_rect = p->managed_rect;
|
||||
resize_frame(p->managed[i], &p->managed[i]->managed_rect, 0, 1);
|
||||
}
|
||||
if (p->managed_stack)
|
||||
XRaiseWindow(dpy, p->managed_stack[0]->win);
|
||||
|
||||
/* raise floatings */
|
||||
for (i = 0; p->floating && p->floating[i]; i++)
|
||||
XRaiseWindow(dpy, p->floating[i]->win);
|
||||
}
|
||||
|
||||
static void
|
||||
init_max(Page * p)
|
||||
{
|
||||
/* max has nothing to init */
|
||||
arrange_max(p);
|
||||
}
|
||||
|
||||
static void
|
||||
deinit_max(Page * p)
|
||||
{
|
||||
/* max has nothing to free */
|
||||
}
|
||||
|
||||
static void
|
||||
manage_max(Frame * f)
|
||||
{
|
||||
Page *p = f->page;
|
||||
int idx;
|
||||
if (!p)
|
||||
return;
|
||||
idx = index_next_item((void **) p->managed, f);
|
||||
if (idx > 0)
|
||||
swap((void **) &p->managed[0], (void **) &p->managed[idx]);
|
||||
arrange_max(f->page);
|
||||
}
|
||||
|
||||
static void
|
||||
unmanage_max(Frame * f)
|
||||
{
|
||||
/* nothing todo */
|
||||
}
|
||||
|
||||
static void
|
||||
resize_max(Frame * f, XRectangle * new, XPoint * pt)
|
||||
{
|
||||
if (f->page)
|
||||
f->managed_rect = f->page->managed_rect;
|
||||
}
|
||||
|
||||
static Frame *
|
||||
select_max(Frame * f, char *what)
|
||||
{
|
||||
Page *p = f->page;
|
||||
Frame *res = 0;
|
||||
int i, idx;
|
||||
|
||||
if (!strncmp(what, "prev", 5)) {
|
||||
idx = index_prev_item((void **) p->managed, f);
|
||||
if (idx >= 0)
|
||||
res = p->managed[idx];
|
||||
} else if (!strncmp(what, "next", 5)) {
|
||||
idx = index_next_item((void **) p->managed, f);
|
||||
if (idx >= 0)
|
||||
res = p->managed[idx];
|
||||
}
|
||||
if (res)
|
||||
XRaiseWindow(dpy, res->win);
|
||||
|
||||
/* raise floatings */
|
||||
for (i = 0; p->floating && p->floating[i]; i++)
|
||||
XRaiseWindow(dpy, p->floating[i]->win);
|
||||
return res;
|
||||
}
|
176
cmd/wm/old/layout_tiled.c
Normal file
176
cmd/wm/old/layout_tiled.c
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "wm.h"
|
||||
#include "layout.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
/* tiled layout definition */
|
||||
static void arrange_tiled(Page * p);
|
||||
static void init_tiled(Page * p);
|
||||
static void deinit_tiled(Page * p);
|
||||
static void manage_tiled(Frame * f);
|
||||
static void unmanage_tiled(Frame * f);
|
||||
static void resize_tiled(Frame * f, XRectangle * new, XPoint * pt);
|
||||
static Frame *select_tiled(Frame * f, char *what);
|
||||
|
||||
static Layout tiled =
|
||||
{"tiled", init_tiled, deinit_tiled, arrange_tiled, manage_tiled,
|
||||
unmanage_tiled, resize_tiled, select_tiled
|
||||
};
|
||||
|
||||
void
|
||||
init_layout_tiled()
|
||||
{
|
||||
layouts =
|
||||
(Layout **) attach_item_end((void **) layouts, &tiled,
|
||||
sizeof(Layout *));
|
||||
}
|
||||
|
||||
static void
|
||||
arrange_tiled(Page * p)
|
||||
{
|
||||
int tw, th, i, size = 0;
|
||||
|
||||
if (!p->managed)
|
||||
return;
|
||||
|
||||
/* determing num of frame and size of page */
|
||||
size = count_items((void **) p->managed);
|
||||
|
||||
if (size > 1) {
|
||||
tw = (p->managed_rect.width * *((int *) p->aux)) / 100;
|
||||
th = p->managed_rect.height / (size - 1);
|
||||
} else {
|
||||
tw = p->managed_rect.width;
|
||||
th = p->managed_rect.height;
|
||||
}
|
||||
|
||||
/* master tile */
|
||||
p->managed[0]->managed_rect.x = p->managed_rect.x;
|
||||
p->managed[0]->managed_rect.y = p->managed_rect.y;
|
||||
p->managed[0]->managed_rect.width = tw;
|
||||
p->managed[0]->managed_rect.height = p->managed_rect.height;
|
||||
resize_frame(p->managed[0], &p->managed[0]->managed_rect, 0, 1);
|
||||
|
||||
if (size == 1)
|
||||
return;
|
||||
for (i = 1; i < size; i++) {
|
||||
p->managed[i]->managed_rect.x = p->managed_rect.x + tw;
|
||||
p->managed[i]->managed_rect.y = p->managed_rect.y + (i - 1) * th;
|
||||
p->managed[i]->managed_rect.width = p->managed_rect.width - tw;
|
||||
p->managed[i]->managed_rect.height = th;
|
||||
resize_frame(p->managed[i], &p->managed[i]->managed_rect, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
init_tiled(Page * p)
|
||||
{
|
||||
p->aux = emalloc(sizeof(int));
|
||||
*((int *) p->aux) =
|
||||
_strtonum(core_files[CORE_PAGE_TILE_WIDTH]->content, 5, 95);
|
||||
}
|
||||
|
||||
static void
|
||||
deinit_tiled(Page * p)
|
||||
{
|
||||
p->aux = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
manage_tiled(Frame * f)
|
||||
{
|
||||
Page *p = f->page;
|
||||
if (!p)
|
||||
return;
|
||||
arrange_tiled(p);
|
||||
}
|
||||
|
||||
static void
|
||||
unmanage_tiled(Frame * f)
|
||||
{
|
||||
manage_tiled(f);
|
||||
}
|
||||
|
||||
static void
|
||||
drop_resize(Frame * f, XRectangle * new)
|
||||
{
|
||||
Page *p = f->page;
|
||||
int num = 0;
|
||||
int rearrange = 0;
|
||||
|
||||
if (!p)
|
||||
return;
|
||||
/* determing num of frame and size of page */
|
||||
num = index_item((void **) p->managed, f);
|
||||
|
||||
if (!num) { /* master tile */
|
||||
if ((f->managed_rect.x == new->x)
|
||||
&& (f->managed_rect.width != new->width)) {
|
||||
f->managed_rect = *new;
|
||||
rearrange = 1;
|
||||
}
|
||||
} else if (f->managed_rect.x != new->x) {
|
||||
int diff = f->managed_rect.width - new->width;
|
||||
p->managed[0]->managed_rect.width += diff;
|
||||
rearrange = 1;
|
||||
}
|
||||
if (rearrange) {
|
||||
int tw =
|
||||
(p->managed[0]->managed_rect.width * 100) /
|
||||
p->managed_rect.width;
|
||||
if (tw > 95)
|
||||
tw = 95;
|
||||
else if (tw < 5)
|
||||
tw = 5;
|
||||
*((int *) p->aux) = tw;
|
||||
arrange_tiled(p);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
resize_tiled(Frame * f, XRectangle * new, XPoint * pt)
|
||||
{
|
||||
if ((f->managed_rect.width == new->width)
|
||||
&& (f->managed_rect.height == new->height))
|
||||
drop_move(f, new, pt);
|
||||
else
|
||||
drop_resize(f, new);
|
||||
}
|
||||
|
||||
static Frame *
|
||||
select_tiled(Frame * f, char *what)
|
||||
{
|
||||
Page *p = f->page;
|
||||
int idx;
|
||||
if (!strncmp(what, "prev", 5)) {
|
||||
idx = index_prev_item((void **) p->managed, f);
|
||||
if (idx >= 0)
|
||||
return p->managed[idx];
|
||||
} else if (!strncmp(what, "next", 5)) {
|
||||
idx = index_next_item((void **) p->managed, f);
|
||||
if (idx >= 0)
|
||||
return p->managed[idx];
|
||||
} else if (!strncmp(what, "zoomed", 7)) {
|
||||
idx = index_item((void **) p->managed, f);
|
||||
if (idx == 0 && p->managed[1])
|
||||
idx = 1;
|
||||
if (idx > 0)
|
||||
swap((void **) &p->managed[0], (void **) &p->managed[idx]);
|
||||
p->managed_stack = (Frame **)
|
||||
attach_item_begin(detach_item
|
||||
((void **) p->managed_stack, p->managed[0],
|
||||
sizeof(Frame *)), p->managed[0],
|
||||
sizeof(Frame *));
|
||||
arrange_tiled(p);
|
||||
return p->managed_stack[0];
|
||||
}
|
||||
return 0;
|
||||
}
|
223
cmd/wm/old/layout_vsplit.c
Normal file
223
cmd/wm/old/layout_vsplit.c
Normal file
@ -0,0 +1,223 @@
|
||||
/*
|
||||
* (C)opyright 2005 Jonas WUSTRACK <doez at supinfo dot com>
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "wm.h"
|
||||
#include "layout.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
static void arrange_vsplit(Page * p);
|
||||
static void init_vsplit(Page * p);
|
||||
static void deinit_vsplit(Page * p);
|
||||
static void manage_vsplit(Frame * f);
|
||||
static void unmanage_vsplit(Frame * f);
|
||||
static void resize_vsplit(Frame * f, XRectangle * new, XPoint * pt);
|
||||
static Frame *select_vsplit(Frame * f, char *what);
|
||||
|
||||
static Layout vsplit =
|
||||
{"vsplit", init_vsplit, deinit_vsplit, arrange_vsplit, manage_vsplit,
|
||||
unmanage_vsplit, resize_vsplit, select_vsplit
|
||||
};
|
||||
|
||||
void
|
||||
init_layout_vsplit()
|
||||
{
|
||||
layouts =
|
||||
(Layout **) attach_item_end((void **) layouts, &vsplit,
|
||||
sizeof(Layout *));
|
||||
}
|
||||
|
||||
static void
|
||||
get_base_geometry_vsplit(void **items, unsigned int *size,
|
||||
unsigned int *cols, unsigned int *rows)
|
||||
{
|
||||
/* float sq, dummy; */
|
||||
|
||||
*size = count_items((void **) items);
|
||||
*cols = 1;
|
||||
*rows = *size;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
arrange_vsplit(Page * p)
|
||||
{
|
||||
unsigned int i, ic, tw, th, rows, cols;
|
||||
|
||||
if (!p->managed)
|
||||
return;
|
||||
|
||||
get_base_geometry_vsplit((void **) p->managed, &ic, &cols, &rows);
|
||||
th = p->managed_rect.height / rows;
|
||||
tw = p->managed_rect.width;
|
||||
|
||||
for (i = 0; i < rows; i++) {
|
||||
if (p->managed[i]) {
|
||||
XRectangle *r = (XRectangle *) p->managed[i]->aux;
|
||||
r->x = p->managed_rect.x;
|
||||
r->y = p->managed_rect.y + i * th;
|
||||
r->width = tw;
|
||||
r->height = th;
|
||||
p->managed[i]->managed_rect = *r;
|
||||
resize_frame(p->managed[i], &p->managed[i]->managed_rect, 0,
|
||||
1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
init_vsplit(Page * p)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; p->managed && p->managed[i]; i++)
|
||||
p->managed[i]->aux = emalloc(sizeof(XRectangle));
|
||||
}
|
||||
|
||||
static void
|
||||
deinit_vsplit(Page * p)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; p->managed && p->managed[i]; i++) {
|
||||
if (p->managed[i]->aux) {
|
||||
free(p->managed[i]->aux);
|
||||
p->managed[i]->aux = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
manage_vsplit(Frame * f)
|
||||
{
|
||||
f->aux = emalloc(sizeof(XRectangle));
|
||||
if (f->page)
|
||||
arrange_vsplit(f->page);
|
||||
}
|
||||
|
||||
static void
|
||||
unmanage_vsplit(Frame * f)
|
||||
{
|
||||
if (f->aux) {
|
||||
free(f->aux);
|
||||
f->aux = 0;
|
||||
}
|
||||
if (f->page)
|
||||
arrange_vsplit(f->page);
|
||||
}
|
||||
|
||||
#define THRESHOLD 30
|
||||
|
||||
static void
|
||||
drop_resize(Frame * f, XRectangle * new)
|
||||
{
|
||||
int diff;
|
||||
unsigned int i, rows, cols, num, idx;
|
||||
Page *p = f->page;
|
||||
XRectangle *r;
|
||||
|
||||
if (!p || !p->managed)
|
||||
return;
|
||||
get_base_geometry_vsplit((void **) p->managed, &i, &cols, &rows);
|
||||
|
||||
num = index_item((void **) p->managed, f);
|
||||
|
||||
/* vertical resize */
|
||||
if (f->managed_rect.y == new->y
|
||||
&& f->managed_rect.height != new->height) {
|
||||
/* south direction resize */
|
||||
if (num == rows - 1)
|
||||
return;
|
||||
if (p->managed[num + 1]
|
||||
&& (new->y + new->height > p->managed[num + 1]->managed_rect.y
|
||||
+ p->managed[num + 1]->managed_rect.height - THRESHOLD))
|
||||
return;
|
||||
diff = new->height - ((XRectangle *) f->aux)->height;
|
||||
idx = num;
|
||||
if (p->managed[idx]) {
|
||||
r = (XRectangle *) p->managed[idx]->aux;
|
||||
r->height = new->height;
|
||||
p->managed[idx]->managed_rect = *r;
|
||||
resize_frame(p->managed[idx], &p->managed[idx]->managed_rect,
|
||||
0, 1);
|
||||
}
|
||||
idx = num + 1;
|
||||
if (p->managed[idx]) {
|
||||
r = (XRectangle *) p->managed[idx]->aux;
|
||||
r->y += diff;
|
||||
r->height -= diff;
|
||||
p->managed[idx]->managed_rect = *r;
|
||||
resize_frame(p->managed[idx], &p->managed[idx]->managed_rect,
|
||||
0, 1);
|
||||
}
|
||||
} else if (f->managed_rect.y != new->y) {
|
||||
/* north direction resize */
|
||||
if (!num)
|
||||
return;
|
||||
if (new->y < p->managed[num - 1]->managed_rect.y + THRESHOLD)
|
||||
return;
|
||||
diff = new->height - ((XRectangle *) f->aux)->height;
|
||||
idx = num;
|
||||
if (p->managed[idx]) {
|
||||
r = (XRectangle *) p->managed[idx]->aux;
|
||||
r->y -= diff;
|
||||
r->height += diff;
|
||||
p->managed[idx]->managed_rect = *r;
|
||||
resize_frame(p->managed[idx], &p->managed[idx]->managed_rect,
|
||||
0, 1);
|
||||
}
|
||||
idx = num - 1;
|
||||
if (p->managed[idx]) {
|
||||
r = (XRectangle *) p->managed[idx]->aux;
|
||||
r->height -= diff;
|
||||
p->managed[idx]->managed_rect = *r;
|
||||
resize_frame(p->managed[idx], &p->managed[idx]->managed_rect,
|
||||
0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
resize_vsplit(Frame * f, XRectangle * new, XPoint * pt)
|
||||
{
|
||||
if ((f->managed_rect.width == new->width)
|
||||
&& (f->managed_rect.height == new->height))
|
||||
drop_move(f, new, pt);
|
||||
else
|
||||
drop_resize(f, new);
|
||||
}
|
||||
|
||||
static Frame *
|
||||
select_vsplit(Frame * f, char *what)
|
||||
{
|
||||
Page *p = f->page;
|
||||
int idx;
|
||||
if (!strncmp(what, "prev", 5)) {
|
||||
idx = index_prev_item((void **) p->managed, f);
|
||||
if (idx >= 0)
|
||||
return p->managed[idx];
|
||||
} else if (!strncmp(what, "next", 5)) {
|
||||
idx = index_next_item((void **) p->managed, f);
|
||||
if (idx >= 0)
|
||||
return p->managed[idx];
|
||||
} else if (!strncmp(what, "zoomed", 7)) {
|
||||
idx = index_item((void **) p->managed, f);
|
||||
if (idx == 0 && p->managed[1])
|
||||
idx = 1;
|
||||
if (idx > 0)
|
||||
swap((void **) &p->managed[0], (void **) &p->managed[idx]);
|
||||
p->managed_stack = (Frame **)
|
||||
attach_item_begin(detach_item
|
||||
((void **) p->managed_stack, p->managed[0],
|
||||
sizeof(Frame *)), p->managed[0],
|
||||
sizeof(Frame *));
|
||||
arrange_vsplit(p);
|
||||
return p->managed_stack[0];
|
||||
}
|
||||
return 0;
|
||||
}
|
463
cmd/wm/page.c
Normal file
463
cmd/wm/page.c
Normal file
@ -0,0 +1,463 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "wm.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
static Page zero_page = {0};
|
||||
|
||||
static void select_frame(void *obj, char *cmd);
|
||||
static void _toggle_frame(void *obj, char *cmd);
|
||||
static void handle_after_write_page(IXPServer * s, File * f);
|
||||
static void handle_before_read_page(IXPServer * s, File * f);
|
||||
|
||||
/* action table for /page/?/ namespace */
|
||||
Action page_acttbl[] = {
|
||||
{"select", select_frame},
|
||||
{"toggle", _toggle_frame},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
Page *
|
||||
alloc_page(char *autodestroy)
|
||||
{
|
||||
Page *p = emalloc(sizeof(Page));
|
||||
char buf[MAX_BUF], buf2[MAX_BUF];
|
||||
int id = count_items((void **) pages) + 1;
|
||||
|
||||
*p = zero_page;
|
||||
p->managed_rect = rect;
|
||||
if (core_files[CORE_PAGE_MANAGED_SIZE]->content)
|
||||
blitz_strtorect(dpy, &rect, &p->managed_rect,
|
||||
core_files[CORE_PAGE_MANAGED_SIZE]->content);
|
||||
|
||||
snprintf(buf, sizeof(buf), "/page/%d", id);
|
||||
p->files[P_PREFIX] = ixp_create(ixps, buf);
|
||||
snprintf(buf, sizeof(buf), "/page/%d/floating", id);
|
||||
p->files[P_FLOATING_PREFIX] = ixp_create(ixps, buf);
|
||||
snprintf(buf, sizeof(buf), "/page/%d/floating/sel", id);
|
||||
p->files[P_FLOATING_SELECTED] = ixp_create(ixps, buf);
|
||||
p->files[P_FLOATING_SELECTED]->bind = 1; /* mount point */
|
||||
snprintf(buf, sizeof(buf), "/page/%d/floating/name", id);
|
||||
p->files[P_FLOATING_LAYOUT] = wmii_create_ixpfile(ixps, buf, "float");
|
||||
snprintf(buf, sizeof(buf), "/page/%d/managed", id);
|
||||
p->files[P_MANAGED_PREFIX] = ixp_create(ixps, buf);
|
||||
snprintf(buf, sizeof(buf), "/page/%d/managed/sel", id);
|
||||
p->files[P_MANAGED_SELECTED] = ixp_create(ixps, buf);
|
||||
p->files[P_MANAGED_SELECTED]->bind = 1; /* mount point */
|
||||
snprintf(buf, sizeof(buf), "/page/%d/ctl", id);
|
||||
p->files[P_CTL] = ixp_create(ixps, buf);
|
||||
p->files[P_CTL]->after_write = handle_after_write_page;
|
||||
snprintf(buf, sizeof(buf), "/page/%d/name", id);
|
||||
snprintf(buf2, sizeof(buf2), "%d", id);
|
||||
p->files[P_NAME] = wmii_create_ixpfile(ixps, buf, buf2);
|
||||
snprintf(buf, sizeof(buf), "/page/%d/managed/name", id);
|
||||
p->files[P_MANAGED_LAYOUT] =
|
||||
wmii_create_ixpfile(ixps, buf,
|
||||
core_files[CORE_PAGE_LAYOUT]->content);
|
||||
p->files[P_MANAGED_LAYOUT]->after_write = handle_after_write_page;
|
||||
snprintf(buf, sizeof(buf), "/page/%d/mode", id);
|
||||
p->files[P_MODE] = ixp_create(ixps, buf);
|
||||
p->files[P_MODE]->bind = 1;
|
||||
snprintf(buf, sizeof(buf), "/page/%d/managed/size", id);
|
||||
p->files[P_MANAGED_SIZE] = wmii_create_ixpfile(ixps, buf, core_files[CORE_PAGE_MANAGED_SIZE]->content);
|
||||
p->files[P_MANAGED_SIZE]->after_write = handle_after_write_page;
|
||||
p->files[P_MANAGED_SIZE]->before_read = handle_before_read_page;
|
||||
snprintf(buf, sizeof(buf), "/page/%d/auto-destroy", id);
|
||||
p->files[P_AUTO_DESTROY] = wmii_create_ixpfile(ixps, buf, autodestroy);
|
||||
p->layout = get_layout(p->files[P_MANAGED_LAYOUT]->content);
|
||||
if (p->layout) {
|
||||
p->layout->init(p);
|
||||
p->files[P_MODE]->content = p->files[P_MANAGED_PREFIX]->content;
|
||||
} else
|
||||
p->files[P_MODE]->content = p->files[P_FLOATING_PREFIX]->content;
|
||||
pages = (Page **) attach_item_end((void **) pages, p, sizeof(Page *));
|
||||
sel_page = index_item((void **) pages, p);
|
||||
core_files[CORE_PAGE_SELECTED]->content = p->files[P_PREFIX]->content;
|
||||
invoke_core_event(core_files[CORE_EVENT_PAGE_UPDATE]);
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
free_page(Page * p)
|
||||
{
|
||||
pages = (Page **) detach_item((void **) pages, p, sizeof(Page *));
|
||||
if (pages) {
|
||||
int i;
|
||||
char buf[8];
|
||||
if (sel_page - 1 >= 0)
|
||||
sel_page--;
|
||||
else
|
||||
sel_page = 0;
|
||||
for (i = 0; pages[i]; i++) {
|
||||
snprintf(buf, sizeof(buf), "%d", i + 1);
|
||||
free(pages[i]->files[P_PREFIX]->name);
|
||||
pages[i]->files[P_PREFIX]->name = estrdup(buf);
|
||||
free(pages[i]->files[P_NAME]->content);
|
||||
pages[i]->files[P_NAME]->content = estrdup(buf);
|
||||
pages[i]->files[P_NAME]->size = strlen(buf);
|
||||
}
|
||||
}
|
||||
if (p->layout)
|
||||
p->layout->deinit(p);
|
||||
core_files[CORE_PAGE_SELECTED]->content = 0;
|
||||
ixp_remove_file(ixps, p->files[P_PREFIX]);
|
||||
if (ixps->errstr)
|
||||
fprintf(stderr, "wmiiwm: free_page(): %s\n", ixps->errstr);
|
||||
free(p);
|
||||
}
|
||||
|
||||
void
|
||||
draw_page(Page * p)
|
||||
{
|
||||
int i;
|
||||
if (!p)
|
||||
return;
|
||||
for (i = 0; p->floating && p->floating[i]; i++)
|
||||
draw_frame(p->floating[i]);
|
||||
for (i = 0; p->managed && p->managed[i]; i++)
|
||||
draw_frame(p->managed[i]);
|
||||
}
|
||||
|
||||
XRectangle *
|
||||
rectangles(unsigned int *num)
|
||||
{
|
||||
XRectangle *result = 0;
|
||||
int i, j = 0;
|
||||
Window d1, d2;
|
||||
Window *wins;
|
||||
XWindowAttributes wa;
|
||||
XRectangle r;
|
||||
|
||||
if (XQueryTree(dpy, root, &d1, &d2, &wins, num)) {
|
||||
result = emalloc(*num * sizeof(XRectangle));
|
||||
for (i = 0; i < *num; i++) {
|
||||
if (!XGetWindowAttributes(dpy, wins[i], &wa))
|
||||
continue;
|
||||
if (wa.override_redirect && (wa.map_state == IsViewable)) {
|
||||
r.x = wa.x;
|
||||
r.y = wa.y;
|
||||
r.width = wa.width;
|
||||
r.height = wa.height;
|
||||
result[j++] = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wins) {
|
||||
XFree(wins);
|
||||
}
|
||||
*num = j;
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
_toggle_frame(void *obj, char *cmd)
|
||||
{
|
||||
Page *p = obj;
|
||||
Frame *f;
|
||||
if (!p || !p->layout)
|
||||
return;
|
||||
f = get_selected(p);
|
||||
if (!f)
|
||||
return;
|
||||
toggle_frame(f);
|
||||
}
|
||||
|
||||
static Frame *
|
||||
select_toggled(Page * p, Frame * f)
|
||||
{
|
||||
if (is_managed_frame(f))
|
||||
return p->floating_stack ? p->floating_stack[0] : 0;
|
||||
else
|
||||
return f = p->managed_stack ? p->managed_stack[0] : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Frame *
|
||||
select_floating(Page * p, Frame * f, char *what)
|
||||
{
|
||||
int idx;
|
||||
if (!strncmp(what, "prev", 5)) {
|
||||
idx = index_prev_item((void **) p->floating, f);
|
||||
if (idx >= 0)
|
||||
return p->floating[idx];
|
||||
} else if (!strncmp(what, "next", 5)) {
|
||||
idx = index_next_item((void **) p->floating, f);
|
||||
if (idx >= 0)
|
||||
return p->floating[idx];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
center_pointer(Frame * f)
|
||||
{
|
||||
|
||||
Window dummy;
|
||||
int wex, wey, ex, ey, i;
|
||||
unsigned int dmask;
|
||||
XRectangle *r;
|
||||
|
||||
if (!f)
|
||||
return;
|
||||
r = rect_of_frame(f);
|
||||
XQueryPointer(dpy, f->win, &dummy, &dummy, &i, &i, &wex, &wey, &dmask);
|
||||
XTranslateCoordinates(dpy, f->win, root, wex, wey, &ex, &ey, &dummy);
|
||||
if (blitz_ispointinrect(ex, ey, r))
|
||||
return;
|
||||
/* suppress EnterNotify's while mouse warping */
|
||||
XSelectInput(dpy, root, ROOT_MASK & ~StructureNotifyMask);
|
||||
XWarpPointer(dpy, None, f->win, 0, 0, 0, 0,
|
||||
r->width / 2, r->height / 2);
|
||||
XSync(dpy, False);
|
||||
XSelectInput(dpy, root, ROOT_MASK);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
select_frame(void *obj, char *cmd)
|
||||
{
|
||||
Page *p = (Page *) obj;
|
||||
Frame *f, *old, *old2;
|
||||
if (!p || !cmd)
|
||||
return;
|
||||
old2 = old = f = get_selected(p);
|
||||
if (!f)
|
||||
return;
|
||||
if (is_managed_frame(f))
|
||||
old2 = p->managed[0];
|
||||
if (!strncmp(cmd, "toggled", 8))
|
||||
f = select_toggled(p, f);
|
||||
else if (is_managed_frame(f))
|
||||
f = p->layout->select(f, cmd);
|
||||
else
|
||||
f = select_floating(p, f, cmd);
|
||||
if (!f)
|
||||
return;
|
||||
focus_frame(f, 1, 1, 1);
|
||||
center_pointer(f);
|
||||
if (old)
|
||||
draw_frame(old);
|
||||
if (old2 != old)
|
||||
draw_frame(old2); /* on zoom */
|
||||
draw_frame(f);
|
||||
}
|
||||
|
||||
void
|
||||
hide_page(Page * p)
|
||||
{
|
||||
|
||||
int i;
|
||||
for (i = 0; p->floating && p->floating[i]; i++)
|
||||
XUnmapWindow(dpy, p->floating[i]->win);
|
||||
for (i = 0; p->managed && p->managed[i]; i++)
|
||||
XUnmapWindow(dpy, p->managed[i]->win);
|
||||
XSync(dpy, False);
|
||||
}
|
||||
|
||||
void
|
||||
show_page(Page * p)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; p->floating && p->floating[i]; i++)
|
||||
XMapWindow(dpy, p->floating[i]->win);
|
||||
for (i = 0; p->managed && p->managed[i]; i++)
|
||||
XMapWindow(dpy, p->managed[i]->win);
|
||||
XSync(dpy, False);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_before_read_page(IXPServer * s, File * f)
|
||||
{
|
||||
int i;
|
||||
XRectangle *rct = 0;
|
||||
for (i = 0; pages && pages[i]; i++) {
|
||||
if (pages[i]->files[P_MANAGED_SIZE] == f) {
|
||||
rct = &pages[i]->managed_rect;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rct) {
|
||||
char buf[64];
|
||||
snprintf(buf, 64, "%d,%d,%d,%d", rct->x, rct->y,
|
||||
rct->width, rct->height);
|
||||
if (f->content)
|
||||
free(f->content);
|
||||
f->content = estrdup(buf);
|
||||
f->size = strlen(buf);
|
||||
}
|
||||
}
|
||||
|
||||
Layout *
|
||||
get_layout(char *name)
|
||||
{
|
||||
int i = 0;
|
||||
size_t len;
|
||||
if (!name)
|
||||
return 0;
|
||||
len = strlen(name);
|
||||
for (i = 0; layouts[i]; i++) {
|
||||
if (!strncmp(name, layouts[i]->name, len))
|
||||
return layouts[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_after_write_page(IXPServer * s, File * f)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; pages && pages[i]; i++) {
|
||||
Page *p = pages[i];
|
||||
if (p->files[P_CTL] == f) {
|
||||
run_action(f, p, page_acttbl);
|
||||
return;
|
||||
} else if (p->files[P_MANAGED_SIZE] == f) {
|
||||
/* resize stuff */
|
||||
blitz_strtorect(dpy, &rect, &p->managed_rect,
|
||||
p->files[P_MANAGED_SIZE]->content);
|
||||
if (!p->managed_rect.width)
|
||||
p->managed_rect.width = 10;
|
||||
if (!p->managed_rect.height)
|
||||
p->managed_rect.height = 10;
|
||||
if (p->layout)
|
||||
p->layout->arrange(p);
|
||||
draw_page(p);
|
||||
return;
|
||||
} else if (p->files[P_MANAGED_LAYOUT] == f) {
|
||||
int had_valid_layout = p->layout ? 1 : 0;
|
||||
if (p->layout)
|
||||
p->layout->deinit(p);
|
||||
p->layout = get_layout(p->files[P_MANAGED_LAYOUT]->content);
|
||||
if (p->layout) {
|
||||
p->layout->init(p);
|
||||
p->layout->arrange(p);
|
||||
if (!had_valid_layout) {
|
||||
int j;
|
||||
Frame **tmp = 0;
|
||||
for (j = 0; p->floating && p->floating[j]; j++) {
|
||||
if (!p->floating[j]->floating)
|
||||
tmp =
|
||||
(Frame **) attach_item_begin((void **) tmp,
|
||||
p->
|
||||
floating[j],
|
||||
sizeof(Frame
|
||||
*));
|
||||
}
|
||||
for (j = 0; tmp && tmp[j]; j++)
|
||||
toggle_frame(tmp[j]);
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
if (!p->layout) {
|
||||
/* make all managed clients floating */
|
||||
int j;
|
||||
Frame **tmp = 0;
|
||||
while (p->managed) {
|
||||
tmp = (Frame **) attach_item_begin((void **) tmp,
|
||||
p->managed[0],
|
||||
sizeof(Frame *));
|
||||
detach_frame_from_page(p->managed[0], 1);
|
||||
}
|
||||
for (j = 0; tmp && tmp[j]; j++) {
|
||||
attach_Frameo_page(p, tmp[j], 0);
|
||||
resize_frame(tmp[j], rect_of_frame(tmp[j]), 0, 1);
|
||||
}
|
||||
free(tmp);
|
||||
}
|
||||
draw_page(p);
|
||||
invoke_core_event(core_files[CORE_EVENT_PAGE_UPDATE]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
attach_Frameo_page(Page * p, Frame * f, int managed)
|
||||
{
|
||||
Frame *old = get_selected(p);
|
||||
XSelectInput(dpy, root, ROOT_MASK & ~StructureNotifyMask);
|
||||
XMapRaised(dpy, f->win);
|
||||
if (!f->floating && managed && p->layout) {
|
||||
int i;
|
||||
p->managed = (Frame **) attach_item_end((void **) p->managed, f,
|
||||
sizeof(Frame *));
|
||||
p->managed_stack =
|
||||
(Frame **) attach_item_begin((void **) p->managed_stack, f,
|
||||
sizeof(Frame *));
|
||||
wmii_move_ixpfile(f->files[F_PREFIX], p->files[P_MANAGED_PREFIX]);
|
||||
p->files[P_MANAGED_SELECTED]->content =
|
||||
f->files[F_PREFIX]->content;
|
||||
if (p == pages[sel_page])
|
||||
for (i = 0; p->floating && p->floating[i]; i++)
|
||||
XRaiseWindow(dpy, p->floating[i]->win);
|
||||
} else {
|
||||
p->floating = (Frame **) attach_item_end((void **) p->floating, f,
|
||||
sizeof(Frame *));
|
||||
p->floating_stack =
|
||||
(Frame **) attach_item_begin((void **) p->floating_stack, f,
|
||||
sizeof(Frame *));
|
||||
wmii_move_ixpfile(f->files[F_PREFIX], p->files[P_FLOATING_PREFIX]);
|
||||
p->files[P_FLOATING_SELECTED]->content =
|
||||
f->files[F_PREFIX]->content;
|
||||
p->files[P_MODE]->content = p->files[P_FLOATING_PREFIX]->content;
|
||||
}
|
||||
f->page = p;
|
||||
focus_frame(f, 1, 0, 1);
|
||||
if (is_managed_frame(f) && p->layout)
|
||||
p->layout->manage(f);
|
||||
center_pointer(f);
|
||||
if (old)
|
||||
draw_frame(old);
|
||||
draw_frame(f);
|
||||
}
|
||||
|
||||
void
|
||||
detach_frame_from_page(Frame * f, int ignore_focus_and_destroy)
|
||||
{
|
||||
Page *p = f->page;
|
||||
wmii_move_ixpfile(f->files[F_PREFIX], core_files[CORE_DETACHED_FRAME]);
|
||||
if (is_managed_frame(f)) {
|
||||
p->managed = (Frame **) detach_item((void **) p->managed, f,
|
||||
sizeof(Frame *));
|
||||
p->managed_stack =
|
||||
(Frame **) detach_item((void **) p->managed_stack, f,
|
||||
sizeof(Frame *));
|
||||
p->files[P_MANAGED_SELECTED]->content = 0;
|
||||
} else {
|
||||
p->floating = (Frame **) detach_item((void **) p->floating, f,
|
||||
sizeof(Frame *));
|
||||
p->floating_stack =
|
||||
(Frame **) detach_item((void **) p->floating_stack, f,
|
||||
sizeof(Frame *));
|
||||
p->files[P_FLOATING_SELECTED]->content = 0;
|
||||
}
|
||||
XUnmapWindow(dpy, f->win);
|
||||
if (is_managed_mode(p) && p->layout)
|
||||
p->layout->unmanage(f);
|
||||
f->page = 0;
|
||||
if (!ignore_focus_and_destroy) {
|
||||
Frame *fr;
|
||||
if (!p->managed && !p->floating
|
||||
&& _strtonum(p->files[P_AUTO_DESTROY]->content, 0, 1)) {
|
||||
destroy_page(p);
|
||||
return;
|
||||
}
|
||||
focus_page(p, 0, 1);
|
||||
fr = get_selected(p);
|
||||
if (fr) {
|
||||
center_pointer(fr);
|
||||
draw_frame(fr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
290
cmd/wm/wm.c
Normal file
290
cmd/wm/wm.c
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <X11/cursorfont.h>
|
||||
#include <X11/Xproto.h>
|
||||
|
||||
#include "wm.h"
|
||||
|
||||
static int other_wm_running;
|
||||
static int (*x_error_handler) (Display *, XErrorEvent *);
|
||||
|
||||
char *version[] = {
|
||||
"wmiiwm - window manager improved 2 - " VERSION "\n"
|
||||
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
|
||||
};
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr, "%s",
|
||||
"usage: wmiiwm -s <socket file> [-c] [-v]\n"
|
||||
" -s socket file\n"
|
||||
" -c checks if another WM is already running\n"
|
||||
" -v version info\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
init_atoms()
|
||||
{
|
||||
wm_state = XInternAtom(dpy, "WM_STATE", False);
|
||||
wm_change_state = XInternAtom(dpy, "WM_CHANGE_STATE", False);
|
||||
wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
|
||||
wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
|
||||
motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
|
||||
net_wm_desktop = XInternAtom(dpy, "_NET_WM_DESKTOP", False);
|
||||
}
|
||||
|
||||
static void
|
||||
init_cursors()
|
||||
{
|
||||
normal_cursor = XCreateFontCursor(dpy, XC_left_ptr);
|
||||
resize_cursor = XCreateFontCursor(dpy, XC_sizing);
|
||||
move_cursor = XCreateFontCursor(dpy, XC_fleur);
|
||||
drag_cursor = XCreateFontCursor(dpy, XC_cross);
|
||||
w_cursor = XCreateFontCursor(dpy, XC_left_side);
|
||||
e_cursor = XCreateFontCursor(dpy, XC_right_side);
|
||||
n_cursor = XCreateFontCursor(dpy, XC_top_side);
|
||||
s_cursor = XCreateFontCursor(dpy, XC_bottom_side);
|
||||
nw_cursor = XCreateFontCursor(dpy, XC_top_left_corner);
|
||||
ne_cursor = XCreateFontCursor(dpy, XC_top_right_corner);
|
||||
sw_cursor = XCreateFontCursor(dpy, XC_bottom_left_corner);
|
||||
se_cursor = XCreateFontCursor(dpy, XC_bottom_right_corner);
|
||||
}
|
||||
|
||||
static void
|
||||
init_defaults()
|
||||
{
|
||||
defaults[WM_DETACHED_FRAME] = ixp_create(ixps, "/detached/frame");
|
||||
defaults[WM_DETACHED_CLIENT] = ixp_create(ixps, "/detached/client");
|
||||
defaults[WM_TRANS_COLOR] = wmii_create_ixpfile(ixps, "/default/transcolor", BLITZ_SEL_FG_COLOR);
|
||||
defaults[WM_TRANS_COLOR]->after_write = handle_after_write;
|
||||
defaults[WM_SEL_BG_COLOR] = wmii_create_ixpfile(ixps, "/default/selstyle/bgcolor", BLITZ_SEL_BG_COLOR);
|
||||
defaults[WM_SEL_FG_COLOR] = wmii_create_ixpfile(ixps, "/default/selstyle/fgcolor", BLITZ_SEL_FG_COLOR);
|
||||
defaults[WM_SEL_BORDER_COLOR] = wmii_create_ixpfile(ixps, "/default/selstyle/fgcolor", BLITZ_SEL_BORDER_COLOR);
|
||||
defaults[WM_NORM_BG_COLOR] = wmii_create_ixpfile(ixps, "/default/normstyle/bgcolor", BLITZ_NORM_BG_COLOR);
|
||||
defaults[WM_NORM_FG_COLOR] = wmii_create_ixpfile(ixps, "/default/normstyle/fgcolor", BLITZ_NORM_FG_COLOR);
|
||||
defaults[WM_NORM_BORDER_COLOR] = wmii_create_ixpfile(ixps, "/default/normstyle/fgcolor", BLITZ_NORM_BORDER_COLOR);
|
||||
defaults[WM_FONT] = wmii_create_ixpfile(ixps, "/default/font", BLITZ_FONT);
|
||||
defaults[WM_FONT]->after_write = handle_after_write;
|
||||
defaults[WM_PAGE_SIZE] = wmii_create_ixpfile(ixps, "/default/pagesize", "0,0,east,south-16");
|
||||
defaults[WM_SNAP_VALUE] = wmii_create_ixpfile(ixps, "/default/snapvalue", "20"); /* 0..1000 */
|
||||
defaults[WM_BORDER] = wmii_create_ixpfile(ixps, "/default/border", "1");
|
||||
defaults[WM_TAB] = wmii_create_ixpfile(ixps, "/default/tab", "1");
|
||||
defaults[WM_HANDLE_INC] = wmii_create_ixpfile(ixps, "/default/handleinc", "1");
|
||||
defaults[WM_LOCKED] = wmii_create_ixpfile(ixps, "/default/locked", "1");
|
||||
defaults[WM_LAYOUT] = wmii_create_ixpfile(ixps, "/default/layout", LAYOUT);
|
||||
defaults[WM_SEL_PAGE] = ixp_create(ixps, "/page/sel");
|
||||
defaults[WM_EVENT_PAGE_UPDATE] = ixp_create(ixps, "/default/event/pageupdate");
|
||||
defaults[WM_EVENT_CLIENT_UPDATE] = ixp_create(ixps, "/default/event/clientupdate");
|
||||
defaults[WM_EVENT_B1PRESS] = ixp_create(ixps, "/defaults/event/b1press");
|
||||
defaults[WM_EVENT_B2PRESS] = ixp_create(ixps, "/defaults/event/b2press");
|
||||
defaults[WM_EVENT_B3PRESS] = ixp_create(ixps, "/defaults/event/b3press");
|
||||
defaults[WM_EVENT_B4PRESS] = ixp_create(ixps, "/defaults/event/b4press");
|
||||
defaults[WM_EVENT_B5PRESS] = ixp_create(ixps, "/defaults/event/b5press");
|
||||
}
|
||||
|
||||
static void
|
||||
init_screen()
|
||||
{
|
||||
XGCValues gcv;
|
||||
XSetWindowAttributes wa;
|
||||
|
||||
XAllocNamedColor(dpy, DefaultColormap(dpy, screen_num),
|
||||
defaults[WM_TRANS_COLOR]->content,
|
||||
&xorcolor, &xorcolor);
|
||||
gcv.subwindow_mode = IncludeInferiors;
|
||||
gcv.function = GXxor;
|
||||
gcv.foreground = xorcolor.pixel;
|
||||
gcv.line_width = 4;
|
||||
gcv.plane_mask = AllPlanes;
|
||||
gcv.graphics_exposures = False;
|
||||
xorgc = XCreateGC(dpy, root, GCForeground | GCGraphicsExposures
|
||||
| GCFunction | GCSubwindowMode | GCLineWidth
|
||||
| GCPlaneMask, &gcv);
|
||||
rect.x = rect.y = 0;
|
||||
rect.width = DisplayWidth(dpy, screen_num);
|
||||
rect.height = DisplayHeight(dpy, screen_num);
|
||||
|
||||
wa.override_redirect = 1;
|
||||
wa.background_pixmap = ParentRelative;
|
||||
wa.event_mask = ExposureMask | ButtonPressMask | PointerMotionMask
|
||||
| SubstructureRedirectMask | SubstructureNotifyMask;
|
||||
transient = XCreateWindow(dpy, root, 0, 0, rect.width, rect.height,
|
||||
0, DefaultDepth(dpy, screen_num),
|
||||
CopyFromParent, DefaultVisual(dpy,
|
||||
screen_num),
|
||||
CWOverrideRedirect | CWBackPixmap |
|
||||
CWEventMask, &wa);
|
||||
|
||||
XSync(dpy, False);
|
||||
transient_gc = XCreateGC(dpy, transient, 0, 0);
|
||||
XDefineCursor(dpy, transient, normal_cursor);
|
||||
XDefineCursor(dpy, root, normal_cursor);
|
||||
XSelectInput(dpy, root, ROOT_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
* There's no way to check accesses to destroyed windows, thus
|
||||
* those cases are ignored (especially on UnmapNotify's).
|
||||
* Other types of errors call Xlib's default error handler, which
|
||||
* calls exit().
|
||||
*/
|
||||
static int
|
||||
wmii_error_handler(Display * dpy, XErrorEvent * error)
|
||||
{
|
||||
if (error->error_code == BadWindow
|
||||
|| (error->request_code == X_SetInputFocus
|
||||
&& error->error_code == BadMatch)
|
||||
|| (error->request_code == X_PolyText8
|
||||
&& error->error_code == BadDrawable)
|
||||
|| (error->request_code == X_PolyFillRectangle
|
||||
&& error->error_code == BadDrawable)
|
||||
|| (error->request_code == X_PolySegment
|
||||
&& error->error_code == BadDrawable)
|
||||
|| (error->request_code == X_ConfigureWindow
|
||||
&& error->error_code == BadMatch))
|
||||
return 0;
|
||||
fprintf(stderr, "%s", "wmiiwm: fatal error");
|
||||
return x_error_handler(dpy, error); /* calls exit() */
|
||||
}
|
||||
|
||||
/*
|
||||
* Startup Error handler to check if another window manager
|
||||
* is already running.
|
||||
*/
|
||||
static int
|
||||
startup_error_handler(Display * dpy, XErrorEvent * error)
|
||||
{
|
||||
other_wm_running = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup()
|
||||
{
|
||||
int i;
|
||||
XWindowChanges wc;
|
||||
|
||||
for (i = 0; clients && clients[i]; i++) {
|
||||
Client *c = clients[i];
|
||||
Frame *f = c->frame;
|
||||
if (f) {
|
||||
gravitate(c, tab_height(f), border_width(f), 1);
|
||||
XReparentWindow(dpy, c->win, root,
|
||||
rect_of_frame(f)->x + c->rect.x,
|
||||
rect_of_frame(f)->y + c->rect.y);
|
||||
wc.border_width = c->border;
|
||||
XConfigureWindow(dpy, c->win, CWBorderWidth, &wc);
|
||||
}
|
||||
}
|
||||
XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
|
||||
}
|
||||
|
||||
static void
|
||||
run()
|
||||
{
|
||||
/* init */
|
||||
init_event_hander();
|
||||
|
||||
if (!(defaults[WM_CTL] = ixp_create(ixps, "/ctl"))) {
|
||||
perror("wmiiwm: cannot connect IXP server");
|
||||
exit(1);
|
||||
}
|
||||
defaults[WM_CTL]->after_write = handle_after_write;
|
||||
|
||||
clients = 0;
|
||||
frames = 0;
|
||||
detached = 0;
|
||||
pages = 0;
|
||||
layouts = 0;
|
||||
sel = 0;
|
||||
sel_client = 0;
|
||||
|
||||
init_atoms();
|
||||
init_cursors();
|
||||
init_defaults();
|
||||
font = blitz_getfont(dpy, defaults[WM_FONT]->content);
|
||||
init_lock_modifiers(dpy, &valid_mask, &num_lock_mask);
|
||||
init_screen();
|
||||
init_layouts();
|
||||
scan_wins();
|
||||
|
||||
/* main event loop */
|
||||
run_server_with_fd_support(ixps, ConnectionNumber(dpy), check_event,
|
||||
0);
|
||||
cleanup();
|
||||
deinit_server(ixps);
|
||||
XCloseDisplay(dpy);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
int checkwm = 0;
|
||||
char *sockfile = 0;
|
||||
|
||||
/* command line args */
|
||||
if (argc > 1) {
|
||||
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
|
||||
switch (argv[i][1]) {
|
||||
case 'v':
|
||||
fprintf(stdout, "%s", version[0]);
|
||||
exit(0);
|
||||
break;
|
||||
case 'c':
|
||||
checkwm = 1;
|
||||
break;
|
||||
case 's':
|
||||
if (i + 1 < argc)
|
||||
sockfile = argv[++i];
|
||||
else
|
||||
usage();
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
dpy = XOpenDisplay(0);
|
||||
if (!dpy) {
|
||||
fprintf(stderr, "%s", "wmiiwm: cannot open display\n");
|
||||
exit(1);
|
||||
}
|
||||
screen_num = DefaultScreen(dpy);
|
||||
root = RootWindow(dpy, screen_num);
|
||||
|
||||
/* check if another WM is already running */
|
||||
other_wm_running = 0;
|
||||
XSetErrorHandler(startup_error_handler);
|
||||
/* this causes an error if some other WM is running */
|
||||
XSelectInput(dpy, root, ROOT_MASK);
|
||||
XSync(dpy, False);
|
||||
if (other_wm_running) {
|
||||
fprintf(stderr,
|
||||
"wmiiwm: another window manager is already running\n");
|
||||
exit(1);
|
||||
}
|
||||
if (checkwm) {
|
||||
XCloseDisplay(dpy);
|
||||
exit(0);
|
||||
}
|
||||
XSetErrorHandler(0);
|
||||
x_error_handler = XSetErrorHandler(wmii_error_handler);
|
||||
|
||||
ixps = wmii_setup_server(sockfile);
|
||||
|
||||
run();
|
||||
|
||||
return 0;
|
||||
}
|
291
cmd/wm/wm.h
Normal file
291
cmd/wm/wm.h
Normal file
@ -0,0 +1,291 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
#include "cext.h"
|
||||
#include "wmii.h"
|
||||
|
||||
/* array indexes of page file pointers */
|
||||
enum {
|
||||
P_PREFIX,
|
||||
P_AREA_PREFIX,
|
||||
P_SEL_AREA,
|
||||
P_CTL,
|
||||
P_MODE,
|
||||
P_AUTO_DESTROY,
|
||||
P_LAST
|
||||
};
|
||||
|
||||
/* array indexes of area file pointers */
|
||||
enum {
|
||||
A_PREFIX,
|
||||
A_SEL_FRAME,
|
||||
A_CTL,
|
||||
A_SIZE,
|
||||
A_LAYOUT,
|
||||
A_LAST
|
||||
};
|
||||
|
||||
/* array indexes of frame file pointers */
|
||||
enum {
|
||||
F_PREFIX,
|
||||
F_CLIENT_PREFIX,
|
||||
F_SEL_CLIENT,
|
||||
F_CTL,
|
||||
F_SIZE,
|
||||
F_BORDER,
|
||||
F_TAB,
|
||||
F_HANDLE_INC,
|
||||
F_LOCKED,
|
||||
F_SEL_BG_COLOR,
|
||||
F_SEL_FG_COLOR,
|
||||
F_SEL_BORDER_COLOR,
|
||||
F_NORM_BG_COLOR,
|
||||
F_NORM_FG_COLOR,
|
||||
F_NORM_BORDER_COLOR,
|
||||
F_EVENT_B2PRESS,
|
||||
F_EVENT_B3PRESS,
|
||||
F_EVENT_B4PRESS,
|
||||
F_EVENT_B5PRESS,
|
||||
F_LAST
|
||||
};
|
||||
|
||||
/* array indexes of client file pointers */
|
||||
enum {
|
||||
C_PREFIX,
|
||||
C_NAME,
|
||||
C_CLASS,
|
||||
C_INSTANCE,
|
||||
C_LAST
|
||||
};
|
||||
|
||||
/* array indexes of wm file pointers */
|
||||
enum {
|
||||
WM_CTL,
|
||||
WM_DETACHED_FRAME,
|
||||
WM_DETACHED_CLIENT,
|
||||
WM_TRANS_COLOR,
|
||||
WM_SEL_BG_COLOR,
|
||||
WM_SEL_BORDER_COLOR,
|
||||
WM_SEL_FG_COLOR,
|
||||
WM_NORM_BG_COLOR,
|
||||
WM_NORM_BORDER_COLOR,
|
||||
WM_NORM_FG_COLOR,
|
||||
WM_FONT,
|
||||
WM_PAGE_SIZE,
|
||||
WM_BORDER,
|
||||
WM_TAB,
|
||||
WM_HANDLE_INC,
|
||||
WM_LOCKED,
|
||||
WM_SNAP_VALUE,
|
||||
WM_SEL_PAGE,
|
||||
WM_LAYOUT,
|
||||
WM_EVENT_PAGE_UPDATE,
|
||||
WM_EVENT_CLIENT_UPDATE,
|
||||
WM_EVENT_B1PRESS,
|
||||
WM_EVENT_B2PRESS,
|
||||
WM_EVENT_B3PRESS,
|
||||
WM_EVENT_B4PRESS,
|
||||
WM_EVENT_B5PRESS,
|
||||
WM_LAST
|
||||
};
|
||||
|
||||
#define PROTO_DEL 1
|
||||
#define BORDER_WIDTH 3
|
||||
#define LAYOUT "column"
|
||||
#define GAP 5
|
||||
|
||||
#define ROOT_MASK (SubstructureRedirectMask | SubstructureNotifyMask | ButtonPressMask | ButtonReleaseMask)
|
||||
#define CLIENT_MASK (SubstructureNotifyMask | PropertyChangeMask | EnterWindowMask)
|
||||
|
||||
typedef struct Page Page;
|
||||
typedef struct Layout Layout;
|
||||
typedef struct Area Area;
|
||||
typedef struct Frame Frame;
|
||||
typedef struct Client Client;
|
||||
|
||||
/* new layout interface:
|
||||
* /page/[1..n]/0/ floating space
|
||||
* /page/[1..n]/[1..n]/ layout space
|
||||
*/
|
||||
|
||||
struct Page {
|
||||
Area **areas;
|
||||
unsigned int sel;
|
||||
File *files[P_LAST];
|
||||
};
|
||||
|
||||
struct Layout {
|
||||
char *name;
|
||||
void (*init) (Area *); /* called when layout is initialized */
|
||||
void (*deinit) (Area *); /* called when layout is uninitialized */
|
||||
void (*arrange) (Area *); /* called when area is resized */
|
||||
void (*attach) (Area *, Client *); /* called on attach */
|
||||
void (*detach) (Area *, Client *, int, int); /* called on detach */
|
||||
void (*resize) (Frame *, XRectangle *, XPoint * pt); /* called after resize */
|
||||
};
|
||||
|
||||
struct Area {
|
||||
Layout *layout;
|
||||
Page *page;
|
||||
Frame **frames;
|
||||
unsigned int sel;
|
||||
XRectangle rect;
|
||||
void *aux; /* free pointer */
|
||||
File *files[A_LAST];
|
||||
};
|
||||
|
||||
struct Frame {
|
||||
Area *area;
|
||||
Window win;
|
||||
GC gc;
|
||||
XRectangle rect;
|
||||
Cursor cursor;
|
||||
Client **clients;
|
||||
int sel;
|
||||
void *aux; /* free pointer */
|
||||
File *files[F_LAST];
|
||||
};
|
||||
|
||||
struct Client {
|
||||
int proto;
|
||||
unsigned int border;
|
||||
Window win;
|
||||
Window trans;
|
||||
XRectangle rect;
|
||||
XSizeHints size;
|
||||
Frame *frame;
|
||||
File *files[C_LAST];
|
||||
};
|
||||
|
||||
#define SELFRAME(x) (x && x->areas[x->sel]->frames ? x->areas[x->sel]->frames[x->areas[x->sel]->sel] : 0)
|
||||
#define ISSELFRAME(x) (pages && SELFRAME(pages[sel]) == x)
|
||||
|
||||
/* global variables */
|
||||
Display *dpy;
|
||||
IXPServer *ixps;
|
||||
int screen_num;
|
||||
Window root;
|
||||
Window transient;
|
||||
XRectangle rect;
|
||||
Client **detached;
|
||||
Page **pages;
|
||||
unsigned int sel;
|
||||
Frame **frames;
|
||||
Client **clients;
|
||||
XFontStruct *font;
|
||||
XColor xorcolor;
|
||||
GC xorgc;
|
||||
GC transient_gc;
|
||||
Layout **layouts;
|
||||
|
||||
Atom wm_state;
|
||||
Atom wm_change_state;
|
||||
Atom wm_protocols;
|
||||
Atom wm_delete;
|
||||
Atom motif_wm_hints;
|
||||
Atom net_wm_desktop;
|
||||
|
||||
Cursor normal_cursor;
|
||||
Cursor resize_cursor;
|
||||
Cursor move_cursor;
|
||||
Cursor drag_cursor;
|
||||
Cursor w_cursor;
|
||||
Cursor e_cursor;
|
||||
Cursor n_cursor;
|
||||
Cursor s_cursor;
|
||||
Cursor nw_cursor;
|
||||
Cursor ne_cursor;
|
||||
Cursor sw_cursor;
|
||||
Cursor se_cursor;
|
||||
|
||||
/* default file pointers */
|
||||
File *defaults[WM_LAST];
|
||||
|
||||
unsigned int valid_mask, num_lock_mask;
|
||||
|
||||
/* area.c */
|
||||
void focus_frame(Frame * f, int raise, int up, int down);
|
||||
void destroy_area(Area *a);
|
||||
void free_area(Area *a);
|
||||
|
||||
|
||||
/* client.c */
|
||||
Client *alloc_client(Window w);
|
||||
void _init_client(Client * c, XWindowAttributes * wa);
|
||||
void free_client(Client * c);
|
||||
void configure_client(Client * c);
|
||||
void handle_client_property(Client * c, XPropertyEvent * e);
|
||||
void close_client(Client * c);
|
||||
void draw_client(Client * c);
|
||||
void draw_clients(Frame * f);
|
||||
void gravitate(Client * c, unsigned int tabh, unsigned int bw, int invert);
|
||||
int manage_class_instance(Client * c);
|
||||
void grab_client(Client * c, unsigned long mod, unsigned int button);
|
||||
void ungrab_client(Client * c, unsigned long mod, unsigned int button);
|
||||
void hide_client(Client * c);
|
||||
void show_client(Client * c);
|
||||
void reparent_client(Client * c, Window w, int x, int y);
|
||||
|
||||
/* core.c */
|
||||
unsigned int tab_height(Frame *f);
|
||||
unsigned int border_width(Frame *f);
|
||||
void invoke_core_event(File * f);
|
||||
void run_action(File * f, void *obj, Action * acttbl);
|
||||
void scan_wins();
|
||||
Client *win_to_client(Window w);
|
||||
int win_proto(Window w);
|
||||
int win_state(Window w);
|
||||
void handle_after_write(IXPServer * s, File * f);
|
||||
void focus_page(Page * p, int raise, int down);
|
||||
void detach(Frame * f, int client_destroyed);
|
||||
int comp_obj(void *f1, void *f2);
|
||||
void destroy_page(Page * p);
|
||||
void set_client_state(Client * c, int state);
|
||||
|
||||
/* frame.c */
|
||||
Frame *win_to_frame(Window w);
|
||||
Frame *alloc_frame(XRectangle * r, int add_frame_border, int floating);
|
||||
void free_frame(Frame * f);
|
||||
void resize_frame(Frame * f, XRectangle * r, XPoint * pt, int ignore_layout);
|
||||
void draw_frame(Frame * f);
|
||||
void handle_frame_buttonpress(XButtonEvent * e, Frame * f);
|
||||
void attach_client(Client * c);
|
||||
void attach_Cliento_frame(Frame * f, Client * c);
|
||||
void detach_client_from_frame(Client * c, int unmapped, int destroyed);
|
||||
void draw_tab(Frame * f, char *text, int x, int y, int w, int h, int sel);
|
||||
void focus_client(Client * c, int raise, int up);
|
||||
int is_managed_frame(Frame * f);
|
||||
XRectangle *rect_of_frame(Frame * f);
|
||||
|
||||
/* event.c */
|
||||
void init_event_hander();
|
||||
void check_event(Connection * c);
|
||||
|
||||
/* mouse.c */
|
||||
void mouse_resize(Frame * f, Align align);
|
||||
void mouse_move(Frame * f);
|
||||
Cursor cursor_for_motion(Frame * f, int x, int y);
|
||||
Align cursor_to_align(Cursor cursor);
|
||||
Align xy_to_align(XRectangle * rect, int x, int y);
|
||||
void drop_move(Frame * f, XRectangle * new, XPoint * pt);
|
||||
|
||||
/* page.c */
|
||||
Page *alloc_page(char *autodestroy);
|
||||
void free_page(Page * p);
|
||||
void focus_area(Area *a, int raise, int up, int down);
|
||||
XRectangle *rectangles(unsigned int *num);
|
||||
void hide_page(Page * p);
|
||||
void show_page(Page * p);
|
||||
void attach_Frameo_page(Page * p, Frame * f, int managed);
|
||||
void detach_frame_from_page(Frame * f, int ignore_focus_and_destroy);
|
||||
void draw_page(Page * p);
|
||||
Layout *get_layout(char *name);
|
||||
Frame *select_floating(Page * p, Frame * f, char *what);
|
||||
|
||||
/* layout.c */
|
||||
void init_layouts();
|
43
cmd/wm/wmii
Normal file
43
cmd/wm/wmii
Normal file
@ -0,0 +1,43 @@
|
||||
#!9PREFIX/bin/rc
|
||||
# window manager improved 2 wrapper
|
||||
|
||||
if(! ~ $#* 0) {
|
||||
exec wmiiwm $*
|
||||
}
|
||||
|
||||
if(! wmiiwm -c) {
|
||||
exit 1 # wmiiwm is already running or $DISPLAY is unset
|
||||
}
|
||||
|
||||
WMII_CONFDIR=CONFPREFIX/wmii-3
|
||||
OLD_PATH=$PATH
|
||||
PATH=$HOME/.wmii-3:$WMII_CONFDIR:9PREFIX/bin:$PATH
|
||||
WMII_IDENT=`{date -n}^'-'^$pid
|
||||
WMIR_SOCKET=/tmp/.ixp-$USER/wmifs-$WMII_IDENT
|
||||
|
||||
# start window manager and utilities:
|
||||
mkdir -p /tmp/.ixp-$USER
|
||||
wmiiwm -s /tmp/.ixp-$USER/wmiiwm-$WMII_IDENT &
|
||||
wmiiwmpid=$apid
|
||||
wmifs -s /tmp/.ixp-$USER/wmifs-$WMII_IDENT &
|
||||
wmikeys -s /tmp/.ixp-$USER/wmikeys-$WMII_IDENT &
|
||||
wmibar -s /tmp/.ixp-$USER/wmibar-$WMII_IDENT &
|
||||
wmimenu -s /tmp/.ixp-$USER/wmimenu-$WMII_IDENT &
|
||||
sleep 1
|
||||
|
||||
# mount ixp file systems:
|
||||
wmir write /ctl 'bind /wm /tmp/.ixp-'^$USER/wmiiwm-$WMII_IDENT
|
||||
wmir write /ctl 'bind /bar /tmp/.ixp-'^$USER/wmibar-$WMII_IDENT
|
||||
wmir write /ctl 'bind /menu /tmp/.ixp-'^$USER/wmimenu-$WMII_IDENT
|
||||
wmir write /ctl 'bind /keys /tmp/.ixp-'^$USER/wmikeys-$WMII_IDENT
|
||||
|
||||
# display the wmii introduction if necessary:
|
||||
if(mkdir $HOME/.wmii-3 >[2]/dev/null) {
|
||||
welcome &
|
||||
}
|
||||
|
||||
# run configuration:
|
||||
wmirc &
|
||||
|
||||
# wait for wmiiwm's termination:
|
||||
wait $wmiiwmpid
|
95
cmd/wm/wmii.1
Normal file
95
cmd/wm/wmii.1
Normal file
@ -0,0 +1,95 @@
|
||||
.TH WMII 1 wmii-3
|
||||
.SH NAME
|
||||
wmii \- window manager improved 2
|
||||
.SH DESCRIPTION
|
||||
.SS Overview
|
||||
.BR wmii (1)
|
||||
is a script that launches the
|
||||
.B wmii
|
||||
window manager and its various utilities and takes care that they are
|
||||
configured for use.
|
||||
.SS Actions
|
||||
An action is a script written for the rc shell, but it can actually be
|
||||
any executable file. It is executed usually by selecting it from the
|
||||
actions menu.
|
||||
You can customize an action by copying it from the global action
|
||||
directory CONFPREFIX/wmii-3 to $HOME/.wmii-3 and then editing the copy to
|
||||
fit your needs. Of course you can also create your own actions there; make
|
||||
sure that they are executable.
|
||||
.P
|
||||
Here is a list of the default actions:
|
||||
.TP 2
|
||||
extern <cmd>
|
||||
clean the environment and execute the given command
|
||||
.TP 2
|
||||
kmode <mode>
|
||||
activate shortcuts of the given mode
|
||||
.TP 2
|
||||
quit
|
||||
quit wmii
|
||||
.TP 2
|
||||
status
|
||||
periodically print date and load average to the bar
|
||||
.TP 2
|
||||
welcome
|
||||
display a welcome message that contains the wmii tutorial
|
||||
.TP 2
|
||||
wmirc
|
||||
configure wmii
|
||||
.SS Configuration
|
||||
If you feel the need to change the default configuration, then customize (as
|
||||
described above) the
|
||||
.B wmirc
|
||||
action. This action is executed at the end of the
|
||||
.BR wmii (1)
|
||||
script and does all the work of setting up the window manager, the key
|
||||
bindings, the bar labels, etc.
|
||||
.SH FILES
|
||||
.TP
|
||||
/tmp/.ixp-$USER
|
||||
Directory where the socket files are stored.
|
||||
.TP
|
||||
CONFPREFIX/wmii-3
|
||||
Global action directory.
|
||||
.TP
|
||||
$HOME/.wmii-3
|
||||
User-specific action directory. Actions are first searched here.
|
||||
.SH ENVIRONMENT
|
||||
.TP
|
||||
HOME, USER
|
||||
See the section
|
||||
.B FILES
|
||||
above.
|
||||
.P
|
||||
The following variables are set and exported within
|
||||
.BR wmii (1)
|
||||
and thus can be used in actions.
|
||||
.TP
|
||||
OLD_PATH
|
||||
PATH as it was before
|
||||
.BR wmii (1)
|
||||
added the local and global action directory and the location where the 9base
|
||||
tools reside. The
|
||||
.B wmire
|
||||
action resets PATH and removes OLD_PATH before executing a command.
|
||||
.TP
|
||||
WMII_CONFDIR
|
||||
Global action directory.
|
||||
.TP
|
||||
WMII_IDENT
|
||||
Unique identifier for the
|
||||
.BR wmii (1)
|
||||
session. Part of the socket file names.
|
||||
.TP
|
||||
WMIR_SOCKET
|
||||
Socket file of
|
||||
.BR wmifs (1).
|
||||
Used by
|
||||
.BR wmir (1).
|
||||
.SH SEE ALSO
|
||||
.BR wmibar (1),
|
||||
.BR wmifs (1),
|
||||
.BR wmiiwm (1),
|
||||
.BR wmikeys (1),
|
||||
.BR wmimenu (1),
|
||||
.BR wmir (1)
|
171
cmd/wm/wmiiwm.1
Normal file
171
cmd/wm/wmiiwm.1
Normal file
@ -0,0 +1,171 @@
|
||||
.de FN
|
||||
\fI\|\\$1\|\fP\\$2
|
||||
..
|
||||
.TH wmii 1
|
||||
.SH NAME
|
||||
wmii \- window manager improved 2 for X11
|
||||
|
||||
.SH DESCRIPTION
|
||||
.B wmii
|
||||
is a dynamic window manager for the X Window System.
|
||||
It provides a synthesis of conventional, tiled and tabbed window
|
||||
management based on dynamic layouts.
|
||||
Several roots of these window management capabilities have been
|
||||
introduced by the Ion and LarsWM window managers.
|
||||
Apart from this, it implements a socket-based fileserver,
|
||||
which is accessed to configure and interoperate with wmii. The idea
|
||||
behind this file-based approach is derived from the plan9
|
||||
operating system and can be found in the Acme programming environment.
|
||||
.B wmii
|
||||
consists of the core window manager itself and several utilities, such
|
||||
as
|
||||
.BR wmibar (1),
|
||||
.BR wmifs (1),
|
||||
.BR wmimenu (1),
|
||||
.BR wmikeys (1),
|
||||
.BR wmiplumb (1),
|
||||
.BR wmir (1)
|
||||
and
|
||||
.BR wmiwarp (1).
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B wmii
|
||||
.RB [ \-s
|
||||
.IR socketfile ]
|
||||
.br
|
||||
.B wmii
|
||||
.RB \-v
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BI \-s " socketfile"
|
||||
lets you override the default socketfile which
|
||||
.B wmii
|
||||
should use for connecting fileserver clients.
|
||||
.TP
|
||||
.B \-v
|
||||
prints version information to stderr, then exits.
|
||||
|
||||
.SH FILES
|
||||
.TP
|
||||
.FN /tmp/.ixp-$USER/wmii\-$WMII_IDENT
|
||||
this file is the default socket file used by
|
||||
.B wmii.
|
||||
.TP
|
||||
.FN $HOME/.wmii/rc [start|stop]
|
||||
this file is executed when
|
||||
.B wmii
|
||||
starts up or shuts down. If it is not present,
|
||||
.B wmii
|
||||
executes
|
||||
.FN $WMII_CONFDIR/rc [start|stop]
|
||||
as fallback.
|
||||
The rc script is used to customize and setup
|
||||
.B wmii
|
||||
beside its components to match the users needs.
|
||||
It can be a simple shell script, a native binary or whatever executable.
|
||||
The only condition is, that it understands the command line arguments
|
||||
.IR start
|
||||
, which is provided while startup and
|
||||
.IR stop
|
||||
, which is provided on shutdown.
|
||||
For details about the shipped default rc configuration system, see
|
||||
.BR wmii.rc (5).
|
||||
|
||||
.SH STRUCTURE
|
||||
The structure of
|
||||
.B wmii
|
||||
consists of following objects which are described in more detail.
|
||||
.TP
|
||||
.B Display
|
||||
The display is a running X server which consists of at least one
|
||||
.B Screen (Monitor),
|
||||
.B Keyboard,
|
||||
and the
|
||||
.B Mouse.
|
||||
Applications with X11 support can connect to such X server display in
|
||||
order to be used.
|
||||
.TP
|
||||
.B Screen (Monitor)
|
||||
A screen is the physical device which displays a part or the whole
|
||||
display driven by the X server. Each screen consists of a root window
|
||||
which matches the physical conditions the X server is configured to
|
||||
drive the graphics adaptor and the connected screen, such as color
|
||||
depth and resolution.
|
||||
.TP
|
||||
.B (Root) Window
|
||||
A window is a rectangular area which is drawn by the X server. A root
|
||||
window is the complete drawable area of a screen. Root windows are
|
||||
top-level windows which are always at the most-background position. If
|
||||
you have set a wallpaper, the root window displays the wallpaper.
|
||||
All other windows are children of the root window. Thus, windows of X
|
||||
clients, frames surrounding them and bars are all windows.
|
||||
.TP
|
||||
.B Client
|
||||
A client is the window provided by X applications (clients) without the
|
||||
surrounding frame. There're X clients supported, such as xmms, which
|
||||
request not to be surrounded by a frame, those are called borderless.
|
||||
.TP
|
||||
.B Frame
|
||||
A frame is a parent window of a client or of nested frames in a layout.
|
||||
Mostly a frame consists of a border and a titlebar. The titlebar
|
||||
provides tabs, if the frame contains nested frames, otherwise it shows the title
|
||||
of the surrounded client.
|
||||
.TP
|
||||
.B Layout
|
||||
A layout defines how to arrange nested frames of a frame. See
|
||||
CUSTOMIZATION section for further details about how such definitions look
|
||||
like.
|
||||
.TP
|
||||
.B Page
|
||||
A page is a container of the size of the root window which contains
|
||||
nested frames. It can be compared to workspaces in other window
|
||||
managers, but with the exception, that a page behaves very similiar to a
|
||||
frame, except that it reuses the root window as frame window.
|
||||
.TP
|
||||
.B Action
|
||||
An action is an internal interface command or an external process call
|
||||
in order to reach a specific window management result, ie resizing a
|
||||
frame or launching a terminal.
|
||||
|
||||
.SH CUSTOMIZATION
|
||||
.B wmii
|
||||
is customized through the rc script, which manipulates the namespace its
|
||||
fileserver provides to other processes. This namespace can be accessed
|
||||
using the
|
||||
.BR wmir (1)
|
||||
utility. The default rc script uses this utility to setup wmii and its
|
||||
components.
|
||||
.P
|
||||
A namespace is a filesystem structure consisting of files
|
||||
and directories, which is specified by its underlying fileserver
|
||||
process, like
|
||||
.B wmii.
|
||||
Such
|
||||
namespaces can be bound (mounted) to userdefined namespaces using the
|
||||
.BR wmifs (1)
|
||||
utility. In the default rc script the namespace provided by wmii, is
|
||||
bound to
|
||||
.FN /wmii
|
||||
.P
|
||||
There are four actions provided by the
|
||||
.BR wmir (1)
|
||||
utility to manipulate your namespace:
|
||||
.BR create ,
|
||||
.BR remove ,
|
||||
.BR write ,
|
||||
and
|
||||
.BR read .
|
||||
.P
|
||||
|
||||
.SH AUTHOR
|
||||
Copyright \(co 2003 - 2005 by Anselm R. Garbe <garbeam (at) gmail (dot) com>
|
||||
.SH SEE ALSO
|
||||
.BR wmibar (1),
|
||||
.BR wmifs (1),
|
||||
.BR wmimenu (1),
|
||||
.BR wmikeys (1),
|
||||
.BR wmiplumb (1),
|
||||
.BR wmir (1),
|
||||
.BR wmiwarp (1),
|
||||
.BR wmii.rc (5).
|
332
cmd/wm/wmiiwm.h.O
Normal file
332
cmd/wm/wmiiwm.h.O
Normal file
@ -0,0 +1,332 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
#include "cext.h"
|
||||
#include "wmii.h"
|
||||
|
||||
/* array indexes of page file pointers */
|
||||
typedef enum {
|
||||
P_PREFIX,
|
||||
P_FLOATING_PREFIX,
|
||||
P_FLOATING_SELECTED,
|
||||
P_FLOATING_LAYOUT,
|
||||
P_MANAGED_PREFIX,
|
||||
P_MANAGED_SELECTED,
|
||||
P_MANAGED_LAYOUT,
|
||||
P_MANAGED_SIZE,
|
||||
P_CTL,
|
||||
P_NAME,
|
||||
P_MODE,
|
||||
P_AUTO_DESTROY,
|
||||
P_LAST
|
||||
} PageIndexes;
|
||||
|
||||
/* array indexes of frame file pointers */
|
||||
typedef enum {
|
||||
F_PREFIX,
|
||||
F_NAME,
|
||||
F_CLIENT_PREFIX,
|
||||
F_CLIENT_SELECTED,
|
||||
F_CTL,
|
||||
F_SIZE,
|
||||
F_BORDER_W,
|
||||
F_TAB_H,
|
||||
F_HANDLE_INC,
|
||||
F_LOCKED,
|
||||
F_SELECTED_BG_COLOR,
|
||||
F_SELECTED_TEXT_FONT,
|
||||
F_SELECTED_FG_COLOR,
|
||||
F_SELECTED_BORDER_COLOR,
|
||||
F_NORMAL_BG_COLOR,
|
||||
F_NORMAL_TEXT_FONT,
|
||||
F_NORMAL_FG_COLOR,
|
||||
F_NORMAL_BORDER_COLOR,
|
||||
F_EVENT_B2PRESS,
|
||||
F_EVENT_B3PRESS,
|
||||
F_EVENT_B4PRESS,
|
||||
F_EVENT_B5PRESS,
|
||||
F_LAST
|
||||
} FrameIndexes;
|
||||
|
||||
/* array indexes of client file pointers */
|
||||
typedef enum {
|
||||
C_PREFIX,
|
||||
C_ID,
|
||||
C_NAME,
|
||||
C_CLASS,
|
||||
C_INSTANCE,
|
||||
C_LAST
|
||||
} ClientIndexes;
|
||||
|
||||
/* array indexes of core file pointers */
|
||||
typedef enum {
|
||||
CORE_CTL,
|
||||
CORE_DETACHED_FRAME,
|
||||
CORE_DETACHED_CLIENT,
|
||||
CORE_TRANS_COLOR,
|
||||
CORE_PAGER_SEL_BG_COLOR,
|
||||
CORE_PAGER_NORM_BG_COLOR,
|
||||
CORE_PAGER_SEL_BORDER_COLOR,
|
||||
CORE_PAGER_NORM_BORDER_COLOR,
|
||||
CORE_PAGER_SEL_TEXT_COLOR,
|
||||
CORE_PAGER_SEL_TEXT_FONT,
|
||||
CORE_PAGER_NORM_TEXT_COLOR,
|
||||
CORE_PAGER_NORM_TEXT_FONT,
|
||||
CORE_PAGER_SEL_CLIENT_COLOR,
|
||||
CORE_PAGER_NORM_CLIENT_COLOR,
|
||||
CORE_PAGER_SEL_CTEXT_COLOR,
|
||||
CORE_PAGER_SEL_CTEXT_FONT,
|
||||
CORE_PAGER_NORM_CTEXT_COLOR,
|
||||
CORE_PAGER_NORM_CTEXT_FONT,
|
||||
CORE_PAGER_SEL_CBORDER_COLOR,
|
||||
CORE_PAGER_NORM_CBORDER_COLOR,
|
||||
CORE_PAGE_MANAGED_SIZE,
|
||||
CORE_SNAP_VALUE,
|
||||
CORE_PAGE_SELECTED,
|
||||
CORE_PAGE_LAYOUT,
|
||||
CORE_PAGE_TILE_WIDTH,
|
||||
CORE_EVENT_PAGE_UPDATE,
|
||||
CORE_EVENT_CLIENT_UPDATE,
|
||||
CORE_LAST
|
||||
} CoreIndexes;
|
||||
|
||||
#define PROTO_DEL 1
|
||||
#define DEFAULT_BORDER_W "3"
|
||||
#define DEFAULT_TAB_H "16"
|
||||
#define DEFAULT_LAYOUT "tiled"
|
||||
#define GAP 5
|
||||
|
||||
#define ROOT_MASK (SubstructureRedirectMask | SubstructureNotifyMask | ButtonPressMask | ButtonReleaseMask)
|
||||
#define CLIENT_MASK (SubstructureNotifyMask | PropertyChangeMask | EnterWindowMask)
|
||||
|
||||
typedef struct Layout Layout;
|
||||
typedef struct LayoutImpl LayoutImpl;
|
||||
typedef struct Frame Frame;
|
||||
typedef struct Client Client;
|
||||
typedef struct Page Page;
|
||||
|
||||
/* new layout interface:
|
||||
* /page/[1..n]/0/ floating space
|
||||
* /page/[1..n]/[1..n]/ layout space
|
||||
*
|
||||
* CORE:
|
||||
* Page **pages;
|
||||
* Client **clients; -- for event lookup
|
||||
* Frames **frames; -- for event lookup
|
||||
*
|
||||
* PAGE:
|
||||
* int sel_layout;
|
||||
* Layout **layout; -- layout[0] == floating
|
||||
*
|
||||
* LAYOUT:
|
||||
* Frame **stack;
|
||||
* Frame **frames;
|
||||
*
|
||||
* FRAME:
|
||||
* Client **stack;
|
||||
* Client **clients;
|
||||
*
|
||||
* CLIENT:
|
||||
* Frame *frame;
|
||||
*
|
||||
*/
|
||||
|
||||
struct LayoutImpl {
|
||||
char *name;
|
||||
void (*init) (Page *, int argc, char **argv); /* called on new layout */
|
||||
void (*deinit) (Page *); /* called when layout gets
|
||||
* released */
|
||||
void (*arrange) (Page *); /* called when area is
|
||||
* resized */
|
||||
void (*attach) (Page *, Client *); /* called on attach */
|
||||
void (*detach) (Page *, Client *, int, int); /* called on detach */
|
||||
void (*resize) (Frame *, XRectangle *, XPoint * pt); /* called after resize */
|
||||
void (*aux) (Frame *, char *); /* auxillary extension
|
||||
* for layout specific
|
||||
* stuff */
|
||||
};
|
||||
|
||||
struct Layout {
|
||||
char *name;
|
||||
void (*init) (Page *); /* called on new layout */
|
||||
void (*deinit) (Page *); /* called when layout gets
|
||||
* released */
|
||||
void (*arrange) (Page *); /* called when area is
|
||||
* resized */
|
||||
void (*manage) (Frame *); /* called on attach */
|
||||
void (*unmanage) (Frame *); /* called on detach */
|
||||
void (*resize) (Frame *, XRectangle *, XPoint * pt);
|
||||
/* called after resize/move */
|
||||
Frame *(*select) (Frame *, char *); /* called to select a
|
||||
* frame */
|
||||
};
|
||||
|
||||
struct Page {
|
||||
Client **clients;
|
||||
Frame **floating;
|
||||
Frame **managed;
|
||||
Frame **floating_stack;
|
||||
Frame **managed_stack;
|
||||
XRectangle managed_rect;
|
||||
File *files[P_LAST];
|
||||
Layout *layout;
|
||||
void *aux; /* free pointer for layout backends */
|
||||
};
|
||||
|
||||
struct Frame {
|
||||
int id;
|
||||
int floating;
|
||||
Window win;
|
||||
GC gc;
|
||||
XRectangle managed_rect;
|
||||
XRectangle floating_rect;
|
||||
XRectangle rect;
|
||||
Cursor cursor;
|
||||
Client **clients;
|
||||
int sel;
|
||||
File *files[F_LAST];
|
||||
Page *page;
|
||||
Page *before_max;
|
||||
void *aux; /* free pointer for layout backends */
|
||||
};
|
||||
|
||||
struct Client {
|
||||
int id;
|
||||
int proto;
|
||||
unsigned int border;
|
||||
Window win;
|
||||
Window trans;
|
||||
XRectangle rect;
|
||||
XSizeHints size;
|
||||
Frame *frame;
|
||||
File *files[C_LAST];
|
||||
};
|
||||
|
||||
/* global variables */
|
||||
Display *dpy;
|
||||
IXPServer *ixps;
|
||||
int screen_num;
|
||||
Window root;
|
||||
Window transient;
|
||||
XRectangle rect;
|
||||
Client **detached;
|
||||
Page **pages;
|
||||
int sel_page;
|
||||
Frame **frames;
|
||||
Client **clients;
|
||||
XColor xorcolor;
|
||||
GC xorgc;
|
||||
GC transient_gc;
|
||||
Client *sel;
|
||||
Layout **layouts;
|
||||
|
||||
Atom wm_state;
|
||||
Atom wm_change_state;
|
||||
Atom wm_protocols;
|
||||
Atom wm_delete;
|
||||
Atom motif_wm_hints;
|
||||
Atom net_wm_desktop;
|
||||
|
||||
Cursor normal_cursor;
|
||||
Cursor resize_cursor;
|
||||
Cursor move_cursor;
|
||||
Cursor drag_cursor;
|
||||
Cursor w_cursor;
|
||||
Cursor e_cursor;
|
||||
Cursor n_cursor;
|
||||
Cursor s_cursor;
|
||||
Cursor nw_cursor;
|
||||
Cursor ne_cursor;
|
||||
Cursor sw_cursor;
|
||||
Cursor se_cursor;
|
||||
|
||||
/* default file pointers */
|
||||
File *defaults[F_LAST];
|
||||
File *core_files[CORE_LAST];
|
||||
|
||||
unsigned int valid_mask, num_lock_mask;
|
||||
|
||||
/* client.c */
|
||||
Client *alloc_client(Window w);
|
||||
void _init_client(Client * c, XWindowAttributes * wa);
|
||||
void free_client(Client * c);
|
||||
void configure_client(Client * c);
|
||||
void handle_client_property(Client * c, XPropertyEvent * e);
|
||||
void close_client(Client * c);
|
||||
void draw_client(Client * c);
|
||||
void draw_clients(Frame * f);
|
||||
void gravitate(Client * c, unsigned int tabh, unsigned int bw, int invert);
|
||||
int manage_class_instance(Client * c);
|
||||
void grab_client(Client * c, unsigned long mod, unsigned int button);
|
||||
void ungrab_client(Client * c, unsigned long mod, unsigned int button);
|
||||
void hide_client(Client * c);
|
||||
void show_client(Client * c);
|
||||
void reparent_client(Client * c, Window w, int x, int y);
|
||||
|
||||
/* core.c */
|
||||
void invoke_core_event(File * f);
|
||||
void run_action(File * f, void *obj, Action * acttbl);
|
||||
void scan_wins();
|
||||
Client *win_to_client(Window w);
|
||||
Frame *win_to_frame(Window w);
|
||||
int win_proto(Window w);
|
||||
int win_state(Window w);
|
||||
void handle_after_write(IXPServer * s, File * f);
|
||||
void focus_page(Page * p, int raise, int down);
|
||||
void detach(Frame * f, int client_destroyed);
|
||||
int comp_obj(void *f1, void *f2);
|
||||
void destroy_page(Page * p);
|
||||
void set_client_state(Client * c, int state);
|
||||
|
||||
/* frame.c */
|
||||
Frame *alloc_frame(XRectangle * r, int add_frame_border, int floating);
|
||||
void free_frame(Frame * f);
|
||||
void
|
||||
resize_frame(Frame * f, XRectangle * r, XPoint * pt,
|
||||
int ignore_layout);
|
||||
void draw_frame(Frame * f);
|
||||
void handle_frame_buttonpress(XButtonEvent * e, Frame * f);
|
||||
void attach_client(Client * c);
|
||||
void attach_Cliento_frame(Frame * f, Client * c);
|
||||
void detach_client_from_frame(Client * c, int unmapped, int destroyed);
|
||||
void draw_tab(Frame * f, char *text, int x, int y, int w, int h, int sel);
|
||||
void focus_client(Client * c, int raise, int up);
|
||||
int is_managed_frame(Frame * f);
|
||||
XRectangle *rect_of_frame(Frame * f);
|
||||
|
||||
/* event.c */
|
||||
void init_event_hander();
|
||||
void check_event(Connection * c);
|
||||
|
||||
/* mouse.c */
|
||||
void mouse_resize(Frame * f, Align align);
|
||||
void mouse_move(Frame * f);
|
||||
Cursor cursor_for_motion(Frame * f, int x, int y);
|
||||
Align cursor_to_align(Cursor cursor);
|
||||
Align xy_to_align(XRectangle * rect, int x, int y);
|
||||
void drop_move(Frame * f, XRectangle * new, XPoint * pt);
|
||||
|
||||
/* page.c */
|
||||
Page *alloc_page(char *autodestroy);
|
||||
void free_page(Page * p);
|
||||
Frame *get_selected(Page * p);
|
||||
int is_selected(Frame * f);
|
||||
XRectangle *rectangles(unsigned int *num);
|
||||
void hide_page(Page * p);
|
||||
void show_page(Page * p);
|
||||
void attach_Frameo_page(Page * p, Frame * f, int managed);
|
||||
void detach_frame_from_page(Frame * f, int ignore_focus_and_destroy);
|
||||
void draw_page(Page * p);
|
||||
void focus_frame(Frame * f, int raise, int up, int down);
|
||||
Layout *get_layout(char *name);
|
||||
int is_managed_mode(Page * p);
|
||||
void toggle_frame(Frame * f);
|
||||
Frame *select_floating(Page * p, Frame * f, char *what);
|
||||
|
||||
/* layout.c */
|
||||
void init_layouts();
|
43
cmd/wmibar.1
Normal file
43
cmd/wmibar.1
Normal file
@ -0,0 +1,43 @@
|
||||
.TH WMIBAR 1 wmii-3
|
||||
.SH NAME
|
||||
wmibar \- window manager improved 2 bar
|
||||
.SH SYNOPSIS
|
||||
.B wmibar
|
||||
.B \-s
|
||||
.I socketfile
|
||||
.SH DESCRIPTION
|
||||
.SS Overview
|
||||
.B wmibar
|
||||
is a generic and highly customizable bar for the X Window System,
|
||||
originally designed for
|
||||
.BR wmii (1).
|
||||
It supports arbitrary sized labels with arbitrary styles on a per label
|
||||
basis and with button click events.
|
||||
Like wmii,
|
||||
.B wmibar
|
||||
also implements a socket-based fileserver, which is accessed to configure and
|
||||
interoperate with other components.
|
||||
.SS Options
|
||||
.TP
|
||||
.BI \-s " socketfile"
|
||||
specifies the socketfile that
|
||||
.B wmibar
|
||||
should create.
|
||||
.TP
|
||||
.B \-v
|
||||
prints version information to stderr, then exits.
|
||||
.SS Customization
|
||||
.B wmibar
|
||||
is customized through manipulating its filesystem namespace.
|
||||
In the default setup of
|
||||
.BR wmii (1)
|
||||
the namespace of
|
||||
.B wmibar
|
||||
can be found in /bar.
|
||||
.SH SEE ALSO
|
||||
.BR wmifs (1),
|
||||
.BR wmii (1),
|
||||
.BR wmiiwm (1),
|
||||
.BR wmikeys (1),
|
||||
.BR wmimenu (1),
|
||||
.BR wmir (1)
|
550
cmd/wmibar.c
Normal file
550
cmd/wmibar.c
Normal file
@ -0,0 +1,550 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/cursorfont.h>
|
||||
#include <X11/Xproto.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
#include "wmii.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
/* array indexes for file pointers */
|
||||
typedef enum {
|
||||
B_CTL,
|
||||
B_NEW,
|
||||
B_EXPANDABLE,
|
||||
B_GEOMETRY,
|
||||
B_FONT,
|
||||
B_FG_COLOR,
|
||||
B_BORDER_COLOR,
|
||||
B_BG_COLOR,
|
||||
B_LAST
|
||||
} BarIndexes;
|
||||
|
||||
typedef struct {
|
||||
File *root;
|
||||
Draw d;
|
||||
} Item;
|
||||
|
||||
static IXPServer *ixps = 0;
|
||||
static Display *dpy;
|
||||
static GC gc;
|
||||
static Window win;
|
||||
static XRectangle rect;
|
||||
static XRectangle brect;
|
||||
static int screen_num;
|
||||
static int displayed = 0;
|
||||
static char *sockfile = 0;
|
||||
static File *files[B_LAST];
|
||||
static Item **items = 0;
|
||||
static unsigned int id = 0;
|
||||
static Pixmap pmap;
|
||||
|
||||
static Draw zero_draw = {0};
|
||||
|
||||
static void draw_bar(void *obj, char *arg);
|
||||
static void quit(void *obj, char *arg);
|
||||
static void display(void *obj, char *arg);
|
||||
static void reset(void *obj, char *arg);
|
||||
static void _destroy(void *obj, char *arg);
|
||||
static void handle_after_write(IXPServer * s, File * f);
|
||||
|
||||
static Action acttbl[] = {
|
||||
{"quit", quit},
|
||||
{"display", display},
|
||||
{"update", draw_bar},
|
||||
{"reset", reset},
|
||||
{"destroy", _destroy},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static char *version[] = {
|
||||
"wmibar - window manager improved bar - " VERSION "\n"
|
||||
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
|
||||
};
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr, "%s",
|
||||
"usage: wmibar -s <socket file> [-v] [<x>,<y>,<width>,<height>]\n"
|
||||
" -s socket file\n" " -v version info\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* <path>/data "<txt value>"
|
||||
* <path>/fgcolor "#RRGGBBAA"
|
||||
* <path>/bgcolor "#RRGGBBAA"
|
||||
* <path>/bordercolor "#RRGGBBAA"
|
||||
* <path>/b1press "<command>"
|
||||
* <path>/b2press "<command>"
|
||||
* <path>/b3press "<command>"
|
||||
* <path>/b4press "<command>"
|
||||
* <path>/b5press "<command>"
|
||||
*/
|
||||
static void
|
||||
create_label(char *path)
|
||||
{
|
||||
File *f;
|
||||
char file[MAX_BUF];
|
||||
int i;
|
||||
|
||||
snprintf(file, MAX_BUF, "%s/data", path);
|
||||
f = ixp_create(ixps, file);
|
||||
f->after_write = handle_after_write;
|
||||
snprintf(file, MAX_BUF, "%s/fgcolor", path);
|
||||
wmii_create_ixpfile(ixps, file, files[B_FG_COLOR]->content);
|
||||
snprintf(file, MAX_BUF, "%s/bgcolor", path);
|
||||
wmii_create_ixpfile(ixps, file, files[B_BG_COLOR]->content);
|
||||
snprintf(file, MAX_BUF, "%s/bordercolor", path);
|
||||
wmii_create_ixpfile(ixps, file, files[B_BORDER_COLOR]->content);
|
||||
for (i = 1; i < 6; i++) { /* 5 buttons events */
|
||||
snprintf(file, MAX_BUF, "%s/b%dpress", path, i);
|
||||
ixp_create(ixps, file);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_destroy(void *obj, char *arg)
|
||||
{
|
||||
char buf[512];
|
||||
if (!arg)
|
||||
return;
|
||||
snprintf(buf, sizeof(buf), "/%s", arg);
|
||||
ixps->remove(ixps, buf);
|
||||
draw_bar(0, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
reset(void *obj, char *arg)
|
||||
{
|
||||
int i;
|
||||
char buf[512];
|
||||
for (i = 0; i < id; i++) {
|
||||
snprintf(buf, sizeof(buf), "/%d", i + 1);
|
||||
ixps->remove(ixps, buf);
|
||||
}
|
||||
id = 0;
|
||||
draw_bar(0, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
quit(void *obj, char *arg)
|
||||
{
|
||||
ixps->runlevel = SHUTDOWN;
|
||||
}
|
||||
|
||||
static void
|
||||
display(void *obj, char *arg)
|
||||
{
|
||||
if (!arg)
|
||||
return;
|
||||
displayed = _strtonum(arg, 0, 1);
|
||||
if (displayed) {
|
||||
XMapRaised(dpy, win);
|
||||
draw_bar(0, 0);
|
||||
} else {
|
||||
XUnmapWindow(dpy, win);
|
||||
XSync(dpy, False);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
init_draw_label(char *path, Draw * d)
|
||||
{
|
||||
char buf[MAX_BUF];
|
||||
File *f;
|
||||
|
||||
/* text stuff */
|
||||
snprintf(buf, MAX_BUF, "%s/data", path);
|
||||
f = ixp_walk(ixps, buf);
|
||||
d->data = f->content;
|
||||
/* style stuff */
|
||||
snprintf(buf, MAX_BUF, "%s/fgcolor", path);
|
||||
f = ixp_walk(ixps, buf);
|
||||
d->fg = blitz_loadcolor(dpy, screen_num, f->content);
|
||||
snprintf(buf, MAX_BUF, "%s/bgcolor", path);
|
||||
f = ixp_walk(ixps, buf);
|
||||
d->bg = blitz_loadcolor(dpy, screen_num, f->content);
|
||||
snprintf(buf, MAX_BUF, "%s/bordercolor", path);
|
||||
f = ixp_walk(ixps, buf);
|
||||
d->border = blitz_loadcolor(dpy, screen_num, f->content);
|
||||
}
|
||||
|
||||
static void
|
||||
init_item(char *path, Item * i)
|
||||
{
|
||||
i->d = zero_draw;
|
||||
i->root = ixp_walk(ixps, path);
|
||||
i->d.gc = gc;
|
||||
i->d.drawable = pmap;
|
||||
i->d.rect = brect;
|
||||
i->d.rect.y = 0;
|
||||
init_draw_label(path, &i->d);
|
||||
}
|
||||
|
||||
static int
|
||||
comp_str(const void *s1, const void *s2)
|
||||
{
|
||||
return strcmp(*(char **) s1, *(char **) s2);
|
||||
}
|
||||
|
||||
static void
|
||||
draw()
|
||||
{
|
||||
unsigned int n = 0, i, w, xoff = 0;
|
||||
XFontStruct *font;
|
||||
unsigned expandable = 0;
|
||||
char buf[32];
|
||||
|
||||
if (!items)
|
||||
return;
|
||||
|
||||
n = count_items((void **) items);
|
||||
font = blitz_getfont(dpy, files[B_FONT]->content);
|
||||
expandable = _strtonum(files[B_EXPANDABLE]->content, 1, id);
|
||||
snprintf(buf, sizeof(buf), "/%d", expandable);
|
||||
if (!ixp_walk(ixps, buf))
|
||||
expandable = 0;
|
||||
|
||||
w = 0;
|
||||
/* precalc */
|
||||
for (i = 0; expandable && items[i]; i++)
|
||||
if (i + 1 != expandable) {
|
||||
items[i]->d.rect.width = brect.height;
|
||||
if (items[i]->d.data) {
|
||||
if (!strncmp(items[i]->d.data, "%m:", 3))
|
||||
/* meter */
|
||||
items[i]->d.rect.width = brect.height / 2;
|
||||
else
|
||||
items[i]->d.rect.width +=
|
||||
XTextWidth(font, items[i]->d.data, strlen(items[i]->d.data));
|
||||
}
|
||||
w += items[i]->d.rect.width;
|
||||
}
|
||||
if (!expandable || w > brect.width) {
|
||||
/* failsafe mode, give all labels same width */
|
||||
w = brect.width / n;
|
||||
for (i = 0; items[i]; i++)
|
||||
items[i]->d.rect.width = w;
|
||||
items[i - 1]->d.rect.width = brect.width - items[i - 1]->d.rect.x;
|
||||
} else
|
||||
items[expandable - 1]->d.rect.width = brect.width - w;
|
||||
|
||||
for (i = 0; items[i]; i++) {
|
||||
items[i]->d.font = font;
|
||||
items[i]->d.rect.x = xoff;
|
||||
xoff += items[i]->d.rect.width;
|
||||
if (items[i]->d.data && !strncmp(items[i]->d.data, "%m:", 3))
|
||||
blitz_drawmeter(dpy, &items[i]->d);
|
||||
else
|
||||
blitz_drawlabel(dpy, &items[i]->d);
|
||||
}
|
||||
XCopyArea(dpy, pmap, win, gc, 0, 0, brect.width, brect.height, 0, 0);
|
||||
XSync(dpy, False);
|
||||
XFreeFont(dpy, font);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_bar(void *obj, char *arg)
|
||||
{
|
||||
File *label = 0;
|
||||
unsigned int i = 0, n = 0;
|
||||
Item *item;
|
||||
char buf[512];
|
||||
|
||||
if (!displayed)
|
||||
return;
|
||||
if (items) {
|
||||
for (i = 0; items[i]; i++) {
|
||||
free(items[i]);
|
||||
}
|
||||
free(items);
|
||||
}
|
||||
items = 0;
|
||||
snprintf(buf, sizeof(buf), "%s", "/1");
|
||||
label = ixp_walk(ixps, buf);
|
||||
if (!label) {
|
||||
Draw d = {0};
|
||||
/* default stuff */
|
||||
d.gc = gc;
|
||||
d.drawable = pmap;
|
||||
d.rect.width = brect.width;
|
||||
d.rect.height = brect.height;
|
||||
d.bg = blitz_loadcolor(dpy, screen_num, files[B_BG_COLOR]->content);
|
||||
d.fg = blitz_loadcolor(dpy, screen_num, files[B_FG_COLOR]->content);
|
||||
d.border = blitz_loadcolor(dpy, screen_num, files[B_BORDER_COLOR]->content);
|
||||
blitz_drawlabelnoborder(dpy, &d);
|
||||
} else {
|
||||
File *f;
|
||||
char **paths = 0;
|
||||
/*
|
||||
* take order into account, directory names are used in
|
||||
* alphabetical order
|
||||
*/
|
||||
n = 0;
|
||||
for (f = label; f; f = f->next)
|
||||
n++;
|
||||
paths = emalloc(sizeof(char *) * n);
|
||||
i = 0;
|
||||
for (f = label; f; f = f->next)
|
||||
paths[i++] = f->name;
|
||||
qsort(paths, n, sizeof(char *), comp_str);
|
||||
for (i = 0; i < n; i++) {
|
||||
snprintf(buf, sizeof(buf), "/%s", paths[i]);
|
||||
item = emalloc(sizeof(Item));
|
||||
items = (Item **) attach_item_end((void **) items, item, sizeof(Item *));
|
||||
init_item(buf, item);
|
||||
}
|
||||
draw();
|
||||
free(paths);
|
||||
}
|
||||
}
|
||||
|
||||
static Item *
|
||||
get_item_for_file(File * f)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; items && items[i]; i++)
|
||||
if (items[i]->root == f)
|
||||
return items[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_buttonpress(XButtonPressedEvent * e)
|
||||
{
|
||||
File *p;
|
||||
char buf[MAX_BUF];
|
||||
char path[512];
|
||||
int i;
|
||||
|
||||
for (i = 0; items && items[i]; i++) {
|
||||
if (blitz_ispointinrect(e->x, e->y, &items[i]->d.rect)) {
|
||||
path[0] = '\0';
|
||||
wmii_get_ixppath(items[i]->root, path, sizeof(path));
|
||||
snprintf(buf, MAX_BUF, "%s/b%upress", path, e->button);
|
||||
if ((p = ixp_walk(ixps, buf)))
|
||||
if (p->content)
|
||||
spawn(dpy, p->content);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_event(Connection * e)
|
||||
{
|
||||
XEvent ev;
|
||||
|
||||
while (XPending(dpy)) {
|
||||
XNextEvent(dpy, &ev);
|
||||
switch (ev.type) {
|
||||
case ButtonPress:
|
||||
handle_buttonpress(&ev.xbutton);
|
||||
break;
|
||||
case Expose:
|
||||
if (ev.xexpose.count == 0) {
|
||||
/* XRaiseWindow(dpy, win); */
|
||||
draw_bar(0, 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
update_geometry(char *size)
|
||||
{
|
||||
blitz_strtorect(dpy, &rect, &brect, size);
|
||||
if (!brect.width)
|
||||
brect.width = DisplayWidth(dpy, screen_num);
|
||||
if (!brect.height)
|
||||
brect.height = 20;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_after_write(IXPServer * s, File * f)
|
||||
{
|
||||
int i;
|
||||
size_t len;
|
||||
Item *item;
|
||||
char buf[512];
|
||||
|
||||
buf[0] = '\0';
|
||||
if (!strncmp(f->name, "data", 5)) {
|
||||
if ((item = get_item_for_file(f->parent))) {
|
||||
wmii_get_ixppath(f->parent, buf, sizeof(buf));
|
||||
init_draw_label(buf, &item->d);
|
||||
draw();
|
||||
}
|
||||
} else if (files[B_GEOMETRY] == f) {
|
||||
char *geom = files[B_GEOMETRY]->content;
|
||||
if (geom && strrchr(geom, ',')) {
|
||||
update_geometry(geom);
|
||||
XMoveResizeWindow(dpy, win, brect.x, brect.y,
|
||||
brect.width, brect.height);
|
||||
XSync(dpy, False);
|
||||
pmap = XCreatePixmap(dpy, win, brect.width, brect.height,
|
||||
DefaultDepth(dpy, screen_num));
|
||||
XSync(dpy, False);
|
||||
draw_bar(0, 0);
|
||||
}
|
||||
} else if (files[B_CTL] == f) {
|
||||
for (i = 0; acttbl[i].name; i++) {
|
||||
len = strlen(acttbl[i].name);
|
||||
if (!strncmp(acttbl[i].name, (char *) f->content, len)) {
|
||||
if (strlen(f->content) > len) {
|
||||
acttbl[i].func(0, &((char *) f->content)[len + 1]);
|
||||
} else {
|
||||
acttbl[i].func(0, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
check_event(0);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_before_read(IXPServer * s, File * f)
|
||||
{
|
||||
char buf[64];
|
||||
if (f == files[B_GEOMETRY]) {
|
||||
snprintf(buf, sizeof(buf), "%d,%d,%d,%d", brect.x, brect.y,
|
||||
brect.width, brect.height);
|
||||
if (f->content)
|
||||
free(f->content);
|
||||
f->content = strdup(buf);
|
||||
f->size = strlen(buf);
|
||||
} else if (f == files[B_NEW]) {
|
||||
snprintf(buf, sizeof(buf), "%d", ++id);
|
||||
if (f->content)
|
||||
free(f->content);
|
||||
f->content = strdup(buf);
|
||||
f->size = strlen(buf);
|
||||
create_label(buf);
|
||||
draw_bar(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
run(char *geom)
|
||||
{
|
||||
XSetWindowAttributes wa;
|
||||
XGCValues gcv;
|
||||
|
||||
/* init */
|
||||
if (!(files[B_CTL] = ixp_create(ixps, "/ctl"))) {
|
||||
perror("wmibar: cannot connect IXP server");
|
||||
exit(1);
|
||||
}
|
||||
files[B_CTL]->after_write = handle_after_write;
|
||||
files[B_NEW] = ixp_create(ixps, "/new");
|
||||
files[B_NEW]->before_read = handle_before_read;
|
||||
files[B_FONT] = wmii_create_ixpfile(ixps, "/font", BLITZ_FONT);
|
||||
files[B_BG_COLOR] = wmii_create_ixpfile(ixps, "/bgcolor", BLITZ_NORM_BG_COLOR);
|
||||
files[B_FG_COLOR] = wmii_create_ixpfile(ixps, "/fgcolor", BLITZ_NORM_FG_COLOR);
|
||||
files[B_BORDER_COLOR] = wmii_create_ixpfile(ixps, "/bordercolor", BLITZ_NORM_BORDER_COLOR);
|
||||
files[B_GEOMETRY] = ixp_create(ixps, "/geometry");
|
||||
files[B_GEOMETRY]->before_read = handle_before_read;
|
||||
files[B_GEOMETRY]->after_write = handle_after_write;
|
||||
files[B_EXPANDABLE] = ixp_create(ixps, "/expandable");
|
||||
|
||||
wa.override_redirect = 1;
|
||||
wa.background_pixmap = ParentRelative;
|
||||
wa.event_mask = ExposureMask | ButtonPressMask | SubstructureRedirectMask | SubstructureNotifyMask;
|
||||
|
||||
brect.x = brect.y = brect.width = brect.height = 0;
|
||||
rect.x = rect.y = 0;
|
||||
rect.width = DisplayWidth(dpy, screen_num);
|
||||
rect.height = DisplayHeight(dpy, screen_num);
|
||||
update_geometry(geom);
|
||||
|
||||
win = XCreateWindow(dpy, RootWindow(dpy, screen_num), brect.x, brect.y,
|
||||
brect.width, brect.height, 0, DefaultDepth(dpy,
|
||||
screen_num),
|
||||
CopyFromParent, DefaultVisual(dpy, screen_num),
|
||||
CWOverrideRedirect | CWBackPixmap | CWEventMask,
|
||||
&wa);
|
||||
XDefineCursor(dpy, win, XCreateFontCursor(dpy, XC_left_ptr));
|
||||
XSync(dpy, False);
|
||||
|
||||
gcv.function = GXcopy;
|
||||
gcv.graphics_exposures = False;
|
||||
gc = XCreateGC(dpy, win, 0, 0);
|
||||
|
||||
pmap =
|
||||
XCreatePixmap(dpy, win, brect.width, brect.height,
|
||||
DefaultDepth(dpy, screen_num));
|
||||
|
||||
/* main event loop */
|
||||
run_server_with_fd_support(ixps, ConnectionNumber(dpy),
|
||||
check_event, 0);
|
||||
deinit_server(ixps);
|
||||
XFreePixmap(dpy, pmap);
|
||||
XFreeGC(dpy, gc);
|
||||
XCloseDisplay(dpy);
|
||||
}
|
||||
|
||||
static int
|
||||
dummy_error_handler(Display * dpy, XErrorEvent * err)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char geom[64];
|
||||
int i;
|
||||
|
||||
/* command line args */
|
||||
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
|
||||
switch (argv[i][1]) {
|
||||
case 'v':
|
||||
fprintf(stdout, "%s", version[0]);
|
||||
exit(0);
|
||||
break;
|
||||
case 's':
|
||||
if (i + 1 < argc)
|
||||
sockfile = argv[++i];
|
||||
else
|
||||
usage();
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dpy = XOpenDisplay(0);
|
||||
if (!dpy) {
|
||||
fprintf(stderr, "%s", "wmibar: cannot open display\n");
|
||||
exit(1);
|
||||
}
|
||||
XSetErrorHandler(dummy_error_handler);
|
||||
screen_num = DefaultScreen(dpy);
|
||||
|
||||
geom[0] = '\0';
|
||||
if (argc > i)
|
||||
_strlcpy(geom, argv[i], sizeof(geom));
|
||||
|
||||
ixps = wmii_setup_server(sockfile);
|
||||
run(geom);
|
||||
|
||||
return 0;
|
||||
}
|
472
cmd/wmibar2.c
Normal file
472
cmd/wmibar2.c
Normal file
@ -0,0 +1,472 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/cursorfont.h>
|
||||
#include <X11/Xproto.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
#include "../libixp2/ixp.h"
|
||||
#include "blitz.h"
|
||||
#include "cext.h"
|
||||
|
||||
/*
|
||||
* filesystem specification
|
||||
* / Droot
|
||||
* /display Fdisplay 'top', 'bottom', 'none'
|
||||
* /font Ffont <xlib font name>
|
||||
* /new Fnew returns id of new item
|
||||
* /default/ Ditem
|
||||
* /default/bNpress Fevent <command, gets executed>
|
||||
* /default/bgcolor Fcolor <#RRGGBB, #RGB>
|
||||
* /default/fgcolor Fcolor <#RRGGBB, #RGB>
|
||||
* /default/bordercolor Fcolor <#RRGGBB, #RGB>
|
||||
* /1/ Ditem
|
||||
* /1/data Fdata <arbitrary data which gets displayed>
|
||||
* /1/bNpress Fevent <command, gets executed>
|
||||
* /1/bgcolor Fcolor <#RRGGBB, #RGB>
|
||||
* /1/fgcolor Fcolor <#RRGGBB, #RGB>
|
||||
* /1/bordercolor Fcolor <#RRGGBB, #RGB> ...
|
||||
*/
|
||||
enum { /* 8-bit qid.path.type */
|
||||
Droot,
|
||||
Ditem,
|
||||
Fdisplay,
|
||||
Fnew,
|
||||
Fdata, /* data to display */
|
||||
Fevent,
|
||||
Fcolor,
|
||||
Ffont
|
||||
};
|
||||
|
||||
#define NONE (u16)0xffff
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
u8 type;
|
||||
} QFile;
|
||||
|
||||
static QFile qfilelist[] = {
|
||||
{"display", Fdisplay},
|
||||
{"font", Ffont},
|
||||
{"new", Fnew},
|
||||
{"data", Fdata},
|
||||
{"bgcolor", Fcolor},
|
||||
{"fgcolor", Fcolor},
|
||||
{"bordercolor", Fcolor},
|
||||
{"b1press", Fevent},
|
||||
{"b2press", Fevent},
|
||||
{"b3press", Fevent},
|
||||
{"b4press", Fevent},
|
||||
{"b5press", Fevent},
|
||||
{0, 0},
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
u32 fid;
|
||||
Qid qid;
|
||||
} Map;
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
char text[256];
|
||||
int value;
|
||||
unsigned long bg;
|
||||
unsigned long fg;
|
||||
unsigned long border[4];
|
||||
XFontStruct *font;
|
||||
char event[5][256];
|
||||
} Item;
|
||||
|
||||
static Item **items = 0;
|
||||
static char *sockfile = 0;
|
||||
static pid_t mypid = 0;
|
||||
static IXPServer srv = {0};
|
||||
static Qid root_qid;
|
||||
static Display *dpy;
|
||||
static int screen_num;
|
||||
static char *align = 0;
|
||||
static char *font = 0;
|
||||
/*
|
||||
static GC gc;
|
||||
static Window win;
|
||||
static XRectangle geom;
|
||||
static int mapped = 0;
|
||||
static Pixmap pmap;
|
||||
|
||||
static Draw zero_draw = { 0 };
|
||||
*/
|
||||
|
||||
static char *version[] = {
|
||||
"wmibar - window manager improved bar - " VERSION "\n"
|
||||
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
|
||||
};
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr, "%s %d",
|
||||
"usage: wmibar -s <socket file> [-v]\n"
|
||||
" -s socket file\n" " -v version info\n",
|
||||
NONE);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int
|
||||
dummy_error_handler(Display * dpy, XErrorEvent * err)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
exit_cleanup()
|
||||
{
|
||||
if (mypid == getpid())
|
||||
unlink(sockfile);
|
||||
}
|
||||
|
||||
static u64
|
||||
make_qpath(u8 type, u16 item, u16 file)
|
||||
{
|
||||
return ((u64) file << 24) | ((u64) item << 8) | (u64) type;
|
||||
}
|
||||
|
||||
static u8
|
||||
qpath_type(u64 path)
|
||||
{
|
||||
return path & 0xff;
|
||||
}
|
||||
|
||||
static u16
|
||||
qpath_item(u64 path)
|
||||
{
|
||||
return (path >> 8) & 0xffff;
|
||||
}
|
||||
|
||||
/*
|
||||
static u16
|
||||
qpath_file(u64 path)
|
||||
{
|
||||
return (path >> 24) & 0xffff;
|
||||
}
|
||||
*/
|
||||
|
||||
static Map *
|
||||
fid_to_map(Map ** maps, u32 fid)
|
||||
{
|
||||
u32 i;
|
||||
for (i = 0; maps && maps[i]; i++)
|
||||
if (maps[i]->fid == fid)
|
||||
return maps[i];
|
||||
return nil;
|
||||
}
|
||||
|
||||
static int
|
||||
qfile_index(char *name, u16 * index)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; qfilelist[i].name; i++)
|
||||
if (!strncmp(name, qfilelist[i].name, strlen(qfilelist[i].name))) {
|
||||
*index = i;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
make_qid(Qid * dir, char *wname, Qid * new)
|
||||
{
|
||||
u16 idx;
|
||||
const char *errstr;
|
||||
if (dir->type != IXP_QTDIR)
|
||||
return FALSE;
|
||||
new->version = 0;
|
||||
if (!qfile_index(wname, &idx)) {
|
||||
new->type = IXP_QTDIR;
|
||||
if (!strncmp(wname, "..", 3)) {
|
||||
*new = root_qid;
|
||||
return TRUE;
|
||||
} else if (!strncmp(wname, "default", 8)) {
|
||||
new->path = make_qpath(Ditem, 0, NONE);
|
||||
return TRUE;
|
||||
}
|
||||
/* check if wname is a number, otherwise file not found */
|
||||
idx = (u16) __strtonum(wname, 1, 0xffff, &errstr);
|
||||
if (errstr || count_items((void **) items) < idx)
|
||||
return FALSE;
|
||||
/* found */
|
||||
new->path = make_qpath(Ditem, idx, NONE);
|
||||
} else {
|
||||
new->type = IXP_QTFILE;
|
||||
new->path =
|
||||
make_qpath(qfilelist[idx].type, qpath_item(dir->path), idx);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
attach(IXPServer * s, IXPConn * c)
|
||||
{
|
||||
Map *map = emalloc(sizeof(Map));
|
||||
fprintf(stderr, "attaching %d %s %s\n", s->fcall.afid, s->fcall.uname, s->fcall.aname);
|
||||
map->qid = root_qid;
|
||||
map->fid = s->fcall.fid;
|
||||
c->aux = (Map **) attach_item_begin((void **) c->aux, map, sizeof(Map *));
|
||||
s->fcall.id = RATTACH;
|
||||
s->fcall.qid = root_qid;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
walk(IXPServer * s, IXPConn * c)
|
||||
{
|
||||
u16 nwqid = 0;
|
||||
Qid qid;
|
||||
Map *map;
|
||||
|
||||
fprintf(stderr, "%s", "walking\n");
|
||||
if (!(map = fid_to_map(c->aux, s->fcall.fid))) {
|
||||
s->errstr = "no directory associated with fid";
|
||||
return FALSE;
|
||||
}
|
||||
if (s->fcall.fid != s->fcall.newfid
|
||||
&& (fid_to_map(c->aux, s->fcall.newfid))) {
|
||||
s->errstr = "fid alreay in use";
|
||||
return FALSE;
|
||||
}
|
||||
if (s->fcall.nwname) {
|
||||
qid = map->qid;
|
||||
for (nwqid = 0; (nwqid < s->fcall.nwname)
|
||||
&& make_qid(&qid, s->fcall.wname[nwqid],
|
||||
&s->fcall.wqid[nwqid]); nwqid++)
|
||||
qid = s->fcall.wqid[nwqid];
|
||||
if (!nwqid) {
|
||||
s->errstr = "file not found";
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* following condition is required by 9P, a fid will only be valid if
|
||||
* the walk was complete
|
||||
*/
|
||||
if (nwqid == s->fcall.nwname) {
|
||||
if (s->fcall.fid == s->fcall.newfid) {
|
||||
c->aux =
|
||||
(Map **) detach_item((void **) c->aux, map, sizeof(Map *));
|
||||
free(map);
|
||||
}
|
||||
map = emalloc(sizeof(Map));
|
||||
map->qid = qid;
|
||||
map->fid = s->fcall.newfid;
|
||||
c->aux =
|
||||
(Map **) attach_item_begin((void **) c->aux, map,
|
||||
sizeof(Map *));
|
||||
}
|
||||
s->fcall.id = RWALK;
|
||||
s->fcall.nwqid = nwqid;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
_open(IXPServer * s, IXPConn * c)
|
||||
{
|
||||
Map *map = fid_to_map(c->aux, s->fcall.fid);
|
||||
|
||||
fprintf(stderr, "%s", "opening\n");
|
||||
if (!map) {
|
||||
s->errstr = "invalid fid";
|
||||
return FALSE;
|
||||
}
|
||||
if ((s->fcall.mode != IXP_OREAD) && (s->fcall.mode != IXP_OWRITE)) {
|
||||
s->errstr = "mode not supported";
|
||||
return FALSE;
|
||||
}
|
||||
s->fcall.id = ROPEN;
|
||||
s->fcall.qid = map->qid;
|
||||
s->fcall.iounit =
|
||||
s->fcall.maxmsg - (sizeof(u8) + sizeof(u16) + 2 * sizeof(u32));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
_read(IXPServer * s, IXPConn * c)
|
||||
{
|
||||
Map *map = fid_to_map(c->aux, s->fcall.fid);
|
||||
Stat stat = {0};
|
||||
u8 *p;
|
||||
|
||||
fprintf(stderr, "%s", "reading\n");
|
||||
if (!map) {
|
||||
s->errstr = "invalid fid";
|
||||
return FALSE;
|
||||
}
|
||||
stat.mode = 0xff;
|
||||
stat.atime = stat.mtime = time(0);
|
||||
_strlcpy(stat.uid, getenv("USER"), sizeof(stat.uid));
|
||||
_strlcpy(stat.gid, getenv("USER"), sizeof(stat.gid));
|
||||
_strlcpy(stat.muid, getenv("USER"), sizeof(stat.muid));
|
||||
|
||||
fprintf(stderr, "%d\n", qpath_item(map->qid.path));
|
||||
switch (qpath_type(map->qid.path)) {
|
||||
default:
|
||||
case Droot:
|
||||
p = s->fcall.data;
|
||||
_strlcpy(stat.name, "display", sizeof(stat.name));
|
||||
stat.length = strlen(align);
|
||||
make_qid(&root_qid, "display", &stat.qid);
|
||||
stat.size = ixp_sizeof_stat(&stat);
|
||||
s->fcall.count = stat.size;
|
||||
p = ixp_enc_stat(p, &stat);
|
||||
_strlcpy(stat.name, "font", sizeof(stat.name));
|
||||
stat.length = strlen(font);
|
||||
make_qid(&root_qid, "font", &stat.qid);
|
||||
stat.size = ixp_sizeof_stat(&stat);;
|
||||
s->fcall.count += stat.size;
|
||||
p = ixp_enc_stat(p, &stat);
|
||||
_strlcpy(stat.name, "new", sizeof(stat.name));
|
||||
stat.length = 0;
|
||||
make_qid(&root_qid, "new", &stat.qid);
|
||||
stat.size = ixp_sizeof_stat(&stat);;
|
||||
s->fcall.count += stat.size;
|
||||
p = ixp_enc_stat(p, &stat);
|
||||
s->fcall.id = RREAD;
|
||||
fprintf(stderr, "%d msize\n", s->fcall.count);
|
||||
break;
|
||||
case Ditem:
|
||||
break;
|
||||
case Fdisplay:
|
||||
break;
|
||||
case Fnew:
|
||||
break;
|
||||
case Fdata:
|
||||
break;
|
||||
case Fevent:
|
||||
break;
|
||||
case Fcolor:
|
||||
break;
|
||||
case Ffont:
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
_write(IXPServer * s, IXPConn * c)
|
||||
{
|
||||
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
clunk(IXPServer * s, IXPConn * c)
|
||||
{
|
||||
Map *map = fid_to_map(c->aux, s->fcall.fid);
|
||||
|
||||
if (!map) {
|
||||
s->errstr = "invalid fid";
|
||||
return FALSE;
|
||||
}
|
||||
c->aux = (Map **) detach_item((void **) c->aux, map, sizeof(Map *));
|
||||
free(map);
|
||||
s->fcall.id = RCLUNK;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
freeconn(IXPServer * s, IXPConn * c)
|
||||
{
|
||||
Map **maps = c->aux;
|
||||
if (maps) {
|
||||
int i;
|
||||
for (i = 0; maps[i]; i++)
|
||||
free(maps[i]);
|
||||
free(maps);
|
||||
}
|
||||
}
|
||||
|
||||
static IXPTFunc funcs[] = {
|
||||
{TVERSION, ixp_server_tversion},
|
||||
{TATTACH, attach},
|
||||
{TWALK, walk},
|
||||
{TOPEN, _open},
|
||||
{TREAD, _read},
|
||||
{TWRITE, _write},
|
||||
{TCLUNK, clunk},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
Item *item;
|
||||
|
||||
/* command line args */
|
||||
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
|
||||
switch (argv[i][1]) {
|
||||
case 'v':
|
||||
fprintf(stdout, "%s", version[0]);
|
||||
exit(0);
|
||||
break;
|
||||
case 's':
|
||||
if (i + 1 < argc)
|
||||
sockfile = argv[++i];
|
||||
else
|
||||
usage();
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dpy = XOpenDisplay(0);
|
||||
if (!dpy) {
|
||||
fprintf(stderr, "%s", "wmibar: cannot open display\n");
|
||||
exit(1);
|
||||
}
|
||||
XSetErrorHandler(dummy_error_handler);
|
||||
screen_num = DefaultScreen(dpy);
|
||||
|
||||
if (!ixp_server_init(&srv, sockfile, funcs, freeconn)) {
|
||||
fprintf(stderr, "wmibar: fatal: %s\n", srv.errstr);
|
||||
exit(1);
|
||||
}
|
||||
root_qid.type = IXP_QTDIR;
|
||||
root_qid.version = 0;
|
||||
root_qid.path = make_qpath(Droot, NONE, NONE);
|
||||
|
||||
mypid = getpid();
|
||||
atexit(exit_cleanup);
|
||||
|
||||
/* default item settings */
|
||||
item = emalloc(sizeof(Item));
|
||||
item->id = 0;
|
||||
item->text[0] = '\0';
|
||||
item->value = 0;
|
||||
|
||||
align = "bottom";
|
||||
font = "fixed";
|
||||
|
||||
ixp_server_loop(&srv);
|
||||
if (srv.errstr) {
|
||||
fprintf(stderr, "wmibar: fatal: %s\n", srv.errstr);
|
||||
ixp_server_deinit(&srv);
|
||||
exit(1);
|
||||
}
|
||||
ixp_server_deinit(&srv);
|
||||
return 0;
|
||||
}
|
38
cmd/wmifs.1
Normal file
38
cmd/wmifs.1
Normal file
@ -0,0 +1,38 @@
|
||||
.TH WMIFS 1 wmii-3
|
||||
.SH NAME
|
||||
wmifs \- window manager improved 2 filesystem
|
||||
.SH SYNOPSIS
|
||||
.B wmifs
|
||||
.B \-s
|
||||
.I socketfile
|
||||
.SH DESCRIPTION
|
||||
.SS Overview
|
||||
.B wmifs
|
||||
is a socket-based fileserver which is used for binding other fileserver
|
||||
filesystems to a specific namespace. It routes filesystem accesses to
|
||||
the specific fileserver and behaves to the outside as one master
|
||||
filesystem.
|
||||
.SS Options
|
||||
.TP
|
||||
.BI \-s " socketfile"
|
||||
specifies the socketfile that
|
||||
.B wmifs
|
||||
should create.
|
||||
.TP
|
||||
.B \-v
|
||||
prints version information to stderr, then exits.
|
||||
.SS Customization
|
||||
.B wmifs
|
||||
is customized through manipulating its filesystem namespace.
|
||||
In the default setup of
|
||||
.BR wmii (1)
|
||||
the namespace of
|
||||
.B wmifs
|
||||
can be found in /.
|
||||
.SH SEE ALSO
|
||||
.BR wmibar (1),
|
||||
.BR wmii (1),
|
||||
.BR wmiiwm (1),
|
||||
.BR wmikeys (1),
|
||||
.BR wmimenu (1),
|
||||
.BR wmir (1)
|
417
cmd/wmifs.c
Normal file
417
cmd/wmifs.c
Normal file
@ -0,0 +1,417 @@
|
||||
/*
|
||||
* (C)opyright MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "wmii.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
/* array indexes for file pointers */
|
||||
typedef enum {
|
||||
F_CTL,
|
||||
F_LAST
|
||||
} FsIndexes;
|
||||
|
||||
typedef struct Route Route;
|
||||
struct Route {
|
||||
File dest;
|
||||
int src;
|
||||
};
|
||||
|
||||
typedef struct Bind Bind;
|
||||
struct Bind {
|
||||
IXPClient *client;
|
||||
Route route[MAX_CONN * MAX_OPEN_FILES];
|
||||
File *mount;
|
||||
char *prefix;
|
||||
};
|
||||
|
||||
static Bind zero_bind = {0};
|
||||
static Display *dpy;
|
||||
static IXPServer *ixps;
|
||||
static char *sockfile = 0;
|
||||
static File *files[F_LAST];
|
||||
static Bind **bindings = 0;
|
||||
|
||||
static void quit(void *obj, char *arg);
|
||||
static void bind(void *obj, char *arg);
|
||||
static void unbind(void *obj, char *arg);
|
||||
static Bind *path_to_bind(char *path);
|
||||
|
||||
static Action acttbl[] = {
|
||||
{"quit", quit},
|
||||
{"unbind", unbind},
|
||||
{"bind", bind},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static char *version[] = {
|
||||
"wmifs - window manager improved filesystem - " VERSION "\n"
|
||||
" (C)opyright MMV Anselm R. Garbe\n", 0
|
||||
};
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: wmifs -s <socket file> [-v]\n"
|
||||
" -s socket file\n" " -v version info\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
quit(void *obj, char *arg)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; bindings && bindings[i]; i++) {
|
||||
if (bindings[i]->mount) {
|
||||
bindings[i]->mount->content = 0;
|
||||
ixp_remove(ixps, bindings[i]->prefix);
|
||||
if (ixps->errstr)
|
||||
fprintf(stderr, "wmifs: error on quit: remove %s: %s\n",
|
||||
bindings[i]->prefix, ixps->errstr);
|
||||
}
|
||||
/* free stuff */
|
||||
if (bindings[i]->prefix)
|
||||
free(bindings[i]->prefix);
|
||||
free(bindings[i]);
|
||||
}
|
||||
free(bindings);
|
||||
bindings = 0;
|
||||
ixps->runlevel = SHUTDOWN;
|
||||
}
|
||||
|
||||
static void
|
||||
_unbind(Bind * b)
|
||||
{
|
||||
bindings =
|
||||
(Bind **) detach_item((void **) bindings, b, sizeof(Bind *));
|
||||
|
||||
if (b->mount) {
|
||||
b->mount->content = 0;
|
||||
ixp_remove(ixps, b->prefix);
|
||||
if (ixps->errstr)
|
||||
fprintf(stderr, "wmifs: error on _unbind: remove %s: %s\n",
|
||||
b->prefix, ixps->errstr);
|
||||
}
|
||||
/* free stuff */
|
||||
deinit_client(b->client);
|
||||
free(b->prefix);
|
||||
free(b);
|
||||
}
|
||||
|
||||
static void
|
||||
unbind(void *obj, char *arg)
|
||||
{
|
||||
Bind *b = path_to_bind(arg);
|
||||
|
||||
if (!b) {
|
||||
fprintf(stderr, "wmifs: unbind: '%s' no such path\n", arg);
|
||||
return;
|
||||
}
|
||||
_unbind(b);
|
||||
}
|
||||
|
||||
static void
|
||||
bind(void *obj, char *arg)
|
||||
{
|
||||
Bind *b = 0;
|
||||
char cmd[1024];
|
||||
char *sfile;
|
||||
|
||||
if (!arg)
|
||||
return;
|
||||
_strlcpy(cmd, arg, sizeof(cmd));
|
||||
sfile = strchr(cmd, ' ');
|
||||
if (!sfile) {
|
||||
fprintf(stderr,
|
||||
"wmifs: bind: '%s' without socket argument, ignoring\n",
|
||||
arg);
|
||||
return; /* shortcut with empty argument */
|
||||
}
|
||||
*sfile = '\0';
|
||||
sfile++;
|
||||
if (*sfile == '\0') {
|
||||
fprintf(stderr,
|
||||
"wmifs: bind: '%s' without socket argument, ignoring\n",
|
||||
arg);
|
||||
return; /* shortcut with empty argument */
|
||||
}
|
||||
b = emalloc(sizeof(Bind));
|
||||
*b = zero_bind;
|
||||
|
||||
b->client = init_client(sfile);
|
||||
|
||||
if (!b->client) {
|
||||
fprintf(stderr,
|
||||
"wmifs: bind: cannot connect to server '%s', ignoring\n",
|
||||
sfile);
|
||||
free(b);
|
||||
return;
|
||||
}
|
||||
b->prefix = strdup(cmd);
|
||||
b->mount = ixp_create(ixps, b->prefix);
|
||||
b->mount->content = b->mount; /* shall be a directory */
|
||||
|
||||
bindings =
|
||||
(Bind **) attach_item_end((void **) bindings, b, sizeof(Bind *));
|
||||
}
|
||||
|
||||
static void
|
||||
handle_after_write(IXPServer * s, File * f)
|
||||
{
|
||||
int i;
|
||||
size_t len;
|
||||
|
||||
for (i = 0; acttbl[i].name; i++) {
|
||||
len = strlen(acttbl[i].name);
|
||||
if (!strncmp(acttbl[i].name, (char *) f->content, len)) {
|
||||
if (strlen(f->content) > len) {
|
||||
acttbl[i].func(0, &((char *) f->content)[len + 1]);
|
||||
} else {
|
||||
acttbl[i].func(0, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Bind *
|
||||
path_to_bind(char *path)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; bindings && bindings[i]; i++)
|
||||
if (!strncmp
|
||||
(bindings[i]->prefix, path, strlen(bindings[i]->prefix)))
|
||||
return bindings[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Bind *
|
||||
fd_to_bind(int fd, int *client_fd)
|
||||
{
|
||||
File *f = fd_to_file(ixps, fd);
|
||||
int i, j;
|
||||
|
||||
if (!f)
|
||||
return 0;
|
||||
for (i = 0; bindings && bindings[i]; i++) {
|
||||
for (j = 0; j < MAX_CONN * MAX_OPEN_FILES; j++) {
|
||||
if (&bindings[i]->route[j].dest == f) {
|
||||
*client_fd = bindings[i]->route[j].src;
|
||||
return bindings[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static File *
|
||||
fixp_create(IXPServer * s, char *path)
|
||||
{
|
||||
Bind *b = path_to_bind(path);
|
||||
size_t len;
|
||||
|
||||
if (!b) {
|
||||
File *f = ixp_create(s, path);
|
||||
return f;
|
||||
}
|
||||
/* route to b */
|
||||
len = strlen(b->prefix);
|
||||
b->client->create(b->client, path[len] == '\0' ? "/" : &path[len]);
|
||||
if (b->client->errstr) {
|
||||
if (!strcmp(b->client->errstr, DEAD_SERVER))
|
||||
_unbind(b);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static File *
|
||||
fixp_open(IXPServer * s, char *path)
|
||||
{
|
||||
Bind *b = path_to_bind(path);
|
||||
int fd;
|
||||
size_t len;
|
||||
|
||||
if (!b) {
|
||||
File *f = ixp_open(s, path);
|
||||
return f;
|
||||
}
|
||||
/* route to b */
|
||||
len = strlen(b->prefix);
|
||||
fd = b->client->open(b->client, path[len] == '\0' ? "/" : &path[len]);
|
||||
if (b->client->errstr) {
|
||||
set_error(s, b->client->errstr);
|
||||
if (!strcmp(b->client->errstr, DEAD_SERVER))
|
||||
_unbind(b);
|
||||
return 0;
|
||||
}
|
||||
b->route[fd].src = fd;
|
||||
return &b->route[fd].dest;
|
||||
}
|
||||
|
||||
static size_t
|
||||
fixp_read(IXPServer * s, int fd, size_t offset, void *out_buf,
|
||||
size_t out_buf_len)
|
||||
{
|
||||
int cfd;
|
||||
Bind *b = fd_to_bind(fd, &cfd);
|
||||
size_t result;
|
||||
|
||||
if (!b) {
|
||||
result = ixp_read(s, fd, offset, out_buf, out_buf_len);
|
||||
return result;
|
||||
}
|
||||
/* route to b */
|
||||
result = seek_read(b->client, cfd, offset, out_buf, out_buf_len);
|
||||
if (b->client->errstr) {
|
||||
set_error(s, b->client->errstr);
|
||||
if (!strcmp(b->client->errstr, DEAD_SERVER))
|
||||
_unbind(b);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
fixp_write(IXPServer * s, int fd, size_t offset, void *content,
|
||||
size_t in_len)
|
||||
{
|
||||
int cfd;
|
||||
Bind *b = fd_to_bind(fd, &cfd);
|
||||
|
||||
if (!b) {
|
||||
ixp_write(s, fd, offset, content, in_len);
|
||||
return;
|
||||
}
|
||||
/* route to b */
|
||||
seek_write(b->client, cfd, offset, content, in_len);
|
||||
if (b->client->errstr) {
|
||||
set_error(s, b->client->errstr);
|
||||
if (!strcmp(b->client->errstr, DEAD_SERVER))
|
||||
_unbind(b);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fixp_close(IXPServer * s, int fd)
|
||||
{
|
||||
int cfd;
|
||||
Bind *b = fd_to_bind(fd, &cfd);
|
||||
|
||||
if (!b) {
|
||||
ixp_close(s, fd);
|
||||
return;
|
||||
}
|
||||
/* route to b */
|
||||
b->client->close(b->client, cfd);
|
||||
if (b->client->errstr) {
|
||||
set_error(s, b->client->errstr);
|
||||
if (!strcmp(b->client->errstr, DEAD_SERVER))
|
||||
_unbind(b);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fixp_remove(IXPServer * s, char *path)
|
||||
{
|
||||
Bind *b = path_to_bind(path);
|
||||
size_t len;
|
||||
|
||||
if (!b) {
|
||||
ixp_remove(s, path);
|
||||
return;
|
||||
}
|
||||
/* route to b */
|
||||
len = strlen(b->prefix);
|
||||
b->client->remove(b->client, path[len] == '\0' ? "/" : &path[len]);
|
||||
if (b->client->errstr) {
|
||||
set_error(s, b->client->errstr);
|
||||
if (!strcmp(b->client->errstr, DEAD_SERVER))
|
||||
_unbind(b);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_event(Connection * e)
|
||||
{
|
||||
XEvent ev;
|
||||
while (XPending(dpy)) {
|
||||
/*
|
||||
* wmifs isn't interested in any X events, so just drop them
|
||||
* all
|
||||
*/
|
||||
XNextEvent(dpy, &ev);
|
||||
}
|
||||
/* why check them? because X won't kill wmifs when X dies */
|
||||
}
|
||||
|
||||
static void
|
||||
run()
|
||||
{
|
||||
if (!(files[F_CTL] = ixp_create(ixps, "/ctl"))) {
|
||||
perror("wmifs: cannot connect IXP server");
|
||||
exit(1);
|
||||
}
|
||||
files[F_CTL]->after_write = handle_after_write;
|
||||
|
||||
/* routing functions */
|
||||
ixps->create = fixp_create;
|
||||
ixps->remove = fixp_remove;
|
||||
ixps->open = fixp_open;
|
||||
ixps->close = fixp_close;
|
||||
ixps->read = fixp_read;
|
||||
ixps->write = fixp_write;
|
||||
|
||||
/* main event loop */
|
||||
run_server_with_fd_support(ixps, ConnectionNumber(dpy),
|
||||
check_event, 0);
|
||||
|
||||
deinit_server(ixps);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
/* command line args */
|
||||
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
|
||||
switch (argv[i][1]) {
|
||||
case 'v':
|
||||
fprintf(stdout, "%s", version[0]);
|
||||
exit(0);
|
||||
break;
|
||||
case 's':
|
||||
if (i + 1 < argc)
|
||||
sockfile = argv[++i];
|
||||
else
|
||||
usage();
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!getenv("HOME")) {
|
||||
fprintf(stderr, "%s",
|
||||
"wmifs: $HOME environment variable is not set\n");
|
||||
usage();
|
||||
}
|
||||
/* just for the case X crashes/gets quit */
|
||||
dpy = XOpenDisplay(0);
|
||||
if (!dpy) {
|
||||
fprintf(stderr, "%s", "wmifs: cannot open display\n");
|
||||
exit(1);
|
||||
}
|
||||
ixps = wmii_setup_server(sockfile);
|
||||
|
||||
run();
|
||||
|
||||
return 0;
|
||||
}
|
45
cmd/wmikeys.1
Normal file
45
cmd/wmikeys.1
Normal file
@ -0,0 +1,45 @@
|
||||
.TH WMIKEYS 1 wmii-3
|
||||
.SH NAME
|
||||
wmikeys \- window manager improved 2 keys
|
||||
.SH SYNOPSIS
|
||||
.B wmikeys
|
||||
.B \-s
|
||||
.I socketfile
|
||||
.SH DESCRIPTION
|
||||
.SS Overview
|
||||
.B wmikeys
|
||||
is a generic, highly customizable, and efficient key grabbing utility for the
|
||||
X Window System, originally designed for
|
||||
.BR wmii (1).
|
||||
It is similar to xbindkeys, but provides more flexibility and comes
|
||||
with equivalent features like the ratpoison keyboard control allows.
|
||||
It supports arbitrary, user defined shortcuts which are bound to
|
||||
arbitrary actions.
|
||||
Like wmii,
|
||||
.B wmikeys
|
||||
also implements a socket-based fileserver, which is accessed to configure and
|
||||
interact with other components.
|
||||
.SS Options
|
||||
.TP
|
||||
.BI \-s " socketfile"
|
||||
specifies the socketfile that
|
||||
.B wmikeys
|
||||
should create.
|
||||
.TP
|
||||
.B \-v
|
||||
prints version information to stderr, then exits.
|
||||
.SS Customization
|
||||
.B wmikeys
|
||||
is customized through manipulating its filesystem namespace.
|
||||
In the default setup of
|
||||
.BR wmii (1)
|
||||
the namespace of
|
||||
.B wmikeys
|
||||
can be found in /keys.
|
||||
.SH SEE ALSO
|
||||
.BR wmibar (1),
|
||||
.BR wmifs (1),
|
||||
.BR wmii (1),
|
||||
.BR wmiiwm (1),
|
||||
.BR wmimenu (1),
|
||||
.BR wmir (1)
|
535
cmd/wmikeys.c
Normal file
535
cmd/wmikeys.c
Normal file
@ -0,0 +1,535 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/cursorfont.h>
|
||||
#include <X11/Xproto.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
#include "wmii.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
/* array indexes for file pointers */
|
||||
typedef enum {
|
||||
K_CTL,
|
||||
K_LOOKUP,
|
||||
K_SIZE,
|
||||
K_GRAB_KB,
|
||||
K_TEXT_FONT,
|
||||
K_TEXT_COLOR,
|
||||
K_BG_COLOR,
|
||||
K_BORDER_COLOR,
|
||||
K_LAST
|
||||
} KeyIndexes;
|
||||
|
||||
typedef struct Shortcut Shortcut;
|
||||
|
||||
struct Shortcut {
|
||||
char name[MAX_BUF];
|
||||
unsigned long mod;
|
||||
KeyCode key;
|
||||
Shortcut *next;
|
||||
File *cmdfile;
|
||||
};
|
||||
|
||||
static IXPServer *ixps = 0;
|
||||
static Display *dpy;
|
||||
static GC gc;
|
||||
static Window win;
|
||||
static Window root;
|
||||
static XRectangle krect;
|
||||
static XRectangle rect;
|
||||
static int screen_num;
|
||||
static char *sockfile = 0;
|
||||
static Shortcut **shortcuts = 0;
|
||||
static File *files[K_LAST];
|
||||
static int grabkb = 0;
|
||||
static unsigned int num_lock_mask, valid_mask;
|
||||
static char buf[MAX_BUF];
|
||||
|
||||
static Shortcut zero_shortcut = {"", 0, 0, 0, 0};
|
||||
|
||||
static void grab_shortcut(Shortcut * s);
|
||||
static void ungrab_shortcut(Shortcut * s);
|
||||
static void draw_shortcut_box(char *text);
|
||||
static void quit(void *obj, char *arg);
|
||||
|
||||
static Action acttbl[] = {
|
||||
{"quit", quit},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static char *version[] = {
|
||||
"wmikeys - window manager improved keys - " VERSION "\n"
|
||||
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
|
||||
};
|
||||
|
||||
static void
|
||||
center()
|
||||
{
|
||||
krect.x = rect.width / 2 - krect.width / 2;
|
||||
krect.y = rect.height / 2 - krect.height / 2;
|
||||
}
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr, "%s",
|
||||
"usage: wmikeys [-s <socket file>] [-v] [<x>,<y>,<width>,<height>]\n"
|
||||
" -s socket file (default: /tmp/.ixp-$USER/wmikeys-$WMII_IDENT)\n"
|
||||
" -v version info\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* grabs shortcut on all screens */
|
||||
static void
|
||||
grab_shortcut(Shortcut * s)
|
||||
{
|
||||
XGrabKey(dpy, s->key, s->mod, root,
|
||||
True, GrabModeAsync, GrabModeAsync);
|
||||
if (num_lock_mask) {
|
||||
XGrabKey(dpy, s->key, s->mod | num_lock_mask, root,
|
||||
True, GrabModeAsync, GrabModeAsync);
|
||||
XGrabKey(dpy, s->key, s->mod | num_lock_mask | LockMask, root,
|
||||
True, GrabModeAsync, GrabModeAsync);
|
||||
}
|
||||
XSync(dpy, False);
|
||||
}
|
||||
|
||||
/*
|
||||
* don't handle evil keys anymore, just define more shortcuts if you cannot
|
||||
* live without evil key handling
|
||||
*/
|
||||
static void
|
||||
ungrab_shortcut(Shortcut * s)
|
||||
{
|
||||
XUngrabKey(dpy, s->key, s->mod, root);
|
||||
if (num_lock_mask) {
|
||||
XUngrabKey(dpy, s->key, s->mod | num_lock_mask, root);
|
||||
XUngrabKey(dpy, s->key, s->mod | num_lock_mask |
|
||||
LockMask, root);
|
||||
}
|
||||
XSync(dpy, False);
|
||||
}
|
||||
|
||||
static void
|
||||
create_shortcut(File * f)
|
||||
{
|
||||
static char *chain[8];
|
||||
char *k;
|
||||
size_t i, toks;
|
||||
Shortcut *s = 0, *r = 0;
|
||||
|
||||
_strlcpy(buf, f->name, sizeof(buf));
|
||||
toks = tokenize(chain, 8, buf, ',');
|
||||
|
||||
for (i = 0; i < toks; i++) {
|
||||
if (!s)
|
||||
r = s = emalloc(sizeof(Shortcut));
|
||||
else {
|
||||
s->next = emalloc(sizeof(Shortcut));
|
||||
s = s->next;
|
||||
}
|
||||
*s = zero_shortcut;
|
||||
_strlcpy(s->name, chain[i], MAX_BUF);
|
||||
k = strrchr(chain[i], '-');
|
||||
if (k)
|
||||
k++;
|
||||
else
|
||||
k = chain[i];
|
||||
s->key = XKeysymToKeycode(dpy, XStringToKeysym(k));
|
||||
s->mod = blitz_strtomod(chain[i]);
|
||||
}
|
||||
if (r) {
|
||||
s->cmdfile = f;
|
||||
shortcuts = (Shortcut **) attach_item_end((void **) shortcuts, r, sizeof(Shortcut *));
|
||||
grab_shortcut(r);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_shortcut(Shortcut * s, int ungrab)
|
||||
{
|
||||
if (s->next)
|
||||
destroy_shortcut(s->next, 0);
|
||||
if (ungrab)
|
||||
ungrab_shortcut(s);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static void
|
||||
next_keystroke(unsigned long *mod, KeyCode * key)
|
||||
{
|
||||
XEvent e;
|
||||
KeySym sym;
|
||||
*mod = 0;
|
||||
do {
|
||||
XMaskEvent(dpy, KeyPressMask, &e);
|
||||
*mod |= e.xkey.state & valid_mask;
|
||||
*key = (KeyCode) e.xkey.keycode;
|
||||
sym = XKeycodeToKeysym(dpy, e.xkey.keycode, 0);
|
||||
} while (IsModifierKey(sym));
|
||||
}
|
||||
|
||||
static void
|
||||
emulate_key_press(unsigned long mod, KeyCode key)
|
||||
{
|
||||
XEvent e;
|
||||
Window client_win;
|
||||
int revert;
|
||||
|
||||
XGetInputFocus(dpy, &client_win, &revert);
|
||||
|
||||
e.xkey.type = KeyPress;
|
||||
e.xkey.time = CurrentTime;
|
||||
e.xkey.window = client_win;
|
||||
e.xkey.display = dpy;
|
||||
e.xkey.state = mod;
|
||||
e.xkey.keycode = key;
|
||||
XSendEvent(dpy, client_win, True, KeyPressMask, &e);
|
||||
e.xkey.type = KeyRelease;
|
||||
XSendEvent(dpy, client_win, True, KeyReleaseMask, &e);
|
||||
XSync(dpy, False);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_shortcut_chain(Window w, Shortcut * processed, char *prefix, int grab)
|
||||
{
|
||||
unsigned long mod;
|
||||
KeyCode key;
|
||||
Shortcut *s = processed->next;
|
||||
|
||||
if (grab) {
|
||||
XGrabKeyboard(dpy, w, True, GrabModeAsync,
|
||||
GrabModeAsync, CurrentTime);
|
||||
XMapRaised(dpy, win);
|
||||
}
|
||||
draw_shortcut_box(prefix);
|
||||
next_keystroke(&mod, &key);
|
||||
|
||||
if ((processed->mod == mod) && (processed->key == key)) {
|
||||
/* double shortcut */
|
||||
emulate_key_press(mod, key);
|
||||
} else if ((s->mod == mod) && (s->key == key)) {
|
||||
if (s->cmdfile && s->cmdfile->content)
|
||||
spawn(dpy, s->cmdfile->content);
|
||||
else if (s->next) {
|
||||
snprintf(buf, sizeof(buf), "%s/%s", prefix, s->name);
|
||||
handle_shortcut_chain(w, s, buf, 0);
|
||||
}
|
||||
}
|
||||
if (grab) {
|
||||
XUngrabKeyboard(dpy, CurrentTime);
|
||||
XUnmapWindow(dpy, win);
|
||||
XSync(dpy, False);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_shortcut_gkb(Window w, unsigned long mod, KeyCode key)
|
||||
{
|
||||
int i;
|
||||
Shortcut *s;
|
||||
if (!files[K_LOOKUP]->content)
|
||||
return;
|
||||
for (i = 0; shortcuts && shortcuts[i]; i++) {
|
||||
s = shortcuts[i];
|
||||
if ((s->mod == mod) && (s->key == key)) {
|
||||
if (s->cmdfile && s->cmdfile->content)
|
||||
spawn(dpy, s->cmdfile->content);
|
||||
return;
|
||||
}
|
||||
}
|
||||
XBell(dpy, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_shortcut(Window w, unsigned long mod, KeyCode key)
|
||||
{
|
||||
int i;
|
||||
Shortcut *s;
|
||||
if (!files[K_LOOKUP]->content)
|
||||
return;
|
||||
for (i = 0; shortcuts && shortcuts[i]; i++) {
|
||||
s = shortcuts[i];
|
||||
if ((s->mod == mod) && (s->key == key)) {
|
||||
if (s->cmdfile && s->cmdfile->content) {
|
||||
spawn(dpy, s->cmdfile->content);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (s->next)
|
||||
handle_shortcut_chain(w, s, s->name, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
quit(void *obj, char *arg)
|
||||
{
|
||||
ixps->runlevel = SHUTDOWN;
|
||||
}
|
||||
|
||||
static void
|
||||
update()
|
||||
{
|
||||
int i;
|
||||
File *f, *p;
|
||||
if (!files[K_LOOKUP]->content)
|
||||
return;
|
||||
|
||||
f = ixp_walk(ixps, files[K_LOOKUP]->content);
|
||||
|
||||
if (!f || !is_directory(f))
|
||||
return; /* cannot update */
|
||||
|
||||
/* destroy existing shortcuts if any */
|
||||
for (i = 0; shortcuts && shortcuts[i]; i++)
|
||||
destroy_shortcut(shortcuts[i], 1);
|
||||
free(shortcuts);
|
||||
shortcuts = 0;
|
||||
|
||||
if (grabkb) {
|
||||
XGrabKeyboard(dpy, root, True, GrabModeAsync,
|
||||
GrabModeAsync, CurrentTime);
|
||||
return;
|
||||
}
|
||||
/* create new shortcuts */
|
||||
for (p = f->content; p; p = p->next)
|
||||
create_shortcut(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function assumes following fs-structure:
|
||||
*
|
||||
* /box/style/text-align "<align>"
|
||||
* /box/style/text-font "<value>"
|
||||
* /box/style/text-color "#RRGGBBAA"
|
||||
* /box/style/bg-color "#RRGGBBAA"
|
||||
*/
|
||||
static void
|
||||
draw_shortcut_box(char *text)
|
||||
{
|
||||
Draw d = {0};
|
||||
|
||||
d.font = blitz_getfont(dpy, files[K_TEXT_FONT]->content);
|
||||
krect.width = XTextWidth(d.font, text, strlen(text)) + krect.height;
|
||||
center();
|
||||
XMoveResizeWindow(dpy, win, krect.x, krect.y, krect.width,
|
||||
krect.height);
|
||||
|
||||
/* default stuff */
|
||||
d.gc = gc;
|
||||
d.drawable = win;
|
||||
d.data = text;
|
||||
d.rect.y = 0;
|
||||
d.rect.width = krect.width;
|
||||
d.rect.height = krect.height;
|
||||
d.bg = blitz_loadcolor(dpy, screen_num, files[K_BG_COLOR]->content);
|
||||
d.fg = blitz_loadcolor(dpy, screen_num, files[K_TEXT_COLOR]->content);
|
||||
d.border =
|
||||
blitz_loadcolor(dpy, screen_num, files[K_BORDER_COLOR]->content);
|
||||
blitz_drawlabel(dpy, &d);
|
||||
}
|
||||
|
||||
static void
|
||||
check_event(Connection * c)
|
||||
{
|
||||
XEvent ev;
|
||||
|
||||
while (XPending(dpy)) {
|
||||
XNextEvent(dpy, &ev);
|
||||
switch (ev.type) {
|
||||
case KeyPress:
|
||||
ev.xkey.state &= valid_mask;
|
||||
if (grabkb)
|
||||
handle_shortcut_gkb(root, ev.xkey.state,
|
||||
(KeyCode) ev.xkey.keycode);
|
||||
else
|
||||
handle_shortcut(root, ev.xkey.state,
|
||||
(KeyCode) ev.xkey.keycode);
|
||||
break;
|
||||
case KeymapNotify:
|
||||
update();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_after_write(IXPServer * s, File * f)
|
||||
{
|
||||
int i;
|
||||
size_t len;
|
||||
|
||||
if (f == files[K_CTL]) {
|
||||
for (i = 0; acttbl[i].name; i++) {
|
||||
len = strlen(acttbl[i].name);
|
||||
if (!strncmp(acttbl[i].name, (char *) f->content, len)) {
|
||||
if (strlen(f->content) > len) {
|
||||
acttbl[i].func(0, &((char *) f->content)[len + 1]);
|
||||
} else {
|
||||
acttbl[i].func(0, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (files[K_SIZE] == f) {
|
||||
char *size = files[K_SIZE]->content;
|
||||
if (size && strrchr(size, ','))
|
||||
blitz_strtorect(dpy, &rect, &krect, size);
|
||||
} else if (f == files[K_GRAB_KB]) {
|
||||
grabkb = _strtonum(files[K_GRAB_KB]->content, 0, 1);
|
||||
if (!grabkb) {
|
||||
XUngrabKeyboard(dpy, CurrentTime);
|
||||
XUnmapWindow(dpy, win);
|
||||
XSync(dpy, False);
|
||||
} else
|
||||
update();
|
||||
} else if (f == files[K_LOOKUP]) {
|
||||
update();
|
||||
}
|
||||
check_event(0);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_before_read(IXPServer * s, File * f)
|
||||
{
|
||||
if (f != files[K_SIZE])
|
||||
return;
|
||||
snprintf(buf, sizeof(buf), "%d,%d,%d,%d", krect.x, krect.y,
|
||||
krect.width, krect.height);
|
||||
if (f->content)
|
||||
free(f->content);
|
||||
f->content = strdup(buf);
|
||||
f->size = strlen(buf);
|
||||
}
|
||||
|
||||
static void
|
||||
run(char *size)
|
||||
{
|
||||
XSetWindowAttributes wa;
|
||||
XGCValues gcv;
|
||||
|
||||
/* init */
|
||||
if (!(files[K_CTL] = ixp_create(ixps, "/ctl"))) {
|
||||
perror("wmikeys: cannot connect IXP server");
|
||||
exit(1);
|
||||
}
|
||||
files[K_CTL]->after_write = handle_after_write;
|
||||
files[K_LOOKUP] = ixp_create(ixps, "/lookup");
|
||||
files[K_LOOKUP]->after_write = handle_after_write;
|
||||
files[K_SIZE] = ixp_create(ixps, "/size");
|
||||
files[K_SIZE]->before_read = handle_before_read;
|
||||
files[K_SIZE]->after_write = handle_after_write;
|
||||
files[K_GRAB_KB] = wmii_create_ixpfile(ixps, "/grab-keyb", "0");
|
||||
files[K_GRAB_KB]->after_write = handle_after_write;
|
||||
files[K_TEXT_FONT] = wmii_create_ixpfile(ixps, "/box/style/text-font", BLITZ_FONT);
|
||||
files[K_TEXT_COLOR] = wmii_create_ixpfile(ixps, "/box/style/text-color", BLITZ_SEL_FG_COLOR);
|
||||
files[K_BG_COLOR] = wmii_create_ixpfile(ixps, "/box/style/bg-color", BLITZ_SEL_BG_COLOR);
|
||||
files[K_BORDER_COLOR] = wmii_create_ixpfile(ixps, "/box/style/border-color", BLITZ_SEL_BORDER_COLOR);
|
||||
|
||||
wa.override_redirect = 1;
|
||||
wa.background_pixmap = ParentRelative;
|
||||
wa.event_mask =
|
||||
ExposureMask | SubstructureRedirectMask | SubstructureNotifyMask;
|
||||
|
||||
root = RootWindow(dpy, screen_num);
|
||||
rect.x = rect.y = 0;
|
||||
rect.width = DisplayWidth(dpy, screen_num);
|
||||
rect.height = DisplayHeight(dpy, screen_num);
|
||||
krect.x = krect.y = -1;
|
||||
krect.width = krect.height = 0;
|
||||
blitz_strtorect(dpy, &rect, &krect, size);
|
||||
/* default is center position */
|
||||
if (!krect.width) {
|
||||
krect.width = 200;
|
||||
}
|
||||
if (!krect.height) {
|
||||
krect.height = 20;
|
||||
}
|
||||
center();
|
||||
|
||||
init_lock_modifiers(dpy, &valid_mask, &num_lock_mask);
|
||||
|
||||
win = XCreateWindow(dpy, RootWindow(dpy, screen_num), krect.x, krect.y,
|
||||
krect.width, krect.height, 0, DefaultDepth(dpy,
|
||||
screen_num),
|
||||
CopyFromParent, DefaultVisual(dpy, screen_num),
|
||||
CWOverrideRedirect | CWBackPixmap | CWEventMask,
|
||||
&wa);
|
||||
XDefineCursor(dpy, win, XCreateFontCursor(dpy, XC_left_ptr));
|
||||
XSync(dpy, False);
|
||||
|
||||
gcv.function = GXcopy;
|
||||
gcv.graphics_exposures = False;
|
||||
|
||||
gc = XCreateGC(dpy, win, 0, 0);
|
||||
|
||||
/* main event loop */
|
||||
run_server_with_fd_support(ixps, ConnectionNumber(dpy),
|
||||
check_event, 0);
|
||||
deinit_server(ixps);
|
||||
XFreeGC(dpy, gc);
|
||||
XCloseDisplay(dpy);
|
||||
}
|
||||
|
||||
static int
|
||||
dummy_error_handler(Display * dpy, XErrorEvent * err)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char size[64];
|
||||
int i;
|
||||
|
||||
/* command line args */
|
||||
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
|
||||
switch (argv[i][1]) {
|
||||
case 'v':
|
||||
fprintf(stdout, "%s", version[0]);
|
||||
exit(0);
|
||||
break;
|
||||
case 's':
|
||||
if (i + 1 < argc)
|
||||
sockfile = argv[++i];
|
||||
else
|
||||
usage();
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dpy = XOpenDisplay(0);
|
||||
if (!dpy) {
|
||||
fprintf(stderr, "%s", "wmikeys: cannot open display\n");
|
||||
exit(1);
|
||||
}
|
||||
XSetErrorHandler(dummy_error_handler);
|
||||
screen_num = DefaultScreen(dpy);
|
||||
size[0] = '\0';
|
||||
if (argc > i)
|
||||
_strlcpy(size, argv[i], sizeof(size));
|
||||
|
||||
ixps = wmii_setup_server(sockfile);
|
||||
|
||||
run(size);
|
||||
|
||||
return 0;
|
||||
}
|
42
cmd/wmimenu.1
Normal file
42
cmd/wmimenu.1
Normal file
@ -0,0 +1,42 @@
|
||||
.TH WMIMENU 1 wmii-3
|
||||
.SH NAME
|
||||
wmimenu \- window manager improved 2 menu
|
||||
.SH SYNOPSIS
|
||||
.B wmimenu
|
||||
.B \-s
|
||||
.I socketfile
|
||||
.SH DESCRIPTION
|
||||
.SS Overview
|
||||
.B wmimenu
|
||||
is a generic, highly customizable, and efficient menu for the X Window System,
|
||||
originally designed for
|
||||
.BR wmii (1).
|
||||
It supports arbitrary, user defined menu contents, which get executed.
|
||||
Like wmii,
|
||||
.B wmimenu
|
||||
also implements a socket-based fileserver, which is accessed to configure and
|
||||
interoperate with other components.
|
||||
.SS Options
|
||||
.TP
|
||||
.BI \-s " socketfile"
|
||||
specifies the socketfile that
|
||||
.B wmimenu
|
||||
should create.
|
||||
.TP
|
||||
.B \-v
|
||||
prints version information to stderr, then exits.
|
||||
.SS Customization
|
||||
.B wmimenu
|
||||
is customized through manipulating its filesystem namespace.
|
||||
In the default setup of
|
||||
.BR wmii (1)
|
||||
the namespace of
|
||||
.B wmimenu
|
||||
can be found in /menu.
|
||||
.SH SEE ALSO
|
||||
.BR wmibar (1),
|
||||
.BR wmifs (1),
|
||||
.BR wmii (1),
|
||||
.BR wmiiwm (1),
|
||||
.BR wmikeys (1),
|
||||
.BR wmir (1)
|
689
cmd/wmimenu.c
Normal file
689
cmd/wmimenu.c
Normal file
@ -0,0 +1,689 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/cursorfont.h>
|
||||
#include <X11/Xproto.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/keysym.h>
|
||||
|
||||
#include "wmii.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
/* array indexes for file pointers */
|
||||
typedef enum {
|
||||
M_CTL,
|
||||
M_SIZE,
|
||||
M_PRE_COMMAND,
|
||||
M_COMMAND,
|
||||
M_HISTORY,
|
||||
M_LOOKUP,
|
||||
M_TEXT_FONT,
|
||||
M_SELECTED_BG_COLOR,
|
||||
M_SELECTED_TEXT_COLOR,
|
||||
M_SELECTED_BORDER_COLOR,
|
||||
M_NORMAL_BG_COLOR,
|
||||
M_NORMAL_TEXT_COLOR,
|
||||
M_NORMAL_BORDER_COLOR,
|
||||
M_RETARDED,
|
||||
M_LAST
|
||||
} InputIndexes;
|
||||
|
||||
enum {
|
||||
OFF_NEXT, OFF_PREV, OFF_CURR, OFF_LAST
|
||||
};
|
||||
|
||||
static IXPServer *ixps = 0;
|
||||
static Display *dpy;
|
||||
static GC gc;
|
||||
static Window win;
|
||||
static XRectangle rect;
|
||||
static XRectangle mrect;
|
||||
static int screen_num;
|
||||
static char *sockfile = 0;
|
||||
static File *files[M_LAST];
|
||||
static File **items = 0;
|
||||
static size_t item_size = 0;
|
||||
static int item = 0;
|
||||
static int offset[OFF_LAST];
|
||||
static unsigned int cmdw = 0;
|
||||
static File **history = 0;
|
||||
static int sel_history = 0;
|
||||
static Pixmap pmap;
|
||||
static const int seek = 30; /* 30px */
|
||||
|
||||
static void check_event(Connection * c);
|
||||
static void draw_menu(void);
|
||||
static void handle_kpress(XKeyEvent * e);
|
||||
static void set_text(char *text);
|
||||
static void quit(void *obj, char *arg);
|
||||
static void display(void *obj, char *arg);
|
||||
static int update_items(char *prefix);
|
||||
|
||||
static Action acttbl[2] = {
|
||||
{"quit", quit},
|
||||
{"display", display}
|
||||
};
|
||||
|
||||
static char *version[] = {
|
||||
"wmimenu - window manager improved menu - " VERSION "\n"
|
||||
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
|
||||
};
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr, "%s",
|
||||
"usage: wmimenu [-s <socket file>] [-r] [-v] [<x>,<y>,<width>,<height>]\n"
|
||||
" -s socket file (default: /tmp/.ixp-$USER/wmimenu-%s-%s)\n"
|
||||
" -v version info\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
add_history(char *cmd)
|
||||
{
|
||||
char buf[MAX_BUF];
|
||||
snprintf(buf, MAX_BUF, "/history/%ld", (long) time(0));
|
||||
history = (File **) attach_item_begin((void **) history,
|
||||
wmii_create_ixpfile(ixps, buf,
|
||||
cmd),
|
||||
sizeof(File *));
|
||||
}
|
||||
|
||||
static void
|
||||
_exec(char *cmd)
|
||||
{
|
||||
char *rc = cmd;
|
||||
|
||||
if (!cmd || cmd[0] == '\0')
|
||||
return;
|
||||
|
||||
if (items && items[0]) {
|
||||
if ((item >= 0) && items[item] && items[item]->size)
|
||||
rc = cmd = items[item]->content;
|
||||
else if ((item == -1) && items[0]->size) /* autolight */
|
||||
rc = cmd = items[0]->content;
|
||||
}
|
||||
add_history(cmd);
|
||||
if (files[M_PRE_COMMAND]->content) {
|
||||
size_t len = strlen(cmd) + files[M_PRE_COMMAND]->size + 2;
|
||||
rc = emalloc(len);
|
||||
snprintf(rc, len, "%s %s", (char *) files[M_PRE_COMMAND]->content,
|
||||
cmd);
|
||||
}
|
||||
/* fallback */
|
||||
spawn(dpy, rc);
|
||||
/* cleanup */
|
||||
if (files[M_PRE_COMMAND]->content)
|
||||
free(rc);
|
||||
}
|
||||
|
||||
static void
|
||||
quit(void *obj, char *arg)
|
||||
{
|
||||
ixps->runlevel = SHUTDOWN;
|
||||
}
|
||||
|
||||
static void
|
||||
show()
|
||||
{
|
||||
set_text(0);
|
||||
XMapRaised(dpy, win);
|
||||
XSync(dpy, False);
|
||||
update_items(files[M_COMMAND]->content);
|
||||
draw_menu();
|
||||
while (XGrabKeyboard
|
||||
(dpy, RootWindow(dpy, screen_num), True, GrabModeAsync,
|
||||
GrabModeAsync, CurrentTime) != GrabSuccess)
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
static void
|
||||
hide()
|
||||
{
|
||||
XUngrabKeyboard(dpy, CurrentTime);
|
||||
XUnmapWindow(dpy, win);
|
||||
XSync(dpy, False);
|
||||
}
|
||||
|
||||
static void
|
||||
display(void *obj, char *arg)
|
||||
{
|
||||
if (!arg)
|
||||
return;
|
||||
if (_strtonum(arg, 0, 1))
|
||||
show();
|
||||
else
|
||||
hide();
|
||||
check_event(0);
|
||||
}
|
||||
|
||||
void
|
||||
set_text(char *text)
|
||||
{
|
||||
if (files[M_COMMAND]->content) {
|
||||
free(files[M_COMMAND]->content);
|
||||
files[M_COMMAND]->content = 0;
|
||||
files[M_COMMAND]->size = 0;
|
||||
}
|
||||
if (text && strlen(text)) {
|
||||
files[M_COMMAND]->content = strdup(text);
|
||||
files[M_COMMAND]->size = strlen(text);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
update_offsets()
|
||||
{
|
||||
int i;
|
||||
XFontStruct *font;
|
||||
unsigned int w = cmdw + 2 * seek;
|
||||
|
||||
if (!items)
|
||||
return;
|
||||
|
||||
font = blitz_getfont(dpy, files[M_TEXT_FONT]->content);
|
||||
|
||||
/* calc next offset */
|
||||
for (i = offset[OFF_CURR]; items[i]; i++) {
|
||||
w += XTextWidth(font, items[i]->content, strlen(items[i]->content)) + mrect.height;
|
||||
if (w > mrect.width)
|
||||
break;
|
||||
}
|
||||
offset[OFF_NEXT] = i;
|
||||
|
||||
|
||||
w = cmdw + 2 * seek;
|
||||
for (i = offset[OFF_CURR] - 1; i >= 0; i--) {
|
||||
w += XTextWidth(font, items[i]->content, strlen(items[i]->content)) + mrect.height;
|
||||
if (w > mrect.width)
|
||||
break;
|
||||
}
|
||||
offset[OFF_PREV] = i + 1;
|
||||
XFreeFont(dpy, font);
|
||||
}
|
||||
|
||||
static int
|
||||
update_items(char *pattern)
|
||||
{
|
||||
size_t plen = pattern ? strlen(pattern) : 0, size = 0, max = 0,
|
||||
len;
|
||||
int matched = pattern ? plen == 0 : 1;
|
||||
XFontStruct *font;
|
||||
File *f, *p, *maxitem = 0;
|
||||
|
||||
cmdw = 0;
|
||||
item = -1;
|
||||
offset[OFF_CURR] = offset[OFF_PREV] = offset[OFF_NEXT] = 0;
|
||||
|
||||
if (!files[M_LOOKUP]->content)
|
||||
return 0;
|
||||
f = ixp_walk(ixps, files[M_LOOKUP]->content);
|
||||
if (!f || !is_directory(f))
|
||||
return 0;
|
||||
|
||||
font = blitz_getfont(dpy, files[M_TEXT_FONT]->content);
|
||||
|
||||
/* build new items */
|
||||
for (p = f->content; p; p = p->next) {
|
||||
size++;
|
||||
len = strlen(p->name);
|
||||
if (max < len) {
|
||||
maxitem = p;
|
||||
max = len;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxitem) {
|
||||
if (files[M_RETARDED]->content)
|
||||
free(files[M_RETARDED]->content);
|
||||
files[M_RETARDED]->content = strdup(maxitem->name);
|
||||
files[M_RETARDED]->size = max;
|
||||
cmdw = XTextWidth(font, maxitem->name, max) + mrect.height;
|
||||
}
|
||||
if (size > item_size) {
|
||||
/* stores always the biggest amount of items in memory */
|
||||
if (items)
|
||||
free((File **) items);
|
||||
items = 0;
|
||||
item_size = size;
|
||||
if (item_size)
|
||||
items = (File **) emalloc((item_size + 1) * sizeof(File *));
|
||||
}
|
||||
size = 0;
|
||||
|
||||
for (p = f->content; p; p = p->next) {
|
||||
if (!p->content)
|
||||
continue; /* ignore bogus files */
|
||||
if (matched || !strncmp(pattern, p->name, plen)) {
|
||||
items[size++] = p;
|
||||
p->parent = 0; /* HACK to prevent doubled items */
|
||||
}
|
||||
}
|
||||
|
||||
for (p = f->content; p; p = p->next) {
|
||||
if (!p->content)
|
||||
continue; /* ignore bogus files */
|
||||
if (p->parent && strstr(p->name, pattern))
|
||||
items[size++] = p;
|
||||
else
|
||||
p->parent = f; /* restore HACK */
|
||||
}
|
||||
items[size] = 0;
|
||||
update_offsets();
|
||||
XFreeFont(dpy, font);
|
||||
return size;
|
||||
}
|
||||
|
||||
/* creates draw structs for menu mode drawing */
|
||||
static void
|
||||
draw_menu()
|
||||
{
|
||||
Draw d = {0};
|
||||
unsigned int offx = 0;
|
||||
int i = 0;
|
||||
XFontStruct *font = blitz_getfont(dpy, files[M_TEXT_FONT]->content);
|
||||
|
||||
d.gc = gc;
|
||||
d.drawable = pmap;
|
||||
d.rect = mrect;
|
||||
d.rect.x = 0;
|
||||
d.rect.y = 0;
|
||||
d.bg = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_BG_COLOR]->content);
|
||||
d.border = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_BORDER_COLOR]->content);
|
||||
blitz_drawlabelnoborder(dpy, &d);
|
||||
|
||||
/* print command */
|
||||
d.align = WEST;
|
||||
d.font = font;
|
||||
d.fg = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_TEXT_COLOR]->content);
|
||||
d.data = files[M_COMMAND]->content;
|
||||
if (cmdw && items && items[0])
|
||||
d.rect.width = cmdw;
|
||||
offx += d.rect.width;
|
||||
blitz_drawlabelnoborder(dpy, &d);
|
||||
|
||||
d.align = CENTER;
|
||||
if (items && items[0]) {
|
||||
d.bg = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_BG_COLOR]->content);
|
||||
d.fg = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_TEXT_COLOR]->content);
|
||||
d.data = offset[OFF_CURR] ? "<" : 0;
|
||||
d.rect.x = offx;
|
||||
d.rect.width = seek;
|
||||
offx += d.rect.width;
|
||||
blitz_drawlabelnoborder(dpy, &d);
|
||||
|
||||
/* determine maximum items */
|
||||
for (i = offset[OFF_CURR]; items[i] && (i < offset[OFF_NEXT]); i++) {
|
||||
d.data = items[i]->name;
|
||||
d.rect.x = offx;
|
||||
d.rect.width = XTextWidth(d.font, d.data, strlen(d.data)) + mrect.height;
|
||||
offx += d.rect.width;
|
||||
if (i == item) {
|
||||
d.bg = blitz_loadcolor(dpy, screen_num, files[M_SELECTED_BG_COLOR]->content);
|
||||
d.fg = blitz_loadcolor(dpy, screen_num, files[M_SELECTED_TEXT_COLOR]->content);
|
||||
d.border = blitz_loadcolor(dpy, screen_num, files[M_SELECTED_BORDER_COLOR]->content);
|
||||
blitz_drawlabel(dpy, &d);
|
||||
} else if (!i && item == -1) {
|
||||
/* fg and bg are inverted */
|
||||
d.fg = blitz_loadcolor(dpy, screen_num, files[M_SELECTED_BG_COLOR]->content);
|
||||
d.bg = blitz_loadcolor(dpy, screen_num, files[M_SELECTED_TEXT_COLOR]->content);
|
||||
d.border = blitz_loadcolor(dpy, screen_num, files[M_SELECTED_BORDER_COLOR]->content);
|
||||
blitz_drawlabel(dpy, &d);
|
||||
} else {
|
||||
d.bg = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_BG_COLOR]->content);
|
||||
d.fg = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_TEXT_COLOR]->content);
|
||||
d.border = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_BORDER_COLOR]->content);
|
||||
blitz_drawlabelnoborder(dpy, &d);
|
||||
}
|
||||
}
|
||||
|
||||
d.bg = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_BG_COLOR]->content);
|
||||
d.fg = blitz_loadcolor(dpy, screen_num, files[M_NORMAL_TEXT_COLOR]->content);
|
||||
d.data = items[i] ? ">" : 0;
|
||||
d.rect.x = mrect.width - seek;
|
||||
d.rect.width = seek;
|
||||
blitz_drawlabelnoborder(dpy, &d);
|
||||
}
|
||||
XCopyArea(dpy, pmap, win, gc, 0, 0, mrect.width, mrect.height, 0, 0);
|
||||
XSync(dpy, False);
|
||||
XFreeFont(dpy, font);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_kpress(XKeyEvent * e)
|
||||
{
|
||||
KeySym ksym;
|
||||
char buf[32];
|
||||
int idx, num;
|
||||
static char text[4096];
|
||||
size_t len = 0;
|
||||
|
||||
text[0] = '\0';
|
||||
if (files[M_COMMAND]->content) {
|
||||
_strlcpy(text, files[M_COMMAND]->content, sizeof(text));
|
||||
len = strlen(text);
|
||||
}
|
||||
buf[0] = '\0';
|
||||
num = XLookupString(e, buf, sizeof(buf), &ksym, 0);
|
||||
|
||||
if (IsFunctionKey(ksym) || IsKeypadKey(ksym)
|
||||
|| IsMiscFunctionKey(ksym) || IsPFKey(ksym)
|
||||
|| IsPrivateKeypadKey(ksym))
|
||||
return;
|
||||
|
||||
/* first check if a control mask is omitted */
|
||||
if (e->state & ShiftMask) {
|
||||
if (ksym == XK_ISO_Left_Tab)
|
||||
ksym = XK_Left;
|
||||
} else if (e->state & ControlMask) {
|
||||
switch (ksym) {
|
||||
case XK_E:
|
||||
case XK_e:
|
||||
ksym = XK_End;
|
||||
break;
|
||||
case XK_H:
|
||||
case XK_h:
|
||||
ksym = XK_BackSpace;
|
||||
break;
|
||||
case XK_J:
|
||||
case XK_j:
|
||||
ksym = XK_Return;
|
||||
break;
|
||||
case XK_U:
|
||||
case XK_u:
|
||||
set_text(0);
|
||||
update_items(0);
|
||||
draw_menu();
|
||||
return;
|
||||
break;
|
||||
default: /* ignore other control sequences */
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (ksym) {
|
||||
case XK_Left:
|
||||
if (!items || !items[0])
|
||||
return;
|
||||
if (item > 0) {
|
||||
item--;
|
||||
set_text(items[item]->name);
|
||||
} else
|
||||
return;
|
||||
break;
|
||||
case XK_Right:
|
||||
case XK_Tab:
|
||||
if (!items || !items[0])
|
||||
return;
|
||||
if (items[item + 1]) {
|
||||
item++;
|
||||
set_text(items[item]->name);
|
||||
} else
|
||||
return;
|
||||
break;
|
||||
case XK_Down:
|
||||
if (history) {
|
||||
set_text(history[sel_history]->content);
|
||||
idx = index_prev_item((void **) history, history[sel_history]);
|
||||
if (idx >= 0)
|
||||
sel_history = idx;
|
||||
}
|
||||
update_items(files[M_COMMAND]->content);
|
||||
break;
|
||||
case XK_Up:
|
||||
if (history) {
|
||||
set_text(history[sel_history]->content);
|
||||
idx = index_next_item((void **) history, history[sel_history]);
|
||||
if (idx >= 0)
|
||||
sel_history = idx;
|
||||
}
|
||||
update_items(files[M_COMMAND]->content);
|
||||
break;
|
||||
case XK_Return:
|
||||
if (items && items[0]) {
|
||||
if (item >= 0)
|
||||
_exec(items[item]->name);
|
||||
else
|
||||
_exec(items[0]->name);
|
||||
} else if (text)
|
||||
_exec(text);
|
||||
case XK_Escape:
|
||||
hide();
|
||||
break;
|
||||
case XK_BackSpace:
|
||||
if (len) {
|
||||
int size = 0;
|
||||
size_t i = len;
|
||||
for (size = 0; items && items[size]; size++);
|
||||
if (i) {
|
||||
do
|
||||
text[--i] = '\0';
|
||||
while (size && i && size == update_items(text));
|
||||
}
|
||||
set_text(text);
|
||||
update_items(files[M_COMMAND]->content);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ((num == 1) && !iscntrl((int) buf[0])) {
|
||||
buf[num] = '\0';
|
||||
if (len > 0)
|
||||
_strlcat(text, buf, sizeof(text));
|
||||
else
|
||||
_strlcpy(text, buf, sizeof(text));
|
||||
set_text(text);
|
||||
update_items(files[M_COMMAND]->content);
|
||||
}
|
||||
}
|
||||
if (items && item > 0) {
|
||||
if (item < offset[OFF_CURR]) {
|
||||
offset[OFF_CURR] = offset[OFF_PREV];
|
||||
update_offsets();
|
||||
} else if (item >= offset[OFF_NEXT]) {
|
||||
offset[OFF_CURR] = offset[OFF_NEXT];
|
||||
update_offsets();
|
||||
}
|
||||
}
|
||||
draw_menu();
|
||||
}
|
||||
|
||||
static void
|
||||
check_event(Connection * c)
|
||||
{
|
||||
XEvent ev;
|
||||
|
||||
while (XPending(dpy)) {
|
||||
XNextEvent(dpy, &ev);
|
||||
switch (ev.type) {
|
||||
case KeyPress:
|
||||
handle_kpress(&ev.xkey);
|
||||
break;
|
||||
case Expose:
|
||||
if (ev.xexpose.count == 0) {
|
||||
draw_menu();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_after_write(IXPServer * s, File * f)
|
||||
{
|
||||
int i;
|
||||
size_t len;
|
||||
|
||||
if (files[M_CTL] == f) {
|
||||
for (i = 0; i < 2; i++) {
|
||||
len = strlen(acttbl[i].name);
|
||||
if (!strncmp(acttbl[i].name, (char *) f->content, len)) {
|
||||
if (strlen(f->content) > len) {
|
||||
acttbl[i].func(0, &((char *) f->content)[len + 1]);
|
||||
} else {
|
||||
acttbl[i].func(0, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (files[M_SIZE] == f) {
|
||||
char *size = files[M_SIZE]->content;
|
||||
if (size && strrchr(size, ',')) {
|
||||
blitz_strtorect(dpy, &rect, &mrect, size);
|
||||
XFreePixmap(dpy, pmap);
|
||||
XMoveResizeWindow(dpy, win, mrect.x, mrect.y,
|
||||
mrect.width, mrect.height);
|
||||
XSync(dpy, False);
|
||||
pmap = XCreatePixmap(dpy, win, mrect.width, mrect.height,
|
||||
DefaultDepth(dpy, screen_num));
|
||||
XSync(dpy, False);
|
||||
draw_menu();
|
||||
}
|
||||
} else if (files[M_COMMAND] == f) {
|
||||
update_items(files[M_COMMAND]->content);
|
||||
draw_menu();
|
||||
}
|
||||
check_event(0);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_before_read(IXPServer * s, File * f)
|
||||
{
|
||||
char buf[64];
|
||||
if (f != files[M_SIZE])
|
||||
return;
|
||||
snprintf(buf, sizeof(buf), "%d,%d,%d,%d", mrect.x, mrect.y,
|
||||
mrect.width, mrect.height);
|
||||
if (f->content)
|
||||
free(f->content);
|
||||
f->content = strdup(buf);
|
||||
f->size = strlen(buf);
|
||||
}
|
||||
|
||||
static void
|
||||
run(char *size)
|
||||
{
|
||||
XSetWindowAttributes wa;
|
||||
XGCValues gcv;
|
||||
|
||||
/* init */
|
||||
if (!(files[M_CTL] = ixp_create(ixps, "/ctl"))) {
|
||||
perror("wmimenu: cannot connect IXP server");
|
||||
exit(1);
|
||||
}
|
||||
files[M_CTL]->after_write = handle_after_write;
|
||||
files[M_SIZE] = ixp_create(ixps, "/size");
|
||||
files[M_SIZE]->before_read = handle_before_read;
|
||||
files[M_SIZE]->after_write = handle_after_write;
|
||||
files[M_PRE_COMMAND] = ixp_create(ixps, "/precmd");
|
||||
files[M_COMMAND] = ixp_create(ixps, "/cmd");
|
||||
files[M_COMMAND]->after_write = handle_after_write;
|
||||
files[M_HISTORY] = ixp_create(ixps, "/history");
|
||||
add_history("");
|
||||
files[M_LOOKUP] = ixp_create(ixps, "/lookup");
|
||||
files[M_TEXT_FONT] = wmii_create_ixpfile(ixps, "/style/text-font", BLITZ_FONT);
|
||||
files[M_SELECTED_BG_COLOR] = wmii_create_ixpfile(ixps, "/sel-style/bg-color", BLITZ_SEL_BG_COLOR);
|
||||
files[M_SELECTED_TEXT_COLOR] = wmii_create_ixpfile(ixps, "/sel-style/text-color", BLITZ_SEL_FG_COLOR);
|
||||
files[M_SELECTED_BORDER_COLOR] = wmii_create_ixpfile(ixps, "/sel-style/border-color", BLITZ_SEL_BORDER_COLOR);
|
||||
files[M_NORMAL_BG_COLOR] = wmii_create_ixpfile(ixps, "/norm-style/bg-color", BLITZ_NORM_BG_COLOR);
|
||||
files[M_NORMAL_TEXT_COLOR] = wmii_create_ixpfile(ixps, "/norm-style/text-color", BLITZ_NORM_FG_COLOR);
|
||||
files[M_NORMAL_BORDER_COLOR] = wmii_create_ixpfile(ixps, "/norm-style/border-color", BLITZ_NORM_BORDER_COLOR);
|
||||
files[M_RETARDED] = ixp_create(ixps, "/retarded");
|
||||
|
||||
wa.override_redirect = 1;
|
||||
wa.background_pixmap = ParentRelative;
|
||||
wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask
|
||||
| SubstructureRedirectMask | SubstructureNotifyMask;
|
||||
|
||||
rect.x = rect.y = 0;
|
||||
rect.width = DisplayWidth(dpy, screen_num);
|
||||
rect.height = DisplayHeight(dpy, screen_num);
|
||||
blitz_strtorect(dpy, &rect, &mrect, size);
|
||||
if (!mrect.width)
|
||||
mrect.width = DisplayWidth(dpy, screen_num);
|
||||
if (!mrect.height)
|
||||
mrect.height = 40;
|
||||
|
||||
win = XCreateWindow(dpy, RootWindow(dpy, screen_num), mrect.x, mrect.y,
|
||||
mrect.width, mrect.height, 0, DefaultDepth(dpy,
|
||||
screen_num),
|
||||
CopyFromParent, DefaultVisual(dpy, screen_num),
|
||||
CWOverrideRedirect | CWBackPixmap | CWEventMask,
|
||||
&wa);
|
||||
XDefineCursor(dpy, win, XCreateFontCursor(dpy, XC_xterm));
|
||||
XSync(dpy, False);
|
||||
|
||||
/* window pixmap */
|
||||
gcv.function = GXcopy;
|
||||
gcv.graphics_exposures = False;
|
||||
|
||||
gc = XCreateGC(dpy, win, 0, 0);
|
||||
pmap =
|
||||
XCreatePixmap(dpy, win, mrect.width, mrect.height,
|
||||
DefaultDepth(dpy, screen_num));
|
||||
|
||||
/* main event loop */
|
||||
run_server_with_fd_support(ixps, ConnectionNumber(dpy),
|
||||
check_event, 0);
|
||||
deinit_server(ixps);
|
||||
XFreePixmap(dpy, pmap);
|
||||
XFreeGC(dpy, gc);
|
||||
XCloseDisplay(dpy);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char size[64];
|
||||
int i;
|
||||
|
||||
/* command line args */
|
||||
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
|
||||
switch (argv[i][1]) {
|
||||
case 'v':
|
||||
fprintf(stdout, "%s", version[0]);
|
||||
exit(0);
|
||||
break;
|
||||
case 's':
|
||||
if (i + 1 < argc)
|
||||
sockfile = argv[++i];
|
||||
else
|
||||
usage();
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dpy = XOpenDisplay(0);
|
||||
if (!dpy) {
|
||||
fprintf(stderr, "%s", "wmimenu: cannot open display\n");
|
||||
exit(1);
|
||||
}
|
||||
screen_num = DefaultScreen(dpy);
|
||||
|
||||
size[0] = '\0';
|
||||
if (argc > i)
|
||||
_strlcpy(size, argv[i], sizeof(size));
|
||||
|
||||
ixps = wmii_setup_server(sockfile);
|
||||
items = 0;
|
||||
|
||||
run(size);
|
||||
|
||||
return 0;
|
||||
}
|
95
cmd/wmiplumb.c
Normal file
95
cmd/wmiplumb.c
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
static char *version[] = {
|
||||
"wmiplumb - window manager improved plumb - " VERSION "\n"
|
||||
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
|
||||
};
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr, "%s\n",
|
||||
"usage: wmiplumb [-v]\n" " -v version info\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
print_sel(Display * dpy, Window w, XSelectionEvent * e)
|
||||
{
|
||||
Atom typeret;
|
||||
int format;
|
||||
unsigned long nitems, bytesleft;
|
||||
unsigned char *data;
|
||||
|
||||
XGetWindowProperty(dpy, w, e->property, 0L, 4096L, False,
|
||||
AnyPropertyType, &typeret, &format,
|
||||
&nitems, &bytesleft, &data);
|
||||
if (format == 8) {
|
||||
int i;
|
||||
for (i = 0; i < nitems; i++)
|
||||
putchar(data[i]);
|
||||
putchar('\n');
|
||||
}
|
||||
XDeleteProperty(dpy, w, e->property);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
Display *dpy;
|
||||
Atom xa_clip_string;
|
||||
Window w;
|
||||
XEvent ev;
|
||||
int pdone = 0;
|
||||
|
||||
|
||||
/* command line args */
|
||||
if (argc > 1) {
|
||||
if (!strncmp(argv[1], "-v", 3)) {
|
||||
fprintf(stdout, "%s", version[0]);
|
||||
exit(0);
|
||||
} else
|
||||
usage();
|
||||
}
|
||||
dpy = XOpenDisplay(0);
|
||||
if (!dpy) {
|
||||
fprintf(stderr, "%s", "wmiplumb: cannot open display\n");
|
||||
exit(1);
|
||||
}
|
||||
xa_clip_string = XInternAtom(dpy, "PLUMB_STRING", False);
|
||||
w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 10, 10, 200, 200,
|
||||
1, CopyFromParent, CopyFromParent);
|
||||
while (1 && !pdone) {
|
||||
XConvertSelection(dpy, XA_PRIMARY, XA_STRING, xa_clip_string,
|
||||
w, CurrentTime);
|
||||
XFlush(dpy);
|
||||
XNextEvent(dpy, &ev);
|
||||
switch (ev.type) {
|
||||
case SelectionNotify:
|
||||
if (ev.xselection.property != None)
|
||||
print_sel(dpy, w, &ev.xselection);
|
||||
else
|
||||
putchar('\n');
|
||||
XDestroyWindow(dpy, w);
|
||||
XCloseDisplay(dpy);
|
||||
pdone = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
/*
|
||||
* XDestroyWindow(dpy, w); XCloseDisplay(dpy); return 1;
|
||||
*/
|
||||
}
|
71
cmd/wmir.1
Normal file
71
cmd/wmir.1
Normal file
@ -0,0 +1,71 @@
|
||||
.TH WMIR 1 wmii-3
|
||||
.SH NAME
|
||||
wmir \- window manager improved 2 remote
|
||||
.SH SYNOPSIS
|
||||
.B wmir
|
||||
.RB [ \-s
|
||||
.IR socketfile ]
|
||||
.I action
|
||||
.I action_arg
|
||||
[...]
|
||||
.br
|
||||
.B wmir
|
||||
.RB [ \-s
|
||||
.IR socketfile ]
|
||||
.B \-f
|
||||
.br
|
||||
.B wmir
|
||||
.B \-v
|
||||
.SH DESCRIPTION
|
||||
.SS Overview
|
||||
.B wmir
|
||||
is a client to access wmi* fileservers for the command line and
|
||||
scripts. It can be used to configure wmii(1).
|
||||
.SS Options
|
||||
.TP
|
||||
.BI \-s " socketfile"
|
||||
lets you specify the socketfile to which
|
||||
.B wmir
|
||||
a connection will be established. If the environment variable
|
||||
.B WMIR_SOCKET
|
||||
is set and points to a socket file, wmir will use that file, if this
|
||||
option is not supplied.
|
||||
.TP
|
||||
.B \-f
|
||||
reads from stdin, useful for interactive wmir sessions or for
|
||||
scripts that write/read a bunch of data, because this speeds things up
|
||||
due to missing process creation/destruction, ie., a hack.
|
||||
.TP
|
||||
.B \-v
|
||||
prints version information to stderr, then exits.
|
||||
.TP
|
||||
The syntax of the actions is as follows:
|
||||
.TP
|
||||
.B create <path/file> [<value>]
|
||||
creates file
|
||||
.TP
|
||||
.B write <file> <value>
|
||||
writes value to a file
|
||||
.TP
|
||||
.B read <path/file>
|
||||
reads file or directory contents
|
||||
.TP
|
||||
.B remove <path/file>
|
||||
removes file or directory tree
|
||||
.SH ENVIRONMENT
|
||||
.TP
|
||||
WMIR_SOCKET
|
||||
See above.
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
$ wmir read /
|
||||
This gives you an idea about what the wmii filesystem currently looks like.
|
||||
.TP
|
||||
$ wmir write /wm/ctl quit
|
||||
.SH SEE ALSO
|
||||
.BR wmibar (1),
|
||||
.BR wmifs (1),
|
||||
.BR wmii (1),
|
||||
.BR wmiiwm (1),
|
||||
.BR wmikeys (1),
|
||||
.BR wmimenu (1)
|
228
cmd/wmir.c
Normal file
228
cmd/wmir.c
Normal file
@ -0,0 +1,228 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ixp.h"
|
||||
|
||||
static IXPClient *c;
|
||||
static int exit_code = 0;
|
||||
|
||||
static char *version[] = {
|
||||
"wmir - window manager improved remote - " VERSION "\n"
|
||||
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
|
||||
};
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr, "%s",
|
||||
"usage: wmir [-s <socket file>] [-v] <action> <action_arg> [...]\n"
|
||||
" -s socket file (default: $WMIR_SOCKET)\n"
|
||||
" -f read actions from stdin\n"
|
||||
" -v version info\n"
|
||||
"actions:\n"
|
||||
" create <file> [<content>] -- creates and optionally writes content to a file\n"
|
||||
" write <file> <content> -- writes content to a file\n"
|
||||
" read <directory/file> -- reads file or directory contents\n"
|
||||
" remove <directory/file> -- removes file or directory, use with care!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
perform(char *action, char *file, char *content)
|
||||
{
|
||||
size_t out_len = 0;
|
||||
char output[2050];
|
||||
int crt, fd = -1;
|
||||
|
||||
if (!action)
|
||||
return;
|
||||
|
||||
crt = !strncmp(action, "create", 7);
|
||||
if (!strncmp(action, "write", 6) || crt) {
|
||||
if (!file)
|
||||
return;
|
||||
/* create file first */
|
||||
if (crt) {
|
||||
c->create(c, file);
|
||||
if (c->errstr) {
|
||||
fprintf(stderr, "wmir: error: create %s: %s\n", file,
|
||||
c->errstr);
|
||||
exit_code = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!content)
|
||||
return;
|
||||
fd = c->open(c, file);
|
||||
if (c->errstr) {
|
||||
fprintf(stderr, "wmir: error: open %s: %s\n", file, c->errstr);
|
||||
exit_code = 1;
|
||||
return;
|
||||
}
|
||||
c->write(c, fd, content, strlen(content));
|
||||
if (c->errstr) {
|
||||
fprintf(stderr, "wmir: error: write %s: %s\n", file,
|
||||
c->errstr);
|
||||
exit_code = 1;
|
||||
if (!strncmp(c->errstr, DEAD_SERVER, strlen(DEAD_SERVER) + 1))
|
||||
return;
|
||||
}
|
||||
} else if (!strncmp(action, "read", 5)) {
|
||||
if (!file)
|
||||
return;
|
||||
fd = c->open(c, file);
|
||||
if (c->errstr) {
|
||||
fprintf(stderr, "wmir: error: open %s: %s\n", file, c->errstr);
|
||||
exit_code = 1;
|
||||
return;
|
||||
}
|
||||
do {
|
||||
out_len = c->read(c, fd, output, 2048);
|
||||
if (c->errstr) {
|
||||
fprintf(stderr, "wmir: error: read %s: %s\n", file,
|
||||
c->errstr);
|
||||
exit_code = 1;
|
||||
if (!strncmp
|
||||
(c->errstr, DEAD_SERVER, strlen(DEAD_SERVER) + 1))
|
||||
return;
|
||||
break;
|
||||
}
|
||||
output[out_len] = '\0';
|
||||
fprintf(stdout, "%s", output);
|
||||
}
|
||||
while (out_len == 2048);
|
||||
fprintf(stdout, "%s", "\n");
|
||||
} else if (!strncmp(action, "remove", 7)) {
|
||||
if (!file)
|
||||
return;
|
||||
c->remove(c, file);
|
||||
if (c->errstr) {
|
||||
fprintf(stderr, "wmir: error: remove %s: %s\n", file,
|
||||
c->errstr);
|
||||
exit_code = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (fd != -1) {
|
||||
c->close(c, fd);
|
||||
if (c->errstr) {
|
||||
fprintf(stderr, "wmir: error: close %s: %s\n", file,
|
||||
c->errstr);
|
||||
exit_code = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i = 0, read_stdin = 0;
|
||||
char line[4096], *p;
|
||||
char *sockfile = getenv("WMIR_SOCKET");
|
||||
|
||||
/* command line args */
|
||||
if (argc > 1) {
|
||||
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
|
||||
switch (argv[i][1]) {
|
||||
case 'v':
|
||||
fprintf(stderr, "%s", version[0]);
|
||||
exit(0);
|
||||
break;
|
||||
case 's':
|
||||
if (i + 1 < argc) {
|
||||
sockfile = argv[++i];
|
||||
} else {
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
read_stdin = 1;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((argc <= 1) || (!read_stdin && (i + 1) >= argc)) {
|
||||
fprintf(stderr, "%s", "wmir: arguments: ");
|
||||
for (i = 1; i < argc; i++)
|
||||
fprintf(stderr, "%s, ", argv[i]);
|
||||
fprintf(stderr, "%s", "\n");
|
||||
usage();
|
||||
}
|
||||
if (!sockfile) {
|
||||
fprintf(stderr, "%s",
|
||||
"wmir: error: WMIR_SOCKET environment not set\n");
|
||||
usage();
|
||||
}
|
||||
/* open socket */
|
||||
if (!(c = init_client(sockfile))) {
|
||||
fprintf(stderr, "wmir: cannot connect to server '%s'\n", sockfile);
|
||||
exit(1);
|
||||
}
|
||||
if (read_stdin) {
|
||||
/* simple shell */
|
||||
char *action, *file, *content;
|
||||
while (fgets(line, 4096, stdin)) {
|
||||
p = line;
|
||||
while (*p != '\0' && (*p == ' ' || *p == '\t'))
|
||||
p++;
|
||||
if (*p == '\0')
|
||||
continue; /* empty line */
|
||||
if (strncmp(p, "create ", 7) &&
|
||||
strncmp(p, "write ", 6) &&
|
||||
strncmp(p, "read ", 5) && strncmp(p, "remove ", 7))
|
||||
continue;
|
||||
|
||||
action = p;
|
||||
while (*p != '\0' && *p != ' ' && *p != '\t' && *p != '\n')
|
||||
p++;
|
||||
if (*p == '\0' || *p == '\n')
|
||||
continue; /* ignore bogus command */
|
||||
*p = '\0';
|
||||
p++;
|
||||
while (*p != '\0' && (*p == ' ' || *p == '\t'))
|
||||
p++;
|
||||
if (*p == '\0')
|
||||
continue; /* ignore bogus command */
|
||||
file = p;
|
||||
while (*p != '\0' && *p != ' ' && *p != '\t' && *p != '\n')
|
||||
p++;
|
||||
if (*p == '\0' || *p == '\n') {
|
||||
content = 0;
|
||||
*p = '\0';
|
||||
} else {
|
||||
*p = '\0';
|
||||
p++;
|
||||
content = p;
|
||||
}
|
||||
if (file[0] == '\0')
|
||||
continue;
|
||||
if (content) {
|
||||
static size_t len;
|
||||
if ((len = strlen(content)))
|
||||
content[len - 1] = '\0';
|
||||
}
|
||||
perform(action, file, content);
|
||||
if (c->errstr)
|
||||
fprintf(stderr, "wmir: error: read %s: %s\n", file,
|
||||
c->errstr);
|
||||
}
|
||||
} else {
|
||||
perform(argv[i], argv[i + 1], (i + 2) < argc ? argv[i + 2] : 0);
|
||||
}
|
||||
|
||||
if (c->errstr) {
|
||||
deinit_client(c);
|
||||
exit_code = 1;
|
||||
}
|
||||
return exit_code;
|
||||
}
|
252
cmd/wmir2.c
Normal file
252
cmd/wmir2.c
Normal file
@ -0,0 +1,252 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../libixp2/ixp.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int (*cmd) (char **argv);
|
||||
int min_argc;
|
||||
} Command;
|
||||
|
||||
static int xcreate(char **argv);
|
||||
static int xread(char **argv);
|
||||
static int xwrite(char **argv);
|
||||
static int xremove(char **argv);
|
||||
static Command cmds[] = {
|
||||
{"create", xcreate, 2},
|
||||
{"read", xread, 1},
|
||||
{"write", xwrite, 2},
|
||||
{"remove", xremove, 1},
|
||||
{0, 0}
|
||||
};
|
||||
static IXPClient c = {0};
|
||||
|
||||
static char *version[] = {
|
||||
"wmir - window manager improved remote - " VERSION "\n"
|
||||
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
|
||||
};
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr, "%s",
|
||||
"usage: wmir [-s <socket file>] [-v] <command> <args> [...]\n"
|
||||
" -s socket file (default: $WMIR_SOCKET)\n"
|
||||
" -f read commands from stdin\n"
|
||||
" -v version info\n"
|
||||
"commands:\n"
|
||||
" create <file> <content> -- creates file\n"
|
||||
" read <file/directory> -- reads file/directory contents\n"
|
||||
" write <file> <content> -- writes content to a file\n"
|
||||
" remove <file/directory> -- removes file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static u32
|
||||
write_data(u32 fid, u8 * data, u32 count)
|
||||
{
|
||||
u32 len, i, runs = count / c.fcall.iounit;
|
||||
|
||||
for (i = 0; i <= runs; i++) {
|
||||
/* write */
|
||||
len = count - (i * c.fcall.iounit);
|
||||
if (len > c.fcall.iounit)
|
||||
len = c.fcall.iounit;
|
||||
if (ixp_client_write
|
||||
(&c, fid, i * c.fcall.iounit, len,
|
||||
&data[i * c.fcall.iounit]) != count) {
|
||||
fprintf(stderr, "wmir: cannot write file: %s\n", c.errstr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int
|
||||
xcreate(char **argv)
|
||||
{
|
||||
u32 fid;
|
||||
char *p = strrchr(argv[0], '/');
|
||||
|
||||
fid = c.root_fid << 2;
|
||||
/* walk to bottom-most directory */
|
||||
*p = 0;
|
||||
if (!ixp_client_walk(&c, fid, argv[0])) {
|
||||
fprintf(stderr, "wmir: cannot walk to %s: %s\n", argv[0],
|
||||
c.errstr);
|
||||
return 1;
|
||||
}
|
||||
/* create */
|
||||
p++;
|
||||
if (!ixp_client_create(&c, fid, p, (u32) 0xff, IXP_OWRITE)) {
|
||||
fprintf(stderr, "wmir: cannot create file: %s\n", c.errstr);
|
||||
return 1;
|
||||
}
|
||||
write_data(fid, (u8 *) argv[1], strlen(argv[1]));
|
||||
return !ixp_client_close(&c, fid);
|
||||
}
|
||||
|
||||
static int
|
||||
xwrite(char **argv)
|
||||
{
|
||||
/* open */
|
||||
u32 fid = c.root_fid << 2;
|
||||
if (!ixp_client_open(&c, fid, argv[0], IXP_OWRITE)) {
|
||||
fprintf(stderr, "wmir: cannot open file: %s\n", c.errstr);
|
||||
return 1;
|
||||
}
|
||||
write_data(fid, (u8 *) argv[1], strlen(argv[1]));
|
||||
return !ixp_client_close(&c, fid);
|
||||
}
|
||||
|
||||
static void
|
||||
print_directory(void *result, u32 msize)
|
||||
{
|
||||
void *p = result;
|
||||
static Stat stat, zerostat = {0};
|
||||
u32 len = 0;
|
||||
do {
|
||||
p = ixp_dec_stat(p, &stat);
|
||||
len += stat.size + sizeof(u16);
|
||||
if (stat.qid.type == IXP_QTDIR)
|
||||
fprintf(stdout, "%s/\n", stat.name);
|
||||
else
|
||||
fprintf(stdout, "%s\n", stat.name);
|
||||
stat = zerostat;
|
||||
}
|
||||
while (len < msize);
|
||||
}
|
||||
|
||||
static int
|
||||
xread(char **argv)
|
||||
{
|
||||
u32 count, fid = c.root_fid << 2;
|
||||
int is_directory = FALSE;
|
||||
static u8 result[IXP_MAX_MSG];
|
||||
|
||||
/* open */
|
||||
if (!ixp_client_open(&c, fid, argv[0], IXP_OREAD)) {
|
||||
fprintf(stderr, "wmir: cannot open file: %s\n", c.errstr);
|
||||
return 1;
|
||||
}
|
||||
is_directory = !c.fcall.nwqid || (c.fcall.qid.type == IXP_QTDIR);
|
||||
/* read */
|
||||
if (!(count = ixp_client_read(&c, fid, 0, result, IXP_MAX_MSG))
|
||||
&& c.errstr) {
|
||||
fprintf(stderr, "wmir: cannot read file: %s\n", c.errstr);
|
||||
return 1;
|
||||
}
|
||||
if (count) {
|
||||
if (is_directory)
|
||||
print_directory(result, count);
|
||||
else {
|
||||
u32 i;
|
||||
for (i = 0; i < count; i++)
|
||||
putchar(result[i]);
|
||||
}
|
||||
}
|
||||
return !ixp_client_close(&c, fid);
|
||||
}
|
||||
|
||||
static int
|
||||
xremove(char **argv)
|
||||
{
|
||||
u32 fid;
|
||||
|
||||
/* remove */
|
||||
fid = c.root_fid << 2;
|
||||
if (!ixp_client_remove(&c, fid, argv[0])) {
|
||||
fprintf(stderr, "wmir: cannot remove file: %s\n", c.errstr);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
perform_cmd(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; cmds[i].name; i++)
|
||||
if (!strncmp(cmds[i].name, argv[0], strlen(cmds[i].name))) {
|
||||
if (cmds[i].min_argc <= argc)
|
||||
return cmds[i].cmd(&argv[1]);
|
||||
else
|
||||
usage();
|
||||
}
|
||||
/* bogus command */
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i = 0, ret, read_stdin = 0;
|
||||
char line[4096];
|
||||
char *sockfile = getenv("WMIR_SOCKET");
|
||||
|
||||
/* command line args */
|
||||
if (argc > 1) {
|
||||
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
|
||||
switch (argv[i][1]) {
|
||||
case 'v':
|
||||
fprintf(stderr, "%s", version[0]);
|
||||
exit(0);
|
||||
break;
|
||||
case 's':
|
||||
if (i + 1 < argc)
|
||||
sockfile = argv[++i];
|
||||
else
|
||||
usage();
|
||||
break;
|
||||
case 'f':
|
||||
read_stdin = 1;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((argc <= 1) || (!read_stdin && (i + 1) >= argc)) {
|
||||
fprintf(stderr, "%s", "wmir: arguments: ");
|
||||
for (i = 1; i < argc; i++)
|
||||
fprintf(stderr, "%s, ", argv[i]);
|
||||
fprintf(stderr, "%s", "\n");
|
||||
usage();
|
||||
}
|
||||
if (!sockfile) {
|
||||
fprintf(stderr, "%s",
|
||||
"wmir: error: WMIR_SOCKET environment not set\n");
|
||||
usage();
|
||||
}
|
||||
/* open socket */
|
||||
if (!ixp_client_init(&c, sockfile)) {
|
||||
fprintf(stderr, "wmir: %s\n", c.errstr);
|
||||
exit(1);
|
||||
}
|
||||
/* wether perform directly or read from stdin */
|
||||
if (read_stdin) {
|
||||
char *_argv[3];
|
||||
int _argc;
|
||||
while (fgets(line, 4096, stdin))
|
||||
if ((_argc = tokenize(_argv, 3, line, ' '))) {
|
||||
if ((ret = perform_cmd(_argc, _argv)))
|
||||
break;
|
||||
}
|
||||
} else
|
||||
ret = perform_cmd(argc - (i + 1), &argv[i]);
|
||||
|
||||
/* close socket */
|
||||
ixp_client_deinit(&c);
|
||||
|
||||
return ret;
|
||||
}
|
56
cmd/wmiwarp.c
Normal file
56
cmd/wmiwarp.c
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
static char *version[] = {
|
||||
"wmiwarp - window manager improved warp - " VERSION "\n"
|
||||
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
|
||||
};
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: wmiwarp [-v] <x>,<y>\n" " -v version info\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
Display *dpy;
|
||||
int x, y;
|
||||
|
||||
/* command line args */
|
||||
if (argc != 2)
|
||||
usage();
|
||||
if (!strncmp(argv[1], "-v", 2)) {
|
||||
fprintf(stdout, "%s", version[0]);
|
||||
exit(0);
|
||||
}
|
||||
dpy = XOpenDisplay(0);
|
||||
if (!dpy) {
|
||||
fprintf(stderr, "%s", "wmiwarp: cannot open display\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!strncmp(argv[1], "center", 7)) {
|
||||
x = DisplayWidth(dpy, DefaultScreen(dpy)) / 2;
|
||||
y = DisplayHeight(dpy, DefaultScreen(dpy)) / 2;
|
||||
} else if (sscanf(argv[1], "%d,%d", &x, &y) != 2) {
|
||||
XCloseDisplay(dpy);
|
||||
usage();
|
||||
}
|
||||
XWarpPointer(dpy, None, RootWindow(dpy, DefaultScreen(dpy)),
|
||||
0, 0, 0, 0, x, y);
|
||||
XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
|
||||
XCloseDisplay(dpy);
|
||||
return 0;
|
||||
}
|
28
config.mk
Normal file
28
config.mk
Normal file
@ -0,0 +1,28 @@
|
||||
# Customize to fit your system
|
||||
|
||||
# paths
|
||||
PREFIX = /usr/local
|
||||
CONFPREFIX = ${PREFIX}/etc
|
||||
MANPREFIX = ${PREFIX}/share/man
|
||||
9PREFIX = ${PREFIX}/9
|
||||
|
||||
INCDIR = ${PREFIX}/include
|
||||
LIBDIR = ${PREFIX}/lib
|
||||
X11INC = /usr/X11R6/include
|
||||
X11LIB = /usr/X11R6/lib
|
||||
|
||||
# includes and libs
|
||||
INCLUDES = -I. -I${INCDIR} -I/usr/include -I${X11INC}
|
||||
LIBS = -L${LIBDIR} -L/usr/lib -lc -lm -L${X11LIB} -lX11
|
||||
VERSION = 3-current
|
||||
|
||||
# flags
|
||||
CFLAGS = -O0 -g -Wall ${INCLUDES} -DVERSION=\"${VERSION}\"
|
||||
LDFLAGS = -g ${LIBS}
|
||||
|
||||
# compiler
|
||||
# Note: - under Solaris add -D__EXTENSIONS__ to CFLAGS
|
||||
MAKE = make
|
||||
AR = ar cr
|
||||
CC = cc
|
||||
RANLIB = ranlib
|
198
doc/NOTES
Normal file
198
doc/NOTES
Normal file
@ -0,0 +1,198 @@
|
||||
Development notes of wmii
|
||||
===============================================
|
||||
Last change: 2005-06-29, garbeam
|
||||
|
||||
|
||||
Audience
|
||||
--------
|
||||
This document is intended to describe the architectural decisions and
|
||||
concepts of the wmii development. It is primary written for developers.
|
||||
|
||||
|
||||
Motivation
|
||||
----------
|
||||
There're several reasons for rewriting wmi:
|
||||
|
||||
1. Monolithic object-oriented design
|
||||
wmi consisted of a single binary which included all components we know,
|
||||
the window manager itself, three different bars, a shortcut box and the
|
||||
slot. Monolithic designs provide some advantages (no shared memory
|
||||
needed between the different components, special optimizations possible),
|
||||
but they lead to high coupling (code overhead) and reduce maintainability.
|
||||
|
||||
2. The XTextProperty based remote interface.
|
||||
Originally the idea of wmiremote was derived from larswm and is based on
|
||||
XTextProperties, which suffer from reliability and correct order under
|
||||
high system load (maybe due to bugs in X itself). But for the context of
|
||||
wmi they just work and do their job fine. However, a guy called Uriel
|
||||
came up one day in #wmi IRC channel and asked to generalize the
|
||||
interface and change its property-based character to a file input/output
|
||||
interface similar to the 9p protocol of plan9.
|
||||
In long discussions we agreed and tried a prototypical implementation in
|
||||
wmi, which can be found between revisions 640-735 of the wmi Subversion
|
||||
repository.
|
||||
|
||||
3. The wmi configuration interface lacked on-the-fly changes
|
||||
If a user wanted to change a setting, he had to restart wmi. As
|
||||
workaround, which produced much complexity, some convenience actions like
|
||||
create-workspace or bind-shortcut have been implemented, that the user
|
||||
was able to change a subset of often needed things on-the-fly. Anyway, this
|
||||
sucked a lot and made wmi less attractive. Within the above revisions we
|
||||
also implemented prototypical support for on-the-fly changes in wmi.
|
||||
|
||||
4. Software law
|
||||
However the prototypical implementation was very time-consuming without
|
||||
stable and usable results (in wmi Subversion repository they can be
|
||||
found in trunk/current/wmi). Thus we realized, that wmi itself was
|
||||
a prototype only around end of September 2004. Originally the
|
||||
prototypical changes between revisions 640-735 were intended to be used
|
||||
in wmi-10, but they changed things to deep. Thus wmii was born.
|
||||
|
||||
|
||||
Architecture
|
||||
------------
|
||||
To solve the above issues found in wmi are the requirements for wmii:
|
||||
|
||||
1. Modularized design
|
||||
wmii separates all different components into different binaries. We
|
||||
evaluated the dlopen() mechanism to load modules dynamically (like ion
|
||||
does), but skipped that for stability reasons (one single invalid memory
|
||||
access would crash the whole application in dlopen() world).
|
||||
To share code between components we separated also two libraries,
|
||||
which are initially written for wmii, and most wmii components link
|
||||
statically against them.
|
||||
The components and libraries of wmii look as follows:
|
||||
|
||||
* libixp (remote interface library)
|
||||
* liblitz (non-wimp GUI-toolkit library)
|
||||
* wmii (core WM)
|
||||
* wmibar (a generic bar)
|
||||
* wmiinput (input bar)
|
||||
* wmir (remote interface client)
|
||||
* wmikeys (shortcut handler)
|
||||
|
||||
The modularization has been chosen by two different point of views:
|
||||
a) The separation of independent tasks, which means that a pager or
|
||||
input bar should be independent from a window manager itself.
|
||||
b) The separation of technically independent stuff, e.g. managing windows
|
||||
is completely independent from shortcut handling - in X world at least.
|
||||
|
||||
The libixp is described below.
|
||||
|
||||
The liblitz contains primary all drawing code, which is used by
|
||||
all separated components, and provides some util functions.
|
||||
If you implement a function within a wmii component, first ask yourself,
|
||||
if that function would make sense also in other components, if so,
|
||||
export it to liblitz, the non-wimp GUI toolkit.
|
||||
|
||||
2. Remote interface
|
||||
The remote interface is implemented with the new libixp library, which
|
||||
is independent from all other parts of wmii and provides a client/server
|
||||
API for a userland memory filesystem with a similar approach as 9p
|
||||
server- and clients of the plan9 operating system.
|
||||
An IXP server provides and manages a memory file system which is accessed
|
||||
concurrently by several clients over a UNIX socket file. All above wmii
|
||||
components, except wmir, are IXP servers. Since the IXP server
|
||||
dispatches its connections with a select() based loop, it also
|
||||
represents the X event loop for above components, because the
|
||||
ConnectionNumber(X11) is the file descriptor of a connection to the X
|
||||
server. If requested, the IXP server also selects() for file descriptors
|
||||
provided by the server implementing developer and runs appropriate read()
|
||||
and write() callbacks. In above components we need only a read()
|
||||
callback for the ConnectionNumber, because the event handling of X is
|
||||
single-directional and awakes if X writes data to that file descriptor.
|
||||
The file system what the server manages, is capable to manage arbitrary
|
||||
length paths, infinite files (as long as memory is available), and byte
|
||||
content of files through following functions:
|
||||
|
||||
* create (creates file/path)
|
||||
* remove (removes file/path)
|
||||
* open (opens file)
|
||||
* read (reads opened file)
|
||||
* write (writes opened file)
|
||||
* close (closes opened file)
|
||||
|
||||
Processes which implement such a file server can access the memory file
|
||||
system explicitly, which is done by all above components for
|
||||
performance reasons. Because the server handles all accesses
|
||||
sequentially there is no race condition.
|
||||
|
||||
An IXP client, like wmir, accesses an IXP server remotely to perform
|
||||
above functions. The wmir of wmii is not allowed to create or to remove
|
||||
files/paths, because all components of wmii handle file creation and
|
||||
destruction internally, because they are IXP servers and can do so.
|
||||
But that is only a special case. In general each client is capable to
|
||||
request the server to create or remove files.
|
||||
Not allowing all IXP functions to wmir is easy to understand, because
|
||||
the problematic of accidentally removals of important files,
|
||||
which guarantees the functionality of wmii components, is prevented.
|
||||
If for example an IXP client would request to remove / in wmii, wmii
|
||||
would crash immediately, because it won't be able to read its needed
|
||||
files.
|
||||
|
||||
As you might notice, libixp provides accessing shared memory through a
|
||||
decent file input/output based interface between an IXP server and IXP
|
||||
clients, which is used for configuration and scripting. To get further
|
||||
details about IXP, start with libixp/libixp.h.
|
||||
|
||||
3. Configuration
|
||||
The above described remote interface is used for configuration. The idea
|
||||
is very similar to the procfs of Linux and other Unices which is
|
||||
accessed to configure the kernel. The above IXP servers provide following
|
||||
special file systems which are used for configuration and can be
|
||||
explored with:
|
||||
|
||||
wmir -s $HOME/.wmii/ixp/<socket file> read <path>
|
||||
|
||||
e.g.
|
||||
|
||||
wmir -s $HOME/.wmii/ixp/wmii-:0 read /
|
||||
|
||||
Reading a path which is no file, returns the contents of a directory. In
|
||||
IXP there's no differentiation between files and directories. Whether a
|
||||
file contains content (byte data) or it contains files (you can compare
|
||||
the term 'file' in IXP to inodes in UNIX world).
|
||||
|
||||
Note: By default all wmi components use a socket file in
|
||||
$HOME/.wmii/ixp/<component>-$DISPLAY. That arose some issues with the
|
||||
$DISPLAY variable, because it is on X startup mostly set to ':0'
|
||||
(':<display number>'), but if wmii runs and you invoke terms it is set
|
||||
to the screen which is focused (important for multihead configurations),
|
||||
that means that a ':0.<screen number>' is appended. Thus reading simply
|
||||
using $HOME/.wmii/ixp/<component>-$DISPLAY might fail, because of the
|
||||
appended '.<screen number>' - keep that in mind!
|
||||
|
||||
Each server developer decides by itself how its file system should look
|
||||
like to configure it. A very basic configuration interface filesystem
|
||||
can be found in wmipager, the most complex one can be found in wmii.
|
||||
|
||||
4. Software law
|
||||
Yes, wmi was implemented with C++, but wmii is implemented with C. Most
|
||||
people are surprised about that fact. But there're several reasons for
|
||||
this. Most are rarely rational, but there're arguable ones:
|
||||
|
||||
* C code compiles faster
|
||||
* all dependencies (Xlib, libc, ...) are implemented with C and
|
||||
don't provide an object oriented API
|
||||
* C code produces smaller binaries
|
||||
|
||||
One design goal of wmii is to win the challenge of simplicity, which
|
||||
means that our upper boundary of code size is 10.000 SLOC (including
|
||||
above components, libwmii and libixp).
|
||||
|
||||
|
||||
Style guide
|
||||
-----------
|
||||
We use the traditional Unix-style from Bell Labs in the source code.
|
||||
We only comment things which are hard to understand or not obvious.
|
||||
|
||||
Technical Details
|
||||
-----------------
|
||||
The window management of wmii conforms to the ICCCM specification,
|
||||
although client supplied icons and iconified window states are not
|
||||
handled for simplicity reasons and because they don't fit well with the
|
||||
tiled window management.
|
||||
Apart from this, wmii conforms partly to the EWMH specification to fit
|
||||
well with the requirements of more recent applications in the area of
|
||||
the KDE and Gnome desktop, although to fulfill these hints are no primary
|
||||
target of wmii's window management capabilities.
|
45
doc/RELEASE
Normal file
45
doc/RELEASE
Normal file
@ -0,0 +1,45 @@
|
||||
Abbadon
|
||||
Amare
|
||||
Andariel
|
||||
Andon
|
||||
ArchDean
|
||||
Beth
|
||||
Bethanel
|
||||
Boyd * (2)
|
||||
Camael
|
||||
Charon
|
||||
Duma
|
||||
Djibril
|
||||
Elerial
|
||||
Foucault
|
||||
Frankie
|
||||
Gabriel
|
||||
Haniel
|
||||
Israfiel
|
||||
Izra'il
|
||||
Japhkiel
|
||||
Johab
|
||||
Loki
|
||||
Lucifer
|
||||
Lycurgus
|
||||
Munkar
|
||||
Michael
|
||||
Mikaal
|
||||
Metathron
|
||||
Melody
|
||||
Naarai
|
||||
Nakir
|
||||
Raphael
|
||||
Raziel
|
||||
Satan
|
||||
Sandalphon
|
||||
Sophia
|
||||
Sunya
|
||||
Tini * (3)
|
||||
Thanatos
|
||||
Tothiel
|
||||
Ultimus Maximus
|
||||
Uriel * (1)
|
||||
Yhprum
|
||||
Zaphkiel
|
||||
Zadkiel * (1.1)
|
61
doc/wmii.svg
Normal file
61
doc/wmii.svg
Normal file
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://web.resource.org/cc/"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
sodipodi:docbase="/home/zahod"
|
||||
sodipodi:docname="wmii9.svg"
|
||||
inkscape:version="0.41"
|
||||
sodipodi:version="0.32"
|
||||
version="1.0"
|
||||
x="0.0000000"
|
||||
y="0.0000000"
|
||||
width="240.00000px"
|
||||
height="120.00000px"
|
||||
id="svg2">
|
||||
<metadata
|
||||
id="metadata1296">
|
||||
<rdf:RDF
|
||||
id="RDF1298">
|
||||
<cc:Work
|
||||
id="Work1300"
|
||||
rdf:about="">
|
||||
<dc:format
|
||||
id="format1302">image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage"
|
||||
id="type1304" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
inkscape:current-layer="svg2"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-x="0"
|
||||
inkscape:cy="50.733714"
|
||||
inkscape:cx="133.35719"
|
||||
inkscape:zoom="1.6877337"
|
||||
inkscape:window-height="538"
|
||||
inkscape:window-width="640"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
borderopacity="1.0"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff"
|
||||
id="base" />
|
||||
<defs
|
||||
id="defs3" />
|
||||
<g
|
||||
transform="translate(0.000000,-932.3622)"
|
||||
id="layer1">
|
||||
<path
|
||||
d="M 0.0000000,971.36227 L 26.999997,971.36227 L 26.999997,1025.3623 L 54.000004,1025.3623 L 54.000004,971.28555 L 81.310128,971.28555 L 81.310128,1025.3623 L 108.00001,1025.3623 L 108.00001,971.36227 L 240.00000,971.36227 L 240.00000,1052.3622 L 213.00001,1052.3622 L 213.00001,998.36218 L 186.00001,998.36218 L 186.00001,1052.3622 L 159.00001,1052.3622 L 159.00001,998.46222 L 132.01646,998.46222 L 132.01646,1052.3622 L 0.0000000,1052.3622 L 0.0000000,971.36227 z M 159.00001,932.36216 L 186.00001,932.36216 L 186.00001,959.36220 L 159.00001,959.36220 L 159.00001,932.36216 z M 213.00001,932.36216 L 240.00000,932.36216 L 240.00000,959.36220 L 213.00001,959.36220 L 213.00001,932.36216 z "
|
||||
style="fill:#010101"
|
||||
id="path1366" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
83
doc/wmii.tex
Normal file
83
doc/wmii.tex
Normal file
@ -0,0 +1,83 @@
|
||||
% (C)opyright 2005 by Anselm R. Garbe
|
||||
\documentclass{article} \usepackage{times} \begin{document}
|
||||
|
||||
\title{Improved GUI concepts for experienced users}
|
||||
|
||||
\author{Anselm R. Garbe\\ \small garbeam at gmail dot com}
|
||||
|
||||
\maketitle \thispagestyle{empty}
|
||||
|
||||
\begin{abstract}
|
||||
This article presents the motivation and concepts of the dynamic
|
||||
window manager wmii and the graphical toolkit liblitz for the
|
||||
\it{X Window System}.
|
||||
\end{abstract}
|
||||
|
||||
|
||||
%-------------------------------------------------------------------------
|
||||
\section{Motivation}
|
||||
|
||||
Most common graphical user interfaces are designed after the WIMP\cite{wimp}
|
||||
paradigm, which has dominated the desktop environment
|
||||
landscape since late 1980s. While research has been done on alternative
|
||||
user interfaces, often the focus targeted more in ease of use and low
|
||||
learning curves for new computer users rather than in efficiency and
|
||||
power of abstraction.
|
||||
|
||||
The main reason has been the economical success of computers
|
||||
in the normal consumer market, which consists of unexperienced users mainly.
|
||||
Our motivation is to change this situation and to provide a graphical
|
||||
user interface for experienced users, though we know that this market is
|
||||
a niche.
|
||||
|
||||
There has been done rarly research in the non-wimp GUI landscape for years.
|
||||
Back in 80s and early 90s there has been some research in
|
||||
this area for the Plan 9\cite{plan9} operating system at Bell Labs.
|
||||
Most recent research has been done by individuals only, like Tuomo Valkonen with
|
||||
his Ion\cite{ion} project and Lars Bernhardsson with his
|
||||
LarsWM\cite{larswm} project.
|
||||
|
||||
The approaches found in the Plan 9 operating system are interesting, because
|
||||
on the one hand they cancelled the Unix tradition to work in Teletype emulators
|
||||
and on the other hand, they didn't followed the WIMP paradigm propagated by Apple,
|
||||
IBM or Microsoft. This makes Plan 9 the most unique approach compared to the
|
||||
classical WIMP world.
|
||||
|
||||
The main aspects of an improved GUI consists of two things, a powerful
|
||||
window management approach and a sane and simple widget set which fits well
|
||||
into this window management approach.
|
||||
|
||||
In the area of improved window management concepts there has been done more
|
||||
research, thus there appeared several different approaches. But the area
|
||||
of improved widget sets which form powerful applications with a simple widget
|
||||
set has been ignored for long time. Instead, the WIMP world introduced many
|
||||
widgets which seem to focus on eye-candy, like progress bars, but don't
|
||||
fix the essential problems with WIMP toolkits.
|
||||
|
||||
|
||||
|
||||
\section{Future}
|
||||
|
||||
|
||||
\section*{Acknowlegdements} Following people provided useful feedback or several
|
||||
grammar fixes to this article:
|
||||
\begin{itemize}
|
||||
\item Frank Boehme (1st version of this article)
|
||||
\item Tuncer M zayamut Ayaz (1st version of this article
|
||||
\end{itemize}
|
||||
|
||||
\begin{thebibliography}{99}
|
||||
\bibitem{wimp} Ashley George Taylor, WIMP Interfaces, CS6751 Topic Report: Winter '97
|
||||
\bibitem{x} X Window System - http://www.freedesktop.org
|
||||
\bibitem{plan9} plan9 operating system - http://cm.bell-labs.com/plan9dist/
|
||||
\bibitem{acme} Rob Pike, Acme: A User Interface for Programmers -
|
||||
http://www.cs.bell-labs.com/sys/doc/acme/acme.html
|
||||
\bibitem{rat} Ratpoison window manager - http://www.nongnu.org/ratpoison/
|
||||
\bibitem{evil} evilwm window manager - http://evilwm.sf.net
|
||||
\bibitem{ion} Ion window manager - http://modeemi.cs.tut.fi/~tuomov/ion/
|
||||
\bibitem{larswm} LarsWM window manager - http://www.fnurt.net/larswm/
|
||||
\bibitem{vi} Vi Improved (VIM) - http://www.vim.org
|
||||
\bibitem{9p} 9P protocol - http://www.cs.bell-labs.com/sys/man/5/INDEX.html
|
||||
\end{thebibliography}
|
||||
|
||||
\end{document}
|
8
extra/README
Normal file
8
extra/README
Normal file
@ -0,0 +1,8 @@
|
||||
Scripts in this directory have been contributed by various different
|
||||
people. For License information look into the source code of the
|
||||
appropriate script.
|
||||
|
||||
|
||||
Name: pkeys
|
||||
Purpose: Print the current key bindings.
|
||||
Requirements: 9base
|
6
extra/pkeys
Normal file
6
extra/pkeys
Normal file
@ -0,0 +1,6 @@
|
||||
#!/usr/local/9/bin/rc
|
||||
|
||||
mdir=/keys/mode/
|
||||
for (mode in `{wmir read $mdir})
|
||||
for (key in `{wmir read $mdir$mode})
|
||||
echo $mode$key '->' `{wmir read $mdir$mode$key}
|
25
libcext/Makefile
Normal file
25
libcext/Makefile
Normal file
@ -0,0 +1,25 @@
|
||||
# libcext - c extensions for wmii project
|
||||
# (C)opyright MMIV-MMV Anselm R. Garbe
|
||||
|
||||
include ../config.mk
|
||||
|
||||
SRC = array.c emalloc.c estrdup.c strlcat.c strlcpy.c strtonum.c tokenize.c
|
||||
|
||||
OBJ = ${SRC:.c=.o}
|
||||
|
||||
.PREFIXES = .c .o
|
||||
|
||||
all: libcext
|
||||
@echo built libcext
|
||||
|
||||
.c.o:
|
||||
@echo CC $<
|
||||
@${CC} -c ${CFLAGS} $<
|
||||
|
||||
libcext: ${OBJ}
|
||||
@echo AR $@.a
|
||||
@${AR} $@.a ${OBJ}
|
||||
@${RANLIB} $@.a
|
||||
|
||||
clean:
|
||||
rm -f libcext.a *.o
|
98
libcext/array.c
Normal file
98
libcext/array.c
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "cext.h"
|
||||
|
||||
void **
|
||||
attach_item_begin(void **old, void *item, size_t size_item)
|
||||
{
|
||||
int i, size_old;
|
||||
void **result = 0;
|
||||
for (size_old = 0; old && old[size_old]; size_old++);
|
||||
result = emalloc(size_item * (size_old + 2));
|
||||
result[0] = item;
|
||||
for (i = 0; old && old[i]; i++)
|
||||
result[i + 1] = old[i];
|
||||
result[i + 1] = 0;
|
||||
if (old)
|
||||
free(old);
|
||||
return result;
|
||||
}
|
||||
|
||||
void **
|
||||
attach_item_end(void **old, void *item, size_t size_item)
|
||||
{
|
||||
int i, size_old;
|
||||
void **result = 0;
|
||||
for (size_old = 0; old && old[size_old]; size_old++);
|
||||
result = emalloc(size_item * (size_old + 2));
|
||||
for (i = 0; old && old[i]; i++)
|
||||
result[i] = old[i];
|
||||
result[i++] = item;
|
||||
result[i] = 0;
|
||||
if (old)
|
||||
free(old);
|
||||
return result;
|
||||
}
|
||||
|
||||
void **
|
||||
detach_item(void **old, void *item, size_t size_item)
|
||||
{
|
||||
int size_old, i, j = 0;
|
||||
void **result = 0;
|
||||
for (size_old = 0; old && old[size_old]; size_old++);
|
||||
if (size_old != 1) {
|
||||
result = emalloc(size_item * size_old);
|
||||
for (i = 0; old[i]; i++)
|
||||
if (old[i] != item)
|
||||
result[j++] = old[i];
|
||||
result[j] = 0;
|
||||
}
|
||||
if (old)
|
||||
free(old);
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
index_item(void **items, void *item)
|
||||
{
|
||||
int i = 0;
|
||||
for (i = 0; items && items[i] && (items[i] != item); i++);
|
||||
return items[i] ? i : -1;
|
||||
}
|
||||
|
||||
int
|
||||
count_items(void **items)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; items && items[i]; i++);
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
index_next_item(void **items, void *item)
|
||||
{
|
||||
int idx = index_item(items, item);
|
||||
if (idx == -1)
|
||||
return idx;
|
||||
if (idx == count_items(items) - 1)
|
||||
return 0;
|
||||
else
|
||||
return idx + 1;
|
||||
}
|
||||
|
||||
int
|
||||
index_prev_item(void **items, void *item)
|
||||
{
|
||||
int idx = index_item(items, item);
|
||||
if (idx == -1)
|
||||
return idx;
|
||||
if (idx == 0)
|
||||
return count_items(items) - 1;
|
||||
else
|
||||
return idx - 1;
|
||||
}
|
45
libcext/cext.h
Normal file
45
libcext/cext.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef nil
|
||||
#define nil (void *)0
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
/* array.c */
|
||||
void **attach_item_end(void **old, void *item, size_t size_item);
|
||||
void **attach_item_begin(void **old, void *item, size_t size_item);
|
||||
void **detach_item(void **old, void *item, size_t size_item);
|
||||
int index_item(void **items, void *item);
|
||||
int index_next_item(void **items, void *item);
|
||||
int index_prev_item(void **items, void *item);
|
||||
int count_items(void **items);
|
||||
|
||||
/* emalloc.c */
|
||||
void *emalloc(size_t size);
|
||||
|
||||
/* estrdup.c */
|
||||
char *estrdup(const char *s);
|
||||
|
||||
/* strlcat.c */
|
||||
size_t _strlcat(char *dst, const char *src, size_t siz);
|
||||
|
||||
/* strlcpy.c */
|
||||
size_t _strlcpy(char *dst, const char *src, size_t siz);
|
||||
|
||||
/* strtonum.c */
|
||||
long long
|
||||
__strtonum(const char *numstr, long long minval,
|
||||
long long maxval, const char **errstrp);
|
||||
|
||||
/* tokenize.c */
|
||||
size_t tokenize(char **result, size_t reslen, char *str, char delim);
|
22
libcext/emalloc.c
Normal file
22
libcext/emalloc.c
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "cext.h"
|
||||
|
||||
void *
|
||||
emalloc(size_t size)
|
||||
{
|
||||
void *res = malloc(size);
|
||||
|
||||
if (!res) {
|
||||
fprintf(stderr, "fatal: could not malloc() %d bytes\n",
|
||||
(int) size);
|
||||
exit(1);
|
||||
}
|
||||
return res;
|
||||
}
|
21
libcext/estrdup.c
Normal file
21
libcext/estrdup.c
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cext.h"
|
||||
|
||||
char *
|
||||
estrdup(const char *s)
|
||||
{
|
||||
char *tmp;
|
||||
|
||||
tmp = (char *) emalloc(strlen(s) + 1);
|
||||
strcpy(tmp, (char *) s);
|
||||
|
||||
return tmp;
|
||||
}
|
55
libcext/strlcat.c
Normal file
55
libcext/strlcat.c
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cext.h"
|
||||
|
||||
/*
|
||||
* Appends src to string dst of size siz (unlike strncat, siz is the
|
||||
* full size of dst, not space left). At most siz-1 characters
|
||||
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
|
||||
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
|
||||
* If retval >= siz, truncation occurred.
|
||||
*/
|
||||
size_t
|
||||
_strlcat(char *dst, const char *src, size_t siz)
|
||||
{
|
||||
register char *d = dst;
|
||||
register const char *s = src;
|
||||
register size_t n = siz;
|
||||
size_t dlen;
|
||||
|
||||
/* Find the end of dst and adjust bytes left but don't go past end */
|
||||
while (n-- != 0 && *d != '\0')
|
||||
d++;
|
||||
dlen = d - dst;
|
||||
n = siz - dlen;
|
||||
|
||||
if (n == 0)
|
||||
return (dlen + strlen(s));
|
||||
while (*s != '\0') {
|
||||
if (n != 1) {
|
||||
*d++ = *s;
|
||||
n--;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
*d = '\0';
|
||||
|
||||
return (dlen + (s - src)); /* count does not include NUL */
|
||||
}
|
47
libcext/strlcpy.c
Normal file
47
libcext/strlcpy.c
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "cext.h"
|
||||
|
||||
/*
|
||||
* Copy src to string dst of size siz. At most siz-1 characters
|
||||
* will be copied. Always NUL terminates (unless siz == 0).
|
||||
* Returns strlen(src); if retval >= siz, truncation occurred.
|
||||
*/
|
||||
size_t
|
||||
_strlcpy(char *dst, const char *src, size_t siz)
|
||||
{
|
||||
register char *d = dst;
|
||||
register const char *s = src;
|
||||
register size_t n = siz;
|
||||
|
||||
/* Copy as many bytes as will fit */
|
||||
if (n != 0 && --n != 0) {
|
||||
do {
|
||||
if ((*d++ = *s++) == 0)
|
||||
break;
|
||||
} while (--n != 0);
|
||||
}
|
||||
/* Not enough room in dst, add NUL and traverse rest of src */
|
||||
if (n == 0) {
|
||||
if (siz != 0)
|
||||
*d = '\0'; /* NUL-terminate dst */
|
||||
while (*s++);
|
||||
}
|
||||
return (s - src - 1); /* count does not include NUL */
|
||||
}
|
68
libcext/strtonum.c
Normal file
68
libcext/strtonum.c
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2004 Ted Unangst and Todd Miller
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define INVALID 1
|
||||
#define TOOSMALL 2
|
||||
#define TOOLARGE 3
|
||||
|
||||
long long
|
||||
__strtonum(const char *numstr, long long minval, long long maxval,
|
||||
const char **errstrp)
|
||||
{
|
||||
long long ll = 0;
|
||||
char *ep;
|
||||
int error = 0;
|
||||
struct errval {
|
||||
const char *errstr;
|
||||
int err;
|
||||
} ev[4] = {
|
||||
{
|
||||
NULL, 0
|
||||
}, {
|
||||
"invalid", EINVAL
|
||||
}, {
|
||||
"too small", ERANGE
|
||||
}, {
|
||||
"too large", ERANGE
|
||||
},
|
||||
};
|
||||
|
||||
ev[0].err = errno;
|
||||
errno = 0;
|
||||
if (minval > maxval)
|
||||
error = INVALID;
|
||||
else {
|
||||
ll = strtoll(numstr, &ep, 10);
|
||||
if (numstr == ep || *ep != '\0')
|
||||
error = INVALID;
|
||||
else if (errno == ERANGE || ll < minval)
|
||||
error = TOOSMALL;
|
||||
else if (errno == ERANGE || ll > maxval)
|
||||
error = TOOLARGE;
|
||||
}
|
||||
if (errstrp != NULL)
|
||||
*errstrp = ev[error].errstr;
|
||||
errno = ev[error].err;
|
||||
if (error)
|
||||
ll = 0;
|
||||
|
||||
return (ll);
|
||||
}
|
34
libcext/tokenize.c
Normal file
34
libcext/tokenize.c
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "cext.h"
|
||||
|
||||
size_t
|
||||
tokenize(char **result, size_t reslen, char *str, char delim)
|
||||
{
|
||||
char *p, *n;
|
||||
size_t i = 0;
|
||||
|
||||
if (!str)
|
||||
return 0;
|
||||
for (n = str; *n == ' '; n++);
|
||||
p = n;
|
||||
for (i = 0; *n != '\0';) {
|
||||
if (i == reslen)
|
||||
return i;
|
||||
if (*n == delim) {
|
||||
*n = '\0';
|
||||
if (strlen(p))
|
||||
result[i++] = p;
|
||||
p = ++n;
|
||||
} else
|
||||
n++;
|
||||
}
|
||||
if (strlen(p))
|
||||
result[i++] = p;
|
||||
return i; /* number of tokens */
|
||||
}
|
26
libixp/Makefile
Normal file
26
libixp/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
# libixp - lib ixp protocol
|
||||
# (C)opyright MMIV-MMV Anselm R. Garbe
|
||||
|
||||
include ../config.mk
|
||||
|
||||
CFLAGS += -I ../libcext
|
||||
LDFLAGS += -L../libcext -lcext
|
||||
|
||||
SRC = client.c message.c ramfs.c server.c
|
||||
|
||||
OBJ = ${SRC:.c=.o}
|
||||
|
||||
all: libixp
|
||||
@echo built libixp
|
||||
|
||||
.c.o:
|
||||
@echo CC $<
|
||||
@${CC} -c ${CFLAGS} $<
|
||||
|
||||
libixp: ${OBJ}
|
||||
@echo AR $@.a
|
||||
@${AR} $@.a ${OBJ}
|
||||
@${RANLIB} $@.a
|
||||
|
||||
clean:
|
||||
rm -f libixp.a *.o
|
302
libixp/client.c
Normal file
302
libixp/client.c
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "ixp.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
static IXPClient zero_client = {0};
|
||||
static size_t offsets[MAX_CONN * MAX_OPEN_FILES][2]; /* set of possible fd's */
|
||||
|
||||
static void
|
||||
check_error(IXPClient * c, void *msg)
|
||||
{
|
||||
ResHeader h;
|
||||
|
||||
if (c->errstr)
|
||||
free(c->errstr);
|
||||
c->errstr = 0;
|
||||
|
||||
memcpy(&h, msg, sizeof(ResHeader));
|
||||
if (h.res == RERROR)
|
||||
c->errstr = strdup((char *) msg + sizeof(ResHeader));
|
||||
}
|
||||
|
||||
static void
|
||||
handle_dead_server(IXPClient * c)
|
||||
{
|
||||
if (c->errstr)
|
||||
free(c->errstr);
|
||||
c->errstr = strdup(DEAD_SERVER);
|
||||
if (c->fd) {
|
||||
shutdown(c->fd, SHUT_RDWR);
|
||||
close(c->fd);
|
||||
}
|
||||
c->fd = -1;
|
||||
}
|
||||
|
||||
static void *
|
||||
poll_server(IXPClient * c, void *request, size_t req_len,
|
||||
size_t * out_len)
|
||||
{
|
||||
size_t num = 0;
|
||||
void *result = 0;
|
||||
int r, header = 0;
|
||||
|
||||
if (c->errstr)
|
||||
free(c->errstr);
|
||||
c->errstr = 0;
|
||||
|
||||
/* first send request */
|
||||
while (num < req_len) {
|
||||
FD_ZERO(&c->wr);
|
||||
FD_SET(c->fd, &c->wr);
|
||||
r = select(c->fd + 1, 0, &c->wr, 0, 0);
|
||||
if (r == -1 && errno == EINTR)
|
||||
continue;
|
||||
if (r < 0) {
|
||||
perror("ixp: client: select");
|
||||
if (result)
|
||||
free(result);
|
||||
handle_dead_server(c);
|
||||
return 0;
|
||||
} else if (r > 0) {
|
||||
if (!header) {
|
||||
/* write header first */
|
||||
r = write(c->fd, &req_len, sizeof(size_t));
|
||||
if (r != sizeof(size_t)) {
|
||||
if (result)
|
||||
free(result);
|
||||
handle_dead_server(c);
|
||||
return 0;
|
||||
}
|
||||
header = 1;
|
||||
}
|
||||
r = write(c->fd, ((char *) request) + num, req_len - num);
|
||||
if (r < 1) {
|
||||
perror("ixp: client: write");
|
||||
if (result)
|
||||
free(result);
|
||||
handle_dead_server(c);
|
||||
return 0;
|
||||
}
|
||||
num += r;
|
||||
}
|
||||
}
|
||||
free(request); /* cleanup */
|
||||
|
||||
num = 0;
|
||||
header = 0;
|
||||
/* now wait for response */
|
||||
do {
|
||||
FD_ZERO(&c->rd);
|
||||
FD_SET(c->fd, &c->rd);
|
||||
r = select(c->fd + 1, &c->rd, 0, 0, 0);
|
||||
if (r == -1 && errno == EINTR)
|
||||
continue;
|
||||
if (r < 0) {
|
||||
perror("ixp: client: select");
|
||||
if (result)
|
||||
free(result);
|
||||
handle_dead_server(c);
|
||||
return 0;
|
||||
} else if (r > 0) {
|
||||
if (!header) {
|
||||
r = read(c->fd, out_len, sizeof(size_t));
|
||||
if (r != sizeof(size_t)) {
|
||||
if (result)
|
||||
free(result);
|
||||
handle_dead_server(c);
|
||||
return 0;
|
||||
}
|
||||
result = emalloc(*out_len);
|
||||
header = 1;
|
||||
}
|
||||
r = read(c->fd, ((char *) result) + num, *out_len - num);
|
||||
if (r < 1) {
|
||||
perror("ixp: client: read");
|
||||
if (result)
|
||||
free(result);
|
||||
handle_dead_server(c);
|
||||
return 0;
|
||||
}
|
||||
num += r;
|
||||
}
|
||||
}
|
||||
while (num != *out_len);
|
||||
|
||||
/* error checking */
|
||||
if (result)
|
||||
check_error(c, result);
|
||||
if (c->errstr) {
|
||||
free(result);
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
cixp_create(IXPClient * c, char *path)
|
||||
{
|
||||
ResHeader h;
|
||||
size_t req_len, res_len;
|
||||
void *req = tcreate_message(path, &req_len);
|
||||
void *result = poll_server(c, req, req_len, &res_len);
|
||||
|
||||
if (!result)
|
||||
return;
|
||||
memcpy(&h, result, sizeof(ResHeader));
|
||||
free(result);
|
||||
}
|
||||
|
||||
static int
|
||||
cixp_open(IXPClient * c, char *path)
|
||||
{
|
||||
ResHeader h;
|
||||
size_t req_len, res_len;
|
||||
void *req = topen_message(path, &req_len);
|
||||
void *result = poll_server(c, req, req_len, &res_len);
|
||||
|
||||
if (!result)
|
||||
return -1;
|
||||
memcpy(&h, result, sizeof(ResHeader));
|
||||
free(result);
|
||||
offsets[h.fd][0] = offsets[h.fd][1] = 0;
|
||||
return h.fd;
|
||||
}
|
||||
|
||||
static size_t
|
||||
cixp_read(IXPClient * c, int fd, void *out_buf, size_t out_buf_len)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
len = seek_read(c, fd, offsets[fd][0], out_buf, out_buf_len);
|
||||
if (c->errstr)
|
||||
return 0;
|
||||
if (len == out_buf_len)
|
||||
offsets[fd][0] += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t
|
||||
seek_read(IXPClient * c, int fd, size_t offset,
|
||||
void *out_buf, size_t out_buf_len)
|
||||
{
|
||||
ResHeader h;
|
||||
size_t req_len, res_len;
|
||||
void *req = tread_message(fd, offset, out_buf_len, &req_len);
|
||||
void *result = poll_server(c, req, req_len, &res_len);
|
||||
|
||||
if (!result)
|
||||
return -1;
|
||||
memcpy(&h, result, sizeof(ResHeader));
|
||||
memcpy(out_buf, ((char *) result) + sizeof(ResHeader), h.buf_len);
|
||||
free(result);
|
||||
return h.buf_len;
|
||||
}
|
||||
|
||||
static void
|
||||
cixp_write(IXPClient * c, int fd, void *content, size_t in_len)
|
||||
{
|
||||
seek_write(c, fd, offsets[fd][1], content, in_len);
|
||||
if (!c->errstr)
|
||||
offsets[fd][1] += in_len;
|
||||
}
|
||||
|
||||
void
|
||||
seek_write(IXPClient * c, int fd, size_t offset, void *content,
|
||||
size_t in_len)
|
||||
{
|
||||
ResHeader h;
|
||||
size_t req_len, res_len;
|
||||
void *req = twrite_message(fd, offset, content, in_len, &req_len);
|
||||
void *result = poll_server(c, req, req_len, &res_len);
|
||||
|
||||
if (!result)
|
||||
return;
|
||||
memcpy(&h, result, sizeof(ResHeader));
|
||||
free(result);
|
||||
}
|
||||
|
||||
static void
|
||||
cixp_close(IXPClient * c, int fd)
|
||||
{
|
||||
ResHeader h;
|
||||
size_t req_len, res_len;
|
||||
void *req = tclose_message(fd, &req_len);
|
||||
void *result = poll_server(c, req, req_len, &res_len);
|
||||
|
||||
if (!result)
|
||||
return;
|
||||
memcpy(&h, result, sizeof(ResHeader));
|
||||
free(result);
|
||||
offsets[fd][0] = offsets[fd][1] = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
cixp_remove(IXPClient * c, char *path)
|
||||
{
|
||||
ResHeader h;
|
||||
size_t req_len, res_len;
|
||||
void *req = tremove_message(path, &req_len);
|
||||
void *result = poll_server(c, req, req_len, &res_len);
|
||||
|
||||
if (!result)
|
||||
return;
|
||||
memcpy(&h, result, sizeof(ResHeader));
|
||||
free(result);
|
||||
}
|
||||
|
||||
IXPClient *
|
||||
init_client(char *sockfile)
|
||||
{
|
||||
struct sockaddr_un addr = {0};
|
||||
socklen_t su_len;
|
||||
|
||||
/* init */
|
||||
IXPClient *c = (IXPClient *) emalloc(sizeof(IXPClient));
|
||||
*c = zero_client;
|
||||
c->create = cixp_create;
|
||||
c->open = cixp_open;
|
||||
c->read = cixp_read;
|
||||
c->write = cixp_write;
|
||||
c->close = cixp_close;
|
||||
c->remove = cixp_remove;
|
||||
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, sockfile, sizeof(addr.sun_path));
|
||||
su_len = sizeof(struct sockaddr) + strlen(addr.sun_path);
|
||||
|
||||
if ((c->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
|
||||
free(c);
|
||||
return 0;
|
||||
}
|
||||
if (connect(c->fd, (struct sockaddr *) & addr, su_len)) {
|
||||
close(c->fd);
|
||||
free(c);
|
||||
return 0;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
deinit_client(IXPClient * c)
|
||||
{
|
||||
if (c->fd) {
|
||||
shutdown(c->fd, SHUT_RDWR);
|
||||
close(c->fd);
|
||||
}
|
||||
c->fd = -1;
|
||||
free(c);
|
||||
}
|
188
libixp/ixp.h
Normal file
188
libixp/ixp.h
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MAX_CONN 32
|
||||
#define MAX_OPEN_FILES 16
|
||||
#define DEAD_SERVER "server closed connection unexpectedly"
|
||||
#define MAX_SEEN_SHUTDOWN 3
|
||||
|
||||
#define _MAX(x,y) ((x) > (y) ? (x) : (y))
|
||||
|
||||
/* 9P message types */
|
||||
enum {
|
||||
TVERSION = 100,
|
||||
RVERSION,
|
||||
TAUTH = 102,
|
||||
RAUTH,
|
||||
TATTACH = 104,
|
||||
RATTACH,
|
||||
TERROR = 106,
|
||||
RERROR,
|
||||
TFLUSH = 108,
|
||||
RFLUSH,
|
||||
TWALK = 110,
|
||||
RWALK,
|
||||
TOPEN = 112,
|
||||
ROPEN,
|
||||
TCREATE = 114,
|
||||
RCREATE,
|
||||
TREAD = 116,
|
||||
RREAD,
|
||||
TWRITE = 118,
|
||||
RWRITE,
|
||||
TCLUNK = 120,
|
||||
RCLUNK,
|
||||
TREMOVE = 122,
|
||||
RREMOVE,
|
||||
TSTAT = 124,
|
||||
RSTAT,
|
||||
TWSTAT = 126,
|
||||
RWSTAT,
|
||||
};
|
||||
|
||||
typedef struct ResHeader ResHeader;
|
||||
typedef struct ReqHeader ReqHeader;
|
||||
typedef struct Connection Connection;
|
||||
typedef struct File File;
|
||||
typedef struct IXPClient IXPClient;
|
||||
typedef struct IXPServer IXPServer;
|
||||
typedef int IXPRequest;
|
||||
typedef int IXPResponse;
|
||||
typedef enum {
|
||||
HALT, SHUTDOWN, RUNNING
|
||||
} IXPRunlevel;
|
||||
|
||||
struct ReqHeader {
|
||||
IXPRequest req;
|
||||
int fd;
|
||||
size_t offset;
|
||||
size_t buf_len;
|
||||
};
|
||||
|
||||
struct ResHeader {
|
||||
IXPResponse res;
|
||||
int fd;
|
||||
size_t buf_len;
|
||||
};
|
||||
|
||||
/** Definition of a connection to IXP */
|
||||
struct Connection {
|
||||
IXPServer *s; /* !< server for this connection */
|
||||
int index; /* !< index inside server */
|
||||
int fd; /* !< file descriptor */
|
||||
int mode; /* 0 for reading, 1 for writing */
|
||||
int header;
|
||||
void *data;
|
||||
size_t len;
|
||||
size_t remain;
|
||||
void (*read) (Connection *);
|
||||
void (*write) (Connection *);
|
||||
File *files[MAX_OPEN_FILES];
|
||||
int seen[MAX_OPEN_FILES];
|
||||
};
|
||||
|
||||
struct File {
|
||||
char *name;
|
||||
void *content;
|
||||
size_t size;
|
||||
int lock;
|
||||
int bind;
|
||||
File *parent;
|
||||
File *next;
|
||||
/* introduced IXPServer in signature for IXPServer->errstr */
|
||||
void (*after_write) (IXPServer * s, File *);
|
||||
void (*before_read) (IXPServer * s, File *);
|
||||
};
|
||||
|
||||
struct IXPClient {
|
||||
int fd;
|
||||
fd_set rd, wr;
|
||||
char *errstr; /* 0 if succes, CHECK AFTER EACH of following
|
||||
* operations */
|
||||
/* returns fd if path exists */
|
||||
void (*create) (IXPClient *, char *path);
|
||||
void (*remove) (IXPClient *, char *path);
|
||||
int (*open) (IXPClient *, char *path);
|
||||
void (*close) (IXPClient *, int fd);
|
||||
size_t(*read) (IXPClient *, int fd, void *out_buf,
|
||||
size_t out_buf_len);
|
||||
void (*write) (IXPClient *, int fd, void *content, size_t in_len);
|
||||
};
|
||||
|
||||
struct IXPServer {
|
||||
char *sockfile;
|
||||
IXPRunlevel runlevel;
|
||||
Connection conn[MAX_CONN];
|
||||
fd_set rd, wr; /* socks to wakeup while select() */
|
||||
int nfds; /* number of file descriptors */
|
||||
File *root;
|
||||
char *errstr; /* 0 if succes, CHECK AFTER EACH of following
|
||||
* operations */
|
||||
File *(*create) (IXPServer *, char *);
|
||||
File *(*open) (IXPServer *, char *);
|
||||
size_t(*read) (IXPServer *, int, size_t, void *, size_t);
|
||||
void (*write) (IXPServer *, int, size_t, void *, size_t);
|
||||
void (*close) (IXPServer *, int);
|
||||
void (*remove) (IXPServer *, char *);
|
||||
};
|
||||
|
||||
/* client.c, implements client stub functions for fs access */
|
||||
IXPClient *init_client(char *sockfile);
|
||||
void deinit_client(IXPClient * c);
|
||||
size_t
|
||||
seek_read(IXPClient * c, int fd, size_t offset, void *out_buf,
|
||||
size_t out_buf_len);
|
||||
void
|
||||
seek_write(IXPClient * c, int fd, size_t offset,
|
||||
void *content, size_t in_len);
|
||||
|
||||
/* message.c */
|
||||
void *tcreate_message(char *path, size_t * msg_len);
|
||||
void *topen_message(char *path, size_t * msg_len);
|
||||
void *
|
||||
tread_message(int fd, size_t offset, size_t buf_len,
|
||||
size_t * msg_len);
|
||||
void *
|
||||
twrite_message(int fd, size_t offset, void *msg, size_t in_len,
|
||||
size_t * msg_len);
|
||||
void *tclose_message(int fd, size_t * msg_len);
|
||||
void *tremove_message(char *path, size_t * msg_len);
|
||||
void *rcreate_message(size_t * msg_len);
|
||||
void *ropen_message(int fd, size_t * msg_len);
|
||||
void *rread_message(void *content, size_t content_len, size_t * msg_len);
|
||||
void *rwrite_message(size_t * msg_len);
|
||||
void *rclose_message(size_t * msg_len);
|
||||
void *rremove_message(size_t * msg_len);
|
||||
void *rerror_message(char *errstr, size_t * msg_len);
|
||||
|
||||
/* ramfs.c */
|
||||
int is_directory(File * f);
|
||||
File *ixp_walk(IXPServer * s, char *path);
|
||||
File *ixp_create(IXPServer * s, char *path);
|
||||
File *ixp_open(IXPServer * s, char *path);
|
||||
size_t
|
||||
ixp_read(IXPServer * s, int fd, size_t offset, void *out_buf,
|
||||
size_t out_buf_len);
|
||||
void
|
||||
ixp_write(IXPServer * s, int fd, size_t offset,
|
||||
void *content, size_t in_len);
|
||||
void ixp_close(IXPServer * s, int fd);
|
||||
void ixp_remove(IXPServer * s, char *path);
|
||||
void ixp_remove_file(IXPServer * s, File * f);
|
||||
|
||||
/* server.c, uses fs directly for manipulation */
|
||||
IXPServer *init_server(char *sockfile, void (*cleanup) (void));
|
||||
void deinit_server(IXPServer * s);
|
||||
File *fd_to_file(IXPServer * s, int fd);
|
||||
void run_server(IXPServer * s);
|
||||
void run_server_with_fd_support(IXPServer * s, int fd, void (*fd_read) (Connection *), /* callback for read on
|
||||
* fd */
|
||||
void (*fd_write) (Connection *)); /* callback for write on
|
||||
* fd */
|
||||
void set_error(IXPServer * s, char *errstr);
|
189
libixp/message.c
Normal file
189
libixp/message.c
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ixp.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
/* request messages ------------------------------------------------ */
|
||||
|
||||
void *
|
||||
tcreate_message(char *path, size_t * msg_len)
|
||||
{
|
||||
char *msg;
|
||||
ReqHeader h;
|
||||
*msg_len = sizeof(ReqHeader) + strlen(path) + 1;
|
||||
msg = emalloc(*msg_len);
|
||||
h.req = TCREATE;
|
||||
memcpy(msg, &h, sizeof(ReqHeader));
|
||||
memcpy(msg + sizeof(ReqHeader), path, strlen(path) + 1);
|
||||
return msg;
|
||||
}
|
||||
|
||||
void *
|
||||
topen_message(char *path, size_t * msg_len)
|
||||
{
|
||||
char *msg;
|
||||
ReqHeader h;
|
||||
*msg_len = sizeof(ReqHeader) + strlen(path) + 1;
|
||||
msg = emalloc(*msg_len);
|
||||
h.req = TOPEN;
|
||||
memcpy(msg, &h, sizeof(ReqHeader));
|
||||
memcpy(msg + sizeof(ReqHeader), path, strlen(path) + 1);
|
||||
return msg;
|
||||
}
|
||||
|
||||
void *
|
||||
tread_message(int fd, size_t offset, size_t buf_len,
|
||||
size_t * msg_len)
|
||||
{
|
||||
char *msg;
|
||||
ReqHeader h;
|
||||
*msg_len = sizeof(ReqHeader);
|
||||
msg = emalloc(*msg_len);
|
||||
h.req = TREAD;
|
||||
h.fd = fd;
|
||||
h.offset = offset;
|
||||
h.buf_len = buf_len;
|
||||
memcpy(msg, &h, sizeof(ReqHeader));
|
||||
return msg;
|
||||
}
|
||||
|
||||
void *
|
||||
twrite_message(int fd, size_t offset, void *content,
|
||||
size_t content_len, size_t * msg_len)
|
||||
{
|
||||
char *msg;
|
||||
ReqHeader h;
|
||||
*msg_len = sizeof(ReqHeader) + content_len;
|
||||
msg = emalloc(*msg_len);
|
||||
h.req = TWRITE;
|
||||
h.fd = fd;
|
||||
h.offset = offset;
|
||||
h.buf_len = content_len;
|
||||
memcpy(msg, &h, sizeof(ReqHeader));
|
||||
memcpy(msg + sizeof(ReqHeader), content, content_len);
|
||||
return msg;
|
||||
}
|
||||
|
||||
void *
|
||||
tclose_message(int fd, size_t * msg_len)
|
||||
{
|
||||
char *msg;
|
||||
ReqHeader h;
|
||||
*msg_len = sizeof(ReqHeader);
|
||||
msg = emalloc(*msg_len);
|
||||
h.req = TCLUNK;
|
||||
h.fd = fd;
|
||||
memcpy(msg, &h, sizeof(ReqHeader));
|
||||
return msg;
|
||||
}
|
||||
|
||||
void *
|
||||
tremove_message(char *path, size_t * msg_len)
|
||||
{
|
||||
char *msg;
|
||||
ReqHeader h;
|
||||
*msg_len = sizeof(ReqHeader) + strlen(path) + 1;
|
||||
msg = emalloc(*msg_len);
|
||||
h.req = TREMOVE;
|
||||
memcpy(msg, &h, sizeof(ReqHeader));
|
||||
memcpy(msg + sizeof(ReqHeader), path, strlen(path) + 1);
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* response messages ----------------------------------------------- */
|
||||
|
||||
void *
|
||||
rcreate_message(size_t * msg_len)
|
||||
{
|
||||
char *msg;
|
||||
ResHeader h;
|
||||
*msg_len = sizeof(ResHeader);
|
||||
msg = emalloc(*msg_len);
|
||||
h.res = RCREATE;
|
||||
memcpy(msg, &h, sizeof(ResHeader));
|
||||
return msg;
|
||||
}
|
||||
|
||||
void *
|
||||
ropen_message(int fd, size_t * msg_len)
|
||||
{
|
||||
char *msg;
|
||||
ResHeader h;
|
||||
*msg_len = sizeof(ResHeader);
|
||||
msg = emalloc(*msg_len);
|
||||
h.res = ROPEN;
|
||||
h.fd = fd;
|
||||
memcpy(msg, &h, sizeof(ResHeader));
|
||||
return msg;
|
||||
}
|
||||
|
||||
void *
|
||||
rread_message(void *content, size_t content_len, size_t * msg_len)
|
||||
{
|
||||
char *msg;
|
||||
ResHeader h;
|
||||
*msg_len = sizeof(ResHeader) + content_len;
|
||||
msg = emalloc(*msg_len);
|
||||
h.res = RREAD;
|
||||
h.buf_len = content_len;
|
||||
memcpy(msg, &h, sizeof(ResHeader));
|
||||
memmove(msg + sizeof(ResHeader), content, content_len);
|
||||
return msg;
|
||||
}
|
||||
|
||||
void *
|
||||
rwrite_message(size_t * msg_len)
|
||||
{
|
||||
char *msg;
|
||||
ResHeader h;
|
||||
*msg_len = sizeof(ResHeader);
|
||||
msg = emalloc(*msg_len);
|
||||
h.res = RWRITE;
|
||||
memcpy(msg, &h, sizeof(ResHeader));
|
||||
return msg;
|
||||
}
|
||||
|
||||
void *
|
||||
rclose_message(size_t * msg_len)
|
||||
{
|
||||
char *msg;
|
||||
ResHeader h;
|
||||
*msg_len = sizeof(ResHeader);
|
||||
msg = emalloc(*msg_len);
|
||||
h.res = RCLUNK;
|
||||
memcpy(msg, &h, sizeof(ResHeader));
|
||||
return msg;
|
||||
}
|
||||
|
||||
void *
|
||||
rremove_message(size_t * msg_len)
|
||||
{
|
||||
char *msg;
|
||||
ResHeader h;
|
||||
*msg_len = sizeof(ResHeader);
|
||||
msg = emalloc(*msg_len);
|
||||
h.res = RREMOVE;
|
||||
memcpy(msg, &h, sizeof(ResHeader));
|
||||
return msg;
|
||||
}
|
||||
|
||||
void *
|
||||
rerror_message(char *errstr, size_t * msg_len)
|
||||
{
|
||||
char *msg;
|
||||
size_t len = strlen(errstr) + 1;
|
||||
ResHeader h;
|
||||
*msg_len = sizeof(ResHeader) + len;
|
||||
msg = emalloc(*msg_len);
|
||||
h.res = RERROR;
|
||||
memcpy(msg, &h, sizeof(ResHeader));
|
||||
memmove(msg + sizeof(ResHeader), errstr, len);
|
||||
return msg;
|
||||
}
|
322
libixp/ramfs.c
Normal file
322
libixp/ramfs.c
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ixp.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
static File zero_file = {0};
|
||||
|
||||
int
|
||||
is_directory(File * f)
|
||||
{
|
||||
return !f->size && f->content;
|
||||
}
|
||||
|
||||
File *
|
||||
ixp_create(IXPServer * s, char *path)
|
||||
{
|
||||
File *f, *p;
|
||||
char *buf = strdup(path);
|
||||
char *tok, *tok_ptr;
|
||||
|
||||
if (!path)
|
||||
return 0;
|
||||
|
||||
/* cannot create a directory with empty name */
|
||||
if (buf[strlen(buf) - 1] == '/')
|
||||
return 0;
|
||||
tok = strtok_r(buf, "/", &tok_ptr);
|
||||
p = s->root;
|
||||
f = p->content;
|
||||
/* first determine the path as it exists already */
|
||||
while (f && tok) {
|
||||
if (!strcmp(f->name, tok)) {
|
||||
tok = strtok_r(0, "/", &tok_ptr);
|
||||
p = f;
|
||||
if (tok && is_directory(f))
|
||||
f = f->content;
|
||||
else
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
f = f->next;
|
||||
}
|
||||
if (p->size && tok) {
|
||||
free(buf);
|
||||
return 0; /* cannot create subdirectory, if file has
|
||||
* content */
|
||||
}
|
||||
/* only create missing parts, if file is directory */
|
||||
while (tok) {
|
||||
f = (File *) emalloc(sizeof(File));
|
||||
*f = zero_file;
|
||||
f->name = strdup(tok);
|
||||
f->parent = p;
|
||||
if (p->content) {
|
||||
p = p->content;
|
||||
while (p->next) {
|
||||
p = p->next;
|
||||
}
|
||||
p->next = f;
|
||||
} else {
|
||||
p->content = f;
|
||||
}
|
||||
p = f;
|
||||
tok = strtok_r(0, "/", &tok_ptr);
|
||||
}
|
||||
free(buf);
|
||||
return f;
|
||||
}
|
||||
|
||||
static int
|
||||
comp_file_name(const void *f1, const void *f2)
|
||||
{
|
||||
File *f = (File *) f1;
|
||||
File *p = (File *) f2;
|
||||
return strcmp(*(char **) f->name, *(char **) p->name);
|
||||
}
|
||||
|
||||
static char *
|
||||
_ls(File * f)
|
||||
{
|
||||
File *p;
|
||||
char *result = 0;
|
||||
size_t size = 1; /* for \n */
|
||||
int num = 0, i;
|
||||
File **tmp;
|
||||
|
||||
for (p = f; p; p = p->next)
|
||||
num++;
|
||||
tmp = emalloc(sizeof(File *) * num);
|
||||
i = 0;
|
||||
for (p = f; p; p = p->next) {
|
||||
size += strlen(p->name) + 1;
|
||||
if (is_directory(p))
|
||||
size++;
|
||||
tmp[i++] = p;
|
||||
}
|
||||
qsort(tmp, num, sizeof(char *), comp_file_name);
|
||||
result = emalloc(size);
|
||||
result[0] = '\0';
|
||||
for (i = 0; i < num; i++) {
|
||||
strncat(result, tmp[i]->name, size);
|
||||
if (is_directory(tmp[i]))
|
||||
strncat(result, "/\n", size);
|
||||
else
|
||||
strncat(result, "\n", size);
|
||||
}
|
||||
free(tmp);
|
||||
return result;
|
||||
}
|
||||
|
||||
File *
|
||||
ixp_open(IXPServer * s, char *path)
|
||||
{
|
||||
File *f;
|
||||
|
||||
f = ixp_walk(s, path);
|
||||
if (!f) {
|
||||
set_error(s, "file does not exist");
|
||||
return 0;
|
||||
}
|
||||
f->lock++;
|
||||
return f;
|
||||
}
|
||||
|
||||
void
|
||||
ixp_close(IXPServer * s, int fd)
|
||||
{
|
||||
File *f = fd_to_file(s, fd);
|
||||
if (!f)
|
||||
set_error(s, "invalid file descriptor");
|
||||
else if (f->lock > 0)
|
||||
f->lock--;
|
||||
}
|
||||
|
||||
size_t
|
||||
ixp_read(IXPServer * s, int fd, size_t offset, void *out_buf,
|
||||
size_t out_buf_len)
|
||||
{
|
||||
File *f = fd_to_file(s, fd);
|
||||
void *result = 0;
|
||||
size_t len = 0, res_len = 0;
|
||||
|
||||
if (!f) {
|
||||
set_error(s, "invalid file descriptor");
|
||||
return 0;
|
||||
}
|
||||
/* callback */
|
||||
if (f->before_read)
|
||||
f->before_read(s, f);
|
||||
if (is_directory(f)) {
|
||||
result = _ls(f->content);
|
||||
res_len = strlen(result);
|
||||
} else if (f->size) {
|
||||
result = f->content;
|
||||
res_len = f->size;
|
||||
}
|
||||
if (offset > res_len) {
|
||||
set_error(s, "invalid offset when reading file");
|
||||
if (is_directory(f))
|
||||
free(result);
|
||||
return 0;
|
||||
}
|
||||
if (result) {
|
||||
len = res_len - offset;
|
||||
if (len > out_buf_len)
|
||||
len = out_buf_len;
|
||||
memcpy(out_buf, (char *) result + offset, len);
|
||||
if (is_directory(f))
|
||||
free(result);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void
|
||||
ixp_write(IXPServer * s, int fd, size_t offset, void *content,
|
||||
size_t in_len)
|
||||
{
|
||||
File *f = fd_to_file(s, fd);
|
||||
|
||||
if (!f) {
|
||||
set_error(s, "invalid file descriptor");
|
||||
return;
|
||||
}
|
||||
if (is_directory(f)) {
|
||||
/* we cannot write to directories */
|
||||
set_error(s, "cannot write to a directory");
|
||||
return;
|
||||
}
|
||||
if (in_len) {
|
||||
/* offset 0 flushes the file */
|
||||
if (!offset || (offset + in_len > f->size)) {
|
||||
f->content = realloc(f->content, offset + in_len + 1);
|
||||
f->size = offset + in_len;
|
||||
}
|
||||
memcpy((char *) f->content + offset, content, in_len);
|
||||
/* internal EOF character */
|
||||
((char *) f->content)[f->size] = '\0';
|
||||
} else if (!offset) {
|
||||
/* blank file */
|
||||
if (f->content)
|
||||
free(f->content);
|
||||
f->content = 0;
|
||||
f->size = 0;
|
||||
}
|
||||
/* callback */
|
||||
if (f->after_write)
|
||||
f->after_write(s, f);
|
||||
}
|
||||
|
||||
static void
|
||||
_ixp_remove(IXPServer * s, File * f)
|
||||
{
|
||||
if (!f)
|
||||
return;
|
||||
if (f->next) {
|
||||
_ixp_remove(s, f->next);
|
||||
if (s->errstr)
|
||||
return;
|
||||
}
|
||||
if (f->lock) {
|
||||
set_error(s, "cannot remove opened file");
|
||||
return; /* a file is opened, so stop removing tree */
|
||||
}
|
||||
if (!f->bind && is_directory(f)) {
|
||||
_ixp_remove(s, f->content);
|
||||
if (s->errstr)
|
||||
return;
|
||||
}
|
||||
if (f->content && f->size) {
|
||||
free(f->content);
|
||||
}
|
||||
if (f != s->root) {
|
||||
if (f->name) {
|
||||
free(f->name);
|
||||
}
|
||||
free(f);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ixp_remove_file(IXPServer * s, File * f)
|
||||
{
|
||||
File *p, *n;
|
||||
set_error(s, 0);
|
||||
if (!f) {
|
||||
set_error(s, "file does not exist");
|
||||
return;
|
||||
}
|
||||
if (f->lock) {
|
||||
set_error(s, "cannot remove opened file");
|
||||
return;
|
||||
}
|
||||
/* detach */
|
||||
p = f->parent;
|
||||
n = f->next;
|
||||
f->next = 0;
|
||||
if (p) {
|
||||
if (p->content == f)
|
||||
p->content = n;
|
||||
else {
|
||||
p = p->content;
|
||||
while (p && (p->next != f))
|
||||
p = p->next;
|
||||
if (p)
|
||||
p->next = n;
|
||||
}
|
||||
}
|
||||
/* remove now */
|
||||
_ixp_remove(s, f);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ixp_remove(IXPServer * s, char *path)
|
||||
{
|
||||
ixp_remove_file(s, ixp_walk(s, path));
|
||||
}
|
||||
|
||||
File *
|
||||
ixp_walk(IXPServer * s, char *path)
|
||||
{
|
||||
File *f = 0;
|
||||
File *n;
|
||||
char *buf;
|
||||
char *tok, *tok_ptr;
|
||||
|
||||
if (!path) {
|
||||
return 0;
|
||||
}
|
||||
buf = strdup(path);
|
||||
|
||||
tok = strtok_r(buf, "/", &tok_ptr);
|
||||
f = s->root->content;
|
||||
if (!tok && buf[0] == '/') {
|
||||
f = s->root;
|
||||
}
|
||||
while (f && tok) {
|
||||
n = f->next;
|
||||
if (!strcmp(f->name, tok)) {
|
||||
tok = strtok_r(0, "/", &tok_ptr);
|
||||
if (tok && f->size)
|
||||
return 0;
|
||||
if (!tok)
|
||||
break;
|
||||
f = f->content;
|
||||
continue;
|
||||
}
|
||||
f = n;
|
||||
}
|
||||
if (f && (path[strlen(path) - 1] == '/') && !is_directory(f))
|
||||
f = 0;
|
||||
free(buf);
|
||||
return f;
|
||||
}
|
475
libixp/server.c
Normal file
475
libixp/server.c
Normal file
@ -0,0 +1,475 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "ixp.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
static File zero_file = {0};
|
||||
static IXPServer zero_server = {0};
|
||||
static Connection zero_conn = {0};
|
||||
static int user_fd = -1;
|
||||
|
||||
void
|
||||
set_error(IXPServer * s, char *errstr)
|
||||
{
|
||||
if (s->errstr)
|
||||
free(s->errstr);
|
||||
if (errstr)
|
||||
s->errstr = strdup(errstr);
|
||||
else
|
||||
s->errstr = 0;
|
||||
}
|
||||
|
||||
File *
|
||||
fd_to_file(IXPServer * s, int fd)
|
||||
{
|
||||
int cidx = fd / MAX_CONN;
|
||||
int fidx = fd - (cidx * MAX_CONN);
|
||||
|
||||
return s->conn[cidx].files[fidx];
|
||||
}
|
||||
|
||||
static void
|
||||
handle_ixp_create(Connection * c)
|
||||
{
|
||||
c->s->create(c->s, ((char *) c->data) + sizeof(ReqHeader));
|
||||
free(c->data);
|
||||
c->data = c->s->errstr ?
|
||||
rerror_message(c->s->errstr, &c->len) : rcreate_message(&c->len);
|
||||
c->remain = c->len;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_ixp_open(Connection * c)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* seek next free slot */
|
||||
for (i = 0; (i < MAX_OPEN_FILES) && c->files[i]; i++);
|
||||
if (i == MAX_OPEN_FILES) {
|
||||
fprintf(stderr, "%s",
|
||||
"ixp: server: maximum of open files, try again later.\n");
|
||||
free(c->data);
|
||||
c->data =
|
||||
rerror_message("maximum open files reached, close files first",
|
||||
&c->len);
|
||||
c->remain = c->len;
|
||||
return;
|
||||
}
|
||||
c->files[i] = c->s->open(c->s, ((char *) c->data) + sizeof(ReqHeader));
|
||||
c->seen[i] = MAX_SEEN_SHUTDOWN;
|
||||
free(c->data);
|
||||
c->data = c->s->errstr ?
|
||||
rerror_message(c->s->errstr,
|
||||
&c->len) : ropen_message(i + MAX_CONN * c->index,
|
||||
&c->len);
|
||||
c->remain = c->len;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_ixp_read(Connection * c, ReqHeader * h)
|
||||
{
|
||||
int fidx = h->fd - (c->index * MAX_CONN);
|
||||
void *data = 0;
|
||||
size_t out_len;
|
||||
|
||||
data = emalloc(h->buf_len);
|
||||
out_len = c->s->read(c->s, h->fd, h->offset, data, h->buf_len);
|
||||
free(c->data);
|
||||
if (c->s->errstr) {
|
||||
c->data = rerror_message(c->s->errstr, &c->len);
|
||||
if (c->files[fidx] && c->files[fidx]->lock > 0)
|
||||
c->files[fidx]->lock--;
|
||||
|
||||
} else
|
||||
c->data = rread_message(data, out_len, &c->len);
|
||||
c->remain = c->len;
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_ixp_write(Connection * c, ReqHeader * h)
|
||||
{
|
||||
int fidx = h->fd - (c->index * MAX_CONN);
|
||||
c->s->write(c->s, h->fd, h->offset,
|
||||
((char *) c->data) + sizeof(ReqHeader), h->buf_len);
|
||||
free(c->data);
|
||||
if (c->s->errstr) {
|
||||
c->data = rerror_message(c->s->errstr, &c->len);
|
||||
if (c->files[fidx] && c->files[fidx]->lock > 0)
|
||||
c->files[fidx]->lock--;
|
||||
|
||||
} else
|
||||
c->data = rwrite_message(&c->len);
|
||||
c->remain = c->len;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_ixp_close(Connection * c, ReqHeader * h)
|
||||
{
|
||||
int fidx = h->fd - (c->index * MAX_CONN);
|
||||
|
||||
c->s->close(c->s, h->fd);
|
||||
c->files[fidx] = 0;
|
||||
free(c->data);
|
||||
if (c->s->errstr) {
|
||||
c->data = rerror_message(c->s->errstr, &c->len);
|
||||
if (c->files[fidx] && c->files[fidx]->lock > 0)
|
||||
c->files[fidx]->lock--;
|
||||
|
||||
} else
|
||||
c->data = rclose_message(&c->len);
|
||||
c->remain = c->len;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_ixp_remove(Connection * c)
|
||||
{
|
||||
c->s->remove(c->s, ((char *) c->data) + sizeof(ReqHeader));
|
||||
free(c->data);
|
||||
c->data = c->s->errstr ?
|
||||
rerror_message(c->s->errstr, &c->len) : rremove_message(&c->len);
|
||||
c->remain = c->len;
|
||||
}
|
||||
|
||||
static void
|
||||
check_ixp_request(Connection * c)
|
||||
{
|
||||
ReqHeader h;
|
||||
/* check pending request */
|
||||
if (c->s->errstr)
|
||||
set_error(c->s, 0);
|
||||
memcpy(&h, c->data, sizeof(ReqHeader));
|
||||
switch (h.req) {
|
||||
case TCREATE:
|
||||
handle_ixp_create(c);
|
||||
break;
|
||||
case TREMOVE:
|
||||
handle_ixp_remove(c);
|
||||
break;
|
||||
case TOPEN:
|
||||
handle_ixp_open(c);
|
||||
break;
|
||||
case TCLUNK:
|
||||
handle_ixp_close(c, &h);
|
||||
break;
|
||||
case TREAD:
|
||||
handle_ixp_read(c, &h);
|
||||
break;
|
||||
case TWRITE:
|
||||
handle_ixp_write(c, &h);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s", "ixp: server: invalid request\n");
|
||||
free(c->data);
|
||||
c->len = c->remain = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
update_conns(IXPServer * s)
|
||||
{
|
||||
int i;
|
||||
|
||||
FD_ZERO(&s->rd);
|
||||
FD_ZERO(&s->wr);
|
||||
for (i = 0; i < MAX_CONN; i++) {
|
||||
if (s->conn[i].fd >= 0) {
|
||||
s->nfds = _MAX(s->nfds, s->conn[i].fd);
|
||||
if (s->conn[i].read && !s->conn[i].mode
|
||||
&& (!s->conn[i].len || s->conn[i].remain)) {
|
||||
FD_SET(s->conn[i].fd, &s->rd);
|
||||
}
|
||||
if (s->conn[i].write && s->conn[i].mode && s->conn[i].remain) {
|
||||
FD_SET(s->conn[i].fd, &s->wr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
close_conn(Connection * c)
|
||||
{
|
||||
int i;
|
||||
/* shutdown connection and cleanup open files */
|
||||
shutdown(c->fd, SHUT_RDWR);
|
||||
close(c->fd);
|
||||
c->fd = -1;
|
||||
c->mode = 0;
|
||||
for (i = 0; i < MAX_OPEN_FILES; i++) {
|
||||
if (c->files[i]) {
|
||||
if (c->files[i]->lock > 0)
|
||||
c->files[i]->lock--;
|
||||
c->files[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
read_conn(Connection * c)
|
||||
{
|
||||
size_t r;
|
||||
|
||||
if (!c->header) {
|
||||
r = read(c->fd, &c->len, sizeof(size_t));
|
||||
if (r != sizeof(size_t)) {
|
||||
close_conn(c);
|
||||
return;
|
||||
}
|
||||
c->remain = c->len;
|
||||
c->data = emalloc(c->len);
|
||||
c->header = 1;
|
||||
}
|
||||
r = read(c->fd, ((char *) c->data) + c->len - c->remain, c->remain);
|
||||
if (r < 1) {
|
||||
close_conn(c);
|
||||
return;
|
||||
}
|
||||
c->remain -= r;
|
||||
|
||||
if (c->remain == 0) {
|
||||
/* check IXP request */
|
||||
c->mode = 1; /* next mode is response */
|
||||
check_ixp_request(c);
|
||||
c->header = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
write_conn(Connection * c)
|
||||
{
|
||||
size_t r;
|
||||
|
||||
if (!c->header) {
|
||||
r = write(c->fd, &c->len, sizeof(size_t));
|
||||
if (r != sizeof(size_t)) {
|
||||
close_conn(c);
|
||||
}
|
||||
c->header = 1;
|
||||
}
|
||||
r = write(c->fd, ((char *) c->data) + c->len - c->remain, c->remain);
|
||||
if (r < 1) {
|
||||
close_conn(c);
|
||||
return;
|
||||
}
|
||||
c->remain -= r;
|
||||
|
||||
if (c->remain == 0) {
|
||||
c->len = 0;
|
||||
c->mode = 0;
|
||||
c->header = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
new_conn(Connection * c)
|
||||
{
|
||||
int r, i;
|
||||
socklen_t l;
|
||||
struct sockaddr_un name = {0};
|
||||
|
||||
l = sizeof(name);
|
||||
if ((r = accept(c->fd, (struct sockaddr *) & name, &l)) < 0) {
|
||||
perror("ixp: server: cannot accept connection");
|
||||
return;
|
||||
}
|
||||
if (c->s->runlevel == SHUTDOWN) {
|
||||
fprintf(stderr, "%s",
|
||||
"ixp: server: connection refused, server is shutting down.\n");
|
||||
close(r);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < MAX_CONN; i++) {
|
||||
if (c->s->conn[i].fd == -1) { /* free connection */
|
||||
c->s->conn[i] = zero_conn;
|
||||
c->s->conn[i].s = c->s;
|
||||
c->s->conn[i].index = i;
|
||||
c->s->conn[i].fd = r;
|
||||
c->s->conn[i].read = read_conn;
|
||||
c->s->conn[i].write = write_conn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == MAX_CONN) {
|
||||
fprintf(stderr, "%s",
|
||||
"ixp: server: connection refused, try again later.\n");
|
||||
close(r);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
check_open_files(Connection * c)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAX_OPEN_FILES; i++) {
|
||||
if (c->files[i] && c->seen[i]) {
|
||||
c->seen[i]--;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_socks(IXPServer * s)
|
||||
{
|
||||
int i, now = 1;
|
||||
for (i = 0; i < MAX_CONN; i++) {
|
||||
if (s->conn[i].fd >= 0) {
|
||||
if (FD_ISSET(s->conn[i].fd, &s->rd) && s->conn[i].read) {
|
||||
/* call back read handler */
|
||||
s->conn[i].read(&s->conn[i]);
|
||||
} else if (FD_ISSET(s->conn[i].fd, &s->wr) && s->conn[i].write) {
|
||||
/* call back write handler */
|
||||
s->conn[i].write(&s->conn[i]);
|
||||
}
|
||||
/*
|
||||
* don't shutdown, if there're remaining bits or if
|
||||
* still responses are sent or still opened files
|
||||
*/
|
||||
if ((s->runlevel == SHUTDOWN)
|
||||
&& (check_open_files(&s->conn[i])
|
||||
|| (s->conn[i].remain > 0)
|
||||
|| s->conn[i].mode))
|
||||
now = 0;
|
||||
}
|
||||
}
|
||||
if ((s->runlevel == SHUTDOWN) && now)
|
||||
s->runlevel = HALT; /* real stop */
|
||||
}
|
||||
|
||||
IXPServer *
|
||||
init_server(char *sockfile, void (*cleanup) (void))
|
||||
{
|
||||
int i;
|
||||
struct sockaddr_un addr = {0};
|
||||
int yes = 1;
|
||||
socklen_t su_len;
|
||||
IXPServer *s;
|
||||
|
||||
/* init */
|
||||
s = (IXPServer *) emalloc(sizeof(IXPServer));
|
||||
*s = zero_server;
|
||||
s->sockfile = sockfile;
|
||||
s->root = (File *) emalloc(sizeof(File));
|
||||
s->runlevel = HALT; /* initially server is not running */
|
||||
s->create = ixp_create;
|
||||
s->remove = ixp_remove;
|
||||
s->open = ixp_open;
|
||||
s->close = ixp_close;
|
||||
s->read = ixp_read;
|
||||
s->write = ixp_write;
|
||||
*s->root = zero_file;
|
||||
s->root->name = strdup("");
|
||||
for (i = 0; i < MAX_CONN; i++) {
|
||||
s->conn[i].s = s;
|
||||
s->conn[i].fd = -1;
|
||||
s->conn[i].index = i;
|
||||
}
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
if ((s->conn[0].fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
|
||||
perror("ixp: server: socket");
|
||||
free(s);
|
||||
return 0;
|
||||
}
|
||||
if (setsockopt(s->conn[0].fd, SOL_SOCKET, SO_REUSEADDR,
|
||||
(char *) &yes, sizeof(yes)) < 0) {
|
||||
perror("ixp: server: setsockopt");
|
||||
close(s->conn[0].fd);
|
||||
free(s);
|
||||
return 0;
|
||||
}
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, sockfile, sizeof(addr.sun_path));
|
||||
su_len = sizeof(struct sockaddr) + strlen(addr.sun_path);
|
||||
|
||||
if (bind(s->conn[0].fd, (struct sockaddr *) & addr, su_len) < 0) {
|
||||
perror("ixp: server: cannot bind socket");
|
||||
close(s->conn[0].fd);
|
||||
free(s);
|
||||
return 0;
|
||||
}
|
||||
chmod(sockfile, S_IRWXU);
|
||||
|
||||
if (listen(s->conn[0].fd, MAX_CONN) < 0) {
|
||||
perror("ixp: server: cannot listen on socket");
|
||||
close(s->conn[0].fd);
|
||||
free(s);
|
||||
return 0;
|
||||
}
|
||||
s->conn[0].read = new_conn;
|
||||
|
||||
/* register to cleanup function, to unlink sockfile */
|
||||
if (cleanup)
|
||||
atexit(cleanup);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
run_server_with_fd_support(IXPServer * s, int fd,
|
||||
void (*fd_read) (Connection *),
|
||||
void (*fd_write) (Connection *))
|
||||
{
|
||||
s->conn[1] = zero_conn;
|
||||
s->conn[1].index = 1;
|
||||
s->conn[1].s = s;
|
||||
s->conn[1].fd = user_fd = fd;
|
||||
s->conn[1].read = fd_read;
|
||||
s->conn[1].write = fd_write;
|
||||
run_server(s);
|
||||
}
|
||||
|
||||
void
|
||||
run_server(IXPServer * s)
|
||||
{
|
||||
int r, i;
|
||||
s->runlevel = RUNNING;
|
||||
|
||||
/* main loop */
|
||||
while (s->runlevel != HALT) {
|
||||
|
||||
update_conns(s);
|
||||
|
||||
r = select(s->nfds + 1, &s->rd, &s->wr, 0, 0);
|
||||
if (r == -1 && errno == EINTR)
|
||||
continue;
|
||||
if (r < 0) {
|
||||
perror("ixp: server: select");
|
||||
break; /* allow cleanups in IXP using app */
|
||||
} else if (r > 0) {
|
||||
handle_socks(s);
|
||||
}
|
||||
}
|
||||
/* shut down server */
|
||||
for (i = MAX_CONN - 1; i >= 0; i--) {
|
||||
if (s->conn[i].fd >= 0 && s->conn[i].fd != user_fd) {
|
||||
close(s->conn[i].fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
deinit_server(IXPServer * s)
|
||||
{
|
||||
unlink(s->sockfile);
|
||||
ixp_remove(s, "/");
|
||||
free(s);
|
||||
}
|
25
libixp2/Makefile
Normal file
25
libixp2/Makefile
Normal file
@ -0,0 +1,25 @@
|
||||
# libixp - lib ixp protocol
|
||||
# (C)opyright MMIV-MMV Anselm R. Garbe
|
||||
|
||||
include ../config.mk
|
||||
|
||||
CFLAGS += -I ../libcext
|
||||
|
||||
SRC = client.c convert.c message.c server.c socket.c transport.c
|
||||
|
||||
OBJ = ${SRC:.c=.o}
|
||||
|
||||
all: libixp
|
||||
@echo built libixp2
|
||||
|
||||
.c.o:
|
||||
@echo CC $<
|
||||
@${CC} -c ${CFLAGS} $<
|
||||
|
||||
libixp: ${OBJ}
|
||||
@echo AR $@.a
|
||||
@${AR} $@.a ${OBJ}
|
||||
@${RANLIB} $@.a
|
||||
|
||||
clean:
|
||||
rm -f libixp.a *.o
|
191
libixp2/client.c
Normal file
191
libixp2/client.c
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cext.h"
|
||||
#include "ixp.h"
|
||||
|
||||
static u8 msg[IXP_MAX_MSG];
|
||||
|
||||
static int
|
||||
do_fcall(IXPClient * c)
|
||||
{
|
||||
u32 msize = ixp_fcall_to_msg(&c->fcall, msg, IXP_MAX_MSG);
|
||||
c->errstr = 0;
|
||||
if (ixp_send_message(c->fd, msg, msize, &c->errstr) != msize)
|
||||
return FALSE;
|
||||
if (!ixp_recv_message(c->fd, msg, IXP_MAX_MSG, &c->errstr))
|
||||
return FALSE;
|
||||
if (!(msize = ixp_msg_to_fcall(msg, IXP_MAX_MSG, &c->fcall))) {
|
||||
c->errstr = "received bad message";
|
||||
return FALSE;
|
||||
}
|
||||
if (c->fcall.id == RERROR) {
|
||||
c->errstr = c->fcall.errstr;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ixp_client_init(IXPClient * c, char *sockfile)
|
||||
{
|
||||
if ((c->fd = ixp_connect_sock(sockfile)) < 0) {
|
||||
c->errstr = "cannot connect server";
|
||||
return FALSE;
|
||||
}
|
||||
/* version */
|
||||
c->fcall.id = TVERSION;
|
||||
c->fcall.tag = IXP_NOTAG;
|
||||
c->fcall.maxmsg = IXP_MAX_MSG;
|
||||
_strlcpy(c->fcall.version, IXP_VERSION, sizeof(c->fcall.version));
|
||||
if (!do_fcall(c)) {
|
||||
ixp_client_deinit(c);
|
||||
return FALSE;
|
||||
}
|
||||
if (strncmp(c->fcall.version, IXP_VERSION, strlen(IXP_VERSION))) {
|
||||
c->errstr = "9P versions differ";
|
||||
ixp_client_deinit(c);
|
||||
return FALSE; /* we cannot handle this version */
|
||||
}
|
||||
c->root_fid = getpid();
|
||||
|
||||
/* attach */
|
||||
c->fcall.id = TATTACH;
|
||||
c->fcall.tag = IXP_NOTAG;
|
||||
c->fcall.fid = c->root_fid;
|
||||
c->fcall.afid = IXP_NOFID;
|
||||
_strlcpy(c->fcall.uname, getenv("USER"), sizeof(c->fcall.uname));
|
||||
c->fcall.aname[0] = '\0';
|
||||
if (!do_fcall(c)) {
|
||||
ixp_client_deinit(c);
|
||||
return FALSE;
|
||||
}
|
||||
c->root_qid = c->fcall.qid;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ixp_client_remove(IXPClient * c, u32 newfid, char *filepath)
|
||||
{
|
||||
if (!ixp_client_walk(c, newfid, filepath))
|
||||
return FALSE;
|
||||
/* remove */
|
||||
c->fcall.id = TREMOVE;
|
||||
c->fcall.tag = IXP_NOTAG;
|
||||
c->fcall.fid = newfid;
|
||||
return do_fcall(c);
|
||||
}
|
||||
|
||||
int
|
||||
ixp_client_create(IXPClient * c, u32 dirfid, char *name, u32 perm,
|
||||
u8 mode)
|
||||
{
|
||||
/* create */
|
||||
c->fcall.id = TCREATE;
|
||||
c->fcall.tag = IXP_NOTAG;
|
||||
c->fcall.fid = dirfid;
|
||||
_strlcpy(c->fcall.name, name, sizeof(c->fcall.name));
|
||||
c->fcall.perm = perm;
|
||||
c->fcall.mode = mode;
|
||||
return do_fcall(c);
|
||||
}
|
||||
|
||||
int
|
||||
ixp_client_walk(IXPClient * c, u32 newfid, char *filepath)
|
||||
{
|
||||
/* walk */
|
||||
c->fcall.id = TWALK;
|
||||
c->fcall.fid = c->root_fid;
|
||||
c->fcall.newfid = newfid;
|
||||
if (filepath) {
|
||||
_strlcpy(c->fcall.name, filepath, sizeof(c->fcall.name));
|
||||
c->fcall.nwname =
|
||||
tokenize((char **) c->fcall.wname, IXP_MAX_WELEM,
|
||||
c->fcall.name, '/');
|
||||
}
|
||||
return do_fcall(c);
|
||||
}
|
||||
|
||||
int
|
||||
ixp_client_open(IXPClient * c, u32 newfid, char *filepath, u8 mode)
|
||||
{
|
||||
if (!ixp_client_walk(c, newfid, filepath))
|
||||
return FALSE;
|
||||
|
||||
/* open */
|
||||
c->fcall.id = TOPEN;
|
||||
c->fcall.tag = IXP_NOTAG;
|
||||
c->fcall.fid = newfid;
|
||||
c->fcall.mode = mode;
|
||||
return do_fcall(c);
|
||||
}
|
||||
|
||||
u32
|
||||
ixp_client_read(IXPClient * c, u32 fid, u64 offset, void *result,
|
||||
u32 res_len)
|
||||
{
|
||||
u32 bytes =
|
||||
c->fcall.maxmsg - (sizeof(u8) + sizeof(u16) + 2 * sizeof(u32) +
|
||||
sizeof(u64));
|
||||
/* read */
|
||||
c->fcall.id = TREAD;
|
||||
c->fcall.tag = IXP_NOTAG;
|
||||
c->fcall.fid = fid;
|
||||
c->fcall.offset = offset;
|
||||
c->fcall.count = res_len < bytes ? res_len : bytes;
|
||||
if (!do_fcall(c))
|
||||
return 0;
|
||||
memcpy(result, c->fcall.data, c->fcall.count);
|
||||
return c->fcall.count;
|
||||
}
|
||||
|
||||
u32
|
||||
ixp_client_write(IXPClient * c, u32 fid, u64 offset, u32 count,
|
||||
u8 * data)
|
||||
{
|
||||
if (count >
|
||||
c->fcall.maxmsg - (sizeof(u8) + sizeof(u16) + 2 * sizeof(u32) +
|
||||
sizeof(u64))) {
|
||||
c->errstr = "message size exceeds buffer size";
|
||||
return 0;
|
||||
}
|
||||
/* write */
|
||||
c->fcall.id = TWRITE;
|
||||
c->fcall.tag = IXP_NOTAG;
|
||||
c->fcall.fid = fid;
|
||||
c->fcall.offset = offset;
|
||||
c->fcall.count = count;
|
||||
memcpy(c->fcall.data, data, count);
|
||||
if (!do_fcall(c))
|
||||
return 0;
|
||||
return c->fcall.count;
|
||||
}
|
||||
|
||||
int
|
||||
ixp_client_close(IXPClient * c, u32 fid)
|
||||
{
|
||||
/* clunk */
|
||||
c->fcall.id = TCLUNK;
|
||||
c->fcall.tag = IXP_NOTAG;
|
||||
c->fcall.fid = fid;
|
||||
return do_fcall(c);
|
||||
}
|
||||
|
||||
void
|
||||
ixp_client_deinit(IXPClient * c)
|
||||
{
|
||||
/* session finished, now shutdown */
|
||||
if (c->fd) {
|
||||
shutdown(c->fd, SHUT_RDWR);
|
||||
close(c->fd);
|
||||
}
|
||||
}
|
187
libixp2/convert.c
Normal file
187
libixp2/convert.c
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "ixp.h"
|
||||
|
||||
/* encode/decode stuff */
|
||||
|
||||
void *
|
||||
ixp_enc_u8(u8 * msg, u8 val)
|
||||
{
|
||||
msg[0] = val;
|
||||
return &msg[1];
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_dec_u8(u8 * msg, u8 * val)
|
||||
{
|
||||
*val = msg[0];
|
||||
return &msg[1];
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_enc_u16(u8 * msg, u16 val)
|
||||
{
|
||||
msg[0] = val;
|
||||
msg[1] = val >> 8;
|
||||
return &msg[2];
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_dec_u16(u8 * msg, u16 * val)
|
||||
{
|
||||
*val = msg[0] | (msg[1] << 8);
|
||||
return &msg[2];
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_enc_u32(u8 * msg, u32 val)
|
||||
{
|
||||
msg[0] = val;
|
||||
msg[1] = val >> 8;
|
||||
msg[2] = val >> 16;
|
||||
msg[3] = val >> 24;
|
||||
return &msg[4];
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_dec_u32(u8 * msg, u32 * val)
|
||||
{
|
||||
*val = msg[0] | (msg[1] << 8) | (msg[2] << 16) | (msg[3] << 24);
|
||||
return &msg[4];
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_enc_u64(u8 * msg, u64 val)
|
||||
{
|
||||
msg[0] = val;
|
||||
msg[1] = val >> 8;
|
||||
msg[2] = val >> 16;
|
||||
msg[3] = val >> 24;
|
||||
msg[4] = val >> 32;
|
||||
msg[5] = val >> 40;
|
||||
msg[6] = val >> 48;
|
||||
msg[7] = val >> 56;
|
||||
return &msg[8];
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_dec_u64(u8 * msg, u64 * val)
|
||||
{
|
||||
*val = (u64) msg[0] | ((u64) msg[1] << 8) |
|
||||
((u64) msg[2] << 16) | ((u64) msg[3] << 24) |
|
||||
((u64) msg[4] << 32) | ((u64) msg[5] << 40) |
|
||||
((u64) msg[6] << 48) | ((u64) msg[7] << 56);
|
||||
return &msg[8];
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_enc_string(u8 * msg, const char *s)
|
||||
{
|
||||
u16 len = s ? strlen(s) : 0;
|
||||
msg = ixp_enc_u16(msg, len);
|
||||
if (s)
|
||||
memcpy(msg, s, len);
|
||||
return &msg[len];
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_dec_string(u8 * msg, char *string, u16 stringlen, u16 * len)
|
||||
{
|
||||
msg = ixp_dec_u16(msg, len);
|
||||
if (!(*len))
|
||||
return msg;
|
||||
if (*len > stringlen - 1)
|
||||
/* might never happen if stringlen == IXP_MAX_MSG */
|
||||
string[0] = '\0';
|
||||
else {
|
||||
memcpy(string, msg, *len);
|
||||
string[*len] = 0;
|
||||
}
|
||||
return &msg[*len];
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_enc_data(u8 * msg, u8 * data, u32 datalen)
|
||||
{
|
||||
memcpy(msg, data, datalen);
|
||||
return &msg[datalen];
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_dec_data(u8 * msg, u8 * data, u32 datalen)
|
||||
{
|
||||
memcpy(data, msg, datalen);
|
||||
return &msg[datalen];
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_enc_prefix(u8 * msg, u32 size, u8 id, u16 tag)
|
||||
{
|
||||
msg = ixp_enc_u32(msg, size);
|
||||
msg = ixp_enc_u8(msg, id);
|
||||
return ixp_enc_u16(msg, tag);
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_dec_prefix(u8 * msg, u32 * size, u8 * id, u16 * tag)
|
||||
{
|
||||
msg = ixp_dec_u32(msg, size);
|
||||
msg = ixp_dec_u8(msg, id);
|
||||
return ixp_dec_u16(msg, tag);
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_enc_qid(u8 * msg, Qid * qid)
|
||||
{
|
||||
msg = ixp_enc_u8(msg, qid->type);
|
||||
msg = ixp_enc_u32(msg, qid->version);
|
||||
return ixp_enc_u64(msg, qid->path);
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_dec_qid(u8 * msg, Qid * qid)
|
||||
{
|
||||
msg = ixp_dec_u8(msg, &qid->type);
|
||||
msg = ixp_dec_u32(msg, &qid->version);
|
||||
return ixp_dec_u64(msg, &qid->path);
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_enc_stat(u8 * msg, Stat * stat)
|
||||
{
|
||||
msg = ixp_enc_u16(msg, stat->size);
|
||||
msg = ixp_enc_u16(msg, stat->type);
|
||||
msg = ixp_enc_u32(msg, stat->dev);
|
||||
msg = ixp_enc_qid(msg, &stat->qid);
|
||||
msg = ixp_enc_u32(msg, stat->mode);
|
||||
msg = ixp_enc_u32(msg, stat->atime);
|
||||
msg = ixp_enc_u32(msg, stat->mtime);
|
||||
msg = ixp_enc_u64(msg, stat->length);
|
||||
msg = ixp_enc_string(msg, stat->name);
|
||||
msg = ixp_enc_string(msg, stat->uid);
|
||||
msg = ixp_enc_string(msg, stat->gid);
|
||||
return ixp_enc_string(msg, stat->muid);
|
||||
}
|
||||
|
||||
void *
|
||||
ixp_dec_stat(u8 * msg, Stat * stat)
|
||||
{
|
||||
u16 len;
|
||||
msg = ixp_dec_u16(msg, &stat->size);
|
||||
msg = ixp_dec_u16(msg, &stat->type);
|
||||
msg = ixp_dec_u32(msg, &stat->dev);
|
||||
msg = ixp_dec_qid(msg, &stat->qid);
|
||||
msg = ixp_dec_u32(msg, &stat->mode);
|
||||
msg = ixp_dec_u32(msg, &stat->atime);
|
||||
msg = ixp_dec_u32(msg, &stat->mtime);
|
||||
msg = ixp_dec_u64(msg, &stat->length);
|
||||
msg = ixp_dec_string(msg, stat->name, sizeof(stat->name), &len);
|
||||
msg = ixp_dec_string(msg, stat->uid, sizeof(stat->uid), &len);
|
||||
msg = ixp_dec_string(msg, stat->gid, sizeof(stat->gid), &len);
|
||||
return ixp_dec_string(msg, stat->muid, sizeof(stat->muid), &len);
|
||||
}
|
241
libixp2/ixp.h
Normal file
241
libixp2/ixp.h
Normal file
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
#ifndef nil
|
||||
#define nil 0
|
||||
#endif
|
||||
|
||||
#define IXP_VERSION "9P2000"
|
||||
#define IXP_MAX_VERSION 32
|
||||
#define IXP_MAX_ERROR 128
|
||||
#define IXP_MAX_CONN 32
|
||||
#define IXP_MAX_MSG 8192
|
||||
#define IXP_MAX_FLEN 128
|
||||
#define IXP_MAX_ULEN 32
|
||||
#define IXP_MAX_STAT 64
|
||||
#define IXP_MAX_WELEM 16 /* MAXWELEM */
|
||||
#define IXP_MAX_TFUNCS 14
|
||||
|
||||
/* 9P message types */
|
||||
enum {
|
||||
TVERSION = 100,
|
||||
RVERSION,
|
||||
TAUTH = 102,
|
||||
RAUTH,
|
||||
TATTACH = 104,
|
||||
RATTACH,
|
||||
TERROR = 106,
|
||||
RERROR,
|
||||
TFLUSH = 108,
|
||||
RFLUSH,
|
||||
TWALK = 110,
|
||||
RWALK,
|
||||
TOPEN = 112,
|
||||
ROPEN,
|
||||
TCREATE = 114,
|
||||
RCREATE,
|
||||
TREAD = 116,
|
||||
RREAD,
|
||||
TWRITE = 118,
|
||||
RWRITE,
|
||||
TCLUNK = 120,
|
||||
RCLUNK,
|
||||
TREMOVE = 122,
|
||||
RREMOVE,
|
||||
TSTAT = 124,
|
||||
RSTAT,
|
||||
TWSTAT = 126,
|
||||
RWSTAT,
|
||||
};
|
||||
|
||||
/* modes */
|
||||
enum {
|
||||
IXP_OREAD = 0x00,
|
||||
IXP_OWRITE = 0x01,
|
||||
IXP_ORDWR = 0x02,
|
||||
IXP_OEXEC = 0x03,
|
||||
IXP_OEXCL = 0x04,
|
||||
IXP_OTRUNC = 0x10,
|
||||
IXP_OREXEC = 0x20,
|
||||
IXP_ORCLOSE = 0x40,
|
||||
IXP_OAPPEND = 0x80,
|
||||
};
|
||||
|
||||
/* qid.types */
|
||||
enum {
|
||||
IXP_QTDIR = 0x80,
|
||||
IXP_QTAPPEND = 0x40,
|
||||
IXP_QTEXCL = 0x20,
|
||||
IXP_QTMOUNT = 0x10,
|
||||
IXP_QTAUTH = 0x08,
|
||||
IXP_QTTMP = 0x04,
|
||||
IXP_QTSYMLINK = 0x02,
|
||||
IXP_QTLINK = 0x01,
|
||||
IXP_QTFILE = 0x00,
|
||||
};
|
||||
|
||||
/* this should work on all architectures */
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
#define IXP_NOTAG (u16)~0U /* Dummy tag */
|
||||
#define IXP_NOFID (u32)~0 /* No auth */
|
||||
|
||||
typedef struct {
|
||||
u8 type;
|
||||
u32 version;
|
||||
u64 path;
|
||||
} Qid;
|
||||
|
||||
/* stat structure */
|
||||
typedef struct {
|
||||
u16 size;
|
||||
u16 type;
|
||||
u32 dev;
|
||||
Qid qid;
|
||||
u32 mode;
|
||||
u32 atime;
|
||||
u32 mtime;
|
||||
u64 length;
|
||||
char name[IXP_MAX_FLEN];
|
||||
char uid[IXP_MAX_ULEN];
|
||||
char gid[IXP_MAX_ULEN];
|
||||
char muid[IXP_MAX_ULEN];
|
||||
} Stat;
|
||||
|
||||
typedef struct {
|
||||
u8 id;
|
||||
u16 tag;
|
||||
u32 fid;
|
||||
u32 maxmsg; /* Tversion, Rversion */
|
||||
char version[IXP_MAX_VERSION]; /* Tversion, Rversion */
|
||||
u16 oldtag; /* Tflush */
|
||||
char errstr[IXP_MAX_ERROR]; /* Rerror */
|
||||
Qid qid; /* Rattach, Ropen, Rcreate */
|
||||
u32 iounit; /* Ropen, Rcreate */
|
||||
Qid aqid; /* Rauth */
|
||||
u32 afid; /* Tauth, Tattach */
|
||||
char uname[IXP_MAX_ULEN]; /* Tauth, Tattach */
|
||||
char aname[IXP_MAX_FLEN]; /* Tauth, Tattach */
|
||||
u32 perm; /* Tcreate */
|
||||
char name[IXP_MAX_FLEN]; /* Tcreate */
|
||||
u8 mode; /* Tcreate, Topen */
|
||||
u32 newfid; /* Twalk */
|
||||
u16 nwname; /* Twalk */
|
||||
char *wname[IXP_MAX_WELEM]; /* Twalk */
|
||||
u16 nwqid; /* Rwalk */
|
||||
Qid wqid[IXP_MAX_WELEM]; /* Rwalk */
|
||||
u64 offset; /* Tread, Twrite */
|
||||
u32 count; /* Tread, Twrite, Rread */
|
||||
Stat stat; /* Rstat */
|
||||
u16 nstat; /* Twstat, Rstat */
|
||||
u8 data[IXP_MAX_MSG]; /* Twrite, Rread, Twstat,
|
||||
* Rstat */
|
||||
} Fcall;
|
||||
|
||||
typedef struct IXPServer IXPServer;
|
||||
typedef struct IXPConn IXPConn;
|
||||
typedef struct {
|
||||
u8 id;
|
||||
int (*tfunc) (IXPServer *, IXPConn *);
|
||||
} IXPTFunc;
|
||||
|
||||
struct IXPConn {
|
||||
int fd;
|
||||
int dont_close;
|
||||
void (*read) (IXPServer *, IXPConn *);
|
||||
void *aux;
|
||||
};
|
||||
|
||||
struct IXPServer {
|
||||
int running;
|
||||
IXPConn conn[IXP_MAX_CONN];
|
||||
void (*freeconn) (IXPServer *, IXPConn *);
|
||||
int maxfd;
|
||||
fd_set rd;
|
||||
IXPTFunc *funcs;
|
||||
Fcall fcall;
|
||||
char *errstr;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
u32 root_fid;
|
||||
Qid root_qid;
|
||||
Fcall fcall;
|
||||
char *errstr;
|
||||
} IXPClient;
|
||||
|
||||
/* client.c */
|
||||
int ixp_client_init(IXPClient * c, char *sockfile);
|
||||
void ixp_client_deinit(IXPClient * c);
|
||||
int ixp_client_remove(IXPClient * c, u32 newfid, char *filepath);
|
||||
int
|
||||
ixp_client_create(IXPClient * c, u32 dirfid, char *name, u32 perm,
|
||||
u8 mode);
|
||||
int ixp_client_walk(IXPClient * c, u32 newfid, char *filepath);
|
||||
int ixp_client_open(IXPClient * c, u32 newfid, char *filepath, u8 mode);
|
||||
u32
|
||||
ixp_client_read(IXPClient * c, u32 fid, u64 offset, void *result,
|
||||
u32 res_len);
|
||||
u32
|
||||
ixp_client_write(IXPClient * c, u32 fid, u64 offset, u32 count,
|
||||
u8 * data);
|
||||
int ixp_client_close(IXPClient * c, u32 fid);
|
||||
|
||||
|
||||
/* convert.c */
|
||||
void *ixp_enc_u8(u8 * msg, u8 val);
|
||||
void *ixp_dec_u8(u8 * msg, u8 * val);
|
||||
void *ixp_enc_u16(u8 * msg, u16 val);
|
||||
void *ixp_dec_u16(u8 * msg, u16 * val);
|
||||
void *ixp_enc_u32(u8 * msg, u32 val);
|
||||
void *ixp_dec_u32(u8 * msg, u32 * val);
|
||||
void *ixp_enc_u64(u8 * msg, u64 val);
|
||||
void *ixp_dec_u64(u8 * msg, u64 * val);
|
||||
void *ixp_enc_string(u8 * msg, const char *s);
|
||||
void *ixp_dec_string(u8 * msg, char *string, u16 stringlen, u16 * len);
|
||||
void *ixp_enc_data(u8 * msg, u8 * data, u32 datalen);
|
||||
void *ixp_dec_data(u8 * msg, u8 * data, u32 datalen);
|
||||
void *ixp_enc_prefix(u8 * msg, u32 size, u8 id, u16 tag);
|
||||
void *ixp_dec_prefix(u8 * msg, u32 * size, u8 * id, u16 * tag);
|
||||
void *ixp_enc_qid(u8 * msg, Qid * qid);
|
||||
void *ixp_dec_qid(u8 * msg, Qid * qid);
|
||||
void *ixp_enc_stat(u8 * msg, Stat * stat);
|
||||
void *ixp_dec_stat(u8 * msg, Stat * stat);
|
||||
|
||||
/* message.c */
|
||||
u16 ixp_sizeof_stat(Stat * stat);
|
||||
u32 ixp_fcall_to_msg(Fcall * fcall, void *msg, u32 msglen);
|
||||
u32 ixp_msg_to_fcall(void *msg, u32 msglen, Fcall * fcall);
|
||||
|
||||
/* server.c */
|
||||
IXPConn *
|
||||
ixp_server_add_conn(IXPServer * s, int fd, int dont_close,
|
||||
void (*read) (IXPServer *, IXPConn *));
|
||||
int ixp_server_tversion(IXPServer *, IXPConn * c);
|
||||
void ixp_server_rm_conn(IXPServer * s, IXPConn * c);
|
||||
void ixp_server_loop(IXPServer * s);
|
||||
int ixp_server_init(IXPServer * s, char *sockfile, IXPTFunc * funcs,
|
||||
void (*freeconn) (IXPServer *, IXPConn *));
|
||||
void ixp_server_deinit(IXPServer * s);
|
||||
|
||||
/* socket.c */
|
||||
int ixp_connect_sock(char *sockfile);
|
||||
int ixp_accept_sock(int fd);
|
||||
int ixp_create_sock(char *sockfile, char **errstr);
|
||||
|
||||
/* transport.c */
|
||||
u32 ixp_send_message(int fd, void *msg, u32 msize, char **errstr);
|
||||
u32 ixp_recv_message(int fd, void *msg, u32 msglen, char **errstr);
|
293
libixp2/message.c
Normal file
293
libixp2/message.c
Normal file
@ -0,0 +1,293 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "ixp.h"
|
||||
|
||||
static u16
|
||||
sizeof_string(const char *s)
|
||||
{
|
||||
return sizeof(u16) + strlen(s);
|
||||
}
|
||||
|
||||
u16
|
||||
ixp_sizeof_stat(Stat * stat)
|
||||
{
|
||||
return sizeof(Qid)
|
||||
+ 2 * sizeof(u16)
|
||||
+ 4 * sizeof(u32)
|
||||
+ sizeof(u64)
|
||||
+ sizeof_string(stat->name)
|
||||
+ sizeof_string(stat->uid)
|
||||
+ sizeof_string(stat->gid)
|
||||
+ sizeof_string(stat->muid);
|
||||
}
|
||||
|
||||
u32
|
||||
ixp_fcall_to_msg(Fcall * fcall, void *msg, u32 msglen)
|
||||
{
|
||||
u32 i, msize = sizeof(u8) + sizeof(u16) + sizeof(u32);
|
||||
void *p = msg;
|
||||
|
||||
switch (fcall->id) {
|
||||
case TVERSION:
|
||||
case RVERSION:
|
||||
msize += sizeof(u32) + sizeof_string(fcall->version);
|
||||
break;
|
||||
case TAUTH:
|
||||
msize +=
|
||||
sizeof(u32) + sizeof_string(fcall->uname) +
|
||||
sizeof_string(fcall->aname);
|
||||
break;
|
||||
case RAUTH:
|
||||
case RATTACH:
|
||||
msize += sizeof(Qid);
|
||||
break;
|
||||
case TATTACH:
|
||||
msize +=
|
||||
2 * sizeof(u32) + sizeof_string(fcall->uname) +
|
||||
sizeof_string(fcall->aname);
|
||||
break;
|
||||
case RERROR:
|
||||
msize += sizeof_string(fcall->errstr) + sizeof(u32);
|
||||
break;
|
||||
case RWRITE:
|
||||
case TCLUNK:
|
||||
case TREMOVE:
|
||||
case TSTAT:
|
||||
msize += sizeof(u32);
|
||||
break;
|
||||
case TWALK:
|
||||
msize += sizeof(u16) + 2 * sizeof(u32);
|
||||
for (i = 0; i < fcall->nwname; i++)
|
||||
msize += sizeof_string(fcall->wname[i]);
|
||||
break;
|
||||
case TFLUSH:
|
||||
msize += sizeof(u16);
|
||||
break;
|
||||
case RWALK:
|
||||
msize += sizeof(u16) + fcall->nwqid * sizeof(Qid);
|
||||
break;
|
||||
case TOPEN:
|
||||
msize += sizeof(u32) + sizeof(u8);
|
||||
break;
|
||||
case ROPEN:
|
||||
case RCREATE:
|
||||
msize += sizeof(Qid) + sizeof(u32);
|
||||
break;
|
||||
case TCREATE:
|
||||
msize += sizeof(u8) + 2 * sizeof(u32) + sizeof_string(fcall->name);
|
||||
break;
|
||||
case TREAD:
|
||||
msize += 2 * sizeof(u32) + sizeof(u64);
|
||||
break;
|
||||
case RREAD:
|
||||
msize += sizeof(u32) + fcall->count;
|
||||
break;
|
||||
case TWRITE:
|
||||
msize += 2 * sizeof(u32) + sizeof(u64) + fcall->count;
|
||||
break;
|
||||
case RSTAT:
|
||||
msize += ixp_sizeof_stat(&fcall->stat);
|
||||
break;
|
||||
case TWSTAT:
|
||||
msize += sizeof(u32) + ixp_sizeof_stat(&fcall->stat);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (msize > msglen)
|
||||
return 0;
|
||||
p = ixp_enc_prefix(p, msize, fcall->id, fcall->tag);
|
||||
|
||||
switch (fcall->id) {
|
||||
case TVERSION:
|
||||
case RVERSION:
|
||||
p = ixp_enc_u32(p, fcall->maxmsg);
|
||||
p = ixp_enc_string(p, fcall->version);
|
||||
break;
|
||||
case TAUTH:
|
||||
p = ixp_enc_u32(p, fcall->afid);
|
||||
p = ixp_enc_string(p, fcall->uname);
|
||||
p = ixp_enc_string(p, fcall->aname);
|
||||
break;
|
||||
case RAUTH:
|
||||
case RATTACH:
|
||||
p = ixp_enc_qid(p, &fcall->qid);
|
||||
break;
|
||||
case TATTACH:
|
||||
p = ixp_enc_u32(p, fcall->fid);
|
||||
p = ixp_enc_u32(p, fcall->afid);
|
||||
p = ixp_enc_string(p, fcall->uname);
|
||||
p = ixp_enc_string(p, fcall->aname);
|
||||
break;
|
||||
case RERROR:
|
||||
p = ixp_enc_string(p, fcall->errstr);
|
||||
break;
|
||||
case TFLUSH:
|
||||
p = ixp_enc_u16(p, fcall->oldtag);
|
||||
break;
|
||||
case TWALK:
|
||||
p = ixp_enc_u32(p, fcall->fid);
|
||||
p = ixp_enc_u32(p, fcall->newfid);
|
||||
p = ixp_enc_u16(p, fcall->nwname);
|
||||
for (i = 0; i < fcall->nwname; i++)
|
||||
p = ixp_enc_string(p, fcall->wname[i]);
|
||||
break;
|
||||
case RWALK:
|
||||
p = ixp_enc_u16(p, fcall->nwqid);
|
||||
for (i = 0; i < fcall->nwqid; i++)
|
||||
p = ixp_enc_qid(p, &fcall->wqid[i]);
|
||||
break;
|
||||
case TOPEN:
|
||||
p = ixp_enc_u32(p, fcall->fid);
|
||||
p = ixp_enc_u8(p, fcall->mode);
|
||||
break;
|
||||
case ROPEN:
|
||||
case RCREATE:
|
||||
p = ixp_enc_qid(p, &fcall->qid);
|
||||
p = ixp_enc_u32(p, fcall->iounit);
|
||||
break;
|
||||
case TCREATE:
|
||||
p = ixp_enc_u32(p, fcall->fid);
|
||||
p = ixp_enc_string(p, fcall->name);
|
||||
p = ixp_enc_u32(p, fcall->perm);
|
||||
p = ixp_enc_u8(p, fcall->mode);
|
||||
break;
|
||||
case TREAD:
|
||||
p = ixp_enc_u32(p, fcall->fid);
|
||||
p = ixp_enc_u64(p, fcall->offset);
|
||||
p = ixp_enc_u32(p, fcall->count);
|
||||
break;
|
||||
case RREAD:
|
||||
p = ixp_enc_u32(p, fcall->count);
|
||||
p = ixp_enc_data(p, fcall->data, fcall->count);
|
||||
break;
|
||||
case TWRITE:
|
||||
p = ixp_enc_u32(p, fcall->fid);
|
||||
p = ixp_enc_u64(p, fcall->offset);
|
||||
p = ixp_enc_u32(p, fcall->count);
|
||||
p = ixp_enc_data(p, fcall->data, fcall->count);
|
||||
break;
|
||||
case RWRITE:
|
||||
p = ixp_enc_u32(p, fcall->count);
|
||||
break;
|
||||
case TCLUNK:
|
||||
case TREMOVE:
|
||||
case TSTAT:
|
||||
p = ixp_enc_u32(p, fcall->fid);
|
||||
break;
|
||||
case RSTAT:
|
||||
p = ixp_enc_stat(p, &fcall->stat);
|
||||
break;
|
||||
case TWSTAT:
|
||||
p = ixp_enc_u32(p, fcall->fid);
|
||||
p = ixp_enc_stat(p, &fcall->stat);
|
||||
break;
|
||||
}
|
||||
return msize;
|
||||
}
|
||||
|
||||
u32
|
||||
ixp_msg_to_fcall(void *msg, u32 msglen, Fcall * fcall)
|
||||
{
|
||||
u32 i, msize;
|
||||
u16 len;
|
||||
void *p = ixp_dec_prefix(msg, &msize, &fcall->id, &fcall->tag);
|
||||
|
||||
if (msize > msglen) /* bad message */
|
||||
return 0;
|
||||
|
||||
switch (fcall->id) {
|
||||
case TVERSION:
|
||||
case RVERSION:
|
||||
p = ixp_dec_u32(p, &fcall->maxmsg);
|
||||
p = ixp_dec_string(p, fcall->version, sizeof(fcall->version),
|
||||
&len);
|
||||
break;
|
||||
case TAUTH:
|
||||
p = ixp_dec_u32(p, &fcall->afid);
|
||||
p = ixp_dec_string(p, fcall->uname, sizeof(fcall->uname), &len);
|
||||
p = ixp_dec_string(p, fcall->aname, sizeof(fcall->aname), &len);
|
||||
break;
|
||||
case RAUTH:
|
||||
case RATTACH:
|
||||
p = ixp_dec_qid(p, &fcall->qid);
|
||||
break;
|
||||
case TATTACH:
|
||||
p = ixp_dec_u32(p, &fcall->fid);
|
||||
p = ixp_dec_u32(p, &fcall->afid);
|
||||
p = ixp_dec_string(p, fcall->uname, sizeof(fcall->uname), &len);
|
||||
p = ixp_dec_string(p, fcall->aname, sizeof(fcall->aname), &len);
|
||||
break;
|
||||
case RERROR:
|
||||
p = ixp_dec_string(p, fcall->errstr, sizeof(fcall->errstr), &len);
|
||||
break;
|
||||
case TFLUSH:
|
||||
p = ixp_dec_u16(p, &fcall->oldtag);
|
||||
break;
|
||||
case TWALK:
|
||||
p = ixp_dec_u32(p, &fcall->fid);
|
||||
p = ixp_dec_u32(p, &fcall->newfid);
|
||||
p = ixp_dec_u16(p, &fcall->nwname);
|
||||
for (i = 0; i < fcall->nwname; i++)
|
||||
p = ixp_dec_string(p, fcall->wname[i], IXP_MAX_FLEN, &len);
|
||||
break;
|
||||
case RWALK:
|
||||
p = ixp_dec_u16(p, &fcall->nwqid);
|
||||
for (i = 0; i < fcall->nwqid; i++)
|
||||
p = ixp_dec_qid(p, &fcall->wqid[i]);
|
||||
break;
|
||||
case TOPEN:
|
||||
p = ixp_dec_u32(p, &fcall->fid);
|
||||
p = ixp_dec_u8(p, &fcall->mode);
|
||||
break;
|
||||
case ROPEN:
|
||||
case RCREATE:
|
||||
p = ixp_dec_qid(p, &fcall->qid);
|
||||
p = ixp_dec_u32(p, &fcall->iounit);
|
||||
break;
|
||||
case TCREATE:
|
||||
p = ixp_dec_u32(p, &fcall->fid);
|
||||
p = ixp_dec_string(p, fcall->name, sizeof(fcall->name), &len);
|
||||
p = ixp_dec_u32(p, &fcall->perm);
|
||||
p = ixp_dec_u8(p, &fcall->mode);
|
||||
break;
|
||||
case TREAD:
|
||||
p = ixp_dec_u32(p, &fcall->fid);
|
||||
p = ixp_dec_u64(p, &fcall->offset);
|
||||
p = ixp_dec_u32(p, &fcall->count);
|
||||
break;
|
||||
case RREAD:
|
||||
p = ixp_dec_u32(p, &fcall->count);
|
||||
p = ixp_dec_data(p, fcall->data, fcall->count);
|
||||
break;
|
||||
case TWRITE:
|
||||
p = ixp_dec_u32(p, &fcall->fid);
|
||||
p = ixp_dec_u64(p, &fcall->offset);
|
||||
p = ixp_dec_u32(p, &fcall->count);
|
||||
p = ixp_dec_data(p, fcall->data, fcall->count);
|
||||
break;
|
||||
case RWRITE:
|
||||
p = ixp_dec_u32(p, &fcall->count);
|
||||
break;
|
||||
case TCLUNK:
|
||||
case TREMOVE:
|
||||
case TSTAT:
|
||||
p = ixp_dec_u32(p, &fcall->fid);
|
||||
break;
|
||||
case RSTAT:
|
||||
p = ixp_dec_stat(p, &fcall->stat);
|
||||
break;
|
||||
case TWSTAT:
|
||||
p = ixp_dec_u32(p, &fcall->fid);
|
||||
p = ixp_dec_stat(p, &fcall->stat);
|
||||
break;
|
||||
}
|
||||
|
||||
return msize;
|
||||
}
|
201
libixp2/server.c
Normal file
201
libixp2/server.c
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ixp.h"
|
||||
#include "cext.h"
|
||||
|
||||
static IXPConn zero_conn = {-1, 0, 0, 0};
|
||||
static u8 msg[IXP_MAX_MSG];
|
||||
|
||||
static IXPConn *
|
||||
next_free_conn(IXPServer * s)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < IXP_MAX_CONN; i++)
|
||||
if (s->conn[i].fd < 0)
|
||||
return &s->conn[i];
|
||||
return nil;
|
||||
}
|
||||
|
||||
static void
|
||||
prepare_select(IXPServer * s)
|
||||
{
|
||||
int i;
|
||||
FD_ZERO(&s->rd);
|
||||
for (i = 0; i < IXP_MAX_CONN; i++) {
|
||||
if (s->conn[i].fd >= 0) {
|
||||
if (s->maxfd < s->conn[i].fd)
|
||||
s->maxfd = s->conn[i].fd;
|
||||
if (s->conn[i].read)
|
||||
FD_SET(s->conn[i].fd, &s->rd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ixp_server_rm_conn(IXPServer * s, IXPConn * c)
|
||||
{
|
||||
if (!c->dont_close) {
|
||||
shutdown(c->fd, SHUT_RDWR);
|
||||
close(c->fd);
|
||||
}
|
||||
if (s->freeconn)
|
||||
s->freeconn(s, c);
|
||||
*c = zero_conn;
|
||||
}
|
||||
|
||||
static IXPConn *
|
||||
init_conn(IXPConn * c, int fd, int dont_close,
|
||||
void (*read) (IXPServer *, IXPConn *))
|
||||
{
|
||||
*c = zero_conn;
|
||||
c->fd = fd;
|
||||
c->dont_close = dont_close;
|
||||
c->read = read;
|
||||
return c;
|
||||
}
|
||||
|
||||
IXPConn *
|
||||
ixp_server_add_conn(IXPServer * s, int fd, int dont_close,
|
||||
void (*read) (IXPServer *, IXPConn *))
|
||||
{
|
||||
IXPConn *c = next_free_conn(s);
|
||||
if (!c)
|
||||
return nil;
|
||||
return init_conn(c, fd, dont_close, read);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_conns(IXPServer * s)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < IXP_MAX_CONN; i++) {
|
||||
if (s->conn[i].fd >= 0) {
|
||||
if (FD_ISSET(s->conn[i].fd, &s->rd) && s->conn[i].read)
|
||||
/* call back read handler */
|
||||
s->conn[i].read(s, &s->conn[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
server_client_read(IXPServer * s, IXPConn * c)
|
||||
{
|
||||
u32 i, msize;
|
||||
s->errstr = 0;
|
||||
if (!(msize = ixp_recv_message(c->fd, msg, IXP_MAX_MSG, &s->errstr))) {
|
||||
ixp_server_rm_conn(s, c);
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "msize=%d\n", msize);
|
||||
if ((msize = ixp_msg_to_fcall(msg, IXP_MAX_MSG, &s->fcall))) {
|
||||
for (i = 0; s->funcs && s->funcs[i].id; i++) {
|
||||
if (s->funcs[i].id == s->fcall.id) {
|
||||
if (!s->funcs[i].tfunc(s, c))
|
||||
break;
|
||||
msize = ixp_fcall_to_msg(&s->fcall, msg, s->fcall.maxmsg);
|
||||
fprintf(stderr, "msize=%d\n", msize);
|
||||
if (ixp_send_message(c->fd, msg, msize, &s->errstr) != msize)
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!s->errstr)
|
||||
s->errstr = "function not supported";
|
||||
s->fcall.id = RERROR;
|
||||
_strlcpy(s->fcall.errstr, s->errstr, sizeof(s->fcall.errstr));
|
||||
msize = ixp_fcall_to_msg(&s->fcall, msg, IXP_MAX_MSG);
|
||||
if (ixp_send_message(c->fd, msg, msize, &s->errstr) != msize)
|
||||
ixp_server_rm_conn(s, c);
|
||||
}
|
||||
|
||||
static void
|
||||
server_read(IXPServer * s, IXPConn * c)
|
||||
{
|
||||
int fd;
|
||||
IXPConn *new = next_free_conn(s);
|
||||
if (new && ((fd = ixp_accept_sock(c->fd)) >= 0))
|
||||
init_conn(new, fd, 0, server_client_read);
|
||||
}
|
||||
|
||||
void
|
||||
ixp_server_loop(IXPServer * s)
|
||||
{
|
||||
int r;
|
||||
s->running = TRUE;
|
||||
s->errstr = 0;
|
||||
|
||||
/* main loop */
|
||||
while (s->running) {
|
||||
|
||||
prepare_select(s);
|
||||
|
||||
r = select(s->maxfd + 1, &s->rd, 0, 0, 0);
|
||||
if (r == -1 && errno == EINTR)
|
||||
continue;
|
||||
if (r < 0) {
|
||||
s->errstr = "fatal select error";
|
||||
break; /* allow cleanups in IXP using app */
|
||||
} else if (r > 0)
|
||||
handle_conns(s);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
ixp_server_tversion(IXPServer * s, IXPConn * c)
|
||||
{
|
||||
fprintf(stderr, "got version %s (%s) %d (%d)\n", s->fcall.version, IXP_VERSION,
|
||||
s->fcall.maxmsg, IXP_MAX_MSG);
|
||||
if (strncmp(s->fcall.version, IXP_VERSION, strlen(IXP_VERSION))) {
|
||||
s->errstr = "9P versions differ";
|
||||
return FALSE;
|
||||
}
|
||||
else if (s->fcall.maxmsg > IXP_MAX_MSG)
|
||||
s->fcall.maxmsg = IXP_MAX_MSG;
|
||||
s->fcall.id = RVERSION;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ixp_server_init(IXPServer * s, char *sockfile, IXPTFunc * funcs,
|
||||
void (*freeconn) (IXPServer *, IXPConn *))
|
||||
{
|
||||
int fd, i;
|
||||
s->funcs = funcs;
|
||||
s->freeconn = freeconn;
|
||||
s->errstr = 0;
|
||||
if (!sockfile) {
|
||||
s->errstr = "no socket file provided or invalid directory";
|
||||
return FALSE;
|
||||
}
|
||||
if ((fd = ixp_create_sock(sockfile, &s->errstr)) < 0)
|
||||
return FALSE;
|
||||
for (i = 0; i < IXP_MAX_CONN; i++)
|
||||
s->conn[i] = zero_conn;
|
||||
ixp_server_add_conn(s, fd, 0, server_read);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
ixp_server_deinit(IXPServer * s)
|
||||
{
|
||||
int i;
|
||||
/* shut down server */
|
||||
for (i = 0; i < IXP_MAX_CONN; i++)
|
||||
if (s->conn[i].fd >= 0)
|
||||
ixp_server_rm_conn(s, &s->conn[i]);
|
||||
}
|
86
libixp2/socket.c
Normal file
86
libixp2/socket.c
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cext.h"
|
||||
#include "ixp.h"
|
||||
|
||||
int
|
||||
ixp_connect_sock(char *sockfile)
|
||||
{
|
||||
int fd = 0;
|
||||
struct sockaddr_un addr = {0};
|
||||
socklen_t su_len;
|
||||
|
||||
/* init */
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, sockfile, sizeof(addr.sun_path));
|
||||
su_len = sizeof(struct sockaddr) + strlen(addr.sun_path);
|
||||
|
||||
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
|
||||
return -1;
|
||||
if (connect(fd, (struct sockaddr *) & addr, su_len)) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
ixp_accept_sock(int fd)
|
||||
{
|
||||
socklen_t su_len;
|
||||
struct sockaddr_un addr = {0};
|
||||
|
||||
su_len = sizeof(struct sockaddr);
|
||||
return accept(fd, (struct sockaddr *) & addr, &su_len);
|
||||
}
|
||||
|
||||
int
|
||||
ixp_create_sock(char *sockfile, char **errstr)
|
||||
{
|
||||
int fd;
|
||||
int yes = 1;
|
||||
struct sockaddr_un addr = {0};
|
||||
socklen_t su_len;
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
|
||||
*errstr = "cannot open socket";
|
||||
return -1;
|
||||
}
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
|
||||
(char *) &yes, sizeof(yes)) < 0) {
|
||||
*errstr = "cannot set socket options";
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, sockfile, sizeof(addr.sun_path));
|
||||
su_len = sizeof(struct sockaddr) + strlen(addr.sun_path);
|
||||
|
||||
if (bind(fd, (struct sockaddr *) & addr, su_len) < 0) {
|
||||
*errstr = "cannot bind socket";
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
chmod(sockfile, S_IRWXU);
|
||||
|
||||
if (listen(fd, IXP_MAX_CONN) < 0) {
|
||||
*errstr = "cannot listen on socket";
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
78
libixp2/transport.c
Normal file
78
libixp2/transport.c
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ixp.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
u32
|
||||
ixp_send_message(int fd, void *msg, u32 msize, char **errstr)
|
||||
{
|
||||
u32 num = 0;
|
||||
int r;
|
||||
|
||||
/* send message */
|
||||
while (num < msize) {
|
||||
r = write(fd, msg + num, msize - num);
|
||||
if (r == -1 && errno == EINTR)
|
||||
continue;
|
||||
if (r < 1) {
|
||||
*errstr = "cannot send message";
|
||||
return 0;
|
||||
}
|
||||
num += r;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
static u32
|
||||
ixp_recv_data(int fd, void *msg, u32 msize, char **errstr)
|
||||
{
|
||||
u32 num = 0;
|
||||
int r = 0;
|
||||
|
||||
/* receive data */
|
||||
while (num < msize) {
|
||||
r = read(fd, msg + num, msize - num);
|
||||
if (r == -1 && errno == EINTR)
|
||||
continue;
|
||||
if (r < 1) {
|
||||
*errstr = "cannot receive data";
|
||||
return 0;
|
||||
}
|
||||
num += r;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
u32
|
||||
ixp_recv_message(int fd, void *msg, u32 msglen, char **errstr)
|
||||
{
|
||||
u32 msize;
|
||||
|
||||
/* receive header */
|
||||
if (ixp_recv_data(fd, msg, sizeof(u32), errstr) != sizeof(u32))
|
||||
return 0;
|
||||
ixp_dec_u32(msg, &msize);
|
||||
if (msize > msglen) {
|
||||
*errstr = "message size exceeds buffer size";
|
||||
return 0;
|
||||
}
|
||||
/* receive message */
|
||||
if (ixp_recv_data(fd, msg + sizeof(u32), msize - sizeof(u32), errstr)
|
||||
!= msize - sizeof(u32))
|
||||
return 0;
|
||||
return msize;
|
||||
}
|
25
liblitz/Makefile
Normal file
25
liblitz/Makefile
Normal file
@ -0,0 +1,25 @@
|
||||
# liblitz - non wimp GUI toolkit
|
||||
# (C)opyright MMIV-MMV Anselm R. Garbe
|
||||
|
||||
include ../config.mk
|
||||
|
||||
CFLAGS += -I../libixp -I../libcext
|
||||
LDFLAGS += -L../libixp -lixp -L../libcext -lcext
|
||||
|
||||
SRC = draw.c geometry.c kb.c mouse.c util.c
|
||||
OBJ = ${SRC:.c=.o}
|
||||
|
||||
all: liblitz
|
||||
@echo built liblitz
|
||||
|
||||
.c.o:
|
||||
@echo CC $<
|
||||
@${CC} -c ${CFLAGS} $<
|
||||
|
||||
liblitz: ${OBJ}
|
||||
@echo AR $@.a
|
||||
@${AR} $@.a ${OBJ}
|
||||
@${RANLIB} $@.a
|
||||
|
||||
clean:
|
||||
rm -f liblitz.a *.o
|
64
liblitz/blitz.h
Normal file
64
liblitz/blitz.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#define BLITZ_FONT "fixed"
|
||||
#define BLITZ_SEL_FG_COLOR "#eeeeee"
|
||||
#define BLITZ_SEL_BG_COLOR "#506070"
|
||||
#define BLITZ_SEL_BORDER_COLOR "#708090"
|
||||
#define BLITZ_NORM_FG_COLOR "#bbbbbb"
|
||||
#define BLITZ_NORM_BG_COLOR "#222222"
|
||||
#define BLITZ_NORM_BORDER_COLOR "#000000"
|
||||
|
||||
typedef enum {
|
||||
CENTER, WEST, NWEST, NORTH, NEAST, EAST,
|
||||
SEAST, SOUTH, SWEST
|
||||
} Align;
|
||||
|
||||
typedef struct Draw Draw;
|
||||
|
||||
struct Draw {
|
||||
Drawable drawable;
|
||||
GC gc;
|
||||
unsigned long bg;
|
||||
unsigned long fg;
|
||||
unsigned long border;
|
||||
Align align;
|
||||
XFontStruct *font;
|
||||
XRectangle rect; /* relative rect */
|
||||
XRectangle *notch; /* relative notch rect */
|
||||
char *data;
|
||||
};
|
||||
|
||||
/* draw.c */
|
||||
XFontStruct *blitz_getfont(Display * dpy, char *fontstr);
|
||||
unsigned long blitz_loadcolor(Display * dpy, int mon, char *colstr);
|
||||
void blitz_drawlabel(Display * dpy, Draw * r);
|
||||
void blitz_drawmeter(Display * dpy, Draw * r);
|
||||
void blitz_drawlabelnoborder(Display * dpy, Draw * r);
|
||||
|
||||
/* geometry.c */
|
||||
int
|
||||
blitz_strtorect(Display * dpy, XRectangle * root, XRectangle * r,
|
||||
char *val);
|
||||
int blitz_ispointinrect(int x, int y, XRectangle * r);
|
||||
int blitz_distance(XRectangle * origin, XRectangle * target);
|
||||
void
|
||||
blitz_getbasegeometry(void **items, unsigned int *size,
|
||||
unsigned int *cols, unsigned int *rows);
|
||||
|
||||
/* mouse.c */
|
||||
char *blitz_buttontostr(unsigned int button);
|
||||
unsigned int blitz_strtobutton(char *val);
|
||||
|
||||
/* kb.c */
|
||||
char *blitz_modtostr(unsigned long mod);
|
||||
unsigned long blitz_strtomod(char *val);
|
||||
|
||||
/* util.c */
|
||||
long long
|
||||
_strtonum(const char *numstr, long long minval,
|
||||
long long maxval);
|
181
liblitz/draw.c
Normal file
181
liblitz/draw.c
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "blitz.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
XFontStruct *
|
||||
blitz_getfont(Display * dpy, char *fontstr)
|
||||
{
|
||||
XFontStruct *font;
|
||||
font = XLoadQueryFont(dpy, fontstr);
|
||||
if (!font) {
|
||||
font = XLoadQueryFont(dpy, "fixed");
|
||||
if (!font) {
|
||||
fprintf(stderr, "%s", "wmii: error, cannot load fixed font\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return font;
|
||||
}
|
||||
|
||||
unsigned long
|
||||
blitz_loadcolor(Display * dpy, int mon, char *colstr)
|
||||
{
|
||||
XColor color;
|
||||
char col[8];
|
||||
|
||||
_strlcpy(col, colstr, sizeof(col));
|
||||
col[7] = '\0';
|
||||
XAllocNamedColor(dpy, DefaultColormap(dpy, mon), col, &color, &color);
|
||||
return color.pixel;
|
||||
}
|
||||
|
||||
static void
|
||||
draw_bg(Display * dpy, Draw * d)
|
||||
{
|
||||
XRectangle rect[4];
|
||||
XSetForeground(dpy, d->gc, d->bg);
|
||||
if (!d->notch) {
|
||||
XFillRectangles(dpy, d->drawable, d->gc, &d->rect, 1);
|
||||
return;
|
||||
}
|
||||
rect[0] = d->rect;
|
||||
rect[0].height = d->notch->y;
|
||||
rect[1] = d->rect;
|
||||
rect[1].y = d->notch->y;
|
||||
rect[1].width = d->notch->x;
|
||||
rect[1].height = d->notch->height;
|
||||
rect[2].x = d->notch->x + d->notch->width;
|
||||
rect[2].y = d->notch->y;
|
||||
rect[2].width = d->rect.width - (d->notch->x + d->notch->width);
|
||||
rect[2].height = d->notch->height;
|
||||
rect[3] = d->rect;
|
||||
rect[3].y = d->notch->y + d->notch->height;
|
||||
rect[3].height = d->rect.height - (d->notch->y + d->notch->height);
|
||||
XFillRectangles(dpy, d->drawable, d->gc, rect, 4);
|
||||
}
|
||||
|
||||
static void
|
||||
_draw_border(Display * dpy, Draw * d)
|
||||
{
|
||||
XPoint points[5];
|
||||
|
||||
XSetLineAttributes(dpy, d->gc, 1, LineSolid, CapButt, JoinMiter);
|
||||
XSetForeground(dpy, d->gc, d->border);
|
||||
points[0].x = d->rect.x;
|
||||
points[0].y = d->rect.y;
|
||||
points[1].x = d->rect.width - 1;
|
||||
points[1].y = 0;
|
||||
points[2].x = 0;
|
||||
points[2].y = d->rect.height - 1;
|
||||
points[3].x = -(d->rect.width - 1);
|
||||
points[3].y = 0;
|
||||
points[4].x = 0;
|
||||
points[4].y = -(d->rect.height - 1);
|
||||
XDrawLines(dpy, d->drawable, d->gc, points, 5, CoordModePrevious);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_text(Display * dpy, Draw * d)
|
||||
{
|
||||
unsigned int x, y, w, h, shortened = FALSE;
|
||||
size_t len = 0;
|
||||
static char text[2048];
|
||||
|
||||
if (!d->data)
|
||||
return;
|
||||
|
||||
len = strlen(d->data);
|
||||
_strlcpy(text, d->data, sizeof(text));
|
||||
XSetFont(dpy, d->gc, d->font->fid);
|
||||
h = d->font->ascent + d->font->descent;
|
||||
y = d->rect.y + d->rect.height / 2 - h / 2 + d->font->ascent;
|
||||
|
||||
/* shorten text if necessary */
|
||||
while (len && (w = XTextWidth(d->font, text, len)) > d->rect.width) {
|
||||
text[len - 1] = '\0';
|
||||
len--;
|
||||
shortened = TRUE;
|
||||
}
|
||||
|
||||
if (w > d->rect.width)
|
||||
return;
|
||||
|
||||
/* mark shortened info in the string */
|
||||
if (shortened) {
|
||||
if (len > 3)
|
||||
text[len - 3] = '.';
|
||||
if (len > 2)
|
||||
text[len - 2] = '.';
|
||||
if (len > 1)
|
||||
text[len - 1] = '.';
|
||||
}
|
||||
switch (d->align) {
|
||||
case WEST:
|
||||
x = d->rect.x + h / 2;
|
||||
break;
|
||||
case EAST:
|
||||
x = d->rect.x + d->rect.width - (h / 2 + w);
|
||||
break;
|
||||
default: /* CENTER */
|
||||
x = d->rect.x + (d->rect.width - w) / 2;
|
||||
break;
|
||||
}
|
||||
|
||||
XSetBackground(dpy, d->gc, d->bg);
|
||||
/*
|
||||
* uncomment, if you want get an shadow effect XSetForeground(dpy,
|
||||
* d->gc, BlackPixel(dpy, DefaultScreen(dpy))); XDrawString(dpy,
|
||||
* d->drawable, d->gc, x + 1, y + 1, text, len);
|
||||
*/
|
||||
XSetForeground(dpy, d->gc, d->fg);
|
||||
XDrawString(dpy, d->drawable, d->gc, x, y, text, len);
|
||||
}
|
||||
|
||||
/* draws meter */
|
||||
void
|
||||
blitz_drawmeter(Display * dpy, Draw * d)
|
||||
{
|
||||
unsigned int offy, mh, val, w = d->rect.width - 4;
|
||||
|
||||
if (!d->data || strncmp(d->data, "%m:", 3))
|
||||
return;
|
||||
|
||||
val = _strtonum(&d->data[3], 0, 100);
|
||||
draw_bg(dpy, d);
|
||||
_draw_border(dpy, d);
|
||||
|
||||
/* draw bg gradient */
|
||||
mh = ((d->rect.height - 4) * val) / 100;
|
||||
offy = d->rect.y + d->rect.height - 2 - mh;
|
||||
XSetForeground(dpy, d->gc, d->fg);
|
||||
XFillRectangle(dpy, d->drawable, d->gc, d->rect.x + 2, offy, w, mh);
|
||||
}
|
||||
|
||||
static void
|
||||
_draw_label(Display * dpy, Draw * d)
|
||||
{
|
||||
draw_bg(dpy, d);
|
||||
if (d->data)
|
||||
draw_text(dpy, d);
|
||||
}
|
||||
|
||||
/* draws label */
|
||||
void
|
||||
blitz_drawlabel(Display * dpy, Draw * d)
|
||||
{
|
||||
_draw_label(dpy, d);
|
||||
_draw_border(dpy, d);
|
||||
}
|
||||
|
||||
void
|
||||
blitz_drawlabelnoborder(Display * dpy, Draw * d)
|
||||
{
|
||||
_draw_label(dpy, d);
|
||||
}
|
222
liblitz/geometry.c
Normal file
222
liblitz/geometry.c
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "blitz.h"
|
||||
#include <cext.h>
|
||||
|
||||
static int
|
||||
strtoalign(Align * result, char *val)
|
||||
{
|
||||
/*
|
||||
* note, resize allows syntax like "east-20", this we cannot do
|
||||
* include zero termination in strncmp checking!
|
||||
*/
|
||||
|
||||
*result = CENTER;
|
||||
if (!strncmp(val, "west", 4))
|
||||
*result = WEST;
|
||||
else if (!strncmp(val, "nwest", 5))
|
||||
*result = NWEST;
|
||||
else if (!strncmp(val, "north", 5))
|
||||
*result = NORTH;
|
||||
else if (!strncmp(val, "neast", 5))
|
||||
*result = NEAST;
|
||||
else if (!strncmp(val, "east", 4))
|
||||
*result = EAST;
|
||||
else if (!strncmp(val, "seast", 5))
|
||||
*result = SEAST;
|
||||
else if (!strncmp(val, "south", 5))
|
||||
*result = SOUTH;
|
||||
else if (!strncmp(val, "swest", 5))
|
||||
*result = SWEST;
|
||||
else if (!strncmp(val, "center", 6))
|
||||
*result = CENTER;
|
||||
else
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic Syntax: <x>,<y>,<width>,<height>
|
||||
* Each component can be of following format:
|
||||
* <...> = [+|-]0..n|<alignment>[[+|-]0..n]
|
||||
*/
|
||||
int
|
||||
blitz_strtorect(Display * dpy, XRectangle * root, XRectangle * r,
|
||||
char *val)
|
||||
{
|
||||
char buf[64];
|
||||
char *x, *y, *w, *h;
|
||||
char *p;
|
||||
int sx, sy, sw, sh;
|
||||
|
||||
if (!val)
|
||||
return FALSE;
|
||||
sx = sy = sw = sh = 0;
|
||||
x = y = w = h = 0;
|
||||
_strlcpy(buf, val, sizeof(buf));
|
||||
|
||||
x = strtok_r(buf, ",", &p);
|
||||
if (x) {
|
||||
y = strtok_r(0, ",", &p);
|
||||
if (y) {
|
||||
w = strtok_r(0, ",", &p);
|
||||
if (w) {
|
||||
h = strtok_r(0, "", &p);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (x && (sx = (x[0] >= '0') && (x[0] <= '9')))
|
||||
r->x = _strtonum(x, 0, 65535);
|
||||
if (y && (sy = (y[0] >= '0') && (y[0] <= '9')))
|
||||
r->y = _strtonum(y, 0, 65535);
|
||||
if (w && (sw = (w[0] >= '0') && (w[0] <= '9')))
|
||||
r->width = _strtonum(w, 0, 65535);
|
||||
if (h && (sh = (h[0] >= '0') && (h[0] <= '9')))
|
||||
r->height = _strtonum(h, 0, 65535);
|
||||
|
||||
if (!sx && !sw && x && w
|
||||
&& x[0] != '-' && x[0] != '+' && w[0] != '-' && w[0] != '+') {
|
||||
Align ax, aw;
|
||||
strtoalign(&ax, x);
|
||||
strtoalign(&aw, w);
|
||||
if ((ax == CENTER) && (aw == EAST)) {
|
||||
r->x = root->x + root->width / 2;
|
||||
r->width = root->width / 2;
|
||||
} else {
|
||||
r->x = root->x;
|
||||
if (aw == CENTER) {
|
||||
r->width = root->width / 2;
|
||||
} else {
|
||||
r->width = root->width;
|
||||
}
|
||||
}
|
||||
} else if (!sx && x && x[0] != '-' && x[0] != '+') {
|
||||
Align ax;
|
||||
strtoalign(&ax, x);
|
||||
if (ax == CENTER) {
|
||||
r->x = root->x + (root->width / 2) - (r->width / 2);
|
||||
} else if (ax == EAST) {
|
||||
r->x = root->x + root->width - r->width;
|
||||
} else {
|
||||
r->x = root->x;
|
||||
}
|
||||
} else if (!sw && w && w[0] != '-' && w[0] != '+') {
|
||||
Align aw;
|
||||
strtoalign(&aw, w);
|
||||
if (aw == CENTER) {
|
||||
r->width = (root->width / 2) - r->x;
|
||||
} else {
|
||||
r->width = root->width - r->x;
|
||||
}
|
||||
}
|
||||
if (!sy && !sh && y && h
|
||||
&& y[0] != '-' && y[0] != '+' && h[0] != '-' && h[0] != '+') {
|
||||
Align ay, ah;
|
||||
strtoalign(&ay, y);
|
||||
strtoalign(&ah, h);
|
||||
if ((ay == CENTER) && (ah == SOUTH)) {
|
||||
r->y = root->y + root->height / 2;
|
||||
r->height = root->height / 2;
|
||||
} else {
|
||||
r->y = root->y;
|
||||
if (ah == CENTER) {
|
||||
r->height = root->height / 2;
|
||||
} else {
|
||||
r->height = root->height;
|
||||
}
|
||||
}
|
||||
} else if (!sy && y && y[0] != '-' && y[0] != '+') {
|
||||
Align ay;
|
||||
strtoalign(&ay, y);
|
||||
if (ay == CENTER) {
|
||||
r->y = root->y + (root->height / 2) - (r->height / 2);
|
||||
} else if (ay == SOUTH) {
|
||||
r->y = root->y + root->height - r->height;
|
||||
} else {
|
||||
r->y = root->y;
|
||||
}
|
||||
} else if (!sh && h && h[0] != '-' && h[0] != '+') {
|
||||
Align ah;
|
||||
strtoalign(&ah, h);
|
||||
if (ah == CENTER) {
|
||||
r->height = (root->height / 2) - r->y;
|
||||
} else {
|
||||
r->height = root->height - r->y;
|
||||
}
|
||||
}
|
||||
/* now do final calculations */
|
||||
if (x) {
|
||||
p = strchr(x, '-');
|
||||
if (p)
|
||||
r->x -= _strtonum(++p, 0, 65535);
|
||||
p = strchr(x, '+');
|
||||
if (p)
|
||||
r->x += _strtonum(++p, 0, 65535);
|
||||
}
|
||||
if (y) {
|
||||
p = strchr(y, '-');
|
||||
if (p)
|
||||
r->y -= _strtonum(++p, 0, 65535);
|
||||
p = strchr(y, '+');
|
||||
if (p)
|
||||
r->y += _strtonum(++p, 0, 65535);
|
||||
}
|
||||
if (w) {
|
||||
p = strchr(w, '-');
|
||||
if (p)
|
||||
r->width -= _strtonum(++p, 0, 65535);
|
||||
p = strchr(w, '+');
|
||||
if (p)
|
||||
r->width += _strtonum(++p, 0, 65535);
|
||||
}
|
||||
if (h) {
|
||||
p = strchr(h, '-');
|
||||
if (p)
|
||||
r->height -= _strtonum(++p, 0, 65535);
|
||||
p = strchr(h, '+');
|
||||
if (p)
|
||||
r->height += _strtonum(++p, 0, 65535);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
blitz_ispointinrect(int x, int y, XRectangle * r)
|
||||
{
|
||||
return (x >= r->x) && (x <= r->x + r->width)
|
||||
&& (y >= r->y) && (y <= r->y + r->height);
|
||||
}
|
||||
|
||||
int
|
||||
blitz_distance(XRectangle * origin, XRectangle * target)
|
||||
{
|
||||
int ox = origin->x + origin->width / 2;
|
||||
int oy = origin->y + origin->height / 2;
|
||||
int tx = target->x + target->width / 2;
|
||||
int ty = target->y + target->height / 2;
|
||||
|
||||
return (int) sqrt((double) (((ox - tx) * (ox - tx)) +
|
||||
((oy - ty) * (oy - ty))));
|
||||
}
|
||||
|
||||
void
|
||||
blitz_getbasegeometry(void **items, unsigned int *size,
|
||||
unsigned int *cols, unsigned int *rows)
|
||||
{
|
||||
float sq, dummy;
|
||||
|
||||
*size = count_items((void **) items);
|
||||
sq = sqrt(*size);
|
||||
if (modff(sq, &dummy) < 0.5)
|
||||
*rows = floor(sq);
|
||||
else
|
||||
*rows = ceil(sq);
|
||||
*cols = ((*rows) * (*rows)) < (*size) ? *rows + 1 : *rows;
|
||||
}
|
55
liblitz/kb.c
Normal file
55
liblitz/kb.c
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "blitz.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
/* free the result manually! */
|
||||
char *
|
||||
blitz_modtostr(unsigned long mod)
|
||||
{
|
||||
char result[60];
|
||||
result[0] = '\0';
|
||||
|
||||
if (mod & ShiftMask)
|
||||
_strlcat(result, "S-", sizeof(result));
|
||||
if (mod & ControlMask)
|
||||
_strlcat(result, "C-", sizeof(result));
|
||||
if (mod & Mod1Mask)
|
||||
_strlcat(result, "M-", sizeof(result));
|
||||
if (mod & Mod2Mask)
|
||||
_strlcat(result, "M2-", sizeof(result));
|
||||
if (mod & Mod3Mask)
|
||||
_strlcat(result, "M3-", sizeof(result));
|
||||
if (mod & Mod4Mask)
|
||||
_strlcat(result, "WIN-", sizeof(result));
|
||||
if (mod & Mod5Mask)
|
||||
_strlcat(result, "M5-", sizeof(result));
|
||||
return estrdup(result);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
blitz_strtomod(char *val)
|
||||
{
|
||||
unsigned long mod = 0;
|
||||
if (strstr(val, "S-"))
|
||||
mod |= ShiftMask;
|
||||
if (strstr(val, "C-"))
|
||||
mod |= ControlMask;
|
||||
if (strstr(val, "M-"))
|
||||
mod |= Mod1Mask;
|
||||
if (strstr(val, "M2-"))
|
||||
mod |= Mod2Mask;
|
||||
if (strstr(val, "M3-"))
|
||||
mod |= Mod3Mask;
|
||||
if (strstr(val, "WIN-"))
|
||||
mod |= Mod4Mask;
|
||||
if (strstr(val, "M5-"))
|
||||
mod |= Mod5Mask;
|
||||
return mod;
|
||||
}
|
31
liblitz/mouse.c
Normal file
31
liblitz/mouse.c
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "blitz.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
/* free the result manually! */
|
||||
char *
|
||||
blitz_buttontostr(unsigned int button)
|
||||
{
|
||||
char result[8];
|
||||
result[0] = '\0';
|
||||
snprintf(result, 8, "Button%ud", button - Button1);
|
||||
return estrdup(result);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
blitz_strtobutton(char *val)
|
||||
{
|
||||
unsigned int res = 0;
|
||||
if (val && strlen(val) > 6 && !strncmp(val, "Button", 6))
|
||||
res = _strtonum(&val[6], 1, 5) + Button1;
|
||||
return res;
|
||||
}
|
21
liblitz/util.c
Normal file
21
liblitz/util.c
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "cext.h"
|
||||
#include "blitz.h"
|
||||
|
||||
long long
|
||||
_strtonum(const char *numstr, long long minval, long long maxval)
|
||||
{
|
||||
const char *errstr;
|
||||
long long ret = __strtonum(numstr, minval, maxval, &errstr);
|
||||
if (errstr)
|
||||
fprintf(stderr,
|
||||
"liblitz: cannot convert '%s' into integer: %s\n",
|
||||
numstr, errstr);
|
||||
return ret;
|
||||
}
|
26
libwmii/Makefile
Normal file
26
libwmii/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
# libwmii - mediator lib for wmii
|
||||
# (C)opyright MMIV-MMV Anselm R. Garbe
|
||||
|
||||
include ../config.mk
|
||||
|
||||
CFLAGS += -I../liblitz -I../libixp -I../libcext
|
||||
LDFLAGS += -L../liblitz -llitz -L../libixp -lixp -L../libcext -lcext
|
||||
|
||||
SRC = ixputil.c spawn.c util.c wm.c
|
||||
|
||||
OBJ = ${SRC:.c=.o}
|
||||
|
||||
all: libwmii
|
||||
@echo built libwmii
|
||||
|
||||
.c.o:
|
||||
@echo CC $<
|
||||
@${CC} -c ${CFLAGS} $<
|
||||
|
||||
libwmii: ${OBJ}
|
||||
@echo AR $@.a
|
||||
@${AR} $@.a ${OBJ}
|
||||
@${RANLIB} $@.a
|
||||
|
||||
clean:
|
||||
rm -f libwmii.a *.o
|
102
libwmii/ixputil.c
Normal file
102
libwmii/ixputil.c
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "wmii.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
static pid_t mypid;
|
||||
static char *mysockfile;
|
||||
|
||||
/* convenience stuff ----------------------------------------------- */
|
||||
|
||||
File *
|
||||
wmii_create_ixpfile(IXPServer * s, char *key, char *val)
|
||||
{
|
||||
File *f = ixp_create(s, key);
|
||||
if (f && !is_directory(f)) {
|
||||
size_t l = val ? strlen(val) : 0;
|
||||
f->content = l ? strdup(val) : 0;
|
||||
f->size = l;
|
||||
return f;
|
||||
}
|
||||
/* forbidden, file is directory */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
wmii_get_ixppath(File * f, char *path, size_t size)
|
||||
{
|
||||
char buf[512];
|
||||
|
||||
buf[0] = '\0';
|
||||
if (path)
|
||||
_strlcpy(buf, path, sizeof(buf));
|
||||
snprintf(path, size, "%s/", f->name);
|
||||
if (buf[0] != '\0')
|
||||
_strlcat(path, buf, size);
|
||||
if (f->parent)
|
||||
wmii_get_ixppath(f->parent, path, size);
|
||||
}
|
||||
|
||||
void
|
||||
wmii_move_ixpfile(File * f, File * to_parent)
|
||||
{
|
||||
File *p = f->parent;
|
||||
File *fil = p->content;
|
||||
|
||||
/* detach */
|
||||
if (p->content == f)
|
||||
p->content = fil->next;
|
||||
else {
|
||||
while (fil->next != f)
|
||||
fil = fil->next;
|
||||
fil->next = f->next;
|
||||
}
|
||||
f->next = 0;
|
||||
|
||||
|
||||
/* attach */
|
||||
if (!to_parent->content)
|
||||
to_parent->content = f;
|
||||
else {
|
||||
for (fil = to_parent->content; fil->next; fil = fil->next);
|
||||
fil->next = f;
|
||||
}
|
||||
f->parent = to_parent;
|
||||
}
|
||||
|
||||
static void
|
||||
exit_cleanup()
|
||||
{
|
||||
if (mypid == getpid())
|
||||
unlink(mysockfile);
|
||||
}
|
||||
|
||||
IXPServer *
|
||||
wmii_setup_server(char *sockfile)
|
||||
{
|
||||
IXPServer *s;
|
||||
|
||||
if (!sockfile) {
|
||||
fprintf(stderr, "%s\n", "libwmii: no socket file provided");
|
||||
exit(1);
|
||||
}
|
||||
mysockfile = sockfile;
|
||||
mypid = getpid();
|
||||
s = init_server(sockfile, exit_cleanup);
|
||||
if (!s) {
|
||||
perror("libwmii: cannot initialize IXP server");
|
||||
exit(1);
|
||||
}
|
||||
return s;
|
||||
}
|
31
libwmii/spawn.c
Normal file
31
libwmii/spawn.c
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "wmii.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
void
|
||||
spawn(void *dpy, char *cmd)
|
||||
{
|
||||
/* the questionable double-fork is done to catch all zombies */
|
||||
if (fork() == 0) {
|
||||
if (fork() == 0) {
|
||||
setsid();
|
||||
close(ConnectionNumber(dpy));
|
||||
execlp("rc", "rc", "-c", cmd, (char *) 0);
|
||||
perror("failed");
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
wait(0);
|
||||
}
|
14
libwmii/util.c
Normal file
14
libwmii/util.c
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include "wmii.h"
|
||||
|
||||
void
|
||||
swap(void **p1, void **p2)
|
||||
{
|
||||
void *tmp = *p1;
|
||||
*p1 = *p2;
|
||||
*p2 = tmp;
|
||||
}
|
95
libwmii/wm.c
Normal file
95
libwmii/wm.c
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
#include "blitz.h"
|
||||
|
||||
#include <cext.h>
|
||||
|
||||
int
|
||||
property(Display * dpy, Window w, Atom a, Atom t, long l,
|
||||
unsigned char **prop)
|
||||
{
|
||||
Atom real;
|
||||
int format;
|
||||
unsigned long res, extra;
|
||||
int status;
|
||||
|
||||
status = XGetWindowProperty(dpy, w, a, 0L, l, False, t, &real, &format,
|
||||
&res, &extra, prop);
|
||||
|
||||
if (status != Success || *prop == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (res == 0) {
|
||||
free((void *) *prop);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
win_prop(Display * dpy, Window w, Atom a, char *res, int len)
|
||||
{
|
||||
unsigned char *prop;
|
||||
|
||||
if (property(dpy, w, a, XA_STRING, 100L, &prop)) {
|
||||
_strlcpy(res, (char *) prop, len);
|
||||
XFree(prop);
|
||||
}
|
||||
res[len - 1] = '\0';
|
||||
XSync(dpy, False);
|
||||
}
|
||||
|
||||
void
|
||||
send_message(Display * dpy, Window w, Atom a, long value)
|
||||
{
|
||||
XEvent e;
|
||||
e.type = ClientMessage;
|
||||
e.xclient.window = w;
|
||||
e.xclient.message_type = a;
|
||||
e.xclient.format = 32;
|
||||
e.xclient.data.l[0] = value;
|
||||
e.xclient.data.l[1] = CurrentTime;
|
||||
|
||||
XSendEvent(dpy, w, False, NoEventMask, &e);
|
||||
XSync(dpy, False);
|
||||
}
|
||||
|
||||
#define NUM_MASKS 8
|
||||
void
|
||||
init_lock_modifiers(Display * dpy, unsigned int *valid_mask,
|
||||
unsigned int *num_lock_mask)
|
||||
{
|
||||
XModifierKeymap *modmap;
|
||||
KeyCode num_lock;
|
||||
static int masks[NUM_MASKS] = {
|
||||
ShiftMask, LockMask, ControlMask, Mod1Mask,
|
||||
Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
|
||||
};
|
||||
int i;
|
||||
|
||||
*num_lock_mask = 0;
|
||||
modmap = XGetModifierMapping(dpy);
|
||||
num_lock = XKeysymToKeycode(dpy, XStringToKeysym("Num_Lock"));
|
||||
|
||||
if (modmap && modmap->max_keypermod > 0) {
|
||||
int max = NUM_MASKS * modmap->max_keypermod;
|
||||
for (i = 0; i < max; i++) {
|
||||
if (num_lock && (modmap->modifiermap[i] == num_lock)) {
|
||||
*num_lock_mask = masks[i / modmap->max_keypermod];
|
||||
}
|
||||
}
|
||||
}
|
||||
XFreeModifiermap(modmap);
|
||||
|
||||
*valid_mask = 255 & ~(*num_lock_mask | LockMask);
|
||||
}
|
39
libwmii/wmii.h
Normal file
39
libwmii/wmii.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
|
||||
#include "ixp.h"
|
||||
#include "blitz.h"
|
||||
|
||||
#define MAX_ID 20
|
||||
#define MAX_BUF 128
|
||||
|
||||
typedef struct Action Action;
|
||||
|
||||
struct Action {
|
||||
char *name;
|
||||
void (*func) (void *obj, char *);
|
||||
};
|
||||
|
||||
/* ixputil.c */
|
||||
File *wmii_create_ixpfile(IXPServer * s, char *key, char *val);
|
||||
void wmii_get_ixppath(File * f, char *path, size_t size);
|
||||
void wmii_move_ixpfile(File * f, File * to_parent);
|
||||
IXPServer *wmii_setup_server(char *sockfile);
|
||||
|
||||
/* spawn.c */
|
||||
void spawn(void *dpy, char *cmd);
|
||||
|
||||
/* util.c */
|
||||
void swap(void **p1, void **p2);
|
||||
|
||||
/* wm.c */
|
||||
int
|
||||
property(Display * dpy, Window w, Atom a, Atom t, long l,
|
||||
unsigned char **prop);
|
||||
void win_prop(Display * dpy, Window w, Atom a, char *res, int len);
|
||||
void send_message(Display * dpy, Window w, Atom a, long value);
|
||||
void
|
||||
init_lock_modifiers(Display * dpy, unsigned int *valid_mask,
|
||||
unsigned int *num_lock_mask);
|
15
rc/extern
Normal file
15
rc/extern
Normal file
@ -0,0 +1,15 @@
|
||||
#!9PREFIX/bin/rc
|
||||
# clean the environment and execute the given command
|
||||
|
||||
PATH=$OLD_PATH
|
||||
OLD_PATH=()
|
||||
wmiiwmpid=()
|
||||
apid=()
|
||||
home=()
|
||||
ifs=()
|
||||
pid=()
|
||||
prompt=()
|
||||
rcname=()
|
||||
status=()
|
||||
|
||||
exec $*
|
11
rc/kmode
Normal file
11
rc/kmode
Normal file
@ -0,0 +1,11 @@
|
||||
#!9PREFIX/bin/rc
|
||||
# activate shortcuts of the given mode
|
||||
|
||||
wmir write /keys/grab-keyb 0
|
||||
wmir write /keys/lookup /mode/$1
|
||||
|
||||
plab_cmd=`{wmir read /wm/event/page-update | sed -e 's/\\//'}
|
||||
if (! ~ $#plab_cmd 0) eval $"plab_cmd
|
||||
|
||||
#wmir write /bar/$plab/data \
|
||||
#`{wmir read /bar/$plab/data | sed -e 's/.$/'^`{echo $2 | cut -b1}^'/'}
|
30
rc/status
Normal file
30
rc/status
Normal file
@ -0,0 +1,30 @@
|
||||
#!9PREFIX/bin/rc
|
||||
# periodically print date and load average to the bar
|
||||
|
||||
PIDFILE=/tmp/.ixp-$USER/statuspid-$WMII_IDENT
|
||||
|
||||
if(test -r $PIDFILE) {
|
||||
kill -2 `{cat $PIDFILE} >[2]/dev/null
|
||||
}
|
||||
echo $pid >$PIDFILE
|
||||
|
||||
label=`{wmir read /bar/new}
|
||||
wmir write /bar/$label/b1press 'wmir write /wm/ctl ''select prev'''
|
||||
wmir write /bar/$label/b3press 'wmir write /wm/ctl ''select next'''
|
||||
wmir write /bar/$label/b4press 'wmir write /wm/ctl ''select prev'''
|
||||
wmir write /bar/$label/b5press 'wmir write /wm/ctl ''select next'''
|
||||
|
||||
# install signal handler for artificial sigexit:
|
||||
fn sigint {
|
||||
if(test -f $PIDFILE && ~ `{cat $PIDFILE} $pid)
|
||||
rm -f $PIDFILE
|
||||
wmir write /bar/ctl 'destroy '^$label
|
||||
exit
|
||||
}
|
||||
|
||||
text=fnord
|
||||
while(wmir write /bar/$label/data $"text >[2]/dev/null) {
|
||||
# if you need formatted date, use /bin/date instead
|
||||
text=(`{date} `{uptime | sed 's/.*://; s/,//g'})
|
||||
sleep 10
|
||||
}
|
65
rc/welcome
Normal file
65
rc/welcome
Normal file
@ -0,0 +1,65 @@
|
||||
#!9PREFIX/bin/rc
|
||||
# display a welcome message that contains the wmii tutorial
|
||||
|
||||
xmessage -file - <<END
|
||||
Welcome to wmii, the non-wimp environment of the WMI Project.
|
||||
|
||||
This is a small step by step tutorial, intended to make you a little bit
|
||||
familiar with the wmii window manager.
|
||||
|
||||
NOTE: Some characters in the shortcuts that are mentioned in the following
|
||||
text have a special meaning. S is the Shift key, C is the Ctrl key, M is
|
||||
the Meta key (usually Alt), and the hyphen means that you have to press the
|
||||
surrounding keys at the same time.
|
||||
|
||||
Let's go!
|
||||
|
||||
- Start a terminal by pressing M-t. This assumes that you have xterm(1)
|
||||
installed.
|
||||
- Open the actions menu: C-M-a
|
||||
Type 'term' and press Enter. You've now used wmimenu(1) to accomplish what
|
||||
wmikeys(1) did before when you pressed M-t: executing the term action.
|
||||
Notice how the tiled layout arranges the windows.
|
||||
- Switch between the three windows: M-j, M-k
|
||||
You can also use M-Tab instead of M-j. If you prefer the mouse, then
|
||||
just move the pointer above the desired window; no click is required.
|
||||
- Create a new page: C-M-y
|
||||
IMPORTANT: this text will then no longer be visible. In order to continue
|
||||
reading you'll now often have to go back to the first page. Use M-h
|
||||
or M-l for cycling through the existing pages. The digit in the left
|
||||
corner at the bottom indicates which page you're on. You can directly
|
||||
select a specific page as well: M-1, etc. Make sure that you are on the
|
||||
second page before performing the next steps.
|
||||
- Apply the float layout to the current page: S-M-f
|
||||
Watch the leftmost bar label: the 't', which stood for 'tiled', has now
|
||||
turned into an 'f', for 'float'.
|
||||
- Open the programs menu: C-M-p
|
||||
Type 'xclock' and press Enter.
|
||||
- Resize the xclock window: left-click on the border and, while holding the
|
||||
button down, move the cursor around.
|
||||
- Move the xclock window: left-click on the title bar and, while holding
|
||||
the button down, move the cursor around.
|
||||
- Remove the current page by opening the actions menu and selecting the premove
|
||||
action. The xclock window will be automatically detached and we'll land on
|
||||
the first page.
|
||||
- Attach the xclock window again: M-a
|
||||
- Select one of the terminals and close it: C-M-c
|
||||
Close xclock as well.
|
||||
- Swap the remaining terminal with the left master tile by moving the terminal
|
||||
window somewhere to the left. Afterwards, swap them once again, but this
|
||||
time by pressing M-return.
|
||||
- We'll now have a look at the internal filesystem used by wmii. Executing
|
||||
wmir read /
|
||||
in the shell of the terminal will list all the files in the root directory.
|
||||
Files ending with a slash are directories. As you can see, / contains a
|
||||
"normal" file and four directories. These directories correspond to the
|
||||
four main components of wmii: wmiiwm, wmibar, wmikeys, and wmimenu. If you
|
||||
are curious, you can now dig deeper into the directory trees. For instance,
|
||||
wmir read /bar/1/b1press
|
||||
will show you which command gets executed when you left-click on the page
|
||||
label in the bar.
|
||||
|
||||
We hope that these steps gave you an idea of how wmii works. Of course, there
|
||||
is much more to say. You should now read the wmii(1) man page and the FAQ at
|
||||
<http://wmii.de>.
|
||||
END
|
218
rc/wmirc
Normal file
218
rc/wmirc
Normal file
@ -0,0 +1,218 @@
|
||||
#!9PREFIX/bin/rc
|
||||
# configure wmii
|
||||
|
||||
TAB_HEIGHT=16
|
||||
BAR_HEIGHT=16
|
||||
BORDER_WIDTH=3
|
||||
|
||||
SELECTED_BG_COLOR='#307080'
|
||||
SELECTED_FG_COLOR='#fefefe'
|
||||
SELECTED_BORDER_COLOR='#5090a0'
|
||||
NORMAL_BG_COLOR='#004050'
|
||||
NORMAL_FG_COLOR='#cccccc'
|
||||
NORMAL_BORDER_COLOR='#206070'
|
||||
TEXT_FONT='fixed'
|
||||
|
||||
MODKEY=M
|
||||
NORTHKEY=k
|
||||
SOUTHKEY=j
|
||||
WESTKEY=h
|
||||
EASTKEY=l
|
||||
|
||||
nl='
|
||||
'
|
||||
|
||||
fn kbind {
|
||||
wmir create /keys/mode/$1/$2 $3
|
||||
}
|
||||
|
||||
fn selstyle {
|
||||
wmir write $1/text-font $TEXT_FONT
|
||||
wmir write $1/text-color $SELECTED_FG_COLOR
|
||||
wmir write $1/bg-color $SELECTED_BG_COLOR
|
||||
wmir write $1/border-color $SELECTED_BORDER_COLOR
|
||||
}
|
||||
|
||||
fn normstyle {
|
||||
wmir write $1/text-font $TEXT_FONT
|
||||
wmir write $1/text-color $NORMAL_FG_COLOR
|
||||
wmir write $1/bg-color $NORMAL_BG_COLOR
|
||||
wmir write $1/border-color $NORMAL_BORDER_COLOR
|
||||
}
|
||||
|
||||
fn items {
|
||||
ifs=:$nl { dirs=`{echo $2} }
|
||||
{
|
||||
for(dir in $dirs) {
|
||||
for(file in $dir/*) {
|
||||
if(! ~ $file $dir^'/*' && ! test -d $file && test -x $file) {
|
||||
file=`{basename $file}
|
||||
echo create /menu/items/$1/$"file $"file
|
||||
}
|
||||
}
|
||||
}
|
||||
} | sort | wmir -f &
|
||||
}
|
||||
|
||||
fn frameconf {
|
||||
wmir write $1/event/b3press 'wmir write /wm/page/sel/ctl toggle'
|
||||
wmir write $1/event/b2press 'wmir write /wm/ctl close'
|
||||
normstyle $1/norm-style
|
||||
selstyle $1/sel-style
|
||||
wmir write $1/tab-height $TAB_HEIGHT
|
||||
if(~ $2 refresh)
|
||||
wmir write $1/size +0,+0,+0,+0 # causes refresh
|
||||
}
|
||||
|
||||
fn framesconf {
|
||||
for(frame in `{wmir read $1 | grep '^[0-9]'})
|
||||
frameconf $1/$frame $2
|
||||
}
|
||||
|
||||
|
||||
# WMIBAR CONFIGURATION
|
||||
|
||||
fn barsucks {
|
||||
wmir write /bar/$1/b1press 'wmir write /wm/ctl ''select prev'''
|
||||
wmir write /bar/$1/b3press 'wmir write /wm/ctl ''select next'''
|
||||
wmir write /bar/$1/b4press 'wmir write /wm/ctl ''select prev'''
|
||||
wmir write /bar/$1/b5press 'wmir write /wm/ctl ''select next'''
|
||||
}
|
||||
|
||||
wmir write /bar/ctl reset
|
||||
wmir write /bar/font $TEXT_FONT
|
||||
wmir write /bar/fgcolor $NORMAL_FG_COLOR
|
||||
wmir write /bar/bgcolor $NORMAL_BG_COLOR
|
||||
wmir write /bar/bordercolor $NORMAL_BORDER_COLOR
|
||||
|
||||
plab=`{wmir read /bar/new}
|
||||
wmir write /bar/$plab/b1press 'wmir write /wm/ctl pager'
|
||||
|
||||
clab=`{wmir read /bar/new}
|
||||
wmir write /bar/$clab/fgcolor $SELECTED_FG_COLOR
|
||||
wmir write /bar/$clab/bgcolor $SELECTED_BG_COLOR
|
||||
wmir write /bar/$clab/bordercolor $SELECTED_BG_COLOR
|
||||
barsucks $clab
|
||||
|
||||
wmir write /bar/expandable 2
|
||||
wmir write /bar/geometry 0,south,east,$BAR_HEIGHT
|
||||
wmir write /bar/ctl 'display 1'
|
||||
|
||||
|
||||
# WMIIWM CONFIGURATION
|
||||
|
||||
# default layout (tiled, max, grid or vsplit):
|
||||
wmir write /wm/default/page/layout tiled
|
||||
|
||||
# width of the left frame in tiled layout in percent:
|
||||
wmir write /wm/default/page/tile-width 60
|
||||
|
||||
wmir write /wm/default/core/trans-color $SELECTED_BG_COLOR
|
||||
wmir write /wm/default/frame/handle-inc 1
|
||||
|
||||
# some broken WIMP apps:
|
||||
wmir create /wm/default/client/'xmms:*'/manage 0
|
||||
wmir create /wm/default/client/'Gimp:*'/manage 0
|
||||
|
||||
wmir write /wm/default/frame/border-width $BORDER_WIDTH
|
||||
wmir write /wm/default/page/size '0,0,east,south-'^$BAR_HEIGHT
|
||||
wmir write /wm/event/client-update \
|
||||
'text=`{wmir read /wm/page/sel/mode/sel/client/sel/name} \
|
||||
wmir write /bar/'^$clab^'/data $"text'
|
||||
wmir write /wm/event/page-update \
|
||||
'text=`{wmir read /wm/page/sel/name} ^ \
|
||||
`{wmir read /wm/page/sel/mode/name|awk ''{print substr($0,0,1)}''} ^ \
|
||||
`{wmir read /keys/lookup|awk ''{print substr($0,7,1)}''} \
|
||||
wmir write /bar/'^$plab^'/data $"text'
|
||||
|
||||
for(page in `{wmir read /wm/page | grep '^[0-9]'}) {
|
||||
framesconf /wm/page/$page/floating refresh
|
||||
framesconf /wm/page/$page/managed refresh
|
||||
}
|
||||
frameconf /wm/default/frame norefresh
|
||||
for(i in norm-style norm-style/client)
|
||||
normstyle /wm/default/core/pager/$i
|
||||
for(i in sel-style sel-style/client)
|
||||
selstyle /wm/default/core/pager/$i
|
||||
wmir write /wm/page/sel/managed/size 0,0,east,south-$BAR_HEIGHT >[2]/dev/null
|
||||
|
||||
|
||||
# WMIKEYS CONFIGURATION
|
||||
|
||||
kbind bare $MODKEY-Escape 'kmode normal'
|
||||
|
||||
kbind move Escape 'kmode normal'
|
||||
kbind move $MODKEY-C-r 'kmode resize'
|
||||
kbind move $NORTHKEY 'wmir write /wm/page/sel/mode/sel/size -0,-30,-0,-0'
|
||||
kbind move $SOUTHKEY 'wmir write /wm/page/sel/mode/sel/size +0,+30,+0,+0'
|
||||
kbind move $WESTKEY 'wmir write /wm/page/sel/mode/sel/size -40,-0,-0,-0'
|
||||
kbind move $EASTKEY 'wmir write /wm/page/sel/mode/sel/size +40,+0,+0,+0'
|
||||
kbind move S-$NORTHKEY 'wmir write /wm/page/sel/mode/sel/size -0,north,-0,-0'
|
||||
kbind move S-$SOUTHKEY 'wmir write /wm/page/sel/mode/sel/size +0,south-'^$BAR_HEIGHT^',+0,+0'
|
||||
kbind move S-$WESTKEY 'wmir write /wm/page/sel/mode/sel/size west,-0,-0,-0'
|
||||
kbind move S-$EASTKEY 'wmir write /wm/page/sel/mode/sel/size east,+0,+0,+0'
|
||||
|
||||
kbind resize Escape 'kmode normal'
|
||||
kbind resize $MODKEY-C-m 'kmode move'
|
||||
kbind resize $NORTHKEY 'wmir write /wm/page/sel/mode/sel/size +0,+0,+0,-30'
|
||||
kbind resize $SOUTHKEY 'wmir write /wm/page/sel/mode/sel/size +0,+0,+0,+30'
|
||||
kbind resize $WESTKEY 'wmir write /wm/page/sel/mode/sel/size +0,+0,-40,+0'
|
||||
kbind resize $EASTKEY 'wmir write /wm/page/sel/mode/sel/size +0,+0,+40,+0'
|
||||
|
||||
kbind normal $MODKEY-C-b 'kmode bare'
|
||||
kbind normal $MODKEY-C-m 'kmode move'
|
||||
kbind normal $MODKEY-C-r 'kmode resize'
|
||||
kbind normal $MODKEY-C-a 'wmir write /menu/precmd ''''; wmir write /menu/lookup /items/actions; wmir write /menu/ctl ''display 1'''
|
||||
kbind normal $MODKEY-C-p 'wmir write /menu/precmd extern; wmir write /menu/lookup /items/programs; wmir write /menu/ctl ''display 1'''
|
||||
kbind normal $MODKEY-C-c 'wmir write /wm/ctl close'
|
||||
kbind normal $MODKEY-C-q,y quit
|
||||
kbind normal $MODKEY-C-w,y wmirc
|
||||
kbind normal $MODKEY-t 'extern xterm ''+sb'' -bg ''#003040'' -fg ''#dddddd'' -cr ''#408090'' -sl 4000'
|
||||
kbind normal $MODKEY-d 'wmir write /wm/ctl detach'
|
||||
kbind normal $MODKEY-a 'wmir write /wm/ctl attach'
|
||||
kbind normal $MODKEY-S-a 'wmir write /wm/ctl icons'
|
||||
kbind normal $MODKEY-S-space 'wmir write /wm/page/sel/ctl toggle'
|
||||
kbind normal $MODKEY-Return 'wmir write /wm/page/sel/ctl ''select zoomed'''
|
||||
kbind normal $MODKEY-C-y 'wmir write /wm/ctl new'
|
||||
kbind normal $MODKEY-u 'wmir write /wm/page/sel/mode/sel/locked 0'
|
||||
kbind normal $MODKEY-S-u 'wmir write /wm/page/sel/mode/sel/locked 1'
|
||||
kbind normal $MODKEY-m 'wmir write /wm/ctl togglemax'
|
||||
kbind normal $MODKEY-S-t 'wmir write /wm/page/sel/managed/name tiled'
|
||||
kbind normal $MODKEY-S-g 'wmir write /wm/page/sel/managed/name grid'
|
||||
kbind normal $MODKEY-S-v 'wmir write /wm/page/sel/managed/name vsplit'
|
||||
kbind normal $MODKEY-S-f 'wmir write /wm/page/sel/managed/name float'
|
||||
kbind normal $MODKEY-S-m 'wmir write /wm/page/sel/managed/name max'
|
||||
kbind normal $MODKEY-$WESTKEY 'wmir write /wm/ctl ''select prev'''
|
||||
kbind normal $MODKEY-$EASTKEY 'wmir write /wm/ctl ''select next'''
|
||||
kbind normal $MODKEY-Tab 'wmir write /wm/page/sel/ctl ''select next'''
|
||||
kbind normal $MODKEY-$SOUTHKEY 'wmir write /wm/page/sel/ctl ''select next'''
|
||||
kbind normal $MODKEY-$NORTHKEY 'wmir write /wm/page/sel/ctl ''select prev'''
|
||||
kbind normal $MODKEY-S-Tab 'wmir write /wm/page/sel/mode/sel/ctl ''select next'''
|
||||
kbind normal $MODKEY-S-$SOUTHKEY 'wmir write /wm/page/sel/mode/sel/ctl ''select next'''
|
||||
kbind normal $MODKEY-S-$NORTHKEY 'wmir write /wm/page/sel/mode/sel/ctl ''select prev'''
|
||||
kbind normal $MODKEY-space 'wmir write /wm/page/sel/ctl ''select toggled'''
|
||||
kbind normal $MODKEY-S-p 'wmir write /wm/ctl pager'
|
||||
kbind normal $MODKEY-S-0 'wmir write /wm/ctl ''select 10'''
|
||||
for(i in 1 2 3 4 5 6 7 8 9) {
|
||||
kbind normal $MODKEY-S-$i 'wmir write /wm/ctl ''select '^$i^''''
|
||||
}
|
||||
|
||||
wmir write /keys/size center,center,100,$BAR_HEIGHT
|
||||
selstyle /keys/box/style
|
||||
kmode normal
|
||||
|
||||
|
||||
# WMIMENU CONFIGURATION
|
||||
|
||||
items actions $WMII_CONFDIR:$HOME/.wmii-3
|
||||
wmir create /menu/items/actions/rmpage 'wmir write /wm/ctl destroy'
|
||||
items programs $OLD_PATH
|
||||
wmir write /menu/size 0,south,east,$BAR_HEIGHT
|
||||
normstyle /menu/style >[2]/dev/null
|
||||
normstyle /menu/norm-style >[2]/dev/null
|
||||
selstyle /menu/sel-style >[2]/dev/null
|
||||
|
||||
|
||||
# MISC
|
||||
xsetroot -mod 2 2 -fg '#003040' -bg '#004050'
|
||||
status &
|
Loading…
Reference in New Issue
Block a user