commit ff3d9c0c987a7b476223925a318352072d1dcddc Author: fabian Date: Sat Sep 14 13:22:36 2024 +0200 archive release diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a895a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +samu +*.swp +*.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0404640 --- /dev/null +++ b/Makefile @@ -0,0 +1,55 @@ +# samu makefile for BSD make and GNU make +# uses pkg-config, DESTDIR and PREFIX + +PROG= samu-desktop + +PREFIX?= /usr/local + +SRCS= samu.c screen.c xmalloc.c client.c \ + util.c xutil.c conf.c xevents.c group.c \ + kbfunc.c parse.y + +OBJS= samu.o screen.o xmalloc.o client.o \ + util.o xutil.o conf.o xevents.o group.o \ + kbfunc.o strlcpy.o y.tab.o \ + strtonum.o reallocarray.o + +PKG_CONFIG?= pkg-config + +CPPFLAGS+= `${PKG_CONFIG} --cflags x11 xft xrandr` + +CFLAGS?= -Wall -O2 -g -D_GNU_SOURCE + +LDFLAGS+= `${PKG_CONFIG} --libs x11 xft xrandr` + +MANPREFIX?= ${PREFIX}/share/man + +all: ${PROG} + +clean: + rm -f ${OBJS} ${PROG} y.tab.c + +y.tab.c: parse.y + yacc parse.y + +${PROG}: ${OBJS} y.tab.o + ${CC} ${OBJS} ${LDFLAGS} -o ${PROG} + +.c.o: + ${CC} -c ${CFLAGS} ${CPPFLAGS} $< + +install: ${PROG} + install -d ${DESTDIR}${PREFIX}/bin ${DESTDIR}${MANPREFIX}/man1 ${DESTDIR}${MANPREFIX}/man5 + install -m 755 samu-desktop ${DESTDIR}${PREFIX}/bin + install -m 644 samu.1 ${DESTDIR}${MANPREFIX}/man1 + install -m 644 samurc.5 ${DESTDIR}${MANPREFIX}/man5 + +release: + VERSION=$$(git describe --tags | sed 's/^v//;s/-[^.]*$$//') && \ + git archive --prefix=samu-$$VERSION/ -o samu-$$VERSION.tar.gz HEAD + +sign: + VERSION=$$(git describe --tags | sed 's/^v//;s/-[^.]*$$//') && \ + gpg2 --armor --detach-sign samu-$$VERSION.tar.gz && \ + signify -S -s ~/.signify/samu.sec -m samu-$$VERSION.tar.gz && \ + sed -i '1cuntrusted comment: verify with samu.pub' samu-$$VERSION.tar.gz.sig diff --git a/README.md b/README.md new file mode 100644 index 0000000..285be7d --- /dev/null +++ b/README.md @@ -0,0 +1,114 @@ +# SAMU πŸͺ· + +**SAMU** is an opinionated Window Manager that manages the windows simple and +without any **BS**. It's meant to be very _Zen like_, the user can focus on +just what ever he wants to do on his PC an it will _just work_. It's my own custom & cute linux desktop (ˢ˃’˂ˢ). + +```bash +date +"%Y is the year of the `uname -o` desktop" +``` + +SAMU is a CWM fork with opinionated changes. The changes are big enough to +justify a rename as it's not to be confused with the regular CWM anymore. + + + +## Roadmap πŸ‘€ + +There will not be any big changes anymore as I'm focused on a new Wayland +window manager. + +There are also still some applications that have issues on Wayland, especially +on NVIDIA cards which makes this the '_final X11 WM until Wayland is there_' for me. + +There are still many CWM keywords in the code that could be cleaned up but +I don't care. The WM is completely tested, stable and done until I +will switch to Wayland. + +## Features πŸ’« + +Some of the notable changes to CWM: + +- no coordinates when moving windows (removed with other useless stuff) +- fix transparency on borders when using picom +- slight changes to mouse behavior when opening new windows +- added autostart +- added new tiling options (top left, bottom right, etc..., center) (the gif shows some tiling, it only lags because of the compression) +- added background color (only works without compositor) +- colored window borders on sticky windows + +Notable features from CWM: + +- good **EWMH** support so that all applications will actually work well +- very optimized performance when dragging around windows and resizing them + +### Supported EWMH hints πŸ€– + +``` +"_NET_SUPPORTED", +"_NET_SUPPORTING_WM_CHECK", +"_NET_ACTIVE_WINDOW", +"_NET_CLIENT_LIST", +"_NET_CLIENT_LIST_STACKING", +"_NET_NUMBER_OF_DESKTOPS", +"_NET_CURRENT_DESKTOP", +"_NET_DESKTOP_VIEWPORT", +"_NET_DESKTOP_GEOMETRY", +"_NET_VIRTUAL_ROOTS", +"_NET_SHOWING_DESKTOP", +"_NET_DESKTOP_NAMES", +"_NET_WORKAREA", +"_NET_WM_NAME", +"_NET_WM_DESKTOP", +"_NET_CLOSE_WINDOW", +"_NET_WM_STATE", +"_NET_WM_STATE_STICKY", +"_NET_WM_STATE_MAXIMIZED_VERT", +"_NET_WM_STATE_MAXIMIZED_HORZ", +"_NET_WM_STATE_HIDDEN", +"_NET_WM_STATE_FULLSCREEN", +"_NET_WM_STATE_DEMANDS_ATTENTION", +"_NET_WM_STATE_SKIP_PAGER", +"_NET_WM_STATE_SKIP_TASKBAR", +``` + +## Autostart πŸš—πŸ’¨ + +SAMU will attempt to run a `autostart.sh` file located by default in +`XDG_CONFIG_HOME/samu/autostart.sh`. This file does not need to be there, +but you can add other applications and commands there to run on launch. + +## Config ✍️ + +Edit `~/.samurc`. Infos with `man samu`. +Check out `conf.c` for pre configured configs, the default terminal is `kitty`. + +## Installation πŸ‘· + +### Manually πŸ—οΈ + +1. Run `make` to build `samu`. +2. Copy it to your bin path or run `make install`. +3. Launch `samu-desktop` with `X` + +The executable of `samu` is called `samu-desktop` to avoid any complications +with other software that might be called `samu` as well. + +## Thanks + +Inspiration taken from: + +- [sowm](https://github.com/dylanaraps/sowm) +- [cwm](https://github.com/leahneukirchen/cwm) +- [bspwm](https://github.com/baskerville/bspwm) +- [2bwm](https://github.com/venam/2bwm) +- [SmallWM](https://github.com/adamnew123456/SmallWM) +- [berry](https://github.com/JLErvin/berry) +- [catwm](https://github.com/pyknite/catwm) +- [dminiwm](https://github.com/moetunes/dminiwm) +- [dwm](https://dwm.suckless.org) +- [monsterwm](https://github.com/c00kiemon5ter/monsterwm) +- [openbox](https://github.com/danakj/openbox) +- [possum-wm](https://github.com/duckinator/possum-wm) +- [swm](https://github.com/dcat/swm) +- [tinywm](http://incise.org/tinywm.html) diff --git a/client.c b/client.c new file mode 100644 index 0000000..5e07ef2 --- /dev/null +++ b/client.c @@ -0,0 +1,1152 @@ +/* + * + * Copyright (c) 2004 Marius Aamodt Eriksen + * + * 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. + * + * $OpenBSD$ + */ + +#include +#include "queue.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "samu.h" + +static void client_class_hint(struct client_ctx *); +static void client_placement(struct client_ctx *); +static void client_mwm_hints(struct client_ctx *); +static void client_wm_protocols(struct client_ctx *); + +struct client_ctx * +client_init(Window win, struct screen_ctx *sc) +{ + struct client_ctx *cc; + XWindowAttributes wattr; + int mapped; + long state; + + if (win == None) + return NULL; + if (!XGetWindowAttributes(X_Dpy, win, &wattr)) + return NULL; + + if (sc == NULL) { + if ((sc = screen_find(wattr.root)) == NULL) + return NULL; + mapped = 1; + } else { + if (wattr.override_redirect || wattr.map_state != IsViewable) + return NULL; + mapped = wattr.map_state != IsUnmapped; + } + + XGrabServer(X_Dpy); + + cc = xmalloc(sizeof(*cc)); + cc->sc = sc; + cc->win = win; + cc->name = NULL; + cc->label = NULL; + cc->gc = NULL; + cc->res_class = NULL; + cc->res_name = NULL; + cc->flags = 0; + cc->stackingorder = 0; + cc->initial_state = 0; + memset(&cc->hint, 0, sizeof(cc->hint)); + TAILQ_INIT(&cc->nameq); + + cc->geom.x = wattr.x; + cc->geom.y = wattr.y; + cc->geom.w = wattr.width; + cc->geom.h = wattr.height; + cc->colormap = wattr.colormap; + cc->obwidth = wattr.border_width; + cc->bwidth = Conf.bwidth; + cc->tgap = Conf.tgap; + + client_set_name(cc); + conf_client(cc); + + client_wm_hints(cc); + client_class_hint(cc); + client_wm_protocols(cc); + client_get_sizehints(cc); + client_transient(cc); + client_mwm_hints(cc); + + if ((cc->flags & CLIENT_IGNORE)) + cc->bwidth = 0; + cc->dim.w = (cc->geom.w - cc->hint.basew) / cc->hint.incw; + cc->dim.h = (cc->geom.h - cc->hint.baseh) / cc->hint.inch; + cc->ptr.x = cc->geom.w / 2; + cc->ptr.y = cc->geom.h / 2; + + if (wattr.map_state != IsViewable) { + client_placement(cc); + client_resize(cc, 0); + if (cc->initial_state) + xu_set_wm_state(cc->win, cc->initial_state); + } + + XSelectInput(X_Dpy, cc->win, + EnterWindowMask | PropertyChangeMask | KeyReleaseMask); + + XAddToSaveSet(X_Dpy, cc->win); + + /* Notify client of its configuration. */ + client_config(cc); + + TAILQ_INSERT_TAIL(&sc->clientq, cc, entry); + + xu_ewmh_net_client_list(sc); + xu_ewmh_net_client_list_stacking(sc); + xu_ewmh_restore_net_wm_state(cc); + + xu_get_wm_state(cc->win, &state); + if (state == IconicState) + client_hide(cc); + else + client_show(cc); + + if (mapped) { + if (cc->gc) { + group_movetogroup(cc, cc->gc->num); + goto out; + } + if (group_restore(cc)) + goto out; + if (group_autogroup(cc)) + goto out; + if (Conf.stickygroups) + group_assign(sc->group_active, cc); + else + group_assign(NULL, cc); + } +out: + XSync(X_Dpy, False); + XUngrabServer(X_Dpy); + + return cc; +} + +struct client_ctx * +client_current(struct screen_ctx *sc) +{ + struct screen_ctx *_sc; + struct client_ctx *cc; + + if (sc) { + TAILQ_FOREACH(cc, &sc->clientq, entry) { + if (cc->flags & CLIENT_ACTIVE) + return cc; + } + } else { + TAILQ_FOREACH(_sc, &Screenq, entry) { + TAILQ_FOREACH(cc, &_sc->clientq, entry) { + if (cc->flags & CLIENT_ACTIVE) + return cc; + } + } + } + return NULL; +} + +struct client_ctx * +client_find(Window win) +{ + struct screen_ctx *sc; + struct client_ctx *cc; + + TAILQ_FOREACH(sc, &Screenq, entry) { + TAILQ_FOREACH(cc, &sc->clientq, entry) { + if (cc->win == win) + return cc; + } + } + return NULL; +} + +struct client_ctx * +client_next(struct client_ctx *cc) +{ + struct screen_ctx *sc = cc->sc; + struct client_ctx *newcc; + + return(((newcc = TAILQ_NEXT(cc, entry)) != NULL) ? + newcc : TAILQ_FIRST(&sc->clientq)); +} + +struct client_ctx * +client_prev(struct client_ctx *cc) +{ + struct screen_ctx *sc = cc->sc; + struct client_ctx *newcc; + + return(((newcc = TAILQ_PREV(cc, client_q, entry)) != NULL) ? + newcc : TAILQ_LAST(&sc->clientq, client_q)); +} + +void +client_remove(struct client_ctx *cc) +{ + struct screen_ctx *sc = cc->sc; + struct winname *wn; + + TAILQ_REMOVE(&sc->clientq, cc, entry); + + xu_ewmh_net_client_list(sc); + xu_ewmh_net_client_list_stacking(sc); + + if (cc->flags & CLIENT_ACTIVE) + xu_ewmh_net_active_window(sc, None); + + while ((wn = TAILQ_FIRST(&cc->nameq)) != NULL) { + TAILQ_REMOVE(&cc->nameq, wn, entry); + free(wn->name); + free(wn); + } + + free(cc->name); + free(cc->label); + free(cc->res_class); + free(cc->res_name); + free(cc); +} + +void +client_set_active(struct client_ctx *cc) +{ + struct screen_ctx *sc = cc->sc; + struct client_ctx *oldcc; + + if (cc->flags & CLIENT_HIDDEN) + return; + + XInstallColormap(X_Dpy, cc->colormap); + + if ((cc->flags & CLIENT_INPUT) || + (!(cc->flags & CLIENT_WM_TAKE_FOCUS))) { + XSetInputFocus(X_Dpy, cc->win, + RevertToPointerRoot, CurrentTime); + } + if (cc->flags & CLIENT_WM_TAKE_FOCUS) + xu_send_clientmsg(cc->win, cwmh[WM_TAKE_FOCUS], Last_Event_Time); + + if ((oldcc = client_current(sc)) != NULL) { + oldcc->flags &= ~CLIENT_ACTIVE; + client_draw_border(oldcc); + } + + /* If we're in the middle of cycling, don't change the order. */ + if (!sc->cycling) + client_mtf(cc); + + cc->flags |= CLIENT_ACTIVE; + cc->flags &= ~CLIENT_URGENCY; + client_draw_border(cc); + conf_grab_mouse(cc->win); + xu_ewmh_net_active_window(sc, cc->win); +} + +void +client_toggle_freeze(struct client_ctx *cc) +{ + if (cc->flags & CLIENT_FULLSCREEN) + return; + + cc->flags ^= CLIENT_FREEZE; + xu_ewmh_set_net_wm_state(cc); +} + +void +client_toggle_hidden(struct client_ctx *cc) +{ + cc->flags ^= CLIENT_HIDDEN; + xu_ewmh_set_net_wm_state(cc); +} + +void +client_toggle_skip_pager(struct client_ctx *cc) +{ + cc->flags ^= CLIENT_SKIP_PAGER; + xu_ewmh_set_net_wm_state(cc); +} + +void +client_toggle_skip_taskbar(struct client_ctx *cc) +{ + cc->flags ^= CLIENT_SKIP_TASKBAR; + xu_ewmh_set_net_wm_state(cc); +} + +void +client_toggle_sticky(struct client_ctx *cc) +{ + cc->flags ^= CLIENT_STICKY; + xu_ewmh_set_net_wm_state(cc); + client_draw_border(cc); +} + +void +client_toggle_fullscreen(struct client_ctx *cc) +{ + XRaiseWindow(X_Dpy, cc->win); + + struct screen_ctx *sc = cc->sc; + struct geom area; + + if ((cc->flags & CLIENT_FREEZE) && + !(cc->flags & CLIENT_FULLSCREEN)) + return; + + if (cc->flags & CLIENT_FULLSCREEN) { + if (!(cc->flags & CLIENT_IGNORE)) + cc->bwidth = Conf.bwidth; + cc->geom = cc->fullgeom; + cc->flags &= ~(CLIENT_FULLSCREEN | CLIENT_FREEZE); + goto resize; + } + + cc->fullgeom = cc->geom; + + area = screen_area(sc, + cc->geom.x + cc->geom.w / 2, + cc->geom.y + cc->geom.h / 2, 0); + + cc->bwidth = 0; + cc->geom = area; + cc->flags |= (CLIENT_FULLSCREEN | CLIENT_FREEZE); + +resize: + client_resize(cc, 0); + xu_ewmh_set_net_wm_state(cc); + client_ptr_inbound(cc, 1); +} + +void +client_toggle_maximize(struct client_ctx *cc) +{ + XRaiseWindow(X_Dpy, cc->win); + struct screen_ctx *sc = cc->sc; + struct geom area; + + if (cc->flags & CLIENT_FREEZE) + return; + + if ((cc->flags & CLIENT_MAXFLAGS) == CLIENT_MAXIMIZED) { + cc->geom = cc->savegeom; + cc->flags &= ~CLIENT_MAXIMIZED; + goto resize; + } + + if (!(cc->flags & CLIENT_VMAXIMIZED)) { + cc->savegeom.h = cc->geom.h; + cc->savegeom.y = cc->geom.y; + } + + if (!(cc->flags & CLIENT_HMAXIMIZED)) { + cc->savegeom.w = cc->geom.w; + cc->savegeom.x = cc->geom.x; + } + + area = screen_area(sc, + cc->geom.x + cc->geom.w / 2, + cc->geom.y + cc->geom.h / 2, 1); + + cc->geom.x = area.x; + cc->geom.y = area.y; + cc->geom.w = area.w - (cc->bwidth * 2); + cc->geom.h = area.h - (cc->bwidth * 2); + cc->flags |= CLIENT_MAXIMIZED; + +resize: + client_resize(cc, 0); + xu_ewmh_set_net_wm_state(cc); + client_ptr_inbound(cc, 1); +} + +void +client_toggle_vmaximize(struct client_ctx *cc) +{ + XRaiseWindow(X_Dpy, cc->win); + struct screen_ctx *sc = cc->sc; + struct geom area; + + if (cc->flags & CLIENT_FREEZE) + return; + + if (cc->flags & CLIENT_VMAXIMIZED) { + cc->geom.y = cc->savegeom.y; + cc->geom.h = cc->savegeom.h; + cc->flags &= ~CLIENT_VMAXIMIZED; + goto resize; + } + + cc->savegeom.y = cc->geom.y; + cc->savegeom.h = cc->geom.h; + + area = screen_area(sc, + cc->geom.x + cc->geom.w / 2, + cc->geom.y + cc->geom.h / 2, 1); + + cc->geom.y = area.y; + cc->geom.h = area.h - (cc->bwidth * 2); + cc->flags |= CLIENT_VMAXIMIZED; + +resize: + client_resize(cc, 0); + xu_ewmh_set_net_wm_state(cc); + client_ptr_inbound(cc, 1); +} + +void +client_toggle_hmaximize(struct client_ctx *cc) +{ + XRaiseWindow(X_Dpy, cc->win); + struct screen_ctx *sc = cc->sc; + struct geom area; + + if (cc->flags & CLIENT_FREEZE) + return; + + if (cc->flags & CLIENT_HMAXIMIZED) { + cc->geom.x = cc->savegeom.x; + cc->geom.w = cc->savegeom.w; + cc->flags &= ~CLIENT_HMAXIMIZED; + goto resize; + } + + cc->savegeom.x = cc->geom.x; + cc->savegeom.w = cc->geom.w; + + area = screen_area(sc, + cc->geom.x + cc->geom.w / 2, + cc->geom.y + cc->geom.h / 2, 1); + + cc->geom.x = area.x; + cc->geom.w = area.w - (cc->bwidth * 2); + cc->flags |= CLIENT_HMAXIMIZED; + +resize: + client_resize(cc, 0); + xu_ewmh_set_net_wm_state(cc); + client_ptr_inbound(cc, 1); +} + +void +client_resize(struct client_ctx *cc, int reset) +{ + if (reset) { + cc->flags &= ~CLIENT_MAXIMIZED; + xu_ewmh_set_net_wm_state(cc); + } + + client_draw_border(cc); + + XMoveResizeWindow(X_Dpy, cc->win, cc->geom.x, + cc->geom.y, cc->geom.w, cc->geom.h); + cc->dim.w = (cc->geom.w - cc->hint.basew) / cc->hint.incw; + cc->dim.h = (cc->geom.h - cc->hint.baseh) / cc->hint.inch; + client_config(cc); +} + +void +client_move(struct client_ctx *cc) +{ + XRaiseWindow(X_Dpy, cc->win); + XMoveWindow(X_Dpy, cc->win, cc->geom.x, cc->geom.y); + client_config(cc); +} + +void +client_lower(struct client_ctx *cc) +{ + XLowerWindow(X_Dpy, cc->win); +} + +void +client_raise(struct client_ctx *cc) +{ + XRaiseWindow(X_Dpy, cc->win); +} + +void +client_config(struct client_ctx *cc) +{ + XConfigureEvent cn; + + (void)memset(&cn, 0, sizeof(cn)); + cn.type = ConfigureNotify; + cn.event = cc->win; + cn.window = cc->win; + cn.x = cc->geom.x; + cn.y = cc->geom.y; + cn.width = cc->geom.w; + cn.height = cc->geom.h; + cn.border_width = cc->bwidth; + cn.above = None; + cn.override_redirect = 0; + + XSendEvent(X_Dpy, cc->win, False, StructureNotifyMask, (XEvent *)&cn); +} + +void +client_ptr_inbound(struct client_ctx *cc, int getpos) +{ + if (getpos) + xu_ptr_get(cc->win, &cc->ptr.x, &cc->ptr.y); + + if (cc->ptr.x < 0) + cc->ptr.x = 0; + else if (cc->ptr.x > cc->geom.w - 1) + cc->ptr.x = cc->geom.w - 1; + if (cc->ptr.y < 0) + cc->ptr.y = 0; + else if (cc->ptr.y > cc->geom.h - 1) + cc->ptr.y = cc->geom.h - 1; + + client_ptr_warp(cc); +} + +void +client_ptr_warp(struct client_ctx *cc) +{ + xu_ptr_set(cc->win, cc->ptr.x, cc->ptr.y); +} + +void +client_ptr_save(struct client_ctx *cc) +{ + int x, y; + + xu_ptr_get(cc->win, &x, &y); + if (client_inbound(cc, x, y)) { + cc->ptr.x = x; + cc->ptr.y = y; + } else { + cc->ptr.x = cc->geom.w / 2; + cc->ptr.y = cc->geom.h / 2; + } +} + +void +client_hide(struct client_ctx *cc) +{ + XUnmapWindow(X_Dpy, cc->win); + + if (cc->flags & CLIENT_ACTIVE) { + cc->flags &= ~CLIENT_ACTIVE; + xu_ewmh_net_active_window(cc->sc, None); + } + cc->flags |= CLIENT_HIDDEN; + xu_set_wm_state(cc->win, IconicState); +} + +void +client_show(struct client_ctx *cc) +{ + XMapRaised(X_Dpy, cc->win); + + cc->flags &= ~CLIENT_HIDDEN; + xu_set_wm_state(cc->win, NormalState); + client_draw_border(cc); +} + +void +client_urgency(struct client_ctx *cc) +{ + if (!(cc->flags & CLIENT_ACTIVE)) + cc->flags |= CLIENT_URGENCY; +} + +void +client_draw_border(struct client_ctx *cc) +{ + struct screen_ctx *sc = cc->sc; + unsigned long pixel; + + if (cc->flags & CLIENT_ACTIVE) + switch (cc->flags & CLIENT_HIGHLIGHT) { + case CLIENT_GROUP: + pixel = sc->xftcolor[CWM_COLOR_BORDER_GROUP].pixel |= 0xff << 24; + break; + case CLIENT_UNGROUP: + pixel = sc->xftcolor[CWM_COLOR_BORDER_UNGROUP].pixel |= 0xff << 24; + break; + default: + if (cc->flags & CLIENT_STICKY) { + pixel = sc->xftcolor[CWM_COLOR_BORDER_ACTIVE_STICKY].pixel |= 0xff << 24; + break; + } + pixel = sc->xftcolor[CWM_COLOR_BORDER_ACTIVE].pixel |= 0xff << 24; + break; + } + else if (cc->flags & CLIENT_STICKY) { + pixel = sc->xftcolor[CWM_COLOR_BORDER_STICKY].pixel |= 0xff << 24; + } + else + pixel = sc->xftcolor[CWM_COLOR_BORDER_INACTIVE].pixel |= 0xff << 24; + + if (cc->flags & CLIENT_URGENCY) + pixel = sc->xftcolor[CWM_COLOR_BORDER_URGENCY].pixel |= 0xff << 24; + + XSetWindowBorderWidth(X_Dpy, cc->win, (unsigned int)cc->bwidth); + XSetWindowBorder(X_Dpy, cc->win, pixel); +} + +static void +client_class_hint(struct client_ctx *cc) +{ + XClassHint ch; + + if (XGetClassHint(X_Dpy, cc->win, &ch)) { + if (ch.res_class) { + cc->res_class = xstrdup(ch.res_class); + XFree(ch.res_class); + } + if (ch.res_name) { + cc->res_name = xstrdup(ch.res_name); + XFree(ch.res_name); + } + } +} + +static void +client_wm_protocols(struct client_ctx *cc) +{ + Atom *p; + int i, j; + + if (XGetWMProtocols(X_Dpy, cc->win, &p, &j)) { + for (i = 0; i < j; i++) { + if (p[i] == cwmh[WM_DELETE_WINDOW]) + cc->flags |= CLIENT_WM_DELETE_WINDOW; + else if (p[i] == cwmh[WM_TAKE_FOCUS]) + cc->flags |= CLIENT_WM_TAKE_FOCUS; + } + XFree(p); + } +} + +void +client_wm_hints(struct client_ctx *cc) +{ + XWMHints *wmh; + + if ((wmh = XGetWMHints(X_Dpy, cc->win)) != NULL) { + if ((wmh->flags & InputHint) && (wmh->input)) + cc->flags |= CLIENT_INPUT; + if ((wmh->flags & XUrgencyHint)) + client_urgency(cc); + if ((wmh->flags & StateHint)) + cc->initial_state = wmh->initial_state; + XFree(wmh); + } +} + +void +client_close(struct client_ctx *cc) +{ + if (cc->flags & CLIENT_WM_DELETE_WINDOW) + xu_send_clientmsg(cc->win, cwmh[WM_DELETE_WINDOW], CurrentTime); + else + XKillClient(X_Dpy, cc->win); +} + +void +client_center(struct client_ctx *cc) +{ + XRaiseWindow(X_Dpy, cc->win); + struct screen_ctx *sc = cc->sc; + + if (cc->flags & CLIENT_FREEZE) + return; + + cc->geom.x = (sc->view.w - cc->geom.w) / 2; + cc->geom.y = (sc->view.h - cc->geom.h) / 2; + client_applysizehints(cc); + client_resize(cc, 1); + + /* Make sure the pointer stays within the window. */ + client_ptr_inbound(cc, 0); +} + +void +client_grow(struct client_ctx *cc) +{ + XRaiseWindow(X_Dpy, cc->win); + cc->geom.x = cc->geom.x - 2; + cc->geom.y = cc->geom.y - 2; + cc->geom.w = cc->geom.w + 4; + cc->geom.h = cc->geom.h + 4; + client_applysizehints(cc); + client_resize(cc, 1); + client_ptr_inbound(cc, 0); +} + +void +client_shrink(struct client_ctx *cc) +{ + XRaiseWindow(X_Dpy, cc->win); + cc->geom.x = cc->geom.x + 2; + cc->geom.y = cc->geom.y + 2; + cc->geom.w = cc->geom.w - 4; + cc->geom.h = cc->geom.h - 4; + client_applysizehints(cc); + client_resize(cc, 1); + client_ptr_inbound(cc, 0); +} + +void +client_set_name(struct client_ctx *cc) +{ + struct winname *wn, *wnnxt; + int i = 0; + + free(cc->name); + if (!xu_get_strprop(cc->win, ewmh[_NET_WM_NAME], &cc->name)) + if (!xu_get_strprop(cc->win, XA_WM_NAME, &cc->name)) + cc->name = xstrdup(""); + + TAILQ_FOREACH_SAFE(wn, &cc->nameq, entry, wnnxt) { + if (strcmp(wn->name, cc->name) == 0) { + TAILQ_REMOVE(&cc->nameq, wn, entry); + free(wn->name); + free(wn); + } + i++; + } + wn = xmalloc(sizeof(*wn)); + wn->name = xstrdup(cc->name); + TAILQ_INSERT_TAIL(&cc->nameq, wn, entry); + + /* Garbage collection. */ + if ((i + 1) > Conf.nameqlen) { + wn = TAILQ_FIRST(&cc->nameq); + TAILQ_REMOVE(&cc->nameq, wn, entry); + free(wn->name); + free(wn); + } +} + +static void +client_placement(struct client_ctx *cc) +{ + struct screen_ctx *sc = cc->sc; + + if (cc->hint.flags & (USPosition | PPosition)) { + if (cc->geom.x >= sc->view.w) + cc->geom.x = sc->view.w - cc->bwidth - 1; + if (cc->geom.x + cc->geom.w + cc->bwidth <= 0) + cc->geom.x = -(cc->geom.w + cc->bwidth - 1); + if (cc->geom.y >= sc->view.h) + cc->geom.x = sc->view.h - cc->bwidth - 1; + if (cc->geom.y + cc->geom.h + cc->bwidth <= 0) + cc->geom.y = -(cc->geom.h + cc->bwidth - 1); + if (cc->flags & CLIENT_IGNORE) { + if (((cc->obwidth * 2) + cc->geom.x + cc->geom.w) == sc->view.w) + cc->geom.x += cc->obwidth * 2; + if (((cc->obwidth * 2) + cc->geom.y + cc->geom.h) == sc->view.h) + cc->geom.y += cc->obwidth * 2; + } + } else { + struct geom area; + int xmouse, ymouse, xslack, yslack; + + xu_ptr_get(sc->rootwin, &xmouse, &ymouse); + area = screen_area(sc, xmouse, ymouse, 1); + + xmouse = MAX(MAX(xmouse, area.x) - cc->geom.w / 2, area.x); + ymouse = MAX(MAX(ymouse, area.y) - cc->geom.h / 2, area.y); + + xslack = area.x + area.w - cc->geom.w - cc->bwidth * 2; + yslack = area.y + area.h - cc->geom.h - cc->bwidth * 2; + + if (xslack >= area.x) { + cc->geom.x = MAX(MIN(xmouse, xslack), area.x); + } else { + cc->geom.x = area.x; + cc->geom.w = area.x + area.w; + } + if (yslack >= area.y) { + cc->geom.y = MAX(MIN(ymouse, yslack), area.y); + } else { + cc->geom.y = area.y; + cc->geom.h = area.y + area.h; + } + } +} + +void +client_mtf(struct client_ctx *cc) +{ + struct screen_ctx *sc = cc->sc; + + TAILQ_REMOVE(&sc->clientq, cc, entry); + TAILQ_INSERT_HEAD(&sc->clientq, cc, entry); +} + +void +client_get_sizehints(struct client_ctx *cc) +{ + long tmp; + XSizeHints size; + + if (!XGetWMNormalHints(X_Dpy, cc->win, &size, &tmp)) + size.flags = 0; + + cc->hint.flags = size.flags; + + if (size.flags & PBaseSize) { + cc->hint.basew = size.base_width; + cc->hint.baseh = size.base_height; + } else if (size.flags & PMinSize) { + cc->hint.basew = size.min_width; + cc->hint.baseh = size.min_height; + } + if (size.flags & PMinSize) { + cc->hint.minw = size.min_width; + cc->hint.minh = size.min_height; + } else if (size.flags & PBaseSize) { + cc->hint.minw = size.base_width; + cc->hint.minh = size.base_height; + } + if (size.flags & PMaxSize) { + cc->hint.maxw = size.max_width; + cc->hint.maxh = size.max_height; + } + if (size.flags & PResizeInc) { + cc->hint.incw = size.width_inc; + cc->hint.inch = size.height_inc; + } + cc->hint.incw = MAX(1, cc->hint.incw); + cc->hint.inch = MAX(1, cc->hint.inch); + cc->hint.minw = MAX(1, cc->hint.minw); + cc->hint.minh = MAX(1, cc->hint.minh); + + if (size.flags & PAspect) { + if (size.min_aspect.x > 0) + cc->hint.mina = (float)size.min_aspect.y / + size.min_aspect.x; + if (size.max_aspect.y > 0) + cc->hint.maxa = (float)size.max_aspect.x / + size.max_aspect.y; + } +} + +void +client_apply_sizehints(struct client_ctx *cc) +{ + Bool baseismin; + + baseismin = (cc->hint.basew == cc->hint.minw) && + (cc->hint.baseh == cc->hint.minh); + + /* temporarily remove base dimensions, ICCCM 4.1.2.3 */ + if (!baseismin) { + cc->geom.w -= cc->hint.basew; + cc->geom.h -= cc->hint.baseh; + } + + /* adjust for aspect limits */ + if (cc->hint.mina && cc->hint.maxa) { + if (cc->hint.maxa < (float)cc->geom.w / cc->geom.h) + cc->geom.w = cc->geom.h * cc->hint.maxa; + else if (cc->hint.mina < (float)cc->geom.h / cc->geom.w) + cc->geom.h = cc->geom.w * cc->hint.mina; + } + + /* remove base dimensions for increment */ + if (baseismin) { + cc->geom.w -= cc->hint.basew; + cc->geom.h -= cc->hint.baseh; + } + + /* adjust for increment value */ + cc->geom.w -= cc->geom.w % cc->hint.incw; + cc->geom.h -= cc->geom.h % cc->hint.inch; + + /* restore base dimensions */ + cc->geom.w += cc->hint.basew; + cc->geom.h += cc->hint.baseh; + + /* adjust for min width/height */ + cc->geom.w = MAX(cc->geom.w, cc->hint.minw); + cc->geom.h = MAX(cc->geom.h, cc->hint.minh); + + /* adjust for max width/height */ + if (cc->hint.maxw) + cc->geom.w = MIN(cc->geom.w, cc->hint.maxw); + if (cc->hint.maxh) + cc->geom.h = MIN(cc->geom.h, cc->hint.maxh); +} + +void +client_applysizehints(struct client_ctx *cc) +{ + Bool baseismin; + + baseismin = (cc->hint.basew == cc->hint.minw) && + (cc->hint.baseh == cc->hint.minh); + + /* temporarily remove base dimensions, ICCCM 4.1.2.3 */ + if (!baseismin) { + cc->geom.w -= cc->hint.basew; + cc->geom.h -= cc->hint.baseh; + } + + /* adjust for aspect limits */ + if (cc->hint.mina && cc->hint.maxa) { + if (cc->hint.maxa < (float)cc->geom.w / cc->geom.h) + cc->geom.w = cc->geom.h * cc->hint.maxa; + else if (cc->hint.mina < (float)cc->geom.h / cc->geom.w) + cc->geom.h = cc->geom.w * cc->hint.mina; + } + + /* remove base dimensions for increment */ + if (baseismin) { + cc->geom.w -= cc->hint.basew; + cc->geom.h -= cc->hint.baseh; + } + + /* adjust for increment value */ + cc->geom.w -= cc->geom.w % cc->hint.incw; + cc->geom.h -= cc->geom.h % cc->hint.inch; + + /* restore base dimensions */ + cc->geom.w += cc->hint.basew; + cc->geom.h += cc->hint.baseh; + + /* adjust for min width/height */ + cc->geom.w = MAX(cc->geom.w, cc->hint.minw); + cc->geom.h = MAX(cc->geom.h, cc->hint.minh); + + /* adjust for max width/height */ + if (cc->hint.maxw) + cc->geom.w = MIN(cc->geom.w, cc->hint.maxw); + if (cc->hint.maxh) + cc->geom.h = MIN(cc->geom.h, cc->hint.maxh); +} + +static void +client_mwm_hints(struct client_ctx *cc) +{ + struct mwm_hints *mwmh; + + if (xu_get_prop(cc->win, cwmh[_MOTIF_WM_HINTS], + cwmh[_MOTIF_WM_HINTS], MWM_HINTS_ELEMENTS, + (unsigned char **)&mwmh) == MWM_HINTS_ELEMENTS) { + if (mwmh->flags & MWM_FLAGS_DECORATIONS && + !(mwmh->decorations & MWM_DECOR_ALL) && + !(mwmh->decorations & MWM_DECOR_BORDER)) + /*cc->bwidth = 0;*/ + XFree(mwmh); + } +} + +void +client_transient(struct client_ctx *cc) +{ + struct client_ctx *tc; + Window trans; + + if (XGetTransientForHint(X_Dpy, cc->win, &trans)) { + if ((tc = client_find(trans)) != NULL) { + if (tc->flags & CLIENT_IGNORE) { + cc->flags |= CLIENT_IGNORE; + cc->bwidth = tc->bwidth; + } + } + } +} + +int +client_inbound(struct client_ctx *cc, int x, int y) +{ + return(x < cc->geom.w && x >= 0 && + y < cc->geom.h && y >= 0); +} + +int +client_snapcalc(int n0, int n1, int e0, int e1, int snapdist) +{ + int s0, s1; + + s0 = s1 = 0; + + if (abs(e0 - n0) <= snapdist) + s0 = e0 - n0; + + if (abs(e1 - n1) <= snapdist) + s1 = e1 - n1; + + /* possible to snap in both directions */ + if (s0 != 0 && s1 != 0) + if (abs(s0) < abs(s1)) + return s0; + else + return s1; + else if (s0 != 0) + return s0; + else if (s1 != 0) + return s1; + else + return 0; +} + +void +client_htile(struct client_ctx *cc) +{ + struct client_ctx *ci; + struct screen_ctx *sc = cc->sc; + struct geom area; + int i, n, mh, x, w, h; + + i = n = 0; + area = screen_area(sc, + cc->geom.x + cc->geom.w / 2, + cc->geom.y + cc->geom.h / 2, 1); + + TAILQ_FOREACH(ci, &sc->clientq, entry) { + if (ci->gc != cc->gc) + continue; + if (ci->flags & CLIENT_HIDDEN || + ci->flags & CLIENT_IGNORE || (ci == cc) || + ci->geom.x < area.x || + ci->geom.x > (area.x + area.w) || + ci->geom.y < area.y || + ci->geom.y > (area.y + area.h)) + continue; + n++; + } + if (n == 0) + return; + + if (cc->flags & CLIENT_VMAXIMIZED || + cc->geom.h + (cc->bwidth * 2) >= area.h) + return; + + cc->flags &= ~CLIENT_HMAXIMIZED; + cc->geom.x = area.x; + cc->geom.y = area.y; + cc->geom.w = area.w - (cc->bwidth * 2); + if (Conf.htile > 0) + cc->geom.h = ((area.h - (cc->bwidth * 2)) * Conf.htile) / 100; + client_resize(cc, 1); + client_ptr_warp(cc); + + mh = cc->geom.h + (cc->bwidth * 2); + x = area.x; + w = area.w / n; + h = area.h - mh; + TAILQ_FOREACH(ci, &sc->clientq, entry) { + if (ci->gc != cc->gc) + continue; + if (ci->flags & CLIENT_HIDDEN || + ci->flags & CLIENT_IGNORE || (ci == cc) || + ci->geom.x < area.x || + ci->geom.x > (area.x + area.w) || + ci->geom.y < area.y || + ci->geom.y > (area.y + area.h)) + continue; + ci->bwidth = Conf.bwidth; + ci->geom.x = x; + ci->geom.y = area.y + mh; + ci->geom.w = w - (ci->bwidth * 2); + ci->geom.h = h - (ci->bwidth * 2); + if (i + 1 == n) + ci->geom.w = area.x + area.w - + ci->geom.x - (ci->bwidth * 2); + x += w; + i++; + client_resize(ci, 1); + } +} + +void +client_vtile(struct client_ctx *cc) +{ + struct client_ctx *ci; + struct screen_ctx *sc = cc->sc; + struct geom area; + int i, n, mw, mh, y, w, h; + + i = n = 0; + area = screen_area(sc, + cc->geom.x + cc->geom.w / 2, + cc->geom.y + cc->geom.h / 2, CWM_GAP); + + TAILQ_FOREACH(ci, &sc->clientq, entry) { + if (ci->gc != cc->gc) + continue; + if (ci->flags & CLIENT_HIDDEN || + ci->flags & CLIENT_IGNORE || (ci == cc) || + ci->geom.x < area.x || + ci->geom.x > (area.x + area.w) || + ci->geom.y < area.y || + ci->geom.y > (area.y + area.h)) + continue; + n++; + } + if (n == 0) + return; + + if (cc->flags & CLIENT_HMAXIMIZED || + cc->geom.w + (cc->bwidth * 2) >= area.w) + return; + + cc->flags &= ~CLIENT_VMAXIMIZED; + cc->geom.x = area.x; + cc->geom.y = area.y; + if (Conf.vtile > 0) + cc->geom.w = ((area.w - (cc->bwidth * 2)) * Conf.vtile) / 100; + cc->geom.h = area.h - (cc->bwidth * 2); + client_resize(cc, 1); + client_ptr_warp(cc); + + mw = cc->geom.w + (cc->bwidth * 2) + cc->tgap; + y = area.y; + h = area.h / n; + w = area.w - mw; + TAILQ_FOREACH(ci, &sc->clientq, entry) { + if (ci->gc != cc->gc) + continue; + if (ci->flags & CLIENT_HIDDEN || + ci->flags & CLIENT_IGNORE || (ci == cc) || + ci->geom.x < area.x || + ci->geom.x > (area.x + area.w) || + ci->geom.y < area.y || + ci->geom.y > (area.y + area.h)) + continue; + ci->bwidth = Conf.bwidth; + ci->geom.x = area.x + mw; + ci->geom.y = y; + ci->geom.w = w - (ci->bwidth * 2); + ci->geom.h = h - (ci->bwidth * 2) - cc->tgap; + if (i + 1 == n) + ci->geom.h = area.y + area.h - + ci->geom.y - (ci->bwidth * 2); + y += h; + i++; + client_resize(ci, 1); + } +} diff --git a/conf.c b/conf.c new file mode 100644 index 0000000..e817969 --- /dev/null +++ b/conf.c @@ -0,0 +1,747 @@ +/* + * + * Copyright (c) 2004 Marius Aamodt Eriksen + * + * 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. + * + * $OpenBSD$ + */ + +#include +#include "queue.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "samu.h" + +static const char *conf_bind_mask(const char *, unsigned int *); +static void conf_unbind_key(struct conf *, struct bind_ctx *); +static void conf_unbind_mouse(struct conf *, struct bind_ctx *); + +static const struct { + int num; + const char *name; +} group_binds[] = { + { 0, "nogroup"}, + { 1, "one" }, + { 2, "two" }, + { 3, "three" }, + { 4, "four" }, + { 5, "five" }, + { 6, "six" }, + { 7, "seven" }, + { 8, "eight" }, + { 9, "nine" }, +}; +static int cursor_binds[] = { + XC_left_ptr, /* CF_NORMAL */ + XC_fleur, /* CF_MOVE */ + XC_bottom_right_corner, /* CF_RESIZE */ + XC_top_right_corner, /* CF_RESIZE_NE */ + XC_top_left_corner, /* CF_RESIZE_NW */ + XC_bottom_right_corner, /* CF_RESIZE_SE */ + XC_bottom_left_corner, /* CF_RESIZE_SW */ + XC_question_arrow, /* CF_QUESTION */ +}; + + /*"#52aaad", [> CWM_COLOR_BORDER_ACTIVE <]*/ + /*"#9cefef", [> CWM_COLOR_BORDER_INACTIVE <]*/ + /*"#F6A9AE", [> CWM_COLOR_BORDER_STICKY <]*/ + /*"#F07078", [> CWM_COLOR_BORDER_ACTIVE_STICKY <]*/ + +static const char *color_binds[] = { + "#52aaad", /* CWM_COLOR_BORDER_ACTIVE */ + "#9cefef", /* CWM_COLOR_BORDER_INACTIVE */ + "#f5c211", /* CWM_COLOR_BORDER_URGENCY */ + "blue", /* CWM_COLOR_BORDER_GROUP */ + "red", /* CWM_COLOR_BORDER_UNGROUP */ + "#F6A9AE", /* CWM_COLOR_BORDER_STICKY */ + "#F07078", /* CWM_COLOR_BORDER_ACTIVE_STICKY */ + "black", /* CWM_COLOR_MENU_FG */ + "white", /* CWM_COLOR_MENU_BG */ + "black", /* CWM_COLOR_MENU_FONT */ + "", /* CWM_COLOR_MENU_FONT_SEL */ +}; +static const struct { + const char *tag; + void (*handler)(void *, struct cargs *); + enum context context; + int flag; +} name_to_func[] = { +#define FUNC_CC(t, h, n) \ + #t, kbfunc_ ## h, CWM_CONTEXT_CC, n +#define FUNC_SC(t, h, n) \ + #t, kbfunc_ ## h, CWM_CONTEXT_SC, n + + { FUNC_CC(window-lower, client_lower, 0) }, + { FUNC_CC(window-raise, client_raise, 0) }, + { FUNC_CC(window-grow, client_grow, 0) }, + { FUNC_CC(window-shrink, client_shrink, 0) }, + { FUNC_CC(window-hide, client_hide, 0) }, + { FUNC_CC(window-close, client_close, 0) }, + { FUNC_CC(window-delete, client_close, 0) }, + { FUNC_CC(window-htile, client_htile, 0) }, + { FUNC_CC(window-vtile, client_vtile, 0) }, + { FUNC_CC(window-stick, client_toggle_sticky, 0) }, + { FUNC_CC(window-fullscreen, client_toggle_fullscreen, 0) }, + { FUNC_CC(window-maximize, client_toggle_maximize, 0) }, + { FUNC_CC(window-vmaximize, client_toggle_vmaximize, 0) }, + { FUNC_CC(window-hmaximize, client_toggle_hmaximize, 0) }, + { FUNC_CC(window-freeze, client_toggle_freeze, 0) }, + { FUNC_CC(window-group, client_toggle_group, 0) }, + { FUNC_CC(window-movetogroup-1, client_movetogroup, 1) }, + { FUNC_CC(window-movetogroup-2, client_movetogroup, 2) }, + { FUNC_CC(window-movetogroup-3, client_movetogroup, 3) }, + { FUNC_CC(window-movetogroup-4, client_movetogroup, 4) }, + { FUNC_CC(window-movetogroup-5, client_movetogroup, 5) }, + { FUNC_CC(window-movetogroup-6, client_movetogroup, 6) }, + { FUNC_CC(window-movetogroup-7, client_movetogroup, 7) }, + { FUNC_CC(window-movetogroup-8, client_movetogroup, 8) }, + { FUNC_CC(window-movetogroup-9, client_movetogroup, 9) }, + { FUNC_CC(window-center, client_snap, 0) }, + { FUNC_CC(window-snap-up, client_snap, (CWM_UP)) }, + { FUNC_CC(window-snap-down, client_snap, (CWM_DOWN)) }, + { FUNC_CC(window-snap-right, client_snap, (CWM_RIGHT)) }, + { FUNC_CC(window-snap-left, client_snap, (CWM_LEFT)) }, + { FUNC_CC(window-snap-up-right, client_snap, (CWM_UP_RIGHT)) }, + { FUNC_CC(window-snap-up-left, client_snap, (CWM_UP_LEFT)) }, + { FUNC_CC(window-snap-down-right, client_snap, (CWM_DOWN_RIGHT)) }, + { FUNC_CC(window-snap-down-left, client_snap, (CWM_DOWN_LEFT)) }, + { FUNC_CC(window-move, client_move, 0) }, + { FUNC_CC(window-move-up, client_move, (CWM_UP)) }, + { FUNC_CC(window-move-down, client_move, (CWM_DOWN)) }, + { FUNC_CC(window-move-right, client_move, (CWM_RIGHT)) }, + { FUNC_CC(window-move-left, client_move, (CWM_LEFT)) }, + { FUNC_CC(window-move-up-big, client_move, (CWM_UP_BIG)) }, + { FUNC_CC(window-move-down-big, client_move, (CWM_DOWN_BIG)) }, + { FUNC_CC(window-move-right-big, client_move, (CWM_RIGHT_BIG)) }, + { FUNC_CC(window-move-left-big, client_move, (CWM_LEFT_BIG)) }, + { FUNC_CC(window-resize, client_resize, 0) }, + { FUNC_CC(window-resize-up, client_resize, (CWM_UP)) }, + { FUNC_CC(window-resize-down, client_resize, (CWM_DOWN)) }, + { FUNC_CC(window-resize-right, client_resize, (CWM_RIGHT)) }, + { FUNC_CC(window-resize-left, client_resize, (CWM_LEFT)) }, + { FUNC_CC(window-resize-up-big, client_resize, (CWM_UP_BIG)) }, + { FUNC_CC(window-resize-down-big, client_resize, (CWM_DOWN_BIG)) }, + { FUNC_CC(window-resize-right-big, client_resize, (CWM_RIGHT_BIG)) }, + { FUNC_CC(window-resize-left-big, client_resize, (CWM_LEFT_BIG)) }, + { FUNC_CC(window-tile-up, client_tile, (CWM_UP)) }, + { FUNC_CC(window-tile-down, client_tile, (CWM_DOWN)) }, + { FUNC_CC(window-tile-left, client_tile, (CWM_LEFT)) }, + { FUNC_CC(window-tile-right, client_tile, (CWM_RIGHT)) }, + { FUNC_CC(window-tile-up-left, client_tile, (CWM_UP_LEFT)) }, + { FUNC_CC(window-tile-up-right, client_tile, (CWM_UP_RIGHT)) }, + { FUNC_CC(window-tile-down-left, client_tile, (CWM_DOWN_LEFT)) }, + { FUNC_CC(window-tile-down-right, client_tile, (CWM_DOWN_RIGHT)) }, + + { FUNC_SC(window-cycle, client_cycle, (CWM_CYCLE_FORWARD)) }, + { FUNC_SC(window-rcycle, client_cycle, (CWM_CYCLE_REVERSE)) }, + { FUNC_SC(window-cycle-ingroup, client_cycle, + (CWM_CYCLE_FORWARD | CWM_CYCLE_INGROUP)) }, + { FUNC_SC(window-rcycle-ingroup, client_cycle, + (CWM_CYCLE_REVERSE | CWM_CYCLE_INGROUP)) }, + + { FUNC_SC(group-cycle, group_cycle, (CWM_CYCLE_FORWARD)) }, + { FUNC_SC(group-rcycle, group_cycle, (CWM_CYCLE_REVERSE)) }, + { FUNC_SC(group-last, group_last, 0) }, + { FUNC_SC(group-toggle-all, group_toggle_all, 0) }, + { FUNC_SC(group-toggle-1, group_toggle, 1) }, + { FUNC_SC(group-toggle-2, group_toggle, 2) }, + { FUNC_SC(group-toggle-3, group_toggle, 3) }, + { FUNC_SC(group-toggle-4, group_toggle, 4) }, + { FUNC_SC(group-toggle-5, group_toggle, 5) }, + { FUNC_SC(group-toggle-6, group_toggle, 6) }, + { FUNC_SC(group-toggle-7, group_toggle, 7) }, + { FUNC_SC(group-toggle-8, group_toggle, 8) }, + { FUNC_SC(group-toggle-9, group_toggle, 9) }, + { FUNC_SC(group-only-1, group_only, 1) }, + { FUNC_SC(group-only-2, group_only, 2) }, + { FUNC_SC(group-only-3, group_only, 3) }, + { FUNC_SC(group-only-4, group_only, 4) }, + { FUNC_SC(group-only-5, group_only, 5) }, + { FUNC_SC(group-only-6, group_only, 6) }, + { FUNC_SC(group-only-7, group_only, 7) }, + { FUNC_SC(group-only-8, group_only, 8) }, + { FUNC_SC(group-only-9, group_only, 9) }, + { FUNC_SC(group-close-1, group_close, 1) }, + { FUNC_SC(group-close-2, group_close, 2) }, + { FUNC_SC(group-close-3, group_close, 3) }, + { FUNC_SC(group-close-4, group_close, 4) }, + { FUNC_SC(group-close-5, group_close, 5) }, + { FUNC_SC(group-close-6, group_close, 6) }, + { FUNC_SC(group-close-7, group_close, 7) }, + { FUNC_SC(group-close-8, group_close, 8) }, + { FUNC_SC(group-close-9, group_close, 9) }, + + { FUNC_SC(pointer-move-up, ptrmove, (CWM_UP)) }, + { FUNC_SC(pointer-move-down, ptrmove, (CWM_DOWN)) }, + { FUNC_SC(pointer-move-left, ptrmove, (CWM_LEFT)) }, + { FUNC_SC(pointer-move-right, ptrmove, (CWM_RIGHT)) }, + { FUNC_SC(pointer-move-up-big, ptrmove, (CWM_UP_BIG)) }, + { FUNC_SC(pointer-move-down-big, ptrmove, (CWM_DOWN_BIG)) }, + { FUNC_SC(pointer-move-left-big, ptrmove, (CWM_LEFT_BIG)) }, + { FUNC_SC(pointer-move-right-big, ptrmove, (CWM_RIGHT_BIG)) }, + + { FUNC_SC(term, exec_term, 0) }, + { FUNC_SC(dmenu, exec_dmenu, 0) }, + { FUNC_SC(dextra, exec_dextra, 0) }, + { FUNC_SC(dprint, exec_dprint, 0) }, + { FUNC_SC(vol-up, exec_vol_up, 0) }, + { FUNC_SC(vol-down, exec_vol_down, 0) }, + { FUNC_SC(vol-mute, exec_vol_mute, 0) }, + { FUNC_SC(restart, cwm_status, CWM_EXEC_WM) }, + { FUNC_SC(quit, cwm_status, CWM_QUIT) }, +}; +static unsigned int ignore_mods[] = { + 0, LockMask, Mod2Mask, Mod2Mask | LockMask +}; +static const struct { + const char ch; + int mask; +} bind_mods[] = { + { 'S', ShiftMask }, + { 'C', ControlMask }, + { 'M', Mod1Mask }, + { '4', Mod4Mask }, + { '5', Mod5Mask }, +}; +static const struct { + const char *key; + const char *func; +} key_binds[] = { + { "4-Return", "term" }, + { "4-space", "dmenu" }, + { "4-period", "dextra" }, + { "4-d", "teleporter" }, + { "Print", "dprint" }, + { "4S-l", "lock" }, + { "XF86AudioRaiseVolume", "vol-up" }, + { "XF86AudioLowerVolume", "vol-down" }, + { "XF86AudioMute", "vol-mute" }, + { "4-comma", "window-hide" }, + { "4M-Down", "window-lower" }, + { "4M-Up", "window-raise" }, + { "4-c", "window-center" }, + { "M-Tab", "window-cycle" }, + { "MS-Tab", "window-rcycle" }, + { "4-Tab", "window-cycle" }, + { "4S-Tab", "window-rcycle" }, + { "4-w", "window-close" }, + { "4-f", "window-fullscreen" }, + { "4-m", "window-maximize" }, + { "4-g", "window-stick" }, + { "4-Left", "window-move-left-big" }, + { "4-Down", "window-move-down-big" }, + { "4-Up", "window-move-up-big" }, + { "4-Right", "window-move-right-big" }, + { "4S-Left", "window-resize-left-big" }, + { "4S-Down", "window-resize-down-big" }, + { "4S-Up", "window-resize-up-big" }, + { "4S-Right", "window-resize-right-big" }, + { "4-t", "window-vtile" }, + { "4-p", "window-tile-right" }, + { "4-o", "window-tile-left" }, + { "4-a", "window-tile-up-left" }, + { "4-s", "window-tile-up-right" }, + { "4-z", "window-tile-down-left" }, + { "4-x", "window-tile-down-right" }, + { "4-equal", "window-grow" }, + { "4-minus", "window-shrink" }, + { "4-0", "group-toggle-all" }, + { "4-1", "group-only-1" }, + { "4-2", "group-only-2" }, + { "4-3", "group-only-3" }, + { "4-4", "group-only-4" }, + { "4-5", "group-only-5" }, + { "4-6", "group-only-6" }, + { "4-7", "group-only-7" }, + { "4-8", "group-only-8" }, + { "4-9", "group-only-9" }, + { "4M-1", "group-toggle-1" }, + { "4M-2", "group-toggle-2" }, + { "4M-3", "group-toggle-3" }, + { "4M-4", "group-toggle-4" }, + { "4M-5", "group-toggle-5" }, + { "4M-6", "group-toggle-6" }, + { "4M-7", "group-toggle-7" }, + { "4M-8", "group-toggle-8" }, + { "4M-9", "group-toggle-9" }, + { "4S-1", "window-movetogroup-1" }, + { "4S-2", "window-movetogroup-2" }, + { "4S-3", "window-movetogroup-3" }, + { "4S-4", "window-movetogroup-4" }, + { "4S-5", "window-movetogroup-5" }, + { "4S-6", "window-movetogroup-6" }, + { "4S-7", "window-movetogroup-7" }, + { "4S-8", "window-movetogroup-8" }, + { "4S-9", "window-movetogroup-9" }, + { "4M-Right", "group-cycle" }, + { "4M-Left", "group-rcycle" }, +}, +mouse_binds[] = { + { "3", "teleporter" }, + { "4-1", "window-move" }, + { "4S-1", "window-resize" }, + { "4-3", "window-resize" }, + { "4M-1", "window-stick" }, + { "4S-3", "window-stick" }, +}; + +void +conf_init(struct conf *c) +{ + const char *home; + struct passwd *pw; + unsigned int i; + + c->stickygroups = 1; + c->resizeallcorners = 0; + c->bwidth = 0; + c->tgap = 4; + c->gap.top = 4; + c->gap.bottom = 4; + c->gap.left = 4; + c->gap.right = 4; + c->gap.bar = 0; + c->mamount = 2; + c->htile = 50; + c->vtile = 60; + c->snapdist = 6; + c->ngroups = 0; + c->nameqlen = 5; + + TAILQ_INIT(&c->ignoreq); + TAILQ_INIT(&c->autogroupq); + TAILQ_INIT(&c->keybindq); + TAILQ_INIT(&c->mousebindq); + TAILQ_INIT(&c->cmdq); + TAILQ_INIT(&c->wmq); + + for (i = 0; i < nitems(key_binds); i++) + conf_bind_key(c, key_binds[i].key, key_binds[i].func); + + for (i = 0; i < nitems(mouse_binds); i++) + conf_bind_mouse(c, mouse_binds[i].key, mouse_binds[i].func); + + for (i = 0; i < nitems(color_binds); i++) + c->color[i] = xstrdup(color_binds[i]); + + conf_cmd_add(c, "term", "kitty"); + conf_cmd_add(c, "dmenu", "dmenu_run -fn 'Arimo'"); + conf_cmd_add(c, "teleporter", "teleporter"); + conf_cmd_add(c, "dextra", "dextra"); + conf_cmd_add(c, "dprint", "dprint"); + conf_cmd_add(c, "vol-up", "vol-up"); + conf_cmd_add(c, "vol-down", "vol-down"); + conf_cmd_add(c, "vol-mute", "vol-mute"); + conf_cmd_add(c, "lock", "i3lock"); + conf_wm_add(c, "cwm", "cwm"); + + c->font = xstrdup("sans-serif:pixelsize=14:bold"); + c->wmname = xstrdup("CWM"); + + home = getenv("HOME"); + if ((home == NULL) || (*home == '\0')) { + pw = getpwuid(getuid()); + if (pw != NULL && pw->pw_dir != NULL && *pw->pw_dir != '\0') + home = pw->pw_dir; + else + home = "/"; + } + xasprintf(&c->conf_file, "%s/%s", home, ".samurc"); + xasprintf(&c->known_hosts, "%s/%s", home, ".ssh/known_hosts"); +} + +void +conf_clear(struct conf *c) +{ + struct autogroup *ag; + struct bind_ctx *kb, *mb; + struct winname *wn; + struct cmd_ctx *cmd, *wm; + int i; + + while ((cmd = TAILQ_FIRST(&c->cmdq)) != NULL) { + TAILQ_REMOVE(&c->cmdq, cmd, entry); + free(cmd->name); + free(cmd->path); + free(cmd); + } + while ((wm = TAILQ_FIRST(&c->wmq)) != NULL) { + TAILQ_REMOVE(&c->wmq, wm, entry); + free(wm->name); + free(wm->path); + free(wm); + } + while ((kb = TAILQ_FIRST(&c->keybindq)) != NULL) { + TAILQ_REMOVE(&c->keybindq, kb, entry); + free(kb); + } + while ((ag = TAILQ_FIRST(&c->autogroupq)) != NULL) { + TAILQ_REMOVE(&c->autogroupq, ag, entry); + free(ag->class); + free(ag->name); + free(ag); + } + while ((wn = TAILQ_FIRST(&c->ignoreq)) != NULL) { + TAILQ_REMOVE(&c->ignoreq, wn, entry); + free(wn->name); + free(wn); + } + while ((mb = TAILQ_FIRST(&c->mousebindq)) != NULL) { + TAILQ_REMOVE(&c->mousebindq, mb, entry); + free(mb); + } + for (i = 0; i < CWM_COLOR_NITEMS; i++) + free(c->color[i]); + + free(c->conf_file); + free(c->known_hosts); + free(c->font); + free(c->wmname); +} + +void +conf_cmd_add(struct conf *c, const char *name, const char *path) +{ + struct cmd_ctx *cmd, *cmdtmp = NULL, *cmdnxt; + + cmd = xmalloc(sizeof(*cmd)); + cmd->name = xstrdup(name); + cmd->path = xstrdup(path); + + TAILQ_FOREACH_SAFE(cmdtmp, &c->cmdq, entry, cmdnxt) { + if (strcmp(cmdtmp->name, name) == 0) { + TAILQ_REMOVE(&c->cmdq, cmdtmp, entry); + free(cmdtmp->name); + free(cmdtmp->path); + free(cmdtmp); + } + } + TAILQ_INSERT_TAIL(&c->cmdq, cmd, entry); +} + +void +conf_wm_add(struct conf *c, const char *name, const char *path) +{ + struct cmd_ctx *wm, *wmtmp = NULL, *wmnxt; + + wm = xmalloc(sizeof(*wm)); + wm->name = xstrdup(name); + wm->path = xstrdup(path); + + TAILQ_FOREACH_SAFE(wmtmp, &c->cmdq, entry, wmnxt) { + if (strcmp(wmtmp->name, name) == 0) { + TAILQ_REMOVE(&c->wmq, wmtmp, entry); + free(wmtmp->name); + free(wmtmp->path); + free(wmtmp); + } + } + TAILQ_INSERT_TAIL(&c->wmq, wm, entry); +} + +void +conf_autogroup(struct conf *c, int num, const char *name, const char *class) +{ + struct autogroup *ag; + char *p; + + ag = xmalloc(sizeof(*ag)); + if ((p = strchr(class, ',')) == NULL) { + if (name == NULL) + ag->name = NULL; + else + ag->name = xstrdup(name); + + ag->class = xstrdup(class); + } else { + *(p++) = '\0'; + if (name == NULL) + ag->name = xstrdup(class); + else + ag->name = xstrdup(name); + + ag->class = xstrdup(p); + } + ag->num = num; + TAILQ_INSERT_TAIL(&c->autogroupq, ag, entry); +} + +void +conf_ignore(struct conf *c, const char *name) +{ + struct winname *wn; + + wn = xmalloc(sizeof(*wn)); + wn->name = xstrdup(name); + TAILQ_INSERT_TAIL(&c->ignoreq, wn, entry); +} + +void +conf_cursor(struct conf *c) +{ + unsigned int i; + + for (i = 0; i < nitems(cursor_binds); i++) + c->cursor[i] = XCreateFontCursor(X_Dpy, cursor_binds[i]); +} + +void +conf_client(struct client_ctx *cc) +{ + struct winname *wn; + + TAILQ_FOREACH(wn, &Conf.ignoreq, entry) { + if (strncasecmp(wn->name, cc->name, strlen(wn->name)) == 0) { + cc->flags |= CLIENT_IGNORE; + break; + } + } +} + +void +conf_screen(struct screen_ctx *sc) +{ + unsigned int i; + XftColor xc; + + sc->gap = Conf.gap; + sc->snapdist = Conf.snapdist; + + sc->xftfont = XftFontOpenXlfd(X_Dpy, sc->which, Conf.font); + if (sc->xftfont == NULL) { + sc->xftfont = XftFontOpenName(X_Dpy, sc->which, Conf.font); + if (sc->xftfont == NULL) + errx(1, "%s: XftFontOpenName: %s", __func__, Conf.font); + } + + for (i = 0; i < nitems(color_binds); i++) { + if (i == CWM_COLOR_MENU_FONT_SEL && *Conf.color[i] == '\0') { + xu_xorcolor(sc->xftcolor[CWM_COLOR_MENU_BG], + sc->xftcolor[CWM_COLOR_MENU_FG], &xc); + xu_xorcolor(sc->xftcolor[CWM_COLOR_MENU_FONT], xc, &xc); + if (!XftColorAllocValue(X_Dpy, sc->visual, sc->colormap, + &xc.color, &sc->xftcolor[CWM_COLOR_MENU_FONT_SEL])) + warnx("XftColorAllocValue: %s", Conf.color[i]); + break; + } + if (!XftColorAllocName(X_Dpy, sc->visual, sc->colormap, + Conf.color[i], &sc->xftcolor[i])) { + warnx("XftColorAllocName: %s", Conf.color[i]); + XftColorAllocName(X_Dpy, sc->visual, sc->colormap, + color_binds[i], &sc->xftcolor[i]); + } + } + + conf_grab_kbd(sc->rootwin); +} + +void +conf_group(struct screen_ctx *sc) +{ + unsigned int i; + + for (i = 0; i < nitems(group_binds); i++) { + group_init(sc, group_binds[i].num, group_binds[i].name); + Conf.ngroups++; + } +} + +static const char * +conf_bind_mask(const char *name, unsigned int *mask) +{ + char *dash; + const char *ch; + unsigned int i; + + *mask = 0; + if ((dash = strchr(name, '-')) == NULL) + return name; + for (i = 0; i < nitems(bind_mods); i++) { + if ((ch = strchr(name, bind_mods[i].ch)) != NULL && ch < dash) + *mask |= bind_mods[i].mask; + } + /* Skip past modifiers. */ + return (dash + 1); +} + +int +conf_bind_key(struct conf *c, const char *bind, const char *cmd) +{ + struct bind_ctx *kb; + struct cargs *cargs; + const char *key; + unsigned int i; + + if ((strcmp(bind, "all") == 0) && (cmd == NULL)) { + conf_unbind_key(c, NULL); + return 1; + } + kb = xmalloc(sizeof(*kb)); + key = conf_bind_mask(bind, &kb->modmask); + kb->press.keysym = XStringToKeysym(key); + if (kb->press.keysym == NoSymbol) { + warnx("unknown symbol: %s", key); + free(kb); + return 0; + } + conf_unbind_key(c, kb); + if (cmd == NULL) { + free(kb); + return 1; + } + cargs = xcalloc(1, sizeof(*cargs)); + for (i = 0; i < nitems(name_to_func); i++) { + if (strcmp(name_to_func[i].tag, cmd) != 0) + continue; + kb->callback = name_to_func[i].handler; + kb->context = name_to_func[i].context; + cargs->flag = name_to_func[i].flag; + goto out; + } + kb->callback = kbfunc_exec_cmd; + kb->context = CWM_CONTEXT_NONE; + cargs->flag = 0; + cargs->cmd = xstrdup(cmd); +out: + kb->cargs = cargs; + TAILQ_INSERT_TAIL(&c->keybindq, kb, entry); + return 1; +} + +static void +conf_unbind_key(struct conf *c, struct bind_ctx *unbind) +{ + struct bind_ctx *key = NULL, *keynxt; + + TAILQ_FOREACH_SAFE(key, &c->keybindq, entry, keynxt) { + if ((unbind == NULL) || + ((key->modmask == unbind->modmask) && + (key->press.keysym == unbind->press.keysym))) { + TAILQ_REMOVE(&c->keybindq, key, entry); + free(key->cargs->cmd); + free(key->cargs); + free(key); + } + } +} + +int +conf_bind_mouse(struct conf *c, const char *bind, const char *cmd) +{ + struct bind_ctx *mb; + struct cargs *cargs; + const char *button, *errstr; + unsigned int i; + + if ((strcmp(bind, "all") == 0) && (cmd == NULL)) { + conf_unbind_mouse(c, NULL); + return 1; + } + mb = xmalloc(sizeof(*mb)); + button = conf_bind_mask(bind, &mb->modmask); + mb->press.button = strtonum(button, Button1, Button5, &errstr); + if (errstr) { + warnx("button number is %s: %s", errstr, button); + free(mb); + return 0; + } + conf_unbind_mouse(c, mb); + if (cmd == NULL) { + free(mb); + return 1; + } + cargs = xcalloc(1, sizeof(*cargs)); + for (i = 0; i < nitems(name_to_func); i++) { + if (strcmp(name_to_func[i].tag, cmd) != 0) + continue; + mb->callback = name_to_func[i].handler; + mb->context = name_to_func[i].context; + cargs->flag = name_to_func[i].flag; + goto out; + } + mb->callback = kbfunc_exec_cmd; + mb->context = CWM_CONTEXT_NONE; + cargs->flag = 0; + cargs->cmd = xstrdup(cmd); +out: + mb->cargs = cargs; + TAILQ_INSERT_TAIL(&c->mousebindq, mb, entry); + return 1; +} + +static void +conf_unbind_mouse(struct conf *c, struct bind_ctx *unbind) +{ + struct bind_ctx *mb = NULL, *mbnxt; + + TAILQ_FOREACH_SAFE(mb, &c->mousebindq, entry, mbnxt) { + if ((unbind == NULL) || + ((mb->modmask == unbind->modmask) && + (mb->press.button == unbind->press.button))) { + TAILQ_REMOVE(&c->mousebindq, mb, entry); + free(mb->cargs->cmd); + free(mb->cargs); + free(mb); + } + } +} + +void +conf_grab_kbd(Window win) +{ + struct bind_ctx *kb; + KeyCode kc; + unsigned int i; + + XUngrabKey(X_Dpy, AnyKey, AnyModifier, win); + + TAILQ_FOREACH(kb, &Conf.keybindq, entry) { + kc = XKeysymToKeycode(X_Dpy, kb->press.keysym); + if ((XkbKeycodeToKeysym(X_Dpy, kc, 0, 0) != kb->press.keysym) && + (XkbKeycodeToKeysym(X_Dpy, kc, 0, 1) == kb->press.keysym)) + kb->modmask |= ShiftMask; + + for (i = 0; i < nitems(ignore_mods); i++) + XGrabKey(X_Dpy, kc, (kb->modmask | ignore_mods[i]), win, + True, GrabModeAsync, GrabModeAsync); + } +} + +void +conf_grab_mouse(Window win) +{ + struct bind_ctx *mb; + unsigned int i; + + XUngrabButton(X_Dpy, AnyButton, AnyModifier, win); + + TAILQ_FOREACH(mb, &Conf.mousebindq, entry) { + if (mb->context != CWM_CONTEXT_CC) + continue; + for (i = 0; i < nitems(ignore_mods); i++) { + XGrabButton(X_Dpy, mb->press.button, + (mb->modmask | ignore_mods[i]), win, False, + BUTTONMASK, GrabModeAsync, GrabModeSync, + None, None); + } + } +} diff --git a/example/samurc b/example/samurc new file mode 100644 index 0000000..3873fa1 --- /dev/null +++ b/example/samurc @@ -0,0 +1,12 @@ +bind-key XF86AudioPause "cmus-remote -u" + +gap 4 4 4 4 26 # Leave space for status bar + +sticky yes + +ignore dmenu +ignore dzen2 +ignore lemonbar +ignore polybar +ignore tint2 +ignore winbar diff --git a/example/screenshots/example.jpg b/example/screenshots/example.jpg new file mode 100644 index 0000000..6ba55be Binary files /dev/null and b/example/screenshots/example.jpg differ diff --git a/group.c b/group.c new file mode 100644 index 0000000..089f614 --- /dev/null +++ b/group.c @@ -0,0 +1,377 @@ +/* + * + * Copyright (c) 2004 Andy Adamson + * Copyright (c) 2004,2005 Marius Aamodt Eriksen + * + * 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. + * + * $OpenBSD$ + */ + +#include +#include "queue.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "samu.h" + +static struct group_ctx *group_next(struct group_ctx *); +static struct group_ctx *group_prev(struct group_ctx *); +static void group_restack(struct group_ctx *); +static void group_set_active(struct group_ctx *); + +void +group_assign(struct group_ctx *gc, struct client_ctx *cc) +{ + if ((gc != NULL) && (gc->num == 0)) + gc = NULL; + + cc->gc = gc; + + xu_ewmh_set_net_wm_desktop(cc); +} + +void +group_hide(struct group_ctx *gc) +{ + struct screen_ctx *sc = gc->sc; + struct client_ctx *cc; + + screen_updatestackingorder(gc->sc); + + TAILQ_FOREACH(cc, &sc->clientq, entry) { + if (cc->gc != gc) + continue; + if (!(cc->flags & CLIENT_STICKY) && + !(cc->flags & CLIENT_HIDDEN)) + client_hide(cc); + } +} + +void +group_show(struct group_ctx *gc) +{ + struct screen_ctx *sc = gc->sc; + struct client_ctx *cc; + + TAILQ_FOREACH(cc, &sc->clientq, entry) { + if (cc->gc != gc) + continue; + if (!(cc->flags & CLIENT_STICKY) && + (cc->flags & CLIENT_HIDDEN)) + client_show(cc); + } + group_restack(gc); + group_set_active(gc); +} + +static void +group_restack(struct group_ctx *gc) +{ + struct screen_ctx *sc = gc->sc; + struct client_ctx *cc; + Window *winlist; + int i, lastempty = -1; + int nwins = 0, highstack = 0; + + TAILQ_FOREACH(cc, &sc->clientq, entry) { + if (cc->gc != gc) + continue; + if (cc->stackingorder > highstack) + highstack = cc->stackingorder; + } + winlist = xreallocarray(NULL, (highstack + 1), sizeof(*winlist)); + + /* Invert the stacking order for XRestackWindows(). */ + TAILQ_FOREACH(cc, &sc->clientq, entry) { + if (cc->gc != gc) + continue; + winlist[highstack - cc->stackingorder] = cc->win; + nwins++; + } + + /* Un-sparseify */ + for (i = 0; i <= highstack; i++) { + if (!winlist[i] && lastempty == -1) + lastempty = i; + else if (winlist[i] && lastempty != -1) { + winlist[lastempty] = winlist[i]; + if (++lastempty == i) + lastempty = -1; + } + } + + XRestackWindows(X_Dpy, winlist, nwins); + free(winlist); +} + +void +group_init(struct screen_ctx *sc, int num, const char *name) +{ + struct group_ctx *gc; + + gc = xmalloc(sizeof(*gc)); + gc->sc = sc; + gc->name = xstrdup(name); + gc->num = num; + TAILQ_INSERT_TAIL(&sc->groupq, gc, entry); + + if (num == 1) + group_set_active(gc); +} + +void +group_set_active(struct group_ctx *gc) +{ + struct screen_ctx *sc = gc->sc; + + sc->group_active = gc; + + xu_ewmh_net_current_desktop(sc); +} + +void +group_movetogroup(struct client_ctx *cc, int idx) +{ + struct screen_ctx *sc = cc->sc; + struct group_ctx *gc; + + TAILQ_FOREACH(gc, &sc->groupq, entry) { + if (gc->num == idx) { + if (cc->gc == gc) + return; + if (gc->num != 0 && group_holds_only_hidden(gc)) + client_hide(cc); + group_assign(gc, cc); + } + } +} + +void +group_toggle_membership(struct client_ctx *cc) +{ + struct screen_ctx *sc = cc->sc; + struct group_ctx *gc = sc->group_active; + + if (cc->gc == gc) { + group_assign(NULL, cc); + cc->flags |= CLIENT_UNGROUP; + } else { + group_assign(gc, cc); + cc->flags |= CLIENT_GROUP; + } + client_draw_border(cc); +} + +int +group_holds_only_sticky(struct group_ctx *gc) +{ + struct screen_ctx *sc = gc->sc; + struct client_ctx *cc; + + TAILQ_FOREACH(cc, &sc->clientq, entry) { + if (cc->gc != gc) + continue; + if (!(cc->flags & CLIENT_STICKY)) + return 0; + } + return 1; +} + +int +group_holds_only_hidden(struct group_ctx *gc) +{ + struct screen_ctx *sc = gc->sc; + struct client_ctx *cc; + + TAILQ_FOREACH(cc, &sc->clientq, entry) { + if (cc->gc != gc) + continue; + if (!(cc->flags & (CLIENT_HIDDEN | CLIENT_STICKY))) + return 0; + } + return 1; +} + +void +group_only(struct screen_ctx *sc, int idx) +{ + struct group_ctx *gc; + + if (sc->group_last != sc->group_active) + sc->group_last = sc->group_active; + + TAILQ_FOREACH(gc, &sc->groupq, entry) { + if (gc->num == idx) + group_show(gc); + else + group_hide(gc); + } +} + +void +group_toggle(struct screen_ctx *sc, int idx) +{ + struct group_ctx *gc; + + TAILQ_FOREACH(gc, &sc->groupq, entry) { + if (gc->num == idx) { + if (group_holds_only_hidden(gc)) + group_show(gc); + else + group_hide(gc); + } + } +} + +void +group_toggle_all(struct screen_ctx *sc) +{ + struct group_ctx *gc; + + TAILQ_FOREACH(gc, &sc->groupq, entry) { + if (sc->hideall) + group_show(gc); + else + group_hide(gc); + } + sc->hideall = !sc->hideall; +} + +void +group_close(struct screen_ctx *sc, int idx) +{ + struct group_ctx *gc; + struct client_ctx *cc; + + TAILQ_FOREACH(gc, &sc->groupq, entry) { + if (gc->num == idx) { + TAILQ_FOREACH(cc, &sc->clientq, entry) { + if (cc->gc != gc) + continue; + client_close(cc); + } + } + } +} + +void +group_cycle(struct screen_ctx *sc, int flags) +{ + struct group_ctx *newgc, *oldgc, *showgroup = NULL; + + oldgc = sc->group_active; + + newgc = oldgc; + for (;;) { + newgc = (flags & CWM_CYCLE_REVERSE) ? group_prev(newgc) : + group_next(newgc); + + if (newgc == oldgc) + break; + + if (!group_holds_only_sticky(newgc) && showgroup == NULL) + showgroup = newgc; + else if (!group_holds_only_hidden(newgc)) + group_hide(newgc); + } + if (showgroup == NULL) + return; + + group_hide(oldgc); + + if (group_holds_only_hidden(showgroup)) + group_show(showgroup); + else + group_set_active(showgroup); +} + +static struct group_ctx * +group_next(struct group_ctx *gc) +{ + struct screen_ctx *sc = gc->sc; + struct group_ctx *newgc; + + return(((newgc = TAILQ_NEXT(gc, entry)) != NULL) ? + newgc : TAILQ_FIRST(&sc->groupq)); +} + +static struct group_ctx * +group_prev(struct group_ctx *gc) +{ + struct screen_ctx *sc = gc->sc; + struct group_ctx *newgc; + + return(((newgc = TAILQ_PREV(gc, group_q, entry)) != NULL) ? + newgc : TAILQ_LAST(&sc->groupq, group_q)); +} + +int +group_restore(struct client_ctx *cc) +{ + struct screen_ctx *sc = cc->sc; + struct group_ctx *gc; + int num; + long grpnum; + + if (!xu_ewmh_get_net_wm_desktop(cc, &grpnum)) + return 0; + + num = (grpnum == -1) ? 0 : grpnum; + num = MIN(num, (Conf.ngroups - 1)); + + TAILQ_FOREACH(gc, &sc->groupq, entry) { + if (gc->num == num) { + group_assign(gc, cc); + return 1; + } + } + return 0; +} + +int +group_autogroup(struct client_ctx *cc) +{ + struct screen_ctx *sc = cc->sc; + struct autogroup *ag; + struct group_ctx *gc; + int num = -1, both_match = 0; + + if (cc->res_class == NULL || cc->res_name == NULL) + return 0; + + TAILQ_FOREACH(ag, &Conf.autogroupq, entry) { + if (strcmp(ag->class, cc->res_class) == 0) { + if ((ag->name != NULL) && + (strcmp(ag->name, cc->res_name) == 0)) { + num = ag->num; + both_match = 1; + } else if (ag->name == NULL && !both_match) + num = ag->num; + } + } + + TAILQ_FOREACH(gc, &sc->groupq, entry) { + if (gc->num == num) { + group_assign(gc, cc); + return 1; + } + } + return 0; +} diff --git a/kbfunc.c b/kbfunc.c new file mode 100644 index 0000000..a538d29 --- /dev/null +++ b/kbfunc.c @@ -0,0 +1,812 @@ +/* + * + * Copyright (c) 2004 Martin Murray + * + * 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. + * + * $OpenBSD$ + */ + +/* For FreeBSD. */ +#define _WITH_GETLINE + +#include +#include "queue.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "samu.h" + +#define HASH_MARKER "|1|" + +extern sig_atomic_t cwm_status; + +static void kbfunc_amount(int, int, int *, int *); +static void kbfunc_client_move_kb(void *, struct cargs *); +static void kbfunc_client_move_mb(void *, struct cargs *); +static void kbfunc_client_resize_kb(void *, struct cargs *); +static void kbfunc_client_resize_mb(void *, struct cargs *); +static void kbfunc_client_resize_mb_original(struct screen_ctx *, struct client_ctx *); +static void kbfunc_client_resize_mb_corners(struct screen_ctx *, struct client_ctx *); + +void +kbfunc_cwm_status(void *ctx, struct cargs *cargs) +{ + cwm_status = cargs->flag; +} + +static void +kbfunc_amount(int flags, int amt, int *mx, int *my) +{ +#define CWM_FACTOR 10 + + if (flags & CWM_BIGAMOUNT) + amt *= CWM_FACTOR; + + switch (flags & (CWM_UP | CWM_DOWN | CWM_LEFT | CWM_RIGHT)) { + case CWM_UP: + *my -= amt; + break; + case CWM_DOWN: + *my += amt; + break; + case CWM_RIGHT: + *mx += amt; + break; + case CWM_LEFT: + *mx -= amt; + break; + } +} + +void +kbfunc_ptrmove(void *ctx, struct cargs *cargs) +{ + struct screen_ctx *sc = ctx; + int x, y; + int mx = 0, my = 0; + + kbfunc_amount(cargs->flag, Conf.mamount, &mx, &my); + + xu_ptr_get(sc->rootwin, &x, &y); + xu_ptr_set(sc->rootwin, x + mx, y + my); +} + +void +kbfunc_client_move(void *ctx, struct cargs *cargs) +{ + if (cargs->xev == CWM_XEV_BTN) + kbfunc_client_move_mb(ctx, cargs); + else + kbfunc_client_move_kb(ctx, cargs); +} + +void +kbfunc_client_resize(void *ctx, struct cargs *cargs) +{ + if (cargs->xev == CWM_XEV_BTN) + kbfunc_client_resize_mb(ctx, cargs); + else + kbfunc_client_resize_kb(ctx, cargs); +} + +static void +kbfunc_client_move_kb(void *ctx, struct cargs *cargs) +{ + struct client_ctx *cc = ctx; + struct screen_ctx *sc = cc->sc; + struct geom area; + int mx = 0, my = 0; + + if (cc->flags & CLIENT_FREEZE) + return; + + kbfunc_amount(cargs->flag, Conf.mamount, &mx, &my); + + cc->geom.x += mx; + if (cc->geom.x < -(cc->geom.w + cc->bwidth - 1)) + cc->geom.x = -(cc->geom.w + cc->bwidth - 1); + if (cc->geom.x > (sc->view.w - cc->bwidth - 1)) + cc->geom.x = sc->view.w - cc->bwidth - 1; + cc->geom.y += my; + if (cc->geom.y < -(cc->geom.h + cc->bwidth - 1)) + cc->geom.y = -(cc->geom.h + cc->bwidth - 1); + if (cc->geom.y > (sc->view.h - cc->bwidth - 1)) + cc->geom.y = sc->view.h - cc->bwidth - 1; + + area = screen_area(sc, + cc->geom.x + cc->geom.w / 2, + cc->geom.y + cc->geom.h / 2, 1); + cc->geom.x += client_snapcalc(cc->geom.x, + cc->geom.x + cc->geom.w + (cc->bwidth * 2), + area.x, area.x + area.w, sc->snapdist); + cc->geom.y += client_snapcalc(cc->geom.y, + cc->geom.y + cc->geom.h + (cc->bwidth * 2), + area.y, area.y + area.h, sc->snapdist); + + client_move(cc); + client_ptr_inbound(cc, 1); + XSync(X_Dpy, True); +} + +static void +kbfunc_client_move_mb(void *ctx, struct cargs *cargs) +{ + struct client_ctx *cc = ctx; + XEvent ev; + Time ltime = 0; + struct screen_ctx *sc = cc->sc; + struct geom area; + int move = 1; + + client_raise(cc); + + if (cc->flags & CLIENT_FREEZE) + return; + + client_ptr_inbound(cc, 1); + + if (XGrabPointer(X_Dpy, cc->win, False, MOUSEMASK, + GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_MOVE], + CurrentTime) != GrabSuccess) + return; + + screen_prop_win_create(sc, cc->win); + screen_prop_win_draw(sc, "%+5d%+5d", cc->geom.x, cc->geom.y); + while (move) { + XMaskEvent(X_Dpy, MOUSEMASK, &ev); + switch (ev.type) { + case MotionNotify: + /* not more than 60 times / second */ + if ((ev.xmotion.time - ltime) <= (1000 / 60)) + continue; + ltime = ev.xmotion.time; + + cc->geom.x = ev.xmotion.x_root - cc->ptr.x - cc->bwidth; + cc->geom.y = ev.xmotion.y_root - cc->ptr.y - cc->bwidth; + + area = screen_area(sc, + cc->geom.x + cc->geom.w / 2, + cc->geom.y + cc->geom.h / 2, 1); + cc->geom.x += client_snapcalc(cc->geom.x, + cc->geom.x + cc->geom.w + (cc->bwidth * 2), + area.x, area.x + area.w, sc->snapdist); + cc->geom.y += client_snapcalc(cc->geom.y, + cc->geom.y + cc->geom.h + (cc->bwidth * 2), + area.y, area.y + area.h, sc->snapdist); + client_move(cc); + screen_prop_win_draw(sc, + "%+5d%+5d", cc->geom.x, cc->geom.y); + break; + case ButtonRelease: + move = 0; + break; + } + } + if (ltime) + client_move(cc); + screen_prop_win_destroy(sc); + XUngrabPointer(X_Dpy, CurrentTime); +} + +static void +kbfunc_client_resize_kb(void *ctx, struct cargs *cargs) +{ + struct client_ctx *cc = ctx; + int mx = 0, my = 0; + int amt = 1; + + if (cc->flags & CLIENT_FREEZE) + return; + + if (!(cc->hint.flags & PResizeInc)) + amt = Conf.mamount; + + kbfunc_amount(cargs->flag, amt, &mx, &my); + + if ((cc->geom.w += mx * cc->hint.incw) < cc->hint.minw) + cc->geom.w = cc->hint.minw; + if ((cc->geom.h += my * cc->hint.inch) < cc->hint.minh) + cc->geom.h = cc->hint.minh; + if (cc->geom.x + cc->geom.w + cc->bwidth - 1 < 0) + cc->geom.x = -(cc->geom.w + cc->bwidth - 1); + if (cc->geom.y + cc->geom.h + cc->bwidth - 1 < 0) + cc->geom.y = -(cc->geom.h + cc->bwidth - 1); + + client_resize(cc, 1); + client_ptr_inbound(cc, 1); + XSync(X_Dpy, True); +} + +static void +kbfunc_client_resize_mb(void *ctx, struct cargs *cargs) +{ + struct client_ctx *cc = ctx; + struct screen_ctx *sc = cc->sc; + + if (cc->flags & CLIENT_FREEZE) + return; + + client_raise(cc); + client_ptr_save(cc); + + if (Conf.resizeallcorners) + kbfunc_client_resize_mb_corners(sc, cc); + else + kbfunc_client_resize_mb_original(sc, cc); + + /* Make sure the pointer stays within the window. */ + client_ptr_inbound(cc, 0); +} + +static void +kbfunc_client_resize_mb_original(struct screen_ctx *sc, struct client_ctx *cc) +{ + int resize = 1; + Time ltime = 0; + XEvent ev; + + xu_ptr_set(cc->win, cc->geom.w, cc->geom.h); + + if (XGrabPointer(X_Dpy, cc->win, False, MOUSEMASK, + GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_RESIZE], + CurrentTime) != GrabSuccess) + return; + + screen_prop_win_create(sc, cc->win); + screen_prop_win_draw(sc, "%4d x %-4d", cc->dim.w, cc->dim.h); + while (resize) { + XMaskEvent(X_Dpy, MOUSEMASK, &ev); + switch (ev.type) { + case MotionNotify: + /* not more than 60 times / second */ + if ((ev.xmotion.time - ltime) <= (1000 / 60)) + continue; + ltime = ev.xmotion.time; + + cc->geom.w = ev.xmotion.x; + cc->geom.h = ev.xmotion.y; + client_apply_sizehints(cc); + client_resize(cc, 1); + screen_prop_win_draw(sc, + "%4d x %-4d", cc->dim.w, cc->dim.h); + break; + case ButtonRelease: + resize = 0; + break; + } + } + if (ltime) + client_resize(cc, 1); + screen_prop_win_destroy(sc); + XUngrabPointer(X_Dpy, CurrentTime); +} + +static void +kbfunc_client_resize_mb_corners(struct screen_ctx *sc, struct client_ctx *cc) +{ + Cursor cursor; + int resize = 1, orig_h, orig_w, orig_x, orig_y, x, y; + Time ltime = 0; + XEvent ev; + + xu_ptr_get(sc->rootwin, &x, &y); + + if (x < cc->geom.x + cc->geom.w / 2) { + if (y < cc->geom.y + cc->geom.h / 2) + cursor = Conf.cursor[CF_RESIZE_NW]; + else + cursor = Conf.cursor[CF_RESIZE_SW]; + } else { + if (y < cc->geom.y + cc->geom.h / 2) + cursor = Conf.cursor[CF_RESIZE_NE]; + else + cursor = Conf.cursor[CF_RESIZE_SE]; + } + + if (XGrabPointer(X_Dpy, cc->win, False, MOUSEMASK, + GrabModeAsync, GrabModeAsync, None, cursor, + CurrentTime) != GrabSuccess) + return; + + orig_x = cc->geom.x; + orig_y = cc->geom.y; + orig_w = cc->geom.w; + orig_h = cc->geom.h; + orig_x = cc->geom.x; + orig_y = cc->geom.y; + orig_w = cc->geom.w; + orig_h = cc->geom.h; + + screen_prop_win_create(sc, cc->win); + screen_prop_win_draw(sc, "%4d x %-4d", cc->dim.w, cc->dim.h); + + while (resize) { + XMaskEvent(X_Dpy, MOUSEMASK, &ev); + switch (ev.type) { + case MotionNotify: + /* not more than 60 times / second */ + if ((ev.xmotion.time - ltime) <= (1000 / 60)) + continue; + ltime = ev.xmotion.time; + + if (x - orig_x < orig_w / 2) { + if (y - orig_y < orig_h / 2) { + cc->geom.x = orig_x + ev.xmotion.x_root - x; + cc->geom.w = orig_w - ev.xmotion.x_root + x; + cc->geom.y = orig_y + ev.xmotion.y_root - y; + cc->geom.h = orig_h - ev.xmotion.y_root + y; + + client_apply_sizehints(cc); + + cc->geom.x -= cc->geom.x + cc->geom.w - orig_x - orig_w; + cc->geom.y -= cc->geom.y + cc->geom.h - orig_y - orig_h; + } else { + cc->geom.x = orig_x + ev.xmotion.x_root - x; + cc->geom.w = orig_w - ev.xmotion.x_root + x; + cc->geom.h = orig_h + ev.xmotion.y_root - y; + + client_apply_sizehints(cc); + + cc->geom.x -= cc->geom.x + cc->geom.w - orig_x - orig_w; + } + } else { + if (y - orig_y < orig_h / 2) { + cc->geom.w = orig_w + ev.xmotion.x_root - x; + cc->geom.y = orig_y + ev.xmotion.y_root - y; + cc->geom.h = orig_h - ev.xmotion.y_root + y; + + client_apply_sizehints(cc); + + cc->geom.y -= cc->geom.y + cc->geom.h - orig_y - orig_h; + } else { + cc->geom.w = orig_w + ev.xmotion.x_root - x; + cc->geom.h = orig_h + ev.xmotion.y_root - y; + + client_apply_sizehints(cc); + } + } + + client_move(cc); + client_resize(cc, 0); + screen_prop_win_draw(sc, + "%4d x %-4d", cc->dim.w, cc->dim.h); + break; + case ButtonRelease: + resize = 0; + break; + } + } + + if (ltime) + client_resize(cc, 1); + screen_prop_win_destroy(sc); + XUngrabPointer(X_Dpy, CurrentTime); + } + +void +kbfunc_client_snap(void *ctx, struct cargs *cargs) +{ + struct client_ctx *cc = ctx; + struct screen_ctx *sc = cc->sc; + struct geom area; + int flags; + + XRaiseWindow(X_Dpy, cc->win); + + area = screen_area(sc, + cc->geom.x + cc->geom.w / 2, + cc->geom.y + cc->geom.h / 2, 1); + + flags = cargs->flag; + if (flags) { + while (flags) { + if (flags & CWM_UP) { + cc->geom.y = area.y; + flags &= ~CWM_UP; + } + if (flags & CWM_LEFT) { + cc->geom.x = area.x; + flags &= ~CWM_LEFT; + } + if (flags & CWM_RIGHT) { + cc->geom.x = area.x + area.w - cc->geom.w - (cc->bwidth * 2); + flags &= ~CWM_RIGHT; + } + if (flags & CWM_DOWN) { + cc->geom.y = area.y + area.h - cc->geom.h - (cc->bwidth * 2); + flags &= ~CWM_DOWN; + } + } + } else { + cc->geom.x = area.x + (area.w - cc->geom.w) / 2 - cc->bwidth; + cc->geom.y = area.y + (area.h - cc->geom.h) / 2 - cc->bwidth; + } + + client_move(cc); + client_ptr_inbound(cc, 1); +} + +void +kbfunc_client_tile(void *ctx, struct cargs *cargs) +{ + struct client_ctx *cc = ctx; + struct screen_ctx *sc = cc->sc; + struct geom area; + int flags; + + XRaiseWindow(X_Dpy, cc->win); + + area = screen_area(sc, + cc->geom.x + cc->geom.w / 2, + cc->geom.y + cc->geom.h / 2, 1); + + flags = cargs->flag; + + if (flags & CWM_UP) { + cc->geom.y = area.y; + cc->geom.h = area.h / 2 - (cc->bwidth * 2) - cc->tgap; + } else if (flags & CWM_DOWN) { + cc->geom.y = area.y + area.h / 2; + cc->geom.h = area.h / 2 - (cc->bwidth * 2); + } else { + cc->geom.y = area.y; + cc->geom.h = area.h - (cc->bwidth * 2); + /*cc->geom.y = area.y;*/ + /*cc->geom.h = area.h - (cc->bwidth * 2) - (cc->tgap / 2);*/ + } + + if (flags & CWM_LEFT) { + cc->geom.x = area.x; + cc->geom.w = area.w / 2 - (cc->bwidth * 2) - cc->tgap; + } else if (flags & CWM_RIGHT) { + cc->geom.x = area.x + area.w / 2; + cc->geom.w = area.w / 2 - (cc->bwidth * 2); + } else { + cc->geom.x = area.x; + cc->geom.w = area.w - (cc->bwidth * 2) - (cc->tgap / 2); + } + + client_resize(cc, 1); + client_move(cc); + client_ptr_inbound(cc, 1); + XSync(X_Dpy, True); +} + +void +kbfunc_client_close(void *ctx, struct cargs *cargs) +{ + client_close(ctx); +} + +void +kbfunc_client_lower(void *ctx, struct cargs *cargs) +{ + client_ptr_save(ctx); + client_lower(ctx); +} + +void +kbfunc_client_raise(void *ctx, struct cargs *cargs) +{ + client_raise(ctx); +} + +void +kbfunc_client_center (void *ctx, struct cargs *cargs) +{ + client_center(ctx); +} + +void +kbfunc_client_grow (void *ctx, struct cargs *cargs) +{ + client_grow(ctx); +} + +void +kbfunc_client_shrink (void *ctx, struct cargs *cargs) +{ + client_shrink(ctx); +} + +void +kbfunc_client_hide(void *ctx, struct cargs *cargs) +{ + client_hide(ctx); +} + +void +kbfunc_client_toggle_freeze(void *ctx, struct cargs *cargs) +{ + client_toggle_freeze(ctx); +} + +void +kbfunc_client_toggle_sticky(void *ctx, struct cargs *cargs) +{ + client_toggle_sticky(ctx); +} + +void +kbfunc_client_toggle_fullscreen(void *ctx, struct cargs *cargs) +{ + client_toggle_fullscreen(ctx); +} + +void +kbfunc_client_toggle_maximize(void *ctx, struct cargs *cargs) +{ + client_toggle_maximize(ctx); +} + +void +kbfunc_client_toggle_hmaximize(void *ctx, struct cargs *cargs) +{ + client_toggle_hmaximize(ctx); +} + +void +kbfunc_client_toggle_vmaximize(void *ctx, struct cargs *cargs) +{ + client_toggle_vmaximize(ctx); +} + +void +kbfunc_client_htile(void *ctx, struct cargs *cargs) +{ + client_htile(ctx); +} + +void +kbfunc_client_vtile(void *ctx, struct cargs *cargs) +{ + client_vtile(ctx); +} + +void +kbfunc_client_cycle(void *ctx, struct cargs *cargs) +{ + struct screen_ctx *sc = ctx; + struct client_ctx *newcc, *oldcc, *prevcc; + int again = 1, flags = cargs->flag; + + /* For X apps that ignore/steal events. */ + if (cargs->xev == CWM_XEV_KEY) + XGrabKeyboard(X_Dpy, sc->rootwin, True, + GrabModeAsync, GrabModeAsync, CurrentTime); + + if (TAILQ_EMPTY(&sc->clientq)) + return; + + prevcc = TAILQ_FIRST(&sc->clientq); + oldcc = client_current(sc); + if (oldcc == NULL) + oldcc = (flags & CWM_CYCLE_REVERSE) ? + TAILQ_LAST(&sc->clientq, client_q) : + TAILQ_FIRST(&sc->clientq); + + newcc = oldcc; + while (again) { + again = 0; + + newcc = (flags & CWM_CYCLE_REVERSE) ? client_prev(newcc) : + client_next(newcc); + + /* Only cycle visible and non-ignored windows. */ + if ((newcc->flags & (CLIENT_SKIP_CYCLE)) || + ((flags & CWM_CYCLE_INGROUP) && + (newcc->gc != oldcc->gc))) + again = 1; + + /* Is oldcc the only non-hidden window? */ + if (newcc == oldcc) { + if (again) + return; /* No windows visible. */ + break; + } + } + + /* Reset when cycling mod is released. XXX I hate this hack */ + sc->cycling = 1; + client_ptr_save(oldcc); + client_raise(prevcc); + client_raise(newcc); + if (!client_inbound(newcc, newcc->ptr.x, newcc->ptr.y)) { + newcc->ptr.x = newcc->geom.w / 2; + newcc->ptr.y = newcc->geom.h / 2; + } + client_ptr_warp(newcc); + + /* When no client is active, warp pointer to last active. */ + if (oldcc->flags & (CLIENT_ACTIVE)) + client_ptr_warp(newcc); + else if (oldcc->flags & (CLIENT_SKIP_CYCLE)) + client_ptr_warp(newcc); + else { + client_raise(oldcc); + client_ptr_warp(oldcc); + } +} + +void +kbfunc_client_toggle_group(void *ctx, struct cargs *cargs) +{ + struct client_ctx *cc = ctx; + + /* For X apps that ignore/steal events. */ + if (cargs->xev == CWM_XEV_KEY) + XGrabKeyboard(X_Dpy, cc->win, True, + GrabModeAsync, GrabModeAsync, CurrentTime); + + group_toggle_membership(cc); +} + +void +kbfunc_client_movetogroup(void *ctx, struct cargs *cargs) +{ + group_movetogroup(ctx, cargs->flag); +} + +void +kbfunc_group_only(void *ctx, struct cargs *cargs) +{ + group_only(ctx, cargs->flag); +} + +void +kbfunc_group_last(void *ctx, struct cargs *cargs) +{ + struct screen_ctx *sc = ctx; + + group_only(ctx, sc->group_last->num); +} + +void +kbfunc_group_toggle(void *ctx, struct cargs *cargs) +{ + group_toggle(ctx, cargs->flag); +} + +void +kbfunc_group_toggle_all(void *ctx, struct cargs *cargs) +{ + group_toggle_all(ctx); +} + +void +kbfunc_group_close(void *ctx, struct cargs *cargs) +{ + group_close(ctx, cargs->flag); +} + +void +kbfunc_group_cycle(void *ctx, struct cargs *cargs) +{ + group_cycle(ctx, cargs->flag); +} + +void +kbfunc_exec_cmd(void *ctx, struct cargs *cargs) +{ + u_spawn(cargs->cmd); +} + +void +kbfunc_exec_term(void *ctx, struct cargs *cargs) +{ + struct cmd_ctx *cmd; + + TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { + if (strcmp(cmd->name, "term") == 0) + u_spawn(cmd->path); + } +} + +void +kbfunc_exec_lock(void *ctx, struct cargs *cargs) +{ + struct cmd_ctx *cmd; + + TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { + if (strcmp(cmd->name, "lock") == 0) + u_spawn(cmd->path); + } +} + +void +kbfunc_exec_dmenu(void *ctx, struct cargs *cargs) +{ + struct cmd_ctx *cmd; + + TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { + if (strcmp(cmd->name, "dmenu") == 0) + u_spawn(cmd->path); + } +} + +void +kbfunc_exec_dextra(void *ctx, struct cargs *cargs) +{ + struct cmd_ctx *cmd; + + TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { + if (strcmp(cmd->name, "dextra") == 0) + u_spawn(cmd->path); + } +} + +void +kbfunc_exec_dprint(void *ctx, struct cargs *cargs) +{ + struct cmd_ctx *cmd; + + TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { + if (strcmp(cmd->name, "dprint") == 0) + u_spawn(cmd->path); + } +} + +void +kbfunc_exec_teleporter(void *ctx, struct cargs *cargs) +{ + struct cmd_ctx *cmd; + + TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { + if (strcmp(cmd->name, "teleporter") == 0) + u_spawn(cmd->path); + } +} + +void +kbfunc_exec_vol_up(void *ctx, struct cargs *cargs) +{ + struct cmd_ctx *cmd; + + TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { + if (strcmp(cmd->name, "vol-up") == 0) + u_spawn(cmd->path); + } +} + +void +kbfunc_exec_vol_down(void *ctx, struct cargs *cargs) +{ + struct cmd_ctx *cmd; + + TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { + if (strcmp(cmd->name, "vol-down") == 0) + u_spawn(cmd->path); + } +} + +void +kbfunc_exec_vol_mute(void *ctx, struct cargs *cargs) +{ + struct cmd_ctx *cmd; + + TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { + if (strcmp(cmd->name, "vol-mute") == 0) + u_spawn(cmd->path); + } +} diff --git a/parse.y b/parse.y new file mode 100644 index 0000000..b4c64cb --- /dev/null +++ b/parse.y @@ -0,0 +1,645 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2002, 2003, 2004 Henning Brauer + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. + * Copyright (c) 2001 Theo de Raadt. 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 +#include "queue.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "samu.h" + +#define YYSTYPE_IS_DECLARED + +TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); +static struct file { + TAILQ_ENTRY(file) entry; + FILE *stream; + char *name; + int lineno; + int errors; +} *file, *topfile; +struct file *pushfile(const char *, FILE *); +int popfile(void); +int yyparse(void); +int yylex(void); +int yyerror(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) + __attribute__((__nonnull__ (1))); +int kw_cmp(const void *, const void *); +int lookup(char *); +int lgetc(int); +int lungetc(int); +int findeol(void); + +static struct conf *conf; + +typedef struct { + union { + int64_t number; + char *string; + } v; + int lineno; +} YYSTYPE; + +%} + +%token BINDKEY UNBINDKEY BINDMOUSE UNBINDMOUSE +%token FONTNAME STICKY GAP +%token AUTOGROUP COMMAND IGNORE WM +%token YES NO BORDERWIDTH MOVEAMOUNT HTILE VTILE +%token TILEGAP +%token COLOR SNAPDIST +%token ACTIVEBORDER INACTIVEBORDER URGENCYBORDER +%token GROUPBORDER UNGROUPBORDER +%token MENUBG MENUFG +%token FONTCOLOR FONTSELCOLOR +%token ERROR +%token RESIZEALLCORNERS +%token STRING +%token NUMBER +%type yesno +%type string +%% + +grammar : /* empty */ + | grammar '\n' + | grammar main '\n' + | grammar color '\n' + | grammar error '\n' { file->errors++; } + ; + +string : string STRING { + if (asprintf(&$$, "%s %s", $1, $2) == -1) { + free($1); + free($2); + yyerror("string: asprintf"); + YYERROR; + } + free($1); + free($2); + } + | STRING + ; + +yesno : YES { $$ = 1; } + | NO { $$ = 0; } + ; + +main : FONTNAME STRING { + free(conf->font); + conf->font = $2; + } + | STICKY yesno { + conf->stickygroups = $2; + } + | BORDERWIDTH NUMBER { + if ($2 < 0 || $2 > INT_MAX) { + yyerror("invalid borderwidth"); + YYERROR; + } + conf->bwidth = $2; + } + | TILEGAP NUMBER { + if ($2 < 0 || $2 > INT_MAX) { + yyerror("invalid tilegap"); + YYERROR; + } + conf->tgap = $2; + } + | HTILE NUMBER { + if ($2 < 0 || $2 > 99) { + yyerror("invalid htile percent"); + YYERROR; + } + conf->htile = $2; + } + | VTILE NUMBER { + if ($2 < 0 || $2 > 99) { + yyerror("invalid vtile percent"); + YYERROR; + } + conf->vtile = $2; + } + | MOVEAMOUNT NUMBER { + if ($2 < 0 || $2 > INT_MAX) { + yyerror("invalid movemount"); + YYERROR; + } + conf->mamount = $2; + } + | SNAPDIST NUMBER { + if ($2 < 0 || $2 > INT_MAX) { + yyerror("invalid snapdist"); + YYERROR; + } + conf->snapdist = $2; + } + | COMMAND STRING string { + if (strlen($3) >= PATH_MAX) { + yyerror("%s command path too long", $2); + free($2); + free($3); + YYERROR; + } + conf_cmd_add(conf, $2, $3); + free($2); + free($3); + } + | WM STRING string { + if (strlen($3) >= PATH_MAX) { + yyerror("%s wm path too long", $2); + free($2); + free($3); + YYERROR; + } + conf_wm_add(conf, $2, $3); + free($2); + free($3); + } + | AUTOGROUP NUMBER STRING { + if ($2 < 0 || $2 > 9) { + yyerror("invalid autogroup"); + free($3); + YYERROR; + } + conf_autogroup(conf, $2, NULL, $3); + free($3); + } + | AUTOGROUP NUMBER STRING ',' STRING { + if ($2 < 0 || $2 > 9) { + yyerror("invalid autogroup"); + free($3); + free($5); + YYERROR; + } + conf_autogroup(conf, $2, $3, $5); + free($3); + free($5); + } + | IGNORE STRING { + conf_ignore(conf, $2); + free($2); + } + | GAP NUMBER NUMBER NUMBER NUMBER NUMBER { + if ($2 < 0 || $2 > INT_MAX || + $3 < 0 || $3 > INT_MAX || + $4 < 0 || $4 > INT_MAX || + $5 < 0 || $5 > INT_MAX) { + yyerror("invalid gap"); + YYERROR; + } + conf->gap.top = $2; + conf->gap.bottom = $3; + conf->gap.left = $4; + conf->gap.right = $5; + conf->gap.bar = $6; + } + | BINDKEY STRING string { + if (!conf_bind_key(conf, $2, $3)) { + yyerror("invalid bind-key: %s %s", $2, $3); + free($2); + free($3); + YYERROR; + } + free($2); + free($3); + } + | UNBINDKEY STRING { + if (!conf_bind_key(conf, $2, NULL)) { + yyerror("invalid unbind-key: %s", $2); + free($2); + YYERROR; + } + free($2); + } + | BINDMOUSE STRING string { + if (!conf_bind_mouse(conf, $2, $3)) { + yyerror("invalid bind-mouse: %s %s", $2, $3); + free($2); + free($3); + YYERROR; + } + free($2); + free($3); + } + | UNBINDMOUSE STRING { + if (!conf_bind_mouse(conf, $2, NULL)) { + yyerror("invalid unbind-mouse: %s", $2); + free($2); + YYERROR; + } + free($2); + } + | RESIZEALLCORNERS yesno { + conf->resizeallcorners = $2; + } + ; + +color : COLOR colors + ; + +colors : ACTIVEBORDER STRING { + free(conf->color[CWM_COLOR_BORDER_ACTIVE]); + conf->color[CWM_COLOR_BORDER_ACTIVE] = $2; + } + | INACTIVEBORDER STRING { + free(conf->color[CWM_COLOR_BORDER_INACTIVE]); + conf->color[CWM_COLOR_BORDER_INACTIVE] = $2; + } + | URGENCYBORDER STRING { + free(conf->color[CWM_COLOR_BORDER_URGENCY]); + conf->color[CWM_COLOR_BORDER_URGENCY] = $2; + } + | GROUPBORDER STRING { + free(conf->color[CWM_COLOR_BORDER_GROUP]); + conf->color[CWM_COLOR_BORDER_GROUP] = $2; + } + | UNGROUPBORDER STRING { + free(conf->color[CWM_COLOR_BORDER_UNGROUP]); + conf->color[CWM_COLOR_BORDER_UNGROUP] = $2; + } + | MENUBG STRING { + free(conf->color[CWM_COLOR_MENU_BG]); + conf->color[CWM_COLOR_MENU_BG] = $2; + } + | MENUFG STRING { + free(conf->color[CWM_COLOR_MENU_FG]); + conf->color[CWM_COLOR_MENU_FG] = $2; + } + | FONTCOLOR STRING { + free(conf->color[CWM_COLOR_MENU_FONT]); + conf->color[CWM_COLOR_MENU_FONT] = $2; + } + | FONTSELCOLOR STRING { + free(conf->color[CWM_COLOR_MENU_FONT_SEL]); + conf->color[CWM_COLOR_MENU_FONT_SEL] = $2; + } + ; +%% + +struct keywords { + const char *k_name; + int k_val; +}; + +int +yyerror(const char *fmt, ...) +{ + va_list ap; + + file->errors++; + va_start(ap, fmt); + fprintf(stderr, "%s:%d: ", file->name, yylval.lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + return (0); +} + +int +kw_cmp(const void *k, const void *e) +{ + return (strcmp(k, ((const struct keywords *)e)->k_name)); +} + +int +lookup(char *s) +{ + /* this has to be sorted always */ + static const struct keywords keywords[] = { + { "activeborder", ACTIVEBORDER}, + { "autogroup", AUTOGROUP}, + { "bind-key", BINDKEY}, + { "bind-mouse", BINDMOUSE}, + { "borderwidth", BORDERWIDTH}, + { "tilegap", TILEGAP}, + { "color", COLOR}, + { "command", COMMAND}, + { "font", FONTCOLOR}, + { "fontname", FONTNAME}, + { "gap", GAP}, + { "groupborder", GROUPBORDER}, + { "htile", HTILE}, + { "ignore", IGNORE}, + { "inactiveborder", INACTIVEBORDER}, + { "menubg", MENUBG}, + { "menufg", MENUFG}, + { "moveamount", MOVEAMOUNT}, + { "no", NO}, + { "resizeallcorners", RESIZEALLCORNERS}, + { "selfont", FONTSELCOLOR}, + { "snapdist", SNAPDIST}, + { "sticky", STICKY}, + { "unbind-key", UNBINDKEY}, + { "unbind-mouse", UNBINDMOUSE}, + { "ungroupborder", UNGROUPBORDER}, + { "urgencyborder", URGENCYBORDER}, + { "vtile", VTILE}, + { "wm", WM}, + { "yes", YES} + }; + const struct keywords *p; + + p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), + sizeof(keywords[0]), kw_cmp); + + if (p) + return (p->k_val); + else + return (STRING); +} + +#define MAXPUSHBACK 128 + +char *parsebuf; +int parseindex; +char pushback_buffer[MAXPUSHBACK]; +int pushback_index = 0; + +int +lgetc(int quotec) +{ + int c, next; + + if (parsebuf) { + /* Read character from the parsebuffer instead of input. */ + if (parseindex >= 0) { + c = parsebuf[parseindex++]; + if (c != '\0') + return (c); + parsebuf = NULL; + } else + parseindex++; + } + + if (pushback_index) + return (pushback_buffer[--pushback_index]); + + if (quotec) { + if ((c = getc(file->stream)) == EOF) { + yyerror("reached end of file while parsing " + "quoted string"); + if (file == topfile || popfile() == EOF) + return (EOF); + return (quotec); + } + return (c); + } + + while ((c = getc(file->stream)) == '\\') { + next = getc(file->stream); + if (next != '\n') { + c = next; + break; + } + yylval.lineno = file->lineno; + file->lineno++; + } + + while (c == EOF) { + if (file == topfile || popfile() == EOF) + return (EOF); + c = getc(file->stream); + } + return (c); +} + +int +lungetc(int c) +{ + if (c == EOF) + return (EOF); + if (parsebuf) { + parseindex--; + if (parseindex >= 0) + return (c); + } + if (pushback_index < MAXPUSHBACK-1) + return (pushback_buffer[pushback_index++] = c); + else + return (EOF); +} + +int +findeol(void) +{ + int c; + + parsebuf = NULL; + + /* skip to either EOF or the first real EOL */ + while (1) { + if (pushback_index) + c = pushback_buffer[--pushback_index]; + else + c = lgetc(0); + if (c == '\n') { + file->lineno++; + break; + } + if (c == EOF) + break; + } + return (ERROR); +} + +int +yylex(void) +{ + char buf[8096]; + char *p; + int quotec, next, c; + int token; + + p = buf; + while ((c = lgetc(0)) == ' ' || c == '\t') + ; /* nothing */ + + yylval.lineno = file->lineno; + if (c == '#') + while ((c = lgetc(0)) != '\n' && c != EOF) + ; /* nothing */ + + switch (c) { + case '\'': + case '"': + quotec = c; + while (1) { + if ((c = lgetc(quotec)) == EOF) + return (0); + if (c == '\n') { + file->lineno++; + continue; + } else if (c == '\\') { + if ((next = lgetc(quotec)) == EOF) + return (0); + if (next == quotec || next == ' ' || + next == '\t') + c = next; + else if (next == '\n') { + file->lineno++; + continue; + } else + lungetc(next); + } else if (c == quotec) { + *p = '\0'; + break; + } else if (c == '\0') { + yyerror("syntax error"); + return (findeol()); + } + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + *p++ = (char)c; + } + yylval.v.string = xstrdup(buf); + return (STRING); + } + +#define allowed_to_end_number(x) \ + (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') + + if (c == '-' || isdigit(c)) { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && isdigit(c)); + lungetc(c); + if (p == buf + 1 && buf[0] == '-') + goto nodigits; + if (c == EOF || allowed_to_end_number(c)) { + const char *errstr = NULL; + + *p = '\0'; + yylval.v.number = strtonum(buf, LLONG_MIN, + LLONG_MAX, &errstr); + if (errstr) { + yyerror("\"%s\" invalid number: %s", + buf, errstr); + return (findeol()); + } + return (NUMBER); + } else { +nodigits: + while (p > buf + 1) + lungetc(*--p); + c = *--p; + if (c == '-') + return (c); + } + } + +/* Similar to other parse.y copies, but also allows '/' in strings */ +#define allowed_in_string(x) \ + (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ + x != '{' && x != '}' && x != '<' && x != '>' && \ + x != '!' && x != '=' && x != '#' && x != ',')) + + if (isalnum(c) || c == ':' || c == '_' || c == '*' || c == '/') { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); + lungetc(c); + *p = '\0'; + if ((token = lookup(buf)) == STRING) + yylval.v.string = xstrdup(buf); + return (token); + } + if (c == '\n') { + yylval.lineno = file->lineno; + file->lineno++; + } + if (c == EOF) + return (0); + return (c); +} + +struct file * +pushfile(const char *name, FILE *stream) +{ + struct file *nfile; + + nfile = xcalloc(1, sizeof(struct file)); + nfile->name = xstrdup(name); + nfile->stream = stream; + nfile->lineno = 1; + TAILQ_INSERT_TAIL(&files, nfile, entry); + return (nfile); +} + +int +popfile(void) +{ + struct file *prev; + + if ((prev = TAILQ_PREV(file, files, entry)) != NULL) + prev->errors += file->errors; + + TAILQ_REMOVE(&files, file, entry); + fclose(file->stream); + free(file->name); + free(file); + file = prev; + return (file ? 0 : EOF); +} + +int +parse_config(const char *filename, struct conf *xconf) +{ + FILE *stream; + int errors = 0; + + conf = xconf; + + stream = fopen(filename, "r"); + if (stream == NULL) { + if (errno == ENOENT) + return (0); + warn("%s", filename); + return (-1); + } + file = pushfile(filename, stream); + topfile = file; + + yyparse(); + errors = file->errors; + popfile(); + + return (errors ? -1 : 0); +} diff --git a/queue.h b/queue.h new file mode 100644 index 0000000..f8f09bf --- /dev/null +++ b/queue.h @@ -0,0 +1,648 @@ +/* $OpenBSD: queue.h,v 1.38 2013/07/03 15:05:21 fgsch Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) +#define _Q_INVALIDATE(a) (a) = ((void *)-1) +#else +#define _Q_INVALIDATE(a) +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST(head); \ + (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->slh_first; \ + \ + while (curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + _Q_INVALIDATE((elm)->field.sle_next); \ + } \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST(head); \ + (var) && ((tvar) = LIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SIMPLEQ_FIRST(head); \ + (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ + == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +/* + * XOR Simple queue definitions. + */ +#define XSIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqx_first; /* first element */ \ + struct type **sqx_last; /* addr of last next element */ \ + unsigned long sqx_cookie; \ +} + +#define XSIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqx_next; /* next element */ \ +} + +/* + * XOR Simple queue access methods. + */ +#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ + (unsigned long)(ptr))) +#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) +#define XSIMPLEQ_END(head) NULL +#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) +#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) + + +#define XSIMPLEQ_FOREACH(var, head, field) \ + for ((var) = XSIMPLEQ_FIRST(head); \ + (var) != XSIMPLEQ_END(head); \ + (var) = XSIMPLEQ_NEXT(head, var, field)) + +#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = XSIMPLEQ_FIRST(head); \ + (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ + (var) = (tvar)) + +/* + * XOR Simple queue functions. + */ +#define XSIMPLEQ_INIT(head) do { \ + arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ + (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ +} while (0) + +#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqx_next = (head)->sqx_first) == \ + XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ + (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ +} while (0) + +#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ + *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ +} while (0) + +#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ + XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ + (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ +} while (0) + +#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ + (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ +} while (0) + +#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ + (elm)->field.sqx_next)->field.sqx_next) \ + == XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = \ + XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ +} while (0) + + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * tail queue access methods + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_NEXT(var, field), 1); \ + (var) = (tvar)) + + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_PREV(var, headname, field), 1); \ + (var) = (tvar)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue access methods + */ +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_END(head) ((void *)(head)) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#define CIRCLEQ_EMPTY(head) \ + (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for((var) = CIRCLEQ_FIRST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_NEXT(var, field)) + +#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = CIRCLEQ_FIRST(head); \ + (var) != CIRCLEQ_END(head) && \ + ((tvar) = CIRCLEQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for((var) = CIRCLEQ_LAST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_PREV(var, field)) + +#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = CIRCLEQ_LAST(head, headname); \ + (var) != CIRCLEQ_END(head) && \ + ((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \ + (var) = (tvar)) + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = CIRCLEQ_END(head); \ + (head)->cqh_last = CIRCLEQ_END(head); \ +} while (0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = CIRCLEQ_END(head); \ + if ((head)->cqh_last == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = CIRCLEQ_END(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ + _Q_INVALIDATE((elm)->field.cqe_prev); \ + _Q_INVALIDATE((elm)->field.cqe_next); \ +} while (0) + +#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ + CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm2); \ + else \ + (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ + if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ + CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm2); \ + else \ + (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ + _Q_INVALIDATE((elm)->field.cqe_prev); \ + _Q_INVALIDATE((elm)->field.cqe_next); \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/reallocarray.c b/reallocarray.c new file mode 100644 index 0000000..ed3244e --- /dev/null +++ b/reallocarray.c @@ -0,0 +1,38 @@ +/* $OpenBSD: reallocarray.c,v 1.2 2014/12/08 03:45:00 bcook Exp $ */ +/* + * Copyright (c) 2008 Otto Moerbeek + * + * 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 +#include +#include +#include + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +void * +reallocarray(void *optr, size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + return realloc(optr, size * nmemb); +} diff --git a/samu.1 b/samu.1 new file mode 100644 index 0000000..8063ff2 --- /dev/null +++ b/samu.1 @@ -0,0 +1,290 @@ +.\" $OpenBSD$ +.\" +.\" Copyright (c) 2004,2005 Marius Aamodt Eriksen +.\" +.\" 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. +.\" +.Dd $Mdocdate$ +.Dt CWM 1 +.Os +.Sh NAME +.Nm samu +.Nd a lightweight and efficient window manager for X11 +.Sh SYNOPSIS +.\" For a program: program [-abc] file ... +.Nm cwm +.Op Fl nv +.Op Fl c Ar file +.Op Fl d Ar display +.Sh DESCRIPTION +.Nm +is a window manager for X11 which contains many features that +concentrate on the efficiency and transparency of window management, +while maintaining the simplest and most pleasant aesthetic. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl c Ar file +Specify an alternative configuration file. +By default, +.Nm +loads +.Pa ~/.cwmrc , +if present. +Any error messages from lines in the configuration file will be sent to +.Em stderr ; +however, +.Nm +will continue to process the rest of the configuration file. +.It Fl d Ar display +Specify the display to use. +.It Fl n +Configtest mode. +Only check the configuration file for validity. +.It Fl v +Verbose mode. +Multiple +.Fl v +options increase the verbosity. +.El +.Pp +.Nm +actions are initiated either via key or mouse bindings. +The following notations are used throughout this page: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It Ic C +Control key. +.It Ic M +Meta key. +.It Ic S +Shift key. +.It Ic 4 +Mod4 (windows) key. +.It Ic M1 +Left mouse button. +.It Ic M2 +Middle mouse button. +.It Ic M3 +Right mouse button. +.El +.Pp +The default key bindings are: +.Pp +.Bl -tag -width "CM-EscapeXXXXX" -offset indent -compact +.It Ic CM-Return +Spawn a new terminal. +.It Ic CM-Delete +Lock the screen. +.It Ic M-Return +Hide current window. +.It Ic M-Down +Lower current window. +.It Ic M-Up +Raise current window. +.It Ic M-slash +Search for windows. +.It Ic C-slash +Search for applications. +.It Ic CM-n +Label current window. +.It Ic M-Tab +Cycle through currently visible windows. +.It Ic MS-Tab +Reverse cycle through currently visible windows. +.It Ic CM-x +Close current window. +.It Ic CM-[n] +Toggle visibility of group n, where n is 1-9. +.It Ic CM-a +Toggle visibility of all groups. +.It Ic CM-g +Toggle group membership of current window. +.It Ic M-Right +Cycle through active groups. +.It Ic M-Left +Reverse cycle through active groups. +.It Ic CMS-f +Toggle freezing geometry of current window. +.It Ic CM-s +Toggle stickiness of current window. +.It Ic CM-f +Toggle full-screen mode of current window. +.It Ic CM-m +Toggle maximization of current window. +.It Ic CM-equal +Toggle vertical maximization of current window. +.It Ic CMS-equal +Toggle horizontal maximization of current window. +.It Ic M-[hjkl] +Move window by a small amount. +.It Ic MS-[hjkl] +Move window by a large amount; see +.Xr cwmrc 5 . +.It Ic CM-[hjkl] +Resize window by a small amount. +.It Ic CMS-[hjkl] +Resize window by a large amount; see +.Xr cwmrc 5 . +.It Ic M-question +Spawn +.Dq exec program +dialog. +.It Ic M-period +Spawn +.Dq ssh to +dialog. +This parses +.Pa $HOME/.ssh/known_hosts +to provide host auto-completion. +.Xr ssh 1 +will be executed via the configured terminal emulator. +.It Ic CM-w +Spawn +.Dq exec WindowManager +menu, allowing a switch to another window manager. +.It Ic CMS-r +Restart. +.It Ic CMS-q +Quit. +.El +.Pp +The default mouse bindings are: +.Pp +.Bl -tag -width "CM-EscapeXXXXX" -offset indent -compact +.It Ic M-M1 +Move current window. +.It Ic CM-M1 +Toggle group membership of current window. +.It Ic M-M2 +Resize current window +.It Ic M-M3 +Lower current window. +.It Ic CMS-M3 +Hide current window. +.El +.Pp +The following key bindings may be used to navigate +search and exec dialogs: +.Pp +.Bl -tag -width "[Down] or C-s or M-j" -offset indent -compact +.It Ic [Return] +Select item. +.It Ic [Down], C-s No or Ic M-j +Next item. +.It Ic [Up], C-r No or Ic M-k +Previous item. +.It Ic [Backspace] No or Ic C-h +Backspace. +.It Ic C-u +Clear input. +.It Ic C-a +List all available items. +.It Ic [Esc] +Cancel. +.El +.Pp +.Nm +rereads its configuration file when it receives a hangup signal, +.Dv SIGHUP , +by executing itself with the name and arguments with which it was started. +This is equivalent to the +.Ar restart +function. +.Sh SEARCH +.Nm +features the ability to search for windows by their current title, +old titles, and by their label. +The priority for the search results are: label, current title, +old titles in reverse order, and finally window class name. +.Nm +keeps a history of the 5 previous titles of a window. +.Pp +When searching, the leftmost character of the result list may show a +flag: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It ! +Window is currently focused. +.It & +Window is hidden. +.El +.Sh APPLICATIONS +.Nm +manages a list of applications defined with the +.Cm command +configuration option. +.Sh GROUPS +.Nm +has the ability to group windows together, and use the groups to +perform operations on the entire group instead of just one window. +Together with the +.Pa sticky +option, this can be used to emulate virtual desktops. +.Pp +To edit groups, use the group selection commands to toggle membership +of a group. +A blue border will be shown briefly on windows added to the current group, +and a red border will be shown on those just removed. +.Sh MENUS +Menus are recalled by clicking the mouse on the root window: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It Ic M1 +Show list of currently defined windows. +Selecting an item will warp to that window, unhiding it if necessary. +.It Ic M2 +Show list of currently defined groups. +Selecting an item will hide/unhide that group. +.It Ic M3 +Show list of applications as defined in the configuration file. +Selecting an item will spawn that application. +.El +.Sh ENVIRONMENT +.Bl -tag -width "DISPLAYXXX" -compact +.It DISPLAY +.Nm +starts on this display unless the +.Fl d +option is given. +.El +.Sh FILES +.Bl -tag -width "~/.cwmrcXX" -compact +.It Pa ~/.cwmrc +Default +.Nm +configuration file. +.El +.Sh SEE ALSO +.Xr cwmrc 5 +.Sh HISTORY +.Nm +was originally inspired by evilwm, but was rewritten from scratch +due to limitations in the evilwm codebase. +The from-scratch rewrite borrowed some code from 9wm, however that code +has since been removed or rewritten. +.Pp +.Nm +first appeared in +.Ox 4.2 . +.Sh AUTHORS +.An -nosplit +.Nm +was developed by +.An Marius Aamodt Eriksen Aq marius@monkey.org +with contributions from +.An Andy Adamson Aq dros@monkey.org , +.An Niels Provos Aq provos@monkey.org , +and +.An Antti Nyk\(:anen Aq aon@iki.fi . +Ideas, discussion with many others. diff --git a/samu.c b/samu.c new file mode 100644 index 0000000..2c30e84 --- /dev/null +++ b/samu.c @@ -0,0 +1,256 @@ +/* + * + * Copyright (c) 2004 Marius Aamodt Eriksen + * + * 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. + * + * $OpenBSD$ + */ + +#include +#include "queue.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "samu.h" + +Display *X_Dpy; +Time Last_Event_Time = CurrentTime; +Atom cwmh[CWMH_NITEMS]; +Atom ewmh[EWMH_NITEMS]; +struct screen_q Screenq = TAILQ_HEAD_INITIALIZER(Screenq); +struct conf Conf; +volatile sig_atomic_t cwm_status; + +void usage(void); +static void sighdlr(int); +static int x_errorhandler(Display *, XErrorEvent *); +static int x_init(const char *); +static void x_teardown(void); +static int x_wmerrorhandler(Display *, XErrorEvent *); + +void autostart(void) { + system("~/.config/samu/autostart.sh &"); +} + +unsigned long getColor(char color[]) { + XColor hex; + Colormap colormap = DefaultColormap(X_Dpy, 0); + XParseColor(X_Dpy, colormap, color, &hex); + XAllocColor(X_Dpy, colormap, &hex); + return hex.pixel; +} + +int +main(int argc, char **argv) +{ + char *display_name = NULL; + char *fallback; + int ch, xfd, nflag = 0; + struct pollfd pfd[1]; + + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + warnx("no locale support"); + mbtowc(NULL, NULL, MB_CUR_MAX); + + conf_init(&Conf); + + fallback = u_argv(argv); + Conf.wm_argv = u_argv(argv); + while ((ch = getopt(argc, argv, "c:d:nv")) != -1) { + switch (ch) { + case 'c': + free(Conf.conf_file); + Conf.conf_file = xstrdup(optarg); + break; + case 'd': + display_name = optarg; + break; + case 'n': + nflag = 1; + break; + case 'v': + Conf.debug++; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (signal(SIGCHLD, sighdlr) == SIG_ERR || + signal(SIGHUP, sighdlr) == SIG_ERR || + signal(SIGINT, sighdlr) == SIG_ERR || + signal(SIGTERM, sighdlr) == SIG_ERR) + err(1, "signal"); + + if (parse_config(Conf.conf_file, &Conf) == -1) { + warnx("error parsing config file"); + if (nflag) + return 1; + } + if (nflag) + return 0; + + xfd = x_init(display_name); + cwm_status = CWM_RUNNING; + + XSetWindowBackground(X_Dpy, DefaultRootWindow(X_Dpy), getColor("#000000")); + XClearWindow(X_Dpy,DefaultRootWindow(X_Dpy)); + + autostart(); + +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec", NULL) == -1) + err(1, "pledge"); +#endif + + memset(&pfd, 0, sizeof(pfd)); + pfd[0].fd = xfd; + pfd[0].events = POLLIN; + while (cwm_status == CWM_RUNNING) { + xev_process(); + if (poll(pfd, 1, -1) == -1) { + if (errno != EINTR) + warn("poll"); + } + } + x_teardown(); + if (cwm_status == CWM_EXEC_WM) { + u_exec(Conf.wm_argv); + warnx("'%s' failed to start, starting fallback", Conf.wm_argv); + u_exec(fallback); + } + + return 0; +} + +static int +x_init(const char *dpyname) +{ + int i; + + if ((X_Dpy = XOpenDisplay(dpyname)) == NULL) + errx(1, "unable to open display \"%s\"", XDisplayName(dpyname)); + + XSetErrorHandler(x_wmerrorhandler); + XSelectInput(X_Dpy, DefaultRootWindow(X_Dpy), SubstructureRedirectMask); + XSync(X_Dpy, False); + XSetErrorHandler(x_errorhandler); + + Conf.xrandr = XRRQueryExtension(X_Dpy, &Conf.xrandr_event_base, &i); + + xu_atom_init(); + conf_cursor(&Conf); + + for (i = 0; i < ScreenCount(X_Dpy); i++) + screen_init(i); + + + return ConnectionNumber(X_Dpy); +} + +static void +x_teardown(void) +{ + struct screen_ctx *sc; + unsigned int i; + + conf_clear(&Conf); + + TAILQ_FOREACH(sc, &Screenq, entry) { + for (i = 0; i < CWM_COLOR_NITEMS; i++) + XftColorFree(X_Dpy, DefaultVisual(X_Dpy, sc->which), + DefaultColormap(X_Dpy, sc->which), + &sc->xftcolor[i]); + XftFontClose(X_Dpy, sc->xftfont); + XUngrabKey(X_Dpy, AnyKey, AnyModifier, sc->rootwin); + } + XUngrabPointer(X_Dpy, CurrentTime); + XUngrabKeyboard(X_Dpy, CurrentTime); + for (i = 0; i < CF_NITEMS; i++) + XFreeCursor(X_Dpy, Conf.cursor[i]); + XSync(X_Dpy, False); + XSetInputFocus(X_Dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XCloseDisplay(X_Dpy); +} + +static int +x_wmerrorhandler(Display *dpy, XErrorEvent *e) +{ + errx(1, "root window unavailable - perhaps another wm is running?"); + return 0; +} + +static int +x_errorhandler(Display *dpy, XErrorEvent *e) +{ +#ifdef DEBUG + char msg[80], number[80], req[80]; + + XGetErrorText(X_Dpy, e->error_code, msg, sizeof(msg)); + (void)snprintf(number, sizeof(number), "%d", e->request_code); + XGetErrorDatabaseText(X_Dpy, "XRequest", number, + "", req, sizeof(req)); + + warnx("%s(0x%x): %s", req, (unsigned int)e->resourceid, msg); +#endif + return 0; +} + +static void +sighdlr(int sig) +{ + pid_t pid; + int save_errno = errno, status; + + switch (sig) { + case SIGCHLD: + /* Collect dead children. */ + while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || + (pid < 0 && errno == EINTR)) + ; + break; + case SIGHUP: + cwm_status = CWM_EXEC_WM; + break; + case SIGINT: + case SIGTERM: + cwm_status = CWM_QUIT; + break; + } + + errno = save_errno; +} + +void +usage(void) +{ + extern char *__progname; + + (void)fprintf(stderr, "usage: %s [-nv] [-c file] [-d display]\n", + __progname); + exit(1); +} diff --git a/samu.h b/samu.h new file mode 100644 index 0000000..b865cb1 --- /dev/null +++ b/samu.h @@ -0,0 +1,595 @@ +/* + * + * Copyright (c) 2004 Marius Aamodt Eriksen + * + * 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. + * + * $OpenBSD$ + */ + +#ifndef _SAMU_H_ +#define _SAMU_H_ + +#include +#include +#include "queue.h" + +/* prototypes for portable-included functions */ +char *fgetln(FILE *, size_t *); +long long strtonum(const char *, long long, long long, const char **); +void *reallocarray(void *, size_t, size_t); + + +#ifdef strlcpy +#define HAVE_STRLCPY +#else +size_t strlcpy(char *, const char *, size_t); +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_DEBUG0(...) log_debug(0, __func__, __VA_ARGS__) +#define LOG_DEBUG1(...) log_debug(1, __func__, __VA_ARGS__) +#define LOG_DEBUG2(...) log_debug(2, __func__, __VA_ARGS__) +#define LOG_DEBUG3(...) log_debug(3, __func__, __VA_ARGS__) + +#undef MIN +#undef MAX +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +#define BUTTONMASK (ButtonPressMask | ButtonReleaseMask) +#define MOUSEMASK (BUTTONMASK | PointerMotionMask) +#define IGNOREMODMASK (LockMask | Mod2Mask | 0x2000) + +/* direction/amount */ +#define CWM_UP 0x0001 +#define CWM_DOWN 0x0002 +#define CWM_LEFT 0x0004 +#define CWM_RIGHT 0x0008 +#define CWM_BIGAMOUNT 0x0010 +#define CWM_UP_BIG (CWM_UP | CWM_BIGAMOUNT) +#define CWM_DOWN_BIG (CWM_DOWN | CWM_BIGAMOUNT) +#define CWM_LEFT_BIG (CWM_LEFT | CWM_BIGAMOUNT) +#define CWM_RIGHT_BIG (CWM_RIGHT | CWM_BIGAMOUNT) +#define CWM_UP_RIGHT (CWM_UP | CWM_RIGHT) +#define CWM_UP_LEFT (CWM_UP | CWM_LEFT) +#define CWM_DOWN_RIGHT (CWM_DOWN | CWM_RIGHT) +#define CWM_DOWN_LEFT (CWM_DOWN | CWM_LEFT) + +#define CWM_CYCLE_FORWARD 0x0001 +#define CWM_CYCLE_REVERSE 0x0002 +#define CWM_CYCLE_INGROUP 0x0004 + +enum cwm_status { + CWM_QUIT, + CWM_RUNNING, + CWM_EXEC_WM +}; +enum cursor_font { + CF_NORMAL, + CF_MOVE, + CF_RESIZE, + CF_RESIZE_NE, + CF_RESIZE_NW, + CF_RESIZE_SE, + CF_RESIZE_SW, + CF_QUESTION, + CF_NITEMS +}; +enum color { + CWM_COLOR_BORDER_ACTIVE, + CWM_COLOR_BORDER_INACTIVE, + CWM_COLOR_BORDER_URGENCY, + CWM_COLOR_BORDER_GROUP, + CWM_COLOR_BORDER_UNGROUP, + CWM_COLOR_BORDER_STICKY, + CWM_COLOR_BORDER_ACTIVE_STICKY, + CWM_COLOR_MENU_FG, + CWM_COLOR_MENU_BG, + CWM_COLOR_MENU_FONT, + CWM_COLOR_MENU_FONT_SEL, + CWM_COLOR_NITEMS +}; + +struct geom { + int x; + int y; + int w; + int h; +}; + +enum apply_gap { + CWM_NOGAP = 0, + CWM_GAP +}; +struct gap { + int top; + int bottom; + int left; + int right; + int bar; +}; + +struct winname { + TAILQ_ENTRY(winname) entry; + char *name; +}; +TAILQ_HEAD(name_q, winname); +TAILQ_HEAD(ignore_q, winname); + +struct client_ctx { + TAILQ_ENTRY(client_ctx) entry; + struct screen_ctx *sc; + struct group_ctx *gc; + Window win; + Colormap colormap; + int bwidth; /* border width */ + int obwidth; /* original border width */ + int tgap; /* tiling gap */ + struct geom geom, savegeom, fullgeom; + struct { + long flags; /* defined hints */ + int basew; /* desired width */ + int baseh; /* desired height */ + int minw; /* minimum width */ + int minh; /* minimum height */ + int maxw; /* maximum width */ + int maxh; /* maximum height */ + int incw; /* width increment progression */ + int inch; /* height increment progression */ + float mina; /* minimum aspect ratio */ + float maxa; /* maximum aspect ratio */ + } hint; + struct { + int x; /* x position */ + int y; /* y position */ + } ptr; + struct { + int h; /* height */ + int w; /* width */ + } dim; +#define CLIENT_HIDDEN 0x0001 +#define CLIENT_IGNORE 0x0002 +#define CLIENT_VMAXIMIZED 0x0004 +#define CLIENT_HMAXIMIZED 0x0008 +#define CLIENT_FREEZE 0x0010 +#define CLIENT_GROUP 0x0020 +#define CLIENT_UNGROUP 0x0040 +#define CLIENT_INPUT 0x0080 +#define CLIENT_WM_DELETE_WINDOW 0x0100 +#define CLIENT_WM_TAKE_FOCUS 0x0200 +#define CLIENT_URGENCY 0x0400 +#define CLIENT_FULLSCREEN 0x0800 +#define CLIENT_STICKY 0x1000 +#define CLIENT_ACTIVE 0x2000 +#define CLIENT_SKIP_PAGER 0x4000 +#define CLIENT_SKIP_TASKBAR 0x8000 + +#define CLIENT_SKIP_CYCLE (CLIENT_HIDDEN | CLIENT_IGNORE | \ + CLIENT_SKIP_TASKBAR | CLIENT_SKIP_PAGER) +#define CLIENT_HIGHLIGHT (CLIENT_GROUP | CLIENT_UNGROUP) +#define CLIENT_MAXFLAGS (CLIENT_VMAXIMIZED | CLIENT_HMAXIMIZED) +#define CLIENT_MAXIMIZED (CLIENT_VMAXIMIZED | CLIENT_HMAXIMIZED) + int flags; + int stackingorder; + struct name_q nameq; + char *name; + char *label; + char *res_class; /* class hint */ + char *res_name; /* class hint */ + int initial_state; /* wm hint */ +}; +TAILQ_HEAD(client_q, client_ctx); + +struct group_ctx { + TAILQ_ENTRY(group_ctx) entry; + struct screen_ctx *sc; + char *name; + int num; +}; +TAILQ_HEAD(group_q, group_ctx); + +struct autogroup { + TAILQ_ENTRY(autogroup) entry; + char *class; + char *name; + int num; +}; +TAILQ_HEAD(autogroup_q, autogroup); + +struct region_ctx { + TAILQ_ENTRY(region_ctx) entry; + int num; + struct geom view; /* viewable area */ + struct geom work; /* workable area, gap-applied */ +}; +TAILQ_HEAD(region_q, region_ctx); + +struct screen_ctx { + TAILQ_ENTRY(screen_ctx) entry; + int which; + Window rootwin; + int cycling; + int hideall; + int snapdist; + struct geom view; /* viewable area */ + struct geom work; /* workable area, gap-applied */ + struct gap gap; + struct client_q clientq; + struct region_q regionq; + struct group_q groupq; + struct group_ctx *group_active; + struct group_ctx *group_last; + Colormap colormap; + Visual *visual; + struct { + Window win; + XftDraw *xftdraw; + } prop; + XftColor xftcolor[CWM_COLOR_NITEMS]; + XftFont *xftfont; +}; +TAILQ_HEAD(screen_q, screen_ctx); + +struct cargs { + char *cmd; + int flag; + enum { + CWM_XEV_KEY, + CWM_XEV_BTN + } xev; +}; +enum context { + CWM_CONTEXT_NONE = 0, + CWM_CONTEXT_CC, + CWM_CONTEXT_SC +}; +struct bind_ctx { + TAILQ_ENTRY(bind_ctx) entry; + void (*callback)(void *, struct cargs *); + struct cargs *cargs; + enum context context; + unsigned int modmask; + union { + KeySym keysym; + unsigned int button; + } press; +}; +TAILQ_HEAD(keybind_q, bind_ctx); +TAILQ_HEAD(mousebind_q, bind_ctx); + +struct cmd_ctx { + TAILQ_ENTRY(cmd_ctx) entry; + char *name; + char *path; +}; +TAILQ_HEAD(cmd_q, cmd_ctx); +TAILQ_HEAD(wm_q, cmd_ctx); + +struct conf { + struct keybind_q keybindq; + struct mousebind_q mousebindq; + struct autogroup_q autogroupq; + struct ignore_q ignoreq; + struct cmd_q cmdq; + struct wm_q wmq; + int ngroups; + int stickygroups; + int resizeallcorners; + int nameqlen; + int bwidth; + int tgap; + int mamount; + int snapdist; + int htile; + int vtile; + struct gap gap; + char *color[CWM_COLOR_NITEMS]; + char *font; + char *wmname; + Cursor cursor[CF_NITEMS]; + int xrandr; + int xrandr_event_base; + char *conf_file; + char *known_hosts; + char *wm_argv; + int debug; +}; + +/* MWM hints */ +struct mwm_hints { +#define MWM_HINTS_ELEMENTS 3L +#define MWM_FLAGS_STATUS (1<<3) + +#define MWM_FLAGS_FUNCTIONS (1<<0) +#define MWM_FLAGS_DECORATIONS (1<<1) +#define MWM_FLAGS_INPUT_MODE (1<<2) + unsigned long flags; + +#define MWM_FUNCS_ALL (1<<0) +#define MWM_FUNCS_RESIZE (1<<1) +#define MWM_FUNCS_MOVE (1<<2) +#define MWM_FUNCS_MINIMIZE (1<<3) +#define MWM_FUNCS_MAXIMIZE (1<<4) +#define MWM_FUNCS_CLOSE (1<<5) + unsigned long functions; + +#define MWM_DECOR_ALL (1<<0) +#define MWM_DECOR_BORDER (1<<1) +#define MWM_DECOR_RESIZE_HANDLE (1<<2) +#define MWM_DECOR_TITLEBAR (1<<3) +#define MWM_DECOR_MENU (1<<4) +#define MWM_DECOR_MINIMIZE (1<<5) +#define MWM_DECOR_MAXIMIZE (1<<6) + unsigned long decorations; +}; + +enum cwmh { + WM_STATE, + WM_DELETE_WINDOW, + WM_TAKE_FOCUS, + WM_PROTOCOLS, + _MOTIF_WM_HINTS, + UTF8_STRING, + WM_CHANGE_STATE, + CWMH_NITEMS +}; +enum ewmh { + _NET_SUPPORTED, + _NET_SUPPORTING_WM_CHECK, + _NET_ACTIVE_WINDOW, + _NET_CLIENT_LIST, + _NET_CLIENT_LIST_STACKING, + _NET_NUMBER_OF_DESKTOPS, + _NET_CURRENT_DESKTOP, + _NET_DESKTOP_VIEWPORT, + _NET_DESKTOP_GEOMETRY, + _NET_VIRTUAL_ROOTS, + _NET_SHOWING_DESKTOP, + _NET_DESKTOP_NAMES, + _NET_WORKAREA, + _NET_WM_NAME, + _NET_WM_DESKTOP, + _NET_CLOSE_WINDOW, + _NET_WM_STATE, +#define _NET_WM_STATES_NITEMS 9 + _NET_WM_STATE_STICKY, + _NET_WM_STATE_MAXIMIZED_VERT, + _NET_WM_STATE_MAXIMIZED_HORZ, + _NET_WM_STATE_HIDDEN, + _NET_WM_STATE_FULLSCREEN, + _NET_WM_STATE_DEMANDS_ATTENTION, + _NET_WM_STATE_SKIP_PAGER, + _NET_WM_STATE_SKIP_TASKBAR, + _CWM_WM_STATE_FREEZE, + EWMH_NITEMS +}; +enum net_wm_state { + _NET_WM_STATE_REMOVE, + _NET_WM_STATE_ADD, + _NET_WM_STATE_TOGGLE +}; + +extern Display *X_Dpy; +extern Time Last_Event_Time; +extern Atom cwmh[CWMH_NITEMS]; +extern Atom ewmh[EWMH_NITEMS]; +extern struct screen_q Screenq; +extern struct conf Conf; + +void usage(void); + +void autostart(void); + +void client_apply_sizehints(struct client_ctx *); +void client_close(struct client_ctx *); +void client_config(struct client_ctx *); +struct client_ctx *client_current(struct screen_ctx *); +void client_draw_border(struct client_ctx *); +struct client_ctx *client_find(Window); +void client_get_sizehints(struct client_ctx *); +void client_hide(struct client_ctx *); +void client_htile(struct client_ctx *); +int client_inbound(struct client_ctx *, int, int); +struct client_ctx *client_init(Window, struct screen_ctx *); +void client_lower(struct client_ctx *); +void client_move(struct client_ctx *); +void client_mtf(struct client_ctx *); +struct client_ctx *client_next(struct client_ctx *); +struct client_ctx *client_prev(struct client_ctx *); +void client_ptr_inbound(struct client_ctx *, int); +void client_ptr_save(struct client_ctx *); +void client_ptr_warp(struct client_ctx *); +void client_raise(struct client_ctx *); +void client_center(struct client_ctx *); +void client_grow(struct client_ctx *); +void client_shrink(struct client_ctx *); +void client_remove(struct client_ctx *); +void client_resize(struct client_ctx *, int); +void client_set_active(struct client_ctx *); +void client_set_name(struct client_ctx *); +void client_show(struct client_ctx *); +int client_snapcalc(int, int, int, int, int); +void client_toggle_hidden(struct client_ctx *); +void client_toggle_hmaximize(struct client_ctx *); +void client_toggle_fullscreen(struct client_ctx *); +void client_toggle_freeze(struct client_ctx *); +void client_toggle_maximize(struct client_ctx *); +void client_toggle_skip_pager(struct client_ctx *); +void client_toggle_skip_taskbar(struct client_ctx *); +void client_toggle_sticky(struct client_ctx *); +void client_toggle_vmaximize(struct client_ctx *); +void client_transient(struct client_ctx *); +void client_urgency(struct client_ctx *); +void client_vtile(struct client_ctx *); +void client_wm_hints(struct client_ctx *); + +void group_assign(struct group_ctx *, struct client_ctx *); +int group_autogroup(struct client_ctx *); +void group_cycle(struct screen_ctx *, int); +void group_hide(struct group_ctx *); +int group_holds_only_hidden(struct group_ctx *); +int group_holds_only_sticky(struct group_ctx *); +void group_init(struct screen_ctx *, int, const char *); +void group_movetogroup(struct client_ctx *, int); +void group_only(struct screen_ctx *, int); +void group_close(struct screen_ctx *, int); +int group_restore(struct client_ctx *); +void group_show(struct group_ctx *); +void group_toggle(struct screen_ctx *, int); +void group_toggle_all(struct screen_ctx *); +void group_toggle_membership(struct client_ctx *); +void group_update_names(struct screen_ctx *); + +struct region_ctx *region_find(struct screen_ctx *, int, int); +void screen_assert_clients_within(struct screen_ctx *); +struct geom screen_area(struct screen_ctx *, int, int, enum apply_gap); +struct screen_ctx *screen_find(Window); +void screen_init(int); +void screen_prop_win_create(struct screen_ctx *, Window); +void screen_prop_win_destroy(struct screen_ctx *); +void screen_prop_win_draw(struct screen_ctx *, + const char *, ...) + __attribute__((__format__ (printf, 2, 3))) + __attribute__((__nonnull__ (2))); +void screen_update_geometry(struct screen_ctx *); +void screen_updatestackingorder(struct screen_ctx *); + +void kbfunc_cwm_status(void *, struct cargs *); +void kbfunc_ptrmove(void *, struct cargs *); +void kbfunc_client_snap(void *, struct cargs *); +void kbfunc_client_move(void *, struct cargs *); +void kbfunc_client_resize(void *, struct cargs *); +void kbfunc_client_tile(void *, struct cargs *); +void kbfunc_client_close(void *, struct cargs *); +void kbfunc_client_lower(void *, struct cargs *); +void kbfunc_client_raise(void *, struct cargs *); +void kbfunc_client_center(void *, struct cargs *); +void kbfunc_client_grow(void *, struct cargs *); +void kbfunc_client_shrink(void *, struct cargs *); +void kbfunc_client_hide(void *, struct cargs *); +void kbfunc_client_toggle_freeze(void *, struct cargs *); +void kbfunc_client_toggle_sticky(void *, struct cargs *); +void kbfunc_client_toggle_fullscreen(void *, + struct cargs *); +void kbfunc_client_toggle_maximize(void *, struct cargs *); +void kbfunc_client_toggle_hmaximize(void *, struct cargs *); +void kbfunc_client_toggle_vmaximize(void *, struct cargs *); +void kbfunc_client_htile(void *, struct cargs *); +void kbfunc_client_vtile(void *, struct cargs *); +void kbfunc_client_cycle(void *, struct cargs *); +void kbfunc_client_toggle_group(void *, struct cargs *); +void kbfunc_client_movetogroup(void *, struct cargs *); +void kbfunc_group_toggle(void *, struct cargs *); +void kbfunc_group_only(void *, struct cargs *); +void kbfunc_group_last(void *, struct cargs *); +void kbfunc_group_close(void *, struct cargs *); +void kbfunc_group_cycle(void *, struct cargs *); +void kbfunc_group_toggle_all(void *, struct cargs *); +void kbfunc_exec_cmd(void *, struct cargs *); +void kbfunc_exec_lock(void *, struct cargs *); +void kbfunc_exec_term(void *, struct cargs *); +void kbfunc_exec_dmenu(void *, struct cargs *); +void kbfunc_exec_dextra(void *, struct cargs *); +void kbfunc_exec_dprint(void *, struct cargs *); +void kbfunc_exec_teleporter(void *, struct cargs *); +void kbfunc_exec_vol_up(void *, struct cargs *); +void kbfunc_exec_vol_down(void *, struct cargs *); +void kbfunc_exec_vol_mute(void *, struct cargs *); + +int parse_config(const char *, struct conf *); + +void conf_autogroup(struct conf *, int, const char *, + const char *); +int conf_bind_key(struct conf *, const char *, + const char *); +int conf_bind_mouse(struct conf *, const char *, + const char *); +void conf_clear(struct conf *); +void conf_client(struct client_ctx *); +void conf_cmd_add(struct conf *, const char *, + const char *); +void conf_wm_add(struct conf *, const char *, + const char *); +void conf_cursor(struct conf *); +void conf_grab_kbd(Window); +void conf_grab_mouse(Window); +void conf_init(struct conf *); +void conf_ignore(struct conf *, const char *); +void conf_screen(struct screen_ctx *); +void conf_group(struct screen_ctx *); + +void xev_process(void); + +int xu_get_prop(Window, Atom, Atom, long, unsigned char **); +int xu_get_strprop(Window, Atom, char **); +void xu_ptr_get(Window, int *, int *); +void xu_ptr_set(Window, int, int); +void xu_get_wm_state(Window, long *); +void xu_set_wm_state(Window, long); +void xu_send_clientmsg(Window, Atom, Time); +void xu_xorcolor(XftColor, XftColor, XftColor *); + +void xu_atom_init(void); +void xu_ewmh_net_supported(struct screen_ctx *); +void xu_ewmh_net_supported_wm_check(struct screen_ctx *); +void xu_ewmh_net_desktop_geometry(struct screen_ctx *); +void xu_ewmh_net_desktop_viewport(struct screen_ctx *); +void xu_ewmh_net_workarea(struct screen_ctx *); +void xu_ewmh_net_client_list(struct screen_ctx *); +void xu_ewmh_net_client_list_stacking(struct screen_ctx *); +void xu_ewmh_net_active_window(struct screen_ctx *, Window); +void xu_ewmh_net_number_of_desktops(struct screen_ctx *); +void xu_ewmh_net_showing_desktop(struct screen_ctx *); +void xu_ewmh_net_virtual_roots(struct screen_ctx *); +void xu_ewmh_net_current_desktop(struct screen_ctx *); +void xu_ewmh_net_desktop_names(struct screen_ctx *); +int xu_ewmh_get_net_wm_desktop(struct client_ctx *, long *); +void xu_ewmh_set_net_wm_desktop(struct client_ctx *); +Atom *xu_ewmh_get_net_wm_state(struct client_ctx *, int *); +void xu_ewmh_handle_net_wm_state_msg(struct client_ctx *, + int, Atom , Atom); +void xu_ewmh_set_net_wm_state(struct client_ctx *); +void xu_ewmh_restore_net_wm_state(struct client_ctx *); + +char *u_argv(char * const *); +void u_exec(char *); +void u_spawn(char *); +void log_debug(int, const char *, const char *, ...) + __attribute__((__format__ (printf, 3, 4))) + __attribute__((__nonnull__ (3))); + +void *xcalloc(size_t, size_t); +void *xmalloc(size_t); +void *xreallocarray(void *, size_t, size_t); +char *xstrdup(const char *); +int xasprintf(char **, const char *, ...) + __attribute__((__format__ (printf, 2, 3))) + __attribute__((__nonnull__ (2))); +int xvasprintf(char **, const char *, va_list) + __attribute__((__nonnull__ (2))); + +#endif /* _SAMU_H_ */ diff --git a/samurc.5 b/samurc.5 new file mode 100644 index 0000000..bcaffce --- /dev/null +++ b/samurc.5 @@ -0,0 +1,522 @@ +.\" $OpenBSD$ +.\" +.\" Copyright (c) 2004,2005 Marius Aamodt Eriksen +.\" +.\" 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. +.\" +.Dd $Mdocdate$ +.Dt CWMRC 5 +.Os +.Sh NAME +.Nm cwmrc +.Nd samu window manager configuration file +.Sh DESCRIPTION +This manual page describes the +.Xr cwm 1 +configuration file. +.Pp +The current line can be extended over multiple lines using a backslash +.Pq Sq \e . +Comments can be put anywhere in the file using a hash mark +.Pq Sq # , +and extend to the end of the current line. +Care should be taken when commenting out multi-line text: +the comment is effective until the end of the entire block. +.Pp +Arguments containing whitespace should be surrounded by double quotes +.Pq \&" . +.Pp +The following options are accepted: +.Bl -tag -width Ds +.It Ic autogroup Ar group Oo Ar windowname , Oc Ns Ar windowclass +Automatically add new windows to +.Ar group +if their class property matches +.Ar windowclass , +or if their name and class properties match +.Ar windowname +and +.Ar windowclass , +respectively. +The more specific last match wins. +.Ar group +is a number between 0 and 9. +If +.Ar group +is 0, matching windows will not be added to any group; this may be +used to override +.Dq sticky group mode . +.Pp +The name and class values, respectively, for existing windows +are both set in the WM_CLASS property and may be obtained using +.Xr xprop 1 . +.It Ic bind-key Ar key function +Bind or rebind key +.Ar key +to +.Ar function . +The modifier keys come first, followed by a +.Sq - , +then a keysym name, taken from +.Pa /usr/X11R6/include/X11/keysymdef.h . +.Pp +The following modifiers are recognised: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It Ic C +Control key. +.It Ic M +Meta key. +.It Ic S +Shift key. +.It Ic 4 +Mod4 (windows) key. +.It Ic 5 +Mod5 (AltGr) key. +.El +.Pp +The +.Ar function +may either be one from the +.Sx BIND FUNCTION LIST +(see below) or the command line that is to be executed. +.It Ic bind-mouse Ar button function +Bind or rebind button +.Ar button +to +.Ar function . +The modifier keys come first, followed by a +.Sq - , +then the button number. +.Pp +The same modifiers are recognised as for +.Ar key +in +.Nm bind-key . +.Pp +The following buttons are recognised: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It Ic 1 +Left mouse button. +.It Ic 2 +Middle mouse button. +.It Ic 3 +Right mouse button. +.It Ic 4 +Scroll up mouse button. +.It Ic 5 +Scroll down mouse button. +.El +.Pp +The +.Ar function +may be taken from the +.Sx BIND FUNCTION LIST +(see below) or the command line that is to be executed. +.It Ic borderwidth Ar pixels +Set the window border width to +.Ar pixels . +.It Ic color activeborder Ar color +Set the color of the active border. +.It Ic color font Ar color +Set menu font color. +.It Ic color selfont Ar color +Set font color for selected menu item. +.It Ic color groupborder Ar color +Set the color of the border while grouping a window. +.It Ic color inactiveborder Ar color +Set the color of the inactive border. +.It Ic color menubg Ar color +Set menu background color. +.It Ic color menufg Ar color +Set menu foreground color. +.It Ic color urgencyborder Ar color +Set the color of the border of a window indicating urgency. +.It Ic color ungroupborder Ar color +Set the color of the border while ungrouping a window. +.It Ic command Ar name path +Every +.Ar name +entry is shown in the application menu. +When selected, the defined +.Ar path +is executed with +.Xr execvp 3 . +.Pp +The +.Ar name +entries +.Nm term +and +.Nm lock +have a special meaning. +They point to the terminal and screen locking programs specified by +key bindings. +The defaults are +.Xr xterm 1 +and +.Xr xlock 1 , +respectively. +.It Ic fontname Ar font +Change the default +.Ar font +for +.Xr Xft 3 . +.It Ic gap Ar top bottom left right +Define a +.Dq gap +in pixels at the edge of the screen, so that when a +window is maximized it will not overlap this area. +This +.Dq gap +can be used for applications such as +.Xr xclock 1 , +where the user may wish to remain visible. +.It Ic htile Ar percent +Set the percentage of screen the master window should occupy +after calling +.Ic window-htile . +If set to 0, the horizontal size of the master window will +remain unchanged. +The default is 50. +.It Ic ignore Ar windowname +Ignore, and do not warp to, windows with the name +.Ar windowname +when drawing borders and cycling through windows. +.It Ic moveamount Ar pixels +Set a default size for the keyboard movement bindings, +in pixels. +The default is 1. +.It Ic snapdist Ar pixels +Minimum distance to snap-to adjacent edge, in pixels. +The default is 0. +.It Ic sticky Ic yes Ns \&| Ns Ic no +Toggle sticky group mode. +The default behavior for new windows is to not assign any group. +By enabling sticky group mode, +.Xr cwm 1 +will assign new windows to the currently selected group. +.It Ic unbind-key Ar key +Unbind function bound to +.Ar key . +A special +.Ar key +keyword +.Dq all +can be used to unbind all keys. +.It Ic unbind-mouse Ar button +Unbind function bound to +.Ar button . +A special +.Ar button +keyword +.Dq all +can be used to unbind all buttons. +.It Ic vtile Ar percent +Set the percentage of screen the master window should occupy +after calling +.Ic window-vtile . +If set to 0, the vertical size of the master window will +remain unchanged. +The default is 50. +.It Ic wm Ar name path +Every +.Ar name +entry is shown in the wm menu. +When selected, the window manager is replaced by +.Ar path . +.El +.Sh BIND FUNCTION LIST +.Bl -tag -width 23n -compact +.It restart +Restart the running +.Xr cwm 1 . +.It quit +Quit +.Xr cwm 1 . +.It terminal +Spawn a new terminal. +.It lock +Lock the screen. +.It menu-window +Launch window search menu. +.It menu-window-hidden +Launch hidden window search menu. +.It menu-cmd +Launch application search menu. +.It menu-group +Launch group search menu. +.It menu-exec +Launch +.Dq exec program +menu. +.It menu-exec-wm +Launch +.Dq exec WindowManager +menu. +.It menu-ssh +Launch +.Dq ssh +menu. +.It group-toggle-[n] +Toggle visibility of group n, where n is 1-9. +.It group-only-[n] +Show only group n, where n is 1-9, hiding other groups. +.It group-last +Show only the previously active group. +.It group-close-[n] +Close all windows in group n, where n is 1-9. +.It group-toggle-all +Toggle visibility of all groups. +.It window-group +Toggle group membership of current window. +.It window-movetogroup-[n] +Hide current window from display and move to group n, where n is 1-9. +.It group-cycle +Forward cycle through groups. +.It group-rcycle +Reverse cycle through groups. +.It window-cycle +Forward cycle through windows. +.It window-rcycle +Reverse cycle through windows. +.It window-cycle-ingroup +Forward cycle through windows in current group. +.It window-rcycle-ingroup +Reverse cycle through windows in current group. +.It window-close +Close current window. +.It window-hide +Hide current window. +.It window-lower +Lower current window. +.It window-raise +Raise current window. +.It window-menu-label +Label current window. +.It window-freeze +Freeze current window geometry. +.It window-stick +Stick current window to all groups (same as assigning to nogroup). +.It window-fullscreen +Full-screen current window (gap + border removed). +.It window-maximize +Maximize current window (gap + border honored). +.It window-vmaximize +Vertically maximize current window (gap + border honored). +.It window-hmaximize +Horizontally maximize current window (gap + border honored). +.It window-htile +Current window is placed at the top of the screen, maximized +horizontally and resized to +.Ar htile +(default half) of the vertical screen space. +Other windows in its group share remaining screen space. +.It window-vtile +Current window is placed on the left of the screen, maximized vertically +and resized to +.Ar vtile +(default half) of the horizontal screen space. +Other windows in its group share remaining screen space. +.It window-move +Move current window. +.It window-resize +Resize current window. +.It window-move-up +Move window +.Ar moveamount +pixels up. +.It window-move-down +Move window +.Ar moveamount +pixels down. +.It window-move-right +Move window +.Ar moveamount +pixels right. +.It window-move-left +Move window +.Ar moveamount +pixels left. +.It window-move-up-big +Move window 10 times +.Ar moveamount +pixels up. +.It window-move-down-big +Move window 10 times +.Ar moveamount +pixels down. +.It window-move-right-big +Move window 10 times +.Ar moveamount +pixels right. +.It window-move-left-big +Move window 10 times +.Ar moveamount +pixels left. +.It window-resize-up +Resize window +.Ar moveamount +pixels up. +.It window-resize-down +Resize window +.Ar moveamount +pixels down. +.It window-resize-right +Resize window +.Ar moveamount +pixels right. +.It window-resize-left +Resize window +.Ar moveamount +pixels left. +.It window-resize-up-big +Resize window 10 times +.Ar moveamount +pixels up. +.It window-resize-down-big +Resize window 10 times +.Ar moveamount +pixels down. +.It window-resize-right-big +Resize window 10 times +.Ar moveamount +pixels right. +.It window-resize-left-big +Resize window 10 times +.Ar moveamount +pixels left. +.It window-snap-up +Snap window to top edge. +.It window-snap-down +Snap window to bottom edge. +.It window-snap-right +Snap window to right edge. +.It window-snap-left +Snap window to left edge. +.It window-snap-up-right +Snap window to top-right corner. +.It window-snap-up-left +Snap window to top-left corner. +.It window-snap-down-right +Snap window to bottom-right corner. +.It window-snap-down-left +Snap window to bottom-left corner. +.It window-tile-up +Fill the top half of the screen with the window. +.It window-tile-down +Fill the bottom half of the screen with the window. +.It window-tile-left +Fill the left half of the screen with the window. +.It window-tile-right +Fill the right half of the screen with the window. +.It window-tile-up-left +Fill the top left quarter of the screen with the window. +.It window-tile-up-right +Fill the top right quarter of the screen with the window. +.It window-tile-down-left +Fill the down left quarter of the screen with the window. +.It window-tile-down-right +Fill the down right quarter of the screen with the window. +.It window-center +Centers the window on the screen. +.It pointer-move-up +Move pointer +.Ar moveamount +pixels up. +.It pointer-move-down +Move pointer +.Ar moveamount +pixels down. +.It pointer-move-right +Move pointer +.Ar moveamount +pixels right. +.It pointer-move-left +Move pointer +.Ar moveamount +pixels left. +.It pointer-move-up-big +Move pointer 10 times +.Ar moveamount +pixels up. +.It pointer-move-down-big +Move pointer 10 times +.Ar moveamount +pixels down. +.It pointer-move-right-big +Move pointer 10 times +.Ar moveamount +pixels right. +.It pointer-move-left-big +Move pointer 10 times +.Ar moveamount +pixels left. +.El +.Sh FILES +.Bl -tag -width "~/.cwmrcXXX" -compact +.It Pa ~/.cwmrc +Default +.Xr cwm 1 +configuration file. +.El +.Sh EXAMPLES +.Bd -literal +# Set default Xft(3) font +fontname "sans-serif:pixelsize=14:bold" + +# Turn on sticky-group mode +sticky yes + +# Any entry here is shown in the application menu +command firefox firefox +command xmms xmms +command top "xterm -e top" + +# Autogroup definitions +autogroup 3 "aterm,XTerm" +autogroup 3 "xterm,XTerm" + +# Ignore programs by that name by not drawing borders around them. +ignore XMMS +ignore xwi +ignore xapm +ignore xclock + +# Key bindings +bind-key CM-r window-menu-label +bind-key CS-Return "xterm -e top" +bind-key C4-equal window-vmaximize +bind-key C4S-equal window-hmaximize +bind-key M-1 group-only-1 +bind-key M-2 group-only-2 +bind-key M-3 group-only-3 +bind-key MS-1 window-movetogroup-1 +bind-key MS-2 window-movetogroup-2 +bind-key MS-3 window-movetogroup-3 +unbind-key 4-o +unbind-key CM-equal +unbind-key CMS-equal + +# Mouse bindings +bind-mouse M-2 window-lower +bind-mouse M-3 window-resize +.Ed +.Sh SEE ALSO +.Xr cwm 1 +.Sh HISTORY +The +.Nm +file format first appeared in +.Ox 4.4 . diff --git a/screen.c b/screen.c new file mode 100644 index 0000000..721fec8 --- /dev/null +++ b/screen.c @@ -0,0 +1,311 @@ +/* + * + * Copyright (c) 2004 Marius Aamodt Eriksen + * + * 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. + * + * $OpenBSD$ + */ + +#include +#include "queue.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "samu.h" + +static struct geom screen_apply_gap(struct screen_ctx *, struct geom, int which); +static void screen_scan(struct screen_ctx *); + +void +screen_init(int which) +{ + struct screen_ctx *sc; + XSetWindowAttributes attr; + + sc = xmalloc(sizeof(*sc)); + + TAILQ_INIT(&sc->clientq); + TAILQ_INIT(&sc->regionq); + TAILQ_INIT(&sc->groupq); + + sc->which = which; + sc->rootwin = RootWindow(X_Dpy, sc->which); + sc->colormap = DefaultColormap(X_Dpy, sc->which); + sc->visual = DefaultVisual(X_Dpy, sc->which); + sc->cycling = 0; + sc->hideall = 0; + + conf_screen(sc); + + xu_ewmh_net_supported(sc); + xu_ewmh_net_supported_wm_check(sc); + + conf_group(sc); + sc->group_last = sc->group_active; + screen_update_geometry(sc); + + xu_ewmh_net_desktop_names(sc); + xu_ewmh_net_number_of_desktops(sc); + xu_ewmh_net_showing_desktop(sc); + xu_ewmh_net_virtual_roots(sc); + + attr.cursor = Conf.cursor[CF_NORMAL]; + attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask | + EnterWindowMask | PropertyChangeMask | ButtonPressMask; + XChangeWindowAttributes(X_Dpy, sc->rootwin, (CWEventMask | CWCursor), &attr); + + if (Conf.xrandr) + XRRSelectInput(X_Dpy, sc->rootwin, RRScreenChangeNotifyMask); + + screen_scan(sc); + screen_updatestackingorder(sc); + + TAILQ_INSERT_TAIL(&Screenq, sc, entry); + + XSync(X_Dpy, False); +} + +static void +screen_scan(struct screen_ctx *sc) +{ + struct client_ctx *cc, *active = NULL; + Window *wins, w0, w1, rwin, cwin; + unsigned int nwins, i, mask; + int rx, ry, wx, wy; + + XQueryPointer(X_Dpy, sc->rootwin, &rwin, &cwin, + &rx, &ry, &wx, &wy, &mask); + + if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) { + for (i = 0; i < nwins; i++) { + if ((cc = client_init(wins[i], sc)) != NULL) + if (cc->win == cwin) + active = cc; + } + XFree(wins); + } + if (active) + client_set_active(active); +} + +struct screen_ctx * +screen_find(Window win) +{ + struct screen_ctx *sc; + + TAILQ_FOREACH(sc, &Screenq, entry) { + if (sc->rootwin == win) + return sc; + } + warnx("%s: failure win 0x%lx", __func__, win); + return NULL; +} + +void +screen_updatestackingorder(struct screen_ctx *sc) +{ + Window *wins, w0, w1; + struct client_ctx *cc; + unsigned int nwins, i, s; + + if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) { + for (s = 0, i = 0; i < nwins; i++) { + /* Skip hidden windows */ + if ((cc = client_find(wins[i])) == NULL || + cc->flags & CLIENT_HIDDEN) + continue; + + cc->stackingorder = s++; + } + XFree(wins); + } +} + +struct region_ctx * +region_find(struct screen_ctx *sc, int x, int y) +{ + struct region_ctx *rc; + + TAILQ_FOREACH(rc, &sc->regionq, entry) { + if ((x >= rc->view.x) && (x < (rc->view.x + rc->view.w)) && + (y >= rc->view.y) && (y < (rc->view.y + rc->view.h))) { + break; + } + } + return rc; +} + +struct geom +screen_area(struct screen_ctx *sc, int x, int y, enum apply_gap apply_gap) +{ + struct region_ctx *rc; + struct geom area = sc->view; + + TAILQ_FOREACH(rc, &sc->regionq, entry) { + if ((x >= rc->view.x) && (x < (rc->view.x + rc->view.w)) && + (y >= rc->view.y) && (y < (rc->view.y + rc->view.h))) { + area = rc->view; + break; + } + } + if (apply_gap) + area = screen_apply_gap(sc, area, sc->which); + return area; +} + +void +screen_update_geometry(struct screen_ctx *sc) +{ + struct region_ctx *rc; + + sc->view.x = 0; + sc->view.y = 0; + sc->view.w = DisplayWidth(X_Dpy, sc->which); + sc->view.h = DisplayHeight(X_Dpy, sc->which); + sc->work = screen_apply_gap(sc, sc->view, sc->which); + + while ((rc = TAILQ_FIRST(&sc->regionq)) != NULL) { + TAILQ_REMOVE(&sc->regionq, rc, entry); + free(rc); + } + + if (Conf.xrandr) { + XRRScreenResources *sr; + XRRCrtcInfo *ci; + int i; + + sr = XRRGetScreenResources(X_Dpy, sc->rootwin); + for (i = 0, ci = NULL; i < sr->ncrtc; i++) { + ci = XRRGetCrtcInfo(X_Dpy, sr, sr->crtcs[i]); + if (ci == NULL) + continue; + if (ci->noutput == 0) { + XRRFreeCrtcInfo(ci); + continue; + } + + rc = xmalloc(sizeof(*rc)); + rc->num = i; + rc->view.x = ci->x; + rc->view.y = ci->y; + rc->view.w = ci->width; + rc->view.h = ci->height; + rc->work = screen_apply_gap(sc, rc->view, sc->which); + TAILQ_INSERT_TAIL(&sc->regionq, rc, entry); + + XRRFreeCrtcInfo(ci); + } + XRRFreeScreenResources(sr); + } else { + rc = xmalloc(sizeof(*rc)); + rc->num = 0; + rc->view.x = 0; + rc->view.y = 0; + rc->view.w = DisplayWidth(X_Dpy, sc->which); + rc->view.h = DisplayHeight(X_Dpy, sc->which); + rc->work = screen_apply_gap(sc, rc->view, sc->which); + TAILQ_INSERT_TAIL(&sc->regionq, rc, entry); + } + + xu_ewmh_net_desktop_geometry(sc); + xu_ewmh_net_desktop_viewport(sc); + xu_ewmh_net_workarea(sc); +} + +static struct geom +screen_apply_gap(struct screen_ctx *sc, struct geom geom, int which) +{ + if (which < 1) { + geom.y += sc->gap.bar; + geom.h -= (sc->gap.bar + sc->gap.bottom); + } else { + geom.y += sc->gap.top; + geom.h -= (sc->gap.top + sc->gap.bottom); + } + geom.x += sc->gap.left; + geom.w -= (sc->gap.left + sc->gap.right); + + return geom; +} + +/* Bring back clients which are beyond the screen. */ +void +screen_assert_clients_within(struct screen_ctx *sc) +{ + struct client_ctx *cc; + int top, left, right, bottom; + + TAILQ_FOREACH(cc, &sc->clientq, entry) { + if (cc->sc != sc) + continue; + top = cc->geom.y; + left = cc->geom.x; + right = cc->geom.x + cc->geom.w + (cc->bwidth * 2) - 1; + bottom = cc->geom.y + cc->geom.h + (cc->bwidth * 2) - 1; + if ((top > sc->view.h || left > sc->view.w) || + (bottom < 0 || right < 0)) { + cc->geom.x = sc->gap.left; + cc->geom.y = sc->gap.top; + client_move(cc); + } + } +} + +void +screen_prop_win_create(struct screen_ctx *sc, Window win) +{ + sc->prop.win = XCreateSimpleWindow(X_Dpy, win, 0, 0, 0, 0, 0, + sc->xftcolor[CWM_COLOR_MENU_BG].pixel |= 0xff << 24, + sc->xftcolor[CWM_COLOR_MENU_BG].pixel |= 0xff << 24); + sc->prop.xftdraw = XftDrawCreate(X_Dpy, sc->prop.win, + sc->visual, sc->colormap); + + XMapWindow(X_Dpy, sc->prop.win); +} + +void +screen_prop_win_destroy(struct screen_ctx *sc) +{ + XftDrawDestroy(sc->prop.xftdraw); + XDestroyWindow(X_Dpy, sc->prop.win); +} + +void +screen_prop_win_draw(struct screen_ctx *sc, const char *fmt, ...) +{ + va_list ap; + char *text; + XGlyphInfo extents; + + va_start(ap, fmt); + xvasprintf(&text, fmt, ap); + va_end(ap); + + XftTextExtentsUtf8(X_Dpy, sc->xftfont, (const FcChar8*)text, + strlen(text), &extents); + XResizeWindow(X_Dpy, sc->prop.win, extents.xOff, sc->xftfont->height); + XClearWindow(X_Dpy, sc->prop.win); + XftDrawStringUtf8(sc->prop.xftdraw, &sc->xftcolor[CWM_COLOR_MENU_FONT], + sc->xftfont, 0, sc->xftfont->ascent + 1, + (const FcChar8*)text, strlen(text)); + + free(text); +} diff --git a/sound.c b/sound.c new file mode 100644 index 0000000..d2ef333 --- /dev/null +++ b/sound.c @@ -0,0 +1,35 @@ +#include +#include + +#define VOL_CH "Master" + +void increaseVolume() +{ +long vol = 0; +long min, max; +snd_mixer_t *handle; +snd_mixer_selem_id_t *sid; + +snd_mixer_open(&handle, 0); +snd_mixer_attach(handle, "default"); +snd_mixer_selem_register(handle, NULL, NULL); +snd_mixer_load(handle); + +snd_mixer_selem_id_alloca(&sid); +snd_mixer_selem_id_set_index(sid, 0); +snd_mixer_selem_id_set_name(sid, VOL_CH); +snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid); + +snd_mixer_selem_get_playback_volume_range(elem, &min, &max); +snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_MONO, &vol ); +long vol_percent = vol * 100 / ( int )max; +long vol_new = vol_percent * max / 100; + +printf( "%ld%%", vol_percent ); +printf( "%ld%%", vol_new ); +int volume = vol_percent + 3; +printf( "%i", volume ); +snd_mixer_selem_set_playback_volume_all(elem, volume * max / 100); + +snd_mixer_close(handle); +} diff --git a/strlcpy.c b/strlcpy.c new file mode 100644 index 0000000..ef3a78c --- /dev/null +++ b/strlcpy.c @@ -0,0 +1,57 @@ +/* $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $ */ + +/* + * Copyright (c) 1998, 2015 Todd C. Miller + * + * 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. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */ + +#include +#include +#include "samu.h" + +#ifndef HAVE_STRLCPY + +/* + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns strlen(src); if retval >= dsize, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t dsize) +{ + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + while (*src++) + ; + } + + return(src - osrc - 1); /* count does not include NUL */ +} + +#endif /* !HAVE_STRLCPY */ diff --git a/strtonum.c b/strtonum.c new file mode 100644 index 0000000..cdd26b6 --- /dev/null +++ b/strtonum.c @@ -0,0 +1,70 @@ +/* $OpenBSD: strtonum.c,v 1.7 2013/04/17 18:40:58 tedu Exp $ */ + +/* + * 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. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/strtonum.c */ + +#ifndef HAVE_STRTONUM +#include +#include +#include + +#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; + int error = 0; + char *ep; + 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 ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) + error = TOOSMALL; + else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) + error = TOOLARGE; + } + if (errstrp != NULL) + *errstrp = ev[error].errstr; + errno = ev[error].err; + if (error) + ll = 0; + + return (ll); +} + +#endif /* HAVE_STRTONUM */ diff --git a/util.c b/util.c new file mode 100644 index 0000000..e158cd8 --- /dev/null +++ b/util.c @@ -0,0 +1,135 @@ +/* + * + * Copyright (c) 2004 Marius Aamodt Eriksen + * + * 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. + * + * $OpenBSD$ + */ + +#include +#include "queue.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "samu.h" + +static void log_msg(const char *, va_list); + +void +u_spawn(char *argstr) +{ + switch (fork()) { + case 0: + u_exec(argstr); + exit(1); + case -1: + warn("fork"); + default: + break; + } +} + +void +u_exec(char *argstr) +{ +#define MAXARGLEN 20 + char *args[MAXARGLEN], **ap = args; + char **end = &args[MAXARGLEN - 2], *tmp; + char *s = argstr; + + while (ap < end && (*ap = strsep(&argstr, " \t")) != NULL) { + if (**ap == '\0') + continue; + ap++; + if (argstr != NULL) { + /* deal with quoted strings */ + switch(argstr[0]) { + case '"': + case '\'': + if ((tmp = strchr(argstr + 1, argstr[0])) + != NULL) { + *(tmp++) = '\0'; + *(ap++) = ++argstr; + argstr = tmp; + } + break; + default: + break; + } + } + } + *ap = NULL; + + (void)setsid(); + (void)execvp(args[0], args); + warn("%s", s); +} + +char * +u_argv(char * const *argv) +{ + size_t siz = 0; + int i; + char *p; + + if (argv == 0) + return NULL; + + for (i = 0; argv[i]; i++) + siz += strlen(argv[i]) + 1; + if (siz == 0) + return NULL; + + p = xmalloc(siz); + strlcpy(p, argv[0], siz); + return p; +} + +static void +log_msg(const char *msg, va_list ap) +{ + char *fmt; + + if (asprintf(&fmt, "%s\n", msg) == -1) { + vfprintf(stderr, msg, ap); + fprintf(stderr, "\n"); + } else { + vfprintf(stderr, fmt, ap); + free(fmt); + } + fflush(stderr); +} + +void +log_debug(int level, const char *func, const char *msg, ...) +{ + char *fmt; + va_list ap; + + if (Conf.debug < level) + return; + + va_start(ap, msg); + xasprintf(&fmt, "debug%d: %s: %s", level, func, msg); + log_msg(fmt, ap); + free(fmt); + va_end(ap); +} diff --git a/xevents.c b/xevents.c new file mode 100644 index 0000000..0049eeb --- /dev/null +++ b/xevents.c @@ -0,0 +1,492 @@ +/* + * + * Copyright (c) 2004 Marius Aamodt Eriksen + * + * 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. + * + * $OpenBSD$ + */ + +/* + * NOTE: + * It is the responsibility of the caller to deal with memory + * management of the xevent's. + */ + +#include +#include "queue.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "samu.h" + +static void xev_handle_maprequest(XEvent *); +static void xev_handle_unmapnotify(XEvent *); +static void xev_handle_destroynotify(XEvent *); +static void xev_handle_configurerequest(XEvent *); +static void xev_handle_propertynotify(XEvent *); +static void xev_handle_enternotify(XEvent *); +static void xev_handle_buttonpress(XEvent *); +static void xev_handle_buttonrelease(XEvent *); +static void xev_handle_keypress(XEvent *); +static void xev_handle_keyrelease(XEvent *); +static void xev_handle_clientmessage(XEvent *); +static void xev_handle_randr(XEvent *); +static void xev_handle_mappingnotify(XEvent *); +static void xev_handle_expose(XEvent *); + +void (*xev_handlers[LASTEvent])(XEvent *) = { + [MapRequest] = xev_handle_maprequest, + [UnmapNotify] = xev_handle_unmapnotify, + [DestroyNotify] = xev_handle_destroynotify, + [ConfigureRequest] = xev_handle_configurerequest, + [PropertyNotify] = xev_handle_propertynotify, + [EnterNotify] = xev_handle_enternotify, + [ButtonPress] = xev_handle_buttonpress, + [ButtonRelease] = xev_handle_buttonrelease, + [KeyPress] = xev_handle_keypress, + [KeyRelease] = xev_handle_keyrelease, + [ClientMessage] = xev_handle_clientmessage, + [MappingNotify] = xev_handle_mappingnotify, + [Expose] = xev_handle_expose, +}; + +static KeySym modkeys[] = { XK_Alt_L, XK_Alt_R, XK_Super_L, XK_Super_R, + XK_Control_L, XK_Control_R, XK_ISO_Level3_Shift }; + +static void +xev_handle_maprequest(XEvent *ee) +{ + XMapRequestEvent *e = &ee->xmaprequest; + struct screen_ctx *sc; + struct client_ctx *cc, *old_cc; + + LOG_DEBUG3("parent: 0x%lx window: 0x%lx", e->parent, e->window); + + if ((sc = screen_find(e->parent)) == NULL) + return; + + if ((old_cc = client_current(sc)) != NULL) + client_ptr_save(old_cc); + + if ((cc = client_find(e->window)) == NULL) + cc = client_init(e->window, NULL); + + /*if ((cc != NULL) && (!(cc->flags & CLIENT_IGNORE)))*/ + /*client_ptr_warp(cc);*/ +} + +static void +xev_handle_unmapnotify(XEvent *ee) +{ + XUnmapEvent *e = &ee->xunmap; + struct client_ctx *cc; + + LOG_DEBUG3("window: 0x%lx", e->window); + + if ((cc = client_find(e->window)) != NULL) { + if (e->send_event) { + xu_set_wm_state(cc->win, WithdrawnState); + } else { + if (!(cc->flags & CLIENT_HIDDEN)) + client_remove(cc); + } + } +} + +static void +xev_handle_destroynotify(XEvent *ee) +{ + XDestroyWindowEvent *e = &ee->xdestroywindow; + struct client_ctx *cc; + + LOG_DEBUG3("window: 0x%lx", e->window); + + if ((cc = client_find(e->window)) != NULL) + client_remove(cc); +} + +static void +xev_handle_configurerequest(XEvent *ee) +{ + XConfigureRequestEvent *e = &ee->xconfigurerequest; + struct client_ctx *cc; + struct screen_ctx *sc; + XWindowChanges wc; + + LOG_DEBUG3("window: 0x%lx", e->window); + + if ((cc = client_find(e->window)) != NULL) { + sc = cc->sc; + + if (e->value_mask & CWWidth) + cc->geom.w = e->width; + if (e->value_mask & CWHeight) + cc->geom.h = e->height; + if (e->value_mask & CWX) + cc->geom.x = e->x; + if (e->value_mask & CWY) + cc->geom.y = e->y; + if (e->value_mask & CWBorderWidth) + cc->bwidth = e->border_width; + if (e->value_mask & CWSibling) + wc.sibling = e->above; + if (e->value_mask & CWStackMode) + wc.stack_mode = e->detail; + + if (cc->geom.x == 0 && cc->geom.w >= sc->view.w) + cc->geom.x -= cc->bwidth; + + if (cc->geom.y == 0 && cc->geom.h >= sc->view.h) + cc->geom.y -= cc->bwidth; + + wc.x = cc->geom.x; + wc.y = cc->geom.y; + wc.width = cc->geom.w; + wc.height = cc->geom.h; + wc.border_width = cc->bwidth; + + XConfigureWindow(X_Dpy, cc->win, e->value_mask, &wc); + client_config(cc); + } else { + /* let it do what it wants, it'll be ours when we map it. */ + wc.x = e->x; + wc.y = e->y; + wc.width = e->width; + wc.height = e->height; + wc.border_width = e->border_width; + wc.stack_mode = Above; + e->value_mask &= ~CWStackMode; + + XConfigureWindow(X_Dpy, e->window, e->value_mask, &wc); + } +} + +static void +xev_handle_propertynotify(XEvent *ee) +{ + XPropertyEvent *e = &ee->xproperty; + struct screen_ctx *sc; + struct client_ctx *cc; + + LOG_DEBUG3("window: 0x%lx", e->window); + + if ((cc = client_find(e->window)) != NULL) { + switch (e->atom) { + case XA_WM_NORMAL_HINTS: + client_get_sizehints(cc); + break; + case XA_WM_NAME: + client_set_name(cc); + break; + case XA_WM_HINTS: + client_wm_hints(cc); + client_draw_border(cc); + break; + case XA_WM_TRANSIENT_FOR: + client_transient(cc); + client_draw_border(cc); + if (cc->gc) + group_movetogroup(cc, cc->gc->num); + break; + default: + if (e->atom == ewmh[_NET_WM_NAME]) + client_set_name(cc); + break; + } + } else { + if (e->atom == ewmh[_NET_DESKTOP_NAMES]) { + if ((sc = screen_find(e->window)) != NULL) + xu_ewmh_net_desktop_names(sc); + } + } +} + +static void +xev_handle_enternotify(XEvent *ee) +{ + XCrossingEvent *e = &ee->xcrossing; + struct client_ctx *cc; + + LOG_DEBUG3("window: 0x%lx", e->window); + + Last_Event_Time = e->time; + + if ((cc = client_find(e->window)) != NULL) + client_set_active(cc); +} + +static void +xev_handle_buttonpress(XEvent *ee) +{ + XButtonEvent *e = &ee->xbutton; + struct client_ctx *cc; + struct screen_ctx *sc; + struct bind_ctx *mb; + + LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx", + e->root, e->window, e->subwindow); + + if ((sc = screen_find(e->root)) == NULL) + return; + + e->state &= ~IGNOREMODMASK; + + TAILQ_FOREACH(mb, &Conf.mousebindq, entry) { + if (e->button == mb->press.button && e->state == mb->modmask) + break; + } + if (mb == NULL) + return; + mb->cargs->xev = CWM_XEV_BTN; + switch (mb->context) { + case CWM_CONTEXT_CC: + if (((cc = client_find(e->window)) == NULL) && + ((cc = client_current(sc)) == NULL)) + return; + (*mb->callback)(cc, mb->cargs); + break; + case CWM_CONTEXT_SC: + (*mb->callback)(sc, mb->cargs); + break; + case CWM_CONTEXT_NONE: + (*mb->callback)(NULL, mb->cargs); + break; + } +} + +static void +xev_handle_buttonrelease(XEvent *ee) +{ + XButtonEvent *e = &ee->xbutton; + struct client_ctx *cc; + + LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx", + e->root, e->window, e->subwindow); + + if ((cc = client_find(e->window)) != NULL) { + if (cc->flags & (CLIENT_ACTIVE | CLIENT_HIGHLIGHT)) { + cc->flags &= ~CLIENT_HIGHLIGHT; + client_draw_border(cc); + } + } +} + +static void +xev_handle_keypress(XEvent *ee) +{ + XKeyEvent *e = &ee->xkey; + struct client_ctx *cc; + struct screen_ctx *sc; + struct bind_ctx *kb; + KeySym keysym, skeysym; + unsigned int modshift; + + LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx", + e->root, e->window, e->subwindow); + + if ((sc = screen_find(e->root)) == NULL) + return; + + keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0); + skeysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 1); + + e->state &= ~IGNOREMODMASK; + + TAILQ_FOREACH(kb, &Conf.keybindq, entry) { + if (keysym != kb->press.keysym && skeysym == kb->press.keysym) + modshift = ShiftMask; + else + modshift = 0; + + if ((kb->modmask | modshift) != e->state) + continue; + + if (kb->press.keysym == ((modshift == 0) ? keysym : skeysym)) + break; + } + if (kb == NULL) + return; + kb->cargs->xev = CWM_XEV_KEY; + switch (kb->context) { + case CWM_CONTEXT_CC: + if (((cc = client_find(e->subwindow)) == NULL) && + ((cc = client_current(sc)) == NULL)) + return; + (*kb->callback)(cc, kb->cargs); + break; + case CWM_CONTEXT_SC: + (*kb->callback)(sc, kb->cargs); + break; + case CWM_CONTEXT_NONE: + (*kb->callback)(NULL, kb->cargs); + break; + } +} + +/* + * This is only used for the modifier suppression detection. + */ +static void +xev_handle_keyrelease(XEvent *ee) +{ + XKeyEvent *e = &ee->xkey; + struct screen_ctx *sc; + struct client_ctx *cc; + KeySym keysym; + unsigned int i; + + LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx", + e->root, e->window, e->subwindow); + + if ((sc = screen_find(e->root)) == NULL) + return; + + keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0); + for (i = 0; i < nitems(modkeys); i++) { + if (keysym == modkeys[i]) { + if ((cc = client_current(sc)) != NULL) { + if (sc->cycling) { + sc->cycling = 0; + client_mtf(cc); + } + if (cc->flags & CLIENT_HIGHLIGHT) { + cc->flags &= ~CLIENT_HIGHLIGHT; + client_draw_border(cc); + } + } + XUngrabKeyboard(X_Dpy, CurrentTime); + break; + } + } +} + +static void +xev_handle_clientmessage(XEvent *ee) +{ + XClientMessageEvent *e = &ee->xclient; + struct client_ctx *cc, *old_cc; + struct screen_ctx *sc; + + LOG_DEBUG3("window: 0x%lx", e->window); + + if (e->message_type == cwmh[WM_CHANGE_STATE]) { + if ((cc = client_find(e->window)) != NULL) { + if (e->data.l[0] == IconicState) + client_hide(cc); + } + } else if (e->message_type == ewmh[_NET_CLOSE_WINDOW]) { + if ((cc = client_find(e->window)) != NULL) { + client_close(cc); + } + } else if (e->message_type == ewmh[_NET_ACTIVE_WINDOW]) { + if ((cc = client_find(e->window)) != NULL) { + if ((old_cc = client_current(NULL)) != NULL) + client_ptr_save(old_cc); + client_show(cc); + /*client_ptr_warp(cc);*/ + } + } else if (e->message_type == ewmh[_NET_WM_DESKTOP]) { + if ((cc = client_find(e->window)) != NULL) { + /* + * The EWMH spec states that if the cardinal returned + * is 0xFFFFFFFF (-1) then the window should appear + * on all desktops, in our case, group 0. + */ + if (e->data.l[0] == (unsigned long)-1) + group_movetogroup(cc, 0); + else + if (e->data.l[0] >= 0 && + e->data.l[0] < Conf.ngroups) + group_movetogroup(cc, e->data.l[0]); + } + } else if (e->message_type == ewmh[_NET_WM_STATE]) { + if ((cc = client_find(e->window)) != NULL) { + xu_ewmh_handle_net_wm_state_msg(cc, + e->data.l[0], e->data.l[1], e->data.l[2]); + } + } else if (e->message_type == ewmh[_NET_CURRENT_DESKTOP]) { + if ((sc = screen_find(e->window)) != NULL) { + if (e->data.l[0] >= 0 && + e->data.l[0] < Conf.ngroups) + group_only(sc, e->data.l[0]); + } + } +} + +static void +xev_handle_randr(XEvent *ee) +{ + XRRScreenChangeNotifyEvent *e = (XRRScreenChangeNotifyEvent *)ee; + struct screen_ctx *sc; + + LOG_DEBUG3("size: %d/%d", e->width, e->height); + + if ((sc = screen_find(e->root)) == NULL) + return; + + XRRUpdateConfiguration(ee); + screen_update_geometry(sc); + screen_assert_clients_within(sc); +} + +/* + * Called when the keymap has changed. + * Ungrab all keys, reload keymap and then regrab + */ +static void +xev_handle_mappingnotify(XEvent *ee) +{ + XMappingEvent *e = &ee->xmapping; + struct screen_ctx *sc; + + LOG_DEBUG3("window: 0x%lx", e->window); + + XRefreshKeyboardMapping(e); + if (e->request == MappingKeyboard) { + TAILQ_FOREACH(sc, &Screenq, entry) + conf_grab_kbd(sc->rootwin); + } +} + +static void +xev_handle_expose(XEvent *ee) +{ + XExposeEvent *e = &ee->xexpose; + struct client_ctx *cc; + + LOG_DEBUG3("window: 0x%lx", e->window); + + if ((cc = client_find(e->window)) != NULL && e->count == 0) + client_draw_border(cc); +} + +void +xev_process(void) +{ + XEvent e; + + while (XPending(X_Dpy)) { + XNextEvent(X_Dpy, &e); + if ((e.type - Conf.xrandr_event_base) == RRScreenChangeNotify) + xev_handle_randr(&e); + else if ((e.type < LASTEvent) && (xev_handlers[e.type] != NULL)) + (*xev_handlers[e.type])(&e); + } +} diff --git a/xmalloc.c b/xmalloc.c new file mode 100644 index 0000000..52cf6b1 --- /dev/null +++ b/xmalloc.c @@ -0,0 +1,109 @@ +/* + * + * Copyright (c) 2004 Marius Aamodt Eriksen + * + * 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. + * + * $OpenBSD$ + */ + +#include +#include "queue.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "samu.h" + +void * +xmalloc(size_t siz) +{ + void *p; + + if (siz == 0) + errx(1, "xmalloc: zero size"); + if ((p = malloc(siz)) == NULL) + err(1, "malloc"); + + return p; +} + +void * +xcalloc(size_t no, size_t siz) +{ + void *p; + + if (siz == 0 || no == 0) + errx(1, "xcalloc: zero size"); + if (SIZE_MAX / no < siz) + errx(1, "xcalloc: no * siz > SIZE_MAX"); + if ((p = calloc(no, siz)) == NULL) + err(1, "calloc"); + + return p; +} + +void * +xreallocarray(void *ptr, size_t nmemb, size_t size) +{ + void *p; + + p = reallocarray(ptr, nmemb, size); + if (p == NULL) + errx(1, "xreallocarray: out of memory (new_size %zu bytes)", + nmemb * size); + return p; +} + +char * +xstrdup(const char *str) +{ + char *p; + + if ((p = strdup(str)) == NULL) + err(1, "strdup"); + + return p; +} + +int +xasprintf(char **ret, const char *fmt, ...) +{ + va_list ap; + int i; + + va_start(ap, fmt); + i = xvasprintf(ret, fmt, ap); + va_end(ap); + + return i; +} + +int +xvasprintf(char **ret, const char *fmt, va_list ap) +{ + int i; + + i = vasprintf(ret, fmt, ap); + if (i == -1) + err(1, "vasprintf"); + + return i; +} diff --git a/xutil.c b/xutil.c new file mode 100644 index 0000000..b904e54 --- /dev/null +++ b/xutil.c @@ -0,0 +1,567 @@ +/* + * + * Copyright (c) 2004 Marius Aamodt Eriksen + * + * 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. + * + * $OpenBSD$ + */ + +#include +#include "queue.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "samu.h" + +void +xu_ptr_get(Window win, int *x, int *y) +{ + Window w0, w1; + int tmp0, tmp1; + unsigned int tmp2; + + XQueryPointer(X_Dpy, win, &w0, &w1, &tmp0, &tmp1, x, y, &tmp2); +} + +void +xu_ptr_set(Window win, int x, int y) +{ + XWarpPointer(X_Dpy, None, win, 0, 0, 0, 0, x, y); +} + +int +xu_get_prop(Window win, Atom atm, Atom type, long len, unsigned char **p) +{ + Atom realtype; + unsigned long n, extra; + int format; + + if (XGetWindowProperty(X_Dpy, win, atm, 0L, len, False, type, + &realtype, &format, &n, &extra, p) != Success || *p == NULL) + return -1; + + if (n == 0) + XFree(*p); + + return n; +} + +int +xu_get_strprop(Window win, Atom atm, char **text) { + XTextProperty prop; + char **list; + int nitems = 0; + + *text = NULL; + + XGetTextProperty(X_Dpy, win, &prop, atm); + if (!prop.nitems) { + XFree(prop.value); + return 0; + } + + if (Xutf8TextPropertyToTextList(X_Dpy, &prop, &list, + &nitems) == Success && nitems > 0 && *list) { + if (nitems > 1) { + XTextProperty prop2; + if (Xutf8TextListToTextProperty(X_Dpy, list, nitems, + XUTF8StringStyle, &prop2) == Success) { + *text = xstrdup((const char *)prop2.value); + XFree(prop2.value); + } + } else { + *text = xstrdup(*list); + } + XFreeStringList(list); + } + XFree(prop.value); + + return nitems; +} + +void +xu_send_clientmsg(Window win, Atom proto, Time ts) +{ + XClientMessageEvent cm; + + (void)memset(&cm, 0, sizeof(cm)); + cm.type = ClientMessage; + cm.window = win; + cm.message_type = cwmh[WM_PROTOCOLS]; + cm.format = 32; + cm.data.l[0] = proto; + cm.data.l[1] = ts; + + XSendEvent(X_Dpy, win, False, NoEventMask, (XEvent *)&cm); +} + +void +xu_get_wm_state(Window win, long *state) +{ + long *p; + + *state = -1; + if (xu_get_prop(win, cwmh[WM_STATE], cwmh[WM_STATE], 2L, + (unsigned char **)&p) > 0) { + *state = *p; + XFree(p); + } +} + +void +xu_set_wm_state(Window win, long state) +{ + long data[] = { state, None }; + + XChangeProperty(X_Dpy, win, cwmh[WM_STATE], cwmh[WM_STATE], 32, + PropModeReplace, (unsigned char *)data, 2); +} +void +xu_xorcolor(XftColor a, XftColor b, XftColor *r) +{ + r->pixel = a.pixel ^ b.pixel; + r->color.red = a.color.red ^ b.color.red; + r->color.green = a.color.green ^ b.color.green; + r->color.blue = a.color.blue ^ b.color.blue; + r->color.alpha = 0xffff; +} + +void +xu_atom_init(void) +{ + char *cwmhints[] = { + "WM_STATE", + "WM_DELETE_WINDOW", + "WM_TAKE_FOCUS", + "WM_PROTOCOLS", + "_MOTIF_WM_HINTS", + "UTF8_STRING", + "WM_CHANGE_STATE", + }; + char *ewmhints[] = { + "_NET_SUPPORTED", + "_NET_SUPPORTING_WM_CHECK", + "_NET_ACTIVE_WINDOW", + "_NET_CLIENT_LIST", + "_NET_CLIENT_LIST_STACKING", + "_NET_NUMBER_OF_DESKTOPS", + "_NET_CURRENT_DESKTOP", + "_NET_DESKTOP_VIEWPORT", + "_NET_DESKTOP_GEOMETRY", + "_NET_VIRTUAL_ROOTS", + "_NET_SHOWING_DESKTOP", + "_NET_DESKTOP_NAMES", + "_NET_WORKAREA", + "_NET_WM_NAME", + "_NET_WM_DESKTOP", + "_NET_CLOSE_WINDOW", + "_NET_WM_STATE", + "_NET_WM_STATE_STICKY", + "_NET_WM_STATE_MAXIMIZED_VERT", + "_NET_WM_STATE_MAXIMIZED_HORZ", + "_NET_WM_STATE_HIDDEN", + "_NET_WM_STATE_FULLSCREEN", + "_NET_WM_STATE_DEMANDS_ATTENTION", + "_NET_WM_STATE_SKIP_PAGER", + "_NET_WM_STATE_SKIP_TASKBAR", + "_CWM_WM_STATE_FREEZE", + }; + + XInternAtoms(X_Dpy, cwmhints, nitems(cwmhints), False, cwmh); + XInternAtoms(X_Dpy, ewmhints, nitems(ewmhints), False, ewmh); +} + +/* Root Window Properties */ +void +xu_ewmh_net_supported(struct screen_ctx *sc) +{ + XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTED], + XA_ATOM, 32, PropModeReplace, (unsigned char *)ewmh, EWMH_NITEMS); +} + +void +xu_ewmh_net_supported_wm_check(struct screen_ctx *sc) +{ + Window w; + + w = XCreateSimpleWindow(X_Dpy, sc->rootwin, -1, -1, 1, 1, 0, 0, 0); + XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTING_WM_CHECK], + XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1); + XChangeProperty(X_Dpy, w, ewmh[_NET_SUPPORTING_WM_CHECK], + XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1); + XChangeProperty(X_Dpy, w, ewmh[_NET_WM_NAME], + cwmh[UTF8_STRING], 8, PropModeReplace, + (unsigned char *)Conf.wmname, strlen(Conf.wmname)); +} + +void +xu_ewmh_net_desktop_geometry(struct screen_ctx *sc) +{ + long geom[2] = { sc->view.w, sc->view.h }; + + XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_GEOMETRY], + XA_CARDINAL, 32, PropModeReplace, (unsigned char *)geom , 2); +} + +void +xu_ewmh_net_desktop_viewport(struct screen_ctx *sc) +{ + long viewports[2] = {0, 0}; + + /* We don't support large desktops, so this is (0, 0). */ + XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_VIEWPORT], + XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2); +} + +void +xu_ewmh_net_workarea(struct screen_ctx *sc) +{ + unsigned long *workarea; + int i, ngroups = Conf.ngroups; + + workarea = xreallocarray(NULL, ngroups * 4, sizeof(unsigned long)); + for (i = 0; i < ngroups; i++) { + workarea[4 * i + 0] = sc->work.x; + workarea[4 * i + 1] = sc->work.y; + workarea[4 * i + 2] = sc->work.w; + workarea[4 * i + 3] = sc->work.h; + } + XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_WORKAREA], + XA_CARDINAL, 32, PropModeReplace, (unsigned char *)workarea, + ngroups * 4); + free(workarea); +} + +void +xu_ewmh_net_client_list(struct screen_ctx *sc) +{ + struct client_ctx *cc; + Window *winlist; + int i = 0, j = 0; + + TAILQ_FOREACH(cc, &sc->clientq, entry) + i++; + if (i == 0) + return; + + winlist = xreallocarray(NULL, i, sizeof(*winlist)); + TAILQ_FOREACH(cc, &sc->clientq, entry) + winlist[j++] = cc->win; + XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CLIENT_LIST], + XA_WINDOW, 32, PropModeReplace, (unsigned char *)winlist, i); + free(winlist); +} + +void +xu_ewmh_net_client_list_stacking(struct screen_ctx *sc) +{ + struct client_ctx *cc; + Window *winlist; + int i = 0, j; + + TAILQ_FOREACH(cc, &sc->clientq, entry) + i++; + if (i == 0) + return; + + j = i; + winlist = xreallocarray(NULL, i, sizeof(*winlist)); + TAILQ_FOREACH(cc, &sc->clientq, entry) + winlist[--j] = cc->win; + XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CLIENT_LIST_STACKING], + XA_WINDOW, 32, PropModeReplace, (unsigned char *)winlist, i); + free(winlist); +} + +void +xu_ewmh_net_active_window(struct screen_ctx *sc, Window w) +{ + XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_ACTIVE_WINDOW], + XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1); +} + +void +xu_ewmh_net_number_of_desktops(struct screen_ctx *sc) +{ + long ndesks = Conf.ngroups; + + XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_NUMBER_OF_DESKTOPS], + XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&ndesks, 1); +} + +void +xu_ewmh_net_showing_desktop(struct screen_ctx *sc) +{ + long zero = 0; + + /* We don't support `showing desktop' mode, so this is zero. + * Note that when we hide all groups, or when all groups are + * hidden we could technically set this later on. + */ + XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SHOWING_DESKTOP], + XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&zero, 1); +} + +void +xu_ewmh_net_virtual_roots(struct screen_ctx *sc) +{ + /* We don't support virtual roots, so delete if set by previous wm. */ + XDeleteProperty(X_Dpy, sc->rootwin, ewmh[_NET_VIRTUAL_ROOTS]); +} + +void +xu_ewmh_net_current_desktop(struct screen_ctx *sc) +{ + long num = sc->group_active->num; + + XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CURRENT_DESKTOP], + XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1); +} + +void +xu_ewmh_net_desktop_names(struct screen_ctx *sc) +{ + struct group_ctx *gc; + char *p, *q; + unsigned char *prop_ret; + int i = 0, j = 0, nstrings = 0, n = 0; + size_t len = 0, tlen, slen; + + /* Let group names be overwritten if _NET_DESKTOP_NAMES is set. */ + + if ((j = xu_get_prop(sc->rootwin, ewmh[_NET_DESKTOP_NAMES], + cwmh[UTF8_STRING], 0xffffff, (unsigned char **)&prop_ret)) > 0) { + prop_ret[j - 1] = '\0'; /* paranoia */ + while (i < j) { + if (prop_ret[i++] == '\0') + nstrings++; + } + } + + p = (char *)prop_ret; + while (n < nstrings) { + TAILQ_FOREACH(gc, &sc->groupq, entry) { + if (gc->num == n) { + free(gc->name); + gc->name = xstrdup(p); + p += strlen(p) + 1; + break; + } + } + n++; + } + if (prop_ret != NULL) + XFree(prop_ret); + + TAILQ_FOREACH(gc, &sc->groupq, entry) + len += strlen(gc->name) + 1; + q = p = xreallocarray(NULL, len, sizeof(*p)); + + tlen = len; + TAILQ_FOREACH(gc, &sc->groupq, entry) { + slen = strlen(gc->name) + 1; + (void)strlcpy(q, gc->name, tlen); + tlen -= slen; + q += slen; + } + + XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_NAMES], + cwmh[UTF8_STRING], 8, PropModeReplace, (unsigned char *)p, len); + free(p); +} + +/* Application Window Properties */ +int +xu_ewmh_get_net_wm_desktop(struct client_ctx *cc, long *n) +{ + long *p; + + if (xu_get_prop(cc->win, ewmh[_NET_WM_DESKTOP], XA_CARDINAL, 1L, + (unsigned char **)&p) <= 0) + return 0; + *n = *p; + XFree(p); + return 1; +} + +void +xu_ewmh_set_net_wm_desktop(struct client_ctx *cc) +{ + long num = 0xffffffff; + + if (cc->gc) + num = cc->gc->num; + + XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_DESKTOP], + XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1); +} + +Atom * +xu_ewmh_get_net_wm_state(struct client_ctx *cc, int *n) +{ + Atom *state, *p = NULL; + + if ((*n = xu_get_prop(cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 64L, + (unsigned char **)&p)) <= 0) + return NULL; + + state = xreallocarray(NULL, *n, sizeof(Atom)); + (void)memcpy(state, p, *n * sizeof(Atom)); + XFree((char *)p); + + return state; +} + +void +xu_ewmh_handle_net_wm_state_msg(struct client_ctx *cc, int action, + Atom first, Atom second) +{ + unsigned int i; + struct handlers { + Atom atom; + int flag; + void (*toggle)(struct client_ctx *); + } handlers[] = { + { _NET_WM_STATE_STICKY, + CLIENT_STICKY, + client_toggle_sticky }, + { _NET_WM_STATE_MAXIMIZED_VERT, + CLIENT_VMAXIMIZED, + client_toggle_vmaximize }, + { _NET_WM_STATE_MAXIMIZED_HORZ, + CLIENT_HMAXIMIZED, + client_toggle_hmaximize }, + { _NET_WM_STATE_HIDDEN, + CLIENT_HIDDEN, + client_toggle_hidden }, + { _NET_WM_STATE_FULLSCREEN, + CLIENT_FULLSCREEN, + client_toggle_fullscreen }, + { _NET_WM_STATE_DEMANDS_ATTENTION, + CLIENT_URGENCY, + client_urgency }, + { _NET_WM_STATE_SKIP_PAGER, + CLIENT_SKIP_PAGER, + client_toggle_skip_pager}, + { _NET_WM_STATE_SKIP_TASKBAR, + CLIENT_SKIP_TASKBAR, + client_toggle_skip_taskbar}, + { _CWM_WM_STATE_FREEZE, + CLIENT_FREEZE, + client_toggle_freeze }, + }; + + for (i = 0; i < nitems(handlers); i++) { + if (first != ewmh[handlers[i].atom] && + second != ewmh[handlers[i].atom]) + continue; + switch (action) { + case _NET_WM_STATE_ADD: + if (!(cc->flags & handlers[i].flag)) + handlers[i].toggle(cc); + break; + case _NET_WM_STATE_REMOVE: + if (cc->flags & handlers[i].flag) + handlers[i].toggle(cc); + break; + case _NET_WM_STATE_TOGGLE: + handlers[i].toggle(cc); + } + } +} + +void +xu_ewmh_restore_net_wm_state(struct client_ctx *cc) +{ + Atom *atoms; + int i, n; + + atoms = xu_ewmh_get_net_wm_state(cc, &n); + for (i = 0; i < n; i++) { + if (atoms[i] == ewmh[_NET_WM_STATE_STICKY]) + client_toggle_sticky(cc); + if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_VERT]) + client_toggle_vmaximize(cc); + if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_HORZ]) + client_toggle_hmaximize(cc); + if (atoms[i] == ewmh[_NET_WM_STATE_HIDDEN]) + client_toggle_hidden(cc); + if (atoms[i] == ewmh[_NET_WM_STATE_FULLSCREEN]) + client_toggle_fullscreen(cc); + if (atoms[i] == ewmh[_NET_WM_STATE_DEMANDS_ATTENTION]) + client_urgency(cc); + if (atoms[i] == ewmh[_NET_WM_STATE_SKIP_PAGER]) + client_toggle_skip_pager(cc); + if (atoms[i] == ewmh[_NET_WM_STATE_SKIP_TASKBAR]) + client_toggle_skip_taskbar(cc); + if (atoms[i] == ewmh[_CWM_WM_STATE_FREEZE]) + client_toggle_freeze(cc); + } + free(atoms); +} + +void +xu_ewmh_set_net_wm_state(struct client_ctx *cc) +{ + Atom *atoms, *oatoms; + int n, i, j; + + oatoms = xu_ewmh_get_net_wm_state(cc, &n); + atoms = xreallocarray(NULL, (n + _NET_WM_STATES_NITEMS), sizeof(Atom)); + for (i = j = 0; i < n; i++) { + if (oatoms[i] != ewmh[_NET_WM_STATE_STICKY] && + oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_VERT] && + oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_HORZ] && + oatoms[i] != ewmh[_NET_WM_STATE_HIDDEN] && + oatoms[i] != ewmh[_NET_WM_STATE_FULLSCREEN] && + oatoms[i] != ewmh[_NET_WM_STATE_DEMANDS_ATTENTION] && + oatoms[i] != ewmh[_NET_WM_STATE_SKIP_PAGER] && + oatoms[i] != ewmh[_NET_WM_STATE_SKIP_TASKBAR] && + oatoms[i] != ewmh[_CWM_WM_STATE_FREEZE]) + atoms[j++] = oatoms[i]; + } + free(oatoms); + if (cc->flags & CLIENT_STICKY) + atoms[j++] = ewmh[_NET_WM_STATE_STICKY]; + if (cc->flags & CLIENT_HIDDEN) + atoms[j++] = ewmh[_NET_WM_STATE_HIDDEN]; + if (cc->flags & CLIENT_FULLSCREEN) + atoms[j++] = ewmh[_NET_WM_STATE_FULLSCREEN]; + else { + if (cc->flags & CLIENT_VMAXIMIZED) + atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_VERT]; + if (cc->flags & CLIENT_HMAXIMIZED) + atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_HORZ]; + } + if (cc->flags & CLIENT_URGENCY) + atoms[j++] = ewmh[_NET_WM_STATE_DEMANDS_ATTENTION]; + if (cc->flags & CLIENT_SKIP_PAGER) + atoms[j++] = ewmh[_NET_WM_STATE_SKIP_PAGER]; + if (cc->flags & CLIENT_SKIP_TASKBAR) + atoms[j++] = ewmh[_NET_WM_STATE_SKIP_TASKBAR]; + if (cc->flags & CLIENT_FREEZE) + atoms[j++] = ewmh[_CWM_WM_STATE_FREEZE]; + if (j > 0) + XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE], + XA_ATOM, 32, PropModeReplace, (unsigned char *)atoms, j); + else + XDeleteProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE]); + free(atoms); +} diff --git a/y.tab.c b/y.tab.c new file mode 100644 index 0000000..1670f86 --- /dev/null +++ b/y.tab.c @@ -0,0 +1,2195 @@ +/* A Bison parser, made by GNU Bison 3.7.6. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output, and Bison version. */ +#define YYBISON 30706 + +/* Bison version string. */ +#define YYBISON_VERSION "3.7.6" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* First part of user prologue. */ +#line 22 "parse.y" + + +#include +#include "queue.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "samu.h" + +#define YYSTYPE_IS_DECLARED + +TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); +static struct file { + TAILQ_ENTRY(file) entry; + FILE *stream; + char *name; + int lineno; + int errors; +} *file, *topfile; +struct file *pushfile(const char *, FILE *); +int popfile(void); +int yyparse(void); +int yylex(void); +int yyerror(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) + __attribute__((__nonnull__ (1))); +int kw_cmp(const void *, const void *); +int lookup(char *); +int lgetc(int); +int lungetc(int); +int findeol(void); + +static struct conf *conf; + +typedef struct { + union { + int64_t number; + char *string; + } v; + int lineno; +} YYSTYPE; + + +#line 122 "y.tab.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + + +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + BINDKEY = 258, /* BINDKEY */ + UNBINDKEY = 259, /* UNBINDKEY */ + BINDMOUSE = 260, /* BINDMOUSE */ + UNBINDMOUSE = 261, /* UNBINDMOUSE */ + FONTNAME = 262, /* FONTNAME */ + STICKY = 263, /* STICKY */ + GAP = 264, /* GAP */ + AUTOGROUP = 265, /* AUTOGROUP */ + COMMAND = 266, /* COMMAND */ + IGNORE = 267, /* IGNORE */ + WM = 268, /* WM */ + YES = 269, /* YES */ + NO = 270, /* NO */ + BORDERWIDTH = 271, /* BORDERWIDTH */ + MOVEAMOUNT = 272, /* MOVEAMOUNT */ + HTILE = 273, /* HTILE */ + VTILE = 274, /* VTILE */ + TILEGAP = 275, /* TILEGAP */ + COLOR = 276, /* COLOR */ + SNAPDIST = 277, /* SNAPDIST */ + ACTIVEBORDER = 278, /* ACTIVEBORDER */ + INACTIVEBORDER = 279, /* INACTIVEBORDER */ + URGENCYBORDER = 280, /* URGENCYBORDER */ + GROUPBORDER = 281, /* GROUPBORDER */ + UNGROUPBORDER = 282, /* UNGROUPBORDER */ + MENUBG = 283, /* MENUBG */ + MENUFG = 284, /* MENUFG */ + FONTCOLOR = 285, /* FONTCOLOR */ + FONTSELCOLOR = 286, /* FONTSELCOLOR */ + ERROR = 287, /* ERROR */ + RESIZEALLCORNERS = 288, /* RESIZEALLCORNERS */ + STRING = 289, /* STRING */ + NUMBER = 290 /* NUMBER */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif +/* Token kinds. */ +#define YYEMPTY -2 +#define YYEOF 0 +#define YYerror 256 +#define YYUNDEF 257 +#define BINDKEY 258 +#define UNBINDKEY 259 +#define BINDMOUSE 260 +#define UNBINDMOUSE 261 +#define FONTNAME 262 +#define STICKY 263 +#define GAP 264 +#define AUTOGROUP 265 +#define COMMAND 266 +#define IGNORE 267 +#define WM 268 +#define YES 269 +#define NO 270 +#define BORDERWIDTH 271 +#define MOVEAMOUNT 272 +#define HTILE 273 +#define VTILE 274 +#define TILEGAP 275 +#define COLOR 276 +#define SNAPDIST 277 +#define ACTIVEBORDER 278 +#define INACTIVEBORDER 279 +#define URGENCYBORDER 280 +#define GROUPBORDER 281 +#define UNGROUPBORDER 282 +#define MENUBG 283 +#define MENUFG 284 +#define FONTCOLOR 285 +#define FONTSELCOLOR 286 +#define ERROR 287 +#define RESIZEALLCORNERS 288 +#define STRING 289 +#define NUMBER 290 + +/* Value type. */ + + +extern YYSTYPE yylval; + +int yyparse (void); + + +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_BINDKEY = 3, /* BINDKEY */ + YYSYMBOL_UNBINDKEY = 4, /* UNBINDKEY */ + YYSYMBOL_BINDMOUSE = 5, /* BINDMOUSE */ + YYSYMBOL_UNBINDMOUSE = 6, /* UNBINDMOUSE */ + YYSYMBOL_FONTNAME = 7, /* FONTNAME */ + YYSYMBOL_STICKY = 8, /* STICKY */ + YYSYMBOL_GAP = 9, /* GAP */ + YYSYMBOL_AUTOGROUP = 10, /* AUTOGROUP */ + YYSYMBOL_COMMAND = 11, /* COMMAND */ + YYSYMBOL_IGNORE = 12, /* IGNORE */ + YYSYMBOL_WM = 13, /* WM */ + YYSYMBOL_YES = 14, /* YES */ + YYSYMBOL_NO = 15, /* NO */ + YYSYMBOL_BORDERWIDTH = 16, /* BORDERWIDTH */ + YYSYMBOL_MOVEAMOUNT = 17, /* MOVEAMOUNT */ + YYSYMBOL_HTILE = 18, /* HTILE */ + YYSYMBOL_VTILE = 19, /* VTILE */ + YYSYMBOL_TILEGAP = 20, /* TILEGAP */ + YYSYMBOL_COLOR = 21, /* COLOR */ + YYSYMBOL_SNAPDIST = 22, /* SNAPDIST */ + YYSYMBOL_ACTIVEBORDER = 23, /* ACTIVEBORDER */ + YYSYMBOL_INACTIVEBORDER = 24, /* INACTIVEBORDER */ + YYSYMBOL_URGENCYBORDER = 25, /* URGENCYBORDER */ + YYSYMBOL_GROUPBORDER = 26, /* GROUPBORDER */ + YYSYMBOL_UNGROUPBORDER = 27, /* UNGROUPBORDER */ + YYSYMBOL_MENUBG = 28, /* MENUBG */ + YYSYMBOL_MENUFG = 29, /* MENUFG */ + YYSYMBOL_FONTCOLOR = 30, /* FONTCOLOR */ + YYSYMBOL_FONTSELCOLOR = 31, /* FONTSELCOLOR */ + YYSYMBOL_ERROR = 32, /* ERROR */ + YYSYMBOL_RESIZEALLCORNERS = 33, /* RESIZEALLCORNERS */ + YYSYMBOL_STRING = 34, /* STRING */ + YYSYMBOL_NUMBER = 35, /* NUMBER */ + YYSYMBOL_36_n_ = 36, /* '\n' */ + YYSYMBOL_37_ = 37, /* ',' */ + YYSYMBOL_YYACCEPT = 38, /* $accept */ + YYSYMBOL_grammar = 39, /* grammar */ + YYSYMBOL_string = 40, /* string */ + YYSYMBOL_yesno = 41, /* yesno */ + YYSYMBOL_main = 42, /* main */ + YYSYMBOL_color = 43, /* color */ + YYSYMBOL_colors = 44 /* colors */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +/* Work around bug in HP-UX 11.23, which defines these macros + incorrectly for preprocessor constants. This workaround can likely + be removed in 2023, as HPE has promised support for HP-UX 11.23 + (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of + . */ +#ifdef __hpux +# undef UINT_LEAST8_MAX +# undef UINT_LEAST16_MAX +# define UINT_LEAST8_MAX 255 +# define UINT_LEAST16_MAX 65535 +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if !defined yyoverflow + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* !defined yyoverflow */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 78 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 38 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 7 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 39 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 81 + +/* YYMAXUTOK -- Last valid token kind. */ +#define YYMAXUTOK 290 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 36, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 37, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 91, 91, 92, 93, 94, 95, 98, 108, 111, + 112, 115, 119, 122, 129, 136, 143, 150, 157, 164, + 175, 186, 195, 206, 210, 224, 234, 242, 252, 260, + 265, 268, 272, 276, 280, 284, 288, 292, 296, 300 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if YYDEBUG || 0 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "BINDKEY", "UNBINDKEY", + "BINDMOUSE", "UNBINDMOUSE", "FONTNAME", "STICKY", "GAP", "AUTOGROUP", + "COMMAND", "IGNORE", "WM", "YES", "NO", "BORDERWIDTH", "MOVEAMOUNT", + "HTILE", "VTILE", "TILEGAP", "COLOR", "SNAPDIST", "ACTIVEBORDER", + "INACTIVEBORDER", "URGENCYBORDER", "GROUPBORDER", "UNGROUPBORDER", + "MENUBG", "MENUFG", "FONTCOLOR", "FONTSELCOLOR", "ERROR", + "RESIZEALLCORNERS", "STRING", "NUMBER", "'\\n'", "','", "$accept", + "grammar", "string", "yesno", "main", "color", "colors", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 10, 44 +}; +#endif + +#define YYPACT_NINF (-35) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-1) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + -35, 0, -35, -34, -20, -10, -6, -5, -4, 12, + -3, -1, 1, 13, 15, 11, 16, 17, 18, 19, + 14, 20, 12, -35, 21, 22, -35, 25, -35, 25, + -35, -35, -35, -35, -35, 26, 28, 25, -35, 25, + -35, -35, -35, -35, -35, 29, 30, 31, 32, 33, + 34, 35, 36, 37, -35, -35, -35, -35, -35, -35, + 38, 38, 39, 23, 38, 38, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, 40, 42, 43, -35, + -35 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 0, 0, 6, 0, 26, 0, + 28, 11, 9, 10, 12, 0, 0, 0, 23, 0, + 13, 17, 15, 16, 14, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 30, 18, 29, 4, 5, 8, + 25, 27, 0, 21, 19, 20, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 7, 0, 0, 0, 22, + 24 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -35, -35, -14, 9, -35, -35, -35 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + 0, 1, 60, 34, 24, 25, 54 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 yytable[] = +{ + 2, 3, 26, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 27, 61, 15, 16, 17, 18, + 19, 20, 21, 64, 28, 65, 32, 33, 29, 30, + 31, 56, 35, 22, 36, 37, 23, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 40, 38, 0, 39, + 0, 41, 42, 43, 44, 55, 0, 57, 58, 59, + 77, 62, 63, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 0, 76, 78, 79, 0, 80 +}; + +static const yytype_int8 yycheck[] = +{ + 0, 1, 36, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 34, 29, 16, 17, 18, 19, + 20, 21, 22, 37, 34, 39, 14, 15, 34, 34, + 34, 22, 35, 33, 35, 34, 36, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 35, 34, -1, 34, + -1, 35, 35, 35, 35, 35, -1, 36, 36, 34, + 37, 35, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, -1, 35, 35, 34, -1, 35 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 39, 0, 1, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, + 21, 22, 33, 36, 42, 43, 36, 34, 34, 34, + 34, 34, 14, 15, 41, 35, 35, 34, 34, 34, + 35, 35, 35, 35, 35, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 44, 35, 41, 36, 36, 34, + 40, 40, 35, 34, 40, 40, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 35, 37, 35, 34, + 35 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 yyr1[] = +{ + 0, 38, 39, 39, 39, 39, 39, 40, 40, 41, + 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 43, 44, 44, 44, 44, 44, 44, 44, 44, 44 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 0, 2, 3, 3, 3, 2, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 5, 2, 6, 3, 2, 3, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +/* This macro is provided for backward compatibility. */ +# ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep) +{ + FILE *yyoutput = yyo; + YY_USE (yyoutput); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yykind < YYNTOKENS) + YYPRINT (yyo, yytoknum[yykind], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + yy_symbol_value_print (yyo, yykind, yyvaluep); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, + int yyrule) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)]); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + + + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep) +{ + YY_USE (yyvaluep); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/* Lookahead token kind. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; +/* Number of syntax errors so far. */ +int yynerrs; + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (void) +{ + yy_state_fast_t yystate = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (); + } + + if (yychar <= YYEOF) + { + yychar = YYEOF; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + goto yyerrlab1; + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 6: /* grammar: grammar error '\n' */ +#line 95 "parse.y" + { file->errors++; } +#line 1306 "y.tab.c" + break; + + case 7: /* string: string STRING */ +#line 98 "parse.y" + { + if (asprintf(&(yyval.v.string), "%s %s", (yyvsp[-1].v.string), (yyvsp[0].v.string)) == -1) { + free((yyvsp[-1].v.string)); + free((yyvsp[0].v.string)); + yyerror("string: asprintf"); + YYERROR; + } + free((yyvsp[-1].v.string)); + free((yyvsp[0].v.string)); + } +#line 1321 "y.tab.c" + break; + + case 9: /* yesno: YES */ +#line 111 "parse.y" + { (yyval.v.number) = 1; } +#line 1327 "y.tab.c" + break; + + case 10: /* yesno: NO */ +#line 112 "parse.y" + { (yyval.v.number) = 0; } +#line 1333 "y.tab.c" + break; + + case 11: /* main: FONTNAME STRING */ +#line 115 "parse.y" + { + free(conf->font); + conf->font = (yyvsp[0].v.string); + } +#line 1342 "y.tab.c" + break; + + case 12: /* main: STICKY yesno */ +#line 119 "parse.y" + { + conf->stickygroups = (yyvsp[0].v.number); + } +#line 1350 "y.tab.c" + break; + + case 13: /* main: BORDERWIDTH NUMBER */ +#line 122 "parse.y" + { + if ((yyvsp[0].v.number) < 0 || (yyvsp[0].v.number) > INT_MAX) { + yyerror("invalid borderwidth"); + YYERROR; + } + conf->bwidth = (yyvsp[0].v.number); + } +#line 1362 "y.tab.c" + break; + + case 14: /* main: TILEGAP NUMBER */ +#line 129 "parse.y" + { + if ((yyvsp[0].v.number) < 0 || (yyvsp[0].v.number) > INT_MAX) { + yyerror("invalid tilegap"); + YYERROR; + } + conf->tgap = (yyvsp[0].v.number); + } +#line 1374 "y.tab.c" + break; + + case 15: /* main: HTILE NUMBER */ +#line 136 "parse.y" + { + if ((yyvsp[0].v.number) < 0 || (yyvsp[0].v.number) > 99) { + yyerror("invalid htile percent"); + YYERROR; + } + conf->htile = (yyvsp[0].v.number); + } +#line 1386 "y.tab.c" + break; + + case 16: /* main: VTILE NUMBER */ +#line 143 "parse.y" + { + if ((yyvsp[0].v.number) < 0 || (yyvsp[0].v.number) > 99) { + yyerror("invalid vtile percent"); + YYERROR; + } + conf->vtile = (yyvsp[0].v.number); + } +#line 1398 "y.tab.c" + break; + + case 17: /* main: MOVEAMOUNT NUMBER */ +#line 150 "parse.y" + { + if ((yyvsp[0].v.number) < 0 || (yyvsp[0].v.number) > INT_MAX) { + yyerror("invalid movemount"); + YYERROR; + } + conf->mamount = (yyvsp[0].v.number); + } +#line 1410 "y.tab.c" + break; + + case 18: /* main: SNAPDIST NUMBER */ +#line 157 "parse.y" + { + if ((yyvsp[0].v.number) < 0 || (yyvsp[0].v.number) > INT_MAX) { + yyerror("invalid snapdist"); + YYERROR; + } + conf->snapdist = (yyvsp[0].v.number); + } +#line 1422 "y.tab.c" + break; + + case 19: /* main: COMMAND STRING string */ +#line 164 "parse.y" + { + if (strlen((yyvsp[0].v.string)) >= PATH_MAX) { + yyerror("%s command path too long", (yyvsp[-1].v.string)); + free((yyvsp[-1].v.string)); + free((yyvsp[0].v.string)); + YYERROR; + } + conf_cmd_add(conf, (yyvsp[-1].v.string), (yyvsp[0].v.string)); + free((yyvsp[-1].v.string)); + free((yyvsp[0].v.string)); + } +#line 1438 "y.tab.c" + break; + + case 20: /* main: WM STRING string */ +#line 175 "parse.y" + { + if (strlen((yyvsp[0].v.string)) >= PATH_MAX) { + yyerror("%s wm path too long", (yyvsp[-1].v.string)); + free((yyvsp[-1].v.string)); + free((yyvsp[0].v.string)); + YYERROR; + } + conf_wm_add(conf, (yyvsp[-1].v.string), (yyvsp[0].v.string)); + free((yyvsp[-1].v.string)); + free((yyvsp[0].v.string)); + } +#line 1454 "y.tab.c" + break; + + case 21: /* main: AUTOGROUP NUMBER STRING */ +#line 186 "parse.y" + { + if ((yyvsp[-1].v.number) < 0 || (yyvsp[-1].v.number) > 9) { + yyerror("invalid autogroup"); + free((yyvsp[0].v.string)); + YYERROR; + } + conf_autogroup(conf, (yyvsp[-1].v.number), NULL, (yyvsp[0].v.string)); + free((yyvsp[0].v.string)); + } +#line 1468 "y.tab.c" + break; + + case 22: /* main: AUTOGROUP NUMBER STRING ',' STRING */ +#line 195 "parse.y" + { + if ((yyvsp[-3].v.number) < 0 || (yyvsp[-3].v.number) > 9) { + yyerror("invalid autogroup"); + free((yyvsp[-2].v.string)); + free((yyvsp[0].v.string)); + YYERROR; + } + conf_autogroup(conf, (yyvsp[-3].v.number), (yyvsp[-2].v.string), (yyvsp[0].v.string)); + free((yyvsp[-2].v.string)); + free((yyvsp[0].v.string)); + } +#line 1484 "y.tab.c" + break; + + case 23: /* main: IGNORE STRING */ +#line 206 "parse.y" + { + conf_ignore(conf, (yyvsp[0].v.string)); + free((yyvsp[0].v.string)); + } +#line 1493 "y.tab.c" + break; + + case 24: /* main: GAP NUMBER NUMBER NUMBER NUMBER NUMBER */ +#line 210 "parse.y" + { + if ((yyvsp[-4].v.number) < 0 || (yyvsp[-4].v.number) > INT_MAX || + (yyvsp[-3].v.number) < 0 || (yyvsp[-3].v.number) > INT_MAX || + (yyvsp[-2].v.number) < 0 || (yyvsp[-2].v.number) > INT_MAX || + (yyvsp[-1].v.number) < 0 || (yyvsp[-1].v.number) > INT_MAX) { + yyerror("invalid gap"); + YYERROR; + } + conf->gap.top = (yyvsp[-4].v.number); + conf->gap.bottom = (yyvsp[-3].v.number); + conf->gap.left = (yyvsp[-2].v.number); + conf->gap.right = (yyvsp[-1].v.number); + conf->gap.bar = (yyvsp[0].v.number); + } +#line 1512 "y.tab.c" + break; + + case 25: /* main: BINDKEY STRING string */ +#line 224 "parse.y" + { + if (!conf_bind_key(conf, (yyvsp[-1].v.string), (yyvsp[0].v.string))) { + yyerror("invalid bind-key: %s %s", (yyvsp[-1].v.string), (yyvsp[0].v.string)); + free((yyvsp[-1].v.string)); + free((yyvsp[0].v.string)); + YYERROR; + } + free((yyvsp[-1].v.string)); + free((yyvsp[0].v.string)); + } +#line 1527 "y.tab.c" + break; + + case 26: /* main: UNBINDKEY STRING */ +#line 234 "parse.y" + { + if (!conf_bind_key(conf, (yyvsp[0].v.string), NULL)) { + yyerror("invalid unbind-key: %s", (yyvsp[0].v.string)); + free((yyvsp[0].v.string)); + YYERROR; + } + free((yyvsp[0].v.string)); + } +#line 1540 "y.tab.c" + break; + + case 27: /* main: BINDMOUSE STRING string */ +#line 242 "parse.y" + { + if (!conf_bind_mouse(conf, (yyvsp[-1].v.string), (yyvsp[0].v.string))) { + yyerror("invalid bind-mouse: %s %s", (yyvsp[-1].v.string), (yyvsp[0].v.string)); + free((yyvsp[-1].v.string)); + free((yyvsp[0].v.string)); + YYERROR; + } + free((yyvsp[-1].v.string)); + free((yyvsp[0].v.string)); + } +#line 1555 "y.tab.c" + break; + + case 28: /* main: UNBINDMOUSE STRING */ +#line 252 "parse.y" + { + if (!conf_bind_mouse(conf, (yyvsp[0].v.string), NULL)) { + yyerror("invalid unbind-mouse: %s", (yyvsp[0].v.string)); + free((yyvsp[0].v.string)); + YYERROR; + } + free((yyvsp[0].v.string)); + } +#line 1568 "y.tab.c" + break; + + case 29: /* main: RESIZEALLCORNERS yesno */ +#line 260 "parse.y" + { + conf->resizeallcorners = (yyvsp[0].v.number); + } +#line 1576 "y.tab.c" + break; + + case 31: /* colors: ACTIVEBORDER STRING */ +#line 268 "parse.y" + { + free(conf->color[CWM_COLOR_BORDER_ACTIVE]); + conf->color[CWM_COLOR_BORDER_ACTIVE] = (yyvsp[0].v.string); + } +#line 1585 "y.tab.c" + break; + + case 32: /* colors: INACTIVEBORDER STRING */ +#line 272 "parse.y" + { + free(conf->color[CWM_COLOR_BORDER_INACTIVE]); + conf->color[CWM_COLOR_BORDER_INACTIVE] = (yyvsp[0].v.string); + } +#line 1594 "y.tab.c" + break; + + case 33: /* colors: URGENCYBORDER STRING */ +#line 276 "parse.y" + { + free(conf->color[CWM_COLOR_BORDER_URGENCY]); + conf->color[CWM_COLOR_BORDER_URGENCY] = (yyvsp[0].v.string); + } +#line 1603 "y.tab.c" + break; + + case 34: /* colors: GROUPBORDER STRING */ +#line 280 "parse.y" + { + free(conf->color[CWM_COLOR_BORDER_GROUP]); + conf->color[CWM_COLOR_BORDER_GROUP] = (yyvsp[0].v.string); + } +#line 1612 "y.tab.c" + break; + + case 35: /* colors: UNGROUPBORDER STRING */ +#line 284 "parse.y" + { + free(conf->color[CWM_COLOR_BORDER_UNGROUP]); + conf->color[CWM_COLOR_BORDER_UNGROUP] = (yyvsp[0].v.string); + } +#line 1621 "y.tab.c" + break; + + case 36: /* colors: MENUBG STRING */ +#line 288 "parse.y" + { + free(conf->color[CWM_COLOR_MENU_BG]); + conf->color[CWM_COLOR_MENU_BG] = (yyvsp[0].v.string); + } +#line 1630 "y.tab.c" + break; + + case 37: /* colors: MENUFG STRING */ +#line 292 "parse.y" + { + free(conf->color[CWM_COLOR_MENU_FG]); + conf->color[CWM_COLOR_MENU_FG] = (yyvsp[0].v.string); + } +#line 1639 "y.tab.c" + break; + + case 38: /* colors: FONTCOLOR STRING */ +#line 296 "parse.y" + { + free(conf->color[CWM_COLOR_MENU_FONT]); + conf->color[CWM_COLOR_MENU_FONT] = (yyvsp[0].v.string); + } +#line 1648 "y.tab.c" + break; + + case 39: /* colors: FONTSELCOLOR STRING */ +#line 300 "parse.y" + { + free(conf->color[CWM_COLOR_MENU_FONT_SEL]); + conf->color[CWM_COLOR_MENU_FONT_SEL] = (yyvsp[0].v.string); + } +#line 1657 "y.tab.c" + break; + + +#line 1661 "y.tab.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + yyerror (YY_("syntax error")); + } + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if !defined yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + goto yyreturn; +#endif + + +/*-------------------------------------------------------. +| yyreturn -- parsing is finished, clean up and return. | +`-------------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + + return yyresult; +} + +#line 305 "parse.y" + + +struct keywords { + const char *k_name; + int k_val; +}; + +int +yyerror(const char *fmt, ...) +{ + va_list ap; + + file->errors++; + va_start(ap, fmt); + fprintf(stderr, "%s:%d: ", file->name, yylval.lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + return (0); +} + +int +kw_cmp(const void *k, const void *e) +{ + return (strcmp(k, ((const struct keywords *)e)->k_name)); +} + +int +lookup(char *s) +{ + /* this has to be sorted always */ + static const struct keywords keywords[] = { + { "activeborder", ACTIVEBORDER}, + { "autogroup", AUTOGROUP}, + { "bind-key", BINDKEY}, + { "bind-mouse", BINDMOUSE}, + { "borderwidth", BORDERWIDTH}, + { "tilegap", TILEGAP}, + { "color", COLOR}, + { "command", COMMAND}, + { "font", FONTCOLOR}, + { "fontname", FONTNAME}, + { "gap", GAP}, + { "groupborder", GROUPBORDER}, + { "htile", HTILE}, + { "ignore", IGNORE}, + { "inactiveborder", INACTIVEBORDER}, + { "menubg", MENUBG}, + { "menufg", MENUFG}, + { "moveamount", MOVEAMOUNT}, + { "no", NO}, + { "resizeallcorners", RESIZEALLCORNERS}, + { "selfont", FONTSELCOLOR}, + { "snapdist", SNAPDIST}, + { "sticky", STICKY}, + { "unbind-key", UNBINDKEY}, + { "unbind-mouse", UNBINDMOUSE}, + { "ungroupborder", UNGROUPBORDER}, + { "urgencyborder", URGENCYBORDER}, + { "vtile", VTILE}, + { "wm", WM}, + { "yes", YES} + }; + const struct keywords *p; + + p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), + sizeof(keywords[0]), kw_cmp); + + if (p) + return (p->k_val); + else + return (STRING); +} + +#define MAXPUSHBACK 128 + +char *parsebuf; +int parseindex; +char pushback_buffer[MAXPUSHBACK]; +int pushback_index = 0; + +int +lgetc(int quotec) +{ + int c, next; + + if (parsebuf) { + /* Read character from the parsebuffer instead of input. */ + if (parseindex >= 0) { + c = parsebuf[parseindex++]; + if (c != '\0') + return (c); + parsebuf = NULL; + } else + parseindex++; + } + + if (pushback_index) + return (pushback_buffer[--pushback_index]); + + if (quotec) { + if ((c = getc(file->stream)) == EOF) { + yyerror("reached end of file while parsing " + "quoted string"); + if (file == topfile || popfile() == EOF) + return (EOF); + return (quotec); + } + return (c); + } + + while ((c = getc(file->stream)) == '\\') { + next = getc(file->stream); + if (next != '\n') { + c = next; + break; + } + yylval.lineno = file->lineno; + file->lineno++; + } + + while (c == EOF) { + if (file == topfile || popfile() == EOF) + return (EOF); + c = getc(file->stream); + } + return (c); +} + +int +lungetc(int c) +{ + if (c == EOF) + return (EOF); + if (parsebuf) { + parseindex--; + if (parseindex >= 0) + return (c); + } + if (pushback_index < MAXPUSHBACK-1) + return (pushback_buffer[pushback_index++] = c); + else + return (EOF); +} + +int +findeol(void) +{ + int c; + + parsebuf = NULL; + + /* skip to either EOF or the first real EOL */ + while (1) { + if (pushback_index) + c = pushback_buffer[--pushback_index]; + else + c = lgetc(0); + if (c == '\n') { + file->lineno++; + break; + } + if (c == EOF) + break; + } + return (ERROR); +} + +int +yylex(void) +{ + char buf[8096]; + char *p; + int quotec, next, c; + int token; + + p = buf; + while ((c = lgetc(0)) == ' ' || c == '\t') + ; /* nothing */ + + yylval.lineno = file->lineno; + if (c == '#') + while ((c = lgetc(0)) != '\n' && c != EOF) + ; /* nothing */ + + switch (c) { + case '\'': + case '"': + quotec = c; + while (1) { + if ((c = lgetc(quotec)) == EOF) + return (0); + if (c == '\n') { + file->lineno++; + continue; + } else if (c == '\\') { + if ((next = lgetc(quotec)) == EOF) + return (0); + if (next == quotec || next == ' ' || + next == '\t') + c = next; + else if (next == '\n') { + file->lineno++; + continue; + } else + lungetc(next); + } else if (c == quotec) { + *p = '\0'; + break; + } else if (c == '\0') { + yyerror("syntax error"); + return (findeol()); + } + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + *p++ = (char)c; + } + yylval.v.string = xstrdup(buf); + return (STRING); + } + +#define allowed_to_end_number(x) \ + (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') + + if (c == '-' || isdigit(c)) { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && isdigit(c)); + lungetc(c); + if (p == buf + 1 && buf[0] == '-') + goto nodigits; + if (c == EOF || allowed_to_end_number(c)) { + const char *errstr = NULL; + + *p = '\0'; + yylval.v.number = strtonum(buf, LLONG_MIN, + LLONG_MAX, &errstr); + if (errstr) { + yyerror("\"%s\" invalid number: %s", + buf, errstr); + return (findeol()); + } + return (NUMBER); + } else { +nodigits: + while (p > buf + 1) + lungetc(*--p); + c = *--p; + if (c == '-') + return (c); + } + } + +/* Similar to other parse.y copies, but also allows '/' in strings */ +#define allowed_in_string(x) \ + (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ + x != '{' && x != '}' && x != '<' && x != '>' && \ + x != '!' && x != '=' && x != '#' && x != ',')) + + if (isalnum(c) || c == ':' || c == '_' || c == '*' || c == '/') { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); + lungetc(c); + *p = '\0'; + if ((token = lookup(buf)) == STRING) + yylval.v.string = xstrdup(buf); + return (token); + } + if (c == '\n') { + yylval.lineno = file->lineno; + file->lineno++; + } + if (c == EOF) + return (0); + return (c); +} + +struct file * +pushfile(const char *name, FILE *stream) +{ + struct file *nfile; + + nfile = xcalloc(1, sizeof(struct file)); + nfile->name = xstrdup(name); + nfile->stream = stream; + nfile->lineno = 1; + TAILQ_INSERT_TAIL(&files, nfile, entry); + return (nfile); +} + +int +popfile(void) +{ + struct file *prev; + + if ((prev = TAILQ_PREV(file, files, entry)) != NULL) + prev->errors += file->errors; + + TAILQ_REMOVE(&files, file, entry); + fclose(file->stream); + free(file->name); + free(file); + file = prev; + return (file ? 0 : EOF); +} + +int +parse_config(const char *filename, struct conf *xconf) +{ + FILE *stream; + int errors = 0; + + conf = xconf; + + stream = fopen(filename, "r"); + if (stream == NULL) { + if (errno == ENOENT) + return (0); + warn("%s", filename); + return (-1); + } + file = pushfile(filename, stream); + topfile = file; + + yyparse(); + errors = file->errors; + popfile(); + + return (errors ? -1 : 0); +}