pax_global_header00006660000000000000000000000064131647542670014531gustar00rootroot0000000000000052 comment=7cd1e4dfb05c487e5cd1fb26848e0a34e42d83c3 spectrwm-SPECTRWM_3_1_0/000077500000000000000000000000001316475426700150645ustar00rootroot00000000000000spectrwm-SPECTRWM_3_1_0/LICENSE.md000066400000000000000000000024751316475426700165000ustar00rootroot00000000000000Copyright (c) 2009-2016 Marco Peereboom Copyright (c) 2009-2011 Ryan McBride Copyright (c) 2009 Darrin Chandler Copyright (c) 2009 Pierre-Yves Ritschard Copyright (c) 2010 Tuukka Kataja Copyright (c) 2011 Jason L. Wright Copyright (c) 2011-2016 Reginald Kennedy Copyright (c) 2011-2012 Lawrence Teo Copyright (c) 2011-2012 Tiago Cunha Copyright (c) 2012-2015 David Hill Copyright (c) 2014-2016 Yuri D'Elia 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. spectrwm-SPECTRWM_3_1_0/Makefile000066400000000000000000000015041316475426700165240ustar00rootroot00000000000000.include PREFIX?=/usr/local BINDIR=${PREFIX}/bin SUBDIR= lib PROG=spectrwm MAN=spectrwm.1 CFLAGS+=-std=c99 -Wmissing-prototypes -Wall -Wextra -Wshadow -Wno-uninitialized -g # Uncomment define below to disallow user settable clock format string #CFLAGS+=-DSWM_DENY_CLOCK_FORMAT CPPFLAGS+= -I${X11BASE}/include -I${X11BASE}/include/freetype2 LDADD+=-lutil -L${X11BASE}/lib -lX11 -lX11-xcb -lxcb-util -lxcb-icccm -lxcb-keysyms -lxcb-randr -lxcb-xtest -lXft -lXcursor BUILDVERSION != sh "${.CURDIR}/buildver.sh" .if !${BUILDVERSION} == "" CPPFLAGS+= -DSPECTRWM_BUILDSTR=\"$(BUILDVERSION)\" .endif MANDIR= ${PREFIX}/man/man obj: _xenocara_obj beforeinstall: ln -sf ${PROG} ${BINDIR}/scrotwm spectrwm.html: spectrwm.1 mandoc -Thtml ${.CURDIR}/spectrwm.1 > spectrwm.html .include .include spectrwm-SPECTRWM_3_1_0/README.md000066400000000000000000000061641316475426700163520ustar00rootroot00000000000000spectrwm ======== spectrwm is a small dynamic tiling and reparenting window manager for X11. It tries to stay out of the way so that valuable screen real estate can be used for much more important stuff. It has sane defaults and does not require one to learn a language to do any configuration. It is written by hackers for hackers and it strives to be small, compact and fast. It was largely inspired by [xmonad](http://xmonad.org) and [dwm](http://dwm.suckless.org). Both are fine products but suffer from things like: crazy-unportable-language-syndrome, silly defaults, asymmetrical window layout, "how hard can it be?" and good old NIH. Nevertheless [dwm](http://dwm.suckless.org) was a phenomenal resource and many good ideas and code was borrowed from it. On the other hand [xmonad](http://xmonad.org) has great defaults, key bindings and xinerama support but is crippled by not being written in C. spectrwm is a beautiful pearl! For it too, was created by grinding irritation. Nothing is a bigger waste of time than moving windows around until they are the right size-ish or having just about any relevant key combination being eaten for some task one never needs. The path of agony is too long to quote and in classical [OpenBSD](http://www.openbsd.org) fashion (put up, or hack up) a brand new window manager was whooped up to serve no other purpose than to obey its masters. It is released under the ISC license. Patches can be accepted provided they are ISC licensed as well. ## Feedback and questions You can and come chat with us on IRC. We use [OFTC](https://www.oftc.net) channel #spectrwm. ## Major features * Dynamic RandR support (multi-head) * Navigation anywhere on all screens with either the keyboard or mouse * Customizable status bar * Human readable configuration file * Restartable without losing state * Quick launch menu * Many screen layouts possible with a few simple key strokes * Windows can be added or removed from master area * Windows can be moved to any workspace or within a region * Resizable master area * Move/resize floating windows * Drag-to-float * Extended Window Manager Hints (EWMH) Support * Configureable tiling * Adjustable tile gap allows for a true one pixel border. * Customizable colors and border width. * User definable regions * User definable modkey & key bindings * User definable quirk bindings * User definable key bindings to launch applications * Multi OS support (*BSD, Linux, OSX, Windows/cygwin) * Reparenting window manager ## Documentation [Click here for current man page](https://htmlpreview.github.io/?https://github.com/conformal/spectrwm/blob/master/spectrwm.html) ## License spectrwm is ISC licensed unless otherwise specified in individual files. ## Screenshots ![Vertical stack](https://github.com/conformal/spectrwm/wiki/Scrotwm1.png) ![Horizontal stack](https://github.com/conformal/spectrwm/wiki/Scrotwm2.png) ![Horizontal stack](https://github.com/conformal/spectrwm/wiki/Scrotwm3.png) ![Vertical stack with floater and extra window in master area](https://github.com/conformal/spectrwm/wiki/Scrotwm4.png) ![mplayer, resized and moved](https://github.com/conformal/spectrwm/wiki/Scrotwm5.png) spectrwm-SPECTRWM_3_1_0/baraction.sh000066400000000000000000000040421316475426700173620ustar00rootroot00000000000000#!/bin/sh # print_date() { # The date is printed to the status bar by default. # To print the date through this script, set clock_enabled to 0 # in spectrwm.conf. Uncomment "print_date" below. FORMAT="%a %b %d %R %Z %Y" DATE=`date "+${FORMAT}"` echo -n "${DATE} " } print_mem() { MEM=`/usr/bin/top | grep Free: | cut -d " " -f6` echo -n "Free mem: $MEM " } _print_cpu() { typeset -R4 _1=${1} _2=${2} _3=${3} _4=${4} _5=${5} echo -n "CPU:${_1}% User${_2}% Nice${_3}% Sys${_4}% Int${_5}% Idle " } print_cpu() { OUT="" # iostat prints each column justified to 3 chars, so if one counter # is 100, it jams up agains the preceeding one. sort this out. while [ "${1}x" != "x" ]; do if [ ${1} -gt 99 ]; then OUT="$OUT ${1%100} 100" else OUT="$OUT ${1}" fi shift; done _print_cpu $OUT } print_apm() { BAT_STATUS=$1 BAT_LEVEL=$2 AC_STATUS=$3 if [ $AC_STATUS -ne 255 -o $BAT_STATUS -lt 4 ]; then if [ $AC_STATUS -eq 0 ]; then echo -n "on battery (${BAT_LEVEL}%)" else case $AC_STATUS in 1) AC_STRING="on AC: " ;; 2) AC_STRING="on backup AC: " ;; *) AC_STRING="" ;; esac; case $BAT_STATUS in 4) BAT_STRING="(no battery)" ;; [0-3]) BAT_STRING="(battery ${BAT_LEVEL}%)" ;; *) BAT_STRING="(battery unknown)" ;; esac; FULL="${AC_STRING}${BAT_STRING}" if [ "$FULL" != "" ]; then echo -n "$FULL" fi fi fi } print_cpuspeed() { CPU_SPEED=`/sbin/sysctl hw.cpuspeed | cut -d "=" -f2` echo -n "CPU speed: $CPU_SPEED MHz " } while :; do # instead of sleeping, use iostat as the update timer. # cache the output of apm(8), no need to call that every second. /usr/sbin/iostat -C -c 3600 |& # wish infinity was an option PID="$!" APM_DATA="" I=0 trap "kill $PID; exit" TERM while read -p; do if [ $(( ${I} % 1 )) -eq 0 ]; then APM_DATA=`/usr/sbin/apm -alb` fi if [ $I -ge 2 ]; then # print_date print_mem $MEM print_cpu $REPLY print_cpuspeed print_apm $APM_DATA echo "" fi I=$(( ( ${I} + 1 ) % 22 )); done done spectrwm-SPECTRWM_3_1_0/buildver.sh000066400000000000000000000001541316475426700172340ustar00rootroot00000000000000#!/bin/sh CURDIR=$(dirname $0) if [ -d "$CURDIR/.git" ]; then cd "$CURDIR" echo $(git rev-parse HEAD) fi spectrwm-SPECTRWM_3_1_0/freebsd/000077500000000000000000000000001316475426700164765ustar00rootroot00000000000000spectrwm-SPECTRWM_3_1_0/freebsd/Makefile000066400000000000000000000030761316475426700201440ustar00rootroot00000000000000.sinclude PREFIX?= /usr/local LOCALBASE?= /usr/local SWM_BINDIR?= $(PREFIX)/bin SWM_LIBDIR?= $(PREFIX)/lib SWM_MANDIR?= $(PREFIX)/man CC?= cc LVERS!= awk -F = '/major/ { printf( "%s.", $$2 ) } /minor/ { printf( "%s", $$2 ) }' ${.CURDIR}/../lib/shlib_version BUILDVERSION!= sh "${.CURDIR}/../buildver.sh" .if !${BUILDVERSION} == "" CFLAGS+= -DSPECTRWM_BUILDSTR=\"$(BUILDVERSION)\" .endif CFLAGS+= -std=c99 -Wmissing-prototypes -Wall -Wextra -Wshadow -Wno-uninitialized -g CFLAGS+= -I. -I${LOCALBASE}/include -I${LOCALBASE}/include/freetype2 CFLAGS+= -DSWM_LIB=\"$(SWM_LIBDIR)/libswmhack.so.$(LVERS)\" LDADD+= -lutil -L${LOCALBASE}/lib -lX11 -lX11-xcb -lxcb \ -lxcb-icccm -lxcb-keysyms -lxcb-randr -lxcb-util -lxcb-xtest -lXft -lXcursor all: spectrwm libswmhack.so.$(LVERS) spectrwm.c: ln -sf ../spectrwm.c ln -sf ../version.h swm_hack.c: ln -sf ../lib/swm_hack.c spectrwm: spectrwm.o $(CC) $(LDFLAGS) $(LDADD) -o ${.TARGET} ${.ALLSRC} swm_hack.so: swm_hack.c $(CC) $(CFLAGS) -c -shared -fpic -DPIC -o ${.TARGET} ${.ALLSRC} libswmhack.so.$(LVERS): swm_hack.so $(CC) $(LDFLAGS) -shared -fpic -o ${.TARGET} ${.ALLSRC} install: all install -m 755 -d $(SWM_BINDIR) install -m 755 -d $(SWM_LIBDIR) install -m 755 -d $(SWM_MANDIR)/man1 install -m 755 spectrwm $(SWM_BINDIR) install -m 755 libswmhack.so.$(LVERS) $(SWM_LIBDIR) install -m 644 ../spectrwm.1 $(SWM_MANDIR)/man1/spectrwm.1 ln -sf spectrwm $(SWM_BINDIR)/scrotwm clean: rm -f spectrwm *.o *.so libswmhack.so.* spectrwm.c swm_hack.c version.h .PHONY: all install clean .sinclude spectrwm-SPECTRWM_3_1_0/freebsd/util.h000066400000000000000000000012261316475426700176250ustar00rootroot00000000000000#include #ifndef TAILQ_END #define TAILQ_END(head) NULL #endif #ifndef SIMPLEQ_HEAD #define SIMPLEQ_HEAD STAILQ_HEAD #define SIMPLEQ_HEAD_INITIALIZER STAILQ_HEAD_INITIALIZER #define SIMPLEQ_ENTRY STAILQ_ENTRY #define SIMPLEQ_INIT STAILQ_INIT #define SIMPLEQ_INSERT_AFTER STAILQ_INSERT_AFTER #define SIMPLEQ_INSERT_HEAD STAILQ_INSERT_HEAD #define SIMPLEQ_INSERT_TAIL STAILQ_INSERT_TAIL #define SIMPLEQ_EMPTY STAILQ_EMPTY #define SIMPLEQ_FIRST STAILQ_FIRST #define SIMPLEQ_REMOVE_AFTER STAILQ_REMOVE_AFTER #define SIMPLEQ_REMOVE_HEAD STAILQ_REMOVE_HEAD #define SIMPLEQ_FOREACH STAILQ_FOREACH #define SIMPLEQ_END(head) NULL #endif spectrwm-SPECTRWM_3_1_0/initscreen.sh000066400000000000000000000001661316475426700175660ustar00rootroot00000000000000#!/bin/sh # # Example xrandr multiscreen init xrandr --output LVDS --auto xrandr --output VGA --auto --right-of LVDS spectrwm-SPECTRWM_3_1_0/lib/000077500000000000000000000000001316475426700156325ustar00rootroot00000000000000spectrwm-SPECTRWM_3_1_0/lib/Makefile000066400000000000000000000007271316475426700173000ustar00rootroot00000000000000.include PREFIX?=/usr/local LIB= swmhack NOMAN= yes SRCS= swm_hack.c LIBDIR= ${X11BASE}/lib CC?= cc DEBUGLIBS= no NOPROFILE= yes CFLAGS+= -std=c99 -Wmissing-prototypes -Wall -Wextra -Wshadow -Wno-uninitialized -g CFLAGS+= -fPIC CFLAGS+= -I${X11BASE}/include install: ${INSTALL} ${INSTALL_COPY} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ lib${LIB}.so.${SHLIB_MAJOR}.${SHLIB_MINOR} \ ${PREFIX}/lib/ .include .include spectrwm-SPECTRWM_3_1_0/lib/shlib_version000066400000000000000000000000201316475426700204130ustar00rootroot00000000000000major=0 minor=0 spectrwm-SPECTRWM_3_1_0/lib/swm_hack.c000066400000000000000000000207631316475426700176020ustar00rootroot00000000000000/* * Copyright (c) 2009 Marco Peereboom * Copyright (c) 2009 Ryan McBride * * 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. */ /* * Copyright (C) 2005-2007 Carsten Haitzler * Copyright (C) 2006-2007 Kim Woelders * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies of the Software, its documentation and marketing & publicity * materials, and acknowledgment shall be given in the documentation, materials * and software packages that this Software was used. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * Basic hack mechanism (dlopen etc.) taken from e_hack.c in e17. */ #include #include #include #include #include #include #include #include #include /* dlopened libs so we can find the symbols in the real one to call them */ static void *lib_xlib = NULL; static void *lib_xtlib = NULL; static Window root = None; static bool xterm = false; static Display *display = NULL; void set_property(Display *, Window, char *, char *); #ifdef _GNU_SOURCE #define DLOPEN(s) RTLD_NEXT #else #define DLOPEN(s) dlopen((s), RTLD_GLOBAL | RTLD_LAZY) #endif /* Find our root window */ static Window MyRoot(Display * dpy) { char *s; if (root != None) return root; root = DefaultRootWindow(dpy); s = getenv("ENL_WM_ROOT"); if (s == NULL) return root; sscanf(s, "%lx", &root); return root; } typedef Atom (XIA) (Display *display, char *atom_name, Bool only_if_exists); typedef int (XCP) (Display *display, Window w, Atom property, Atom type, int format, int mode, unsigned char *data, int nelements); #define SWM_PROPLEN (16) void set_property(Display *dpy, Window id, char *name, char *val) { Atom atom = 0; char prop[SWM_PROPLEN]; static XIA *xia = NULL; static XCP *xcp = NULL; if (lib_xlib == NULL) lib_xlib = DLOPEN("libX11.so"); if (lib_xlib) { if (xia == NULL) xia = (XIA *) dlsym(lib_xlib, "XInternAtom"); if (xcp == NULL) xcp = (XCP *) dlsym(lib_xlib, "XChangeProperty"); } if (xia == NULL || xcp == NULL) { fprintf(stderr, "libswmhack.so: ERROR: %s\n", dlerror()); return; } /* Try to update the window's workspace property */ atom = (*xia)(dpy, name, False); if (atom) if (snprintf(prop, SWM_PROPLEN, "%s", val) < SWM_PROPLEN) (*xcp)(dpy, id, atom, XA_STRING, 8, PropModeReplace, (unsigned char *)prop, strlen((char *)prop)); } typedef Window(CWF) (Display * _display, Window _parent, int _x, int _y, unsigned int _width, unsigned int _height, unsigned int _border_width, int _depth, unsigned int _class, Visual * _visual, unsigned long _valuemask, XSetWindowAttributes * _attributes); /* XCreateWindow intercept hack */ Window XCreateWindow(Display *dpy, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth, unsigned int clss, Visual * visual, unsigned long valuemask, XSetWindowAttributes * attributes) { static CWF *func = NULL; char *env; Window id; if (lib_xlib == NULL) lib_xlib = DLOPEN("libX11.so"); if (lib_xlib && func == NULL) { func = (CWF *) dlsym(lib_xlib, "XCreateWindow"); display = dpy; } if (func == NULL) { fprintf(stderr, "libswmhack.so: ERROR: %s\n", dlerror()); return None; } if (parent == DefaultRootWindow(dpy)) parent = MyRoot(dpy); id = (*func) (dpy, parent, x, y, width, height, border_width, depth, clss, visual, valuemask, attributes); if (id) { if ((env = getenv("_SWM_WS")) != NULL) set_property(dpy, id, "_SWM_WS", env); if ((env = getenv("_SWM_PID")) != NULL) set_property(dpy, id, "_SWM_PID", env); if ((env = getenv("_SWM_XTERM_FONTADJ")) != NULL) { unsetenv("_SWM_XTERM_FONTADJ"); xterm = true; } } return (id); } typedef Window(CSWF) (Display * _display, Window _parent, int _x, int _y, unsigned int _width, unsigned int _height, unsigned int _border_width, unsigned long _border, unsigned long _background); /* XCreateSimpleWindow intercept hack */ Window XCreateSimpleWindow(Display *dpy, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, unsigned long border, unsigned long background) { static CSWF *func = NULL; char *env; Window id; if (lib_xlib == NULL) lib_xlib = DLOPEN("libX11.so"); if (lib_xlib && func == NULL) func = (CSWF *) dlsym(lib_xlib, "XCreateSimpleWindow"); if (func == NULL) { fprintf(stderr, "libswmhack.so: ERROR: %s\n", dlerror()); return None; } if (parent == DefaultRootWindow(dpy)) parent = MyRoot(dpy); id = (*func) (dpy, parent, x, y, width, height, border_width, border, background); if (id) { if ((env = getenv("_SWM_WS")) != NULL) set_property(dpy, id, "_SWM_WS", env); if ((env = getenv("_SWM_PID")) != NULL) set_property(dpy, id, "_SWM_PID", env); if ((env = getenv("_SWM_XTERM_FONTADJ")) != NULL) { unsetenv("_SWM_XTERM_FONTADJ"); xterm = true; } } return (id); } typedef int (RWF) (Display * _display, Window _window, Window _parent, int x, int y); /* XReparentWindow intercept hack */ int XReparentWindow(Display *dpy, Window window, Window parent, int x, int y) { static RWF *func = NULL; if (lib_xlib == NULL) lib_xlib = DLOPEN("libX11.so"); if (lib_xlib && func == NULL) func = (RWF *) dlsym(lib_xlib, "XReparentWindow"); if (func == NULL) { fprintf(stderr, "libswmhack.so: ERROR: %s\n", dlerror()); /* Xlib function always returns 1, so return 0 here. */ return 0; } if (parent == DefaultRootWindow(dpy)) parent = MyRoot(dpy); return (*func) (dpy, window, parent, x, y); } typedef void (ANEF) (XtAppContext app_context, XEvent *event_return); int evcount = 0; /* * XtAppNextEvent Intercept Hack * Normally xterm rejects "synthetic" (XSendEvent) events to prevent spoofing. * We don't want to disable this completely, it's insecure. But hook here * and allow these mostly harmless ones that we use to adjust fonts. */ void XtAppNextEvent(XtAppContext app_context, XEvent *event_return) { static ANEF *func = NULL; static KeyCode kp_add = 0, kp_subtract = 0; if (lib_xtlib == NULL) lib_xtlib = DLOPEN("libXt.so"); if (lib_xtlib && func == NULL) { func = (ANEF *) dlsym(lib_xtlib, "XtAppNextEvent"); if (display) { kp_add = XKeysymToKeycode(display, XK_KP_Add); kp_subtract = XKeysymToKeycode(display, XK_KP_Subtract); } } if (func == NULL) { fprintf(stderr, "libswmhack.so: ERROR: %s\n", dlerror()); return; } (*func) (app_context, event_return); /* Return here if it's not an Xterm. */ if (!xterm) return; /* Allow spoofing of font change keystrokes. */ if ((event_return->type == KeyPress || event_return->type == KeyRelease) && event_return->xkey.state == ShiftMask && (event_return->xkey.keycode == kp_add || event_return->xkey.keycode == kp_subtract)) event_return->xkey.send_event = 0; } spectrwm-SPECTRWM_3_1_0/linux/000077500000000000000000000000001316475426700162235ustar00rootroot00000000000000spectrwm-SPECTRWM_3_1_0/linux/Makefile000066400000000000000000000056701316475426700176730ustar00rootroot00000000000000PREFIX ?= /usr/local BINDIR ?= $(PREFIX)/bin LIBDIR ?= $(PREFIX)/lib DATAROOTDIR ?= $(PREFIX)/share MANDIR ?= $(DATAROOTDIR)/man XSESSIONSDIR ?= $(DATAROOTDIR)/xsessions BUILDVERSION = $(shell sh $(CURDIR)/../buildver.sh) LIBVERSION = $(shell . $(CURDIR)/../lib/shlib_version; echo $$major.$$minor) LIBMAJORVERSION = $(shell . $(CURDIR)/../lib/shlib_version; echo $$major) MAINT_CFLAGS = -std=c99 -Wmissing-prototypes -Wall -Wextra -Wshadow -Wno-uninitialized -g MAINT_LDFLAGS = -Wl,--as-needed MAINT_CPPFLAGS = -I. -D_GNU_SOURCE -DSWM_LIB=\"$(LIBDIR)/libswmhack.so.$(LIBVERSION)\" ifneq ("${BUILDVERSION}", "") MAINT_CPPFLAGS += -DSPECTRWM_BUILDSTR=\"$(BUILDVERSION)\" endif BIN_CFLAGS = -fPIE BIN_LDFLAGS = -fPIE -pie BIN_CPPFLAGS = $(shell pkg-config --cflags x11 x11-xcb xcb-icccm xcb-keysyms xcb-randr xcb-util xcb-xtest xcursor xft) BIN_LDLIBS = $(shell pkg-config --libs x11 x11-xcb xcb-icccm xcb-keysyms xcb-randr xcb-util xcb-xtest xcursor xft) LIB_CFLAGS = -fPIC LIB_LDFLAGS = -fPIC -shared LIB_CPPFLAGS = $(shell pkg-config --cflags x11) LIB_LDLIBS = $(shell pkg-config --libs x11) -ldl all: spectrwm libswmhack.so.$(LIBVERSION) spectrwm: spectrwm.o linux.o $(CC) $(MAINT_LDFLAGS) $(BIN_LDFLAGS) $(LDFLAGS) -o $@ $+ $(BIN_LDLIBS) $(LDLIBS) spectrwm.o: ../spectrwm.c ../version.h tree.h util.h $(CC) $(MAINT_CFLAGS) $(BIN_CFLAGS) $(CFLAGS) $(MAINT_CPPFLAGS) $(BIN_CPPFLAGS) $(CPPFLAGS) -c -o $@ $< linux.o: linux.c util.h $(CC) $(MAINT_CFLAGS) $(BIN_CFLAGS) $(CFLAGS) $(MAINT_CPPFLAGS) $(BIN_CPPFLAGS) $(CPPFLAGS) -c -o $@ $< libswmhack.so.$(LIBVERSION): swm_hack.so $(CC) $(MAINT_LDFLAGS) $(LIB_LDFLAGS) $(LDFLAGS) -Wl,-soname,$@ -o $@ $+ $(LIB_LDLIBS) $(LDLIBS) swm_hack.so: ../lib/swm_hack.c $(CC) $(MAINT_CFLAGS) $(LIB_CFLAGS) $(CFLAGS) $(MAINT_CPPFLAGS) $(LIB_CPPFLAGS) $(CPPFLAGS) -c -o $@ $< clean: rm -f spectrwm *.o libswmhack.so.* *.so install: all install -m 755 -d $(DESTDIR)$(BINDIR) install -m 755 -d $(DESTDIR)$(LIBDIR) install -m 755 -d $(DESTDIR)$(MANDIR)/man1 install -m 755 -d $(DESTDIR)$(XSESSIONSDIR) install -m 755 spectrwm $(DESTDIR)$(BINDIR) ln -sf spectrwm $(DESTDIR)$(BINDIR)/scrotwm install -m 644 libswmhack.so.$(LIBVERSION) $(DESTDIR)$(LIBDIR) ln -sf libswmhack.so.$(LIBVERSION) $(DESTDIR)$(LIBDIR)/libswmhack.so.$(LIBMAJORVERSION) ln -sf libswmhack.so.$(LIBVERSION) $(DESTDIR)$(LIBDIR)/libswmhack.so install -m 644 ../spectrwm.1 $(DESTDIR)$(MANDIR)/man1/spectrwm.1 install -m 644 spectrwm.desktop $(DESTDIR)$(XSESSIONSDIR) uninstall: rm -f $(DESTDIR)$(BINDIR)/spectrwm rm -f $(DESTDIR)$(BINDIR)/scrotwm rm -f $(DESTDIR)$(LIBDIR)/libswmhack.so.$(LIBVERSION) rm -f $(DESTDIR)$(LIBDIR)/libswmhack.so.$(LIBMAJORVERSION) rm -f $(DESTDIR)$(LIBDIR)/libswmhack.so rm -f $(DESTDIR)$(MANDIR)/man1/spectrwm.1 rm -f $(DESTDIR)$(XSESSIONSDIR)/spectrwm.desktop .PHONY: all clean install uninstall spectrwm-SPECTRWM_3_1_0/linux/linux.c000066400000000000000000000306501316475426700175320ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "util.h" /* * All the workarounds for glibc stupidity are piled into this file... */ /* --------------------------------------------------------------------------- */ /* $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */ /* * Copyright (c) 1998 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. */ /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0 && --n != 0) { do { if ((*d++ = *s++) == 0) break; } while (--n != 0); } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } /* --------------------------------------------------------------------------- */ /* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ /* * Copyright (c) 1998 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. */ /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). * Returns strlen(src) + MIN(siz, strlen(initial dst)). * If retval >= siz, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ } /* --------------------------------------------------------------------------- */ /* $NetBSD: fgetln.c,v 1.3 2007/08/07 02:06:58 lukem Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Christos Zoulas. * * 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 NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ char * fgetln(fp, len) FILE *fp; size_t *len; { static char *buf = NULL; static size_t bufsiz = 0; char *ptr; if (buf == NULL) { bufsiz = BUFSIZ; if ((buf = malloc(bufsiz)) == NULL) return NULL; } if (fgets(buf, bufsiz, fp) == NULL) return NULL; *len = 0; while ((ptr = strchr(&buf[*len], '\n')) == NULL) { size_t nbufsiz = bufsiz + BUFSIZ; char *nbuf = realloc(buf, nbufsiz); if (nbuf == NULL) { int oerrno = errno; free(buf); errno = oerrno; buf = NULL; return NULL; } else buf = nbuf; *len = bufsiz; if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL) return buf; bufsiz = nbufsiz; } *len = (ptr - buf) + 1; return buf; } /* --------------------------------------------------------------------------- */ /* $OpenBSD: fparseln.c,v 1.6 2005/08/02 21:46:23 espie Exp $ */ /* $NetBSD: fparseln.c,v 1.7 1999/07/02 15:49:12 simonb Exp $ */ /* * Copyright (c) 1997 Christos Zoulas. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Christos Zoulas. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #define FPARSELN_UNESCESC 0x01 #define FPARSELN_UNESCCONT 0x02 #define FPARSELN_UNESCCOMM 0x04 #define FPARSELN_UNESCREST 0x08 #define FPARSELN_UNESCALL 0x0f static int isescaped(const char *, const char *, int); /* isescaped(): * Return true if the character in *p that belongs to a string * that starts in *sp, is escaped by the escape character esc. */ static int isescaped(const char *sp, const char *p, int esc) { const char *cp; size_t ne; /* No escape character */ if (esc == '\0') return 1; /* Count the number of escape characters that precede ours */ for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++) continue; /* Return true if odd number of escape characters */ return (ne & 1) != 0; } /* fparseln(): * Read a line from a file parsing continuations ending in \ * and eliminating trailing newlines, or comments starting with * the comment char. */ char * fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], int flags) { static const char dstr[3] = { '\\', '\\', '#' }; char *buf = NULL, *ptr, *cp, esc, con, nl, com; size_t s, len = 0; int cnt = 1; if (str == NULL) str = dstr; esc = str[0]; con = str[1]; com = str[2]; /* * XXX: it would be cool to be able to specify the newline character, * but unfortunately, fgetln does not let us */ nl = '\n'; while (cnt) { cnt = 0; if (lineno) (*lineno)++; if ((ptr = fgetln(fp, &s)) == NULL) break; if (s && com) { /* Check and eliminate comments */ for (cp = ptr; cp < ptr + s; cp++) if (*cp == com && !isescaped(ptr, cp, esc)) { s = cp - ptr; cnt = s == 0 && buf == NULL; break; } } if (s && nl) { /* Check and eliminate newlines */ cp = &ptr[s - 1]; if (*cp == nl) s--; /* forget newline */ } if (s && con) { /* Check and eliminate continuations */ cp = &ptr[s - 1]; if (*cp == con && !isescaped(ptr, cp, esc)) { s--; /* forget escape */ cnt = 1; } } if (s == 0 && buf != NULL) continue; if ((cp = realloc(buf, len + s + 1)) == NULL) { free(buf); return NULL; } buf = cp; (void) memcpy(buf + len, ptr, s); len += s; buf[len] = '\0'; } if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL && strchr(buf, esc) != NULL) { ptr = cp = buf; while (cp[0] != '\0') { int skipesc; while (cp[0] != '\0' && cp[0] != esc) *ptr++ = *cp++; if (cp[0] == '\0' || cp[1] == '\0') break; skipesc = 0; if (cp[1] == com) skipesc += (flags & FPARSELN_UNESCCOMM); if (cp[1] == con) skipesc += (flags & FPARSELN_UNESCCONT); if (cp[1] == esc) skipesc += (flags & FPARSELN_UNESCESC); if (cp[1] != com && cp[1] != con && cp[1] != esc) skipesc = (flags & FPARSELN_UNESCREST); if (skipesc) cp++; else *ptr++ = *cp++; *ptr++ = *cp++; } *ptr = '\0'; len = strlen(buf); } if (size) *size = len; return buf; } /* --------------------------------------------------------------------------- */ /* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert 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. */ #define INVALID 1 #define TOOSMALL 2 #define TOOLARGE 3 long long strtonum(const char *numstr, long long minval, long long maxval, const char **errstrp) { long long ll = 0; char *ep; int error = 0; struct errval { const char *errstr; int err; } ev[4] = { { NULL, 0 }, { "invalid", EINVAL }, { "too small", ERANGE }, { "too large", ERANGE }, }; ev[0].err = errno; errno = 0; if (minval > maxval) error = INVALID; else { ll = strtoll(numstr, &ep, 10); if (numstr == ep || *ep != '\0') error = INVALID; else if ((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); } spectrwm-SPECTRWM_3_1_0/linux/spectrwm.desktop000066400000000000000000000001621316475426700214610ustar00rootroot00000000000000[Desktop Entry] Name=spectrwm Comment=The spectrwm window manager Type=Application Exec=spectrwm TryExec=spectrwm spectrwm-SPECTRWM_3_1_0/linux/tree.h000066400000000000000000000606461316475426700173470ustar00rootroot00000000000000/* $xxxterm$ */ /* $OpenBSD: tree.h,v 1.12 2009/03/02 09:42:55 mikeb Exp $ */ /* * Copyright 2002 Niels Provos * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #ifndef _SYS_TREE_H_ #define _SYS_TREE_H_ /* * This file defines data structures for different types of trees: * splay trees and red-black trees. * * A splay tree is a self-organizing data structure. Every operation * on the tree causes a splay to happen. The splay moves the requested * node to the root of the tree and partly rebalances it. * * This has the benefit that request locality causes faster lookups as * the requested nodes move to the top of the tree. On the other hand, * every lookup causes memory writes. * * The Balance Theorem bounds the total access time for m operations * and n inserts on an initially empty tree as O((m + n)lg n). The * amortized cost for a sequence of m accesses to a splay tree is O(lg n); * * A red-black tree is a binary search tree with the node color as an * extra attribute. It fulfills a set of conditions: * - every search path from the root to a leaf consists of the * same number of black nodes, * - each red node (except for the root) has a black parent, * - each leaf node is black. * * Every operation on a red-black tree is bounded as O(lg n). * The maximum height of a red-black tree is 2lg (n+1). */ #define SPLAY_HEAD(name, type) \ struct name { \ struct type *sph_root; /* root of the tree */ \ } #define SPLAY_INITIALIZER(root) \ { NULL } #define SPLAY_INIT(root) do { \ (root)->sph_root = NULL; \ } while (0) #define SPLAY_ENTRY(type) \ struct { \ struct type *spe_left; /* left element */ \ struct type *spe_right; /* right element */ \ } #define SPLAY_LEFT(elm, field) (elm)->field.spe_left #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right #define SPLAY_ROOT(head) (head)->sph_root #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) /* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_LINKLEFT(head, tmp, field) do { \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ } while (0) #define SPLAY_LINKRIGHT(head, tmp, field) do { \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ } while (0) #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ } while (0) /* Generates prototypes and inline functions */ #define SPLAY_PROTOTYPE(name, type, field, cmp) \ void name##_SPLAY(struct name *, struct type *); \ void name##_SPLAY_MINMAX(struct name *, int); \ struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ \ /* Finds the node with the same key as elm */ \ static __inline struct type * \ name##_SPLAY_FIND(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) \ return(NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) \ return (head->sph_root); \ return (NULL); \ } \ \ static __inline struct type * \ name##_SPLAY_NEXT(struct name *head, struct type *elm) \ { \ name##_SPLAY(head, elm); \ if (SPLAY_RIGHT(elm, field) != NULL) { \ elm = SPLAY_RIGHT(elm, field); \ while (SPLAY_LEFT(elm, field) != NULL) { \ elm = SPLAY_LEFT(elm, field); \ } \ } else \ elm = NULL; \ return (elm); \ } \ \ static __inline struct type * \ name##_SPLAY_MIN_MAX(struct name *head, int val) \ { \ name##_SPLAY_MINMAX(head, val); \ return (SPLAY_ROOT(head)); \ } /* Main splay operation. * Moves node close to the key of elm to top */ #define SPLAY_GENERATE(name, type, field, cmp) \ struct type * \ name##_SPLAY_INSERT(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) { \ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ } else { \ int __comp; \ name##_SPLAY(head, elm); \ __comp = (cmp)(elm, (head)->sph_root); \ if(__comp < 0) { \ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ SPLAY_RIGHT(elm, field) = (head)->sph_root; \ SPLAY_LEFT((head)->sph_root, field) = NULL; \ } else if (__comp > 0) { \ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT(elm, field) = (head)->sph_root; \ SPLAY_RIGHT((head)->sph_root, field) = NULL; \ } else \ return ((head)->sph_root); \ } \ (head)->sph_root = (elm); \ return (NULL); \ } \ \ struct type * \ name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ { \ struct type *__tmp; \ if (SPLAY_EMPTY(head)) \ return (NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) { \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ } else { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ name##_SPLAY(head, elm); \ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ } \ return (elm); \ } \ return (NULL); \ } \ \ void \ name##_SPLAY(struct name *head, struct type *elm) \ { \ struct type __node, *__left, *__right, *__tmp; \ int __comp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while ((__comp = (cmp)(elm, (head)->sph_root))) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) > 0){ \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } \ \ /* Splay with either the minimum or the maximum element \ * Used to find minimum or maximum element in tree. \ */ \ void name##_SPLAY_MINMAX(struct name *head, int __comp) \ { \ struct type __node, *__left, *__right, *__tmp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while (1) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp > 0) { \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } #define SPLAY_NEGINF -1 #define SPLAY_INF 1 #define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) #define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) #define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) #define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) #define SPLAY_FOREACH(x, name, head) \ for ((x) = SPLAY_MIN(name, head); \ (x) != NULL; \ (x) = SPLAY_NEXT(name, head, x)) /* Macros that define a red-black tree */ #define RB_HEAD(name, type) \ struct name { \ struct type *rbh_root; /* root of the tree */ \ } #define RB_INITIALIZER(root) \ { NULL } #define RB_INIT(root) do { \ (root)->rbh_root = NULL; \ } while (0) #define RB_BLACK 0 #define RB_RED 1 #define RB_ENTRY(type) \ struct { \ struct type *rbe_left; /* left element */ \ struct type *rbe_right; /* right element */ \ struct type *rbe_parent; /* parent element */ \ int rbe_color; /* node color */ \ } #define RB_LEFT(elm, field) (elm)->field.rbe_left #define RB_RIGHT(elm, field) (elm)->field.rbe_right #define RB_PARENT(elm, field) (elm)->field.rbe_parent #define RB_COLOR(elm, field) (elm)->field.rbe_color #define RB_ROOT(head) (head)->rbh_root #define RB_EMPTY(head) (RB_ROOT(head) == NULL) #define RB_SET(elm, parent, field) do { \ RB_PARENT(elm, field) = parent; \ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ RB_COLOR(elm, field) = RB_RED; \ } while (0) #define RB_SET_BLACKRED(black, red, field) do { \ RB_COLOR(black, field) = RB_BLACK; \ RB_COLOR(red, field) = RB_RED; \ } while (0) #ifndef RB_AUGMENT #define RB_AUGMENT(x) do {} while (0) #endif #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ (tmp) = RB_RIGHT(elm, field); \ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_LEFT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (0) #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ (tmp) = RB_LEFT(elm, field); \ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_RIGHT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (0) /* Generates prototypes and inline functions */ #define RB_PROTOTYPE(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) #define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ attr struct type *name##_RB_INSERT(struct name *, struct type *); \ attr struct type *name##_RB_FIND(struct name *, struct type *); \ attr struct type *name##_RB_NFIND(struct name *, struct type *); \ attr struct type *name##_RB_NEXT(struct type *); \ attr struct type *name##_RB_PREV(struct type *); \ attr struct type *name##_RB_MINMAX(struct name *, int); \ \ /* Main rb operation. * Moves node close to the key of elm to top */ #define RB_GENERATE(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp,) #define RB_GENERATE_STATIC(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ attr void \ name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ { \ struct type *parent, *gparent, *tmp; \ while ((parent = RB_PARENT(elm, field)) && \ RB_COLOR(parent, field) == RB_RED) { \ gparent = RB_PARENT(parent, field); \ if (parent == RB_LEFT(gparent, field)) { \ tmp = RB_RIGHT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_RIGHT(parent, field) == elm) { \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_RIGHT(head, gparent, tmp, field); \ } else { \ tmp = RB_LEFT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_LEFT(parent, field) == elm) { \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_LEFT(head, gparent, tmp, field); \ } \ } \ RB_COLOR(head->rbh_root, field) = RB_BLACK; \ } \ \ attr void \ name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ { \ struct type *tmp; \ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ elm != RB_ROOT(head)) { \ if (RB_LEFT(parent, field) == elm) { \ tmp = RB_RIGHT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = RB_RIGHT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ struct type *oleft; \ if ((oleft = RB_LEFT(tmp, field)))\ RB_COLOR(oleft, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_RIGHT(head, tmp, oleft, field);\ tmp = RB_RIGHT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_RIGHT(tmp, field)) \ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_LEFT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } else { \ tmp = RB_LEFT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = RB_LEFT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ struct type *oright; \ if ((oright = RB_RIGHT(tmp, field)))\ RB_COLOR(oright, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_LEFT(head, tmp, oright, field);\ tmp = RB_LEFT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_LEFT(tmp, field)) \ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_RIGHT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } \ } \ if (elm) \ RB_COLOR(elm, field) = RB_BLACK; \ } \ \ attr struct type * \ name##_RB_REMOVE(struct name *head, struct type *elm) \ { \ struct type *child, *parent, *old = elm; \ int color; \ if (RB_LEFT(elm, field) == NULL) \ child = RB_RIGHT(elm, field); \ else if (RB_RIGHT(elm, field) == NULL) \ child = RB_LEFT(elm, field); \ else { \ struct type *left; \ elm = RB_RIGHT(elm, field); \ while ((left = RB_LEFT(elm, field))) \ elm = left; \ child = RB_RIGHT(elm, field); \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ if (RB_PARENT(elm, field) == old) \ parent = elm; \ (elm)->field = (old)->field; \ if (RB_PARENT(old, field)) { \ if (RB_LEFT(RB_PARENT(old, field), field) == old)\ RB_LEFT(RB_PARENT(old, field), field) = elm;\ else \ RB_RIGHT(RB_PARENT(old, field), field) = elm;\ RB_AUGMENT(RB_PARENT(old, field)); \ } else \ RB_ROOT(head) = elm; \ RB_PARENT(RB_LEFT(old, field), field) = elm; \ if (RB_RIGHT(old, field)) \ RB_PARENT(RB_RIGHT(old, field), field) = elm; \ if (parent) { \ left = parent; \ do { \ RB_AUGMENT(left); \ } while ((left = RB_PARENT(left, field))); \ } \ goto color; \ } \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ color: \ if (color == RB_BLACK) \ name##_RB_REMOVE_COLOR(head, parent, child); \ return (old); \ } \ \ /* Inserts a node into the RB tree */ \ attr struct type * \ name##_RB_INSERT(struct name *head, struct type *elm) \ { \ struct type *tmp; \ struct type *parent = NULL; \ int comp = 0; \ tmp = RB_ROOT(head); \ while (tmp) { \ parent = tmp; \ comp = (cmp)(elm, parent); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ RB_SET(elm, parent, field); \ if (parent != NULL) { \ if (comp < 0) \ RB_LEFT(parent, field) = elm; \ else \ RB_RIGHT(parent, field) = elm; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = elm; \ name##_RB_INSERT_COLOR(head, elm); \ return (NULL); \ } \ \ /* Finds the node with the same key as elm */ \ attr struct type * \ name##_RB_FIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (NULL); \ } \ \ /* Finds the first node greater than or equal to the search key */ \ attr struct type * \ name##_RB_NFIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *res = NULL; \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) { \ res = tmp; \ tmp = RB_LEFT(tmp, field); \ } \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (res); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name##_RB_NEXT(struct type *elm) \ { \ if (RB_RIGHT(elm, field)) { \ elm = RB_RIGHT(elm, field); \ while (RB_LEFT(elm, field)) \ elm = RB_LEFT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name##_RB_PREV(struct type *elm) \ { \ if (RB_LEFT(elm, field)) { \ elm = RB_LEFT(elm, field); \ while (RB_RIGHT(elm, field)) \ elm = RB_RIGHT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ attr struct type * \ name##_RB_MINMAX(struct name *head, int val) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *parent = NULL; \ while (tmp) { \ parent = tmp; \ if (val < 0) \ tmp = RB_LEFT(tmp, field); \ else \ tmp = RB_RIGHT(tmp, field); \ } \ return (parent); \ } #define RB_NEGINF -1 #define RB_INF 1 #define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) #define RB_FIND(name, x, y) name##_RB_FIND(x, y) #define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) #define RB_NEXT(name, x, y) name##_RB_NEXT(y) #define RB_PREV(name, x, y) name##_RB_PREV(y) #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) #define RB_FOREACH(x, name, head) \ for ((x) = RB_MIN(name, head); \ (x) != NULL; \ (x) = name##_RB_NEXT(x)) #define RB_FOREACH_SAFE(x, name, head, y) \ for ((x) = RB_MIN(name, head); \ ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ (x) = (y)) #define RB_FOREACH_REVERSE(x, name, head) \ for ((x) = RB_MAX(name, head); \ (x) != NULL; \ (x) = name##_RB_PREV(x)) #endif /* _SYS_TREE_H_ */ spectrwm-SPECTRWM_3_1_0/linux/util.h000066400000000000000000000014571316475426700173600ustar00rootroot00000000000000#define FPARSELN_UNESCESC 0x01 #define FPARSELN_UNESCCONT 0x02 #define FPARSELN_UNESCCOMM 0x04 #define FPARSELN_UNESCREST 0x08 #define FPARSELN_UNESCALL 0x0f size_t strlcpy(char *, const char *, size_t); size_t strlcat(char *, const char *, size_t); char *fgetln(FILE *, size_t *); char *fparseln(FILE *, size_t *, size_t *, const char [3], int); long long strtonum(const char *, long long, long long, const char **); #ifndef WAIT_ANY #define WAIT_ANY (-1) #endif /* there is no limit to ulrich drepper's crap */ #ifndef TAILQ_END #define TAILQ_END(head) NULL #endif #ifndef TAILQ_FOREACH_SAFE #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)) #endif spectrwm-SPECTRWM_3_1_0/osx/000077500000000000000000000000001316475426700156755ustar00rootroot00000000000000spectrwm-SPECTRWM_3_1_0/osx/Makefile000066400000000000000000000042621316475426700173410ustar00rootroot00000000000000PREFIX?= /usr/local BINDIR?= $(PREFIX)/bin LIBDIR?= $(PREFIX)/lib MANDIR?= $(PREFIX)/share/man BUILDVERSION = $(shell sh $(CURDIR)/../buildver.sh) # To use xquartz, uncomment the following three lines. INCFLAGS+= -I/opt/X11/include -I/opt/X11/include/freetype2/ LDADD+= -L/opt/X11/lib -lX11 -lXcursor -lXft # To use homebrew, uncomment the following three lines. #INCFLAGS+= -I/opt/X11/include #LDADD+= -lX11 -lXcursor -lXft -L/opt/X11/lib #INCFLAGS+= -I/usr/local/Cellar/freetype/2.5.0.1/include/freetype2 # To use homebrew , comment out the following two lines. #INCFLAGS+= -I/opt/local/include/freetype2 -I/opt/local/include #LDADD+= -L/opt/local/lib -lX11 -lXcursor -lXft LDADD+= -lxcb-keysyms -lxcb-util -lxcb-randr -lX11-xcb -lxcb-xtest -lxcb -lxcb-icccm LVERS= $(shell . ../lib/shlib_version; echo $$major.$$minor) CFLAGS+= -std=c99 -Wmissing-prototypes -Wall -Wextra -Wshadow -Wno-uninitialized -g CFLAGS+= -O2 -D_GNU_SOURCE -D__OSX__ -I. -I.. ${INCFLAGS} CFLAGS+= -DSWM_LIB=\"$(LIBDIR)/libswmhack.so.$(LVERS)\" ifneq ("${BUILDVERSION}", "") CFLAGS+= -DSPECTRWM_BUILDSTR=\"$(BUILDVERSION)\" endif CC?= cc all: spectrwm libswmhack.so.$(LVERS) spectrwm.c: ln -sf ../linux/tree.h ln -sf ../spectrwm.c ln -sf ../version.h swm_hack.c: ln -sf ../lib/swm_hack.c spectrwm: spectrwm.o osx.o $(CC) $(LDFLAGS) -o $@ $+ $(LDADD) %.so: %.c $(CC) $(CFLAGS) -c -fpic -DPIC $+ -o $@ libswmhack.so.$(LVERS): swm_hack.so $(CC) $(LDFLAGS) -shared -fpic -o libswmhack.so.$(LVERS) swm_hack.so $(LDADD) # replace above line with this for OSX 10.5 # $(CC) -shared -bundle -fpic -o libswmhack.so.$(LVERS) $(LDADD) swm_hack.so install: all install -m 755 -d $(DESTDIR)$(BINDIR) install -m 755 -d $(DESTDIR)$(LIBDIR) install -m 755 -d $(DESTDIR)$(MANDIR)/man1 install -m 755 spectrwm $(DESTDIR)$(BINDIR) install -m 755 libswmhack.so.$(LVERS) $(DESTDIR)$(LIBDIR) install -m 644 ../spectrwm.1 $(DESTDIR)$(MANDIR)/man1/spectrwm.1 ln -sf spectrwm $(DESTDIR)$(BINDIR)/scrotwm ln -sf libswmhack.so.0.0 $(DESTDIR)$(LIBDIR)/libswmhack.so.0 ln -sf libswmhack.so.0.0 $(DESTDIR)$(LIBDIR)/libswmhack.so clean: rm -f spectrwm *.o *.so libswmhack.so.* spectrwm.c swm_hack.c tree.h version.h .PHONY: all install clean spectrwm-SPECTRWM_3_1_0/osx/osx.c000066400000000000000000000036661316475426700166650ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "osx.h" /* --------------------------------------------------------------------------- */ /* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert 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. */ #define INVALID 1 #define TOOSMALL 2 #define TOOLARGE 3 long long strtonum(const char *numstr, long long minval, long long maxval, const char **errstrp) { long long ll = 0; char *ep; int error = 0; struct errval { const char *errstr; int err; } ev[4] = { { NULL, 0 }, { "invalid", EINVAL }, { "too small", ERANGE }, { "too large", ERANGE }, }; ev[0].err = errno; errno = 0; if (minval > maxval) error = INVALID; else { ll = strtoll(numstr, &ep, 10); if (numstr == ep || *ep != '\0') error = INVALID; else if ((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); } spectrwm-SPECTRWM_3_1_0/osx/osx.h000066400000000000000000000001071316475426700166550ustar00rootroot00000000000000long long strtonum(const char *, long long, long long, const char **); spectrwm-SPECTRWM_3_1_0/osx/queue.h000066400000000000000000000560601316475426700172010ustar00rootroot00000000000000/* $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) #define SLIST_SWAP(head1, head2, type) do { \ struct type *swap_first = SLIST_FIRST(head1); \ SLIST_FIRST(head1) = SLIST_FIRST(head2); \ SLIST_FIRST(head2) = swap_first; \ } 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) #define LIST_SWAP(head1, head2, type, field) do { \ struct type *swap_tmp = LIST_FIRST((head1)); \ LIST_FIRST((head1)) = LIST_FIRST((head2)); \ LIST_FIRST((head2)) = swap_tmp; \ if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ } 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) #define TAILQ_SWAP(head1, head2, type, field) do { \ struct type *swap_first = (head1)->tqh_first; \ struct type **swap_last = (head1)->tqh_last; \ (head1)->tqh_first = (head2)->tqh_first; \ (head1)->tqh_last = (head2)->tqh_last; \ (head2)->tqh_first = swap_first; \ (head2)->tqh_last = swap_last; \ if ((swap_first = (head1)->tqh_first) != NULL) \ swap_first->field.tqe_prev = &(head1)->tqh_first; \ else \ (head1)->tqh_last = &(head1)->tqh_first; \ if ((swap_first = (head2)->tqh_first) != NULL) \ swap_first->field.tqe_prev = &(head2)->tqh_first; \ else \ (head2)->tqh_last = &(head2)->tqh_first; \ } 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_ */ spectrwm-SPECTRWM_3_1_0/outdated_man_pages/000077500000000000000000000000001316475426700207075ustar00rootroot00000000000000spectrwm-SPECTRWM_3_1_0/outdated_man_pages/spectrwm_es.1000066400000000000000000000470251316475426700233340ustar00rootroot00000000000000.\" Copyright (c) 2009 Marco Peereboom .\" Copyright (c) 2009 Darrin Chandler .\" .\" 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: February 15 2012 $ .Dt SPECTRWM 1 .Os .Sh NOMBRE .Nm spectrwm .Nd es un manejador de ventanas para X11 .Sh SYNOPSIS .Nm spectrwm .Sh DESCRIPCIÓN .Nm es un manejador de ventanas super minimalista para X11. Intenta no superponer las ventanas para que las mismas puedan usarse de manera eficiente y para cosas mas importantes. Tiene configuraciones normales y no requiere que sepas un lenguaje de programacion para configurarlo. Esta escrito por hackers para hackers y apunta a ser pequeño, compacto y rápido. .Pp Cuando .Nm inicia, lo primero que hace es leer el archivo de configuracion, .Pa spectrwm.conf . Ver .Sx ARCHIVOS DE CONFIGURACIÓN . .Pp La siguiente anotacion se usa a través de esta pagina: .Pp .Bl -tag -width Ds -offset indent -compact .It Cm M Meta .It Cm S Shift .It Aq Cm Name Nombre de tecla .It Cm M1 Boton 1 del mouse .It Cm M3 Boton 3 del mouse .El .Pp .Nm es muy simple de usar. La mayoria de las acciones se hacen con los mapeos (bindings) de mouse o teclado. Ver la sección de .Sx BINDINGS para las personalizaciones y configuraciones por defecto. .Sh ARCHIVOS DE CONFIGURACIÓN .Nm primero trata de abrir el archivo por defecto en el directorio del usuario, .Pa ~/.spectrwm.conf . Si ese archivo no esta disponible, luego trata de abrir el archivo global de configuracion .Pa /etc/spectrwm.conf . .Pp El formato del archivo es \*(Lttecla\*(Gt = \*(Ltconfiguracion\*(Gt. Por ejemplo: .Pp .Dl color_focus = red .Pp Habilitamos o deshabilitamos la opción usando 1 o 0 respectivamente. .Pp El archivo soporta las siguientes palabras clave: .Pp .Bl -tag -width "title_class_enabledXXX" -offset indent -compact .It Cm autorun Inicia una aplicacion en un escritorio en particular al primer inicio. Definido por el formato ws[]:aplicacion, ej. ws[2]:xterm lanza xterm en el escritorio 2. .It Cm color_focus Color del borde de la ventana en foco. .It Cm color_unfocus Color del borde de la ventana fuera de foco. .It Cm bar_enabled Habilitar o deshabilitar la barra de estado. .It Cm bar_border Ns Bq Ar x Color del borde de la barra de estado en pantalla. .Ar x . .It Cm bar_border_width Setea el grosor de la barra de estado en pixels. Deshabilitado seteando 0. .It Cm bar_color Ns Bq Ar x Color de la ventana de la barra de estado en pantalla. .Ar x . .It Cm bar_font_color Ns Bq Ar x Color de la fuente en la barra de estado en pantalla. .Ar x . .It Cm bar_font Fuente de la barra de estado. .It Cm bar_action Scripts externos con populares agregados de información para la barra de estado, como la vida de la bateria. .It Cm bar_at_bottom Puedes posicionar la statusbar en la parte inferior de la pantalla. .It Cm stack_enabled Habilitar o deshabilitar mostrar el algoritmo de apilamiento en la barra de estado. .It Cm clock_enabled Habilitar o deshabilitar el reloj en la barra de estado, deshabilitado por defecto con un 0, para usar el reloj de la barra de estado (bar_action) .Pa bar_action script. .It Cm dialog_ratio Algunas aplicaciones tienen ventanas de dialogo muy pequeñas como para ser usables. Este relación (ratio) es el tamaño de la pantalla, por ejemplo 0.6 es 60% del tamaño físico de la pantalla. .It Cm layout Selecciona una disposicion para usar en el primer inicio. Definido con el formato ws[idx]:master_grow:master_add:stack_inc:layout:always_raise:stack_mode, ej. ws[2]:-4:0:1:0:horizontal setea el escritorio 2 en horizontal, el stack principal y reduce 4 puntos agregando una ventana al stack, mientras mantiene el comportamiento de ventanas flotantes. Modos posible de stack_mode .Pa vertical , .Pa horizontal and .Pa fullscreen . .Pp Ver .Pa master_grow , .Pa master_shrink , .Pa master_add , .Pa master_del , .Pa stack_inc , .Pa stack_del , y .Pa always_raise para mas informacion. Tenga en cuenta que las opciones de stack son complicados y tienen efectos secundarios. Uno debe familiarizarse con estos comandos antes de experimentar con la opcion .Pa layout .Pp Esta opcion no necesita un reinicio. .It Cm region Acomodar una region personalizada, removiendo cualquier autodeteción de regiones que ocupe el espacio en la pantalla. Definiendo el formato screen[]:WIDTHxHEIGHT+X+Y, e.g.\& screen[1]:800x1200+0+0. .It Cm term_width Setear un ancho minimo preferido para la terminal. Si el valor es mayor que 0, .Nm intentará ajustar el tamaño de la fuente de la terminal para mantener el ancho de la terminal por encima de este número cuando la ventana cambia de tamaño. Actualmente solo es soportado por .Xr xterm 1 El binario de .Xr xterm 1 no debe ser un setuid o setgid, que no sea el que viene por defecto en la mayoria de los sistemas. Los usuarios pueden necesitar setear program[term] (ver la sección .Sx PROGRAMAS ) para usar una copia alternativa del binario de .Xr xterm 1 sin el seteo del setgid. .It Cm title_class_enabled Habilitar o deshabilitar la clase de ventana en la barre de estado. Habilitado seteando 1 .It Cm title_name_enabled Habilitar o deshabilita el titulo de la ventana en la barra de estado. Habilitado seteando 1 .It Cm urgent_enabled Habilitar o deshabilitar el aviso de urgencia. Tenga en cuenta que muchos emuladores de terminal requieren de este parametro habilitado para que funcione. En xterm, por ejemplo, hay que agregar la siguiente linea .Pa xterm.urgentOnBell: true to .Pa .Xdefaults . .It Cm window_name_enabled Habilitar o deshabilita el nombre de la ventana en la barra de estado. Habilitado seteando 1 .It Cm verbose_layout Habilitar o deshabilita la notificacion del area principal y el stack en la barra de estado. Habilitado seteandolo a 1. .It Cm modkey Cambiar mod key. Mod1 generalmente es la tecla ALT y Mod4 la tecla de windows en una PC. .It Cm focus_mode Usando el valor de .Pa follow_cursor puedes hacer que el manejador de ventanas se enfoque en la ventana cuando el cursor pase por arriba de las mismas o bien cambiando de estacion de trabajo. .It Cm disable_border Remueve el borde de una sola ventana cuando la barra de estado esta desactivada. .It Cm border_width Setea el grosor del borde de la ventana en pixels. Deshabilitar todos los bordes seteandolo a 0. .It Cm program Ns Bq Ar p Definir una nueva accion para ejecutar un programa. .Ar p . Ver la sección de .Sx PROGRAMAS .It Cm bind Ns Bq Ar x Combinación de teclas para una acción .Ar x . Ver la sección .Sx BINDINGS .It Cm quirk Ns Bq Ar c:n Agregar un "quirk" (o forzados) para ventanas con clase .Ar c y nombre .Ar n . Ver la sección .Sx QUIRKS .El .Pp Los colores deben ser especificados por la especificación .Xr XQueryColor 3 y las fuentes por la especificación .Xr XQueryFont 3 .Sh PROGRAMAS .Nm te permite definir acciones personales para lanzar los programas que quieras y luego obligar a la misma con una función de acciones. Ver la sección .Sx BINDINGS .Pp Los programas por defecto se describen acá: .Pp .Bl -tag -width "screenshot_wind" -offset indent -compact .It Cm term xterm .It Cm screenshot_all screenshot.sh completo .It Cm screenshot_wind screenshot.sh por ventana .It Cm lock xlock .It Cm initscr initscreen.sh .It Cm menu dmenu_run \-fn $bar_font \-nb $bar_color \-nf $bar_font_color \-sb $bar_border \-sf $bar_color .El .Pp Los programas en la configuración personal, se especifican aca: .Pp .Dl program[] = [ [... ]] .Pp .Aq nombre es un identificador, no genera conflictos con ninguna accion o palabra clave, .Aq progpath es la ruta al programa, y .Aq arg es ninguno o mas de un argumento para el programa. .Pp Las siguientes variables de configuracion en .Nm (ver .Sx ARCHIVOS DE CONFIGURACIÓN ), y pueden ser usadas en los campos de .Aq arg como asi tambien sustituidas por valores al momento del inicio de un programa: .Pp .Bl -tag -width "$bar_font_color" -offset indent -compact .It Cm $bar_border .It Cm $bar_color .It Cm $bar_font .It Cm $bar_font_color .It Cm $color_focus .It Cm $color_unfocus .El .Pp Ejemplo: .Bd -literal -offset indent program[ff] = /usr/local/bin/firefox http://spectrwm.com.ar/ bind[ff] = Mod+f # Ahora Mod+F inicia firefox .Ed .Pp Para deshacer lo anterior: .Bd -literal -offset indent bind[] = Mod+f program[ff] = .Ed .Sh BINDINGS .Nm provee muchas funciones (o acciones) accesibles por medio de la asignación (bindings) de teclas o el mouse. .Pp Las corrientes asignaciones (bindings) del mouse son: .Pp .Bl -tag -width "M-j, M-XXX" -offset indent -compact .It Cm M1 Enfoco una ventana .It Cm M-M1 Muevo una ventana .It Cm M-M3 Redimenciono una ventana .It Cm M-S-M3 Redimenciono una ventana hasta que quede centrada .El .Pp Las corrientes asignaciones (bindings) de teclas son: .Pp .Bl -tag -width "M-j, M-XXX" -offset indent -compact .It Cm M-S- Ns Aq Cm Return term .It Cm M-p menu .It Cm M-S-q quit .It Cm M-q restart .Nm .It Cm M- Ns Aq Cm Space cycle_layout .It Cm M-S- Ns Aq Cm Space reset_layout .It Cm M-h master_shrink .It Cm M-l master_grow .It Cm M-, master_add .It Cm M-. master_del .It Cm M-S-, stack_inc .It Cm M-S-. stack_del .It Cm M- Ns Aq Cm Return swap_main .It Xo .Cm M-j , .Cm M- Ns Aq Cm TAB .Xc focus_next .It Xo .Cm M-k , .Cm M-S- Ns Aq Cm TAB .Xc focus_prev .It Cm M-m focus_main .It Cm M-S-j swap_next .It Cm M-S-k swap_prev .It Cm M-b bar_toggle .It Cm M-x wind_del .It Cm M-S-x wind_kill .It Cm M- Ns Aq Ar n .Pf ws_ Ns Ar n .It Cm M-S- Ns Aq Ar n .Pf mvws_ Ns Ar n .It Cm M- Ns Aq Cm Right ws_next .It Cm M- Ns Aq Cm Left ws_prev .It Cm M-a ws_prior .It Cm M-S- Ns Aq Cm Right screen_next .It Cm M-S- Ns Aq Cm Left screen_prev .It Cm M-s screenshot_all .It Cm M-S-s screenshot_wind .It Cm M-S-v version .It Cm M-t float_toggle .It Cm M-S Aq Cm Delete lock .It Cm M-S-i initscr .It Cm M-w iconify .It Cm M-S-w uniconify .It Cm M-S-r always_raise .It Cm M-v button2 .It Cm M-- width_shrink .It Cm M-= width_grow .It Cm M-S- height_shrink .It Cm M-S-= height_grow .It Cm M-[ move_left .It Cm M-] move_right .It Cm M-S-[ move_up .It Cm M-S-] move_down .El .Pp El nombre de las acciónes descripta a continuación: .Pp .Bl -tag -width "M-j, M-XXX" -offset indent -compact .It Cm term Ejecutar una terminal (ver .Sx PROGRAMAS ) .It Cm menu Menu (ver .Sx PROGRAMAS ) .It Cm quit Salir .Nm .It Cm restart Reiniciar .Nm .It Cm cycle_layout Disposición de las ventanas .It Cm reset_layout Reiniciar la disposición de las ventanas .It Cm master_shrink Achicar la region principal .It Cm master_grow Agrandar la region principal .It Cm master_add Agregar una ventana a la region principal .It Cm master_del Quitar una ventana de la region principal .It Cm stack_inc Agregar columnas/filas a las pilas .It Cm stack_del Quitar columnas/filas de las pilas .It Cm swap_main Mover la ventana corriente a la region principal .It Cm focus_next Enfocar la proxima ventana en la estación de trabajo .It Cm focus_prev Enfocar la anterior ventana en la estación de trabajo .It Cm focus_main Enfocar en la ventana principal de la estación de trabajo .It Cm swap_next Ejecutar con la siguiente ventana en la estación de trabajo .It Cm swap_prev Ejecutar con la anterior ventana en la estación de trabajo .It Cm bar_toggle Cambiar la barra de estado en todas las estaciones de trabajo .It Cm wind_del Borrar la ventana corriente en la estación de trabajo .It Cm wind_kill Destruir la ventana corriente en la estación de trabajo .It Cm ws_ Ns Ar n Cambiar entre estaciones de trabajo .Ar n , donde .Ar n es 1 por 10 .It Cm mvws_ Ns Ar n Mover la ventana corriente a una estación de trabajo .Ar n , donde .Ar n es 1 por 10 .It Cm ws_next Cambiar a la proxima estación de trabajo con una ventana en ella .It Cm ws_prev Cambiar a la anterior estación de trabajo con una ventana en ella .It Cm screen_next Mover el puntero a la proxima region .It Cm screen_prev Mover el puntero a la anterior region .It Cm screenshot_all Tomar una captura de pantalla de todo la pantalla (si esta habilitado) (ver .Sx PROGRAMAS ) .It Cm screenshot_wind Tomar una captura de pantalla de la ventana seleccionada (si esta habilitado) (ver .Sx PROGRAMAS ) .It Cm version Mostrar la version en la barra de estado .It Cm float_toggle Mostar la ventana en foco entre las flotantes y acomodadas .It Cm lock Bloquear pantalla (ver .Sx PROGRAMAS ) .It Cm initscr Reiniciar la pantalla (ver .Sx PROGRAMAS ) .It Cm iconify Minimiza (unmap) la ventana en foco. .It Cm uniconify Maximiza (map) la ventana seleccionada por dmenu. .It Cm always_raise Cuando se establece las ventanas en cascada se esconden las ventanas flotantes. .It Cm button2 Falsifica el boton del medio del mouse. .It Cm width_shrink Reducir el ancho de una ventana flotante. .It Cm width_grow Agranda el ancho de una ventana flotante. .It Cm height_shrink Reducir la altura de una ventana flotante. .It Cm height_grow Agranda la altura de una ventana flotante. .It Cm move_left Mueve la ventana flotante un paso a la izquierda. .It Cm move_right Mueve la ventana flotante un paso a la derecha. .It Cm move_up Mueve la ventana flotante un paso arriba. .It Cm move_down Mueve la ventana flotante un paso abajo. .El .Pp Personalizar mapeos (bindings) en el archivo de configuración: .Pp .Dl bind[] = .Pp .Aq accion una de las acciones listadas (o ninguna) y .Aq teclas una o mas teclas modificadas (puede ser ninguna tambien) (MOD, Mod1, Shift, etc.) y una o mas teclas normales (b, barra espaciadora, etc.), separadas por un "+". Por ejemplo: .Bd -literal -offset indent bind[reset] = Mod4+q # combinación Tecla de Windows + q reinicia bind[] = Mod1+q # des-hace la combinación Alt + q .Ed .Pp Multiples combinaciones de teclas pueden hacer lo mismo. .Sh QUIRKS .Nm te da "quirks" (o forzados) ventanas que tienen que ser tratas de manera especial, como por ejemplo, popups, aplicaciones de pantalla completa, etc. .Pp Los "quirks" (o forzados) por defecto son: .Pp .Bl -tag -width "OpenOffice.org N.M:VCLSalFrameXXX" -offset indent -compact .It Firefox\-bin:firefox\-bin TRANSSZ .It Firefox:Dialog FLOAT .It Gimp:gimp FLOAT + ANYWHERE .It MPlayer:xv FLOAT + FULLSCREEN .It OpenOffice.org 2.4:VCLSalFrame FLOAT .It OpenOffice.org 3.1:VCLSalFrame FLOAT .It pcb:pcb FLOAT .It xine:Xine Window FLOAT + ANYWHERE .It xine:xine Panel FLOAT + ANYWHERE .It xine:xine Video Fullscreen Window FULLSCREEN + FLOAT .It Xitk:Xitk Combo FLOAT + ANYWHERE .It Xitk:Xine Window FLOAT + ANYWHERE .It XTerm:xterm XTERM_FONTADJ .El .Pp Los "quirks" (o forzados) se describen a continuación: .Pp .Bl -tag -width "XTERM_FONTADJXXX" -offset indent -compact .It FLOAT Esta ventana no tiene que ser acomodada, pero le permitimos flotar libremente. .It TRANSSZ Ajusta el tamaño de las ventanas transitorias que son demasiado pequeñas utilizando dialog_ratio (ver .Sx ARCHIVOS DE CONFIGURACIÓN ) . .It ANYWHERE Permite que la ventana se ponga donde quiera. .It XTERM_FONTADJ Ajusta las fuentes de xterm cuando se redimenciona. .It FULLSCREEN Quita el borde para permitir las ventanas en pantalla completa. .It FOCUSPREV El enfoque de salida fuerza la solicitud de aplicacisn que anteriormente se centraba en la aplicacion anterior del stack. .El .Pp Las configuraciones de "quirks" (o forzados) en el archivo de configuración se ven a continuación: .Pp .Dl quirk[:] = [ + ... ] .Pp .Aq clases y .Aq nombre especifica la ventana en la cual el "quirk(s)" (o forzados) se aplica, y .Aq quirk es uno de los "quirks" (o forzados) de la lista. Por ejemplo: .Bd -literal -offset indent quirk[MPlayer:xv] = FLOAT + FULLSCREEN # dejamos que mplayer funcione libremente quirk[pcb:pcb] = NONE # borramos el quirk existente .Ed .Pp Podes obtener .Aq clases y .Aq nombre corriendo el programa xprop(1) y luego clickear en la ventana que quieras. En el proximo ejemplo, podremos verlo en acción con una ventana de Firefox: .Bd -literal -offset indent $ xprop | grep WM_CLASS WM_CLASS(STRING) = "Navigator", "Firefox" .Ed .Sh EWMH .Nm parcialmente implementa los Consejos de ventana extendido Manager (EWMH) especificacion. Esto permite el control de las ventanas, asi como .Nm si a partir de scripts y programas externos. Esto se logra mediante .Nm responder a ciertos eventos ClientMessage. Desde la terminal de estos eventos se puede enviar facilmente el uso de herramientas tales como .Xr wmctrl 1 y .Xr xdotool 1 . para el formato real de estos eventos ClientMessage, consulte la especificacion EWMH. .Pp La Identificacion de la ventana actualmente enfocada se almacena en el _NET_ACTIVE_WINDOW propiedad de la ventana raiz. Esto puede ser usado por ejemplo para recuperar el titulo de la ventana activa con .Xr xprop 1 y .Xr grep 1 : .Bd -literal -offset indent $ WINDOWID=`xprop \-root _NET_ACTIVE_WINDOW | grep \-o "0x.*"` $ xprop \-id $WINDOWID WM_NAME | grep \-o "\\".*\\"" .Ed .Pp Una ventana se puede enfocar mediante el envio de un mensaje del cliente _NET_ACTIVE_WINDOW a la ventana principal. Por ejemplo, usando .Xr wmctrl 1 para enviar el mensaje (suponiendo que 0x4a0000b es el ID de la ventana para ser especifico): .Bd -literal -offset indent $ wmctrl \-i \-a 0x4a0000b .Ed .Pp Ventanas se pueden cerrar mediante el envmo de un mensaje del cliente _NET_CLOSE_WINDOW a la ventana principal. Por ejemplo, usando .Xr wmctrl 1 para enviar el mensaje (suponiendo que 0x4a0000b es el ID de la ventana se cierre): .Bd -literal -offset indent $ wmctrl \-i \-c 0x4a0000b .Ed .Pp Las ventanas se pueden flotar y flotar sin-mediante la adicion o eliminacion de la _NET_WM_STATE_ABOVE atom desde _NET_WM_STATE la propiedad de la ventana Esto se puede lograr mediante el envio de un mensaje a los clientes _NET_WM_STATE raiz de la ventana. Por ejemplo, el siguiente cambia el estado de la flota. .Xr wmctrl 1 para enviar el mensaje (suponiendo que 0x4a0000b es el ID de la ventana flotante o no-flotante): .Bd -literal -offset indent $ wmctrl \-i \-r 0x4a0000b \-b toggle,_NET_WM_STATE_ABOVE .Ed .Pp Ventanas flotantes tambien se puede cambiar el tamano y movido por el envio de un _NET_MOVERESIZE_WINDOW Mensaje del cliente de la ventana raiz. Por ejemplo, uso .Xr wmctrl 1 para enviar el mensaje (suponiendo que 0x4a0000b es el ID de la ventana a redimensionar / mover): .Bd -literal -offset indent $ wmctrl \-i \-r 0x4a0000b \-e 0,100,50,640,480 .Ed .Pp Esto mueve la ventana de (100,50) y cambia el tamaqo a 640x480. .Pp Todos los eventos _NET_MOVERESIZE_WINDOW recibido por las ventanas apiladas se ignoran. .Sh SIGNALS Enviando .Nm una senal de HUP reinicia spectrwm. .Sh ARCHIVOS .Bl -tag -width "/etc/spectrwm.confXXX" -compact .It Pa ~/.spectrwm.conf .Nm archivo de configuración especifico del usuario. .It Pa /etc/spectrwm.conf .Nm configuraciones globales. .El .Sh HISTORIA .Nm fue inspirado en xmonad y dwm. .Sh AUTORES .An -nosplit .Nm fue escrito por .Pp .Bl -tag -width "Ryan Thomas McBride Aq mcbride@countersiege.com " -offset indent -compact .It Cm Marco Peereboom Aq marco@peereboom.us .It Cm Ryan Thomas McBride Aq mcbride@countersiege.com .It Cm Darrin Chandler Aq dwchandler@stilyagin.com .It Cm Pierre-Yves Ritschard Aq pyr@spootnik.org .It Cm Tuukka Kataja Aq stuge@xor.fi .It Cm Jason L. Wright Aq jason@thought.net .It Cm Reginald Kennedy Aq rk@rejii.com .It Cm Lawrence Teo Aq lteo@lteo.net .It Cm Tiago Cunha Aq tcunha@gmx.com .El spectrwm-SPECTRWM_3_1_0/outdated_man_pages/spectrwm_it.1000066400000000000000000001110441316475426700233320ustar00rootroot00000000000000.\" Copyright (c) 2009-2012 Marco Peereboom .\" Copyright (c) 2009 Darrin Chandler .\" Copyright (c) 2011-2014 Reginald Kennedy .\" Copyright (c) 2011-2012 Lawrence Teo .\" Copyright (c) 2011-2012 Tiago Cunha .\" Copyright (c) 2012 David Hill .\" .\" 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: February 15 2012 $ .Dt SPECTRWM 1 .Os .Sh NOME .Nm spectrwm .Nd gestore di finestre per X11 .Sh SINTASSI .Nm spectrwm .Sh DESCRIZIONE .Nm \[`e] un gestore di finestre minimale che cerca di stare in disparte, in modo che il prezioso spazio sullo schermo possa essere usato per cose molto pi\[`u] importanti. Ha delle impostazioni predefinite ragionevoli e non richiede all'utente di imparare un nuovo linguaggio per modificarne la configurazione. \[`E] stato scritto dagli hacker per gli hacker, ed ha come obiettivo quello di essere piccolo, compatto e veloce. .Pp All'avvio, .Nm legge le impostazioni presenti nel suo file di configurazione, .Pa spectrwm.conf . Vedere la sezione .Sx FILE DI CONFIGURAZIONE pi\[`u] sotto. .Pp Le seguenti notazioni verranno utilizzate all'interno di questa pagina: .Pp .Bl -tag -width Ds -offset indent -compact .It Cm M Meta .It Cm S Shift .It Aq Cm Name Tasto Name .It Cm M1 Tasto 1 (sinistro) del mouse .It Cm M3 Tasto 3 (destro) del mouse .El .Pp .Nm \[`e] molto semplice da usare. La maggior parte delle azioni vengono svolte utilizzando combinazioni di tasti sulla tastiera o sul mouse. Vedere la sezione .Sx SCORCIATOIE pi\[`u] sotto per informazioni sulle impostazioni predefinite e sulle possibilit\[`a] di personalizzazione. .Sh FILE DI CONFIGURAZIONE .Nm da precedenza al file di configurazione specifico dell'utente, .Pa ~/.spectrwm.conf . Se quel file non \[`e] disponibile, tenta di utilizzare il file di configurazione globale .Pa /etc/spectrwm.conf . .Pp Il formato del file \[`e] .Pp .Dl Ar keyword Li = Ar setting .Pp Ad esempio: .Pp .Dl color_focus = red .Pp Le impostazioni di tipo binario possono essere abilitate e disabilitate utilizzando rispettivamente i valori 1 e 0. .Pp I colori devono essere specificati usando il formato usato da .Xr XQueryColor 3 . .Pp I commenti iniziano con #. Qualora fosse necessario usare usare .Ql # come parte di un'opzione, questo dovr\[`a] essere preceduto da un backslash, eg. \e#. .Pp Il file di configurazione supporta le seguenti impostazioni: .Bl -tag -width 2m .It Ic autorun All'avvio, esegue un'applicazione nell'area di lavoro specificata. Il formato da utilizzare \[`e] .Li ws Ns Bo Ar idx Bc : Ns Ar application , eg. ws[2]:xterm esegue .Xr xterm 1 nell'area di lavoro 2. .It Ic bar_action Script esterno che produce informazioni aggiuntive, come ad esempio quelle sullo stato di carica della batteria, da inserire nella barra di stato. .It Ic bar_at_bottom Posiziona la barra di stato in fondo ad ogni regione anzich\['e] in cima. .It Ic bar_borders Ns Bq Ar x Colore del bordo della barra di stato nello schermo .Ar x . .It Ic bar_border_unfocus Ns Bq Ar x Colore del bordo della barra di stato nelle regioni dello schermo .Ar x che non hanno il focus. .It Ic bar_border_width Imposta lo spessore del bordo della barra di stato, in pixel. Il bordo pu\[`o] essere disabilitato usando il valore 0. .It Ic bar_color Ns Bq Ar x Colore di sfondo della barra di stato nello schermo .Ar x . .It Ic bar_enabled Imposta il valore predefinito di .Ic bar_toggle ; il valore predefinito per questa opzione \[`e] 1. .It Ic bar_enabled_ws Ns Bq Ar x Imposta il valore predefinito di .Ic bar_toggle_ws per l'area di lavoro .Ar x ; il valore predefinito per questa opzione \[`e] 1. .It Ic bar_font Font da usare per la barra di stato. Il font pu\[`o] essere specificato usando Xft o X Logical Font Description (XLFD). \[`E] possibile specificare dei font secondari separandoli tra loro con una virgola. Se tutte le specifiche sono nel formato XLFD verranno usati i gruppi di font; se almeno una delle specifiche \[`e] in formato Xft, verr\[`a] usato Xft. Quando Xft \[`e] in uso, verr\[`a] usato solo il primo font caricato con successo anche nel caso in cui non dovesse comprendere tutti i glifi necessari. L'impostazione predefinita prevede l'uso dei gruppi di font; \[`e] bene tenere conto del fatto che .Xr dmenu 1 non supporta Xft. .Pp Esempio con Xft: .Bd -literal -offset indent bar_font = Terminus:style=Regular:pixelsize=14:antialias=true bar_font = -*-profont-medium-*-*-*-11-*-*-*-*-*-*-*,Terminus:pixelsize=14,\ -*-clean-medium-*-*-*-12-*-*-*-*-*-*-* .Ed .Pp Esempio con gruppi di font: .Bd -literal -offset indent bar_font = -*-terminus-medium-*-*-*-14-*-*-*-*-*-*-* bar_font = -*-profont-medium-*-*-*-11-*-*-*-*-*-*-*,\ -*-terminus-medium-*-*-*-14-*-*-*-*-*-*-*,\ -*-clean-medium-*-*-*-12-*-*-*-*-*-*-* .Ed .Pp Per ottenere un elenco dei font disponibili sul proprio sistema, usare .Xr fc-list 1 o .Xr xlsfonts 1 . L'applicazione .Xr xfontsel 1 pu\[`o] essere d'aiuto per il formato XLFD. .It Ic bar_font_color Ns Bq Ar x Colore del testo nella barra di stato nello schermo .Ar x . .It Ic bar_format Imposta il formato da utilizzare per la barra di stato, sovrascrivendo .Ic clock_format e tutte le opzioni che terminano con .Ic enabled . Il formato viene passato a .Xr strftime 3 prima di essere usato, e pu\[`o] contenere le seguenti sequenze di caratteri: .Bl -column "Sequenza di caratteri" "Sostituita con" -offset indent .It Sy "Sequenza di caratteri" Ta Sy "Sostituita con" .It Li "+<" Ta "Lascia uno spazio" .It Li "+A" Ta "Output dello script esterno" .It Li "+C" Ta "Classe della finestra corrente (da WM_CLASS)" .It Li "+D" Ta "Nome dell'area di lavoro corrente" .It Li "+F" Ta "Indicatore di float" .It Li "+I" Ta "Indice dell'area di lavoro corrente" .It Li "+M" Ta "Numero di finestre minimizzate nell'area di lavoro corrente" .It Li "+N" Ta "Numero dello schermo corrente" .It Li "+P" Ta "Classe e istanza della finestra corrente, separate da due punti" .It Li "+S" Ta "Algoritmo di gestione delle finestre in uso" .It Li "+T" Ta "Istanza della finestra corrente (da WM_CLASS)" .It Li "+U" Ta "Indicatore di urgenza" .It Li "+V" Ta "Versione del programma" .It Li "+W" Ta "Nome della finestra corrente (da _NET_WM_NAME o WM_NAME)" .It Li "++" Ta "Il carattere" Ql + .El .Pp Tutte le sequenze di caratteri possono limitare il numero di caratteri massimo utilizzato, eg. +64A. Il testo che non contribuisce a formare una di queste sequenze di caratteri viene copiato senza subire modifiche. .It Ic bar_justify Allinea il test all'interno della barra di stato. I valori possibili sono .Ar left (sinistra), .Ar center (centro) e .Ar right (destra). .Pp Se il valore scelto non \[`e] .Ar left , il testo potrebbe non risultare allineato correttamente. Vedere .Ic bar_format per maggiori informazioni. .It Ic bind Ns Bq Ar x Associa una combinazione di tasti all'azione .Ar x . Vedere la sezione .Sx SCORCIATOIE pi\[`u] sotto. .It Ic border_width Imposta lo spessore del bordo delle finestre, in pixel. Il bordo pu\[`o] essere disabilitato usando il valore 0. .It Ic boundary_width Imposta la larghezza dell'area di contenimento delle regioni, in pixel. Questo valore permette di controllare di quanto una finestra debba essere trascinata o ridimensionata oltre il limite di una regione prima che venga considerata al di fuori di essa. Questa impostazione non viene presa in considerazione quando le finestre vengono manipolate utilizzando la tastiera. L'area di contenimento pu\[`o] essere disabilitata usando il valore 0. .It Ic clock_enabled Abilita o disabilita l'orologio nella barra di stato. Se viene usato il valore 0, \[`e] possibile inserire un orologio personalizzato nello script esterno definito in .Ic bar action . .It Ic iconic_enabled Visualizza nella barra di stato il numero di finestre minimizzate. Questo indicatore pu\[`o] essere abilitato usando il valore 1. .It Ic color_focus Colore del bordo della finestra che detiene il focus. Il colore predefinito \[`e] il rosso. .It Ic color_focus_maximized Colore del bordo della finestra che detiene il focus, se massimizzata. Il colore predefinito \[`e] quello usato per .Ic color_focus . .It Ic color_unfocus Colore del bordo delle finestre che non detengono il focus. Il colore predefinito \[`e] rgb:88/88/88. .It Ic color_unfocus_maximized Colore del bordo delle finestre che non detengono il focus, se massimizzate. Il colore predefinito \[`e] quello usato per .Ic color_unfocus . .It Ic dialog_ratio Alcune applicazioni creano finestre di dialogo troppo piccole per essere utilizzate. Questa opzione indica la percentuale dello schermo da utilizzare per le finestre di dialogo: ad esempio, il valore 0.6 indica che dovr\[`a] essere usato il 60% dello spazio disponibile. .It Ic disable_border Non mostrare i bordi quando la barra di stato \[`e] nascosta e c'\[`e] una sola finestra nella regione. .It Ic focus_close Imposta quale finestra ricever\[`a] il focus in seguito alla chiusura della finestra che lo detiene al momento. I valori possibili sono .Ar first (prima), .Ar next (successiva), .Ar previous (precedente, impostazione predefinita) e .Ar last (ultima). .Ar next e .Ar previous sono intese relativamente alla finestra che \[`e] stata chiusa. .It Ic focus_close_wrap Se abilitata, l'ultima finestra ricever\[`a] il focus quando l'ultima viene chiusa, e viceversa. Questa opzione pu\[`o] essere disabilitata usando il valore 0. .It Ic focus_default Finestra che deve ricevere il focus quando nessun'altra finestra lo detiene. I valori possibili sono .Ar first (prima) e .Ar last (ultima, impostazione predefinita). .It Ic focus_mode Comportamento del focus in relazione al cursore del mouse. I valori possibili sono: .Pp .Bl -tag -width "default" -offset indent -compact .It Ar default Modifica il focus quando viene attraversato un bordo in seguito ad un movimento del cursore o all'interazione con una finestra. .It Ar follow Modifica il focus ogni volta che il cursore attraversa un bordo, anche se questo avviene in seguito al passaggio ad un'area di lavoro diversa o ad un cambio di layout. .It Ar manual Modifica il focus solo quando si interagisce con una finestra. .El .It Ic java_workaround Evita alcuni problemi di rendering nelle GUI Java impersonando il window manager LG3D, scritto da Sun. Il valore predefinito \[`e] 1. .It Ic keyboard_mapping Rimuove tutte le scorciatoie da tastiera esistenti e carica le nuove scorciatoie dal file specificato. Questo permette di caricare scorciatoie specifiche del proprio layout di tastiera. Vedere la sezione .Sx MAPPE DI TASTIERA pi\[`u] sotto per un elenco dei file che vengono forniti. .It Ic layout Imposta il layout da utilizzare all'avvio. Definito nel formato .Li ws Ns Bo Ar idx Bc : Ns Ar master_grow : Ns Ar master_add : Ns Ar stack_inc : Ns Ar always_raise : Ns Ar stack_mode , eg. ws[2]:-4:0:1:0:horizontal configura l'area di lavoro 2 per utilizzare un algoritmo di stacking orizzontale, riduce l'area principale di 4 unit\[`a] e aggiunge una finestra all'area di stacking, mantenendo il comportamento predefinito per le finestre floating. I valori possibili per .Ar stack_mode sono .Ar vertical , .Ar vertical_flip , .Ar horizontal , .Ar horizontal_flip and .Ar fullscreen . Vedere .Ic master_grow , .Ic master_shrink , .Ic master_add , .Ic master_del , .Ic stack_inc , .Ic stack_dec , e .Ic always_raise per maggiori informazioni. Le opzioni che controllano gli algoritmi di gestione delle finestre sono complesse e possono influenzare altre opzioni, quindi \[`e] opportuno prendere confidenza con esse prima di utilizzare l'opzione .Ic layout . .Pp Questa impostazione non viene applicata al riavvio. .It Ic modkey Cambia il modificatore. Solitamente Mod1 \[`e] il tasto Alt e Mod4 il tasto Windows su un PC. .It Ic name Imposta il nome di un'area di lavoro all'avvio. Definito nel formato .Li ws Ns Bo Ar idx Bc : Ns Ar name , eg. ws[1]:Console assegna il nome .Dq Console all'area di lavoro 1. .It Ic program Ns Bq Ar p Definisce la nuova azione .Ar p , corrispondente all'esecuzione di un programma. Vedere la sezione .Sx PROGRAMMI pi\[`u] sotto. .It Ic quirk Ns Bq Ar c Ns Li : Ns Ar i Ns Li : Ns Ar n Aggiunge un "quirk" per le finestre di classe .Ar c , istanza .Ar i e nome .Ar n . Vedere la sezione .Sx QUIRK pi\[`u] sotto. .It Ic region Definisce una regione personalizzata, rimuovendo tutte le regioni create in automatico che occupano la stessa parte dello schermo. Definita nel formato .Li screen Ns Bo Ar idx Ns Bc : Ns Ar width Ns x Ns Ar height Ns + Ns Ar x Ns + Ns Ar y , eg. screen[1]:800x1200+0+0. .Pp Per fare s\[`i] che una regione copra pi\[`u] monitor, \[`e] sufficiente definirla in modo che li occupi tutti, eg. screen[1]:2048x768+0+0 definisce una regione che copre due monitor con risoluzione 1024x768 posizionati uno di fianco all'altro. .It Ic region_padding Larghezza, in pixel, dello spazio lasciato vuoto all'interno di una regione. Questa opzione pu\[`o] essere disabilitata usando il valore 0. .It Ic spawn_position Posizione da assegnare alle finestre al momento della loro creazione. I valori possibili sono .Ar first (prima), .Ar next (successiva), .Ar previous (precedente) e .Ar last (ultima, predefinito). .Ar next e .Ar previous sono intese relativamente alla finestra che detiene il focus. .It Ic stack_enabled Abilita o disabilita la visualizzazione dell'algoritmo di stacking in uso all'interno della barra di stato. .It Ic term_width Imposta la larghezza minima desiderata per i terminali. Se il valore \[`e] maggiore di 0, .Nm cercher\[`a] di regolare la dimensione del font usato dal terminale per fare s\[`i] che la larghezza rimanga superiore ad esso mentre la finestra viene ridimensionata. Solo .Xr xterm 1 \[`e] supportato al momento. L'eseguibile di .Xr xterm 1 non deve avere i bit setuid o setgid abilitati, contrariamente a quanto avviene nella maggior parte dei sistemi. L'utente potrebbe dover creare una copia dell'eseguibile di .Xr xterm 1 , priva dei bit setuid e setgid, e modificare program[term] (vedere la sezione .Sx PROGRAMMI pi\[`u] sotto) in modo che punti a questa copia. .It Ic title_gap Larghezza, in pixel, dello spazio lasciato vuoto tra una finestra e l'altra. L'utilizzo di un valore negativo fa s\[`i] che le finestre si sovrappongano. Se il valore impostato \[`e] l'opposto di .Ic border_width , non verr\[`a] visualizzato alcun bordo tra le finestre. Questa opzione pu\[`o] essere disabilitata usando il valore 0. .It Ic urgent_collapse Disabilita la visualizzazione di un testo sostitutivo per le aree di lavoro che non contengono finestre urgenti. Questa opzione pu\[`o] essere abilitata usando il valore 1. .It Ic urgent_enabled Abilita o disabilita la visualizzazione dell'indicatore di urgenza all'interno della barra di stato. Molti emulatore di terminale devono essere configurati esplicitamente per fare s\[`i] che il carattere "bell" causi la modifica dello stato di urgenza della finestra. Ad esempio, in .Xr xterm 1 , \[`e] necessario aggiungere al file .Pa .Xdefaults la seguente riga: .Bd -literal -offset indent xterm.bellIsUrgent: true .Ed .It Ic verbose_layout Abilita o disabilita la visualizzazione del numero di finestre nell'area principale e del numero di righe (o colonne) nell'area di stacking. Questa opzione pu\[`o] essere disabilitata usando il valore 1. Vedre .Ar master_add , .Ar master_del , .Ar stack_inc e .Ar stack_dec per maggiori informazioni. .It Ic window_class_enabled Abilita o disabilita la visualizzazione del nome della classe (da WM_CLASS) all'interno della barra di stato. Questa opzione pu\[`o] essere abilitata usando il valore 1. .It Ic window_instance_enabled Abilita o disabilita la visualizzazione del nome dell'istanza (da WM_CLASS) all'interno della barra di stato. Questa opzione pu\[`o] essere abilitata usando il valore 1. .It Ic window_name_enabled Abilita o disabilita la visualizzazione del titolo della finestra (da _NET_WM_NAME o WM_NAME) all'interno della barra di stato. Questa opzione pu\[`o] essere abilitata usando il valore 1. .Pp Per impedire che titoli di finestra troppo lunghi impediscano di visualizzare altre informazioni, lo spazio dedicato al titolo \[`e] limitato a 64 caratteri. Vedere .Ic bar_format per maggiori informazioni. .It Ic warp_pointer Posiziona il cursore del mouse al centro della finestra che ha il focus quando vengono utilizzate scorciatoie da tastiera per modificare il focus, cambiare area di lavoro, cambiare regione, etc. Questa opzione pu\[`o] essere abilitata usando il valore 1. .It Ic workspace_limit Imposta il numero di aree di lavoro disponibili. Il valore minimo \[`e] 1, quello massimo \[`e] 22, quello predefinito \[`e] 10. .El .Sh PROGRAMMI .Nm consente di definire azioni personalizzate per l'esecuzione di programmi, e di assegnare queste azioni a scorciatoie da tastiera come \[`e] possibile per quelle predefinite. Vedere la sezione .Sx SCORCIATOIE pi\[`u] sotto. .Pp I programmi vengono definiti come segue: .Pp .Dl program Ns Bo Ar action Bc = Ar progpath Op Ar arg Op Ar arg ... .Pp .Ar action \[`e] qualsiasi identificatore che non vada in conflitto con una delle azioni predefinite, .Ar progpath \[`e] il percorso del programma da eseguire e .Ar arg sono gli argomenti (uno o pi\[`u]) da passare al programma. .Pp Se il percorso o gli argomenti comprendono il carattere .Ql # , questo dovr\[`a] essere preceduto da un carattere di escape, diventando \e#. .Pp I seguenti argomenti verranno sostituiti, al momento dell'esecuzione, con il valore assegnato all'opzione corrispondente: .Pp .Bl -tag -width "$bar_font_color" -offset indent -compact .It Cm $bar_border .It Cm $bar_color .It Cm $bar_font .It Cm $bar_font_color .It Cm $color_focus .It Cm $color_unfocus .It Cm $dmenu_bottom \-b se .Ic bar_at_bottom \[`e] abilitato .It Cm $region_index .It Cm $workspace_index .El .Pp Esempio: .Bd -literal -offset indent program[ff] = /usr/local/bin/firefox http://spectrwm.org/ bind[ff] = MOD+Shift+b # Ora M-S-b esegue Firefox .Ed .Pp Per eliminare la scorciatoia appena definita: .Bd -literal -offset indent bind[] = MOD+Shift+b .Ed .Pp Programmi predefiniti: .Pp .Bl -tag -width "screenshot_wind" -offset indent -compact .It Cm lock xlock .It Cm menu dmenu_run $dmenu_bottom \-fn $bar_font \-nb $bar_color \-nf $bar_font_color \-sb $bar_border \-sf $bar_color .It Cm term xterm .It Cm initscr initscreen.sh # opzionale .It Cm screenshot_all screenshot.sh full # opzionale .It Cm screenshot_wind screenshot.sh window # opzionale .El .Pp I programmi opzionali non verranno verificati a meno di non essere ridefiniti dall'utente. Se uno dei programmi predefiniti fallisce la verifica, \[`e] possibile risolvere l'errore installando il programma corrispondente, modificando il percorso del programma o disabilitando la scorciatoia relativa. .Pp Ad esempio, per ridefinire .Ic lock : .Bd -literal -offset indent program[lock] = xscreensaver\-command \-lock .Ed .Pp Per disabilitare la scorciatoia assegnata a .Ic lock ed impedirne la verifica: .Bd -literal -offset indent bind[] = MOD+Shift+Delete .Ed .Sh SCORCIATOIE .Nm fornisce numerose azioni che possono essere attivate usando combinazioni di tasti sulla tastiera o sul mouse. .Pp Le combinazioni di tasti che coinvolgono il mouse sono le seguenti: .Pp .Bl -tag -width "M-j, M-XXX" -offset indent -compact .It Cm M1 Assegna il focus ad una finestra .It Cm M-M1 Sposta una finestra .It Cm M-M3 Ridimensiona una finestra .It Cm M-S-M3 Ridimensiona una finestra, mantenendola centrata .El .Pp Le scorciatoie da tastiera predefinite sono le seguenti: .Pp .Bl -tag -width "M-j, M-XXXXXX" -offset indent -compact .It Cm M-S- Ns Aq Cm Return term .It Cm M-p menu .It Cm M-S-q quit .It Cm M-q restart .It Cm M- Ns Aq Cm Space cycle_layout .It Cm M-S-\e flip_layout .It Cm M-S- Ns Aq Cm Space stack_reset .It Cm M-h master_shrink .It Cm M-l master_grow .It Cm M-, master_add .It Cm M-. master_del .It Cm M-S-, stack_inc .It Cm M-S-. stack_dec .It Cm M- Ns Aq Cm Return swap_main .It Xo .Cm M-j , .Cm M- Ns Aq Cm TAB .Xc focus_next .It Xo .Cm M-k , .Cm M-S- Ns Aq Cm TAB .Xc focus_prev .It Cm M-m focus_main .It Cm M-u focus_urgent .It Cm M-S-j swap_next .It Cm M-S-k swap_prev .It Cm M-b bar_toggle .It Cm M-S-b bar_toggle_ws .It Cm M-x wind_del .It Cm M-S-x wind_kill .It Cm M- Ns Aq Ar 1-9,0,F1-F12 .Pf ws_ Aq Ar 1-22 .It Cm M-S- Ns Aq Ar 1-9,0,F1-F12 .Pf mvws_ Ns Aq Ar 1-22 .It Cm M- Ns Aq Ar Keypad 1-9 .Pf rg_ Aq Ar 1-9 .It Cm M-S- Ns Aq Ar Keypad 1-9 .Pf mvrg_ Aq Ar 1-9 .It Cm M- Ns Aq Cm Right ws_next .It Cm M- Ns Aq Cm Left ws_prev .It Cm M- Ns Aq Cm Up ws_next_all .It Cm M- Ns Aq Cm Down ws_prev_all .It Cm M-a ws_next_move .It Cm M-S- Ns Aq Cm Left ws_prev_move .It Cm M-S- Ns Aq Cm Up ws_prior .It Cm M-S- Ns Aq Cm Right rg_next .It Cm M-S- Ns Aq Cm Left rg_prev .It Cm M-s screenshot_all .It Cm M-S-s screenshot_wind .It Cm M-S-v version .It Cm M-t float_toggle .It Cm M-S- Ns Aq Cm Delete lock .It Cm M-S-i initscr .It Cm M-w iconify .It Cm M-S-w uniconify .It Cm M-e maximize_toggle .It Cm M-S-r always_raise .It Cm M-v button2 .It Cm M-- width_shrink .It Cm M-= width_grow .It Cm M-S-- height_shrink .It Cm M-S-= height_grow .It Cm M-[ move_left .It Cm M-] move_right .It Cm M-S-[ move_up .It Cm M-S-] move_down .It Cm M-S-/ name_workspace .It Cm M-/ search_workspace .It Cm M-f search_win .El .Pp Le azioni predefinite disponibili sono le seguenti: .Pp .Bl -tag -width "M-j, M-XXXX" -offset indent -compact .It Cm term Esegue un terminale (vedere la sezione .Sx PROGRAMMI pi\[`u] in alto). .It Cm menu Mostra il menu (vedere la sezione .Sx PROGRAMMI pi\[`u] in alto). .It Cm quit Chiude .Nm . .It Cm restart Riavvia .Nm . .It Cm cycle_layout Passa al layout successivo. .It Cm flip_layout Inverte l'area principale e quella di stacking. .It Cm stack_reset Riporta il layout al suo stato iniziale. .It Cm master_shrink Riduce la dimensione dell'area principale. .It Cm master_grow Aumenta la dimensione dell'area principale. .It Cm master_add Aggiunge una finestra all'area principale. .It Cm master_del Rimuove una finestra dall'area principale. .It Cm stack_inc Aggiunge una riga (o colonna) all'area di stacking. .It Cm stack_dec Rimuove una riga (o colonna) dall'area di stacking. .It Cm swap_main Sposta la finestra corrente nell'area principale. .It Cm focus_next Assegna il focus alla finestra successiva. .It Cm focus_prev Assegna il focus alla finestra precedente. .It Cm focus_main Assegna il focus alla finestra principale dell'area di lavoro. .It Cm focus_urgent Assegna il focus alla finestra urgente successiva. Verr\[`a] effettuato, se necessario, il passaggio ad un'altra area di lavoro. .It Cm swap_next Inverte la finestra corrente con quella successiva. .It Cm swap_prev Inverte la finestra corrente con quella precedente. .It Cm bar_toggle Modifica la visibilit\[`a] della barra di stato a livello globale. .It Cm bar_toggle_ws Modifica la visibilit\[`a] della barra di stato nell'area di lavoro corrente. .It Cm wind_del Chiude la finestra corrente. .It Cm wind_kill Distrugge la finestra corrente. .It Cm ws_ Ns Ar n Passa all'area di lavoro .Ar n , dove .Ar n \[`e] un valore compreso tra 1 e .Ic workspace_limit . .It Cm mvws_ Ns Ar n Sposta la finestra corrente nell'area di lavoro .Ar n , dove .Ar n \[`e] un numero compreso tra 1 e .Ic workspace_limit . .It Cm rg_ Ns Ar n Assegna il focus alla regione .Ar n , dove .Ar n \[`e] un numero compreso tra 1 e 9. .It Cm mvrg_ Ns Ar n Sposta la finestra corrente nella regione .Ar n , dove .Ar n \[`e] un numero compreso tra 1 e 9. .It Cm ws_next Passa all'area di lavoro non vuota successiva. .It Cm ws_prev Passa all'area di lavoro non vuota precedente. .It Cm ws_next_all Passa all'area di lavoro successiva. .It Cm ws_prev_all Passa all'area di lavoro precedente. .It Cm ws_next_move Passa all'area di lavoro successiva, spostando allo stesso tempo la finestra corrente. .It Cm ws_prev_move Passa all'area di lavoro precedente, spostando allo stesso tempo la finestra corrente. .It Cm ws_prior Passa all'ultima area di lavoro visitata. .It Cm rg_next Passa alla regione successiva. .It Cm rg_prev Passa alla regione precedente. .It Cm screenshot_all Cattura l'intera schermata chiamando l'apposito script (vedere la sezione .Sx PROGRAMMI pi\[`u] in alto). .It Cm screenshot_wind Cattura una singola finestra chiamando l'apposito script (vedere la sezione .Sx PROGRAMMI pi\[`u] in alto). .It Cm version Modifica la visibilit\[`a] del numero di versione all'interno della barra di stato. .It Cm float_toggle Modifica la finestra che detiene il focus, portandola da floating a tiled e viceversa. .It Cm lock Blocca lo schermo (vedere la sezione .Sx PROGRAMMI pi\[`u] in alto). .It Cm initscr Inizializza nuovamente tutti gli schermi (vedere la sezione .Sx PROGRAMMI pi\[`u] in alto). .It Cm iconify Minimizza la finestra che detiene il focus. .It Cm uniconify Ripristina la finestra selezionata tramite .Xr dmenu 1 . .It Cm maximize_toggle Modifica lo stato di massimizzazione della finestra che detiene il focus. .It Cm always_raise Se impostato, le finestre tiled possono oscurare le finestre floating. .It Cm button2 Simula la pressione del tasto centrale del mouse. .It Cm width_shrink Riduce la larghezza di una finestra floating. .It Cm width_grow Aumenta la larghezza di una finestra floating. .It Cm height_shrink Riduce l'altezza di una finestra floating. .It Cm height_grow Aumenta l'altezza di una finestra floating. .It Cm move_left Sposta una finestra floating verso sinistra di un'unit\[`a]. .It Cm move_right Sposta una finestra floating verso destra di un'unit\[`a]. .It Cm move_up Sposta una finestra floating verso l'alto di un'unit\[`a]. .It Cm move_down Sposta una finestra floating verso il basso di un'unit\[`a]. .It Cm name_workspace Assegna un nome all'area di lavoro corrente. .It Cm search_workspace Cerca un'area di lavoro. .It Cm search_win Cerca una finestra all'interno dell'area di lavoro corrente. .El .Pp Le scorciatoie da tastiera personalizzate vengono definite come segue: .Pp .Dl bind Ns Bo Ar action Bc = Ar keys .Pp .Ar action \[`e] una delle azioni predefinite descritte sopra, oppure la stringa vuota (per disabilitare la scorciatoia). .Ar keys \[`e] composta da uno o pi\[`u] tasti modificatore (eg. MOD, Mod1, Shift, etc.) e uno o pi\[`u] tasti normali (eg. b, Space, etc.) separati da .Ql + . .Pp Esempio: .Bd -literal -offset indent bind[reset] = Mod4+q # Assegna l'azione reset alla scorciatoia Win+q bind[] = Mod1+q # disabilita la scorciatoia predefinita Alt+q .Ed .Pp Per usare il valore dell'opzione .Ic modkey in una scorciatoia, specificare MOD come modificatore. .Pp Pi\[`u] scorciatoie possono essere assegnate alla stessa azione. .Pp Per usare dei caratteri non latini, come \[oa] o \[*p], all'interno di una scorciatoia, \[`e] necessario specificare il nome xkb del carattere anzich\['e] il carattere stesso. Eseguendo .Xr xev 1 e premendo un tasto mentre la finestra del programma detiene il focus, \[`e] possibile leggere il nome xkb corrispondente al tasto premuto. Ad esempio, per \[oa]: .Bd -literal -offset indent KeyPress event, serial 41, synthetic NO, window 0x2600001, root 0x15a, subw 0x0, time 106213808, (11,5), root:(359,823), state 0x0, keycode 24 (keysym 0xe5, aring), same_screen YES, XLookupString gives 2 bytes: (c3 a5) "\[oa]" XmbLookupString gives 2 bytes: (c3 a5) "\[oa]" XFilterEvent returns: False .Ed .Pp Il nome xkb \[`e] aring. Quindi, all'interno di .Pa spectrwm.conf sar\[`a] possibile aggiungere la seguente riga: .Bd -literal -offset indent bind[program] = MOD+aring .Ed .Sh MAPPE DI TASTIERA \[`E] possibile caricare le scorciatoie da un file di configurazione separato tramite l'opzione .Ic keyboard_mapping : questo consente di utilizzare scorciatoie specifiche del proprio layout di tastiera. .Pp Vengono forniti i seguenti file: .Pp .Bl -tag -width "spectrwm_XX.confXXX" -offset indent -compact .It Cm spectrwm_cz.conf Layout per tastiere ceche .It Cm spectrwm_es.conf Layout per tastiere spagnole .It Cm spectrwm_fr.conf Layout per tastiere francesi .It Cm spectrwm_fr_ch.conf Layout per tastiere francesi (Svizzera) .It Cm spectrwm_se.conf Layout per tastiere svedesi .It Cm spectrwm_us.conf Layout per tastiere americane .El .Sh QUIRK \[`E] possibile specificare "quirk" da applicare a quelle finestre (ad esempio applicazioni a schermo intero e finestre di dialogo) che richiedono un comportamento speciale da parte di un gestore di finestre tiling come .Nm . .Pp La configurazione predefinita, per quanto riguarda i quirk, \[`e] la seguente: .Pp .Bl -tag -width "OpenOffice.org N.M:VCLSalFrameXXX" -offset indent \ -compact .It Firefox\-bin:firefox\-bin TRANSSZ .It Firefox:Dialog FLOAT .It Gimp:gimp FLOAT + ANYWHERE .It MPlayer:xv FLOAT + FULLSCREEN + FOCUSPREV .It OpenOffice.org 2.4:VCLSalFrame FLOAT .It OpenOffice.org 3.1:VCLSalFrame FLOAT .It pcb:pcb FLOAT .It xine:Xine Window FLOAT + ANYWHERE .It xine:xine Panel FLOAT + ANYWHERE .It xine:xine Video Fullscreen Window FULLSCREEN + FLOAT .It Xitk:Xitk Combo FLOAT + ANYWHERE .It Xitk:Xine Window FLOAT + ANYWHERE .It XTerm:xterm XTERM_FONTADJ .El .Pp I quirk disponibili sono i seguenti: .Pp .Bl -tag -width "XTERM_FONTADJXXX" -offset indent -compact .It FLOAT Le finestre saranno sempre floating. .It TRANSSZ Modifica la dimensione delle finestre di dialogo in base al valore di .Ic dialog_ratio (vedere la sezione .Sx FILE DI CONFIGURAZIONE pi\[`u] in alto). .It ANYWHERE Consenti alle finestre di decidere la propria posizione. .It XTERM_FONTADJ Regola la dimensione dei font di .Xr xterm 1 quando la dimensione delle finestre viene modificata. .It FULLSCREEN Non mostrare il bordo. .It FOCUSPREV Alla chiusura di una finestra, il focus verr\[`a] assegnato alla finestra che lo deteneva in precedenza e non alla finestra precedente nello stack. .It NOFOCUSONMAP Non assegnare il focus alle finestre quando vengono create. Questo quirk viene ignorato se il valore di .Ic focus_mode \[`e] .Ar follow . .It FOCUSONMAP_SINGLE Assegna il focus alle finestre quando vengono create solo se non sono gi\[`a] presenti delle finestre con la stessa classe e istanza nell'area di lavoro. Questo quirk viene ignorato se il valore di .Ic focus_mode \[`e] .Ar follow . .It OBEYAPPFOCUSREQ Assegna il focus alle finestre quando viene richiesto tramite un messaggio di tipo _NET_ACTIVE_WINDOW con sorgente 1. Se la sorgente \[`e] 0 (non specificato) o 2 (pager), la richiesta viene sempre accolta. .It IGNOREPID Ignora il PID nella scelta dell'area di lavoro iniziale per le nuove finestre. Molto utile per le applicazioni (eg. terminali) che creano pi\[`u] finestre all'interno dello stesso processo. .It IGNORESPAWNWS Ignora l'area di lavoro in cui \[`e] stata eseguita la scorciatoia da tastiera nella scelta dell'area di lavoro iniziale per le nuove finestre. .It WS Ns Bq Ar n Obbliga le nuove finestre ad essere assegnate all'area di lavoro .Ar n . .El .Pp I quirk personalizzati vengono definiti come segue: .Pp .Dl quirk Ns Bo Ar class Ns Bo : Ns Ar instance Ns Bo : Ns Ar name Bc Bc Bc = Ar quirk Op + Ar quirk ... .Pp .Ar class , .Ar instance (opzionale) e .Ar name (opzionale) sono dei pattern che vengono usati per determinare a quali finestre i quirk debbano essere applicati e .Ar quirk \[`e] uno dei quirk descritti in precedenza. .Pp I pattern vengono interpretati come espressioni regolari estese POSIX. I simboli ':', '[' e ']' devono essere preceduti da '\\' per essere considerati letteralmente. Vedere .Xr regex 7 per ulteriori informazioni sulle espressioni regolari estese POSIX. .Pp Ad esempio: .Bd -literal -offset indent quirk[MPlayer] = FLOAT + FULLSCREEN + FOCUSPREV # Le finestre con \ classe 'MPlayer' sono floating quirk[.*] = FLOAT # Le finestre sono floating quirk[.*:.*:.*] = FLOAT # Come sopra quirk[Firefox:Navigator] = FLOAT # Le finestre di navigazione di \ Firefox sono floating quirk[::Console] = FLOAT # Le finestre la cui classe non \[`e] impostata \ e il cui nome \[`e] 'Console' sono floating quirk[\\[0-9\\].*:.*:\\[\\[\\:alnum\\:\\]\\]*] = FLOAT # Le finestre la \ cui classe inizia con un numero e il cui nome \[`e] non definito o \ contiene solo caratteri alfanumerici, senza spazi, sono floating quirk[pcb:pcb] = NONE # Rimuove i quirk predefiniti .Ed \[`E] possibile ottenere .Ar class , .Ar instance e .Ar name eseguendo .Xr xprop 1 e selezionando la finestra desiderata. In questo esempio, \[`e] stata selezionata la finestra principale di Firefox: .Bd -literal -offset indent $ xprop | grep \-E "^(WM_CLASS|_NET_WM_NAME|WM_NAME)" WM_CLASS(STRING) = "Navigator", "Firefox" WM_NAME(STRING) = "spectrwm - ConformalOpenSource" _NET_WM_NAME(UTF8_STRING) = "spectrwm - ConformalOpenSource" .Ed .Pp Il comando .Xr xprop 1 visualizza WM_CLASS nel seguente formato: .Bd -literal -offset indent WM_CLASS(STRING) = "", "" .Ed .Pp In questo caso, bisognerebbe aggiungere al file di configurazione la seguente riga: .Bd -literal -offset indent quirk[Firefox:Navigator] = FLOAT .Ed .Pp .Nm assegna alcuni quirk in automatico, basandosi sul valore della propriet\[`a] _NET_WM_WINDOW_TYPE, nel seguente modo: .Pp .Bl -tag -width "_NET_WM_WINDOW_TYPE_TOOLBARXXX" -offset indent -compact .It _NET_WM_WINDOW_TYPE_DOCK FLOAT + ANYWHERE .It _NET_WM_WINDOW_TYPE_TOOLBAR FLOAT + ANYWHERE .It _NET_WM_WINDOW_TYPE_UTILITY FLOAT + ANYWHERE .It _NET_WM_WINDOW_TYPE_SPLASH FLOAT .It _NET_WM_WINDOW_TYPE_DIALOG FLOAT .El .Pp In tutti gli altri casi gli unici quirk assegnati alle finestre saranno quelli predefiniti o, con precedenza maggiore, quelli specificati nel file di configurazione. .Sh EWMH .Nm implementa in maniera parziale la specifica EWMH, Extended Window Manager Hints: questo consente di controllare le finestre, oltre che .Nm stesso, da script e programmi esterni. Per sfruttare questo supporto, .Nm dovr\[`a] ricevere degli eventi di tipo ClientMessage; questo tipo di messaggio pu\[`o] essere inviato dalla riga di comando usando tool come .Xr wmctrl 1 e .Xr xdotool 1 . Il formato dei messaggi \[`e] definito nella specifica EWMH. .Pp L'identificativo della finestra che detiene il focus pu\[`o] essere ottenuto leggendo il valore della propriet\[`a] _NET_ACTIVE_WINDOWS della root window. Ad esempio, per visualizzare il titolo della finestra: .Bd -literal -offset indent $ WINDOWID=`xprop \-root _NET_ACTIVE_WINDOW | grep \-o "0x.*"` $ xprop \-id $WINDOWID _NET_WM_NAME | grep \-o "\\".*\\"" .Ed .Pp Il focus pu\[`o] essere assegnato ad una finestra inviando un messaggio di tipo _NET_ACTIVE_WINDOW alla root window, ad esempio: .Bd -literal -offset indent $ wmctrl \-i \-a 0x4a0000b .Ed .Pp Le finestre possono essere chiuse inviando un messaggio di tipo _NET_CLOSE_WINDOW, ad esempio: .Bd -literal -offset indent $ wmctrl \-i \-c 0x4a0000b .Ed .Pp Le finestre possono essere portate da floating a tiled, e viceversa, aggiungendo o rimuovendo l'atomo _NET_WM_STATE_ABOVE dalla propriet\[`a] _NET_WM_STATE della finestra. Per fare ci\[`o], \[`e] necessario inviare un messaggio di tipo _NET_WM_STATE, ad esempio: .Bd -literal -offset indent $ wmctrl \-i \-r 0x4a0000b \-b toggle,_NET_WM_STATE_ABOVE .Ed .Pp Le finestre possono essere minimizzate e ripristinate sostituendo _NET_WM_STATE_HIDDEN a _NET_WM_STATE_ABOVE nell'esempio precedente: .Bd -literal -offset indent $ wmctrl \-i \-r 0x4a0000b \-b toggle,_NET_WM_STATE_HIDDEN .Ed .Pp Le finestre floating possono essere ridimensionate e spostate tramite l'invio di un messaggio di tipo _NET_MOVERESIZE_WINDOW, ad esempio: .Bd -literal -offset indent $ wmctrl \-i \-r 0x4a0000b \-e 0,100,50,640,480 .Ed In questo caso, la con id 0x4a0000b finestra viene spostata in (100,50) e la sua dimensione diventa 640x480. .Pp I messaggi di tipo _NET_MOVERESIZE_WINDOW che fanno riferimento a finestre tiled verranno ignorati. .Sh SEGNALI Il segnale HUP fa riavviare .Nm . .Sh FILE .Bl -tag -width "/etc/spectrwm.confXXX" -compact .It Pa ~/.spectrwm.conf File di configurazione specifico dell'utente. .Nm . .It Pa /etc/spectrwm.conf File di configurazione globale. .El .Sh ORIGINE .Nm \[`e] ispirato a xmonad & dwm. .Sh AUTORI .An -nosplit .Nm \[`e] stato scritto da: .Pp .Bl -tag -width "Ryan Thomas McBride Aq mcbride@countersiege.com " -offset \ indent -compact .It Cm Marco Peereboom Aq marco@peereboom.us .It Cm Ryan Thomas McBride Aq mcbride@countersiege.com .It Cm Darrin Chandler Aq dwchandler@stilyagin.com .It Cm Pierre-Yves Ritschard Aq pyr@spootnik.org .It Cm Tuukka Kataja Aq stuge@xor.fi .It Cm Jason L. Wright Aq jason@thought.net .It Cm Reginald Kennedy Aq rk@rejii.com .It Cm Lawrence Teo Aq lteo@lteo.net .It Cm Tiago Cunha Aq tcunha@gmx.com .It Cm David Hill Aq dhill@mindcry.org .El spectrwm-SPECTRWM_3_1_0/outdated_man_pages/spectrwm_pt.1000066400000000000000000000360631316475426700233500ustar00rootroot00000000000000.\" Copyright (c) 2009 Marco Peereboom .\" Copyright (c) 2009 Darrin Chandler .\" .\" 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 SPECTRWM 1 .Os .Sh NOME .Nm spectrwm .Nd gerenciador de janela para o X11 .Sh SINOPSE .Nm spectrwm .Sh DESCRI\(,C\(~AO .Nm \('e um gerenciador de janela minimalista que tenta n\(~ao atrapalhar a valorosa forma real da tela para que essa possa ser usada para coisas muito mais importantes. Tem sensatos defaults e n\(~ao requer que algu\('em aprenda uma linguagem de programa\(,c\(~ao para fazer qualquer configura\(,c\(~ao. Ele foi escrito por hackers para hackers e esfor\(,ca-se em ser pequeno, compacto e r\('apido. .Pp Quando o .Nm inicia, ele l\(^e as configura\(,c\(~oes do seu arquivo de configura\(,c\(~ao, .Pa spectrwm.conf . Veja a se\(,c\(~ao .Sx ARQUIVOS DE CONFIGURA\(,C\(~AO logo abaixo. .Pp A seguinte nota\(,c\(~ao \('e usada por toda essa p\('agina: .Pp .Bl -tag -width Ds -offset indent -compact .It Cm M Meta .It Cm S Shift .It Aq Cm Name Nome da tecla .It Cm M1 Bot\(~ao 1 do mouse .It Cm M3 Bot\(~ao 3 do mouse .El .Pp .Nm \('e muito simples de usar. Muitas das a\(,c\(~oes s\(~ao iniciadas por atalhos do mouse ou do teclado. Veja a se\(,c\(~ao .Sx ATALHOS logo abaixo para os defaults e as personaliza\(,c\(~oes. .Sh ARQUIVOS DE CONFIGURA\(,C\(~AO .Nm primeiro tenta abrir o arquivo de configura\(,c\(~ao no diret\('orio do usu\('ario, .Pa ~/.spectrwm.conf . Se o arquivo n\(~ao estiver dispon\('ivel, ent\(~ao tenta abrir o arquivo de configura\(,c\(~ao global .Pa /etc/spectrwm.conf . .Pp Assim \('e o formato do arquivo:\*(Ltpalavra-chave\*(Gt = \*(Ltconfigura\(,c\(~ao\*(Gt. Por exemplo: .Pp .Dl color_focus = red .Pp Para habilitar ou desabilitar uma op\(,c\(~ao usa-se o 1 ou 0, respectivamente. .Pp O arquivo suporta as seguintes palavras-chave: .Pp .Bl -tag -width "title_class_enabledXXX" -offset indent -compact .It Cm color_focus Cor da borda da janela atualmente focada. .It Cm color_unfocus Cor da borda das janelas fora de foco. .It Cm bar_enabled Habilita ou desabilita a barra de status. .It Cm bar_border Ns Bq Ar x Cor da borda da barra de status na tela .Ar x . .It Cm bar_color Ns Bq Ar x Cor da janela da barra de status na tela .Ar x . .It Cm bar_font_color Ns Bq Ar x Cor da fonte na barra de status na tela .Ar x . .It Cm bar_font Fonte da barra de status. .It Cm bar_action Script externo que preenche a barra de status com informa\(,c\(~oes adicionais, como tempo de vida da bateria. .It Cm bar_at_bottom Coloca a barra de status na parte inferior de cada regi\(~ao, ao inv\('es da parte superior. .It Cm stack_enabled Habilita ou desabilita mostrar o atual algor\('itmo de empilhamento na barra de status. .It Cm clock_enabled Habilita ou desabilita mostrar o rel\('ogio na barra de status. Desabilite configurando para 0, ent\(~ao um rel\('ogio personalizado pode ser usado no script bar_action. .It Cm dialog_ratio Algumas aplica\(,c\(~oes tem janelas de di\('alogo que s\(~ao muito pequenas para serem \('uteis. Essa taxa \('e o tamanho da tela para o qual elas ser\(~ao redimencionadas. Por exemplo, 0.6 equivale a 60% do tamanho da tela f\('isica. .It Cm region Aloca uma regi\(~ao personalizada, removendo qualquer regi\(~ao automaticamente detectada que ocupe o mesmo espa\(,co na tela. Definido no formato screen[]:WIDTHxHEIGHT+X+Y, e.g.\& screen[1]:800x1200+0+0. .It Cm term_width Configura a largura m\('inima preferida para o terminal Se esse valor for maior do que 0, .Nm vai tentar ajustar os tamanhos da fonte no terminal para manter a largura do terminal acima desse n\('umero enquanto a janela \('e redimencionada. Apenas o .Xr xterm 1 \('e suportado atualmente. O bin\('ario do .Xr xterm 1 n\(~ao deve ser setuid ou setgid, que \('e o default em muitos sistemas. Os usu\('arios podem precisar de configurar program[term] (veja a se\(,c\(~ao .Sx PROGRAMAS ) para usar uma c\('opia alternativa do bin\('ario do .Xr xterm 1 sem o bit setgid ativado. .It Cm title_class_enabled Habilita ou desabilita mostrar a classe da janela na barra de status. Habilite configurando para 1. .It Cm title_name_enabled Habilita ou desabilita mostrar o t\('itulo da janela na barra de status. Habilite configurando para 1. .It Cm window_name_enabled Habilita ou desabilita mostrar a nome da janela na barra de status. Habilite configurando para 1. .It Cm modkey Muda a tecla de modifica\(,c\(~ao. Mod1 \('e geralmente a tecla ALT e Mod4 \('e a tecla windows em um PC. .It Cm focus_mode Usar um valor de follow_cursor vai fazer o gerenciador de janela focar a janela sob o mouse quando trocando \('areas de trabalho e criando janelas. .It Cm disable_border Remove a borda quando a barra estiver desabilitada e houver apenas uma janela na tela. .It Cm program Ns Bq Ar p Define uma nova a\(,c\(~ao para executar um programa .Ar p . Veja a se\(,c\(~ao .Sx PROGRAMAS logo abaixo. .It Cm bind Ns Bq Ar x Cria uma combina\(,c\(~ao de teclas de atalho para a a\(,c\(~ao .Ar x . Veja a se\(,c\(~ao .Sx ATALHOS logo abaixo. .It Cm quirk Ns Bq Ar c:n Adicione "quirk" para janelas com classe .Ar c e nome .Ar n . Veja a se\(,c\(~ao .Sx QUIRKS logo abaixo. .El .Pp Cores precisam ser especificadas pela especifica\(,c\(~ao .Xr XQueryColor 3 e fontes pela especifica\(,c\(~ao .Xr XQueryFont 3 . .Pp Para listar as fontes dispon\('iveis em seu sistema veja o manual do .Xr fc-list 1 ou do .Xr xlsfonts 1 . A aplica\(,c\(~ao .Xr xfontsel 1 pode te ajudar a mostrar a X Logical Font Description ("XLFD") usada na configura\(,c\(~ao da palavra-chave bar_font. .Sh PROGRAMAS .Nm te permite definir a\(,c\(~oes personalizadas para executar programas de sua escolha e ent\(~ao criar um atalho para elas da mesma forma que as a\(,c\(~oes embutidas. Veja a se\(,c\(~ao .Sx ATALHOS logo abaixo. .Pp Os programas default s\(~ao descritos abaixo: .Pp .Bl -tag -width "screenshot_wind" -offset indent -compact .It Cm term xterm .It Cm screenshot_all screenshot.sh full .It Cm screenshot_wind screenshot.sh window .It Cm lock xlock .It Cm initscr initscreen.sh .It Cm menu dmenu_run \-fn $bar_font \-nb $bar_color \-nf $bar_font_color \-sb $bar_border \-sf $bar_color .El .Pp Programas personalizados no arquivo de configura\(,c\(~ao s\(~ao especificados da seguinte maneira: .Pp .Dl program[] = [ [... ]] .Pp .Aq name \('e um identificador qualquer que n\(~ao conflite com uma a\(,c\(~ao ou palavra-chave embutida, .Aq progpath \('e o programa desejado, e .Aq arg \('e zero ou mais argumentos para o programa. .Pp As seguintes vari\('aveis representam valores configur\('aveis no .Nm (veja a se\(,c\(~ao .Sx ARQUIVOS DE CONFIGURA\(,C\(~AO logo acima), e podem ser usadas nos campos .Aq arg e ser\(~ao substitu\('idas pelos valores na hora em que o programa for executado: .Pp .Bl -tag -width "$bar_font_color" -offset indent -compact .It Cm $bar_border .It Cm $bar_color .It Cm $bar_font .It Cm $bar_font_color .It Cm $color_focus .It Cm $color_unfocus .El .Pp Exemplo: .Bd -literal -offset indent program[ff] = /usr/local/bin/firefox http://spectrwm.org/ bind[ff] = Mod+f # Agora Mod+F executa o firefox .Ed .Pp Para desfazer a configura\(,c\(~ao anterior: .Bd -literal -offset indent bind[] = Mod+f program[ff] = .Ed .Pp .Sh ATALHOS .Nm prov\(^e muitas fun\(,c\(~oes (ou a\(,cões) acessadas pelos atalhos do teclado ou do mouse. .Pp Os atuais atalhos do mouse s\(~ao descritos abaixo: .Pp .Bl -tag -width "M-j, M-XXX" -offset indent -compact .It Cm M1 Foca a janela .It Cm M-M1 Move a janela .It Cm M-M3 Redimenciona a janela .It Cm M-S-M3 Redimenciona a janela enquanto a mant\('em centralizada .El .Pp Os atalhos default do teclado s\(~ao descritos abaixo: .Pp .Bl -tag -width "M-j, M-XXX" -offset indent -compact .It Cm M-S- Ns Aq Cm Return term .It Cm M-p menu .It Cm M-S-q quit .It Cm M-q restart .Nm .It Cm M- Ns Aq Cm Space cycle_layout .It Cm M-S- Ns Aq Cm Space reset_layout .It Cm M-h master_shrink .It Cm M-l master_grow .It Cm M-, master_add .It Cm M-. master_del .It Cm M-S-, stack_inc .It Cm M-S-. stack_del .It Cm M- Ns Aq Cm Return swap_main .It Xo .Cm M-j , .Cm M- Ns Aq Cm TAB .Xc focus_next .It Xo .Cm M-k , .Cm M-S- Ns Aq Cm TAB .Xc focus_prev .It Cm M-m focus_main .It Cm M-S-j swap_next .It Cm M-S-k swap_prev .It Cm M-b bar_toggle .It Cm M-x wind_del .It Cm M-S-x wind_kill .It Cm M- Ns Aq Ar n .Ns ws_ Ns Ar n .It Cm M-S- Ns Aq Ar n .Ns mvws_ Ns Ar n .It Cm M- Ns Aq Cm Right ws_next .It Cm M- Ns Aq Cm Left ws_prev .It Cm M-a ws_prior .It Cm M-S- Ns Aq Cm Right screen_next .It Cm M-S- Ns Aq Cm Left screen_prev .It Cm M-s screenshot_all .It Cm M-S-s screenshot_wind .It Cm M-S-v version .It Cm M-t float_toggle .It Cm M-S Aq Cm Delete lock .It Cm M-S-i initscr .El .Pp Os nomes das a\(,c\(~oes e suas descri\(,cões est\(~ao listados abaixo: .Pp .Bl -tag -width "M-j, M-XXX" -offset indent -compact .It Cm term Executa um novo terminal (veja a se\(,c\(~ao .Sx PROGRAMAS logo acima) .It Cm menu Menu (veja a se\(,c\(~ao .Sx PROGRAMAS logo acima) .It Cm quit Sair .Nm .It Cm restart Reiniciar .Nm .It Cm cycle_layout Circula entre os poss\('iveis layouts .It Cm reset_layout Reinicia o layout .It Cm master_shrink Encolhe a \('area mestre .It Cm master_grow Aumenta a \('area mestre .It Cm master_add Adiciona janelas na \('area mestre .It Cm master_del Remove janelas da \('area mestre .It Cm stack_inc Adiciona colunas/linhas para a \('area de empilhamento .It Cm stack_del Remove colunas/linhas da \('area de empilhamento .It Cm swap_main Move a janela atual para a \('area mestre .It Cm focus_next Foca a pr\('oxima janela da \('area de trabalho .It Cm focus_prev Foca a janela anterior da \('area de trabalho .It Cm focus_main Foca a janela principal da \('area de trabalho .It Cm swap_next Troca com a pr\('oxima janela da \('area de trabalho .It Cm swap_prev Troca com a janela anterior da \('area de trabalho .It Cm bar_toggle Ativa/desativa a barra de status em todas as \('areas de trabalho .It Cm wind_del Apaga a janela atual da \('area de trabalho .It Cm wind_kill Destr\('oi a janela atual da \('area de trabalho .It Cm ws_ Ns Ar n Troca para a \('area de trabalho .Ar n , onde .Ar n vai de 1 at\('e 10 .It Cm mvws_ Ns Ar n Move a janela atual para a \('area de trabalho .Ar n , onde .Ar n vai de 1 at\('e 10 .It Cm ws_next Troca para a pr\('oxima \('area de trabalho que possua uma janela .It Cm ws_prev Troca para a \('area de trabalho anterior que possua uma janela .It Cm ws_prior Troca para a \('ultima \('area de trabalho visitada .It Cm screen_next Move o ponteiro para a pr\('oxima regi\(~ao .It Cm screen_prev Move o ponteiro para a regi\(~ao anterior .It Cm screenshot_all Tira screenshot da tela inteira (se habilitado) (veja a se\(,c\(~ao .Sx PROGRAMAS logo acima) .It Cm screenshot_wind Tira screenshot da janela selecionada (se habilitado) (veja a se\(,c\(~ao .Sx PROGRAMAS logo acima) .It Cm version Ativa/desativa a vers\(~ao na barras de status .It Cm float_toggle Troca o estado da janela focada entre flutuante e tiled .It Cm lock Trava a tela (veja a se\(,c\(~ao .Sx PROGRAMAS logo acima) .It Cm initscr Reinicializa as telas f\('isicas (veja a se\(,c\(~ao .Sx PROGRAMAS logo acima) .El .Pp Atalhos personalizados no arquivo de configura\(,c\(~ao s\(~ao especificados da seguinte maneira: .Pp .Dl bind[] = .Pp .Aq action \('e uma das a\(,c\(~oes listadas acima (ou vazio) e .Aq keys est\('a na forma de zero ou mais teclas de modifica\(,c\(~ao (MOD, Mod1, Shift, etc.) e uma ou mais teclas normais (b, space, etc.), separadas pelo "+". Por exemplo: .Bd -literal -offset indent bind[reset] = Mod4+q # combina a tecla Windows + q para reiniciar bind[] = Mod1+q # desfaz a combina\(,c\(~ao Alt + q .Ed .Pp M\('ultiplas combina\(,c\(~oes de teclas podem ser usadas para a mesma a\(,c\(~ao. .Sh QUIRKS .Nm prov\(^e "quirks" que manipulam janelas que devem ser tratadas especialmente em um gerenciador de janela "tiling", tal como algumas aplica\(,c\(~oes de di\('alogos e tela cheia. .Pp Os quirks default est\(~ao descritos abaixo: .Pp .Bl -tag -width "OpenOffice.org N.M:VCLSalFrameXXX" -offset indent -compact .It Firefox\-bin:firefox\-bin TRANSSZ .It Firefox:Dialog FLOAT .It Gimp:gimp FLOAT + ANYWHERE .It MPlayer:xv FLOAT + FULLSCREEN .It OpenOffice.org 2.4:VCLSalFrame FLOAT .It OpenOffice.org 3.1:VCLSalFrame FLOAT .It pcb:pcb FLOAT .It xine:Xine Window FLOAT + ANYWHERE .It xine:xine Panel FLOAT + ANYWHERE .It xine:xine Video Fullscreen Window FULLSCREEN + FLOAT .It Xitk:Xitk Combo FLOAT + ANYWHERE .It Xitk:Xine Window FLOAT + ANYWHERE .It XTerm:xterm XTERM_FONTADJ .El .Pp Os quirks em si est\(~ao descritos abaixo: .Pp .Bl -tag -width "XTERM_FONTADJXXX" -offset indent -compact .It FLOAT Esta janela n\(~ao deve ser "tiled", mas permitida a flutuar livremente. .It TRANSSZ Ajusta o tamanho das janelas transit\('orias que sejam muito pequenas usando dialog_ratio (veja a se\(,c\(~ao .Sx ARQUIVOS DE CONFIGURA\(,C\(~AO ) . .It ANYWHERE Permite que a janela posicione a si mesma, n\(~ao-centrada. .It XTERM_FONTADJ Ajusta as fontes do xterm quando redimencionando. .It FULLSCREEN Remove a borda para permitir a janela usar todo o tamanho da tela. .El .Pp Quirks personalizados no arquivo de configura\(,c\(~ao s\(~ao especificados da seguinte maneira: .Pp .Dl quirk[:] = [ + ... ] .Pp .Aq class e .Aq name especificam a janela ao qual o quirk se aplica, e .Aq quirk \('e um dos quirks da lista acima. Por exemplo: .Bd -literal -offset indent quirk[MPlayer:xv] = FLOAT + FULLSCREEN # faz o mplayer tocar livremente quirk[pcb:pcb] = NONE # remove quirk existente .Ed .Pp Voc\(^e pode obter .Aq class e .Aq name executando o xprop(1) e ent\(~ao clicando na janela desejada. No seguinte exemplo a jenela principal do Firefox foi clicada: .Bd -literal -offset indent $ xprop | grep WM_CLASS WM_CLASS(STRING) = "Navigator", "Firefox" .Ed .Pp Note que usando o grep(1) para WM_CLASS voc\(^e obt\('em class e name. No exemplo acima a configura\(,c\(~ao do quirk poderia ser: .Bd -literal -offset indent quirk[Firefox:Navigator] = FLOAT .Ed .Sh SINAIS Enviar ao .Nm um sinal HUP far\('a com que o mesmo seja reiniciado. .Sh ARQUIVOS .Bl -tag -width "/etc/spectrwm.confXXX" -compact .It Pa ~/.spectrwm.conf Configura\(,c\(~oes espec\('ificas do usu\('ario. .It Pa /etc/spectrwm.conf Configura\(,c\(~oes globais. .El .Sh HIST\('ORIA .Nm foi inspirado pelo xmonad & dwm. .Sh AUTORES .An -nosplit .Pp .Nm foi escrito por .An Marco Peereboom Aq marco@peereboom.us , .An Ryan Thomas McBride Aq mcbride@countersiege.com e .An Darrin Chandler Aq dwchandler@stilyagin.com . .Sh BUGS Atualmente o menu, invocado com .Cm M-p , depende do dmenu. spectrwm-SPECTRWM_3_1_0/outdated_man_pages/spectrwm_ru.1000066400000000000000000000430771316475426700233560ustar00rootroot00000000000000.\" Copyright (c) 2009 Marco Peereboom .\" Copyright (c) 2009 Darrin Chandler .\" .\" 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 SPECTRWM 1 .Os .Sh ÐÐЗВÐÐИЕ .Nm spectrwm .Nd Оконный менеджер Ð´Ð»Ñ X11 .Sh ИСПОЛЬЗОВÐÐИЕ .Nm spectrwm .Sh ОПИСÐÐИЕ .Nm Ñто минималиÑтичный менеджер окон, ÑтавÑщий Ñвоей целью не мешать вам и не занимать ценное проÑтранÑтво Ñкрана. Его наÑтройки по-умолчанию разумны и, кроме того, он не требует Ð·Ð½Ð°Ð½Ð¸Ñ Ñзыков Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¼Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¾Ð½Ð½Ñ‹Ð¼ файлом. Он напиÑан хакерами Ð´Ð»Ñ Ñ…Ð°ÐºÐµÑ€Ð¾Ð² и ÑтараетÑÑ Ð±Ñ‹Ñ‚ÑŒ легким, компактным и быÑтрым. .Pp Когда .Nm запуÑкаетÑÑ, он читает наÑтройки из Ñвоего конфигурационного файла, .Pa spectrwm.conf . Смотрите Ñекцию .Sx КОÐФИГУРÐЦИОÐÐЫЕ ФÐЙЛЫ ниже. .Pp Ðа Ñтой Ñтранице иÑпользуютÑÑ Ñледующие обозначениÑ: .Pp .Bl -tag -width Ds -offset indent -compact .It Cm M Мета-клавиша .It Cm S Shift .It Aq Cm Name Ð˜Ð¼Ñ ÐºÐ»Ð°Ð²Ð¸ÑˆÐ¸ .It Cm M1 Кнопка мыши 1 .It Cm M3 Кнопка мыши 3 .El .Pp .Nm должен быть понÑтным и очевидным. БольшинÑтво дейÑтвий выполнÑетÑÑ ÐºÐ¾Ð¼Ð±Ð¸Ð½Ð°Ñ†Ð¸Ñми клавиш. Смотрите Ñекцию .Sx ПРИВЯЗКИ ниже, чтобы узнать о Ñтандартных наÑтройках. .Sh КОÐФИГУРÐЦИОÐÐЫЕ ФÐЙЛЫ .Nm пытаетÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚ÑŒ файл в домашнем каталоге, .Pa ~/.spectrwm.conf . Ð’ Ñлучае, еÑли он недоÑтупен, проиÑходит обращение к глобальному файлу наÑтроек, .Pa /etc/spectrwm.conf . .Pp Формат файла Ñледующий: \*(Ltключ\*(Gt = \*(Ltзначение\*(Gt. Ðапример: .Pp .Dl color_focus = red .Pp Однозначное включение и выключение задаетÑÑ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñми 1 и 0. .Pp ПоддерживаютÑÑ Ñледующие ключевые Ñлова: .Pp .Bl -tag -width "title_class_enabledXXX" -offset indent -compact .It Cm color_focus Цвет рамки окна в фокуÑе. .It Cm color_unfocus Цвет рамки окон не в фокуÑе. .It Cm bar_enabled Включение ÑтатуÑной Ñтроки. .It Cm bar_border Ns Bq Ar x Цвет рамки ÑтатуÑной Ñтроки .Ar x . .It Cm bar_color Ns Bq Ar x Цвет ÑтатуÑной Ñтроки .Ar x . .It Cm bar_font_color Ns Bq Ar x Цвет шрифта ÑтатуÑной Ñтроки .Ar x . .It Cm bar_font Тип шрифта ÑтатуÑной Ñтроки. .It Cm bar_action Внешний файл Ñкрипта Ð´Ð»Ñ ÑтатуÑной Ñтроки, выводÑщий туда информацию, например, уровень зарÑда батарей. .It Cm stack_enabled Включить отображение ÑпоÑоба укладки окон в ÑтатуÑной Ñтроке. .It Cm clock_enabled Включить чаÑÑ‹ в ÑтатуÑной Ñтроке. Можно отключить, уÑтановив 0, и Ð’Ñ‹ Ñможете иÑпользовать ÑобÑтвенные чаÑÑ‹ из внешнего Ñкрипта. .It Cm dialog_ratio РÑд приложений имеет Ñлишком маленькие диалоговые окна. Это значение - Ð´Ð¾Ð»Ñ Ñ€Ð°Ð·Ð¼ÐµÑ€Ð° Ñкрана, к которой они будут приведены. Ðапример, значение 0.6 будет ÑоответÑтвовать 60% от реального размера Ñкрана. .It Cm region ВыделÑет облаÑть Ñкрана на Ваше уÑмотрение, уничтожает вÑе перекрытые облаÑти Ñкрана, определенные автоматичеÑки. Формат: screen[]:WIDTHxHEIGHT+X+Y, например\& screen[1]:1280x800+0+0. .It Cm term_width УÑтановить минимальную допуÑтимую ширину ÑмулÑтора терминала. ЕÑли Ñто значение больше 0, .Nm попытаетÑÑ Ð¾Ñ‚Ð¼Ð°Ñштабировать шрифты в терминале, чтобы ширина была больше Ñтого Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ . ПоодерживаетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ .Xr xterm 1 . Также .Xr xterm 1 не может быть Ñ setuid или setgid, Ñ…Ð¾Ñ‚Ñ Ñто так на многих ÑиÑтемах. Возможно необходимо задать program[term] (Смотрите Ñекцию .Sx ПРОГРÐММЫ ) чтобы иÑпользовалаÑÑŒ Ð´Ñ€ÑƒÐ³Ð°Ñ ÐºÐ¾Ð¿Ð¸Ñ .Xr xterm 1 без заданного бита setgid. .It Cm title_class_enabled Отображать клаÑÑ Ð¾ÐºÐ½Ð° в ÑтатуÑной Ñтроке. Обычно выключено .It Cm title_name_enabled Отображать заголовок окна в ÑтатуÑной Ñтроке. Обычно выключено .It Cm modkey Ðазначить Мета-клавишу, клавишу-модификатор. Mod1 ÑоответÑтвует клавише ALT, а Mod4 ÑоответÑтвует клавише WIN на PC. .It Cm program Ns Bq Ar p Добавить пользовательÑкую программу Ð´Ð»Ñ Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸Ð²Ñзки .Ar p . Смотрите Ñекцию .Sx ПРОГРÐММЫ ниже. .It Cm bind Ns Bq Ar x Ðазначить привÑзку на дейÑтвие .Ar x . Смотрите Ñекцию .Sx ПРИВЯЗКИ ниже. .It Cm quirk Ns Bq Ar c:n Добавить коÑтыль Ð´Ð»Ñ Ð¾ÐºÐ¾Ð½ Ñ ÐºÐ»Ð°ÑÑом .Ar c и именем .Ar n . Смотрите Ñекцию .Sx КОСТЫЛИ ниже. .El .Pp Цвета задаютÑÑ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ .Xr XQueryColor 3 Ршрифты задаютÑÑ Ñ Ð¸Ñпользованием .Xr XQueryFont 3 . .Sh ПРОГРÐММЫ .Nm позволÑет Вам добавлÑть Ваши ÑобÑтвенные дейÑÑ‚Ð²Ð¸Ñ Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑка программ и делать к ним привÑзки как ко вÑем оÑтальным дейÑтвиÑм Смотрите Ñекцию .Sx ПРИВЯЗКИ ниже. .Pp Стандартные программы: .Pp .Bl -tag -width "screenshot_wind" -offset indent -compact .It Cm term xterm .It Cm screenshot_all screenshot.sh full .It Cm screenshot_wind screenshot.sh window .It Cm lock xlock .It Cm initscr initscreen.sh .It Cm menu dmenu_run \-fn $bar_font \-nb $bar_color \-nf $bar_font_color \-sb $bar_border \-sf $bar_color .El .Pp Ваши ÑобÑтвенные программы задаютÑÑ Ñледующим образом: .Pp .Dl program[] = [ [... ]] .Pp .Aq name Ñто любой идентификатор, не мешающийÑÑ Ñ ÑƒÐ¶Ðµ ÑущеÑтвующими, .Aq progpath Ñто ÑобÑтвенно путь к программе, .Aq arg Ñто ÑпиÑок передаваемых аргументов или оÑтавьте пуÑтым. .Pp Следующие переменные можно получать из .Nm (Смотрите Ñекцию .Sx КОÐФИГУРÐЦИОÐÐЫЕ ФÐЙЛЫ выше), и их можно иÑпользовать как .Aq arg (в момент запуÑка программы будет выполнена подÑтановка значений): .Pp .Bl -tag -width "$bar_font_color" -offset indent -compact .It Cm $bar_border .It Cm $bar_color .It Cm $bar_font .It Cm $bar_font_color .It Cm $color_focus .It Cm $color_unfocus .El .Pp Ðапример: .Bd -literal -offset indent program[ff] = /usr/local/bin/firefox http://spectrwm.org/ bind[ff] = Mod+f # Значит Mod+F запуÑкает firefox .Ed .Pp Чтобы отменить назначение: .Bd -literal -offset indent bind[] = Mod+f program[ff] = .Ed .Pp .Sh ПРИВЯЗКИ .Nm предоÑтавлÑет доÑтуп к дейÑтвиÑм Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ клавиатурных комбинаций. .Pp УÑтановленные привÑзки Ð´Ð»Ñ Ð¼Ñ‹ÑˆÐ¸: .Pp .Bl -tag -width "M-j, M-XXX" -offset indent -compact .It Cm M1 СфокуÑироватьÑÑ Ð½Ð° окне .It Cm M-M1 ПеремеÑтить окно .It Cm M-M3 Изменить размер окна .It Cm M-S-M3 Изменить размер окна, ÑƒÐ´ÐµÑ€Ð¶Ð¸Ð²Ð°Ñ ÐµÐ³Ð¾ в центре .El .Pp Стандартные клавиатурные привÑзки: .Pp .Bl -tag -width "M-j, M-XXX" -offset indent -compact .It Cm M-S- Ns Aq Cm Return term .It Cm M-p menu .It Cm M-S-q quit .It Cm M-q restart .Nm .It Cm M- Ns Aq Cm Space cycle_layout .It Cm M-S- Ns Aq Cm Space reset_layout .It Cm M-h master_shrink .It Cm M-l master_grow .It Cm M-, master_add .It Cm M-. master_del .It Cm M-S-, stack_inc .It Cm M-S-. stack_del .It Cm M- Ns Aq Cm Return swap_main .It Xo .Cm M-j , .Cm M- Ns Aq Cm TAB .Xc focus_next .It Xo .Cm M-k , .Cm M-S- Ns Aq Cm TAB .Xc focus_prev .It Cm M-m focus_main .It Cm M-S-j swap_next .It Cm M-S-k swap_prev .It Cm M-b bar_toggle .It Cm M-x wind_del .It Cm M-S-x wind_kill .It Cm M- Ns Aq Ar n .Ns ws_ Ns Ar n .It Cm M-S- Ns Aq Ar n .Ns mvws_ Ns Ar n .It Cm M- Ns Aq Cm Right ws_next .It Cm M- Ns Aq Cm Left ws_prev .It Cm M-S- Ns Aq Cm Right screen_next .It Cm M-S- Ns Aq Cm Left screen_prev .It Cm M-s screenshot_all .It Cm M-S-s screenshot_wind .It Cm M-S-v version .It Cm M-t float_toggle .It Cm M-S Aq Cm Delete lock .It Cm M-S-i initscr .El .Pp ОпиÑÐ°Ð½Ð¸Ñ Ð´ÐµÐ¹Ñтвий перечиÑлены ниже: .Pp .Bl -tag -width "M-j, M-XXX" -offset indent -compact .It Cm term ЗапуÑтить ÑмулÑтор терминала (Смотрите Ñекцию .Sx ПРОГРÐММЫ выше) .It Cm menu Меню (Смотрите Ñекцию .Sx ПРОГРÐММЫ выше) .It Cm quit Выйти .Nm .It Cm restart ПерезапуÑтить .Nm .It Cm cycle_layout МенÑть укладку окон .It Cm reset_layout Ð¡Ñ‚Ð°Ð½Ð´Ð°Ñ€Ñ‚Ð½Ð°Ñ ÑƒÐºÐ»Ð°Ð´ÐºÐ° .It Cm master_shrink Сжать облаÑть главного окна .It Cm master_grow РаÑширить облаÑть главного окна .It Cm master_add Добавить окна в главную облаÑть .It Cm master_del Убрать окна из главной облаÑти .It Cm stack_inc Увеличить чиÑло Ñтолбцов или Ñ€Ñдов в текущей укладке .It Cm stack_del Уменьшить чиÑло Ñтолбцов или Ñ€Ñдов в текущей укладке .It Cm swap_main Отправить текущее окно в главную облаÑть, Ñделать главным .It Cm focus_next ФокуÑироватьÑÑ Ð½Ð° Ñледующем окне .It Cm focus_prev ФокуÑироватьÑÑ Ð½Ð° предыдущем окне .It Cm focus_main ФокуÑироватьÑÑ Ð½Ð° главном окне .It Cm swap_next ПоменÑть Ñо Ñледующим окном .It Cm swap_prev ПоменÑть Ñо предыдущим окном .It Cm bar_toggle Выключить ÑтатуÑную Ñтроку на вÑех рабочих Ñтолах .It Cm wind_del Закрыть фокуÑированное окно .It Cm wind_kill Грохнуть фокуÑированное окно .It Cm ws_ Ns Ar n ПереключитьÑÑ Ð½Ð° рабочий Ñтол .Ar n , где .Ar n от 1 до 10 .It Cm mvws_ Ns Ar n ПеремеÑтить фокуÑированное окно в рабочий Ñтол .Ar n , где .Ar n от 1 до 10 .It Cm ws_next Перейти к Ñледующему не пуÑтому рабочему Ñтолу .It Cm ws_prev Перейти к Ñледующему не пуÑтому рабочему Ñтолу .It Cm screen_next ПеремеÑтить указатель в Ñледующую облаÑть .It Cm screen_prev ПеремеÑтить указатель в Ñледующую облаÑть .It Cm screenshot_all Сделать Ñнимок вÑего Ñкрана (еÑли возможно) (Смотрите Ñекцию .Sx ПРОГРÐММЫ выше) .It Cm screenshot_wind Сделать Ñнимок окна (еÑли возможно) (Смотрите Ñекцию .Sx ПРОГРÐММЫ выше) .It Cm version Показать верÑию в ÑтатуÑной Ñтроке .It Cm float_toggle Переключить окно в фокуÑе в плавающий режим, float .It Cm lock Заблокировать Ñкран (Смотрите Ñекцию .Sx ПРОГРÐММЫ выше) .It Cm initscr Инициализировать Ñкран еще раз (Смотрите Ñекцию .Sx ПРОГРÐММЫ выше) .El .Pp СобÑтвенные привÑзки назначаютÑÑ Ñледующим образом: .Pp .Dl bind[] = .Pp .Aq action Ñто дейÑтвие из ÑпиÑка программ .Aq keys Ñто не более одной клавиши-модификатора (MOD, Mod1, Shift, и.Ñ‚.п.) и обычные клавиши (b, space, и.Ñ‚.п.), разделенные "+". Ðапример: .Bd -literal -offset indent bind[reset] = Mod4+q # назначить WIN + q на дейÑтвие reset bind[] = Mod1+q # ÑнÑть вÑе дейÑÑ‚Ð²Ð¸Ñ Ñ Alt + q .Ed .Pp Ðа одно дейÑтвие можно назначить неÑколько комбинаций. .Sh КОСТЫЛИ .Nm позволÑет наÑтроить коÑтыли, нужные Ð´Ð»Ñ Ñпециальной работы spectrwm Ñ Ñ€Ñдом приложений, который вы определÑете Ñами. То еÑть, Ð’Ñ‹ можете принудительно уÑтановить ÑпоÑоб тайлинга Ð´Ð»Ñ ÐºÐ°ÐºÐ¾Ð³Ð¾-нибудь Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ .Pp СпиÑок Ñтандартных коÑтылей: .Pp .Bl -tag -width "OpenOffice.org N.M:VCLSalFrameXXX" -offset indent -compact .It Firefox\-bin:firefox\-bin TRANSSZ .It Firefox:Dialog FLOAT .It Gimp:gimp FLOAT + ANYWHERE .It MPlayer:xv FLOAT + FULLSCREEN .It OpenOffice.org 2.4:VCLSalFrame FLOAT .It OpenOffice.org 3.1:VCLSalFrame FLOAT .It pcb:pcb FLOAT .It xine:Xine Window FLOAT + ANYWHERE .It xine:xine Panel FLOAT + ANYWHERE .It xine:xine Video Fullscreen Window FULLSCREEN + FLOAT .It Xitk:Xitk Combo FLOAT + ANYWHERE .It Xitk:Xine Window FLOAT + ANYWHERE .It XTerm:xterm XTERM_FONTADJ .El .Pp ОпиÑание: .Pp .Bl -tag -width "XTERM_FONTADJXXX" -offset indent -compact .It FLOAT Такое окно не нужно тайлить вообще, разрешить ему float .It TRANSSZ Тразиентое окно (Смотрите Ñекцию .Sx КОÐФИГУРÐЦИОÐÐЫЕ ФÐЙЛЫ) . .It ANYWHERE Позволить окну ÑамоÑтоÑтельно выбрать меÑтоположение .It XTERM_FONTADJ ИзменÑть шрифты xterm при изменении размеров окна .It FULLSCREEN Позволить окну запуÑкатьÑÑ Ð² полноÑкранном режиме .El .Pp Ðазначать коÑтыли можно Ñледующим образом: .Pp .Dl quirk[:] = [ + ... ] .Pp .Aq class и .Aq name определÑÑŽÑ‚ к какому окну будет применÑтьÑÑ ÐºÐ¾Ñтыль, а .Aq quirk один из вышеперечиÑленных ÑпоÑобов. Ðапример: .Bd -literal -offset indent quirk[MPlayer:xv] = FLOAT + FULLSCREEN # mplayer наÑтроен quirk[pcb:pcb] = NONE # убрать ÑущеÑтвующий коÑтыль .Ed .Pp Ð’Ñ‹ можете узнать .Aq class и .Aq name запуÑтив xprop и нажав в интереÑующее окно. Вот как будет выглÑдеть вывод Ð´Ð»Ñ Firefox: .Bd -literal -offset indent $ xprop | grep WM_CLASS WM_CLASS(STRING) = "Navigator", "Firefox" .Ed .Pp Обратите внимание, клаÑÑ Ð¸ Ð¸Ð¼Ñ Ð¼ÐµÐ½ÑÑŽÑ‚ÑÑ Ð¼ÐµÑтами, правильный коÑтыль будет выглÑдеть так: .Bd -literal -offset indent quirk[Firefox:Navigator] = FLOAT .Ed .Sh ФÐЙЛЫ .Bl -tag -width "/etc/spectrwm.confXXX" -compact .It Pa ~/.spectrwm.conf .Nm Личные наÑтройки пользователÑ. .It Pa /etc/spectrwm.conf .Nm Глобавльные наÑтройки. .El .Sh ИСТОРИЯ .Nm идейно оÑнован на dwm и xmonad .Sh ÐВТОРЫ .An -nosplit .Pp .Nm напиÑан: .An Marco Peereboom Aq marco@peereboom.us , .An Ryan Thomas McBride Aq mcbride@countersiege.com and .An Darrin Chandler Aq dwchandler@stilyagin.com . .Sh БÐГИ При вызове меню Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ .Cm M-p , необходима ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ð° dmenu. spectrwm-SPECTRWM_3_1_0/screenshot.sh000066400000000000000000000002031316475426700175700ustar00rootroot00000000000000#!/bin/sh # screenshot() { case $1 in full) scrot -m ;; window) sleep 1 scrot -s ;; *) ;; esac; } screenshot $1 spectrwm-SPECTRWM_3_1_0/spectrwm.1000066400000000000000000001063551316475426700170240ustar00rootroot00000000000000.\" Copyright (c) 2009-2012 Marco Peereboom .\" Copyright (c) 2009 Darrin Chandler .\" Copyright (c) 2011-2015 Reginald Kennedy .\" Copyright (c) 2011-2012 Lawrence Teo .\" Copyright (c) 2011-2012 Tiago Cunha .\" Copyright (c) 2012 David Hill .\" .\" 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: February 15 2012 $ .Dt SPECTRWM 1 .Os .Sh NAME .Nm spectrwm .Nd window manager for X11 .Sh SYNOPSIS .Nm spectrwm .Sh DESCRIPTION .Nm is a minimalistic window manager that tries to stay out of the way so that valuable screen real estate can be used for much more important stuff. It has sane defaults and does not require one to learn a language to do any configuration. It was written by hackers for hackers and it strives to be small, compact and fast. .Pp When .Nm starts up, it reads settings from its configuration file, .Pa spectrwm.conf . See the .Sx CONFIGURATION FILES section below. .Pp The following notation is used throughout this page: .Pp .Bl -tag -width Ds -offset indent -compact .It Cm M Meta .It Cm S Shift .It Aq Cm Name Named key or button .El .Pp .Nm is very simple in its use. Most of the actions are initiated via key or pointer bindings. See the .Sx BINDINGS section below for defaults and customizations. .Sh CONFIGURATION FILES .Nm first tries to open the user specific file, .Pa ~/.spectrwm.conf . If that file is unavailable, it then tries to open the global configuration file .Pa /etc/spectrwm.conf . .Pp The format of the file is .Pp .Dl Ar keyword Li = Ar setting .Pp For example: .Pp .Dl color_focus = red .Pp Enabling or disabling an option is done by using 1 or 0 respectively. .Pp Colors need to be specified per the .Xr XQueryColor 3 specification. .Pp Comments begin with a #. When a literal .Ql # is desired in an option, then it must be escaped with a backslash, i.e. \e# .Pp The file supports the following keywords: .Bl -tag -width 2m .It Ic autorun Launch an application in a specified workspace at start-of-day. Defined in the format .Li ws Ns Bo Ar idx Bc : Ns Ar application , e.g. ws[2]:xterm launches an .Xr xterm 1 in workspace 2. .Pp Note that workspace mapping is handled via .Pa libswmhack.so . When .Ic autorun spawns windows via a daemon, ensure the daemon is started with the correct .Pa LD_PRELOAD in its environment. .Pp For example, starting .Xr urxvtd 1 via .Xr xinit 1 : .Bd -literal -offset indent LD_PRELOAD=/usr/lib/libswmhack.so.0.0 urxvtd -q -o -f .Ed .Pp Spawned programs automatically have .Pa LD_PRELOAD set when executed. .It Ic bar_action External script that populates additional information in the status bar, such as battery life. .It Ic bar_at_bottom Place the statusbar at the bottom of each region instead of the top. .It Ic bar_border Ns Bq Ar x Border color of the status bar(s) in screen .Ar x . .It Ic bar_border_unfocus Ns Bq Ar x Border color of the status bar(s) on unfocused region(s) in screen .Ar x . .It Ic bar_border_width Set status bar border thickness in pixels. Disable border by setting to 0. .It Ic bar_color Ns Bq Ar x Background color of the status bar(s) in screen .Ar x . .It Ic bar_color_selected Ns Bq Ar x Background color for selections on the status bar(s) in screen .Ar x . Defaults to the value of .Ic bar_border . .It Ic bar_enabled Set default .Ic bar_toggle state; default is 1. .It Ic bar_enabled_ws Ns Bq Ar x Set default .Ic bar_toggle_ws state on workspace .Ar x ; default is 1. .It Ic bar_font Font used in the status bar. Either Xft or X Logical Font Description (XLFD) may be used to specify fonts. Fallback fonts may be specified by separating each font with a comma. If all entries are in XLFD syntax, font set will be used. If at least one entry is Xft, Xft will be used. Note that if Xft is in use, only the first font that successfully loads will be used regardless of missing glyphs. The default is to use font set. Also note that .Xr dmenu 1 does not support Xft fonts. .Pp Xft examples: .Bd -literal -offset indent bar_font = Terminus:style=Regular:pixelsize=14:antialias=true bar_font = -*-profont-medium-*-*-*-11-*-*-*-*-*-*-*,Terminus:pixelsize=14,\ -*-clean-medium-*-*-*-12-*-*-*-*-*-*-* .Ed .Pp Font set examples: .Bd -literal -offset indent bar_font = -*-terminus-medium-*-*-*-14-*-*-*-*-*-*-* bar_font = -*-profont-medium-*-*-*-11-*-*-*-*-*-*-*,\ -*-terminus-medium-*-*-*-14-*-*-*-*-*-*-*,\ -*-clean-medium-*-*-*-12-*-*-*-*-*-*-* .Ed .Pp To list the available fonts in your system see .Xr fc-list 1 or .Xr xlsfonts 1 manpages. The .Xr xfontsel 1 application can help with the XLFD setting. .It Ic bar_font_color Ns Bq Ar x Foreground color of the status bar(s) in screen .Ar x . .It Ic bar_font_color_selected Ns Bq Ar x Foreground color for selections on the status bar(s) in screen .Ar x . Defaults to the value of .Ic bar_color . .It Ic bar_format Set the bar format string, overriding .Ic clock_format and all of the .Ic enabled options. The format is passed through .Xr strftime 3 before being used. It may contain the following character sequences: .Bl -column "Character sequence" "Replaced with" -offset indent .It Sy "Character sequence" Ta Sy "Replaced with" .It Li "+<" Ta "Pad with a space" .It Li "+A" Ta "Output of the external script" .It Li "+C" Ta "Window class (from WM_CLASS)" .It Li "+D" Ta "Workspace name" .It Li "+F" Ta "Floating indicator" .It Li "+I" Ta "Workspace index" .It Li "+M" Ta "Number of iconic (minimized) windows in workspace" .It Li "+N" Ta "Screen number" .It Li "+P" Ta "Window class and instance separated by a colon" .It Li "+R" Ta "Region index" .It Li "+S" Ta "Stacking algorithm" .It Li "+T" Ta "Window instance (from WM_CLASS)" .It Li "+U" Ta "Urgency hint" .It Li "+V" Ta "Program version" .It Li "+W" Ta "Window name (from _NET_WM_NAME/WM_NAME)" .It Li "++" Ta "A literal" Ql + .El .Pp All character sequences may limit its output to a specific length, for example +64A. By default, no padding/alignment is done in case the length of the replaced string is less than the specified length (64 in the example). The padding/alignment can be enabled using a '_' character in the sequence. For example: +_64W, +64_W and +_64_W enable padding before (right alignment), after (left alignment), and both before and after (center alignment) window name, respectively. Any characters that don't match the specification are copied as-is. .It Ic bar_justify Justify the status bar text. Possible values are .Ar left , .Ar center , and .Ar right . .Pp Note that if the output is not left justified, it may not be properly aligned in some circumstances, due to the white-spaces in the default static format. See the .Ic bar_format option for more details. .It Ic bind Ns Bq Ar x Bind key or button combo to action .Ar x . See the .Sx BINDINGS section below. .It Ic border_width Set window border thickness in pixels. Disable all borders by setting to 0. .It Ic boundary_width Set region containment boundary width in pixels. This is how far a window must be dragged/resized (with the pointer) beyond the region edge before it is allowed outside the region. Disable the window containment effect by setting to 0. .It Ic clock_enabled Enable or disable displaying the clock in the status bar. Disable by setting to 0 so a custom clock could be used in the .Ic bar_action script. .It Ic iconic_enabled Display the number of iconic (minimized) windows in the status bar. Enable by setting to 1. .It Ic color_focus Border color of the currently focused window. Default is red. .It Ic color_focus_maximized Border color of the currently focused, maximized window. Defaults to the value of .Ic color_focus . .It Ic color_unfocus Border color of unfocused windows, default is rgb:88/88/88. .It Ic color_unfocus_maximized Border color of unfocused, maximized windows. Defaults to the value of .Ic color_unfocus . .It Ic dialog_ratio Some applications have dialogue windows that are too small to be useful. This ratio is the screen size to what they will be resized. For example, 0.6 is 60% of the physical screen size. .It Ic disable_border Remove border when bar is disabled and there is only one window on the region. .It Ic focus_close Window to put focus when the focused window is closed. Possible values are .Ar first , .Ar next , .Ar previous (default) and .Ar last . .Ar next and .Ar previous are relative to the window that is closed. .It Ic focus_close_wrap Whether to allow the focus to jump to the last window when the first window is closed or vice versa. Disable by setting to 0. .It Ic focus_default Window to put focus when no window has been focused. Possible values are .Ar first and .Ar last (default). .It Ic focus_mode Window focus behavior with respect to the pointer. Possible values: .Pp .Bl -tag -width "default" -offset indent -compact .It Ar default Set window focus on border crossings caused by cursor motion and window interaction. .It Ar follow Set window focus on all cursor border crossings, including workspace switches and changes to layout. .It Ar manual Set window focus on window interaction only. .El .It Ic maximize_hide_bar When set to 1, .Ic maximize_toggle will also hide/restore the bar visibility of the affected workspace. Defaults to 0. .It Ic keyboard_mapping Clear all key bindings (not button bindings) and load new bindings from the specified file. This allows you to load pre-defined key bindings for your keyboard layout. See the .Sx KEYBOARD MAPPING FILES section below for a list of keyboard mapping files that have been provided for several keyboard layouts. .It Ic layout Select layout to use at start-of-day. Defined in the format .Li ws Ns Bo Ar idx Bc : Ns Ar master_grow : Ns Ar master_add : Ns Ar stack_inc : Ns Ar always_raise : Ns Ar stack_mode , e.g. ws[2]:-4:0:1:0:horizontal sets worskspace 2 to the horizontal stack mode, shrinks the master area by 4 ticks and adds one window to the stack, while maintaining default floating window behavior. Possible .Ar stack_mode values are .Ar vertical , .Ar vertical_flip , .Ar horizontal , .Ar horizontal_flip and .Ar fullscreen . .Pp See .Ic master_grow , .Ic master_shrink , .Ic master_add , .Ic master_del , .Ic stack_inc , .Ic stack_dec , .Ic stack_balance , and .Ic always_raise for more information. Note that the stacking options are complicated and have side-effects. One should familiarize oneself with these commands before experimenting with the .Ic layout option. .Pp This setting is not retained at restart. .It Ic modkey Change mod key. Mod1 is generally the ALT key and Mod4 is the windows key on a PC. .It Ic name Set the name of a workspace at start-of-day. Defined in the format .Li ws Ns Bo Ar idx Bc : Ns Ar name , e.g. ws[1]:Console sets the name of workspace 1 to .Dq Console . .It Ic program Ns Bq Ar p Define new action to spawn a program .Ar p . See the .Sx PROGRAMS section below. .It Ic quirk Ns Bq Ar c Ns Li : Ns Ar i Ns Li : Ns Ar n Add "quirk" for windows with class .Ar c , instance .Ar i and name .Ar n . See the .Sx QUIRKS section below. .It Ic region Allocates a custom region, removing any autodetected regions which occupy the same space on the screen. Defined in the format .Li screen Ns Bo Ar idx Ns Bc : Ns Ar width Ns x Ns Ar height Ns + Ns Ar x Ns + Ns Ar y , e.g. screen[1]:800x1200+0+0. .Pp To make a region span multiple monitors, create a region big enough to cover them all, e.g. screen[1]:2048x768+0+0 makes the region span two monitors with 1024x768 resolution sitting one next to the other. .It Ic region_padding Pixel width of empty space within region borders. Disable by setting to 0. .It Ic spawn_position Position in stack to place newly spawned windows. Possible values are .Ar first , .Ar next , .Ar previous and .Ar last (default). .Ar next and .Ar previous are relative to the focused window. .It Ic stack_enabled Enable or disable displaying the current stacking algorithm in the status bar. .It Ic term_width Set a preferred minimum width for the terminal. If this value is greater than 0, .Nm will attempt to adjust the font sizes in the terminal to keep the terminal width above this number as the window is resized. Only .Xr xterm 1 is currently supported. The .Xr xterm 1 binary must not be setuid or setgid, which it is by default on most systems. Users may need to set program[term] (see the .Sx PROGRAMS section) to use an alternate copy of the .Xr xterm 1 binary without the setgid bit set. .It Ic tile_gap Pixel width of empty space between tiled windows. Negative values cause overlap. Set this to the opposite of .Ic border_width to collapse the border between tiles. Disable by setting to 0. .It Ic urgent_collapse Minimizes the space consumed by the urgency hint indicator by removing the placeholders for non-urgent workspaces, the trailing space when there are urgent windows and the default leading space. Enable by setting to 1. .It Ic urgent_enabled Enable or disable the urgency hint indicator in the status bar. Note that many terminal emulators require an explicit setting for the bell character to trigger urgency on the window. In .Xr xterm 1 , for example, one needs to add the following line to .Pa .Xdefaults : .Bd -literal -offset indent xterm.bellIsUrgent: true .Ed .It Ic verbose_layout Enable or disable displaying the current master window count and stack column/row count in the status bar. Enable by setting to 1. See .Ar master_add , .Ar master_del , .Ar stack_inc and .Ar stack_dec for more information. .It Ic workspace_clamp Prevents workspaces from being swapped when attempting to switch to a workspace that is mapped to another region. Use .Ar warp_focus if you want to focus on the region containing the workspace and .Ar warp_pointer if you want to also send the pointer. Enable by setting to 1. .It Ic window_class_enabled Enable or disable displaying the window class name (from WM_CLASS) in the status bar. Enable by setting to 1. .It Ic window_instance_enabled Enable or disable displaying the window instance name (from WM_CLASS) in the status bar. Enable by setting to 1. .It Ic window_name_enabled Enable or disable displaying the window display name (from _NET_WM_NAME/WM_NAME) in the status bar. Enable by setting to 1. .Pp To prevent excessively large window names from pushing the remaining text off the bar, it's limited to 64 characters, by default. See the .Ic bar_format option for more details. .It Ic warp_focus Focus on the target window/workspace/region when clamped. For example, when attempting to switch to a workspace that is mapped on another region and .Ar workspace_clamp is enabled, focus on the region with the target workspace. Enable by setting to 1. .It Ic warp_pointer Centers the pointer on the focused window when using bindings to change focus, switch workspaces, change regions, etc. Enable by setting to 1. .It Ic workspace_limit Set the total number of workspaces available. Minimum is 1, maximum is 22, default is 10. .El .Sh PROGRAMS .Nm allows you to define custom actions to launch programs of your choice and then bind them the same as with built-in actions. See the .Sx BINDINGS section below. .Pp Custom programs in the configuration file are specified as follows: .Pp .Dl program Ns Bo Ar action Bc = Ar progpath Op Ar arg Op Ar arg ... .Pp .Ar action is any identifier that does not conflict with a built-in action or keyword, .Ar progpath is the desired program, and .Ar arg is zero or more arguments to the program. .Pp Remember that when using .Ql # in your program call, it must be escaped with a backslash, i.e. \e# .Pp The following argument variables will be substituted for values at the time the program is spawned: .Pp .Bl -tag -width "$bar_font_color" -offset indent -compact .It Cm $bar_border .It Cm $bar_color .It Cm $bar_color_selected .It Cm $bar_font .It Cm $bar_font_color .It Cm $bar_font_color_selected .It Cm $color_focus .It Cm $color_unfocus .It Cm $dmenu_bottom \-b if .Ic bar_at_bottom is enabled. .It Cm $region_index .It Cm $workspace_index .El .Pp Example: .Bd -literal -offset indent program[ff] = /usr/local/bin/firefox http://spectrwm.org/ bind[ff] = MOD+Shift+b # Now M-S-b launches firefox .Ed .Pp To cancel the previous, unbind it: .Bd -literal -offset indent bind[] = MOD+Shift+b .Ed .Pp Default programs: .Bl -tag -width "screenshot_wind" -offset indent -compact .It Cm term xterm .It Cm lock xlock .It Cm menu dmenu_run $dmenu_bottom \-fn $bar_font \-nb $bar_color \-nf $bar_font_color \-sb $bar_color_selected \-sf $bar_font_color_selected .It Cm search dmenu $dmenu_bottom \-i \-fn $bar_font \-nb $bar_color \-nf $bar_font_color \-sb $bar_color_selected \-sf $bar_font_color_selected .It Cm name_workspace dmenu $dmenu_bottom \-p Workspace \-fn $bar_font \-nb $bar_color \-nf $bar_font_color \-sb $bar_color_selected \-sf $bar_font_color_selected .It Cm initscr initscreen.sh # optional .It Cm screenshot_all screenshot.sh full # optional .It Cm screenshot_wind screenshot.sh window # optional .El .Pp Note that optional default programs will not be validated unless overridden. If a default program fails validation, you can resolve the exception by installing the program, modifying the program call or disabling the program by freeing the respective binding. .Pp For example, to override .Ic lock : .Bd -literal -offset indent program[lock] = xscreensaver\-command \-lock .Ed .Pp To unbind .Ic lock and prevent it from being validated: .Bd -literal -offset indent bind[] = MOD+Shift+Delete .Ed .Sh BINDINGS .Nm provides many functions (or actions) accessed via key or pointer bindings. .Pp The default bindings are listed below: .Pp .Bl -tag -width "M-j, M-XXXXXX" -offset indent -compact .It Aq Cm Button1 focus .It Cm M- Ns Aq Cm Button1 move .It Cm M- Ns Aq Cm Button3 resize .It Cm M-S- Ns Aq Cm Button3 resize_centered .It Cm M-S- Ns Aq Cm Return term .It Cm M-p menu .It Cm M-S-q quit .It Cm M-q restart .It Cm M- Ns Aq Cm Space cycle_layout .It Cm M-S-\e flip_layout .It Cm M-S- Ns Aq Cm Space stack_reset .It Aq Ar unbound stack_balance .It Cm M-h master_shrink .It Cm M-l master_grow .It Cm M-, master_add .It Cm M-. master_del .It Cm M-S-, stack_inc .It Cm M-S-. stack_dec .It Cm M- Ns Aq Cm Return swap_main .It Xo .Cm M-j , .Cm M- Ns Aq Cm TAB .Xc focus_next .It Xo .Cm M-k , .Cm M-S- Ns Aq Cm TAB .Xc focus_prev .It Cm M-m focus_main .It Cm M-u focus_urgent .It Cm M-S-j swap_next .It Cm M-S-k swap_prev .It Cm M-b bar_toggle .It Cm M-S-b bar_toggle_ws .It Cm M-x wind_del .It Cm M-S-x wind_kill .It Cm M- Ns Aq Ar 1-9,0,F1-F12 .Pf ws_ Aq Ar 1-22 .It Cm M-S- Ns Aq Ar 1-9,0,F1-F12 .Pf mvws_ Ns Aq Ar 1-22 .It Cm M- Ns Aq Ar Keypad 1-9 .Pf rg_ Aq Ar 1-9 .It Cm M-S- Ns Aq Ar Keypad 1-9 .Pf mvrg_ Aq Ar 1-9 .It Aq Ar unbound mvrg_next .It Aq Ar unbound mvrg_prev .It Aq Ar unbound ws_empty .It Cm M- Ns Aq Cm Right ws_next .It Cm M- Ns Aq Cm Left ws_prev .It Cm M- Ns Aq Cm Up ws_next_all .It Cm M- Ns Aq Cm Down ws_prev_all .It Cm M-a ws_prior .It Cm M-S- Ns Aq Cm Down ws_prev_move .It Cm M-S- Ns Aq Cm Up ws_next_move .It Cm M-S- Ns Aq Cm Right rg_next .It Cm M-S- Ns Aq Cm Left rg_prev .It Aq Ar unbound rg_move_next .It Aq Ar unbound rg_move_prev .It Cm M-s screenshot_all .It Cm M-S-s screenshot_wind .It Cm M-S-v version .It Cm M-t float_toggle .It Cm M-S- Ns Aq Cm Delete lock .It Cm M-S-i initscr .It Cm M-w iconify .It Cm M-S-w uniconify .It Cm M-e maximize_toggle .It Cm M-S-e fullscreen_toggle .It Cm M-r raise .It Cm M-S-r always_raise .It Cm M-v button2 .It Cm M-- width_shrink .It Cm M-= width_grow .It Cm M-S-- height_shrink .It Cm M-S-= height_grow .It Cm M-[ move_left .It Cm M-] move_right .It Cm M-S-[ move_up .It Cm M-S-] move_down .It Cm M-S-/ name_workspace .It Cm M-/ search_workspace .It Cm M-f search_win .El .Pp The action names and descriptions are listed below: .Pp .Bl -tag -width "M-j, M-XXXX" -offset indent -compact .It Cm focus Focus window/region under pointer. .It Cm move Move window with pointer while binding is pressed. .It Cm resize Resize window with pointer while binding is pressed. .It Cm resize_centered Same as .Ic resize but keep window centered. .It Cm term Spawn a new terminal (see .Sx PROGRAMS above). .It Cm menu Menu (see .Sx PROGRAMS above). .It Cm quit Quit .Nm . .It Cm restart Restart .Nm . .It Cm cycle_layout Cycle layout. .It Cm flip_layout Swap the master and stacking areas. .It Cm stack_reset Reset layout. .It Cm stack_balance Balance master/stacking area. .It Cm master_shrink Shrink master area. .It Cm master_grow Grow master area. .It Cm master_add Add windows to master area. .It Cm master_del Remove windows from master area. .It Cm stack_inc Add columns/rows to stacking area. .It Cm stack_dec Remove columns/rows from stacking area. .It Cm swap_main Move current window to master area. .It Cm focus_next Focus next window in workspace. .It Cm focus_prev Focus previous window in workspace. .It Cm focus_main Focus on main window in workspace. .It Cm focus_urgent Focus on next window with the urgency hint flag set. The workspace is switched if needed. .It Cm swap_next Swap with next window in workspace. .It Cm swap_prev Swap with previous window in workspace. .It Cm bar_toggle Toggle overall visibility of status bars. .It Cm bar_toggle_ws Toggle status bar on current workspace. .It Cm wind_del Delete current window in workspace. .It Cm wind_kill Destroy current window in workspace. .It Cm ws_ Ns Ar n Switch to workspace .Ar n , where .Ar n is 1 through .Ic workspace_limit . .It Cm mvws_ Ns Ar n Move current window to workspace .Ar n , where .Ar n is 1 through .Ic workspace_limit . .It Cm rg_ Ns Ar n Focus on region .Ar n , where .Ar n is 1 through 9. .It Cm mvrg_ Ns Ar n Move current window to region .Ar n , where .Ar n is 1 through 9. .It Cm mvrg_next Move current window to workspace in next region. .It Cm mvrg_prev Move current window to workspace in previous region. .It Cm ws_empty Switch to the first empty workspace. .It Cm ws_next Switch to next workspace with a window in it. .It Cm ws_prev Switch to previous workspace with a window in it. .It Cm ws_next_all Switch to next workspace. .It Cm ws_prev_all Switch to previous workspace. .It Cm ws_next_move Switch to next workspace with the current window. .It Cm ws_prev_move Switch to previous workspace with the current window. .It Cm ws_prior Switch to last visited workspace. .It Cm rg_next Switch to next region. .It Cm rg_prev Switch to previous region. .It Cm rg_move_next Switch region to next screen. .It Cm rg_move_prev Switch region to previous screen. .It Cm screenshot_all Take screenshot of entire screen (if enabled) (see .Sx PROGRAMS above). .It Cm screenshot_wind Take screenshot of selected window (if enabled) (see .Sx PROGRAMS above). .It Cm version Toggle version in status bar. .It Cm float_toggle Toggle focused window between tiled and floating. .It Cm lock Lock screen (see .Sx PROGRAMS above). .It Cm initscr Reinitialize physical screens (see .Sx PROGRAMS above). .It Cm iconify Minimize (unmap) currently focused window. .It Cm uniconify Restore (map) window returned by .Xr dmenu 1 selection. .It Cm maximize_toggle Toggle maximization of focused window. .It Cm fullscreen_toggle Toggle fullscreen state of focused window. .It Cm raise Raise the current window. .It Cm always_raise When set tiled windows are allowed to obscure floating windows. .It Cm button2 Fake a middle mouse button click (Button2). .It Cm width_shrink Shrink the width of a floating window. .It Cm width_grow Grow the width of a floating window. .It Cm height_shrink Shrink the height of a floating window. .It Cm height_grow Grow the height of a floating window. .It Cm move_left Move a floating window a step to the left. .It Cm move_right Move a floating window a step to the right. .It Cm move_up Move a floating window a step upwards. .It Cm move_down Move a floating window a step downwards. .It Cm name_workspace Name the current workspace. .It Cm search_workspace Search for a workspace. .It Cm search_win Search the windows in the current workspace. .El .Pp Custom bindings in the configuration file are specified as follows: .Pp .Dl bind Ns Bo Ar action Bc = Ar combo .Pp .Ar action is one of the actions listed above (or empty to unbind) and .Ar combo is in the form of zero or more modifier keys and/or special arguments (Mod1, Shift, MOD, etc.) and a normal key (b, Space, etc.) or a button (Button1 .. Button255), separated by .Ql + . Multiple key/button combinations may be bound to the same action. .Pp Special arguments: .Bl -tag -width "anymodxxxx" -offset indent -compact .It Cm MOD Substituted for the currently defined .Ic modkey . .It Cm ANYMOD Select all modifier combinations not handled by another binding. .It Cm REPLAY Reprocess binding press/release events for other programs to handle. Unavailable for .Ic move , .Ic resize and .Ic resize_centered . .El .Pp .Cm MOD example: .Bd -literal -offset indent bind[reset] = Mod4+q # bind Windows-key + q to reset bind[] = Mod1+q # unbind Alt + q bind[move] = MOD+Button3 # Bind move to M-Button3 bind[] = MOD+Button1 # Unbind default move binding. .Ed .Pp .Cm ANYMOD example: .Bd -literal -offset indent bind[focus] = ANYMOD+Button3 bind[move] = MOD+Button3 .Ed .Pp In the above example, .Cm M- Ns Aq Cm Button3 initiates .Ic move and .Aq Cm Button3 pressed with any other combination of modifiers sets focus to the window/region under the pointer. .Pp .Cm REPLAY example: .Bd -literal -offset indent bind[focus] = REPLAY+Button3 .Ed .Pp In the above example, when .Aq Cm Button3 is pressed without any modifier(s), focus is set to the window under the pointer and the button press is passed to the window. .Pp To bind non-latin characters such as \[oa] or \[*p] you must enter the xkb character name instead of the character itself. Run .Xr xev 1 , focus the window and press the specific key and in the terminal output read the symbol name. In the following example for \[oa]: .Bd -literal -offset indent KeyPress event, serial 41, synthetic NO, window 0x2600001, root 0x15a, subw 0x0, time 106213808, (11,5), root:(359,823), state 0x0, keycode 24 (keysym 0xe5, aring), same_screen YES, XLookupString gives 2 bytes: (c3 a5) "\[oa]" XmbLookupString gives 2 bytes: (c3 a5) "\[oa]" XFilterEvent returns: False .Ed .Pp The xkb name is aring. In other words, in .Pa spectrwm.conf add: .Bd -literal -offset indent bind[program] = MOD+aring .Ed .Sh KEYBOARD MAPPING FILES Keyboard mapping files for several keyboard layouts are listed below. These files can be used with the .Ic keyboard_mapping setting to load pre-defined key bindings for the specified keyboard layout. .Pp .Bl -tag -width "spectrwm_XX.confXXX" -offset indent -compact .It Cm spectrwm_cz.conf Czech Republic keyboard layout .It Cm spectrwm_es.conf Spanish keyboard layout .It Cm spectrwm_fr.conf French keyboard layout .It Cm spectrwm_fr_ch.conf Swiss French keyboard layout .It Cm spectrwm_se.conf Swedish keyboard layout .It Cm spectrwm_us.conf United States keyboard layout .El .Sh QUIRKS .Nm provides "quirks" which handle windows that must be treated specially in a tiling window manager, such as some dialogs and fullscreen apps. .Pp The default quirks are described below: .Pp .Bl -tag -width "OpenOffice.org N.M:VCLSalFrameXXX" -offset indent \ -compact .It Firefox\-bin:firefox\-bin TRANSSZ .It Firefox:Dialog FLOAT .It Gimp:gimp FLOAT + ANYWHERE .It MPlayer:xv FLOAT + FULLSCREEN + FOCUSPREV .It OpenOffice.org 2.4:VCLSalFrame FLOAT .It OpenOffice.org 3.1:VCLSalFrame FLOAT .It pcb:pcb FLOAT .It xine:Xine Window FLOAT + ANYWHERE .It xine:xine Panel FLOAT + ANYWHERE .It xine:xine Video Fullscreen Window FULLSCREEN + FLOAT .It Xitk:Xitk Combo FLOAT + ANYWHERE .It Xitk:Xine Window FLOAT + ANYWHERE .It XTerm:xterm XTERM_FONTADJ .El .Pp The quirks themselves are described below: .Pp .Bl -tag -width "XTERM_FONTADJXXX" -offset indent -compact .It ANYWHERE Allow window to position itself, uncentered. .It FLOAT This window should not be tiled, but allowed to float freely. .It FOCUSONMAP_SINGLE When the window first appears on the screen, change focus to the window if there are no other windows on the workspace with the same WM_CLASS class/instance value. Has no effect when .Ic focus_mode is set to .Ar follow . .It FOCUSPREV On exit force focus on previously focused application not previous application in the stack. .It FULLSCREEN Remove border to allow window to use full region size. .It IGNOREPID Ignore the PID when determining the initial workspace for a new window. Especially useful for terminal windows that share a process. .It IGNORESPAWNWS Ignore the spawn workspace when determining the initial workspace for a new window. .It MINIMALBORDER Remove border when window is unfocused and floating. .It NOFOCUSCYCLE Remove from normal focus cycle (focus_prev or focus_next). The window can still be focused using search_win. .It NOFOCUSONMAP Don't change focus to the window when it first appears on the screen. Has no effect when .Ic focus_mode is set to .Ar follow . .It OBEYAPPFOCUSREQ When an application requests focus on the window via a _NET_ACTIVE_WINDOW client message (source indication of 1), comply with the request. Note that a source indication of 0 (unspecified) or 2 (pager) are always obeyed. .It TRANSSZ Adjusts size on transient windows that are too small using .Ic dialog_ratio (see .Sx CONFIGURATION FILES ) . .It WS Ns Bq Ar n Force a new window to appear on workspace .Ar n . .It XTERM_FONTADJ Adjust .Xr xterm 1 fonts when resizing. .El .Pp Custom quirks in the configuration file are specified as follows: .Pp .Dl quirk Ns Bo Ar class Ns Bo : Ns Ar instance Ns Bo : Ns Ar name Bc Bc Bc = Ar quirk Op + Ar quirk ... .Pp .Ar class , .Ar instance (optional) and .Ar name (optional) are patterns used to determine which window(s) the quirk(s) apply to and .Ar quirk is one of the quirks from the list above. .Pp Note that patterns are interpreted as POSIX Extended Regular Expressions. Any ':', '[' or ']' must be escaped with '\\'. See .Xr regex 7 for more information on POSIX Extended Regular Expressions. .Pp For example: .Bd -literal -offset indent quirk[MPlayer] = FLOAT + FULLSCREEN + FOCUSPREV # Float all windows having a \ class of 'MPlayer' quirk[.*] = FLOAT # Float all windows by default. quirk[.*:.*:.*] = FLOAT # Same as above. quirk[Firefox:Navigator] = FLOAT # Float all Firefox browser windows. quirk[::Console] = FLOAT # Float windows with WM_CLASS not set and a \ window name of 'Console'. quirk[\\[0-9\\].*:.*:\\[\\[\\:alnum\\:\\]\\]*] = FLOAT # Float windows with WM_CLASS \ class beginning with a number, any WM_CLASS instance and a \ _NET_WM_NAME/WM_NAME either blank or containing alphanumeric characters without spaces. quirk[pcb:pcb] = NONE # remove existing quirk .Ed .Pp You can obtain .Ar class , .Ar instance and .Ar name by running .Xr xprop 1 and then clicking on the desired window. In the following example the main window of Firefox was clicked: .Bd -literal -offset indent $ xprop | grep \-E "^(WM_CLASS|_NET_WM_NAME|WM_NAME)" WM_CLASS(STRING) = "Navigator", "Firefox" WM_NAME(STRING) = "spectrwm - ConformalOpenSource" _NET_WM_NAME(UTF8_STRING) = "spectrwm - ConformalOpenSource" .Ed .Pp Note that .Xr xprop 1 displays WM_CLASS as: .Bd -literal -offset indent WM_CLASS(STRING) = "", "" .Ed .Pp In the example above the quirk entry would be: .Bd -literal -offset indent quirk[Firefox:Navigator] = FLOAT .Ed .Pp .Nm also automatically assigns quirks to windows based on the value of the window's _NET_WM_WINDOW_TYPE property as follows: .Pp .Bl -tag -width "_NET_WM_WINDOW_TYPE_TOOLBARXXX" -offset indent -compact .It _NET_WM_WINDOW_TYPE_DOCK FLOAT + ANYWHERE .It _NET_WM_WINDOW_TYPE_TOOLBAR FLOAT + ANYWHERE .It _NET_WM_WINDOW_TYPE_UTILITY FLOAT + ANYWHERE .It _NET_WM_WINDOW_TYPE_SPLASH FLOAT .It _NET_WM_WINDOW_TYPE_DIALOG FLOAT .El .Pp In all other cases, no automatic quirks are assigned to the window. Quirks specified in the configuration file override the automatic quirks. .Sh EWMH .Nm partially implements the Extended Window Manager Hints (EWMH) specification. This enables controlling windows as well as .Nm itself from external scripts and programs. This is achieved by .Nm responding to certain ClientMessage events. From the terminal these events can be conveniently sent using tools such as .Xr wmctrl 1 and .Xr xdotool 1 . For the actual format of these ClientMessage events, see the EWMH specification. .Pp The id of the currently focused window is stored in the _NET_ACTIVE_WINDOW property of the root window. This can be used for example to retrieve the title of the currently active window with .Xr xprop 1 and .Xr grep 1 : .Bd -literal -offset indent $ WINDOWID=`xprop \-root _NET_ACTIVE_WINDOW | grep \-o "0x.*"` $ xprop \-id $WINDOWID _NET_WM_NAME | grep \-o "\\".*\\"" .Ed .Pp A window can be focused by sending a _NET_ACTIVE_WINDOW client message to the root window. For example, using .Xr wmctrl 1 to send the message (assuming 0x4a0000b is the id of the window to be focused): .Bd -literal -offset indent $ wmctrl \-i \-a 0x4a0000b .Ed .Pp Windows can be closed by sending a _NET_CLOSE_WINDOW client message to the root window. For example, using .Xr wmctrl 1 to send the message (assuming 0x4a0000b is the id of the window to be closed): .Bd -literal -offset indent $ wmctrl \-i \-c 0x4a0000b .Ed .Pp Windows can be floated and un-floated by adding or removing the _NET_WM_STATE_ABOVE atom from the _NET_WM_STATE property of the window. This can be achieved by sending a _NET_WM_STATE client message to the root window. For example, the following toggles the floating state of a window using .Xr wmctrl 1 to send the message (assuming 0x4a0000b is the id of the window to be floated or un-floated): .Bd -literal -offset indent $ wmctrl \-i \-r 0x4a0000b \-b toggle,_NET_WM_STATE_ABOVE .Ed .Pp Windows can also be iconified and un-iconified by substituting _NET_WM_STATE_HIDDEN for _NET_WM_STATE_ABOVE in the previous example: .Bd -literal -offset indent $ wmctrl \-i \-r 0x4a0000b \-b toggle,_NET_WM_STATE_HIDDEN .Ed .Pp Floating windows can also be resized and moved by sending a _NET_MOVERESIZE_WINDOW client message to the root window. For example, using .Xr wmctrl 1 to send the message (assuming 0x4a0000b is the id of the window to be resize/moved): .Bd -literal -offset indent $ wmctrl \-i \-r 0x4a0000b \-e 0,100,50,640,480 .Ed .Pp This moves the window to (100,50) and resizes it to 640x480. .Pp Any _NET_MOVERESIZE_WINDOW events received for stacked windows are ignored. .Sh SIGNALS Sending .Nm a HUP signal will restart it. .Sh FILES .Bl -tag -width "/etc/spectrwm.confXXX" -compact .It Pa ~/.spectrwm.conf .Nm user specific settings. .It Pa /etc/spectrwm.conf .Nm global settings. .El .Sh HISTORY .Nm was inspired by xmonad & dwm. .Sh AUTHORS .An -nosplit .Nm was written by: .Pp .Bl -tag -width "Ryan Thomas McBride Aq mcbride@countersiege.com " -offset \ indent -compact .It Cm Marco Peereboom Aq marco@peereboom.us .It Cm Ryan Thomas McBride Aq mcbride@countersiege.com .It Cm Darrin Chandler Aq dwchandler@stilyagin.com .It Cm Pierre-Yves Ritschard Aq pyr@spootnik.org .It Cm Tuukka Kataja Aq stuge@xor.fi .It Cm Jason L. Wright Aq jason@thought.net .It Cm Reginald Kennedy Aq rk@rejii.com .It Cm Lawrence Teo Aq lteo@lteo.net .It Cm Tiago Cunha Aq tcunha@gmx.com .It Cm David Hill Aq dhill@mindcry.org .El spectrwm-SPECTRWM_3_1_0/spectrwm.c000066400000000000000000011372211316475426700171030ustar00rootroot00000000000000/* * Copyright (c) 2009-2015 Marco Peereboom * Copyright (c) 2009-2011 Ryan McBride * Copyright (c) 2009 Darrin Chandler * Copyright (c) 2009 Pierre-Yves Ritschard * Copyright (c) 2010 Tuukka Kataja * Copyright (c) 2011 Jason L. Wright * Copyright (c) 2011-2017 Reginald Kennedy * Copyright (c) 2011-2012 Lawrence Teo * Copyright (c) 2011-2012 Tiago Cunha * Copyright (c) 2012-2015 David Hill * Copyright (c) 2014-2015 Yuri D'Elia * * 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. */ /* kernel includes */ #include #include #include #include #ifdef __OSX__ #include "queue.h" #else #include #endif #include #include #if defined(__linux__) #include "tree.h" #elif defined(__OpenBSD__) #include #elif defined(__FreeBSD__) #include #else #include "tree.h" #endif /* /usr/includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* local includes */ #include "version.h" #ifdef __OSX__ #include #endif #ifdef SPECTRWM_BUILDSTR static const char *buildstr = SPECTRWM_BUILDSTR; #else static const char *buildstr = SPECTRWM_VERSION; #endif #if !defined(__CYGWIN__) /* cygwin chokes on randr stuff */ # if RANDR_MAJOR < 1 # error RandR versions less than 1.0 are not supported #endif # if RANDR_MAJOR >= 1 # if RANDR_MINOR >= 2 # define SWM_XRR_HAS_CRTC # endif # endif #endif /* __CYGWIN__ */ #ifndef XCB_ICCCM_NUM_WM_HINTS_ELEMENTS #define XCB_ICCCM_SIZE_HINT_P_MIN_SIZE XCB_SIZE_HINT_P_MIN_SIZE #define XCB_ICCCM_SIZE_HINT_P_MAX_SIZE XCB_SIZE_HINT_P_MAX_SIZE #define XCB_ICCCM_SIZE_HINT_P_RESIZE_INC XCB_SIZE_HINT_P_RESIZE_INC #define XCB_ICCCM_WM_HINT_INPUT XCB_WM_HINT_INPUT #define XCB_ICCCM_WM_HINT_X_URGENCY XCB_WM_HINT_X_URGENCY #define XCB_ICCCM_WM_STATE_ICONIC XCB_WM_STATE_ICONIC #define XCB_ICCCM_WM_STATE_WITHDRAWN XCB_WM_STATE_WITHDRAWN #define XCB_ICCCM_WM_STATE_NORMAL XCB_WM_STATE_NORMAL #define xcb_icccm_get_text_property_reply_t xcb_get_text_property_reply_t #define xcb_icccm_get_text_property_reply_wipe xcb_get_text_property_reply_wipe #define xcb_icccm_get_wm_class xcb_get_wm_class #define xcb_icccm_get_wm_class_reply xcb_get_wm_class_reply #define xcb_icccm_get_wm_class_reply_t xcb_get_wm_class_reply_t #define xcb_icccm_get_wm_class_reply_wipe xcb_get_wm_class_reply_wipe #define xcb_icccm_get_wm_hints xcb_get_wm_hints #define xcb_icccm_wm_hints_get_urgency xcb_wm_hints_get_urgency #define xcb_icccm_get_wm_hints_reply xcb_get_wm_hints_reply #define xcb_icccm_get_wm_name xcb_get_wm_name #define xcb_icccm_get_wm_name_reply xcb_get_wm_name_reply #define xcb_icccm_get_wm_normal_hints xcb_get_wm_normal_hints #define xcb_icccm_get_wm_normal_hints_reply xcb_get_wm_normal_hints_reply #define xcb_icccm_get_wm_protocols xcb_get_wm_protocols #define xcb_icccm_get_wm_protocols_reply xcb_get_wm_protocols_reply #define xcb_icccm_get_wm_protocols_reply_t xcb_get_wm_protocols_reply_t #define xcb_icccm_get_wm_protocols_reply_wipe xcb_get_wm_protocols_reply_wipe #define xcb_icccm_get_wm_transient_for xcb_get_wm_transient_for #define xcb_icccm_get_wm_transient_for_reply xcb_get_wm_transient_for_reply #define xcb_icccm_wm_hints_t xcb_wm_hints_t #endif /*#define SWM_DEBUG*/ #ifdef SWM_DEBUG #define DPRINTF(x...) do { \ if (swm_debug) \ fprintf(stderr, x); \ } while (0) #define DNPRINTF(n,x...) do { \ if (swm_debug & n) { \ fprintf(stderr, "%ld ", (long)(time(NULL) - time_started)); \ fprintf(stderr, x); \ } \ } while (0) #define SWM_D_MISC 0x0001 #define SWM_D_EVENT 0x0002 #define SWM_D_WS 0x0004 #define SWM_D_FOCUS 0x0008 #define SWM_D_MOVE 0x0010 #define SWM_D_STACK 0x0020 #define SWM_D_MOUSE 0x0040 #define SWM_D_PROP 0x0080 #define SWM_D_CLASS 0x0100 #define SWM_D_KEY 0x0200 #define SWM_D_QUIRK 0x0400 #define SWM_D_SPAWN 0x0800 #define SWM_D_EVENTQ 0x1000 #define SWM_D_CONF 0x2000 #define SWM_D_BAR 0x4000 #define SWM_D_INIT 0x8000 uint32_t swm_debug = 0 | SWM_D_MISC | SWM_D_EVENT | SWM_D_WS | SWM_D_FOCUS | SWM_D_MOVE | SWM_D_STACK | SWM_D_MOUSE | SWM_D_PROP | SWM_D_CLASS | SWM_D_KEY | SWM_D_QUIRK | SWM_D_SPAWN | SWM_D_EVENTQ | SWM_D_CONF | SWM_D_BAR | SWM_D_INIT ; #else #define DPRINTF(x...) #define DNPRINTF(n,x...) #endif #define SWM_EWMH_ACTION_COUNT_MAX (8) #define EWMH_F_FULLSCREEN (0x001) #define EWMH_F_ABOVE (0x002) #define EWMH_F_HIDDEN (0x004) #define EWMH_F_MAXIMIZED_VERT (0x008) #define EWMH_F_MAXIMIZED_HORZ (0x010) #define EWMH_F_SKIP_PAGER (0x020) #define EWMH_F_SKIP_TASKBAR (0x040) #define SWM_F_MANUAL (0x080) #define EWMH_F_MAXIMIZED (EWMH_F_MAXIMIZED_VERT | EWMH_F_MAXIMIZED_HORZ) /* convert 8-bit to 16-bit */ #define RGB_8_TO_16(col) (((col) << 8) + (col)) #define PIXEL_TO_XRENDERCOLOR(px, xrc) \ xrc.red = RGB_8_TO_16((px) >> 16 & 0xff); \ xrc.green = RGB_8_TO_16((px) >> 8 & 0xff); \ xrc.blue = RGB_8_TO_16((px) & 0xff); \ xrc.alpha = 0xffff; #define LENGTH(x) (int)(sizeof (x) / sizeof (x)[0]) #define MODKEY XCB_MOD_MASK_1 #define ANYMOD XCB_MOD_MASK_ANY #define CLEANMASK(mask) ((mask) & (XCB_KEY_BUT_MASK_SHIFT | \ XCB_KEY_BUT_MASK_CONTROL | XCB_KEY_BUT_MASK_MOD_1 | \ XCB_KEY_BUT_MASK_MOD_2 | XCB_KEY_BUT_MASK_MOD_3 | \ XCB_KEY_BUT_MASK_MOD_4 | XCB_KEY_BUT_MASK_MOD_5) & ~(numlockmask)) #define BUTTONMASK (XCB_EVENT_MASK_BUTTON_PRESS | \ XCB_EVENT_MASK_BUTTON_RELEASE) #define MOUSEMASK (BUTTONMASK|XCB_EVENT_MASK_POINTER_MOTION) #define SWM_PROPLEN (16) #define SWM_FUNCNAME_LEN (32) #define SWM_QUIRK_LEN (64) #define X(r) ((r)->g.x) #define Y(r) ((r)->g.y) #define WIDTH(r) ((r)->g.w) #define HEIGHT(r) ((r)->g.h) #define MAX_X(r) ((r)->g.x + (r)->g.w) #define MAX_Y(r) ((r)->g.y + (r)->g.h) #define SH_MIN(w) ((w)->sh.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) #define SH_MIN_W(w) ((w)->sh.min_width) #define SH_MIN_H(w) ((w)->sh.min_height) #define SH_MAX(w) ((w)->sh.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE) #define SH_MAX_W(w) ((w)->sh.max_width) #define SH_MAX_H(w) ((w)->sh.max_height) #define SH_INC(w) ((w)->sh.flags & XCB_ICCCM_SIZE_HINT_P_RESIZE_INC) #define SH_INC_W(w) ((w)->sh.width_inc) #define SH_INC_H(w) ((w)->sh.height_inc) #define SWM_MAX_FONT_STEPS (3) #define WINID(w) ((w) ? (w)->id : XCB_WINDOW_NONE) #define ACCEPTS_FOCUS(w) (!((w)->hints.flags & XCB_ICCCM_WM_HINT_INPUT) \ || ((w)->hints.input)) #define WS_FOCUSED(ws) ((ws)->r && (ws)->r->s->r_focus == (ws)->r) #define YESNO(x) ((x) ? "yes" : "no") #define ICONIC(w) ((w)->ewmh_flags & EWMH_F_HIDDEN) #define ABOVE(w) ((w)->ewmh_flags & EWMH_F_ABOVE) #define FULLSCREEN(w) ((w)->ewmh_flags & EWMH_F_FULLSCREEN) #define MAXIMIZED_VERT(w) ((w)->ewmh_flags & EWMH_F_MAXIMIZED_VERT) #define MAXIMIZED_HORZ(w) ((w)->ewmh_flags & EWMH_F_MAXIMIZED_HORZ) #define MAXIMIZED(w) (MAXIMIZED_VERT(w) || MAXIMIZED_HORZ(w)) #define MANUAL(w) ((w)->ewmh_flags & SWM_F_MANUAL) #define TRANS(w) ((w)->transient != XCB_WINDOW_NONE) #define FLOATING(w) (ABOVE(w) || TRANS(w) || FULLSCREEN(w) || \ MAXIMIZED(w)) /* Constrain Window flags */ #define SWM_CW_RESIZABLE (0x01) #define SWM_CW_SOFTBOUNDARY (0x02) #define SWM_CW_HARDBOUNDARY (0x04) #define SWM_CW_RIGHT (0x10) #define SWM_CW_LEFT (0x20) #define SWM_CW_BOTTOM (0x40) #define SWM_CW_TOP (0x80) #define SWM_CW_ALLSIDES (0xf0) #define SWM_FOCUS_DEFAULT (0) #define SWM_FOCUS_FOLLOW (1) #define SWM_FOCUS_MANUAL (2) #define SWM_CK_ALL (0xf) #define SWM_CK_FOCUS (0x1) #define SWM_CK_POINTER (0x2) #define SWM_CK_FALLBACK (0x4) #define SWM_CK_REGION (0x8) #define SWM_CONF_DEFAULT (0) #define SWM_CONF_KEYMAPPING (1) #ifndef SWM_LIB #define SWM_LIB "/usr/local/lib/libswmhack.so" #endif char **start_argv; xcb_atom_t a_state; xcb_atom_t a_prot; xcb_atom_t a_delete; xcb_atom_t a_net_frame_extents; xcb_atom_t a_net_wm_check; xcb_atom_t a_net_supported; xcb_atom_t a_takefocus; xcb_atom_t a_utf8_string; xcb_atom_t a_swm_ws; volatile sig_atomic_t running = 1; volatile sig_atomic_t restart_wm = 0; xcb_timestamp_t last_event_time = 0; int outputs = 0; bool randr_support; int randr_eventbase; unsigned int numlockmask = 0; Display *display; xcb_connection_t *conn; xcb_key_symbols_t *syms; int boundary_width = 50; bool cycle_empty = false; bool cycle_visible = false; int term_width = 0; int font_adjusted = 0; uint16_t mod_key = MODKEY; bool warp_focus = false; bool warp_pointer = false; bool workspace_clamp = false; /* dmenu search */ struct swm_region *search_r; int select_list_pipe[2]; int select_resp_pipe[2]; pid_t searchpid; volatile sig_atomic_t search_resp; int search_resp_action; struct search_window { TAILQ_ENTRY(search_window) entry; int idx; struct ws_win *win; xcb_gcontext_t gc; xcb_window_t indicator; }; TAILQ_HEAD(search_winlist, search_window) search_wl = TAILQ_HEAD_INITIALIZER(search_wl); /* search actions */ enum { SWM_SEARCH_NONE, SWM_SEARCH_UNICONIFY, SWM_SEARCH_NAME_WORKSPACE, SWM_SEARCH_SEARCH_WORKSPACE, SWM_SEARCH_SEARCH_WINDOW }; #define SWM_STACK_TOP (0) #define SWM_STACK_BOTTOM (1) #define SWM_STACK_ABOVE (2) #define SWM_STACK_BELOW (3) /* dialog windows */ double dialog_ratio = 0.6; /* status bar */ #define SWM_BAR_MAX (256) #define SWM_BAR_JUSTIFY_LEFT (0) #define SWM_BAR_JUSTIFY_CENTER (1) #define SWM_BAR_JUSTIFY_RIGHT (2) #define SWM_BAR_OFFSET (4) #define SWM_BAR_FONTS "-*-terminus-medium-*-*-*-12-*-*-*-*-*-*-*," \ "-*-profont-*-*-*-*-12-*-*-*-*-*-*-*," \ "-*-times-medium-r-*-*-12-*-*-*-*-*-*-*," \ "-misc-fixed-medium-r-*-*-12-*-*-*-*-*-*-*," \ "-*-*-*-r-*-*-*-*-*-*-*-*-*-*" #ifdef X_HAVE_UTF8_STRING #define DRAWSTRING(x...) Xutf8DrawString(x) #else #define DRAWSTRING(x...) XmbDrawString(x) #endif char *bar_argv[] = { NULL, NULL }; int bar_pipe[2]; char bar_ext[SWM_BAR_MAX]; char bar_ext_buf[SWM_BAR_MAX]; char bar_vertext[SWM_BAR_MAX]; bool bar_version = false; bool bar_enabled = true; int bar_border_width = 1; bool bar_at_bottom = false; bool bar_extra = false; int bar_height = 0; int bar_justify = SWM_BAR_JUSTIFY_LEFT; char *bar_format = NULL; bool stack_enabled = true; bool clock_enabled = true; bool iconic_enabled = false; bool maximize_hide_bar = false; bool urgent_enabled = false; bool urgent_collapse = false; char *clock_format = NULL; bool window_class_enabled = false; bool window_instance_enabled = false; bool window_name_enabled = false; int focus_mode = SWM_FOCUS_DEFAULT; int focus_close = SWM_STACK_BELOW; bool focus_close_wrap = true; int focus_default = SWM_STACK_TOP; int spawn_position = SWM_STACK_TOP; bool disable_border = false; int border_width = 1; int region_padding = 0; int tile_gap = 0; bool verbose_layout = false; #ifdef SWM_DEBUG bool debug_enabled; time_t time_started; #endif pid_t bar_pid; XFontSet bar_fs = NULL; XFontSetExtents *bar_fs_extents; XftFont *bar_font = NULL; bool bar_font_legacy = true; char *bar_fonts = NULL; XftColor bar_font_color; XftColor search_font_color; char *startup_exception = NULL; unsigned int nr_exceptions = 0; /* layout manager data */ struct swm_geometry { int x; int y; int w; int h; }; struct swm_screen; struct workspace; struct swm_bar { xcb_window_t id; xcb_pixmap_t buffer; struct swm_geometry g; struct swm_region *r; /* Associated region. */ }; /* virtual "screens" */ struct swm_region { TAILQ_ENTRY(swm_region) entry; xcb_window_t id; struct swm_geometry g; struct workspace *ws; /* current workspace on this region */ struct workspace *ws_prior; /* prior workspace on this region */ struct swm_screen *s; /* screen idx */ struct swm_bar *bar; }; TAILQ_HEAD(swm_region_list, swm_region); enum { SWM_WIN_STATE_REPARENTING, SWM_WIN_STATE_REPARENTED, SWM_WIN_STATE_UNPARENTING, SWM_WIN_STATE_UNPARENTED, }; struct ws_win { TAILQ_ENTRY(ws_win) entry; TAILQ_ENTRY(ws_win) stack_entry; xcb_window_t id; xcb_window_t frame; xcb_window_t transient; struct ws_win *focus_child; /* focus on child transient */ struct swm_geometry g; /* current geometry */ struct swm_geometry g_prev; /* prev configured geometry */ struct swm_geometry g_float; /* region coordinates */ bool g_floatvalid; /* g_float geometry validity */ bool mapped; uint8_t state; bool bordered; uint32_t ewmh_flags; int font_size_boundary[SWM_MAX_FONT_STEPS]; int font_steps; int last_inc; bool can_delete; bool take_focus; bool java; uint32_t quirks; struct workspace *ws; /* always valid */ struct swm_screen *s; /* always valid, never changes */ xcb_size_hints_t sh; xcb_icccm_get_wm_class_reply_t ch; xcb_icccm_wm_hints_t hints; #ifdef SWM_DEBUG xcb_window_t debug; #endif }; TAILQ_HEAD(ws_win_list, ws_win); TAILQ_HEAD(ws_win_stack, ws_win); /* pid goo */ struct pid_e { TAILQ_ENTRY(pid_e) entry; pid_t pid; int ws; }; TAILQ_HEAD(pid_list, pid_e) pidlist = TAILQ_HEAD_INITIALIZER(pidlist); /* layout handlers */ void stack(struct swm_region *); void vertical_config(struct workspace *, int); void vertical_stack(struct workspace *, struct swm_geometry *); void horizontal_config(struct workspace *, int); void horizontal_stack(struct workspace *, struct swm_geometry *); void max_stack(struct workspace *, struct swm_geometry *); void plain_stacker(struct workspace *); void fancy_stacker(struct workspace *); struct layout { void (*l_stack)(struct workspace *, struct swm_geometry *); void (*l_config)(struct workspace *, int); uint32_t flags; #define SWM_L_FOCUSPREV (1<<0) #define SWM_L_MAPONFOCUS (1<<1) void (*l_string)(struct workspace *); } layouts[] = { /* stack, configure */ { vertical_stack, vertical_config, 0, plain_stacker }, { horizontal_stack, horizontal_config, 0, plain_stacker }, { max_stack, NULL, SWM_L_MAPONFOCUS | SWM_L_FOCUSPREV, plain_stacker }, { NULL, NULL, 0, NULL }, }; /* position of max_stack mode in the layouts array, index into layouts! */ #define SWM_V_STACK (0) #define SWM_H_STACK (1) #define SWM_MAX_STACK (2) #define SWM_H_SLICE (32) #define SWM_V_SLICE (32) /* define work spaces */ struct workspace { int idx; /* workspace index */ char *name; /* workspace name */ bool always_raise; /* raise windows on focus */ bool bar_enabled; /* bar visibility */ struct layout *cur_layout; /* current layout handlers */ struct ws_win *focus; /* may be NULL */ struct ws_win *focus_prev; /* may be NULL */ struct ws_win *focus_pending; /* may be NULL */ struct ws_win *focus_raise; /* may be NULL */ struct swm_region *r; /* may be NULL */ struct swm_region *old_r; /* may be NULL */ struct ws_win_list winlist; /* list of windows in ws */ struct ws_win_list unmanagedlist; /* list of dead windows in ws */ struct ws_win_stack stack; /* stacking order */ int state; /* mapping state */ char stacker[10]; /* display stacker and layout */ /* stacker state */ struct { int horizontal_msize; int horizontal_mwin; int horizontal_stacks; bool horizontal_flip; int vertical_msize; int vertical_mwin; int vertical_stacks; bool vertical_flip; } l_state; }; enum { SWM_WS_STATE_HIDDEN, SWM_WS_STATE_MAPPING, SWM_WS_STATE_MAPPED, }; enum { SWM_S_COLOR_BAR, SWM_S_COLOR_BAR_SELECTED, SWM_S_COLOR_BAR_BORDER, SWM_S_COLOR_BAR_BORDER_UNFOCUS, SWM_S_COLOR_BAR_FONT, SWM_S_COLOR_BAR_FONT_SELECTED, SWM_S_COLOR_FOCUS, SWM_S_COLOR_FOCUS_MAXIMIZED, SWM_S_COLOR_UNFOCUS, SWM_S_COLOR_UNFOCUS_MAXIMIZED, SWM_S_COLOR_MAX }; /* physical screen mapping */ #define SWM_WS_MAX (22) /* hard limit */ int workspace_limit = 10; /* soft limit */ struct swm_screen { int idx; /* screen index */ struct swm_region_list rl; /* list of regions on this screen */ struct swm_region_list orl; /* list of old regions */ xcb_window_t root; struct workspace ws[SWM_WS_MAX]; struct swm_region *r_focus; /* colors */ struct { uint32_t pixel; char *name; int manual; } c[SWM_S_COLOR_MAX]; xcb_gcontext_t bar_gc; GC bar_gc_legacy; }; struct swm_screen *screens; /* args to functions */ union arg { int id; #define SWM_ARG_ID_FOCUSNEXT (0) #define SWM_ARG_ID_FOCUSPREV (1) #define SWM_ARG_ID_FOCUSMAIN (2) #define SWM_ARG_ID_FOCUSURGENT (3) #define SWM_ARG_ID_SWAPNEXT (10) #define SWM_ARG_ID_SWAPPREV (11) #define SWM_ARG_ID_SWAPMAIN (12) #define SWM_ARG_ID_MOVELAST (13) #define SWM_ARG_ID_MASTERSHRINK (20) #define SWM_ARG_ID_MASTERGROW (21) #define SWM_ARG_ID_MASTERADD (22) #define SWM_ARG_ID_MASTERDEL (23) #define SWM_ARG_ID_FLIPLAYOUT (24) #define SWM_ARG_ID_STACKRESET (30) #define SWM_ARG_ID_STACKINIT (31) #define SWM_ARG_ID_STACKBALANCE (32) #define SWM_ARG_ID_CYCLEWS_UP (40) #define SWM_ARG_ID_CYCLEWS_DOWN (41) #define SWM_ARG_ID_CYCLERG_UP (42) #define SWM_ARG_ID_CYCLERG_DOWN (43) #define SWM_ARG_ID_CYCLEWS_UP_ALL (44) #define SWM_ARG_ID_CYCLEWS_DOWN_ALL (45) #define SWM_ARG_ID_CYCLEWS_MOVE_UP (46) #define SWM_ARG_ID_CYCLEWS_MOVE_DOWN (47) #define SWM_ARG_ID_STACKINC (50) #define SWM_ARG_ID_STACKDEC (51) #define SWM_ARG_ID_DONTCENTER (70) #define SWM_ARG_ID_CENTER (71) #define SWM_ARG_ID_KILLWINDOW (80) #define SWM_ARG_ID_DELETEWINDOW (81) #define SWM_ARG_ID_WIDTHGROW (90) #define SWM_ARG_ID_WIDTHSHRINK (91) #define SWM_ARG_ID_HEIGHTGROW (92) #define SWM_ARG_ID_HEIGHTSHRINK (93) #define SWM_ARG_ID_MOVEUP (100) #define SWM_ARG_ID_MOVEDOWN (101) #define SWM_ARG_ID_MOVELEFT (102) #define SWM_ARG_ID_MOVERIGHT (103) #define SWM_ARG_ID_BAR_TOGGLE (110) #define SWM_ARG_ID_BAR_TOGGLE_WS (111) #define SWM_ARG_ID_CYCLERG_MOVE_UP (112) #define SWM_ARG_ID_CYCLERG_MOVE_DOWN (113) char **argv; }; /* quirks */ struct quirk { TAILQ_ENTRY(quirk) entry; char *class; /* WM_CLASS:class */ char *instance; /* WM_CLASS:instance */ char *name; /* WM_NAME */ regex_t regex_class; regex_t regex_instance; regex_t regex_name; uint32_t quirk; int ws; /* Initial workspace. */ #define SWM_Q_FLOAT (1<<0) /* Float this window. */ #define SWM_Q_TRANSSZ (1<<1) /* Transient window size too small. */ #define SWM_Q_ANYWHERE (1<<2) /* Don't position this window */ #define SWM_Q_XTERM_FONTADJ (1<<3) /* Adjust xterm fonts when resizing. */ #define SWM_Q_FULLSCREEN (1<<4) /* Remove border when fullscreen. */ #define SWM_Q_FOCUSPREV (1<<5) /* Focus on caller. */ #define SWM_Q_NOFOCUSONMAP (1<<6) /* Don't focus on window when mapped. */ #define SWM_Q_FOCUSONMAP_SINGLE (1<<7) /* Only focus if single win of type. */ #define SWM_Q_OBEYAPPFOCUSREQ (1<<8) /* Focus when applications ask. */ #define SWM_Q_IGNOREPID (1<<9) /* Ignore PID when determining ws. */ #define SWM_Q_IGNORESPAWNWS (1<<10) /* Ignore _SWM_WS when managing win. */ #define SWM_Q_NOFOCUSCYCLE (1<<11) /* Remove from normal focus cycle. */ #define SWM_Q_MINIMALBORDER (1<<12) /* No border when floating/unfocused. */ }; TAILQ_HEAD(quirk_list, quirk) quirks = TAILQ_HEAD_INITIALIZER(quirks); /* * Supported EWMH hints should be added to * both the enum and the ewmh array */ enum { _NET_ACTIVE_WINDOW, _NET_CLIENT_LIST, _NET_CLOSE_WINDOW, _NET_CURRENT_DESKTOP, _NET_DESKTOP_GEOMETRY, _NET_DESKTOP_NAMES, _NET_DESKTOP_VIEWPORT, _NET_MOVERESIZE_WINDOW, _NET_NUMBER_OF_DESKTOPS, _NET_REQUEST_FRAME_EXTENTS, _NET_RESTACK_WINDOW, _NET_WM_ACTION_ABOVE, _NET_WM_ACTION_CLOSE, _NET_WM_ACTION_FULLSCREEN, _NET_WM_ACTION_MOVE, _NET_WM_ACTION_RESIZE, _NET_WM_ALLOWED_ACTIONS, _NET_WM_DESKTOP, _NET_WM_FULL_PLACEMENT, _NET_WM_NAME, _NET_WM_STATE, _NET_WM_STATE_ABOVE, _NET_WM_STATE_FULLSCREEN, _NET_WM_STATE_HIDDEN, _NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_SKIP_PAGER, _NET_WM_STATE_SKIP_TASKBAR, _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_WINDOW_TYPE_DOCK, _NET_WM_WINDOW_TYPE_NORMAL, _NET_WM_WINDOW_TYPE_SPLASH, _NET_WM_WINDOW_TYPE_TOOLBAR, _NET_WM_WINDOW_TYPE_UTILITY, _SWM_WM_STATE_MANUAL, SWM_EWMH_HINT_MAX }; struct ewmh_hint { char *name; xcb_atom_t atom; } ewmh[SWM_EWMH_HINT_MAX] = { /* must be in same order as in the enum */ {"_NET_ACTIVE_WINDOW", XCB_ATOM_NONE}, {"_NET_CLIENT_LIST", XCB_ATOM_NONE}, {"_NET_CLOSE_WINDOW", XCB_ATOM_NONE}, {"_NET_CURRENT_DESKTOP", XCB_ATOM_NONE}, {"_NET_DESKTOP_GEOMETRY", XCB_ATOM_NONE}, {"_NET_DESKTOP_NAMES", XCB_ATOM_NONE}, {"_NET_DESKTOP_VIEWPORT", XCB_ATOM_NONE}, {"_NET_MOVERESIZE_WINDOW", XCB_ATOM_NONE}, {"_NET_NUMBER_OF_DESKTOPS", XCB_ATOM_NONE}, {"_NET_REQUEST_FRAME_EXTENTS", XCB_ATOM_NONE}, {"_NET_RESTACK_WINDOW", XCB_ATOM_NONE}, {"_NET_WM_ACTION_ABOVE", XCB_ATOM_NONE}, {"_NET_WM_ACTION_CLOSE", XCB_ATOM_NONE}, {"_NET_WM_ACTION_FULLSCREEN", XCB_ATOM_NONE}, {"_NET_WM_ACTION_MOVE", XCB_ATOM_NONE}, {"_NET_WM_ACTION_RESIZE", XCB_ATOM_NONE}, {"_NET_WM_ALLOWED_ACTIONS", XCB_ATOM_NONE}, {"_NET_WM_DESKTOP", XCB_ATOM_NONE}, {"_NET_WM_FULL_PLACEMENT", XCB_ATOM_NONE}, {"_NET_WM_NAME", XCB_ATOM_NONE}, {"_NET_WM_STATE", XCB_ATOM_NONE}, {"_NET_WM_STATE_ABOVE", XCB_ATOM_NONE}, {"_NET_WM_STATE_FULLSCREEN", XCB_ATOM_NONE}, {"_NET_WM_STATE_HIDDEN", XCB_ATOM_NONE}, {"_NET_WM_STATE_MAXIMIZED_VERT", XCB_ATOM_NONE}, {"_NET_WM_STATE_MAXIMIZED_HORZ", XCB_ATOM_NONE}, {"_NET_WM_STATE_SKIP_PAGER", XCB_ATOM_NONE}, {"_NET_WM_STATE_SKIP_TASKBAR", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE_DIALOG", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE_DOCK", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE_NORMAL", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE_SPLASH", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE_TOOLBAR", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE_UTILITY", XCB_ATOM_NONE}, {"_SWM_WM_STATE_MANUAL", XCB_ATOM_NONE}, }; /* EWMH source type */ enum { EWMH_SOURCE_TYPE_NONE = 0, EWMH_SOURCE_TYPE_NORMAL = 1, EWMH_SOURCE_TYPE_OTHER = 2, }; /* Cursors */ enum { XC_FLEUR, XC_LEFT_PTR, XC_BOTTOM_LEFT_CORNER, XC_BOTTOM_RIGHT_CORNER, XC_SIZING, XC_TOP_LEFT_CORNER, XC_TOP_RIGHT_CORNER, XC_MAX }; struct cursors { char *name; /* Name used by Xcursor .*/ uint8_t cf_char; /* cursorfont index. */ xcb_cursor_t cid; } cursors[XC_MAX] = { {"fleur", XC_fleur, XCB_CURSOR_NONE}, {"left_ptr", XC_left_ptr, XCB_CURSOR_NONE}, {"bottom_left_corner", XC_bottom_left_corner, XCB_CURSOR_NONE}, {"bottom_right_corner", XC_bottom_right_corner, XCB_CURSOR_NONE}, {"sizing", XC_sizing, XCB_CURSOR_NONE}, {"top_left_corner", XC_top_left_corner, XCB_CURSOR_NONE}, {"top_right_corner", XC_top_right_corner, XCB_CURSOR_NONE}, }; #define SWM_SPAWN_OPTIONAL 0x1 /* spawn */ struct spawn_prog { TAILQ_ENTRY(spawn_prog) entry; char *name; int argc; char **argv; int flags; }; TAILQ_HEAD(spawn_list, spawn_prog) spawns = TAILQ_HEAD_INITIALIZER(spawns); enum { FN_F_NOREPLAY = 0x1, }; /* User callable function IDs. */ enum actionid { FN_BAR_TOGGLE, FN_BAR_TOGGLE_WS, FN_BUTTON2, FN_CYCLE_LAYOUT, FN_FLIP_LAYOUT, FN_FLOAT_TOGGLE, FN_FOCUS, FN_FOCUS_MAIN, FN_FOCUS_NEXT, FN_FOCUS_PREV, FN_FOCUS_URGENT, FN_FULLSCREEN_TOGGLE, FN_MAXIMIZE_TOGGLE, FN_HEIGHT_GROW, FN_HEIGHT_SHRINK, FN_ICONIFY, FN_MASTER_SHRINK, FN_MASTER_GROW, FN_MASTER_ADD, FN_MASTER_DEL, FN_MOVE, FN_MOVE_DOWN, FN_MOVE_LEFT, FN_MOVE_RIGHT, FN_MOVE_UP, FN_MVRG_1, FN_MVRG_2, FN_MVRG_3, FN_MVRG_4, FN_MVRG_5, FN_MVRG_6, FN_MVRG_7, FN_MVRG_8, FN_MVRG_9, KF_MVRG_NEXT, KF_MVRG_PREV, FN_MVWS_1, FN_MVWS_2, FN_MVWS_3, FN_MVWS_4, FN_MVWS_5, FN_MVWS_6, FN_MVWS_7, FN_MVWS_8, FN_MVWS_9, FN_MVWS_10, FN_MVWS_11, FN_MVWS_12, FN_MVWS_13, FN_MVWS_14, FN_MVWS_15, FN_MVWS_16, FN_MVWS_17, FN_MVWS_18, FN_MVWS_19, FN_MVWS_20, FN_MVWS_21, FN_MVWS_22, FN_NAME_WORKSPACE, FN_QUIT, FN_RAISE, FN_RAISE_TOGGLE, FN_RESIZE, FN_RESIZE_CENTERED, FN_RESTART, FN_RG_1, FN_RG_2, FN_RG_3, FN_RG_4, FN_RG_5, FN_RG_6, FN_RG_7, FN_RG_8, FN_RG_9, FN_RG_MOVE_NEXT, FN_RG_MOVE_PREV, FN_RG_NEXT, FN_RG_PREV, FN_SCREEN_NEXT, FN_SCREEN_PREV, FN_SEARCH_WIN, FN_SEARCH_WORKSPACE, FN_SPAWN_CUSTOM, FN_STACK_BALANCE, FN_STACK_INC, FN_STACK_DEC, FN_STACK_RESET, FN_SWAP_MAIN, FN_SWAP_NEXT, FN_SWAP_PREV, FN_UNICONIFY, FN_VERSION, FN_WIDTH_GROW, FN_WIDTH_SHRINK, FN_WIND_DEL, FN_WIND_KILL, FN_WS_1, FN_WS_2, FN_WS_3, FN_WS_4, FN_WS_5, FN_WS_6, FN_WS_7, FN_WS_8, FN_WS_9, FN_WS_10, FN_WS_11, FN_WS_12, FN_WS_13, FN_WS_14, FN_WS_15, FN_WS_16, FN_WS_17, FN_WS_18, FN_WS_19, FN_WS_20, FN_WS_21, FN_WS_22, FN_WS_EMPTY, FN_WS_NEXT, FN_WS_NEXT_ALL, FN_WS_NEXT_MOVE, FN_WS_PREV, FN_WS_PREV_ALL, FN_WS_PREV_MOVE, FN_WS_PRIOR, /* SWM_DEBUG actions MUST be here: */ FN_DEBUG_TOGGLE, FN_DUMPWINS, /* ALWAYS last: */ FN_INVALID }; enum binding_type { KEYBIND, BTNBIND }; enum { BINDING_F_REPLAY = 0x1, }; struct binding { RB_ENTRY(binding) entry; uint16_t mod; /* Modifier Mask. */ enum binding_type type; /* Key or Button. */ uint32_t value; /* KeySym or Button Index. */ enum actionid action; /* Action Identifier. */ uint32_t flags; char *spawn_name; }; RB_HEAD(binding_tree, binding) bindings = RB_INITIALIZER(&bindings); /* function prototypes */ void adjust_font(struct ws_win *); char *argsep(char **); void bar_cleanup(struct swm_region *); void bar_extra_setup(void); void bar_extra_stop(void); int bar_extra_update(void); void bar_fmt(const char *, char *, struct swm_region *, size_t); void bar_fmt_expand(char *, size_t); void bar_draw(struct swm_bar *); void bar_print(struct swm_region *, const char *); void bar_print_legacy(struct swm_region *, const char *); void bar_replace(char *, char *, struct swm_region *, size_t); void bar_replace_pad(char *, int *, size_t); char *bar_replace_seq(char *, char *, struct swm_region *, size_t *, size_t); void bar_setup(struct swm_region *); void bar_toggle(struct binding *, struct swm_region *, union arg *); void bar_urgent(char *, size_t); void bar_window_class(char *, size_t, struct swm_region *); void bar_window_class_instance(char *, size_t, struct swm_region *); void bar_window_float(char *, size_t, struct swm_region *); void bar_window_instance(char *, size_t, struct swm_region *); void bar_window_name(char *, size_t, struct swm_region *); void bar_window_state(char *, size_t, struct swm_region *); void bar_workspace_name(char *, size_t, struct swm_region *); int binding_cmp(struct binding *, struct binding *); void binding_insert(uint16_t, enum binding_type, uint32_t, enum actionid, uint32_t, const char *); struct binding *binding_lookup(uint16_t, enum binding_type, uint32_t); void binding_remove(struct binding *); void buttonpress(xcb_button_press_event_t *); void buttonrelease(xcb_button_release_event_t *); void center_pointer(struct swm_region *); void check_conn(void); void clear_bindings(void); void clear_keybindings(void); int clear_maximized(struct workspace *); void clear_quirks(void); void clear_spawns(void); void clientmessage(xcb_client_message_event_t *); void client_msg(struct ws_win *, xcb_atom_t, xcb_timestamp_t); int conf_load(const char *, int); void configurenotify(xcb_configure_notify_event_t *); void configurerequest(xcb_configure_request_event_t *); void config_win(struct ws_win *, xcb_configure_request_event_t *); void constrain_window(struct ws_win *, struct swm_geometry *, int *); int count_win(struct workspace *, bool); void cursors_cleanup(void); void cursors_load(void); void custom_region(const char *); void cycle_layout(struct binding *, struct swm_region *, union arg *); void cyclerg(struct binding *, struct swm_region *, union arg *); void cyclews(struct binding *, struct swm_region *, union arg *); #ifdef SWM_DEBUG void debug_refresh(struct ws_win *); #endif void debug_toggle(struct binding *, struct swm_region *, union arg *); void destroynotify(xcb_destroy_notify_event_t *); void dumpwins(struct binding *, struct swm_region *, union arg *); void emptyws(struct binding *, struct swm_region *, union arg *); int enable_wm(void); void enternotify(xcb_enter_notify_event_t *); void event_drain(uint8_t); void event_error(xcb_generic_error_t *); void event_handle(xcb_generic_event_t *); void ewmh_apply_flags(struct ws_win *, uint32_t); void ewmh_autoquirk(struct ws_win *); void ewmh_get_desktop_names(void); void ewmh_get_wm_state(struct ws_win *); void ewmh_update_actions(struct ws_win *); void ewmh_update_client_list(void); void ewmh_update_current_desktop(void); void ewmh_update_desktop_names(void); void ewmh_update_desktops(void); void ewmh_change_wm_state(struct ws_win *, xcb_atom_t, long); void ewmh_update_wm_state(struct ws_win *); char *expand_tilde(const char *); void expose(xcb_expose_event_t *); void fake_keypress(struct ws_win *, xcb_keysym_t, uint16_t); struct swm_bar *find_bar(xcb_window_t); struct ws_win *find_frame_window(xcb_window_t); struct pid_e *find_pid(pid_t); struct swm_region *find_region(xcb_window_t); struct ws_win *find_unmanaged_window(xcb_window_t); struct ws_win *find_window(xcb_window_t); void floating_toggle(struct binding *, struct swm_region *, union arg *); void focus(struct binding *, struct swm_region *, union arg *); void focus_flush(void); void focus_pointer(struct binding *, struct swm_region *, union arg *); void focus_region(struct swm_region *); void focus_win(struct ws_win *); void focusin(xcb_focus_in_event_t *); #ifdef SWM_DEBUG void focusout(xcb_focus_out_event_t *); #endif void focusrg(struct binding *, struct swm_region *, union arg *); void fontset_init(void); void free_window(struct ws_win *); void fullscreen_toggle(struct binding *, struct swm_region *, union arg *); xcb_atom_t get_atom_from_string(const char *); #ifdef SWM_DEBUG char *get_atom_name(xcb_atom_t); #endif struct ws_win *get_focus_magic(struct ws_win *); struct ws_win *get_focus_prev(struct ws_win *); xcb_generic_event_t *get_next_event(bool); #ifdef SWM_DEBUG char *get_notify_detail_label(uint8_t); char *get_notify_mode_label(uint8_t); #endif struct ws_win *get_pointer_win(xcb_window_t); struct ws_win *get_region_focus(struct swm_region *); int get_region_index(struct swm_region *); xcb_screen_t *get_screen(int); int get_screen_count(void); #ifdef SWM_DEBUG char *get_source_type_label(uint32_t); char *get_stack_mode_name(uint8_t); char *get_state_mask_label(uint16_t); #endif int32_t get_swm_ws(xcb_window_t); bool get_urgent(struct ws_win *); #ifdef SWM_DEBUG char *get_win_input_model(struct ws_win *); #endif char *get_win_name(xcb_window_t); uint8_t get_win_state(xcb_window_t); void get_wm_protocols(struct ws_win *); int get_ws_idx(struct ws_win *); void grab_windows(void); void grabbuttons(void); void grabkeys(void); void iconify(struct binding *, struct swm_region *, union arg *); bool isxlfd(char *); bool keybindreleased(struct binding *, xcb_key_release_event_t *); void keypress(xcb_key_press_event_t *); void keyrelease(xcb_key_release_event_t *); bool keyrepeating(xcb_key_release_event_t *); void kill_bar_extra_atexit(void); void kill_refs(struct ws_win *); #ifdef SWM_DEBUG void leavenotify(xcb_leave_notify_event_t *); #endif void load_float_geom(struct ws_win *); void lower_window(struct ws_win *); struct ws_win *manage_window(xcb_window_t, int, bool); void map_window(struct ws_win *); void mapnotify(xcb_map_notify_event_t *); void mappingnotify(xcb_mapping_notify_event_t *); void maprequest(xcb_map_request_event_t *); void maximize_toggle(struct binding *, struct swm_region *, union arg *); void motionnotify(xcb_motion_notify_event_t *); void move(struct binding *, struct swm_region *, union arg *); void move_win(struct ws_win *, struct binding *, int); uint32_t name_to_pixel(int, const char *); void name_workspace(struct binding *, struct swm_region *, union arg *); void new_region(struct swm_screen *, int, int, int, int); int parse_rgb(const char *, uint16_t *, uint16_t *, uint16_t *); int parsebinding(const char *, uint16_t *, enum binding_type *, uint32_t *, uint32_t *); int parsequirks(const char *, uint32_t *, int *); void pressbutton(struct binding *, struct swm_region *, union arg *); void priorws(struct binding *, struct swm_region *, union arg *); #ifdef SWM_DEBUG void print_win_geom(xcb_window_t); #endif void propertynotify(xcb_property_notify_event_t *); void put_back_event(xcb_generic_event_t *); void quirk_free(struct quirk *); void quirk_insert(const char *, const char *, const char *, uint32_t, int); void quirk_remove(struct quirk *); void quirk_replace(struct quirk *, const char *, const char *, const char *, uint32_t, int); void quit(struct binding *, struct swm_region *, union arg *); void raise_focus(struct binding *, struct swm_region *, union arg *); void raise_toggle(struct binding *, struct swm_region *, union arg *); void raise_window(struct ws_win *); void region_containment(struct ws_win *, struct swm_region *, int); struct swm_region *region_under(struct swm_screen *, int, int); void regionize(struct ws_win *, int, int); void reparent_window(struct ws_win *); void reparentnotify(xcb_reparent_notify_event_t *); void resize(struct binding *, struct swm_region *, union arg *); void resize_win(struct ws_win *, struct binding *, int); void restart(struct binding *, struct swm_region *, union arg *); struct swm_region *root_to_region(xcb_window_t, int); void screenchange(xcb_randr_screen_change_notify_event_t *); void scan_randr(int); void search_do_resp(void); void search_resp_name_workspace(const char *, size_t); void search_resp_search_window(const char *); void search_resp_search_workspace(const char *); void search_resp_uniconify(const char *, size_t); void search_win(struct binding *, struct swm_region *, union arg *); void search_win_cleanup(void); void search_workspace(struct binding *, struct swm_region *, union arg *); void send_to_rg(struct binding *, struct swm_region *, union arg *); void send_to_rg_relative(struct binding *, struct swm_region *, union arg *); void send_to_ws(struct binding *, struct swm_region *, union arg *); void set_region(struct swm_region *); int setautorun(const char *, const char *, int); void setbinding(uint16_t, enum binding_type, uint32_t, enum actionid, uint32_t, const char *); int setconfbinding(const char *, const char *, int); int setconfcolor(const char *, const char *, int); int setconfmodkey(const char *, const char *, int); int setconfquirk(const char *, const char *, int); int setconfregion(const char *, const char *, int); int setconfspawn(const char *, const char *, int); int setconfvalue(const char *, const char *, int); int setkeymapping(const char *, const char *, int); int setlayout(const char *, const char *, int); void setquirk(const char *, const char *, const char *, uint32_t, int); void setscreencolor(const char *, int, int); void setspawn(const char *, const char *, int); void setup_btnbindings(void); void setup_ewmh(void); void setup_globals(void); void setup_keybindings(void); void setup_quirks(void); void setup_screens(void); void setup_spawn(void); void set_child_transient(struct ws_win *, xcb_window_t *); void set_win_state(struct ws_win *, uint8_t); void shutdown_cleanup(void); void sighdlr(int); void socket_setnonblock(int); void sort_windows(struct ws_win_list *); void spawn(int, union arg *, bool); void spawn_custom(struct swm_region *, union arg *, const char *); int spawn_expand(struct swm_region *, union arg *, const char *, char ***); void spawn_insert(const char *, const char *, int); struct spawn_prog *spawn_find(const char *); void spawn_remove(struct spawn_prog *); void spawn_replace(struct spawn_prog *, const char *, const char *, int); void spawn_select(struct swm_region *, union arg *, const char *, int *); void stack_config(struct binding *, struct swm_region *, union arg *); void stack_master(struct workspace *, struct swm_geometry *, int, bool); void store_float_geom(struct ws_win *); char *strdupsafe(const char *); void swapwin(struct binding *, struct swm_region *, union arg *); void switchws(struct binding *, struct swm_region *, union arg *); void teardown_ewmh(void); void unescape_selector(char *); void unfocus_win(struct ws_win *); void uniconify(struct binding *, struct swm_region *, union arg *); void unmanage_window(struct ws_win *); void unmap_all(void); void unmap_window(struct ws_win *); void unmapnotify(xcb_unmap_notify_event_t *); void unparent_window(struct ws_win *); void update_floater(struct ws_win *); void update_modkey(uint16_t); void update_win_stacking(struct ws_win *); void update_window(struct ws_win *); void draw_frame(struct ws_win *); void update_wm_state(struct ws_win *win); void updatenumlockmask(void); void validate_spawns(void); int validate_win(struct ws_win *); int validate_ws(struct workspace *); void version(struct binding *, struct swm_region *, union arg *); void win_to_ws(struct ws_win *, int, bool); pid_t window_get_pid(xcb_window_t); void wkill(struct binding *, struct swm_region *, union arg *); void update_ws_stack(struct workspace *); void xft_init(struct swm_region *); void _add_startup_exception(const char *, va_list); void add_startup_exception(const char *, ...); RB_PROTOTYPE(binding_tree, binding, entry, binding_cmp); #ifndef __clang_analyzer__ /* Suppress false warnings. */ RB_GENERATE(binding_tree, binding, entry, binding_cmp); #endif void cursors_load(void) { xcb_font_t cf = XCB_NONE; int i; for (i = 0; i < LENGTH(cursors); ++i) { /* try to load Xcursor first. */ cursors[i].cid = XcursorLibraryLoadCursor(display, cursors[i].name); /* fallback to cursorfont. */ if (cursors[i].cid == XCB_CURSOR_NONE) { if (cf == XCB_NONE) { cf = xcb_generate_id(conn); xcb_open_font(conn, cf, strlen("cursor"), "cursor"); } cursors[i].cid = xcb_generate_id(conn); xcb_create_glyph_cursor(conn, cursors[i].cid, cf, cf, cursors[i].cf_char, cursors[i].cf_char + 1, 0, 0, 0, 0xffff, 0xffff, 0xffff); } } if (cf != XCB_NONE) xcb_close_font(conn, cf); } void cursors_cleanup(void) { int i; for (i = 0; i < LENGTH(cursors); ++i) xcb_free_cursor(conn, cursors[i].cid); } char * expand_tilde(const char *s) { struct passwd *ppwd; int i; long max; char *user; const char *sc = s; char *result; if (s == NULL) errx(1, "expand_tilde: NULL string."); if (s[0] != '~') { result = strdup(sc); goto out; } ++s; if ((max = sysconf(_SC_LOGIN_NAME_MAX)) == -1) errx(1, "expand_tilde: sysconf"); if ((user = calloc(1, max + 1)) == NULL) errx(1, "expand_tilde: calloc"); for (i = 0; s[i] != '/' && s[i] != '\0'; ++i) user[i] = s[i]; user[i] = '\0'; s = &s[i]; ppwd = strlen(user) == 0 ? getpwuid(getuid()) : getpwnam(user); free(user); if (ppwd == NULL) result = strdup(sc); else if (asprintf(&result, "%s%s", ppwd->pw_dir, s) == -1) result = NULL; out: if (result == NULL) errx(1, "expand_tilde: failed to allocate memory."); return result; } int parse_rgb(const char *rgb, uint16_t *rr, uint16_t *gg, uint16_t *bb) { unsigned int tmpr, tmpg, tmpb; if (sscanf(rgb, "rgb:%x/%x/%x", &tmpr, &tmpg, &tmpb) != 3) return (-1); *rr = RGB_8_TO_16(tmpr); *gg = RGB_8_TO_16(tmpg); *bb = RGB_8_TO_16(tmpb); return (0); } xcb_screen_t * get_screen(int screen) { const xcb_setup_t *r; xcb_screen_iterator_t iter; if ((r = xcb_get_setup(conn)) == NULL) { DNPRINTF(SWM_D_MISC, "get_screen: xcb_get_setup\n"); check_conn(); } iter = xcb_setup_roots_iterator(r); for (; iter.rem; --screen, xcb_screen_next(&iter)) if (screen == 0) return (iter.data); return (NULL); } int get_screen_count(void) { const xcb_setup_t *r; if ((r = xcb_get_setup(conn)) == NULL) { DNPRINTF(SWM_D_MISC, "get_screen_count: xcb_get_setup\n"); check_conn(); } return xcb_setup_roots_length(r); } int get_region_index(struct swm_region *r) { struct swm_region *rr; int ridx = 0; if (r == NULL) return -1; TAILQ_FOREACH(rr, &r->s->rl, entry) { if (rr == r) break; ++ridx; } if (rr == NULL) return -1; return ridx; } void focus_flush(void) { if (focus_mode == SWM_FOCUS_DEFAULT) event_drain(XCB_ENTER_NOTIFY); else xcb_flush(conn); } xcb_atom_t get_atom_from_string(const char *str) { xcb_intern_atom_cookie_t c; xcb_intern_atom_reply_t *r; xcb_atom_t atom; c = xcb_intern_atom(conn, 0, strlen(str), str); r = xcb_intern_atom_reply(conn, c, NULL); if (r) { atom = r->atom; free(r); return (atom); } return (XCB_ATOM_NONE); } void get_wm_protocols(struct ws_win *win) { int i; xcb_icccm_get_wm_protocols_reply_t wpr; if (xcb_icccm_get_wm_protocols_reply(conn, xcb_icccm_get_wm_protocols(conn, win->id, a_prot), &wpr, NULL)) { for (i = 0; i < (int)wpr.atoms_len; i++) { if (wpr.atoms[i] == a_takefocus) win->take_focus = true; if (wpr.atoms[i] == a_delete) win->can_delete = true; } xcb_icccm_get_wm_protocols_reply_wipe(&wpr); } } void setup_ewmh(void) { xcb_window_t root, win; int i, j, num_screens; for (i = 0; i < LENGTH(ewmh); i++) ewmh[i].atom = get_atom_from_string(ewmh[i].name); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { root = screens[i].root; /* Set up _NET_SUPPORTING_WM_CHECK. */ win = xcb_generate_id(conn); xcb_create_window(conn, XCB_COPY_FROM_PARENT, win, root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, NULL); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, a_net_wm_check, XCB_ATOM_WINDOW, 32, 1, &win); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, a_net_wm_check, XCB_ATOM_WINDOW, 32, 1, &win); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, ewmh[_NET_WM_NAME].atom, a_utf8_string, 8, strlen("spectrwm"), "spectrwm"); /* Report supported atoms */ xcb_delete_property(conn, root, a_net_supported); for (j = 0; j < LENGTH(ewmh); j++) xcb_change_property(conn, XCB_PROP_MODE_APPEND, root, a_net_supported, XCB_ATOM_ATOM, 32, 1, &ewmh[j].atom); } ewmh_update_desktops(); ewmh_get_desktop_names(); } void teardown_ewmh(void) { int i, num_screens; xcb_window_t id; xcb_get_property_cookie_t pc; xcb_get_property_reply_t *pr; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { /* Get the support check window and destroy it */ pc = xcb_get_property(conn, 0, screens[i].root, a_net_wm_check, XCB_ATOM_WINDOW, 0, 1); pr = xcb_get_property_reply(conn, pc, NULL); if (pr == NULL) continue; if (pr->format == a_net_wm_check) { id = *((xcb_window_t *)xcb_get_property_value(pr)); xcb_destroy_window(conn, id); xcb_delete_property(conn, screens[i].root, a_net_wm_check); xcb_delete_property(conn, screens[i].root, a_net_supported); } free(pr); } } void ewmh_autoquirk(struct ws_win *win) { xcb_get_property_reply_t *r; xcb_get_property_cookie_t c; xcb_atom_t *type; int i, n; c = xcb_get_property(conn, 0, win->id, ewmh[_NET_WM_WINDOW_TYPE].atom, XCB_ATOM_ATOM, 0, UINT32_MAX); r = xcb_get_property_reply(conn, c, NULL); if (r == NULL) return; type = xcb_get_property_value(r); n = xcb_get_property_value_length(r) / sizeof(xcb_atom_t); for (i = 0; i < n; i++) { if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_NORMAL].atom) break; if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_DOCK].atom || type[i] == ewmh[_NET_WM_WINDOW_TYPE_TOOLBAR].atom || type[i] == ewmh[_NET_WM_WINDOW_TYPE_UTILITY].atom) { win->quirks = SWM_Q_FLOAT | SWM_Q_ANYWHERE; break; } if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_SPLASH].atom || type[i] == ewmh[_NET_WM_WINDOW_TYPE_DIALOG].atom) { win->quirks = SWM_Q_FLOAT; break; } } free(r); } void ewmh_update_actions(struct ws_win *win) { xcb_atom_t actions[SWM_EWMH_ACTION_COUNT_MAX]; int n = 0; if (win == NULL) return; actions[n++] = ewmh[_NET_WM_ACTION_CLOSE].atom; if (ABOVE(win)) { actions[n++] = ewmh[_NET_WM_ACTION_MOVE].atom; actions[n++] = ewmh[_NET_WM_ACTION_RESIZE].atom; actions[n++] = ewmh[_NET_WM_ACTION_ABOVE].atom; } xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id, ewmh[_NET_WM_ALLOWED_ACTIONS].atom, XCB_ATOM_ATOM, 32, 1, actions); } #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ #define _NET_WM_STATE_ADD 1 /* add/set property */ #define _NET_WM_STATE_TOGGLE 2 /* toggle property */ void ewmh_change_wm_state(struct ws_win *win, xcb_atom_t state, long action) { uint32_t flag = 0; uint32_t new_flags; #ifdef SWM_DEBUG char *name; name = get_atom_name(state); DNPRINTF(SWM_D_PROP, "ewmh_change_wm_state: win %#x, state: %s, " "action: %ld\n", WINID(win), name, action); free(name); #endif if (win == NULL) goto out; if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom) flag = EWMH_F_FULLSCREEN; else if (state == ewmh[_NET_WM_STATE_ABOVE].atom) flag = EWMH_F_ABOVE; else if (state == ewmh[_NET_WM_STATE_HIDDEN].atom) flag = EWMH_F_HIDDEN; else if (state == ewmh[_NET_WM_STATE_MAXIMIZED_VERT].atom || state == ewmh[_NET_WM_STATE_MAXIMIZED_HORZ].atom) flag = EWMH_F_MAXIMIZED; else if (state == ewmh[_SWM_WM_STATE_MANUAL].atom) flag = SWM_F_MANUAL; else if (state == ewmh[_NET_WM_STATE_SKIP_PAGER].atom) flag = EWMH_F_SKIP_PAGER; else if (state == ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom) flag = EWMH_F_SKIP_TASKBAR; /* Disallow unfloating transients. */ if (TRANS(win) && flag == EWMH_F_ABOVE) goto out; new_flags = win->ewmh_flags; switch (action) { case _NET_WM_STATE_REMOVE: new_flags &= ~flag; break; case _NET_WM_STATE_ADD: new_flags |= flag; break; case _NET_WM_STATE_TOGGLE: new_flags ^= flag; break; } ewmh_apply_flags(win, new_flags); out: DNPRINTF(SWM_D_PROP, "ewmh_change_wm_state: done.\n"); } void ewmh_apply_flags(struct ws_win *win, uint32_t pending) { struct workspace *ws; uint32_t changed; changed = win->ewmh_flags ^ pending; if (changed == 0) return; DNPRINTF(SWM_D_PROP, "ewmh_apply_flags: pending: %d\n", pending); win->ewmh_flags = pending; ws = win->ws; if (changed & EWMH_F_HIDDEN) { if (ICONIC(win)) { if (focus_mode != SWM_FOCUS_FOLLOW) ws->focus_pending = get_focus_prev(win); unfocus_win(win); unmap_window(win); } else { /* Reload floating geometry in case region changed. */ if (FLOATING(win)) load_float_geom(win); /* The window is no longer iconic, prepare focus. */ if (focus_mode != SWM_FOCUS_FOLLOW) ws->focus_pending = get_focus_magic(win); raise_window(win); } } if (changed & EWMH_F_ABOVE) { if (ws->cur_layout != &layouts[SWM_MAX_STACK]) { if (ABOVE(win)) load_float_geom(win); else if (!MAXIMIZED(win)) store_float_geom(win); win->ewmh_flags &= ~EWMH_F_MAXIMIZED; changed &= ~EWMH_F_MAXIMIZED; raise_window(win); } else { /* Revert. */ win->ewmh_flags ^= EWMH_F_ABOVE & pending; } } if (changed & EWMH_F_MAXIMIZED) { /* VERT and/or HORZ changed. */ if (ABOVE(win)) { if (!MAXIMIZED(win)) load_float_geom(win); else store_float_geom(win); } if (MAXIMIZED(win)) { if (focus_mode != SWM_FOCUS_FOLLOW && ws->cur_layout != &layouts[SWM_MAX_STACK]) { if (WS_FOCUSED(ws)) focus_win(win); else ws->focus_pending = win; } } draw_frame(win); raise_window(win); } if (changed & EWMH_F_FULLSCREEN) { if (FULLSCREEN(win)) { if (focus_mode != SWM_FOCUS_FOLLOW) { if (WS_FOCUSED(ws)) focus_win(win); else ws->focus_pending = win; } } else { load_float_geom(win); } win->ewmh_flags &= ~EWMH_F_MAXIMIZED; raise_window(win); } DNPRINTF(SWM_D_PROP, "ewmh_apply_flags: done.\n"); } void ewmh_update_wm_state(struct ws_win *win) { xcb_atom_t vals[SWM_EWMH_ACTION_COUNT_MAX]; int n = 0; if (ICONIC(win)) vals[n++] = ewmh[_NET_WM_STATE_HIDDEN].atom; if (FULLSCREEN(win)) vals[n++] = ewmh[_NET_WM_STATE_FULLSCREEN].atom; if (MAXIMIZED_VERT(win)) vals[n++] = ewmh[_NET_WM_STATE_MAXIMIZED_VERT].atom; if (MAXIMIZED_HORZ(win)) vals[n++] = ewmh[_NET_WM_STATE_MAXIMIZED_HORZ].atom; if (win->ewmh_flags & EWMH_F_SKIP_PAGER) vals[n++] = ewmh[_NET_WM_STATE_SKIP_PAGER].atom; if (win->ewmh_flags & EWMH_F_SKIP_TASKBAR) vals[n++] = ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom; if (win->ewmh_flags & EWMH_F_ABOVE) vals[n++] = ewmh[_NET_WM_STATE_ABOVE].atom; if (win->ewmh_flags & SWM_F_MANUAL) vals[n++] = ewmh[_SWM_WM_STATE_MANUAL].atom; if (n > 0) xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id, ewmh[_NET_WM_STATE].atom, XCB_ATOM_ATOM, 32, n, vals); else xcb_delete_property(conn, win->id, ewmh[_NET_WM_STATE].atom); } void ewmh_get_wm_state(struct ws_win *win) { xcb_atom_t *states; xcb_get_property_cookie_t c; xcb_get_property_reply_t *r; int i, n; if (win == NULL) return; win->ewmh_flags = 0; c = xcb_get_property(conn, 0, win->id, ewmh[_NET_WM_STATE].atom, XCB_ATOM_ATOM, 0, UINT32_MAX); r = xcb_get_property_reply(conn, c, NULL); if (r == NULL) return; states = xcb_get_property_value(r); n = xcb_get_property_value_length(r) / sizeof(xcb_atom_t); for (i = 0; i < n; i++) ewmh_change_wm_state(win, states[i], _NET_WM_STATE_ADD); free(r); } /* events */ #ifdef SWM_DEBUG void dumpwins(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *w; uint32_t state; xcb_get_window_attributes_cookie_t c; xcb_get_window_attributes_reply_t *wa; /* suppress unused warning since var is needed */ (void)bp; (void)args; if (r->ws == NULL) { DPRINTF("dumpwins: invalid workspace\n"); return; } DPRINTF("=== managed window list ws %02d ===\n", r->ws->idx); TAILQ_FOREACH(w, &r->ws->winlist, entry) { state = get_win_state(w->id); c = xcb_get_window_attributes(conn, w->id); wa = xcb_get_window_attributes_reply(conn, c, NULL); if (wa) { DPRINTF("win %#x (%#x), map_state: %d, state: %u, " "transient: %#x\n", w->frame, w->id, wa->map_state, state, w->transient); free(wa); } else DPRINTF("win %#x, failed xcb_get_window_attributes\n", w->id); } DPRINTF("=== stacking order (top down) === \n"); TAILQ_FOREACH(w, &r->ws->stack, stack_entry) { DPRINTF("win %#x (%#x), fs: %s, maximized: %s, above: %s, " "iconic: %s\n", w->frame, w->id, YESNO(FULLSCREEN(w)), YESNO(MAXIMIZED(w)), YESNO(ABOVE(w)), YESNO(ICONIC(w))); } DPRINTF("===== unmanaged window list =====\n"); TAILQ_FOREACH(w, &r->ws->unmanagedlist, entry) { state = get_win_state(w->id); c = xcb_get_window_attributes(conn, w->id); wa = xcb_get_window_attributes_reply(conn, c, NULL); if (wa) { DPRINTF("win %#x, map_state: %d, state: %u, " "transient: %#x\n", w->id, wa->map_state, state, w->transient); free(wa); } else DPRINTF("win %#x, failed xcb_get_window_attributes\n", w->id); } DPRINTF("=================================\n"); } void debug_toggle(struct binding *b, struct swm_region *r, union arg *s) { struct ws_win *win; int num_screens, i, j; /* Suppress warnings. */ (void)b; (void)r; (void)s; DNPRINTF(SWM_D_MISC, "debug_toggle\n"); debug_enabled = !debug_enabled; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) debug_refresh(win); xcb_flush(conn); } void debug_refresh(struct ws_win *win) { struct ws_win *w; XftDraw *draw; XGlyphInfo info; GC l_draw; XGCValues l_gcv; XRectangle l_ibox, l_lbox; xcb_rectangle_t rect; size_t len; uint32_t wc[4], mask, width, height, gcv[1]; int widx, sidx; char *s; xcb_screen_t *screen; if (debug_enabled) { /* Create debug window if it doesn't exist. */ if (win->debug == XCB_WINDOW_NONE) { if ((screen = get_screen(win->s->idx)) == NULL) errx(1, "ERROR: can't get screen %d.", win->s->idx); win->debug = xcb_generate_id(conn); wc[0] = win->s->c[SWM_S_COLOR_BAR].pixel; wc[1] = win->s->c[SWM_S_COLOR_BAR_BORDER].pixel; wc[2] = screen->default_colormap; xcb_create_window(conn, screen->root_depth, win->debug, win->frame, 0, 0, 10, 10, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP, wc); xcb_map_window(conn, win->debug); } /* Determine workspace window list index. */ widx = 0; TAILQ_FOREACH(w, &win->ws->winlist, entry) { ++widx; if (w == win) break; } /* Determine stacking index (top down). */ sidx = 0; TAILQ_FOREACH(w, &win->ws->stack, stack_entry) { ++sidx; if (w == win) break; } if (asprintf(&s, "%#x f:%#x wl:%d s:%d im:%s", win->id, win->frame, widx, sidx, get_win_input_model(win)) == -1) return; len = strlen(s); /* Update window to an appropriate dimension. */ if (bar_font_legacy) { XmbTextExtents(bar_fs, s, len, &l_ibox, &l_lbox); width = l_lbox.width + 4; height = bar_fs_extents->max_logical_extent.height + 4; } else { XftTextExtentsUtf8(display, bar_font, (FcChar8 *)s, len, &info); width = info.width + 4; height = bar_font->height + 4; } mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; if (win->bordered) wc[0] = wc[1] = border_width; else wc[0] = wc[1] = 0; wc[2] = width; wc[3] = height; xcb_configure_window(conn, win->debug, mask, wc); /* Draw a filled rectangle to 'clear' window. */ rect.x = 0; rect.y = 0; rect.width = width; rect.height = height; gcv[0] = win->s->c[SWM_S_COLOR_BAR].pixel; xcb_change_gc(conn, win->s->bar_gc, XCB_GC_FOREGROUND, gcv); xcb_poly_fill_rectangle(conn, win->debug, win->s->bar_gc, 1, &rect); /* Draw text. */ if (bar_font_legacy) { l_gcv.graphics_exposures = 0; l_draw = XCreateGC(display, win->debug, 0, &l_gcv); XSetForeground(display, l_draw, win->s->c[SWM_S_COLOR_BAR_FONT].pixel); DRAWSTRING(display, win->debug, bar_fs, l_draw, 2, (bar_fs_extents->max_logical_extent.height - l_lbox.height) / 2 - l_lbox.y, s, len); XFreeGC(display, l_draw); } else { draw = XftDrawCreate(display, win->debug, DefaultVisual(display, win->s->idx), DefaultColormap(display, win->s->idx)); XftDrawStringUtf8(draw, &bar_font_color, bar_font, 2, (bar_height + bar_font->height) / 2 - bar_font->descent, (FcChar8 *)s, len); XftDrawDestroy(draw); } free(s); } else if (win->debug != XCB_WINDOW_NONE) { xcb_destroy_window(conn, win->debug); win->debug = XCB_WINDOW_NONE; } } #else void dumpwins(struct binding *b, struct swm_region *r, union arg *s) { (void)b; (void)r; (void)s; } void debug_toggle(struct binding *b, struct swm_region *r, union arg *s) { (void)b; (void)r; (void)s; } #endif /* SWM_DEBUG */ void sighdlr(int sig) { int saved_errno, status; pid_t pid; saved_errno = errno; switch (sig) { case SIGCHLD: while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) { if (pid == -1) { if (errno == EINTR) continue; #ifdef SWM_DEBUG if (errno != ECHILD) warn("sighdlr: waitpid"); #endif /* SWM_DEBUG */ break; } if (pid == searchpid) search_resp = 1; #ifdef SWM_DEBUG if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) warnx("sighdlr: child exit status: %d", WEXITSTATUS(status)); } else warnx("sighdlr: child is terminated " "abnormally"); #endif /* SWM_DEBUG */ } break; case SIGHUP: restart_wm = 1; break; case SIGINT: case SIGTERM: case SIGQUIT: running = 0; break; } errno = saved_errno; } struct pid_e * find_pid(pid_t pid) { struct pid_e *p = NULL; DNPRINTF(SWM_D_MISC, "find_pid: %d\n", pid); if (pid == 0) return (NULL); TAILQ_FOREACH(p, &pidlist, entry) { if (p->pid == pid) return (p); } return (NULL); } uint32_t name_to_pixel(int sidx, const char *colorname) { uint32_t result = 0; char cname[32] = "#"; xcb_screen_t *screen; xcb_colormap_t cmap; xcb_alloc_color_reply_t *cr; xcb_alloc_named_color_reply_t *nr; uint16_t rr, gg, bb; screen = get_screen(sidx); cmap = screen->default_colormap; /* color is in format rgb://rr/gg/bb */ if (strncmp(colorname, "rgb:", 4) == 0) { if (parse_rgb(colorname, &rr, &gg, &bb) == -1) warnx("could not parse rgb %s", colorname); else { cr = xcb_alloc_color_reply(conn, xcb_alloc_color(conn, cmap, rr, gg, bb), NULL); if (cr) { result = cr->pixel; free(cr); } else warnx("color '%s' not found", colorname); } } else { nr = xcb_alloc_named_color_reply(conn, xcb_alloc_named_color(conn, cmap, strlen(colorname), colorname), NULL); if (nr == NULL) { strlcat(cname, colorname + 2, sizeof cname - 1); nr = xcb_alloc_named_color_reply(conn, xcb_alloc_named_color(conn, cmap, strlen(cname), cname), NULL); } if (nr) { result = nr->pixel; free(nr); } else warnx("color '%s' not found", colorname); } return (result); } void setscreencolor(const char *val, int i, int c) { if (i < 0 || i >= get_screen_count()) return; screens[i].c[c].pixel = name_to_pixel(i, val); free(screens[i].c[c].name); if ((screens[i].c[c].name = strdup(val)) == NULL) err(1, "strdup"); } void fancy_stacker(struct workspace *ws) { strlcpy(ws->stacker, "[ ]", sizeof ws->stacker); if (ws->cur_layout->l_stack == vertical_stack) snprintf(ws->stacker, sizeof ws->stacker, ws->l_state.vertical_flip ? "[%d>%d]" : "[%d|%d]", ws->l_state.vertical_mwin, ws->l_state.vertical_stacks); else if (ws->cur_layout->l_stack == horizontal_stack) snprintf(ws->stacker, sizeof ws->stacker, ws->l_state.horizontal_flip ? "[%dv%d]" : "[%d-%d]", ws->l_state.horizontal_mwin, ws->l_state.horizontal_stacks); } void plain_stacker(struct workspace *ws) { strlcpy(ws->stacker, "[ ]", sizeof ws->stacker); if (ws->cur_layout->l_stack == vertical_stack) strlcpy(ws->stacker, ws->l_state.vertical_flip ? "[>]" : "[|]", sizeof ws->stacker); else if (ws->cur_layout->l_stack == horizontal_stack) strlcpy(ws->stacker, ws->l_state.horizontal_flip ? "[v]" : "[-]", sizeof ws->stacker); } void custom_region(const char *val) { unsigned int x, y, w, h; int sidx, num_screens; xcb_screen_t *screen; DNPRINTF(SWM_D_CONF, "custom_region: %s\n", val); num_screens = get_screen_count(); if (sscanf(val, "screen[%d]:%ux%u+%u+%u", &sidx, &w, &h, &x, &y) != 5) errx(1, "invalid custom region, " "should be 'screen[]:x++"); if (sidx < 1 || sidx > num_screens) errx(1, "invalid screen index: %d out of bounds (maximum %d)", sidx, num_screens); sidx--; if ((screen = get_screen(sidx)) == NULL) errx(1, "ERROR: can't get screen %d.", sidx); if (w < 1 || h < 1) errx(1, "region %ux%u+%u+%u too small", w, h, x, y); if (x > screen->width_in_pixels || y > screen->height_in_pixels || w + x > screen->width_in_pixels || h + y > screen->height_in_pixels) { warnx("ignoring region %ux%u+%u+%u - not within screen " "boundaries (%ux%u)", w, h, x, y, screen->width_in_pixels, screen->height_in_pixels); return; } new_region(&screens[sidx], x, y, w, h); } void socket_setnonblock(int fd) { int flags; if ((flags = fcntl(fd, F_GETFL, 0)) == -1) err(1, "fcntl F_GETFL"); flags |= O_NONBLOCK; if ((flags = fcntl(fd, F_SETFL, flags)) == -1) err(1, "fcntl F_SETFL"); } void bar_print_legacy(struct swm_region *r, const char *s) { xcb_rectangle_t rect; uint32_t gcv[1]; XGCValues gcvd; int x = 0; size_t len; XRectangle ibox, lbox; GC draw; len = strlen(s); XmbTextExtents(bar_fs, s, len, &ibox, &lbox); switch (bar_justify) { case SWM_BAR_JUSTIFY_LEFT: x = SWM_BAR_OFFSET; break; case SWM_BAR_JUSTIFY_CENTER: x = (WIDTH(r) - lbox.width) / 2; break; case SWM_BAR_JUSTIFY_RIGHT: x = WIDTH(r) - lbox.width - SWM_BAR_OFFSET; break; } if (x < SWM_BAR_OFFSET) x = SWM_BAR_OFFSET; rect.x = 0; rect.y = 0; rect.width = WIDTH(r->bar); rect.height = HEIGHT(r->bar); /* clear back buffer */ gcv[0] = r->s->c[SWM_S_COLOR_BAR].pixel; xcb_change_gc(conn, r->s->bar_gc, XCB_GC_FOREGROUND, gcv); xcb_poly_fill_rectangle(conn, r->bar->buffer, r->s->bar_gc, 1, &rect); /* draw back buffer */ gcvd.graphics_exposures = 0; draw = XCreateGC(display, r->bar->buffer, GCGraphicsExposures, &gcvd); XSetForeground(display, draw, r->s->c[SWM_S_COLOR_BAR_FONT].pixel); DRAWSTRING(display, r->bar->buffer, bar_fs, draw, x, (bar_fs_extents->max_logical_extent.height - lbox.height) / 2 - lbox.y, s, len); XFreeGC(display, draw); /* blt */ xcb_copy_area(conn, r->bar->buffer, r->bar->id, r->s->bar_gc, 0, 0, 0, 0, WIDTH(r->bar), HEIGHT(r->bar)); } void bar_print(struct swm_region *r, const char *s) { size_t len; xcb_rectangle_t rect; uint32_t gcv[1]; int32_t x = 0; XGlyphInfo info; XftDraw *draw; len = strlen(s); XftTextExtentsUtf8(display, bar_font, (FcChar8 *)s, len, &info); switch (bar_justify) { case SWM_BAR_JUSTIFY_LEFT: x = SWM_BAR_OFFSET; break; case SWM_BAR_JUSTIFY_CENTER: x = (WIDTH(r) - info.width) / 2; break; case SWM_BAR_JUSTIFY_RIGHT: x = WIDTH(r) - info.width - SWM_BAR_OFFSET; break; } if (x < SWM_BAR_OFFSET) x = SWM_BAR_OFFSET; rect.x = 0; rect.y = 0; rect.width = WIDTH(r->bar); rect.height = HEIGHT(r->bar); /* clear back buffer */ gcv[0] = r->s->c[SWM_S_COLOR_BAR].pixel; xcb_change_gc(conn, r->s->bar_gc, XCB_GC_FOREGROUND, gcv); xcb_poly_fill_rectangle(conn, r->bar->buffer, r->s->bar_gc, 1, &rect); /* draw back buffer */ draw = XftDrawCreate(display, r->bar->buffer, DefaultVisual(display, r->s->idx), DefaultColormap(display, r->s->idx)); XftDrawStringUtf8(draw, &bar_font_color, bar_font, x, (HEIGHT(r->bar) + bar_font->height) / 2 - bar_font->descent, (FcChar8 *)s, len); XftDrawDestroy(draw); /* blt */ xcb_copy_area(conn, r->bar->buffer, r->bar->id, r->s->bar_gc, 0, 0, 0, 0, WIDTH(r->bar), HEIGHT(r->bar)); } void bar_extra_stop(void) { if (bar_pipe[0]) { close(bar_pipe[0]); bzero(bar_pipe, sizeof bar_pipe); } if (bar_pid) { kill(bar_pid, SIGTERM); bar_pid = 0; } strlcpy(bar_ext, "", sizeof bar_ext); bar_extra = false; } void bar_window_class(char *s, size_t sz, struct swm_region *r) { if (r == NULL || r->ws == NULL || r->ws->focus == NULL) return; if (r->ws->focus->ch.class_name != NULL) strlcat(s, r->ws->focus->ch.class_name, sz); } void bar_window_instance(char *s, size_t sz, struct swm_region *r) { if (r == NULL || r->ws == NULL || r->ws->focus == NULL) return; if (r->ws->focus->ch.instance_name != NULL) strlcat(s, r->ws->focus->ch.instance_name, sz); } void bar_window_class_instance(char *s, size_t sz, struct swm_region *r) { if (r == NULL || r->ws == NULL || r->ws->focus == NULL) return; bar_window_class(s, sz, r); strlcat(s, ":", sz); bar_window_instance(s, sz, r); } void bar_window_state(char *s, size_t sz, struct swm_region *r) { if (r == NULL || r ->ws == NULL || r->ws->focus == NULL) return; if (MAXIMIZED(r->ws->focus)) strlcat(s, "(m)", sz); else if (ABOVE(r->ws->focus)) strlcat(s, "(f)", sz); } void bar_window_name(char *s, size_t sz, struct swm_region *r) { char *title; if (r == NULL || r->ws == NULL || r->ws->focus == NULL) return; title = get_win_name(r->ws->focus->id); strlcat(s, title, sz); free(title); } bool get_urgent(struct ws_win *win) { xcb_icccm_wm_hints_t hints; xcb_get_property_cookie_t c; bool urgent = false; if (win) { c = xcb_icccm_get_wm_hints(conn, win->id); if (xcb_icccm_get_wm_hints_reply(conn, c, &hints, NULL)) urgent = xcb_icccm_wm_hints_get_urgency(&hints); } return urgent; } void bar_urgent(char *s, size_t sz) { struct ws_win *win; int i, j, num_screens, urgent[SWM_WS_MAX]; char b[8]; memset(&urgent, 0, sizeof(urgent)); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) if (get_urgent(win)) urgent[j] = 1; for (i = 0; i < workspace_limit; i++) { if (urgent[i]) { snprintf(b, sizeof b, "%d ", i + 1); strlcat(s, b, sz); } else if (!urgent_collapse) { strlcat(s, "- ", sz); } } if (urgent_collapse && s[0]) s[strlen(s) - 1] = 0; } void bar_workspace_name(char *s, size_t sz, struct swm_region *r) { if (r == NULL || r->ws == NULL) return; if (r->ws->name != NULL) strlcat(s, r->ws->name, sz); } /* build the default bar format according to the defined enabled options */ void bar_fmt(const char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz) { struct ws_win *w; /* if format provided, just copy the buffers */ if (bar_format != NULL) { strlcpy(fmtnew, fmtexp, sz); return; } /* reset the output buffer */ *fmtnew = '\0'; strlcat(fmtnew, "+N:+I ", sz); if (stack_enabled) strlcat(fmtnew, "+S", sz); strlcat(fmtnew, " ", sz); /* only show the workspace name if there's actually one */ if (r != NULL && r->ws != NULL && r->ws->name != NULL) strlcat(fmtnew, "<+D>", sz); /* If enabled, only show the iconic count if there are iconic wins. */ if (iconic_enabled && r != NULL && r->ws != NULL) TAILQ_FOREACH(w, &r->ws->winlist, entry) if (ICONIC(w)) { strlcat(fmtnew, "{+M}", sz); break; } strlcat(fmtnew, "+3<", sz); if (clock_enabled) { strlcat(fmtnew, fmtexp, sz); strlcat(fmtnew, "+4<", sz); } /* bar_urgent already adds the space before the last asterisk */ if (urgent_enabled) strlcat(fmtnew, (urgent_collapse ? "*+U*+4<" : "* +U*+4<"), sz); if (window_class_enabled) { strlcat(fmtnew, "+C", sz); if (!window_instance_enabled) strlcat(fmtnew, "+4<", sz); } /* checks needed by the colon and floating strlcat(3) calls below */ if (r != NULL && r->ws != NULL && r->ws->focus != NULL) { if (window_instance_enabled) { if (window_class_enabled) strlcat(fmtnew, ":", sz); strlcat(fmtnew, "+T+4<", sz); } if (window_name_enabled) { if (ABOVE(r->ws->focus) || MAXIMIZED(r->ws->focus)) strlcat(fmtnew, "+F ", sz); strlcat(fmtnew, "+64W ", sz); } } /* finally add the action script output and the version */ strlcat(fmtnew, "+4<+A+4<+V", sz); } void bar_replace_pad(char *tmp, int *limit, size_t sz) { /* special case; no limit given, pad one space, instead */ if (*limit == (int)sz - 1) *limit = 1; snprintf(tmp, sz, "%*s", *limit, " "); } /* replaces the bar format character sequences (like in tmux(1)) */ char * bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep, size_t sz) { struct ws_win *w; char *ptr, *cur = fmt; char tmp[SWM_BAR_MAX]; int limit, size, count; size_t len; int pre_padding = 0; int post_padding = 0; int padding_len = 0; /* Reset replace buffer. */ bzero(tmp, sizeof tmp); cur++; /* determine if pre-padding is requested */ if (*cur == '_') { pre_padding = 1; cur++; } /* get number, if any */ size = 0; if (sscanf(cur, "%d%n", &limit, &size) != 1) limit = sizeof tmp - 1; if (limit <= 0 || limit >= (int)sizeof tmp) limit = sizeof tmp - 1; cur += size; /* determine if post padding is requested */ if (*cur == '_') { post_padding = 1; cur++; } /* character sequence */ switch (*cur) { case '<': bar_replace_pad(tmp, &limit, sizeof tmp); break; case 'A': snprintf(tmp, sizeof tmp, "%s", bar_ext); break; case 'C': bar_window_class(tmp, sizeof tmp, r); break; case 'D': bar_workspace_name(tmp, sizeof tmp, r); break; case 'F': bar_window_state(tmp, sizeof tmp, r); break; case 'I': snprintf(tmp, sizeof tmp, "%d", r->ws->idx + 1); break; case 'M': count = 0; TAILQ_FOREACH(w, &r->ws->winlist, entry) if (ICONIC(w)) ++count; snprintf(tmp, sizeof tmp, "%d", count); break; case 'N': snprintf(tmp, sizeof tmp, "%d", r->s->idx + 1); break; case 'P': bar_window_class_instance(tmp, sizeof tmp, r); break; case 'R': snprintf(tmp, sizeof tmp, "%d", get_region_index(r) + 1); break; case 'S': snprintf(tmp, sizeof tmp, "%s", r->ws->stacker); break; case 'T': bar_window_instance(tmp, sizeof tmp, r); break; case 'U': bar_urgent(tmp, sizeof tmp); break; case 'V': snprintf(tmp, sizeof tmp, "%s", bar_vertext); break; case 'W': bar_window_name(tmp, sizeof tmp, r); break; default: /* Unknown character sequence or EOL; copy as-is. */ strlcpy(tmp, fmt, cur - fmt + 2); break; } len = strlen(tmp); /* calculate the padding lengths */ padding_len = limit - (int)len; if (padding_len > 0) { limit = len; if (pre_padding) pre_padding = padding_len / (pre_padding + post_padding); if (post_padding) post_padding = padding_len - pre_padding; } else { pre_padding = 0; post_padding = 0; } /* do pre padding */ while (pre_padding-- > 0) { if (*offrep >= sz - 1) break; fmtrep[(*offrep)++] = ' '; } ptr = tmp; while (limit-- > 0) { if (*offrep >= sz - 1) break; fmtrep[(*offrep)++] = *ptr++; } /* do post padding */ while (post_padding-- > 0) { if (*offrep >= sz - 1) break; fmtrep[(*offrep)++] = ' '; } if (*cur != '\0') cur++; return (cur); } void bar_replace(char *fmt, char *fmtrep, struct swm_region *r, size_t sz) { size_t off; off = 0; while (*fmt != '\0') { if (*fmt != '+') { /* skip ordinary characters */ if (off >= sz - 1) break; fmtrep[off++] = *fmt++; continue; } /* character sequence found; replace it */ fmt = bar_replace_seq(fmt, fmtrep, r, &off, sz); if (off >= sz - 1) break; } fmtrep[off] = '\0'; } void bar_fmt_expand(char *fmtexp, size_t sz) { char *fmt = NULL; size_t len; struct tm tm; time_t tmt; /* start by grabbing the current time and date */ time(&tmt); localtime_r(&tmt, &tm); /* figure out what to expand */ if (bar_format != NULL) fmt = bar_format; else if (bar_format == NULL && clock_enabled) fmt = clock_format; /* if nothing to expand bail out */ if (fmt == NULL) { *fmtexp = '\0'; return; } /* copy as-is, just in case the format shouldn't be expanded below */ strlcpy(fmtexp, fmt, sz); /* finally pass the string through strftime(3) */ #ifndef SWM_DENY_CLOCK_FORMAT if ((len = strftime(fmtexp, sz, fmt, &tm)) == 0) warnx("format too long"); fmtexp[len] = '\0'; #endif } /* Redraws a region bar; need to follow with xcb_flush() or focus_flush(). */ void bar_draw(struct swm_bar *bar) { struct swm_region *r; char fmtexp[SWM_BAR_MAX], fmtnew[SWM_BAR_MAX]; char fmtrep[SWM_BAR_MAX]; /* expand the format by first passing it through strftime(3) */ bar_fmt_expand(fmtexp, sizeof fmtexp); if (bar == NULL) return; r = bar->r; if (bar_enabled && r->ws->bar_enabled) xcb_map_window(conn, bar->id); else { xcb_unmap_window(conn, bar->id); return; } if (startup_exception) snprintf(fmtrep, sizeof fmtrep, "total " "exceptions: %d, first exception: %s", nr_exceptions, startup_exception); else { bar_fmt(fmtexp, fmtnew, r, sizeof fmtnew); bar_replace(fmtnew, fmtrep, r, sizeof fmtrep); } if (bar_font_legacy) bar_print_legacy(r, fmtrep); else bar_print(r, fmtrep); } /* * Reads external script output; call when stdin is readable. * Returns 1 if bar_ext was updated; otherwise 0. */ int bar_extra_update(void) { size_t len; char b[SWM_BAR_MAX]; bool changed = false; if (!bar_extra) return changed; while (fgets(b, sizeof(b), stdin) != NULL) { if (bar_enabled) { len = strlen(b); if (b[len - 1] == '\n') { /* Remove newline. */ b[--len] = '\0'; /* "Clear" bar_ext. */ bar_ext[0] = '\0'; /* Flush buffered output. */ strlcpy(bar_ext, bar_ext_buf, sizeof(bar_ext)); bar_ext_buf[0] = '\0'; /* Append new output to bar. */ strlcat(bar_ext, b, sizeof(bar_ext)); changed = true; } else { /* Buffer output. */ strlcat(bar_ext_buf, b, sizeof(bar_ext_buf)); } } } if (errno != EAGAIN) { warn("bar_action failed"); bar_extra_stop(); changed = true; } return changed; } void bar_toggle(struct binding *bp, struct swm_region *r, union arg *args) { struct swm_region *tmpr; int i, num_screens; /* suppress unused warnings since vars are needed */ (void)bp; (void)r; (void)args; DNPRINTF(SWM_D_BAR, "bar_toggle\n"); switch (args->id) { case SWM_ARG_ID_BAR_TOGGLE_WS: /* Only change if master switch is enabled. */ if (bar_enabled) r->ws->bar_enabled = !r->ws->bar_enabled; else bar_enabled = r->ws->bar_enabled = true; break; case SWM_ARG_ID_BAR_TOGGLE: bar_enabled = !bar_enabled; break; } /* update bars as necessary */ num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(tmpr, &screens[i].rl, entry) if (tmpr->bar) { if (bar_enabled && tmpr->ws->bar_enabled) xcb_map_window(conn, tmpr->bar->id); else xcb_unmap_window(conn, tmpr->bar->id); } /* Restack all regions and redraw bar. */ num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(tmpr, &screens[i].rl, entry) { stack(tmpr); bar_draw(tmpr->bar); } focus_flush(); } void bar_extra_setup(void) { /* do this here because the conf file is in memory */ if (!bar_extra && bar_argv[0]) { /* launch external status app */ bar_extra = true; if (pipe(bar_pipe) == -1) err(1, "pipe error"); socket_setnonblock(bar_pipe[0]); socket_setnonblock(bar_pipe[1]); /* XXX hmmm, really? */ /* Set stdin to read from the pipe. */ if (dup2(bar_pipe[0], STDIN_FILENO) == -1) err(1, "dup2"); /* Set stdout to write to the pipe. */ if (dup2(bar_pipe[1], STDOUT_FILENO) == -1) err(1, "dup2"); if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) err(1, "could not disable SIGPIPE"); switch (bar_pid = fork()) { case -1: err(1, "cannot fork"); break; case 0: /* child */ close(bar_pipe[0]); execvp(bar_argv[0], bar_argv); err(1, "%s external app failed", bar_argv[0]); break; default: /* parent */ close(bar_pipe[1]); break; } atexit(kill_bar_extra_atexit); } } void kill_bar_extra_atexit(void) { if (bar_pid) kill(bar_pid, SIGTERM); } bool isxlfd(char *s) { int count = 0; while ((s = index(s, '-'))) { ++count; ++s; } return (count == 14); } void fontset_init(void) { char *default_string; char **missing_charsets; int num_missing_charsets = 0; int i; if (bar_fs) { XFreeFontSet(display, bar_fs); bar_fs = NULL; } DNPRINTF(SWM_D_INIT, "fontset_init: loading bar_fonts: %s\n", bar_fonts); bar_fs = XCreateFontSet(display, bar_fonts, &missing_charsets, &num_missing_charsets, &default_string); if (num_missing_charsets > 0) { warnx("Unable to load charset(s):"); for (i = 0; i < num_missing_charsets; ++i) warnx("%s", missing_charsets[i]); XFreeStringList(missing_charsets); if (strcmp(default_string, "")) warnx("Glyphs from those sets will be replaced " "by '%s'.", default_string); else warnx("Glyphs from those sets won't be drawn."); } if (bar_fs == NULL) errx(1, "Error creating font set structure."); bar_fs_extents = XExtentsOfFontSet(bar_fs); bar_height = bar_fs_extents->max_logical_extent.height + 2 * bar_border_width; if (bar_height < 1) bar_height = 1; } void xft_init(struct swm_region *r) { char *font, *str, *search; XRenderColor color; if (bar_font == NULL) { if ((search = str = strdup(bar_fonts)) == NULL) errx(1, "insufficient memory."); while ((font = strsep(&search, ",")) != NULL) { if (*font == '\0') continue; DNPRINTF(SWM_D_INIT, "xft_init: try font %s\n", font); if (isxlfd(font)) { bar_font = XftFontOpenXlfd(display, r->s->idx, font); } else { bar_font = XftFontOpenName(display, r->s->idx, font); } if (bar_font == NULL) { warnx("unable to load font %s", font); continue; } else { DNPRINTF(SWM_D_INIT, "successfully opened " "font %s\n", font); break; } } free(str); } if (bar_font == NULL) errx(1, "unable to open a font"); PIXEL_TO_XRENDERCOLOR(r->s->c[SWM_S_COLOR_BAR_FONT].pixel, color); if (!XftColorAllocValue(display, DefaultVisual(display, r->s->idx), DefaultColormap(display, r->s->idx), &color, &bar_font_color)) warn("Xft error: unable to allocate color."); PIXEL_TO_XRENDERCOLOR(r->s->c[SWM_S_COLOR_BAR].pixel, color); if (!XftColorAllocValue(display, DefaultVisual(display, r->s->idx), DefaultColormap(display, r->s->idx), &color, &search_font_color)) warn("Xft error: unable to allocate color."); bar_height = bar_font->height + 2 * bar_border_width; if (bar_height < 1) bar_height = 1; } void bar_setup(struct swm_region *r) { xcb_screen_t *screen; uint32_t wa[3]; DNPRINTF(SWM_D_BAR, "bar_setup: screen %d.\n", r->s->idx); if ((screen = get_screen(r->s->idx)) == NULL) errx(1, "ERROR: can't get screen %d.", r->s->idx); if (r->bar != NULL) return; if ((r->bar = calloc(1, sizeof(struct swm_bar))) == NULL) err(1, "bar_setup: calloc: failed to allocate memory."); if (bar_font_legacy) fontset_init(); else xft_init(r); r->bar->r = r; X(r->bar) = X(r); Y(r->bar) = bar_at_bottom ? (Y(r) + HEIGHT(r) - bar_height) : Y(r); WIDTH(r->bar) = WIDTH(r) - 2 * bar_border_width; HEIGHT(r->bar) = bar_height - 2 * bar_border_width; /* Assume region is unfocused when we create the bar. */ r->bar->id = xcb_generate_id(conn); wa[0] = r->s->c[SWM_S_COLOR_BAR].pixel; wa[1] = r->s->c[SWM_S_COLOR_BAR_BORDER_UNFOCUS].pixel; wa[2] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_POINTER_MOTION_HINT; xcb_create_window(conn, XCB_COPY_FROM_PARENT, r->bar->id, r->s->root, X(r->bar), Y(r->bar), WIDTH(r->bar), HEIGHT(r->bar), bar_border_width, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK, wa); /* Stack bar window above region window to start. */ wa[0] = r->id; wa[1] = XCB_STACK_MODE_ABOVE; xcb_configure_window(conn, r->bar->id, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, wa); r->bar->buffer = xcb_generate_id(conn); xcb_create_pixmap(conn, screen->root_depth, r->bar->buffer, r->bar->id, WIDTH(r->bar), HEIGHT(r->bar)); if (randr_support) xcb_randr_select_input(conn, r->bar->id, XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE); if (bar_enabled) xcb_map_window(conn, r->bar->id); DNPRINTF(SWM_D_BAR, "bar_setup: win %#x, (x,y) w x h: (%d,%d) " "%d x %d\n", WINID(r->bar), X(r->bar), Y(r->bar), WIDTH(r->bar), HEIGHT(r->bar)); bar_extra_setup(); } void bar_cleanup(struct swm_region *r) { if (r->bar == NULL) return; xcb_destroy_window(conn, r->bar->id); xcb_free_pixmap(conn, r->bar->buffer); free(r->bar); r->bar = NULL; } void set_win_state(struct ws_win *win, uint8_t state) { uint32_t data[2] = { state, XCB_ATOM_NONE }; DNPRINTF(SWM_D_EVENT, "set_win_state: win %#x, state: %u\n", WINID(win), state); if (win == NULL) return; xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id, a_state, a_state, 32, 2, data); } uint8_t get_win_state(xcb_window_t w) { xcb_get_property_reply_t *r; xcb_get_property_cookie_t c; uint32_t result = 0; c = xcb_get_property(conn, 0, w, a_state, a_state, 0L, 2L); r = xcb_get_property_reply(conn, c, NULL); if (r) { if (r->type == a_state && r->format == 32 && r->length == 2) result = *((uint32_t *)xcb_get_property_value(r)); free(r); } DNPRINTF(SWM_D_MISC, "get_win_state property: win %#x state %u\n", w, result); return (result); } void version(struct binding *bp, struct swm_region *r, union arg *args) { struct swm_region *tmpr; int i, num_screens; /* suppress unused warnings since vars are needed */ (void)bp; (void)r; (void)args; bar_version = !bar_version; if (bar_version) snprintf(bar_vertext, sizeof bar_vertext, "Version: %s Build: %s", SPECTRWM_VERSION, buildstr); else strlcpy(bar_vertext, "", sizeof bar_vertext); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { TAILQ_FOREACH(tmpr, &screens[i].rl, entry) { bar_draw(tmpr->bar); xcb_flush(conn); } } } void client_msg(struct ws_win *win, xcb_atom_t a, xcb_timestamp_t t) { xcb_client_message_event_t ev; #ifdef SWM_DEBUG char *name; #endif if (win == NULL) return; #ifdef SWM_DEBUG name = get_atom_name(a); DNPRINTF(SWM_D_EVENT, "client_msg: win %#x, atom: %s(%u), " "time: %#x\n", win->id, name, a, t); free(name); #endif bzero(&ev, sizeof ev); ev.response_type = XCB_CLIENT_MESSAGE; ev.window = win->id; ev.type = a_prot; ev.format = 32; ev.data.data32[0] = a; ev.data.data32[1] = t; xcb_send_event(conn, 0, win->id, XCB_EVENT_MASK_NO_EVENT, (const char *)&ev); } /* synthetic response to a ConfigureRequest when not making a change */ void config_win(struct ws_win *win, xcb_configure_request_event_t *ev) { xcb_configure_notify_event_t ce; if (win == NULL) return; /* send notification of unchanged state. */ bzero(&ce, sizeof(ce)); ce.response_type = XCB_CONFIGURE_NOTIFY; ce.x = X(win); ce.y = Y(win); ce.width = WIDTH(win); ce.height = HEIGHT(win); ce.border_width = 0; ce.override_redirect = 0; if (ev == NULL) { /* EWMH */ ce.event = win->id; ce.window = win->id; ce.above_sibling = XCB_WINDOW_NONE; } else { /* normal */ ce.event = ev->window; ce.window = ev->window; /* make response appear more WM_SIZE_HINTS-compliant */ if (win->sh.flags) { DNPRINTF(SWM_D_MISC, "config_win: hints: win %#x," " sh.flags: %u, min: %d x %d, max: %d x %d, inc: " "%d x %d\n", win->id, win->sh.flags, SH_MIN_W(win), SH_MIN_H(win), SH_MAX_W(win), SH_MAX_H(win), SH_INC_W(win), SH_INC_H(win)); } /* min size */ if (SH_MIN(win)) { /* the hint may be set... to 0! */ if (SH_MIN_W(win) > 0 && ce.width < SH_MIN_W(win)) ce.width = SH_MIN_W(win); if (SH_MIN_H(win) > 0 && ce.height < SH_MIN_H(win)) ce.height = SH_MIN_H(win); } /* max size */ if (SH_MAX(win)) { /* may also be advertized as 0 */ if (SH_MAX_W(win) > 0 && ce.width > SH_MAX_W(win)) ce.width = SH_MAX_W(win); if (SH_MAX_H(win) > 0 && ce.height > SH_MAX_H(win)) ce.height = SH_MAX_H(win); } /* resize increment. */ if (SH_INC(win)) { if (SH_INC_W(win) > 1 && ce.width > SH_INC_W(win)) ce.width -= (ce.width - SH_MIN_W(win)) % SH_INC_W(win); if (SH_INC_H(win) > 1 && ce.height > SH_INC_H(win)) ce.height -= (ce.height - SH_MIN_H(win)) % SH_INC_H(win); } /* adjust x and y for requested border_width. */ ce.x += ev->border_width; ce.y += ev->border_width; ce.above_sibling = ev->sibling; } DNPRINTF(SWM_D_MISC, "config_win: ewmh: %s, win %#x, (x,y) w x h: " "(%d,%d) %d x %d, border: %d\n", YESNO(ev == NULL), win->id, ce.x, ce.y, ce.width, ce.height, ce.border_width); xcb_send_event(conn, 0, win->id, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char *)&ce); } int count_win(struct workspace *ws, bool count_transient) { struct ws_win *win; int count = 0; TAILQ_FOREACH(win, &ws->winlist, entry) { if (!count_transient && FLOATING(win)) continue; if (ICONIC(win)) continue; count++; } DNPRINTF(SWM_D_MISC, "count_win: %d\n", count); return (count); } void quit(struct binding *bp, struct swm_region *r, union arg *args) { /* suppress unused warnings since vars are needed */ (void)bp; (void)r; (void)args; DNPRINTF(SWM_D_MISC, "quit\n"); running = 0; } void lower_window(struct ws_win *win) { struct ws_win *target = NULL; struct workspace *ws; DNPRINTF(SWM_D_EVENT, "lower_window: win %#x\n", WINID(win)); if (win == NULL) return; ws = win->ws; TAILQ_FOREACH(target, &ws->stack, stack_entry) { if (target == win || ICONIC(target)) continue; if (ws->cur_layout == &layouts[SWM_MAX_STACK]) break; if (TRANS(win)) { if (win->transient == target->transient) continue; if (win->transient == target->id) break; } if (FULLSCREEN(target)) continue; if (FULLSCREEN(win)) break; if (MAXIMIZED(target)) continue; if (MAXIMIZED(win)) break; if (ABOVE(target) || TRANS(target)) continue; if (ABOVE(win) || TRANS(win)) break; } /* Change stack position. */ TAILQ_REMOVE(&ws->stack, win, stack_entry); if (target) TAILQ_INSERT_BEFORE(target, win, stack_entry); else TAILQ_INSERT_TAIL(&ws->stack, win, stack_entry); update_win_stacking(win); #ifdef SWM_DEBUG if (swm_debug & SWM_D_STACK) { DPRINTF("=== stacking order (top down) === \n"); TAILQ_FOREACH(target, &ws->stack, stack_entry) { DPRINTF("win %#x, fs: %s, maximized: %s, above: %s, " "iconic: %s\n", target->id, YESNO(FULLSCREEN(target)), YESNO(MAXIMIZED(target)), YESNO(ABOVE(target)), YESNO(ICONIC(target))); } } #endif DNPRINTF(SWM_D_EVENT, "lower_window: done\n"); } void raise_window(struct ws_win *win) { struct ws_win *target = NULL; struct workspace *ws; DNPRINTF(SWM_D_EVENT, "raise_window: win %#x\n", WINID(win)); if (win == NULL) return; ws = win->ws; TAILQ_FOREACH(target, &ws->stack, stack_entry) { if (target == win || ICONIC(target)) continue; if (ws->cur_layout == &layouts[SWM_MAX_STACK]) break; if (TRANS(win) && (win->transient == target->transient || win->transient == target->id)) break; if (FULLSCREEN(win)) break; if (FULLSCREEN(target)) continue; if (MAXIMIZED(win)) break; if (MAXIMIZED(target)) continue; if (ABOVE(win) || TRANS(win) || (win->ws->focus == win && ws->always_raise)) break; if (!ABOVE(target) && !TRANS(target)) break; } TAILQ_REMOVE(&ws->stack, win, stack_entry); if (target) TAILQ_INSERT_BEFORE(target, win, stack_entry); else TAILQ_INSERT_TAIL(&ws->stack, win, stack_entry); update_win_stacking(win); #ifdef SWM_DEBUG if (swm_debug & SWM_D_STACK) { DPRINTF("=== stacking order (top down) === \n"); TAILQ_FOREACH(target, &ws->stack, stack_entry) { DPRINTF("win %#x, fs: %s, maximized: %s, above: %s, " "iconic: %s\n", target->id, YESNO(FULLSCREEN(target)), YESNO(MAXIMIZED(target)), YESNO(ABOVE(target)), YESNO(ICONIC(target))); } } #endif DNPRINTF(SWM_D_EVENT, "raise_window: done\n"); } void update_win_stacking(struct ws_win *win) { struct ws_win *sibling; #ifdef SWM_DEBUG struct ws_win *w; #endif struct swm_region *r; uint32_t val[2]; if (win == NULL || (r = win->ws->r) == NULL) return; if (win->frame == XCB_WINDOW_NONE) { DNPRINTF(SWM_D_EVENT, "update_window_stacking: win %#x not " "reparented.\n", win->id); return; } sibling = TAILQ_NEXT(win, stack_entry); if (sibling != NULL && (FLOATING(win) == FLOATING(sibling) || (win->ws->always_raise && win->ws->focus == win))) val[0] = sibling->frame; else if (FLOATING(win) || (win->ws->always_raise && win->ws->focus == win)) val[0] = r->bar->id; else val[0] = r->id; DNPRINTF(SWM_D_EVENT, "update_win_stacking: win %#x (%#x), " "sibling %#x\n", win->frame, win->id, val[0]); val[1] = XCB_STACK_MODE_ABOVE; xcb_configure_window(conn, win->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, val); #ifdef SWM_DEBUG TAILQ_FOREACH(w, &win->ws->winlist, entry) debug_refresh(w); #endif } void map_window(struct ws_win *win) { if (win == NULL) return; DNPRINTF(SWM_D_EVENT, "map_window: win %#x, mapped: %s\n", win->id, YESNO(win->mapped)); if (win->mapped) return; xcb_map_window(conn, win->frame); xcb_map_window(conn, win->id); set_win_state(win, XCB_ICCCM_WM_STATE_NORMAL); win->mapped = true; } void unmap_window(struct ws_win *win) { if (win == NULL) return; DNPRINTF(SWM_D_EVENT, "unmap_window: win %#x, mapped: %s\n", win->id, YESNO(win->mapped)); if (!win->mapped) return; xcb_unmap_window(conn, win->id); xcb_unmap_window(conn, win->frame); set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC); win->mapped = false; } void unmap_all(void) { struct ws_win *win; int i, j, num_screens; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) unmap_window(win); } void fake_keypress(struct ws_win *win, xcb_keysym_t keysym, uint16_t modifiers) { xcb_key_press_event_t event; xcb_keycode_t *keycode; if (win == NULL) return; keycode = xcb_key_symbols_get_keycode(syms, keysym); DNPRINTF(SWM_D_MISC, "fake_keypress: win %#x, keycode %u\n", win->id, *keycode); bzero(&event, sizeof(event)); event.event = win->id; event.root = win->s->root; event.child = XCB_WINDOW_NONE; event.time = XCB_CURRENT_TIME; event.event_x = X(win); event.event_y = Y(win); event.root_x = 1; event.root_y = 1; event.same_screen = 1; event.detail = *keycode; event.state = modifiers; event.response_type = XCB_KEY_PRESS; xcb_send_event(conn, 1, win->id, XCB_EVENT_MASK_KEY_PRESS, (const char *)&event); event.response_type = XCB_KEY_RELEASE; xcb_send_event(conn, 1, win->id, XCB_EVENT_MASK_KEY_RELEASE, (const char *)&event); free(keycode); } void restart(struct binding *bp, struct swm_region *r, union arg *args) { /* suppress unused warning since var is needed */ (void)bp; (void)r; (void)args; DNPRINTF(SWM_D_MISC, "restart: %s\n", start_argv[0]); shutdown_cleanup(); execvp(start_argv[0], start_argv); warn("execvp failed"); quit(NULL, NULL, NULL); } struct ws_win * get_pointer_win(xcb_window_t root) { struct ws_win *win = NULL; xcb_query_pointer_reply_t *r; DNPRINTF(SWM_D_EVENT, "get_pointer_win: root: %#x.\n", root); r = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, root), NULL); if (r) { win = find_window(r->child); if (win) { DNPRINTF(SWM_D_EVENT, "get_pointer_win: %#x.\n", win->id); } else { DNPRINTF(SWM_D_EVENT, "get_pointer_win: none.\n"); } free(r); } return win; } void center_pointer(struct swm_region *r) { struct ws_win *win; if (!warp_pointer || r == NULL) return; win = r->ws->focus; DNPRINTF(SWM_D_EVENT, "center_pointer: win %#x.\n", WINID(win)); if (win && win->mapped) xcb_warp_pointer(conn, XCB_NONE, win->frame, 0, 0, 0, 0, WIDTH(win) / 2, HEIGHT(win) / 2); else xcb_warp_pointer(conn, XCB_NONE, r->id, 0, 0, 0, 0, WIDTH(r) / 2, HEIGHT(r) / 2); } struct swm_region * root_to_region(xcb_window_t root, int check) { struct ws_win *cfw; struct swm_region *r = NULL; int i, num_screens; xcb_query_pointer_reply_t *qpr; xcb_get_input_focus_reply_t *gifr; DNPRINTF(SWM_D_MISC, "root_to_region: win %#x\n", root); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) if (screens[i].root == root) break; if (check & SWM_CK_REGION) r = screens[i].r_focus; if (r == NULL && check & SWM_CK_FOCUS) { /* Try to find an actively focused window */ gifr = xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL); if (gifr) { cfw = find_window(gifr->focus); if (cfw && cfw->ws->r) r = cfw->ws->r; free(gifr); } } if (r == NULL && check & SWM_CK_POINTER) { /* No region with an active focus; try to use pointer. */ qpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, screens[i].root), NULL); if (qpr) { DNPRINTF(SWM_D_MISC, "root_to_region: pointer: " "(%d,%d)\n", qpr->root_x, qpr->root_y); TAILQ_FOREACH(r, &screens[i].rl, entry) if (X(r) <= qpr->root_x && qpr->root_x < MAX_X(r) && Y(r) <= qpr->root_y && qpr->root_y < MAX_Y(r)) break; free(qpr); } } /* Last resort. */ if (r == NULL && check & SWM_CK_FALLBACK) r = TAILQ_FIRST(&screens[i].rl); DNPRINTF(SWM_D_MISC, "root_to_region: idx: %d\n", get_region_index(r)); return (r); } struct swm_region * find_region(xcb_window_t id) { struct swm_region *r; int i, num_screens; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) if (r->id == id) return r; return NULL; } struct swm_bar * find_bar(xcb_window_t id) { struct swm_region *r; int i, num_screens; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) if (r->bar && r->bar->id == id) return r->bar; return NULL; } struct ws_win * find_frame_window(xcb_window_t id) { struct swm_region *r; struct ws_win *w; int i, num_screens; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) TAILQ_FOREACH(w, &r->ws->winlist, entry) if (w->frame == id) return w; return NULL; } struct ws_win * find_window(xcb_window_t id) { struct ws_win *win = NULL; int i, j, num_screens; xcb_query_tree_reply_t *qtr; DNPRINTF(SWM_D_MISC, "find_window: id: %#x\n", id); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) if (id == win->id || id == win->frame) return (win); /* If window isn't top-level, try to find managed ancestor. */ qtr = xcb_query_tree_reply(conn, xcb_query_tree(conn, id), NULL); if (qtr) { if (qtr->parent != XCB_WINDOW_NONE && qtr->parent != qtr->root) win = find_window(qtr->parent); #ifdef SWM_DEBUG if (win) DNPRINTF(SWM_D_MISC, "find_window: found child %#x " "of %#x.\n", win->id, qtr->parent); #endif free(qtr); } return (win); } struct ws_win * find_unmanaged_window(xcb_window_t id) { struct ws_win *win; int i, j, num_screens; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) TAILQ_FOREACH(win, &screens[i].ws[j].unmanagedlist, entry) if (id == win->id) return (win); return (NULL); } void spawn(int ws_idx, union arg *args, bool close_fd) { int fd; char *ret = NULL; if (args == NULL || args->argv[0] == NULL) return; DNPRINTF(SWM_D_MISC, "spawn: %s\n", args->argv[0]); close(xcb_get_file_descriptor(conn)); if ((ret = getenv("LD_PRELOAD"))) { if (asprintf(&ret, "%s:%s", SWM_LIB, ret) == -1) { warn("spawn: asprintf LD_PRELOAD"); _exit(1); } setenv("LD_PRELOAD", ret, 1); free(ret); } else { setenv("LD_PRELOAD", SWM_LIB, 1); } if (asprintf(&ret, "%d", ws_idx) == -1) { warn("spawn: asprintf SWM_WS"); _exit(1); } setenv("_SWM_WS", ret, 1); free(ret); ret = NULL; if (asprintf(&ret, "%d", getpid()) == -1) { warn("spawn: asprintf _SWM_PID"); _exit(1); } setenv("_SWM_PID", ret, 1); free(ret); ret = NULL; if (setsid() == -1) { warn("spawn: setsid"); _exit(1); } if (close_fd) { /* * close stdin and stdout to prevent interaction between apps * and the baraction script * leave stderr open to record errors */ if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) { warn("spawn: open"); _exit(1); } dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); if (fd > 2) close(fd); } if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) err(1, "could not reset SIGPIPE"); execvp(args->argv[0], args->argv); warn("spawn: execvp"); _exit(1); } void kill_refs(struct ws_win *win) { struct workspace *ws; struct ws_win *w; int i, j, num_screens; if (win == NULL) return; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { for (j = 0; j < workspace_limit; j++) { ws = &screens[i].ws[j]; if (win == ws->focus) ws->focus = NULL; if (win == ws->focus_prev) ws->focus_prev = NULL; if (win == ws->focus_pending) ws->focus_pending = NULL; if (win == ws->focus_raise) ws->focus_raise = NULL; if (TRANS(win)) TAILQ_FOREACH(w, &ws->winlist, entry) if (win == w->focus_child) w->focus_child = NULL; } } } int validate_win(struct ws_win *testwin) { struct ws_win *win; struct workspace *ws; struct swm_region *r; int i, x, num_screens; if (testwin == NULL) return (0); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) for (x = 0; x < workspace_limit; x++) { ws = &r->s->ws[x]; TAILQ_FOREACH(win, &ws->winlist, entry) if (win == testwin) return (0); } return (1); } int validate_ws(struct workspace *testws) { struct swm_region *r; struct workspace *ws; int i, x, num_screens; /* validate all ws */ num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) for (x = 0; x < workspace_limit; x++) { ws = &r->s->ws[x]; if (ws == testws) return (0); } return (1); } void unfocus_win(struct ws_win *win) { xcb_window_t none = XCB_WINDOW_NONE; DNPRINTF(SWM_D_FOCUS, "unfocus_win: win %#x\n", WINID(win)); if (win == NULL) return; if (win->ws == NULL) { DNPRINTF(SWM_D_FOCUS, "unfocus_win: NULL ws.\n"); return; } if (validate_ws(win->ws)) { DNPRINTF(SWM_D_FOCUS, "unfocus_win: invalid ws.\n"); return; } if (win->ws->r == NULL) { DNPRINTF(SWM_D_FOCUS, "unfocus_win: NULL region.\n"); return; } if (validate_win(win)) { DNPRINTF(SWM_D_FOCUS, "unfocus_win: invalid win.\n"); kill_refs(win); return; } if (win->ws->focus == win) { win->ws->focus = NULL; win->ws->focus_prev = win; if (win->ws->focus_raise == win && !FLOATING(win)) { update_win_stacking(win); } } if (validate_win(win->ws->focus)) { kill_refs(win->ws->focus); win->ws->focus = NULL; } if (validate_win(win->ws->focus_prev)) { kill_refs(win->ws->focus_prev); win->ws->focus_prev = NULL; } draw_frame(win); /* Raise window to "top unfocused position." */ if (win->ws->always_raise) raise_window(win); /* Update border width */ if (win->bordered && (win->quirks & SWM_Q_MINIMALBORDER) && FLOATING(win)) { win->bordered = 0; X(win) += border_width; Y(win) += border_width; update_window(win); } xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root, ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1, &none); DNPRINTF(SWM_D_FOCUS, "unfocus_win: done.\n"); } void focus_win(struct ws_win *win) { struct ws_win *cfw = NULL, *parent = NULL, *w, *tmpw; struct workspace *ws; xcb_get_input_focus_reply_t *gifr = NULL; xcb_get_window_attributes_reply_t *war = NULL; DNPRINTF(SWM_D_FOCUS, "focus_win: win %#x\n", WINID(win)); if (win == NULL || win->ws == NULL || !win->mapped) goto out; ws = win->ws; if (validate_ws(ws)) goto out; if (validate_win(win)) { kill_refs(win); goto out; } gifr = xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL); if (gifr) { DNPRINTF(SWM_D_FOCUS, "focus_win: cur focus: %#x\n", gifr->focus); cfw = find_window(gifr->focus); if (cfw) { if (cfw != win) { if (cfw->ws != ws && cfw->ws->r != NULL && cfw->frame != XCB_WINDOW_NONE) { draw_frame(cfw); } else { unfocus_win(cfw); } } } else { war = xcb_get_window_attributes_reply(conn, xcb_get_window_attributes(conn, gifr->focus), NULL); if (war && war->override_redirect && ws->focus == win) { DNPRINTF(SWM_D_FOCUS, "focus_win: skip refocus " "from override_redirect.\n"); goto out; } } } if (ws->focus != win) { if (ws->focus && ws->focus != cfw) unfocus_win(ws->focus); ws->focus = win; } /* Clear focus child redirect. */ win->focus_child = NULL; /* If transient, adjust parent's focus child for focus_magic. */ if (TRANS(win)) { parent = find_window(win->transient); if (parent && parent->focus_child != win) parent->focus_child = win; } /* Update window border even if workspace is hidden. */ draw_frame(win); if (cfw == win) { DNPRINTF(SWM_D_FOCUS, "focus_win: already focused.\n"); goto out; } if (ws->r) { /* Set input focus if no input hint, or indicated by hint. */ if (ACCEPTS_FOCUS(win)) { DNPRINTF(SWM_D_FOCUS, "focus_win: set_input_focus: %#x," " revert-to: parent, time: %#x\n", win->id, last_event_time); xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, win->id, last_event_time); } else if (!win->take_focus) { DNPRINTF(SWM_D_FOCUS, "focus_win: set_input_focus: %#x," " revert-to: parent, time: 0\n", ws->r->id); xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, ws->r->id, XCB_CURRENT_TIME); } /* Tell app it can adjust focus to a specific window. */ if (win->take_focus) { /* java is special; always tell parent */ if (TRANS(win) && win->java) client_msg(parent, a_takefocus, last_event_time); else client_msg(win, a_takefocus, last_event_time); } if (ws->cur_layout->flags & SWM_L_MAPONFOCUS || ws->always_raise) { /* If a parent exists, map it first. */ if (parent) { raise_window(parent); map_window(parent); /* Map siblings next. */ TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw) if (w != win && !ICONIC(w) && w->transient == parent->id) { raise_window(w); map_window(w); } } /* Map focused window. */ raise_window(win); map_window(win); /* Stack any children of focus window. */ TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw) if (w->transient == win->id && !ICONIC(w)) { raise_window(w); map_window(w); } } else if (tile_gap < 0 && !ABOVE(win)) { /* * Windows overlap in the layout. * Raise focused win above all tiled wins. */ raise_window(win); map_window(win); } set_region(ws->r); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root, ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1, &win->id); bar_draw(ws->r->bar); } out: free(gifr); free(war); DNPRINTF(SWM_D_FOCUS, "focus_win: done.\n"); } /* If a child window should have focus instead, return it. */ struct ws_win * get_focus_magic(struct ws_win *win) { struct ws_win *parent = NULL; struct ws_win *child = NULL; DNPRINTF(SWM_D_FOCUS, "get_focus_magic: win %#x\n", WINID(win)); if (win == NULL) return win; if (TRANS(win)) { parent = find_window(win->transient); /* If parent prefers focus elsewhere, then try to do so. */ if (parent && (child = parent->focus_child)) { if (validate_win(child) == 0 && child->mapped) win = child; else parent->focus_child = NULL; } } /* If this window prefers focus elsewhere, then try to do so. */ if ((child = win->focus_child)) { if (validate_win(child) == 0 && child->mapped) win = child; else win->focus_child = NULL; } return win; } void event_drain(uint8_t rt) { xcb_generic_event_t *evt; /* ensure all pending requests have been processed before filtering. */ xcb_aux_sync(conn); while ((evt = get_next_event(false))) { if (XCB_EVENT_RESPONSE_TYPE(evt) != rt) event_handle(evt); free(evt); } } void set_region(struct swm_region *r) { struct swm_region *rf; int vals[2]; if (r == NULL) return; rf = r->s->r_focus; /* Unfocus old region bar. */ if (rf != NULL) { if (rf == r) return; xcb_change_window_attributes(conn, rf->bar->id, XCB_CW_BORDER_PIXEL, &r->s->c[SWM_S_COLOR_BAR_BORDER_UNFOCUS].pixel); } if (rf != NULL && rf != r && (X(rf) != X(r) || Y(rf) != Y(r) || WIDTH(rf) != WIDTH(r) || HEIGHT(rf) != HEIGHT(r))) { /* Set _NET_DESKTOP_GEOMETRY. */ vals[0] = WIDTH(r); vals[1] = HEIGHT(r); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, r->s->root, ewmh[_NET_DESKTOP_GEOMETRY].atom, XCB_ATOM_CARDINAL, 32, 2, &vals); } /* Set region bar border to focus_color. */ xcb_change_window_attributes(conn, r->bar->id, XCB_CW_BORDER_PIXEL, &r->s->c[SWM_S_COLOR_BAR_BORDER].pixel); r->s->r_focus = r; /* Update the focus window frame on the now unfocused region. */ if (rf && rf->ws->focus) draw_frame(rf->ws->focus); ewmh_update_current_desktop(); } void focus_region(struct swm_region *r) { struct ws_win *nfw; struct swm_region *old_r; if (r == NULL) return; old_r = r->s->r_focus; set_region(r); nfw = get_region_focus(r); if (nfw) { focus_win(nfw); } else { /* New region is empty; need to manually unfocus win. */ if (old_r) { unfocus_win(old_r->ws->focus); /* Clear bar since empty. */ bar_draw(old_r->bar); } DNPRINTF(SWM_D_FOCUS, "focus_region: set_input_focus: %#x, " "revert-to: parent, time: 0\n", r->id); xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, r->id, XCB_CURRENT_TIME); } } void switchws(struct binding *bp, struct swm_region *r, union arg *args) { struct swm_region *this_r, *other_r; struct ws_win *win; struct workspace *new_ws, *old_ws; xcb_window_t none = XCB_WINDOW_NONE; int wsid = args->id; bool unmap_old = false; if (!(r && r->s)) return; if (wsid >= workspace_limit) return; this_r = r; old_ws = this_r->ws; new_ws = &this_r->s->ws[wsid]; DNPRINTF(SWM_D_WS, "switchws: screen[%d]:%dx%d+%d+%d: %d -> %d\n", r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), old_ws->idx, wsid); if (new_ws == NULL || old_ws == NULL) return; if (new_ws == old_ws) return; other_r = new_ws->r; if (other_r && workspace_clamp && bp->action != FN_RG_MOVE_NEXT && bp->action != FN_RG_MOVE_PREV) { DNPRINTF(SWM_D_WS, "switchws: ws clamped.\n"); if (warp_focus) { DNPRINTF(SWM_D_WS, "switchws: warping focus to region " "with ws %d.\n", wsid); focus_region(other_r); center_pointer(other_r); focus_flush(); } return; } if ((win = old_ws->focus) != NULL) { draw_frame(win); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root, ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1, &none); } if (other_r) { /* the other ws is visible in another region, exchange them */ other_r->ws_prior = new_ws; other_r->ws = old_ws; old_ws->r = other_r; } else { /* the other workspace is hidden, hide this one */ old_ws->r = NULL; unmap_old = true; } this_r->ws_prior = old_ws; this_r->ws = new_ws; new_ws->r = this_r; /* Set focus_pending before stacking, if needed. */ if (focus_mode != SWM_FOCUS_FOLLOW && (!new_ws->focus_pending || validate_win(new_ws->focus_pending))) { new_ws->focus_pending = get_region_focus(new_ws->r); new_ws->focus = new_ws->focus_prev; new_ws->focus_prev = NULL; } new_ws->state = SWM_WS_STATE_MAPPING; stack(other_r); stack(this_r); /* unmap old windows */ if (unmap_old) { TAILQ_FOREACH(win, &old_ws->winlist, entry) unmap_window(win); old_ws->state = SWM_WS_STATE_HIDDEN; } /* if workspaces were swapped, then don't wait to set focus */ if (old_ws->r && focus_mode != SWM_FOCUS_FOLLOW) { if (new_ws->focus_pending) { focus_win(new_ws->focus_pending); new_ws->focus_pending = NULL; } } /* Clear bar and set focus on region input win if new ws is empty. */ if (new_ws->focus_pending == NULL && new_ws->focus == NULL) { DNPRINTF(SWM_D_FOCUS, "switchws: set_input_focus: %#x, " "revert-to: parent, time: 0\n", r->id); xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, r->id, XCB_CURRENT_TIME); bar_draw(r->bar); } ewmh_update_current_desktop(); center_pointer(r); focus_flush(); new_ws->state = SWM_WS_STATE_MAPPED; DNPRINTF(SWM_D_WS, "switchws: done.\n"); } void cyclews(struct binding *bp, struct swm_region *r, union arg *args) { union arg a; struct swm_screen *s = r->s; bool cycle_all = false, mv = false; DNPRINTF(SWM_D_WS, "cyclews: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n", args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx); a.id = r->ws->idx; do { switch (args->id) { case SWM_ARG_ID_CYCLEWS_MOVE_UP: mv = true; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_UP_ALL: cycle_all = true; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_UP: a.id = (a.id < workspace_limit - 1) ? a.id + 1 : 0; break; case SWM_ARG_ID_CYCLEWS_MOVE_DOWN: mv = true; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_DOWN_ALL: cycle_all = true; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_DOWN: a.id = (a.id > 0) ? a.id - 1 : workspace_limit - 1; break; default: return; }; if (!cycle_all && (!cycle_empty && TAILQ_EMPTY(&s->ws[a.id].winlist))) continue; if (!cycle_visible && s->ws[a.id].r != NULL) continue; if (mv) send_to_ws(bp, r, &a); switchws(bp, r, &a); } while (a.id != r->ws->idx); DNPRINTF(SWM_D_FOCUS, "cyclews: done\n"); } void emptyws(struct binding *bp, struct swm_region *r, union arg *args) { int i; union arg a; (void)args; DNPRINTF(SWM_D_WS, "emptyws: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n", args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx); for (i = 0; i < workspace_limit; ++i) if (TAILQ_EMPTY(&r->s->ws[i].winlist)) { a.id = i; switchws(bp, r, &a); break; } DNPRINTF(SWM_D_FOCUS, "emptyws: done\n"); } void priorws(struct binding *bp, struct swm_region *r, union arg *args) { union arg a; (void)args; DNPRINTF(SWM_D_WS, "priorws: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n", args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx); if (r->ws_prior == NULL) return; a.id = r->ws_prior->idx; switchws(bp, r, &a); DNPRINTF(SWM_D_FOCUS, "priorws: done\n"); } void focusrg(struct binding *bp, struct swm_region *r, union arg *args) { int ridx = args->id, i, num_screens; struct swm_region *rr = NULL; (void)bp; num_screens = get_screen_count(); /* do nothing if we don't have more than one screen */ if (!(num_screens > 1 || outputs > 1)) return; DNPRINTF(SWM_D_FOCUS, "focusrg: id: %d\n", ridx); rr = TAILQ_FIRST(&r->s->rl); for (i = 0; i < ridx && rr != NULL; ++i) rr = TAILQ_NEXT(rr, entry); if (rr == NULL) return; focus_region(rr); center_pointer(rr); focus_flush(); DNPRINTF(SWM_D_FOCUS, "focusrg: done\n"); } void cyclerg(struct binding *bp, struct swm_region *r, union arg *args) { union arg a; struct swm_region *rr = NULL; int i, num_screens; num_screens = get_screen_count(); /* do nothing if we don't have more than one screen */ if (!(num_screens > 1 || outputs > 1)) return; i = r->s->idx; DNPRINTF(SWM_D_FOCUS, "cyclerg: id: %d, region: %d\n", args->id, i); switch (args->id) { case SWM_ARG_ID_CYCLERG_UP: case SWM_ARG_ID_CYCLERG_MOVE_UP: rr = TAILQ_NEXT(r, entry); if (rr == NULL) rr = TAILQ_FIRST(&screens[i].rl); break; case SWM_ARG_ID_CYCLERG_DOWN: case SWM_ARG_ID_CYCLERG_MOVE_DOWN: rr = TAILQ_PREV(r, swm_region_list, entry); if (rr == NULL) rr = TAILQ_LAST(&screens[i].rl, swm_region_list); break; default: return; }; if (rr == NULL) return; switch (args->id) { case SWM_ARG_ID_CYCLERG_UP: case SWM_ARG_ID_CYCLERG_DOWN: focus_region(rr); center_pointer(rr); focus_flush(); break; case SWM_ARG_ID_CYCLERG_MOVE_UP: case SWM_ARG_ID_CYCLERG_MOVE_DOWN: a.id = rr->ws->idx; switchws(bp, r, &a); break; default: return; }; DNPRINTF(SWM_D_FOCUS, "cyclerg: done\n"); } /* Sorts transients after parent. */ void sort_windows(struct ws_win_list *wl) { struct ws_win *win, *parent, *nxt; if (wl == NULL) return; for (win = TAILQ_FIRST(wl); win != TAILQ_END(wl); win = nxt) { nxt = TAILQ_NEXT(win, entry); if (TRANS(win)) { parent = find_window(win->transient); if (parent == NULL) { warnx("not possible bug"); continue; } TAILQ_REMOVE(wl, win, entry); TAILQ_INSERT_AFTER(wl, parent, win, entry); } } } void swapwin(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *target, *source; struct ws_win *cur_focus; struct ws_win_list *wl; (void)bp; DNPRINTF(SWM_D_WS, "swapwin: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n", args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx); cur_focus = r->ws->focus; if (cur_focus == NULL || FULLSCREEN(cur_focus)) return; /* Adjust stacking in floating layer. */ if (ABOVE(cur_focus)) { switch (args->id) { case SWM_ARG_ID_SWAPPREV: target = TAILQ_PREV(cur_focus, ws_win_stack, stack_entry); if (target != NULL && FLOATING(target)) { TAILQ_REMOVE(&cur_focus->ws->stack, cur_focus, stack_entry); TAILQ_INSERT_BEFORE(target, cur_focus, stack_entry); update_win_stacking(cur_focus); focus_flush(); } break; case SWM_ARG_ID_SWAPNEXT: target = TAILQ_NEXT(cur_focus, stack_entry); if (target != NULL && FLOATING(target)) { TAILQ_REMOVE(&cur_focus->ws->stack, cur_focus, stack_entry); TAILQ_INSERT_AFTER(&cur_focus->ws->stack, target, cur_focus, stack_entry); update_win_stacking(cur_focus); focus_flush(); } break; } goto out; } if (r->ws->cur_layout == &layouts[SWM_MAX_STACK]) return; clear_maximized(r->ws); source = cur_focus; wl = &source->ws->winlist; switch (args->id) { case SWM_ARG_ID_SWAPPREV: if (TRANS(source)) source = find_window(source->transient); target = TAILQ_PREV(source, ws_win_list, entry); if (target && target->transient) target = find_window(target->transient); TAILQ_REMOVE(wl, source, entry); if (target == NULL) TAILQ_INSERT_TAIL(wl, source, entry); else TAILQ_INSERT_BEFORE(target, source, entry); break; case SWM_ARG_ID_SWAPNEXT: target = TAILQ_NEXT(source, entry); /* move the parent and let the sort handle the move */ if (TRANS(source)) source = find_window(source->transient); TAILQ_REMOVE(wl, source, entry); if (target == NULL) TAILQ_INSERT_HEAD(wl, source, entry); else TAILQ_INSERT_AFTER(wl, target, source, entry); break; case SWM_ARG_ID_SWAPMAIN: target = TAILQ_FIRST(wl); if (target == source) { if (source->ws->focus_prev != NULL && source->ws->focus_prev != target) source = source->ws->focus_prev; else return; } if (target == NULL || source == NULL) return; source->ws->focus_prev = target; TAILQ_REMOVE(wl, target, entry); TAILQ_INSERT_BEFORE(source, target, entry); TAILQ_REMOVE(wl, source, entry); TAILQ_INSERT_HEAD(wl, source, entry); break; case SWM_ARG_ID_MOVELAST: TAILQ_REMOVE(wl, source, entry); TAILQ_INSERT_TAIL(wl, source, entry); break; default: DNPRINTF(SWM_D_MOVE, "swapwin: invalid id: %d\n", args->id); return; } sort_windows(wl); ewmh_update_client_list(); stack(r); center_pointer(r); focus_flush(); out: DNPRINTF(SWM_D_MOVE, "swapwin: done\n"); } struct ws_win * get_focus_prev(struct ws_win *win) { struct ws_win *winfocus = NULL; struct ws_win *cur_focus = NULL; struct ws_win_list *wl = NULL; struct workspace *ws = NULL; if (!(win && win->ws)) return NULL; ws = win->ws; wl = &ws->winlist; cur_focus = ws->focus; DNPRINTF(SWM_D_FOCUS, "get_focus_prev: win %#x, cur_focus: %#x, " "focus_prev: %#x\n", WINID(win), WINID(cur_focus), WINID(ws->focus_prev)); /* pickle, just focus on whatever */ if (cur_focus == NULL) { /* use prev_focus if valid */ if (ws->focus_prev && find_window(ws->focus_prev->id)) winfocus = ws->focus_prev; goto done; } /* if transient focus on parent */ if (TRANS(cur_focus)) { winfocus = find_window(cur_focus->transient); goto done; } /* if in max_stack try harder */ if ((win->quirks & SWM_Q_FOCUSPREV) || (ws->cur_layout->flags & SWM_L_FOCUSPREV)) { if (cur_focus != ws->focus_prev) winfocus = ws->focus_prev; else winfocus = TAILQ_PREV(win, ws_win_list, entry); if (winfocus) goto done; } DNPRINTF(SWM_D_FOCUS, "get_focus_prev: focus_close: %d\n", focus_close); if (winfocus == NULL || winfocus == win) { switch (focus_close) { case SWM_STACK_BOTTOM: TAILQ_FOREACH(winfocus, wl, entry) if (!ICONIC(winfocus) && winfocus != cur_focus) break; break; case SWM_STACK_TOP: TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry) if (!ICONIC(winfocus) && winfocus != cur_focus) break; break; case SWM_STACK_ABOVE: winfocus = TAILQ_NEXT(cur_focus, entry); while (winfocus && ICONIC(winfocus)) winfocus = TAILQ_NEXT(winfocus, entry); if (winfocus == NULL) { if (focus_close_wrap) { TAILQ_FOREACH(winfocus, wl, entry) if (!ICONIC(winfocus) && winfocus != cur_focus) break; } else { TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry) if (!ICONIC(winfocus) && winfocus != cur_focus) break; } } break; case SWM_STACK_BELOW: winfocus = TAILQ_PREV(cur_focus, ws_win_list, entry); while (winfocus && ICONIC(winfocus)) winfocus = TAILQ_PREV(winfocus, ws_win_list, entry); if (winfocus == NULL) { if (focus_close_wrap) { TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry) if (!ICONIC(winfocus) && winfocus != cur_focus) break; } else { TAILQ_FOREACH(winfocus, wl, entry) if (!ICONIC(winfocus) && winfocus != cur_focus) break; } } break; } } done: if (winfocus == NULL || (winfocus && (ICONIC(winfocus) || winfocus == cur_focus))) { if (focus_default == SWM_STACK_TOP) { TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry) if (!ICONIC(winfocus) && winfocus != cur_focus) break; } else { TAILQ_FOREACH(winfocus, wl, entry) if (!ICONIC(winfocus) && winfocus != cur_focus) break; } } kill_refs(win); return get_focus_magic(winfocus); } struct ws_win * get_region_focus(struct swm_region *r) { struct ws_win *winfocus = NULL; if (!(r && r->ws)) return NULL; if (r->ws->focus && !ICONIC(r->ws->focus)) winfocus = r->ws->focus; else if (r->ws->focus_prev && !ICONIC(r->ws->focus_prev)) winfocus = r->ws->focus_prev; else TAILQ_FOREACH(winfocus, &r->ws->winlist, entry) if (!ICONIC(winfocus)) break; return get_focus_magic(winfocus); } void focus(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *head, *cur_focus = NULL, *winfocus = NULL; struct ws_win_list *wl = NULL; struct workspace *ws = NULL; union arg a; int i; if (!(r && r->ws)) goto out; cur_focus = r->ws->focus; ws = r->ws; wl = &ws->winlist; DNPRINTF(SWM_D_FOCUS, "focus: id: %d, cur_focus: %#x\n", args->id, WINID(cur_focus)); /* Make sure an uniconified window has focus, if one exists. */ if (cur_focus == NULL) { cur_focus = TAILQ_FIRST(wl); while (cur_focus != NULL && ICONIC(cur_focus)) cur_focus = TAILQ_NEXT(cur_focus, entry); DNPRINTF(SWM_D_FOCUS, "focus: new cur_focus: %#x\n", WINID(cur_focus)); } switch (args->id) { case SWM_ARG_ID_FOCUSPREV: if (cur_focus == NULL) goto out; winfocus = cur_focus; do { winfocus = TAILQ_PREV(winfocus, ws_win_list, entry); if (winfocus == NULL) winfocus = TAILQ_LAST(wl, ws_win_list); if (winfocus == cur_focus) break; } while (winfocus && (ICONIC(winfocus) || winfocus->id == cur_focus->transient || (cur_focus->transient != XCB_WINDOW_NONE && winfocus->transient == cur_focus->transient) || (winfocus->quirks & SWM_Q_NOFOCUSCYCLE))); break; case SWM_ARG_ID_FOCUSNEXT: if (cur_focus == NULL) goto out; winfocus = cur_focus; do { winfocus = TAILQ_NEXT(winfocus, entry); if (winfocus == NULL) winfocus = TAILQ_FIRST(wl); if (winfocus == cur_focus) break; } while (winfocus && (ICONIC(winfocus) || winfocus->id == cur_focus->transient || (cur_focus->transient != XCB_WINDOW_NONE && winfocus->transient == cur_focus->transient) || (winfocus->quirks & SWM_Q_NOFOCUSCYCLE))); break; case SWM_ARG_ID_FOCUSMAIN: if (cur_focus == NULL) goto out; winfocus = TAILQ_FIRST(wl); if (winfocus == cur_focus) winfocus = cur_focus->ws->focus_prev; break; case SWM_ARG_ID_FOCUSURGENT: /* Search forward for the next urgent window. */ winfocus = NULL; head = cur_focus; for (i = 0; i <= workspace_limit; ++i) { if (head == NULL) head = TAILQ_FIRST(&r->s->ws[(ws->idx + i) % workspace_limit].winlist); while (head) { if (head == cur_focus) { if (i > 0) { winfocus = cur_focus; break; } } else if (get_urgent(head)) { winfocus = head; break; } head = TAILQ_NEXT(head, entry); } if (winfocus) break; } /* Switch ws if new focus is on a different ws. */ if (winfocus && winfocus->ws != ws) { a.id = winfocus->ws->idx; switchws(bp, r, &a); } break; default: goto out; } if (clear_maximized(ws) > 0) stack(r); focus_win(get_focus_magic(winfocus)); center_pointer(r); focus_flush(); out: DNPRINTF(SWM_D_FOCUS, "focus: done\n"); } void focus_pointer(struct binding *bp, struct swm_region *r, union arg *args) { (void)args; /* Not needed for buttons since this is already done in buttonpress. */ if (bp->type == KEYBIND) { focus_win(get_pointer_win(r->s->root)); focus_flush(); } } void cycle_layout(struct binding *bp, struct swm_region *r, union arg *args) { struct workspace *ws = r->ws; /* suppress unused warning since var is needed */ (void)bp; (void)args; DNPRINTF(SWM_D_EVENT, "cycle_layout: workspace: %d\n", ws->idx); ws->cur_layout++; if (ws->cur_layout->l_stack == NULL) ws->cur_layout = &layouts[0]; clear_maximized(ws); stack(r); bar_draw(r->bar); focus_win(get_region_focus(r)); center_pointer(r); focus_flush(); } void stack_config(struct binding *bp, struct swm_region *r, union arg *args) { struct workspace *ws = r->ws; (void)bp; DNPRINTF(SWM_D_STACK, "stack_config: id: %d workspace: %d\n", args->id, ws->idx); if (clear_maximized(ws) > 0) stack(r); if (ws->cur_layout->l_config != NULL) ws->cur_layout->l_config(ws, args->id); if (args->id != SWM_ARG_ID_STACKINIT) stack(r); bar_draw(r->bar); center_pointer(r); focus_flush(); } void stack(struct swm_region *r) { struct swm_geometry g; struct swm_region *r_prev; uint32_t val[2]; if (r == NULL) return; DNPRINTF(SWM_D_STACK, "stack: begin\n"); /* Adjust stack area for region bar and padding. */ g = r->g; g.x += region_padding; g.y += region_padding; g.w -= 2 * border_width + 2 * region_padding; g.h -= 2 * border_width + 2 * region_padding; if (bar_enabled && r->ws->bar_enabled) { if (!bar_at_bottom) g.y += bar_height; g.h -= bar_height; } DNPRINTF(SWM_D_STACK, "stack: workspace: %d (screen: " "%d, region: %d), (x,y) WxH: (%d,%d) %d x %d\n", r->ws->idx, r->s->idx, get_region_index(r), g.x, g.y, g.w, g.h); r_prev = TAILQ_PREV(r, swm_region_list, entry); if (r_prev) { /* Stack bar/input relative to prev. region. */ val[1] = XCB_STACK_MODE_ABOVE; val[0] = r_prev->id; DNPRINTF(SWM_D_STACK, "stack: region input %#x " "relative to %#x.\n", r->id, val[0]); xcb_configure_window(conn, r->id, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, val); val[0] = r_prev->bar->id; DNPRINTF(SWM_D_STACK, "stack: region bar %#x " "relative to %#x.\n", r->bar->id, val[0]); xcb_configure_window(conn, r->bar->id, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, val); } r->ws->cur_layout->l_stack(r->ws, &g); r->ws->cur_layout->l_string(r->ws); /* save r so we can track region changes */ r->ws->old_r = r; if (font_adjusted) font_adjusted--; DNPRINTF(SWM_D_STACK, "stack: end\n"); } void store_float_geom(struct ws_win *win) { if (win == NULL || win->ws->r == NULL) return; /* retain window geom and region geom */ win->g_float = win->g; win->g_float.x -= X(win->ws->r); win->g_float.y -= Y(win->ws->r); win->g_floatvalid = true; DNPRINTF(SWM_D_MISC, "store_float_geom: win %#x, g: (%d,%d)" " %d x %d, g_float: (%d,%d) %d x %d\n", win->id, X(win), Y(win), WIDTH(win), HEIGHT(win), win->g_float.x, win->g_float.y, win->g_float.w, win->g_float.h); } void load_float_geom(struct ws_win *win) { if (win == NULL || win->ws->r == NULL) return; if (win->g_floatvalid) { win->g = win->g_float; X(win) += X(win->ws->r); Y(win) += Y(win->ws->r); DNPRINTF(SWM_D_MISC, "load_float_geom: win %#x, g: (%d,%d)" "%d x %d\n", win->id, X(win), Y(win), WIDTH(win), HEIGHT(win)); } else { DNPRINTF(SWM_D_MISC, "load_float_geom: win %#x, g_float " "is not set.\n", win->id); } } void update_floater(struct ws_win *win) { struct workspace *ws; struct swm_region *r; DNPRINTF(SWM_D_MISC, "update_floater: win %#x\n", WINID(win)); if (win == NULL) return; ws = win->ws; if ((r = ws->r) == NULL) return; win->bordered = true; if (FULLSCREEN(win)) { /* _NET_WM_FULLSCREEN: fullscreen without border. */ if (!win->g_floatvalid) store_float_geom(win); win->g = r->g; win->bordered = false; } else if (MAXIMIZED(win)) { /* Maximize: like a single stacked window. */ if (!win->g_floatvalid) store_float_geom(win); win->g = r->g; if (bar_enabled && ws->bar_enabled && !maximize_hide_bar) { if (!bar_at_bottom) Y(win) += bar_height; HEIGHT(win) -= bar_height; } else if (disable_border) { win->bordered = false; } if (win->bordered) { /* Window geometry excludes frame. */ X(win) += border_width; Y(win) += border_width; HEIGHT(win) -= 2 * border_width; WIDTH(win) -= 2 * border_width; } } else { /* Normal floating window. */ /* Update geometry if new region. */ if (r != ws->old_r) load_float_geom(win); if (((win->quirks & SWM_Q_FULLSCREEN) && WIDTH(win) >= WIDTH(r) && HEIGHT(win) >= HEIGHT(r)) || ((!WS_FOCUSED(win->ws) || win->ws->focus != win) && (win->quirks & SWM_Q_MINIMALBORDER))) { /* Remove border */ win->bordered = false; } else if (!MANUAL(win)) { if (TRANS(win) && (win->quirks & SWM_Q_TRANSSZ)) { /* Adjust size on TRANSSZ quirk. */ WIDTH(win) = (double)WIDTH(r) * dialog_ratio; HEIGHT(win) = (double)HEIGHT(r) * dialog_ratio; } if (!(win->quirks & SWM_Q_ANYWHERE)) { /* * Floaters and transients are auto-centred * unless manually moved, resized or ANYWHERE * quirk is set. */ X(win) = X(r) + (WIDTH(r) - WIDTH(win)) / 2; Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2; store_float_geom(win); } } } /* Ensure at least 1 pixel of the window is in the region. */ region_containment(win, r, SWM_CW_ALLSIDES); update_window(win); } /* * Send keystrokes to terminal to decrease/increase the font size as the * window size changes. */ void adjust_font(struct ws_win *win) { if (!(win->quirks & SWM_Q_XTERM_FONTADJ) || ABOVE(win) || TRANS(win)) return; if (win->sh.width_inc && win->last_inc != win->sh.width_inc && WIDTH(win) / win->sh.width_inc < term_width && win->font_steps < SWM_MAX_FONT_STEPS) { win->font_size_boundary[win->font_steps] = (win->sh.width_inc * term_width) + win->sh.base_width; win->font_steps++; font_adjusted++; win->last_inc = win->sh.width_inc; fake_keypress(win, XK_KP_Subtract, XCB_MOD_MASK_SHIFT); } else if (win->font_steps && win->last_inc != win->sh.width_inc && WIDTH(win) > win->font_size_boundary[win->font_steps - 1]) { win->font_steps--; font_adjusted++; win->last_inc = win->sh.width_inc; fake_keypress(win, XK_KP_Add, XCB_MOD_MASK_SHIFT); } } #define SWAPXY(g) do { \ int tmp; \ tmp = (g)->y; (g)->y = (g)->x; (g)->x = tmp; \ tmp = (g)->h; (g)->h = (g)->w; (g)->w = tmp; \ } while (0) void stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip) { struct swm_geometry cell, r_g = *g; struct ws_win *win; int i = 0, j = 0, s = 0, stacks = 0; int w_inc = 1, h_inc, w_base = 1, h_base; int hrh = 0, extra = 0, h_slice = 0, last_h = 0; int split = 0, colno = 0; int winno, mwin = 0, msize = 0; int remain, missing, v_slice, mscale; bool bordered = true, reconfigure = false; /* * cell: geometry for window, including frame. * mwin: # of windows in master area. * mscale: size increment of master area. * stacks: # of stack columns */ DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d, rot: %s, " "flip: %s\n", ws->idx, YESNO(rot), YESNO(flip)); memset(&cell, 0, sizeof(cell)); /* Prepare tiling variables, if needed. */ if ((winno = count_win(ws, false)) > 0) { /* Find first tiled window. */ TAILQ_FOREACH(win, &ws->winlist, entry) if (!FLOATING(win) && !ICONIC(win)) break; /* Take into account size hints of first tiled window. */ if (rot) { w_inc = win->sh.width_inc; w_base = win->sh.base_width; mwin = ws->l_state.horizontal_mwin; mscale = ws->l_state.horizontal_msize; stacks = ws->l_state.horizontal_stacks; SWAPXY(&r_g); } else { w_inc = win->sh.height_inc; w_base = win->sh.base_height; mwin = ws->l_state.vertical_mwin; mscale = ws->l_state.vertical_msize; stacks = ws->l_state.vertical_stacks; } cell = r_g; cell.x += border_width; cell.y += border_width; if (stacks > winno - mwin) stacks = winno - mwin; if (stacks < 1) stacks = 1; h_slice = r_g.h / SWM_H_SLICE; if (mwin && winno > mwin) { v_slice = r_g.w / SWM_V_SLICE; split = mwin; colno = split; cell.w = v_slice * mscale; if (w_inc > 1 && w_inc < v_slice) { /* Adjust for requested size increment. */ remain = (cell.w - w_base) % w_inc; cell.w -= remain; } msize = cell.w; if (flip) cell.x += r_g.w - msize; } else { msize = -2; colno = split = winno / stacks; cell.w = ((r_g.w - (stacks * 2 * border_width) + 2 * border_width) / stacks); } hrh = r_g.h / colno; extra = r_g.h - (colno * hrh); cell.h = hrh - 2 * border_width; i = j = 0, s = stacks; } /* Update window geometry. */ TAILQ_FOREACH(win, &ws->winlist, entry) { if (ICONIC(win)) continue; if (FLOATING(win)) { update_floater(win); continue; } /* Tiled. */ if (split && i == split) { colno = (winno - mwin) / stacks; if (s <= (winno - mwin) % stacks) colno++; split += colno; hrh = r_g.h / colno; extra = r_g.h - (colno * hrh); if (!flip) cell.x += cell.w + 2 * border_width + tile_gap; cell.w = (r_g.w - msize - (stacks * (2 * border_width + tile_gap))) / stacks; if (s == 1) cell.w += (r_g.w - msize - (stacks * (2 * border_width + tile_gap))) % stacks; if (flip) cell.x -= cell.w + 2 * border_width + tile_gap; s--; j = 0; } cell.h = hrh - 2 * border_width - tile_gap; if (rot) { h_inc = win->sh.width_inc; h_base = win->sh.base_width; } else { h_inc = win->sh.height_inc; h_base = win->sh.base_height; } if (j == colno - 1) { cell.h = hrh + extra; } else if (h_inc > 1 && h_inc < h_slice) { /* adjust for window's requested size increment */ remain = (cell.h - h_base) % h_inc; missing = h_inc - remain; if (missing <= extra || j == 0) { extra -= missing; cell.h += missing; } else { cell.h -= remain; extra += remain; } } if (j == 0) cell.y = r_g.y + border_width; else cell.y += last_h + 2 * border_width + tile_gap; /* Window coordinates exclude frame. */ if (winno > 1 || !disable_border || (bar_enabled && ws->bar_enabled)) { bordered = true; } else { bordered = false; } if (rot) { if (X(win) != cell.y || Y(win) != cell.x || WIDTH(win) != cell.h || HEIGHT(win) != cell.w) { reconfigure = true; X(win) = cell.y; Y(win) = cell.x; WIDTH(win) = cell.h; HEIGHT(win) = cell.w; } } else { if (X(win) != cell.x || Y(win) != cell.y || WIDTH(win) != cell.w || HEIGHT(win) != cell.h) { reconfigure = true; X(win) = cell.x; Y(win) = cell.y; WIDTH(win) = cell.w; HEIGHT(win) = cell.h; } } if (!bordered) { X(win) -= border_width; Y(win) -= border_width; WIDTH(win) += 2 * border_width; HEIGHT(win) += 2 * border_width; } if (bordered != win->bordered) { reconfigure = true; win->bordered = bordered; } if (reconfigure) { adjust_font(win); update_window(win); } last_h = cell.h; i++; j++; } /* Stack all windows from bottom up. */ TAILQ_FOREACH_REVERSE(win, &ws->stack, ws_win_stack, stack_entry) if (!ICONIC(win)) update_win_stacking(win); /* Map all windows from top down. */ TAILQ_FOREACH(win, &ws->stack, stack_entry) if (!ICONIC(win)) map_window(win); DNPRINTF(SWM_D_STACK, "stack_master: done\n"); } void vertical_config(struct workspace *ws, int id) { DNPRINTF(SWM_D_STACK, "vertical_config: id: %d, workspace: %d\n", id, ws->idx); switch (id) { case SWM_ARG_ID_STACKRESET: case SWM_ARG_ID_STACKINIT: ws->l_state.vertical_msize = SWM_V_SLICE / 2; ws->l_state.vertical_mwin = 1; ws->l_state.vertical_stacks = 1; break; case SWM_ARG_ID_MASTERSHRINK: if (ws->l_state.vertical_msize > 1) ws->l_state.vertical_msize--; break; case SWM_ARG_ID_MASTERGROW: if (ws->l_state.vertical_msize < SWM_V_SLICE - 1) ws->l_state.vertical_msize++; break; case SWM_ARG_ID_MASTERADD: ws->l_state.vertical_mwin++; break; case SWM_ARG_ID_MASTERDEL: if (ws->l_state.vertical_mwin > 0) ws->l_state.vertical_mwin--; break; case SWM_ARG_ID_STACKBALANCE: ws->l_state.vertical_msize = SWM_V_SLICE / (ws->l_state.vertical_stacks + 1); break; case SWM_ARG_ID_STACKINC: ws->l_state.vertical_stacks++; break; case SWM_ARG_ID_STACKDEC: if (ws->l_state.vertical_stacks > 1) ws->l_state.vertical_stacks--; break; case SWM_ARG_ID_FLIPLAYOUT: ws->l_state.vertical_flip = !ws->l_state.vertical_flip; break; default: return; } } void vertical_stack(struct workspace *ws, struct swm_geometry *g) { DNPRINTF(SWM_D_STACK, "vertical_stack: workspace: %d\n", ws->idx); stack_master(ws, g, 0, ws->l_state.vertical_flip); } void horizontal_config(struct workspace *ws, int id) { DNPRINTF(SWM_D_STACK, "horizontal_config: workspace: %d\n", ws->idx); switch (id) { case SWM_ARG_ID_STACKRESET: case SWM_ARG_ID_STACKINIT: ws->l_state.horizontal_mwin = 1; ws->l_state.horizontal_msize = SWM_H_SLICE / 2; ws->l_state.horizontal_stacks = 1; break; case SWM_ARG_ID_MASTERSHRINK: if (ws->l_state.horizontal_msize > 1) ws->l_state.horizontal_msize--; break; case SWM_ARG_ID_MASTERGROW: if (ws->l_state.horizontal_msize < SWM_H_SLICE - 1) ws->l_state.horizontal_msize++; break; case SWM_ARG_ID_MASTERADD: ws->l_state.horizontal_mwin++; break; case SWM_ARG_ID_MASTERDEL: if (ws->l_state.horizontal_mwin > 0) ws->l_state.horizontal_mwin--; break; case SWM_ARG_ID_STACKBALANCE: ws->l_state.horizontal_msize = SWM_H_SLICE / (ws->l_state.horizontal_stacks + 1); break; case SWM_ARG_ID_STACKINC: ws->l_state.horizontal_stacks++; break; case SWM_ARG_ID_STACKDEC: if (ws->l_state.horizontal_stacks > 1) ws->l_state.horizontal_stacks--; break; case SWM_ARG_ID_FLIPLAYOUT: ws->l_state.horizontal_flip = !ws->l_state.horizontal_flip; break; default: return; } } void horizontal_stack(struct workspace *ws, struct swm_geometry *g) { DNPRINTF(SWM_D_STACK, "horizontal_stack: workspace: %d\n", ws->idx); stack_master(ws, g, 1, ws->l_state.horizontal_flip); } /* fullscreen view */ void max_stack(struct workspace *ws, struct swm_geometry *g) { struct swm_geometry gg = *g; struct ws_win *w, *win = NULL, *parent = NULL, *tmpw; int winno; DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx); if (ws == NULL) return; winno = count_win(ws, false); if (winno == 0 && count_win(ws, true) == 0) return; /* Figure out which top level window should be visible. */ if (ws->focus_pending) win = ws->focus_pending; else if (ws->focus) win = ws->focus; else if (ws->focus_prev) win = ws->focus_prev; else win = TAILQ_FIRST(&ws->winlist); DNPRINTF(SWM_D_STACK, "max_stack: focus_pending: %#x, focus: %#x, " "focus_prev: %#x, first: %#x, win: %#x\n", WINID(ws->focus_pending), WINID(ws->focus), WINID(ws->focus_prev), WINID(TAILQ_FIRST(&ws->winlist)), win->id); /* Update window geometry. */ TAILQ_FOREACH(w, &ws->winlist, entry) { if (ICONIC(w)) continue; if (TRANS(w) || FULLSCREEN(w)) { update_floater(w); continue; } /* Set maximized flag for all maxed windows. */ if (!MAXIMIZED(w)) { /* Preserve floating geometry. */ if (ABOVE(w)) store_float_geom(w); ewmh_apply_flags(w, w->ewmh_flags | EWMH_F_MAXIMIZED); ewmh_update_wm_state(w); } /* Only reconfigure if necessary. */ if (X(w) != gg.x || Y(w) != gg.y || WIDTH(w) != gg.w || HEIGHT(w) != gg.h) { w->g = gg; if (disable_border && !(bar_enabled && ws->bar_enabled)) { w->bordered = false; WIDTH(w) += 2 * border_width; HEIGHT(w) += 2 * border_width; } else { w->bordered = true; X(w) += border_width; Y(w) += border_width; } update_window(w); } } /* If transient, stack parent and its children. */ if (TRANS(win) && (parent = find_window(win->transient))) { raise_window(parent); TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw) if (w->transient == parent->id) raise_window(w); } /* Make sure focus window is on top. */ raise_window(win); /* Stack any children of focus window. */ TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw) if (w->transient == win->id) raise_window(w); /* Map all windows. */ TAILQ_FOREACH(w, &ws->stack, stack_entry) if (!ICONIC(w)) map_window(w); } void send_to_rg(struct binding *bp, struct swm_region *r, union arg *args) { int ridx = args->id, i, num_screens; struct swm_region *rr = NULL; union arg a; num_screens = get_screen_count(); /* do nothing if we don't have more than one screen */ if (!(num_screens > 1 || outputs > 1)) return; DNPRINTF(SWM_D_FOCUS, "send_to_rg: id: %d\n", ridx); rr = TAILQ_FIRST(&r->s->rl); for (i = 0; i < ridx && rr != NULL; ++i) rr = TAILQ_NEXT(rr, entry); if (rr == NULL) return; a.id = rr->ws->idx; send_to_ws(bp, r, &a); } struct swm_region * region_under(struct swm_screen *s, int x, int y) { struct swm_region *r = NULL; TAILQ_FOREACH(r, &s->rl, entry) { DNPRINTF(SWM_D_MISC, "region_under: ws: %d, region g: (%d,%d) " "%d x %d, coords: (%d,%d)\n", r->ws->idx, X(r), Y(r), WIDTH(r), HEIGHT(r), x, y); if (X(r) <= x && x < MAX_X(r)) if (Y(r) <= y && y < MAX_Y(r)) return (r); } return (NULL); } /* Transfer focused window to target workspace and focus. */ void send_to_ws(struct binding *bp, struct swm_region *r, union arg *args) { int wsid = args->id; struct ws_win *win = NULL; (void)bp; if (r && r->ws && r->ws->focus) win = r->ws->focus; else return; DNPRINTF(SWM_D_MOVE, "send_to_ws: win %#x, ws %d\n", win->id, wsid); if (wsid < 0 || wsid >= workspace_limit) return; if (win->ws->idx == wsid) return; win_to_ws(win, wsid, true); /* Set new focus on target ws. */ if (focus_mode != SWM_FOCUS_FOLLOW) { win->ws->focus_prev = win->ws->focus; win->ws->focus = win; win->ws->focus_pending = NULL; if (win->ws->focus_prev) draw_frame(win->ws->focus_prev); } DNPRINTF(SWM_D_STACK, "send_to_ws: focus_pending: %#x, focus: %#x, " "focus_prev: %#x, first: %#x, win: %#x\n", WINID(r->ws->focus_pending), WINID(r->ws->focus), WINID(r->ws->focus_prev), WINID(TAILQ_FIRST(&r->ws->winlist)), win->id); ewmh_apply_flags(win, win->ewmh_flags & ~EWMH_F_MAXIMIZED); ewmh_update_wm_state(win); /* Restack focused region. */ stack(r); /* If destination ws has a region, restack. */ if (win->ws->r) { if (FLOATING(win)) load_float_geom(win); stack(win->ws->r); } /* Set new focus on current ws. */ if (focus_mode != SWM_FOCUS_FOLLOW) { if (r->ws->focus != NULL) { focus_win(r->ws->focus); } else { DNPRINTF(SWM_D_FOCUS, "send_to_ws: set_input_focus: " "%#x, revert-to: parent, time: 0\n", r->id); xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, r->id, XCB_CURRENT_TIME); bar_draw(r->bar); } } center_pointer(r); focus_flush(); } /* Transfer focused window to region-relative workspace and focus. */ void send_to_rg_relative(struct binding *bp, struct swm_region *r, union arg *args) { union arg args_abs; struct swm_region *r_other; if (args->id == 1) { r_other = TAILQ_NEXT(r, entry); if (r_other == NULL) r_other = TAILQ_FIRST(&r->s->rl); } else { r_other = TAILQ_PREV(r, swm_region_list, entry); if (r_other == NULL) r_other = TAILQ_LAST(&r->s->rl, swm_region_list); } /* Map relative to absolute */ args_abs = *args; args_abs.id = r_other->ws->idx; send_to_ws(bp, r, &args_abs); } void win_to_ws(struct ws_win *win, int wsid, bool unfocus) { struct ws_win *parent; struct workspace *ws, *nws, *pws; if (wsid < 0 || wsid >= workspace_limit) return; if (win->ws->idx == wsid) return; ws = win->ws; nws = &win->s->ws[wsid]; DNPRINTF(SWM_D_MOVE, "win_to_ws: win %#x, ws %d -> %d\n", win->id, ws->idx, wsid); /* Cleanup focus on source ws. */ if (focus_mode != SWM_FOCUS_FOLLOW && (ws->focus == win || ws->focus_pending == win)) ws->focus_pending = get_focus_prev(win); /* Move the parent if this is a transient window. */ if (TRANS(win)) { parent = find_window(win->transient); if (parent) { pws = parent->ws; /* Set new focus in parent's ws if needed. */ if (pws->focus == parent) { if (focus_mode != SWM_FOCUS_FOLLOW) pws->focus_pending = get_focus_prev(parent); unfocus_win(parent); if (focus_mode != SWM_FOCUS_FOLLOW) { pws->focus = pws->focus_pending; pws->focus_pending = NULL; } } /* Don't unmap parent if new ws is visible */ if (nws->r == NULL) unmap_window(parent); /* Transfer */ TAILQ_REMOVE(&ws->winlist, parent, entry); TAILQ_REMOVE(&ws->stack, parent, stack_entry); TAILQ_INSERT_TAIL(&nws->winlist, parent, entry); TAILQ_INSERT_TAIL(&nws->stack, parent, stack_entry); parent->ws = nws; DNPRINTF(SWM_D_PROP, "win_to_ws: set property: " "_NET_WM_DESKTOP: %d\n", wsid); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, parent->id, ewmh[_NET_WM_DESKTOP].atom, XCB_ATOM_CARDINAL, 32, 1, &wsid); } } if (unfocus) unfocus_win(win); if (ws->focus_prev == win) ws->focus_prev = NULL; if (focus_mode != SWM_FOCUS_FOLLOW && ws->focus_pending != NULL) { ws->focus = ws->focus_pending; ws->focus_pending = NULL; } /* Don't unmap if new ws is visible */ if (nws->r == NULL) unmap_window(win); /* Transfer */ TAILQ_REMOVE(&ws->winlist, win, entry); TAILQ_REMOVE(&ws->stack, win, stack_entry); TAILQ_INSERT_TAIL(&nws->winlist, win, entry); TAILQ_INSERT_TAIL(&nws->stack, win, stack_entry); win->ws = nws; /* Update the window's workspace property: _NET_WM_DESKTOP */ DNPRINTF(SWM_D_PROP, "win_to_ws: set property: " "_NET_WM_DESKTOP: %d\n", wsid); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id, ewmh[_NET_WM_DESKTOP].atom, XCB_ATOM_CARDINAL, 32, 1, &wsid); ewmh_update_client_list(); DNPRINTF(SWM_D_MOVE, "win_to_ws: done.\n"); } void pressbutton(struct binding *bp, struct swm_region *r, union arg *args) { /* suppress unused warning since var is needed */ (void)bp; (void)r; xcb_test_fake_input(conn, XCB_BUTTON_PRESS, args->id, XCB_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0); xcb_test_fake_input(conn, XCB_BUTTON_RELEASE, args->id, XCB_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0); } void raise_focus(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *win; uint32_t val; /* Suppress warning. */ (void)bp; (void)args; if (r == NULL || r->ws == NULL || r->ws->focus == NULL) return; win = r->ws->focus; r->ws->focus_raise = win; raise_window(win); /* Temporarily override stacking order also in the stack */ if (!FLOATING(win)) { val = XCB_STACK_MODE_ABOVE; xcb_configure_window(conn, win->frame, XCB_CONFIG_WINDOW_STACK_MODE, &val); } } void raise_toggle(struct binding *bp, struct swm_region *r, union arg *args) { /* Suppress warning. */ (void)bp; (void)args; if (r == NULL || r->ws == NULL) return; if (r->ws->focus && MAXIMIZED(r->ws->focus)) return; r->ws->always_raise = !r->ws->always_raise; /* Update focused win stacking order based on new always_raise value. */ raise_window(r->ws->focus); focus_flush(); } void iconify(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *w; /* Suppress warning. */ (void)bp; (void)args; if ((w = r->ws->focus) == NULL) return; ewmh_apply_flags(w, w->ewmh_flags | EWMH_F_HIDDEN); ewmh_update_wm_state(w); stack(r); focus_flush(); } char * get_win_name(xcb_window_t win) { char *name = NULL; xcb_get_property_cookie_t c; xcb_get_property_reply_t *r; /* First try _NET_WM_NAME for UTF-8. */ c = xcb_get_property(conn, 0, win, ewmh[_NET_WM_NAME].atom, XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX); r = xcb_get_property_reply(conn, c, NULL); if (r && r->type == XCB_NONE) { free(r); /* Use WM_NAME instead; no UTF-8. */ c = xcb_get_property(conn, 0, win, XCB_ATOM_WM_NAME, XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX); r = xcb_get_property_reply(conn, c, NULL); } if (r && r->type != XCB_NONE && r->length > 0) name = strndup(xcb_get_property_value(r), xcb_get_property_value_length(r)); else name = strdup(""); if (name == NULL) err(1, "get_win_name: failed to allocate memory."); free(r); return (name); } void uniconify(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *win; FILE *lfile; char *name; int count = 0; (void)bp; DNPRINTF(SWM_D_MISC, "uniconify\n"); if (r == NULL || r->ws == NULL) return; /* make sure we have anything to uniconify */ TAILQ_FOREACH(win, &r->ws->winlist, entry) { if (win->ws == NULL) continue; /* should never happen */ if (!ICONIC(win)) continue; count++; } DNPRINTF(SWM_D_MISC, "uniconify: count: %d\n", count); if (count == 0) return; search_r = r; search_resp_action = SWM_SEARCH_UNICONIFY; spawn_select(r, args, "search", &searchpid); if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL) return; TAILQ_FOREACH(win, &r->ws->winlist, entry) { if (win->ws == NULL) continue; /* should never happen */ if (!ICONIC(win)) continue; name = get_win_name(win->id); fprintf(lfile, "%s.%u\n", name, win->id); free(name); } fclose(lfile); } void name_workspace(struct binding *bp, struct swm_region *r, union arg *args) { FILE *lfile; (void)bp; DNPRINTF(SWM_D_MISC, "name_workspace\n"); if (r == NULL) return; search_r = r; search_resp_action = SWM_SEARCH_NAME_WORKSPACE; spawn_select(r, args, "name_workspace", &searchpid); if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL) return; fprintf(lfile, "%s", ""); fclose(lfile); } void search_workspace(struct binding *bp, struct swm_region *r, union arg *args) { int i; struct workspace *ws; FILE *lfile; (void)bp; DNPRINTF(SWM_D_MISC, "search_workspace\n"); if (r == NULL) return; search_r = r; search_resp_action = SWM_SEARCH_SEARCH_WORKSPACE; spawn_select(r, args, "search", &searchpid); if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL) return; for (i = 0; i < workspace_limit; i++) { ws = &r->s->ws[i]; if (ws == NULL) continue; fprintf(lfile, "%d%s%s\n", ws->idx + 1, (ws->name ? ":" : ""), (ws->name ? ws->name : "")); } fclose(lfile); } void search_win_cleanup(void) { struct search_window *sw = NULL; #ifndef __clang_analyzer__ /* Suppress false warnings. */ while ((sw = TAILQ_FIRST(&search_wl)) != NULL) { xcb_destroy_window(conn, sw->indicator); TAILQ_REMOVE(&search_wl, sw, entry); free(sw); } #endif } void search_win(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *win = NULL; struct search_window *sw = NULL; xcb_window_t w; uint32_t wa[3]; xcb_screen_t *screen; int i, width, height; char s[11]; FILE *lfile; size_t len; XftDraw *draw; XGlyphInfo info; GC l_draw; XGCValues l_gcv; XRectangle l_ibox, l_lbox; (void)bp; DNPRINTF(SWM_D_MISC, "search_win\n"); search_r = r; search_resp_action = SWM_SEARCH_SEARCH_WINDOW; spawn_select(r, args, "search", &searchpid); if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL) return; if ((screen = get_screen(r->s->idx)) == NULL) errx(1, "ERROR: can't get screen %d.", r->s->idx); i = 1; TAILQ_FOREACH(win, &r->ws->winlist, entry) { if (ICONIC(win)) continue; sw = calloc(1, sizeof(struct search_window)); if (sw == NULL) { warn("search_win: calloc"); fclose(lfile); search_win_cleanup(); return; } sw->idx = i; sw->win = win; snprintf(s, sizeof s, "%d", i); len = strlen(s); w = xcb_generate_id(conn); wa[0] = r->s->c[SWM_S_COLOR_FOCUS].pixel; wa[1] = r->s->c[SWM_S_COLOR_UNFOCUS].pixel; wa[2] = screen->default_colormap; if (bar_font_legacy) { XmbTextExtents(bar_fs, s, len, &l_ibox, &l_lbox); width = l_lbox.width + 4; height = bar_fs_extents->max_logical_extent.height + 4; } else { XftTextExtentsUtf8(display, bar_font, (FcChar8 *)s, len, &info); width = info.width + 4; height = bar_font->height + 4; } xcb_create_window(conn, screen->root_depth, w, win->frame, 0, 0, width, height, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP, wa); xcb_map_window(conn, w); sw->indicator = w; TAILQ_INSERT_TAIL(&search_wl, sw, entry); if (bar_font_legacy) { l_gcv.graphics_exposures = 0; l_draw = XCreateGC(display, w, 0, &l_gcv); XSetForeground(display, l_draw, r->s->c[SWM_S_COLOR_BAR].pixel); DRAWSTRING(display, w, bar_fs, l_draw, 2, (bar_fs_extents->max_logical_extent.height - l_lbox.height) / 2 - l_lbox.y, s, len); XFreeGC(display, l_draw); } else { draw = XftDrawCreate(display, w, DefaultVisual(display, r->s->idx), DefaultColormap(display, r->s->idx)); XftDrawStringUtf8(draw, &search_font_color, bar_font, 2, (HEIGHT(r->bar) + bar_font->height) / 2 - bar_font->descent, (FcChar8 *)s, len); XftDrawDestroy(draw); } DNPRINTF(SWM_D_MISC, "search_win: mapped win %#x\n", w); fprintf(lfile, "%d\n", i); i++; } fclose(lfile); xcb_flush(conn); } void search_resp_uniconify(const char *resp, size_t len) { char *name; struct ws_win *win; char *s; DNPRINTF(SWM_D_MISC, "search_resp_uniconify: resp: %s\n", resp); TAILQ_FOREACH(win, &search_r->ws->winlist, entry) { if (!ICONIC(win)) continue; name = get_win_name(win->id); if (asprintf(&s, "%s.%u", name, win->id) == -1) { free(name); continue; } free(name); if (strncmp(s, resp, len) == 0) { /* XXX this should be a callback to generalize */ ewmh_apply_flags(win, win->ewmh_flags & ~EWMH_F_HIDDEN); ewmh_update_wm_state(win); stack(search_r); free(s); break; } free(s); } } void search_resp_name_workspace(const char *resp, size_t len) { struct workspace *ws; DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: resp: %s\n", resp); if (search_r->ws == NULL) return; ws = search_r->ws; if (ws->name) { free(search_r->ws->name); search_r->ws->name = NULL; } if (len > 1) { ws->name = strdup(resp); if (ws->name == NULL) { DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: " "strdup: %s", strerror(errno)); return; } ewmh_update_desktop_names(); ewmh_get_desktop_names(); } } void ewmh_update_desktop_names(void) { char *name_list = NULL, *p; int num_screens, i, j, len = 0, tot = 0; num_screens = get_screen_count(); for (i = 0; i < num_screens; ++i) { for (j = 0; j < workspace_limit; ++j) { if (screens[i].ws[j].name != NULL) len += strlen(screens[i].ws[j].name); ++len; } if ((name_list = calloc(len, sizeof(char))) == NULL) err(1, "update_desktop_names: calloc: failed to " "allocate memory."); p = name_list; for (j = 0; j < workspace_limit; ++j) { if (screens[i].ws[j].name != NULL) { len = strlen(screens[i].ws[j].name); memcpy(p, screens[i].ws[j].name, len); } else { len = 0; } p += len + 1; tot += len + 1; } xcb_change_property(conn, XCB_PROP_MODE_REPLACE, screens[i].root, ewmh[_NET_DESKTOP_NAMES].atom, a_utf8_string, 8, tot, name_list); free(name_list); name_list = NULL; } free(name_list); } void ewmh_get_desktop_names(void) { char *names = NULL; xcb_get_property_cookie_t c; xcb_get_property_reply_t *r; int num_screens, i, j, n, k; num_screens = get_screen_count(); for (i = 0; i < num_screens; ++i) { for (j = 0; j < workspace_limit; ++j) { free(screens[i].ws[j].name); screens[i].ws[j].name = NULL; } c = xcb_get_property(conn, 0, screens[i].root, ewmh[_NET_DESKTOP_NAMES].atom, a_utf8_string, 0, UINT32_MAX); r = xcb_get_property_reply(conn, c, NULL); if (r == NULL) continue; names = xcb_get_property_value(r); n = xcb_get_property_value_length(r); for (j = 0, k = 0; j < n; ++j) { if (*(names + j) != '\0') { screens[i].ws[k].name = strdup(names + j); j += strlen(names + j); } ++k; } free(r); } } void ewmh_update_client_list(void) { struct ws_win *win; int num_screens, i, j, k = 0, count = 0; xcb_window_t *wins; num_screens = get_screen_count(); for (i = 0; i < num_screens; ++i) { for (j = 0; j < workspace_limit; ++j) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) ++count; DNPRINTF(SWM_D_PROP, "ewmh_update_client_list: win count: %d\n", count); if (count == 0) continue; wins = calloc(count, sizeof(xcb_window_t)); if (wins == NULL) err(1, "ewmh_update_client_list: calloc: failed to " "allocate memory."); for (j = 0, k = 0; j < workspace_limit; ++j) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) wins[k++] = win->id; xcb_change_property(conn, XCB_PROP_MODE_REPLACE, screens[i].root, ewmh[_NET_CLIENT_LIST].atom, XCB_ATOM_WINDOW, 32, count, wins); free(wins); } } void ewmh_update_current_desktop(void) { int num_screens, i; num_screens = get_screen_count(); for (i = 0; i < num_screens; ++i) { if (screens[i].r_focus) xcb_change_property(conn, XCB_PROP_MODE_REPLACE, screens[i].root, ewmh[_NET_CURRENT_DESKTOP].atom, XCB_ATOM_CARDINAL, 32, 1, &screens[i].r_focus->ws->idx); } } void ewmh_update_desktops(void) { int num_screens, i, j; uint32_t *vals; vals = calloc(workspace_limit * 2, sizeof(uint32_t)); if (vals == NULL) err(1, "ewmh_update_desktops: calloc: failed to allocate " "memory."); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { xcb_change_property(conn, XCB_PROP_MODE_REPLACE, screens[i].root, ewmh[_NET_NUMBER_OF_DESKTOPS].atom, XCB_ATOM_CARDINAL, 32, 1, &workspace_limit); for (j = 0; j < workspace_limit; ++j) { if (screens[i].ws[j].r != NULL) { vals[j * 2] = X(screens[i].ws[j].r); vals[j * 2 + 1] = Y(screens[i].ws[j].r); } else if (screens[i].ws[j].old_r != NULL) { vals[j * 2] = X(screens[i].ws[j].old_r); vals[j * 2 + 1] = Y(screens[i].ws[j].old_r); } else { vals[j * 2] = vals[j * 2 + 1] = 0; } } xcb_change_property(conn, XCB_PROP_MODE_REPLACE, screens[i].root, ewmh[_NET_DESKTOP_VIEWPORT].atom, XCB_ATOM_CARDINAL, 32, workspace_limit * 2, vals); } free(vals); } void search_resp_search_workspace(const char *resp) { char *p, *q; int ws_idx; const char *errstr; union arg a; DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: resp: %s\n", resp); q = strdup(resp); if (q == NULL) { DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: strdup: %s", strerror(errno)); return; } p = strchr(q, ':'); if (p != NULL) *p = '\0'; ws_idx = (int)strtonum(q, 1, workspace_limit, &errstr); if (errstr) { DNPRINTF(SWM_D_MISC, "workspace idx is %s: %s", errstr, q); free(q); return; } free(q); a.id = ws_idx - 1; switchws(NULL, search_r, &a); } void search_resp_search_window(const char *resp) { char *s; int idx; const char *errstr; struct search_window *sw; DNPRINTF(SWM_D_MISC, "search_resp_search_window: resp: %s\n", resp); s = strdup(resp); if (s == NULL) { DNPRINTF(SWM_D_MISC, "search_resp_search_window: strdup: %s", strerror(errno)); return; } idx = (int)strtonum(s, 1, INT_MAX, &errstr); if (errstr) { DNPRINTF(SWM_D_MISC, "window idx is %s: %s", errstr, s); free(s); return; } free(s); TAILQ_FOREACH(sw, &search_wl, entry) if (idx == sw->idx) { focus_win(sw->win); break; } } #define MAX_RESP_LEN 1024 void search_do_resp(void) { ssize_t rbytes; char *resp; size_t len; DNPRINTF(SWM_D_MISC, "search_do_resp:\n"); search_resp = 0; searchpid = 0; if ((resp = calloc(1, MAX_RESP_LEN + 1)) == NULL) { warn("search: calloc"); goto done; } rbytes = read(select_resp_pipe[0], resp, MAX_RESP_LEN); if (rbytes <= 0) { warn("search: read error"); goto done; } resp[rbytes] = '\0'; /* XXX: * Older versions of dmenu (Atleast pre 4.4.1) do not send a * newline, so work around that by sanitizing the resp now. */ resp[strcspn(resp, "\n")] = '\0'; len = strlen(resp); switch (search_resp_action) { case SWM_SEARCH_UNICONIFY: search_resp_uniconify(resp, len); break; case SWM_SEARCH_NAME_WORKSPACE: search_resp_name_workspace(resp, len); break; case SWM_SEARCH_SEARCH_WORKSPACE: search_resp_search_workspace(resp); break; case SWM_SEARCH_SEARCH_WINDOW: search_resp_search_window(resp); break; } done: if (search_resp_action == SWM_SEARCH_SEARCH_WINDOW) search_win_cleanup(); search_resp_action = SWM_SEARCH_NONE; close(select_resp_pipe[0]); free(resp); xcb_flush(conn); } void wkill(struct binding *bp, struct swm_region *r, union arg *args) { (void)bp; DNPRINTF(SWM_D_MISC, "wkill: win %#x, id: %d\n", WINID(r->ws->focus), args->id); if (r->ws->focus == NULL) return; if (args->id == SWM_ARG_ID_KILLWINDOW) xcb_kill_client(conn, r->ws->focus->id); else if (r->ws->focus->can_delete) client_msg(r->ws->focus, a_delete, 0); focus_flush(); } int clear_maximized(struct workspace *ws) { struct ws_win *w; int count = 0; /* Clear any maximized win(s) on ws, from bottom up. */ TAILQ_FOREACH_REVERSE(w, &ws->stack, ws_win_stack, stack_entry) if (MAXIMIZED(w)) { ewmh_apply_flags(w, w->ewmh_flags & ~EWMH_F_MAXIMIZED); ewmh_update_wm_state(w); ++count; } return count; } void maximize_toggle(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *w = r->ws->focus; /* suppress unused warning since var is needed */ (void)bp; (void)args; if (w == NULL) return; DNPRINTF(SWM_D_MISC, "maximize_toggle: win %#x\n", w->id); if (FULLSCREEN(w)) return; if (w->ws->cur_layout == &layouts[SWM_MAX_STACK]) return; ewmh_apply_flags(w, w->ewmh_flags ^ EWMH_F_MAXIMIZED); ewmh_update_wm_state(w); stack(r); if (w == w->ws->focus) focus_win(w); center_pointer(r); focus_flush(); DNPRINTF(SWM_D_MISC, "maximize_toggle: done\n"); } void floating_toggle(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *w = r->ws->focus; /* suppress unused warning since var is needed */ (void)bp; (void)args; if (w == NULL) return; DNPRINTF(SWM_D_MISC, "floating_toggle: win %#x\n", w->id); if (FULLSCREEN(w) || TRANS(w)) return; if (w->ws->cur_layout == &layouts[SWM_MAX_STACK]) return; ewmh_apply_flags(w, w->ewmh_flags ^ EWMH_F_ABOVE); ewmh_update_wm_state(w); stack(r); if (w == w->ws->focus) focus_win(w); center_pointer(r); focus_flush(); DNPRINTF(SWM_D_MISC, "floating_toggle: done\n"); } void fullscreen_toggle(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *w = r->ws->focus; /* suppress unused warning since var is needed */ (void)bp; (void)args; if (w == NULL) return; DNPRINTF(SWM_D_MISC, "fullscreen_toggle: win %#x\n", w->id); ewmh_apply_flags(w, w->ewmh_flags ^ EWMH_F_FULLSCREEN); ewmh_update_wm_state(w); stack(r); if (w == w->ws->focus) focus_win(w); center_pointer(r); focus_flush(); DNPRINTF(SWM_D_MISC, "fullscreen_toggle: done\n"); } void region_containment(struct ws_win *win, struct swm_region *r, int opts) { struct swm_geometry g = r->g; int rt, lt, tp, bm, bw; bw = (opts & SWM_CW_SOFTBOUNDARY) ? boundary_width : 0; /* * Perpendicular distance of each side of the window to the respective * side of the region boundary. Positive values indicate the side of * the window has passed beyond the region boundary. */ rt = opts & SWM_CW_RIGHT ? MAX_X(win) - MAX_X(r) : bw; lt = opts & SWM_CW_LEFT ? X(r) - X(win) : bw; bm = opts & SWM_CW_BOTTOM ? MAX_Y(win) - MAX_Y(r) : bw; tp = opts & SWM_CW_TOP ? Y(r) - Y(win) : bw; DNPRINTF(SWM_D_MISC, "region_containment: win %#x, rt: %d, lt: %d, " "bm: %d, tp: %d, SOFTBOUNDARY: %s, HARDBOUNDARY: %s\n", win->id, rt, lt, bm, tp, YESNO(opts & SWM_CW_SOFTBOUNDARY), YESNO(opts & SWM_CW_HARDBOUNDARY)); /* * Disable containment if any of the flagged sides went beyond the * containment boundary, or if containment is disabled. */ if (!(opts & SWM_CW_HARDBOUNDARY || opts & SWM_CW_SOFTBOUNDARY) || (bw != 0 && ((rt > bw) || (lt > bw) || (bm > bw) || (tp > bw)))) { /* Make sure window has at least 1 pixel in the region */ g.x += 1 - WIDTH(win); g.y += 1 - HEIGHT(win); g.w += 2 * WIDTH(win) - 2; g.h += 2 * HEIGHT(win) - 2; } constrain_window(win, &g, &opts); } /* Move or resize a window so that flagged side(s) fit into the supplied box. */ void constrain_window(struct ws_win *win, struct swm_geometry *b, int *opts) { DNPRINTF(SWM_D_MISC, "constrain_window: win %#x, (x,y) w x h: " "(%d,%d) %d x %d, box: (x,y) w x h: (%d,%d) %d x %d, rt: %s, " "lt: %s, bt: %s, tp: %s, allow resize: %s\n", win->id, X(win), Y(win), WIDTH(win), HEIGHT(win), b->x, b->y, b->w, b->h, YESNO(*opts & SWM_CW_RIGHT), YESNO(*opts & SWM_CW_LEFT), YESNO(*opts & SWM_CW_BOTTOM), YESNO(*opts & SWM_CW_TOP), YESNO(*opts & SWM_CW_RESIZABLE)); if ((*opts & SWM_CW_RIGHT) && MAX_X(win) > b->x + b->w) { if (*opts & SWM_CW_RESIZABLE) WIDTH(win) = b->x + b->w - X(win); else X(win) = b->x + b->w - WIDTH(win); } if ((*opts & SWM_CW_LEFT) && X(win) < b->x) { if (*opts & SWM_CW_RESIZABLE) WIDTH(win) -= b->x - X(win); X(win) = b->x; } if ((*opts & SWM_CW_BOTTOM) && MAX_Y(win) > b->y + b->h) { if (*opts & SWM_CW_RESIZABLE) HEIGHT(win) = b->y + b->h - Y(win); else Y(win) = b->y + b->h - HEIGHT(win); } if ((*opts & SWM_CW_TOP) && Y(win) < b->y) { if (*opts & SWM_CW_RESIZABLE) HEIGHT(win) -= b->y - Y(win); Y(win) = b->y; } if (*opts & SWM_CW_RESIZABLE) { if (WIDTH(win) < 1) WIDTH(win) = 1; if (HEIGHT(win) < 1) HEIGHT(win) = 1; } } void draw_frame(struct ws_win *win) { xcb_rectangle_t rect[4]; uint32_t *pixel; int n = 0; if (win->frame == XCB_WINDOW_NONE) { DNPRINTF(SWM_D_EVENT, "draw_frame: win %#x not " "reparented.\n", win->id); return; } if (!win->bordered) { DNPRINTF(SWM_D_EVENT, "draw_frame: win %#x frame " "disabled\n", win->id); } if (WS_FOCUSED(win->ws) && win->ws->focus == win) pixel = MAXIMIZED(win) ? &win->s->c[SWM_S_COLOR_FOCUS_MAXIMIZED].pixel : &win->s->c[SWM_S_COLOR_FOCUS].pixel; else pixel = MAXIMIZED(win) ? &win->s->c[SWM_S_COLOR_UNFOCUS_MAXIMIZED].pixel : &win->s->c[SWM_S_COLOR_UNFOCUS].pixel; /* Top (with corners) */ rect[n].x = 0; rect[n].y = 0; rect[n].width = WIDTH(win) + 2 * border_width; rect[n].height = border_width; /* Left (without corners) */ rect[++n].x = 0; rect[n].y = border_width; rect[n].width = border_width; rect[n].height = HEIGHT(win); /* Right (without corners) */ rect[++n].x = border_width + WIDTH(win); rect[n].y = border_width; rect[n].width = border_width; rect[n].height = HEIGHT(win); /* Bottom (with corners)*/ rect[++n].x = 0; rect[n].y = border_width + HEIGHT(win); rect[n].width = WIDTH(win) + 2 * border_width; rect[n].height = border_width; xcb_change_gc(conn, win->s->bar_gc, XCB_GC_FOREGROUND, pixel); xcb_poly_fill_rectangle(conn, win->frame, win->s->bar_gc, 4, rect); } void update_window(struct ws_win *win) { uint16_t mask; uint32_t wc[5]; if (win->frame == XCB_WINDOW_NONE) { DNPRINTF(SWM_D_EVENT, "update_window: skip win %#x; " "not reparented.\n", win->id); return; } mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_BORDER_WIDTH; /* Reconfigure frame. */ if (win->bordered) { wc[0] = X(win) - border_width; wc[1] = Y(win) - border_width; wc[2] = WIDTH(win) + 2 * border_width; wc[3] = HEIGHT(win) + 2 * border_width; } else { wc[0] = X(win); wc[1] = Y(win); wc[2] = WIDTH(win); wc[3] = HEIGHT(win); } wc[4] = 0; DNPRINTF(SWM_D_EVENT, "update_window: win %#x frame %#x, (x,y) w x h: " "(%d,%d) %d x %d, bordered: %s\n", win->id, win->frame, wc[0], wc[1], wc[2], wc[3], YESNO(win->bordered)); xcb_configure_window(conn, win->frame, mask, wc); /* Reconfigure client window. */ wc[0] = wc[1] = win->bordered ? border_width : 0; wc[2] = WIDTH(win); wc[3] = HEIGHT(win); DNPRINTF(SWM_D_EVENT, "update_window: win %#x, (x,y) w x h: " "(%d,%d) %d x %d, bordered: %s\n", win->id, wc[0], wc[1], wc[2], wc[3], YESNO(win->bordered)); xcb_configure_window(conn, win->id, mask, wc); /* ICCCM 4.2.3 Synthetic ConfigureNotify when moved and not resized. */ if ((X(win) != win->g_prev.x || Y(win) != win->g_prev.y) && (win->java || (WIDTH(win) == win->g_prev.w && HEIGHT(win) == win->g_prev.h))) { /* Java has special needs when moved together with a resize. */ config_win(win, NULL); } win->g_prev = win->g; } struct event { SIMPLEQ_ENTRY(event) entry; xcb_generic_event_t *ev; }; SIMPLEQ_HEAD(event_queue, event) events = SIMPLEQ_HEAD_INITIALIZER(events); xcb_generic_event_t * get_next_event(bool dowait) { struct event *ep; xcb_generic_event_t *evt; /* Try queue first. */ if ((ep = SIMPLEQ_FIRST(&events))) { evt = ep->ev; SIMPLEQ_REMOVE_HEAD(&events, entry); free(ep); } else if (dowait) evt = xcb_wait_for_event(conn); else evt = xcb_poll_for_event(conn); return evt; } void put_back_event(xcb_generic_event_t *evt) { struct event *ep; if ((ep = malloc(sizeof (struct event))) == NULL) err(1, "put_back_event: failed to allocate memory."); ep->ev = evt; SIMPLEQ_INSERT_HEAD(&events, ep, entry); } /* Peeks at next event to detect auto-repeat. */ bool keyrepeating(xcb_key_release_event_t *kre) { xcb_generic_event_t *evt; /* Ensure repeating keypress is finished processing. */ xcb_aux_sync(conn); if ((evt = get_next_event(false))) { put_back_event(evt); if (XCB_EVENT_RESPONSE_TYPE(evt) == XCB_KEY_PRESS && kre->sequence == evt->sequence && kre->detail == ((xcb_key_press_event_t *)evt)->detail) return true; } return false; } bool keybindreleased(struct binding *bp, xcb_key_release_event_t *kre) { if (bp->type == KEYBIND && !keyrepeating(kre) && bp->value == xcb_key_press_lookup_keysym(syms, kre, 0)) return true; return false; } #define SWM_RESIZE_STEPS (50) void resize_win(struct ws_win *win, struct binding *bp, int opt) { xcb_timestamp_t timestamp = 0; struct swm_region *r = NULL; struct swm_geometry g; int top = 0, left = 0; int dx, dy; xcb_cursor_t cursor; xcb_query_pointer_reply_t *xpr = NULL; xcb_generic_event_t *evt; xcb_motion_notify_event_t *mne; bool resizing, step = false; if (win == NULL) return; r = win->ws->r; if (FULLSCREEN(win)) return; /* In max_stack mode, should only resize transients. */ if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !TRANS(win)) return; DNPRINTF(SWM_D_EVENT, "resize: win %#x, floating: %s, " "transient: %#x\n", win->id, YESNO(ABOVE(win)), win->transient); if (MAXIMIZED(win)) store_float_geom(win); else if (!(TRANS(win) || ABOVE(win))) return; ewmh_apply_flags(win, (win->ewmh_flags | SWM_F_MANUAL | EWMH_F_ABOVE) & ~EWMH_F_MAXIMIZED); ewmh_update_wm_state(win); stack(r); focus_flush(); /* It's possible for win to have been freed during focus_flush(). */ if (validate_win(win)) { DNPRINTF(SWM_D_EVENT, "resize: invalid win.\n"); goto out; } switch (opt) { case SWM_ARG_ID_WIDTHSHRINK: WIDTH(win) -= SWM_RESIZE_STEPS; step = true; break; case SWM_ARG_ID_WIDTHGROW: WIDTH(win) += SWM_RESIZE_STEPS; step = true; break; case SWM_ARG_ID_HEIGHTSHRINK: HEIGHT(win) -= SWM_RESIZE_STEPS; step = true; break; case SWM_ARG_ID_HEIGHTGROW: HEIGHT(win) += SWM_RESIZE_STEPS; step = true; break; default: break; } if (step) { region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_RESIZABLE | SWM_CW_HARDBOUNDARY); update_window(win); store_float_geom(win); return; } region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_RESIZABLE | SWM_CW_SOFTBOUNDARY); update_window(win); /* get cursor offset from window root */ xpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, win->id), NULL); if (xpr == NULL) return; g = win->g; if (xpr->win_x < WIDTH(win) / 2) left = 1; if (xpr->win_y < HEIGHT(win) / 2) top = 1; if (opt == SWM_ARG_ID_CENTER) cursor = cursors[XC_SIZING].cid; else if (top) cursor = cursors[left ? XC_TOP_LEFT_CORNER : XC_TOP_RIGHT_CORNER].cid; else cursor = cursors[left ? XC_BOTTOM_LEFT_CORNER : XC_BOTTOM_RIGHT_CORNER].cid; xcb_grab_pointer(conn, 0, win->id, MOUSEMASK, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, cursor, XCB_CURRENT_TIME); /* Release keyboard freeze if called via keybind. */ if (bp->type == KEYBIND) xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, XCB_CURRENT_TIME); xcb_flush(conn); resizing = true; while (resizing && (evt = get_next_event(true))) { switch (XCB_EVENT_RESPONSE_TYPE(evt)) { case XCB_BUTTON_RELEASE: if (bp->type == BTNBIND && bp->value == ((xcb_button_release_event_t *)evt)->detail) resizing = false; break; case XCB_KEY_RELEASE: if (keybindreleased(bp, (xcb_key_release_event_t *)evt)) resizing = false; break; case XCB_MOTION_NOTIFY: mne = (xcb_motion_notify_event_t *)evt; DNPRINTF(SWM_D_EVENT, "resize: MOTION_NOTIFY: " "root: %#x\n", mne->root); /* cursor offset/delta from start of the operation */ dx = mne->root_x - xpr->root_x; dy = mne->root_y - xpr->root_y; /* vertical */ if (top) dy = -dy; if (opt == SWM_ARG_ID_CENTER) { if (g.h / 2 + dy < 1) dy = 1 - g.h / 2; Y(win) = g.y - dy; HEIGHT(win) = g.h + 2 * dy; } else { if (g.h + dy < 1) dy = 1 - g.h; if (top) Y(win) = g.y - dy; HEIGHT(win) = g.h + dy; } /* horizontal */ if (left) dx = -dx; if (opt == SWM_ARG_ID_CENTER) { if (g.w / 2 + dx < 1) dx = 1 - g.w / 2; X(win) = g.x - dx; WIDTH(win) = g.w + 2 * dx; } else { if (g.w + dx < 1) dx = 1 - g.w; if (left) X(win) = g.x - dx; WIDTH(win) = g.w + dx; } /* not free, don't sync more than 120 times / second */ if ((mne->time - timestamp) > (1000 / 120) ) { timestamp = mne->time; regionize(win, mne->root_x, mne->root_y); region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_RESIZABLE | SWM_CW_HARDBOUNDARY | SWM_CW_SOFTBOUNDARY); update_window(win); xcb_flush(conn); } break; case XCB_BUTTON_PRESS: /* Ignore. */ DNPRINTF(SWM_D_EVENT, "resize: BUTTON_PRESS ignored\n"); xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, ((xcb_button_press_event_t *)evt)->time); xcb_flush(conn); break; case XCB_KEY_PRESS: /* Ignore. */ DNPRINTF(SWM_D_EVENT, "resize: KEY_PRESS ignored\n"); xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, ((xcb_key_press_event_t *)evt)->time); xcb_flush(conn); break; default: event_handle(evt); /* It's possible for win to have been freed above. */ if (validate_win(win)) { DNPRINTF(SWM_D_EVENT, "resize: invalid win.\n"); goto out; } break; } free(evt); } if (timestamp) { region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_RESIZABLE | SWM_CW_HARDBOUNDARY | SWM_CW_SOFTBOUNDARY); update_window(win); xcb_flush(conn); } store_float_geom(win); out: xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); free(xpr); DNPRINTF(SWM_D_EVENT, "resize: done.\n"); } void resize(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *win = NULL; xcb_query_pointer_reply_t *qpr = NULL; if (r == NULL) return; if (args->id != SWM_ARG_ID_DONTCENTER && args->id != SWM_ARG_ID_CENTER) { /* {height,width}_{grow,shrink} use the focus window. */ if (r->ws) win = r->ws->focus; } else { /* resize uses window under pointer. */ qpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, r->s->root), NULL); if (qpr) win = find_window(qpr->child); } if (win == NULL) return; resize_win(win, bp, args->id); if (args->id && bp->type == KEYBIND) center_pointer(r); focus_flush(); } /* Try to set window region based on supplied coordinates or window center. */ void regionize(struct ws_win *win, int x, int y) { struct swm_region *r = NULL; r = region_under(win->s, x, y); if (r == NULL) r = region_under(win->s, X(win) + WIDTH(win) / 2, Y(win) + HEIGHT(win) / 2); if (r && r != win->ws->r) { clear_maximized(r->ws); win_to_ws(win, r->ws->idx, false); /* Stack old and new region. */ stack(r); stack(win->ws->r); /* Set focus on new ws. */ unfocus_win(r->ws->focus); r->ws->focus = win; set_region(r); raise_window(win); } } #define SWM_MOVE_STEPS (50) void move_win(struct ws_win *win, struct binding *bp, int opt) { struct swm_region *r; xcb_timestamp_t timestamp = 0; xcb_query_pointer_reply_t *qpr = NULL; xcb_generic_event_t *evt; xcb_motion_notify_event_t *mne; bool moving, restack = false, step = false; if (win == NULL) return; if ((r = win->ws->r) == NULL) return; if (FULLSCREEN(win)) return; DNPRINTF(SWM_D_EVENT, "move: win %#x, floating: %s, transient: " "%#x\n", win->id, YESNO(ABOVE(win)), win->transient); /* in max_stack mode should only move transients */ if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !TRANS(win)) return; if (!(ABOVE(win) || TRANS(win)) || MAXIMIZED(win)) { store_float_geom(win); restack = true; } ewmh_apply_flags(win, (win->ewmh_flags | SWM_F_MANUAL | EWMH_F_ABOVE) & ~EWMH_F_MAXIMIZED); ewmh_update_wm_state(win); if (restack) stack(r); focus_flush(); /* It's possible for win to have been freed during focus_flush(). */ if (validate_win(win)) { DNPRINTF(SWM_D_EVENT, "move: invalid win.\n"); goto out; } switch (opt) { case SWM_ARG_ID_MOVELEFT: X(win) -= (SWM_MOVE_STEPS - border_width); step = true; break; case SWM_ARG_ID_MOVERIGHT: X(win) += (SWM_MOVE_STEPS - border_width); step = true; break; case SWM_ARG_ID_MOVEUP: Y(win) -= (SWM_MOVE_STEPS - border_width); step = true; break; case SWM_ARG_ID_MOVEDOWN: Y(win) += (SWM_MOVE_STEPS - border_width); step = true; break; default: break; } if (step) { regionize(win, -1, -1); region_containment(win, win->ws->r, SWM_CW_ALLSIDES); update_window(win); store_float_geom(win); return; } xcb_grab_pointer(conn, 0, win->id, MOUSEMASK, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, cursors[XC_FLEUR].cid, XCB_CURRENT_TIME); /* get cursor offset from window root */ qpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, win->id), NULL); if (qpr == NULL) { xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); return; } /* Release keyboard freeze if called via keybind. */ if (bp->type == KEYBIND) xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, XCB_CURRENT_TIME); regionize(win, qpr->root_x, qpr->root_y); region_containment(win, win->ws->r, SWM_CW_ALLSIDES | SWM_CW_SOFTBOUNDARY); update_window(win); xcb_flush(conn); moving = true; while (moving && (evt = get_next_event(true))) { switch (XCB_EVENT_RESPONSE_TYPE(evt)) { case XCB_BUTTON_RELEASE: if (bp->type == BTNBIND && bp->value == ((xcb_button_release_event_t *)evt)->detail) moving = false; xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, ((xcb_button_release_event_t *)evt)->time); xcb_flush(conn); break; case XCB_KEY_RELEASE: if (keybindreleased(bp, (xcb_key_release_event_t *)evt)) moving = false; xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, ((xcb_key_release_event_t *)evt)->time); xcb_flush(conn); break; case XCB_MOTION_NOTIFY: mne = (xcb_motion_notify_event_t *)evt; DNPRINTF(SWM_D_EVENT, "move: MOTION_NOTIFY: " "root: %#x\n", mne->root); X(win) = mne->root_x - qpr->win_x - border_width; Y(win) = mne->root_y - qpr->win_y - border_width; /* not free, don't sync more than 120 times / second */ if ((mne->time - timestamp) > (1000 / 120) ) { timestamp = mne->time; regionize(win, mne->root_x, mne->root_y); region_containment(win, win->ws->r, SWM_CW_ALLSIDES | SWM_CW_SOFTBOUNDARY); update_window(win); xcb_flush(conn); } break; case XCB_BUTTON_PRESS: /* Thaw and ignore. */ xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, ((xcb_button_press_event_t *)evt)->time); xcb_flush(conn); break; case XCB_KEY_PRESS: /* Ignore. */ xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, ((xcb_key_press_event_t *)evt)->time); xcb_flush(conn); break; default: event_handle(evt); /* It's possible for win to have been freed. */ if (validate_win(win)) { DNPRINTF(SWM_D_EVENT, "move: invalid win.\n"); goto out; } break; } free(evt); } if (timestamp) { region_containment(win, win->ws->r, SWM_CW_ALLSIDES | SWM_CW_SOFTBOUNDARY); update_window(win); xcb_flush(conn); } store_float_geom(win); /* New region set to fullscreen layout. */ if (win->ws->r && win->ws->cur_layout == &layouts[SWM_MAX_STACK]) { stack(win->ws->r); focus_flush(); } out: free(qpr); xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); DNPRINTF(SWM_D_EVENT, "move: done.\n"); } void move(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *win = NULL; xcb_query_pointer_reply_t *qpr = NULL; if (r == NULL) return; if (args->id) { /* move_* uses focus window. */ if (r->ws) win = r->ws->focus; /* Disallow move_ on tiled. */ if (win && !(TRANS(win) || ABOVE(win))) return; } else { /* move uses window under pointer. */ qpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, r->s->root), NULL); if (qpr) win = find_window(qpr->child); } if (win == NULL) return; move_win(win, bp, args->id); if (args->id && bp->type == KEYBIND) center_pointer(r); focus_flush(); } /* action definitions */ struct action { char name[SWM_FUNCNAME_LEN]; void (*func)(struct binding *, struct swm_region *, union arg *); uint32_t flags; union arg args; } actions[FN_INVALID + 2] = { /* name function argument */ { "bar_toggle", bar_toggle, 0, {.id = SWM_ARG_ID_BAR_TOGGLE} }, { "bar_toggle_ws", bar_toggle, 0, {.id = SWM_ARG_ID_BAR_TOGGLE_WS} }, { "button2", pressbutton, 0, {.id = 2} }, { "cycle_layout", cycle_layout, 0, {0} }, { "flip_layout", stack_config, 0, {.id = SWM_ARG_ID_FLIPLAYOUT} }, { "float_toggle", floating_toggle,0, {0} }, { "focus", focus_pointer, 0, {0} }, { "focus_main", focus, 0, {.id = SWM_ARG_ID_FOCUSMAIN} }, { "focus_next", focus, 0, {.id = SWM_ARG_ID_FOCUSNEXT} }, { "focus_prev", focus, 0, {.id = SWM_ARG_ID_FOCUSPREV} }, { "focus_urgent", focus, 0, {.id = SWM_ARG_ID_FOCUSURGENT} }, { "fullscreen_toggle", fullscreen_toggle, 0, {0} }, { "maximize_toggle", maximize_toggle,0, {0} }, { "height_grow", resize, 0, {.id = SWM_ARG_ID_HEIGHTGROW} }, { "height_shrink", resize, 0, {.id = SWM_ARG_ID_HEIGHTSHRINK} }, { "iconify", iconify, 0, {0} }, { "master_shrink", stack_config, 0, {.id = SWM_ARG_ID_MASTERSHRINK} }, { "master_grow", stack_config, 0, {.id = SWM_ARG_ID_MASTERGROW} }, { "master_add", stack_config, 0, {.id = SWM_ARG_ID_MASTERADD} }, { "master_del", stack_config, 0, {.id = SWM_ARG_ID_MASTERDEL} }, { "move", move, FN_F_NOREPLAY, {0} }, { "move_down", move, 0, {.id = SWM_ARG_ID_MOVEDOWN} }, { "move_left", move, 0, {.id = SWM_ARG_ID_MOVELEFT} }, { "move_right", move, 0, {.id = SWM_ARG_ID_MOVERIGHT} }, { "move_up", move, 0, {.id = SWM_ARG_ID_MOVEUP} }, { "mvrg_1", send_to_rg, 0, {.id = 0} }, { "mvrg_2", send_to_rg, 0, {.id = 1} }, { "mvrg_3", send_to_rg, 0, {.id = 2} }, { "mvrg_4", send_to_rg, 0, {.id = 3} }, { "mvrg_5", send_to_rg, 0, {.id = 4} }, { "mvrg_6", send_to_rg, 0, {.id = 5} }, { "mvrg_7", send_to_rg, 0, {.id = 6} }, { "mvrg_8", send_to_rg, 0, {.id = 7} }, { "mvrg_9", send_to_rg, 0, {.id = 8} }, { "mvrg_next", send_to_rg_relative, 0, {.id = 1} }, { "mvrg_prev", send_to_rg_relative, 0, {.id = -1} }, { "mvws_1", send_to_ws, 0, {.id = 0} }, { "mvws_2", send_to_ws, 0, {.id = 1} }, { "mvws_3", send_to_ws, 0, {.id = 2} }, { "mvws_4", send_to_ws, 0, {.id = 3} }, { "mvws_5", send_to_ws, 0, {.id = 4} }, { "mvws_6", send_to_ws, 0, {.id = 5} }, { "mvws_7", send_to_ws, 0, {.id = 6} }, { "mvws_8", send_to_ws, 0, {.id = 7} }, { "mvws_9", send_to_ws, 0, {.id = 8} }, { "mvws_10", send_to_ws, 0, {.id = 9} }, { "mvws_11", send_to_ws, 0, {.id = 10} }, { "mvws_12", send_to_ws, 0, {.id = 11} }, { "mvws_13", send_to_ws, 0, {.id = 12} }, { "mvws_14", send_to_ws, 0, {.id = 13} }, { "mvws_15", send_to_ws, 0, {.id = 14} }, { "mvws_16", send_to_ws, 0, {.id = 15} }, { "mvws_17", send_to_ws, 0, {.id = 16} }, { "mvws_18", send_to_ws, 0, {.id = 17} }, { "mvws_19", send_to_ws, 0, {.id = 18} }, { "mvws_20", send_to_ws, 0, {.id = 19} }, { "mvws_21", send_to_ws, 0, {.id = 20} }, { "mvws_22", send_to_ws, 0, {.id = 21} }, { "name_workspace", name_workspace, 0, {0} }, { "quit", quit, 0, {0} }, { "raise", raise_focus, 0, {0} }, { "raise_toggle", raise_toggle, 0, {0} }, { "resize", resize, FN_F_NOREPLAY, {.id = SWM_ARG_ID_DONTCENTER} }, { "resize_centered", resize, FN_F_NOREPLAY, {.id = SWM_ARG_ID_CENTER} }, { "restart", restart, 0, {0} }, { "rg_1", focusrg, 0, {.id = 0} }, { "rg_2", focusrg, 0, {.id = 1} }, { "rg_3", focusrg, 0, {.id = 2} }, { "rg_4", focusrg, 0, {.id = 3} }, { "rg_5", focusrg, 0, {.id = 4} }, { "rg_6", focusrg, 0, {.id = 5} }, { "rg_7", focusrg, 0, {.id = 6} }, { "rg_8", focusrg, 0, {.id = 7} }, { "rg_9", focusrg, 0, {.id = 8} }, { "rg_move_next", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_MOVE_UP} }, { "rg_move_prev", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_MOVE_DOWN} }, { "rg_next", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_UP} }, { "rg_prev", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_DOWN} }, { "screen_next", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_UP} }, { "screen_prev", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_DOWN} }, { "search_win", search_win, 0, {0} }, { "search_workspace", search_workspace, 0, {0} }, { "spawn_custom", NULL, 0, {0} }, { "stack_balance", stack_config, 0, {.id = SWM_ARG_ID_STACKBALANCE} }, { "stack_inc", stack_config, 0, {.id = SWM_ARG_ID_STACKINC} }, { "stack_dec", stack_config, 0, {.id = SWM_ARG_ID_STACKDEC} }, { "stack_reset", stack_config, 0, {.id = SWM_ARG_ID_STACKRESET} }, { "swap_main", swapwin, 0, {.id = SWM_ARG_ID_SWAPMAIN} }, { "swap_next", swapwin, 0, {.id = SWM_ARG_ID_SWAPNEXT} }, { "swap_prev", swapwin, 0, {.id = SWM_ARG_ID_SWAPPREV} }, { "uniconify", uniconify, 0, {0} }, { "version", version, 0, {0} }, { "width_grow", resize, 0, {.id = SWM_ARG_ID_WIDTHGROW} }, { "width_shrink", resize, 0, {.id = SWM_ARG_ID_WIDTHSHRINK} }, { "wind_del", wkill, 0, {.id = SWM_ARG_ID_DELETEWINDOW} }, { "wind_kill", wkill, 0, {.id = SWM_ARG_ID_KILLWINDOW} }, { "ws_1", switchws, 0, {.id = 0} }, { "ws_2", switchws, 0, {.id = 1} }, { "ws_3", switchws, 0, {.id = 2} }, { "ws_4", switchws, 0, {.id = 3} }, { "ws_5", switchws, 0, {.id = 4} }, { "ws_6", switchws, 0, {.id = 5} }, { "ws_7", switchws, 0, {.id = 6} }, { "ws_8", switchws, 0, {.id = 7} }, { "ws_9", switchws, 0, {.id = 8} }, { "ws_10", switchws, 0, {.id = 9} }, { "ws_11", switchws, 0, {.id = 10} }, { "ws_12", switchws, 0, {.id = 11} }, { "ws_13", switchws, 0, {.id = 12} }, { "ws_14", switchws, 0, {.id = 13} }, { "ws_15", switchws, 0, {.id = 14} }, { "ws_16", switchws, 0, {.id = 15} }, { "ws_17", switchws, 0, {.id = 16} }, { "ws_18", switchws, 0, {.id = 17} }, { "ws_19", switchws, 0, {.id = 18} }, { "ws_20", switchws, 0, {.id = 19} }, { "ws_21", switchws, 0, {.id = 20} }, { "ws_22", switchws, 0, {.id = 21} }, { "ws_empty", emptyws, 0, {0} }, { "ws_next", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_UP} }, { "ws_next_all", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_UP_ALL} }, { "ws_next_move", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_MOVE_UP} }, { "ws_prev", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_DOWN} }, { "ws_prev_all", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_DOWN_ALL} }, { "ws_prev_move", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_MOVE_DOWN} }, { "ws_prior", priorws, 0, {0} }, /* SWM_DEBUG actions MUST be here: */ { "debug_toggle", debug_toggle, 0, {0} }, { "dumpwins", dumpwins, 0, {0} }, /* ALWAYS last: */ { "invalid action", NULL, 0, {0} }, }; void update_modkey(uint16_t mod) { struct binding *bp; /* Replace all instances of the old mod key. */ RB_FOREACH(bp, binding_tree, &bindings) if (bp->mod & mod_key) bp->mod = (bp->mod & ~mod_key) | mod; mod_key = mod; } int spawn_expand(struct swm_region *r, union arg *args, const char *spawn_name, char ***ret_args) { struct spawn_prog *prog = NULL; int i, c; char *ap, **real_args; /* suppress unused warning since var is needed */ (void)args; DNPRINTF(SWM_D_SPAWN, "spawn_expand: %s\n", spawn_name); /* find program */ TAILQ_FOREACH(prog, &spawns, entry) { if (strcasecmp(spawn_name, prog->name) == 0) break; } if (prog == NULL) { warnx("spawn_custom: program %s not found", spawn_name); return (-1); } /* make room for expanded args */ if ((real_args = calloc(prog->argc + 1, sizeof(char *))) == NULL) err(1, "spawn_custom: calloc real_args"); /* expand spawn_args into real_args */ for (i = c = 0; i < prog->argc; i++) { ap = prog->argv[i]; DNPRINTF(SWM_D_SPAWN, "spawn_custom: raw arg: %s\n", ap); if (strcasecmp(ap, "$bar_border") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_BAR_BORDER].name)) == NULL) err(1, "spawn_custom border color"); } else if (strcasecmp(ap, "$bar_color") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_BAR].name)) == NULL) err(1, "spawn_custom bar color"); } else if (strcasecmp(ap, "$bar_color_selected") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_BAR_SELECTED].name)) == NULL) err(1, "spawn_custom bar color selected"); } else if (strcasecmp(ap, "$bar_font") == 0) { if ((real_args[c] = strdup(bar_fonts)) == NULL) err(1, "spawn_custom bar fonts"); } else if (strcasecmp(ap, "$bar_font_color") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_BAR_FONT].name)) == NULL) err(1, "spawn_custom color font"); } else if (strcasecmp(ap, "$bar_font_color_selected") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_BAR_FONT_SELECTED].name)) == NULL) err(1, "spawn_custom bar font color selected"); } else if (strcasecmp(ap, "$color_focus") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_FOCUS].name)) == NULL) err(1, "spawn_custom color focus"); } else if (strcasecmp(ap, "$color_focus_maximized") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_FOCUS_MAXIMIZED].name)) == NULL) err(1, "spawn_custom color focus maximized"); } else if (strcasecmp(ap, "$color_unfocus") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_UNFOCUS].name)) == NULL) err(1, "spawn_custom color unfocus"); } else if (strcasecmp(ap, "$color_unfocus_maximized") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_UNFOCUS_MAXIMIZED].name)) == NULL) err(1, "spawn_custom color unfocus maximized"); } else if (strcasecmp(ap, "$region_index") == 0) { if (asprintf(&real_args[c], "%d", get_region_index(r) + 1) < 1) err(1, "spawn_custom region index"); } else if (strcasecmp(ap, "$workspace_index") == 0) { if (asprintf(&real_args[c], "%d", r->ws->idx + 1) < 1) err(1, "spawn_custom workspace index"); } else if (strcasecmp(ap, "$dmenu_bottom") == 0) { if (!bar_at_bottom) continue; if ((real_args[c] = strdup("-b")) == NULL) err(1, "spawn_custom workspace index"); } else { /* no match --> copy as is */ if ((real_args[c] = strdup(ap)) == NULL) err(1, "spawn_custom strdup(ap)"); } DNPRINTF(SWM_D_SPAWN, "spawn_custom: cooked arg: %s\n", real_args[c]); ++c; } #ifdef SWM_DEBUG DNPRINTF(SWM_D_SPAWN, "spawn_custom: result: "); for (i = 0; i < c; ++i) DPRINTF("\"%s\" ", real_args[i]); DPRINTF("\n"); #endif *ret_args = real_args; return (c); } void spawn_custom(struct swm_region *r, union arg *args, const char *spawn_name) { union arg a; char **real_args; int spawn_argc, i; if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0) return; a.argv = real_args; if (fork() == 0) spawn(r->ws->idx, &a, true); for (i = 0; i < spawn_argc; i++) free(real_args[i]); free(real_args); } void spawn_select(struct swm_region *r, union arg *args, const char *spawn_name, int *pid) { union arg a; char **real_args; int i, spawn_argc; if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0) return; a.argv = real_args; if (pipe(select_list_pipe) == -1) err(1, "pipe error"); if (pipe(select_resp_pipe) == -1) err(1, "pipe error"); if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) err(1, "could not disable SIGPIPE"); switch (*pid = fork()) { case -1: err(1, "cannot fork"); break; case 0: /* child */ if (dup2(select_list_pipe[0], STDIN_FILENO) == -1) err(1, "dup2"); if (dup2(select_resp_pipe[1], STDOUT_FILENO) == -1) err(1, "dup2"); close(select_list_pipe[1]); close(select_resp_pipe[0]); spawn(r->ws->idx, &a, false); break; default: /* parent */ close(select_list_pipe[0]); close(select_resp_pipe[1]); break; } for (i = 0; i < spawn_argc; i++) free(real_args[i]); free(real_args); } /* Argument tokenizer. */ char * argsep(char **sp) { char *arg, *cp, *next; bool single_quoted = false, double_quoted = false; if (*sp == NULL) return NULL; /* Eat and move characters until end of argument is found. */ for (arg = next = cp = *sp; *cp != '\0'; ++cp) { if (!double_quoted && *cp == '\'') { /* Eat single-quote. */ single_quoted = !single_quoted; } else if (!single_quoted && *cp == '"') { /* Eat double-quote. */ double_quoted = !double_quoted; } else if (!single_quoted && *cp == '\\' && *(cp + 1) == '"') { /* Eat backslash; copy escaped character to arg. */ *next++ = *(++cp); } else if (!single_quoted && !double_quoted && *cp == '\\' && (*(cp + 1) == '\'' || *(cp + 1) == ' ')) { /* Eat backslash; move escaped character. */ *next++ = *(++cp); } else if (!single_quoted && !double_quoted && (*cp == ' ' || *cp == '\t')) { /* Terminate argument. */ *next++ = '\0'; /* Point sp to beginning of next argument. */ *sp = ++cp; break; } else { /* Move regular character. */ *next++ = *cp; } } /* Terminate argument if end of string. */ if (*cp == '\0') { *next = '\0'; *sp = NULL; } return arg; } void spawn_insert(const char *name, const char *args, int flags) { struct spawn_prog *sp; char *arg, *cp, *ptr; DNPRINTF(SWM_D_SPAWN, "spawn_insert: %s[%s]\n", name, args); if (args == NULL || *args == '\0') return; if ((sp = calloc(1, sizeof *sp)) == NULL) err(1, "spawn_insert: calloc"); if ((sp->name = strdup(name)) == NULL) err(1, "spawn_insert: strdup"); /* Convert the arguments to an argument list. */ if ((ptr = cp = strdup(args)) == NULL) err(1, "spawn_insert: strdup"); while ((arg = argsep(&ptr)) != NULL) { /* Null argument; skip it. */ if (*arg == '\0') continue; sp->argc++; if ((sp->argv = realloc(sp->argv, sp->argc * sizeof *sp->argv)) == NULL) err(1, "spawn_insert: realloc"); if ((sp->argv[sp->argc - 1] = strdup(arg)) == NULL) err(1, "spawn_insert: strdup"); } free(cp); sp->flags = flags; DNPRINTF(SWM_D_SPAWN, "arg %d: [%s]\n", sp->argc, sp->argv[sp->argc-1]); TAILQ_INSERT_TAIL(&spawns, sp, entry); DNPRINTF(SWM_D_SPAWN, "spawn_insert: leave\n"); } void spawn_remove(struct spawn_prog *sp) { int i; DNPRINTF(SWM_D_SPAWN, "spawn_remove: %s\n", sp->name); TAILQ_REMOVE(&spawns, sp, entry); for (i = 0; i < sp->argc; i++) free(sp->argv[i]); free(sp->argv); free(sp->name); free(sp); DNPRINTF(SWM_D_SPAWN, "spawn_remove: leave\n"); } void clear_spawns(void) { struct spawn_prog *sp; #ifndef __clang_analyzer__ /* Suppress false warnings. */ while ((sp = TAILQ_FIRST(&spawns)) != NULL) { spawn_remove(sp); } #endif } struct spawn_prog* spawn_find(const char *name) { struct spawn_prog *sp; TAILQ_FOREACH(sp, &spawns, entry) if (strcasecmp(sp->name, name) == 0) return sp; return NULL; } void setspawn(const char *name, const char *args, int flags) { struct spawn_prog *sp; DNPRINTF(SWM_D_SPAWN, "setspawn: %s\n", name); if (name == NULL) return; /* Remove any old spawn under the same name. */ if ((sp = spawn_find(name)) != NULL) spawn_remove(sp); if (*args != '\0') spawn_insert(name, args, flags); else warnx("error: setspawn: cannot find program: %s", name); DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n"); } int setconfspawn(const char *selector, const char *value, int flags) { char *args; if (selector == NULL || strlen(selector) == 0) return (1); args = expand_tilde(value); DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, args); setspawn(selector, args, flags); free(args); DNPRINTF(SWM_D_SPAWN, "setconfspawn: done.\n"); return (0); } void validate_spawns(void) { struct binding *bp; struct spawn_prog *sp; char which[PATH_MAX]; size_t i; RB_FOREACH(bp, binding_tree, &bindings) { if (bp->action != FN_SPAWN_CUSTOM) continue; /* find program */ sp = spawn_find(bp->spawn_name); if (sp == NULL || sp->flags & SWM_SPAWN_OPTIONAL) continue; /* verify we have the goods */ snprintf(which, sizeof which, "which %s", sp->argv[0]); DNPRINTF(SWM_D_CONF, "validate_spawns: which %s\n", sp->argv[0]); for (i = strlen("which "); i < strlen(which); i++) if (which[i] == ' ') { which[i] = '\0'; break; } if (system(which) != 0) add_startup_exception("could not find %s", &which[strlen("which ")]); } } void setup_spawn(void) { setconfspawn("lock", "xlock", 0); setconfspawn("term", "xterm", 0); setconfspawn("spawn_term", "xterm", 0); setconfspawn("menu", "dmenu_run" " $dmenu_bottom" " -fn $bar_font" " -nb $bar_color" " -nf $bar_font_color" " -sb $bar_color_selected" " -sf $bar_font_color_selected", 0); setconfspawn("search", "dmenu" " $dmenu_bottom" " -i" " -fn $bar_font" " -nb $bar_color" " -nf $bar_font_color" " -sb $bar_color_selected" " -sf $bar_font_color_selected", 0); setconfspawn("name_workspace", "dmenu" " $dmenu_bottom" " -p Workspace" " -fn $bar_font" " -nb $bar_color" " -nf $bar_font_color" " -sb $bar_color_selected" " -sf $bar_font_color_selected", 0); /* These are not verified for existence, even with a binding set. */ setconfspawn("screenshot_all", "screenshot.sh full", SWM_SPAWN_OPTIONAL); setconfspawn("screenshot_wind", "screenshot.sh window", SWM_SPAWN_OPTIONAL); setconfspawn("initscr", "initscreen.sh", SWM_SPAWN_OPTIONAL); } /* bindings */ #define SWM_MODNAME_SIZE 32 #define SWM_KEY_WS "\n+ \t" int parsebinding(const char *bindstr, uint16_t *mod, enum binding_type *type, uint32_t *val, uint32_t *flags) { char *str, *cp, *name; KeySym ks, lks, uks; DNPRINTF(SWM_D_KEY, "parsebinding: enter [%s]\n", bindstr); if (mod == NULL || val == NULL) { DNPRINTF(SWM_D_KEY, "parsebinding: no mod or key vars\n"); return (1); } if (bindstr == NULL || strlen(bindstr) == 0) { DNPRINTF(SWM_D_KEY, "parsebinding: no bindstr\n"); return (1); } if ((cp = str = strdup(bindstr)) == NULL) err(1, "parsebinding: strdup"); *val = XCB_NO_SYMBOL; *mod = 0; *flags = 0; *type = KEYBIND; while ((name = strsep(&cp, SWM_KEY_WS)) != NULL) { DNPRINTF(SWM_D_KEY, "parsebinding: entry [%s]\n", name); if (cp) cp += (long)strspn(cp, SWM_KEY_WS); if (strncasecmp(name, "MOD", SWM_MODNAME_SIZE) == 0) *mod |= mod_key; else if (strncasecmp(name, "Mod1", SWM_MODNAME_SIZE) == 0) *mod |= XCB_MOD_MASK_1; else if (strncasecmp(name, "Mod2", SWM_MODNAME_SIZE) == 0) *mod |= XCB_MOD_MASK_2; else if (strncmp(name, "Mod3", SWM_MODNAME_SIZE) == 0) *mod |= XCB_MOD_MASK_3; else if (strncmp(name, "Mod4", SWM_MODNAME_SIZE) == 0) *mod |= XCB_MOD_MASK_4; else if (strncmp(name, "Mod5", SWM_MODNAME_SIZE) == 0) *mod |= XCB_MOD_MASK_5; else if (strncasecmp(name, "SHIFT", SWM_MODNAME_SIZE) == 0) *mod |= XCB_MOD_MASK_SHIFT; else if (strncasecmp(name, "CONTROL", SWM_MODNAME_SIZE) == 0) *mod |= XCB_MOD_MASK_CONTROL; else if (strncasecmp(name, "ANYMOD", SWM_MODNAME_SIZE) == 0) *mod |= XCB_MOD_MASK_ANY; else if (strncasecmp(name, "REPLAY", SWM_MODNAME_SIZE) == 0) *flags |= BINDING_F_REPLAY; else if (sscanf(name, "Button%u", val) == 1) { DNPRINTF(SWM_D_KEY, "parsebinding: button %u\n", *val); *type = BTNBIND; if (*val > 255 || *val == 0) { DNPRINTF(SWM_D_KEY, "parsebinding: invalid button %u\n", *val); free(str); return (1); } } else { /* TODO: do this without Xlib. */ ks = XStringToKeysym(name); if (ks == NoSymbol) { DNPRINTF(SWM_D_KEY, "parsebinding: invalid key %s\n", name); free(str); return (1); } XConvertCase(ks, &lks, &uks); *val = (uint32_t)lks; } } /* If ANYMOD was specified, ignore the rest. */ if (*mod & XCB_MOD_MASK_ANY) *mod = XCB_MOD_MASK_ANY; free(str); DNPRINTF(SWM_D_KEY, "parsebinding: leave ok\n"); return (0); } char * strdupsafe(const char *str) { if (str == NULL) return (NULL); else return (strdup(str)); } int binding_cmp(struct binding *bp1, struct binding *bp2) { if (bp1->type < bp2->type) return (-1); if (bp1->type > bp2->type) return (1); if (bp1->value < bp2->value) return (-1); if (bp1->value > bp2->value) return (1); if (bp1->mod < bp2->mod) return (-1); if (bp1->mod > bp2->mod) return (1); return (0); } void binding_insert(uint16_t mod, enum binding_type type, uint32_t val, enum actionid aid, uint32_t flags, const char *spawn_name) { struct binding *bp; DNPRINTF(SWM_D_KEY, "binding_insert: mod: %u, type: %d, val: %u, " "action: %s(%d), spawn_name: %s\n", mod, type, val, actions[aid].name, aid, spawn_name); if ((bp = malloc(sizeof *bp)) == NULL) err(1, "binding_insert: malloc"); bp->mod = mod; bp->type = type; bp->value = val; bp->action = aid; bp->flags = flags; bp->spawn_name = strdupsafe(spawn_name); RB_INSERT(binding_tree, &bindings, bp); DNPRINTF(SWM_D_KEY, "binding_insert: leave\n"); } struct binding * binding_lookup(uint16_t mod, enum binding_type type, uint32_t val) { struct binding bp; bp.mod = mod; bp.type = type; bp.value = val; return (RB_FIND(binding_tree, &bindings, &bp)); } void binding_remove(struct binding *bp) { DNPRINTF(SWM_D_KEY, "binding_remove: mod: %u, type: %d, val: %u, " "action: %s(%d), spawn_name: %s\n", bp->mod, bp->type, bp->value, actions[bp->action].name, bp->action, bp->spawn_name); RB_REMOVE(binding_tree, &bindings, bp); free(bp->spawn_name); free(bp); DNPRINTF(SWM_D_KEY, "binding_remove: leave\n"); } void setbinding(uint16_t mod, enum binding_type type, uint32_t val, enum actionid aid, uint32_t flags, const char *spawn_name) { struct binding *bp; DNPRINTF(SWM_D_KEY, "setbinding: enter %s [%s]\n", actions[aid].name, spawn_name); /* Unbind any existing. Loop is to handle MOD_MASK_ANY. */ while ((bp = binding_lookup(mod, type, val))) binding_remove(bp); if (aid != FN_INVALID) binding_insert(mod, type, val, aid, flags, spawn_name); DNPRINTF(SWM_D_KEY, "setbinding: leave\n"); } int setconfbinding(const char *selector, const char *value, int flags) { struct spawn_prog *sp; uint32_t keybtn, opts; uint16_t mod; enum actionid aid; enum binding_type type; /* suppress unused warning since var is needed */ (void)flags; DNPRINTF(SWM_D_KEY, "setconfbinding: enter selector: [%s], " "value: [%s]\n", selector, value); if (selector == NULL || strlen(selector) == 0) { DNPRINTF(SWM_D_KEY, "setconfbinding: unbind %s\n", value); if (parsebinding(value, &mod, &type, &keybtn, &opts) == 0) { setbinding(mod, type, keybtn, FN_INVALID, opts, NULL); return (0); } else return (1); } /* search by key function name */ for (aid = 0; aid < FN_INVALID; aid++) { if (strncasecmp(selector, actions[aid].name, SWM_FUNCNAME_LEN) == 0) { DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match " "action\n", selector); if (parsebinding(value, &mod, &type, &keybtn, &opts) == 0) { setbinding(mod, type, keybtn, aid, opts, NULL); return (0); } else return (1); } } /* search by custom spawn name */ if ((sp = spawn_find(selector)) != NULL) { DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match " "spawn\n", selector); if (parsebinding(value, &mod, &type, &keybtn, &opts) == 0) { setbinding(mod, type, keybtn, FN_SPAWN_CUSTOM, opts, sp->name); return (0); } else return (1); } DNPRINTF(SWM_D_KEY, "setconfbinding: no match\n"); return (1); } #define MODSHIFT MODKEY | XCB_MOD_MASK_SHIFT void setup_keybindings(void) { #define BINDKEY(m, k, a) setbinding(m, KEYBIND, k, a, 0, NULL) #define BINDKEYSPAWN(m, k, s) setbinding(m, KEYBIND, k, FN_SPAWN_CUSTOM, 0, s) BINDKEY(MODKEY, XK_b, FN_BAR_TOGGLE); BINDKEY(MODSHIFT, XK_b, FN_BAR_TOGGLE_WS); BINDKEY(MODKEY, XK_b, FN_BAR_TOGGLE); BINDKEY(MODSHIFT, XK_b, FN_BAR_TOGGLE_WS); BINDKEY(MODKEY, XK_v, FN_BUTTON2); BINDKEY(MODKEY, XK_space, FN_CYCLE_LAYOUT); BINDKEY(MODSHIFT, XK_backslash, FN_FLIP_LAYOUT); BINDKEY(MODKEY, XK_t, FN_FLOAT_TOGGLE); BINDKEY(MODKEY, XK_m, FN_FOCUS_MAIN); BINDKEY(MODKEY, XK_j, FN_FOCUS_NEXT); BINDKEY(MODKEY, XK_Tab, FN_FOCUS_NEXT); BINDKEY(MODKEY, XK_k, FN_FOCUS_PREV); BINDKEY(MODSHIFT, XK_Tab, FN_FOCUS_PREV); BINDKEY(MODKEY, XK_u, FN_FOCUS_URGENT); BINDKEY(MODSHIFT, XK_e, FN_FULLSCREEN_TOGGLE); BINDKEY(MODKEY, XK_e, FN_MAXIMIZE_TOGGLE); BINDKEY(MODSHIFT, XK_equal, FN_HEIGHT_GROW); BINDKEY(MODSHIFT, XK_minus, FN_HEIGHT_SHRINK); BINDKEY(MODKEY, XK_w, FN_ICONIFY); BINDKEY(MODKEY, XK_h, FN_MASTER_SHRINK); BINDKEY(MODKEY, XK_l, FN_MASTER_GROW); BINDKEY(MODKEY, XK_comma, FN_MASTER_ADD); BINDKEY(MODKEY, XK_period, FN_MASTER_DEL); BINDKEY(MODSHIFT, XK_bracketright, FN_MOVE_DOWN); BINDKEY(MODKEY, XK_bracketleft, FN_MOVE_LEFT); BINDKEY(MODKEY, XK_bracketright, FN_MOVE_RIGHT); BINDKEY(MODSHIFT, XK_bracketleft, FN_MOVE_UP); BINDKEY(MODSHIFT, XK_KP_End, FN_MVRG_1); BINDKEY(MODSHIFT, XK_KP_Down, FN_MVRG_2); BINDKEY(MODSHIFT, XK_KP_Next, FN_MVRG_3); BINDKEY(MODSHIFT, XK_KP_Left, FN_MVRG_4); BINDKEY(MODSHIFT, XK_KP_Begin, FN_MVRG_5); BINDKEY(MODSHIFT, XK_KP_Right, FN_MVRG_6); BINDKEY(MODSHIFT, XK_KP_Home, FN_MVRG_7); BINDKEY(MODSHIFT, XK_KP_Up, FN_MVRG_8); BINDKEY(MODSHIFT, XK_KP_Prior, FN_MVRG_9); BINDKEY(MODSHIFT, XK_1, FN_MVWS_1); BINDKEY(MODSHIFT, XK_2, FN_MVWS_2); BINDKEY(MODSHIFT, XK_3, FN_MVWS_3); BINDKEY(MODSHIFT, XK_4, FN_MVWS_4); BINDKEY(MODSHIFT, XK_5, FN_MVWS_5); BINDKEY(MODSHIFT, XK_6, FN_MVWS_6); BINDKEY(MODSHIFT, XK_7, FN_MVWS_7); BINDKEY(MODSHIFT, XK_8, FN_MVWS_8); BINDKEY(MODSHIFT, XK_9, FN_MVWS_9); BINDKEY(MODSHIFT, XK_0, FN_MVWS_10); BINDKEY(MODSHIFT, XK_F1, FN_MVWS_11); BINDKEY(MODSHIFT, XK_F2, FN_MVWS_12); BINDKEY(MODSHIFT, XK_F3, FN_MVWS_13); BINDKEY(MODSHIFT, XK_F4, FN_MVWS_14); BINDKEY(MODSHIFT, XK_F5, FN_MVWS_15); BINDKEY(MODSHIFT, XK_F6, FN_MVWS_16); BINDKEY(MODSHIFT, XK_F7, FN_MVWS_17); BINDKEY(MODSHIFT, XK_F8, FN_MVWS_18); BINDKEY(MODSHIFT, XK_F9, FN_MVWS_19); BINDKEY(MODSHIFT, XK_F10, FN_MVWS_20); BINDKEY(MODSHIFT, XK_F11, FN_MVWS_21); BINDKEY(MODSHIFT, XK_F12, FN_MVWS_22); BINDKEY(MODSHIFT, XK_slash, FN_NAME_WORKSPACE); BINDKEY(MODSHIFT, XK_q, FN_QUIT); BINDKEY(MODKEY, XK_r, FN_RAISE); BINDKEY(MODSHIFT, XK_r, FN_RAISE_TOGGLE); BINDKEY(MODKEY, XK_q, FN_RESTART); BINDKEY(MODKEY, XK_KP_End, FN_RG_1); BINDKEY(MODKEY, XK_KP_Down, FN_RG_2); BINDKEY(MODKEY, XK_KP_Next, FN_RG_3); BINDKEY(MODKEY, XK_KP_Left, FN_RG_4); BINDKEY(MODKEY, XK_KP_Begin, FN_RG_5); BINDKEY(MODKEY, XK_KP_Right, FN_RG_6); BINDKEY(MODKEY, XK_KP_Home, FN_RG_7); BINDKEY(MODKEY, XK_KP_Up, FN_RG_8); BINDKEY(MODKEY, XK_KP_Prior, FN_RG_9); BINDKEY(MODSHIFT, XK_Right, FN_RG_NEXT); BINDKEY(MODSHIFT, XK_Left, FN_RG_PREV); BINDKEY(MODKEY, XK_f, FN_SEARCH_WIN); BINDKEY(MODKEY, XK_slash, FN_SEARCH_WORKSPACE); BINDKEYSPAWN(MODSHIFT, XK_i, "initscr"); BINDKEYSPAWN(MODSHIFT, XK_Delete, "lock"); BINDKEYSPAWN(MODKEY, XK_p, "menu"); BINDKEYSPAWN(MODKEY, XK_s, "screenshot_all"); BINDKEYSPAWN(MODSHIFT, XK_s, "screenshot_wind"); BINDKEYSPAWN(MODSHIFT, XK_Return, "term"); BINDKEY(MODSHIFT, XK_comma, FN_STACK_INC); BINDKEY(MODSHIFT, XK_period, FN_STACK_DEC); BINDKEY(MODSHIFT, XK_space, FN_STACK_RESET); BINDKEY(MODKEY, XK_Return, FN_SWAP_MAIN); BINDKEY(MODSHIFT, XK_j, FN_SWAP_NEXT); BINDKEY(MODSHIFT, XK_k, FN_SWAP_PREV); BINDKEY(MODSHIFT, XK_w, FN_UNICONIFY); BINDKEY(MODSHIFT, XK_v, FN_VERSION); BINDKEY(MODKEY, XK_equal, FN_WIDTH_GROW); BINDKEY(MODKEY, XK_minus, FN_WIDTH_SHRINK); BINDKEY(MODKEY, XK_x, FN_WIND_DEL); BINDKEY(MODSHIFT, XK_x, FN_WIND_KILL); BINDKEY(MODKEY, XK_1, FN_WS_1); BINDKEY(MODKEY, XK_2, FN_WS_2); BINDKEY(MODKEY, XK_3, FN_WS_3); BINDKEY(MODKEY, XK_4, FN_WS_4); BINDKEY(MODKEY, XK_5, FN_WS_5); BINDKEY(MODKEY, XK_6, FN_WS_6); BINDKEY(MODKEY, XK_7, FN_WS_7); BINDKEY(MODKEY, XK_8, FN_WS_8); BINDKEY(MODKEY, XK_9, FN_WS_9); BINDKEY(MODKEY, XK_0, FN_WS_10); BINDKEY(MODKEY, XK_F1, FN_WS_11); BINDKEY(MODKEY, XK_F2, FN_WS_12); BINDKEY(MODKEY, XK_F3, FN_WS_13); BINDKEY(MODKEY, XK_F4, FN_WS_14); BINDKEY(MODKEY, XK_F5, FN_WS_15); BINDKEY(MODKEY, XK_F6, FN_WS_16); BINDKEY(MODKEY, XK_F7, FN_WS_17); BINDKEY(MODKEY, XK_F8, FN_WS_18); BINDKEY(MODKEY, XK_F9, FN_WS_19); BINDKEY(MODKEY, XK_F10, FN_WS_20); BINDKEY(MODKEY, XK_F11, FN_WS_21); BINDKEY(MODKEY, XK_F12, FN_WS_22); BINDKEY(MODKEY, XK_Right, FN_WS_NEXT); BINDKEY(MODKEY, XK_Left, FN_WS_PREV); BINDKEY(MODKEY, XK_Up, FN_WS_NEXT_ALL); BINDKEY(MODKEY, XK_Down, FN_WS_PREV_ALL); BINDKEY(MODSHIFT, XK_Up, FN_WS_NEXT_MOVE); BINDKEY(MODSHIFT, XK_Down, FN_WS_PREV_MOVE); BINDKEY(MODKEY, XK_a, FN_WS_PRIOR); #ifdef SWM_DEBUG BINDKEY(MODKEY, XK_d, FN_DEBUG_TOGGLE); BINDKEY(MODSHIFT, XK_d, FN_DUMPWINS); #endif #undef BINDKEY #undef BINDKEYSPAWN } void setup_btnbindings(void) { setbinding(ANYMOD, BTNBIND, XCB_BUTTON_INDEX_1, FN_FOCUS, BINDING_F_REPLAY, NULL); setbinding(MODKEY, BTNBIND, XCB_BUTTON_INDEX_3, FN_RESIZE, 0, NULL); setbinding(MODSHIFT, BTNBIND, XCB_BUTTON_INDEX_3, FN_RESIZE_CENTERED, 0, NULL); setbinding(MODKEY, BTNBIND, XCB_BUTTON_INDEX_1, FN_MOVE, 0, NULL); } #undef MODSHIFT void clear_bindings(void) { struct binding *bp; while ((bp = RB_ROOT(&bindings))) binding_remove(bp); } void clear_keybindings(void) { struct binding *bp, *bptmp; RB_FOREACH_SAFE(bp, binding_tree, &bindings, bptmp) { if (bp->type != KEYBIND) continue; binding_remove(bp); } } int setkeymapping(const char *selector, const char *value, int flags) { char *keymapping_file; /* suppress unused warnings since vars are needed */ (void)selector; (void)flags; DNPRINTF(SWM_D_KEY, "setkeymapping: enter\n"); keymapping_file = expand_tilde(value); clear_keybindings(); /* load new key bindings; if it fails, revert to default bindings */ if (conf_load(keymapping_file, SWM_CONF_KEYMAPPING)) { clear_keybindings(); setup_keybindings(); } free(keymapping_file); DNPRINTF(SWM_D_KEY, "setkeymapping: leave\n"); return (0); } void updatenumlockmask(void) { unsigned int i, j; xcb_get_modifier_mapping_reply_t *modmap_r; xcb_keycode_t *modmap, kc, *keycode; numlockmask = 0; modmap_r = xcb_get_modifier_mapping_reply(conn, xcb_get_modifier_mapping(conn), NULL); if (modmap_r) { modmap = xcb_get_modifier_mapping_keycodes(modmap_r); for (i = 0; i < 8; i++) { for (j = 0; j < modmap_r->keycodes_per_modifier; j++) { kc = modmap[i * modmap_r->keycodes_per_modifier + j]; keycode = xcb_key_symbols_get_keycode(syms, XK_Num_Lock); if (keycode) { if (kc == *keycode) numlockmask = (1 << i); free(keycode); } } } free(modmap_r); } DNPRINTF(SWM_D_MISC, "updatenumlockmask: %d\n", numlockmask); } void grabkeys(void) { struct binding *bp; int num_screens, k, j; uint16_t modifiers[4]; xcb_keycode_t *code; DNPRINTF(SWM_D_MISC, "grabkeys\n"); updatenumlockmask(); modifiers[0] = 0; modifiers[1] = numlockmask; modifiers[2] = XCB_MOD_MASK_LOCK; modifiers[3] = numlockmask | XCB_MOD_MASK_LOCK; num_screens = get_screen_count(); for (k = 0; k < num_screens; k++) { if (TAILQ_EMPTY(&screens[k].rl)) continue; xcb_ungrab_key(conn, XCB_GRAB_ANY, screens[k].root, XCB_MOD_MASK_ANY); RB_FOREACH(bp, binding_tree, &bindings) { if (bp->type != KEYBIND) continue; /* If there is a catch-all, only bind that. */ if ((binding_lookup(ANYMOD, KEYBIND, bp->value)) && bp->mod != ANYMOD) continue; /* Skip unused ws binds. */ if ((int)bp->action > FN_WS_1 + workspace_limit - 1 && bp->action <= FN_WS_22) continue; /* Skip unused mvws binds. */ if ((int)bp->action > FN_MVWS_1 + workspace_limit - 1 && bp->action <= FN_MVWS_22) continue; if ((code = xcb_key_symbols_get_keycode(syms, bp->value)) == NULL) continue; if (bp->mod == XCB_MOD_MASK_ANY) { /* All modifiers are grabbed in one pass. */ DNPRINTF(SWM_D_MOUSE, "grabkeys: grab, " "key: %u, modifiers: %d\n", bp->value, bp->mod); xcb_grab_key(conn, 1, screens[k].root, bp->mod, *code, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_SYNC); } else { /* Need to grab each modifier permutation. */ for (j = 0; j < LENGTH(modifiers); j++) { DNPRINTF(SWM_D_MOUSE, "grabkeys: grab, " "key: %u, modifiers: %d\n", bp->value, bp->mod | modifiers[j]); xcb_grab_key(conn, 1, screens[k].root, bp->mod | modifiers[j], *code, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_SYNC); } } free(code); } } } void grabbuttons(void) { struct binding *bp; int num_screens, i, k; uint16_t modifiers[4]; DNPRINTF(SWM_D_MOUSE, "grabbuttons\n"); updatenumlockmask(); modifiers[0] = 0; modifiers[1] = numlockmask; modifiers[2] = XCB_MOD_MASK_LOCK; modifiers[3] = numlockmask | XCB_MOD_MASK_LOCK; num_screens = get_screen_count(); for (k = 0; k < num_screens; k++) { if (TAILQ_EMPTY(&screens[k].rl)) continue; xcb_ungrab_button(conn, XCB_BUTTON_INDEX_ANY, screens[k].root, XCB_MOD_MASK_ANY); RB_FOREACH(bp, binding_tree, &bindings) { if (bp->type != BTNBIND) continue; /* If there is a catch-all, only bind that. */ if ((binding_lookup(ANYMOD, BTNBIND, bp->value)) && bp->mod != ANYMOD) continue; /* Skip unused ws binds. */ if ((int)bp->action > FN_WS_1 + workspace_limit - 1 && bp->action <= FN_WS_22) continue; /* Skip unused mvws binds. */ if ((int)bp->action > FN_MVWS_1 + workspace_limit - 1 && bp->action <= FN_MVWS_22) continue; if (bp->mod == XCB_MOD_MASK_ANY) { /* All modifiers are grabbed in one pass. */ DNPRINTF(SWM_D_MOUSE, "grabbuttons: grab, " "button: %u, modifiers: %d\n", bp->value, bp->mod); xcb_grab_button(conn, 0, screens[k].root, BUTTONMASK, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, XCB_CURSOR_NONE, bp->value, bp->mod); } else { /* Need to grab each modifier permutation. */ for (i = 0; i < LENGTH(modifiers); ++i) { DNPRINTF(SWM_D_MOUSE, "grabbuttons: " "grab, button: %u, modifiers: %u\n", bp->value, bp->mod | modifiers[i]); xcb_grab_button(conn, 0, screens[k].root, BUTTONMASK, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, XCB_CURSOR_NONE, bp->value, bp->mod | modifiers[i]); } } } } } const char *quirkname[] = { "NONE", /* config string for "no value" */ "FLOAT", "TRANSSZ", "ANYWHERE", "XTERM_FONTADJ", "FULLSCREEN", "FOCUSPREV", "NOFOCUSONMAP", "FOCUSONMAP_SINGLE", "OBEYAPPFOCUSREQ", "IGNOREPID", "IGNORESPAWNWS", "NOFOCUSCYCLE", "MINIMALBORDER", }; /* SWM_Q_DELIM: retain '|' for back compat for now (2009-08-11) */ #define SWM_Q_DELIM "\n|+ \t" int parsequirks(const char *qstr, uint32_t *quirk, int *ws) { char *str, *cp, *name; int i; if (quirk == NULL || qstr == NULL) return (1); if ((cp = str = strdup(qstr)) == NULL) err(1, "parsequirks: strdup"); *quirk = 0; while ((name = strsep(&cp, SWM_Q_DELIM)) != NULL) { if (cp) cp += (long)strspn(cp, SWM_Q_DELIM); if (sscanf(name, "WS[%d]", ws) == 1) { if (*ws > 0) *ws -= 1; continue; } for (i = 0; i < LENGTH(quirkname); i++) { if (strncasecmp(name, quirkname[i], SWM_QUIRK_LEN) == 0) { DNPRINTF(SWM_D_QUIRK, "parsequirks: %s\n", name); if (i == 0) { *quirk = 0; free(str); return (0); } *quirk |= 1 << (i-1); break; } } if (i >= LENGTH(quirkname)) { DNPRINTF(SWM_D_QUIRK, "parsequirks: invalid quirk [%s]\n", name); free(str); return (1); } } free(str); return (0); } void quirk_insert(const char *class, const char *instance, const char *name, uint32_t quirk, int ws) { struct quirk *qp; char *str; bool failed = false; DNPRINTF(SWM_D_QUIRK, "quirk_insert: class: %s, instance: %s, name: %s," " value: %u, ws: %d\n", class, instance, name, quirk, ws); if ((qp = malloc(sizeof *qp)) == NULL) err(1, "quirk_insert: malloc"); if ((qp->class = strdup(class)) == NULL) err(1, "quirk_insert: strdup"); if ((qp->instance = strdup(instance)) == NULL) err(1, "quirk_insert: strdup"); if ((qp->name = strdup(name)) == NULL) err(1, "quirk_insert: strdup"); if (asprintf(&str, "^%s$", class) == -1) err(1, "quirk_insert: asprintf"); if (regcomp(&qp->regex_class, str, REG_EXTENDED | REG_NOSUB)) { add_startup_exception("regex failed to compile quirk 'class' " "field: %s", class); failed = true; } DNPRINTF(SWM_D_QUIRK, "quirk_insert: compiled: %s\n", str); free(str); if (asprintf(&str, "^%s$", instance) == -1) err(1, "quirk_insert: asprintf"); if (regcomp(&qp->regex_instance, str, REG_EXTENDED | REG_NOSUB)) { add_startup_exception("regex failed to compile quirk 'instance'" " field: %s", instance); failed = true; } DNPRINTF(SWM_D_QUIRK, "quirk_insert: compiled: %s\n", str); free(str); if (asprintf(&str, "^%s$", name) == -1) err(1, "quirk_insert: asprintf"); if (regcomp(&qp->regex_name, str, REG_EXTENDED | REG_NOSUB)) { add_startup_exception("regex failed to compile quirk 'name' " "field: %s", name); failed = true; } DNPRINTF(SWM_D_QUIRK, "quirk_insert: compiled: %s\n", str); free(str); if (failed) { DNPRINTF(SWM_D_QUIRK, "quirk_insert: regex error; skipping.\n"); quirk_free(qp); } else { qp->quirk = quirk; qp->ws = ws; TAILQ_INSERT_TAIL(&quirks, qp, entry); } DNPRINTF(SWM_D_QUIRK, "quirk_insert: leave\n"); } void quirk_remove(struct quirk *qp) { DNPRINTF(SWM_D_QUIRK, "quirk_remove: %s:%s [%u]\n", qp->class, qp->name, qp->quirk); TAILQ_REMOVE(&quirks, qp, entry); quirk_free(qp); DNPRINTF(SWM_D_QUIRK, "quirk_remove: leave\n"); } void quirk_free(struct quirk *qp) { regfree(&qp->regex_class); regfree(&qp->regex_instance); regfree(&qp->regex_name); free(qp->class); free(qp->instance); free(qp->name); free(qp); } void clear_quirks(void) { struct quirk *qp; #ifndef __clang_analyzer__ /* Suppress false warnings. */ while ((qp = TAILQ_FIRST(&quirks)) != NULL) { quirk_remove(qp); } #endif } void quirk_replace(struct quirk *qp, const char *class, const char *instance, const char *name, uint32_t quirk, int ws) { DNPRINTF(SWM_D_QUIRK, "quirk_replace: %s:%s:%s [%u], ws: %d\n", qp->class, qp->instance, qp->name, qp->quirk, qp->ws); quirk_remove(qp); quirk_insert(class, instance, name, quirk, ws); DNPRINTF(SWM_D_QUIRK, "quirk_replace: leave\n"); } void setquirk(const char *class, const char *instance, const char *name, uint32_t quirk, int ws) { struct quirk *qp; DNPRINTF(SWM_D_QUIRK, "setquirk: enter %s:%s:%s [%u], ws: %d\n", class, instance, name, quirk, ws); /* Remove/replace existing quirk. */ TAILQ_FOREACH(qp, &quirks, entry) { if (strcmp(qp->class, class) == 0 && strcmp(qp->instance, instance) == 0 && strcmp(qp->name, name) == 0) { if (quirk == 0 && ws == -1) quirk_remove(qp); else quirk_replace(qp, class, instance, name, quirk, ws); DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n"); return; } } /* Only insert if quirk is not NONE or forced ws is set. */ if (quirk || ws != -1) quirk_insert(class, instance, name, quirk, ws); DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n"); } /* Eat '\' in str used to escape square brackets and colon. */ void unescape_selector(char *str) { char *cp; for (cp = str; *str != '\0'; ++str, ++cp) { if (*str == '\\' && (*(str + 1) == ':' || *(str + 1) == ']' || *(str + 1) == '[')) ++str; *cp = *str; } *cp = '\0'; } int setconfquirk(const char *selector, const char *value, int flags) { char *str, *cp, *class; char *instance = NULL, *name = NULL; int retval, count = 0, ws = -1; uint32_t qrks; /* suppress unused warning since var is needed */ (void)flags; if (selector == NULL || strlen(selector) == 0) return (0); if ((str = strdup(selector)) == NULL) err(1, "setconfquirk: strdup"); /* Find non-escaped colon. */ class = cp = str; if (*cp == ':') { *cp = '\0'; ++count; } for (++cp; *cp != '\0'; ++cp) { if (*cp == ':' && *(cp - 1) != '\\') { *cp = '\0'; ++count; } } unescape_selector(class); if (count) { instance = class + strlen(class) + 1; unescape_selector(instance); } else { instance = ".*"; } if (count > 1) { name = instance + strlen(instance) + 1; unescape_selector(name); } else { name = ".*"; } DNPRINTF(SWM_D_CONF, "setconfquirk: class: %s, instance: %s, " "name: %s\n", class, instance, name); if ((retval = parsequirks(value, &qrks, &ws)) == 0) setquirk(class, instance, name, qrks, ws); free(str); return (retval); } void setup_quirks(void) { setquirk("MPlayer", "xv", ".*", SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV, -1); setquirk("OpenOffice.org 3.2", "VCLSalFrame", ".*", SWM_Q_FLOAT, -1); setquirk("Firefox-bin", "firefox-bin", ".*", SWM_Q_TRANSSZ, -1); setquirk("Firefox", "Dialog", ".*", SWM_Q_FLOAT, -1); setquirk("Gimp", "gimp", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1); setquirk("XTerm", "xterm", ".*", SWM_Q_XTERM_FONTADJ, -1); setquirk("xine", "Xine Window", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1); setquirk("Xitk", "Xitk Combo", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1); setquirk("xine", "xine Panel", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1); setquirk("Xitk", "Xine Window", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1); setquirk("xine", "xine Video Fullscreen Window", ".*", SWM_Q_FULLSCREEN | SWM_Q_FLOAT, -1); setquirk("pcb", "pcb", ".*", SWM_Q_FLOAT, -1); setquirk("SDL_App", "SDL_App", ".*", SWM_Q_FLOAT | SWM_Q_FULLSCREEN, -1); } /* conf file stuff */ #define SWM_CONF_FILE "spectrwm.conf" #define SWM_CONF_FILE_OLD "scrotwm.conf" enum { SWM_S_BAR_ACTION, SWM_S_BAR_AT_BOTTOM, SWM_S_BAR_BORDER_WIDTH, SWM_S_BAR_ENABLED, SWM_S_BAR_ENABLED_WS, SWM_S_BAR_FONT, SWM_S_BAR_FORMAT, SWM_S_BAR_JUSTIFY, SWM_S_BORDER_WIDTH, SWM_S_BOUNDARY_WIDTH, SWM_S_CLOCK_ENABLED, SWM_S_CLOCK_FORMAT, SWM_S_CYCLE_EMPTY, SWM_S_CYCLE_VISIBLE, SWM_S_DIALOG_RATIO, SWM_S_DISABLE_BORDER, SWM_S_FOCUS_CLOSE, SWM_S_FOCUS_CLOSE_WRAP, SWM_S_FOCUS_DEFAULT, SWM_S_FOCUS_MODE, SWM_S_ICONIC_ENABLED, SWM_S_MAXIMIZE_HIDE_BAR, SWM_S_REGION_PADDING, SWM_S_SPAWN_ORDER, SWM_S_SPAWN_TERM, SWM_S_STACK_ENABLED, SWM_S_TERM_WIDTH, SWM_S_TILE_GAP, SWM_S_URGENT_COLLAPSE, SWM_S_URGENT_ENABLED, SWM_S_VERBOSE_LAYOUT, SWM_S_WARP_FOCUS, SWM_S_WARP_POINTER, SWM_S_WINDOW_CLASS_ENABLED, SWM_S_WINDOW_INSTANCE_ENABLED, SWM_S_WINDOW_NAME_ENABLED, SWM_S_WORKSPACE_CLAMP, SWM_S_WORKSPACE_LIMIT, SWM_S_WORKSPACE_NAME, }; int setconfvalue(const char *selector, const char *value, int flags) { struct workspace *ws; int i, ws_id, num_screens; char *b, *str, *sp, s[1024]; switch (flags) { case SWM_S_BAR_ACTION: free(bar_argv[0]); if ((bar_argv[0] = expand_tilde(value)) == NULL) err(1, "setconfvalue: bar_action"); break; case SWM_S_BAR_AT_BOTTOM: bar_at_bottom = (atoi(value) != 0); break; case SWM_S_BAR_BORDER_WIDTH: bar_border_width = atoi(value); if (bar_border_width < 0) bar_border_width = 0; break; case SWM_S_BAR_ENABLED: bar_enabled = (atoi(value) != 0); break; case SWM_S_BAR_ENABLED_WS: ws_id = atoi(selector) - 1; if (ws_id < 0 || ws_id >= workspace_limit) errx(1, "setconfvalue: bar_enabled_ws: invalid " "workspace %d.", ws_id + 1); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { ws = (struct workspace *)&screens[i].ws; ws[ws_id].bar_enabled = (atoi(value) != 0); } break; case SWM_S_BAR_FONT: b = bar_fonts; if (asprintf(&bar_fonts, "%s,%s", value, bar_fonts) == -1) err(1, "setconfvalue: asprintf: failed to allocate " "memory for bar_fonts."); free(b); /* If already in xft mode, then we are done. */ if (!bar_font_legacy) break; if ((sp = str = strdup(value)) == NULL) err(1, "setconfvalue: strdup"); /* If there are any non-XLFD entries, switch to Xft mode. */ while ((b = strsep(&sp, ",")) != NULL) { if (*b == '\0') continue; if (!isxlfd(b)) { bar_font_legacy = false; break; } } free(str); break; case SWM_S_BAR_FORMAT: free(bar_format); if ((bar_format = strdup(value)) == NULL) err(1, "setconfvalue: bar_format"); break; case SWM_S_BAR_JUSTIFY: if (strcmp(value, "left") == 0) bar_justify = SWM_BAR_JUSTIFY_LEFT; else if (strcmp(value, "center") == 0) bar_justify = SWM_BAR_JUSTIFY_CENTER; else if (strcmp(value, "right") == 0) bar_justify = SWM_BAR_JUSTIFY_RIGHT; else errx(1, "invalid bar_justify"); break; case SWM_S_BORDER_WIDTH: border_width = atoi(value); if (border_width < 0) border_width = 0; break; case SWM_S_BOUNDARY_WIDTH: boundary_width = atoi(value); if (boundary_width < 0) boundary_width = 0; break; case SWM_S_CLOCK_ENABLED: clock_enabled = (atoi(value) != 0); break; case SWM_S_CLOCK_FORMAT: #ifndef SWM_DENY_CLOCK_FORMAT free(clock_format); if ((clock_format = strdup(value)) == NULL) err(1, "setconfvalue: clock_format"); #endif break; case SWM_S_CYCLE_EMPTY: cycle_empty = (atoi(value) != 0); break; case SWM_S_CYCLE_VISIBLE: cycle_visible = (atoi(value) != 0); break; case SWM_S_DIALOG_RATIO: dialog_ratio = atof(value); if (dialog_ratio > 1.0 || dialog_ratio <= .3) dialog_ratio = .6; break; case SWM_S_DISABLE_BORDER: disable_border = (atoi(value) != 0); break; case SWM_S_FOCUS_CLOSE: if (strcmp(value, "first") == 0) focus_close = SWM_STACK_BOTTOM; else if (strcmp(value, "last") == 0) focus_close = SWM_STACK_TOP; else if (strcmp(value, "next") == 0) focus_close = SWM_STACK_ABOVE; else if (strcmp(value, "previous") == 0) focus_close = SWM_STACK_BELOW; else errx(1, "focus_close"); break; case SWM_S_FOCUS_CLOSE_WRAP: focus_close_wrap = (atoi(value) != 0); break; case SWM_S_FOCUS_DEFAULT: if (strcmp(value, "last") == 0) focus_default = SWM_STACK_TOP; else if (strcmp(value, "first") == 0) focus_default = SWM_STACK_BOTTOM; else errx(1, "focus_default"); break; case SWM_S_FOCUS_MODE: if (strcmp(value, "default") == 0) focus_mode = SWM_FOCUS_DEFAULT; else if (strcmp(value, "follow") == 0 || strcmp(value, "follow_cursor") == 0) focus_mode = SWM_FOCUS_FOLLOW; else if (strcmp(value, "manual") == 0) focus_mode = SWM_FOCUS_MANUAL; else errx(1, "focus_mode"); break; case SWM_S_ICONIC_ENABLED: iconic_enabled = (atoi(value) != 0); break; case SWM_S_MAXIMIZE_HIDE_BAR: maximize_hide_bar = atoi(value); break; case SWM_S_REGION_PADDING: region_padding = atoi(value); if (region_padding < 0) region_padding = 0; break; case SWM_S_SPAWN_ORDER: if (strcmp(value, "first") == 0) spawn_position = SWM_STACK_BOTTOM; else if (strcmp(value, "last") == 0) spawn_position = SWM_STACK_TOP; else if (strcmp(value, "next") == 0) spawn_position = SWM_STACK_ABOVE; else if (strcmp(value, "previous") == 0) spawn_position = SWM_STACK_BELOW; else errx(1, "spawn_position"); break; case SWM_S_SPAWN_TERM: setconfspawn("term", value, 0); setconfspawn("spawn_term", value, 0); break; case SWM_S_STACK_ENABLED: stack_enabled = (atoi(value) != 0); break; case SWM_S_TERM_WIDTH: term_width = atoi(value); if (term_width < 0) term_width = 0; break; case SWM_S_TILE_GAP: tile_gap = atoi(value); break; case SWM_S_URGENT_COLLAPSE: urgent_collapse = (atoi(value) != 0); break; case SWM_S_URGENT_ENABLED: urgent_enabled = (atoi(value) != 0); break; case SWM_S_VERBOSE_LAYOUT: verbose_layout = (atoi(value) != 0); for (i = 0; layouts[i].l_stack != NULL; i++) { if (verbose_layout) layouts[i].l_string = fancy_stacker; else layouts[i].l_string = plain_stacker; } break; case SWM_S_WARP_FOCUS: warp_focus = (atoi(value) != 0); break; case SWM_S_WARP_POINTER: warp_pointer = (atoi(value) != 0); break; case SWM_S_WINDOW_CLASS_ENABLED: window_class_enabled = (atoi(value) != 0); break; case SWM_S_WINDOW_INSTANCE_ENABLED: window_instance_enabled = (atoi(value) != 0); break; case SWM_S_WINDOW_NAME_ENABLED: window_name_enabled = (atoi(value) != 0); break; case SWM_S_WORKSPACE_CLAMP: workspace_clamp = (atoi(value) != 0); break; case SWM_S_WORKSPACE_LIMIT: workspace_limit = atoi(value); if (workspace_limit > SWM_WS_MAX) workspace_limit = SWM_WS_MAX; else if (workspace_limit < 1) workspace_limit = 1; ewmh_update_desktops(); break; case SWM_S_WORKSPACE_NAME: if (getenv("SWM_STARTED") != NULL) return (0); bzero(s, sizeof s); if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2) errx(1, "invalid entry, should be 'ws[]:name'"); ws_id--; if (ws_id < 0 || ws_id >= workspace_limit) errx(1, "setconfvalue: workspace_name: invalid " "workspace %d.", ws_id + 1); num_screens = get_screen_count(); for (i = 0; i < num_screens; ++i) { ws = (struct workspace *)&screens[i].ws; if (strlen(s) > 0) { free(ws[ws_id].name); if ((ws[ws_id].name = strdup(s)) == NULL) err(1, "setconfvalue: workspace_name."); ewmh_update_desktop_names(); ewmh_get_desktop_names(); } } break; default: return (1); } return (0); } int setconfmodkey(const char *selector, const char *value, int flags) { /* suppress unused warnings since vars are needed */ (void)selector; (void)flags; if (strncasecmp(value, "Mod1", strlen("Mod1")) == 0) update_modkey(XCB_MOD_MASK_1); else if (strncasecmp(value, "Mod2", strlen("Mod2")) == 0) update_modkey(XCB_MOD_MASK_2); else if (strncasecmp(value, "Mod3", strlen("Mod3")) == 0) update_modkey(XCB_MOD_MASK_3); else if (strncasecmp(value, "Mod4", strlen("Mod4")) == 0) update_modkey(XCB_MOD_MASK_4); else if (strncasecmp(value, "Mod5", strlen("Mod5")) == 0) update_modkey(XCB_MOD_MASK_5); else return (1); return (0); } int setconfcolor(const char *selector, const char *value, int flags) { int first, last, i = 0, num_screens; num_screens = get_screen_count(); /* conf screen indices begin at 1; treat vals <= 0 as 'all screens.' */ if (selector == NULL || strlen(selector) == 0 || (last = atoi(selector) - 1) < 0) { first = 0; last = num_screens - 1; } else { first = last; } if (last >= num_screens) { add_startup_exception("invalid screen index: %d out of bounds " "(maximum %d)", last + 1, num_screens); return (1); } for (i = first; i <= last; ++i) { setscreencolor(value, i, flags); /* * Need to sync 'maximized' and 'selected' colors with their * respective colors if they haven't been customized. */ switch (flags) { case SWM_S_COLOR_FOCUS: if (!screens[i].c[SWM_S_COLOR_FOCUS_MAXIMIZED].manual) setscreencolor(value, i, SWM_S_COLOR_FOCUS_MAXIMIZED); break; case SWM_S_COLOR_UNFOCUS: if (!screens[i].c[SWM_S_COLOR_UNFOCUS_MAXIMIZED].manual) setscreencolor(value, i, SWM_S_COLOR_UNFOCUS_MAXIMIZED); break; case SWM_S_COLOR_BAR: if (!screens[i].c[SWM_S_COLOR_BAR_FONT_SELECTED].manual) setscreencolor(value, i, SWM_S_COLOR_BAR_FONT_SELECTED); break; case SWM_S_COLOR_BAR_BORDER: if (!screens[i].c[SWM_S_COLOR_BAR_SELECTED].manual) setscreencolor(value, i, SWM_S_COLOR_BAR_SELECTED); break; } screens[i].c[flags].manual = 1; } return (0); } int setconfregion(const char *selector, const char *value, int flags) { /* suppress unused warnings since vars are needed */ (void)selector; (void)flags; custom_region(value); return (0); } int setautorun(const char *selector, const char *value, int flags) { int ws_id; char s[1024]; char *ap, *sp, *str; union arg a; int argc = 0; pid_t pid; struct pid_e *p; /* suppress unused warnings since vars are needed */ (void)selector; (void)flags; if (getenv("SWM_STARTED")) return (0); bzero(s, sizeof s); if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2) errx(1, "invalid autorun entry, should be 'ws[]:command'"); ws_id--; if (ws_id < 0 || ws_id >= workspace_limit) errx(1, "autorun: invalid workspace %d", ws_id + 1); sp = str = expand_tilde((char *)&s); /* * This is a little intricate * * If the pid already exists we simply reuse it because it means it was * used before AND not claimed by manage_window. We get away with * altering it in the parent after INSERT because this can not be a race */ a.argv = NULL; while ((ap = strsep(&sp, " \t")) != NULL) { if (*ap == '\0') continue; DNPRINTF(SWM_D_SPAWN, "setautorun: arg [%s]\n", ap); argc++; if ((a.argv = realloc(a.argv, argc * sizeof(char *))) == NULL) err(1, "setautorun: realloc"); a.argv[argc - 1] = ap; } if ((a.argv = realloc(a.argv, (argc + 1) * sizeof(char *))) == NULL) err(1, "setautorun: realloc"); a.argv[argc] = NULL; if ((pid = fork()) == 0) { spawn(ws_id, &a, true); /* NOTREACHED */ _exit(1); } free(a.argv); free(str); /* parent */ p = find_pid(pid); if (p == NULL) { p = calloc(1, sizeof *p); if (p == NULL) return (1); TAILQ_INSERT_TAIL(&pidlist, p, entry); } p->pid = pid; p->ws = ws_id; return (0); } int setlayout(const char *selector, const char *value, int flags) { struct workspace *ws; int ws_id, i, x, mg, ma, si, ar; int st = SWM_V_STACK, num_screens; char s[1024]; bool f = false; /* suppress unused warnings since vars are needed */ (void)selector; (void)flags; if (getenv("SWM_STARTED")) return (0); bzero(s, sizeof s); if (sscanf(value, "ws[%d]:%d:%d:%d:%d:%1023c", &ws_id, &mg, &ma, &si, &ar, s) != 6) errx(1, "invalid layout entry, should be 'ws[]:" "::::" "'"); ws_id--; if (ws_id < 0 || ws_id >= workspace_limit) errx(1, "layout: invalid workspace %d", ws_id + 1); if (strcasecmp(s, "vertical") == 0) st = SWM_V_STACK; else if (strcasecmp(s, "vertical_flip") == 0) { st = SWM_V_STACK; f = true; } else if (strcasecmp(s, "horizontal") == 0) st = SWM_H_STACK; else if (strcasecmp(s, "horizontal_flip") == 0) { st = SWM_H_STACK; f = true; } else if (strcasecmp(s, "fullscreen") == 0) st = SWM_MAX_STACK; else errx(1, "invalid layout entry, should be 'ws[]:" "::::" "'"); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { ws = (struct workspace *)&screens[i].ws; ws[ws_id].cur_layout = &layouts[st]; ws[ws_id].always_raise = (ar != 0); if (st == SWM_MAX_STACK) continue; /* master grow */ for (x = 0; x < abs(mg); x++) { ws[ws_id].cur_layout->l_config(&ws[ws_id], mg >= 0 ? SWM_ARG_ID_MASTERGROW : SWM_ARG_ID_MASTERSHRINK); } /* master add */ for (x = 0; x < abs(ma); x++) { ws[ws_id].cur_layout->l_config(&ws[ws_id], ma >= 0 ? SWM_ARG_ID_MASTERADD : SWM_ARG_ID_MASTERDEL); } /* stack inc */ for (x = 0; x < abs(si); x++) { ws[ws_id].cur_layout->l_config(&ws[ws_id], si >= 0 ? SWM_ARG_ID_STACKINC : SWM_ARG_ID_STACKDEC); } /* Apply flip */ if (f) { ws[ws_id].cur_layout->l_config(&ws[ws_id], SWM_ARG_ID_FLIPLAYOUT); } } return (0); } /* config options */ struct config_option { char *name; int (*func)(const char*, const char*, int); int flags; }; struct config_option configopt[] = { { "autorun", setautorun, 0 }, { "bar_action", setconfvalue, SWM_S_BAR_ACTION }, { "bar_at_bottom", setconfvalue, SWM_S_BAR_AT_BOTTOM }, { "bar_border", setconfcolor, SWM_S_COLOR_BAR_BORDER }, { "bar_border_unfocus", setconfcolor, SWM_S_COLOR_BAR_BORDER_UNFOCUS }, { "bar_border_width", setconfvalue, SWM_S_BAR_BORDER_WIDTH }, { "bar_color", setconfcolor, SWM_S_COLOR_BAR }, { "bar_color_selected", setconfcolor, SWM_S_COLOR_BAR_SELECTED }, { "bar_delay", NULL, 0 }, /* dummy */ { "bar_enabled", setconfvalue, SWM_S_BAR_ENABLED }, { "bar_enabled_ws", setconfvalue, SWM_S_BAR_ENABLED_WS }, { "bar_font", setconfvalue, SWM_S_BAR_FONT }, { "bar_font_color", setconfcolor, SWM_S_COLOR_BAR_FONT }, { "bar_font_color_selected", setconfcolor, SWM_S_COLOR_BAR_FONT_SELECTED }, { "bar_format", setconfvalue, SWM_S_BAR_FORMAT }, { "bar_justify", setconfvalue, SWM_S_BAR_JUSTIFY }, { "bind", setconfbinding, 0 }, { "border_width", setconfvalue, SWM_S_BORDER_WIDTH }, { "boundary_width", setconfvalue, SWM_S_BOUNDARY_WIDTH }, { "clock_enabled", setconfvalue, SWM_S_CLOCK_ENABLED }, { "clock_format", setconfvalue, SWM_S_CLOCK_FORMAT }, { "color_focus", setconfcolor, SWM_S_COLOR_FOCUS }, { "color_focus_maximized", setconfcolor, SWM_S_COLOR_FOCUS_MAXIMIZED }, { "color_unfocus", setconfcolor, SWM_S_COLOR_UNFOCUS }, { "color_unfocus_maximized", setconfcolor, SWM_S_COLOR_UNFOCUS_MAXIMIZED }, { "cycle_empty", setconfvalue, SWM_S_CYCLE_EMPTY }, { "cycle_visible", setconfvalue, SWM_S_CYCLE_VISIBLE }, { "dialog_ratio", setconfvalue, SWM_S_DIALOG_RATIO }, { "disable_border", setconfvalue, SWM_S_DISABLE_BORDER }, { "focus_close", setconfvalue, SWM_S_FOCUS_CLOSE }, { "focus_close_wrap", setconfvalue, SWM_S_FOCUS_CLOSE_WRAP }, { "focus_default", setconfvalue, SWM_S_FOCUS_DEFAULT }, { "focus_mode", setconfvalue, SWM_S_FOCUS_MODE }, { "iconic_enabled", setconfvalue, SWM_S_ICONIC_ENABLED }, { "java_workaround", NULL, 0 }, /* dummy */ { "keyboard_mapping", setkeymapping, 0 }, { "layout", setlayout, 0 }, { "maximize_hide_bar", setconfvalue, SWM_S_MAXIMIZE_HIDE_BAR }, { "modkey", setconfmodkey, 0 }, { "program", setconfspawn, 0 }, { "quirk", setconfquirk, 0 }, { "region", setconfregion, 0 }, { "region_padding", setconfvalue, SWM_S_REGION_PADDING }, { "screenshot_app", NULL, 0 }, /* dummy */ { "screenshot_enabled", NULL, 0 }, /* dummy */ { "spawn_position", setconfvalue, SWM_S_SPAWN_ORDER }, { "spawn_term", setconfvalue, SWM_S_SPAWN_TERM }, { "stack_enabled", setconfvalue, SWM_S_STACK_ENABLED }, { "term_width", setconfvalue, SWM_S_TERM_WIDTH }, { "tile_gap", setconfvalue, SWM_S_TILE_GAP }, { "title_class_enabled", setconfvalue, SWM_S_WINDOW_CLASS_ENABLED }, /* For backwards compat. */ { "title_name_enabled", setconfvalue, SWM_S_WINDOW_INSTANCE_ENABLED }, /* For backwards compat. */ { "urgent_collapse", setconfvalue, SWM_S_URGENT_COLLAPSE }, { "urgent_enabled", setconfvalue, SWM_S_URGENT_ENABLED }, { "verbose_layout", setconfvalue, SWM_S_VERBOSE_LAYOUT }, { "warp_focus", setconfvalue, SWM_S_WARP_FOCUS }, { "warp_pointer", setconfvalue, SWM_S_WARP_POINTER }, { "window_class_enabled", setconfvalue, SWM_S_WINDOW_CLASS_ENABLED }, { "window_instance_enabled", setconfvalue, SWM_S_WINDOW_INSTANCE_ENABLED }, { "window_name_enabled", setconfvalue, SWM_S_WINDOW_NAME_ENABLED }, { "workspace_clamp", setconfvalue, SWM_S_WORKSPACE_CLAMP }, { "workspace_limit", setconfvalue, SWM_S_WORKSPACE_LIMIT }, { "name", setconfvalue, SWM_S_WORKSPACE_NAME }, }; void _add_startup_exception(const char *fmt, va_list ap) { if (vasprintf(&startup_exception, fmt, ap) == -1) warn("%s: asprintf", __func__); } void add_startup_exception(const char *fmt, ...) { va_list ap; nr_exceptions++; if (startup_exception) return; /* force bar to be enabled due to exception */ bar_enabled = true; va_start(ap, fmt); _add_startup_exception(fmt, ap); va_end(ap); } int conf_load(const char *filename, int keymapping) { FILE *config; char *line = NULL, *cp, *ce, *optsub, *optval = NULL; size_t linelen, lineno = 0; int wordlen, i, optidx, count; struct config_option *opt = NULL; DNPRINTF(SWM_D_CONF, "conf_load: begin\n"); if (filename == NULL) { warnx("conf_load: no filename"); return (1); } DNPRINTF(SWM_D_CONF, "conf_load: open %s\n", filename); if ((config = fopen(filename, "r")) == NULL) { warn("conf_load: fopen: %s", filename); return (1); } while (!feof(config)) { if (line) free(line); if ((line = fparseln(config, &linelen, &lineno, NULL, FPARSELN_UNESCCOMM | FPARSELN_UNESCCONT)) == NULL) { if (ferror(config)) err(1, "%s", filename); else continue; } cp = line; cp += strspn(cp, " \t\n"); /* eat whitespace */ if (cp[0] == '\0') { /* empty line */ continue; } /* get config option */ wordlen = strcspn(cp, "=[ \t\n"); if (wordlen == 0) { add_startup_exception("%s: line %zd: no option found", filename, lineno); continue; } optidx = -1; for (i = 0; i < LENGTH(configopt); i++) { opt = &configopt[i]; if (strncasecmp(cp, opt->name, wordlen) == 0 && (int)strlen(opt->name) == wordlen) { optidx = i; break; } } if (optidx == -1) { add_startup_exception("%s: line %zd: unknown option " "%.*s", filename, lineno, wordlen, cp); continue; } if (keymapping && opt && strcmp(opt->name, "bind")) { add_startup_exception("%s: line %zd: invalid option " "%.*s", filename, lineno, wordlen, cp); continue; } cp += wordlen; cp += strspn(cp, " \t\n"); /* eat whitespace */ /* get [selector] if any */ optsub = NULL; count = 0; if (*cp == '[') { ++count; /* Get length of selector. */ for (ce = ++cp; *ce != '\0'; ++ce) { /* Find matching (not escaped) bracket. */ if (*ce == ']' && *(ce - 1) != '\\') { --count; break; } } if (count > 0) { add_startup_exception("%s: line %zd: syntax " "error: unterminated selector", filename, lineno); continue; } /* ce points at ']'; terminate optsub. */ *ce = '\0'; optsub = cp; cp = ce + 1; } cp += strspn(cp, "= \t\n"); /* eat trailing */ /* get RHS value */ optval = cp; if (strlen(optval) == 0) { add_startup_exception("%s: line %zd: must supply value " "to %s", filename, lineno, opt->name); continue; } /* trim trailing spaces */ ce = optval + strlen(optval) - 1; while (ce > optval && isspace(*ce)) --ce; *(ce + 1) = '\0'; /* call function to deal with it all */ if (opt->func && opt->func(optsub, optval, opt->flags) != 0) { add_startup_exception("%s: line %zd: invalid data for " "%s", filename, lineno, opt->name); continue; } } if (line) free(line); fclose(config); DNPRINTF(SWM_D_CONF, "conf_load: end\n"); return (0); } void set_child_transient(struct ws_win *win, xcb_window_t *trans) { struct ws_win *parent, *w; struct swm_region *r; struct workspace *ws; xcb_icccm_wm_hints_t wmh; parent = find_window(win->transient); if (parent) parent->focus_child = win; else { DNPRINTF(SWM_D_MISC, "set_child_transient: parent doesn't exist" " for %#x trans %#x\n", win->id, win->transient); r = root_to_region(win->s->root, SWM_CK_ALL); ws = r->ws; /* parent doen't exist in our window list */ TAILQ_FOREACH(w, &ws->winlist, entry) { if (xcb_icccm_get_wm_hints_reply(conn, xcb_icccm_get_wm_hints(conn, w->id), &wmh, NULL) != 1) { warnx("can't get hints for %#x", w->id); continue; } if (win->hints.window_group != wmh.window_group) continue; w->focus_child = win; win->transient = w->id; *trans = w->id; DNPRINTF(SWM_D_MISC, "set_child_transient: adjusting " "transient to %#x\n", win->transient); break; } } } pid_t window_get_pid(xcb_window_t win) { pid_t ret = 0; const char *errstr; xcb_atom_t apid; xcb_get_property_cookie_t pc; xcb_get_property_reply_t *pr; apid = get_atom_from_string("_NET_WM_PID"); if (apid == XCB_ATOM_NONE) goto tryharder; pc = xcb_get_property(conn, 0, win, apid, XCB_ATOM_CARDINAL, 0, 1); pr = xcb_get_property_reply(conn, pc, NULL); if (pr == NULL) goto tryharder; if (pr->type != XCB_ATOM_CARDINAL) { free(pr); goto tryharder; } if (pr->type == apid && pr->format == 32) ret = *((pid_t *)xcb_get_property_value(pr)); free(pr); return (ret); tryharder: apid = get_atom_from_string("_SWM_PID"); pc = xcb_get_property(conn, 0, win, apid, XCB_ATOM_STRING, 0, SWM_PROPLEN); pr = xcb_get_property_reply(conn, pc, NULL); if (pr == NULL) return (0); if (pr->type != apid) { free(pr); return (0); } ret = (pid_t)strtonum(xcb_get_property_value(pr), 0, INT_MAX, &errstr); free(pr); return (ret); } int get_swm_ws(xcb_window_t id) { int ws_idx = -1; char *prop = NULL; size_t proplen; const char *errstr; xcb_get_property_reply_t *gpr; gpr = xcb_get_property_reply(conn, xcb_get_property(conn, 0, id, a_swm_ws, XCB_ATOM_STRING, 0, SWM_PROPLEN), NULL); if (gpr == NULL) return (-1); if (gpr->type) { proplen = xcb_get_property_value_length(gpr); if (proplen > 0) { prop = malloc(proplen + 1); if (prop) { memcpy(prop, xcb_get_property_value(gpr), proplen); prop[proplen] = '\0'; } } } free(gpr); if (prop) { DNPRINTF(SWM_D_PROP, "get_swm_ws: _SWM_WS: %s\n", prop); ws_idx = (int)strtonum(prop, 0, workspace_limit - 1, &errstr); if (errstr) { DNPRINTF(SWM_D_PROP, "get_swm_ws: win #%s: %s", errstr, prop); } free(prop); } return ws_idx; } int get_ws_idx(struct ws_win *win) { xcb_get_property_reply_t *gpr; int ws_idx = -1; if (win == NULL) return -1; gpr = xcb_get_property_reply(conn, xcb_get_property(conn, 0, win->id, ewmh[_NET_WM_DESKTOP].atom, XCB_ATOM_CARDINAL, 0, 1), NULL); if (gpr) { if (gpr->type == XCB_ATOM_CARDINAL && gpr->format == 32) ws_idx = *((int *)xcb_get_property_value(gpr)); free(gpr); } if (ws_idx == -1 && !(win->quirks & SWM_Q_IGNORESPAWNWS)) ws_idx = get_swm_ws(win->id); if (ws_idx > workspace_limit - 1 || ws_idx < -1) ws_idx = -1; DNPRINTF(SWM_D_PROP, "get_ws_idx: win %#x, ws_idx: %d\n", win->id, ws_idx); return ws_idx; } void reparent_window(struct ws_win *win) { xcb_void_cookie_t c; xcb_generic_error_t *error; uint32_t wa[2]; win->frame = xcb_generate_id(conn); DNPRINTF(SWM_D_MISC, "reparent_window: win %#x, frame: %#x\n", win->id, win->frame); wa[0] = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_EXPOSURE; #ifdef SWM_DEBUG wa[0] |= XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE; #endif xcb_create_window(conn, XCB_COPY_FROM_PARENT, win->frame, win->s->root, X(win), Y(win), WIDTH(win), HEIGHT(win), 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, wa); win->state = SWM_WIN_STATE_REPARENTING; c = xcb_reparent_window_checked(conn, win->id, win->frame, 0, 0); if ((error = xcb_request_check(conn, c))) { DNPRINTF(SWM_D_MISC, "reparent_window: error:\n"); event_error(error); free(error); /* Abort. */ xcb_destroy_window(conn, win->frame); win->frame = XCB_WINDOW_NONE; } else { xcb_change_save_set(conn, XCB_SET_MODE_INSERT, win->id); } DNPRINTF(SWM_D_MISC, "reparent_window: done.\n"); } void unparent_window(struct ws_win *win) { xcb_change_save_set(conn, XCB_SET_MODE_DELETE, win->id); xcb_reparent_window(conn, win->id, win->s->root, X(win), Y(win)); xcb_destroy_window(conn, win->frame); win->frame = XCB_WINDOW_NONE; win->state = SWM_WIN_STATE_UNPARENTING; } struct ws_win * manage_window(xcb_window_t id, int spawn_pos, bool mapping) { struct ws_win *win = NULL, *ww; struct swm_region *r; struct pid_e *p; struct quirk *qp; xcb_get_geometry_reply_t *gr; xcb_get_window_attributes_reply_t *war = NULL; xcb_window_t trans = XCB_WINDOW_NONE; uint32_t i, wa[1], new_flags; int ws_idx, force_ws = -1; char *class, *instance, *name; if (find_bar(id)) { DNPRINTF(SWM_D_MISC, "manage_window: win %#x is region bar; " "skipping.\n", id); goto out; } if (find_region(id)) { DNPRINTF(SWM_D_MISC, "manage_window: win %#x is region window; " "skipping.\n", id); goto out; } if ((win = find_window(id)) != NULL) { DNPRINTF(SWM_D_MISC, "manage_window: win %#x already " "managed; skipping.)\n", id); return (win); /* Already managed. */ } /* See if window is on the unmanaged list. */ if ((win = find_unmanaged_window(id)) != NULL) { DNPRINTF(SWM_D_MISC, "manage_window: win %#x found on " "unmanaged list.\n", id); TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry); if (TRANS(win)) set_child_transient(win, &trans); goto remanage; } else { DNPRINTF(SWM_D_MISC, "manage_window: win %#x is new.\n", id); } war = xcb_get_window_attributes_reply(conn, xcb_get_window_attributes(conn, id), NULL); if (war == NULL) { DNPRINTF(SWM_D_EVENT, "manage_window: window lost.\n"); goto out; } if (war->override_redirect) { DNPRINTF(SWM_D_EVENT, "manage_window: override_redirect; " "skipping.\n"); goto out; } if (!mapping && war->map_state == XCB_MAP_STATE_UNMAPPED && get_win_state(id) == XCB_ICCCM_WM_STATE_WITHDRAWN) { DNPRINTF(SWM_D_EVENT, "manage_window: window withdrawn; " "skipping.\n"); goto out; } /* Try to get initial window geometry. */ gr = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, id), NULL); if (gr == NULL) { DNPRINTF(SWM_D_MISC, "manage_window: get geometry failed.\n"); goto out; } /* Create and initialize ws_win object. */ if ((win = calloc(1, sizeof(struct ws_win))) == NULL) err(1, "manage_window: calloc: failed to allocate memory for " "new window"); win->id = id; /* Figureout which region the window belongs to. */ r = root_to_region(gr->root, SWM_CK_ALL); /* Ignore window border if there is one. */ WIDTH(win) = gr->width; HEIGHT(win) = gr->height; X(win) = gr->x + gr->border_width; Y(win) = gr->y + gr->border_width; win->bordered = false; win->mapped = (war->map_state != XCB_MAP_STATE_UNMAPPED); win->s = r->s; /* this never changes */ free(gr); /* Select which X events to monitor and set border pixel color. */ wa[0] = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY; xcb_change_window_attributes(conn, win->id, XCB_CW_EVENT_MASK, wa); /* Get WM_SIZE_HINTS. */ xcb_icccm_get_wm_normal_hints_reply(conn, xcb_icccm_get_wm_normal_hints(conn, win->id), &win->sh, NULL); /* Get WM_HINTS. */ xcb_icccm_get_wm_hints_reply(conn, xcb_icccm_get_wm_hints(conn, win->id), &win->hints, NULL); /* Get WM_TRANSIENT_FOR; see if window is a transient. */ xcb_icccm_get_wm_transient_for_reply(conn, xcb_icccm_get_wm_transient_for(conn, win->id), &trans, NULL); if (trans) { win->transient = trans; set_child_transient(win, &win->transient); } /* Get WM_PROTOCOLS. */ get_wm_protocols(win); #ifdef SWM_DEBUG /* Must be after getting WM_HINTS and WM_PROTOCOLS. */ DNPRINTF(SWM_D_FOCUS, "manage_window: input_model: %s\n", get_win_input_model(win)); #endif /* Set initial quirks based on EWMH. */ ewmh_autoquirk(win); /* Determine initial quirks. */ xcb_icccm_get_wm_class_reply(conn, xcb_icccm_get_wm_class(conn, win->id), &win->ch, NULL); class = win->ch.class_name ? win->ch.class_name : ""; instance = win->ch.instance_name ? win->ch.instance_name : ""; name = get_win_name(win->id); DNPRINTF(SWM_D_CLASS, "manage_window: class: %s, instance: %s, " "name: %s\n", class, instance, name); /* java is retarded so treat it special */ if (strstr(instance, "sun-awt")) { DNPRINTF(SWM_D_CLASS, "manage_window: java window detected.\n"); win->java = true; } TAILQ_FOREACH(qp, &quirks, entry) { if (regexec(&qp->regex_class, class, 0, NULL, 0) == 0 && regexec(&qp->regex_instance, instance, 0, NULL, 0) == 0 && regexec(&qp->regex_name, name, 0, NULL, 0) == 0) { DNPRINTF(SWM_D_CLASS, "manage_window: matched " "quirk: %s:%s:%s mask: %#x, ws: %d\n", qp->class, qp->instance, qp->name, qp->quirk, qp->ws); win->quirks = qp->quirk; if (qp->ws >= 0 && qp->ws < workspace_limit) force_ws = qp->ws; } } free(name); /* Reset font sizes (the bruteforce way; no default keybinding). */ if (win->quirks & SWM_Q_XTERM_FONTADJ) { for (i = 0; i < SWM_MAX_FONT_STEPS; i++) fake_keypress(win, XK_KP_Subtract, XCB_MOD_MASK_SHIFT); for (i = 0; i < SWM_MAX_FONT_STEPS; i++) fake_keypress(win, XK_KP_Add, XCB_MOD_MASK_SHIFT); } /* Figure out which workspace the window belongs to. */ if (!(win->quirks & SWM_Q_IGNOREPID) && (p = find_pid(window_get_pid(win->id))) != NULL) { win->ws = &r->s->ws[p->ws]; TAILQ_REMOVE(&pidlist, p, entry); free(p); p = NULL; } else if ((ws_idx = get_ws_idx(win)) != -1 && !TRANS(win)) { /* _SWM_WS is set; use that. */ win->ws = &r->s->ws[ws_idx]; } else if (trans && (ww = find_window(trans)) != NULL) { /* Launch transients in the same ws as parent. */ win->ws = ww->ws; } else { win->ws = r->ws; } if (force_ws != -1) win->ws = &r->s->ws[force_ws]; /* Set the _NET_WM_DESKTOP atom. */ DNPRINTF(SWM_D_PROP, "manage_window: set _NET_WM_DESKTOP: %d\n", win->ws->idx); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id, ewmh[_NET_WM_DESKTOP].atom, XCB_ATOM_CARDINAL, 32, 1, &win->ws->idx); /* Remove any _SWM_WS now that we set _NET_WM_DESKTOP. */ xcb_delete_property(conn, win->id, a_swm_ws); /* WS must already be set for this to work. */ store_float_geom(win); /* Make sure window is positioned inside its region, if its active. */ if (win->ws->r) { region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_HARDBOUNDARY); update_window(win); } remanage: /* Figure out where to insert the window in the workspace list. */ if (trans && (ww = find_window(trans))) TAILQ_INSERT_AFTER(&win->ws->winlist, ww, win, entry); else if (win->ws->focus && spawn_pos == SWM_STACK_ABOVE) TAILQ_INSERT_AFTER(&win->ws->winlist, win->ws->focus, win, entry); else if (win->ws->focus && spawn_pos == SWM_STACK_BELOW) TAILQ_INSERT_BEFORE(win->ws->focus, win, entry); else switch (spawn_pos) { default: case SWM_STACK_TOP: case SWM_STACK_ABOVE: TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry); break; case SWM_STACK_BOTTOM: case SWM_STACK_BELOW: TAILQ_INSERT_HEAD(&win->ws->winlist, win, entry); } ewmh_update_client_list(); TAILQ_INSERT_HEAD(&win->ws->stack, win, stack_entry); lower_window(win); /* Get/apply initial _NET_WM_STATE */ ewmh_get_wm_state(win); /* Apply quirks. */ new_flags = win->ewmh_flags; if (win->quirks & SWM_Q_FLOAT) new_flags |= EWMH_F_ABOVE; if (win->quirks & SWM_Q_ANYWHERE) new_flags |= SWM_F_MANUAL; ewmh_apply_flags(win, new_flags); ewmh_update_wm_state(win); /* Set initial _NET_WM_ALLOWED_ACTIONS */ ewmh_update_actions(win); reparent_window(win); DNPRINTF(SWM_D_MISC, "manage_window: done. win %#x, (x,y) w x h: " "(%d,%d) %d x %d, ws: %d, iconic: %s, transient: %#x\n", win->id, X(win), Y(win), WIDTH(win), HEIGHT(win), win->ws->idx, YESNO(ICONIC(win)), win->transient); out: free(war); return (win); } void free_window(struct ws_win *win) { DNPRINTF(SWM_D_MISC, "free_window: win %#x\n", WINID(win)); if (win == NULL) return; xcb_icccm_get_wm_class_reply_wipe(&win->ch); /* paint memory */ memset(win, 0xff, sizeof *win); /* XXX kill later */ free(win); DNPRINTF(SWM_D_MISC, "free_window: done.\n"); } void unmanage_window(struct ws_win *win) { DNPRINTF(SWM_D_MISC, "unmanage_window: win %#x\n", WINID(win)); if (win == NULL) return; kill_refs(win); unparent_window(win); TAILQ_REMOVE(&win->ws->stack, win, stack_entry); TAILQ_REMOVE(&win->ws->winlist, win, entry); TAILQ_INSERT_TAIL(&win->ws->unmanagedlist, win, entry); ewmh_update_client_list(); } void expose(xcb_expose_event_t *e) { struct ws_win *w; struct swm_bar *b; #ifdef SWM_DEBUG struct workspace *ws; #endif DNPRINTF(SWM_D_EVENT, "expose: win %#x, count: %d\n", e->window, e->count); if (e->count > 0) return; if ((b = find_bar(e->window))) { bar_draw(b); xcb_flush(conn); } else if ((w = find_window(e->window)) && w->frame == e->window) { draw_frame(w); #ifdef SWM_DEBUG ws = w->ws; TAILQ_FOREACH(w, &ws->winlist, entry) debug_refresh(w); #endif xcb_flush(conn); } DNPRINTF(SWM_D_EVENT, "expose: done\n"); } void focusin(xcb_focus_in_event_t *e) { struct ws_win *win; DNPRINTF(SWM_D_EVENT, "focusin: win %#x, mode: %s(%u), " "detail: %s(%u)\n", e->event, get_notify_mode_label(e->mode), e->mode, get_notify_detail_label(e->detail), e->detail); if ((win = find_window(e->event)) && win != win->ws->focus && win != win->ws->focus_pending && e->mode == XCB_NOTIFY_MODE_NORMAL) { win->ws->focus_prev = win->ws->focus; win->ws->focus = win; win->ws->focus_pending = NULL; if (win->ws->focus_prev) draw_frame(win->ws->focus_prev); draw_frame(win); raise_window(win); } } #ifdef SWM_DEBUG void focusout(xcb_focus_out_event_t *e) { DNPRINTF(SWM_D_EVENT, "focusout: win %#x, mode: %s(%u), " "detail: %s(%u)\n", e->event, get_notify_mode_label(e->mode), e->mode, get_notify_detail_label(e->detail), e->detail); } #endif void keypress(xcb_key_press_event_t *e) { struct action *ap; struct binding *bp; xcb_keysym_t keysym; bool replay = true; last_event_time = e->time; keysym = xcb_key_press_lookup_keysym(syms, e, 0); DNPRINTF(SWM_D_EVENT, "keypress: keysym: %u, win (x,y): %#x (%d,%d), " "detail: %u, time: %u, root (x,y): %#x (%d,%d), child: %#x, " "state: %u, cleaned: %u, same_screen: %s\n", keysym, e->event, e->event_x, e->event_y, e->detail, e->time, e->root, e->root_x, e->root_y, e->child, e->state, CLEANMASK(e->state), YESNO(e->same_screen)); bp = binding_lookup(CLEANMASK(e->state), KEYBIND, keysym); if (bp == NULL) { /* Look for catch-all. */ if ((bp = binding_lookup(ANYMOD, KEYBIND, keysym)) == NULL) goto out; } replay = bp->flags & BINDING_F_REPLAY; if ((ap = &actions[bp->action]) == NULL) goto out; if (bp->action == FN_SPAWN_CUSTOM) spawn_custom(root_to_region(e->root, SWM_CK_ALL), &ap->args, bp->spawn_name); else if (ap->func) ap->func(bp, root_to_region(e->root, SWM_CK_ALL), &ap->args); replay = replay && !(ap->flags & FN_F_NOREPLAY); out: if (replay) { DNPRINTF(SWM_D_EVENT, "keypress: replaying.\n"); /* Pass keypress to event window and unfreeze keyboard queue. */ xcb_allow_events(conn, XCB_ALLOW_REPLAY_KEYBOARD, e->time); } else { /* Release freeze. */ xcb_allow_events(conn, XCB_ALLOW_SYNC_KEYBOARD, e->time); } xcb_flush(conn); DNPRINTF(SWM_D_EVENT, "keypress: done.\n"); } void keyrelease(xcb_key_release_event_t *e) { struct action *ap; struct binding *bp; xcb_keysym_t keysym; last_event_time = e->time; keysym = xcb_key_release_lookup_keysym(syms, e, 0); DNPRINTF(SWM_D_EVENT, "keyrelease: keysym: %u, win (x,y): %#x (%d,%d), " "detail: %u, time: %u, root (x,y): %#x (%d,%d), child: %#x, " "state: %u, same_screen: %s\n", keysym, e->event, e->event_x, e->event_y, e->detail, e->time, e->root, e->root_x, e->root_y, e->child, e->state, YESNO(e->same_screen)); bp = binding_lookup(CLEANMASK(e->state), KEYBIND, keysym); if (bp == NULL) /* Look for catch-all. */ bp = binding_lookup(ANYMOD, KEYBIND, keysym); if (bp && (ap = &actions[bp->action]) && !(ap->flags & FN_F_NOREPLAY) && bp->flags & BINDING_F_REPLAY) { xcb_allow_events(conn, XCB_ALLOW_REPLAY_KEYBOARD, e->time); DNPRINTF(SWM_D_EVENT, "keyrelease: replaying.\n"); } else { xcb_allow_events(conn, XCB_ALLOW_SYNC_KEYBOARD, e->time); DNPRINTF(SWM_D_EVENT, "keyrelease: unfreezing.\n"); } xcb_flush(conn); DNPRINTF(SWM_D_EVENT, "keyrelease: done.\n"); } void buttonpress(xcb_button_press_event_t *e) { struct ws_win *win = NULL, *newf; struct swm_region *r, *old_r; struct action *ap; struct binding *bp; bool replay = true; last_event_time = e->time; DNPRINTF(SWM_D_EVENT, "buttonpress: win (x,y): %#x (%d,%d), " "detail: %u, time: %u, root (x,y): %#x (%d,%d), child: %#x, " "state: %u, same_screen: %s\n", e->event, e->event_x, e->event_y, e->detail, e->time, e->root, e->root_x, e->root_y, e->child, e->state, YESNO(e->same_screen)); if (e->event == e->root) { if (e->child) { win = find_window(e->child); } else { /* Focus on empty region */ /* If no windows on region if its empty. */ r = root_to_region(e->root, SWM_CK_POINTER); if (r && TAILQ_EMPTY(&r->ws->winlist)) { old_r = root_to_region(e->root, SWM_CK_FOCUS); if (old_r && old_r != r) unfocus_win(old_r->ws->focus); DNPRINTF(SWM_D_FOCUS, "buttonpress: " "set_input_focus: %#x, revert-to: parent, " "time: %#x\n", e->root, e->time); xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, e->root, e->time); /* Clear bar since empty. */ bar_draw(r->bar); /* No need to replay event. */ replay = false; } } } else { win = find_window(e->event); } if (win) { newf = get_focus_magic(win); if (win->ws->focus == newf && newf != win) { if (win->focus_child == win) win->focus_child = NULL; newf = win; } focus_win(newf); } /* Handle any bound action. */ bp = binding_lookup(CLEANMASK(e->state), BTNBIND, e->detail); if (bp == NULL) { /* Look for catch-all. */ if ((bp = binding_lookup(ANYMOD, BTNBIND, e->detail)) == NULL) goto out; } replay = bp->flags & BINDING_F_REPLAY; if ((ap = &actions[bp->action]) == NULL) goto out; if (bp->action == FN_SPAWN_CUSTOM) spawn_custom(root_to_region(e->root, SWM_CK_ALL), &ap->args, bp->spawn_name); else if (ap->func) ap->func(bp, root_to_region(e->root, SWM_CK_ALL), &ap->args); replay = replay && !(ap->flags & FN_F_NOREPLAY); out: if (replay) { DNPRINTF(SWM_D_EVENT, "buttonpress: replaying.\n"); /* Replay event to event window */ xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, e->time); } else { /* Unfreeze grab events. */ xcb_allow_events(conn, XCB_ALLOW_SYNC_POINTER, e->time); } focus_flush(); } void buttonrelease(xcb_button_release_event_t *e) { struct action *ap; struct binding *bp; last_event_time = e->time; DNPRINTF(SWM_D_EVENT, "buttonrelease: win (x,y): %#x (%d,%d), " "detail: %u, time: %u, root (x,y): %#x (%d,%d), child: %#x, " "state: %u, same_screen: %s\n", e->event, e->event_x, e->event_y, e->detail, e->time, e->root, e->root_x, e->root_y, e->child, e->state, YESNO(e->same_screen)); bp = binding_lookup(CLEANMASK(e->state), BTNBIND, e->detail); if (bp == NULL) /* Look for catch-all. */ bp = binding_lookup(ANYMOD, BTNBIND, e->detail); if (bp && (ap = &actions[bp->action]) && !(ap->flags & FN_F_NOREPLAY) && bp->flags & BINDING_F_REPLAY) { DNPRINTF(SWM_D_EVENT, "buttonrelease: replaying.\n"); xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, e->time); } else { xcb_allow_events(conn, XCB_ALLOW_SYNC_POINTER, e->time); } xcb_flush(conn); } #ifdef SWM_DEBUG char * get_win_input_model(struct ws_win *win) { char *inputmodel; /* * Input Model Input Field WM_TAKE_FOCUS * No Input False Absent * Passive True Absent * Locally Active True Present * Globally Active False Present */ if (ACCEPTS_FOCUS(win)) inputmodel = (win->take_focus) ? "Locally Active" : "Passive"; else inputmodel = (win->take_focus) ? "Globally Active" : "No Input"; return inputmodel; } void print_win_geom(xcb_window_t w) { xcb_get_geometry_reply_t *wa; wa = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, w), NULL); if (wa == NULL) { DNPRINTF(SWM_D_MISC, "print_win_geom: window not found: %#x\n", w); return; } DNPRINTF(SWM_D_MISC, "print_win_geom: win %#x, root: %#x, " "depth: %u, (x,y) w x h: (%d,%d) %d x %d, border: %d\n", w, wa->root, wa->depth, wa->x, wa->y, wa->width, wa->height, wa->border_width); free(wa); } #endif #ifdef SWM_DEBUG char * get_stack_mode_name(uint8_t mode) { char *name; switch (mode) { case XCB_STACK_MODE_ABOVE: name = "Above"; break; case XCB_STACK_MODE_BELOW: name = "Below"; break; case XCB_STACK_MODE_TOP_IF: name = "TopIf"; break; case XCB_STACK_MODE_BOTTOM_IF: name = "BottomIf"; break; case XCB_STACK_MODE_OPPOSITE: name = "Opposite"; break; default: name = "Unknown"; } return name; } #endif void configurerequest(xcb_configure_request_event_t *e) { struct ws_win *win; struct swm_region *r = NULL; int i = 0; uint32_t wc[7] = {0}; uint16_t mask = 0; bool new = false; if ((win = find_window(e->window)) == NULL) if ((win = find_unmanaged_window(e->window)) == NULL) new = true; #ifdef SWM_DEBUG if (swm_debug & SWM_D_EVENT) { print_win_geom(e->window); DNPRINTF(SWM_D_EVENT, "configurerequest: win %#x, " "parent: %#x, new: %s, value_mask: %u { ", e->window, e->parent, YESNO(new), e->value_mask); if (e->value_mask & XCB_CONFIG_WINDOW_X) DPRINTF("X: %d ", e->x); if (e->value_mask & XCB_CONFIG_WINDOW_Y) DPRINTF("Y: %d ", e->y); if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) DPRINTF("W: %u ", e->width); if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) DPRINTF("H: %u ", e->height); if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) DPRINTF("Border: %u ", e->border_width); if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) DPRINTF("Sibling: %#x ", e->sibling); if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) DPRINTF("StackMode: %s(%u) ", get_stack_mode_name(e->stack_mode), e->stack_mode); DPRINTF("}\n"); } #endif if (new) { if (e->value_mask & XCB_CONFIG_WINDOW_X) { mask |= XCB_CONFIG_WINDOW_X; wc[i++] = e->x; } if (e->value_mask & XCB_CONFIG_WINDOW_Y) { mask |= XCB_CONFIG_WINDOW_Y; wc[i++] = e->y; } if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) { mask |= XCB_CONFIG_WINDOW_WIDTH; wc[i++] = e->width; } if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) { mask |= XCB_CONFIG_WINDOW_HEIGHT; wc[i++] = e->height; } if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) { mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH; wc[i++] = e->border_width; } if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) { mask |= XCB_CONFIG_WINDOW_SIBLING; wc[i++] = e->sibling; } if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) { mask |= XCB_CONFIG_WINDOW_STACK_MODE; wc[i++] = e->stack_mode; } if (mask != 0) { xcb_configure_window(conn, e->window, mask, wc); xcb_flush(conn); } } else if ((!MANUAL(win) || win->quirks & SWM_Q_ANYWHERE) && !FULLSCREEN(win) && !MAXIMIZED(win)) { if (win->ws->r) r = win->ws->r; else if (win->ws->old_r) r = win->ws->old_r; /* windows are centered unless ANYWHERE quirk is set. */ if (win->quirks & SWM_Q_ANYWHERE) { if (e->value_mask & XCB_CONFIG_WINDOW_X) { win->g_float.x = e->x; if (r) win->g_float.x -= X(r); } if (e->value_mask & XCB_CONFIG_WINDOW_Y) { win->g_float.y = e->y; if (r) win->g_float.y -= Y(r); } } if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) win->g_float.w = e->width; if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) win->g_float.h = e->height; win->g_floatvalid = true; if (!MAXIMIZED(win) && !FULLSCREEN(win) && (TRANS(win) || (ABOVE(win) && win->ws->cur_layout != &layouts[SWM_MAX_STACK]))) { WIDTH(win) = win->g_float.w; HEIGHT(win) = win->g_float.h; if (r != NULL) { update_floater(win); focus_flush(); } else { config_win(win, e); xcb_flush(conn); } } else { config_win(win, e); xcb_flush(conn); } } else { config_win(win, e); xcb_flush(conn); } DNPRINTF(SWM_D_EVENT, "configurerequest: done.\n"); } void configurenotify(xcb_configure_notify_event_t *e) { struct ws_win *win; DNPRINTF(SWM_D_EVENT, "configurenotify: win %#x, event win: %#x, " "(x,y) WxH: (%d,%d) %ux%u, border: %u, above_sibling: %#x, " "override_redirect: %s\n", e->window, e->event, e->x, e->y, e->width, e->height, e->border_width, e->above_sibling, YESNO(e->override_redirect)); win = find_window(e->window); if (win) { adjust_font(win); if (font_adjusted && win->ws->r) { stack(win->ws->r); xcb_flush(conn); } } } void destroynotify(xcb_destroy_notify_event_t *e) { struct ws_win *win; struct workspace *ws; DNPRINTF(SWM_D_EVENT, "destroynotify: win %#x\n", e->window); if ((win = find_window(e->window)) == NULL) { if ((win = find_unmanaged_window(e->window)) == NULL) goto out; /* Window is on unmanaged list. */ TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry); free_window(win); goto out; } ws = win->ws; if (win->frame == e->window) { DNPRINTF(SWM_D_EVENT, "destroynotify: frame for win %#x\n", win->id); win->frame = XCB_WINDOW_NONE; goto out; } if (focus_mode != SWM_FOCUS_FOLLOW) { /* If we were focused, make sure we focus on something else. */ if (win == ws->focus) { ws->focus_pending = get_focus_prev(win); if (ws->focus_pending == win) ws->focus_pending = NULL; } } unmanage_window(win); TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry); free_window(win); stack(ws->r); if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(ws)) { if (ws->focus_pending) { focus_win(ws->focus_pending); ws->focus_pending = NULL; } else if (ws->focus == NULL) { DNPRINTF(SWM_D_FOCUS, "destroynotify: set_input_focus: " "%#x, revert-to: parent, time: 0\n", ws->r->id); xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, ws->r->id, XCB_CURRENT_TIME); } focus_flush(); } out: DNPRINTF(SWM_D_EVENT, "destroynotify: done.\n"); } #ifdef SWM_DEBUG char * get_notify_detail_label(uint8_t detail) { char *label; switch (detail) { case XCB_NOTIFY_DETAIL_ANCESTOR: label = "Ancestor"; break; case XCB_NOTIFY_DETAIL_VIRTUAL: label = "Virtual"; break; case XCB_NOTIFY_DETAIL_INFERIOR: label = "Inferior"; break; case XCB_NOTIFY_DETAIL_NONLINEAR: label = "Nonlinear"; break; case XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL: label = "NonlinearVirtual"; break; case XCB_NOTIFY_DETAIL_POINTER: label = "Pointer"; break; case XCB_NOTIFY_DETAIL_POINTER_ROOT: label = "PointerRoot"; break; case XCB_NOTIFY_DETAIL_NONE: label = "None"; break; default: label = "Unknown"; } return label; } char * get_notify_mode_label(uint8_t mode) { char *label; switch (mode) { case XCB_NOTIFY_MODE_NORMAL: label = "Normal"; break; case XCB_NOTIFY_MODE_GRAB: label = "Grab"; break; case XCB_NOTIFY_MODE_UNGRAB: label = "Ungrab"; break; case XCB_NOTIFY_MODE_WHILE_GRABBED: label = "WhileGrabbed"; break; default: label = "Unknown"; } return label; } char * get_state_mask_label(uint16_t state) { char *label; switch (state) { case XCB_KEY_BUT_MASK_SHIFT: label = "ShiftMask"; break; case XCB_KEY_BUT_MASK_LOCK: label = "LockMask"; break; case XCB_KEY_BUT_MASK_CONTROL: label = "ControlMask"; break; case XCB_KEY_BUT_MASK_MOD_1: label = "Mod1Mask"; break; case XCB_KEY_BUT_MASK_MOD_2: label = "Mod2Mask"; break; case XCB_KEY_BUT_MASK_MOD_3: label = "Mod3Mask"; break; case XCB_KEY_BUT_MASK_MOD_4: label = "Mod4Mask"; break; case XCB_KEY_BUT_MASK_MOD_5: label = "Mod5Mask"; break; case XCB_KEY_BUT_MASK_BUTTON_1: label = "Button1Mask"; break; case XCB_KEY_BUT_MASK_BUTTON_2: label = "Button2Mask"; break; case XCB_KEY_BUT_MASK_BUTTON_3: label = "Button3Mask"; break; case XCB_KEY_BUT_MASK_BUTTON_4: label = "Button4Mask"; break; case XCB_KEY_BUT_MASK_BUTTON_5: label = "Button5Mask"; break; case 0: label = "None"; break; default: label = "Unknown"; } return label; } #endif void enternotify(xcb_enter_notify_event_t *e) { struct ws_win *win; struct swm_region *r; last_event_time = e->time; DNPRINTF(SWM_D_FOCUS, "enternotify: time: %u, win (x,y): %#x " "(%d,%d), mode: %s(%d), detail: %s(%d), root (x,y): %#x (%d,%d), " "child: %#x, same_screen_focus: %s, state: %s(%d)\n", e->time, e->event, e->event_x, e->event_y, get_notify_mode_label(e->mode), e->mode, get_notify_detail_label(e->detail), e->detail, e->root, e->root_x, e->root_y, e->child, YESNO(e->same_screen_focus), get_state_mask_label(e->state), e->state); if (e->event == e->root && e->child == XCB_WINDOW_NONE && e->mode == XCB_NOTIFY_MODE_GRAB && e->detail == XCB_NOTIFY_DETAIL_INFERIOR) { DNPRINTF(SWM_D_EVENT, "enternotify: grab inferior; ignoring.\n"); return; } if (focus_mode == SWM_FOCUS_MANUAL && e->mode == XCB_NOTIFY_MODE_NORMAL) { DNPRINTF(SWM_D_EVENT, "enternotify: manual focus; ignoring.\n"); return; } if (focus_mode != SWM_FOCUS_FOLLOW && e->mode == XCB_NOTIFY_MODE_UNGRAB && e->detail != XCB_NOTIFY_DETAIL_ANCESTOR) { DNPRINTF(SWM_D_EVENT, "enternotify: ungrab; ignoring.\n"); return; } if ((win = find_window(e->event)) == NULL) { if (e->event == e->root) { /* If no windows on pointer region, then focus root. */ r = root_to_region(e->root, SWM_CK_POINTER); if (r == NULL) { DNPRINTF(SWM_D_EVENT, "enternotify: " "NULL region; ignoring.\n"); return; } focus_region(r); } else { DNPRINTF(SWM_D_EVENT, "enternotify: window is NULL; " "ignoring\n"); return; } } else { if (e->mode == XCB_NOTIFY_MODE_NORMAL && e->detail == XCB_NOTIFY_DETAIL_INFERIOR) { DNPRINTF(SWM_D_EVENT, "enternotify: entering from " "inferior; ignoring\n"); return; } focus_win(get_focus_magic(win)); } DNPRINTF(SWM_D_EVENT, "enternotify: done\n"); xcb_flush(conn); } #ifdef SWM_DEBUG void leavenotify(xcb_leave_notify_event_t *e) { last_event_time = e->time; DNPRINTF(SWM_D_FOCUS, "leavenotify: time: %u, win (x,y): %#x " "(%d,%d), mode: %s(%d), detail: %s(%d), root (x,y): %#x (%d,%d), " "child: %#x, same_screen_focus: %s, state: %s(%d)\n", e->time, e->event, e->event_x, e->event_y, get_notify_mode_label(e->mode), e->mode, get_notify_detail_label(e->detail), e->detail, e->root, e->root_x, e->root_y, e->child, YESNO(e->same_screen_focus), get_state_mask_label(e->state), e->state); } #endif void mapnotify(xcb_map_notify_event_t *e) { struct ws_win *win, *parent = NULL; struct workspace *ws; DNPRINTF(SWM_D_EVENT, "mapnotify: win %#x\n", e->window); if ((win = manage_window(e->window, spawn_position, false)) == NULL) return; ws = win->ws; if (win->state == SWM_WIN_STATE_REPARENTING) return; if (ws->r == NULL) { unmap_window(win); goto out; } /* Need to know if win was mapped due to ws switch. */ if (ws->state == SWM_WS_STATE_MAPPED) { if (ws->focus_pending && TRANS(ws->focus_pending)) parent = find_window(win->transient); /* If window's parent is maximized, don't clear it. */ if ((parent == NULL) || !MAXIMIZED(parent)) if (clear_maximized(ws) > 0) stack(ws->r); } win->mapped = true; set_win_state(win, XCB_ICCCM_WM_STATE_NORMAL); if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(win->ws)) { if (ws->focus_pending == win) { focus_win(win); ws->focus_pending = NULL; center_pointer(ws->r); focus_flush(); } } out: DNPRINTF(SWM_D_EVENT, "mapnotify: done\n"); xcb_flush(conn); } void mappingnotify(xcb_mapping_notify_event_t *e) { if (e->request != XCB_MAPPING_POINTER) { xcb_refresh_keyboard_mapping(syms, e); grabkeys(); } grabbuttons(); } void maprequest(xcb_map_request_event_t *e) { struct ws_win *win, *w = NULL; DNPRINTF(SWM_D_EVENT, "maprequest: win %#x\n", e->window); win = manage_window(e->window, spawn_position, true); if (win == NULL) goto out; /* The new window should get focus; prepare. */ if (focus_mode != SWM_FOCUS_FOLLOW && !(win->quirks & SWM_Q_NOFOCUSONMAP) && ACCEPTS_FOCUS(win)) { if (win->quirks & SWM_Q_FOCUSONMAP_SINGLE) { /* See if other wins of same type are already mapped. */ TAILQ_FOREACH(w, &win->ws->winlist, entry) { if (w == win || !w->mapped) continue; if (w->ch.class_name && win->ch.class_name && strcmp(w->ch.class_name, win->ch.class_name) == 0 && w->ch.instance_name && win->ch.instance_name && strcmp(w->ch.instance_name, win->ch.instance_name) == 0) break; } } if (w == NULL) win->ws->focus_pending = get_focus_magic(win); } /* All windows need to be mapped if they are in the current workspace.*/ stack(win->ws->r); /* Ignore EnterNotify to handle the mapnotify without interference. */ if (focus_mode == SWM_FOCUS_DEFAULT) event_drain(XCB_ENTER_NOTIFY); out: DNPRINTF(SWM_D_EVENT, "maprequest: done.\n"); } void motionnotify(xcb_motion_notify_event_t *e) { struct swm_region *r = NULL; int i, num_screens; last_event_time = e->time; DNPRINTF(SWM_D_FOCUS, "motionnotify: time: %u, win (x,y): %#x " "(%d,%d), detail: %s(%d), root (x,y): %#x (%d,%d), " "child: %#x, same_screen_focus: %s, state: %d\n", e->time, e->event, e->event_x, e->event_y, get_notify_detail_label(e->detail), e->detail, e->root, e->root_x, e->root_y, e->child, YESNO(e->same_screen), e->state); if (focus_mode == SWM_FOCUS_MANUAL) return; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) if (screens[i].root == e->root) break; TAILQ_FOREACH(r, &screens[i].rl, entry) if (X(r) <= e->root_x && e->root_x < MAX_X(r) && Y(r) <= e->root_y && e->root_y < MAX_Y(r)) break; focus_region(r); } #ifdef SWM_DEBUG char * get_atom_name(xcb_atom_t atom) { char *name = NULL; #ifdef SWM_DEBUG_ATOM_NAMES /* * This should be disabled during most debugging since * xcb_get_* causes an xcb_flush. */ size_t len; xcb_get_atom_name_reply_t *r; r = xcb_get_atom_name_reply(conn, xcb_get_atom_name(conn, atom), NULL); if (r) { len = xcb_get_atom_name_name_length(r); if (len > 0) { name = malloc(len + 1); if (name) { memcpy(name, xcb_get_atom_name_name(r), len); name[len] = '\0'; } } free(r); } #else (void)atom; #endif return (name); } #endif void propertynotify(xcb_property_notify_event_t *e) { struct ws_win *win; struct workspace *ws; #ifdef SWM_DEBUG char *name; name = get_atom_name(e->atom); DNPRINTF(SWM_D_EVENT, "propertynotify: win %#x, atom: %s(%u), " "time: %#x, state: %u\n", e->window, name, e->atom, e->time, e->state); free(name); #endif last_event_time = e->time; win = find_window(e->window); if (win == NULL) return; ws = win->ws; if (e->atom == a_state) { /* State just changed, make sure it gets focused if mapped. */ if (e->state == XCB_PROPERTY_NEW_VALUE) { if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(ws)) { if (win->mapped && win->state == SWM_WIN_STATE_REPARENTED && ws->focus_pending == win) { focus_win(ws->focus_pending); ws->focus_pending = NULL; } } } } else if (e->atom == XCB_ATOM_WM_CLASS || e->atom == XCB_ATOM_WM_NAME) { if (ws->r) bar_draw(ws->r->bar); } else if (e->atom == a_prot) { get_wm_protocols(win); } else if (e->atom == XCB_ATOM_WM_NORMAL_HINTS) { xcb_icccm_get_wm_normal_hints_reply(conn, xcb_icccm_get_wm_normal_hints(conn, win->id), &win->sh, NULL); } xcb_flush(conn); } void reparentnotify(xcb_reparent_notify_event_t *e) { struct ws_win *win; DNPRINTF(SWM_D_EVENT, "reparentnotify: event: %#x, win %#x, " "parent: %#x, (x,y): (%u,%u), override_redirect: %u\n", e->event, e->window, e->parent, e->x, e->y, e->override_redirect); win = find_window(e->window); if (win) { if (win->state == SWM_WIN_STATE_REPARENTING) { win->state = SWM_WIN_STATE_REPARENTED; if (win->ws->r && !ICONIC(win)) map_window(win); else unmap_window(win); update_window(win); update_win_stacking(win); } else if (win->state == SWM_WIN_STATE_UNPARENTING) { win->state = SWM_WIN_STATE_UNPARENTED; } } } void unmapnotify(xcb_unmap_notify_event_t *e) { struct ws_win *win; struct workspace *ws; DNPRINTF(SWM_D_EVENT, "unmapnotify: win %#x\n", e->window); /* If we aren't managing the window, then ignore. */ win = find_window(e->window); if (win == NULL || win->id != e->window) { DNPRINTF(SWM_D_EVENT, "unmapnotify: ignore unmanaged.\n"); return; } /* Do nothing if already withdrawn. */ if (!win->mapped && !ICONIC(win)) { DNPRINTF(SWM_D_EVENT, "unmapnotify: ignore withdrawn.\n"); return; } ws = win->ws; win->mapped = false; /* Ignore if reparenting-related. */ if (win->state != SWM_WIN_STATE_REPARENTED) { DNPRINTF(SWM_D_EVENT, "unmapnotify: ignore not reparented.\n"); return; } /* If win was focused, make sure to focus on something else. */ if (win == ws->focus) { if (focus_mode != SWM_FOCUS_FOLLOW) { ws->focus_pending = get_focus_prev(win); DNPRINTF(SWM_D_EVENT, "unmapnotify: " "focus_pending: %#x\n", WINID(ws->focus_pending)); } unfocus_win(win); } if (ICONIC(win)) { /* Iconify. */ DNPRINTF(SWM_D_EVENT, "unmapnotify: iconify.\n"); set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC); } else { /* Withdraw. */ DNPRINTF(SWM_D_EVENT, "unmapnotify: withdraw.\n"); set_win_state(win, XCB_ICCCM_WM_STATE_WITHDRAWN); unmanage_window(win); } stack(ws->r); /* Update focus if ws is active. */ if (WS_FOCUSED(ws)) { if (focus_mode == SWM_FOCUS_FOLLOW) { focus_win(get_pointer_win(ws->r->s->root)); } else if (ws->focus_pending) { focus_win(ws->focus_pending); ws->focus_pending = NULL; } else if (ws->focus == NULL) { DNPRINTF(SWM_D_FOCUS, "unmapnotify: set_input_focus: " "%#x, revert-to: parent, time: 0\n", ws->r->id); xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, ws->r->id, XCB_CURRENT_TIME); } } center_pointer(ws->r); focus_flush(); DNPRINTF(SWM_D_EVENT, "unmapnotify: done.\n"); } #ifdef SWM_DEBUG char * get_source_type_label(uint32_t type) { char *label; switch (type) { case EWMH_SOURCE_TYPE_NONE: label = "None"; break; case EWMH_SOURCE_TYPE_NORMAL: label = "Normal"; break; case EWMH_SOURCE_TYPE_OTHER: label = "Other"; break; default: label = "Invalid"; } return label; } #endif void clientmessage(xcb_client_message_event_t *e) { struct ws_win *win; struct swm_region *r = NULL; union arg a; uint32_t vals[4]; int num_screens, i; xcb_map_request_event_t mre; #ifdef SWM_DEBUG char *name; name = get_atom_name(e->type); DNPRINTF(SWM_D_EVENT, "clientmessage: win %#x, atom: %s(%u)\n", e->window, name, e->type); free(name); #endif if (e->type == ewmh[_NET_CURRENT_DESKTOP].atom) { num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) if (screens[i].root == e->window) { r = screens[i].r_focus; break; } if (r && e->data.data32[0] < (uint32_t)workspace_limit) { a.id = e->data.data32[0]; switchws(NULL, r, &a); focus_flush(); } return; } else if (e->type == ewmh[_NET_REQUEST_FRAME_EXTENTS].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: set _NET_FRAME_EXTENTS on window.\n"); vals[0] = vals[1] = vals[2] = vals[3] = border_width; xcb_change_property(conn, XCB_PROP_MODE_REPLACE, e->window, a_net_frame_extents, XCB_ATOM_CARDINAL, 32, 4, vals); xcb_flush(conn); return; } win = find_window(e->window); if (win == NULL) { if (e->type == ewmh[_NET_ACTIVE_WINDOW].atom) { /* Manage the window with maprequest. */ DNPRINTF(SWM_D_EVENT, "clientmessage: request focus on " "unmanaged window.\n"); mre.window = e->window; maprequest(&mre); } return; } if (e->type == ewmh[_NET_ACTIVE_WINDOW].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_ACTIVE_WINDOW, " "source_type: %s(%d)\n", get_source_type_label(e->data.data32[0]), e->data.data32[0]); /* * Allow focus changes that are a result of direct user * action and from applications that use the old EWMH spec. */ if (e->data.data32[0] != EWMH_SOURCE_TYPE_NORMAL || win->quirks & SWM_Q_OBEYAPPFOCUSREQ) { if (WS_FOCUSED(win->ws)) focus_win(win); else win->ws->focus_pending = win; } } else if (e->type == ewmh[_NET_CLOSE_WINDOW].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_CLOSE_WINDOW\n"); if (win->can_delete) client_msg(win, a_delete, 0); else xcb_kill_client(conn, win->id); } else if (e->type == ewmh[_NET_MOVERESIZE_WINDOW].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_MOVERESIZE_WINDOW\n"); if (ABOVE(win)) { if (e->data.data32[0] & (1<<8)) /* x */ X(win) = e->data.data32[1]; if (e->data.data32[0] & (1<<9)) /* y */ Y(win) = e->data.data32[2]; if (e->data.data32[0] & (1<<10)) /* width */ WIDTH(win) = e->data.data32[3]; if (e->data.data32[0] & (1<<11)) /* height */ HEIGHT(win) = e->data.data32[4]; update_window(win); } else { /* Notify no change was made. */ config_win(win, NULL); /* TODO: Change stack sizes */ } } else if (e->type == ewmh[_NET_RESTACK_WINDOW].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_RESTACK_WINDOW\n"); vals[0] = e->data.data32[1]; /* Sibling window. */ vals[1] = e->data.data32[2]; /* Stack mode detail. */ if (win->frame != XCB_WINDOW_NONE) xcb_configure_window(conn, win->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, vals); } else if (e->type == ewmh[_NET_WM_STATE].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_STATE\n"); ewmh_change_wm_state(win, e->data.data32[1], e->data.data32[0]); if (e->data.data32[2]) ewmh_change_wm_state(win, e->data.data32[2], e->data.data32[0]); ewmh_update_wm_state(win); stack(win->ws->r); } else if (e->type == ewmh[_NET_WM_DESKTOP].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_DESKTOP\n"); r = win->ws->r; win_to_ws(win, e->data.data32[0], true); /* Stack source and destination ws, if mapped. */ if (r != win->ws->r) { if (r) stack(r); if (win->ws->r) { if (FLOATING(win)) load_float_geom(win); stack(win->ws->r); } } } focus_flush(); } void check_conn(void) { int errcode = xcb_connection_has_error(conn); #ifdef XCB_CONN_ERROR char *s; switch (errcode) { case XCB_CONN_ERROR: s = "Socket error, pipe error or other stream error."; break; case XCB_CONN_CLOSED_EXT_NOTSUPPORTED: s = "Extension not supported."; break; case XCB_CONN_CLOSED_MEM_INSUFFICIENT: s = "Insufficient memory."; break; case XCB_CONN_CLOSED_REQ_LEN_EXCEED: s = "Request length was exceeded."; break; case XCB_CONN_CLOSED_PARSE_ERR: s = "Error parsing display string."; break; default: s = "Unknown error."; } if (errcode) errx(errcode, "X CONNECTION ERROR: %s", s); #else if (errcode) errx(errcode, "X CONNECTION ERROR"); #endif } int enable_wm(void) { int num_screens, i; const uint32_t val = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_OWNER_GRAB_BUTTON | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_PROPERTY_CHANGE; xcb_screen_t *sc; xcb_void_cookie_t wac; xcb_generic_error_t *error; /* this causes an error if some other window manager is running */ num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { if ((sc = get_screen(i)) == NULL) errx(1, "ERROR: can't get screen %d.", i); DNPRINTF(SWM_D_INIT, "enable_wm: screen %d, root: %#x\n", i, sc->root); wac = xcb_change_window_attributes_checked(conn, sc->root, XCB_CW_EVENT_MASK, &val); if ((error = xcb_request_check(conn, wac))) { DNPRINTF(SWM_D_INIT, "enable_wm: error_code: %u\n", error->error_code); free(error); return 1; } } return 0; } void new_region(struct swm_screen *s, int x, int y, int w, int h) { struct swm_region *r = NULL, *n; struct workspace *ws = NULL; int i; uint32_t wa[1]; DNPRINTF(SWM_D_MISC, "new region: screen[%d]:%dx%d+%d+%d\n", s->idx, w, h, x, y); /* remove any conflicting regions */ n = TAILQ_FIRST(&s->rl); while (n) { r = n; n = TAILQ_NEXT(r, entry); if (X(r) < (x + w) && (X(r) + WIDTH(r)) > x && Y(r) < (y + h) && (Y(r) + HEIGHT(r)) > y) { if (r->ws->r != NULL) r->ws->old_r = r->ws->r; r->ws->r = NULL; bar_cleanup(r); xcb_destroy_window(conn, r->id); r->id = XCB_WINDOW_NONE; TAILQ_REMOVE(&s->rl, r, entry); TAILQ_INSERT_TAIL(&s->orl, r, entry); } } /* search old regions for one to reuse */ /* size + location match */ TAILQ_FOREACH(r, &s->orl, entry) if (X(r) == x && Y(r) == y && HEIGHT(r) == h && WIDTH(r) == w) break; /* size match */ TAILQ_FOREACH(r, &s->orl, entry) if (HEIGHT(r) == h && WIDTH(r) == w) break; if (r != NULL) { TAILQ_REMOVE(&s->orl, r, entry); /* try to use old region's workspace */ if (r->ws->r == NULL) ws = r->ws; } else if ((r = calloc(1, sizeof(struct swm_region))) == NULL) err(1, "new_region: calloc: failed to allocate memory " "for screen"); /* if we don't have a workspace already, find one */ if (ws == NULL) { for (i = 0; i < workspace_limit; i++) if (s->ws[i].r == NULL) { ws = &s->ws[i]; break; } } if (ws == NULL) errx(1, "new_region: no free workspaces"); if (ws->state == SWM_WS_STATE_HIDDEN) ws->state = SWM_WS_STATE_MAPPING; X(r) = x; Y(r) = y; WIDTH(r) = w; HEIGHT(r) = h; r->bar = NULL; r->s = s; r->ws = ws; r->ws_prior = NULL; ws->r = r; outputs++; TAILQ_INSERT_TAIL(&s->rl, r, entry); /* Invisible region window to detect pointer events on empty regions. */ r->id = xcb_generate_id(conn); wa[0] = XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_POINTER_MOTION_HINT; xcb_create_window(conn, XCB_COPY_FROM_PARENT, r->id, r->s->root, X(r), Y(r), WIDTH(r), HEIGHT(r), 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, wa); /* Make sure region input is at the bottom. */ wa[0] = XCB_STACK_MODE_BELOW; xcb_configure_window(conn, r->id, XCB_CONFIG_WINDOW_STACK_MODE, wa); xcb_map_window(conn, r->id); } void scan_randr(int idx) { #ifdef SWM_XRR_HAS_CRTC int c; int ncrtc = 0; #endif /* SWM_XRR_HAS_CRTC */ struct swm_region *r; int num_screens; xcb_randr_get_screen_resources_current_cookie_t src; xcb_randr_get_screen_resources_current_reply_t *srr; xcb_randr_get_crtc_info_cookie_t cic; xcb_randr_get_crtc_info_reply_t *cir = NULL; xcb_randr_crtc_t *crtc; xcb_screen_t *screen; DNPRINTF(SWM_D_MISC, "scan_randr: screen: %d\n", idx); if ((screen = get_screen(idx)) == NULL) errx(1, "ERROR: can't get screen %d.", idx); num_screens = get_screen_count(); if (idx >= num_screens) errx(1, "scan_randr: invalid screen"); /* remove any old regions */ while ((r = TAILQ_FIRST(&screens[idx].rl)) != NULL) { r->ws->old_r = r->ws->r = NULL; bar_cleanup(r); xcb_destroy_window(conn, r->id); r->id = XCB_WINDOW_NONE; TAILQ_REMOVE(&screens[idx].rl, r, entry); TAILQ_INSERT_TAIL(&screens[idx].orl, r, entry); } outputs = 0; /* map virtual screens onto physical screens */ #ifdef SWM_XRR_HAS_CRTC if (randr_support) { src = xcb_randr_get_screen_resources_current(conn, screens[idx].root); srr = xcb_randr_get_screen_resources_current_reply(conn, src, NULL); if (srr == NULL) { new_region(&screens[idx], 0, 0, screen->width_in_pixels, screen->height_in_pixels); goto out; } else ncrtc = srr->num_crtcs; crtc = xcb_randr_get_screen_resources_current_crtcs(srr); for (c = 0; c < ncrtc; c++) { cic = xcb_randr_get_crtc_info(conn, crtc[c], XCB_CURRENT_TIME); cir = xcb_randr_get_crtc_info_reply(conn, cic, NULL); if (cir == NULL) continue; if (cir->num_outputs == 0) { free(cir); continue; } if (cir->mode == 0) new_region(&screens[idx], 0, 0, screen->width_in_pixels, screen->height_in_pixels); else new_region(&screens[idx], cir->x, cir->y, cir->width, cir->height); free(cir); } free(srr); } #endif /* SWM_XRR_HAS_CRTC */ /* If detection failed, create a single region that spans the screen. */ if (TAILQ_EMPTY(&screens[idx].rl)) new_region(&screens[idx], 0, 0, screen->width_in_pixels, screen->height_in_pixels); out: /* The screen shouldn't focus on unused regions. */ TAILQ_FOREACH(r, &screens[idx].orl, entry) { if (screens[idx].r_focus == r) screens[idx].r_focus = NULL; } DNPRINTF(SWM_D_MISC, "scan_randr: done.\n"); } void screenchange(xcb_randr_screen_change_notify_event_t *e) { struct swm_region *r; struct workspace *ws; struct ws_win *win; int i, j, num_screens; DNPRINTF(SWM_D_EVENT, "screenchange: root: %#x\n", e->root); num_screens = get_screen_count(); /* silly event doesn't include the screen index */ for (i = 0; i < num_screens; i++) if (screens[i].root == e->root) break; if (i >= num_screens) errx(1, "screenchange: screen not found"); /* brute force for now, just re-enumerate the regions */ scan_randr(i); #ifdef SWM_DEBUG print_win_geom(e->root); #endif /* add bars to all regions */ TAILQ_FOREACH(r, &screens[i].rl, entry) bar_setup(r); /* Stack all regions. */ TAILQ_FOREACH(r, &screens[i].rl, entry) stack(r); /* Make sure a region has focus. */ if (screens[i].r_focus == NULL) { r = TAILQ_FIRST(&screens[i].rl); if (r != NULL) focus_region(r); } /* Cleanup unused previously visible workspaces. */ for (j = 0; j < workspace_limit; j++) { ws = &screens[i].ws[j]; if (ws->r == NULL && ws->state != SWM_WS_STATE_HIDDEN) { TAILQ_FOREACH(win, &ws->winlist, entry) unmap_window(win); ws->state = SWM_WS_STATE_HIDDEN; } } focus_flush(); /* Update workspace state and bar on all regions. */ TAILQ_FOREACH(r, &screens[i].rl, entry) { r->ws->state = SWM_WS_STATE_MAPPED; bar_draw(r->bar); } } void grab_windows(void) { struct swm_region *r = NULL; xcb_query_tree_cookie_t qtc; xcb_query_tree_reply_t *qtr; xcb_get_property_cookie_t pc; xcb_get_property_reply_t *pr; xcb_window_t *wins = NULL, trans, *cwins = NULL; int i, j, k, n, no, num_screens; DNPRINTF(SWM_D_INIT, "grab_windows: begin\n"); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { qtc = xcb_query_tree(conn, screens[i].root); qtr = xcb_query_tree_reply(conn, qtc, NULL); if (qtr == NULL) continue; wins = xcb_query_tree_children(qtr); no = xcb_query_tree_children_length(qtr); /* Try to sort windows according to _NET_CLIENT_LIST. */ pr = xcb_get_property_reply(conn, xcb_get_property(conn, 0, screens[i].root, ewmh[_NET_CLIENT_LIST].atom, XCB_ATOM_WINDOW, 0, UINT32_MAX), NULL); if (pr != NULL) { cwins = xcb_get_property_value(pr); n = xcb_get_property_value_length(pr) / sizeof(xcb_atom_t); for (j = 0; j < n; ++j) { for (k = j; k < no; ++k) { if (wins[k] == cwins[j]) { /* Swap wins j and k. */ wins[k] = wins[j]; wins[j] = cwins[j]; } } } free(pr); } /* Manage top-level windows first, then transients. */ /* TODO: allow transients to be managed before leader. */ DNPRINTF(SWM_D_INIT, "grab_windows: grab top-level windows.\n"); for (j = 0; j < no; j++) { TAILQ_FOREACH(r, &screens[i].rl, entry) { if (r->id == wins[j]) { DNPRINTF(SWM_D_INIT, "grab_windows: " "skip %#x; region input window.\n", wins[j]); break; } else if (r->bar->id == wins[j]) { DNPRINTF(SWM_D_INIT, "grab_windows: " "skip %#x; region bar.\n", wins[j]); break; } } if (r) continue; pc = xcb_icccm_get_wm_transient_for(conn, wins[j]); if (xcb_icccm_get_wm_transient_for_reply(conn, pc, &trans, NULL)) { DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " "is transient for %#x.\n", wins[j], trans); continue; } manage_window(wins[j], SWM_STACK_TOP, false); } DNPRINTF(SWM_D_INIT, "grab_windows: grab transient windows.\n"); for (j = 0; j < no; j++) { pc = xcb_icccm_get_wm_transient_for(conn, wins[j]); if (xcb_icccm_get_wm_transient_for_reply(conn, pc, &trans, NULL)) manage_window(wins[j], SWM_STACK_TOP, false); } free(qtr); } DNPRINTF(SWM_D_INIT, "grab_windows: done.\n"); } void setup_screens(void) { int i, j, k, num_screens; struct workspace *ws; uint32_t gcv[1], wa[1]; const xcb_query_extension_reply_t *qep; xcb_screen_t *screen; xcb_randr_query_version_cookie_t c; xcb_randr_query_version_reply_t *r; num_screens = get_screen_count(); if ((screens = calloc(num_screens, sizeof(struct swm_screen))) == NULL) err(1, "setup_screens: calloc: failed to allocate memory for " "screens"); /* Initial RandR setup. */ randr_support = false; qep = xcb_get_extension_data(conn, &xcb_randr_id); if (qep->present) { c = xcb_randr_query_version(conn, 1, 1); r = xcb_randr_query_version_reply(conn, c, NULL); if (r) { if (r->major_version >= 1) { randr_support = true; randr_eventbase = qep->first_event; } free(r); } } wa[0] = cursors[XC_LEFT_PTR].cid; /* map physical screens */ for (i = 0; i < num_screens; i++) { DNPRINTF(SWM_D_WS, "setup_screens: init screen: %d\n", i); screens[i].idx = i; screens[i].r_focus = NULL; TAILQ_INIT(&screens[i].rl); TAILQ_INIT(&screens[i].orl); if ((screen = get_screen(i)) == NULL) errx(1, "ERROR: can't get screen %d.", i); screens[i].root = screen->root; /* set default colors */ setscreencolor("red", i, SWM_S_COLOR_FOCUS); setscreencolor("red", i, SWM_S_COLOR_FOCUS_MAXIMIZED); setscreencolor("rgb:88/88/88", i, SWM_S_COLOR_UNFOCUS); setscreencolor("rgb:88/88/88", i, SWM_S_COLOR_UNFOCUS_MAXIMIZED); setscreencolor("rgb:00/80/80", i, SWM_S_COLOR_BAR_BORDER); setscreencolor("rgb:00/40/40", i, SWM_S_COLOR_BAR_BORDER_UNFOCUS); setscreencolor("black", i, SWM_S_COLOR_BAR); setscreencolor("rgb:00/80/80", i, SWM_S_COLOR_BAR_SELECTED); setscreencolor("rgb:a0/a0/a0", i, SWM_S_COLOR_BAR_FONT); setscreencolor("black", i, SWM_S_COLOR_BAR_FONT_SELECTED); /* create graphics context on screen */ screens[i].bar_gc = xcb_generate_id(conn); gcv[0] = 0; xcb_create_gc(conn, screens[i].bar_gc, screens[i].root, XCB_GC_GRAPHICS_EXPOSURES, gcv); /* set default cursor */ xcb_change_window_attributes(conn, screens[i].root, XCB_CW_CURSOR, wa); /* init all workspaces */ /* XXX these should be dynamically allocated too */ for (j = 0; j < SWM_WS_MAX; j++) { ws = &screens[i].ws[j]; ws->idx = j; ws->name = NULL; ws->bar_enabled = true; ws->focus = NULL; ws->focus_prev = NULL; ws->focus_pending = NULL; ws->focus_raise = NULL; ws->r = NULL; ws->old_r = NULL; ws->state = SWM_WS_STATE_HIDDEN; TAILQ_INIT(&ws->stack); TAILQ_INIT(&ws->winlist); TAILQ_INIT(&ws->unmanagedlist); for (k = 0; layouts[k].l_stack != NULL; k++) if (layouts[k].l_config != NULL) layouts[k].l_config(ws, SWM_ARG_ID_STACKINIT); ws->cur_layout = &layouts[0]; ws->cur_layout->l_string(ws); } scan_randr(i); if (randr_support) xcb_randr_select_input(conn, screens[i].root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE); } } void setup_globals(void) { if ((bar_fonts = strdup(SWM_BAR_FONTS)) == NULL) err(1, "setup_globals: strdup: failed to allocate memory."); if ((clock_format = strdup("%a %b %d %R %Z %Y")) == NULL) err(1, "setup_globals: strdup: failed to allocate memory."); if ((syms = xcb_key_symbols_alloc(conn)) == NULL) errx(1, "unable to allocate key symbols"); a_state = get_atom_from_string("WM_STATE"); a_prot = get_atom_from_string("WM_PROTOCOLS"); a_delete = get_atom_from_string("WM_DELETE_WINDOW"); a_net_frame_extents = get_atom_from_string("_NET_FRAME_EXTENTS"); a_net_supported = get_atom_from_string("_NET_SUPPORTED"); a_net_wm_check = get_atom_from_string("_NET_SUPPORTING_WM_CHECK"); a_takefocus = get_atom_from_string("WM_TAKE_FOCUS"); a_utf8_string = get_atom_from_string("UTF8_STRING"); a_swm_ws = get_atom_from_string("_SWM_WS"); } void shutdown_cleanup(void) { struct swm_region *r; struct ws_win *w; struct workspace *ws; int i, num_screens; /* disable alarm because the following code may not be interrupted */ alarm(0); if (signal(SIGALRM, SIG_IGN) == SIG_ERR) err(1, "can't disable alarm"); bar_extra_stop(); unmap_all(); cursors_cleanup(); clear_quirks(); clear_spawns(); clear_bindings(); teardown_ewmh(); num_screens = get_screen_count(); for (i = 0; i < num_screens; ++i) { int j; DNPRINTF(SWM_D_FOCUS, "shutdown_cleanup: set_input_focus: " "%#x, revert-to: root, time: 0\n", screens[i].root); xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, screens[i].root, XCB_CURRENT_TIME); if (screens[i].bar_gc != XCB_NONE) xcb_free_gc(conn, screens[i].bar_gc); if (!bar_font_legacy) { XftColorFree(display, DefaultVisual(display, i), DefaultColormap(display, i), &bar_font_color); XftColorFree(display, DefaultVisual(display, i), DefaultColormap(display, i), &search_font_color); } for (j = 0; j < SWM_S_COLOR_MAX; ++j) { free(screens[i].c[j].name); } /* Free window memory. */ for (j = 0; j < SWM_WS_MAX; ++j) { ws = &screens[i].ws[j]; free(ws->name); while ((w = TAILQ_FIRST(&ws->winlist)) != NULL) { TAILQ_REMOVE(&ws->winlist, w, entry); free_window(w); } while ((w = TAILQ_FIRST(&ws->unmanagedlist)) != NULL) { TAILQ_REMOVE(&ws->unmanagedlist, w, entry); free_window(w); } } /* Free region memory. */ while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) { TAILQ_REMOVE(&screens[i].rl, r, entry); free(r->bar); free(r); } while ((r = TAILQ_FIRST(&screens[i].orl)) != NULL) { TAILQ_REMOVE(&screens[i].rl, r, entry); free(r->bar); free(r); } } free(screens); free(bar_format); free(bar_fonts); free(clock_format); free(startup_exception); if (bar_fs) XFreeFontSet(display, bar_fs); if (bar_font) XftFontClose(display, bar_font); xcb_key_symbols_free(syms); xcb_flush(conn); xcb_aux_sync(conn); xcb_disconnect(conn); } void event_error(xcb_generic_error_t *e) { (void)e; DNPRINTF(SWM_D_EVENT, "event_error: %s(%u) from %s(%u), sequence: %u, " "resource_id: %u, minor_code: %u\n", xcb_event_get_error_label(e->error_code), e->error_code, xcb_event_get_request_label(e->major_code), e->major_code, e->sequence, e->resource_id, e->minor_code); } void event_handle(xcb_generic_event_t *evt) { uint8_t type = XCB_EVENT_RESPONSE_TYPE(evt); DNPRINTF(SWM_D_EVENT, "XCB Event: %s(%d), seq %u\n", xcb_event_get_label(XCB_EVENT_RESPONSE_TYPE(evt)), XCB_EVENT_RESPONSE_TYPE(evt), evt->sequence); switch (type) { #define EVENT(type, callback) case type: callback((void *)evt); return EVENT(0, event_error); EVENT(XCB_BUTTON_PRESS, buttonpress); EVENT(XCB_BUTTON_RELEASE, buttonrelease); /*EVENT(XCB_CIRCULATE_NOTIFY, );*/ /*EVENT(XCB_CIRCULATE_REQUEST, );*/ EVENT(XCB_CLIENT_MESSAGE, clientmessage); /*EVENT(XCB_COLORMAP_NOTIFY, );*/ EVENT(XCB_CONFIGURE_NOTIFY, configurenotify); EVENT(XCB_CONFIGURE_REQUEST, configurerequest); /*EVENT(XCB_CREATE_NOTIFY, );*/ EVENT(XCB_DESTROY_NOTIFY, destroynotify); EVENT(XCB_ENTER_NOTIFY, enternotify); EVENT(XCB_EXPOSE, expose); EVENT(XCB_FOCUS_IN, focusin); #ifdef SWM_DEBUG EVENT(XCB_FOCUS_OUT, focusout); #endif /*EVENT(XCB_GRAPHICS_EXPOSURE, );*/ /*EVENT(XCB_GRAVITY_NOTIFY, );*/ EVENT(XCB_KEY_PRESS, keypress); EVENT(XCB_KEY_RELEASE, keyrelease); /*EVENT(XCB_KEYMAP_NOTIFY, );*/ #ifdef SWM_DEBUG EVENT(XCB_LEAVE_NOTIFY, leavenotify); #endif EVENT(XCB_MAP_NOTIFY, mapnotify); EVENT(XCB_MAP_REQUEST, maprequest); EVENT(XCB_MAPPING_NOTIFY, mappingnotify); EVENT(XCB_MOTION_NOTIFY, motionnotify); /*EVENT(XCB_NO_EXPOSURE, );*/ EVENT(XCB_PROPERTY_NOTIFY, propertynotify); EVENT(XCB_REPARENT_NOTIFY, reparentnotify); /*EVENT(XCB_RESIZE_REQUEST, );*/ /*EVENT(XCB_SELECTION_CLEAR, );*/ /*EVENT(XCB_SELECTION_NOTIFY, );*/ /*EVENT(XCB_SELECTION_REQUEST, );*/ EVENT(XCB_UNMAP_NOTIFY, unmapnotify); /*EVENT(XCB_VISIBILITY_NOTIFY, );*/ #undef EVENT } if (type - randr_eventbase == XCB_RANDR_SCREEN_CHANGE_NOTIFY) screenchange((void *)evt); } int main(int argc, char *argv[]) { struct pollfd pfd[2]; struct sigaction sact; struct stat sb; struct passwd *pwd; struct swm_region *r; xcb_generic_event_t *evt; xcb_mapping_notify_event_t *mne; int xfd, i, num_screens, num_readable; char conf[PATH_MAX], *cfile = NULL; bool stdin_ready = false, startup = true; /* suppress unused warning since var is needed */ (void)argc; #ifdef SWM_DEBUG time_started = time(NULL); #endif start_argv = argv; warnx("Welcome to spectrwm V%s Build: %s", SPECTRWM_VERSION, buildstr); if (setlocale(LC_CTYPE, "") == NULL || setlocale(LC_TIME, "") == NULL) warnx("no locale support"); /* handle some signals */ bzero(&sact, sizeof(sact)); sigemptyset(&sact.sa_mask); sact.sa_flags = 0; sact.sa_handler = sighdlr; sigaction(SIGINT, &sact, NULL); sigaction(SIGQUIT, &sact, NULL); sigaction(SIGTERM, &sact, NULL); sigaction(SIGHUP, &sact, NULL); sact.sa_handler = sighdlr; sact.sa_flags = SA_NOCLDSTOP; sigaction(SIGCHLD, &sact, NULL); if ((display = XOpenDisplay(0)) == NULL) errx(1, "can not open display"); conn = XGetXCBConnection(display); if (xcb_connection_has_error(conn)) errx(1, "can not get XCB connection"); XSetEventQueueOwner(display, XCBOwnsEventQueue); xcb_prefetch_extension_data(conn, &xcb_randr_id); xfd = xcb_get_file_descriptor(conn); /* look for local and global conf file */ pwd = getpwuid(getuid()); if (pwd == NULL) errx(1, "invalid user: %d", getuid()); xcb_grab_server(conn); xcb_aux_sync(conn); /* Flush the event queue. */ while ((evt = get_next_event(false))) { switch (XCB_EVENT_RESPONSE_TYPE(evt)) { case XCB_MAPPING_NOTIFY: /* Need to handle mapping changes during startup. */ mne = (xcb_mapping_notify_event_t *)evt; if (mne->request == XCB_MAPPING_KEYBOARD) xcb_refresh_keyboard_mapping(syms, mne); break; #ifdef SWM_DEBUG case 0: /* Display errors. */ event_handle(evt); break; #endif } free(evt); } if (enable_wm()) errx(1, "another window manager is currently running"); /* Load Xcursors and/or cursorfont glyph cursors. */ cursors_load(); xcb_aux_sync(conn); setup_globals(); setup_screens(); setup_ewmh(); setup_keybindings(); setup_btnbindings(); setup_quirks(); setup_spawn(); /* load config */ for (i = 0; ; i++) { conf[0] = '\0'; switch (i) { case 0: /* ~ */ snprintf(conf, sizeof conf, "%s/.%s", pwd->pw_dir, SWM_CONF_FILE); break; case 1: /* global */ snprintf(conf, sizeof conf, "/etc/%s", SWM_CONF_FILE); break; case 2: /* ~ compat */ snprintf(conf, sizeof conf, "%s/.%s", pwd->pw_dir, SWM_CONF_FILE_OLD); break; case 3: /* global compat */ snprintf(conf, sizeof conf, "/etc/%s", SWM_CONF_FILE_OLD); break; default: goto noconfig; } if (strlen(conf) && stat(conf, &sb) != -1) if (S_ISREG(sb.st_mode)) { cfile = conf; break; } } noconfig: /* load conf (if any) */ if (cfile) conf_load(cfile, SWM_CONF_DEFAULT); validate_spawns(); if (getenv("SWM_STARTED") == NULL) setenv("SWM_STARTED", "YES", 1); /* setup all bars */ num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) bar_setup(r); /* Manage existing windows. */ grab_windows(); grabkeys(); grabbuttons(); /* Stack all regions to trigger mapping. */ for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) stack(r); xcb_ungrab_server(conn); xcb_flush(conn); /* Update state and bar of each newly mapped workspace. */ for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) { r->ws->state = SWM_WS_STATE_MAPPED; bar_draw(r->bar); } memset(&pfd, 0, sizeof(pfd)); pfd[0].fd = xfd; pfd[0].events = POLLIN; pfd[1].fd = STDIN_FILENO; pfd[1].events = POLLIN; while (running) { while ((evt = get_next_event(false))) { if (!running) goto done; event_handle(evt); free(evt); } /* If just (re)started, set default focus if needed. */ if (startup) { startup = false; if (focus_mode == SWM_FOCUS_FOLLOW) r = root_to_region(screens[0].root, SWM_CK_POINTER); else r = TAILQ_FIRST(&screens[0].rl); if (r) { focus_region(r); focus_flush(); continue; } } if (search_resp) search_do_resp(); num_readable = poll(pfd, bar_extra ? 2 : 1, 1000); if (num_readable == -1) { DNPRINTF(SWM_D_MISC, "poll failed: %s", strerror(errno)); } else if (num_readable > 0 && bar_extra && pfd[1].revents & POLLIN) { stdin_ready = true; } if (restart_wm) restart(NULL, NULL, NULL); if (!running) goto done; if (stdin_ready) { stdin_ready = false; bar_extra_update(); } /* Need to ensure the bar(s) are always updated. */ for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) bar_draw(r->bar); xcb_flush(conn); } done: shutdown_cleanup(); return (0); } spectrwm-SPECTRWM_3_1_0/spectrwm.conf000066400000000000000000000076341316475426700176110ustar00rootroot00000000000000# PLEASE READ THE MAN PAGE BEFORE EDITING THIS FILE! # https://htmlpreview.github.io/?https://github.com/conformal/spectrwm/blob/master/spectrwm.html # NOTE: all rgb color values in this file are in hex! see XQueryColor for examples # workspace_limit = 22 # focus_mode = default # focus_close = previous # focus_close_wrap = 1 # focus_default = last # spawn_position = next # workspace_clamp = 1 # warp_focus = 1 # warp_pointer = 1 # Window Decoration # border_width = 1 # color_focus = red # color_focus_maximized = yellow # color_unfocus = rgb:88/88/88 # color_unfocus_maximized = rgb:88/88/00 # region_padding = 0 # tile_gap = 0 # Region containment # Distance window must be dragged/resized beyond the region edge before it is # allowed outside the region. # boundary_width = 50 # Remove window border when bar is disabled and there is only one window in workspace # disable_border = 1 # Bar Settings # bar_enabled = 1 # bar_border_width = 1 # bar_border[1] = rgb:00/80/80 # bar_border_unfocus[1] = rgb:00/40/40 # bar_color[1] = black # bar_font_color[1] = rgb:a0/a0/a0 # bar_font = -*-terminus-medium-*-*-*-*-*-*-*-*-*-*-* # bar_action = baraction.sh # bar_justify = left # bar_format = +N:+I +S <+D>+4<%a %b %d %R %Z %Y+8<+A+4<+V # bar_at_bottom = 1 # stack_enabled = 1 # clock_enabled = 1 # clock_format = %a %b %d %R %Z %Y # iconic_enabled = 0 # maximize_hide_bar = 0 # window_class_enabled = 0 # window_instance_enabled = 0 # window_name_enabled = 0 # verbose_layout = 1 # urgent_enabled = 1 # Dialog box size ratio when using TRANSSZ quirk; 0.3 < dialog_ratio <= 1.0 # dialog_ratio = 0.6 # Split a non-RandR dual head setup into one region per monitor # (non-standard driver-based multihead is not seen by spectrwm) # region = screen[1]:1280x1024+0+0 # region = screen[1]:1280x1024+1280+0 # Launch applications in a workspace of choice # autorun = ws[1]:xterm # autorun = ws[2]:xombrero http://www.openbsd.org # Customize workspace layout at start # layout = ws[1]:4:0:0:0:vertical # layout = ws[2]:0:0:0:0:horizontal # layout = ws[3]:0:0:0:0:fullscreen # layout = ws[4]:4:0:0:0:vertical_flip # layout = ws[5]:0:0:0:0:horizontal_flip # Set workspace name at start # name = ws[1]:IRC # name = ws[2]:Email # name = ws[3]:Browse # name = ws[10]:Music # Mod key, (Windows key is Mod4) (Apple key on OSX is Mod2) # modkey = Mod1 # This allows you to include pre-defined key bindings for your keyboard layout. # keyboard_mapping = ~/.spectrwm_us.conf # PROGRAMS # Validated default programs: # program[lock] = xlock # program[term] = xterm # program[menu] = dmenu_run $dmenu_bottom -fn $bar_font -nb $bar_color -nf $bar_font_color -sb $bar_border -sf $bar_color # To disable validation of the above, free the respective binding(s): # bind[] = MOD+Shift+Delete # disable lock # bind[] = MOD+Shift+Return # disable term # bind[] = MOD+p # disable menu # Optional default programs that will only be validated if you override: # program[screenshot_all] = screenshot.sh full # optional # program[screenshot_wind] = screenshot.sh window # optional # program[initscr] = initscreen.sh # optional # EXAMPLE: Define 'firefox' action and bind to key. # program[firefox] = firefox http://spectrwm.org/ # bind[firefox] = MOD+Shift+b # QUIRKS # Default quirks, remove with: quirk[class:name] = NONE # quirk[MPlayer:xv] = FLOAT + FULLSCREEN + FOCUSPREV # quirk[OpenOffice.org 2.4:VCLSalFrame] = FLOAT # quirk[OpenOffice.org 3.0:VCLSalFrame] = FLOAT # quirk[OpenOffice.org 3.1:VCLSalFrame] = FLOAT # quirk[Firefox-bin:firefox-bin] = TRANSSZ # quirk[Firefox:Dialog] = FLOAT # quirk[Gimp:gimp] = FLOAT + ANYWHERE # quirk[XTerm:xterm] = XTERM_FONTADJ # quirk[xine:Xine Window] = FLOAT + ANYWHERE # quirk[Xitk:Xitk Combo] = FLOAT + ANYWHERE # quirk[xine:xine Panel] = FLOAT + ANYWHERE # quirk[Xitk:Xine Window] = FLOAT + ANYWHERE # quirk[xine:xine Video Fullscreen Window] = FULLSCREEN + FLOAT # quirk[pcb:pcb] = FLOAT spectrwm-SPECTRWM_3_1_0/spectrwm.html000066400000000000000000002346211316475426700176260ustar00rootroot00000000000000 SPECTRWM(1)
SPECTRWM(1) General Commands Manual SPECTRWM(1)

NAME

spectrwmwindow manager for X11

SYNOPSIS

spectrwm

DESCRIPTION

spectrwm is a minimalistic window manager that tries to stay out of the way so that valuable screen real estate can be used for much more important stuff. It has sane defaults and does not require one to learn a language to do any configuration. It was written by hackers for hackers and it strives to be small, compact and fast.
When spectrwm starts up, it reads settings from its configuration file, spectrwm.conf. See the CONFIGURATION FILES section below.
The following notation is used throughout this page:
M
Meta
S
Shift
Name
Named key or button
spectrwm is very simple in its use. Most of the actions are initiated via key or pointer bindings. See the BINDINGS section below for defaults and customizations.

CONFIGURATION FILES

spectrwm first tries to open the user specific file, ~/.spectrwm.conf. If that file is unavailable, it then tries to open the global configuration file /etc/spectrwm.conf.
The format of the file is
keyword = setting
For example:
color_focus = red
Enabling or disabling an option is done by using 1 or 0 respectively.
Colors need to be specified per the XQueryColor(3) specification.
Comments begin with a #. When a literal ‘#’ is desired in an option, then it must be escaped with a backslash, i.e. \#
The file supports the following keywords:
autorun
Launch an application in a specified workspace at start-of-day. Defined in the format ws[idx]:application, e.g. ws[2]:xterm launches an xterm(1) in workspace 2.
Note that workspace mapping is handled via libswmhack.so. When autorun spawns windows via a daemon, ensure the daemon is started with the correct LD_PRELOAD in its environment.
For example, starting urxvtd(1) via xinit(1):
LD_PRELOAD=/usr/lib/libswmhack.so.0.0 urxvtd -q -o -f
Spawned programs automatically have LD_PRELOAD set when executed.
bar_action
External script that populates additional information in the status bar, such as battery life.
bar_at_bottom
Place the statusbar at the bottom of each region instead of the top.
bar_border[x]
Border color of the status bar(s) in screen x.
bar_border_unfocus[x]
Border color of the status bar(s) on unfocused region(s) in screen x.
bar_border_width
Set status bar border thickness in pixels. Disable border by setting to 0.
bar_color[x]
Background color of the status bar(s) in screen x.
bar_enabled
Set default bar_toggle state; default is 1.
bar_enabled_ws[x]
Set default bar_toggle_ws state on workspace x; default is 1.
bar_font
Font used in the status bar. Either Xft or X Logical Font Description (XLFD) may be used to specify fonts. Fallback fonts may be specified by separating each font with a comma. If all entries are in XLFD syntax, font set will be used. If at least one entry is Xft, Xft will be used. Note that if Xft is in use, only the first font that successfully loads will be used regardless of missing glyphs. The default is to use font set. Also note that dmenu(1) does not support Xft fonts.
Xft examples:
bar_font = Terminus:style=Regular:pixelsize=14:antialias=true 
 
bar_font = -*-profont-medium-*-*-*-11-*-*-*-*-*-*-*,Terminus:pixelsize=14,-*-clean-medium-*-*-*-12-*-*-*-*-*-*-*
Font set examples:
bar_font = -*-terminus-medium-*-*-*-14-*-*-*-*-*-*-* 
 
bar_font = -*-profont-medium-*-*-*-11-*-*-*-*-*-*-*,-*-terminus-medium-*-*-*-14-*-*-*-*-*-*-*,-*-clean-medium-*-*-*-12-*-*-*-*-*-*-*
To list the available fonts in your system see fc-list(1) or xlsfonts(1) manpages. The xfontsel(1) application can help with the XLFD setting.
bar_font_color[x]
Color of the font in status bar in screen x.
bar_format
Set the bar format string, overriding clock_format and all of the enabled options. The format is passed through strftime(3) before being used. It may contain the following character sequences:
Character sequence Replaced with
+< Pad with a space
+A Output of the external script
+C Window class (from WM_CLASS)
+D Workspace name
+F Floating indicator
+I Workspace index
+M Number of iconic (minimized) windows in workspace
+N Screen number
+P Window class and instance separated by a colon
+S Stacking algorithm
+T Window instance (from WM_CLASS)
+U Urgency hint
+V Program version
+W Window name (from _NET_WM_NAME/WM_NAME)
++ A literal ‘+
All character sequences may limit its output to a specific length, for example +64A. Any characters that don't match the specification are copied as-is.
bar_justify
Justify the status bar text. Possible values are left, center, and right.
Note that if the output is not left justified, it may not be properly aligned in some circumstances, due to the white-spaces in the default static format. See the bar_format option for more details.
bind[x]
Bind key or button combo to action x. See the BINDINGS section below.
border_width
Set window border thickness in pixels. Disable all borders by setting to 0.
boundary_width
Set region containment boundary width in pixels. This is how far a window must be dragged/resized (with the pointer) beyond the region edge before it is allowed outside the region. Disable the window containment effect by setting to 0.
clock_enabled
Enable or disable displaying the clock in the status bar. Disable by setting to 0 so a custom clock could be used in the bar_action script.
iconic_enabled
Display the number of iconic (minimized) windows in the status bar. Enable by setting to 1.
color_focus
Border color of the currently focused window. Default is red.
color_focus_maximized
Border color of the currently focused, maximized window. Defaults to the value of color_focus.
color_unfocus
Border color of unfocused windows, default is rgb:88/88/88.
color_unfocus_maximized
Border color of unfocused, maximized windows. Defaults to the value of color_unfocus.
dialog_ratio
Some applications have dialogue windows that are too small to be useful. This ratio is the screen size to what they will be resized. For example, 0.6 is 60% of the physical screen size.
disable_border
Remove border when bar is disabled and there is only one window on the region.
focus_close
Window to put focus when the focused window is closed. Possible values are first, next, previous (default) and last. next and previous are relative to the window that is closed.
focus_close_wrap
Whether to allow the focus to jump to the last window when the first window is closed or vice versa. Disable by setting to 0.
focus_default
Window to put focus when no window has been focused. Possible values are first and last (default).
focus_mode
Window focus behavior with respect to the pointer. Possible values:
default
Set window focus on border crossings caused by cursor motion and window interaction.
follow
Set window focus on all cursor border crossings, including workspace switches and changes to layout.
manual
Set window focus on window interaction only.
maximize_hide_bar
When set to 1, maximize_toggle will also hide/restore the bar visibility of the affected workspace. Defaults to 0.
java_workaround
Workaround a Java GUI rendering issue on non-reparenting window managers by impersonating the LG3D window manager, written by Sun. Default is 1.
keyboard_mapping
Clear all key bindings (not button bindings) and load new bindings from the specified file. This allows you to load pre-defined key bindings for your keyboard layout. See the KEYBOARD MAPPING FILES section below for a list of keyboard mapping files that have been provided for several keyboard layouts.
layout
Select layout to use at start-of-day. Defined in the format ws[idx]:master_grow:master_add:stack_inc:always_raise:stack_mode, e.g. ws[2]:-4:0:1:0:horizontal sets worskspace 2 to the horizontal stack mode, shrinks the master area by 4 ticks and adds one window to the stack, while maintaining default floating window behavior. Possible stack_mode values are vertical, vertical_flip, horizontal, horizontal_flip and fullscreen.
See master_grow, master_shrink, master_add, master_del, stack_inc, stack_dec, stack_balance, and always_raise for more information. Note that the stacking options are complicated and have side-effects. One should familiarize oneself with these commands before experimenting with the layout option.
This setting is not retained at restart.
modkey
Change mod key. Mod1 is generally the ALT key and Mod4 is the windows key on a PC.
name
Set the name of a workspace at start-of-day. Defined in the format ws[idx]:name, e.g. ws[1]:Console sets the name of workspace 1 to “Console”.
program[p]
Define new action to spawn a program p. See the PROGRAMS section below.
quirk[c:i:n]
Add "quirk" for windows with class c, instance i and name n. See the QUIRKS section below.
region
Allocates a custom region, removing any autodetected regions which occupy the same space on the screen. Defined in the format screen[idx]:widthxheight+x+y, e.g. screen[1]:800x1200+0+0.
To make a region span multiple monitors, create a region big enough to cover them all, e.g. screen[1]:2048x768+0+0 makes the region span two monitors with 1024x768 resolution sitting one next to the other.
region_padding
Pixel width of empty space within region borders. Disable by setting to 0.
spawn_position
Position in stack to place newly spawned windows. Possible values are first, next, previous and last (default). next and previous are relative to the focused window.
stack_enabled
Enable or disable displaying the current stacking algorithm in the status bar.
term_width
Set a preferred minimum width for the terminal. If this value is greater than 0, spectrwm will attempt to adjust the font sizes in the terminal to keep the terminal width above this number as the window is resized. Only xterm(1) is currently supported. The xterm(1) binary must not be setuid or setgid, which it is by default on most systems. Users may need to set program[term] (see the PROGRAMS section) to use an alternate copy of the xterm(1) binary without the setgid bit set.
tile_gap
Pixel width of empty space between tiled windows. Negative values cause overlap. Set this to the opposite of border_width to collapse the border between tiles. Disable by setting to 0.
urgent_collapse
Minimizes the space consumed by the urgency hint indicator by removing the placeholders for non-urgent workspaces, the trailing space when there are urgent windows and the default leading space. Enable by setting to 1.
urgent_enabled
Enable or disable the urgency hint indicator in the status bar. Note that many terminal emulators require an explicit setting for the bell character to trigger urgency on the window. In xterm(1), for example, one needs to add the following line to .Xdefaults:
xterm.bellIsUrgent: true
verbose_layout
Enable or disable displaying the current master window count and stack column/row count in the status bar. Enable by setting to 1. See master_add, master_del, stack_inc and stack_dec for more information.
workspace_clamp
Prevents workspaces from being swapped when attempting to switch to a workspace that is mapped to another region. Use warp_focus if you want to focus on the region containing the workspace and warp_pointer if you want to also send the pointer. Enable by setting to 1.
window_class_enabled
Enable or disable displaying the window class name (from WM_CLASS) in the status bar. Enable by setting to 1.
window_instance_enabled
Enable or disable displaying the window instance name (from WM_CLASS) in the status bar. Enable by setting to 1.
window_name_enabled
Enable or disable displaying the window display name (from _NET_WM_NAME/WM_NAME) in the status bar. Enable by setting to 1.
To prevent excessively large window names from pushing the remaining text off the bar, it's limited to 64 characters, by default. See the bar_format option for more details.
warp_focus
Focus on the target window/workspace/region when clamped. For example, when attempting to switch to a workspace that is mapped on another region and workspace_clamp is enabled, focus on the region with the target workspace. Enable by setting to 1.
warp_pointer
Centers the pointer on the focused window when using bindings to change focus, switch workspaces, change regions, etc. Enable by setting to 1.
workspace_limit
Set the total number of workspaces available. Minimum is 1, maximum is 22, default is 10.

PROGRAMS

spectrwm allows you to define custom actions to launch programs of your choice and then bind them the same as with built-in actions. See the BINDINGS section below.
Custom programs in the configuration file are specified as follows:
program[action] = progpath [arg [arg ...]]
action is any identifier that does not conflict with a built-in action or keyword, progpath is the desired program, and arg is zero or more arguments to the program.
Remember that when using ‘#’ in your program call, it must be escaped with a backslash, i.e. \#
The following argument variables will be substituted for values at the time the program is spawned:
$bar_border
$bar_color
$bar_font
$bar_font_color
$color_focus
$color_unfocus
$dmenu_bottom
-b if bar_at_bottom is enabled.
$region_index
$workspace_index
Example:
program[ff] = /usr/local/bin/firefox http://spectrwm.org/ 
bind[ff] = MOD+Shift+b # Now M-S-b launches firefox
To cancel the previous, unbind it:
bind[] = MOD+Shift+b
Default programs:
lock
xlock
menu
dmenu_run $dmenu_bottom -fn $bar_font -nb $bar_color -nf $bar_font_color -sb $bar_border -sf $bar_color
term
xterm
initscr
initscreen.sh # optional
screenshot_all
screenshot.sh full # optional
screenshot_wind
screenshot.sh window # optional
Note that optional default programs will not be validated unless overridden. If a default program fails validation, you can resolve the exception by installing the program, modifying the program call or disabling the program by freeing the respective binding.
For example, to override lock:
program[lock] = xscreensaver-command -lock
To unbind lock and prevent it from being validated:
bind[] = MOD+Shift+Delete

BINDINGS

spectrwm provides many functions (or actions) accessed via key or pointer bindings.
The default bindings are listed below:
Button1
focus
M-Button1
move
M-Button3
resize
M-S-Button3
resize_centered
M-S-Return
term
M-p
menu
M-S-q
quit
M-q
restart
M-Space
cycle_layout
M-S-\
flip_layout
M-S-Space
stack_reset
unbound
stack_balance
M-h
master_shrink
M-l
master_grow
M-,
master_add
M-.
master_del
M-S-,
stack_inc
M-S-.
stack_dec
M-Return
swap_main
M-j, M-TAB
focus_next
M-k, M-S-TAB
focus_prev
M-m
focus_main
M-u
focus_urgent
M-S-j
swap_next
M-S-k
swap_prev
M-b
bar_toggle
M-S-b
bar_toggle_ws
M-x
wind_del
M-S-x
wind_kill
M-1-9,0,F1-F12
ws_⟨1-22
M-S-1-9,0,F1-F12
mvws_⟨1-22
M-Keypad 1-9
rg_⟨1-9
M-S-Keypad 1-9
mvrg_⟨1-9
unbound
mvrg_next
unbound
mvrg_prev
M-Right
ws_next
M-Left
ws_prev
M-Up
ws_next_all
M-Down
ws_prev_all
M-a
ws_prior
M-S-Down
ws_prev_move
M-S-Up
ws_next_move
M-S-Right
rg_next
M-S-Left
rg_prev
unbound
rg_move_next
unbound
rg_move_prev
M-s
screenshot_all
M-S-s
screenshot_wind
M-S-v
version
M-t
float_toggle
M-S-Delete
lock
M-S-i
initscr
M-w
iconify
M-S-w
uniconify
M-e
maximize_toggle
M-S-e
fullscreen_toggle
M-r
raise
M-S-r
always_raise
M-v
button2
M--
width_shrink
M-=
width_grow
M-S--
height_shrink
M-S-=
height_grow
M-[
move_left
M-]
move_right
M-S-[
move_up
M-S-]
move_down
M-S-/
name_workspace
M-/
search_workspace
M-f
search_win
The action names and descriptions are listed below:
focus
Focus window/region under pointer.
move
Move window with pointer while binding is pressed.
resize
Resize window with pointer while binding is pressed.
resize_centered
Same as resize but keep window centered.
term
Spawn a new terminal (see PROGRAMS above).
menu
Menu (see PROGRAMS above).
quit
Quit spectrwm.
restart
Restart spectrwm.
cycle_layout
Cycle layout.
flip_layout
Swap the master and stacking areas.
stack_reset
Reset layout.
stack_balance
Balance master/stacking area.
master_shrink
Shrink master area.
master_grow
Grow master area.
master_add
Add windows to master area.
master_del
Remove windows from master area.
stack_inc
Add columns/rows to stacking area.
stack_dec
Remove columns/rows from stacking area.
swap_main
Move current window to master area.
focus_next
Focus next window in workspace.
focus_prev
Focus previous window in workspace.
focus_main
Focus on main window in workspace.
focus_urgent
Focus on next window with the urgency hint flag set. The workspace is switched if needed.
swap_next
Swap with next window in workspace.
swap_prev
Swap with previous window in workspace.
bar_toggle
Toggle overall visibility of status bars.
bar_toggle_ws
Toggle status bar on current workspace.
wind_del
Delete current window in workspace.
wind_kill
Destroy current window in workspace.
ws_n
Switch to workspace n, where n is 1 through workspace_limit.
mvws_n
Move current window to workspace n, where n is 1 through workspace_limit.
rg_n
Focus on region n, where n is 1 through 9.
mvrg_n
Move current window to region n, where n is 1 through 9.
mvrg_next
Move current window to workspace in next region.
mvrg_prev
Move current window to workspace in previous region.
ws_next
Switch to next workspace with a window in it.
ws_prev
Switch to previous workspace with a window in it.
ws_next_all
Switch to next workspace.
ws_prev_all
Switch to previous workspace.
ws_next_move
Switch to next workspace with the current window.
ws_prev_move
Switch to previous workspace with the current window.
ws_prior
Switch to last visited workspace.
rg_next
Switch to next region.
rg_prev
Switch to previous region.
rg_move_next
Switch region to next screen.
rg_move_prev
Switch region to previous screen.
screenshot_all
Take screenshot of entire screen (if enabled) (see PROGRAMS above).
screenshot_wind
Take screenshot of selected window (if enabled) (see PROGRAMS above).
version
Toggle version in status bar.
float_toggle
Toggle focused window between tiled and floating.
lock
Lock screen (see PROGRAMS above).
initscr
Reinitialize physical screens (see PROGRAMS above).
iconify
Minimize (unmap) currently focused window.
uniconify
Restore (map) window returned by dmenu(1) selection.
maximize_toggle
Toggle maximization of focused window.
fullscreen_toggle
Toggle fullscreen state of focused window.
raise
Raise the current window.
always_raise
When set tiled windows are allowed to obscure floating windows.
button2
Fake a middle mouse button click (Button2).
width_shrink
Shrink the width of a floating window.
width_grow
Grow the width of a floating window.
height_shrink
Shrink the height of a floating window.
height_grow
Grow the height of a floating window.
move_left
Move a floating window a step to the left.
move_right
Move a floating window a step to the right.
move_up
Move a floating window a step upwards.
move_down
Move a floating window a step downwards.
name_workspace
Name the current workspace.
search_workspace
Search for a workspace.
search_win
Search the windows in the current workspace.
Custom bindings in the configuration file are specified as follows:
bind[action] = combo
action is one of the actions listed above (or empty to unbind) and combo is in the form of zero or more modifier keys and/or special arguments (Mod1, Shift, MOD, etc.) and a normal key (b, Space, etc.) or a button (Button1 .. Button255), separated by ‘+’. Multiple key/button combinations may be bound to the same action.
Special arguments:
MOD
Substituted for the currently defined modkey.
ANYMOD
Select all modifier combinations not handled by another binding.
REPLAY
Reprocess binding press/release events for other programs to handle. Unavailable for move, resize and resize_centered.
MOD example:
bind[reset] = Mod4+q # bind Windows-key + q to reset 
bind[] = Mod1+q # unbind Alt + q 
bind[move] = MOD+Button3 # Bind move to M-Button3 
bind[] = MOD+Button1 # Unbind default move binding.
ANYMOD example:
bind[focus] = ANYMOD+Button3 
bind[move] = MOD+Button3
In the above example, M-Button3⟩ initiates move and ⟨Button3⟩ pressed with any other combination of modifiers sets focus to the window/region under the pointer.
REPLAY example:
bind[focus] = REPLAY+Button3
In the above example, when ⟨Button3⟩ is pressed without any modifier(s), focus is set to the window under the pointer and the button press is passed to the window.
To bind non-latin characters such as å or π you must enter the xkb character name instead of the character itself. Run xev(1), focus the window and press the specific key and in the terminal output read the symbol name. In the following example for å:
KeyPress event, serial 41, synthetic NO, window 0x2600001, 
    root 0x15a, subw 0x0, time 106213808, (11,5), root:(359,823), 
    state 0x0, keycode 24 (keysym 0xe5, aring), same_screen YES, 
    XLookupString gives 2 bytes: (c3 a5) "å" 
    XmbLookupString gives 2 bytes: (c3 a5) "å" 
    XFilterEvent returns: False
The xkb name is aring. In other words, in spectrwm.conf add:
bind[program] = MOD+aring

KEYBOARD MAPPING FILES

Keyboard mapping files for several keyboard layouts are listed below. These files can be used with the keyboard_mapping setting to load pre-defined key bindings for the specified keyboard layout.
spectrwm_cz.conf
Czech Republic keyboard layout
spectrwm_es.conf
Spanish keyboard layout
spectrwm_fr.conf
French keyboard layout
spectrwm_fr_ch.conf
Swiss French keyboard layout
spectrwm_se.conf
Swedish keyboard layout
spectrwm_us.conf
United States keyboard layout

QUIRKS

spectrwm provides "quirks" which handle windows that must be treated specially in a tiling window manager, such as some dialogs and fullscreen apps.
The default quirks are described below:
Firefox-bin:firefox-bin
TRANSSZ
Firefox:Dialog
FLOAT
Gimp:gimp
FLOAT + ANYWHERE
MPlayer:xv
FLOAT + FULLSCREEN + FOCUSPREV
OpenOffice.org 2.4:VCLSalFrame
FLOAT
OpenOffice.org 3.1:VCLSalFrame
FLOAT
pcb:pcb
FLOAT
xine:Xine Window
FLOAT + ANYWHERE
xine:xine Panel
FLOAT + ANYWHERE
xine:xine Video Fullscreen Window
FULLSCREEN + FLOAT
Xitk:Xitk Combo
FLOAT + ANYWHERE
Xitk:Xine Window
FLOAT + ANYWHERE
XTerm:xterm
XTERM_FONTADJ
The quirks themselves are described below:
ANYWHERE
Allow window to position itself, uncentered.
FLOAT
This window should not be tiled, but allowed to float freely.
FOCUSONMAP_SINGLE
When the window first appears on the screen, change focus to the window if there are no other windows on the workspace with the same WM_CLASS class/instance value. Has no effect when focus_mode is set to follow.
FOCUSPREV
On exit force focus on previously focused application not previous application in the stack.
FULLSCREEN
Remove border to allow window to use full region size.
IGNOREPID
Ignore the PID when determining the initial workspace for a new window. Especially useful for terminal windows that share a process.
IGNORESPAWNWS
Ignore the spawn workspace when determining the initial workspace for a new window.
MINIMALBORDER
Remove border when window is unfocused and floating.
NOFOCUSCYCLE
Remove from normal focus cycle (focus_prev or focus_next). The window can still be focused using search_win.
NOFOCUSONMAP
Don't change focus to the window when it first appears on the screen. Has no effect when focus_mode is set to follow.
OBEYAPPFOCUSREQ
When an application requests focus on the window via a _NET_ACTIVE_WINDOW client message (source indication of 1), comply with the request. Note that a source indication of 0 (unspecified) or 2 (pager) are always obeyed.
TRANSSZ
Adjusts size on transient windows that are too small using dialog_ratio (see CONFIGURATION FILES).
WS[n]
Force a new window to appear on workspace n.
XTERM_FONTADJ
Adjust xterm(1) fonts when resizing.
Custom quirks in the configuration file are specified as follows:
quirk[class[:instance[:name]]] = quirk [+ quirk ...]
class, instance (optional) and name (optional) are patterns used to determine which window(s) the quirk(s) apply to and quirk is one of the quirks from the list above.
Note that patterns are interpreted as POSIX Extended Regular Expressions. Any ':', '[' or ']' must be escaped with '\'. See regex(7) for more information on POSIX Extended Regular Expressions.
For example:
quirk[MPlayer] = FLOAT + FULLSCREEN + FOCUSPREV # Float all windows having a class of 'MPlayer' 
quirk[.*] = FLOAT # Float all windows by default. 
quirk[.*:.*:.*] = FLOAT # Same as above. 
quirk[Firefox:Navigator] = FLOAT # Float all Firefox browser windows. 
quirk[::Console] = FLOAT # Float windows with WM_CLASS not set and a window name of 'Console'. 
quirk[\[0-9\].*:.*:\[\[\:alnum\:\]\]*] = FLOAT # Float windows with WM_CLASS class beginning with a number, any WM_CLASS instance and a _NET_WM_NAME/WM_NAME either blank or containing alphanumeric characters without spaces. 
quirk[pcb:pcb] = NONE # remove existing quirk
You can obtain class, instance and name by running xprop(1) and then clicking on the desired window. In the following example the main window of Firefox was clicked:
$ xprop | grep -E "^(WM_CLASS|_NET_WM_NAME|WM_NAME)" 
WM_CLASS(STRING) = "Navigator", "Firefox" 
WM_NAME(STRING) = "spectrwm - ConformalOpenSource" 
_NET_WM_NAME(UTF8_STRING) = "spectrwm - ConformalOpenSource"
Note that xprop(1) displays WM_CLASS as:
WM_CLASS(STRING) = "<instance>", "<class>"
In the example above the quirk entry would be:
quirk[Firefox:Navigator] = FLOAT
spectrwm also automatically assigns quirks to windows based on the value of the window's _NET_WM_WINDOW_TYPE property as follows:
_NET_WM_WINDOW_TYPE_DOCK
FLOAT + ANYWHERE
_NET_WM_WINDOW_TYPE_TOOLBAR
FLOAT + ANYWHERE
_NET_WM_WINDOW_TYPE_UTILITY
FLOAT + ANYWHERE
_NET_WM_WINDOW_TYPE_SPLASH
FLOAT
_NET_WM_WINDOW_TYPE_DIALOG
FLOAT
In all other cases, no automatic quirks are assigned to the window. Quirks specified in the configuration file override the automatic quirks.

EWMH

spectrwm partially implements the Extended Window Manager Hints (EWMH) specification. This enables controlling windows as well as spectrwm itself from external scripts and programs. This is achieved by spectrwm responding to certain ClientMessage events. From the terminal these events can be conveniently sent using tools such as wmctrl(1) and xdotool(1). For the actual format of these ClientMessage events, see the EWMH specification.
The id of the currently focused window is stored in the _NET_ACTIVE_WINDOW property of the root window. This can be used for example to retrieve the title of the currently active window with xprop(1) and grep(1):
$ WINDOWID=`xprop -root _NET_ACTIVE_WINDOW | grep -o "0x.*"` 
$ xprop -id $WINDOWID _NET_WM_NAME | grep -o "\".*\""
A window can be focused by sending a _NET_ACTIVE_WINDOW client message to the root window. For example, using wmctrl(1) to send the message (assuming 0x4a0000b is the id of the window to be focused):
$ wmctrl -i -a 0x4a0000b
Windows can be closed by sending a _NET_CLOSE_WINDOW client message to the root window. For example, using wmctrl(1) to send the message (assuming 0x4a0000b is the id of the window to be closed):
$ wmctrl -i -c 0x4a0000b
Windows can be floated and un-floated by adding or removing the _NET_WM_STATE_ABOVE atom from the _NET_WM_STATE property of the window. This can be achieved by sending a _NET_WM_STATE client message to the root window. For example, the following toggles the floating state of a window using wmctrl(1) to send the message (assuming 0x4a0000b is the id of the window to be floated or un-floated):
$ wmctrl -i -r 0x4a0000b -b toggle,_NET_WM_STATE_ABOVE
Windows can also be iconified and un-iconified by substituting _NET_WM_STATE_HIDDEN for _NET_WM_STATE_ABOVE in the previous example:
$ wmctrl -i -r 0x4a0000b -b toggle,_NET_WM_STATE_HIDDEN
Floating windows can also be resized and moved by sending a _NET_MOVERESIZE_WINDOW client message to the root window. For example, using wmctrl(1) to send the message (assuming 0x4a0000b is the id of the window to be resize/moved):
$ wmctrl -i -r 0x4a0000b -e 0,100,50,640,480
This moves the window to (100,50) and resizes it to 640x480.
Any _NET_MOVERESIZE_WINDOW events received for stacked windows are ignored.

SIGNALS

Sending spectrwm a HUP signal will restart it.

FILES

~/.spectrwm.conf
spectrwm user specific settings.
/etc/spectrwm.conf
spectrwm global settings.

HISTORY

spectrwm was inspired by xmonad & dwm.

AUTHORS

spectrwm was written by:
Marco Peereboom ⟨marco@peereboom.us⟩
Ryan Thomas McBride ⟨mcbride@countersiege.com⟩
Darrin Chandler ⟨dwchandler@stilyagin.com⟩
Pierre-Yves Ritschard ⟨pyr@spootnik.org⟩
Tuukka Kataja ⟨stuge@xor.fi⟩
Jason L. Wright ⟨jason@thought.net⟩
Reginald Kennedy ⟨rk@rejii.com⟩
Lawrence Teo ⟨lteo@lteo.net⟩
Tiago Cunha ⟨tcunha@gmx.com⟩
David Hill ⟨dhill@mindcry.org⟩
February 15, 2012 Bitrig 1.0
spectrwm-SPECTRWM_3_1_0/spectrwm_cz.conf000066400000000000000000000067451316475426700203070ustar00rootroot00000000000000# Key bindings for Czech Republic (cz) keyboards # unbind with: bind[] = bind[bar_toggle] = MOD+b bind[bar_toggle_ws] = MOD+Shift+b bind[button2] = MOD+v bind[cycle_layout] = MOD+space bind[flip_layout] = MOD+Shift+backslash bind[float_toggle] = MOD+t bind[focus_main] = MOD+m bind[focus_next] = MOD+j bind[focus_next] = MOD+Tab bind[focus_prev] = MOD+k bind[focus_prev] = MOD+Shift+Tab bind[focus_urgent] = MOD+u bind[height_grow] = MOD+Shift+dead_acute bind[height_shrink] = MOD+Shift+equal bind[iconify] = MOD+w bind[initscr] = MOD+Shift+i bind[lock] = MOD+Shift+Delete bind[master_add] = MOD+comma bind[master_del] = MOD+period bind[master_grow] = MOD+l bind[master_shrink] = MOD+h bind[maximize_toggle] = MOD+e bind[menu] = MOD+p bind[move_down] = MOD+Shift+parenright bind[move_left] = MOD+uacute bind[move_right] = MOD+parenright bind[move_up] = MOD+Shift+uacute bind[mvrg_1] = MOD+Shift+KP_End bind[mvrg_2] = MOD+Shift+KP_Down bind[mvrg_3] = MOD+Shift+KP_Next bind[mvrg_4] = MOD+Shift+KP_Left bind[mvrg_5] = MOD+Shift+KP_Begin bind[mvrg_6] = MOD+Shift+KP_Right bind[mvrg_7] = MOD+Shift+KP_Home bind[mvrg_8] = MOD+Shift+KP_Up bind[mvrg_9] = MOD+Shift+KP_Prior bind[mvws_1] = MOD+Shift+plus bind[mvws_2] = MOD+Shift+ecaron bind[mvws_3] = MOD+Shift+scaron bind[mvws_4] = MOD+Shift+ccaron bind[mvws_5] = MOD+Shift+rcaron bind[mvws_6] = MOD+Shift+zcaron bind[mvws_7] = MOD+Shift+yacute bind[mvws_8] = MOD+Shift+aacute bind[mvws_9] = MOD+Shift+iacute bind[mvws_10] = MOD+Shift+eacute bind[mvws_11] = MOD+Shift+F1 bind[mvws_12] = MOD+Shift+F2 bind[mvws_13] = MOD+Shift+F3 bind[mvws_14] = MOD+Shift+F4 bind[mvws_15] = MOD+Shift+F5 bind[mvws_16] = MOD+Shift+F6 bind[mvws_17] = MOD+Shift+F7 bind[mvws_18] = MOD+Shift+F8 bind[mvws_19] = MOD+Shift+F9 bind[mvws_20] = MOD+Shift+F10 bind[mvws_21] = MOD+Shift+F11 bind[mvws_22] = MOD+Shift+F12 bind[name_workspace] = MOD+Shift+slash bind[quit] = MOD+Shift+q bind[raise_toggle] = MOD+Shift+r bind[restart] = MOD+q bind[rg_1] = MOD+KP_End bind[rg_2] = MOD+KP_Down bind[rg_3] = MOD+KP_Next bind[rg_4] = MOD+KP_Left bind[rg_5] = MOD+KP_Begin bind[rg_6] = MOD+KP_Right bind[rg_7] = MOD+KP_Home bind[rg_8] = MOD+KP_Up bind[rg_9] = MOD+KP_Prior bind[rg_next] = MOD+Shift+Right bind[rg_prev] = MOD+Shift+Left bind[screenshot_all] = MOD+s bind[screenshot_wind] = MOD+Shift+s bind[search_win] = MOD+f bind[search_workspace] = MOD+slash bind[stack_dec] = MOD+Shift+period bind[stack_inc] = MOD+Shift+comma bind[stack_reset] = MOD+Shift+space bind[swap_main] = MOD+Return bind[swap_next] = MOD+Shift+j bind[swap_prev] = MOD+Shift+k bind[term] = MOD+Shift+Return bind[uniconify] = MOD+Shift+w bind[version] = MOD+Shift+v bind[width_grow] = MOD+dead_acute bind[width_shrink] = MOD+equal bind[wind_del] = MOD+x bind[wind_kill] = MOD+Shift+x bind[ws_1] = MOD+plus bind[ws_2] = MOD+ecaron bind[ws_3] = MOD+scaron bind[ws_4] = MOD+ccaron bind[ws_5] = MOD+rcaron bind[ws_6] = MOD+zcaron bind[ws_7] = MOD+yacute bind[ws_8] = MOD+aacute bind[ws_9] = MOD+iacute bind[ws_10] = MOD+eacute bind[ws_11] = MOD+F1 bind[ws_12] = MOD+F2 bind[ws_13] = MOD+F3 bind[ws_14] = MOD+F4 bind[ws_15] = MOD+F5 bind[ws_16] = MOD+F6 bind[ws_17] = MOD+F7 bind[ws_18] = MOD+F8 bind[ws_19] = MOD+F9 bind[ws_20] = MOD+F10 bind[ws_21] = MOD+F11 bind[ws_22] = MOD+F12 bind[ws_next] = MOD+Right bind[ws_next_all] = MOD+Up bind[ws_next_move] = MOD+Shift+Up bind[ws_prev] = MOD+Left bind[ws_prev_all] = MOD+Down bind[ws_prev_move] = MOD+Shift+Down bind[ws_prior] = MOD+a spectrwm-SPECTRWM_3_1_0/spectrwm_es.conf000066400000000000000000000066041316475426700202740ustar00rootroot00000000000000# Key bindings for Spanish (es) keyboards # unbind with: bind[] = bind[bar_toggle] = MOD+b bind[bar_toggle_ws] = MOD+Shift+b bind[button2] = MOD+v bind[cycle_layout] = MOD+space bind[flip_layout] = MOD+Shift+backslash bind[float_toggle] = MOD+t bind[focus_main] = MOD+m bind[focus_next] = MOD+j bind[focus_next] = MOD+Tab bind[focus_prev] = MOD+k bind[focus_prev] = MOD+Shift+Tab bind[focus_urgent] = MOD+u bind[height_grow] = MOD+Shift+exclamdown bind[height_shrink] = MOD+Shift+apostrophe bind[iconify] = MOD+w bind[initscr] = MOD+Shift+i bind[lock] = MOD+Shift+Delete bind[master_add] = MOD+comma bind[master_del] = MOD+period bind[master_grow] = MOD+l bind[master_shrink] = MOD+h bind[maximize_toggle] = MOD+e bind[menu] = MOD+p bind[move_down] = MOD+Shift+plus bind[move_left] = MOD+dead_grave bind[move_right] = MOD+plus bind[move_up] = MOD+Shift+dead_grave bind[mvrg_1] = MOD+Shift+KP_End bind[mvrg_2] = MOD+Shift+KP_Down bind[mvrg_3] = MOD+Shift+KP_Next bind[mvrg_4] = MOD+Shift+KP_Left bind[mvrg_5] = MOD+Shift+KP_Begin bind[mvrg_6] = MOD+Shift+KP_Right bind[mvrg_7] = MOD+Shift+KP_Home bind[mvrg_8] = MOD+Shift+KP_Up bind[mvrg_9] = MOD+Shift+KP_Prior bind[mvws_1] = MOD+Shift+1 bind[mvws_2] = MOD+Shift+2 bind[mvws_3] = MOD+Shift+3 bind[mvws_4] = MOD+Shift+4 bind[mvws_5] = MOD+Shift+5 bind[mvws_6] = MOD+Shift+6 bind[mvws_7] = MOD+Shift+7 bind[mvws_8] = MOD+Shift+8 bind[mvws_9] = MOD+Shift+9 bind[mvws_10] = MOD+Shift+0 bind[mvws_11] = MOD+Shift+F1 bind[mvws_12] = MOD+Shift+F2 bind[mvws_13] = MOD+Shift+F3 bind[mvws_14] = MOD+Shift+F4 bind[mvws_15] = MOD+Shift+F5 bind[mvws_16] = MOD+Shift+F6 bind[mvws_17] = MOD+Shift+F7 bind[mvws_18] = MOD+Shift+F8 bind[mvws_19] = MOD+Shift+F9 bind[mvws_20] = MOD+Shift+F10 bind[mvws_21] = MOD+Shift+F11 bind[mvws_22] = MOD+Shift+F12 bind[name_workspace] = MOD+Shift+slash bind[quit] = MOD+Shift+q bind[raise_toggle] = MOD+Shift+r bind[restart] = MOD+q bind[rg_1] = MOD+KP_End bind[rg_2] = MOD+KP_Down bind[rg_3] = MOD+KP_Next bind[rg_4] = MOD+KP_Left bind[rg_5] = MOD+KP_Begin bind[rg_6] = MOD+KP_Right bind[rg_7] = MOD+KP_Home bind[rg_8] = MOD+KP_Up bind[rg_9] = MOD+KP_Prior bind[rg_next] = MOD+Shift+Right bind[rg_prev] = MOD+Shift+Left bind[screenshot_all] = MOD+s bind[screenshot_wind] = MOD+Shift+s bind[search_win] = MOD+f bind[search_workspace] = MOD+slash bind[stack_dec] = MOD+Shift+period bind[stack_inc] = MOD+Shift+comma bind[stack_reset] = MOD+Shift+space bind[swap_main] = MOD+Return bind[swap_next] = MOD+Shift+j bind[swap_prev] = MOD+Shift+k bind[term] = MOD+Shift+Return bind[uniconify] = MOD+Shift+w bind[version] = MOD+Shift+v bind[width_grow] = MOD+exclamdown bind[width_shrink] = MOD+apostrophe bind[wind_del] = MOD+x bind[wind_kill] = MOD+Shift+x bind[ws_1] = MOD+1 bind[ws_2] = MOD+2 bind[ws_3] = MOD+3 bind[ws_4] = MOD+4 bind[ws_5] = MOD+5 bind[ws_6] = MOD+6 bind[ws_7] = MOD+7 bind[ws_8] = MOD+8 bind[ws_9] = MOD+9 bind[ws_10] = MOD+0 bind[ws_11] = MOD+F1 bind[ws_12] = MOD+F2 bind[ws_13] = MOD+F3 bind[ws_14] = MOD+F4 bind[ws_15] = MOD+F5 bind[ws_16] = MOD+F6 bind[ws_17] = MOD+F7 bind[ws_18] = MOD+F8 bind[ws_19] = MOD+F9 bind[ws_20] = MOD+F10 bind[ws_21] = MOD+F11 bind[ws_22] = MOD+F12 bind[ws_next] = MOD+Right bind[ws_next_all] = MOD+Up bind[ws_next_move] = MOD+Shift+Up bind[ws_prev] = MOD+Left bind[ws_prev_all] = MOD+Down bind[ws_prev_move] = MOD+Shift+Down bind[ws_prior] = MOD+a spectrwm-SPECTRWM_3_1_0/spectrwm_fr.conf000066400000000000000000000067751316475426700203050ustar00rootroot00000000000000# Key bindings for French (fr) keyboards # unbind with: bind[] = bind[bar_toggle] = MOD+b bind[bar_toggle_ws] = MOD+Shift+b bind[button2] = MOD+v bind[cycle_layout] = MOD+space bind[flip_layout] = MOD+Shift+backslash bind[float_toggle] = MOD+t bind[focus_main] = MOD+m bind[focus_next] = MOD+j bind[focus_next] = MOD+Tab bind[focus_prev] = MOD+k bind[focus_prev] = MOD+Shift+Tab bind[focus_urgent] = MOD+u bind[height_grow] = MOD+Shift+equal bind[height_shrink] = MOD+Shift+minus bind[iconify] = MOD+w bind[initscr] = MOD+Shift+i bind[lock] = MOD+Shift+Delete bind[master_add] = MOD+comma bind[master_del] = MOD+semicolon bind[master_grow] = MOD+l bind[master_shrink] = MOD+h bind[maximize_toggle] = MOD+e bind[menu] = MOD+p bind[move_down] = MOD+Shift+asterisk bind[move_left] = MOD+ugrave bind[move_right] = MOD+asterisk bind[move_up] = MOD+Shift+ugrave bind[mvrg_1] = MOD+Shift+KP_End bind[mvrg_2] = MOD+Shift+KP_Down bind[mvrg_3] = MOD+Shift+KP_Next bind[mvrg_4] = MOD+Shift+KP_Left bind[mvrg_5] = MOD+Shift+KP_Begin bind[mvrg_6] = MOD+Shift+KP_Right bind[mvrg_7] = MOD+Shift+KP_Home bind[mvrg_8] = MOD+Shift+KP_Up bind[mvrg_9] = MOD+Shift+KP_Prior bind[mvws_1] = MOD+Shift+ampersand bind[mvws_2] = MOD+Shift+eacute bind[mvws_3] = MOD+Shift+quotedbl bind[mvws_4] = MOD+Shift+apostrophe bind[mvws_5] = MOD+Shift+parenleft bind[mvws_6] = MOD+Shift+hyphen bind[mvws_7] = MOD+Shift+egrave bind[mvws_8] = MOD+Shift+underscore bind[mvws_9] = MOD+Shift+ccedilla bind[mvws_10] = MOD+Shift+agrave bind[mvws_11] = MOD+Shift+F1 bind[mvws_12] = MOD+Shift+F2 bind[mvws_13] = MOD+Shift+F3 bind[mvws_14] = MOD+Shift+F4 bind[mvws_15] = MOD+Shift+F5 bind[mvws_16] = MOD+Shift+F6 bind[mvws_17] = MOD+Shift+F7 bind[mvws_18] = MOD+Shift+F8 bind[mvws_19] = MOD+Shift+F9 bind[mvws_20] = MOD+Shift+F10 bind[mvws_21] = MOD+Shift+F11 bind[mvws_22] = MOD+Shift+F12 bind[name_workspace] = MOD+Shift+slash bind[quit] = MOD+Shift+q bind[raise_toggle] = MOD+Shift+r bind[restart] = MOD+q bind[rg_1] = MOD+KP_End bind[rg_2] = MOD+KP_Down bind[rg_3] = MOD+KP_Next bind[rg_4] = MOD+KP_Left bind[rg_5] = MOD+KP_Begin bind[rg_6] = MOD+KP_Right bind[rg_7] = MOD+KP_Home bind[rg_8] = MOD+KP_Up bind[rg_9] = MOD+KP_Prior bind[rg_next] = MOD+Shift+Right bind[rg_prev] = MOD+Shift+Left bind[screenshot_all] = MOD+s bind[screenshot_wind] = MOD+Shift+s bind[search_win] = MOD+f bind[search_workspace] = MOD+slash bind[stack_dec] = MOD+Shift+semicolon bind[stack_inc] = MOD+Shift+comma bind[stack_reset] = MOD+Shift+space bind[swap_main] = MOD+Return bind[swap_next] = MOD+Shift+j bind[swap_prev] = MOD+Shift+k bind[term] = MOD+Shift+Return bind[uniconify] = MOD+Shift+w bind[version] = MOD+Shift+v bind[width_grow] = MOD+equal bind[width_shrink] = MOD+minus bind[wind_del] = MOD+x bind[wind_kill] = MOD+Shift+x bind[ws_1] = MOD+ampersand bind[ws_2] = MOD+eacute bind[ws_3] = MOD+quotedbl bind[ws_4] = MOD+apostrophe bind[ws_5] = MOD+parenleft bind[ws_6] = MOD+hyphen bind[ws_7] = MOD+egrave bind[ws_8] = MOD+underscore bind[ws_9] = MOD+ccedilla bind[ws_10] = MOD+agrave bind[ws_11] = MOD+F1 bind[ws_12] = MOD+F2 bind[ws_13] = MOD+F3 bind[ws_14] = MOD+F4 bind[ws_15] = MOD+F5 bind[ws_16] = MOD+F6 bind[ws_17] = MOD+F7 bind[ws_18] = MOD+F8 bind[ws_19] = MOD+F9 bind[ws_20] = MOD+F10 bind[ws_21] = MOD+F11 bind[ws_22] = MOD+F12 bind[ws_next] = MOD+Right bind[ws_next_all] = MOD+Up bind[ws_next_move] = MOD+Shift+Up bind[ws_prev] = MOD+Left bind[ws_prev_all] = MOD+Down bind[ws_prev_move] = MOD+Shift+Down bind[ws_prior] = MOD+a spectrwm-SPECTRWM_3_1_0/spectrwm_fr_ch.conf000066400000000000000000000066001316475426700207420ustar00rootroot00000000000000# Key bindings for Swiss French (FR_CH) keyboards # unbind with: bind[] = bind[bar_toggle] = MOD+b bind[bar_toggle_ws] = MOD+Shift+b bind[button2] = MOD+v bind[cycle_layout] = MOD+space bind[flip_layout] = MOD+Shift+backslash bind[float_toggle] = MOD+t bind[focus_main] = MOD+m bind[focus_next] = MOD+j bind[focus_next] = MOD+Tab bind[focus_prev] = MOD+k bind[focus_prev] = MOD+Shift+Tab bind[focus_urgent] = MOD+u bind[height_grow] = MOD+Shift+egrave bind[height_shrink] = MOD+Shift+minus bind[iconify] = MOD+w bind[initscr] = MOD+Shift+i bind[lock] = MOD+Shift+Delete bind[master_add] = MOD+comma bind[master_del] = MOD+period bind[master_grow] = MOD+l bind[master_shrink] = MOD+h bind[maximize_toggle] = MOD+e bind[menu] = MOD+p bind[move_down] = MOD+Shift+agrave bind[move_left] = MOD+eacute bind[move_right] = MOD+agrave bind[move_up] = MOD+Shift+eacute bind[mvrg_1] = MOD+Shift+KP_End bind[mvrg_2] = MOD+Shift+KP_Down bind[mvrg_3] = MOD+Shift+KP_Next bind[mvrg_4] = MOD+Shift+KP_Left bind[mvrg_5] = MOD+Shift+KP_Begin bind[mvrg_6] = MOD+Shift+KP_Right bind[mvrg_7] = MOD+Shift+KP_Home bind[mvrg_8] = MOD+Shift+KP_Up bind[mvrg_9] = MOD+Shift+KP_Prior bind[mvws_1] = MOD+Shift+1 bind[mvws_2] = MOD+Shift+2 bind[mvws_3] = MOD+Shift+3 bind[mvws_4] = MOD+Shift+4 bind[mvws_5] = MOD+Shift+5 bind[mvws_6] = MOD+Shift+6 bind[mvws_7] = MOD+Shift+7 bind[mvws_8] = MOD+Shift+8 bind[mvws_9] = MOD+Shift+9 bind[mvws_10] = MOD+Shift+0 bind[mvws_11] = MOD+Shift+F1 bind[mvws_12] = MOD+Shift+F2 bind[mvws_13] = MOD+Shift+F3 bind[mvws_14] = MOD+Shift+F4 bind[mvws_15] = MOD+Shift+F5 bind[mvws_16] = MOD+Shift+F6 bind[mvws_17] = MOD+Shift+F7 bind[mvws_18] = MOD+Shift+F8 bind[mvws_19] = MOD+Shift+F9 bind[mvws_20] = MOD+Shift+F10 bind[mvws_21] = MOD+Shift+F11 bind[mvws_22] = MOD+Shift+F12 bind[name_workspace] = MOD+Shift+apostrophe bind[quit] = MOD+Shift+q bind[raise_toggle] = MOD+Shift+r bind[restart] = MOD+q bind[rg_1] = MOD+KP_End bind[rg_2] = MOD+KP_Down bind[rg_3] = MOD+KP_Next bind[rg_4] = MOD+KP_Left bind[rg_5] = MOD+KP_Begin bind[rg_6] = MOD+KP_Right bind[rg_7] = MOD+KP_Home bind[rg_8] = MOD+KP_Up bind[rg_9] = MOD+KP_Prior bind[rg_next] = MOD+Shift+Right bind[rg_prev] = MOD+Shift+Left bind[screenshot_all] = MOD+s bind[screenshot_wind] = MOD+Shift+s bind[search_win] = MOD+f bind[search_workspace] = MOD+apostrophe bind[stack_dec] = MOD+Shift+period bind[stack_inc] = MOD+Shift+comma bind[stack_reset] = MOD+Shift+space bind[swap_main] = MOD+Return bind[swap_next] = MOD+Shift+j bind[swap_prev] = MOD+Shift+k bind[term] = MOD+Shift+Return bind[uniconify] = MOD+Shift+w bind[version] = MOD+Shift+v bind[width_grow] = MOD+egrave bind[width_shrink] = MOD+minus bind[wind_del] = MOD+x bind[wind_kill] = MOD+Shift+x bind[ws_1] = MOD+1 bind[ws_2] = MOD+2 bind[ws_3] = MOD+3 bind[ws_4] = MOD+4 bind[ws_5] = MOD+5 bind[ws_6] = MOD+6 bind[ws_7] = MOD+7 bind[ws_8] = MOD+8 bind[ws_9] = MOD+9 bind[ws_10] = MOD+0 bind[ws_11] = MOD+F1 bind[ws_12] = MOD+F2 bind[ws_13] = MOD+F3 bind[ws_14] = MOD+F4 bind[ws_15] = MOD+F5 bind[ws_16] = MOD+F6 bind[ws_17] = MOD+F7 bind[ws_18] = MOD+F8 bind[ws_19] = MOD+F9 bind[ws_20] = MOD+F10 bind[ws_21] = MOD+F11 bind[ws_22] = MOD+F12 bind[ws_next] = MOD+Right bind[ws_next_all] = MOD+Up bind[ws_next_move] = MOD+Shift+Up bind[ws_prev] = MOD+Left bind[ws_prev_all] = MOD+Down bind[ws_prev_move] = MOD+Shift+Down bind[ws_prior] = MOD+a spectrwm-SPECTRWM_3_1_0/spectrwm_se.conf000066400000000000000000000066031316475426700202730ustar00rootroot00000000000000# Key bindings for Swedish (se) keyboards # unbind with: bind[] = bind[bar_toggle] = MOD+b bind[bar_toggle_ws] = MOD+Shift+b bind[button2] = MOD+v bind[cycle_layout] = MOD+space bind[flip_layout] = MOD+Shift+apostrophe bind[float_toggle] = MOD+t bind[focus_main] = MOD+m bind[focus_next] = MOD+j bind[focus_next] = MOD+Tab bind[focus_prev] = MOD+k bind[focus_prev] = MOD+Shift+Tab bind[focus_urgent] = MOD+u bind[height_grow] = MOD+Shift+dead_acute bind[height_shrink] = MOD+Shift+plus bind[iconify] = MOD+w bind[initscr] = MOD+Shift+i bind[lock] = MOD+Shift+Delete bind[master_add] = MOD+comma bind[master_del] = MOD+period bind[master_grow] = MOD+l bind[master_shrink] = MOD+h bind[maximize_toggle] = MOD+e bind[menu] = MOD+p bind[move_down] = MOD+Shift+dead_diaeresis bind[move_left] = MOD+aring bind[move_right] = MOD+dead_diaeresis bind[move_up] = MOD+Shift+aring bind[mvrg_1] = MOD+Shift+KP_End bind[mvrg_2] = MOD+Shift+KP_Down bind[mvrg_3] = MOD+Shift+KP_Next bind[mvrg_4] = MOD+Shift+KP_Left bind[mvrg_5] = MOD+Shift+KP_Begin bind[mvrg_6] = MOD+Shift+KP_Right bind[mvrg_7] = MOD+Shift+KP_Home bind[mvrg_8] = MOD+Shift+KP_Up bind[mvrg_9] = MOD+Shift+KP_Prior bind[mvws_1] = MOD+Shift+1 bind[mvws_2] = MOD+Shift+2 bind[mvws_3] = MOD+Shift+3 bind[mvws_4] = MOD+Shift+4 bind[mvws_5] = MOD+Shift+5 bind[mvws_6] = MOD+Shift+6 bind[mvws_7] = MOD+Shift+7 bind[mvws_8] = MOD+Shift+8 bind[mvws_9] = MOD+Shift+9 bind[mvws_10] = MOD+Shift+0 bind[mvws_11] = MOD+Shift+F1 bind[mvws_12] = MOD+Shift+F2 bind[mvws_13] = MOD+Shift+F3 bind[mvws_14] = MOD+Shift+F4 bind[mvws_15] = MOD+Shift+F5 bind[mvws_16] = MOD+Shift+F6 bind[mvws_17] = MOD+Shift+F7 bind[mvws_18] = MOD+Shift+F8 bind[mvws_19] = MOD+Shift+F9 bind[mvws_20] = MOD+Shift+F10 bind[mvws_21] = MOD+Shift+F11 bind[mvws_22] = MOD+Shift+F12 bind[name_workspace] = MOD+Shift+minus bind[quit] = MOD+Shift+q bind[raise_toggle] = MOD+Shift+r bind[restart] = MOD+q bind[rg_1] = MOD+KP_End bind[rg_2] = MOD+KP_Down bind[rg_3] = MOD+KP_Next bind[rg_4] = MOD+KP_Left bind[rg_5] = MOD+KP_Begin bind[rg_6] = MOD+KP_Right bind[rg_7] = MOD+KP_Home bind[rg_8] = MOD+KP_Up bind[rg_9] = MOD+KP_Prior bind[rg_next] = MOD+Shift+Right bind[rg_prev] = MOD+Shift+Left bind[screenshot_all] = MOD+s bind[screenshot_wind] = MOD+Shift+s bind[search_win] = MOD+f bind[search_workspace] = MOD+minus bind[stack_dec] = MOD+Shift+period bind[stack_inc] = MOD+Shift+comma bind[stack_reset] = MOD+Shift+space bind[swap_main] = MOD+Return bind[swap_next] = MOD+Shift+j bind[swap_prev] = MOD+Shift+k bind[term] = MOD+Shift+Return bind[uniconify] = MOD+Shift+w bind[version] = MOD+Shift+v bind[width_grow] = MOD+dead_acute bind[width_shrink] = MOD+plus bind[wind_del] = MOD+x bind[wind_kill] = MOD+Shift+x bind[ws_1] = MOD+1 bind[ws_2] = MOD+2 bind[ws_3] = MOD+3 bind[ws_4] = MOD+4 bind[ws_5] = MOD+5 bind[ws_6] = MOD+6 bind[ws_7] = MOD+7 bind[ws_8] = MOD+8 bind[ws_9] = MOD+9 bind[ws_10] = MOD+0 bind[ws_11] = MOD+F1 bind[ws_12] = MOD+F2 bind[ws_13] = MOD+F3 bind[ws_14] = MOD+F4 bind[ws_15] = MOD+F5 bind[ws_16] = MOD+F6 bind[ws_17] = MOD+F7 bind[ws_18] = MOD+F8 bind[ws_19] = MOD+F9 bind[ws_20] = MOD+F10 bind[ws_21] = MOD+F11 bind[ws_22] = MOD+F12 bind[ws_next] = MOD+Right bind[ws_next_all] = MOD+Up bind[ws_next_move] = MOD+Shift+Up bind[ws_prev] = MOD+Left bind[ws_prev_all] = MOD+Down bind[ws_prev_move] = MOD+Shift+Down bind[ws_prior] = MOD+a spectrwm-SPECTRWM_3_1_0/spectrwm_us.conf000066400000000000000000000066101316475426700203110ustar00rootroot00000000000000# Key bindings for United States (us) keyboards # unbind with: bind[] = bind[bar_toggle] = MOD+b bind[bar_toggle_ws] = MOD+Shift+b bind[button2] = MOD+v bind[cycle_layout] = MOD+space bind[flip_layout] = MOD+Shift+backslash bind[float_toggle] = MOD+t bind[focus_main] = MOD+m bind[focus_next] = MOD+j bind[focus_next] = MOD+Tab bind[focus_prev] = MOD+k bind[focus_prev] = MOD+Shift+Tab bind[focus_urgent] = MOD+u bind[height_grow] = MOD+Shift+equal bind[height_shrink] = MOD+Shift+minus bind[iconify] = MOD+w bind[initscr] = MOD+Shift+i bind[lock] = MOD+Shift+Delete bind[master_add] = MOD+comma bind[master_del] = MOD+period bind[master_grow] = MOD+l bind[master_shrink] = MOD+h bind[maximize_toggle] = MOD+e bind[menu] = MOD+p bind[move_down] = MOD+Shift+bracketright bind[move_left] = MOD+bracketleft bind[move_right] = MOD+bracketright bind[move_up] = MOD+Shift+bracketleft bind[mvrg_1] = MOD+Shift+KP_End bind[mvrg_2] = MOD+Shift+KP_Down bind[mvrg_3] = MOD+Shift+KP_Next bind[mvrg_4] = MOD+Shift+KP_Left bind[mvrg_5] = MOD+Shift+KP_Begin bind[mvrg_6] = MOD+Shift+KP_Right bind[mvrg_7] = MOD+Shift+KP_Home bind[mvrg_8] = MOD+Shift+KP_Up bind[mvrg_9] = MOD+Shift+KP_Prior bind[mvws_1] = MOD+Shift+1 bind[mvws_2] = MOD+Shift+2 bind[mvws_3] = MOD+Shift+3 bind[mvws_4] = MOD+Shift+4 bind[mvws_5] = MOD+Shift+5 bind[mvws_6] = MOD+Shift+6 bind[mvws_7] = MOD+Shift+7 bind[mvws_8] = MOD+Shift+8 bind[mvws_9] = MOD+Shift+9 bind[mvws_10] = MOD+Shift+0 bind[mvws_11] = MOD+Shift+F1 bind[mvws_12] = MOD+Shift+F2 bind[mvws_13] = MOD+Shift+F3 bind[mvws_14] = MOD+Shift+F4 bind[mvws_15] = MOD+Shift+F5 bind[mvws_16] = MOD+Shift+F6 bind[mvws_17] = MOD+Shift+F7 bind[mvws_18] = MOD+Shift+F8 bind[mvws_19] = MOD+Shift+F9 bind[mvws_20] = MOD+Shift+F10 bind[mvws_21] = MOD+Shift+F11 bind[mvws_22] = MOD+Shift+F12 bind[name_workspace] = MOD+Shift+slash bind[quit] = MOD+Shift+q bind[raise_toggle] = MOD+Shift+r bind[restart] = MOD+q bind[rg_1] = MOD+KP_End bind[rg_2] = MOD+KP_Down bind[rg_3] = MOD+KP_Next bind[rg_4] = MOD+KP_Left bind[rg_5] = MOD+KP_Begin bind[rg_6] = MOD+KP_Right bind[rg_7] = MOD+KP_Home bind[rg_8] = MOD+KP_Up bind[rg_9] = MOD+KP_Prior bind[rg_next] = MOD+Shift+Right bind[rg_prev] = MOD+Shift+Left bind[screenshot_all] = MOD+s bind[screenshot_wind] = MOD+Shift+s bind[search_win] = MOD+f bind[search_workspace] = MOD+slash bind[stack_dec] = MOD+Shift+period bind[stack_inc] = MOD+Shift+comma bind[stack_reset] = MOD+Shift+space bind[swap_main] = MOD+Return bind[swap_next] = MOD+Shift+j bind[swap_prev] = MOD+Shift+k bind[term] = MOD+Shift+Return bind[uniconify] = MOD+Shift+w bind[version] = MOD+Shift+v bind[width_grow] = MOD+equal bind[width_shrink] = MOD+minus bind[wind_del] = MOD+x bind[wind_kill] = MOD+Shift+x bind[ws_1] = MOD+1 bind[ws_2] = MOD+2 bind[ws_3] = MOD+3 bind[ws_4] = MOD+4 bind[ws_5] = MOD+5 bind[ws_6] = MOD+6 bind[ws_7] = MOD+7 bind[ws_8] = MOD+8 bind[ws_9] = MOD+9 bind[ws_10] = MOD+0 bind[ws_11] = MOD+F1 bind[ws_12] = MOD+F2 bind[ws_13] = MOD+F3 bind[ws_14] = MOD+F4 bind[ws_15] = MOD+F5 bind[ws_16] = MOD+F6 bind[ws_17] = MOD+F7 bind[ws_18] = MOD+F8 bind[ws_19] = MOD+F9 bind[ws_20] = MOD+F10 bind[ws_21] = MOD+F11 bind[ws_22] = MOD+F12 bind[ws_next] = MOD+Right bind[ws_next_all] = MOD+Up bind[ws_next_move] = MOD+Shift+Up bind[ws_prev] = MOD+Left bind[ws_prev_all] = MOD+Down bind[ws_prev_move] = MOD+Shift+Down bind[ws_prior] = MOD+a spectrwm-SPECTRWM_3_1_0/version.h000066400000000000000000000022541316475426700167250ustar00rootroot00000000000000/* * Copyright (c) 2011 Conformal Systems LLC * * 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. */ #ifndef SPECTRWM_VERSION_H #define SPECTRWM_VERSION_H #define SPECTRWM_STR(x) #x #define SPECTRWM_STRINGIZE(x) SPECTRWM_STR(x) #define SPECTRWM_MAJOR 3 #define SPECTRWM_MINOR 1 #define SPECTRWM_PATCH 0 #define SPECTRWM_VERSION SPECTRWM_STRINGIZE(SPECTRWM_MAJOR) "." \ SPECTRWM_STRINGIZE(SPECTRWM_MINOR) "." \ SPECTRWM_STRINGIZE(SPECTRWM_PATCH) #endif /* SPECTRWM_VERSION_H */