spectrwm-1.0.0004075500017500000000000000000001171750520200123135ustar00marcowheelspectrwm-1.0.0/Makefile010064400017500000000000000017101171750520200140260ustar00marcowheel.include PREFIX?=/usr/local BINDIR=${PREFIX}/bin SUBDIR= lib PROG=spectrwm #MAN=spectrwm_pt.1 spectrwm_ru.1 MAN=spectrwm.1 spectrwm_es.1 spectrwm_it.1 CFLAGS+=-std=c89 -Wall -Wno-uninitialized -ggdb3 # Uncomment define below to disallow user settable clock format string #CFLAGS+=-DSWM_DENY_CLOCK_FORMAT CPPFLAGS+= -I${X11BASE}/include LDADD+=-lutil -L${X11BASE}/lib -lX11 -lXrandr -lXtst BUILDVERSION != sh "${.CURDIR}/buildver.sh" .if !${BUILDVERSION} == "" CPPFLAGS+= -DSPECTRWM_BUILDSTR=\"$(BUILDVERSION)\" .endif MANDIR= ${PREFIX}/man/man #spectrwm_ru.cat1: spectrwm_ru.1 # nroff -mandoc ${.CURDIR}/spectrwm_ru.1 > ${.TARGET} obj: _xenocara_obj beforeinstall: ln -sf ${BINDIR}/${PROG} ${BINDIR}/scrotwm # clang targets .if ${.TARGETS:M*analyze*} CC=clang CXX=clang++ CPP=clang -E CFLAGS+=--analyze .elif ${.TARGETS:M*clang*} CC=clang CXX=clang++ CPP=clang -E .endif analyze: all clang: all .include .include spectrwm-1.0.0/baraction.sh010064400017500000000000000040311171750520200146630ustar00marcowheel#!/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 -gt 2 ]; then # print_date print_mem $MEM print_cpu $REPLY print_cpuspeed print_apm $APM_DATA echo "" fi I=$(( ${I} + 1 )); done done spectrwm-1.0.0/buildver.sh010064400017500000000000000001621171750520200145360ustar00marcowheel#!/bin/sh CURDIR=$(dirname $0) if [ -d "$CURDIR/.git" ]; then cd "$CURDIR" git describe --tags | tr -d '\n' fi spectrwm-1.0.0/freebsd004075500017500000000000000000001171750520200137255ustar00marcowheelspectrwm-1.0.0/freebsd/Makefile010064400017500000000000000032041171750520200154400ustar00marcowheel.sinclude PREFIX?= /usr/local LOCALBASE?= /usr/local SWM_BINDIR?= $(PREFIX)/bin SWM_LIBDIR?= $(PREFIX)/lib SWM_MANDIR?= $(PREFIX)/man 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+= -Wall -Wno-uninitialized -I. -I${LOCALBASE}/include CFLAGS+= -DSWM_LIB=\"$(SWM_LIBDIR)/libswmhack.so.$(LVERS)\" LDADD+= -lutil -L${LOCALBASE}/lib -lX11 -lXrandr -lXtst 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 install -m 644 ../spectrwm_es.1 $(SWM_MANDIR)/man1/spectrwm_es.1 install -m 644 ../spectrwm_it.1 $(SWM_MANDIR)/man1/spectrwm_it.1 install -m 644 ../spectrwm_pt.1 $(SWM_MANDIR)/man1/spectrwm_pt.1 install -m 644 ../spectrwm_ru.1 $(SWM_MANDIR)/man1/spectrwm_ru.1 ln -sf $(SWM_BINDIR)/spectrwm $(SWM_BINDIR)/scrotwm clean: rm -f spectrwm *.o *.so libswmhack.so.* spectrwm.c swm_hack.c version.h .sinclude spectrwm-1.0.0/freebsd/util.h010064400017500000000000000001161171750520200151250ustar00marcowheel#include #ifndef TAILQ_END #define TAILQ_END(head) NULL #endif spectrwm-1.0.0/initscreen.sh010064400017500000000000000001661171750520200150710ustar00marcowheel#!/bin/sh # # Example xrandr multiscreen init xrandr --output LVDS --auto xrandr --output VGA --auto --right-of LVDS spectrwm-1.0.0/lib004075500017500000000000000000001171750520200130615ustar00marcowheelspectrwm-1.0.0/lib/Makefile010064400017500000000000000006311171750520200145750ustar00marcowheel.include PREFIX?=/usr/local LIB= swmhack NOMAN= yes SRCS= swm_hack.c LIBDIR= ${X11BASE}/lib DEBUGLIBS= no NOPROFILE= yes CFLAGS+=-Wall -Wno-uninitialized -ggdb3 -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-1.0.0/lib/shlib_version010064400017500000000000000000201171750520200157160ustar00marcowheelmajor=0 minor=0 spectrwm-1.0.0/lib/swm_hack.c010064400017500000000000000200251171750520200150740ustar00marcowheel/* * 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 /* 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 int xterm = 0; static Display *dpy = NULL; /* 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) 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; /* find the real Xlib and the real X function */ if (!lib_xlib) lib_xlib = dlopen("libX11.so", RTLD_GLOBAL | RTLD_LAZY); if (!xia) xia = (XIA *) dlsym(lib_xlib, "XInternAtom"); if (!xcp) xcp = (XCP *) dlsym(lib_xlib, "XChangeProperty"); /* 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 * display, 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; /* find the real Xlib and the real X function */ if (!lib_xlib) lib_xlib = dlopen("libX11.so", RTLD_GLOBAL | RTLD_LAZY); if (!func) { func = (CWF *) dlsym(lib_xlib, "XCreateWindow"); dpy = display; } if (parent == DefaultRootWindow(display)) parent = MyRoot(display); id = (*func) (display, parent, x, y, width, height, border_width, depth, clss, visual, valuemask, attributes); if (id) { if ((env = getenv("_SWM_WS")) != NULL) set_property(display, id, "_SWM_WS", env); if ((env = getenv("_SWM_PID")) != NULL) set_property(display, id, "_SWM_PID", env); if ((env = getenv("_SWM_XTERM_FONTADJ")) != NULL) { unsetenv("_SWM_XTERM_FONTADJ"); xterm = 1; } } 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 * display, 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; /* find the real Xlib and the real X function */ if (!lib_xlib) lib_xlib = dlopen("libX11.so", RTLD_GLOBAL | RTLD_LAZY); if (!func) func = (CSWF *) dlsym(lib_xlib, "XCreateSimpleWindow"); if (parent == DefaultRootWindow(display)) parent = MyRoot(display); id = (*func) (display, parent, x, y, width, height, border_width, border, background); if (id) { if ((env = getenv("_SWM_WS")) != NULL) set_property(display, id, "_SWM_WS", env); if ((env = getenv("_SWM_PID")) != NULL) set_property(display, id, "_SWM_PID", env); if ((env = getenv("_SWM_XTERM_FONTADJ")) != NULL) { unsetenv("_SWM_XTERM_FONTADJ"); xterm = 1; } } return (id); } typedef int (RWF) (Display * _display, Window _window, Window _parent, int x, int y); /* XReparentWindow intercept hack */ int XReparentWindow(Display * display, Window window, Window parent, int x, int y) { static RWF *func = NULL; /* find the real Xlib and the real X function */ if (!lib_xlib) lib_xlib = dlopen("libX11.so", RTLD_GLOBAL | RTLD_LAZY); if (!func) func = (RWF *) dlsym(lib_xlib, "XReparentWindow"); if (parent == DefaultRootWindow(display)) parent = MyRoot(display); return (*func) (display, 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 int kp_add = 0, kp_subtract = 0; /* find the real Xlib and the real X function */ if (!lib_xtlib) lib_xtlib = dlopen("libXt.so", RTLD_GLOBAL | RTLD_LAZY); if (!func) { func = (ANEF *) dlsym(lib_xtlib, "XtAppNextEvent"); if (dpy != NULL) { kp_add = XKeysymToKeycode(dpy, XK_KP_Add); kp_subtract = XKeysymToKeycode(dpy, XK_KP_Subtract); } } (*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-1.0.0/linux004075500017500000000000000000001171750520200134525ustar00marcowheelspectrwm-1.0.0/linux/Makefile010064400017500000000000000030201171750520200151610ustar00marcowheelCFLAGS+= -Wall -ggdb -D_GNU_SOURCE -I. CFLAGS+= -DSWM_LIB=\"$(LIBDIR)/libswmhack.so.$(LVERS)\" LDADD+= -lX11 -lXrandr -lXtst PREFIX?= /usr/local BINDIR?= $(PREFIX)/bin LIBDIR?= $(PREFIX)/lib MANDIR?= $(PREFIX)/share/man CC= gcc LVERS= $(shell . ../lib/shlib_version; echo $$major.$$minor) BUILDVERSION= $(shell sh $(CURDIR)/../buildver.sh) ifneq ("${BUILDVERSION}", "") CFLAGS+= -DSPECTRWM_BUILDSTR=\"$(BUILDVERSION)\" endif 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 linux.o $(CC) $(LDFLAGS) -o $@ $+ $(LDADD) %.so: %.c $(CC) $(CFLAGS) -c -fpic -DPIC $+ -o $@ libswmhack.so.$(LVERS): swm_hack.so $(CC) -shared -fpic -o libswmhack.so.$(LVERS) swm_hack.so $(LDADD) 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 install -m 644 ../spectrwm_es.1 $(DESTDIR)$(MANDIR)/man1/spectrwm_es.1 install -m 644 ../spectrwm_it.1 $(DESTDIR)$(MANDIR)/man1/spectrwm_it.1 install -m 644 ../spectrwm_pt.1 $(DESTDIR)$(MANDIR)/man1/spectrwm_pt.1 install -m 644 ../spectrwm_ru.1 $(DESTDIR)$(MANDIR)/man1/spectrwm_ru.1 ln -sf $(DESTDIR)$(BINDIR)/spectrwm $(DESTDIR)$(BINDIR)/scrotwm clean: rm -f spectrwm *.o *.so libswmhack.so.* spectrwm.c swm_hack.c version.h .PHONY: all install clean spectrwm-1.0.0/linux/linux.c010064400017500000000000000306501171750520200150350ustar00marcowheel#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-1.0.0/linux/spectrwm.desktop010064400017500000000000000002041171750520200167610ustar00marcowheel[Desktop Entry] Name=spectrwm Comment=The spectrwm window manager Type=Application Exec=/usr/bin/spectrwm TryExec=/usr/bin/spectrwm spectrwm-1.0.0/linux/tree.h010064400017500000000000000603771171750520200146530ustar00marcowheel/* $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_REVERSE(x, name, head) \ for ((x) = RB_MAX(name, head); \ (x) != NULL; \ (x) = name##_RB_PREV(x)) #endif /* _SYS_TREE_H_ */ spectrwm-1.0.0/linux/util.h010064400017500000000000000011031171750520200146470ustar00marcowheel#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 spectrwm-1.0.0/osx004075500017500000000000000000001171750520200131245ustar00marcowheelspectrwm-1.0.0/osx/Makefile010064400017500000000000000030031171750520200146340ustar00marcowheelCFLAGS+= -O2 -Wall -ggdb -D_GNU_SOURCE -D__OSX__ -I. -I.. CFLAGS+= -DSWM_LIB=\"$(LIBDIR)/libswmhack.so.$(LVERS)\" LDADD+= -L/usr/X11/lib/ -lX11 -lXrandr -lXtst PREFIX?= /usr/local BINDIR?= $(PREFIX)/bin LIBDIR?= $(PREFIX)/lib MANDIR?= $(PREFIX)/share/man CC= gcc LVERS= $(shell . ../lib/shlib_version; echo $$major.$$minor) all: spectrwm libswmhack.so.$(LVERS) spectrwm.c: ln -sf ../spectrwm.c 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) -shared -fpic -o libswmhack.so.$(LVERS) $(LDADD) swm_hack.so # 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 install -m 644 ../spectrwm_es.1 $(DESTDIR)$(MANDIR)/man1/spectrwm_es.1 install -m 644 ../spectrwm_it.1 $(DESTDIR)$(MANDIR)/man1/spectrwm_it.1 install -m 644 ../spectrwm_pt.1 $(DESTDIR)$(MANDIR)/man1/spectrwm_pt.1 install -m 644 ../spectrwm_ru.1 $(DESTDIR)$(MANDIR)/man1/spectrwm_ru.1 ln -sf $(DESTDIR)$(BINDIR)/spectrwm $(DESTDIR)$(BINDIR)/scrotwm clean: rm -f spectrwm *.o *.so libswmhack.so.* spectrwm.c swm_hack.c .PHONY: all install clean spectrwm-1.0.0/osx/osx.c010064400017500000000000000036661171750520200141700ustar00marcowheel#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-1.0.0/osx/osx.h010064400017500000000000000002001171750520200141520ustar00marcowheellong long strtonum(const char *, long long, long long, const char **); #ifndef TAILQ_END #define TAILQ_END(head) NULL #endif spectrwm-1.0.0/release.sh010064400017500000000000000067121171750520200143510ustar00marcowheel#!/bin/sh # # Prepares a release: # - Bumps version according to specified level (major, minor, or patch) # - Updates all necessary headers with new version # - Commits the changes # - Tags the release # - Creates a release tarball PROJECT=spectrwm PROJECT_UC=$(echo $PROJECT | tr '[:lower:]' '[:upper:]') SCRIPT=$(basename $0) HEADER=version.h # verify params if [ $# -lt 1 ]; then echo "usage: $SCRIPT {major | minor | patch}" exit 1 fi report_err() { echo "$SCRIPT: error: $1" 1>&2 exit 1 } cd "$(dirname $0)" # verify header exists if [ ! -f "$HEADER" ]; then report_err "$HEADER does not exist" fi # verify valid release type RTYPE="$1" if [ "$RTYPE" != "major" -a "$RTYPE" != "minor" -a "$RTYPE" != "patch" ]; then report_err "release type must be major, minor, or patch" fi # verify git is available if ! type git >/dev/null 2>&1; then report_err "unable to find 'git' in the system path" fi # verify the git repository is on the master branch BRANCH=$(git branch | grep '\*' | cut -c3-) if [ "$BRANCH" != "master" ]; then report_err "git repository must be on the master branch" fi # verify there are no uncommitted modifications prior to release modifications NUM_MODIFIED=$(git diff 2>/dev/null | wc -l | sed 's/^[ \t]*//') NUM_STAGED=$(git diff --cached 2>/dev/null | wc -l | sed 's/^[ \t]*//') if [ "$NUM_MODIFIED" != "0" -o "$NUM_STAGED" != "0" ]; then report_err "the working directory contains uncommitted modifications" fi # get version PAT_PREFIX="(^#define[[:space:]]+${PROJECT_UC}" PAT_SUFFIX='[[:space:]]+)[0-9]+$' MAJOR=$(egrep "${PAT_PREFIX}_MAJOR${PAT_SUFFIX}" $HEADER | awk '{print $3}') MINOR=$(egrep "${PAT_PREFIX}_MINOR${PAT_SUFFIX}" $HEADER | awk '{print $3}') PATCH=$(egrep "${PAT_PREFIX}_PATCH${PAT_SUFFIX}" $HEADER | awk '{print $3}') if [ -z "$MAJOR" -o -z "$MINOR" -o -z "$PATCH" ]; then report_err "unable to get version from $HEADER" fi # bump version according to level if [ "$RTYPE" = "major" ]; then MAJOR=$(expr $MAJOR + 1) MINOR=0 PATCH=0 elif [ "$RTYPE" = "minor" ]; then MINOR=$(expr $MINOR + 1) PATCH=0 elif [ "$RTYPE" = "patch" ]; then PATCH=$(expr $PATCH + 1) fi PROJ_VER="$MAJOR.$MINOR.$PATCH" # update header with new version sed -E " s/${PAT_PREFIX}_MAJOR${PAT_SUFFIX}/\1${MAJOR}/; s/${PAT_PREFIX}_MINOR${PAT_SUFFIX}/\1${MINOR}/; s/${PAT_PREFIX}_PATCH${PAT_SUFFIX}/\1${PATCH}/; " <"$HEADER" >"${HEADER}.tmp" # apply changes mv "${HEADER}.tmp" "$HEADER" # commit and tag TAG="${PROJECT_UC}_${MAJOR}_${MINOR}_${PATCH}" git commit -am "Prepare for release ${PROJ_VER}." || report_err "unable to commit changes" git tag -a "$TAG" -m "Release ${PROJ_VER}" || report_err "unable to create tag" # create temp working space and copy repo over TD=$(mktemp -d /tmp/release.XXXXXXXXXX) if [ ! -d "$TD" ]; then report_err "unable to create temp directory" fi RELEASE_DIR="$PROJECT-$PROJ_VER" RELEASE_TAR="$PROJECT-$PROJ_VER.tgz" git clone . "$TD/$RELEASE_DIR" || report_err "unable to copy to $TD/$RELEASE_DIR" # cleanup repository files cd "$TD" if [ -d "$RELEASE_DIR" -a -d "$RELEASE_DIR/.git" ]; then rm -rf "$RELEASE_DIR/.git" fi if [ -d "$RELEASE_DIR" -a -f "$RELEASE_DIR/.gitignore" ]; then rm -f "$RELEASE_DIR/.gitignore" fi # make snap tar -zcf "$RELEASE_TAR" "$RELEASE_DIR" || report_err "unable to create $RELEASE_TAR" echo "Release tarball:" echo " $TD/$RELEASE_TAR" echo "" echo "If everything is accurate, use the following command to push the changes:" echo " git push --tags origin master" spectrwm-1.0.0/screenshot.sh010064400017500000000000000002031171750520200150730ustar00marcowheel#!/bin/sh # screenshot() { case $1 in full) scrot -m ;; window) sleep 1 scrot -s ;; *) ;; esac; } screenshot $1 spectrwm-1.0.0/spectrwm.1010064400017500000000000000514521171750520200143240ustar00marcowheel.\" 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 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 .It Cm M1 Mouse button 1 .It Cm M3 Mouse button 3 .El .Pp .Nm is very simple in its use. Most of the actions are initiated via key or mouse 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 \*(Ltkeyword\*(Gt = \*(Ltsetting\*(Gt. For example: .Pp .Dl color_focus = red .Pp Enabling or disabling an option is done by using 1 or 0 respectively. .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 ws[]:application, e.g. ws[2]:xterm launches an xterm in workspace 2. .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 Color of the status bar border 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 Color of the status bar window in screen .Ar x . .It Ic bar_delay Update frequency, in seconds, of external script that populates the status bar. .It Ic bar_enabled Enable or disable status bar. .It Ic bar_font Status bar font. .It Ic bar_font_color Ns Bq Ar x Color of the font in status bar in screen .Ar x . .It Ic bar_justify Justify the status bar text. Possible values are .Pa left , .Pa center , and .Pa right . .It Ic bind Ns Bq Ar x Bind key 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 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 .Pa bar_action script. .It Ic color_focus Border color of the currently focussed window. .It Ic color_unfocus Border color of unfocussed windows. .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 screen. .It Ic focus_mode Using a value of .Pa follow_cursor will make the window manager focus the window under the mouse when switching workspaces and creating windows. .It Ic keyboard_mapping Clear all key bindings and load new key 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 ws[idx]:master_grow:master_add:stack_inc:layout:always_raise:stack_mode, e.g. ws[2]:-4:0:1:0:horizontal sets worskspace 2 to the horizontal stack mode and 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 .Pa vertical , .Pa horizontal and .Pa fullscreen . .Pp See .Pa master_grow , .Pa master_shrink , .Pa master_add , .Pa master_del , .Pa stack_inc , .Pa stack_dec , and .Pa 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 .Pa 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 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:n Add "quirk" for windows with class .Ar c 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 screen[]:WIDTHxHEIGHT+X+Y, e.g.\& screen[1]:800x1200+0+0. .Pp To make a screen span multiple monitors, create a region big enough to cover them all, e.g. screen[1]:2048x768+0+0 makes the screen span two monitors with 1024x768 resolution sitting one next to the other. .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 title_class_enabled Enable or disable displaying the window class in the status bar. Enable by setting to 1. .It Ic title_name_enabled Enable or disable displaying the window title in the status bar. Enable by setting to 1. .It Ic urgent_enabled Enable or disable the urgency hint. Note that many terminal emulators require this to be enabled for it to propagate. In xterm, for example, one needs to add the following line .Pa xterm.urgentOnBell: true to .Pa .Xdefaults . .It Ic verbose_layout Enable or disable displaying the current master and stack values in the status bar. Enable by setting to 1. .It Ic window_name_enabled Enable or disable displaying the window name in the status bar. Enable by setting to 1. .El .Pp Colors need to be specified per the .Xr XQueryColor 3 specification and fonts need to be specified per the .Xr XQueryFont 3 specification. .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 you to show the X Logical Font Description ("XLFD") used as setting in the keyword .Pa bar_font . .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 The default programs are described below: .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 Custom programs in the configuration file are specified as follows: .Pp .Dl program[] = [ [... ]] .Pp .Aq name is any identifier that does not conflict with a built-in action or keyword, .Aq progpath is the desired program, and .Aq arg is zero or more arguments to the program. .Pp The following variables represent settable values in .Nm (see the .Sx CONFIGURATION FILES section above), and may be used in the .Aq arg fields and 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_font .It Cm $bar_font_color .It Cm $color_focus .It Cm $color_unfocus .El .Pp Example: .Bd -literal -offset indent program[ff] = /usr/local/bin/firefox http://spectrwm.org/ bind[ff] = Mod+Shift+b # Now Mod+Shift+B launches firefox .Ed .Pp To undo the previous: .Bd -literal -offset indent bind[] = Mod+Shift+b program[ff] = .Ed .Sh BINDINGS .Nm provides many functions (or actions) accessed via key or mouse bindings. .Pp The current mouse bindings are described below: .Pp .Bl -tag -width "M-j, M-XXX" -offset indent -compact .It Cm M1 Focus window .It Cm M-M1 Move window .It Cm M-M3 Resize window .It Cm M-S-M3 Resize window while maintaining it centered .El .Pp The default key bindings are described below: .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 .It Cm M- Ns Aq Cm Space cycle_layout .It Cm M-S- Ns Aq Cm \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-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_ Ar n .It Cm M-S- Ns Aq Ar n .Pf mvws_ Ar n .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 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- 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-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 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 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 swap_next Swap with next window in workspace. .It Cm swap_prev Swap with previous window in workspace. .It Cm bar_toggle Toggle status bar in all workspaces. .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 10. .It Cm mvws_ Ns Ar n Move current window to workspace .Ar n , where .Ar n is 1 through 10. .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_prior Switch to last visited workspace. .It Cm screen_next Move pointer to next region. .It Cm screen_prev Move pointer to previous region. .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 Maximize (map) window returned by dmenu selection. .It Cm always_raise When set tiled windows are allowed to obscure floating windows. .It Cm button2 Fake a middle mouse button click (mouse button 2). .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[] = .Pp .Aq action is one of the actions listed above (or empty) and .Aq keys is in the form of zero or more modifier keys (MOD, Mod1, Shift, etc.) and one or more normal keys (b, space, etc.), separated by "+". For example: .Bd -literal -offset indent bind[reset] = Mod4+q # bind Windows-key + q to reset bind[] = Mod1+q # unbind Alt + q .Ed .Pp To use the currently defined .Ic modkey , specify MOD as the modifier key. .Pp Multiple key combinations may be bound to the same action. .Sh KEYBOARD MAPPING FILES Keyboard mapping files for several keyboard layouts are listed below. These files can be used with the .Pa 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 FLOAT This window should not be tiled, but allowed to float freely. .It TRANSSZ Adjusts size on transient windows that are too small using dialog_ratio (see .Sx CONFIGURATION FILES ) . .It ANYWHERE Allow window to position itself, uncentered. .It XTERM_FONTADJ Adjust xterm fonts when resizing. .It FULLSCREEN Remove border to allow window to use full screen size. .It FOCUSPREV On exit force focus on previously focused application not previous application in the stack. .El .Pp Custom quirks in the configuration file are specified as follows: .Pp .Dl quirk[:] = [ + ... ] .Pp .Aq class and .Aq name specify the window to which the quirk(s) apply, and .Aq quirk is one of the quirks from the list above. For example: .Bd -literal -offset indent quirk[MPlayer:xv] = FLOAT + FULLSCREEN + FOCUSPREV quirk[pcb:pcb] = NONE # remove existing quirk .Ed .Pp You can obtain .Aq class and .Aq 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 WM_CLASS WM_CLASS(STRING) = "Navigator", "Firefox" .Ed .Pp Note that grepping for WM_CLASS flips class and name. 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 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 floated or un-floated): .Bd -literal -offset indent $ wmctrl \-i \-r 0x4a0000b \-b toggle,_NET_WM_STATE_ABOVE .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 .El spectrwm-1.0.0/spectrwm.c010064400017500000000000005206711171750520200144120ustar00marcowheel/* * Copyright (c) 2009-2012 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-2012 Reginald Kennedy * Copyright (c) 2011-2012 Lawrence Teo * Copyright (c) 2011-2012 Tiago Cunha * * 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. */ /* * Much code and ideas taken from dwm under the following license: * MIT/X Consortium License * * 2006-2008 Anselm R Garbe * 2006-2007 Sander van Dijk * 2006-2007 Jukka Salmi * 2007 Premysl Hruby * 2007 Szabolcs Nagy * 2007 Christof Musik * 2007-2008 Enno Gottox Boland * 2007-2008 Peter Hartlich * 2008 Martin Hurton * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__linux__) #include "tree.h" #elif defined(__OpenBSD__) #include #elif defined(__FreeBSD__) #include #else #include "tree.h" #endif #include #include #include #include #include #include #include #include #ifdef __OSX__ #include #endif #include "version.h" #ifdef SPECTRWM_BUILDSTR static const char *buildstr = SPECTRWM_BUILDSTR; #else static const char *buildstr = SPECTRWM_VERSION; #endif #if RANDR_MAJOR < 1 # error XRandR versions less than 1.0 are not supported #endif #if RANDR_MAJOR >= 1 #if RANDR_MINOR >= 2 #define SWM_XRR_HAS_CRTC #endif #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, 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 u_int32_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 ; #else #define DPRINTF(x...) #define DNPRINTF(n,x...) #endif #define LENGTH(x) (sizeof x / sizeof x[0]) #define MODKEY Mod1Mask #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) #define MOUSEMASK (BUTTONMASK|PointerMotionMask) #define SWM_PROPLEN (16) #define SWM_FUNCNAME_LEN (32) #define SWM_KEYS_LEN (255) #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 SH_MIN(w) (w)->sh_mask & PMinSize #define SH_MIN_W(w) (w)->sh.min_width #define SH_MIN_H(w) (w)->sh.min_height #define SH_MAX(w) (w)->sh_mask & PMaxSize #define SH_MAX_W(w) (w)->sh.max_width #define SH_MAX_H(w) (w)->sh.max_height #define SH_INC(w) (w)->sh_mask & PResizeInc #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 : 0) #define YESNO(x) ((x) ? "yes" : "no") #define SWM_FOCUS_DEFAULT (0) #define SWM_FOCUS_SYNERGY (1) #define SWM_FOCUS_FOLLOW (2) #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; Atom astate; Atom aprot; Atom adelete; Atom takefocus; Atom a_wmname; Atom a_netwmname; Atom a_utf8_string; Atom a_string; Atom a_swm_iconic; volatile sig_atomic_t running = 1; volatile sig_atomic_t restart_wm = 0; int outputs = 0; int last_focus_event = FocusOut; int (*xerrorxlib)(Display *, XErrorEvent *); int other_wm; int ss_enabled = 0; int xrandr_support; int xrandr_eventbase; unsigned int numlockmask = 0; Display *display; int cycle_empty = 0; int cycle_visible = 0; int term_width = 0; int font_adjusted = 0; unsigned int mod_key = MODKEY; /* 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; GC gc; Window indicator; }; TAILQ_HEAD(search_winlist, search_window); struct search_winlist search_wl; /* search actions */ enum { SWM_SEARCH_NONE, SWM_SEARCH_UNICONIFY, SWM_SEARCH_NAME_WORKSPACE, SWM_SEARCH_SEARCH_WORKSPACE, SWM_SEARCH_SEARCH_WINDOW }; /* 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-*-*-*-*-*-*-*-*-*-*-*," \ "-*-profont-*-*-*-*-*-*-*-*-*-*-*-*," \ "-*-times-medium-r-*-*-*-*-*-*-*-*-*-*," \ "-misc-fixed-medium-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]; unsigned char bar_ext[SWM_BAR_MAX]; char bar_vertext[SWM_BAR_MAX]; int bar_version = 0; sig_atomic_t bar_alarm = 0; int bar_delay = 30; int bar_enabled = 1; int bar_border_width = 1; int bar_at_bottom = 0; int bar_extra = 1; int bar_extra_running = 0; int bar_verbose = 1; int bar_height = 0; int bar_justify = SWM_BAR_JUSTIFY_LEFT; int stack_enabled = 1; int clock_enabled = 1; int urgent_enabled = 0; char *clock_format = NULL; int title_name_enabled = 0; int title_class_enabled = 0; int window_name_enabled = 0; int focus_mode = SWM_FOCUS_DEFAULT; int disable_border = 0; int border_width = 1; int verbose_layout = 0; pid_t bar_pid; XFontSet bar_fs; XFontSetExtents *bar_fs_extents; char *bar_fonts; char *spawn_term[] = { NULL, NULL }; /* XXX fully dynamic */ struct passwd *pwd; #define SWM_MENU_FN (2) #define SWM_MENU_NB (4) #define SWM_MENU_NF (6) #define SWM_MENU_SB (8) #define SWM_MENU_SF (10) /* layout manager data */ struct swm_geometry { int x; int y; int w; int h; }; struct swm_screen; struct workspace; /* virtual "screens" */ struct swm_region { TAILQ_ENTRY(swm_region) entry; 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 */ Window bar_window; }; TAILQ_HEAD(swm_region_list, swm_region); struct ws_win { TAILQ_ENTRY(ws_win) entry; Window id; Window transient; struct ws_win *child_trans; /* transient child window */ struct swm_geometry g; /* current geometry */ struct swm_geometry g_float; /* geometry when floating */ struct swm_geometry rg_float; /* region geom when floating */ int g_floatvalid; /* g_float geometry validity */ int floatmaxed; /* whether maxed by max_stack */ int floating; int manual; int iconic; unsigned int ewmh_flags; int font_size_boundary[SWM_MAX_FONT_STEPS]; int font_steps; int last_inc; int can_delete; int take_focus; int java; unsigned long quirks; struct workspace *ws; /* always valid */ struct swm_screen *s; /* always valid, never changes */ XWindowAttributes wa; XSizeHints sh; long sh_mask; XClassHint ch; XWMHints *hints; }; TAILQ_HEAD(ws_win_list, ws_win); /* pid goo */ struct pid_e { TAILQ_ENTRY(pid_e) entry; long pid; int ws; }; TAILQ_HEAD(pid_list, pid_e); struct pid_list pidlist = TAILQ_HEAD_INITIALIZER(pidlist); /* layout handlers */ void stack(void); 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 ws_win *find_window(Window); void grabbuttons(struct ws_win *, int); void new_region(struct swm_screen *, int, int, int, int); void unmanage_window(struct ws_win *); long getstate(Window); int conf_load(char *, int); struct layout { void (*l_stack)(struct workspace *, struct swm_geometry *); void (*l_config)(struct workspace *, int); u_int32_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 */ int always_raise; /* raise windows on focus */ struct layout *cur_layout; /* current layout handlers */ struct ws_win *focus; /* may be NULL */ struct ws_win *focus_prev; /* 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 */ char stacker[10]; /* display stacker and layout */ /* stacker state */ struct { int horizontal_msize; int horizontal_mwin; int horizontal_stacks; int horizontal_flip; int vertical_msize; int vertical_mwin; int vertical_stacks; int vertical_flip; } l_state; }; enum { SWM_S_COLOR_BAR, SWM_S_COLOR_BAR_BORDER, SWM_S_COLOR_BAR_FONT, SWM_S_COLOR_FOCUS, SWM_S_COLOR_UNFOCUS, SWM_S_COLOR_MAX }; /* physical screen mapping */ #define SWM_WS_MAX (10) 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 */ Window root; struct workspace ws[SWM_WS_MAX]; /* colors */ struct { unsigned long color; char *name; } c[SWM_S_COLOR_MAX]; GC bar_gc; }; struct swm_screen *screens; int num_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_FOCUSCUR (4) #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_CYCLEWS_UP (40) #define SWM_ARG_ID_CYCLEWS_DOWN (41) #define SWM_ARG_ID_CYCLESC_UP (42) #define SWM_ARG_ID_CYCLESC_DOWN (43) #define SWM_ARG_ID_CYCLEWS_UP_ALL (44) #define SWM_ARG_ID_CYCLEWS_DOWN_ALL (45) #define SWM_ARG_ID_STACKINC (50) #define SWM_ARG_ID_STACKDEC (51) #define SWM_ARG_ID_SS_ALL (60) #define SWM_ARG_ID_SS_WINDOW (61) #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) char **argv; }; void focus(struct swm_region *, union arg *); void focus_magic(struct ws_win *); /* quirks */ struct quirk { TAILQ_ENTRY(quirk) entry; char *class; char *name; unsigned long quirk; #define SWM_Q_FLOAT (1<<0) /* float this window */ #define SWM_Q_TRANSSZ (1<<1) /* transiend 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 */ #define SWM_Q_FOCUSPREV (1<<5) /* focus on caller */ }; TAILQ_HEAD(quirk_list, quirk); struct quirk_list quirks = TAILQ_HEAD_INITIALIZER(quirks); /* * Supported EWMH hints should be added to * both the enum and the ewmh array */ enum { _NET_ACTIVE_WINDOW, _NET_MOVERESIZE_WINDOW, _NET_CLOSE_WINDOW, _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DOCK, _NET_WM_WINDOW_TYPE_TOOLBAR, _NET_WM_WINDOW_TYPE_UTILITY, _NET_WM_WINDOW_TYPE_SPLASH, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_WINDOW_TYPE_NORMAL, _NET_WM_STATE, _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_SKIP_TASKBAR, _NET_WM_STATE_SKIP_PAGER, _NET_WM_STATE_HIDDEN, _NET_WM_STATE_ABOVE, _SWM_WM_STATE_MANUAL, _NET_WM_STATE_FULLSCREEN, _NET_WM_ALLOWED_ACTIONS, _NET_WM_ACTION_MOVE, _NET_WM_ACTION_RESIZE, _NET_WM_ACTION_FULLSCREEN, _NET_WM_ACTION_CLOSE, SWM_EWMH_HINT_MAX }; struct ewmh_hint { char *name; Atom atom; } ewmh[SWM_EWMH_HINT_MAX] = { /* must be in same order as in the enum */ {"_NET_ACTIVE_WINDOW", None}, {"_NET_MOVERESIZE_WINDOW", None}, {"_NET_CLOSE_WINDOW", None}, {"_NET_WM_WINDOW_TYPE", None}, {"_NET_WM_WINDOW_TYPE_DOCK", None}, {"_NET_WM_WINDOW_TYPE_TOOLBAR", None}, {"_NET_WM_WINDOW_TYPE_UTILITY", None}, {"_NET_WM_WINDOW_TYPE_SPLASH", None}, {"_NET_WM_WINDOW_TYPE_DIALOG", None}, {"_NET_WM_WINDOW_TYPE_NORMAL", None}, {"_NET_WM_STATE", None}, {"_NET_WM_STATE_MAXIMIZED_HORZ", None}, {"_NET_WM_STATE_MAXIMIZED_VERT", None}, {"_NET_WM_STATE_SKIP_TASKBAR", None}, {"_NET_WM_STATE_SKIP_PAGER", None}, {"_NET_WM_STATE_HIDDEN", None}, {"_NET_WM_STATE_ABOVE", None}, {"_SWM_WM_STATE_MANUAL", None}, {"_NET_WM_STATE_FULLSCREEN", None}, {"_NET_WM_ALLOWED_ACTIONS", None}, {"_NET_WM_ACTION_MOVE", None}, {"_NET_WM_ACTION_RESIZE", None}, {"_NET_WM_ACTION_FULLSCREEN", None}, {"_NET_WM_ACTION_CLOSE", None}, }; void store_float_geom(struct ws_win *, struct swm_region *); int floating_toggle_win(struct ws_win *); void spawn_select(struct swm_region *, union arg *, char *, int *); unsigned char *get_win_name(Window); int get_property(Window id, Atom atom, long count, Atom type, unsigned long *nitems, unsigned long *nbytes, unsigned char **data) { int format, status; unsigned long *nbytes_ret, *nitems_ret; unsigned long nbytes_tmp, nitems_tmp; Atom real; nbytes_ret = nbytes != NULL ? nbytes : &nbytes_tmp; nitems_ret = nitems != NULL ? nitems : &nitems_tmp; status = XGetWindowProperty(display, id, atom, 0L, count, False, type, &real, &format, nitems_ret, nbytes_ret, data); if (status != Success) return False; if (real != type) return False; return True; } void update_iconic(struct ws_win *win, int newv) { int32_t v = newv; Atom iprop; win->iconic = newv; iprop = XInternAtom(display, "_SWM_ICONIC", False); if (!iprop) return; if (newv) XChangeProperty(display, win->id, iprop, XA_INTEGER, 32, PropModeReplace, (unsigned char *)&v, 1); else XDeleteProperty(display, win->id, iprop); } int get_iconic(struct ws_win *win) { int32_t v = 0; int retfmt, status; Atom iprop, rettype; unsigned long nitems, extra; unsigned char *prop = NULL; iprop = XInternAtom(display, "_SWM_ICONIC", False); if (!iprop) goto out; status = XGetWindowProperty(display, win->id, iprop, 0L, 1L, False, XA_INTEGER, &rettype, &retfmt, &nitems, &extra, &prop); if (status != Success) goto out; if (rettype != XA_INTEGER || retfmt != 32) goto out; if (nitems != 1) goto out; v = *((int32_t *)prop); out: if (prop != NULL) XFree(prop); return (v); } void setup_ewmh(void) { int i,j; Atom sup_list; sup_list = XInternAtom(display, "_NET_SUPPORTED", False); for (i = 0; i < LENGTH(ewmh); i++) ewmh[i].atom = XInternAtom(display, ewmh[i].name, False); for (i = 0; i < ScreenCount(display); i++) { /* Support check window will be created by workaround(). */ /* Report supported atoms */ XDeleteProperty(display, screens[i].root, sup_list); for (j = 0; j < LENGTH(ewmh); j++) XChangeProperty(display, screens[i].root, sup_list, XA_ATOM, 32, PropModeAppend, (unsigned char *)&ewmh[j].atom,1); } } void teardown_ewmh(void) { int i, success; unsigned char *data = NULL; unsigned long n; Atom sup_check, sup_list; Window id; sup_check = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False); sup_list = XInternAtom(display, "_NET_SUPPORTED", False); for (i = 0; i < ScreenCount(display); i++) { /* Get the support check window and destroy it */ success = get_property(screens[i].root, sup_check, 1, XA_WINDOW, &n, NULL, &data); if (success) { id = data[0]; XDestroyWindow(display, id); XDeleteProperty(display, screens[i].root, sup_check); XDeleteProperty(display, screens[i].root, sup_list); } XFree(data); } } void ewmh_autoquirk(struct ws_win *win) { int success, i; unsigned long *data = NULL, n; Atom type; success = get_property(win->id, ewmh[_NET_WM_WINDOW_TYPE].atom, (~0L), XA_ATOM, &n, NULL, (void *)&data); if (!success) { XFree(data); return; } for (i = 0; i < n; i++) { type = data[i]; if (type == ewmh[_NET_WM_WINDOW_TYPE_NORMAL].atom) break; if (type == ewmh[_NET_WM_WINDOW_TYPE_DOCK].atom || type == ewmh[_NET_WM_WINDOW_TYPE_TOOLBAR].atom || type == ewmh[_NET_WM_WINDOW_TYPE_UTILITY].atom) { win->floating = 1; win->quirks = SWM_Q_FLOAT | SWM_Q_ANYWHERE; break; } if (type == ewmh[_NET_WM_WINDOW_TYPE_SPLASH].atom || type == ewmh[_NET_WM_WINDOW_TYPE_DIALOG].atom) { win->floating = 1; win->quirks = SWM_Q_FLOAT; break; } } XFree(data); } #define SWM_EWMH_ACTION_COUNT_MAX (6) #define EWMH_F_FULLSCREEN (1<<0) #define EWMH_F_ABOVE (1<<1) #define EWMH_F_HIDDEN (1<<2) #define EWMH_F_SKIP_PAGER (1<<3) #define EWMH_F_SKIP_TASKBAR (1<<4) #define SWM_F_MANUAL (1<<5) int ewmh_set_win_fullscreen(struct ws_win *win, int fs) { struct swm_geometry rg; if (!win->ws->r) return 0; if (!win->floating) return 0; DNPRINTF(SWM_D_MISC, "ewmh_set_win_fullscreen: window: 0x%lx, " "fullscreen %s\n", win->id, YESNO(fs)); rg = win->ws->r->g; if (fs) { store_float_geom(win, win->ws->r); win->g = rg; } else { if (win->g_floatvalid) { /* refloat at last floating relative position */ X(win) = win->g_float.x - win->rg_float.x + rg.x; Y(win) = win->g_float.y - win->rg_float.y + rg.y; WIDTH(win) = win->g_float.w; HEIGHT(win) = win->g_float.h; } } return 1; } void ewmh_update_actions(struct ws_win *win) { Atom actions[SWM_EWMH_ACTION_COUNT_MAX]; int n = 0; if (win == NULL) return; actions[n++] = ewmh[_NET_WM_ACTION_CLOSE].atom; if (win->floating) { actions[n++] = ewmh[_NET_WM_ACTION_MOVE].atom; actions[n++] = ewmh[_NET_WM_ACTION_RESIZE].atom; } XChangeProperty(display, win->id, ewmh[_NET_WM_ALLOWED_ACTIONS].atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)actions, n); } #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_update_win_state(struct ws_win *win, long state, long action) { unsigned int mask = 0; unsigned int changed = 0; unsigned int orig_flags; if (win == NULL) return; if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom) mask = EWMH_F_FULLSCREEN; if (state == ewmh[_NET_WM_STATE_ABOVE].atom) mask = EWMH_F_ABOVE; if (state == ewmh[_SWM_WM_STATE_MANUAL].atom) mask = SWM_F_MANUAL; if (state == ewmh[_NET_WM_STATE_SKIP_PAGER].atom) mask = EWMH_F_SKIP_PAGER; if (state == ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom) mask = EWMH_F_SKIP_TASKBAR; orig_flags = win->ewmh_flags; switch (action) { case _NET_WM_STATE_REMOVE: win->ewmh_flags &= ~mask; break; case _NET_WM_STATE_ADD: win->ewmh_flags |= mask; break; case _NET_WM_STATE_TOGGLE: win->ewmh_flags ^= mask; break; } changed = (win->ewmh_flags & mask) ^ (orig_flags & mask) ? 1 : 0; if (state == ewmh[_NET_WM_STATE_ABOVE].atom) if (changed) if (!floating_toggle_win(win)) win->ewmh_flags = orig_flags; /* revert */ if (state == ewmh[_SWM_WM_STATE_MANUAL].atom) if (changed) win->manual = (win->ewmh_flags & SWM_F_MANUAL) != 0; if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom) if (changed) if (!ewmh_set_win_fullscreen(win, win->ewmh_flags & EWMH_F_FULLSCREEN)) win->ewmh_flags = orig_flags; /* revert */ XDeleteProperty(display, win->id, ewmh[_NET_WM_STATE].atom); if (win->ewmh_flags & EWMH_F_FULLSCREEN) XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom, XA_ATOM, 32, PropModeAppend, (unsigned char *)&ewmh[_NET_WM_STATE_FULLSCREEN].atom, 1); if (win->ewmh_flags & EWMH_F_SKIP_PAGER) XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom, XA_ATOM, 32, PropModeAppend, (unsigned char *)&ewmh[_NET_WM_STATE_SKIP_PAGER].atom, 1); if (win->ewmh_flags & EWMH_F_SKIP_TASKBAR) XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom, XA_ATOM, 32, PropModeAppend, (unsigned char *)&ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom, 1); if (win->ewmh_flags & EWMH_F_ABOVE) XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom, XA_ATOM, 32, PropModeAppend, (unsigned char *)&ewmh[_NET_WM_STATE_ABOVE].atom, 1); if (win->ewmh_flags & SWM_F_MANUAL) XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom, XA_ATOM, 32, PropModeAppend, (unsigned char *)&ewmh[_SWM_WM_STATE_MANUAL].atom, 1); } void ewmh_get_win_state(struct ws_win *win) { int success, i; unsigned long n; Atom *states; if (win == NULL) return; win->ewmh_flags = 0; if (win->floating) win->ewmh_flags |= EWMH_F_ABOVE; if (win->manual) win->ewmh_flags |= SWM_F_MANUAL; success = get_property(win->id, ewmh[_NET_WM_STATE].atom, (~0L), XA_ATOM, &n, NULL, (void *)&states); if (!success) return; for (i = 0; i < n; i++) ewmh_update_win_state(win, states[i], _NET_WM_STATE_ADD); XFree(states); } /* events */ #ifdef SWM_DEBUG char * geteventname(XEvent *e) { char *name = NULL; switch (e->type) { case KeyPress: name = "KeyPress"; break; case KeyRelease: name = "KeyRelease"; break; case ButtonPress: name = "ButtonPress"; break; case ButtonRelease: name = "ButtonRelease"; break; case MotionNotify: name = "MotionNotify"; break; case EnterNotify: name = "EnterNotify"; break; case LeaveNotify: name = "LeaveNotify"; break; case FocusIn: name = "FocusIn"; break; case FocusOut: name = "FocusOut"; break; case KeymapNotify: name = "KeymapNotify"; break; case Expose: name = "Expose"; break; case GraphicsExpose: name = "GraphicsExpose"; break; case NoExpose: name = "NoExpose"; break; case VisibilityNotify: name = "VisibilityNotify"; break; case CreateNotify: name = "CreateNotify"; break; case DestroyNotify: name = "DestroyNotify"; break; case UnmapNotify: name = "UnmapNotify"; break; case MapNotify: name = "MapNotify"; break; case MapRequest: name = "MapRequest"; break; case ReparentNotify: name = "ReparentNotify"; break; case ConfigureNotify: name = "ConfigureNotify"; break; case ConfigureRequest: name = "ConfigureRequest"; break; case GravityNotify: name = "GravityNotify"; break; case ResizeRequest: name = "ResizeRequest"; break; case CirculateNotify: name = "CirculateNotify"; break; case CirculateRequest: name = "CirculateRequest"; break; case PropertyNotify: name = "PropertyNotify"; break; case SelectionClear: name = "SelectionClear"; break; case SelectionRequest: name = "SelectionRequest"; break; case SelectionNotify: name = "SelectionNotify"; break; case ColormapNotify: name = "ColormapNotify"; break; case ClientMessage: name = "ClientMessage"; break; case MappingNotify: name = "MappingNotify"; break; default: name = "Unknown"; } return name; } char * xrandr_geteventname(XEvent *e) { char *name = NULL; switch(e->type - xrandr_eventbase) { case RRScreenChangeNotify: name = "RRScreenChangeNotify"; break; default: name = "Unknown"; } return name; } void dumpwins(struct swm_region *r, union arg *args) { struct ws_win *win; unsigned int state; XWindowAttributes wa; if (r->ws == NULL) { warnx("dumpwins: invalid workspace"); return; } warnx("=== managed window list ws %02d ===", r->ws->idx); TAILQ_FOREACH(win, &r->ws->winlist, entry) { state = getstate(win->id); if (!XGetWindowAttributes(display, win->id, &wa)) warnx("window: 0x%lx, failed XGetWindowAttributes", win->id); warnx("window: 0x%lx, map_state: %d, state: %d, " "transient: 0x%lx", win->id, wa.map_state, state, win->transient); } warnx("===== unmanaged window list ====="); TAILQ_FOREACH(win, &r->ws->unmanagedlist, entry) { state = getstate(win->id); if (!XGetWindowAttributes(display, win->id, &wa)) warnx("window: 0x%lx, failed XGetWindowAttributes", win->id); warnx("window: 0x%lx, map_state: %d, state: %d, " "transient: 0x%lx", win->id, wa.map_state, state, win->transient); } warnx("================================="); } #else void dumpwins(struct swm_region *r, union arg *args) { } #endif /* SWM_DEBUG */ void expose(XEvent *); void keypress(XEvent *); void buttonpress(XEvent *); void configurerequest(XEvent *); void configurenotify(XEvent *); void destroynotify(XEvent *); void enternotify(XEvent *); void focusevent(XEvent *); void mapnotify(XEvent *); void mappingnotify(XEvent *); void maprequest(XEvent *); void propertynotify(XEvent *); void unmapnotify(XEvent *); void visibilitynotify(XEvent *); void clientmessage(XEvent *); void (*handler[LASTEvent])(XEvent *) = { [Expose] = expose, [KeyPress] = keypress, [ButtonPress] = buttonpress, [ConfigureRequest] = configurerequest, [ConfigureNotify] = configurenotify, [DestroyNotify] = destroynotify, [EnterNotify] = enternotify, [FocusIn] = focusevent, [FocusOut] = focusevent, [MapNotify] = mapnotify, [MappingNotify] = mappingnotify, [MapRequest] = maprequest, [PropertyNotify] = propertynotify, [UnmapNotify] = unmapnotify, [VisibilityNotify] = visibilitynotify, [ClientMessage] = clientmessage, }; 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(long pid) { struct pid_e *p = NULL; DNPRINTF(SWM_D_MISC, "find_pid: %lu\n", pid); if (pid == 0) return (NULL); TAILQ_FOREACH(p, &pidlist, entry) { if (p->pid == pid) return (p); } return (NULL); } unsigned long name_to_color(char *colorname) { Colormap cmap; Status status; XColor screen_def, exact_def; unsigned long result = 0; char cname[32] = "#"; cmap = DefaultColormap(display, screens[0].idx); status = XAllocNamedColor(display, cmap, colorname, &screen_def, &exact_def); if (!status) { strlcat(cname, colorname + 2, sizeof cname - 1); status = XAllocNamedColor(display, cmap, cname, &screen_def, &exact_def); } if (status) result = screen_def.pixel; else warnx("color '%s' not found", colorname); return (result); } void setscreencolor(char *val, int i, int c) { if (i > 0 && i <= ScreenCount(display)) { screens[i - 1].c[c].color = name_to_color(val); free(screens[i - 1].c[c].name); if ((screens[i - 1].c[c].name = strdup(val)) == NULL) err(1, "strdup"); } else if (i == -1) { for (i = 0; i < ScreenCount(display); i++) { screens[i].c[c].color = name_to_color(val); free(screens[i].c[c].name); if ((screens[i].c[c].name = strdup(val)) == NULL) err(1, "strdup"); } } else errx(1, "invalid screen index: %d out of bounds (maximum %d)", i, ScreenCount(display)); } 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); 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); if (ws->cur_layout->l_stack == horizontal_stack) strlcpy(ws->stacker, ws->l_state.horizontal_flip ? "[v]" : "[-]", sizeof ws->stacker); } void custom_region(char *val) { unsigned int sidx, x, y, w, h; if (sscanf(val, "screen[%u]:%ux%u+%u+%u", &sidx, &w, &h, &x, &y) != 5) errx(1, "invalid custom region, " "should be 'screen[]:x++"); if (sidx < 1 || sidx > ScreenCount(display)) errx(1, "invalid screen index: %d out of bounds (maximum %d)", sidx, ScreenCount(display)); sidx--; if (w < 1 || h < 1) errx(1, "region %ux%u+%u+%u too small", w, h, x, y); if (x > DisplayWidth(display, sidx) || y > DisplayHeight(display, sidx) || w + x > DisplayWidth(display, sidx) || h + y > DisplayHeight(display, sidx)) { warnx("ignoring region %ux%u+%u+%u - not within screen " "boundaries (%ux%u)", w, h, x, y, DisplayWidth(display, sidx), DisplayHeight(display, sidx)); 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(struct swm_region *r, char *s) { int x = 0; size_t len; XRectangle ibox, lbox; XClearWindow(display, r->bar_window); 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; DRAWSTRING(display, r->bar_window, bar_fs, r->s->bar_gc, x, (bar_fs_extents->max_logical_extent.height - lbox.height) / 2 - lbox.y, s, len); } 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((char *)bar_ext, "", sizeof bar_ext); bar_extra = 0; } void bar_class_name(char *s, ssize_t sz, struct ws_win *cur_focus) { int do_class, do_name; Status status; XClassHint *xch = NULL; if ((title_name_enabled == 1 || title_class_enabled == 1) && cur_focus != NULL) { if ((xch = XAllocClassHint()) == NULL) goto out; status = XGetClassHint(display, cur_focus->id, xch); if (status == BadWindow || status == BadAlloc) goto out; do_class = (title_class_enabled && xch->res_class != NULL); do_name = (title_name_enabled && xch->res_name != NULL); if (do_class) strlcat(s, xch->res_class, sz); if (do_class && do_name) strlcat(s, ":", sz); if (do_name) strlcat(s, xch->res_name, sz); strlcat(s, " ", sz); } out: if (xch) { XFree(xch->res_name); XFree(xch->res_class); XFree(xch); } } void bar_window_name(char *s, ssize_t sz, struct ws_win *cur_focus) { unsigned char *title; if (window_name_enabled && cur_focus != NULL) { title = get_win_name(cur_focus->id); if (title != NULL) { DNPRINTF(SWM_D_BAR, "bar_window_name: title: %s\n", title); if (cur_focus->floating) strlcat(s, "(f) ", sz); strlcat(s, (char *)title, sz); strlcat(s, " ", sz); XFree(title); } } } int urgent[SWM_WS_MAX]; void bar_urgent(char *s, ssize_t sz) { XWMHints *wmh = NULL; struct ws_win *win; int i, j; char b[8]; if (urgent_enabled == 0) return; for (i = 0; i < SWM_WS_MAX; i++) urgent[i] = 0; for (i = 0; i < ScreenCount(display); i++) for (j = 0; j < SWM_WS_MAX; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) { wmh = XGetWMHints(display, win->id); if (wmh == NULL) continue; if (wmh->flags & XUrgencyHint) urgent[j] = 1; XFree(wmh); } strlcat(s, "* ", sz); for (i = 0; i < SWM_WS_MAX; i++) { if (urgent[i]) snprintf(b, sizeof b, "%d ", i + 1); else snprintf(b, sizeof b, "- "); strlcat(s, b, sz); } strlcat(s, "* ", sz); } void bar_update(void) { time_t tmt; struct tm tm; struct swm_region *r; int i, x; size_t len; char ws[SWM_BAR_MAX]; char s[SWM_BAR_MAX]; unsigned char cn[SWM_BAR_MAX]; char loc[SWM_BAR_MAX]; char *b, *stack = ""; if (bar_enabled == 0) return; if (bar_extra && bar_extra_running) { /* ignore short reads; it'll correct itself */ while ((b = fgetln(stdin, &len)) != NULL) if (b && b[len - 1] == '\n') { b[len - 1] = '\0'; strlcpy((char *)bar_ext, b, sizeof bar_ext); } if (b == NULL && errno != EAGAIN) { warn("bar_update: bar_extra failed"); bar_extra_stop(); } } else strlcpy((char *)bar_ext, "", sizeof bar_ext); if (clock_enabled == 0) strlcpy(s, "", sizeof s); else { time(&tmt); localtime_r(&tmt, &tm); len = strftime(s, sizeof s, clock_format, &tm); s[len] = '\0'; strlcat(s, " ", sizeof s); } for (i = 0; i < ScreenCount(display); i++) { x = 1; TAILQ_FOREACH(r, &screens[i].rl, entry) { strlcpy((char *)cn, "", sizeof cn); strlcpy(ws, "", sizeof ws); if (r && r->ws) { bar_urgent((char *)cn, sizeof cn); bar_class_name((char *)cn, sizeof cn, r->ws->focus); bar_window_name((char *)cn, sizeof cn, r->ws->focus); if (r->ws->name) snprintf(ws, sizeof ws, "<%s>", r->ws->name); if (stack_enabled) stack = r->ws->stacker; snprintf(loc, sizeof loc, "%d:%d %s %s %s%s %s %s", x++, r->ws->idx + 1, stack, ws, s, cn, bar_ext, bar_vertext); bar_print(r, loc); } } } alarm(bar_delay); } void bar_check_opts(void) { if (title_class_enabled || title_name_enabled || window_name_enabled) bar_update(); } void bar_signal(int sig) { bar_alarm = 1; } void bar_toggle(struct swm_region *r, union arg *args) { struct swm_region *tmpr; int i, sc = ScreenCount(display); DNPRINTF(SWM_D_BAR, "bar_toggle\n"); if (bar_enabled) for (i = 0; i < sc; i++) TAILQ_FOREACH(tmpr, &screens[i].rl, entry) XUnmapWindow(display, tmpr->bar_window); else for (i = 0; i < sc; i++) TAILQ_FOREACH(tmpr, &screens[i].rl, entry) XMapRaised(display, tmpr->bar_window); bar_enabled = !bar_enabled; stack(); /* must be after stack */ bar_update(); } void bar_refresh(void) { XSetWindowAttributes wa; struct swm_region *r; int i; /* do this here because the conf file is in memory */ if (bar_extra && bar_extra_running == 0 && bar_argv[0]) { /* launch external status app */ bar_extra_running = 1; if (pipe(bar_pipe) == -1) err(1, "pipe error"); socket_setnonblock(bar_pipe[0]); socket_setnonblock(bar_pipe[1]); /* XXX hmmm, really? */ if (dup2(bar_pipe[0], 0) == -1) err(1, "dup2"); if (dup2(bar_pipe[1], 1) == -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; } } bzero(&wa, sizeof wa); for (i = 0; i < ScreenCount(display); i++) TAILQ_FOREACH(r, &screens[i].rl, entry) { wa.border_pixel = screens[i].c[SWM_S_COLOR_BAR_BORDER].color; wa.background_pixel = screens[i].c[SWM_S_COLOR_BAR].color; XChangeWindowAttributes(display, r->bar_window, CWBackPixel | CWBorderPixel, &wa); } bar_update(); } void bar_setup(struct swm_region *r) { char *default_string; char **missing_charsets; int num_missing_charsets = 0; int i, x, y; if (bar_fs) { XFreeFontSet(display, bar_fs); bar_fs = NULL; } DNPRINTF(SWM_D_BAR, "bar_setup: 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; x = X(r); y = bar_at_bottom ? (Y(r) + HEIGHT(r) - bar_height) : Y(r); r->bar_window = XCreateSimpleWindow(display, r->s->root, x, y, WIDTH(r) - 2 * bar_border_width, bar_height - 2 * bar_border_width, bar_border_width, r->s->c[SWM_S_COLOR_BAR_BORDER].color, r->s->c[SWM_S_COLOR_BAR].color); XSelectInput(display, r->bar_window, VisibilityChangeMask); if (bar_enabled) XMapRaised(display, r->bar_window); DNPRINTF(SWM_D_BAR, "bar_setup: bar_window: 0x%lx\n", r->bar_window); if (signal(SIGALRM, bar_signal) == SIG_ERR) err(1, "could not install bar_signal"); bar_refresh(); } void drain_enter_notify(void) { int i = 0; XEvent cne; while (XCheckMaskEvent(display, EnterWindowMask, &cne)) i++; DNPRINTF(SWM_D_MISC, "drain_enter_notify: drained: %d\n", i); } void set_win_state(struct ws_win *win, long state) { long data[] = {state, None}; DNPRINTF(SWM_D_EVENT, "set_win_state: window: 0x%lx\n", win->id); if (win == NULL) return; XChangeProperty(display, win->id, astate, astate, 32, PropModeReplace, (unsigned char *)data, 2); } long getstate(Window w) { long result = -1; unsigned char *p = NULL; unsigned long n; if (!get_property(w, astate, 2L, astate, &n, NULL, &p)) return (-1); if (n != 0) result = *((long *)p); XFree(p); return (result); } void version(struct swm_region *r, union arg *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); bar_update(); } void client_msg(struct ws_win *win, Atom a) { XClientMessageEvent cm; if (win == NULL) return; bzero(&cm, sizeof cm); cm.type = ClientMessage; cm.window = win->id; cm.message_type = aprot; cm.format = 32; cm.data.l[0] = a; cm.data.l[1] = CurrentTime; XSendEvent(display, win->id, False, 0L, (XEvent *)&cm); } /* synthetic response to a ConfigureRequest when not making a change */ void config_win(struct ws_win *win, XConfigureRequestEvent *ev) { XConfigureEvent ce; if (win == NULL) return; /* send notification of unchanged state. */ ce.type = ConfigureNotify; ce.x = X(win); ce.y = Y(win); ce.width = WIDTH(win); ce.height = HEIGHT(win); ce.override_redirect = False; if (ev == NULL) { /* EWMH */ ce.display = display; ce.event = win->id; ce.window = win->id; ce.border_width = border_width; ce.above = None; } else { /* normal */ ce.display = ev->display; ce.event = ev->window; ce.window = ev->window; /* make response appear more WM_SIZE_HINTS-compliant */ if (win->sh_mask) DNPRINTF(SWM_D_MISC, "config_win: hints: window: 0x%lx," " sh_mask: %ld, min: %d x %d, max: %d x %d, inc: " "%d x %d\n", win->id, win->sh_mask, 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 += border_width - ev->border_width; ce.y += border_width - ev->border_width; ce.border_width = ev->border_width; ce.above = ev->above; } DNPRINTF(SWM_D_MISC, "config_win: ewmh: %s, window: 0x%lx, (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); XSendEvent(display, win->id, False, StructureNotifyMask, (XEvent *)&ce); } int count_win(struct workspace *ws, int count_transient) { struct ws_win *win; int count = 0; TAILQ_FOREACH(win, &ws->winlist, entry) { if (count_transient == 0 && win->floating) continue; if (count_transient == 0 && win->transient) continue; if (win->iconic) continue; count++; } DNPRINTF(SWM_D_MISC, "count_win: %d\n", count); return (count); } void quit(struct swm_region *r, union arg *args) { DNPRINTF(SWM_D_MISC, "quit\n"); running = 0; } void unmap_window(struct ws_win *win) { if (win == NULL) return; /* don't unmap again */ if (getstate(win->id) == IconicState) return; set_win_state(win, IconicState); XUnmapWindow(display, win->id); XSetWindowBorder(display, win->id, win->s->c[SWM_S_COLOR_UNFOCUS].color); } void unmap_all(void) { struct ws_win *win; int i, j; for (i = 0; i < ScreenCount(display); i++) for (j = 0; j < SWM_WS_MAX; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) unmap_window(win); } void fake_keypress(struct ws_win *win, int keysym, int modifiers) { XKeyEvent event; if (win == NULL) return; event.display = display; /* Ignored, but what the hell */ event.window = win->id; event.root = win->s->root; event.subwindow = None; event.time = CurrentTime; event.x = X(win); event.y = Y(win); event.x_root = 1; event.y_root = 1; event.same_screen = True; event.keycode = XKeysymToKeycode(display, keysym); event.state = modifiers; event.type = KeyPress; XSendEvent(event.display, event.window, True, KeyPressMask, (XEvent *)&event); event.type = KeyRelease; XSendEvent(event.display, event.window, True, KeyPressMask, (XEvent *)&event); } void restart(struct swm_region *r, union arg *args) { DNPRINTF(SWM_D_MISC, "restart: %s\n", start_argv[0]); /* 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(); bar_extra = 1; unmap_all(); XCloseDisplay(display); execvp(start_argv[0], start_argv); warn("execvp failed"); quit(NULL, NULL); } struct swm_region * root_to_region(Window root) { struct swm_region *r = NULL; Window rr, cr; int i, x, y, wx, wy; unsigned int mask; for (i = 0; i < ScreenCount(display); i++) if (screens[i].root == root) break; if (XQueryPointer(display, screens[i].root, &rr, &cr, &x, &y, &wx, &wy, &mask) != False) { /* choose a region based on pointer location */ TAILQ_FOREACH(r, &screens[i].rl, entry) if (x >= X(r) && x <= X(r) + WIDTH(r) && y >= Y(r) && y <= Y(r) + HEIGHT(r)) break; } if (r == NULL) r = TAILQ_FIRST(&screens[i].rl); return (r); } struct ws_win * find_unmanaged_window(Window id) { struct ws_win *win; int i, j; for (i = 0; i < ScreenCount(display); i++) for (j = 0; j < SWM_WS_MAX; j++) TAILQ_FOREACH(win, &screens[i].ws[j].unmanagedlist, entry) if (id == win->id) return (win); return (NULL); } struct ws_win * find_window(Window id) { struct ws_win *win; Window wrr, wpr, *wcr = NULL; int i, j; unsigned int nc; for (i = 0; i < ScreenCount(display); i++) for (j = 0; j < SWM_WS_MAX; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) if (id == win->id) return (win); /* if we were looking for the parent return that window instead */ if (XQueryTree(display, id, &wrr, &wpr, &wcr, &nc) == 0) return (NULL); if (wcr) XFree(wcr); /* ignore not found and root */ if (wpr == 0 || wrr == wpr) return (NULL); /* look for parent */ for (i = 0; i < ScreenCount(display); i++) for (j = 0; j < SWM_WS_MAX; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) if (wpr == win->id) return (win); return (NULL); } void spawn(int ws_idx, union arg *args, int close_fd) { int fd; char *ret = NULL; DNPRINTF(SWM_D_MISC, "spawn: %s\n", args->argv[0]); if (display) close(ConnectionNumber(display)); 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); } execvp(args->argv[0], args->argv); warn("spawn: execvp"); _exit(1); } void spawnterm(struct swm_region *r, union arg *args) { DNPRINTF(SWM_D_MISC, "spawnterm\n"); if (fork() == 0) { if (term_width) setenv("_SWM_XTERM_FONTADJ", "", 1); spawn(r->ws->idx, args, 1); } } void kill_refs(struct ws_win *win) { int i, x; struct swm_region *r; struct workspace *ws; if (win == NULL) return; for (i = 0; i < ScreenCount(display); i++) TAILQ_FOREACH(r, &screens[i].rl, entry) for (x = 0; x < SWM_WS_MAX; x++) { ws = &r->s->ws[x]; if (win == ws->focus) ws->focus = NULL; if (win == ws->focus_prev) ws->focus_prev = NULL; } } int validate_win(struct ws_win *testwin) { struct ws_win *win; struct workspace *ws; struct swm_region *r; int i, x; if (testwin == NULL) return (0); for (i = 0; i < ScreenCount(display); i++) TAILQ_FOREACH(r, &screens[i].rl, entry) for (x = 0; x < SWM_WS_MAX; 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; /* validate all ws */ for (i = 0; i < ScreenCount(display); i++) TAILQ_FOREACH(r, &screens[i].rl, entry) for (x = 0; x < SWM_WS_MAX; x++) { ws = &r->s->ws[x]; if (ws == testws) return (0); } return (1); } void unfocus_win(struct ws_win *win) { XEvent cne; Window none = None; DNPRINTF(SWM_D_FOCUS, "unfocus_win: window: 0x%lx\n", WINID(win)); if (win == NULL) return; if (win->ws == NULL) return; if (validate_ws(win->ws)) return; /* XXX this gets hit with thunderbird, needs fixing */ if (win->ws->r == NULL) return; if (validate_win(win)) { kill_refs(win); return; } if (win->ws->focus == win) { win->ws->focus = NULL; win->ws->focus_prev = 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; } /* drain all previous unfocus events */ while (XCheckTypedEvent(display, FocusOut, &cne) == True) ; grabbuttons(win, 0); XSetWindowBorder(display, win->id, win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color); XChangeProperty(display, win->s->root, ewmh[_NET_ACTIVE_WINDOW].atom, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&none,1); } void unfocus_all(void) { struct ws_win *win; int i, j; DNPRINTF(SWM_D_FOCUS, "unfocus_all\n"); for (i = 0; i < ScreenCount(display); i++) for (j = 0; j < SWM_WS_MAX; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) unfocus_win(win); } void focus_win(struct ws_win *win) { XEvent cne; Window cur_focus; int rr; struct ws_win *cfw = NULL; DNPRINTF(SWM_D_FOCUS, "focus_win: window: 0x%lx\n", WINID(win)); if (win == NULL) return; if (win->ws == NULL) return; if (validate_ws(win->ws)) return; /* XXX this gets hit with thunderbird, needs fixing */ if (validate_win(win)) { kill_refs(win); return; } if (validate_win(win)) { kill_refs(win); return; } XGetInputFocus(display, &cur_focus, &rr); if ((cfw = find_window(cur_focus)) != NULL) unfocus_win(cfw); else { /* use larger hammer since the window was killed somehow */ TAILQ_FOREACH(cfw, &win->ws->winlist, entry) if (cfw->ws && cfw->ws->r && cfw->ws->r->s) XSetWindowBorder(display, cfw->id, cfw->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color); } win->ws->focus = win; if (win->ws->r != NULL) { /* drain all previous focus events */ while (XCheckTypedEvent(display, FocusIn, &cne) == True) ; if (win->java == 0) XSetInputFocus(display, win->id, RevertToParent, CurrentTime); grabbuttons(win, 1); XSetWindowBorder(display, win->id, win->ws->r->s->c[SWM_S_COLOR_FOCUS].color); if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS || win->ws->always_raise) XMapRaised(display, win->id); XChangeProperty(display, win->s->root, ewmh[_NET_ACTIVE_WINDOW].atom, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&win->id,1); } bar_check_opts(); } void switchws(struct swm_region *r, union arg *args) { int wsid = args->id, unmap_old = 0; struct swm_region *this_r, *other_r; struct ws_win *win; struct workspace *new_ws, *old_ws; union arg a; if (!(r && r->s)) 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 == NULL) { /* the other workspace is hidden, hide this one */ old_ws->r = NULL; unmap_old = 1; } else { /* 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; } this_r->ws_prior = old_ws; this_r->ws = new_ws; new_ws->r = this_r; /* this is needed so that we can click on a window after a restart */ unfocus_all(); stack(); a.id = SWM_ARG_ID_FOCUSCUR; focus(new_ws->r, &a); bar_update(); /* unmap old windows */ if (unmap_old) TAILQ_FOREACH(win, &old_ws->winlist, entry) unmap_window(win); if (focus_mode == SWM_FOCUS_DEFAULT) drain_enter_notify(); } void cyclews(struct swm_region *r, union arg *args) { union arg a; struct swm_screen *s = r->s; int cycle_all = 0; 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_UP_ALL: cycle_all = 1; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_UP: if (a.id < SWM_WS_MAX - 1) a.id++; else a.id = 0; break; case SWM_ARG_ID_CYCLEWS_DOWN_ALL: cycle_all = 1; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_DOWN: if (a.id > 0) a.id--; else a.id = SWM_WS_MAX - 1; break; default: return; }; if (!cycle_all && (cycle_empty == 0 && TAILQ_EMPTY(&s->ws[a.id].winlist))) continue; if (cycle_visible == 0 && s->ws[a.id].r != NULL) continue; switchws(r, &a); } while (a.id != r->ws->idx); } void priorws(struct swm_region *r, union arg *args) { union arg a; 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(r, &a); } void cyclescr(struct swm_region *r, union arg *args) { struct swm_region *rr = NULL; union arg a; int i, x, y; /* do nothing if we don't have more than one screen */ if (!(ScreenCount(display) > 1 || outputs > 1)) return; i = r->s->idx; switch (args->id) { case SWM_ARG_ID_CYCLESC_UP: rr = TAILQ_NEXT(r, entry); if (rr == NULL) rr = TAILQ_FIRST(&screens[i].rl); break; case SWM_ARG_ID_CYCLESC_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; /* move mouse to region */ x = X(rr) + 1; y = Y(rr) + 1 + (bar_enabled ? bar_height : 0); XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, x, y); a.id = SWM_ARG_ID_FOCUSCUR; focus(rr, &a); if (rr->ws->focus) { /* move to focus window */ x = X(rr->ws->focus) + 1; y = Y(rr->ws->focus) + 1; XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, x, y); } } 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 (win->transient) { 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 swm_region *r, union arg *args) { struct ws_win *target, *source; struct ws_win *cur_focus; struct ws_win_list *wl; 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) return; source = cur_focus; wl = &source->ws->winlist; switch (args->id) { case SWM_ARG_ID_SWAPPREV: if (source->transient) 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 (source->transient) 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); stack(); } void 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; DNPRINTF(SWM_D_FOCUS, "focus_prev: window: 0x%lx\n", WINID(win)); if (!(win && win->ws)) return; ws = win->ws; wl = &ws->winlist; cur_focus = ws->focus; /* pickle, just focus on whatever */ if (cur_focus == NULL) { /* use prev_focus if valid */ if (ws->focus_prev && ws->focus_prev != cur_focus && find_window(WINID(ws->focus_prev))) winfocus = ws->focus_prev; if (winfocus == NULL) winfocus = TAILQ_FIRST(wl); goto done; } /* if transient focus on parent */ if (cur_focus->transient) { 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; } if (cur_focus == win) winfocus = TAILQ_PREV(win, ws_win_list, entry); if (winfocus == NULL) winfocus = TAILQ_LAST(wl, ws_win_list); if (winfocus == NULL || winfocus == win) winfocus = TAILQ_NEXT(cur_focus, entry); done: focus_magic(winfocus); } void focus(struct swm_region *r, union arg *args) { struct ws_win *winfocus = NULL, *head; struct ws_win *cur_focus = NULL; struct ws_win_list *wl = NULL; struct workspace *ws = NULL; int all_iconics; if (!(r && r->ws)) return; DNPRINTF(SWM_D_FOCUS, "focus: id: %d\n", args->id); /* treat FOCUS_CUR special */ if (args->id == SWM_ARG_ID_FOCUSCUR) { if (r->ws->focus && r->ws->focus->iconic == 0) winfocus = r->ws->focus; else if (r->ws->focus_prev && r->ws->focus_prev->iconic == 0) winfocus = r->ws->focus_prev; else TAILQ_FOREACH(winfocus, &r->ws->winlist, entry) if (winfocus->iconic == 0) break; focus_magic(winfocus); return; } if ((cur_focus = r->ws->focus) == NULL) return; ws = r->ws; wl = &ws->winlist; if (TAILQ_EMPTY(wl)) return; /* make sure there is at least one uniconified window */ all_iconics = 1; TAILQ_FOREACH(winfocus, wl, entry) if (winfocus->iconic == 0) { all_iconics = 0; break; } if (all_iconics) return; switch (args->id) { case SWM_ARG_ID_FOCUSPREV: head = TAILQ_PREV(cur_focus, ws_win_list, entry); if (head == NULL) head = TAILQ_LAST(wl, ws_win_list); winfocus = head; if (WINID(winfocus) == cur_focus->transient) { head = TAILQ_PREV(winfocus, ws_win_list, entry); if (head == NULL) head = TAILQ_LAST(wl, ws_win_list); winfocus = head; } /* skip iconics */ if (winfocus && winfocus->iconic) { while (winfocus != cur_focus) { if (winfocus == NULL) winfocus = TAILQ_LAST(wl, ws_win_list); if (winfocus->iconic == 0) break; winfocus = TAILQ_PREV(winfocus, ws_win_list, entry); } } break; case SWM_ARG_ID_FOCUSNEXT: head = TAILQ_NEXT(cur_focus, entry); if (head == NULL) head = TAILQ_FIRST(wl); winfocus = head; /* skip iconics */ if (winfocus && winfocus->iconic) { while (winfocus != cur_focus) { if (winfocus == NULL) winfocus = TAILQ_FIRST(wl); if (winfocus->iconic == 0) break; winfocus = TAILQ_NEXT(winfocus, entry); } } break; case SWM_ARG_ID_FOCUSMAIN: winfocus = TAILQ_FIRST(wl); if (winfocus == cur_focus) winfocus = cur_focus->ws->focus_prev; break; default: return; } focus_magic(winfocus); } void cycle_layout(struct swm_region *r, union arg *args) { struct workspace *ws = r->ws; union arg a; 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]; stack(); if (focus_mode == SWM_FOCUS_DEFAULT) drain_enter_notify(); a.id = SWM_ARG_ID_FOCUSCUR; focus(r, &a); bar_update(); } void stack_config(struct swm_region *r, union arg *args) { struct workspace *ws = r->ws; DNPRINTF(SWM_D_STACK, "stack_config: id: %d workspace: %d\n", args->id, ws->idx); if (ws->cur_layout->l_config != NULL) ws->cur_layout->l_config(ws, args->id); if (args->id != SWM_ARG_ID_STACKINIT) stack(); bar_update(); } void stack(void) { struct swm_geometry g; struct swm_region *r; int i; #ifdef SWM_DEBUG int j; #endif DNPRINTF(SWM_D_STACK, "stack: begin\n"); for (i = 0; i < ScreenCount(display); i++) { #ifdef SWM_DEBUG j = 0; #endif TAILQ_FOREACH(r, &screens[i].rl, entry) { DNPRINTF(SWM_D_STACK, "stack: workspace: %d " "(screen: %d, region: %d)\n", r->ws->idx, i, j++); /* start with screen geometry, adjust for bar */ g = r->g; g.w -= 2 * border_width; g.h -= 2 * border_width; if (bar_enabled) { if (!bar_at_bottom) g.y += bar_height; g.h -= bar_height; } 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--; if (focus_mode == SWM_FOCUS_DEFAULT) drain_enter_notify(); DNPRINTF(SWM_D_STACK, "stack: end\n"); } void store_float_geom(struct ws_win *win, struct swm_region *r) { /* retain window geom and region geom */ win->g_float = win->g; win->rg_float = r->g; win->g_floatvalid = 1; } void stack_floater(struct ws_win *win, struct swm_region *r) { unsigned int mask; XWindowChanges wc; if (win == NULL) return; bzero(&wc, sizeof wc); mask = CWX | CWY | CWBorderWidth | CWWidth | CWHeight; /* * to allow windows to change their size (e.g. mplayer fs) only retrieve * geom on ws switches or return from max mode */ if (win->floatmaxed || (r != r->ws->old_r && win->g_floatvalid && !(win->ewmh_flags & EWMH_F_FULLSCREEN))) { /* * use stored g and rg to set relative position and size * as in old region or before max stack mode */ X(win) = win->g_float.x - win->rg_float.x + X(r); Y(win) = win->g_float.y - win->rg_float.y + Y(r); WIDTH(win) = win->g_float.w; HEIGHT(win) = win->g_float.h; win->g_floatvalid = 0; } win->floatmaxed = 0; if ((win->quirks & SWM_Q_FULLSCREEN) && (WIDTH(win) >= WIDTH(r)) && (HEIGHT(win) >= HEIGHT(r))) wc.border_width = 0; else wc.border_width = border_width; if (win->transient && (win->quirks & SWM_Q_TRANSSZ)) { WIDTH(win) = (double)WIDTH(r) * dialog_ratio; HEIGHT(win) = (double)HEIGHT(r) * dialog_ratio; } if (!win->manual) { /* * floaters and transients are auto-centred unless moved * or resized */ X(win) = X(r) + (WIDTH(r) - WIDTH(win)) / 2 - wc.border_width; Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2 - wc.border_width; } /* win can be outside r if new r smaller than old r */ /* Ensure top left corner inside r (move probs otherwise) */ if (X(win) < X(r) - wc.border_width) X(win) = X(r) - wc.border_width; if (X(win) > X(r) + WIDTH(r) - 1) X(win) = (WIDTH(win) > WIDTH(r)) ? X(r) : (X(r) + WIDTH(r) - WIDTH(win) - 2 * wc.border_width); if (Y(win) < Y(r) - wc.border_width) Y(win) = Y(r) - wc.border_width; if (Y(win) > Y(r) + HEIGHT(r) - 1) Y(win) = (HEIGHT(win) > HEIGHT(r)) ? Y(r) : (Y(r) + HEIGHT(r) - HEIGHT(win) - 2 * wc.border_width); wc.x = X(win); wc.y = Y(win); wc.width = WIDTH(win); wc.height = HEIGHT(win); /* * Retain floater and transient geometry for correct positioning * when ws changes region */ if (!(win->ewmh_flags & EWMH_F_FULLSCREEN)) store_float_geom(win, r); DNPRINTF(SWM_D_MISC, "stack_floater: window: %lu, (x,y) w x h: (%d,%d) " "%d x %d\n", win->id, wc.x, wc.y, wc.width, wc.height); XConfigureWindow(display, win->id, mask, &wc); } /* * 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) || win->floating || win->transient) 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, ShiftMask); } 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, ShiftMask); } } #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, int flip) { XWindowChanges wc; XWindowAttributes wa; struct swm_geometry win_g, r_g = *g; struct ws_win *win, *fs_win = 0; int i, j, s, stacks; int w_inc = 1, h_inc, w_base = 1, h_base; int hrh, extra = 0, h_slice, last_h = 0; int split, colno, winno, mwin, msize, mscale; int remain, missing, v_slice, reconfigure; unsigned int mask; DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d, rot: %s, " "flip: %s\n", ws->idx, YESNO(rot), YESNO(flip)); winno = count_win(ws, 0); if (winno == 0 && count_win(ws, 1) == 0) return; TAILQ_FOREACH(win, &ws->winlist, entry) if (win->transient == 0 && win->floating == 0 && win->iconic == 0) break; if (win == NULL) goto notiles; 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; } win_g = r_g; 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; win_g.w = v_slice * mscale; if (w_inc > 1 && w_inc < v_slice) { /* adjust for window's requested size increment */ remain = (win_g.w - w_base) % w_inc; win_g.w -= remain; } msize = win_g.w; if (flip) win_g.x += r_g.w - msize; } else { msize = -2; colno = split = winno / stacks; win_g.w = ((r_g.w - (stacks * 2 * border_width) + 2 * border_width) / stacks); } hrh = r_g.h / colno; extra = r_g.h - (colno * hrh); win_g.h = hrh - 2 * border_width; /* stack all the tiled windows */ i = j = 0, s = stacks; TAILQ_FOREACH(win, &ws->winlist, entry) { if (win->transient != 0 || win->floating != 0) continue; if (win->iconic != 0) continue; if (win->ewmh_flags & EWMH_F_FULLSCREEN) { fs_win = win; continue; } if (split && i == split) { colno = (winno - mwin) / stacks; if (s <= (winno - mwin) % stacks) colno++; split = split + colno; hrh = (r_g.h / colno); extra = r_g.h - (colno * hrh); if (flip) win_g.x = r_g.x; else win_g.x += win_g.w + 2 * border_width; win_g.w = (r_g.w - msize - (stacks * 2 * border_width)) / stacks; if (s == 1) win_g.w += (r_g.w - msize - (stacks * 2 * border_width)) % stacks; s--; j = 0; } win_g.h = hrh - 2 * border_width; 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) { win_g.h = hrh + extra; } else if (h_inc > 1 && h_inc < h_slice) { /* adjust for window's requested size increment */ remain = (win_g.h - h_base) % h_inc; missing = h_inc - remain; if (missing <= extra || j == 0) { extra -= missing; win_g.h += missing; } else { win_g.h -= remain; extra += remain; } } if (j == 0) win_g.y = r_g.y; else win_g.y += last_h + 2 * border_width; bzero(&wc, sizeof wc); if (disable_border && bar_enabled == 0 && winno == 1){ wc.border_width = 0; win_g.w += 2 * border_width; win_g.h += 2 * border_width; } else wc.border_width = border_width; reconfigure = 0; if (rot) { if (X(win) != win_g.y || Y(win) != win_g.x || WIDTH(win) != win_g.h || HEIGHT(win) != win_g.w) { reconfigure = 1; X(win) = wc.x = win_g.y; Y(win) = wc.y = win_g.x; WIDTH(win) = wc.width = win_g.h; HEIGHT(win) = wc.height = win_g.w; } } else { if (X(win) != win_g.x || Y(win) != win_g.y || WIDTH(win) != win_g.w || HEIGHT(win) != win_g.h) { reconfigure = 1; X(win) = wc.x = win_g.x; Y(win) = wc.y = win_g.y; WIDTH(win) = wc.width = win_g.w; HEIGHT(win) = wc.height = win_g.h; } } if (reconfigure) { adjust_font(win); mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth; XConfigureWindow(display, win->id, mask, &wc); } if (XGetWindowAttributes(display, win->id, &wa)) if (wa.map_state == IsUnmapped) XMapRaised(display, win->id); last_h = win_g.h; i++; j++; } notiles: /* now, stack all the floaters and transients */ TAILQ_FOREACH(win, &ws->winlist, entry) { if (win->transient == 0 && win->floating == 0) continue; if (win->iconic == 1) continue; if (win->ewmh_flags & EWMH_F_FULLSCREEN) { fs_win = win; continue; } stack_floater(win, ws->r); XMapRaised(display, win->id); } if (fs_win) { stack_floater(fs_win, ws->r); XMapRaised(display, fs_win->id); } } 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_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_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) { XWindowChanges wc; struct swm_geometry gg = *g; struct ws_win *win, *wintrans = NULL, *parent = NULL; unsigned int mask; int winno; DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx); if (ws == NULL) return; winno = count_win(ws, 0); if (winno == 0 && count_win(ws, 1) == 0) return; TAILQ_FOREACH(win, &ws->winlist, entry) { if (win->transient) { wintrans = win; parent = find_window(win->transient); continue; } if (win->floating && win->floatmaxed == 0 ) { /* * retain geometry for retrieval on exit from * max_stack mode */ store_float_geom(win, ws->r); win->floatmaxed = 1; } /* only reconfigure if necessary */ if (X(win) != gg.x || Y(win) != gg.y || WIDTH(win) != gg.w || HEIGHT(win) != gg.h) { bzero(&wc, sizeof wc); X(win) = wc.x = gg.x; Y(win) = wc.y = gg.y; if (bar_enabled){ wc.border_width = border_width; WIDTH(win) = wc.width = gg.w; HEIGHT(win) = wc.height = gg.h; } else { wc.border_width = 0; WIDTH(win) = wc.width = gg.w + 2 * border_width; HEIGHT(win) = wc.height = gg.h + 2 * border_width; } mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth; XConfigureWindow(display, win->id, mask, &wc); } /* unmap only if we don't have multi screen */ if (win != ws->focus) if (!(ScreenCount(display) > 1 || outputs > 1)) unmap_window(win); } /* put the last transient on top */ if (wintrans) { if (parent) XMapRaised(display, parent->id); stack_floater(wintrans, ws->r); focus_magic(wintrans); } } void send_to_ws(struct swm_region *r, union arg *args) { int wsid = args->id; struct ws_win *win = NULL, *parent; struct workspace *ws, *nws; Atom ws_idx_atom = 0; unsigned char ws_idx_str[SWM_PROPLEN]; union arg a; if (r && r->ws && r->ws->focus) win = r->ws->focus; else return; if (win == NULL) return; if (win->ws->idx == wsid) return; DNPRINTF(SWM_D_MOVE, "send_to_ws: window: 0x%lx\n", win->id); ws = win->ws; nws = &win->s->ws[wsid]; a.id = SWM_ARG_ID_FOCUSPREV; focus(r, &a); if (win->transient) { parent = find_window(win->transient); if (parent) { unmap_window(parent); TAILQ_REMOVE(&ws->winlist, parent, entry); TAILQ_INSERT_TAIL(&nws->winlist, parent, entry); parent->ws = nws; } } unmap_window(win); TAILQ_REMOVE(&ws->winlist, win, entry); TAILQ_INSERT_TAIL(&nws->winlist, win, entry); if (TAILQ_EMPTY(&ws->winlist)) r->ws->focus = NULL; win->ws = nws; /* Try to update the window's workspace property */ ws_idx_atom = XInternAtom(display, "_SWM_WS", False); if (ws_idx_atom && snprintf((char *)ws_idx_str, SWM_PROPLEN, "%d", nws->idx) < SWM_PROPLEN) { DNPRINTF(SWM_D_PROP, "send_to_ws: set property: _SWM_WS: %s\n", ws_idx_str); XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8, PropModeReplace, ws_idx_str, strlen((char *)ws_idx_str)); } stack(); } void pressbutton(struct swm_region *r, union arg *args) { XTestFakeButtonEvent(display, args->id, True, CurrentTime); XTestFakeButtonEvent(display, args->id, False, CurrentTime); } void raise_toggle(struct swm_region *r, union arg *args) { if (r == NULL || r->ws == NULL) return; r->ws->always_raise = !r->ws->always_raise; /* bring floaters back to top */ if (r->ws->always_raise == 0) stack(); } void iconify(struct swm_region *r, union arg *args) { union arg a; if (r->ws->focus == NULL) return; unmap_window(r->ws->focus); update_iconic(r->ws->focus, 1); stack(); if (focus_mode == SWM_FOCUS_DEFAULT) drain_enter_notify(); r->ws->focus = NULL; a.id = SWM_ARG_ID_FOCUSCUR; focus(r, &a); } unsigned char * get_win_name(Window win) { unsigned char *prop = NULL; unsigned long nbytes, nitems; /* try _NET_WM_NAME first */ if (get_property(win, a_netwmname, 0L, a_utf8_string, NULL, &nbytes, &prop)) { XFree(prop); if (get_property(win, a_netwmname, nbytes, a_utf8_string, &nitems, NULL, &prop)) return (prop); } /* fallback to WM_NAME */ if (!get_property(win, a_wmname, 0L, a_string, NULL, &nbytes, &prop)) return (NULL); XFree(prop); if (get_property(win, a_wmname, nbytes, a_string, &nitems, NULL, &prop)) return (prop); return (NULL); } void uniconify(struct swm_region *r, union arg *args) { struct ws_win *win; FILE *lfile; unsigned char *name; int count = 0; 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 (win->iconic == 0) continue; 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 (win->iconic == 0) continue; name = get_win_name(win->id); if (name == NULL) continue; fprintf(lfile, "%s.%lu\n", name, win->id); XFree(name); } fclose(lfile); } void name_workspace(struct swm_region *r, union arg *args) { FILE *lfile; 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 swm_region *r, union arg *args) { int i; struct workspace *ws; FILE *lfile; 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 < SWM_WS_MAX; 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; while ((sw = TAILQ_FIRST(&search_wl)) != NULL) { XDestroyWindow(display, sw->indicator); XFreeGC(display, sw->gc); TAILQ_REMOVE(&search_wl, sw, entry); free(sw); } } void search_win(struct swm_region *r, union arg *args) { struct ws_win *win = NULL; struct search_window *sw = NULL; Window w; XGCValues gcv; int i; char s[8]; FILE *lfile; size_t len; XRectangle ibox, lbox; 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; TAILQ_INIT(&search_wl); i = 1; TAILQ_FOREACH(win, &r->ws->winlist, entry) { if (win->iconic == 1) 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); XmbTextExtents(bar_fs, s, len, &ibox, &lbox); w = XCreateSimpleWindow(display, win->id, 0, 0,lbox.width + 4, bar_fs_extents->max_logical_extent.height, 1, r->s->c[SWM_S_COLOR_UNFOCUS].color, r->s->c[SWM_S_COLOR_FOCUS].color); sw->indicator = w; TAILQ_INSERT_TAIL(&search_wl, sw, entry); sw->gc = XCreateGC(display, w, 0, &gcv); XMapRaised(display, w); XSetForeground(display, sw->gc, r->s->c[SWM_S_COLOR_BAR].color); DRAWSTRING(display, w, bar_fs, sw->gc, 2, (bar_fs_extents->max_logical_extent.height - lbox.height) / 2 - lbox.y, s, len); fprintf(lfile, "%d\n", i); i++; } fclose(lfile); } void search_resp_uniconify(char *resp, unsigned long len) { unsigned 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 (win->iconic == 0) continue; name = get_win_name(win->id); if (name == NULL) continue; if (asprintf(&s, "%s.%lu", name, win->id) == -1) { XFree(name); continue; } XFree(name); if (strncmp(s, resp, len) == 0) { /* XXX this should be a callback to generalize */ update_iconic(win, 0); free(s); break; } free(s); } } void search_resp_name_workspace(char *resp, unsigned long 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; } } } void search_resp_search_workspace(char *resp, unsigned long len) { 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) { DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: strdup: %s", strerror(errno)); return; } p = strchr(q, ':'); if (p != NULL) *p = '\0'; ws_idx = strtonum(q, 1, SWM_WS_MAX, &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(search_r, &a); } void search_resp_search_window(char *resp, unsigned long len) { 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) { DNPRINTF(SWM_D_MISC, "search_resp_search_window: strdup: %s", strerror(errno)); return; } idx = 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; unsigned long 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, len); break; case SWM_SEARCH_SEARCH_WINDOW: search_resp_search_window(resp, len); 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); } void wkill(struct swm_region *r, union arg *args) { DNPRINTF(SWM_D_MISC, "wkill: id: %d\n", args->id); if (r->ws->focus == NULL) return; if (args->id == SWM_ARG_ID_KILLWINDOW) XKillClient(display, r->ws->focus->id); else if (r->ws->focus->can_delete) client_msg(r->ws->focus, adelete); } int floating_toggle_win(struct ws_win *win) { struct swm_region *r; if (win == NULL) return 0; if (!win->ws->r) return 0; r = win->ws->r; /* reject floating toggles in max stack mode */ if (win->ws->cur_layout == &layouts[SWM_MAX_STACK]) return 0; if (win->floating) { if (!win->floatmaxed) { /* retain position for refloat */ store_float_geom(win, r); } win->floating = 0; } else { if (win->g_floatvalid) { /* refloat at last floating relative position */ X(win) = win->g_float.x - win->rg_float.x + X(r); Y(win) = win->g_float.y - win->rg_float.y + Y(r); WIDTH(win) = win->g_float.w; HEIGHT(win) = win->g_float.h; } win->floating = 1; } ewmh_update_actions(win); return 1; } void floating_toggle(struct swm_region *r, union arg *args) { struct ws_win *win = r->ws->focus; union arg a; if (win == NULL) return; ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom, _NET_WM_STATE_TOGGLE); stack(); if (focus_mode == SWM_FOCUS_DEFAULT) drain_enter_notify(); if (win == win->ws->focus) { a.id = SWM_ARG_ID_FOCUSCUR; focus(win->ws->r, &a); } } void constrain_window(struct ws_win *win, struct swm_region *r, int resizable) { if (X(win) + WIDTH(win) > X(r) + WIDTH(r) - border_width) { if (resizable) WIDTH(win) = X(r) + WIDTH(r) - X(win) - border_width; else X(win) = X(r) + WIDTH(r) - WIDTH(win) - border_width; } if (X(win) < X(r) - border_width) { if (resizable) WIDTH(win) -= X(r) - X(win) - border_width; X(win) = X(r) - border_width; } if (Y(win) + HEIGHT(win) > Y(r) + HEIGHT(r) - border_width) { if (resizable) HEIGHT(win) = Y(r) + HEIGHT(r) - Y(win) - border_width; else Y(win) = Y(r) + HEIGHT(r) - HEIGHT(win) - border_width; } if (Y(win) < Y(r) - border_width) { if (resizable) HEIGHT(win) -= Y(r) - Y(win) - border_width; Y(win) = Y(r) - border_width; } if (WIDTH(win) < 1) WIDTH(win) = 1; if (HEIGHT(win) < 1) HEIGHT(win) = 1; } void update_window(struct ws_win *win) { unsigned int mask; XWindowChanges wc; bzero(&wc, sizeof wc); mask = CWBorderWidth | CWWidth | CWHeight | CWX | CWY; wc.border_width = border_width; wc.x = X(win); wc.y = Y(win); wc.width = WIDTH(win); wc.height = HEIGHT(win); DNPRINTF(SWM_D_MISC, "update_window: window: 0x%lx, (x,y) w x h: " "(%d,%d) %d x %d\n", win->id, wc.x, wc.y, wc.width, wc.height); XConfigureWindow(display, win->id, mask, &wc); } #define SWM_RESIZE_STEPS (50) void resize(struct ws_win *win, union arg *args) { XEvent ev; Time time = 0; struct swm_region *r = NULL; int resize_step = 0; Window rr, cr; int x, y, wx, wy; unsigned int mask; struct swm_geometry g; int top = 0, left = 0; int dx, dy; Cursor cursor; unsigned int shape; /* cursor style */ if (win == NULL) return; r = win->ws->r; DNPRINTF(SWM_D_MOUSE, "resize: window: 0x%lx, floating: %s, " "transient: 0x%lx\n", win->id, YESNO(win->floating), win->transient); if (!(win->transient != 0 || win->floating != 0)) return; /* reject resizes in max mode for floaters (transient ok) */ if (win->floatmaxed) return; win->manual = 1; ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom, _NET_WM_STATE_ADD); stack(); switch (args->id) { case SWM_ARG_ID_WIDTHSHRINK: WIDTH(win) -= SWM_RESIZE_STEPS; resize_step = 1; break; case SWM_ARG_ID_WIDTHGROW: WIDTH(win) += SWM_RESIZE_STEPS; resize_step = 1; break; case SWM_ARG_ID_HEIGHTSHRINK: HEIGHT(win) -= SWM_RESIZE_STEPS; resize_step = 1; break; case SWM_ARG_ID_HEIGHTGROW: HEIGHT(win) += SWM_RESIZE_STEPS; resize_step = 1; break; default: break; } if (resize_step) { constrain_window(win, r, 1); update_window(win); store_float_geom(win,r); return; } if (focus_mode == SWM_FOCUS_DEFAULT) drain_enter_notify(); /* get cursor offset from window root */ if (!XQueryPointer(display, win->id, &rr, &cr, &x, &y, &wx, &wy, &mask)) return; g = win->g; if (wx < WIDTH(win) / 2) left = 1; if (wy < HEIGHT(win) / 2) top = 1; if (args->id == SWM_ARG_ID_CENTER) shape = XC_sizing; else if (top) shape = (left) ? XC_top_left_corner : XC_top_right_corner; else shape = (left) ? XC_bottom_left_corner : XC_bottom_right_corner; cursor = XCreateFontCursor(display, shape); if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime) != GrabSuccess) { XFreeCursor(display, cursor); return; } do { XMaskEvent(display, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); switch (ev.type) { case ConfigureRequest: case Expose: case MapRequest: handler[ev.type](&ev); break; case MotionNotify: /* cursor offset/delta from start of the operation */ dx = ev.xmotion.x_root - x; dy = ev.xmotion.y_root - y; /* vertical */ if (top) dy = -dy; if (args->id == 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 (args->id == 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; } constrain_window(win, r, 1); /* not free, don't sync more than 120 times / second */ if ((ev.xmotion.time - time) > (1000 / 120) ) { time = ev.xmotion.time; XSync(display, False); update_window(win); } break; } } while (ev.type != ButtonRelease); if (time) { XSync(display, False); update_window(win); } store_float_geom(win,r); XUngrabPointer(display, CurrentTime); XFreeCursor(display, cursor); /* drain events */ drain_enter_notify(); } void resize_step(struct swm_region *r, union arg *args) { struct ws_win *win = NULL; if (r && r->ws && r->ws->focus) win = r->ws->focus; else return; resize(win, args); } #define SWM_MOVE_STEPS (50) void move(struct ws_win *win, union arg *args) { XEvent ev; Time time = 0; int move_step = 0; struct swm_region *r = NULL; Window rr, cr; int x, y, wx, wy; unsigned int mask; if (win == NULL) return; r = win->ws->r; DNPRINTF(SWM_D_MOUSE, "move: window: 0x%lx, floating: %s, transient: " "0x%lx\n", win->id, YESNO(win->floating), win->transient); /* in max_stack mode should only move transients */ if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !win->transient) return; win->manual = 1; if (win->floating == 0 && !win->transient) { store_float_geom(win,r); ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom, _NET_WM_STATE_ADD); } ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom, _NET_WM_STATE_ADD); stack(); move_step = 0; switch (args->id) { case SWM_ARG_ID_MOVELEFT: X(win) -= (SWM_MOVE_STEPS - border_width); move_step = 1; break; case SWM_ARG_ID_MOVERIGHT: X(win) += (SWM_MOVE_STEPS - border_width); move_step = 1; break; case SWM_ARG_ID_MOVEUP: Y(win) -= (SWM_MOVE_STEPS - border_width); move_step = 1; break; case SWM_ARG_ID_MOVEDOWN: Y(win) += (SWM_MOVE_STEPS - border_width); move_step = 1; break; default: break; } if (move_step) { constrain_window(win, r, 0); update_window(win); store_float_geom(win, r); return; } if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, None, XCreateFontCursor(display, XC_fleur), CurrentTime) != GrabSuccess) return; /* get cursor offset from window root */ if (!XQueryPointer(display, win->id, &rr, &cr, &x, &y, &wx, &wy, &mask)) return; do { XMaskEvent(display, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); switch (ev.type) { case ConfigureRequest: case Expose: case MapRequest: handler[ev.type](&ev); break; case MotionNotify: X(win) = ev.xmotion.x_root - wx - border_width; Y(win) = ev.xmotion.y_root - wy - border_width; constrain_window(win, r, 0); /* not free, don't sync more than 120 times / second */ if ((ev.xmotion.time - time) > (1000 / 120) ) { time = ev.xmotion.time; XSync(display, False); update_window(win); } break; } } while (ev.type != ButtonRelease); if (time) { XSync(display, False); update_window(win); } store_float_geom(win,r); XUngrabPointer(display, CurrentTime); /* drain events */ drain_enter_notify(); } void move_step(struct swm_region *r, union arg *args) { struct ws_win *win = NULL; if (r && r->ws && r->ws->focus) win = r->ws->focus; else return; if (!(win->transient != 0 || win->floating != 0)) return; move(win, args); } /* user/key callable function IDs */ enum keyfuncid { kf_cycle_layout, kf_flip_layout, kf_stack_reset, kf_master_shrink, kf_master_grow, kf_master_add, kf_master_del, kf_stack_inc, kf_stack_dec, kf_swap_main, kf_focus_next, kf_focus_prev, kf_swap_next, kf_swap_prev, kf_spawn_term, kf_spawn_menu, kf_quit, kf_restart, kf_focus_main, kf_ws_1, kf_ws_2, kf_ws_3, kf_ws_4, kf_ws_5, kf_ws_6, kf_ws_7, kf_ws_8, kf_ws_9, kf_ws_10, kf_ws_next, kf_ws_prev, kf_ws_next_all, kf_ws_prev_all, kf_ws_prior, kf_screen_next, kf_screen_prev, kf_mvws_1, kf_mvws_2, kf_mvws_3, kf_mvws_4, kf_mvws_5, kf_mvws_6, kf_mvws_7, kf_mvws_8, kf_mvws_9, kf_mvws_10, kf_bar_toggle, kf_wind_kill, kf_wind_del, kf_screenshot_all, kf_screenshot_wind, kf_float_toggle, kf_version, kf_spawn_lock, kf_spawn_initscr, kf_spawn_custom, kf_iconify, kf_uniconify, kf_raise_toggle, kf_button2, kf_width_shrink, kf_width_grow, kf_height_shrink, kf_height_grow, kf_move_left, kf_move_right, kf_move_up, kf_move_down, kf_name_workspace, kf_search_workspace, kf_search_win, kf_dumpwins, /* MUST BE LAST */ kf_invalid }; /* key definitions */ void dummykeyfunc(struct swm_region *r, union arg *args) { }; void legacyfunc(struct swm_region *r, union arg *args) { }; struct keyfunc { char name[SWM_FUNCNAME_LEN]; void (*func)(struct swm_region *r, union arg *); union arg args; } keyfuncs[kf_invalid + 1] = { /* name function argument */ { "cycle_layout", cycle_layout, {0} }, { "flip_layout", stack_config, {.id = SWM_ARG_ID_FLIPLAYOUT} }, { "stack_reset", stack_config, {.id = SWM_ARG_ID_STACKRESET} }, { "master_shrink", stack_config, {.id = SWM_ARG_ID_MASTERSHRINK} }, { "master_grow", stack_config, {.id = SWM_ARG_ID_MASTERGROW} }, { "master_add", stack_config, {.id = SWM_ARG_ID_MASTERADD} }, { "master_del", stack_config, {.id = SWM_ARG_ID_MASTERDEL} }, { "stack_inc", stack_config, {.id = SWM_ARG_ID_STACKINC} }, { "stack_dec", stack_config, {.id = SWM_ARG_ID_STACKDEC} }, { "swap_main", swapwin, {.id = SWM_ARG_ID_SWAPMAIN} }, { "focus_next", focus, {.id = SWM_ARG_ID_FOCUSNEXT} }, { "focus_prev", focus, {.id = SWM_ARG_ID_FOCUSPREV} }, { "swap_next", swapwin, {.id = SWM_ARG_ID_SWAPNEXT} }, { "swap_prev", swapwin, {.id = SWM_ARG_ID_SWAPPREV} }, { "spawn_term", spawnterm, {.argv = spawn_term} }, { "spawn_menu", legacyfunc, {0} }, { "quit", quit, {0} }, { "restart", restart, {0} }, { "focus_main", focus, {.id = SWM_ARG_ID_FOCUSMAIN} }, { "ws_1", switchws, {.id = 0} }, { "ws_2", switchws, {.id = 1} }, { "ws_3", switchws, {.id = 2} }, { "ws_4", switchws, {.id = 3} }, { "ws_5", switchws, {.id = 4} }, { "ws_6", switchws, {.id = 5} }, { "ws_7", switchws, {.id = 6} }, { "ws_8", switchws, {.id = 7} }, { "ws_9", switchws, {.id = 8} }, { "ws_10", switchws, {.id = 9} }, { "ws_next", cyclews, {.id = SWM_ARG_ID_CYCLEWS_UP} }, { "ws_prev", cyclews, {.id = SWM_ARG_ID_CYCLEWS_DOWN} }, { "ws_next_all", cyclews, {.id = SWM_ARG_ID_CYCLEWS_UP_ALL} }, { "ws_prev_all", cyclews, {.id = SWM_ARG_ID_CYCLEWS_DOWN_ALL} }, { "ws_prior", priorws, {0} }, { "screen_next", cyclescr, {.id = SWM_ARG_ID_CYCLESC_UP} }, { "screen_prev", cyclescr, {.id = SWM_ARG_ID_CYCLESC_DOWN} }, { "mvws_1", send_to_ws, {.id = 0} }, { "mvws_2", send_to_ws, {.id = 1} }, { "mvws_3", send_to_ws, {.id = 2} }, { "mvws_4", send_to_ws, {.id = 3} }, { "mvws_5", send_to_ws, {.id = 4} }, { "mvws_6", send_to_ws, {.id = 5} }, { "mvws_7", send_to_ws, {.id = 6} }, { "mvws_8", send_to_ws, {.id = 7} }, { "mvws_9", send_to_ws, {.id = 8} }, { "mvws_10", send_to_ws, {.id = 9} }, { "bar_toggle", bar_toggle, {0} }, { "wind_kill", wkill, {.id = SWM_ARG_ID_KILLWINDOW} }, { "wind_del", wkill, {.id = SWM_ARG_ID_DELETEWINDOW} }, { "screenshot_all", legacyfunc, {0} }, { "screenshot_wind", legacyfunc, {0} }, { "float_toggle", floating_toggle,{0} }, { "version", version, {0} }, { "spawn_lock", legacyfunc, {0} }, { "spawn_initscr", legacyfunc, {0} }, { "spawn_custom", dummykeyfunc, {0} }, { "iconify", iconify, {0} }, { "uniconify", uniconify, {0} }, { "raise_toggle", raise_toggle, {0} }, { "button2", pressbutton, {2} }, { "width_shrink", resize_step, {.id = SWM_ARG_ID_WIDTHSHRINK} }, { "width_grow", resize_step, {.id = SWM_ARG_ID_WIDTHGROW} }, { "height_shrink", resize_step, {.id = SWM_ARG_ID_HEIGHTSHRINK} }, { "height_grow", resize_step, {.id = SWM_ARG_ID_HEIGHTGROW} }, { "move_left", move_step, {.id = SWM_ARG_ID_MOVELEFT} }, { "move_right", move_step, {.id = SWM_ARG_ID_MOVERIGHT} }, { "move_up", move_step, {.id = SWM_ARG_ID_MOVEUP} }, { "move_down", move_step, {.id = SWM_ARG_ID_MOVEDOWN} }, { "name_workspace", name_workspace, {0} }, { "search_workspace", search_workspace, {0} }, { "search_win", search_win, {0} }, { "dumpwins", dumpwins, {0} }, /* MUST BE LAST */ { "invalid key func", NULL, {0} }, }; struct key { RB_ENTRY(key) entry; unsigned int mod; KeySym keysym; enum keyfuncid funcid; char *spawn_name; }; RB_HEAD(key_list, key); int key_cmp(struct key *kp1, struct key *kp2) { if (kp1->keysym < kp2->keysym) return (-1); if (kp1->keysym > kp2->keysym) return (1); if (kp1->mod < kp2->mod) return (-1); if (kp1->mod > kp2->mod) return (1); return (0); } RB_GENERATE_STATIC(key_list, key, entry, key_cmp); struct key_list keys; /* mouse */ enum { client_click, root_click }; struct button { unsigned int action; unsigned int mask; unsigned int button; void (*func)(struct ws_win *, union arg *); union arg args; } buttons[] = { /* action key mouse button func args */ { client_click, MODKEY, Button3, resize, {.id = SWM_ARG_ID_DONTCENTER} }, { client_click, MODKEY | ShiftMask, Button3, resize, {.id = SWM_ARG_ID_CENTER} }, { client_click, MODKEY, Button1, move, {0} }, }; void update_modkey(unsigned int mod) { int i; struct key *kp; mod_key = mod; RB_FOREACH(kp, key_list, &keys) if (kp->mod & ShiftMask) kp->mod = mod | ShiftMask; else kp->mod = mod; for (i = 0; i < LENGTH(buttons); i++) if (buttons[i].mask & ShiftMask) buttons[i].mask = mod | ShiftMask; else buttons[i].mask = mod; } /* spawn */ struct spawn_prog { TAILQ_ENTRY(spawn_prog) entry; char *name; int argc; char **argv; }; TAILQ_HEAD(spawn_list, spawn_prog); struct spawn_list spawns = TAILQ_HEAD_INITIALIZER(spawns); int spawn_expand(struct swm_region *r, union arg *args, char *spawn_name, char ***ret_args) { struct spawn_prog *prog = NULL; int i; char *ap, **real_args; DNPRINTF(SWM_D_SPAWN, "spawn_expand: %s\n", spawn_name); /* find program */ TAILQ_FOREACH(prog, &spawns, entry) { if (!strcasecmp(spawn_name, prog->name)) 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 = 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")) { if ((real_args[i] = strdup(r->s->c[SWM_S_COLOR_BAR_BORDER].name)) == NULL) err(1, "spawn_custom border color"); } else if (!strcasecmp(ap, "$bar_color")) { if ((real_args[i] = strdup(r->s->c[SWM_S_COLOR_BAR].name)) == NULL) err(1, "spawn_custom bar color"); } else if (!strcasecmp(ap, "$bar_font")) { if ((real_args[i] = strdup(bar_fonts)) == NULL) err(1, "spawn_custom bar fonts"); } else if (!strcasecmp(ap, "$bar_font_color")) { if ((real_args[i] = strdup(r->s->c[SWM_S_COLOR_BAR_FONT].name)) == NULL) err(1, "spawn_custom color font"); } else if (!strcasecmp(ap, "$color_focus")) { if ((real_args[i] = strdup(r->s->c[SWM_S_COLOR_FOCUS].name)) == NULL) err(1, "spawn_custom color focus"); } else if (!strcasecmp(ap, "$color_unfocus")) { if ((real_args[i] = strdup(r->s->c[SWM_S_COLOR_UNFOCUS].name)) == NULL) err(1, "spawn_custom color unfocus"); } else { /* no match --> copy as is */ if ((real_args[i] = strdup(ap)) == NULL) err(1, "spawn_custom strdup(ap)"); } DNPRINTF(SWM_D_SPAWN, "spawn_custom: cooked arg: %s\n", real_args[i]); } #ifdef SWM_DEBUG DNPRINTF(SWM_D_SPAWN, "spawn_custom: result: "); for (i = 0; i < prog->argc; i++) DNPRINTF(SWM_D_SPAWN, "\"%s\" ", real_args[i]); DNPRINTF(SWM_D_SPAWN, "\n"); #endif *ret_args = real_args; return (prog->argc); } void spawn_custom(struct swm_region *r, union arg *args, 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, 1); for (i = 0; i < spawn_argc; i++) free(real_args[i]); free(real_args); } void spawn_select(struct swm_region *r, union arg *args, 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], 0) == -1) err(1, "dup2"); if (dup2(select_resp_pipe[1], 1) == -1) err(1, "dup2"); close(select_list_pipe[1]); close(select_resp_pipe[0]); spawn(r->ws->idx, &a, 0); 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); } void spawn_insert(char *name, char *args) { char *arg, *cp, *ptr; struct spawn_prog *sp; DNPRINTF(SWM_D_SPAWN, "spawn_insert: %s\n", name); if ((sp = calloc(1, sizeof *sp)) == NULL) err(1, "spawn_insert: malloc"); 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 = strsep(&ptr, " \t")) != NULL) { /* empty field; 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); 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 spawn_replace(struct spawn_prog *sp, char *name, char *args) { DNPRINTF(SWM_D_SPAWN, "spawn_replace: %s [%s]\n", sp->name, name); spawn_remove(sp); spawn_insert(name, args); DNPRINTF(SWM_D_SPAWN, "spawn_replace: leave\n"); } void setspawn(char *name, char *args) { struct spawn_prog *sp; DNPRINTF(SWM_D_SPAWN, "setspawn: %s\n", name); if (name == NULL) return; TAILQ_FOREACH(sp, &spawns, entry) { if (!strcmp(sp->name, name)) { if (*args == '\0') spawn_remove(sp); else spawn_replace(sp, name, args); DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n"); return; } } if (*args == '\0') { warnx("error: setspawn: cannot find program: %s", name); return; } spawn_insert(name, args); DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n"); } int setconfspawn(char *selector, char *value, int flags) { DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, value); setspawn(selector, value); DNPRINTF(SWM_D_SPAWN, "setconfspawn: done\n"); return (0); } void setup_spawn(void) { setconfspawn("term", "xterm", 0); setconfspawn("screenshot_all", "screenshot.sh full", 0); setconfspawn("screenshot_wind", "screenshot.sh window", 0); setconfspawn("lock", "xlock", 0); setconfspawn("initscr", "initscreen.sh", 0); setconfspawn("menu", "dmenu_run" " -fn $bar_font" " -nb $bar_color" " -nf $bar_font_color" " -sb $bar_border" " -sf $bar_color", 0); setconfspawn("search", "dmenu" " -i" " -fn $bar_font" " -nb $bar_color" " -nf $bar_font_color" " -sb $bar_border" " -sf $bar_color", 0); setconfspawn("name_workspace", "dmenu" " -p Workspace" " -fn $bar_font" " -nb $bar_color" " -nf $bar_font_color" " -sb $bar_border" " -sf $bar_color", 0); } /* key bindings */ #define SWM_MODNAME_SIZE 32 #define SWM_KEY_WS "\n+ \t" int parsekeys(char *keystr, unsigned int currmod, unsigned int *mod, KeySym *ks) { char *cp, *name; KeySym uks; DNPRINTF(SWM_D_KEY, "parsekeys: enter [%s]\n", keystr); if (mod == NULL || ks == NULL) { DNPRINTF(SWM_D_KEY, "parsekeys: no mod or key vars\n"); return (1); } if (keystr == NULL || strlen(keystr) == 0) { DNPRINTF(SWM_D_KEY, "parsekeys: no keystr\n"); return (1); } cp = keystr; *ks = NoSymbol; *mod = 0; while ((name = strsep(&cp, SWM_KEY_WS)) != NULL) { DNPRINTF(SWM_D_KEY, "parsekeys: key [%s]\n", name); if (cp) cp += (long)strspn(cp, SWM_KEY_WS); if (strncasecmp(name, "MOD", SWM_MODNAME_SIZE) == 0) *mod |= currmod; else if (!strncasecmp(name, "Mod1", SWM_MODNAME_SIZE)) *mod |= Mod1Mask; else if (!strncasecmp(name, "Mod2", SWM_MODNAME_SIZE)) *mod += Mod2Mask; else if (!strncmp(name, "Mod3", SWM_MODNAME_SIZE)) *mod |= Mod3Mask; else if (!strncmp(name, "Mod4", SWM_MODNAME_SIZE)) *mod |= Mod4Mask; else if (strncasecmp(name, "SHIFT", SWM_MODNAME_SIZE) == 0) *mod |= ShiftMask; else if (strncasecmp(name, "CONTROL", SWM_MODNAME_SIZE) == 0) *mod |= ControlMask; else { *ks = XStringToKeysym(name); XConvertCase(*ks, ks, &uks); if (ks == NoSymbol) { DNPRINTF(SWM_D_KEY, "parsekeys: invalid key %s\n", name); return (1); } } } DNPRINTF(SWM_D_KEY, "parsekeys: leave ok\n"); return (0); } char * strdupsafe(char *str) { if (str == NULL) return (NULL); else return (strdup(str)); } void key_insert(unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name) { struct key *kp; DNPRINTF(SWM_D_KEY, "key_insert: enter %s [%s]\n", keyfuncs[kfid].name, spawn_name); if ((kp = malloc(sizeof *kp)) == NULL) err(1, "key_insert: malloc"); kp->mod = mod; kp->keysym = ks; kp->funcid = kfid; kp->spawn_name = strdupsafe(spawn_name); RB_INSERT(key_list, &keys, kp); DNPRINTF(SWM_D_KEY, "key_insert: leave\n"); } struct key * key_lookup(unsigned int mod, KeySym ks) { struct key kp; kp.keysym = ks; kp.mod = mod; return (RB_FIND(key_list, &keys, &kp)); } void key_remove(struct key *kp) { DNPRINTF(SWM_D_KEY, "key_remove: %s\n", keyfuncs[kp->funcid].name); RB_REMOVE(key_list, &keys, kp); free(kp->spawn_name); free(kp); DNPRINTF(SWM_D_KEY, "key_remove: leave\n"); } void key_replace(struct key *kp, unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name) { DNPRINTF(SWM_D_KEY, "key_replace: %s [%s]\n", keyfuncs[kp->funcid].name, spawn_name); key_remove(kp); key_insert(mod, ks, kfid, spawn_name); DNPRINTF(SWM_D_KEY, "key_replace: leave\n"); } void setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name) { struct key *kp; DNPRINTF(SWM_D_KEY, "setkeybinding: enter %s [%s]\n", keyfuncs[kfid].name, spawn_name); if ((kp = key_lookup(mod, ks)) != NULL) { if (kfid == kf_invalid) key_remove(kp); else key_replace(kp, mod, ks, kfid, spawn_name); DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); return; } if (kfid == kf_invalid) { warnx("error: setkeybinding: cannot find mod/key combination"); DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); return; } key_insert(mod, ks, kfid, spawn_name); DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); } int setconfbinding(char *selector, char *value, int flags) { enum keyfuncid kfid; unsigned int mod; KeySym ks; struct spawn_prog *sp; DNPRINTF(SWM_D_KEY, "setconfbinding: enter\n"); if (selector == NULL) { DNPRINTF(SWM_D_KEY, "setconfbinding: unbind %s\n", value); if (parsekeys(value, mod_key, &mod, &ks) == 0) { kfid = kf_invalid; setkeybinding(mod, ks, kfid, NULL); return (0); } else return (1); } /* search by key function name */ for (kfid = 0; kfid < kf_invalid; (kfid)++) { if (strncasecmp(selector, keyfuncs[kfid].name, SWM_FUNCNAME_LEN) == 0) { DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n", selector); if (parsekeys(value, mod_key, &mod, &ks) == 0) { setkeybinding(mod, ks, kfid, NULL); return (0); } else return (1); } } /* search by custom spawn name */ TAILQ_FOREACH(sp, &spawns, entry) { if (strcasecmp(selector, sp->name) == 0) { DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n", selector); if (parsekeys(value, mod_key, &mod, &ks) == 0) { setkeybinding(mod, ks, kf_spawn_custom, sp->name); return (0); } else return (1); } } DNPRINTF(SWM_D_KEY, "setconfbinding: no match\n"); return (1); } void setup_keys(void) { setkeybinding(MODKEY, XK_space, kf_cycle_layout,NULL); setkeybinding(MODKEY|ShiftMask, XK_backslash, kf_flip_layout, NULL); setkeybinding(MODKEY|ShiftMask, XK_space, kf_stack_reset, NULL); setkeybinding(MODKEY, XK_h, kf_master_shrink,NULL); setkeybinding(MODKEY, XK_l, kf_master_grow, NULL); setkeybinding(MODKEY, XK_comma, kf_master_add, NULL); setkeybinding(MODKEY, XK_period, kf_master_del, NULL); setkeybinding(MODKEY|ShiftMask, XK_comma, kf_stack_inc, NULL); setkeybinding(MODKEY|ShiftMask, XK_period, kf_stack_dec, NULL); setkeybinding(MODKEY, XK_Return, kf_swap_main, NULL); setkeybinding(MODKEY, XK_j, kf_focus_next, NULL); setkeybinding(MODKEY, XK_k, kf_focus_prev, NULL); setkeybinding(MODKEY|ShiftMask, XK_j, kf_swap_next, NULL); setkeybinding(MODKEY|ShiftMask, XK_k, kf_swap_prev, NULL); setkeybinding(MODKEY|ShiftMask, XK_Return, kf_spawn_term, NULL); setkeybinding(MODKEY, XK_p, kf_spawn_custom,"menu"); setkeybinding(MODKEY|ShiftMask, XK_q, kf_quit, NULL); setkeybinding(MODKEY, XK_q, kf_restart, NULL); setkeybinding(MODKEY, XK_m, kf_focus_main, NULL); setkeybinding(MODKEY, XK_1, kf_ws_1, NULL); setkeybinding(MODKEY, XK_2, kf_ws_2, NULL); setkeybinding(MODKEY, XK_3, kf_ws_3, NULL); setkeybinding(MODKEY, XK_4, kf_ws_4, NULL); setkeybinding(MODKEY, XK_5, kf_ws_5, NULL); setkeybinding(MODKEY, XK_6, kf_ws_6, NULL); setkeybinding(MODKEY, XK_7, kf_ws_7, NULL); setkeybinding(MODKEY, XK_8, kf_ws_8, NULL); setkeybinding(MODKEY, XK_9, kf_ws_9, NULL); setkeybinding(MODKEY, XK_0, kf_ws_10, NULL); setkeybinding(MODKEY, XK_Right, kf_ws_next, NULL); setkeybinding(MODKEY, XK_Left, kf_ws_prev, NULL); setkeybinding(MODKEY, XK_Up, kf_ws_next_all, NULL); setkeybinding(MODKEY, XK_Down, kf_ws_prev_all, NULL); setkeybinding(MODKEY, XK_a, kf_ws_prior, NULL); setkeybinding(MODKEY|ShiftMask, XK_Right, kf_screen_next, NULL); setkeybinding(MODKEY|ShiftMask, XK_Left, kf_screen_prev, NULL); setkeybinding(MODKEY|ShiftMask, XK_1, kf_mvws_1, NULL); setkeybinding(MODKEY|ShiftMask, XK_2, kf_mvws_2, NULL); setkeybinding(MODKEY|ShiftMask, XK_3, kf_mvws_3, NULL); setkeybinding(MODKEY|ShiftMask, XK_4, kf_mvws_4, NULL); setkeybinding(MODKEY|ShiftMask, XK_5, kf_mvws_5, NULL); setkeybinding(MODKEY|ShiftMask, XK_6, kf_mvws_6, NULL); setkeybinding(MODKEY|ShiftMask, XK_7, kf_mvws_7, NULL); setkeybinding(MODKEY|ShiftMask, XK_8, kf_mvws_8, NULL); setkeybinding(MODKEY|ShiftMask, XK_9, kf_mvws_9, NULL); setkeybinding(MODKEY|ShiftMask, XK_0, kf_mvws_10, NULL); setkeybinding(MODKEY, XK_b, kf_bar_toggle, NULL); setkeybinding(MODKEY, XK_Tab, kf_focus_next, NULL); setkeybinding(MODKEY|ShiftMask, XK_Tab, kf_focus_prev, NULL); setkeybinding(MODKEY|ShiftMask, XK_x, kf_wind_kill, NULL); setkeybinding(MODKEY, XK_x, kf_wind_del, NULL); setkeybinding(MODKEY, XK_s, kf_spawn_custom,"screenshot_all"); setkeybinding(MODKEY|ShiftMask, XK_s, kf_spawn_custom,"screenshot_wind"); setkeybinding(MODKEY, XK_t, kf_float_toggle,NULL); setkeybinding(MODKEY|ShiftMask, XK_v, kf_version, NULL); setkeybinding(MODKEY|ShiftMask, XK_Delete, kf_spawn_custom,"lock"); setkeybinding(MODKEY|ShiftMask, XK_i, kf_spawn_custom,"initscr"); setkeybinding(MODKEY, XK_w, kf_iconify, NULL); setkeybinding(MODKEY|ShiftMask, XK_w, kf_uniconify, NULL); setkeybinding(MODKEY|ShiftMask, XK_r, kf_raise_toggle,NULL); setkeybinding(MODKEY, XK_v, kf_button2, NULL); setkeybinding(MODKEY, XK_equal, kf_width_grow, NULL); setkeybinding(MODKEY, XK_minus, kf_width_shrink,NULL); setkeybinding(MODKEY|ShiftMask, XK_equal, kf_height_grow, NULL); setkeybinding(MODKEY|ShiftMask, XK_minus, kf_height_shrink,NULL); setkeybinding(MODKEY, XK_bracketleft, kf_move_left, NULL); setkeybinding(MODKEY, XK_bracketright,kf_move_right, NULL); setkeybinding(MODKEY|ShiftMask, XK_bracketleft, kf_move_up, NULL); setkeybinding(MODKEY|ShiftMask, XK_bracketright,kf_move_down, NULL); setkeybinding(MODKEY|ShiftMask, XK_slash, kf_name_workspace,NULL); setkeybinding(MODKEY, XK_slash, kf_search_workspace,NULL); setkeybinding(MODKEY, XK_f, kf_search_win, NULL); #ifdef SWM_DEBUG setkeybinding(MODKEY|ShiftMask, XK_d, kf_dumpwins, NULL); #endif } void clear_keys(void) { struct key *kp; while (RB_EMPTY(&keys) == 0) { kp = RB_ROOT(&keys); key_remove(kp); } } int setkeymapping(char *selector, char *value, int flags) { char keymapping_file[PATH_MAX]; DNPRINTF(SWM_D_KEY, "setkeymapping: enter\n"); if (value[0] == '~') snprintf(keymapping_file, sizeof keymapping_file, "%s/%s", pwd->pw_dir, &value[1]); else strlcpy(keymapping_file, value, sizeof keymapping_file); clear_keys(); /* load new key bindings; if it fails, revert to default bindings */ if (conf_load(keymapping_file, SWM_CONF_KEYMAPPING)) { clear_keys(); setup_keys(); } DNPRINTF(SWM_D_KEY, "setkeymapping: leave\n"); return (0); } void updatenumlockmask(void) { unsigned int i, j; XModifierKeymap *modmap; DNPRINTF(SWM_D_MISC, "updatenumlockmask\n"); numlockmask = 0; modmap = XGetModifierMapping(display); for (i = 0; i < 8; i++) for (j = 0; j < modmap->max_keypermod; j++) if (modmap->modifiermap[i * modmap->max_keypermod + j] == XKeysymToKeycode(display, XK_Num_Lock)) numlockmask = (1 << i); XFreeModifiermap(modmap); } void grabkeys(void) { unsigned int j, k; KeyCode code; unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask | LockMask }; struct key *kp; DNPRINTF(SWM_D_MISC, "grabkeys\n"); updatenumlockmask(); for (k = 0; k < ScreenCount(display); k++) { if (TAILQ_EMPTY(&screens[k].rl)) continue; XUngrabKey(display, AnyKey, AnyModifier, screens[k].root); RB_FOREACH(kp, key_list, &keys) { if ((code = XKeysymToKeycode(display, kp->keysym))) for (j = 0; j < LENGTH(modifiers); j++) XGrabKey(display, code, kp->mod | modifiers[j], screens[k].root, True, GrabModeAsync, GrabModeAsync); } } } void grabbuttons(struct ws_win *win, int focused) { unsigned int i, j; unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; updatenumlockmask(); XUngrabButton(display, AnyButton, AnyModifier, win->id); if (focused) { for (i = 0; i < LENGTH(buttons); i++) if (buttons[i].action == client_click) for (j = 0; j < LENGTH(modifiers); j++) XGrabButton(display, buttons[i].button, buttons[i].mask | modifiers[j], win->id, False, BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); } else XGrabButton(display, AnyButton, AnyModifier, win->id, False, BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); } const char *quirkname[] = { "NONE", /* config string for "no value" */ "FLOAT", "TRANSSZ", "ANYWHERE", "XTERM_FONTADJ", "FULLSCREEN", "FOCUSPREV", }; /* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */ #define SWM_Q_WS "\n|+ \t" int parsequirks(char *qstr, unsigned long *quirk) { char *cp, *name; int i; if (quirk == NULL) return (1); cp = qstr; *quirk = 0; while ((name = strsep(&cp, SWM_Q_WS)) != NULL) { if (cp) cp += (long)strspn(cp, SWM_Q_WS); for (i = 0; i < LENGTH(quirkname); i++) { if (!strncasecmp(name, quirkname[i], SWM_QUIRK_LEN)) { DNPRINTF(SWM_D_QUIRK, "parsequirks: %s\n", name); if (i == 0) { *quirk = 0; return (0); } *quirk |= 1 << (i-1); break; } } if (i >= LENGTH(quirkname)) { DNPRINTF(SWM_D_QUIRK, "parsequirks: invalid quirk [%s]\n", name); return (1); } } return (0); } void quirk_insert(const char *class, const char *name, unsigned long quirk) { struct quirk *qp; DNPRINTF(SWM_D_QUIRK, "quirk_insert: %s:%s [%lu]\n", class, name, quirk); if ((qp = malloc(sizeof *qp)) == NULL) err(1, "quirk_insert: malloc"); if ((qp->class = strdup(class)) == NULL) err(1, "quirk_insert: strdup"); if ((qp->name = strdup(name)) == NULL) err(1, "quirk_insert: strdup"); qp->quirk = quirk; 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 [%lu]\n", qp->class, qp->name, qp->quirk); TAILQ_REMOVE(&quirks, qp, entry); free(qp->class); free(qp->name); free(qp); DNPRINTF(SWM_D_QUIRK, "quirk_remove: leave\n"); } void quirk_replace(struct quirk *qp, const char *class, const char *name, unsigned long quirk) { DNPRINTF(SWM_D_QUIRK, "quirk_replace: %s:%s [%lu]\n", qp->class, qp->name, qp->quirk); quirk_remove(qp); quirk_insert(class, name, quirk); DNPRINTF(SWM_D_QUIRK, "quirk_replace: leave\n"); } void setquirk(const char *class, const char *name, unsigned long quirk) { struct quirk *qp; DNPRINTF(SWM_D_QUIRK, "setquirk: enter %s:%s [%lu]\n", class, name, quirk); TAILQ_FOREACH(qp, &quirks, entry) { if (!strcmp(qp->class, class) && !strcmp(qp->name, name)) { if (!quirk) quirk_remove(qp); else quirk_replace(qp, class, name, quirk); DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n"); return; } } if (!quirk) { warnx("error: setquirk: cannot find class/name combination"); return; } quirk_insert(class, name, quirk); DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n"); } int setconfquirk(char *selector, char *value, int flags) { char *cp, *class, *name; int retval; unsigned long quirks; if (selector == NULL) return (0); if ((cp = strchr(selector, ':')) == NULL) return (0); *cp = '\0'; class = selector; name = cp + 1; if ((retval = parsequirks(value, &quirks)) == 0) setquirk(class, name, quirks); return (retval); } void setup_quirks(void) { setquirk("MPlayer", "xv", SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV); setquirk("OpenOffice.org 3.2", "VCLSalFrame", SWM_Q_FLOAT); setquirk("Firefox-bin", "firefox-bin", SWM_Q_TRANSSZ); setquirk("Firefox", "Dialog", SWM_Q_FLOAT); setquirk("Gimp", "gimp", SWM_Q_FLOAT | SWM_Q_ANYWHERE); setquirk("XTerm", "xterm", SWM_Q_XTERM_FONTADJ); setquirk("xine", "Xine Window", SWM_Q_FLOAT | SWM_Q_ANYWHERE); setquirk("Xitk", "Xitk Combo", SWM_Q_FLOAT | SWM_Q_ANYWHERE); setquirk("xine", "xine Panel", SWM_Q_FLOAT | SWM_Q_ANYWHERE); setquirk("Xitk", "Xine Window", SWM_Q_FLOAT | SWM_Q_ANYWHERE); setquirk("xine", "xine Video Fullscreen Window", SWM_Q_FULLSCREEN | SWM_Q_FLOAT); setquirk("pcb", "pcb", SWM_Q_FLOAT); setquirk("SDL_App", "SDL_App", SWM_Q_FLOAT | SWM_Q_FULLSCREEN); } /* conf file stuff */ #define SWM_CONF_FILE "spectrwm.conf" #define SWM_CONF_FILE_OLD "scrotwm.conf" enum { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_BAR_BORDER_WIDTH, SWM_S_STACK_ENABLED, SWM_S_CLOCK_ENABLED, SWM_S_CLOCK_FORMAT, SWM_S_CYCLE_EMPTY, SWM_S_CYCLE_VISIBLE, SWM_S_SS_ENABLED, SWM_S_TERM_WIDTH, SWM_S_TITLE_CLASS_ENABLED, SWM_S_TITLE_NAME_ENABLED, SWM_S_WINDOW_NAME_ENABLED, SWM_S_URGENT_ENABLED, SWM_S_FOCUS_MODE, SWM_S_DISABLE_BORDER, SWM_S_BORDER_WIDTH, SWM_S_BAR_FONT, SWM_S_BAR_ACTION, SWM_S_SPAWN_TERM, SWM_S_SS_APP, SWM_S_DIALOG_RATIO, SWM_S_BAR_AT_BOTTOM, SWM_S_VERBOSE_LAYOUT, SWM_S_BAR_JUSTIFY }; int setconfvalue(char *selector, char *value, int flags) { int i; char *b; switch (flags) { case SWM_S_BAR_DELAY: bar_delay = atoi(value); break; case SWM_S_BAR_ENABLED: bar_enabled = atoi(value); break; case SWM_S_BAR_BORDER_WIDTH: bar_border_width = atoi(value); break; case SWM_S_BAR_AT_BOTTOM: bar_at_bottom = atoi(value); break; case SWM_S_BAR_JUSTIFY: if (!strcmp(value, "left")) bar_justify = SWM_BAR_JUSTIFY_LEFT; else if (!strcmp(value, "center")) bar_justify = SWM_BAR_JUSTIFY_CENTER; else if (!strcmp(value, "right")) bar_justify = SWM_BAR_JUSTIFY_RIGHT; else errx(1, "invalid bar_justify"); break; case SWM_S_STACK_ENABLED: stack_enabled = atoi(value); break; case SWM_S_CLOCK_ENABLED: clock_enabled = atoi(value); 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); break; case SWM_S_CYCLE_VISIBLE: cycle_visible = atoi(value); break; case SWM_S_SS_ENABLED: ss_enabled = atoi(value); break; case SWM_S_TERM_WIDTH: term_width = atoi(value); break; case SWM_S_TITLE_CLASS_ENABLED: title_class_enabled = atoi(value); break; case SWM_S_WINDOW_NAME_ENABLED: window_name_enabled = atoi(value); break; case SWM_S_TITLE_NAME_ENABLED: title_name_enabled = atoi(value); break; case SWM_S_URGENT_ENABLED: urgent_enabled = atoi(value); break; case SWM_S_FOCUS_MODE: if (!strcmp(value, "default")) focus_mode = SWM_FOCUS_DEFAULT; else if (!strcmp(value, "follow_cursor")) focus_mode = SWM_FOCUS_FOLLOW; else if (!strcmp(value, "synergy")) focus_mode = SWM_FOCUS_SYNERGY; else errx(1, "focus_mode"); break; case SWM_S_DISABLE_BORDER: disable_border = atoi(value); break; case SWM_S_BORDER_WIDTH: border_width = atoi(value); 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); break; case SWM_S_BAR_ACTION: free(bar_argv[0]); if ((bar_argv[0] = strdup(value)) == NULL) err(1, "setconfvalue: bar_action"); break; case SWM_S_SPAWN_TERM: free(spawn_term[0]); if ((spawn_term[0] = strdup(value)) == NULL) err(1, "setconfvalue: spawn_term"); break; case SWM_S_SS_APP: 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_VERBOSE_LAYOUT: verbose_layout = atoi(value); 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; default: return (1); } return (0); } int setconfmodkey(char *selector, char *value, int flags) { if (!strncasecmp(value, "Mod1", strlen("Mod1"))) update_modkey(Mod1Mask); else if (!strncasecmp(value, "Mod2", strlen("Mod2"))) update_modkey(Mod2Mask); else if (!strncasecmp(value, "Mod3", strlen("Mod3"))) update_modkey(Mod3Mask); else if (!strncasecmp(value, "Mod4", strlen("Mod4"))) update_modkey(Mod4Mask); else return (1); return (0); } int setconfcolor(char *selector, char *value, int flags) { setscreencolor(value, ((selector == NULL)?-1:atoi(selector)), flags); return (0); } int setconfregion(char *selector, char *value, int flags) { custom_region(value); return (0); } int setautorun(char *selector, char *value, int flags) { int ws_id; char s[1024]; char *ap, *sp = s; union arg a; int argc = 0; long pid; struct pid_e *p; 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 >= SWM_WS_MAX) errx(1, "autorun: invalid workspace %d", ws_id + 1); /* * 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, 1); /* NOTREACHED */ _exit(1); } free(a.argv); /* 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(char *selector, char *value, int flags) { int ws_id, i, x, mg, ma, si, raise; int st = SWM_V_STACK; char s[1024]; struct workspace *ws; 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, &raise, s) != 6) errx(1, "invalid layout entry, should be 'ws[]:" "::::" "'"); ws_id--; if (ws_id < 0 || ws_id >= SWM_WS_MAX) errx(1, "layout: invalid workspace %d", ws_id + 1); if (!strcasecmp(s, "vertical")) st = SWM_V_STACK; else if (!strcasecmp(s, "horizontal")) st = SWM_H_STACK; else if (!strcasecmp(s, "fullscreen")) st = SWM_MAX_STACK; else errx(1, "invalid layout entry, should be 'ws[]:" "::::" "'"); for (i = 0; i < ScreenCount(display); i++) { ws = (struct workspace *)&screens[i].ws; ws[ws_id].cur_layout = &layouts[st]; ws[ws_id].always_raise = raise; 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); stack(); } /* 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(); } /* 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); stack(); } } return (0); } /* config options */ struct config_option { char *optname; int (*func)(char*, char*, int); int funcflags; }; struct config_option configopt[] = { { "bar_enabled", setconfvalue, SWM_S_BAR_ENABLED }, { "bar_at_bottom", setconfvalue, SWM_S_BAR_AT_BOTTOM }, { "bar_border", setconfcolor, SWM_S_COLOR_BAR_BORDER }, { "bar_border_width", setconfvalue, SWM_S_BAR_BORDER_WIDTH }, { "bar_color", setconfcolor, SWM_S_COLOR_BAR }, { "bar_font_color", setconfcolor, SWM_S_COLOR_BAR_FONT }, { "bar_font", setconfvalue, SWM_S_BAR_FONT }, { "bar_action", setconfvalue, SWM_S_BAR_ACTION }, { "bar_delay", setconfvalue, SWM_S_BAR_DELAY }, { "bar_justify", setconfvalue, SWM_S_BAR_JUSTIFY }, { "keyboard_mapping", setkeymapping, 0 }, { "bind", setconfbinding, 0 }, { "stack_enabled", setconfvalue, SWM_S_STACK_ENABLED }, { "clock_enabled", setconfvalue, SWM_S_CLOCK_ENABLED }, { "clock_format", setconfvalue, SWM_S_CLOCK_FORMAT }, { "color_focus", setconfcolor, SWM_S_COLOR_FOCUS }, { "color_unfocus", setconfcolor, SWM_S_COLOR_UNFOCUS }, { "cycle_empty", setconfvalue, SWM_S_CYCLE_EMPTY }, { "cycle_visible", setconfvalue, SWM_S_CYCLE_VISIBLE }, { "dialog_ratio", setconfvalue, SWM_S_DIALOG_RATIO }, { "verbose_layout", setconfvalue, SWM_S_VERBOSE_LAYOUT }, { "modkey", setconfmodkey, 0 }, { "program", setconfspawn, 0 }, { "quirk", setconfquirk, 0 }, { "region", setconfregion, 0 }, { "spawn_term", setconfvalue, SWM_S_SPAWN_TERM }, { "screenshot_enabled", setconfvalue, SWM_S_SS_ENABLED }, { "screenshot_app", setconfvalue, SWM_S_SS_APP }, { "window_name_enabled", setconfvalue, SWM_S_WINDOW_NAME_ENABLED }, { "urgent_enabled", setconfvalue, SWM_S_URGENT_ENABLED }, { "term_width", setconfvalue, SWM_S_TERM_WIDTH }, { "title_class_enabled", setconfvalue, SWM_S_TITLE_CLASS_ENABLED }, { "title_name_enabled", setconfvalue, SWM_S_TITLE_NAME_ENABLED }, { "focus_mode", setconfvalue, SWM_S_FOCUS_MODE }, { "disable_border", setconfvalue, SWM_S_DISABLE_BORDER }, { "border_width", setconfvalue, SWM_S_BORDER_WIDTH }, { "autorun", setautorun, 0 }, { "layout", setlayout, 0 }, }; int conf_load(char *filename, int keymapping) { FILE *config; char *line, *cp, *optsub, *optval; size_t linelen, lineno = 0; int wordlen, i, optind; struct config_option *opt; DNPRINTF(SWM_D_CONF, "conf_load: begin\n"); if (filename == NULL) { warnx("conf_load: no filename"); return (1); } if ((config = fopen(filename, "r")) == NULL) { warn("conf_load: fopen: %s", filename); return (1); } while (!feof(config)) { if ((line = fparseln(config, &linelen, &lineno, NULL, 0)) == 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 */ free(line); continue; } /* get config option */ wordlen = strcspn(cp, "=[ \t\n"); if (wordlen == 0) { warnx("%s: line %zd: no option found", filename, lineno); goto out; } optind = -1; for (i = 0; i < LENGTH(configopt); i++) { opt = &configopt[i]; if (!strncasecmp(cp, opt->optname, wordlen) && strlen(opt->optname) == wordlen) { optind = i; break; } } if (optind == -1) { warnx("%s: line %zd: unknown option %.*s", filename, lineno, wordlen, cp); goto out; } if (keymapping && strcmp(opt->optname, "bind")) { warnx("%s: line %zd: invalid option %.*s", filename, lineno, wordlen, cp); goto out; } cp += wordlen; cp += strspn(cp, " \t\n"); /* eat whitespace */ /* get [selector] if any */ optsub = NULL; if (*cp == '[') { cp++; wordlen = strcspn(cp, "]"); if (*cp != ']') { if (wordlen == 0) { warnx("%s: line %zd: syntax error", filename, lineno); goto out; } if (asprintf(&optsub, "%.*s", wordlen, cp) == -1) { warnx("%s: line %zd: unable to allocate" "memory for selector", filename, lineno); goto out; } } cp += wordlen; cp += strspn(cp, "] \t\n"); /* eat trailing */ } cp += strspn(cp, "= \t\n"); /* eat trailing */ /* get RHS value */ optval = strdup(cp); /* call function to deal with it all */ if (configopt[optind].func(optsub, optval, configopt[optind].funcflags) != 0) errx(1, "%s: line %zd: invalid data for %s", filename, lineno, configopt[optind].optname); free(optval); free(optsub); free(line); } fclose(config); DNPRINTF(SWM_D_CONF, "conf_load: end\n"); return (0); out: free(line); fclose(config); DNPRINTF(SWM_D_CONF, "conf_load: end with error.\n"); return (1); } void set_child_transient(struct ws_win *win, Window *trans) { struct ws_win *parent, *w; XWMHints *wmh = NULL; struct swm_region *r; struct workspace *ws; parent = find_window(win->transient); if (parent) parent->child_trans = win; else { DNPRINTF(SWM_D_MISC, "set_child_transient: parent doesn't exist" " for 0x%lx trans 0x%lx\n", win->id, win->transient); if (win->hints == NULL) { warnx("no hints for 0x%lx", win->id); return; } r = root_to_region(win->wa.root); ws = r->ws; /* parent doen't exist in our window list */ TAILQ_FOREACH(w, &ws->winlist, entry) { if (wmh) XFree(wmh); if ((wmh = XGetWMHints(display, w->id)) == NULL) { warnx("can't get hints for 0x%lx", w->id); continue; } if (win->hints->window_group != wmh->window_group) continue; w->child_trans = win; win->transient = w->id; *trans = w->id; DNPRINTF(SWM_D_MISC, "set_child_transient: asjusting " "transient to 0x%lx\n", win->transient); break; } } if (wmh) XFree(wmh); } long window_get_pid(Window win) { Atom actual_type_return; int actual_format_return = 0; unsigned long nitems_return = 0; unsigned long bytes_after_return = 0; long *pid = NULL; long ret = 0; const char *errstr; unsigned char *prop = NULL; if (XGetWindowProperty(display, win, XInternAtom(display, "_NET_WM_PID", False), 0, 1, False, XA_CARDINAL, &actual_type_return, &actual_format_return, &nitems_return, &bytes_after_return, (unsigned char**)(void*)&pid) != Success) goto tryharder; if (actual_type_return != XA_CARDINAL) goto tryharder; if (pid == NULL) goto tryharder; ret = *pid; XFree(pid); return (ret); tryharder: if (XGetWindowProperty(display, win, XInternAtom(display, "_SWM_PID", False), 0, SWM_PROPLEN, False, XA_STRING, &actual_type_return, &actual_format_return, &nitems_return, &bytes_after_return, &prop) != Success) return (0); if (actual_type_return != XA_STRING) return (0); if (prop == NULL) return (0); ret = strtonum((const char *)prop, 0, UINT_MAX, &errstr); /* ignore error because strtonum returns 0 anyway */ XFree(prop); return (ret); } struct ws_win * manage_window(Window id) { Window trans = 0; struct workspace *ws; struct ws_win *win, *ww; int format, i, ws_idx, n, border_me = 0; unsigned long nitems, bytes; Atom ws_idx_atom = 0, type; Atom *prot = NULL, *pp; unsigned char ws_idx_str[SWM_PROPLEN], *prop = NULL; struct swm_region *r; long mask = 0; const char *errstr; XWindowChanges wc; struct pid_e *p; struct quirk *qp; if ((win = find_window(id)) != NULL) return (win); /* already being managed */ /* see if we are on the unmanaged list */ if ((win = find_unmanaged_window(id)) != NULL) { DNPRINTF(SWM_D_MISC, "manage_window: previously unmanaged " "window: 0x%lx\n", win->id); TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry); if (win->transient) { set_child_transient(win, &trans); } if (trans && (ww = find_window(trans))) TAILQ_INSERT_AFTER(&win->ws->winlist, ww, win, entry); else TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry); ewmh_update_actions(win); return (win); } if ((win = calloc(1, sizeof(struct ws_win))) == NULL) err(1, "manage_window: calloc: failed to allocate memory for " "new window"); win->id = id; /* see if we need to override the workspace */ p = find_pid(window_get_pid(id)); /* Get all the window data in one shot */ ws_idx_atom = XInternAtom(display, "_SWM_WS", False); if (ws_idx_atom) { XGetWindowProperty(display, id, ws_idx_atom, 0, SWM_PROPLEN, False, XA_STRING, &type, &format, &nitems, &bytes, &prop); } XGetWindowAttributes(display, id, &win->wa); XGetWMNormalHints(display, id, &win->sh, &win->sh_mask); win->hints = XGetWMHints(display, id); XGetTransientForHint(display, id, &trans); if (trans) { win->transient = trans; set_child_transient(win, &trans); DNPRINTF(SWM_D_MISC, "manage_window: window: 0x%lx, " "transient: 0x%lx\n", win->id, win->transient); } /* get supported protocols */ if (XGetWMProtocols(display, id, &prot, &n)) { for (i = 0, pp = prot; i < n; i++, pp++) { if (*pp == takefocus) win->take_focus = 1; if (*pp == adelete) win->can_delete = 1; } if (prot) XFree(prot); } win->iconic = get_iconic(win); /* * Figure out where to put the window. If it was previously assigned to * a workspace (either by spawn() or manually moving), and isn't * transient, * put it in the same workspace */ r = root_to_region(win->wa.root); if (p) { ws = &r->s->ws[p->ws]; TAILQ_REMOVE(&pidlist, p, entry); free(p); p = NULL; } else if (prop && win->transient == 0) { DNPRINTF(SWM_D_PROP, "manage_window: get _SWM_WS: %s\n", prop); ws_idx = strtonum((const char *)prop, 0, 9, &errstr); if (errstr) { DNPRINTF(SWM_D_EVENT, "manage_window: window: #%s: %s", errstr, prop); } ws = &r->s->ws[ws_idx]; } else { ws = r->ws; /* this should launch transients in the same ws as parent */ if (id && trans) if ((ww = find_window(trans)) != NULL) if (ws->r) { ws = ww->ws; if (ww->ws->r) r = ww->ws->r; else warnx("manage_window: fix this " "bug mcbride"); border_me = 1; } } /* set up the window layout */ win->id = id; win->ws = ws; win->s = r->s; /* this never changes */ if (trans && (ww = find_window(trans))) TAILQ_INSERT_AFTER(&ws->winlist, ww, win, entry); else TAILQ_INSERT_TAIL(&ws->winlist, win, entry); WIDTH(win) = win->wa.width; HEIGHT(win) = win->wa.height; X(win) = win->wa.x; Y(win) = win->wa.y; win->g_floatvalid = 0; win->floatmaxed = 0; win->ewmh_flags = 0; /* Set window properties so we can remember this after reincarnation */ if (ws_idx_atom && prop == NULL && snprintf((char *)ws_idx_str, SWM_PROPLEN, "%d", ws->idx) < SWM_PROPLEN) { DNPRINTF(SWM_D_PROP, "manage_window: set _SWM_WS: %s\n", ws_idx_str); XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8, PropModeReplace, ws_idx_str, strlen((char *)ws_idx_str)); } if (prop) XFree(prop); ewmh_autoquirk(win); if (XGetClassHint(display, win->id, &win->ch)) { DNPRINTF(SWM_D_CLASS, "manage_window: class: %s, name: %s\n", win->ch.res_class, win->ch.res_name); /* java is retarded so treat it special */ if (strstr(win->ch.res_name, "sun-awt")) { win->java = 1; border_me = 1; } TAILQ_FOREACH(qp, &quirks, entry) { if (!strcmp(win->ch.res_class, qp->class) && !strcmp(win->ch.res_name, qp->name)) { DNPRINTF(SWM_D_CLASS, "manage_window: found: " "class: %s, name: %s\n", win->ch.res_class, win->ch.res_name); if (qp->quirk & SWM_Q_FLOAT) { win->floating = 1; border_me = 1; } win->quirks = qp->quirk; } } } /* alter window position if quirky */ if (win->quirks & SWM_Q_ANYWHERE) { win->manual = 1; /* don't center the quirky windows */ bzero(&wc, sizeof wc); mask = 0; if (bar_enabled && Y(win) < bar_height) { Y(win) = wc.y = bar_height; mask |= CWY; } if (WIDTH(win) + X(win) > WIDTH(r)) { X(win) = wc.x = WIDTH(r) - WIDTH(win) - 2; mask |= CWX; } border_me = 1; } /* 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, ShiftMask); for (i = 0; i < SWM_MAX_FONT_STEPS; i++) fake_keypress(win, XK_KP_Add, ShiftMask); } ewmh_get_win_state(win); ewmh_update_actions(win); ewmh_update_win_state(win, None, _NET_WM_STATE_REMOVE); /* border me */ if (border_me) { bzero(&wc, sizeof wc); wc.border_width = border_width; mask |= CWBorderWidth; XConfigureWindow(display, win->id, mask, &wc); } XSelectInput(display, id, EnterWindowMask | FocusChangeMask | PropertyChangeMask | StructureNotifyMask); /* floaters need to be mapped if they are in the current workspace */ if ((win->floating || win->transient) && (ws->idx == r->ws->idx)) XMapRaised(display, win->id); return (win); } void free_window(struct ws_win *win) { DNPRINTF(SWM_D_MISC, "free_window: window: 0x%lx\n", win->id); if (win == NULL) return; /* needed for restart wm */ set_win_state(win, WithdrawnState); TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry); if (win->ch.res_class) XFree(win->ch.res_class); if (win->ch.res_name) XFree(win->ch.res_name); kill_refs(win); /* paint memory */ memset(win, 0xff, sizeof *win); /* XXX kill later */ free(win); } void unmanage_window(struct ws_win *win) { struct ws_win *parent; if (win == NULL) return; DNPRINTF(SWM_D_MISC, "unmanage_window: window: 0x%lx\n", win->id); if (win->transient) { parent = find_window(win->transient); if (parent) parent->child_trans = NULL; } /* focus on root just in case */ XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime); focus_prev(win); if (win->hints) { XFree(win->hints); win->hints = NULL; } TAILQ_REMOVE(&win->ws->winlist, win, entry); TAILQ_INSERT_TAIL(&win->ws->unmanagedlist, win, entry); kill_refs(win); } void focus_magic(struct ws_win *win) { DNPRINTF(SWM_D_FOCUS, "focus_magic: window: 0x%lx\n", WINID(win)); if (win == NULL) { /* if there are no windows clear the status-bar */ bar_check_opts(); return; } if (win->child_trans) { /* win = parent & has a transient so focus on that */ if (win->java) { focus_win(win->child_trans); if (win->child_trans->take_focus) client_msg(win, takefocus); } else { /* make sure transient hasn't disappeared */ if (validate_win(win->child_trans) == 0) { focus_win(win->child_trans); if (win->child_trans->take_focus) client_msg(win->child_trans, takefocus); } else { win->child_trans = NULL; focus_win(win); if (win->take_focus) client_msg(win, takefocus); } } } else { /* regular focus */ focus_win(win); if (win->take_focus) client_msg(win, takefocus); } } void expose(XEvent *e) { DNPRINTF(SWM_D_EVENT, "expose: window: 0x%lx\n", e->xexpose.window); } void keypress(XEvent *e) { KeySym keysym; XKeyEvent *ev = &e->xkey; struct key *kp; struct swm_region *r; keysym = XKeycodeToKeysym(display, (KeyCode)ev->keycode, 0); if ((kp = key_lookup(CLEANMASK(ev->state), keysym)) == NULL) return; if (keyfuncs[kp->funcid].func == NULL) return; r = root_to_region(ev->root); if (kp->funcid == kf_spawn_custom) spawn_custom(r, &(keyfuncs[kp->funcid].args), kp->spawn_name); else keyfuncs[kp->funcid].func(r, &(keyfuncs[kp->funcid].args)); } void buttonpress(XEvent *e) { struct ws_win *win; int i, action; XButtonPressedEvent *ev = &e->xbutton; if ((win = find_window(ev->window)) == NULL) return; focus_magic(win); action = client_click; for (i = 0; i < LENGTH(buttons); i++) if (action == buttons[i].action && buttons[i].func && buttons[i].button == ev->button && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) buttons[i].func(win, &buttons[i].args); } void configurerequest(XEvent *e) { XConfigureRequestEvent *ev = &e->xconfigurerequest; struct ws_win *win; int new = 0; XWindowChanges wc; if ((win = find_window(ev->window)) == NULL) if ((win = find_unmanaged_window(ev->window)) == NULL) new = 1; DNPRINTF(SWM_D_EVENT, "configurerequest: window: 0x%lx, new: %s\n", ev->window, YESNO(new)); if (new) { bzero(&wc, sizeof wc); wc.x = ev->x; wc.y = ev->y; wc.width = ev->width; wc.height = ev->height; wc.border_width = ev->border_width; wc.sibling = ev->above; wc.stack_mode = ev->detail; XConfigureWindow(display, ev->window, ev->value_mask, &wc); } else { config_win(win, ev); } } void configurenotify(XEvent *e) { struct ws_win *win; DNPRINTF(SWM_D_EVENT, "configurenotify: window: 0x%lx\n", e->xconfigure.window); win = find_window(e->xconfigure.window); if (win) { XGetWMNormalHints(display, win->id, &win->sh, &win->sh_mask); adjust_font(win); if (font_adjusted) stack(); if (focus_mode == SWM_FOCUS_DEFAULT) drain_enter_notify(); } } void destroynotify(XEvent *e) { struct ws_win *win; XDestroyWindowEvent *ev = &e->xdestroywindow; DNPRINTF(SWM_D_EVENT, "destroynotify: window: 0x%lx\n", ev->window); if ((win = find_window(ev->window)) == NULL) { if ((win = find_unmanaged_window(ev->window)) == NULL) return; free_window(win); return; } /* make sure we focus on something */ win->floating = 0; unmanage_window(win); stack(); if (focus_mode == SWM_FOCUS_DEFAULT) drain_enter_notify(); free_window(win); } void enternotify(XEvent *e) { XCrossingEvent *ev = &e->xcrossing; XEvent cne; struct ws_win *win; #if 0 struct ws_win *w; Window focus_return; int revert_to_return; #endif DNPRINTF(SWM_D_FOCUS, "enternotify: window: 0x%lx, mode: %d, detail: " "%d, root: 0x%lx, subwindow: 0x%lx, same_screen: %s, focus: %s, " "state: %d\n", ev->window, ev->mode, ev->detail, ev->root, ev->subwindow, YESNO(ev->same_screen), YESNO(ev->focus), ev->state); if (ev->mode != NotifyNormal) { DNPRINTF(SWM_D_EVENT, "skip enternotify: generated by " "cursor grab.\n"); return; } switch (focus_mode) { case SWM_FOCUS_DEFAULT: break; case SWM_FOCUS_FOLLOW: break; case SWM_FOCUS_SYNERGY: #if 0 /* * all these checks need to be in this order because the * XCheckTypedWindowEvent relies on weeding out the previous events * * making this code an option would enable a follow mouse for focus * feature */ /* * state is set when we are switching workspaces and focus is set when * the window or a subwindow already has focus (occurs during restart). * * Only honor the focus flag if last_focus_event is not FocusOut, * this allows spectrwm to continue to control focus when another * program is also playing with it. */ if (ev->state || (ev->focus && last_focus_event != FocusOut)) { DNPRINTF(SWM_D_EVENT, "ignoring enternotify: focus\n"); return; } /* * happens when a window is created or destroyed and the border * crosses the mouse pointer and when switching ws * * we need the subwindow test to see if we came from root in order * to give focus to floaters */ if (ev->mode == NotifyNormal && ev->detail == NotifyVirtual && ev->subwindow == 0) { DNPRINTF(SWM_D_EVENT, "ignoring enternotify: NotifyVirtual\n"); return; } /* this window already has focus */ if (ev->mode == NotifyNormal && ev->detail == NotifyInferior) { DNPRINTF(SWM_D_EVENT, "ignoring enternotify: win has focus\n"); return; } /* this window is being deleted or moved to another ws */ if (XCheckTypedWindowEvent(display, ev->window, ConfigureNotify, &cne) == True) { DNPRINTF(SWM_D_EVENT, "ignoring enternotify: configurenotify\n"); XPutBackEvent(display, &cne); return; } if ((win = find_window(ev->window)) == NULL) { DNPRINTF(SWM_D_EVENT, "ignoring enternotify: win == NULL\n"); return; } /* * In fullstack kill all enters unless they come from a different ws * (i.e. another region) or focus has been grabbed externally. */ if (win->ws->cur_layout->flags & SWM_L_FOCUSPREV && last_focus_event != FocusOut) { XGetInputFocus(display, &focus_return, &revert_to_return); if ((w = find_window(focus_return)) == NULL || w->ws == win->ws) { DNPRINTF(SWM_D_EVENT, "ignoring event: fullstack\n"); return; } } #endif break; } if ((win = find_window(ev->window)) == NULL) { DNPRINTF(SWM_D_EVENT, "skip enternotify: window is NULL\n"); return; } /* * if we have more enternotifies let them handle it in due time */ if (XCheckTypedEvent(display, EnterNotify, &cne) == True) { DNPRINTF(SWM_D_EVENT, "ignoring enternotify: got more enternotify\n"); XPutBackEvent(display, &cne); return; } focus_magic(win); } /* lets us use one switch statement for arbitrary mode/detail combinations */ #define MERGE_MEMBERS(a,b) (((a & 0xffff) << 16) | (b & 0xffff)) void focusevent(XEvent *e) { #if 0 struct ws_win *win; u_int32_t mode_detail; XFocusChangeEvent *ev = &e->xfocus; DNPRINTF(SWM_D_EVENT, "focusevent: %s window: 0x%lx mode: %d " "detail: %d\n", ev->type == FocusIn ? "entering" : "leaving", ev->window, ev->mode, ev->detail); if (last_focus_event == ev->type) { DNPRINTF(SWM_D_FOCUS, "ignoring focusevent: bad ordering\n"); return; } last_focus_event = ev->type; mode_detail = MERGE_MEMBERS(ev->mode, ev->detail); switch (mode_detail) { /* synergy client focus operations */ case MERGE_MEMBERS(NotifyNormal, NotifyNonlinear): case MERGE_MEMBERS(NotifyNormal, NotifyNonlinearVirtual): /* synergy server focus operations */ case MERGE_MEMBERS(NotifyWhileGrabbed, NotifyNonlinear): /* Entering applications like rdesktop that mangle the pointer */ case MERGE_MEMBERS(NotifyNormal, NotifyPointer): if ((win = find_window(e->xfocus.window)) != NULL && win->ws->r) XSetWindowBorder(display, win->id, win->ws->r->s->c[ev->type == FocusIn ? SWM_S_COLOR_FOCUS : SWM_S_COLOR_UNFOCUS].color); break; default: warnx("ignoring focusevent"); DNPRINTF(SWM_D_FOCUS, "ignoring focusevent\n"); break; } #endif } void mapnotify(XEvent *e) { struct ws_win *win; XMapEvent *ev = &e->xmap; DNPRINTF(SWM_D_EVENT, "mapnotify: window: 0x%lx\n", ev->window); win = manage_window(ev->window); if (win) set_win_state(win, NormalState); } void mappingnotify(XEvent *e) { XMappingEvent *ev = &e->xmapping; XRefreshKeyboardMapping(ev); if (ev->request == MappingKeyboard) grabkeys(); } void maprequest(XEvent *e) { struct ws_win *win; struct swm_region *r; XWindowAttributes wa; XMapRequestEvent *ev = &e->xmaprequest; DNPRINTF(SWM_D_EVENT, "maprequest: window: 0x%lx\n", e->xmaprequest.window); if (!XGetWindowAttributes(display, ev->window, &wa)) return; if (wa.override_redirect) return; win = manage_window(e->xmaprequest.window); if (win == NULL) return; /* can't happen */ stack(); /* make new win focused */ r = root_to_region(win->wa.root); if (win->ws == r->ws) focus_magic(win); } void propertynotify(XEvent *e) { struct ws_win *win; XPropertyEvent *ev = &e->xproperty; #ifdef SWM_DEBUG char *name; name = XGetAtomName(display, ev->atom); DNPRINTF(SWM_D_EVENT, "propertynotify: window: 0x%lx, atom: %s\n", ev->window, name); XFree(name); #endif win = find_window(ev->window); if (win == NULL) return; if (ev->state == PropertyDelete && ev->atom == a_swm_iconic) { update_iconic(win, 0); XMapRaised(display, win->id); stack(); focus_win(win); return; } switch (ev->atom) { #if 0 case XA_WM_NORMAL_HINTS: long mask; XGetWMNormalHints(display, win->id, &win->sh, &mask); warnx("normal hints: flag 0x%x", win->sh.flags); if (win->sh.flags & PMinSize) { WIDTH(win) = win->sh.min_width; HEIGHT(win) = win->sh.min_height; warnx("min %d %d", WIDTH(win), HEIGHT(win)); } XMoveResizeWindow(display, win->id, X(win), Y(win), WIDTH(win), HEIGHT(win)); #endif case XA_WM_CLASS: if (title_name_enabled || title_class_enabled) bar_update(); break; case XA_WM_NAME: if (window_name_enabled) bar_update(); break; default: break; } } void unmapnotify(XEvent *e) { struct ws_win *win; DNPRINTF(SWM_D_EVENT, "unmapnotify: window: 0x%lx\n", e->xunmap.window); /* determine if we need to help unmanage this window */ win = find_window(e->xunmap.window); if (win == NULL) return; if (getstate(e->xunmap.window) == NormalState) { unmanage_window(win); stack(); /* giant hack for apps that don't destroy transient windows */ /* eat a bunch of events to prevent remanaging the window */ XEvent cne; while (XCheckWindowEvent(display, e->xunmap.window, EnterWindowMask, &cne)) ; while (XCheckWindowEvent(display, e->xunmap.window, StructureNotifyMask, &cne)) ; while (XCheckWindowEvent(display, e->xunmap.window, SubstructureNotifyMask, &cne)) ; /* resend unmap because we ated it */ XUnmapWindow(display, e->xunmap.window); } if (focus_mode == SWM_FOCUS_DEFAULT) drain_enter_notify(); } void visibilitynotify(XEvent *e) { int i; struct swm_region *r; DNPRINTF(SWM_D_EVENT, "visibilitynotify: window: 0x%lx\n", e->xvisibility.window); if (e->xvisibility.state == VisibilityUnobscured) for (i = 0; i < ScreenCount(display); i++) TAILQ_FOREACH(r, &screens[i].rl, entry) if (e->xvisibility.window == r->bar_window) bar_update(); } void clientmessage(XEvent *e) { XClientMessageEvent *ev; struct ws_win *win; ev = &e->xclient; win = find_window(ev->window); if (win == NULL) return; DNPRINTF(SWM_D_EVENT, "clientmessage: window: 0x%lx, type: %ld\n", ev->window, ev->message_type); if (ev->message_type == ewmh[_NET_ACTIVE_WINDOW].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_ACTIVE_WINDOW\n"); focus_win(win); } if (ev->message_type == ewmh[_NET_CLOSE_WINDOW].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_CLOSE_WINDOW\n"); if (win->can_delete) client_msg(win, adelete); else XKillClient(display, win->id); } if (ev->message_type == ewmh[_NET_MOVERESIZE_WINDOW].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_MOVERESIZE_WINDOW\n"); if (win->floating) { if (ev->data.l[0] & (1<<8)) /* x */ X(win) = ev->data.l[1]; if (ev->data.l[0] & (1<<9)) /* y */ Y(win) = ev->data.l[2]; if (ev->data.l[0] & (1<<10)) /* width */ WIDTH(win) = ev->data.l[3]; if (ev->data.l[0] & (1<<11)) /* height */ HEIGHT(win) = ev->data.l[4]; update_window(win); } else { /* TODO: Change stack sizes */ /* notify no change was made. */ config_win(win, NULL); } } if (ev->message_type == ewmh[_NET_WM_STATE].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_STATE\n"); ewmh_update_win_state(win, ev->data.l[1], ev->data.l[0]); if (ev->data.l[2]) ewmh_update_win_state(win, ev->data.l[2], ev->data.l[0]); stack(); } } int xerror_start(Display *d, XErrorEvent *ee) { other_wm = 1; return (-1); } int xerror(Display *d, XErrorEvent *ee) { /* warnx("error: %p %p", display, ee); */ return (-1); } int active_wm(void) { other_wm = 0; xerrorxlib = XSetErrorHandler(xerror_start); /* this causes an error if some other window manager is running */ XSelectInput(display, DefaultRootWindow(display), SubstructureRedirectMask); XSync(display, False); if (other_wm) return (1); XSetErrorHandler(xerror); XSync(display, False); return (0); } void new_region(struct swm_screen *s, int x, int y, int w, int h) { struct swm_region *r, *n; struct workspace *ws = NULL; int i; 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; XDestroyWindow(display, r->bar_window); 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 < SWM_WS_MAX; i++) if (s->ws[i].r == NULL) { ws = &s->ws[i]; break; } } if (ws == NULL) errx(1, "new_region: no free workspaces"); X(r) = x; Y(r) = y; WIDTH(r) = w; HEIGHT(r) = h; r->s = s; r->ws = ws; r->ws_prior = NULL; ws->r = r; outputs++; TAILQ_INSERT_TAIL(&s->rl, r, entry); } void scan_xrandr(int i) { #ifdef SWM_XRR_HAS_CRTC XRRCrtcInfo *ci; XRRScreenResources *sr; int c; int ncrtc = 0; #endif /* SWM_XRR_HAS_CRTC */ struct swm_region *r; if (i >= ScreenCount(display)) errx(1, "scan_xrandr: invalid screen"); /* remove any old regions */ while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) { r->ws->old_r = r->ws->r = NULL; XDestroyWindow(display, r->bar_window); TAILQ_REMOVE(&screens[i].rl, r, entry); TAILQ_INSERT_TAIL(&screens[i].orl, r, entry); } outputs = 0; /* map virtual screens onto physical screens */ #ifdef SWM_XRR_HAS_CRTC if (xrandr_support) { sr = XRRGetScreenResources(display, screens[i].root); if (sr == NULL) new_region(&screens[i], 0, 0, DisplayWidth(display, i), DisplayHeight(display, i)); else ncrtc = sr->ncrtc; for (c = 0, ci = NULL; c < ncrtc; c++) { ci = XRRGetCrtcInfo(display, sr, sr->crtcs[c]); if (ci->noutput == 0) continue; if (ci != NULL && ci->mode == None) new_region(&screens[i], 0, 0, DisplayWidth(display, i), DisplayHeight(display, i)); else new_region(&screens[i], ci->x, ci->y, ci->width, ci->height); } if (ci) XRRFreeCrtcInfo(ci); XRRFreeScreenResources(sr); } else #endif /* SWM_XRR_HAS_CRTC */ { new_region(&screens[i], 0, 0, DisplayWidth(display, i), DisplayHeight(display, i)); } } void screenchange(XEvent *e) { XRRScreenChangeNotifyEvent *xe = (XRRScreenChangeNotifyEvent *)e; struct swm_region *r; int i; DNPRINTF(SWM_D_EVENT, "screenchange: root: 0x%lx\n", xe->root); if (!XRRUpdateConfiguration(e)) return; /* silly event doesn't include the screen index */ for (i = 0; i < ScreenCount(display); i++) if (screens[i].root == xe->root) break; if (i >= ScreenCount(display)) errx(1, "screenchange: screen not found"); /* brute force for now, just re-enumerate the regions */ scan_xrandr(i); /* add bars to all regions */ for (i = 0; i < ScreenCount(display); i++) TAILQ_FOREACH(r, &screens[i].rl, entry) bar_setup(r); stack(); if (focus_mode == SWM_FOCUS_DEFAULT) drain_enter_notify(); } void grab_windows(void) { Window d1, d2, *wins = NULL; XWindowAttributes wa; unsigned int no; int i, j; long state, manage; for (i = 0; i < ScreenCount(display); i++) { if (!XQueryTree(display, screens[i].root, &d1, &d2, &wins, &no)) continue; /* attach windows to a region */ /* normal windows */ for (j = 0; j < no; j++) { if (!XGetWindowAttributes(display, wins[j], &wa) || wa.override_redirect || XGetTransientForHint(display, wins[j], &d1)) continue; state = getstate(wins[j]); manage = state == IconicState; if (wa.map_state == IsViewable || manage) manage_window(wins[j]); } /* transient windows */ for (j = 0; j < no; j++) { if (!XGetWindowAttributes(display, wins[j], &wa) || wa.override_redirect) continue; state = getstate(wins[j]); manage = state == IconicState; if (XGetTransientForHint(display, wins[j], &d1) && manage) manage_window(wins[j]); } if (wins) { XFree(wins); wins = NULL; } } } void setup_screens(void) { int i, j, k; int errorbase, major, minor; struct workspace *ws; XGCValues gcv; if ((screens = calloc(ScreenCount(display), sizeof(struct swm_screen))) == NULL) err(1, "setup_screens: calloc: failed to allocate memory for " "screens"); /* initial Xrandr setup */ xrandr_support = XRRQueryExtension(display, &xrandr_eventbase, &errorbase); if (xrandr_support) if (XRRQueryVersion(display, &major, &minor) && major < 1) xrandr_support = 0; /* map physical screens */ for (i = 0; i < ScreenCount(display); i++) { DNPRINTF(SWM_D_WS, "setup_screens: init screen: %d\n", i); screens[i].idx = i; TAILQ_INIT(&screens[i].rl); TAILQ_INIT(&screens[i].orl); screens[i].root = RootWindow(display, i); /* set default colors */ setscreencolor("red", i + 1, SWM_S_COLOR_FOCUS); setscreencolor("rgb:88/88/88", i + 1, SWM_S_COLOR_UNFOCUS); setscreencolor("rgb:00/80/80", i + 1, SWM_S_COLOR_BAR_BORDER); setscreencolor("black", i + 1, SWM_S_COLOR_BAR); setscreencolor("rgb:a0/a0/a0", i + 1, SWM_S_COLOR_BAR_FONT); /* create graphics context on screen with default font color */ screens[i].bar_gc = XCreateGC(display, screens[i].root, 0, &gcv); XSetForeground(display, screens[i].bar_gc, screens[i].c[SWM_S_COLOR_BAR_FONT].color); /* set default cursor */ XDefineCursor(display, screens[i].root, XCreateFontCursor(display, XC_left_ptr)); /* 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->focus = NULL; ws->r = NULL; ws->old_r = NULL; 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_xrandr(i); if (xrandr_support) XRRSelectInput(display, screens[i].root, RRScreenChangeNotifyMask); } } void setup_globals(void) { if ((bar_fonts = strdup(SWM_BAR_FONTS)) == NULL) err(1, "setup_globals: strdup: failed to allocate memory."); if ((spawn_term[0] = strdup("xterm")) == 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."); } void workaround(void) { int i; Atom netwmcheck, netwmname, utf8_string; Window root, win; /* work around sun jdk bugs, code from wmname */ netwmcheck = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False); netwmname = XInternAtom(display, "_NET_WM_NAME", False); utf8_string = XInternAtom(display, "UTF8_STRING", False); for (i = 0; i < ScreenCount(display); i++) { root = screens[i].root; win = XCreateSimpleWindow(display,root, 0, 0, 1, 1, 0, screens[i].c[SWM_S_COLOR_UNFOCUS].color, screens[i].c[SWM_S_COLOR_UNFOCUS].color); XChangeProperty(display, root, netwmcheck, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&win,1); XChangeProperty(display, win, netwmcheck, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&win,1); XChangeProperty(display, win, netwmname, utf8_string, 8, PropModeReplace, (unsigned char*)"LG3D", strlen("LG3D")); } } int main(int argc, char *argv[]) { struct swm_region *r, *rr; struct ws_win *winfocus = NULL; struct timeval tv; union arg a; char conf[PATH_MAX], *cfile = NULL; struct stat sb; XEvent e; int xfd, i; fd_set rd; struct sigaction sact; start_argv = argv; warnx("Welcome to spectrwm V%s Build: %s", SPECTRWM_VERSION, buildstr); if (!setlocale(LC_CTYPE, "") || !setlocale(LC_TIME, "") || !XSupportsLocale()) warnx("no locale support"); if (!X_HAVE_UTF8_STRING) warnx("no UTF-8 support"); if (!(display = XOpenDisplay(0))) errx(1, "can not open display"); if (active_wm()) errx(1, "other wm running"); /* 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); astate = XInternAtom(display, "WM_STATE", False); aprot = XInternAtom(display, "WM_PROTOCOLS", False); adelete = XInternAtom(display, "WM_DELETE_WINDOW", False); takefocus = XInternAtom(display, "WM_TAKE_FOCUS", False); a_wmname = XInternAtom(display, "WM_NAME", False); a_netwmname = XInternAtom(display, "_NET_WM_NAME", False); a_utf8_string = XInternAtom(display, "UTF8_STRING", False); a_string = XInternAtom(display, "STRING", False); a_swm_iconic = XInternAtom(display, "_SWM_ICONIC", False); /* look for local and global conf file */ pwd = getpwuid(getuid()); if (pwd == NULL) errx(1, "invalid user: %d", getuid()); setup_globals(); setup_screens(); setup_keys(); 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) and refresh font color in bar graphics contexts */ if (cfile && conf_load(cfile, SWM_CONF_DEFAULT) == 0) for (i = 0; i < ScreenCount(display); ++i) XSetForeground(display, screens[i].bar_gc, screens[i].c[SWM_S_COLOR_BAR_FONT].color); setup_ewmh(); /* set some values to work around bad programs */ workaround(); /* grab existing windows (before we build the bars) */ grab_windows(); if (getenv("SWM_STARTED") == NULL) setenv("SWM_STARTED", "YES", 1); /* setup all bars */ for (i = 0; i < ScreenCount(display); i++) TAILQ_FOREACH(r, &screens[i].rl, entry) { if (winfocus == NULL) winfocus = TAILQ_FIRST(&r->ws->winlist); bar_setup(r); } unfocus_all(); grabkeys(); stack(); if (focus_mode == SWM_FOCUS_DEFAULT) drain_enter_notify(); xfd = ConnectionNumber(display); while (running) { while (XPending(display)) { XNextEvent(display, &e); if (running == 0) goto done; if (e.type < LASTEvent) { DNPRINTF(SWM_D_EVENTQ ,"XEvent: handled: %s, " "window: 0x%lx, type: %s (%d), %d remaining" "\n", YESNO(handler[e.type]), e.xany.window, geteventname(&e), e.type, QLength(display)); if (handler[e.type]) handler[e.type](&e); } else { DNPRINTF(SWM_D_EVENTQ, "XRandr Event: window: " "0x%lx, type: %s (%d)\n", e.xany.window, xrandr_geteventname(&e), e.type); switch (e.type - xrandr_eventbase) { case RRScreenChangeNotify: screenchange(&e); break; default: break; } } } /* if we are being restarted go focus on first window */ if (winfocus) { rr = winfocus->ws->r; if (rr == NULL) { /* not a visible window */ winfocus = NULL; continue; } /* move pointer to first screen if multi screen */ if (ScreenCount(display) > 1 || outputs > 1) XWarpPointer(display, None, rr->s[0].root, 0, 0, 0, 0, X(rr), Y(rr) + (bar_enabled ? bar_height : 0)); a.id = SWM_ARG_ID_FOCUSCUR; focus(rr, &a); winfocus = NULL; continue; } FD_ZERO(&rd); FD_SET(xfd, &rd); tv.tv_sec = 1; tv.tv_usec = 0; if (select(xfd + 1, &rd, NULL, NULL, &tv) == -1) if (errno != EINTR) DNPRINTF(SWM_D_MISC, "select failed"); if (restart_wm == 1) restart(NULL, NULL); if (search_resp == 1) search_do_resp(); if (running == 0) goto done; if (bar_alarm) { bar_alarm = 0; bar_update(); } } done: teardown_ewmh(); bar_extra_stop(); for (i = 0; i < ScreenCount(display); ++i) if (screens[i].bar_gc != NULL) XFreeGC(display, screens[i].bar_gc); XFreeFontSet(display, bar_fs); XCloseDisplay(display); return (0); } spectrwm-1.0.0/spectrwm.conf010064400017500000000000000053271171750520200151110ustar00marcowheel# PLEASE READ THE MAN PAGE BEFORE EDITING THIS FILE! # http://opensource.conformal.com/cgi-bin/man-cgi?spectrwm # colors for focussed and unfocussed window borders # NOTE: all colors in this file are in hex! see XQueryColor for examples # color_focus = red # color_unfocus = rgb:88/88/88 # bar settings # bar_enabled = 1 # bar_border_width = 1 # bar_border[1] = rgb:00/80/80 # bar_color[1] = black # bar_font_color[1] = rgb:a0/a0/a0 # bar_font = -*-terminus-medium-*-*-*-*-*-*-*-*-*-*-* # bar_action = baraction.sh # bar_delay = 1 # bar_justify = left # bar_at_bottom = 1 # stack_enabled = 1 # clock_enabled = 1 # clock_format = %a %b %d %R %Z %Y # title_name_enabled = 0 # title_class_enabled = 0 # window_name_enabled = 0 # verbose_layout = 1 # focus_mode = default # disable_border = 1 # border_width = 1 # urgent_enabled = 1 # spawn app # program[term] = xterm # program[screenshot_all] = screenshot.sh full # program[screenshot_wind] = screenshot.sh window # program[lock] = xlock # program[initscr] = initscreen.sh # program[menu] = dmenu_run -fn $bar_font -nb $bar_color -nf $bar_font_color -sb $bar_border -sf $bar_color # spawn_term = xterm # dialog box size ratio .3 >= r < 1 # dialog_ratio = 0.6 # Split a non-Xrandr 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]:xxxterm http://www.openbsd.org # workspace layout # layout = ws[1]:4:0:0:0:vertical # layout = ws[2]:0:0:0:0:horizontal # layout = ws[3]:0:0:0:0:fullscreen # mod key, (windows key is Mod4) (apple key on OSX is Mod2) # modkey = Mod1 # Clear key bindings and load new key bindings from the specified file. # This allows you to load pre-defined key bindings for your keyboard layout. # keyboard_mapping = ~/.spectrwm_us.conf # 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 # EXAMPLE: define firefox program and bind to key # program[firefox] = firefox http://spectrwm.org/ # bind[firefox] = MOD+Shift+b spectrwm-1.0.0/spectrwm_cz.conf010064400017500000000000000042751171750520200156060ustar00marcowheel# Key bindings for Czech Republic (cz) keyboards # unbind with: bind[] = bind[cycle_layout] = MOD+space bind[flip_layout] = MOD+Shift+backslash bind[stack_reset] = MOD+Shift+space bind[master_shrink] = MOD+h bind[master_grow] = MOD+l bind[master_add] = MOD+comma bind[master_del] = MOD+period bind[stack_inc] = MOD+Shift+comma bind[stack_dec] = MOD+Shift+period bind[swap_main] = MOD+Return bind[focus_next] = MOD+j bind[focus_prev] = MOD+k bind[swap_next] = MOD+Shift+j bind[swap_prev] = MOD+Shift+k bind[spawn_term] = MOD+Shift+Return bind[menu] = MOD+p bind[quit] = MOD+Shift+q bind[restart] = MOD+q bind[focus_main] = MOD+m 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_next] = MOD+Right bind[ws_prev] = MOD+Left bind[ws_next_all] = MOD+Up bind[ws_prev_all] = MOD+Down bind[ws_prior] = MOD+a bind[screen_next] = MOD+Shift+Right bind[screen_prev] = MOD+Shift+Left 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[bar_toggle] = MOD+b bind[focus_next] = MOD+Tab bind[focus_prev] = MOD+Shift+Tab bind[wind_kill] = MOD+Shift+x bind[wind_del] = MOD+x bind[screenshot_all] = MOD+s bind[screenshot_wind] = MOD+Shift+s bind[float_toggle] = MOD+t bind[version] = MOD+Shift+v bind[lock] = MOD+Shift+Delete bind[initscr] = MOD+Shift+i bind[iconify] = MOD+w bind[uniconify] = MOD+Shift+w bind[raise_toggle] = MOD+Shift+r bind[button2] = MOD+v bind[width_shrink] = MOD+equal bind[width_grow] = MOD+dead_acute bind[height_shrink] = MOD+Shift+equal bind[height_grow] = MOD+Shift+dead_acute bind[move_left] = MOD+uacute bind[move_right] = MOD+parenright bind[move_up] = MOD+Shift+uacute bind[move_down] = MOD+Shift+parenright bind[name_workspace] = MOD+Shift+slash bind[search_workspace] = MOD+slash bind[search_win] = MOD+f spectrwm-1.0.0/spectrwm_es.1010064400017500000000000000466261171750520200150220ustar00marcowheel.\" 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 NOMBRE .Nm spectrwm .Nd es un manejador de ventanas para X11 .Sh SYNOPSIS .Nm spectrwm .Sh DESCRIPCIN .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 pequeo, compacto y rpido. .Pp Cuando .Nm inicia, lo primero que hace es leer el archivo de configuracion, .Pa spectrwm.conf . Ver .Sx ARCHIVOS DE CONFIGURACIN . .Pp La siguiente anotacion se usa a travs 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 seccin de .Sx BINDINGS para las personalizaciones y configuraciones por defecto. .Sh ARCHIVOS DE CONFIGURACIN .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 opcin 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 informacin para la barra de estado, como la vida de la bateria. .It Cm bar_delay Frecuencia de actualizacin, en segundos, de los scripts de la barra de estado. .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 pequeas como para ser usables. Este relacin (ratio) es el tamao de la pantalla, por ejemplo 0.6 es 60% del tamao fsico 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 autodetecin 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 tamao de la fuente de la terminal para mantener el ancho de la terminal por encima de este nmero cuando la ventana cambia de tamao. 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 seccin .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 seccin de .Sx PROGRAMAS .It Cm bind Ns Bq Ar x Combinacin de teclas para una accin .Ar x . Ver la seccin .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 seccin .Sx QUIRKS .El .Pp Los colores deben ser especificados por la especificacin .Xr XQueryColor 3 y las fuentes por la especificacin .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 funcin de acciones. Ver la seccin .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 configuracin 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 CONFIGURACIN ), 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 asignacin (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 .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 .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 accines descripta a continuacin: .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 Disposicin de las ventanas .It Cm reset_layout Reiniciar la disposicin 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 estacin de trabajo .It Cm focus_prev Enfocar la anterior ventana en la estacin de trabajo .It Cm focus_main Enfocar en la ventana principal de la estacin de trabajo .It Cm swap_next Ejecutar con la siguiente ventana en la estacin de trabajo .It Cm swap_prev Ejecutar con la anterior ventana en la estacin 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 estacin de trabajo .It Cm wind_kill Destruir la ventana corriente en la estacin 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 estacin de trabajo .Ar n , donde .Ar n es 1 por 10 .It Cm ws_next Cambiar a la proxima estacin de trabajo con una ventana en ella .It Cm ws_prev Cambiar a la anterior estacin 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 configuracin: .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 # combinacin Tecla de Windows + q reinicia bind[] = Mod1+q # des-hace la combinacin 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 continuacin: .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 tamao de las ventanas transitorias que son demasiado pequeas utilizando dialog_ratio (ver .Sx ARCHIVOS DE CONFIGURACIN ) . .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 configuracin se ven a continuacin: .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 accin 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. .Pp .Sh SIGNALS Enviando .Nm una senal de HUP reinicia spectrwm. .Pp .Sh ARCHIVOS .Bl -tag -width "/etc/spectrwm.confXXX" -compact .It Pa ~/.spectrwm.conf .Nm archivo de configuracin 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 .Pp .Nm fue escrito por .An Marco Peereboom Aq marco@peereboom.us , .An Ryan Thomas McBride Aq mcbride@countersiege.com and .An Darrin Chandler Aq dwchandler@stilyagin.com . .Sh BUGS Actualmente el menu, se llama con .Cm M-p , depende de dmenu. spectrwm-1.0.0/spectrwm_es.conf010064400017500000000000000041341171750520200155730ustar00marcowheel# Key bindings for Spanish (es) keyboards # unbind with: bind[] = bind[cycle_layout] = MOD+space bind[flip_layout] = MOD+Shift+backslash bind[stack_reset] = MOD+Shift+space bind[master_shrink] = MOD+h bind[master_grow] = MOD+l bind[master_add] = MOD+comma bind[master_del] = MOD+period bind[stack_inc] = MOD+Shift+comma bind[stack_dec] = MOD+Shift+period bind[swap_main] = MOD+Return bind[focus_next] = MOD+j bind[focus_prev] = MOD+k bind[swap_next] = MOD+Shift+j bind[swap_prev] = MOD+Shift+k bind[spawn_term] = MOD+Shift+Return bind[menu] = MOD+p bind[quit] = MOD+Shift+q bind[restart] = MOD+q bind[focus_main] = MOD+m 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_next] = MOD+Right bind[ws_prev] = MOD+Left bind[ws_next_all] = MOD+Up bind[ws_prev_all] = MOD+Down bind[ws_prior] = MOD+a bind[screen_next] = MOD+Shift+Right bind[screen_prev] = MOD+Shift+Left 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[bar_toggle] = MOD+b bind[focus_next] = MOD+Tab bind[focus_prev] = MOD+Shift+Tab bind[wind_kill] = MOD+Shift+x bind[wind_del] = MOD+x bind[screenshot_all] = MOD+s bind[screenshot_wind] = MOD+Shift+s bind[float_toggle] = MOD+t bind[version] = MOD+Shift+v bind[lock] = MOD+Shift+Delete bind[initscr] = MOD+Shift+i bind[iconify] = MOD+w bind[uniconify] = MOD+Shift+w bind[raise_toggle] = MOD+Shift+r bind[button2] = MOD+v bind[width_shrink] = MOD+apostrophe bind[width_grow] = MOD+exclamdown bind[height_shrink] = MOD+Shift+apostrophe bind[height_grow] = MOD+Shift+exclamdown bind[move_left] = MOD+dead_grave bind[move_right] = MOD+plus bind[move_up] = MOD+Shift+dead_grave bind[move_down] = MOD+Shift+plus bind[name_workspace] = MOD+Shift+slash bind[search_workspace] = MOD+slash bind[search_win] = MOD+f spectrwm-1.0.0/spectrwm_fr.conf010064400017500000000000000043251171750520200155750ustar00marcowheel# Key bindings for French (fr) keyboards # unbind with: bind[] = bind[cycle_layout] = MOD+space bind[flip_layout] = MOD+Shift+backslash bind[stack_reset] = MOD+Shift+space bind[master_shrink] = MOD+h bind[master_grow] = MOD+l bind[master_add] = MOD+comma bind[master_del] = MOD+semicolon bind[stack_inc] = MOD+Shift+comma bind[stack_dec] = MOD+Shift+semicolon bind[swap_main] = MOD+Return bind[focus_next] = MOD+j bind[focus_prev] = MOD+k bind[swap_next] = MOD+Shift+j bind[swap_prev] = MOD+Shift+k bind[spawn_term] = MOD+Shift+Return bind[menu] = MOD+p bind[quit] = MOD+Shift+q bind[restart] = MOD+q bind[focus_main] = MOD+m 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_next] = MOD+Right bind[ws_prev] = MOD+Left bind[ws_next_all] = MOD+Up bind[ws_prev_all] = MOD+Down bind[ws_prior] = MOD+a bind[screen_next] = MOD+Shift+Right bind[screen_prev] = MOD+Shift+Left 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[bar_toggle] = MOD+b bind[focus_next] = MOD+Tab bind[focus_prev] = MOD+Shift+Tab bind[wind_kill] = MOD+Shift+x bind[wind_del] = MOD+x bind[screenshot_all] = MOD+s bind[screenshot_wind] = MOD+Shift+s bind[float_toggle] = MOD+t bind[version] = MOD+Shift+v bind[lock] = MOD+Shift+Delete bind[initscr] = MOD+Shift+i bind[iconify] = MOD+w bind[uniconify] = MOD+Shift+w bind[raise_toggle] = MOD+Shift+r bind[button2] = MOD+v bind[width_shrink] = MOD+minus bind[width_grow] = MOD+equal bind[height_shrink] = MOD+Shift+minus bind[height_grow] = MOD+Shift+equal bind[move_left] = MOD+ugrave bind[move_right] = MOD+asterisk bind[move_up] = MOD+Shift+ugrave bind[move_down] = MOD+Shift+asterisk bind[name_workspace] = MOD+Shift+slash bind[search_workspace] = MOD+slash bind[search_win] = MOD+f spectrwm-1.0.0/spectrwm_fr_ch.conf010064400017500000000000000041301171750520200162410ustar00marcowheel# Key bindings for Swiss French (FR_CH) keyboards # unbind with: bind[] = bind[cycle_layout] = MOD+space bind[flip_layout] = MOD+Shift+backslash bind[stack_reset] = MOD+Shift+space bind[master_shrink] = MOD+h bind[master_grow] = MOD+l bind[master_add] = MOD+comma bind[master_del] = MOD+period bind[stack_inc] = MOD+Shift+comma bind[stack_dec] = MOD+Shift+period bind[swap_main] = MOD+Return bind[focus_next] = MOD+j bind[focus_prev] = MOD+k bind[swap_next] = MOD+Shift+j bind[swap_prev] = MOD+Shift+k bind[spawn_term] = MOD+Shift+Return bind[menu] = MOD+p bind[quit] = MOD+Shift+q bind[restart] = MOD+q bind[focus_main] = MOD+m 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_next] = MOD+Right bind[ws_prev] = MOD+Left bind[ws_next_all] = MOD+Up bind[ws_prev_all] = MOD+Down bind[ws_prior] = MOD+a bind[screen_next] = MOD+Shift+Right bind[screen_prev] = MOD+Shift+Left 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[bar_toggle] = MOD+b bind[focus_next] = MOD+Tab bind[focus_prev] = MOD+Shift+Tab bind[wind_kill] = MOD+Shift+x bind[wind_del] = MOD+x bind[screenshot_all] = MOD+s bind[screenshot_wind] = MOD+Shift+s bind[float_toggle] = MOD+t bind[version] = MOD+Shift+v bind[lock] = MOD+Shift+Delete bind[initscr] = MOD+Shift+i bind[iconify] = MOD+w bind[uniconify] = MOD+Shift+w bind[raise_toggle] = MOD+Shift+r bind[button2] = MOD+v bind[width_shrink] = MOD+minus bind[width_grow] = MOD+egrave bind[height_shrink] = MOD+Shift+minus bind[height_grow] = MOD+Shift+egrave bind[move_left] = MOD+eacute bind[move_right] = MOD+agrave bind[move_up] = MOD+Shift+eacute bind[move_down] = MOD+Shift+agrave bind[name_workspace] = MOD+Shift+apostrophe bind[search_workspace] = MOD+apostrophe bind[search_win] = MOD+f spectrwm-1.0.0/spectrwm_it.1010064400017500000000000000510051171750520200150120ustar00marcowheel.\" 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: September 15 2011 $ .Dt SPECTRWM 1 .Os .Sh NOME .Nm spectrwm .Nd gestore di finestre per X11 .Sh SINTASSI .Nm spectrwm .Sh DESCRIZIONE .Nm ? un gestore di finestre minimale che cerca di stare in disparte, in modo che il prezioso spazio sullo schermo possa essere usato per cose pi? importanti. Hai dei default sensati e non costringe l'utente ad imparare un linguaggio di programmazione per configurarlo. ? stato scritto dagli hacker per gli hacker e cerca di essere piccolo, compatto e veloce. .Pp Quando .Nm viene avviato, legge le impostazioni presenti nel file di configurazione .Pa spectrwm.conf . Vedere la sezione .Sx FILE DI CONFIGURAZIONE pi? sotto. .Pp In questa pagina di manuale viene usata la seguente notazione: .Pp .Bl -tag -width Ds -offset indent -compact .It Cm M Meta .It Cm S Shift .It Aq Cm Nome Tasto nome .It Cm M1 Tasto 1 (sinistro) del mouse .It Cm M3 Tasto 3 (destro) del mouse .El .Pp .Nm ? molto semplice da usare. La maggior parte delle operazioni vengono svolte usando combinazioni di tasti sulla tastiera o sul mouse. Vedere la sezione .Sx SCORCIATOIE pi? sotto per i default e le possibilit? di personalizzazione. .Sh FILE DI CONFIGURAZIONE .Nm prova per prima cosa ad aprire il file di configurazione personale dell'utente, .Pa ~/.spectrwm.conf . Se il file in questione non ? disponibile, prova ad usare il file di configurazione globale .Pa /etc/spectrwm.conf . .Pp Il formato di questo file ? \*(Ltchiave\*(Gt = \*(Ltvalore\*(Gt. Per esempio: .Pp .Dl color_focus = red .Pp Per abilitare o disabilitare un'opzione, usare i valori 1 o 0 rispettivamente. .Pp Il file supporta le seguenti chiavi: .Pp .Bl -tag -width "title_class_enabledXXX" -offset indent -compact .It Cm autorun Esegue un'applicazione nel workspace specificato all'avvio. Definito nel formato ws[]:applicazione, eg. ws[2]:xterm esegue un xterm nell'area di lavoro 2. .It Cm color_focus Colore del bordo della finestra che ha il focus. .It Cm color_unfocus Colore del bordo delle finestre che non hanno il focus. .It Cm bar_enabled Abilita o disabilita la barra di stato. .It Cm bar_border Ns Bq Ar x Colore del bordo della barra di stato nello schermo .Ar x . .It Cm bar_border_width Spessore del bordo attorno alla barra di stato in pixel. Il bordo pu? essere disabilitato usando il valore 0. .It Cm bar_color Ns Bq Ar x Colore della barra di stato nello schermo .Ar x . .It Cm bar_font_color Ns Bq Ar x Colore del testo della barra di stato nello schermo .Ar x . .It Cm bar_font Font della barra di stato. .It Cm bar_action Script esterno che aggiunge informazioni come la carica della batteria alla barra di stato. .It Cm bar_delay Frequenza di aggiornamento, in secondi, dello script esterno che aggiunge informazioni alla barra di stato. .It Cm bar_at_bottom Posiziona la barra di stato sul fondo dello schermo anzich? in cima. .It Cm stack_enabled Abilita o disabilita la visualizzazione dell'algoritmo di stacking corrente nella barra di stato. .It Cm clock_enabled Abilita o disabilita l'orologio nella barra di stato. Se disabilitato, usando il valore 0, ? possibile usare un orologio personalizzato nello script specificato in .Pa bar_action . .It Cm dialog_ratio Alcune applicazioni hanno finestre di dialogo troppo piccole per risultare utili. Questa ? la percentuale dello schermo che verr? usata per le finestre di dialogo: ad esempio, 0.6 indica il 60% della dimensione fisica dello schermo. .It Cm layout Layout da utilizzare all'avvio. Definito nel formato ws[]:master_grow:master_add:stack_inc:layout:always_raise:stack_mode, eg. ws[2]:-4:0:1:0:horizontal assegna il layout orizzontale all'area di lavoro 2, riduce l'area principale di 4 unit?, aggiunge una finestra allo stack e mantiene il comportamento predefinito per quanto riguarda le finestre floating. I valori possibili per stack_mode sono .Pa vertical , .Pa horizontal e .Pa fullscreen . .Pp Fare riferimento a .Pa master_grow , .Pa master_shrink , .Pa master_add , .Pa master_del , .Pa stack_inc , .Pa stack_del e .Pa always_raise per ulteriori informazioni. Queste impostazioni sono complesse e hanno effetti secondari; ? opportuno familiarizzare con questi comandi prima di modificare l'opzione .Pa layout . .Pp Questa impostazione non viene applicata dopo il restart. .It Cm region Alloca una regione personalizzata, rimuovendo qualsiasi regione automaticamente rilevata stia occupando lo stesso spazio sullo schermo. Definita nel formato screen[]:LARGHEZZAxALTEZZA+X+Y, ad esempio \& screen[1]:800x1200+0+0. .Pp Per fare s? che pi? monitor vengano considerati come una singola entit? ? sufficiente creare una regione sufficientemente grande da contenerli, eg. screen[1]:2048x760+0+0 unisce due monitor con risoluzione 1024x768 posizionati uno di fianco all'altro. .It Cm term_width Imposta la dimensione minima preferita per il terminale. Se questo valore ? maggiore di 0, .Nm cercher? di riaggiustare la dimensione del testo nel terminale in modo che la larghezza del terminale rimanga sopra il valore quando la finestra viene ridimensionata. Al momento solo .Xr xterm 1 ? supportato. Il binario di .Xr xterm 1 deve essere setuid o setgid perch? questo funzioni: nella maggior parte dei sistemi, questo ? il default. L'utente potrebbe voler impostare program[term] (vedere la sezione .Sx PROGRAMMI pi? sotto) per usare una seconda copia del binario di .Xr xterm 1 che non abbia il bit setgid impostato. .It Cm title_class_enabled Abilita o disabilita la visualizzazione della classe della finestra nella barra di stato. Impostare a 1 per abilitare. .It Cm title_name_enabled Abilita o disabilita la visualizzazione del titolo della finestra nella barra di stato. Impostare a 1 per abilitare. .It Cm urgent_enabled Abilita o disabilita l'hint "urgente". In molti emulatori di terminale, il supporto deve essere abilitato separatamente: per xterm, ad esempio, ? necessario aggiungere la riga .Pa xterm.urgentOnBell: true al file .Pa .Xdefaults . .It Cm window_name_enabled Abilita o disabilita la visualizzazione del nome della finestra nella barra di stato. Impostare a 1 per abilitare. .It Cm verbose_layout Abilita o disabilita la visualizzazione dei valori correnti di master e stack nella barra di stato. Impostare a 1 per abilitare. .It Cm modkey Cambia il tasto modificatore. Solitamente Mod1 ? il tasto ALT e Mod4 ? il tasto Windows su un PC. .It Cm focus_mode Se viene usato il valore .Pa follow_cursor , il gestore di finestre dar? il focus alla finestra sotto il puntatore quando si cambia area di lavoro o si creano finestre. .It Cm disable_border Rimuovi il bordo dalle finestre se la barra di stato ? nascosta e c'? una sola finestra sullo schermo. .It Cm border_width Spessore del bordo delle finestre in pixel. Il valore 0 disabilita il bordo. .It Cm program Ns Bq Ar p Definisce una nuova azione per lanciare il programma .Ar p . Vedere la sezione .Sx PROGRAMMI pi? sotto. .It Cm bind Ns Bq Ar x Assegna una combinazione di tasti all'azione .Ar x . Vedere la sezione .Sx SCORCIATOIE pi? sotto. .It Cm quirk Ns Bq Ar c:n Aggiunge un "quirk" per le finestre di classe .Ar c e nome .Ar n . Vedere la sezione .Sx QUIRKS pi? sotto. .El .Pp I colori devono essere specificati nel formato usato da .Xr XQueryColor 3 e i font in quello usato da .Xr XQueryFont 3 . .Pp Per avere una lista dei font disponibili sul proprio sistema utilizzare .Xr fc-list 1 o .Xr xlsfonts 1 . L'applicazione .Xr xfontsel 1 ? utile per visualizzare la X Logical Font Description ("XLFD") usata per la chiave .Pa bar_font . .Sh PROGRAMMI .Nm consente la definizione di azioni personalizzate per lanciare programmi di propria scelta, che possono essere assegnate a combinazioni di tasti nello stesso modo in cui ? possibile farlo con le azioni predefinite. Vedere la sezione .Sx SCORCIATOIE pi? sotto. .Pp I programmi di default sono descritte qui sotto: .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 I programmi personalizzati vengono specificati con la seguente sintassi: .Pp .Dl program[] = [ [... ]] .Pp .Aq nome ? un qualsiasi identificatore che non va in conflitto con un'azione predefinita o una chiave, .Aq percorso ? il programma desiderato, e .Aq arg sono zero o pi? argomenti da passare al programma. .Pp Le seguenti variabili rappresentano valori impostabili in .Nm (vedere la sezione .Sx FILE DI CONFIGURAZIONE sopra), e possono essere usati nel campo .Aq arg dove saranno sostituite con il valore al momento del lancio del programma: .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 Esempio: .Bd -literal -offset indent program[ff] = /usr/local/bin/firefox http://spectrwm.org/ bind[ff] = Mod+f # adesso Mod+F lancia firefox .Ed .Pp Per eliminare la combinazione precedente: .Bd -literal -offset indent bind[] = Mod+f program[ff] = .Ed .Pp .Sh SCORCIATOIE .Nm fornisce molte funzioni (o azioni) accessibili tramite combinazioni di tasti sul mouse o sulla tastiera. .Pp Le scorciatoie assegnate al mouse sono: .Pp .Bl -tag -width "M-j, M-XXX" -offset indent -compact .It Cm M1 D? focus alla finestra .It Cm M-M1 Muove la finestra .It Cm M-M3 Ridimensiona la finestra .It Cm M-S-M3 Ridimensiona la finestra mantenendola centrata .El .Pp Le scorciatoie da tastiera di default sono: .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 .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 .It Cm M-w iconify .It Cm M-S-w uniconify .It Cm M-S-r always_raise .It Cm M-v button2 .El .Pp I nomi delle azioni e le relative descrizioni sono le seguenti: .Pp .Bl -tag -width "M-j, M-XXX" -offset indent -compact .It Cm term Lancia un nuovo terminale (vedi .Sx PROGRAMMI pi? in alto). .It Cm menu Menu (vedi .Sx PROGRAMMI pi? in alto). .It Cm quit Chiude .Nm . .It Cm restart Riavvia .Nm . .It Cm cycle_layout Cambia layout. .It Cm reset_layout Re-inizializza il layout. .It Cm master_shrink Restringe l'area principale. .It Cm master_grow Allarga l'area principale. .It Cm master_add Aggiunge finestre all'area principale. .It Cm master_del Rimuove finestre dall'area principale. .It Cm stack_inc Aggiunge righe/colonne all'area di stacking. .It Cm stack_del Rimuove righe/colonne dall'area di stacking. .It Cm swap_main Muove la finestra corrente nell'area principale. .It Cm focus_next D? il focus alla finestra successiva. .It Cm focus_prev D? il focus alla finestra precedente. .It Cm focus_main D? il focus alla finestra principale. .It Cm swap_next Scambia con la finestra successiva dell'area di lavoro. .It Cm swap_prev Scambia con la finestra precedente dell'area di lavoro. .It Cm bar_toggle Mostra/nascondi la barra di stato da tutte le aree di lavoro. .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 ? compreso tra 1 e 10. .It Cm mvws_ Ns Ar n Sposta la finestra corrente nell'area di lavoro .Ar n , dove .Ar n ? compreso tra 1 e 10. .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_prior Passa all'ultima area di lavoro visitata. .It Cm screen_next Sposta il puntatore nella regione successiva. .It Cm screen_prev Sposta il puntatore nella regione precedente. .It Cm screenshot_all Cattura uno screenshot dell'intero schermo, se abilitato (vedere la sezione .Sx PROGRAMMI pi? in alto). .It Cm screenshot_wind Cattura uno screenshot di una singola finestra, se abilitato (vedere la sezione .Sx PROGRAMMI pi? in alto). .It Cm version Abilita/disabilita il numero di versione nella barra di stato. .It Cm float_toggle Passa la finestra che ha il focus da floating a tiled. .It Cm lock Blocca lo schermo (vedere la sezione .Sx PROGRAMMI pi? in alto). .It Cm initscr Re-inizializza gli schermi fisici (vedere la sezione .Sx PROGRAMMI pi? in alto). .It Cm iconify Minimizza (unmap) la finesta che ha il focus. .It Cm uniconify Massimizza (map) la finestra selezionata tramite dmenu. .It Cm always_raise Quando ? abilitato, le finestre floating possono essere oscurate da finestre tiled. .It Cm button2 Simula la pressione del tasto centrale del mouse. .El .Pp Le scorciatoie personalizzate sono specificate nel file di configurazione come segue: .Pp .Dl bind[] = .Pp .Aq azione ? una delle azioni elencate sopra (oppure nulla) e .Aq tasti ? dato da zero o pi? modificatori (MOD, Mod1, Shift, ecc.) e uno o pi? tasti normali (b, space, ecc.), separati da "+". Per esempio: .Bd -literal -offset indent bind[reset] = Mod4+q # assegna reset ai tasti Windows + q bind[] = Mod1+q # rimuovi l'assegnazione di Alt + q .Ed .Pp Pi? combinazioni di tasti possono essere assegnate alla stessa azione. .Sh QUIRK .Nm fornisce la possibilit? di specificare dei "quirk" per la gestione di finestre che devono subire un trattamento speciale da un gestore di finestre tiling, come ad esempio alcune finestre di dialogo e applicazioni a schermo intero. .Pp I quirk abilitati di default sono elencati qui sotto: .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 sono descritti qui sotto: .Pp .Bl -tag -width "XTERM_FONTADJXXX" -offset indent -compact .It FLOAT Questa finestra deve essere lasciata libera di muoversi (float). .It TRANSSZ Aggiusta la dimensione delle finestre troppo piccole usando dialog_ratio (vedere .Sx FILE DI CONFIGURAZIONE ) . .It ANYWHERE Consente alla finestra di decidere da sola dove posizionarsi. .It XTERM_FONTADJ Ridimensiona il font di xterm quando viene ridimensionata la finestra. .It FULLSCREEN Rimuove i bordi, consentendo alla finestra di usare l'intera dimensione dello schermo. .It FOCUSPREV Quando la finestra viene chiusa, d? il focus alla finestra che aveva il focus precedente anzich? all'applicazione precedente nello stack. .El .Pp I quirk personalizzati vanno specificati nel file di configurazione come segue: .Pp .Dl quirk[:] = [ + ... ] .Pp .Aq classe e .Aq nome specificano a quali finestre i quirk vanno applicati, e .Aq quirk ? uno dei quirk presente nella lista sopra. Ad esempio: .Bd -literal -offset indent quirk[MPlayer:xv] = FLOAT + FULLSCREEN + FOCUSPREV quirk[pcb:pcb] = NONE # rimuovi un quirk precedentemente specificato .Ed .Pp ? possibile ottenere .Aq classe e .Aq nome usando xprop(1) e facendo click sulla finestra desiderata. Nel seguente esempio ? stato fatto click sulla finestra principale di Firefox: .Bd -literal -offset indent $ xprop | grep WM_CLASS WM_CLASS(STRING) = "Navigator", "Firefox" .Ed .Pp Bisogna tenere conto del fatto che usare grep per trovare WM_CLASS inverte la classe e il nome. Nell'esempio precedente, la dichiarazione del quirk sarebbe .Bd -literal -offset indent quirk[Firefox:Navigator] = FLOAT .Ed .Pp .Nm assegna automaticamente i quirk alle finestre in base al valore della propriet? _NET_WM_WINDOW_TYPE in base al seguente schema: .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, nessun quirk ? automaticamente assegnato alla finestra. I quirk specificati nel file di configurazione hanno la precedenza sui quirk assegnati in automatico. .Sh EWMH .Nm implementa in maniera parziale la specifica Extended Window Manager Hints (EWMH). Ci? permette di controllare sia le finestre che .Nm stesso tramite script e programmi esterni. Per renderlo possibile, .Nm risponde ad alcuni eventi di tipo ClientMessage; questo tipo di messaggio pu? essere inviato da un terminale usando programmi come .Xr wmctrl 1 e .Xr xdotool 1 . Per il formato esatto di questi messaggi, si veda la specifica EWMH. .Pp L'id della finestra che ha il focus ? memorizzato nella propriet? _NET_ACTIVE_WINDOW della root window. ? quindi possibile ottenere il titolo della finestra attiva usando .Xr xprop 1 e .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 Per dare il focus ad una finestra, ? sufficiente inviare il messaggio _NET_ACTIVE_WINDOW alla root window. Ad esempio, usando .Xr wmctrl 1 (supponendo che 0x4a0000b sia l'id della finestra a cui dare il focus): .Bd -literal -offset indent $ wmctrl \-i \-c 0x4a0000b .Ed .Pp Per chiudere una finestra si pu? inviare il messaggio _NET_CLOSE_WINDOW alla root window. Ad esempio, usando .Xr wmctrl 1 (supponendo che 0x4a0000b sia l'id della finestra da chiudere): .Bd -literal -offset indent $ wmctrl \-i \-c 0x4a0000b .Ed .Pp Per passare una finestra da floating a tiled si pu? aggiungere o rimuovere l'atomo _NET_WM_STATE_ABOVE alla propriet? _NET_WM_STATE della finestra, inviando il messaggio _NET_WM_STATE alla root window. Ad esempio, usando .Xr wmctrl 1 (supponendo che 0x4a0000b sia l'id della finestra): .Bd -literal -offset indent $ wmctrl \-i \-r 0x4a0000b \-b toggle,_NET_WM_STATE_ABOVE .Ed .Pp Le finestre floating possono essere ridimensionate o spostate inviando il messaggio _NET_MOVERESIZE_WINDOW alla root window. Ad esempio, usando .Xr wmctrl 1 (supponendo che 0x4a0000b sia l'id della finestra da spostare): .Bd -literal -offset indent $ wmctrl \-i \-r 0x4a0000b \-e 0,100,50,640,480 .Ed .Pp Questo comando sposta la finestra in (100,50) e la ridimensiona a 640x480. .Pp I messaggi _NET_MOVERESIZE_WINDOW vengono ignorati per le finestre stacked. .Sh SEGNALI ? possibile riavviare .Nm inviandogli il segnale HUP. .Sh FILE .Bl -tag -width "/etc/spectrwm.confXXX" -compact .It Pa ~/.spectrwm.conf impostazioni di .Nm dell'utente. .It Pa /etc/spectrwm.conf impostazioni globali di .Nm . .El .Sh ORIGINE .Nm prende ispirazione da xmonad & dwm. .Sh AUTORI .An -nosplit .Pp .Nm ? 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 .El .Sh BUGS Al momento il menu, invocato usando .Cm M-p , dipende da dmenu. spectrwm-1.0.0/spectrwm_pt.1010064400017500000000000000362521171750520200150300ustar00marcowheel.\" 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_delay Freq\(:u\(^encia da atualiza\(,c\(~ao, em segundos, do script externo que preenche a barra de status. .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\(,ces) 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\(,ces 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-1.0.0/spectrwm_ru.1010064400017500000000000000433051171750520200150300ustar00marcowheel.\" 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 bar_delay Частота выполнения внешнего скрипта статусной строки, секунды. .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-1.0.0/spectrwm_se.conf010064400017500000000000000041331171750520200155720ustar00marcowheel# Key bindings for Swedish (se) keyboards # unbind with: bind[] = bind[cycle_layout] = MOD+space bind[flip_layout] = MOD+Shift+apostrophe bind[stack_reset] = MOD+Shift+space bind[master_shrink] = MOD+h bind[master_grow] = MOD+l bind[master_add] = MOD+comma bind[master_del] = MOD+period bind[stack_inc] = MOD+Shift+comma bind[stack_dec] = MOD+Shift+period bind[swap_main] = MOD+Return bind[focus_next] = MOD+j bind[focus_prev] = MOD+k bind[swap_next] = MOD+Shift+j bind[swap_prev] = MOD+Shift+k bind[spawn_term] = MOD+Shift+Return bind[menu] = MOD+p bind[quit] = MOD+Shift+q bind[restart] = MOD+q bind[focus_main] = MOD+m 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_next] = MOD+Right bind[ws_prev] = MOD+Left bind[ws_next_all] = MOD+Up bind[ws_prev_all] = MOD+Down bind[ws_prior] = MOD+a bind[screen_next] = MOD+Shift+Right bind[screen_prev] = MOD+Shift+Left 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[bar_toggle] = MOD+b bind[focus_next] = MOD+Tab bind[focus_prev] = MOD+Shift+Tab bind[wind_kill] = MOD+Shift+x bind[wind_del] = MOD+x bind[screenshot_all] = MOD+s bind[screenshot_wind] = MOD+Shift+s bind[float_toggle] = MOD+t bind[version] = MOD+Shift+v bind[lock] = MOD+Shift+Delete bind[initscr] = MOD+Shift+i bind[iconify] = MOD+w bind[uniconify] = MOD+Shift+w bind[raise_toggle] = MOD+Shift+r bind[button2] = MOD+v bind[width_shrink] = MOD+plus bind[width_grow] = MOD+dead_acute bind[height_shrink] = MOD+Shift+plus bind[height_grow] = MOD+Shift+dead_acute bind[move_left] = MOD+aring bind[move_right] = MOD+dead_diaeresis bind[move_up] = MOD+Shift+aring bind[move_down] = MOD+Shift+dead_diaeresis bind[name_workspace] = MOD+Shift+minus bind[search_workspace] = MOD+minus bind[search_win] = MOD+f spectrwm-1.0.0/spectrwm_us.conf010064400017500000000000000041401171750520200156100ustar00marcowheel# Key bindings for United States (us) keyboards # unbind with: bind[] = bind[cycle_layout] = MOD+space bind[flip_layout] = MOD+Shift+backslash bind[stack_reset] = MOD+Shift+space bind[master_shrink] = MOD+h bind[master_grow] = MOD+l bind[master_add] = MOD+comma bind[master_del] = MOD+period bind[stack_inc] = MOD+Shift+comma bind[stack_dec] = MOD+Shift+period bind[swap_main] = MOD+Return bind[focus_next] = MOD+j bind[focus_prev] = MOD+k bind[swap_next] = MOD+Shift+j bind[swap_prev] = MOD+Shift+k bind[spawn_term] = MOD+Shift+Return bind[menu] = MOD+p bind[quit] = MOD+Shift+q bind[restart] = MOD+q bind[focus_main] = MOD+m 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_next] = MOD+Right bind[ws_prev] = MOD+Left bind[ws_next_all] = MOD+Up bind[ws_prev_all] = MOD+Down bind[ws_prior] = MOD+a bind[screen_next] = MOD+Shift+Right bind[screen_prev] = MOD+Shift+Left 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[bar_toggle] = MOD+b bind[focus_next] = MOD+Tab bind[focus_prev] = MOD+Shift+Tab bind[wind_kill] = MOD+Shift+x bind[wind_del] = MOD+x bind[screenshot_all] = MOD+s bind[screenshot_wind] = MOD+Shift+s bind[float_toggle] = MOD+t bind[version] = MOD+Shift+v bind[lock] = MOD+Shift+Delete bind[initscr] = MOD+Shift+i bind[iconify] = MOD+w bind[uniconify] = MOD+Shift+w bind[raise_toggle] = MOD+Shift+r bind[button2] = MOD+v bind[width_shrink] = MOD+minus bind[width_grow] = MOD+equal bind[height_shrink] = MOD+Shift+minus bind[height_grow] = MOD+Shift+equal bind[move_left] = MOD+bracketleft bind[move_right] = MOD+bracketright bind[move_up] = MOD+Shift+bracketleft bind[move_down] = MOD+Shift+bracketright bind[name_workspace] = MOD+Shift+slash bind[search_workspace] = MOD+slash bind[search_win] = MOD+f spectrwm-1.0.0/version.h010064400017500000000000000022541171750520200142300ustar00marcowheel/* * 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 1 #define SPECTRWM_MINOR 0 #define SPECTRWM_PATCH 0 #define SPECTRWM_VERSION SPECTRWM_STRINGIZE(SPECTRWM_MAJOR) "." \ SPECTRWM_STRINGIZE(SPECTRWM_MINOR) "." \ SPECTRWM_STRINGIZE(SPECTRWM_PATCH) #endif /* SPECTRWM_VERSION_H */