spectrwm-2.6.0004075500017500000000000000000001237571307300123325ustar00marcowheelspectrwm-2.6.0/Makefile010064400017500000000000000020101237571307300140370ustar00marcowheel.include PREFIX?=/usr/local BINDIR=${PREFIX}/bin SUBDIR= lib PROG=spectrwm #MAN=spectrwm_pt.1 spectrwm_ru.1 spectrwm_es.1 spectrwm_it.1 MAN=spectrwm.1 CFLAGS+=-std=c99 -Wmissing-prototypes -Wall -Wextra -Wshadow -Wno-uninitialized -g # Uncomment define below to disallow user settable clock format string #CFLAGS+=-DSWM_DENY_CLOCK_FORMAT CPPFLAGS+= -I${X11BASE}/include -I${X11BASE}/include/freetype2 LDADD+=-lutil -L${X11BASE}/lib -lX11 -lX11-xcb -lxcb-aux -lxcb-icccm -lxcb-keysyms -lxcb-randr -lxcb-xtest -lXft -lXcursor BUILDVERSION != sh "${.CURDIR}/buildver.sh" .if !${BUILDVERSION} == "" CPPFLAGS+= -DSPECTRWM_BUILDSTR=\"$(BUILDVERSION)\" .endif MANDIR= ${PREFIX}/man/man #spectrwm_ru.cat1: spectrwm_ru.1 # nroff -mandoc ${.CURDIR}/spectrwm_ru.1 > ${.TARGET} obj: _xenocara_obj beforeinstall: ln -sf ${PROG} ${BINDIR}/scrotwm # clang targets .if ${.TARGETS:M*analyze*} CC=clang CXX=clang++ CPP=clang -E CFLAGS+=--analyze .endif analyze: all clang: all .include .include spectrwm-2.6.0/README.md010064400017500000000000000007021237571307300136640ustar00marcowheelspectrwm ======== spectrwm is a small dynamic tiling window manager for X11. It tries to stay out of the way so that valuable screen real estate can be used for much more important stuff. It has sane defaults and does not require one to learn a language to do any configuration. It was written by hackers for hackers and it strives to be small, compact and fast. ## License spectrwm is ISC licensed unless otherwise specified in individual files. spectrwm-2.6.0/baraction.sh010064400017500000000000000040421237571307300147040ustar00marcowheel#!/bin/sh # print_date() { # The date is printed to the status bar by default. # To print the date through this script, set clock_enabled to 0 # in spectrwm.conf. Uncomment "print_date" below. FORMAT="%a %b %d %R %Z %Y" DATE=`date "+${FORMAT}"` echo -n "${DATE} " } print_mem() { MEM=`/usr/bin/top | grep Free: | cut -d " " -f6` echo -n "Free mem: $MEM " } _print_cpu() { typeset -R4 _1=${1} _2=${2} _3=${3} _4=${4} _5=${5} echo -n "CPU:${_1}% User${_2}% Nice${_3}% Sys${_4}% Int${_5}% Idle " } print_cpu() { OUT="" # iostat prints each column justified to 3 chars, so if one counter # is 100, it jams up agains the preceeding one. sort this out. while [ "${1}x" != "x" ]; do if [ ${1} -gt 99 ]; then OUT="$OUT ${1%100} 100" else OUT="$OUT ${1}" fi shift; done _print_cpu $OUT } print_apm() { BAT_STATUS=$1 BAT_LEVEL=$2 AC_STATUS=$3 if [ $AC_STATUS -ne 255 -o $BAT_STATUS -lt 4 ]; then if [ $AC_STATUS -eq 0 ]; then echo -n "on battery (${BAT_LEVEL}%)" else case $AC_STATUS in 1) AC_STRING="on AC: " ;; 2) AC_STRING="on backup AC: " ;; *) AC_STRING="" ;; esac; case $BAT_STATUS in 4) BAT_STRING="(no battery)" ;; [0-3]) BAT_STRING="(battery ${BAT_LEVEL}%)" ;; *) BAT_STRING="(battery unknown)" ;; esac; FULL="${AC_STRING}${BAT_STRING}" if [ "$FULL" != "" ]; then echo -n "$FULL" fi fi fi } print_cpuspeed() { CPU_SPEED=`/sbin/sysctl hw.cpuspeed | cut -d "=" -f2` echo -n "CPU speed: $CPU_SPEED MHz " } while :; do # instead of sleeping, use iostat as the update timer. # cache the output of apm(8), no need to call that every second. /usr/sbin/iostat -C -c 3600 |& # wish infinity was an option PID="$!" APM_DATA="" I=0 trap "kill $PID; exit" TERM while read -p; do if [ $(( ${I} % 1 )) -eq 0 ]; then APM_DATA=`/usr/sbin/apm -alb` fi if [ $I -ge 2 ]; then # print_date print_mem $MEM print_cpu $REPLY print_cpuspeed print_apm $APM_DATA echo "" fi I=$(( ( ${I} + 1 ) % 22 )); done done spectrwm-2.6.0/buildver.sh010064400017500000000000000001621237571307300145550ustar00marcowheel#!/bin/sh CURDIR=$(dirname $0) if [ -d "$CURDIR/.git" ]; then cd "$CURDIR" git describe --tags | tr -d '\n' fi spectrwm-2.6.0/freebsd004075500017500000000000000000001237571307300137445ustar00marcowheelspectrwm-2.6.0/freebsd/Makefile010064400017500000000000000035061237571307300154640ustar00marcowheel.sinclude PREFIX?= /usr/local LOCALBASE?= /usr/local SWM_BINDIR?= $(PREFIX)/bin SWM_LIBDIR?= $(PREFIX)/lib SWM_MANDIR?= $(PREFIX)/man CC?= cc LVERS!= awk -F = '/major/ { printf( "%s.", $$2 ) } /minor/ { printf( "%s", $$2 ) }' ${.CURDIR}/../lib/shlib_version BUILDVERSION!= sh "${.CURDIR}/../buildver.sh" .if !${BUILDVERSION} == "" CFLAGS+= -DSPECTRWM_BUILDSTR=\"$(BUILDVERSION)\" .endif CFLAGS+= -std=c99 -Wmissing-prototypes -Wall -Wextra -Wshadow -Wno-uninitialized -g CFLAGS+= -I. -I${LOCALBASE}/include -I${LOCALBASE}/include/freetype2 CFLAGS+= -DSWM_LIB=\"$(SWM_LIBDIR)/libswmhack.so.$(LVERS)\" LDADD+= -lutil -L${LOCALBASE}/lib -lX11 -lX11-xcb -lxcb \ -lxcb-icccm -lxcb-keysyms -lxcb-randr -lxcb-util -lxcb-xtest -lXft -lXcursor all: spectrwm libswmhack.so.$(LVERS) spectrwm.c: ln -sf ../spectrwm.c ln -sf ../version.h swm_hack.c: ln -sf ../lib/swm_hack.c spectrwm: spectrwm.o $(CC) $(LDFLAGS) $(LDADD) -o ${.TARGET} ${.ALLSRC} swm_hack.so: swm_hack.c $(CC) $(CFLAGS) -c -shared -fpic -DPIC -o ${.TARGET} ${.ALLSRC} libswmhack.so.$(LVERS): swm_hack.so $(CC) $(LDFLAGS) -shared -fpic -o ${.TARGET} ${.ALLSRC} install: all install -m 755 -d $(SWM_BINDIR) install -m 755 -d $(SWM_LIBDIR) install -m 755 -d $(SWM_MANDIR)/man1 install -m 755 spectrwm $(SWM_BINDIR) install -m 755 libswmhack.so.$(LVERS) $(SWM_LIBDIR) install -m 644 ../spectrwm.1 $(SWM_MANDIR)/man1/spectrwm.1 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 spectrwm $(SWM_BINDIR)/scrotwm clean: rm -f spectrwm *.o *.so libswmhack.so.* spectrwm.c swm_hack.c version.h .PHONY: all install clean .sinclude spectrwm-2.6.0/freebsd/util.h010064400017500000000000000001161237571307300151440ustar00marcowheel#include #ifndef TAILQ_END #define TAILQ_END(head) NULL #endif spectrwm-2.6.0/initscreen.sh010064400017500000000000000001661237571307300151100ustar00marcowheel#!/bin/sh # # Example xrandr multiscreen init xrandr --output LVDS --auto xrandr --output VGA --auto --right-of LVDS spectrwm-2.6.0/lib004075500017500000000000000000001237571307300131005ustar00marcowheelspectrwm-2.6.0/lib/Makefile010064400017500000000000000007271237571307300146220ustar00marcowheel.include PREFIX?=/usr/local LIB= swmhack NOMAN= yes SRCS= swm_hack.c LIBDIR= ${X11BASE}/lib CC?= cc DEBUGLIBS= no NOPROFILE= yes CFLAGS+= -std=c99 -Wmissing-prototypes -Wall -Wextra -Wshadow -Wno-uninitialized -g CFLAGS+= -fPIC CFLAGS+= -I${X11BASE}/include install: ${INSTALL} ${INSTALL_COPY} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ lib${LIB}.so.${SHLIB_MAJOR}.${SHLIB_MINOR} \ ${PREFIX}/lib/ .include .include spectrwm-2.6.0/lib/shlib_version010064400017500000000000000000201237571307300157350ustar00marcowheelmajor=0 minor=0 spectrwm-2.6.0/lib/swm_hack.c010064400017500000000000000200351237571307300151140ustar00marcowheel/* * 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 *display = NULL; void set_property(Display *, Window, char *, char *); /* 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 *dpy, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth, unsigned int clss, Visual * visual, unsigned long valuemask, XSetWindowAttributes * attributes) { static CWF *func = NULL; char *env; Window id; /* 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"); display = dpy; } if (parent == DefaultRootWindow(dpy)) parent = MyRoot(dpy); id = (*func) (dpy, parent, x, y, width, height, border_width, depth, clss, visual, valuemask, attributes); if (id) { if ((env = getenv("_SWM_WS")) != NULL) set_property(dpy, id, "_SWM_WS", env); if ((env = getenv("_SWM_PID")) != NULL) set_property(dpy, id, "_SWM_PID", env); if ((env = getenv("_SWM_XTERM_FONTADJ")) != NULL) { unsetenv("_SWM_XTERM_FONTADJ"); xterm = 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 *dpy, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, unsigned long border, unsigned long background) { static CSWF *func = NULL; char *env; Window id; /* 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(dpy)) parent = MyRoot(dpy); id = (*func) (dpy, parent, x, y, width, height, border_width, border, background); if (id) { if ((env = getenv("_SWM_WS")) != NULL) set_property(dpy, id, "_SWM_WS", env); if ((env = getenv("_SWM_PID")) != NULL) set_property(dpy, id, "_SWM_PID", env); if ((env = getenv("_SWM_XTERM_FONTADJ")) != NULL) { unsetenv("_SWM_XTERM_FONTADJ"); xterm = 1; } } return (id); } typedef int (RWF) (Display * _display, Window _window, Window _parent, int x, int y); /* XReparentWindow intercept hack */ int XReparentWindow(Display *dpy, Window window, Window parent, int x, int y) { static RWF *func = NULL; /* 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(dpy)) parent = MyRoot(dpy); return (*func) (dpy, window, parent, x, y); } typedef void (ANEF) (XtAppContext app_context, XEvent *event_return); int evcount = 0; /* * XtAppNextEvent Intercept Hack * Normally xterm rejects "synthetic" (XSendEvent) events to prevent spoofing. * We don't want to disable this completely, it's insecure. But hook here * and allow these mostly harmless ones that we use to adjust fonts. */ void XtAppNextEvent(XtAppContext app_context, XEvent *event_return) { static ANEF *func = NULL; static KeyCode kp_add = 0, kp_subtract = 0; /* 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 (display != NULL) { kp_add = XKeysymToKeycode(display, XK_KP_Add); kp_subtract = XKeysymToKeycode(display, 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-2.6.0/linux004075500017500000000000000000001237571307300134715ustar00marcowheelspectrwm-2.6.0/linux/Makefile010064400017500000000000000063251237571307300152130ustar00marcowheelPREFIX ?= /usr/local BINDIR ?= $(PREFIX)/bin LIBDIR ?= $(PREFIX)/lib DATAROOTDIR ?= $(PREFIX)/share MANDIR ?= $(DATAROOTDIR)/man XSESSIONSDIR ?= $(DATAROOTDIR)/xsessions BUILDVERSION = $(shell sh $(CURDIR)/../buildver.sh) LIBVERSION = $(shell . $(CURDIR)/../lib/shlib_version; echo $$major.$$minor) LIBMAJORVERSION = $(shell . $(CURDIR)/../lib/shlib_version; echo $$major) MAINT_CFLAGS = -std=c99 -Wmissing-prototypes -Wall -Wextra -Wshadow -Wno-uninitialized -g MAINT_CPPFLAGS = -D_GNU_SOURCE -I. -I/usr/include/freetype2 -DSWM_LIB=\"$(LIBDIR)/libswmhack.so.$(LIBVERSION)\" MAINT_LDLIBS = -lX11 -lX11-xcb -lxcb -lxcb-icccm -lxcb-randr -lxcb-keysyms -lxcb-util -lxcb-xtest -lXft -lXcursor ifneq ("${BUILDVERSION}", "") MAINT_CPPFLAGS += -DSPECTRWM_BUILDSTR=\"$(BUILDVERSION)\" endif all: spectrwm libswmhack.so.$(LIBVERSION) spectrwm: spectrwm.o linux.o $(CC) $(MAINT_LDFLAGS) $(LDFLAGS) $(MAINT_LDLIBS) $(LDLIBS) -o $@ $+ spectrwm.o: ../spectrwm.c ../version.h tree.h util.h $(CC) $(MAINT_CFLAGS) $(CFLAGS) $(MAINT_CPPFLAGS) $(CPPFLAGS) -c -o $@ $< linux.o: linux.c util.h $(CC) $(MAINT_CFLAGS) $(CFLAGS) $(MAINT_CPPFLAGS) $(CPPFLAGS) -c -o $@ $< libswmhack.so.$(LIBVERSION): swm_hack.so $(CC) $(MAINT_LDFLAGS) $(LDFLAGS) $(MAINT_LDLIBS) $(LDLIBS) -Wl,-soname,$@ -shared -fpic -o $@ $+ swm_hack.so: ../lib/swm_hack.c $(CC) $(MAINT_CFLAGS) $(CFLAGS) $(MAINT_CPPFLAGS) $(CPPFLAGS) -fpic -DPIC -c -o $@ $< clean: rm -f spectrwm *.o libswmhack.so.* *.so install: all install -m 755 -d $(DESTDIR)$(BINDIR) install -m 755 -d $(DESTDIR)$(LIBDIR) install -m 755 -d $(DESTDIR)$(MANDIR)/man1 install -m 755 -d $(DESTDIR)$(MANDIR)/es/man1 install -m 755 -d $(DESTDIR)$(MANDIR)/it/man1 install -m 755 -d $(DESTDIR)$(MANDIR)/pt/man1 install -m 755 -d $(DESTDIR)$(MANDIR)/ru/man1 install -m 755 -d $(DESTDIR)$(XSESSIONSDIR) install -m 755 spectrwm $(DESTDIR)$(BINDIR) ln -sf spectrwm $(DESTDIR)$(BINDIR)/scrotwm install -m 644 libswmhack.so.$(LIBVERSION) $(DESTDIR)$(LIBDIR) ln -sf libswmhack.so.$(LIBVERSION) $(DESTDIR)$(LIBDIR)/libswmhack.so.$(LIBMAJORVERSION) ln -sf libswmhack.so.$(LIBVERSION) $(DESTDIR)$(LIBDIR)/libswmhack.so install -m 644 ../spectrwm.1 $(DESTDIR)$(MANDIR)/man1/spectrwm.1 install -m 644 ../spectrwm_es.1 $(DESTDIR)$(MANDIR)/es/man1/spectrwm.1 install -m 644 ../spectrwm_it.1 $(DESTDIR)$(MANDIR)/it/man1/spectrwm.1 install -m 644 ../spectrwm_pt.1 $(DESTDIR)$(MANDIR)/pt/man1/spectrwm.1 install -m 644 ../spectrwm_ru.1 $(DESTDIR)$(MANDIR)/ru/man1/spectrwm.1 install -m 644 spectrwm.desktop $(DESTDIR)$(XSESSIONSDIR) uninstall: rm -f $(DESTDIR)$(BINDIR)/spectrwm rm -f $(DESTDIR)$(BINDIR)/scrotwm rm -f $(DESTDIR)$(LIBDIR)/libswmhack.so.$(LIBVERSION) rm -f $(DESTDIR)$(LIBDIR)/libswmhack.so.$(LIBMAJORVERSION) rm -f $(DESTDIR)$(LIBDIR)/libswmhack.so rm -f $(DESTDIR)$(MANDIR)/man1/spectrwm.1 rm -f $(DESTDIR)$(MANDIR)/es/man1/spectrwm.1 rm -f $(DESTDIR)$(MANDIR)/it/man1/spectrwm.1 rm -f $(DESTDIR)$(MANDIR)/pt/man1/spectrwm.1 rm -f $(DESTDIR)$(MANDIR)/ru/man1/spectrwm.1 rm -f $(DESTDIR)$(XSESSIONSDIR)/spectrwm.desktop .PHONY: all clean install uninstall spectrwm-2.6.0/linux/linux.c010064400017500000000000000306501237571307300150540ustar00marcowheel#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-2.6.0/linux/spectrwm.desktop010064400017500000000000000001621237571307300170030ustar00marcowheel[Desktop Entry] Name=spectrwm Comment=The spectrwm window manager Type=Application Exec=spectrwm TryExec=spectrwm spectrwm-2.6.0/linux/tree.h010064400017500000000000000603771237571307300146720ustar00marcowheel/* $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-2.6.0/linux/util.h010064400017500000000000000014571237571307300147020ustar00marcowheel#define FPARSELN_UNESCESC 0x01 #define FPARSELN_UNESCCONT 0x02 #define FPARSELN_UNESCCOMM 0x04 #define FPARSELN_UNESCREST 0x08 #define FPARSELN_UNESCALL 0x0f size_t strlcpy(char *, const char *, size_t); size_t strlcat(char *, const char *, size_t); char *fgetln(FILE *, size_t *); char *fparseln(FILE *, size_t *, size_t *, const char [3], int); long long strtonum(const char *, long long, long long, const char **); #ifndef WAIT_ANY #define WAIT_ANY (-1) #endif /* there is no limit to ulrich drepper's crap */ #ifndef TAILQ_END #define TAILQ_END(head) NULL #endif #ifndef TAILQ_FOREACH_SAFE #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head) && \ ((tvar) = TAILQ_NEXT(var, field), 1); \ (var) = (tvar)) #endif spectrwm-2.6.0/osx004075500017500000000000000000001237571307300131435ustar00marcowheelspectrwm-2.6.0/osx/Makefile010064400017500000000000000045051237571307300146630ustar00marcowheelPREFIX?= /usr/local BINDIR?= $(PREFIX)/bin LIBDIR?= $(PREFIX)/lib MANDIR?= $(PREFIX)/share/man # To use xquartz, uncomment the following three lines. INCFLAGS+= -I/opt/X11/include -I/opt/X11/include/freetype2/ LDADD+= -L/opt/X11/lib -lX11 -lXcursor -lXft # To use homebrew, uncomment the following three lines. #INCFLAGS+= -I/opt/X11/include #LDADD+= -lX11 -lXcursor -lXft -L/opt/X11/lib #INCFLAGS+= -I/usr/local/Cellar/freetype/2.5.0.1/include/freetype2 # To use homebrew , comment out the following two lines. #INCFLAGS+= -I/opt/local/include/freetype2 -I/opt/local/include #LDADD+= -L/opt/local/lib -lX11 -lXcursor -lXft LDADD+= -lxcb-keysyms -lxcb-util -lxcb-randr -lX11-xcb -lxcb-xtest -lxcb -lxcb-icccm LVERS= $(shell . ../lib/shlib_version; echo $$major.$$minor) CFLAGS+= -std=c99 -Wmissing-prototypes -Wall -Wextra -Wshadow -Wno-uninitialized -g CFLAGS+= -O2 -D_GNU_SOURCE -D__OSX__ -I. -I.. ${INCFLAGS} CFLAGS+= -DSWM_LIB=\"$(LIBDIR)/libswmhack.so.$(LVERS)\" CC?= cc all: spectrwm libswmhack.so.$(LVERS) spectrwm.c: ln -sf ../linux/tree.h ln -sf ../spectrwm.c ln -sf ../version.h swm_hack.c: ln -sf ../lib/swm_hack.c spectrwm: spectrwm.o osx.o $(CC) $(LDFLAGS) -o $@ $+ $(LDADD) %.so: %.c $(CC) $(CFLAGS) -c -fpic -DPIC $+ -o $@ libswmhack.so.$(LVERS): swm_hack.so $(CC) $(LDFLAGS) -shared -fpic -o libswmhack.so.$(LVERS) swm_hack.so $(LDADD) # replace above line with this for OSX 10.5 # $(CC) -shared -bundle -fpic -o libswmhack.so.$(LVERS) $(LDADD) swm_hack.so install: all install -m 755 -d $(DESTDIR)$(BINDIR) install -m 755 -d $(DESTDIR)$(LIBDIR) install -m 755 -d $(DESTDIR)$(MANDIR)/man1 install -m 755 spectrwm $(DESTDIR)$(BINDIR) install -m 755 libswmhack.so.$(LVERS) $(DESTDIR)$(LIBDIR) install -m 644 ../spectrwm.1 $(DESTDIR)$(MANDIR)/man1/spectrwm.1 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 spectrwm $(DESTDIR)$(BINDIR)/scrotwm ln -sf libswmhack.so.0.0 $(DESTDIR)$(LIBDIR)/libswmhack.so.0 ln -sf libswmhack.so.0.0 $(DESTDIR)$(LIBDIR)/libswmhack.so clean: rm -f spectrwm *.o *.so libswmhack.so.* spectrwm.c swm_hack.c tree.h version.h .PHONY: all install clean spectrwm-2.6.0/osx/osx.c010064400017500000000000000036661237571307300142070ustar00marcowheel#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-2.6.0/osx/osx.h010064400017500000000000000002001237571307300141710ustar00marcowheellong long strtonum(const char *, long long, long long, const char **); #ifndef TAILQ_END #define TAILQ_END(head) NULL #endif spectrwm-2.6.0/release.sh010064400017500000000000000067121237571307300143700ustar00marcowheel#!/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-2.6.0/screenshot.sh010064400017500000000000000002031237571307300151120ustar00marcowheel#!/bin/sh # screenshot() { case $1 in full) scrot -m ;; window) sleep 1 scrot -s ;; *) ;; esac; } screenshot $1 spectrwm-2.6.0/spectrwm.1010064400017500000000000000763341237571307300143510ustar00marcowheel.\" Copyright (c) 2009-2012 Marco Peereboom .\" Copyright (c) 2009 Darrin Chandler .\" Copyright (c) 2011-2014 Reginald Kennedy .\" Copyright (c) 2011-2012 Lawrence Teo .\" Copyright (c) 2011-2012 Tiago Cunha .\" Copyright (c) 2012 David Hill .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate: February 15 2012 $ .Dt SPECTRWM 1 .Os .Sh 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 .Pp .Dl Ar keyword Li = Ar setting .Pp For example: .Pp .Dl color_focus = red .Pp Enabling or disabling an option is done by using 1 or 0 respectively. .Pp Colors need to be specified per the .Xr XQueryColor 3 specification. .Pp Comments begin with a #. When a literal .Ql # is desired in an option, then it must be escaped with a backslash. i.e. \e# .Pp The file supports the following keywords: .Bl -tag -width 2m .It Ic autorun Launch an application in a specified workspace at start-of-day. Defined in the format .Li ws Ns Bo Ar idx Bc : Ns Ar application , e.g. ws[2]:xterm launches an 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 Border color of the status bar(s) in screen .Ar x . .It Ic bar_border_unfocus Ns Bq Ar x Border color of the status bar(s) on unfocused region(s) in screen .Ar x . .It Ic bar_border_width Set status bar border thickness in pixels. Disable border by setting to 0. .It Ic bar_color Ns Bq Ar x Background color of the status bar(s) in screen .Ar x . .It Ic bar_enabled Set default .Ic bar_toggle state; default is 1. .It Ic bar_enabled_ws Ns Bq Ar x Set default .Ic bar_toggle_ws state on workspace .Ar x ; default is 1. .It Ic bar_font Font used in the status bar. Either Xft or X Logical Font Description (XLFD) may be used to specify fonts. Fallback fonts may be specified by separating each font with a comma. If all entries are in XLFD syntax, font set will be used. If at least one entry is Xft, Xft will be used. Note that if Xft is in use, only the first font that successfully loads will be used regardless of missing glyphs. The default is to use font set. Also note that .Xr dmenu 1 does not support Xft fonts. .Pp Xft examples: .Bd -literal -offset indent bar_font = Terminus:style=Regular:pixelsize=14:antialias=true bar_font = -*-profont-medium-*-*-*-11-*-*-*-*-*-*-*,Terminus:pixelsize=14,\ -*-clean-medium-*-*-*-12-*-*-*-*-*-*-* .Ed .Pp Font set examples: .Bd -literal -offset indent bar_font = -*-terminus-medium-*-*-*-14-*-*-*-*-*-*-* bar_font = -*-profont-medium-*-*-*-11-*-*-*-*-*-*-*,\ -*-terminus-medium-*-*-*-14-*-*-*-*-*-*-*,\ -*-clean-medium-*-*-*-12-*-*-*-*-*-*-* .Ed .Pp To list the available fonts in your system see .Xr fc-list 1 or .Xr xlsfonts 1 manpages. The .Xr xfontsel 1 application can help with the XLFD setting. .It Ic bar_font_color Ns Bq Ar x Color of the font in status bar in screen .Ar x . .It Ic bar_format Set the bar format string, overriding .Ic clock_format and all of the .Ic enabled options. The format is passed through .Xr strftime 3 before being used. It may contain the following character sequences: .Bl -column "Character sequence" "Replaced with" -offset indent .It Sy "Character sequence" Ta Sy "Replaced with" .It Li "+<" Ta "Pad with a space" .It Li "+A" Ta "Output of the external script" .It Li "+C" Ta "Window class (from WM_CLASS)" .It Li "+D" Ta "Workspace name" .It Li "+F" Ta "Floating indicator" .It Li "+I" Ta "Workspace index" .It Li "+M" Ta "Number of iconic (minimized) windows in workspace" .It Li "+N" Ta "Screen number" .It Li "+P" Ta "Window class and instance separated by a colon" .It Li "+S" Ta "Stacking algorithm" .It Li "+T" Ta "Window instance (from WM_CLASS)" .It Li "+U" Ta "Urgency hint" .It Li "+V" Ta "Program version" .It Li "+W" Ta "Window name (from _NET_WM_NAME/WM_NAME)" .It Li "++" Ta "A literal" Ql + .El .Pp All character sequences may limit its output to a specific length, for example +64A. Any characters that don't match the specification are copied as-is. .It Ic bar_justify Justify the status bar text. Possible values are .Ar left , .Ar center , and .Ar right . .Pp Note that if the output is not left justified, it may not be properly aligned in some circumstances, due to the white-spaces in the default static format. See the .Ic bar_format option for more details. .It Ic bind Ns Bq Ar x Bind key combo to action .Ar x . See the .Sx BINDINGS section below. .It Ic border_width Set window border thickness in pixels. Disable all borders by setting to 0. .It Ic boundary_width Set region containment boundary width in pixels. This is how far a window must be dragged/resized beyond the region edge before it is allowed outside the region. This has no effect when manipulating the window with key bindings. Disable the window containment effect by setting to 0. .It Ic clock_enabled Enable or disable displaying the clock in the status bar. Disable by setting to 0 so a custom clock could be used in the .Ic bar_action script. .It Ic iconic_enabled Display the number of iconic (minimized) windows in the status bar. Enable by setting to 1. .It Ic color_focus Border color of the currently focused window. Default is red. .It Ic color_focus_maximized Border color of the currently focused, maximized window. Defaults to the value of .Ic color_focus . .It Ic color_unfocus Border color of unfocused windows, default is rgb:88/88/88. .It Ic color_unfocus_maximized Border color of unfocused, maximized windows. Defaults to the value of .Ic color_unfocus . .It Ic dialog_ratio Some applications have dialogue windows that are too small to be useful. This ratio is the screen size to what they will be resized. For example, 0.6 is 60% of the physical screen size. .It Ic disable_border Remove border when bar is disabled and there is only one window on the region. .It Ic focus_close Window to put focus when the focused window is closed. Possible values are .Ar first , .Ar next , .Ar previous (default) and .Ar last . .Ar next and .Ar previous are relative to the window that is closed. .It Ic focus_close_wrap Whether to allow the focus to jump to the last window when the first window is closed or vice versa. Disable by setting to 0. .It Ic focus_default Window to put focus when no window has been focused. Possible values are .Ar first and .Ar last (default). .It Ic focus_mode Window focus behavior with respect to the mouse cursor. Possible values: .Pp .Bl -tag -width "default" -offset indent -compact .It Ar default Set window focus on border crossings caused by cursor motion and window interaction. .It Ar follow Set window focus on all cursor border crossings, including workspace switches and changes to layout. .It Ar manual Set window focus on window interaction only. .El .It Ic java_workaround Workaround a Java GUI rendering issue on non-reparenting window managers by impersonating the LG3D window manager, written by Sun. Default is 1. .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 .Li ws Ns Bo Ar idx Bc : Ns Ar master_grow : Ns Ar master_add : Ns Ar stack_inc : Ns Ar always_raise : Ns Ar stack_mode , e.g. ws[2]:-4:0:1:0:horizontal sets worskspace 2 to the horizontal stack mode, shrinks the master area by 4 ticks and adds one window to the stack, while maintaining default floating window behavior. Possible .Ar stack_mode values are .Ar vertical , .Ar vertical_flip , .Ar horizontal , .Ar horizontal_flip and .Ar fullscreen . .Pp See .Ic master_grow , .Ic master_shrink , .Ic master_add , .Ic master_del , .Ic stack_inc , .Ic stack_dec , and .Ic always_raise for more information. Note that the stacking options are complicated and have side-effects. One should familiarize oneself with these commands before experimenting with the .Ic layout option. .Pp This setting is not retained at restart. .It Ic modkey Change mod key. Mod1 is generally the ALT key and Mod4 is the windows key on a PC. .It Ic name Set the name of a workspace at start-of-day. Defined in the format .Li ws Ns Bo Ar idx Bc : Ns Ar name , e.g. ws[1]:Console sets the name of workspace 1 to .Dq Console . .It Ic program Ns Bq Ar p Define new action to spawn a program .Ar p . See the .Sx PROGRAMS section below. .It Ic quirk Ns Bq Ar c Ns Li : Ns Ar i Ns Li : Ns Ar n Add "quirk" for windows with class .Ar c , instance .Ar i and name .Ar n . See the .Sx QUIRKS section below. .It Ic region Allocates a custom region, removing any autodetected regions which occupy the same space on the screen. Defined in the format .Li screen Ns Bo Ar idx Ns Bc : Ns Ar width Ns x Ns Ar height Ns + Ns Ar x Ns + Ns Ar y , e.g. screen[1]:800x1200+0+0. .Pp To make a region span multiple monitors, create a region big enough to cover them all, e.g. screen[1]:2048x768+0+0 makes the region span two monitors with 1024x768 resolution sitting one next to the other. .It Ic region_padding Pixel width of empty space within region borders. Disable by setting to 0. .It Ic spawn_position Position in stack to place newly spawned windows. Possible values are .Ar first , .Ar next , .Ar previous and .Ar last (default). .Ar next and .Ar previous are relative to the focused window. .It Ic stack_enabled Enable or disable displaying the current stacking algorithm in the status bar. .It Ic term_width Set a preferred minimum width for the terminal. If this value is greater than 0, .Nm will attempt to adjust the font sizes in the terminal to keep the terminal width above this number as the window is resized. Only .Xr xterm 1 is currently supported. The .Xr xterm 1 binary must not be setuid or setgid, which it is by default on most systems. Users may need to set program[term] (see the .Sx PROGRAMS section) to use an alternate copy of the .Xr xterm 1 binary without the setgid bit set. .It Ic tile_gap Pixel width of empty space between tiled windows. Negative values cause overlap. Set this to the opposite of border_width to collapse the border between tiles. Disable by setting to 0. .It Ic urgent_collapse Enables hiding of placeholders in the urgency hint indicator for workspaces that do not have any urgent windows. Enable by setting to 1. .It Ic urgent_enabled Enable or disable the urgency hint indicator in the status bar. Note that many terminal emulators require an explicit setting for the bell character to trigger urgency on the window. In .Xr xterm 1 , for example, one needs to add the following line to .Pa .Xdefaults : .Bd -literal -offset indent xterm.bellIsUrgent: true .Ed .It Ic verbose_layout Enable or disable displaying the current master window count and stack column/row count in the status bar. Enable by setting to 1. See .Ar master_add , .Ar master_del , .Ar stack_inc and .Ar stack_dec for more information. .It Ic window_class_enabled Enable or disable displaying the window class name (from WM_CLASS) in the status bar. Enable by setting to 1. .It Ic window_instance_enabled Enable or disable displaying the window instance name (from WM_CLASS) in the status bar. Enable by setting to 1. .It Ic window_name_enabled Enable or disable displaying the window display name (from _NET_WM_NAME/WM_NAME) in the status bar. Enable by setting to 1. .Pp To prevent excessively large window names from pushing the remaining text off the bar, it's limited to 64 characters, by default. See the .Ic bar_format option for more details. .It Ic warp_pointer Centers the mouse pointer on the focused window when using key bindings to change focus, switch workspaces, change regions, etc. Enable by setting to 1. .It Ic workspace_limit Set the total number of workspaces available. Minimum is 1, maximum is 22, default is 10. .El .Sh PROGRAMS .Nm allows you to define custom actions to launch programs of your choice and then bind them the same as with built-in actions. See the .Sx BINDINGS section below. .Pp Custom programs in the configuration file are specified as follows: .Pp .Dl program Ns Bo Ar action Bc = Ar progpath Op Ar arg Op Ar arg ... .Pp .Ar action is any identifier that does not conflict with a built-in action or keyword, .Ar progpath is the desired program, and .Ar arg is zero or more arguments to the program. .Pp Remember that when using # in your program call, it must be escaped with a backslash. i.e. \e# .Pp The following argument variables will be substituted for values at the time the program is spawned: .Pp .Bl -tag -width "$bar_font_color" -offset indent -compact .It Cm $bar_border .It Cm $bar_color .It Cm $bar_font .It Cm $bar_font_color .It Cm $color_focus .It Cm $color_unfocus .It Cm $dmenu_bottom \-b if bar_at_bottom is enabled. .It Cm $region_index .It Cm $workspace_index .El .Pp Example: .Bd -literal -offset indent program[ff] = /usr/local/bin/firefox http://spectrwm.org/ bind[ff] = MOD+Shift+b # Now M-S-b launches firefox .Ed .Pp To cancel the previous, unbind it: .Bd -literal -offset indent bind[] = MOD+Shift+b .Ed .Pp Default programs: .Bl -tag -width "screenshot_wind" -offset indent -compact .It Cm lock xlock .It Cm menu dmenu_run $dmenu_bottom \-fn $bar_font \-nb $bar_color \-nf $bar_font_color \-sb $bar_border \-sf $bar_color .It Cm term xterm .It Cm initscr initscreen.sh # optional .It Cm screenshot_all screenshot.sh full # optional .It Cm screenshot_wind screenshot.sh window # optional .El .Pp Note that optional default programs will not be validated unless overridden. If a default program fails validation, you can resolve the exception by installing the program, modifying the program call or disabling the program by freeing the respective key binding. .Pp For example, to override .Ic lock : .Bd -literal -offset indent program[lock] = xscreensaver\-command \-\-lock .Ed .Pp To unbind .Ic lock and prevent it from being validated: .Bd -literal -offset indent bind[] = MOD+Shift+Delete .Ed .Sh BINDINGS .Nm provides many functions (or actions) accessed via key or 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-XXXXXX" -offset indent -compact .It Cm M-S- Ns Aq Cm Return term .It Cm M-p menu .It Cm M-S-q quit .It Cm M-q restart .It Cm M- Ns Aq Cm Space cycle_layout .It Cm M-S-\e flip_layout .It Cm M-S- Ns Aq Cm Space stack_reset .It Cm M-h master_shrink .It Cm M-l master_grow .It Cm M-, master_add .It Cm M-. master_del .It Cm M-S-, stack_inc .It Cm M-S-. stack_dec .It Cm M- Ns Aq Cm Return swap_main .It Xo .Cm M-j , .Cm M- Ns Aq Cm TAB .Xc focus_next .It Xo .Cm M-k , .Cm M-S- Ns Aq Cm TAB .Xc focus_prev .It Cm M-m focus_main .It Cm M-u focus_urgent .It Cm M-S-j swap_next .It Cm M-S-k swap_prev .It Cm M-b bar_toggle .It Cm M-S-b bar_toggle_ws .It Cm M-x wind_del .It Cm M-S-x wind_kill .It Cm M- Ns Aq Ar 1-9,0,F1-F12 .Pf ws_ Aq Ar 1-22 .It Cm M-S- Ns Aq Ar 1-9,0,F1-F12 .Pf mvws_ Ns Aq Ar 1-22 .It Cm M- Ns Aq Ar Keypad 1-9 .Pf rg_ Aq Ar 1-9 .It Cm M-S- Ns Aq Ar Keypad 1-9 .Pf mvrg_ Aq Ar 1-9 .It Cm M- Ns Aq Cm Right ws_next .It Cm M- Ns Aq Cm Left ws_prev .It Cm M- Ns Aq Cm Up ws_next_all .It Cm M- Ns Aq Cm Down ws_prev_all .It Cm M-a ws_next_move .It Cm M-S- Ns Aq Cm Left ws_prev_move .It Cm M-S- Ns Aq Cm Up ws_prior .It Cm M-S- Ns Aq Cm Right rg_next .It Cm M-S- Ns Aq Cm Left rg_prev .It Cm M-s screenshot_all .It Cm M-S-s screenshot_wind .It Cm M-S-v version .It Cm M-t float_toggle .It Cm M-S- Ns Aq Cm Delete lock .It Cm M-S-i initscr .It Cm M-w iconify .It Cm M-S-w uniconify .It Cm M-e maximize_toggle .It Cm M-S-r always_raise .It Cm M-v button2 .It Cm M-- width_shrink .It Cm M-= width_grow .It Cm M-S-- height_shrink .It Cm M-S-= height_grow .It Cm M-[ move_left .It Cm M-] move_right .It Cm M-S-[ move_up .It Cm M-S-] move_down .It Cm M-S-/ name_workspace .It Cm M-/ search_workspace .It Cm M-f search_win .El .Pp 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 focus_urgent Focus on next window with the urgency hint flag set. The workspace is switched if needed. .It Cm swap_next Swap with next window in workspace. .It Cm swap_prev Swap with previous window in workspace. .It Cm bar_toggle Toggle overall visibility of status bars. .It Cm bar_toggle_ws Toggle status bar on current workspace. .It Cm wind_del Delete current window in workspace. .It Cm wind_kill Destroy current window in workspace. .It Cm ws_ Ns Ar n Switch to workspace .Ar n , where .Ar n is 1 through .Ic workspace_limit . .It Cm mvws_ Ns Ar n Move current window to workspace .Ar n , where .Ar n is 1 through .Ic workspace_limit . .It Cm rg_ Ns Ar n Focus on region .Ar n , where .Ar n is 1 through 9. .It Cm mvrg_ Ns Ar n Move current window to region .Ar n , where .Ar n is 1 through 9. .It Cm ws_next Switch to next workspace with a window in it. .It Cm ws_prev Switch to previous workspace with a window in it. .It Cm ws_next_all Switch to next workspace. .It Cm ws_prev_all Switch to previous workspace. .It Cm ws_next_move Switch to next workspace with the current window. .It Cm ws_prev_move Switch to previous workspace with the current window. .It Cm ws_prior Switch to last visited workspace. .It Cm rg_next Switch to next region. .It Cm rg_prev Switch to previous region. .It Cm screenshot_all Take screenshot of entire screen (if enabled) (see .Sx PROGRAMS above). .It Cm screenshot_wind Take screenshot of selected window (if enabled) (see .Sx PROGRAMS above). .It Cm version Toggle version in status bar. .It Cm float_toggle Toggle focused window between tiled and floating. .It Cm lock Lock screen (see .Sx PROGRAMS above). .It Cm initscr Reinitialize physical screens (see .Sx PROGRAMS above). .It Cm iconify Minimize (unmap) currently focused window. .It Cm uniconify Restore (map) window returned by .Xr dmenu 1 selection. .It Cm maximize_toggle Toggle maximization of focused window. .It Cm 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 Ns Bo Ar action Bc = Ar keys .Pp .Ar action is one of the actions listed above (or empty to unbind) and .Ar 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 .Ql + . .Pp 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. .Pp To bind non-latin characters such as \[oa] or \[*p] you must enter the xkb character name instead of the character itself. Run xev, focus the window and press the specific key and in the terminal output read the symbol name. In the following example for \[oa]: .Bd -literal -offset indent KeyPress event, serial 41, synthetic NO, window 0x2600001, root 0x15a, subw 0x0, time 106213808, (11,5), root:(359,823), state 0x0, keycode 24 (keysym 0xe5, aring), same_screen YES, XLookupString gives 2 bytes: (c3 a5) "\[oa]" XmbLookupString gives 2 bytes: (c3 a5) "\[oa]" XFilterEvent returns: False .Ed .Pp The xkb name is aring. In other words, in .Pa spectrwm.conf add: .Bd -literal -offset indent bind[program] = MOD+aring .Ed .Sh KEYBOARD MAPPING FILES Keyboard mapping files for several keyboard layouts are listed below. These files can be used with the .Ic keyboard_mapping setting to load pre-defined key bindings for the specified keyboard layout. .Pp .Bl -tag -width "spectrwm_XX.confXXX" -offset indent -compact .It Cm spectrwm_cz.conf Czech Republic keyboard layout .It Cm spectrwm_es.conf Spanish keyboard layout .It Cm spectrwm_fr.conf French keyboard layout .It Cm spectrwm_fr_ch.conf Swiss French keyboard layout .It Cm spectrwm_se.conf Swedish keyboard layout .It Cm spectrwm_us.conf United States keyboard layout .El .Sh QUIRKS .Nm provides "quirks" which handle windows that must be treated specially in a tiling window manager, such as some dialogs and fullscreen apps. .Pp The default quirks are described below: .Pp .Bl -tag -width "OpenOffice.org N.M:VCLSalFrameXXX" -offset indent \ -compact .It Firefox\-bin:firefox\-bin TRANSSZ .It Firefox:Dialog FLOAT .It Gimp:gimp FLOAT + ANYWHERE .It MPlayer:xv FLOAT + FULLSCREEN + FOCUSPREV .It OpenOffice.org 2.4:VCLSalFrame FLOAT .It OpenOffice.org 3.1:VCLSalFrame FLOAT .It pcb:pcb FLOAT .It xine:Xine Window FLOAT + ANYWHERE .It xine:xine Panel FLOAT + ANYWHERE .It xine:xine Video Fullscreen Window FULLSCREEN + FLOAT .It Xitk:Xitk Combo FLOAT + ANYWHERE .It Xitk:Xine Window FLOAT + ANYWHERE .It XTerm:xterm XTERM_FONTADJ .El .Pp The quirks themselves are described below: .Pp .Bl -tag -width "XTERM_FONTADJXXX" -offset indent -compact .It FLOAT This window should not be tiled, but allowed to float freely. .It TRANSSZ Adjusts size on transient windows that are too small using .Ic 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 region size. .It FOCUSPREV On exit force focus on previously focused application not previous application in the stack. .It NOFOCUSONMAP Don't change focus to the window when it first appears on the screen. Has no effect when .Ic focus_mode is set to .Ar follow . .It FOCUSONMAP_SINGLE When the window first appears on the screen, change focus to the window if there are no other windows on the workspace with the same WM_CLASS class/instance value. Has no effect when .Ic focus_mode is set to .Ar follow . .It OBEYAPPFOCUSREQ When an application requests focus on the window via a _NET_ACTIVE_WINDOW client message (source indication of 1), comply with the request. Note that a source indication of 0 (unspecified) or 2 (pager) are always obeyed. .It IGNOREPID Ignore the PID when determining the initial workspace for a new window. Especially useful for terminal windows that share a process. .It IGNORESPAWNWS Ignore the spawn workspace when determining the initial workspace for a new window. .It WS Ns Bq Ar n Force a new window to appear on workspace .Ar n . .El .Pp Custom quirks in the configuration file are specified as follows: .Pp .Dl quirk Ns Bo Ar class Ns Bo : Ns Ar instance Ns Bo : Ns Ar name Bc Bc Bc = Ar quirk Op + Ar quirk ... .Pp .Ar class , .Ar instance (optional) and .Ar name (optional) are patterns used to determine which window(s) the quirk(s) apply to and .Ar quirk is one of the quirks from the list above. .Pp Note that patterns are interpreted as POSIX Extended Regular Expressions. Any ':', '[' or ']' must be escaped with '\\'. See .Xr regex 7 for more information on POSIX Extended Regular Expressions. .Pp For example: .Bd -literal -offset indent quirk[MPlayer] = FLOAT + FULLSCREEN + FOCUSPREV # Float all windows having a \ class of 'MPlayer' quirk[.*] = FLOAT # Float all windows by default. quirk[.*:.*:.*] = FLOAT # Same as above. quirk[Firefox:Navigator] = FLOAT # Float all Firefox browser windows. quirk[::Console] = FLOAT # Float windows with WM_CLASS not set and a \ window name of 'Console'. quirk[\\[0-9\\].*:.*:\\[\\[\\:alnum\\:\\]\\]*] = FLOAT # Float windows with WM_CLASS \ class beginning with a number, any WM_CLASS instance and a \ _NET_WM_NAME/WM_NAME either blank or containing alphanumeric characters without spaces. quirk[pcb:pcb] = NONE # remove existing quirk .Ed .Pp You can obtain .Ar class , .Ar instance and .Ar name by running .Xr xprop 1 and then clicking on the desired window. In the following example the main window of Firefox was clicked: .Bd -literal -offset indent $ xprop | grep \-E "^(WM_CLASS|_NET_WM_NAME|WM_NAME)" WM_CLASS(STRING) = "Navigator", "Firefox" WM_NAME(STRING) = "spectrwm - ConformalOpenSource" _NET_WM_NAME(UTF8_STRING) = "spectrwm - ConformalOpenSource" .Ed .Pp Note that .Xr xprop 1 displays WM_CLASS as: .Bd -literal -offset indent WM_CLASS(STRING) = "", "" .Ed .Pp In the example above the quirk entry would be: .Bd -literal -offset indent quirk[Firefox:Navigator] = FLOAT .Ed .Pp .Nm also automatically assigns quirks to windows based on the value of the window's _NET_WM_WINDOW_TYPE property as follows: .Pp .Bl -tag -width "_NET_WM_WINDOW_TYPE_TOOLBARXXX" -offset indent -compact .It _NET_WM_WINDOW_TYPE_DOCK FLOAT + ANYWHERE .It _NET_WM_WINDOW_TYPE_TOOLBAR FLOAT + ANYWHERE .It _NET_WM_WINDOW_TYPE_UTILITY FLOAT + ANYWHERE .It _NET_WM_WINDOW_TYPE_SPLASH FLOAT .It _NET_WM_WINDOW_TYPE_DIALOG FLOAT .El .Pp In all other cases, no automatic quirks are assigned to the window. Quirks specified in the configuration file override the automatic quirks. .Sh EWMH .Nm partially implements the Extended Window Manager Hints (EWMH) specification. This enables controlling windows as well as .Nm itself from external scripts and programs. This is achieved by .Nm responding to certain ClientMessage events. From the terminal these events can be conveniently sent using tools such as .Xr wmctrl 1 and .Xr xdotool 1 . For the actual format of these ClientMessage events, see the EWMH specification. .Pp The id of the currently focused window is stored in the _NET_ACTIVE_WINDOW property of the root window. This can be used for example to retrieve the title of the currently active window with .Xr xprop 1 and .Xr grep 1 : .Bd -literal -offset indent $ WINDOWID=`xprop \-root _NET_ACTIVE_WINDOW | grep \-o "0x.*"` $ xprop \-id $WINDOWID _NET_WM_NAME | grep \-o "\\".*\\"" .Ed .Pp A window can be focused by sending a _NET_ACTIVE_WINDOW client message to the root window. For example, using .Xr wmctrl 1 to send the message (assuming 0x4a0000b is the id of the window to be focused): .Bd -literal -offset indent $ wmctrl \-i \-a 0x4a0000b .Ed .Pp Windows can be closed by sending a _NET_CLOSE_WINDOW client message to the root window. For example, using .Xr wmctrl 1 to send the message (assuming 0x4a0000b is the id of the window to be closed): .Bd -literal -offset indent $ wmctrl \-i \-c 0x4a0000b .Ed .Pp Windows can be floated and un-floated by adding or removing the _NET_WM_STATE_ABOVE atom from the _NET_WM_STATE property of the window. This can be achieved by sending a _NET_WM_STATE client message to the root window. For example, the following toggles the floating state of a window using .Xr wmctrl 1 to send the message (assuming 0x4a0000b is the id of the window to be floated or un-floated): .Bd -literal -offset indent $ wmctrl \-i \-r 0x4a0000b \-b toggle,_NET_WM_STATE_ABOVE .Ed .Pp Windows can also be iconified and un-iconified by substituting _NET_WM_STATE_HIDDEN for _NET_WM_STATE_ABOVE in the previous example: .Bd -literal -offset indent $ wmctrl \-i \-r 0x4a0000b \-b toggle,_NET_WM_STATE_HIDDEN .Ed .Pp Floating windows can also be resized and moved by sending a _NET_MOVERESIZE_WINDOW client message to the root window. For example, using .Xr wmctrl 1 to send the message (assuming 0x4a0000b is the id of the window to be resize/moved): .Bd -literal -offset indent $ wmctrl \-i \-r 0x4a0000b \-e 0,100,50,640,480 .Ed .Pp This moves the window to (100,50) and resizes it to 640x480. .Pp Any _NET_MOVERESIZE_WINDOW events received for stacked windows are ignored. .Sh SIGNALS Sending .Nm a HUP signal will restart it. .Sh FILES .Bl -tag -width "/etc/spectrwm.confXXX" -compact .It Pa ~/.spectrwm.conf .Nm user specific settings. .It Pa /etc/spectrwm.conf .Nm global settings. .El .Sh HISTORY .Nm was inspired by xmonad & dwm. .Sh AUTHORS .An -nosplit .Nm was written by: .Pp .Bl -tag -width "Ryan Thomas McBride Aq mcbride@countersiege.com " -offset \ indent -compact .It Cm Marco Peereboom Aq marco@peereboom.us .It Cm Ryan Thomas McBride Aq mcbride@countersiege.com .It Cm Darrin Chandler Aq dwchandler@stilyagin.com .It Cm Pierre-Yves Ritschard Aq pyr@spootnik.org .It Cm Tuukka Kataja Aq stuge@xor.fi .It Cm Jason L. Wright Aq jason@thought.net .It Cm Reginald Kennedy Aq rk@rejii.com .It Cm Lawrence Teo Aq lteo@lteo.net .It Cm Tiago Cunha Aq tcunha@gmx.com .It Cm David Hill Aq dhill@mindcry.org .El spectrwm-2.6.0/spectrwm.c010064400017500000000000010303431237571307300144220ustar00marcowheel/* * 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-2014 Reginald Kennedy * Copyright (c) 2011-2012 Lawrence Teo * Copyright (c) 2011-2012 Tiago Cunha * Copyright (c) 2012-2013 David Hill * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * 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. */ /* kernel includes */ #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 /* /usr/includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* local includes */ #include "version.h" #ifdef __OSX__ #include #endif #ifdef SPECTRWM_BUILDSTR static const char *buildstr = SPECTRWM_BUILDSTR; #else static const char *buildstr = SPECTRWM_VERSION; #endif #if !defined(__CYGWIN__) /* cygwin chokes on randr stuff */ # if RANDR_MAJOR < 1 # error RandR versions less than 1.0 are not supported #endif # if RANDR_MAJOR >= 1 # if RANDR_MINOR >= 2 # define SWM_XRR_HAS_CRTC # endif # endif #endif /* __CYGWIN__ */ #ifndef XCB_ICCCM_NUM_WM_HINTS_ELEMENTS #define XCB_ICCCM_SIZE_HINT_P_MIN_SIZE XCB_SIZE_HINT_P_MIN_SIZE #define XCB_ICCCM_SIZE_HINT_P_MAX_SIZE XCB_SIZE_HINT_P_MAX_SIZE #define XCB_ICCCM_SIZE_HINT_P_RESIZE_INC XCB_SIZE_HINT_P_RESIZE_INC #define XCB_ICCCM_WM_HINT_INPUT XCB_WM_HINT_INPUT #define XCB_ICCCM_WM_HINT_X_URGENCY XCB_WM_HINT_X_URGENCY #define XCB_ICCCM_WM_STATE_ICONIC XCB_WM_STATE_ICONIC #define XCB_ICCCM_WM_STATE_WITHDRAWN XCB_WM_STATE_WITHDRAWN #define XCB_ICCCM_WM_STATE_NORMAL XCB_WM_STATE_NORMAL #define xcb_icccm_get_text_property_reply_t xcb_get_text_property_reply_t #define xcb_icccm_get_text_property_reply_wipe xcb_get_text_property_reply_wipe #define xcb_icccm_get_wm_class xcb_get_wm_class #define xcb_icccm_get_wm_class_reply xcb_get_wm_class_reply #define xcb_icccm_get_wm_class_reply_t xcb_get_wm_class_reply_t #define xcb_icccm_get_wm_class_reply_wipe xcb_get_wm_class_reply_wipe #define xcb_icccm_get_wm_hints xcb_get_wm_hints #define xcb_icccm_wm_hints_get_urgency xcb_wm_hints_get_urgency #define xcb_icccm_get_wm_hints_reply xcb_get_wm_hints_reply #define xcb_icccm_get_wm_name xcb_get_wm_name #define xcb_icccm_get_wm_name_reply xcb_get_wm_name_reply #define xcb_icccm_get_wm_normal_hints xcb_get_wm_normal_hints #define xcb_icccm_get_wm_normal_hints_reply xcb_get_wm_normal_hints_reply #define xcb_icccm_get_wm_protocols xcb_get_wm_protocols #define xcb_icccm_get_wm_protocols_reply xcb_get_wm_protocols_reply #define xcb_icccm_get_wm_protocols_reply_t xcb_get_wm_protocols_reply_t #define xcb_icccm_get_wm_protocols_reply_wipe xcb_get_wm_protocols_reply_wipe #define xcb_icccm_get_wm_transient_for xcb_get_wm_transient_for #define xcb_icccm_get_wm_transient_for_reply xcb_get_wm_transient_for_reply #define xcb_icccm_wm_hints_t xcb_wm_hints_t #endif /*#define SWM_DEBUG*/ #ifdef SWM_DEBUG #define DPRINTF(x...) do { \ if (swm_debug) \ fprintf(stderr, x); \ } while (0) #define DNPRINTF(n,x...) do { \ if (swm_debug & n) { \ fprintf(stderr, "%ld ", (long)(time(NULL) - time_started)); \ fprintf(stderr, x); \ } \ } while (0) #define SWM_D_MISC 0x0001 #define SWM_D_EVENT 0x0002 #define SWM_D_WS 0x0004 #define SWM_D_FOCUS 0x0008 #define SWM_D_MOVE 0x0010 #define SWM_D_STACK 0x0020 #define SWM_D_MOUSE 0x0040 #define SWM_D_PROP 0x0080 #define SWM_D_CLASS 0x0100 #define SWM_D_KEY 0x0200 #define SWM_D_QUIRK 0x0400 #define SWM_D_SPAWN 0x0800 #define SWM_D_EVENTQ 0x1000 #define SWM_D_CONF 0x2000 #define SWM_D_BAR 0x4000 #define SWM_D_INIT 0x8000 uint32_t swm_debug = 0 | SWM_D_MISC | SWM_D_EVENT | SWM_D_WS | SWM_D_FOCUS | SWM_D_MOVE | SWM_D_STACK | SWM_D_MOUSE | SWM_D_PROP | SWM_D_CLASS | SWM_D_KEY | SWM_D_QUIRK | SWM_D_SPAWN | SWM_D_EVENTQ | SWM_D_CONF | SWM_D_BAR | SWM_D_INIT ; #else #define DPRINTF(x...) #define DNPRINTF(n,x...) #endif #define SWM_EWMH_ACTION_COUNT_MAX (8) #define EWMH_F_FULLSCREEN (0x001) #define EWMH_F_ABOVE (0x002) #define EWMH_F_HIDDEN (0x004) #define EWMH_F_MAXIMIZED_VERT (0x008) #define EWMH_F_MAXIMIZED_HORZ (0x010) #define EWMH_F_SKIP_PAGER (0x020) #define EWMH_F_SKIP_TASKBAR (0x040) #define SWM_F_MANUAL (0x080) #define EWMH_F_MAXIMIZED (EWMH_F_MAXIMIZED_VERT | EWMH_F_MAXIMIZED_HORZ) /* convert 8-bit to 16-bit */ #define RGB_8_TO_16(col) (((col) << 8) + (col)) #define PIXEL_TO_XRENDERCOLOR(px, xrc) \ xrc.red = RGB_8_TO_16((px) >> 16 & 0xff); \ xrc.green = RGB_8_TO_16((px) >> 8 & 0xff); \ xrc.blue = RGB_8_TO_16((px) & 0xff); \ xrc.alpha = 0xffff; #define LENGTH(x) (int)(sizeof (x) / sizeof (x)[0]) #define MODKEY XCB_MOD_MASK_1 #define CLEANMASK(mask) ((mask) & ~(numlockmask | XCB_MOD_MASK_LOCK)) #define BUTTONMASK (XCB_EVENT_MASK_BUTTON_PRESS | \ XCB_EVENT_MASK_BUTTON_RELEASE) #define MOUSEMASK (BUTTONMASK|XCB_EVENT_MASK_POINTER_MOTION) #define SWM_PROPLEN (16) #define SWM_FUNCNAME_LEN (32) #define SWM_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 BORDER(w) ((w)->bordered ? border_width : 0) #define MAX_X(r) ((r)->g.x + (r)->g.w) #define MAX_Y(r) ((r)->g.y + (r)->g.h) #define SH_MIN(w) ((w)->sh.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) #define SH_MIN_W(w) ((w)->sh.min_width) #define SH_MIN_H(w) ((w)->sh.min_height) #define SH_MAX(w) ((w)->sh.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE) #define SH_MAX_W(w) ((w)->sh.max_width) #define SH_MAX_H(w) ((w)->sh.max_height) #define SH_INC(w) ((w)->sh.flags & XCB_ICCCM_SIZE_HINT_P_RESIZE_INC) #define SH_INC_W(w) ((w)->sh.width_inc) #define SH_INC_H(w) ((w)->sh.height_inc) #define SWM_MAX_FONT_STEPS (3) #define WINID(w) ((w) ? (w)->id : XCB_WINDOW_NONE) #define WS_FOCUSED(ws) ((ws)->r && (ws)->r->s->r_focus == (ws)->r) #define YESNO(x) ((x) ? "yes" : "no") #define ICONIC(w) ((w)->ewmh_flags & EWMH_F_HIDDEN) #define ABOVE(w) ((w)->ewmh_flags & EWMH_F_ABOVE) #define FULLSCREEN(w) ((w)->ewmh_flags & EWMH_F_FULLSCREEN) #define MAXIMIZED_VERT(w) ((w)->ewmh_flags & EWMH_F_MAXIMIZED_VERT) #define MAXIMIZED_HORZ(w) ((w)->ewmh_flags & EWMH_F_MAXIMIZED_HORZ) #define MAXIMIZED(w) (MAXIMIZED_VERT(w) || MAXIMIZED_HORZ(w)) #define MANUAL(w) ((w)->ewmh_flags & SWM_F_MANUAL) #define TRANS(w) ((w)->transient != XCB_WINDOW_NONE) #define FLOATING(w) (ABOVE(w) || TRANS(w) || FULLSCREEN(w) || \ MAXIMIZED(w)) /* Constrain Window flags */ #define SWM_CW_RESIZABLE (0x01) #define SWM_CW_SOFTBOUNDARY (0x02) #define SWM_CW_HARDBOUNDARY (0x04) #define SWM_CW_RIGHT (0x10) #define SWM_CW_LEFT (0x20) #define SWM_CW_BOTTOM (0x40) #define SWM_CW_TOP (0x80) #define SWM_CW_ALLSIDES (0xf0) #define SWM_FOCUS_DEFAULT (0) #define SWM_FOCUS_FOLLOW (1) #define SWM_FOCUS_MANUAL (2) #define SWM_CK_NONE (0) #define SWM_CK_ALL (0xf) #define SWM_CK_FOCUS (0x1) #define SWM_CK_POINTER (0x2) #define SWM_CK_FALLBACK (0x4) #define SWM_CK_REGION (0x8) #define SWM_G_ALL (0xf) #define SWM_G_SIZE (0x1) #define SWM_G_POS (0x2) #define SWM_CONF_DEFAULT (0) #define SWM_CONF_KEYMAPPING (1) #ifndef SWM_LIB #define SWM_LIB "/usr/local/lib/libswmhack.so" #endif char **start_argv; xcb_atom_t a_state; xcb_atom_t a_prot; xcb_atom_t a_delete; xcb_atom_t a_net_wm_check; xcb_atom_t a_net_supported; xcb_atom_t a_takefocus; xcb_atom_t a_utf8_string; xcb_atom_t a_swm_ws; volatile sig_atomic_t running = 1; volatile sig_atomic_t restart_wm = 0; xcb_timestamp_t last_event_time = 0; int outputs = 0; bool randr_support; int randr_eventbase; unsigned int numlockmask = 0; Display *display; xcb_connection_t *conn; xcb_key_symbols_t *syms; int boundary_width = 50; bool cycle_empty = false; bool cycle_visible = false; int term_width = 0; int font_adjusted = 0; unsigned int mod_key = MODKEY; bool warp_pointer = false; /* dmenu search */ struct swm_region *search_r; int select_list_pipe[2]; int select_resp_pipe[2]; pid_t searchpid; volatile sig_atomic_t search_resp; int search_resp_action; struct search_window { TAILQ_ENTRY(search_window) entry; int idx; struct ws_win *win; xcb_gcontext_t gc; xcb_window_t indicator; }; TAILQ_HEAD(search_winlist, search_window); 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 }; #define SWM_STACK_TOP (0) #define SWM_STACK_BOTTOM (1) #define SWM_STACK_ABOVE (2) #define SWM_STACK_BELOW (3) /* dialog windows */ double dialog_ratio = 0.6; /* status bar */ #define SWM_BAR_MAX (256) #define SWM_BAR_JUSTIFY_LEFT (0) #define SWM_BAR_JUSTIFY_CENTER (1) #define SWM_BAR_JUSTIFY_RIGHT (2) #define SWM_BAR_OFFSET (4) #define SWM_BAR_FONTS "-*-terminus-medium-*-*-*-12-*-*-*-*-*-*-*," \ "-*-profont-*-*-*-*-12-*-*-*-*-*-*-*," \ "-*-times-medium-r-*-*-12-*-*-*-*-*-*-*," \ "-misc-fixed-medium-r-*-*-12-*-*-*-*-*-*-*," \ "-*-*-*-r-*-*-*-*-*-*-*-*-*-*" #ifdef X_HAVE_UTF8_STRING #define DRAWSTRING(x...) Xutf8DrawString(x) #else #define DRAWSTRING(x...) XmbDrawString(x) #endif char *bar_argv[] = { NULL, NULL }; int bar_pipe[2]; char bar_ext[SWM_BAR_MAX]; char bar_ext_buf[SWM_BAR_MAX]; char bar_vertext[SWM_BAR_MAX]; bool bar_version = false; bool bar_enabled = true; int bar_border_width = 1; bool bar_at_bottom = false; bool bar_extra = false; int bar_height = 0; int bar_justify = SWM_BAR_JUSTIFY_LEFT; char *bar_format = NULL; bool stack_enabled = true; bool clock_enabled = true; bool iconic_enabled = false; bool urgent_enabled = false; bool urgent_collapse = false; char *clock_format = NULL; bool window_class_enabled = false; bool window_instance_enabled = false; bool window_name_enabled = false; int focus_mode = SWM_FOCUS_DEFAULT; int focus_close = SWM_STACK_BELOW; bool focus_close_wrap = true; int focus_default = SWM_STACK_TOP; int spawn_position = SWM_STACK_TOP; bool disable_border = false; int border_width = 1; int region_padding = 0; int tile_gap = 0; bool java_workaround = true; bool verbose_layout = false; #ifdef SWM_DEBUG time_t time_started; #endif pid_t bar_pid; XFontSet bar_fs; XFontSetExtents *bar_fs_extents; XftFont *bar_font; bool bar_font_legacy = true; char *bar_fonts; XftColor bar_font_color; XftColor search_font_color; struct passwd *pwd; char *startup_exception; unsigned int nr_exceptions = 0; /* layout manager data */ struct swm_geometry { int x; int y; int w; int h; }; struct swm_screen; struct workspace; struct swm_bar { xcb_window_t id; xcb_pixmap_t buffer; struct swm_geometry g; }; /* virtual "screens" */ struct swm_region { TAILQ_ENTRY(swm_region) entry; xcb_window_t id; struct swm_geometry g; struct workspace *ws; /* current workspace on this region */ struct workspace *ws_prior; /* prior workspace on this region */ struct swm_screen *s; /* screen idx */ struct swm_bar *bar; }; TAILQ_HEAD(swm_region_list, swm_region); struct ws_win { TAILQ_ENTRY(ws_win) entry; TAILQ_ENTRY(ws_win) stack_entry; xcb_window_t id; xcb_window_t transient; struct ws_win *focus_child; /* focus on child transient */ struct swm_geometry g; /* current geometry */ struct swm_geometry g_float; /* region coordinates */ bool g_floatvalid; /* g_float geometry validity */ bool mapped; bool bordered; uint32_t ewmh_flags; int font_size_boundary[SWM_MAX_FONT_STEPS]; int font_steps; int last_inc; bool can_delete; bool take_focus; bool java; uint32_t quirks; struct workspace *ws; /* always valid */ struct swm_screen *s; /* always valid, never changes */ xcb_size_hints_t sh; xcb_icccm_get_wm_class_reply_t ch; xcb_icccm_wm_hints_t hints; }; TAILQ_HEAD(ws_win_list, ws_win); TAILQ_HEAD(ws_win_stack, ws_win); /* pid goo */ struct pid_e { TAILQ_ENTRY(pid_e) entry; pid_t pid; int ws; }; TAILQ_HEAD(pid_list, pid_e); 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 layout { void (*l_stack)(struct workspace *, struct swm_geometry *); void (*l_config)(struct workspace *, int); uint32_t flags; #define SWM_L_FOCUSPREV (1<<0) #define SWM_L_MAPONFOCUS (1<<1) void (*l_string)(struct workspace *); } layouts[] = { /* stack, configure */ { vertical_stack, vertical_config, 0, plain_stacker }, { horizontal_stack, horizontal_config, 0, plain_stacker }, { max_stack, NULL, SWM_L_MAPONFOCUS | SWM_L_FOCUSPREV, plain_stacker }, { NULL, NULL, 0, NULL }, }; /* position of max_stack mode in the layouts array, index into layouts! */ #define SWM_V_STACK (0) #define SWM_H_STACK (1) #define SWM_MAX_STACK (2) #define SWM_H_SLICE (32) #define SWM_V_SLICE (32) /* define work spaces */ struct workspace { int idx; /* workspace index */ char *name; /* workspace name */ bool always_raise; /* raise windows on focus */ bool bar_enabled; /* bar visibility */ struct layout *cur_layout; /* current layout handlers */ struct ws_win *focus; /* may be NULL */ struct ws_win *focus_prev; /* may be NULL */ struct ws_win *focus_pending; /* may be NULL */ struct swm_region *r; /* may be NULL */ struct swm_region *old_r; /* may be NULL */ struct ws_win_list winlist; /* list of windows in ws */ struct ws_win_list unmanagedlist; /* list of dead windows in ws */ struct ws_win_stack stack; /* stacking order */ int state; /* mapping state */ char stacker[10]; /* display stacker and layout */ /* stacker state */ struct { int horizontal_msize; int horizontal_mwin; int horizontal_stacks; bool horizontal_flip; int vertical_msize; int vertical_mwin; int vertical_stacks; bool vertical_flip; } l_state; }; enum { SWM_WS_STATE_HIDDEN, SWM_WS_STATE_MAPPING, SWM_WS_STATE_MAPPED, }; enum { SWM_S_COLOR_BAR, SWM_S_COLOR_BAR_BORDER, SWM_S_COLOR_BAR_BORDER_UNFOCUS, SWM_S_COLOR_BAR_FONT, SWM_S_COLOR_FOCUS, SWM_S_COLOR_FOCUS_MAXIMIZED, SWM_S_COLOR_UNFOCUS, SWM_S_COLOR_UNFOCUS_MAXIMIZED, SWM_S_COLOR_MAX }; /* physical screen mapping */ #define SWM_WS_MAX (22) /* hard limit */ int workspace_limit = 10; /* soft limit */ struct swm_screen { int idx; /* screen index */ struct swm_region_list rl; /* list of regions on this screen */ struct swm_region_list orl; /* list of old regions */ xcb_window_t root; struct workspace ws[SWM_WS_MAX]; struct swm_region *r_focus; /* colors */ struct { uint32_t pixel; char *name; int manual; } c[SWM_S_COLOR_MAX]; xcb_gcontext_t bar_gc; GC bar_gc_legacy; }; struct swm_screen *screens; /* args to functions */ union arg { int id; #define SWM_ARG_ID_FOCUSNEXT (0) #define SWM_ARG_ID_FOCUSPREV (1) #define SWM_ARG_ID_FOCUSMAIN (2) #define SWM_ARG_ID_FOCUSURGENT (3) #define SWM_ARG_ID_SWAPNEXT (10) #define SWM_ARG_ID_SWAPPREV (11) #define SWM_ARG_ID_SWAPMAIN (12) #define SWM_ARG_ID_MOVELAST (13) #define SWM_ARG_ID_MASTERSHRINK (20) #define SWM_ARG_ID_MASTERGROW (21) #define SWM_ARG_ID_MASTERADD (22) #define SWM_ARG_ID_MASTERDEL (23) #define SWM_ARG_ID_FLIPLAYOUT (24) #define SWM_ARG_ID_STACKRESET (30) #define SWM_ARG_ID_STACKINIT (31) #define SWM_ARG_ID_CYCLEWS_UP (40) #define SWM_ARG_ID_CYCLEWS_DOWN (41) #define SWM_ARG_ID_CYCLERG_UP (42) #define SWM_ARG_ID_CYCLERG_DOWN (43) #define SWM_ARG_ID_CYCLEWS_UP_ALL (44) #define SWM_ARG_ID_CYCLEWS_DOWN_ALL (45) #define SWM_ARG_ID_CYCLEWS_MOVE_UP (46) #define SWM_ARG_ID_CYCLEWS_MOVE_DOWN (47) #define SWM_ARG_ID_STACKINC (50) #define SWM_ARG_ID_STACKDEC (51) #define SWM_ARG_ID_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) #define SWM_ARG_ID_RAISE (105) #define SWM_ARG_ID_LOWER (106) #define SWM_ARG_ID_BAR_TOGGLE (110) #define SWM_ARG_ID_BAR_TOGGLE_WS (111) char **argv; }; /* quirks */ struct quirk { TAILQ_ENTRY(quirk) entry; char *class; /* WM_CLASS:class */ char *instance; /* WM_CLASS:instance */ char *name; /* WM_NAME */ regex_t regex_class; regex_t regex_instance; regex_t regex_name; uint32_t quirk; int ws; /* Initial workspace. */ #define SWM_Q_FLOAT (1<<0) /* float this window */ #define SWM_Q_TRANSSZ (1<<1) /* 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 */ #define SWM_Q_NOFOCUSONMAP (1<<6) /* Don't focus on window when mapped. */ #define SWM_Q_FOCUSONMAP_SINGLE (1<<7) /* Only focus if single win of type. */ #define SWM_Q_OBEYAPPFOCUSREQ (1<<8) /* Focus when applications ask. */ #define SWM_Q_IGNOREPID (1<<9) /* Ignore PID when determining ws. */ #define SWM_Q_IGNORESPAWNWS (1<<10) /* Ignore _SWM_WS when managing win. */ }; 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_CLIENT_LIST, _NET_CLOSE_WINDOW, _NET_CURRENT_DESKTOP, _NET_DESKTOP_GEOMETRY, _NET_DESKTOP_NAMES, _NET_DESKTOP_VIEWPORT, _NET_MOVERESIZE_WINDOW, _NET_NUMBER_OF_DESKTOPS, _NET_RESTACK_WINDOW, _NET_WM_ACTION_ABOVE, _NET_WM_ACTION_CLOSE, _NET_WM_ACTION_FULLSCREEN, _NET_WM_ACTION_MOVE, _NET_WM_ACTION_RESIZE, _NET_WM_ALLOWED_ACTIONS, _NET_WM_DESKTOP, _NET_WM_FULL_PLACEMENT, _NET_WM_NAME, _NET_WM_STATE, _NET_WM_STATE_ABOVE, _NET_WM_STATE_FULLSCREEN, _NET_WM_STATE_HIDDEN, _NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_SKIP_PAGER, _NET_WM_STATE_SKIP_TASKBAR, _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_WINDOW_TYPE_DOCK, _NET_WM_WINDOW_TYPE_NORMAL, _NET_WM_WINDOW_TYPE_SPLASH, _NET_WM_WINDOW_TYPE_TOOLBAR, _NET_WM_WINDOW_TYPE_UTILITY, _SWM_WM_STATE_MANUAL, SWM_EWMH_HINT_MAX }; struct ewmh_hint { char *name; xcb_atom_t atom; } ewmh[SWM_EWMH_HINT_MAX] = { /* must be in same order as in the enum */ {"_NET_ACTIVE_WINDOW", XCB_ATOM_NONE}, {"_NET_CLIENT_LIST", XCB_ATOM_NONE}, {"_NET_CLOSE_WINDOW", XCB_ATOM_NONE}, {"_NET_CURRENT_DESKTOP", XCB_ATOM_NONE}, {"_NET_DESKTOP_GEOMETRY", XCB_ATOM_NONE}, {"_NET_DESKTOP_NAMES", XCB_ATOM_NONE}, {"_NET_DESKTOP_VIEWPORT", XCB_ATOM_NONE}, {"_NET_MOVERESIZE_WINDOW", XCB_ATOM_NONE}, {"_NET_NUMBER_OF_DESKTOPS", XCB_ATOM_NONE}, {"_NET_RESTACK_WINDOW", XCB_ATOM_NONE}, {"_NET_WM_ACTION_ABOVE", XCB_ATOM_NONE}, {"_NET_WM_ACTION_CLOSE", XCB_ATOM_NONE}, {"_NET_WM_ACTION_FULLSCREEN", XCB_ATOM_NONE}, {"_NET_WM_ACTION_MOVE", XCB_ATOM_NONE}, {"_NET_WM_ACTION_RESIZE", XCB_ATOM_NONE}, {"_NET_WM_ALLOWED_ACTIONS", XCB_ATOM_NONE}, {"_NET_WM_DESKTOP", XCB_ATOM_NONE}, {"_NET_WM_FULL_PLACEMENT", XCB_ATOM_NONE}, {"_NET_WM_NAME", XCB_ATOM_NONE}, {"_NET_WM_STATE", XCB_ATOM_NONE}, {"_NET_WM_STATE_ABOVE", XCB_ATOM_NONE}, {"_NET_WM_STATE_FULLSCREEN", XCB_ATOM_NONE}, {"_NET_WM_STATE_HIDDEN", XCB_ATOM_NONE}, {"_NET_WM_STATE_MAXIMIZED_VERT", XCB_ATOM_NONE}, {"_NET_WM_STATE_MAXIMIZED_HORZ", XCB_ATOM_NONE}, {"_NET_WM_STATE_SKIP_PAGER", XCB_ATOM_NONE}, {"_NET_WM_STATE_SKIP_TASKBAR", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE_DIALOG", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE_DOCK", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE_NORMAL", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE_SPLASH", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE_TOOLBAR", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE_UTILITY", XCB_ATOM_NONE}, {"_SWM_WM_STATE_MANUAL", XCB_ATOM_NONE}, }; /* EWMH source type */ enum { EWMH_SOURCE_TYPE_NONE = 0, EWMH_SOURCE_TYPE_NORMAL = 1, EWMH_SOURCE_TYPE_OTHER = 2, }; /* Cursors */ enum { XC_FLEUR, XC_LEFT_PTR, XC_BOTTOM_LEFT_CORNER, XC_BOTTOM_RIGHT_CORNER, XC_SIZING, XC_TOP_LEFT_CORNER, XC_TOP_RIGHT_CORNER, XC_MAX }; struct cursors { char *name; /* Name used by Xcursor .*/ uint8_t cf_char; /* cursorfont index. */ xcb_cursor_t cid; } cursors[XC_MAX] = { {"fleur", XC_fleur, XCB_CURSOR_NONE}, {"left_ptr", XC_left_ptr, XCB_CURSOR_NONE}, {"bottom_left_corner", XC_bottom_left_corner, XCB_CURSOR_NONE}, {"bottom_right_corner", XC_bottom_right_corner, XCB_CURSOR_NONE}, {"sizing", XC_sizing, XCB_CURSOR_NONE}, {"top_left_corner", XC_top_left_corner, XCB_CURSOR_NONE}, {"top_right_corner", XC_top_right_corner, XCB_CURSOR_NONE}, }; #define SWM_SPAWN_OPTIONAL 0x1 /* spawn */ struct spawn_prog { TAILQ_ENTRY(spawn_prog) entry; char *name; int argc; char **argv; int flags; }; TAILQ_HEAD(spawn_list, spawn_prog); struct spawn_list spawns = TAILQ_HEAD_INITIALIZER(spawns); /* user/key callable function IDs */ enum keyfuncid { KF_BAR_TOGGLE, KF_BAR_TOGGLE_WS, KF_BUTTON2, KF_CYCLE_LAYOUT, KF_FLIP_LAYOUT, KF_FLOAT_TOGGLE, KF_FOCUS_MAIN, KF_FOCUS_NEXT, KF_FOCUS_PREV, KF_FOCUS_URGENT, KF_MAXIMIZE_TOGGLE, KF_HEIGHT_GROW, KF_HEIGHT_SHRINK, KF_ICONIFY, KF_MASTER_SHRINK, KF_MASTER_GROW, KF_MASTER_ADD, KF_MASTER_DEL, KF_MOVE_DOWN, KF_MOVE_LEFT, KF_MOVE_RIGHT, KF_MOVE_UP, KF_MVRG_1, KF_MVRG_2, KF_MVRG_3, KF_MVRG_4, KF_MVRG_5, KF_MVRG_6, KF_MVRG_7, KF_MVRG_8, KF_MVRG_9, 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_MVWS_11, KF_MVWS_12, KF_MVWS_13, KF_MVWS_14, KF_MVWS_15, KF_MVWS_16, KF_MVWS_17, KF_MVWS_18, KF_MVWS_19, KF_MVWS_20, KF_MVWS_21, KF_MVWS_22, KF_NAME_WORKSPACE, KF_QUIT, KF_RAISE_TOGGLE, KF_RESTART, KF_RG_1, KF_RG_2, KF_RG_3, KF_RG_4, KF_RG_5, KF_RG_6, KF_RG_7, KF_RG_8, KF_RG_9, KF_RG_NEXT, KF_RG_PREV, KF_SCREEN_NEXT, KF_SCREEN_PREV, KF_SEARCH_WIN, KF_SEARCH_WORKSPACE, KF_SPAWN_CUSTOM, KF_STACK_INC, KF_STACK_DEC, KF_STACK_RESET, KF_SWAP_MAIN, KF_SWAP_NEXT, KF_SWAP_PREV, KF_UNICONIFY, KF_VERSION, KF_WIDTH_GROW, KF_WIDTH_SHRINK, KF_WIND_DEL, KF_WIND_KILL, 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_11, KF_WS_12, KF_WS_13, KF_WS_14, KF_WS_15, KF_WS_16, KF_WS_17, KF_WS_18, KF_WS_19, KF_WS_20, KF_WS_21, KF_WS_22, KF_WS_NEXT, KF_WS_NEXT_ALL, KF_WS_NEXT_MOVE, KF_WS_PREV, KF_WS_PREV_ALL, KF_WS_PREV_MOVE, KF_WS_PRIOR, KF_DUMPWINS, /* MUST BE LAST */ KF_INVALID }; struct key { RB_ENTRY(key) entry; unsigned int mod; KeySym keysym; enum keyfuncid funcid; char *spawn_name; }; RB_HEAD(key_tree, key); /* function prototypes */ void adjust_font(struct ws_win *); char *argsep(char **); void bar_cleanup(struct swm_region *); void bar_extra_setup(void); void bar_extra_stop(void); int bar_extra_update(void); void bar_fmt(const char *, char *, struct swm_region *, size_t); void bar_fmt_expand(char *, size_t); void bar_draw(void); void bar_print(struct swm_region *, const char *); void bar_print_legacy(struct swm_region *, const char *); void bar_replace(char *, char *, struct swm_region *, size_t); void bar_replace_pad(char *, int *, size_t); char *bar_replace_seq(char *, char *, struct swm_region *, size_t *, size_t); void bar_setup(struct swm_region *); void bar_toggle(struct swm_region *, union arg *); void bar_urgent(char *, size_t); void bar_window_class(char *, size_t, struct swm_region *); void bar_window_class_instance(char *, size_t, struct swm_region *); void bar_window_float(char *, size_t, struct swm_region *); void bar_window_instance(char *, size_t, struct swm_region *); void bar_window_name(char *, size_t, struct swm_region *); void bar_window_state(char *, size_t, struct swm_region *); void bar_workspace_name(char *, size_t, struct swm_region *); void buttonpress(xcb_button_press_event_t *); void center_pointer(struct swm_region *); void check_conn(void); void clear_keys(void); int clear_maximized(struct workspace *); void clientmessage(xcb_client_message_event_t *); void client_msg(struct ws_win *, xcb_atom_t, xcb_timestamp_t); int conf_load(const char *, int); void configurenotify(xcb_configure_notify_event_t *); void configurerequest(xcb_configure_request_event_t *); void config_win(struct ws_win *, xcb_configure_request_event_t *); void constrain_window(struct ws_win *, struct swm_geometry *, int *); int count_win(struct workspace *, bool); void cursors_cleanup(void); void cursors_load(void); void custom_region(const char *); void cyclerg(struct swm_region *, union arg *); void cyclews(struct swm_region *, union arg *); void cycle_layout(struct swm_region *, union arg *); void destroynotify(xcb_destroy_notify_event_t *); void dumpwins(struct swm_region *, union arg *); int enable_wm(void); void enternotify(xcb_enter_notify_event_t *); void event_drain(uint8_t); void event_error(xcb_generic_error_t *); void event_handle(xcb_generic_event_t *); void ewmh_apply_flags(struct ws_win *, uint32_t); void ewmh_autoquirk(struct ws_win *); void ewmh_get_desktop_names(void); void ewmh_get_wm_state(struct ws_win *); void ewmh_update_actions(struct ws_win *); void ewmh_update_client_list(void); void ewmh_update_current_desktop(void); void ewmh_update_desktop_names(void); void ewmh_update_desktops(void); void ewmh_change_wm_state(struct ws_win *, xcb_atom_t, long); void ewmh_update_wm_state(struct ws_win *); char *expand_tilde(const char *); void expose(xcb_expose_event_t *); void fake_keypress(struct ws_win *, xcb_keysym_t, uint16_t); struct pid_e *find_pid(pid_t); struct ws_win *find_unmanaged_window(xcb_window_t); struct ws_win *find_window(xcb_window_t); void floating_toggle(struct swm_region *, union arg *); void focus(struct swm_region *, union arg *); #ifdef SWM_DEBUG void focusin(xcb_focus_in_event_t *); void focusout(xcb_focus_out_event_t *); #endif void focus_flush(void); void focus_region(struct swm_region *); void focusrg(struct swm_region *, union arg *); void focus_win(struct ws_win *); void fontset_init(void); void free_window(struct ws_win *); xcb_atom_t get_atom_from_string(const char *); #ifdef SWM_DEBUG char *get_atom_name(xcb_atom_t); #endif struct ws_win *get_focus_magic(struct ws_win *); struct ws_win *get_focus_prev(struct ws_win *); #ifdef SWM_DEBUG char *get_notify_detail_label(uint8_t); char *get_notify_mode_label(uint8_t); #endif struct ws_win *get_pointer_win(xcb_window_t); struct ws_win *get_region_focus(struct swm_region *); int get_region_index(struct swm_region *); xcb_screen_t *get_screen(int); int get_screen_count(void); #ifdef SWM_DEBUG char *get_source_type_label(uint32_t); char *get_stack_mode_name(uint8_t); #endif int32_t get_swm_ws(xcb_window_t); char *get_win_name(xcb_window_t); uint8_t get_win_state(xcb_window_t); void get_wm_protocols(struct ws_win *); int get_ws_idx(struct ws_win *); void grabbuttons(struct ws_win *); void grabkeys(void); void grab_windows(void); void iconify(struct swm_region *, union arg *); bool isxlfd(char *); void keypress(xcb_key_press_event_t *); int key_cmp(struct key *, struct key *); void key_insert(unsigned int, KeySym, enum keyfuncid, const char *); struct key *key_lookup(unsigned int, KeySym); void key_remove(struct key *); void key_replace(struct key *, unsigned int, KeySym, enum keyfuncid, const char *); void kill_bar_extra_atexit(void); void kill_refs(struct ws_win *); #ifdef SWM_DEBUG void leavenotify(xcb_leave_notify_event_t *); #endif void load_float_geom(struct ws_win *); struct ws_win *manage_window(xcb_window_t, int, bool); void map_window(struct ws_win *); void mapnotify(xcb_map_notify_event_t *); void mappingnotify(xcb_mapping_notify_event_t *); void maprequest(xcb_map_request_event_t *); void maximize_toggle(struct swm_region *, union arg *); void motionnotify(xcb_motion_notify_event_t *); void move(struct ws_win *, union arg *); void move_step(struct swm_region *, union arg *); uint32_t name_to_pixel(int, const char *); void name_workspace(struct swm_region *, union arg *); void new_region(struct swm_screen *, int, int, int, int); int parsekeys(const char *, unsigned int, unsigned int *, KeySym *); int parsequirks(const char *, uint32_t *, int *); int parse_rgb(const char *, uint16_t *, uint16_t *, uint16_t *); void pressbutton(struct swm_region *, union arg *); void priorws(struct swm_region *, union arg *); #ifdef SWM_DEBUG void print_win_geom(xcb_window_t); #endif void propertynotify(xcb_property_notify_event_t *); void quirk_free(struct quirk *); void quirk_insert(const char *, const char *, const char *, uint32_t, int); void quirk_remove(struct quirk *); void quirk_replace(struct quirk *, const char *, const char *, const char *, uint32_t, int); void quit(struct swm_region *, union arg *); void raise_toggle(struct swm_region *, union arg *); void raise_window(struct ws_win *); void region_containment(struct ws_win *, struct swm_region *, int); struct swm_region *region_under(struct swm_screen *, int, int); void regionize(struct ws_win *, int, int); void resize(struct ws_win *, union arg *); void resize_step(struct swm_region *, union arg *); void restart(struct swm_region *, union arg *); struct swm_region *root_to_region(xcb_window_t, int); void screenchange(xcb_randr_screen_change_notify_event_t *); void scan_randr(int); void search_do_resp(void); void search_resp_name_workspace(const char *, size_t); void search_resp_search_window(const char *); void search_resp_search_workspace(const char *); void search_resp_uniconify(const char *, size_t); void search_win(struct swm_region *, union arg *); void search_win_cleanup(void); void search_workspace(struct swm_region *, union arg *); void send_to_rg(struct swm_region *, union arg *); void send_to_ws(struct swm_region *, union arg *); void set_region(struct swm_region *); int setautorun(const char *, const char *, int); int setconfbinding(const char *, const char *, int); int setconfcolor(const char *, const char *, int); int setconfmodkey(const char *, const char *, int); int setconfquirk(const char *, const char *, int); int setconfregion(const char *, const char *, int); int setconfspawn(const char *, const char *, int); int setconfvalue(const char *, const char *, int); void setkeybinding(unsigned int, KeySym, enum keyfuncid, const char *); int setkeymapping(const char *, const char *, int); int setlayout(const char *, const char *, int); void setquirk(const char *, const char *, const char *, uint32_t, int); void setscreencolor(const char *, int, int); void setspawn(const char *, const char *, int); void setup_ewmh(void); void setup_globals(void); void setup_keys(void); void setup_quirks(void); void setup_screens(void); void setup_spawn(void); void set_child_transient(struct ws_win *, xcb_window_t *); void set_win_state(struct ws_win *, uint8_t); void shutdown_cleanup(void); void sighdlr(int); void socket_setnonblock(int); void sort_windows(struct ws_win_list *); void spawn(int, union arg *, bool); void spawn_custom(struct swm_region *, union arg *, const char *); int spawn_expand(struct swm_region *, union arg *, const char *, char ***); void spawn_insert(const char *, const char *, int); struct spawn_prog *spawn_find(const char *); void spawn_remove(struct spawn_prog *); void spawn_replace(struct spawn_prog *, const char *, const char *, int); void spawn_select(struct swm_region *, union arg *, const char *, int *); void stack_config(struct swm_region *, union arg *); void stack_master(struct workspace *, struct swm_geometry *, int, bool); void store_float_geom(struct ws_win *); char *strdupsafe(const char *); void swapwin(struct swm_region *, union arg *); void switchws(struct swm_region *, union arg *); void teardown_ewmh(void); void unescape_selector(char *); void unfocus_win(struct ws_win *); void uniconify(struct swm_region *, union arg *); void unmanage_window(struct ws_win *); void unmapnotify(xcb_unmap_notify_event_t *); void unmap_all(void); void unmap_window(struct ws_win *); void updatenumlockmask(void); void update_floater(struct ws_win *); void update_modkey(unsigned int); void update_win_stacking(struct ws_win *); void update_window(struct ws_win *); void update_window_color(struct ws_win *); void update_wm_state(struct ws_win *win); void validate_spawns(void); int validate_win(struct ws_win *); int validate_ws(struct workspace *); void version(struct swm_region *, union arg *); void win_to_ws(struct ws_win *, int, bool); pid_t window_get_pid(xcb_window_t); void wkill(struct swm_region *, union arg *); void update_ws_stack(struct workspace *); void xft_init(struct swm_region *); void _add_startup_exception(const char *, va_list); void add_startup_exception(const char *, ...); RB_PROTOTYPE(key_tree, key, entry, key_cmp); RB_GENERATE(key_tree, key, entry, key_cmp); struct key_tree keys; void cursors_load(void) { xcb_font_t cf = XCB_NONE; int i; for (i = 0; i < LENGTH(cursors); ++i) { /* try to load Xcursor first. */ cursors[i].cid = XcursorLibraryLoadCursor(display, cursors[i].name); /* fallback to cursorfont. */ if (cursors[i].cid == XCB_CURSOR_NONE) { if (cf == XCB_NONE) { cf = xcb_generate_id(conn); xcb_open_font(conn, cf, strlen("cursor"), "cursor"); } cursors[i].cid = xcb_generate_id(conn); xcb_create_glyph_cursor(conn, cursors[i].cid, cf, cf, cursors[i].cf_char, cursors[i].cf_char + 1, 0, 0, 0, 0xffff, 0xffff, 0xffff); } } if (cf != XCB_NONE) xcb_close_font(conn, cf); } void cursors_cleanup(void) { int i; for (i = 0; i < LENGTH(cursors); ++i) xcb_free_cursor(conn, cursors[i].cid); } char * expand_tilde(const char *s) { struct passwd *ppwd; int i; long max; char *user; const char *sc = s; char *result; if (s == NULL) errx(1, "expand_tilde: NULL string."); if (s[0] != '~') { result = strdup(sc); goto out; } ++s; if ((max = sysconf(_SC_LOGIN_NAME_MAX)) == -1) errx(1, "expand_tilde: sysconf"); if ((user = calloc(1, max + 1)) == NULL) errx(1, "expand_tilde: calloc"); for (i = 0; s[i] != '/' && s[i] != '\0'; ++i) user[i] = s[i]; user[i] = '\0'; s = &s[i]; ppwd = strlen(user) == 0 ? getpwuid(getuid()) : getpwnam(user); free(user); if (ppwd == NULL) result = strdup(sc); else if (asprintf(&result, "%s%s", ppwd->pw_dir, s) == -1) result = NULL; out: if (result == NULL) errx(1, "expand_tilde: failed to allocate memory."); return result; } int parse_rgb(const char *rgb, uint16_t *rr, uint16_t *gg, uint16_t *bb) { unsigned int tmpr, tmpg, tmpb; if (sscanf(rgb, "rgb:%x/%x/%x", &tmpr, &tmpg, &tmpb) != 3) return (-1); *rr = RGB_8_TO_16(tmpr); *gg = RGB_8_TO_16(tmpg); *bb = RGB_8_TO_16(tmpb); return (0); } xcb_screen_t * get_screen(int screen) { const xcb_setup_t *r; xcb_screen_iterator_t iter; if ((r = xcb_get_setup(conn)) == NULL) { DNPRINTF(SWM_D_MISC, "get_screen: xcb_get_setup\n"); check_conn(); } iter = xcb_setup_roots_iterator(r); for (; iter.rem; --screen, xcb_screen_next(&iter)) if (screen == 0) return (iter.data); return (NULL); } int get_screen_count(void) { const xcb_setup_t *r; if ((r = xcb_get_setup(conn)) == NULL) { DNPRINTF(SWM_D_MISC, "get_screen_count: xcb_get_setup\n"); check_conn(); } return xcb_setup_roots_length(r); } int get_region_index(struct swm_region *r) { struct swm_region *rr; int ridx = 0; if (r == NULL) return -1; TAILQ_FOREACH(rr, &r->s->rl, entry) { if (rr == r) break; ++ridx; } if (rr == NULL) return -1; return ridx; } void focus_flush(void) { if (focus_mode == SWM_FOCUS_DEFAULT) event_drain(XCB_ENTER_NOTIFY); else xcb_flush(conn); } xcb_atom_t get_atom_from_string(const char *str) { xcb_intern_atom_cookie_t c; xcb_intern_atom_reply_t *r; xcb_atom_t atom; c = xcb_intern_atom(conn, 0, strlen(str), str); r = xcb_intern_atom_reply(conn, c, NULL); if (r) { atom = r->atom; free(r); return (atom); } return (XCB_ATOM_NONE); } void get_wm_protocols(struct ws_win *win) { int i; xcb_icccm_get_wm_protocols_reply_t wpr; if (xcb_icccm_get_wm_protocols_reply(conn, xcb_icccm_get_wm_protocols(conn, win->id, a_prot), &wpr, NULL)) { for (i = 0; i < (int)wpr.atoms_len; i++) { if (wpr.atoms[i] == a_takefocus) win->take_focus = true; if (wpr.atoms[i] == a_delete) win->can_delete = true; } xcb_icccm_get_wm_protocols_reply_wipe(&wpr); } } void setup_ewmh(void) { xcb_window_t root, win; int i, j, num_screens; for (i = 0; i < LENGTH(ewmh); i++) ewmh[i].atom = get_atom_from_string(ewmh[i].name); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { root = screens[i].root; /* Set up _NET_SUPPORTING_WM_CHECK. */ win = xcb_generate_id(conn); xcb_create_window(conn, XCB_COPY_FROM_PARENT, win, root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, NULL); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, a_net_wm_check, XCB_ATOM_WINDOW, 32, 1, &win); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, a_net_wm_check, XCB_ATOM_WINDOW, 32, 1, &win); /* * Impersonate LG3D non-reparenting WM, written by Sun, to * workaround a Java GUI rendering issue. */ if (java_workaround) xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, ewmh[_NET_WM_NAME].atom, a_utf8_string, 8, strlen("LG3D"), "LG3D"); else xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, ewmh[_NET_WM_NAME].atom, a_utf8_string, 8, strlen("spectrwm"), "spectrwm"); /* Report supported atoms */ xcb_delete_property(conn, root, a_net_supported); for (j = 0; j < LENGTH(ewmh); j++) xcb_change_property(conn, XCB_PROP_MODE_APPEND, root, a_net_supported, XCB_ATOM_ATOM, 32, 1, &ewmh[j].atom); } ewmh_update_desktops(); ewmh_get_desktop_names(); } void teardown_ewmh(void) { int i, num_screens; xcb_window_t id; xcb_get_property_cookie_t pc; xcb_get_property_reply_t *pr; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { /* Get the support check window and destroy it */ pc = xcb_get_property(conn, 0, screens[i].root, a_net_wm_check, XCB_ATOM_WINDOW, 0, 1); pr = xcb_get_property_reply(conn, pc, NULL); if (pr == NULL) continue; if (pr->format == a_net_wm_check) { id = *((xcb_window_t *)xcb_get_property_value(pr)); xcb_destroy_window(conn, id); xcb_delete_property(conn, screens[i].root, a_net_wm_check); xcb_delete_property(conn, screens[i].root, a_net_supported); } free(pr); } } void ewmh_autoquirk(struct ws_win *win) { xcb_get_property_reply_t *r; xcb_get_property_cookie_t c; xcb_atom_t *type; int i, n; c = xcb_get_property(conn, 0, win->id, ewmh[_NET_WM_WINDOW_TYPE].atom, XCB_ATOM_ATOM, 0, UINT32_MAX); r = xcb_get_property_reply(conn, c, NULL); if (r == NULL) return; type = xcb_get_property_value(r); n = xcb_get_property_value_length(r) / sizeof(xcb_atom_t); for (i = 0; i < n; i++) { if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_NORMAL].atom) break; if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_DOCK].atom || type[i] == ewmh[_NET_WM_WINDOW_TYPE_TOOLBAR].atom || type[i] == ewmh[_NET_WM_WINDOW_TYPE_UTILITY].atom) { win->quirks = SWM_Q_FLOAT | SWM_Q_ANYWHERE; break; } if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_SPLASH].atom || type[i] == ewmh[_NET_WM_WINDOW_TYPE_DIALOG].atom) { win->quirks = SWM_Q_FLOAT; break; } } free(r); } void ewmh_update_actions(struct ws_win *win) { xcb_atom_t actions[SWM_EWMH_ACTION_COUNT_MAX]; int n = 0; if (win == NULL) return; actions[n++] = ewmh[_NET_WM_ACTION_CLOSE].atom; if (ABOVE(win)) { actions[n++] = ewmh[_NET_WM_ACTION_MOVE].atom; actions[n++] = ewmh[_NET_WM_ACTION_RESIZE].atom; actions[n++] = ewmh[_NET_WM_ACTION_ABOVE].atom; } xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id, ewmh[_NET_WM_ALLOWED_ACTIONS].atom, XCB_ATOM_ATOM, 32, 1, actions); } #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ #define _NET_WM_STATE_ADD 1 /* add/set property */ #define _NET_WM_STATE_TOGGLE 2 /* toggle property */ void ewmh_change_wm_state(struct ws_win *win, xcb_atom_t state, long action) { uint32_t flag = 0; uint32_t new_flags; #ifdef SWM_DEBUG char *name; name = get_atom_name(state); DNPRINTF(SWM_D_PROP, "ewmh_change_wm_state: win %#x, state: %s, " "action: %ld\n", WINID(win), name, action); free(name); #endif if (win == NULL) goto out; if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom) flag = EWMH_F_FULLSCREEN; else if (state == ewmh[_NET_WM_STATE_ABOVE].atom) flag = EWMH_F_ABOVE; else if (state == ewmh[_NET_WM_STATE_HIDDEN].atom) flag = EWMH_F_HIDDEN; else if (state == ewmh[_NET_WM_STATE_MAXIMIZED_VERT].atom || state == ewmh[_NET_WM_STATE_MAXIMIZED_HORZ].atom) flag = EWMH_F_MAXIMIZED; else if (state == ewmh[_SWM_WM_STATE_MANUAL].atom) flag = SWM_F_MANUAL; else if (state == ewmh[_NET_WM_STATE_SKIP_PAGER].atom) flag = EWMH_F_SKIP_PAGER; else if (state == ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom) flag = EWMH_F_SKIP_TASKBAR; /* Disallow unfloating transients. */ if (TRANS(win) && flag == EWMH_F_ABOVE) goto out; new_flags = win->ewmh_flags; switch (action) { case _NET_WM_STATE_REMOVE: new_flags &= ~flag; break; case _NET_WM_STATE_ADD: new_flags |= flag; break; case _NET_WM_STATE_TOGGLE: new_flags ^= flag; break; } ewmh_apply_flags(win, new_flags); out: DNPRINTF(SWM_D_PROP, "ewmh_change_wm_state: done.\n"); } void ewmh_apply_flags(struct ws_win *win, uint32_t pending) { struct workspace *ws; uint32_t changed; changed = win->ewmh_flags ^ pending; if (changed == 0) return; DNPRINTF(SWM_D_PROP, "ewmh_apply_flags: pending: %d\n", pending); win->ewmh_flags = pending; ws = win->ws; if (changed & EWMH_F_HIDDEN) { if (ICONIC(win)) { if (focus_mode != SWM_FOCUS_FOLLOW) ws->focus_pending = get_focus_prev(win); unfocus_win(win); unmap_window(win); } else { /* Reload floating geometry in case region changed. */ if (FLOATING(win)) load_float_geom(win); /* The window is no longer iconic, prepare focus. */ if (focus_mode != SWM_FOCUS_FOLLOW) ws->focus_pending = get_focus_magic(win); raise_window(win); } } if (changed & EWMH_F_ABOVE) { if (ws->cur_layout != &layouts[SWM_MAX_STACK]) { if (ABOVE(win)) load_float_geom(win); else if (!MAXIMIZED(win)) store_float_geom(win); win->ewmh_flags &= ~EWMH_F_MAXIMIZED; changed &= ~EWMH_F_MAXIMIZED; raise_window(win); } else { /* Revert. */ win->ewmh_flags ^= EWMH_F_ABOVE & pending; } } if (changed & EWMH_F_MAXIMIZED) { /* VERT and/or HORZ changed. */ if (ABOVE(win)) { if (!MAXIMIZED(win)) load_float_geom(win); else store_float_geom(win); } if (MAXIMIZED(win)) { if (focus_mode != SWM_FOCUS_FOLLOW && ws->cur_layout != &layouts[SWM_MAX_STACK]) { if (WS_FOCUSED(ws)) focus_win(win); else ws->focus_pending = win; } } update_window_color(win); raise_window(win); } if (changed & EWMH_F_FULLSCREEN) { if (FULLSCREEN(win)) { if (focus_mode != SWM_FOCUS_FOLLOW) { if (WS_FOCUSED(ws)) focus_win(win); else ws->focus_pending = win; } } else { load_float_geom(win); } win->ewmh_flags &= ~EWMH_F_MAXIMIZED; raise_window(win); } DNPRINTF(SWM_D_PROP, "ewmh_apply_flags: done.\n"); } void ewmh_update_wm_state(struct ws_win *win) { xcb_atom_t vals[SWM_EWMH_ACTION_COUNT_MAX]; int n = 0; if (ICONIC(win)) vals[n++] = ewmh[_NET_WM_STATE_HIDDEN].atom; if (FULLSCREEN(win)) vals[n++] = ewmh[_NET_WM_STATE_FULLSCREEN].atom; if (MAXIMIZED_VERT(win)) vals[n++] = ewmh[_NET_WM_STATE_MAXIMIZED_VERT].atom; if (MAXIMIZED_HORZ(win)) vals[n++] = ewmh[_NET_WM_STATE_MAXIMIZED_HORZ].atom; if (win->ewmh_flags & EWMH_F_SKIP_PAGER) vals[n++] = ewmh[_NET_WM_STATE_SKIP_PAGER].atom; if (win->ewmh_flags & EWMH_F_SKIP_TASKBAR) vals[n++] = ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom; if (win->ewmh_flags & EWMH_F_ABOVE) vals[n++] = ewmh[_NET_WM_STATE_ABOVE].atom; if (win->ewmh_flags & SWM_F_MANUAL) vals[n++] = ewmh[_SWM_WM_STATE_MANUAL].atom; if (n > 0) xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id, ewmh[_NET_WM_STATE].atom, XCB_ATOM_ATOM, 32, n, vals); else xcb_delete_property(conn, win->id, ewmh[_NET_WM_STATE].atom); } void ewmh_get_wm_state(struct ws_win *win) { xcb_atom_t *states; xcb_get_property_cookie_t c; xcb_get_property_reply_t *r; int i, n; if (win == NULL) return; win->ewmh_flags = 0; c = xcb_get_property(conn, 0, win->id, ewmh[_NET_WM_STATE].atom, XCB_ATOM_ATOM, 0, UINT32_MAX); r = xcb_get_property_reply(conn, c, NULL); if (r == NULL) return; states = xcb_get_property_value(r); n = xcb_get_property_value_length(r) / sizeof(xcb_atom_t); for (i = 0; i < n; i++) ewmh_change_wm_state(win, states[i], _NET_WM_STATE_ADD); free(r); } /* events */ #ifdef SWM_DEBUG void dumpwins(struct swm_region *r, union arg *args) { struct ws_win *w; uint32_t state; xcb_get_window_attributes_cookie_t c; xcb_get_window_attributes_reply_t *wa; /* suppress unused warning since var is needed */ (void)args; if (r->ws == NULL) { DPRINTF("dumpwins: invalid workspace\n"); return; } DPRINTF("=== managed window list ws %02d ===\n", r->ws->idx); TAILQ_FOREACH(w, &r->ws->winlist, entry) { state = get_win_state(w->id); c = xcb_get_window_attributes(conn, w->id); wa = xcb_get_window_attributes_reply(conn, c, NULL); if (wa) { DPRINTF("win %#x, map_state: %d, state: %u, " "transient: %#x\n", w->id, wa->map_state, state, w->transient); free(wa); } else DPRINTF("win %#x, failed xcb_get_window_attributes\n", w->id); } DPRINTF("=== stacking order (top down) === \n"); TAILQ_FOREACH(w, &r->ws->stack, stack_entry) { DPRINTF("win %#x, fs: %s, maximized: %s, above: %s, " "iconic: %s\n", w->id, YESNO(FULLSCREEN(w)), YESNO(MAXIMIZED(w)), YESNO(ABOVE(w)), YESNO(ICONIC(w))); } DPRINTF("===== unmanaged window list =====\n"); TAILQ_FOREACH(w, &r->ws->unmanagedlist, entry) { state = get_win_state(w->id); c = xcb_get_window_attributes(conn, w->id); wa = xcb_get_window_attributes_reply(conn, c, NULL); if (wa) { DPRINTF("win %#x, map_state: %d, state: %u, " "transient: %#x\n", w->id, wa->map_state, state, w->transient); free(wa); } else DPRINTF("win %#x, failed xcb_get_window_attributes\n", w->id); } DPRINTF("=================================\n"); } #else void dumpwins(struct swm_region *r, union arg *s) { (void)r; (void)s; } #endif /* SWM_DEBUG */ void sighdlr(int sig) { int saved_errno, status; pid_t pid; saved_errno = errno; switch (sig) { case SIGCHLD: while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) { if (pid == -1) { if (errno == EINTR) continue; #ifdef SWM_DEBUG if (errno != ECHILD) warn("sighdlr: waitpid"); #endif /* SWM_DEBUG */ break; } if (pid == searchpid) search_resp = 1; #ifdef SWM_DEBUG if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) warnx("sighdlr: child exit status: %d", WEXITSTATUS(status)); } else warnx("sighdlr: child is terminated " "abnormally"); #endif /* SWM_DEBUG */ } break; case SIGHUP: restart_wm = 1; break; case SIGINT: case SIGTERM: case SIGQUIT: running = 0; break; } errno = saved_errno; } struct pid_e * find_pid(pid_t pid) { struct pid_e *p = NULL; DNPRINTF(SWM_D_MISC, "find_pid: %d\n", pid); if (pid == 0) return (NULL); TAILQ_FOREACH(p, &pidlist, entry) { if (p->pid == pid) return (p); } return (NULL); } uint32_t name_to_pixel(int sidx, const char *colorname) { uint32_t result = 0; char cname[32] = "#"; xcb_screen_t *screen; xcb_colormap_t cmap; xcb_alloc_color_reply_t *cr; xcb_alloc_named_color_reply_t *nr; uint16_t rr, gg, bb; screen = get_screen(sidx); cmap = screen->default_colormap; /* color is in format rgb://rr/gg/bb */ if (strncmp(colorname, "rgb:", 4) == 0) { if (parse_rgb(colorname, &rr, &gg, &bb) == -1) warnx("could not parse rgb %s", colorname); else { cr = xcb_alloc_color_reply(conn, xcb_alloc_color(conn, cmap, rr, gg, bb), NULL); if (cr) { result = cr->pixel; free(cr); } else warnx("color '%s' not found", colorname); } } else { nr = xcb_alloc_named_color_reply(conn, xcb_alloc_named_color(conn, cmap, strlen(colorname), colorname), NULL); if (nr == NULL) { strlcat(cname, colorname + 2, sizeof cname - 1); nr = xcb_alloc_named_color_reply(conn, xcb_alloc_named_color(conn, cmap, strlen(cname), cname), NULL); } if (nr) { result = nr->pixel; free(nr); } else warnx("color '%s' not found", colorname); } return (result); } void setscreencolor(const char *val, int i, int c) { if (i < 0 || i >= get_screen_count()) return; screens[i].c[c].pixel = name_to_pixel(i, val); free(screens[i].c[c].name); if ((screens[i].c[c].name = strdup(val)) == NULL) err(1, "strdup"); } void fancy_stacker(struct workspace *ws) { strlcpy(ws->stacker, "[ ]", sizeof ws->stacker); if (ws->cur_layout->l_stack == vertical_stack) snprintf(ws->stacker, sizeof ws->stacker, ws->l_state.vertical_flip ? "[%d>%d]" : "[%d|%d]", ws->l_state.vertical_mwin, ws->l_state.vertical_stacks); else if (ws->cur_layout->l_stack == horizontal_stack) snprintf(ws->stacker, sizeof ws->stacker, ws->l_state.horizontal_flip ? "[%dv%d]" : "[%d-%d]", ws->l_state.horizontal_mwin, ws->l_state.horizontal_stacks); } void plain_stacker(struct workspace *ws) { strlcpy(ws->stacker, "[ ]", sizeof ws->stacker); if (ws->cur_layout->l_stack == vertical_stack) strlcpy(ws->stacker, ws->l_state.vertical_flip ? "[>]" : "[|]", sizeof ws->stacker); else if (ws->cur_layout->l_stack == horizontal_stack) strlcpy(ws->stacker, ws->l_state.horizontal_flip ? "[v]" : "[-]", sizeof ws->stacker); } void custom_region(const char *val) { unsigned int x, y, w, h; int sidx, num_screens; xcb_screen_t *screen; num_screens = get_screen_count(); if (sscanf(val, "screen[%d]:%ux%u+%u+%u", &sidx, &w, &h, &x, &y) != 5) errx(1, "invalid custom region, " "should be 'screen[]:x++"); if (sidx < 1 || sidx > num_screens) errx(1, "invalid screen index: %d out of bounds (maximum %d)", sidx, num_screens); sidx--; if ((screen = get_screen(sidx)) == NULL) errx(1, "ERROR: can't get screen %d.", sidx); if (w < 1 || h < 1) errx(1, "region %ux%u+%u+%u too small", w, h, x, y); if (x > screen->width_in_pixels || y > screen->height_in_pixels || w + x > screen->width_in_pixels || h + y > screen->height_in_pixels) { warnx("ignoring region %ux%u+%u+%u - not within screen " "boundaries (%ux%u)", w, h, x, y, screen->width_in_pixels, screen->height_in_pixels); return; } new_region(&screens[sidx], x, y, w, h); } void socket_setnonblock(int fd) { int flags; if ((flags = fcntl(fd, F_GETFL, 0)) == -1) err(1, "fcntl F_GETFL"); flags |= O_NONBLOCK; if ((flags = fcntl(fd, F_SETFL, flags)) == -1) err(1, "fcntl F_SETFL"); } void bar_print_legacy(struct swm_region *r, const char *s) { xcb_rectangle_t rect; uint32_t gcv[1]; XGCValues gcvd; int x = 0; size_t len; XRectangle ibox, lbox; GC draw; len = strlen(s); XmbTextExtents(bar_fs, s, len, &ibox, &lbox); switch (bar_justify) { case SWM_BAR_JUSTIFY_LEFT: x = SWM_BAR_OFFSET; break; case SWM_BAR_JUSTIFY_CENTER: x = (WIDTH(r) - lbox.width) / 2; break; case SWM_BAR_JUSTIFY_RIGHT: x = WIDTH(r) - lbox.width - SWM_BAR_OFFSET; break; } if (x < SWM_BAR_OFFSET) x = SWM_BAR_OFFSET; rect.x = 0; rect.y = 0; rect.width = WIDTH(r->bar); rect.height = HEIGHT(r->bar); /* clear back buffer */ gcv[0] = r->s->c[SWM_S_COLOR_BAR].pixel; xcb_change_gc(conn, r->s->bar_gc, XCB_GC_FOREGROUND, gcv); xcb_poly_fill_rectangle(conn, r->bar->buffer, r->s->bar_gc, 1, &rect); /* draw back buffer */ gcvd.graphics_exposures = 0; draw = XCreateGC(display, r->bar->buffer, GCGraphicsExposures, &gcvd); XSetForeground(display, draw, r->s->c[SWM_S_COLOR_BAR_FONT].pixel); DRAWSTRING(display, r->bar->buffer, bar_fs, draw, x, (bar_fs_extents->max_logical_extent.height - lbox.height) / 2 - lbox.y, s, len); XFreeGC(display, draw); /* blt */ xcb_copy_area(conn, r->bar->buffer, r->bar->id, r->s->bar_gc, 0, 0, 0, 0, WIDTH(r->bar), HEIGHT(r->bar)); } void bar_print(struct swm_region *r, const char *s) { size_t len; xcb_rectangle_t rect; uint32_t gcv[1]; int32_t x = 0; XGlyphInfo info; XftDraw *draw; len = strlen(s); XftTextExtentsUtf8(display, bar_font, (FcChar8 *)s, len, &info); switch (bar_justify) { case SWM_BAR_JUSTIFY_LEFT: x = SWM_BAR_OFFSET; break; case SWM_BAR_JUSTIFY_CENTER: x = (WIDTH(r) - info.width) / 2; break; case SWM_BAR_JUSTIFY_RIGHT: x = WIDTH(r) - info.width - SWM_BAR_OFFSET; break; } if (x < SWM_BAR_OFFSET) x = SWM_BAR_OFFSET; rect.x = 0; rect.y = 0; rect.width = WIDTH(r->bar); rect.height = HEIGHT(r->bar); /* clear back buffer */ gcv[0] = r->s->c[SWM_S_COLOR_BAR].pixel; xcb_change_gc(conn, r->s->bar_gc, XCB_GC_FOREGROUND, gcv); xcb_poly_fill_rectangle(conn, r->bar->buffer, r->s->bar_gc, 1, &rect); /* draw back buffer */ draw = XftDrawCreate(display, r->bar->buffer, DefaultVisual(display, r->s->idx), DefaultColormap(display, r->s->idx)); XftDrawStringUtf8(draw, &bar_font_color, bar_font, x, (HEIGHT(r->bar) + bar_font->height) / 2 - bar_font->descent, (FcChar8 *)s, len); XftDrawDestroy(draw); /* blt */ xcb_copy_area(conn, r->bar->buffer, r->bar->id, r->s->bar_gc, 0, 0, 0, 0, WIDTH(r->bar), HEIGHT(r->bar)); } void bar_extra_stop(void) { if (bar_pipe[0]) { close(bar_pipe[0]); bzero(bar_pipe, sizeof bar_pipe); } if (bar_pid) { kill(bar_pid, SIGTERM); bar_pid = 0; } strlcpy(bar_ext, "", sizeof bar_ext); bar_extra = false; } void bar_window_class(char *s, size_t sz, struct swm_region *r) { if (r == NULL || r->ws == NULL || r->ws->focus == NULL) return; if (r->ws->focus->ch.class_name != NULL) strlcat(s, r->ws->focus->ch.class_name, sz); } void bar_window_instance(char *s, size_t sz, struct swm_region *r) { if (r == NULL || r->ws == NULL || r->ws->focus == NULL) return; if (r->ws->focus->ch.instance_name != NULL) strlcat(s, r->ws->focus->ch.instance_name, sz); } void bar_window_class_instance(char *s, size_t sz, struct swm_region *r) { if (r == NULL || r->ws == NULL || r->ws->focus == NULL) return; bar_window_class(s, sz, r); strlcat(s, ":", sz); bar_window_instance(s, sz, r); } void bar_window_state(char *s, size_t sz, struct swm_region *r) { if (r == NULL || r ->ws == NULL || r->ws->focus == NULL) return; if (MAXIMIZED(r->ws->focus)) strlcat(s, "(m)", sz); else if (ABOVE(r->ws->focus)) strlcat(s, "(f)", sz); } void bar_window_name(char *s, size_t sz, struct swm_region *r) { char *title; if (r == NULL || r->ws == NULL || r->ws->focus == NULL) return; title = get_win_name(r->ws->focus->id); strlcat(s, title, sz); free(title); } void bar_urgent(char *s, size_t sz) { struct ws_win *win; int i, j, num_screens; bool urgent[SWM_WS_MAX]; char b[8]; xcb_get_property_cookie_t c; xcb_icccm_wm_hints_t hints; for (i = 0; i < workspace_limit; i++) urgent[i] = false; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) { c = xcb_icccm_get_wm_hints(conn, win->id); if (xcb_icccm_get_wm_hints_reply(conn, c, &hints, NULL) == 0) continue; if (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY) urgent[j] = true; } for (i = 0; i < workspace_limit; i++) { if (urgent[i]) { snprintf(b, sizeof b, "%d ", i + 1); strlcat(s, b, sz); } else if (!urgent_collapse) { strlcat(s, "- ", sz); } } } void bar_workspace_name(char *s, size_t sz, struct swm_region *r) { if (r == NULL || r->ws == NULL) return; if (r->ws->name != NULL) strlcat(s, r->ws->name, sz); } /* build the default bar format according to the defined enabled options */ void bar_fmt(const char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz) { struct ws_win *w; /* if format provided, just copy the buffers */ if (bar_format != NULL) { strlcpy(fmtnew, fmtexp, sz); return; } /* reset the output buffer */ *fmtnew = '\0'; strlcat(fmtnew, "+N:+I ", sz); if (stack_enabled) strlcat(fmtnew, "+S", sz); strlcat(fmtnew, " ", sz); /* only show the workspace name if there's actually one */ if (r != NULL && r->ws != NULL && r->ws->name != NULL) strlcat(fmtnew, "<+D>", sz); /* If enabled, only show the iconic count if there are iconic wins. */ if (iconic_enabled && r != NULL && r->ws != NULL) TAILQ_FOREACH(w, &r->ws->winlist, entry) if (ICONIC(w)) { strlcat(fmtnew, "{+M}", sz); break; } strlcat(fmtnew, "+3<", sz); if (clock_enabled) { strlcat(fmtnew, fmtexp, sz); strlcat(fmtnew, "+4<", sz); } /* bar_urgent already adds the space before the last asterisk */ if (urgent_enabled) strlcat(fmtnew, "* +U*+4<", sz); if (window_class_enabled) { strlcat(fmtnew, "+C", sz); if (!window_instance_enabled) strlcat(fmtnew, "+4<", sz); } /* checks needed by the colon and floating strlcat(3) calls below */ if (r != NULL && r->ws != NULL && r->ws->focus != NULL) { if (window_instance_enabled) { if (window_class_enabled) strlcat(fmtnew, ":", sz); strlcat(fmtnew, "+T+4<", sz); } if (window_name_enabled) { if (ABOVE(r->ws->focus) || MAXIMIZED(r->ws->focus)) strlcat(fmtnew, "+F ", sz); strlcat(fmtnew, "+64W ", sz); } } /* finally add the action script output and the version */ strlcat(fmtnew, "+4<+A+4<+V", sz); } void bar_replace_pad(char *tmp, int *limit, size_t sz) { /* special case; no limit given, pad one space, instead */ if (*limit == (int)sz - 1) *limit = 1; snprintf(tmp, sz, "%*s", *limit, " "); } /* replaces the bar format character sequences (like in tmux(1)) */ char * bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep, size_t sz) { struct ws_win *w; char *ptr; char tmp[SWM_BAR_MAX]; int limit, size, count; size_t len; /* reset strlcat(3) buffer */ *tmp = '\0'; /* get number, if any */ fmt++; size = 0; if (sscanf(fmt, "%d%n", &limit, &size) != 1) limit = sizeof tmp - 1; if (limit <= 0 || limit >= (int)sizeof tmp) limit = sizeof tmp - 1; /* there is nothing to replace (ie EOL) */ fmt += size; if (*fmt == '\0') return (fmt); switch (*fmt) { case '<': bar_replace_pad(tmp, &limit, sizeof tmp); break; case 'A': snprintf(tmp, sizeof tmp, "%s", bar_ext); break; case 'C': bar_window_class(tmp, sizeof tmp, r); break; case 'D': bar_workspace_name(tmp, sizeof tmp, r); break; case 'F': bar_window_state(tmp, sizeof tmp, r); break; case 'I': snprintf(tmp, sizeof tmp, "%d", r->ws->idx + 1); break; case 'M': count = 0; TAILQ_FOREACH(w, &r->ws->winlist, entry) if (ICONIC(w)) ++count; snprintf(tmp, sizeof tmp, "%d", count); break; case 'N': snprintf(tmp, sizeof tmp, "%d", r->s->idx + 1); break; case 'P': bar_window_class_instance(tmp, sizeof tmp, r); break; case 'S': snprintf(tmp, sizeof tmp, "%s", r->ws->stacker); break; case 'T': bar_window_instance(tmp, sizeof tmp, r); break; case 'U': bar_urgent(tmp, sizeof tmp); break; case 'V': snprintf(tmp, sizeof tmp, "%s", bar_vertext); break; case 'W': bar_window_name(tmp, sizeof tmp, r); break; default: /* unknown character sequence; copy as-is */ snprintf(tmp, sizeof tmp, "+%c", *fmt); break; } len = strlen(tmp); ptr = tmp; if ((int)len < limit) limit = len; while (limit-- > 0) { if (*offrep >= sz - 1) break; fmtrep[(*offrep)++] = *ptr++; } fmt++; return (fmt); } void bar_replace(char *fmt, char *fmtrep, struct swm_region *r, size_t sz) { size_t off; off = 0; while (*fmt != '\0') { if (*fmt != '+') { /* skip ordinary characters */ if (off >= sz - 1) break; fmtrep[off++] = *fmt++; continue; } /* character sequence found; replace it */ fmt = bar_replace_seq(fmt, fmtrep, r, &off, sz); if (off >= sz - 1) break; } fmtrep[off] = '\0'; } void bar_fmt_expand(char *fmtexp, size_t sz) { char *fmt = NULL; size_t len; struct tm tm; time_t tmt; /* start by grabbing the current time and date */ time(&tmt); localtime_r(&tmt, &tm); /* figure out what to expand */ if (bar_format != NULL) fmt = bar_format; else if (bar_format == NULL && clock_enabled) fmt = clock_format; /* if nothing to expand bail out */ if (fmt == NULL) { *fmtexp = '\0'; return; } /* copy as-is, just in case the format shouldn't be expanded below */ strlcpy(fmtexp, fmt, sz); /* finally pass the string through strftime(3) */ #ifndef SWM_DENY_CLOCK_FORMAT if ((len = strftime(fmtexp, sz, fmt, &tm)) == 0) warnx("format too long"); fmtexp[len] = '\0'; #endif } /* Redraws the bar; need to follow with xcb_flush() or focus_flush(). */ void bar_draw(void) { char fmtexp[SWM_BAR_MAX], fmtnew[SWM_BAR_MAX]; char fmtrep[SWM_BAR_MAX]; int i, num_screens; struct swm_region *r; /* expand the format by first passing it through strftime(3) */ bar_fmt_expand(fmtexp, sizeof fmtexp); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { TAILQ_FOREACH(r, &screens[i].rl, entry) { if (r->bar == NULL) continue; if (bar_enabled && r->ws->bar_enabled) xcb_map_window(conn, r->bar->id); else { xcb_unmap_window(conn, r->bar->id); continue; } if (startup_exception) snprintf(fmtrep, sizeof fmtrep, "total " "exceptions: %d, first exception: %s", nr_exceptions, startup_exception); else { bar_fmt(fmtexp, fmtnew, r, sizeof fmtnew); bar_replace(fmtnew, fmtrep, r, sizeof fmtrep); } if (bar_font_legacy) bar_print_legacy(r, fmtrep); else bar_print(r, fmtrep); } } } /* * Reads external script output; call when stdin is readable. * Returns 1 if bar_ext was updated; otherwise 0. */ int bar_extra_update(void) { size_t len; char b[SWM_BAR_MAX]; bool changed = false; if (!bar_extra) return changed; while (fgets(b, sizeof(b), stdin) != NULL) { if (bar_enabled) { len = strlen(b); if (b[len - 1] == '\n') { /* Remove newline. */ b[--len] = '\0'; /* "Clear" bar_ext. */ bar_ext[0] = '\0'; /* Flush buffered output. */ strlcpy(bar_ext, bar_ext_buf, sizeof(bar_ext)); bar_ext_buf[0] = '\0'; /* Append new output to bar. */ strlcat(bar_ext, b, sizeof(bar_ext)); changed = true; } else { /* Buffer output. */ strlcat(bar_ext_buf, b, sizeof(bar_ext_buf)); } } } if (errno != EAGAIN) { warn("bar_action failed"); bar_extra_stop(); changed = true; } return changed; } void bar_toggle(struct swm_region *r, union arg *args) { struct swm_region *tmpr; int i, num_screens; /* suppress unused warnings since vars are needed */ (void)r; (void)args; DNPRINTF(SWM_D_BAR, "bar_toggle\n"); switch (args->id) { case SWM_ARG_ID_BAR_TOGGLE_WS: /* Only change if master switch is enabled. */ if (bar_enabled) r->ws->bar_enabled = !r->ws->bar_enabled; else bar_enabled = r->ws->bar_enabled = true; break; case SWM_ARG_ID_BAR_TOGGLE: bar_enabled = !bar_enabled; break; } /* update bars as necessary */ num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(tmpr, &screens[i].rl, entry) if (tmpr->bar) { if (bar_enabled && tmpr->ws->bar_enabled) xcb_map_window(conn, tmpr->bar->id); else xcb_unmap_window(conn, tmpr->bar->id); } stack(); /* must be after stack */ bar_draw(); focus_flush(); } void bar_extra_setup(void) { /* do this here because the conf file is in memory */ if (!bar_extra && bar_argv[0]) { /* launch external status app */ bar_extra = true; if (pipe(bar_pipe) == -1) err(1, "pipe error"); socket_setnonblock(bar_pipe[0]); socket_setnonblock(bar_pipe[1]); /* XXX hmmm, really? */ /* Set stdin to read from the pipe. */ if (dup2(bar_pipe[0], STDIN_FILENO) == -1) err(1, "dup2"); /* Set stdout to write to the pipe. */ if (dup2(bar_pipe[1], STDOUT_FILENO) == -1) err(1, "dup2"); if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) err(1, "could not disable SIGPIPE"); switch (bar_pid = fork()) { case -1: err(1, "cannot fork"); break; case 0: /* child */ close(bar_pipe[0]); execvp(bar_argv[0], bar_argv); err(1, "%s external app failed", bar_argv[0]); break; default: /* parent */ close(bar_pipe[1]); break; } atexit(kill_bar_extra_atexit); } } void kill_bar_extra_atexit(void) { if (bar_pid) kill(bar_pid, SIGTERM); } bool isxlfd(char *s) { int count = 0; while ((s = index(s, '-'))) { ++count; ++s; } return (count == 14); } void fontset_init(void) { char *default_string; char **missing_charsets; int num_missing_charsets = 0; int i; if (bar_fs) { XFreeFontSet(display, bar_fs); bar_fs = NULL; } DNPRINTF(SWM_D_INIT, "fontset_init: loading bar_fonts: %s\n", bar_fonts); bar_fs = XCreateFontSet(display, bar_fonts, &missing_charsets, &num_missing_charsets, &default_string); if (num_missing_charsets > 0) { warnx("Unable to load charset(s):"); for (i = 0; i < num_missing_charsets; ++i) warnx("%s", missing_charsets[i]); XFreeStringList(missing_charsets); if (strcmp(default_string, "")) warnx("Glyphs from those sets will be replaced " "by '%s'.", default_string); else warnx("Glyphs from those sets won't be drawn."); } if (bar_fs == NULL) errx(1, "Error creating font set structure."); bar_fs_extents = XExtentsOfFontSet(bar_fs); bar_height = bar_fs_extents->max_logical_extent.height + 2 * bar_border_width; if (bar_height < 1) bar_height = 1; } void xft_init(struct swm_region *r) { char *font, *d, *search; XRenderColor color; if (bar_font == NULL) { if ((d = strdup(bar_fonts)) == NULL) errx(1, "insufficient memory."); search = d; while ((font = strsep(&search, ",")) != NULL) { if (*font == '\0') continue; DNPRINTF(SWM_D_INIT, "xft_init: try font %s\n", font); if (isxlfd(font)) { bar_font = XftFontOpenXlfd(display, r->s->idx, font); } else { bar_font = XftFontOpenName(display, r->s->idx, font); } if (bar_font == NULL) { warnx("unable to load font %s", font); continue; } else { DNPRINTF(SWM_D_INIT, "successfully opened " "font %s\n", font); break; } } free(d); } if (bar_font == NULL) errx(1, "unable to open a font"); PIXEL_TO_XRENDERCOLOR(r->s->c[SWM_S_COLOR_BAR_FONT].pixel, color); if (!XftColorAllocValue(display, DefaultVisual(display, r->s->idx), DefaultColormap(display, r->s->idx), &color, &bar_font_color)) warn("Xft error: unable to allocate color."); PIXEL_TO_XRENDERCOLOR(r->s->c[SWM_S_COLOR_BAR].pixel, color); if (!XftColorAllocValue(display, DefaultVisual(display, r->s->idx), DefaultColormap(display, r->s->idx), &color, &search_font_color)) warn("Xft error: unable to allocate color."); bar_height = bar_font->height + 2 * bar_border_width; if (bar_height < 1) bar_height = 1; } void bar_setup(struct swm_region *r) { xcb_screen_t *screen; uint32_t wa[3]; DNPRINTF(SWM_D_BAR, "bar_setup: screen %d.\n", r->s->idx); if ((screen = get_screen(r->s->idx)) == NULL) errx(1, "ERROR: can't get screen %d.", r->s->idx); if (r->bar != NULL) return; if ((r->bar = calloc(1, sizeof(struct swm_bar))) == NULL) err(1, "bar_setup: calloc: failed to allocate memory."); if (bar_font_legacy) fontset_init(); else xft_init(r); X(r->bar) = X(r); Y(r->bar) = bar_at_bottom ? (Y(r) + HEIGHT(r) - bar_height) : Y(r); WIDTH(r->bar) = WIDTH(r) - 2 * bar_border_width; HEIGHT(r->bar) = bar_height - 2 * bar_border_width; /* Assume region is unfocused when we create the bar. */ r->bar->id = xcb_generate_id(conn); wa[0] = r->s->c[SWM_S_COLOR_BAR].pixel; wa[1] = r->s->c[SWM_S_COLOR_BAR_BORDER_UNFOCUS].pixel; wa[2] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_POINTER_MOTION_HINT; xcb_create_window(conn, XCB_COPY_FROM_PARENT, r->bar->id, r->s->root, X(r->bar), Y(r->bar), WIDTH(r->bar), HEIGHT(r->bar), bar_border_width, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK, wa); /* Stack bar window above region window to start. */ wa[0] = r->id; wa[1] = XCB_STACK_MODE_ABOVE; xcb_configure_window(conn, r->bar->id, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, wa); r->bar->buffer = xcb_generate_id(conn); xcb_create_pixmap(conn, screen->root_depth, r->bar->buffer, r->bar->id, WIDTH(r->bar), HEIGHT(r->bar)); if (randr_support) xcb_randr_select_input(conn, r->bar->id, XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE); if (bar_enabled) xcb_map_window(conn, r->bar->id); DNPRINTF(SWM_D_BAR, "bar_setup: win %#x, (x,y) w x h: (%d,%d) " "%d x %d\n", WINID(r->bar), X(r->bar), Y(r->bar), WIDTH(r->bar), HEIGHT(r->bar)); bar_extra_setup(); } void bar_cleanup(struct swm_region *r) { if (r->bar == NULL) return; xcb_destroy_window(conn, r->bar->id); xcb_free_pixmap(conn, r->bar->buffer); free(r->bar); r->bar = NULL; } void set_win_state(struct ws_win *win, uint8_t state) { uint16_t data[2] = { state, XCB_ATOM_NONE }; DNPRINTF(SWM_D_EVENT, "set_win_state: win %#x, state: %u\n", win->id, state); if (win == NULL) return; xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id, a_state, a_state, 32, 2, data); } uint8_t get_win_state(xcb_window_t w) { xcb_get_property_reply_t *r; xcb_get_property_cookie_t c; uint32_t result = 0; c = xcb_get_property(conn, 0, w, a_state, a_state, 0L, 2L); r = xcb_get_property_reply(conn, c, NULL); if (r) { if (r->type == a_state && r->format == 32 && r->length == 2) result = *((uint32_t *)xcb_get_property_value(r)); free(r); } DNPRINTF(SWM_D_MISC, "get_win_state property: win %#x state %u\n", w, result); return (result); } void version(struct swm_region *r, union arg *args) { /* suppress unused warnings since vars are needed */ (void)r; (void)args; bar_version = !bar_version; if (bar_version) snprintf(bar_vertext, sizeof bar_vertext, "Version: %s Build: %s", SPECTRWM_VERSION, buildstr); else strlcpy(bar_vertext, "", sizeof bar_vertext); bar_draw(); xcb_flush(conn); } void client_msg(struct ws_win *win, xcb_atom_t a, xcb_timestamp_t t) { xcb_client_message_event_t ev; #ifdef SWM_DEBUG char *name; #endif if (win == NULL) return; #ifdef SWM_DEBUG name = get_atom_name(a); DNPRINTF(SWM_D_EVENT, "client_msg: win %#x, atom: %s(%u), " "time: %#x\n", win->id, name, a, t); free(name); #endif bzero(&ev, sizeof ev); ev.response_type = XCB_CLIENT_MESSAGE; ev.window = win->id; ev.type = a_prot; ev.format = 32; ev.data.data32[0] = a; ev.data.data32[1] = t; xcb_send_event(conn, 0, win->id, XCB_EVENT_MASK_NO_EVENT, (const char *)&ev); } /* synthetic response to a ConfigureRequest when not making a change */ void config_win(struct ws_win *win, xcb_configure_request_event_t *ev) { xcb_configure_notify_event_t ce; if (win == NULL) return; /* send notification of unchanged state. */ bzero(&ce, sizeof(ce)); ce.response_type = XCB_CONFIGURE_NOTIFY; ce.x = X(win); ce.y = Y(win); ce.width = WIDTH(win); ce.height = HEIGHT(win); ce.override_redirect = 0; if (ev == NULL) { /* EWMH */ ce.event = win->id; ce.window = win->id; ce.border_width = BORDER(win); ce.above_sibling = XCB_WINDOW_NONE; } else { /* normal */ ce.event = ev->window; ce.window = ev->window; /* make response appear more WM_SIZE_HINTS-compliant */ if (win->sh.flags) { DNPRINTF(SWM_D_MISC, "config_win: hints: win %#x," " sh.flags: %u, min: %d x %d, max: %d x %d, inc: " "%d x %d\n", win->id, win->sh.flags, SH_MIN_W(win), SH_MIN_H(win), SH_MAX_W(win), SH_MAX_H(win), SH_INC_W(win), SH_INC_H(win)); } /* min size */ if (SH_MIN(win)) { /* the hint may be set... to 0! */ if (SH_MIN_W(win) > 0 && ce.width < SH_MIN_W(win)) ce.width = SH_MIN_W(win); if (SH_MIN_H(win) > 0 && ce.height < SH_MIN_H(win)) ce.height = SH_MIN_H(win); } /* max size */ if (SH_MAX(win)) { /* may also be advertized as 0 */ if (SH_MAX_W(win) > 0 && ce.width > SH_MAX_W(win)) ce.width = SH_MAX_W(win); if (SH_MAX_H(win) > 0 && ce.height > SH_MAX_H(win)) ce.height = SH_MAX_H(win); } /* resize increment. */ if (SH_INC(win)) { if (SH_INC_W(win) > 1 && ce.width > SH_INC_W(win)) ce.width -= (ce.width - SH_MIN_W(win)) % SH_INC_W(win); if (SH_INC_H(win) > 1 && ce.height > SH_INC_H(win)) ce.height -= (ce.height - SH_MIN_H(win)) % SH_INC_H(win); } /* adjust x and y for requested border_width. */ ce.x += BORDER(win) - ev->border_width; ce.y += BORDER(win) - ev->border_width; ce.border_width = ev->border_width; ce.above_sibling = ev->sibling; } DNPRINTF(SWM_D_MISC, "config_win: ewmh: %s, win %#x, (x,y) w x h: " "(%d,%d) %d x %d, border: %d\n", YESNO(ev == NULL), win->id, ce.x, ce.y, ce.width, ce.height, ce.border_width); xcb_send_event(conn, 0, win->id, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char *)&ce); } int count_win(struct workspace *ws, bool count_transient) { struct ws_win *win; int count = 0; TAILQ_FOREACH(win, &ws->winlist, entry) { if (!count_transient && FLOATING(win)) continue; if (ICONIC(win)) continue; count++; } DNPRINTF(SWM_D_MISC, "count_win: %d\n", count); return (count); } void quit(struct swm_region *r, union arg *args) { /* suppress unused warnings since vars are needed */ (void)r; (void)args; DNPRINTF(SWM_D_MISC, "quit\n"); running = 0; } void raise_window(struct ws_win *win) { struct ws_win *target = NULL; struct swm_region *r; struct workspace *ws; if (win == NULL || (r = win->ws->r) == NULL) return; ws = win->ws; DNPRINTF(SWM_D_EVENT, "raise_window: win %#x\n", win->id); TAILQ_FOREACH(target, &ws->stack, stack_entry) { if (target == win || ICONIC(target)) continue; if (ws->cur_layout == &layouts[SWM_MAX_STACK]) break; if (TRANS(win) && (win->transient == target->transient || win->transient == target->id)) break; if (FULLSCREEN(win)) break; if (FULLSCREEN(target)) continue; if (MAXIMIZED(win)) break; if (MAXIMIZED(target)) continue; if (ABOVE(win) || TRANS(win)) break; if (!ABOVE(target) && !TRANS(target)) break; } if (target != NULL) { /* Change stack position. */ TAILQ_REMOVE(&ws->stack, win, stack_entry); TAILQ_INSERT_BEFORE(target, win, stack_entry); update_win_stacking(win); } #ifdef SWM_DEBUG if (swm_debug & SWM_D_STACK) { DPRINTF("=== stacking order (top down) === \n"); TAILQ_FOREACH(target, &r->ws->stack, stack_entry) { DPRINTF("win %#x, fs: %s, maximized: %s, above: %s, " "iconic: %s\n", target->id, YESNO(FULLSCREEN(target)), YESNO(MAXIMIZED(target)), YESNO(ABOVE(target)), YESNO(ICONIC(target))); } } #endif DNPRINTF(SWM_D_EVENT, "raise_window: done\n"); } void update_win_stacking(struct ws_win *win) { struct ws_win *sibling; struct swm_region *r; uint32_t val[2]; if (win == NULL || (r = win->ws->r) == NULL) return; sibling = TAILQ_NEXT(win, stack_entry); if (sibling != NULL && FLOATING(win) == FLOATING(sibling)) val[0] = sibling->id; else val[0] = FLOATING(win) ? r->bar->id : r->id; DNPRINTF(SWM_D_EVENT, "update_win_stacking: %#x, sibling %#x\n", win->id, val[0]); val[1] = XCB_STACK_MODE_ABOVE; xcb_configure_window(conn, win->id, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, val); } void map_window(struct ws_win *win) { if (win == NULL) return; DNPRINTF(SWM_D_EVENT, "map_window: win %#x, mapped: %s\n", win->id, YESNO(win->mapped)); if (win->mapped) return; xcb_map_window(conn, win->id); set_win_state(win, XCB_ICCCM_WM_STATE_NORMAL); win->mapped = true; } void unmap_window(struct ws_win *win) { if (win == NULL) return; DNPRINTF(SWM_D_EVENT, "unmap_window: win %#x, mapped: %s\n", win->id, YESNO(win->mapped)); if (!win->mapped) return; xcb_unmap_window(conn, win->id); set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC); win->mapped = false; } void unmap_all(void) { struct ws_win *win; int i, j, num_screens; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) unmap_window(win); } void fake_keypress(struct ws_win *win, xcb_keysym_t keysym, uint16_t modifiers) { xcb_key_press_event_t event; xcb_keycode_t *keycode; if (win == NULL) return; keycode = xcb_key_symbols_get_keycode(syms, keysym); DNPRINTF(SWM_D_MISC, "fake_keypress: win %#x, keycode %u\n", win->id, *keycode); bzero(&event, sizeof(event)); event.event = win->id; event.root = win->s->root; event.child = XCB_WINDOW_NONE; event.time = XCB_CURRENT_TIME; event.event_x = X(win); event.event_y = Y(win); event.root_x = 1; event.root_y = 1; event.same_screen = 1; event.detail = *keycode; event.state = modifiers; event.response_type = XCB_KEY_PRESS; xcb_send_event(conn, 1, win->id, XCB_EVENT_MASK_KEY_PRESS, (const char *)&event); event.response_type = XCB_KEY_RELEASE; xcb_send_event(conn, 1, win->id, XCB_EVENT_MASK_KEY_RELEASE, (const char *)&event); free(keycode); } void restart(struct swm_region *r, union arg *args) { /* suppress unused warning since var is needed */ (void)r; (void)args; DNPRINTF(SWM_D_MISC, "restart: %s\n", start_argv[0]); shutdown_cleanup(); execvp(start_argv[0], start_argv); warn("execvp failed"); quit(NULL, NULL); } struct ws_win * get_pointer_win(xcb_window_t root) { struct ws_win *win = NULL; xcb_query_pointer_reply_t *r; DNPRINTF(SWM_D_EVENT, "get_pointer_win: root: %#x.\n", root); r = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, root), NULL); if (r) { win = find_window(r->child); if (win) { DNPRINTF(SWM_D_EVENT, "get_pointer_win: %#x.\n", win->id); } else { DNPRINTF(SWM_D_EVENT, "get_pointer_win: none.\n"); } free(r); } return win; } void center_pointer(struct swm_region *r) { struct ws_win *win; if (!warp_pointer || r == NULL) return; win = r->ws->focus; if (win && win->mapped) xcb_warp_pointer(conn, XCB_NONE, win->id, 0, 0, 0, 0, WIDTH(win) / 2, HEIGHT(win) / 2); else xcb_warp_pointer(conn, XCB_NONE, r->id, 0, 0, 0, 0, WIDTH(r) / 2, HEIGHT(r) / 2); } struct swm_region * root_to_region(xcb_window_t root, int check) { struct ws_win *cfw; struct swm_region *r = NULL; int i, num_screens; xcb_query_pointer_reply_t *qpr; xcb_get_input_focus_reply_t *gifr; DNPRINTF(SWM_D_MISC, "root_to_region: win %#x\n", root); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) if (screens[i].root == root) break; if (check & SWM_CK_REGION) r = screens[i].r_focus; if (r == NULL && check & SWM_CK_FOCUS) { /* Try to find an actively focused window */ gifr = xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL); if (gifr) { cfw = find_window(gifr->focus); if (cfw && cfw->ws->r) r = cfw->ws->r; free(gifr); } } if (r == NULL && check & SWM_CK_POINTER) { /* No region with an active focus; try to use pointer. */ qpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, screens[i].root), NULL); if (qpr) { DNPRINTF(SWM_D_MISC, "root_to_region: pointer: " "(%d,%d)\n", qpr->root_x, qpr->root_y); TAILQ_FOREACH(r, &screens[i].rl, entry) if (X(r) <= qpr->root_x && qpr->root_x < MAX_X(r) && Y(r) <= qpr->root_y && qpr->root_y < MAX_Y(r)) break; free(qpr); } } /* Last resort. */ if (r == NULL && check & SWM_CK_FALLBACK) r = TAILQ_FIRST(&screens[i].rl); DNPRINTF(SWM_D_MISC, "root_to_region: idx: %d\n", get_region_index(r)); return (r); } struct ws_win * find_unmanaged_window(xcb_window_t id) { struct ws_win *win; int i, j, num_screens; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) TAILQ_FOREACH(win, &screens[i].ws[j].unmanagedlist, entry) if (id == win->id) return (win); return (NULL); } struct ws_win * find_window(xcb_window_t id) { struct ws_win *win; int i, j, num_screens; xcb_query_tree_reply_t *r; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) if (id == win->id) return (win); r = xcb_query_tree_reply(conn, xcb_query_tree(conn, id), NULL); if (r == NULL) return (NULL); /* if we were looking for the parent return that window instead */ if (r->parent == 0 || r->root == r->parent) { free(r); return (NULL); } /* look for parent */ for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) if (r->parent == win->id) { free(r); return (win); } free(r); return (NULL); } void spawn(int ws_idx, union arg *args, bool close_fd) { int fd; char *ret = NULL; DNPRINTF(SWM_D_MISC, "spawn: %s\n", args->argv[0]); close(xcb_get_file_descriptor(conn)); 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 kill_refs(struct ws_win *win) { int i, x, num_screens; struct swm_region *r; struct workspace *ws; if (win == NULL) return; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) for (x = 0; x < workspace_limit; x++) { ws = &r->s->ws[x]; if (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, num_screens; if (testwin == NULL) return (0); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) for (x = 0; x < workspace_limit; x++) { ws = &r->s->ws[x]; TAILQ_FOREACH(win, &ws->winlist, entry) if (win == testwin) return (0); } return (1); } int validate_ws(struct workspace *testws) { struct swm_region *r; struct workspace *ws; int i, x, num_screens; /* validate all ws */ num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) for (x = 0; x < workspace_limit; x++) { ws = &r->s->ws[x]; if (ws == testws) return (0); } return (1); } void unfocus_win(struct ws_win *win) { xcb_window_t none = XCB_WINDOW_NONE; DNPRINTF(SWM_D_FOCUS, "unfocus_win: win %#x\n", WINID(win)); if (win == NULL) return; if (win->ws == NULL) { DNPRINTF(SWM_D_FOCUS, "unfocus_win: NULL ws.\n"); return; } if (validate_ws(win->ws)) { DNPRINTF(SWM_D_FOCUS, "unfocus_win: invalid ws.\n"); return; } if (win->ws->r == NULL) { DNPRINTF(SWM_D_FOCUS, "unfocus_win: NULL region.\n"); return; } if (validate_win(win)) { DNPRINTF(SWM_D_FOCUS, "unfocus_win: invalid win.\n"); kill_refs(win); return; } if (win->ws->focus == win) { win->ws->focus = NULL; win->ws->focus_prev = win; } if (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; } update_window_color(win); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root, ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1, &none); DNPRINTF(SWM_D_FOCUS, "unfocus_win: done.\n"); } void focus_win(struct ws_win *win) { struct ws_win *cfw = NULL, *parent = NULL, *w, *tmpw; struct workspace *ws; xcb_get_input_focus_reply_t *gifr; DNPRINTF(SWM_D_FOCUS, "focus_win: win %#x\n", WINID(win)); if (win == NULL || win->ws == NULL || !win->mapped) goto out; ws = win->ws; if (validate_ws(ws)) goto out; if (validate_win(win)) { kill_refs(win); goto out; } gifr = xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL); if (gifr) { cfw = find_window(gifr->focus); if (cfw != NULL && cfw != win) { if (cfw->ws != ws && cfw->ws->r != NULL) { /* Change border to unfocused color. */ xcb_change_window_attributes(conn, cfw->id, XCB_CW_BORDER_PIXEL, &cfw->s->c[(MAXIMIZED(cfw) ? SWM_S_COLOR_UNFOCUS_MAXIMIZED : SWM_S_COLOR_UNFOCUS)].pixel); } else { unfocus_win(cfw); } } free(gifr); } if (ws->focus != win) { if (ws->focus && ws->focus != cfw) unfocus_win(ws->focus); ws->focus = win; } /* If this window directs focus to a child window, then clear. */ if (win->focus_child) win->focus_child = NULL; /* If transient, adjust parent's focus child for focus_magic. */ if (TRANS(win)) { parent = find_window(win->transient); if (parent && parent->focus_child != win) parent->focus_child = win; } if (cfw != win && ws->r != NULL) { /* Set input focus if no input hint, or indicated by hint. */ if (!(win->hints.flags & XCB_ICCCM_WM_HINT_INPUT) || (win->hints.flags & XCB_ICCCM_WM_HINT_INPUT && win->hints.input)) xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, win->id, last_event_time); else xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, ws->r->id, XCB_CURRENT_TIME); /* Tell app it can adjust focus to a specific window. */ if (win->take_focus) { /* java is special; always tell parent */ if (TRANS(win) && win->java) client_msg(parent, a_takefocus, last_event_time); else client_msg(win, a_takefocus, last_event_time); } if (ws->cur_layout->flags & SWM_L_MAPONFOCUS || ws->always_raise) { /* If a parent exists, map it first. */ if (parent) { raise_window(parent); map_window(parent); /* Map siblings next. */ TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw) if (w != win && !ICONIC(w) && w->transient == parent->id) { raise_window(w); map_window(w); } } /* Map focused window. */ raise_window(win); map_window(win); /* Stack any children of focus window. */ TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw) if (w->transient == win->id && !ICONIC(w)) { raise_window(w); map_window(w); } } else if (tile_gap < 0 && !ABOVE(win)) { /* * Windows overlap in the layout. * Raise focused win above all tiled wins. */ raise_window(win); map_window(win); } set_region(ws->r); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root, ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1, &win->id); } if (cfw != win) /* Update window border even if workspace is hidden. */ update_window_color(win); out: bar_draw(); DNPRINTF(SWM_D_FOCUS, "focus_win: done.\n"); } /* If a child window should have focus instead, return it. */ struct ws_win * get_focus_magic(struct ws_win *win) { struct ws_win *parent = NULL; struct ws_win *child = NULL; DNPRINTF(SWM_D_FOCUS, "get_focus_magic: win %#x\n", WINID(win)); if (win == NULL) return win; if (TRANS(win)) { parent = find_window(win->transient); /* If parent prefers focus elsewhere, then try to do so. */ if (parent && (child = parent->focus_child)) { if (validate_win(child) == 0 && child->mapped) win = child; else parent->focus_child = NULL; } } /* If this window prefers focus elsewhere, then try to do so. */ if ((child = win->focus_child)) { if (validate_win(child) == 0 && child->mapped) win = child; else win->focus_child = NULL; } return win; } void event_drain(uint8_t rt) { xcb_generic_event_t *evt; /* ensure all pending requests have been processed before filtering. */ xcb_aux_sync(conn); while ((evt = xcb_poll_for_event(conn))) { if (XCB_EVENT_RESPONSE_TYPE(evt) != rt) event_handle(evt); free(evt); } } void set_region(struct swm_region *r) { struct swm_region *rf; int vals[2]; if (r == NULL) return; rf = r->s->r_focus; /* Unfocus old region bar. */ if (rf != NULL) { if (rf == r) return; xcb_change_window_attributes(conn, rf->bar->id, XCB_CW_BORDER_PIXEL, &r->s->c[SWM_S_COLOR_BAR_BORDER_UNFOCUS].pixel); } if (rf != NULL && rf != r && (X(rf) != X(r) || Y(rf) != Y(r) || WIDTH(rf) != WIDTH(r) || HEIGHT(rf) != HEIGHT(r))) { /* Set _NET_DESKTOP_GEOMETRY. */ vals[0] = WIDTH(r); vals[1] = HEIGHT(r); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, r->s->root, ewmh[_NET_DESKTOP_GEOMETRY].atom, XCB_ATOM_CARDINAL, 32, 2, &vals); } /* Set region bar border to focus_color. */ xcb_change_window_attributes(conn, r->bar->id, XCB_CW_BORDER_PIXEL, &r->s->c[SWM_S_COLOR_BAR_BORDER].pixel); r->s->r_focus = r; ewmh_update_current_desktop(); } void focus_region(struct swm_region *r) { struct ws_win *nfw; struct swm_region *old_r; if (r == NULL) return; old_r = r->s->r_focus; set_region(r); nfw = get_region_focus(r); if (nfw) { focus_win(nfw); } else { /* New region is empty; need to manually unfocus win. */ if (old_r) unfocus_win(old_r->ws->focus); xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, r->id, XCB_CURRENT_TIME); /* Clear bar since empty. */ bar_draw(); } } void switchws(struct swm_region *r, union arg *args) { struct swm_region *this_r, *other_r; struct ws_win *win; struct workspace *new_ws, *old_ws; xcb_window_t none = XCB_WINDOW_NONE; int wsid = args->id; bool unmap_old = false; if (!(r && r->s)) return; if (wsid >= workspace_limit) return; this_r = r; old_ws = this_r->ws; new_ws = &this_r->s->ws[wsid]; DNPRINTF(SWM_D_WS, "switchws: screen[%d]:%dx%d+%d+%d: %d -> %d\n", r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), old_ws->idx, wsid); if (new_ws == NULL || old_ws == NULL) return; if (new_ws == old_ws) return; if ((win = old_ws->focus) != NULL) { update_window_color(win); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root, ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1, &none); } other_r = new_ws->r; if (other_r == NULL) { /* the other workspace is hidden, hide this one */ old_ws->r = NULL; unmap_old = true; } 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; /* Set focus_pending before stacking, if needed. */ if (focus_mode != SWM_FOCUS_FOLLOW && (!new_ws->focus_pending || validate_win(new_ws->focus_pending))) { new_ws->focus_pending = get_region_focus(new_ws->r); new_ws->focus = new_ws->focus_prev; new_ws->focus_prev = NULL; } new_ws->state = SWM_WS_STATE_MAPPING; stack(); /* unmap old windows */ if (unmap_old) { TAILQ_FOREACH(win, &old_ws->winlist, entry) unmap_window(win); old_ws->state = SWM_WS_STATE_HIDDEN; } /* if workspaces were swapped, then don't wait to set focus */ if (old_ws->r && focus_mode != SWM_FOCUS_FOLLOW) { if (new_ws->focus_pending) { focus_win(new_ws->focus_pending); new_ws->focus_pending = NULL; } } /* Clear bar and set focus on region input win if new ws is empty. */ if (new_ws->focus_pending == NULL && new_ws->focus == NULL) { xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, r->id, XCB_CURRENT_TIME); bar_draw(); } ewmh_update_current_desktop(); center_pointer(r); focus_flush(); new_ws->state = SWM_WS_STATE_MAPPED; DNPRINTF(SWM_D_WS, "switchws: done.\n"); } void cyclews(struct swm_region *r, union arg *args) { union arg a; struct swm_screen *s = r->s; bool cycle_all = false, mv = false; DNPRINTF(SWM_D_WS, "cyclews: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n", args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx); a.id = r->ws->idx; do { switch (args->id) { case SWM_ARG_ID_CYCLEWS_MOVE_UP: mv = true; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_UP_ALL: cycle_all = true; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_UP: a.id = (a.id < workspace_limit - 1) ? a.id + 1 : 0; break; case SWM_ARG_ID_CYCLEWS_MOVE_DOWN: mv = true; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_DOWN_ALL: cycle_all = true; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_DOWN: a.id = (a.id > 0) ? a.id - 1 : workspace_limit - 1; break; default: return; }; if (!cycle_all && (!cycle_empty && TAILQ_EMPTY(&s->ws[a.id].winlist))) continue; if (!cycle_visible && s->ws[a.id].r != NULL) continue; if (mv) send_to_ws(r, &a); switchws(r, &a); } while (a.id != r->ws->idx); DNPRINTF(SWM_D_FOCUS, "cyclews: done\n"); } void priorws(struct swm_region *r, union arg *args) { union arg a; (void)args; DNPRINTF(SWM_D_WS, "priorws: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n", args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx); if (r->ws_prior == NULL) return; a.id = r->ws_prior->idx; switchws(r, &a); DNPRINTF(SWM_D_FOCUS, "priorws: done\n"); } void focusrg(struct swm_region *r, union arg *args) { int ridx = args->id, i, num_screens; struct swm_region *rr = NULL; num_screens = get_screen_count(); /* do nothing if we don't have more than one screen */ if (!(num_screens > 1 || outputs > 1)) return; DNPRINTF(SWM_D_FOCUS, "focusrg: id: %d\n", ridx); rr = TAILQ_FIRST(&r->s->rl); for (i = 0; i < ridx && rr != NULL; ++i) rr = TAILQ_NEXT(rr, entry); if (rr == NULL) return; focus_region(rr); center_pointer(rr); focus_flush(); DNPRINTF(SWM_D_FOCUS, "focusrg: done\n"); } void cyclerg(struct swm_region *r, union arg *args) { struct swm_region *rr = NULL; int i, num_screens; num_screens = get_screen_count(); /* do nothing if we don't have more than one screen */ if (!(num_screens > 1 || outputs > 1)) return; i = r->s->idx; DNPRINTF(SWM_D_FOCUS, "cyclerg: id: %d, region: %d\n", args->id, i); switch (args->id) { case SWM_ARG_ID_CYCLERG_UP: rr = TAILQ_NEXT(r, entry); if (rr == NULL) rr = TAILQ_FIRST(&screens[i].rl); break; case SWM_ARG_ID_CYCLERG_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; focus_region(rr); center_pointer(rr); focus_flush(); DNPRINTF(SWM_D_FOCUS, "cyclerg: done\n"); } /* Sorts transients after parent. */ void sort_windows(struct ws_win_list *wl) { struct ws_win *win, *parent, *nxt; if (wl == NULL) return; for (win = TAILQ_FIRST(wl); win != TAILQ_END(wl); win = nxt) { nxt = TAILQ_NEXT(win, entry); if (TRANS(win)) { parent = find_window(win->transient); if (parent == NULL) { warnx("not possible bug"); continue; } TAILQ_REMOVE(wl, win, entry); TAILQ_INSERT_AFTER(wl, parent, win, entry); } } } void swapwin(struct 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 || FULLSCREEN(cur_focus)) return; /* Adjust stacking in floating layer. */ if (ABOVE(cur_focus)) { switch (args->id) { case SWM_ARG_ID_SWAPPREV: target = TAILQ_PREV(cur_focus, ws_win_stack, stack_entry); if (target != NULL && FLOATING(target)) { TAILQ_REMOVE(&cur_focus->ws->stack, cur_focus, stack_entry); TAILQ_INSERT_BEFORE(target, cur_focus, stack_entry); update_win_stacking(cur_focus); focus_flush(); } break; case SWM_ARG_ID_SWAPNEXT: target = TAILQ_NEXT(cur_focus, stack_entry); if (target != NULL && FLOATING(target)) { TAILQ_REMOVE(&cur_focus->ws->stack, cur_focus, stack_entry); TAILQ_INSERT_AFTER(&cur_focus->ws->stack, target, cur_focus, stack_entry); update_win_stacking(cur_focus); focus_flush(); } break; } goto out; } if (r->ws->cur_layout == &layouts[SWM_MAX_STACK]) return; clear_maximized(r->ws); source = cur_focus; wl = &source->ws->winlist; switch (args->id) { case SWM_ARG_ID_SWAPPREV: if (TRANS(source)) source = find_window(source->transient); target = TAILQ_PREV(source, ws_win_list, entry); if (target && target->transient) target = find_window(target->transient); TAILQ_REMOVE(wl, source, entry); if (target == NULL) TAILQ_INSERT_TAIL(wl, source, entry); else TAILQ_INSERT_BEFORE(target, source, entry); break; case SWM_ARG_ID_SWAPNEXT: target = TAILQ_NEXT(source, entry); /* move the parent and let the sort handle the move */ if (TRANS(source)) source = find_window(source->transient); TAILQ_REMOVE(wl, source, entry); if (target == NULL) TAILQ_INSERT_HEAD(wl, source, entry); else TAILQ_INSERT_AFTER(wl, target, source, entry); break; case SWM_ARG_ID_SWAPMAIN: target = TAILQ_FIRST(wl); if (target == source) { if (source->ws->focus_prev != NULL && source->ws->focus_prev != target) source = source->ws->focus_prev; else return; } if (target == NULL || source == NULL) return; source->ws->focus_prev = target; TAILQ_REMOVE(wl, target, entry); TAILQ_INSERT_BEFORE(source, target, entry); TAILQ_REMOVE(wl, source, entry); TAILQ_INSERT_HEAD(wl, source, entry); break; case SWM_ARG_ID_MOVELAST: TAILQ_REMOVE(wl, source, entry); TAILQ_INSERT_TAIL(wl, source, entry); break; default: DNPRINTF(SWM_D_MOVE, "swapwin: invalid id: %d\n", args->id); return; } sort_windows(wl); ewmh_update_client_list(); stack(); focus_flush(); out: DNPRINTF(SWM_D_MOVE, "swapwin: done\n"); } struct ws_win * get_focus_prev(struct ws_win *win) { struct ws_win *winfocus = NULL; struct ws_win *cur_focus = NULL; struct ws_win_list *wl = NULL; struct workspace *ws = NULL; if (!(win && win->ws)) return NULL; ws = win->ws; wl = &ws->winlist; cur_focus = ws->focus; DNPRINTF(SWM_D_FOCUS, "get_focus_prev: win %#x, cur_focus: %#x, " "focus_prev: %#x\n", WINID(win), WINID(cur_focus), WINID(ws->focus_prev)); /* pickle, just focus on whatever */ if (cur_focus == NULL) { /* use prev_focus if valid */ if (ws->focus_prev && find_window(ws->focus_prev->id)) winfocus = ws->focus_prev; goto done; } /* if transient focus on parent */ if (TRANS(cur_focus)) { winfocus = find_window(cur_focus->transient); goto done; } /* if in max_stack try harder */ if ((win->quirks & SWM_Q_FOCUSPREV) || (ws->cur_layout->flags & SWM_L_FOCUSPREV)) { if (cur_focus != ws->focus_prev) winfocus = ws->focus_prev; else winfocus = TAILQ_PREV(win, ws_win_list, entry); if (winfocus) goto done; } DNPRINTF(SWM_D_FOCUS, "get_focus_prev: focus_close: %d\n", focus_close); if (winfocus == NULL || winfocus == win) { switch (focus_close) { case SWM_STACK_BOTTOM: TAILQ_FOREACH(winfocus, wl, entry) if (!ICONIC(winfocus) && winfocus != cur_focus) break; break; case SWM_STACK_TOP: TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry) if (!ICONIC(winfocus) && winfocus != cur_focus) break; break; case SWM_STACK_ABOVE: winfocus = TAILQ_NEXT(cur_focus, entry); while (winfocus && ICONIC(winfocus)) winfocus = TAILQ_NEXT(winfocus, entry); if (winfocus == NULL) { if (focus_close_wrap) { TAILQ_FOREACH(winfocus, wl, entry) if (!ICONIC(winfocus) && winfocus != cur_focus) break; } else { TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry) if (!ICONIC(winfocus) && winfocus != cur_focus) break; } } break; case SWM_STACK_BELOW: winfocus = TAILQ_PREV(cur_focus, ws_win_list, entry); while (winfocus && ICONIC(winfocus)) winfocus = TAILQ_PREV(winfocus, ws_win_list, entry); if (winfocus == NULL) { if (focus_close_wrap) { TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry) if (!ICONIC(winfocus) && winfocus != cur_focus) break; } else { TAILQ_FOREACH(winfocus, wl, entry) if (!ICONIC(winfocus) && winfocus != cur_focus) break; } } break; } } done: if (winfocus == NULL || (winfocus && (ICONIC(winfocus) || winfocus == cur_focus))) { if (focus_default == SWM_STACK_TOP) { TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry) if (!ICONIC(winfocus) && winfocus != cur_focus) break; } else { TAILQ_FOREACH(winfocus, wl, entry) if (!ICONIC(winfocus) && winfocus != cur_focus) break; } } kill_refs(win); return get_focus_magic(winfocus); } struct ws_win * get_region_focus(struct swm_region *r) { struct ws_win *winfocus = NULL; if (!(r && r->ws)) return NULL; if (r->ws->focus && !ICONIC(r->ws->focus)) winfocus = r->ws->focus; else if (r->ws->focus_prev && !ICONIC(r->ws->focus_prev)) winfocus = r->ws->focus_prev; else TAILQ_FOREACH(winfocus, &r->ws->winlist, entry) if (!ICONIC(winfocus)) break; return get_focus_magic(winfocus); } void focus(struct swm_region *r, union arg *args) { struct ws_win *head, *cur_focus = NULL, *winfocus = NULL; struct ws_win_list *wl = NULL; struct workspace *ws = NULL; union arg a; int i; xcb_icccm_wm_hints_t hints; if (!(r && r->ws)) goto out; cur_focus = r->ws->focus; ws = r->ws; wl = &ws->winlist; DNPRINTF(SWM_D_FOCUS, "focus: id: %d, cur_focus: %#x\n", args->id, WINID(cur_focus)); /* Make sure an uniconified window has focus, if one exists. */ if (cur_focus == NULL) { cur_focus = TAILQ_FIRST(wl); while (cur_focus != NULL && ICONIC(cur_focus)) cur_focus = TAILQ_NEXT(cur_focus, entry); DNPRINTF(SWM_D_FOCUS, "focus: new cur_focus: %#x\n", WINID(cur_focus)); } switch (args->id) { case SWM_ARG_ID_FOCUSPREV: if (cur_focus == NULL) goto out; winfocus = cur_focus; do { winfocus = TAILQ_PREV(winfocus, ws_win_list, entry); if (winfocus == NULL) winfocus = TAILQ_LAST(wl, ws_win_list); if (winfocus == cur_focus) break; } while (winfocus && (ICONIC(winfocus) || winfocus->id == cur_focus->transient || (cur_focus->transient != XCB_WINDOW_NONE && winfocus->transient == cur_focus->transient))); break; case SWM_ARG_ID_FOCUSNEXT: if (cur_focus == NULL) goto out; winfocus = cur_focus; do { winfocus = TAILQ_NEXT(winfocus, entry); if (winfocus == NULL) winfocus = TAILQ_FIRST(wl); if (winfocus == cur_focus) break; } while (winfocus && (ICONIC(winfocus) || winfocus->id == cur_focus->transient || (cur_focus->transient != XCB_WINDOW_NONE && winfocus->transient == cur_focus->transient))); break; case SWM_ARG_ID_FOCUSMAIN: if (cur_focus == NULL) goto out; winfocus = TAILQ_FIRST(wl); if (winfocus == cur_focus) winfocus = cur_focus->ws->focus_prev; break; case SWM_ARG_ID_FOCUSURGENT: /* Search forward for the next urgent window. */ winfocus = NULL; head = cur_focus; for (i = 0; i <= workspace_limit; ++i) { if (head == NULL) head = TAILQ_FIRST(&r->s->ws[(ws->idx + i) % workspace_limit].winlist); while (head != NULL && (head = TAILQ_NEXT(head, entry)) != NULL) { if (head == cur_focus) { winfocus = cur_focus; break; } if (xcb_icccm_get_wm_hints_reply(conn, xcb_icccm_get_wm_hints(conn, head->id), &hints, NULL) != 0 && xcb_icccm_wm_hints_get_urgency(&hints)) { winfocus = head; break; } } if (winfocus != NULL) break; } /* Switch ws if new focus is on a different ws. */ if (winfocus != NULL && winfocus->ws != ws) { a.id = winfocus->ws->idx; switchws(r, &a); } break; default: goto out; } if (clear_maximized(ws) > 0) stack(); focus_win(get_focus_magic(winfocus)); center_pointer(r); focus_flush(); out: DNPRINTF(SWM_D_FOCUS, "focus: done\n"); } void cycle_layout(struct swm_region *r, union arg *args) { struct workspace *ws = r->ws; /* suppress unused warning since var is needed */ (void)args; DNPRINTF(SWM_D_EVENT, "cycle_layout: workspace: %d\n", ws->idx); ws->cur_layout++; if (ws->cur_layout->l_stack == NULL) ws->cur_layout = &layouts[0]; clear_maximized(ws); stack(); bar_draw(); focus_win(get_region_focus(r)); focus_flush(); } 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 (clear_maximized(ws) > 0) stack(); if (ws->cur_layout->l_config != NULL) ws->cur_layout->l_config(ws, args->id); if (args->id != SWM_ARG_ID_STACKINIT) stack(); bar_draw(); focus_flush(); } void stack(void) { struct swm_geometry g; struct swm_region *r, *r_prev = NULL; int i, num_screens; uint32_t val[2]; #ifdef SWM_DEBUG int j; #endif DNPRINTF(SWM_D_STACK, "stack: begin\n"); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { #ifdef SWM_DEBUG j = 0; #endif TAILQ_FOREACH(r, &screens[i].rl, entry) { /* Adjust stack area for region bar and padding. */ g = r->g; g.x += region_padding; g.y += region_padding; g.w -= 2 * border_width + 2 * region_padding; g.h -= 2 * border_width + 2 * region_padding; if (bar_enabled && r->ws->bar_enabled) { if (!bar_at_bottom) g.y += bar_height; g.h -= bar_height; } DNPRINTF(SWM_D_STACK, "stack: workspace: %d (screen: " "%d, region: %d), (x,y) WxH: (%d,%d) %d x %d\n", r->ws->idx, i, j++, g.x, g.y, g.w, g.h); if (r_prev) { /* Stack bar/input relative to prev. region. */ val[1] = XCB_STACK_MODE_ABOVE; val[0] = r_prev->id; DNPRINTF(SWM_D_STACK, "stack: region input %#x " "relative to %#x.\n", r->id, val[0]); xcb_configure_window(conn, r->id, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, val); val[0] = r_prev->bar->id; DNPRINTF(SWM_D_STACK, "stack: region bar %#x " "relative to %#x.\n", r->bar->id, val[0]); xcb_configure_window(conn, r->bar->id, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, val); } r->ws->cur_layout->l_stack(r->ws, &g); r->ws->cur_layout->l_string(r->ws); /* save r so we can track region changes */ r->ws->old_r = r; r_prev = r; } } if (font_adjusted) font_adjusted--; DNPRINTF(SWM_D_STACK, "stack: end\n"); } void store_float_geom(struct ws_win *win) { if (win == NULL || win->ws->r == NULL) return; /* retain window geom and region geom */ win->g_float = win->g; win->g_float.x -= X(win->ws->r); win->g_float.y -= Y(win->ws->r); win->g_floatvalid = true; DNPRINTF(SWM_D_MISC, "store_float_geom: win %#x, g: (%d,%d)" " %d x %d, g_float: (%d,%d) %d x %d\n", win->id, X(win), Y(win), WIDTH(win), HEIGHT(win), win->g_float.x, win->g_float.y, win->g_float.w, win->g_float.h); } void load_float_geom(struct ws_win *win) { if (win == NULL || win->ws->r == NULL) return; if (win->g_floatvalid) { win->g = win->g_float; X(win) += X(win->ws->r); Y(win) += Y(win->ws->r); DNPRINTF(SWM_D_MISC, "load_float_geom: win %#x, g: (%d,%d)" "%d x %d\n", win->id, X(win), Y(win), WIDTH(win), HEIGHT(win)); } else { DNPRINTF(SWM_D_MISC, "load_float_geom: win %#x, g_float " "is not set.\n", win->id); } } void update_floater(struct ws_win *win) { struct workspace *ws; struct swm_region *r; if (win == NULL) return; ws = win->ws; if ((r = ws->r) == NULL) return; DNPRINTF(SWM_D_MISC, "update_floater: win %#x\n", win->id); win->bordered = true; if (FULLSCREEN(win)) { /* _NET_WM_FULLSCREEN: fullscreen without border. */ if (!win->g_floatvalid) store_float_geom(win); win->g = r->g; win->bordered = false; } else if (MAXIMIZED(win)) { /* Maximize: like a single stacked window. */ if (!win->g_floatvalid) store_float_geom(win); win->g = r->g; if (bar_enabled && ws->bar_enabled) { if (!bar_at_bottom) Y(win) += bar_height; HEIGHT(win) -= bar_height; } else if (disable_border) { win->bordered = false; } if (win->bordered) { HEIGHT(win) -= 2 * border_width; WIDTH(win) -= 2 * border_width; } } else { /* Normal floating window. */ /* Update geometry if new region. */ if (r != ws->old_r) load_float_geom(win); if ((win->quirks & SWM_Q_FULLSCREEN) && WIDTH(win) >= WIDTH(r) && HEIGHT(win) >= HEIGHT(r)) { /* Remove border for FULLSCREEN quirk. */ win->bordered = false; } else if (!MANUAL(win)) { if (TRANS(win) && (win->quirks & SWM_Q_TRANSSZ)) { /* Adjust size on TRANSSZ quirk. */ WIDTH(win) = (double)WIDTH(r) * dialog_ratio; HEIGHT(win) = (double)HEIGHT(r) * dialog_ratio; } if (!(win->quirks & SWM_Q_ANYWHERE)) { /* * Floaters and transients are auto-centred * unless manually moved, resized or ANYWHERE * quirk is set. */ X(win) = X(r) + (WIDTH(r) - WIDTH(win)) / 2 - BORDER(win); Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2 - BORDER(win); store_float_geom(win); } } } /* Ensure at least 1 pixel of the window is in the region. */ region_containment(win, r, SWM_CW_ALLSIDES); update_window(win); } /* * Send keystrokes to terminal to decrease/increase the font size as the * window size changes. */ void adjust_font(struct ws_win *win) { if (!(win->quirks & SWM_Q_XTERM_FONTADJ) || ABOVE(win) || TRANS(win)) return; if (win->sh.width_inc && win->last_inc != win->sh.width_inc && WIDTH(win) / win->sh.width_inc < term_width && win->font_steps < SWM_MAX_FONT_STEPS) { win->font_size_boundary[win->font_steps] = (win->sh.width_inc * term_width) + win->sh.base_width; win->font_steps++; font_adjusted++; win->last_inc = win->sh.width_inc; fake_keypress(win, XK_KP_Subtract, XCB_MOD_MASK_SHIFT); } else if (win->font_steps && win->last_inc != win->sh.width_inc && WIDTH(win) > win->font_size_boundary[win->font_steps - 1]) { win->font_steps--; font_adjusted++; win->last_inc = win->sh.width_inc; fake_keypress(win, XK_KP_Add, XCB_MOD_MASK_SHIFT); } } #define SWAPXY(g) do { \ int tmp; \ tmp = (g)->y; (g)->y = (g)->x; (g)->x = tmp; \ tmp = (g)->h; (g)->h = (g)->w; (g)->w = tmp; \ } while (0) void stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip) { struct swm_geometry win_g, r_g = *g; struct ws_win *win; 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; bool bordered = true, reconfigure = false; DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d, rot: %s, " "flip: %s\n", ws->idx, YESNO(rot), YESNO(flip)); /* Prepare tiling variables, if needed. */ if ((winno = count_win(ws, false)) > 0) { /* Find first tiled window. */ TAILQ_FOREACH(win, &ws->winlist, entry) if (!FLOATING(win) && !ICONIC(win)) break; /* Take into account size hints of first tiled window. */ if (rot) { w_inc = win->sh.width_inc; w_base = win->sh.base_width; mwin = ws->l_state.horizontal_mwin; mscale = ws->l_state.horizontal_msize; stacks = ws->l_state.horizontal_stacks; SWAPXY(&r_g); } else { w_inc = win->sh.height_inc; w_base = win->sh.base_height; mwin = ws->l_state.vertical_mwin; mscale = ws->l_state.vertical_msize; stacks = ws->l_state.vertical_stacks; } 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 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; i = j = 0, s = stacks; } /* Update window geometry. */ TAILQ_FOREACH(win, &ws->winlist, entry) { if (ICONIC(win)) continue; if (FLOATING(win)) { update_floater(win); continue; } /* Tiled. */ if (split && i == split) { colno = (winno - mwin) / stacks; if (s <= (winno - mwin) % stacks) colno++; split += colno; hrh = r_g.h / colno; extra = r_g.h - (colno * hrh); if (!flip) win_g.x += win_g.w + 2 * border_width + tile_gap; win_g.w = (r_g.w - msize - (stacks * (2 * border_width + tile_gap))) / stacks; if (s == 1) win_g.w += (r_g.w - msize - (stacks * (2 * border_width + tile_gap))) % stacks; if (flip) win_g.x -= win_g.w + 2 * border_width + tile_gap; s--; j = 0; } win_g.h = hrh - 2 * border_width - tile_gap; if (rot) { h_inc = win->sh.width_inc; h_base = win->sh.base_width; } else { h_inc = win->sh.height_inc; h_base = win->sh.base_height; } if (j == colno - 1) { 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 + tile_gap; if (disable_border && !(bar_enabled && ws->bar_enabled) && winno == 1){ bordered = false; win_g.w += 2 * border_width; win_g.h += 2 * border_width; } else { bordered = true; } if (rot) { if (X(win) != win_g.y || Y(win) != win_g.x || WIDTH(win) != win_g.h || HEIGHT(win) != win_g.w) { reconfigure = true; X(win) = win_g.y; Y(win) = win_g.x; WIDTH(win) = win_g.h; HEIGHT(win) = 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 = true; X(win) = win_g.x; Y(win) = win_g.y; WIDTH(win) = win_g.w; HEIGHT(win) = win_g.h; } } if (bordered != win->bordered) { reconfigure = true; win->bordered = bordered; } if (reconfigure) { adjust_font(win); update_window(win); } last_h = win_g.h; i++; j++; } /* Stack all windows from bottom up. */ TAILQ_FOREACH_REVERSE(win, &ws->stack, ws_win_stack, stack_entry) if (!ICONIC(win)) update_win_stacking(win); /* Map all windows from top down. */ TAILQ_FOREACH(win, &ws->stack, stack_entry) if (!ICONIC(win)) map_window(win); DNPRINTF(SWM_D_STACK, "stack_master: done\n"); } void vertical_config(struct workspace *ws, int id) { DNPRINTF(SWM_D_STACK, "vertical_config: id: %d, workspace: %d\n", id, ws->idx); switch (id) { case SWM_ARG_ID_STACKRESET: case SWM_ARG_ID_STACKINIT: ws->l_state.vertical_msize = SWM_V_SLICE / 2; ws->l_state.vertical_mwin = 1; ws->l_state.vertical_stacks = 1; break; case SWM_ARG_ID_MASTERSHRINK: if (ws->l_state.vertical_msize > 1) ws->l_state.vertical_msize--; break; case SWM_ARG_ID_MASTERGROW: if (ws->l_state.vertical_msize < SWM_V_SLICE - 1) ws->l_state.vertical_msize++; break; case SWM_ARG_ID_MASTERADD: ws->l_state.vertical_mwin++; break; case SWM_ARG_ID_MASTERDEL: if (ws->l_state.vertical_mwin > 0) ws->l_state.vertical_mwin--; break; case SWM_ARG_ID_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) { struct swm_geometry gg = *g; struct ws_win *w, *win = NULL, *parent = NULL, *tmpw; int winno; DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx); if (ws == NULL) return; winno = count_win(ws, false); if (winno == 0 && count_win(ws, true) == 0) return; /* Figure out which top level window should be visible. */ if (ws->focus_pending) win = ws->focus_pending; else if (ws->focus) win = ws->focus; else if (ws->focus_prev) win = ws->focus_prev; else win = TAILQ_FIRST(&ws->winlist); DNPRINTF(SWM_D_STACK, "max_stack: focus_pending: %#x, focus: %#x, " "focus_prev: %#x, first: %#x, win: %#x\n", WINID(ws->focus_pending), WINID(ws->focus), WINID(ws->focus_prev), WINID(TAILQ_FIRST(&ws->winlist)), win->id); /* Update window geometry. */ TAILQ_FOREACH(w, &ws->winlist, entry) { if (ICONIC(w)) continue; if (TRANS(w)) { update_floater(w); continue; } /* Set maximized flag for all maxed windows. */ if (!MAXIMIZED(w)) { /* Preserve floating geometry. */ if (ABOVE(w)) store_float_geom(w); ewmh_apply_flags(w, w->ewmh_flags | EWMH_F_MAXIMIZED); ewmh_update_wm_state(w); } /* Only reconfigure if necessary. */ if (X(w) != gg.x || Y(w) != gg.y || WIDTH(w) != gg.w || HEIGHT(w) != gg.h) { w->g = gg; if (bar_enabled && ws->bar_enabled){ w->bordered = true; } else { w->bordered = false; WIDTH(w) += 2 * border_width; HEIGHT(w) += 2 * border_width; } update_window(w); } } /* If transient, stack parent and its children. */ if (TRANS(win) && (parent = find_window(win->transient))) { raise_window(parent); TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw) if (w->transient == parent->id) raise_window(w); } /* Make sure focus window is on top. */ raise_window(win); /* Stack any children of focus window. */ TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw) if (w->transient == win->id) raise_window(w); /* Map all windows. */ TAILQ_FOREACH(w, &ws->stack, stack_entry) if (!ICONIC(w)) map_window(w); } void send_to_rg(struct swm_region *r, union arg *args) { int ridx = args->id, i, num_screens; struct swm_region *rr = NULL; union arg a; num_screens = get_screen_count(); /* do nothing if we don't have more than one screen */ if (!(num_screens > 1 || outputs > 1)) return; DNPRINTF(SWM_D_FOCUS, "send_to_rg: id: %d\n", ridx); rr = TAILQ_FIRST(&r->s->rl); for (i = 0; i < ridx && rr != NULL; ++i) rr = TAILQ_NEXT(rr, entry); if (rr == NULL) return; a.id = rr->ws->idx; send_to_ws(r, &a); } struct swm_region * region_under(struct swm_screen *s, int x, int y) { struct swm_region *r = NULL; TAILQ_FOREACH(r, &s->rl, entry) { DNPRINTF(SWM_D_MISC, "region_under: ws: %d, region g: (%d,%d) " "%d x %d, coords: (%d,%d)\n", r->ws->idx, X(r), Y(r), WIDTH(r), HEIGHT(r), x, y); if (X(r) <= x && x < MAX_X(r)) if (Y(r) <= y && y < MAX_Y(r)) return (r); } return (NULL); } /* Transfer focused window to target workspace and focus. */ void send_to_ws(struct swm_region *r, union arg *args) { int wsid = args->id; struct ws_win *win = NULL; if (r && r->ws && r->ws->focus) win = r->ws->focus; else return; DNPRINTF(SWM_D_MOVE, "send_to_ws: win %#x, ws %d\n", win->id, wsid); if (wsid < 0 || wsid >= workspace_limit) return; if (win->ws->idx == wsid) return; win_to_ws(win, wsid, true); /* Set new focus on target ws. */ if (focus_mode != SWM_FOCUS_FOLLOW) { win->ws->focus_prev = win->ws->focus; win->ws->focus = win; win->ws->focus_pending = NULL; if (win->ws->focus_prev) update_window_color(win->ws->focus_prev); } DNPRINTF(SWM_D_STACK, "send_to_ws: focus_pending: %#x, focus: %#x, " "focus_prev: %#x, first: %#x, win: %#x\n", WINID(r->ws->focus_pending), WINID(r->ws->focus), WINID(r->ws->focus_prev), WINID(TAILQ_FIRST(&r->ws->winlist)), win->id); ewmh_apply_flags(win, win->ewmh_flags & ~EWMH_F_MAXIMIZED); ewmh_update_wm_state(win); /* Restack and set new focus on current ws. */ if (FLOATING(win)) load_float_geom(win); stack(); if (focus_mode != SWM_FOCUS_FOLLOW) { if (r->ws->focus != NULL) { focus_win(r->ws->focus); } else { xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, r->id, XCB_CURRENT_TIME); bar_draw(); } } center_pointer(r); focus_flush(); } void win_to_ws(struct ws_win *win, int wsid, bool unfocus) { struct ws_win *parent; struct workspace *ws, *nws, *pws; if (wsid < 0 || wsid >= workspace_limit) return; if (win->ws->idx == wsid) return; ws = win->ws; nws = &win->s->ws[wsid]; DNPRINTF(SWM_D_MOVE, "win_to_ws: win %#x, ws %d -> %d\n", win->id, ws->idx, wsid); /* Cleanup focus on source ws. */ if (focus_mode != SWM_FOCUS_FOLLOW && (ws->focus == win || ws->focus_pending == win)) ws->focus_pending = get_focus_prev(win); /* Move the parent if this is a transient window. */ if (TRANS(win)) { parent = find_window(win->transient); if (parent) { pws = parent->ws; /* Set new focus in parent's ws if needed. */ if (pws->focus == parent) { if (focus_mode != SWM_FOCUS_FOLLOW) pws->focus_pending = get_focus_prev(parent); unfocus_win(parent); if (focus_mode != SWM_FOCUS_FOLLOW) { pws->focus = pws->focus_pending; pws->focus_pending = NULL; } } /* Don't unmap parent if new ws is visible */ if (nws->r == NULL) unmap_window(parent); /* Transfer */ TAILQ_REMOVE(&ws->winlist, parent, entry); TAILQ_REMOVE(&ws->stack, parent, stack_entry); TAILQ_INSERT_TAIL(&nws->winlist, parent, entry); TAILQ_INSERT_TAIL(&nws->stack, parent, stack_entry); parent->ws = nws; DNPRINTF(SWM_D_PROP, "win_to_ws: set property: " "_NET_WM_DESKTOP: %d\n", wsid); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, parent->id, ewmh[_NET_WM_DESKTOP].atom, XCB_ATOM_CARDINAL, 32, 1, &wsid); } } if (unfocus) unfocus_win(win); if (ws->focus_prev == win) ws->focus_prev = NULL; if (focus_mode != SWM_FOCUS_FOLLOW && ws->focus_pending != NULL) { ws->focus = ws->focus_pending; ws->focus_pending = NULL; } /* Don't unmap if new ws is visible */ if (nws->r == NULL) unmap_window(win); /* Transfer */ TAILQ_REMOVE(&ws->winlist, win, entry); TAILQ_REMOVE(&ws->stack, win, stack_entry); TAILQ_INSERT_TAIL(&nws->winlist, win, entry); TAILQ_INSERT_TAIL(&nws->stack, win, stack_entry); win->ws = nws; /* Update the window's workspace property: _NET_WM_DESKTOP */ DNPRINTF(SWM_D_PROP, "win_to_ws: set property: " "_NET_WM_DESKTOP: %d\n", wsid); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id, ewmh[_NET_WM_DESKTOP].atom, XCB_ATOM_CARDINAL, 32, 1, &wsid); ewmh_update_client_list(); DNPRINTF(SWM_D_MOVE, "win_to_ws: done.\n"); } void pressbutton(struct swm_region *r, union arg *args) { /* suppress unused warning since var is needed */ (void)r; xcb_test_fake_input(conn, XCB_BUTTON_PRESS, args->id, XCB_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0); xcb_test_fake_input(conn, XCB_BUTTON_RELEASE, args->id, XCB_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0); } void raise_toggle(struct swm_region *r, union arg *args) { /* suppress unused warning since var is needed */ (void)args; if (r == NULL || r->ws == NULL) return; if (r->ws->focus && MAXIMIZED(r->ws->focus)) return; r->ws->always_raise = !r->ws->always_raise; /* bring floaters back to top */ if (!r->ws->always_raise) stack(); focus_flush(); } void iconify(struct swm_region *r, union arg *args) { struct ws_win *w; /* suppress unused warning since var is needed */ (void)args; if ((w = r->ws->focus) == NULL) return; ewmh_apply_flags(w, w->ewmh_flags | EWMH_F_HIDDEN); ewmh_update_wm_state(w); stack(); focus_flush(); } char * get_win_name(xcb_window_t win) { char *name = NULL; xcb_get_property_cookie_t c; xcb_get_property_reply_t *r; /* First try _NET_WM_NAME for UTF-8. */ c = xcb_get_property(conn, 0, win, ewmh[_NET_WM_NAME].atom, XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX); r = xcb_get_property_reply(conn, c, NULL); if (r && r->type == XCB_NONE) { free(r); /* Use WM_NAME instead; no UTF-8. */ c = xcb_get_property(conn, 0, win, XCB_ATOM_WM_NAME, XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX); r = xcb_get_property_reply(conn, c, NULL); } if (r && r->type != XCB_NONE && r->length > 0) name = strndup(xcb_get_property_value(r), xcb_get_property_value_length(r)); else name = strdup(""); if (name == NULL) err(1, "get_win_name: failed to allocate memory."); free(r); return (name); } void uniconify(struct swm_region *r, union arg *args) { struct ws_win *win; FILE *lfile; 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 (!ICONIC(win)) continue; count++; } DNPRINTF(SWM_D_MISC, "uniconify: count: %d\n", count); if (count == 0) return; search_r = r; search_resp_action = SWM_SEARCH_UNICONIFY; spawn_select(r, args, "search", &searchpid); if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL) return; TAILQ_FOREACH(win, &r->ws->winlist, entry) { if (win->ws == NULL) continue; /* should never happen */ if (!ICONIC(win)) continue; name = get_win_name(win->id); fprintf(lfile, "%s.%u\n", name, win->id); free(name); } fclose(lfile); } void name_workspace(struct 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 < workspace_limit; i++) { ws = &r->s->ws[i]; if (ws == NULL) continue; fprintf(lfile, "%d%s%s\n", ws->idx + 1, (ws->name ? ":" : ""), (ws->name ? ws->name : "")); } fclose(lfile); } void search_win_cleanup(void) { struct search_window *sw = NULL; while ((sw = TAILQ_FIRST(&search_wl)) != NULL) { xcb_destroy_window(conn, sw->indicator); 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; xcb_window_t w; uint32_t wa[3]; xcb_screen_t *screen; int i, width, height; char s[8]; FILE *lfile; size_t len; XftDraw *draw; XGlyphInfo info; GC l_draw; XGCValues l_gcv; XRectangle l_ibox, l_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; if ((screen = get_screen(r->s->idx)) == NULL) errx(1, "ERROR: can't get screen %d.", r->s->idx); TAILQ_INIT(&search_wl); i = 1; TAILQ_FOREACH(win, &r->ws->winlist, entry) { if (ICONIC(win)) continue; sw = calloc(1, sizeof(struct search_window)); if (sw == NULL) { warn("search_win: calloc"); fclose(lfile); search_win_cleanup(); return; } sw->idx = i; sw->win = win; snprintf(s, sizeof s, "%d", i); len = strlen(s); w = xcb_generate_id(conn); wa[0] = r->s->c[SWM_S_COLOR_FOCUS].pixel; wa[1] = r->s->c[SWM_S_COLOR_UNFOCUS].pixel; wa[2] = screen->default_colormap; if (bar_font_legacy) { XmbTextExtents(bar_fs, s, len, &l_ibox, &l_lbox); width = l_lbox.width + 4; height = bar_fs_extents->max_logical_extent.height + 4; } else { XftTextExtentsUtf8(display, bar_font, (FcChar8 *)s, len, &info); width = info.width + 4; height = bar_font->height + 4; } xcb_create_window(conn, screen->root_depth, w, win->id, 0, 0, width, height, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP, wa); xcb_map_window(conn, w); sw->indicator = w; TAILQ_INSERT_TAIL(&search_wl, sw, entry); if (bar_font_legacy) { l_gcv.graphics_exposures = 0; l_draw = XCreateGC(display, w, 0, &l_gcv); XSetForeground(display, l_draw, r->s->c[SWM_S_COLOR_BAR].pixel); DRAWSTRING(display, w, bar_fs, l_draw, 2, (bar_fs_extents->max_logical_extent.height - l_lbox.height) / 2 - l_lbox.y, s, len); XFreeGC(display, l_draw); } else { draw = XftDrawCreate(display, w, DefaultVisual(display, r->s->idx), DefaultColormap(display, r->s->idx)); XftDrawStringUtf8(draw, &search_font_color, bar_font, 2, (HEIGHT(r->bar) + bar_font->height) / 2 - bar_font->descent, (FcChar8 *)s, len); XftDrawDestroy(draw); } DNPRINTF(SWM_D_MISC, "search_win: mapped win %#x\n", w); fprintf(lfile, "%d\n", i); i++; } fclose(lfile); xcb_flush(conn); } void search_resp_uniconify(const char *resp, size_t len) { char *name; struct ws_win *win; char *s; DNPRINTF(SWM_D_MISC, "search_resp_uniconify: resp: %s\n", resp); TAILQ_FOREACH(win, &search_r->ws->winlist, entry) { if (!ICONIC(win)) continue; name = get_win_name(win->id); if (asprintf(&s, "%s.%u", name, win->id) == -1) { free(name); continue; } free(name); if (strncmp(s, resp, len) == 0) { /* XXX this should be a callback to generalize */ ewmh_apply_flags(win, win->ewmh_flags & ~EWMH_F_HIDDEN); ewmh_update_wm_state(win); stack(); free(s); break; } free(s); } } void search_resp_name_workspace(const char *resp, size_t len) { struct workspace *ws; DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: resp: %s\n", resp); if (search_r->ws == NULL) return; ws = search_r->ws; if (ws->name) { free(search_r->ws->name); search_r->ws->name = NULL; } if (len > 1) { ws->name = strdup(resp); if (ws->name == NULL) { DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: " "strdup: %s", strerror(errno)); return; } ewmh_update_desktop_names(); ewmh_get_desktop_names(); } } void ewmh_update_desktop_names(void) { char *name_list = NULL, *p; int num_screens, i, j, len = 0, tot = 0; num_screens = get_screen_count(); for (i = 0; i < num_screens; ++i) { for (j = 0; j < workspace_limit; ++j) { if (screens[i].ws[j].name != NULL) len += strlen(screens[i].ws[j].name); ++len; } if((name_list = calloc(len, sizeof(char))) == NULL) err(1, "update_desktop_names: calloc: failed to " "allocate memory."); p = name_list; for (j = 0; j < workspace_limit; ++j) { if (screens[i].ws[j].name != NULL) { len = strlen(screens[i].ws[j].name); memcpy(p, screens[i].ws[j].name, len); } else { len = 0; } p += len + 1; tot += len + 1; } xcb_change_property(conn, XCB_PROP_MODE_REPLACE, screens[i].root, ewmh[_NET_DESKTOP_NAMES].atom, a_utf8_string, 8, tot, name_list); free(name_list); name_list = NULL; } free(name_list); } void ewmh_get_desktop_names(void) { char *names = NULL; xcb_get_property_cookie_t c; xcb_get_property_reply_t *r; int num_screens, i, j, n, k; num_screens = get_screen_count(); for (i = 0; i < num_screens; ++i) { for (j = 0; j < workspace_limit; ++j) { free(screens[i].ws[j].name); screens[i].ws[j].name = NULL; } c = xcb_get_property(conn, 0, screens[i].root, ewmh[_NET_DESKTOP_NAMES].atom, a_utf8_string, 0, UINT32_MAX); r = xcb_get_property_reply(conn, c, NULL); if (r == NULL) continue; names = xcb_get_property_value(r); n = xcb_get_property_value_length(r); for (j = 0, k = 0; j < n; ++j) { if (*(names + j) != '\0') { screens[i].ws[k].name = strdup(names + j); j += strlen(names + j); } ++k; } free(r); } } void ewmh_update_client_list(void) { struct ws_win *win; int num_screens, i, j, k = 0, count = 0; xcb_window_t *wins; num_screens = get_screen_count(); for (i = 0; i < num_screens; ++i) { for (j = 0; j < workspace_limit; ++j) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) ++count; DNPRINTF(SWM_D_PROP, "ewmh_update_client_list: win count: %d\n", count); if (count == 0) continue; wins = calloc(count, sizeof(xcb_window_t)); if (wins == NULL) err(1, "ewmh_update_client_list: calloc: failed to " "allocate memory."); for (j = 0, k = 0; j < workspace_limit; ++j) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) wins[k++] = win->id; xcb_change_property(conn, XCB_PROP_MODE_REPLACE, screens[i].root, ewmh[_NET_CLIENT_LIST].atom, XCB_ATOM_WINDOW, 32, count, wins); free(wins); } } void ewmh_update_current_desktop(void) { int num_screens, i; num_screens = get_screen_count(); for (i = 0; i < num_screens; ++i) xcb_change_property(conn, XCB_PROP_MODE_REPLACE, screens[i].root, ewmh[_NET_CURRENT_DESKTOP].atom, XCB_ATOM_CARDINAL, 32, 1, &screens[i].r_focus->ws->idx); } void ewmh_update_desktops(void) { int num_screens, i, j; uint32_t *vals; vals = calloc(workspace_limit * 2, sizeof(uint32_t)); if (vals == NULL) err(1, "ewmh_update_desktops: calloc: failed to allocate " "memory."); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { xcb_change_property(conn, XCB_PROP_MODE_REPLACE, screens[i].root, ewmh[_NET_NUMBER_OF_DESKTOPS].atom, XCB_ATOM_CARDINAL, 32, 1, &workspace_limit); for (j = 0; j < workspace_limit; ++j) { if (screens[i].ws[j].r != NULL) { vals[j * 2] = X(screens[i].ws[j].r); vals[j * 2 + 1] = Y(screens[i].ws[j].r); } else if (screens[i].ws[j].old_r != NULL) { vals[j * 2] = X(screens[i].ws[j].old_r); vals[j * 2 + 1] = Y(screens[i].ws[j].old_r); } else { vals[j * 2] = vals[j * 2 + 1] = 0; } } xcb_change_property(conn, XCB_PROP_MODE_REPLACE, screens[i].root, ewmh[_NET_DESKTOP_VIEWPORT].atom, XCB_ATOM_CARDINAL, 32, workspace_limit * 2, vals); } free(vals); } void search_resp_search_workspace(const char *resp) { char *p, *q; int ws_idx; const char *errstr; union arg a; DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: resp: %s\n", resp); q = strdup(resp); if (q == NULL) { DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: strdup: %s", strerror(errno)); return; } p = strchr(q, ':'); if (p != NULL) *p = '\0'; ws_idx = (int)strtonum(q, 1, workspace_limit, &errstr); if (errstr) { DNPRINTF(SWM_D_MISC, "workspace idx is %s: %s", errstr, q); free(q); return; } free(q); a.id = ws_idx - 1; switchws(search_r, &a); } void search_resp_search_window(const char *resp) { char *s; int idx; const char *errstr; struct search_window *sw; DNPRINTF(SWM_D_MISC, "search_resp_search_window: resp: %s\n", resp); s = strdup(resp); if (s == NULL) { DNPRINTF(SWM_D_MISC, "search_resp_search_window: strdup: %s", strerror(errno)); return; } idx = (int)strtonum(s, 1, INT_MAX, &errstr); if (errstr) { DNPRINTF(SWM_D_MISC, "window idx is %s: %s", errstr, s); free(s); return; } free(s); TAILQ_FOREACH(sw, &search_wl, entry) if (idx == sw->idx) { focus_win(sw->win); break; } } #define MAX_RESP_LEN 1024 void search_do_resp(void) { ssize_t rbytes; char *resp; size_t len; DNPRINTF(SWM_D_MISC, "search_do_resp:\n"); search_resp = 0; searchpid = 0; if ((resp = calloc(1, MAX_RESP_LEN + 1)) == NULL) { warn("search: calloc"); goto done; } rbytes = read(select_resp_pipe[0], resp, MAX_RESP_LEN); if (rbytes <= 0) { warn("search: read error"); goto done; } resp[rbytes] = '\0'; /* XXX: * Older versions of dmenu (Atleast pre 4.4.1) do not send a * newline, so work around that by sanitizing the resp now. */ resp[strcspn(resp, "\n")] = '\0'; len = strlen(resp); switch (search_resp_action) { case SWM_SEARCH_UNICONIFY: search_resp_uniconify(resp, len); break; case SWM_SEARCH_NAME_WORKSPACE: search_resp_name_workspace(resp, len); break; case SWM_SEARCH_SEARCH_WORKSPACE: search_resp_search_workspace(resp); break; case SWM_SEARCH_SEARCH_WINDOW: search_resp_search_window(resp); break; } done: if (search_resp_action == SWM_SEARCH_SEARCH_WINDOW) search_win_cleanup(); search_resp_action = SWM_SEARCH_NONE; close(select_resp_pipe[0]); free(resp); xcb_flush(conn); } void wkill(struct swm_region *r, union arg *args) { DNPRINTF(SWM_D_MISC, "wkill: win %#x, id: %d\n", WINID(r->ws->focus), args->id); if (r->ws->focus == NULL) return; if (args->id == SWM_ARG_ID_KILLWINDOW) xcb_kill_client(conn, r->ws->focus->id); else if (r->ws->focus->can_delete) client_msg(r->ws->focus, a_delete, 0); focus_flush(); } int clear_maximized(struct workspace *ws) { struct ws_win *w; int count = 0; /* Clear any maximized win(s) on ws, from bottom up. */ TAILQ_FOREACH_REVERSE(w, &ws->stack, ws_win_stack, stack_entry) if (MAXIMIZED(w)) { ewmh_apply_flags(w, w->ewmh_flags & ~EWMH_F_MAXIMIZED); ewmh_update_wm_state(w); ++count; } return count; } void maximize_toggle(struct swm_region *r, union arg *args) { struct ws_win *w = r->ws->focus; /* suppress unused warning since var is needed */ (void)args; if (w == NULL) return; DNPRINTF(SWM_D_MISC, "maximize_toggle: win %#x\n", w->id); if (FULLSCREEN(w)) return; if (w->ws->cur_layout == &layouts[SWM_MAX_STACK]) return; ewmh_apply_flags(w, w->ewmh_flags ^ EWMH_F_MAXIMIZED); ewmh_update_wm_state(w); stack(); if (w == w->ws->focus) focus_win(w); focus_flush(); DNPRINTF(SWM_D_MISC, "maximize_toggle: done\n"); } void floating_toggle(struct swm_region *r, union arg *args) { struct ws_win *w = r->ws->focus; /* suppress unused warning since var is needed */ (void)args; if (w == NULL) return; DNPRINTF(SWM_D_MISC, "floating_toggle: win %#x\n", w->id); if (FULLSCREEN(w) || TRANS(w)) return; if (w->ws->cur_layout == &layouts[SWM_MAX_STACK]) return; ewmh_apply_flags(w, w->ewmh_flags ^ EWMH_F_ABOVE); ewmh_update_wm_state(w); stack(); if (w == w->ws->focus) focus_win(w); center_pointer(r); focus_flush(); DNPRINTF(SWM_D_MISC, "floating_toggle: done\n"); } void region_containment(struct ws_win *win, struct swm_region *r, int opts) { struct swm_geometry g = r->g; int rt, lt, tp, bm, bw; bw = (opts & SWM_CW_SOFTBOUNDARY) ? boundary_width : 0; /* * Perpendicular distance of each side of the window to the respective * side of the region boundary. Positive values indicate the side of * the window has passed beyond the region boundary. */ rt = opts & SWM_CW_RIGHT ? MAX_X(win) + BORDER(win) - MAX_X(r) : bw; lt = opts & SWM_CW_LEFT ? X(r) - X(win) + BORDER(win) : bw; bm = opts & SWM_CW_BOTTOM ? MAX_Y(win) + BORDER(win) - MAX_Y(r) : bw; tp = opts & SWM_CW_TOP ? Y(r) - Y(win) + BORDER(win) : bw; DNPRINTF(SWM_D_MISC, "region_containment: win %#x, rt: %d, lt: %d, " "bm: %d, tp: %d, SOFTBOUNDARY: %s, HARDBOUNDARY: %s\n", win->id, rt, lt, bm, tp, YESNO(opts & SWM_CW_SOFTBOUNDARY), YESNO(opts & SWM_CW_HARDBOUNDARY)); /* * Disable containment if any of the flagged sides went beyond the * containment boundary, or if containment is disabled. */ if (!(opts & SWM_CW_HARDBOUNDARY || opts & SWM_CW_SOFTBOUNDARY) || (bw != 0 && ((rt > bw) || (lt > bw) || (bm > bw) || (tp > bw)))) { /* Make sure window has at least 1 pixel in the region */ g.x += 1 - WIDTH(win); g.y += 1 - HEIGHT(win); g.w += 2 * WIDTH(win) - 2; g.h += 2 * HEIGHT(win) - 2; } constrain_window(win, &g, &opts); } /* Move or resize a window so that flagged side(s) fit into the supplied box. */ void constrain_window(struct ws_win *win, struct swm_geometry *b, int *opts) { DNPRINTF(SWM_D_MISC, "constrain_window: win %#x, (x,y) w x h: " "(%d,%d) %d x %d, box: (x,y) w x h: (%d,%d) %d x %d, rt: %s, " "lt: %s, bt: %s, tp: %s, allow resize: %s\n", win->id, X(win), Y(win), WIDTH(win), HEIGHT(win), b->x, b->y, b->w, b->h, YESNO(*opts & SWM_CW_RIGHT), YESNO(*opts & SWM_CW_LEFT), YESNO(*opts & SWM_CW_BOTTOM), YESNO(*opts & SWM_CW_TOP), YESNO(*opts & SWM_CW_RESIZABLE)); if ((*opts & SWM_CW_RIGHT) && MAX_X(win) + BORDER(win) > b->x + b->w) { if (*opts & SWM_CW_RESIZABLE) WIDTH(win) = b->x + b->w - X(win) - BORDER(win); else X(win) = b->x + b->w - WIDTH(win) - BORDER(win); } if ((*opts & SWM_CW_LEFT) && X(win) + BORDER(win) < b->x) { if (*opts & SWM_CW_RESIZABLE) WIDTH(win) -= b->x - X(win) - BORDER(win); X(win) = b->x - BORDER(win); } if ((*opts & SWM_CW_BOTTOM) && MAX_Y(win) + BORDER(win) > b->y + b->h) { if (*opts & SWM_CW_RESIZABLE) HEIGHT(win) = b->y + b->h - Y(win) - BORDER(win); else Y(win) = b->y + b->h - HEIGHT(win) - BORDER(win); } if ((*opts & SWM_CW_TOP) && Y(win) + BORDER(win) < b->y) { if (*opts & SWM_CW_RESIZABLE) HEIGHT(win) -= b->y - Y(win) - BORDER(win); Y(win) = b->y - BORDER(win); } if (*opts & SWM_CW_RESIZABLE) { if (WIDTH(win) < 1) WIDTH(win) = 1; if (HEIGHT(win) < 1) HEIGHT(win) = 1; } } void update_window_color(struct ws_win *win) { uint32_t *pixel; if (WS_FOCUSED(win->ws) && win->ws->focus == win) pixel = MAXIMIZED(win) ? &win->s->c[SWM_S_COLOR_FOCUS_MAXIMIZED].pixel : &win->s->c[SWM_S_COLOR_FOCUS].pixel; else pixel = MAXIMIZED(win) ? &win->s->c[SWM_S_COLOR_UNFOCUS_MAXIMIZED].pixel : &win->s->c[SWM_S_COLOR_UNFOCUS].pixel; xcb_change_window_attributes(conn, win->id, XCB_CW_BORDER_PIXEL, pixel); } void update_window(struct ws_win *win) { uint16_t mask; uint32_t wc[5]; mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_BORDER_WIDTH; wc[0] = X(win); wc[1] = Y(win); wc[2] = WIDTH(win); wc[3] = HEIGHT(win); wc[4] = BORDER(win); DNPRINTF(SWM_D_EVENT, "update_window: win %#x, (x,y) w x h: " "(%d,%d) %d x %d, bordered: %s\n", win->id, wc[0], wc[1], wc[2], wc[3], YESNO(win->bordered)); xcb_configure_window(conn, win->id, mask, wc); } #define SWM_RESIZE_STEPS (50) void resize(struct ws_win *win, union arg *args) { xcb_timestamp_t timestamp = 0; struct swm_region *r = NULL; struct swm_geometry g; int resize_stp = 0; int top = 0, left = 0; int dx, dy; xcb_cursor_t cursor; xcb_query_pointer_reply_t *xpr = NULL; xcb_generic_event_t *evt; xcb_motion_notify_event_t *mne; bool resizing; if (win == NULL) return; r = win->ws->r; if (FULLSCREEN(win)) return; /* In max_stack mode, should only resize transients. */ if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !TRANS(win)) return; DNPRINTF(SWM_D_EVENT, "resize: win %#x, floating: %s, " "transient: %#x\n", win->id, YESNO(ABOVE(win)), win->transient); if (MAXIMIZED(win)) store_float_geom(win); else if (!(TRANS(win) || ABOVE(win))) return; ewmh_apply_flags(win, (win->ewmh_flags | SWM_F_MANUAL | EWMH_F_ABOVE) & ~EWMH_F_MAXIMIZED); ewmh_update_wm_state(win); stack(); focus_flush(); /* It's possible for win to have been freed during focus_flush(). */ if (validate_win(win)) { DNPRINTF(SWM_D_EVENT, "resize: invalid win.\n"); goto out; } switch (args->id) { case SWM_ARG_ID_WIDTHSHRINK: WIDTH(win) -= SWM_RESIZE_STEPS; resize_stp = 1; break; case SWM_ARG_ID_WIDTHGROW: WIDTH(win) += SWM_RESIZE_STEPS; resize_stp = 1; break; case SWM_ARG_ID_HEIGHTSHRINK: HEIGHT(win) -= SWM_RESIZE_STEPS; resize_stp = 1; break; case SWM_ARG_ID_HEIGHTGROW: HEIGHT(win) += SWM_RESIZE_STEPS; resize_stp = 1; break; default: break; } if (resize_stp) { region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_RESIZABLE | SWM_CW_HARDBOUNDARY); update_window(win); store_float_geom(win); return; } region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_RESIZABLE | SWM_CW_SOFTBOUNDARY); update_window(win); /* get cursor offset from window root */ xpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, win->id), NULL); if (xpr == NULL) return; g = win->g; if (xpr->win_x < WIDTH(win) / 2) left = 1; if (xpr->win_y < HEIGHT(win) / 2) top = 1; if (args->id == SWM_ARG_ID_CENTER) cursor = cursors[XC_SIZING].cid; else if (top) cursor = cursors[left ? XC_TOP_LEFT_CORNER : XC_TOP_RIGHT_CORNER].cid; else cursor = cursors[left ? XC_BOTTOM_LEFT_CORNER : XC_BOTTOM_RIGHT_CORNER].cid; xcb_grab_pointer(conn, 0, win->id, MOUSEMASK, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, cursor, XCB_CURRENT_TIME), xcb_flush(conn); resizing = true; while (resizing && (evt = xcb_wait_for_event(conn))) { switch (XCB_EVENT_RESPONSE_TYPE(evt)) { case XCB_BUTTON_RELEASE: DNPRINTF(SWM_D_EVENT, "resize: BUTTON_RELEASE\n"); resizing = false; break; case XCB_MOTION_NOTIFY: mne = (xcb_motion_notify_event_t *)evt; /* cursor offset/delta from start of the operation */ dx = mne->root_x - xpr->root_x; dy = mne->root_y - xpr->root_y; /* vertical */ if (top) dy = -dy; if (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; } /* not free, don't sync more than 120 times / second */ if ((mne->time - timestamp) > (1000 / 120) ) { timestamp = mne->time; regionize(win, mne->root_x, mne->root_y); region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_RESIZABLE | SWM_CW_HARDBOUNDARY | SWM_CW_SOFTBOUNDARY); update_window(win); xcb_flush(conn); } break; case XCB_KEY_PRESS: /* Ignore. */ xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, ((xcb_key_press_event_t *)evt)->time); xcb_flush(conn); break; default: event_handle(evt); /* It's possible for win to have been freed above. */ if (validate_win(win)) { DNPRINTF(SWM_D_EVENT, "resize: invalid win.\n"); goto out; } break; } free(evt); } if (timestamp) { region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_RESIZABLE | SWM_CW_HARDBOUNDARY | SWM_CW_SOFTBOUNDARY); update_window(win); xcb_flush(conn); } store_float_geom(win); out: xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); free(xpr); DNPRINTF(SWM_D_EVENT, "resize: done.\n"); } void resize_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); focus_flush(); } /* Try to set window region based on supplied coordinates or window center. */ void regionize(struct ws_win *win, int x, int y) { struct swm_region *r = NULL; r = region_under(win->s, x, y); if (r == NULL) r = region_under(win->s, X(win) + WIDTH(win) / 2, Y(win) + HEIGHT(win) / 2); if (r != NULL && r != win->ws->r) { if (clear_maximized(r->ws) > 0) stack(); win_to_ws(win, r->ws->idx, false); /* Set focus on new ws. */ unfocus_win(r->ws->focus); r->ws->focus = win; set_region(r); raise_window(win); } } #define SWM_MOVE_STEPS (50) void move(struct ws_win *win, union arg *args) { struct swm_region *r; xcb_timestamp_t timestamp = 0; int move_stp = 0; xcb_query_pointer_reply_t *qpr = NULL; xcb_generic_event_t *evt; xcb_motion_notify_event_t *mne; bool moving, restack = false; if (win == NULL) return; if ((r = win->ws->r) == NULL) return; if (FULLSCREEN(win)) return; DNPRINTF(SWM_D_EVENT, "move: win %#x, floating: %s, transient: " "%#x\n", win->id, YESNO(ABOVE(win)), win->transient); /* in max_stack mode should only move transients */ if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !TRANS(win)) return; if (!(ABOVE(win) || TRANS(win)) || MAXIMIZED(win)) { store_float_geom(win); restack = true; } ewmh_apply_flags(win, (win->ewmh_flags | SWM_F_MANUAL | EWMH_F_ABOVE) & ~EWMH_F_MAXIMIZED); ewmh_update_wm_state(win); if (restack) stack(); focus_flush(); /* It's possible for win to have been freed during focus_flush(). */ if (validate_win(win)) { DNPRINTF(SWM_D_EVENT, "move: invalid win.\n"); goto out; } move_stp = 0; switch (args->id) { case SWM_ARG_ID_MOVELEFT: X(win) -= (SWM_MOVE_STEPS - border_width); move_stp = 1; break; case SWM_ARG_ID_MOVERIGHT: X(win) += (SWM_MOVE_STEPS - border_width); move_stp = 1; break; case SWM_ARG_ID_MOVEUP: Y(win) -= (SWM_MOVE_STEPS - border_width); move_stp = 1; break; case SWM_ARG_ID_MOVEDOWN: Y(win) += (SWM_MOVE_STEPS - border_width); move_stp = 1; break; default: break; } if (move_stp) { regionize(win, -1, -1); region_containment(win, win->ws->r, SWM_CW_ALLSIDES); update_window(win); store_float_geom(win); return; } xcb_grab_pointer(conn, 0, win->id, MOUSEMASK, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, cursors[XC_FLEUR].cid, XCB_CURRENT_TIME); /* get cursor offset from window root */ qpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, win->id), NULL); if (qpr == NULL) { xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); return; } regionize(win, qpr->root_x, qpr->root_y); region_containment(win, win->ws->r, SWM_CW_ALLSIDES | SWM_CW_SOFTBOUNDARY); update_window(win); xcb_flush(conn); moving = true; while (moving && (evt = xcb_wait_for_event(conn))) { switch (XCB_EVENT_RESPONSE_TYPE(evt)) { case XCB_BUTTON_RELEASE: DNPRINTF(SWM_D_EVENT, "move: BUTTON_RELEASE\n"); moving = false; break; case XCB_MOTION_NOTIFY: mne = (xcb_motion_notify_event_t *)evt; DNPRINTF(SWM_D_EVENT, "motion: root: %#x\n", mne->root); X(win) = mne->root_x - qpr->win_x - border_width; Y(win) = mne->root_y - qpr->win_y - border_width; /* not free, don't sync more than 120 times / second */ if ((mne->time - timestamp) > (1000 / 120) ) { timestamp = mne->time; regionize(win, mne->root_x, mne->root_y); region_containment(win, win->ws->r, SWM_CW_ALLSIDES | SWM_CW_SOFTBOUNDARY); update_window(win); xcb_flush(conn); } break; case XCB_KEY_PRESS: /* Ignore. */ xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, ((xcb_key_press_event_t *)evt)->time); xcb_flush(conn); break; default: event_handle(evt); /* It's possible for win to have been freed above. */ if (validate_win(win)) { DNPRINTF(SWM_D_EVENT, "move: invalid win.\n"); goto out; } break; } free(evt); } if (timestamp) { region_containment(win, win->ws->r, SWM_CW_ALLSIDES | SWM_CW_SOFTBOUNDARY); update_window(win); xcb_flush(conn); } store_float_geom(win); /* New region set to fullscreen layout. */ if (win->ws->cur_layout == &layouts[SWM_MAX_STACK]) { stack(); focus_flush(); } out: free(qpr); xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); DNPRINTF(SWM_D_EVENT, "move: done.\n"); } 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 (!TRANS(win) && !ABOVE(win)) return; move(win, args); focus_flush(); } /* key definitions */ 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 */ { "bar_toggle", bar_toggle, {.id = SWM_ARG_ID_BAR_TOGGLE} }, { "bar_toggle_ws", bar_toggle, {.id = SWM_ARG_ID_BAR_TOGGLE_WS} }, { "button2", pressbutton, {2} }, { "cycle_layout", cycle_layout, {0} }, { "flip_layout", stack_config, {.id = SWM_ARG_ID_FLIPLAYOUT} }, { "float_toggle", floating_toggle,{0} }, { "focus_main", focus, {.id = SWM_ARG_ID_FOCUSMAIN} }, { "focus_next", focus, {.id = SWM_ARG_ID_FOCUSNEXT} }, { "focus_prev", focus, {.id = SWM_ARG_ID_FOCUSPREV} }, { "focus_urgent", focus, {.id = SWM_ARG_ID_FOCUSURGENT} }, { "maximize_toggle", maximize_toggle,{0} }, { "height_grow", resize_step, {.id = SWM_ARG_ID_HEIGHTGROW} }, { "height_shrink", resize_step, {.id = SWM_ARG_ID_HEIGHTSHRINK} }, { "iconify", iconify, {0} }, { "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} }, { "move_down", move_step, {.id = SWM_ARG_ID_MOVEDOWN} }, { "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} }, { "mvrg_1", send_to_rg, {.id = 0} }, { "mvrg_2", send_to_rg, {.id = 1} }, { "mvrg_3", send_to_rg, {.id = 2} }, { "mvrg_4", send_to_rg, {.id = 3} }, { "mvrg_5", send_to_rg, {.id = 4} }, { "mvrg_6", send_to_rg, {.id = 5} }, { "mvrg_7", send_to_rg, {.id = 6} }, { "mvrg_8", send_to_rg, {.id = 7} }, { "mvrg_9", send_to_rg, {.id = 8} }, { "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} }, { "mvws_11", send_to_ws, {.id = 10} }, { "mvws_12", send_to_ws, {.id = 11} }, { "mvws_13", send_to_ws, {.id = 12} }, { "mvws_14", send_to_ws, {.id = 13} }, { "mvws_15", send_to_ws, {.id = 14} }, { "mvws_16", send_to_ws, {.id = 15} }, { "mvws_17", send_to_ws, {.id = 16} }, { "mvws_18", send_to_ws, {.id = 17} }, { "mvws_19", send_to_ws, {.id = 18} }, { "mvws_20", send_to_ws, {.id = 19} }, { "mvws_21", send_to_ws, {.id = 20} }, { "mvws_22", send_to_ws, {.id = 21} }, { "name_workspace", name_workspace, {0} }, { "quit", quit, {0} }, { "raise_toggle", raise_toggle, {0} }, { "restart", restart, {0} }, { "rg_1", focusrg, {.id = 0} }, { "rg_2", focusrg, {.id = 1} }, { "rg_3", focusrg, {.id = 2} }, { "rg_4", focusrg, {.id = 3} }, { "rg_5", focusrg, {.id = 4} }, { "rg_6", focusrg, {.id = 5} }, { "rg_7", focusrg, {.id = 6} }, { "rg_8", focusrg, {.id = 7} }, { "rg_9", focusrg, {.id = 8} }, { "rg_next", cyclerg, {.id = SWM_ARG_ID_CYCLERG_UP} }, { "rg_prev", cyclerg, {.id = SWM_ARG_ID_CYCLERG_DOWN} }, { "screen_next", cyclerg, {.id = SWM_ARG_ID_CYCLERG_UP} }, { "screen_prev", cyclerg, {.id = SWM_ARG_ID_CYCLERG_DOWN} }, { "search_win", search_win, {0} }, { "search_workspace", search_workspace, {0} }, { "spawn_custom", NULL, {0} }, { "stack_inc", stack_config, {.id = SWM_ARG_ID_STACKINC} }, { "stack_dec", stack_config, {.id = SWM_ARG_ID_STACKDEC} }, { "stack_reset", stack_config, {.id = SWM_ARG_ID_STACKRESET} }, { "swap_main", swapwin, {.id = SWM_ARG_ID_SWAPMAIN} }, { "swap_next", swapwin, {.id = SWM_ARG_ID_SWAPNEXT} }, { "swap_prev", swapwin, {.id = SWM_ARG_ID_SWAPPREV} }, { "uniconify", uniconify, {0} }, { "version", version, {0} }, { "width_grow", resize_step, {.id = SWM_ARG_ID_WIDTHGROW} }, { "width_shrink", resize_step, {.id = SWM_ARG_ID_WIDTHSHRINK} }, { "wind_del", wkill, {.id = SWM_ARG_ID_DELETEWINDOW} }, { "wind_kill", wkill, {.id = SWM_ARG_ID_KILLWINDOW} }, { "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_11", switchws, {.id = 10} }, { "ws_12", switchws, {.id = 11} }, { "ws_13", switchws, {.id = 12} }, { "ws_14", switchws, {.id = 13} }, { "ws_15", switchws, {.id = 14} }, { "ws_16", switchws, {.id = 15} }, { "ws_17", switchws, {.id = 16} }, { "ws_18", switchws, {.id = 17} }, { "ws_19", switchws, {.id = 18} }, { "ws_20", switchws, {.id = 19} }, { "ws_21", switchws, {.id = 20} }, { "ws_22", switchws, {.id = 21} }, { "ws_next", cyclews, {.id = SWM_ARG_ID_CYCLEWS_UP} }, { "ws_next_all", cyclews, {.id = SWM_ARG_ID_CYCLEWS_UP_ALL} }, { "ws_next_move", cyclews, {.id = SWM_ARG_ID_CYCLEWS_MOVE_UP} }, { "ws_prev", cyclews, {.id = SWM_ARG_ID_CYCLEWS_DOWN} }, { "ws_prev_all", cyclews, {.id = SWM_ARG_ID_CYCLEWS_DOWN_ALL} }, { "ws_prev_move", cyclews, {.id = SWM_ARG_ID_CYCLEWS_MOVE_DOWN} }, { "ws_prior", priorws, {0} }, { "dumpwins", dumpwins, {0} }, /* MUST BE LAST */ { "invalid key func", NULL, {0} }, }; 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); } /* 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[] = { #define MODKEY_SHIFT MODKEY | XCB_MOD_MASK_SHIFT /* action key mouse button func args */ { client_click, MODKEY, XCB_BUTTON_INDEX_3, resize, {.id = SWM_ARG_ID_DONTCENTER} }, { client_click, MODKEY_SHIFT, XCB_BUTTON_INDEX_3, resize, {.id = SWM_ARG_ID_CENTER} }, { client_click, MODKEY, XCB_BUTTON_INDEX_1, move, {0} }, #undef MODKEY_SHIFT }; void update_modkey(unsigned int mod) { int i; struct key *kp; mod_key = mod; RB_FOREACH(kp, key_tree, &keys) if (kp->mod & XCB_MOD_MASK_SHIFT) kp->mod = mod | XCB_MOD_MASK_SHIFT; else kp->mod = mod; for (i = 0; i < LENGTH(buttons); i++) if (buttons[i].mask & XCB_MOD_MASK_SHIFT) buttons[i].mask = mod | XCB_MOD_MASK_SHIFT; else buttons[i].mask = mod; } int spawn_expand(struct swm_region *r, union arg *args, const char *spawn_name, char ***ret_args) { struct spawn_prog *prog = NULL; int i, c; char *ap, **real_args; /* suppress unused warning since var is needed */ (void)args; DNPRINTF(SWM_D_SPAWN, "spawn_expand: %s\n", spawn_name); /* find program */ TAILQ_FOREACH(prog, &spawns, entry) { if (strcasecmp(spawn_name, prog->name) == 0) break; } if (prog == NULL) { warnx("spawn_custom: program %s not found", spawn_name); return (-1); } /* make room for expanded args */ if ((real_args = calloc(prog->argc + 1, sizeof(char *))) == NULL) err(1, "spawn_custom: calloc real_args"); /* expand spawn_args into real_args */ for (i = c = 0; i < prog->argc; i++) { ap = prog->argv[i]; DNPRINTF(SWM_D_SPAWN, "spawn_custom: raw arg: %s\n", ap); if (strcasecmp(ap, "$bar_border") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_BAR_BORDER].name)) == NULL) err(1, "spawn_custom border color"); } else if (strcasecmp(ap, "$bar_color") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_BAR].name)) == NULL) err(1, "spawn_custom bar color"); } else if (strcasecmp(ap, "$bar_font") == 0) { if ((real_args[c] = strdup(bar_fonts)) == NULL) err(1, "spawn_custom bar fonts"); } else if (strcasecmp(ap, "$bar_font_color") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_BAR_FONT].name)) == NULL) err(1, "spawn_custom color font"); } else if (strcasecmp(ap, "$color_focus") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_FOCUS].name)) == NULL) err(1, "spawn_custom color focus"); } else if (strcasecmp(ap, "$color_focus_maximized") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_FOCUS_MAXIMIZED].name)) == NULL) err(1, "spawn_custom color focus maximized"); } else if (strcasecmp(ap, "$color_unfocus") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_UNFOCUS].name)) == NULL) err(1, "spawn_custom color unfocus"); } else if (strcasecmp(ap, "$color_unfocus_maximized") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_UNFOCUS_MAXIMIZED].name)) == NULL) err(1, "spawn_custom color unfocus maximized"); } else if (strcasecmp(ap, "$region_index") == 0) { if (asprintf(&real_args[c], "%d", get_region_index(r) + 1) < 1) err(1, "spawn_custom region index"); } else if (strcasecmp(ap, "$workspace_index") == 0) { if (asprintf(&real_args[c], "%d", r->ws->idx + 1) < 1) err(1, "spawn_custom workspace index"); } else if (strcasecmp(ap, "$dmenu_bottom") == 0) { if (!bar_at_bottom) continue; if ((real_args[c] = strdup("-b")) == NULL) err(1, "spawn_custom workspace index"); } else { /* no match --> copy as is */ if ((real_args[c] = strdup(ap)) == NULL) err(1, "spawn_custom strdup(ap)"); } DNPRINTF(SWM_D_SPAWN, "spawn_custom: cooked arg: %s\n", real_args[c]); ++c; } #ifdef SWM_DEBUG DNPRINTF(SWM_D_SPAWN, "spawn_custom: result: "); for (i = 0; i < c; ++i) DPRINTF("\"%s\" ", real_args[i]); DPRINTF("\n"); #endif *ret_args = real_args; return (c); } void spawn_custom(struct swm_region *r, union arg *args, const char *spawn_name) { union arg a; char **real_args; int spawn_argc, i; if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0) return; a.argv = real_args; if (fork() == 0) spawn(r->ws->idx, &a, true); for (i = 0; i < spawn_argc; i++) free(real_args[i]); free(real_args); } void spawn_select(struct swm_region *r, union arg *args, const char *spawn_name, int *pid) { union arg a; char **real_args; int i, spawn_argc; if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0) return; a.argv = real_args; if (pipe(select_list_pipe) == -1) err(1, "pipe error"); if (pipe(select_resp_pipe) == -1) err(1, "pipe error"); if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) err(1, "could not disable SIGPIPE"); switch (*pid = fork()) { case -1: err(1, "cannot fork"); break; case 0: /* child */ if (dup2(select_list_pipe[0], STDIN_FILENO) == -1) err(1, "dup2"); if (dup2(select_resp_pipe[1], STDOUT_FILENO) == -1) err(1, "dup2"); close(select_list_pipe[1]); close(select_resp_pipe[0]); spawn(r->ws->idx, &a, false); break; default: /* parent */ close(select_list_pipe[0]); close(select_resp_pipe[1]); break; } for (i = 0; i < spawn_argc; i++) free(real_args[i]); free(real_args); } /* Argument tokenizer. */ char * argsep(char **sp) { char *arg, *cp, *next; bool single_quoted = false, double_quoted = false; if (*sp == NULL) return NULL; /* Eat and move characters until end of argument is found. */ for (arg = next = cp = *sp; *cp != '\0'; ++cp) { if (!double_quoted && *cp == '\'') { /* Eat single-quote. */ single_quoted = !single_quoted; } else if (!single_quoted && *cp == '"') { /* Eat double-quote. */ double_quoted = !double_quoted; } else if (!single_quoted && *cp == '\\' && *(cp + 1) == '"') { /* Eat backslash; copy escaped character to arg. */ *next++ = *(++cp); } else if (!single_quoted && !double_quoted && *cp == '\\' && (*(cp + 1) == '\'' || *(cp + 1) == ' ')) { /* Eat backslash; move escaped character. */ *next++ = *(++cp); } else if (!single_quoted && !double_quoted && (*cp == ' ' || *cp == '\t')) { /* Terminate argument. */ *next++ = '\0'; /* Point sp to beginning of next argument. */ *sp = ++cp; break; } else { /* Move regular character. */ *next++ = *cp; } } /* Terminate argument if end of string. */ if (*cp == '\0') { *next = '\0'; *sp = NULL; } return arg; } void spawn_insert(const char *name, const char *args, int flags) { struct spawn_prog *sp; char *arg, *cp, *ptr; DNPRINTF(SWM_D_SPAWN, "spawn_insert: %s[%s]\n", name, args); if (args == NULL || *args == '\0') return; if ((sp = calloc(1, sizeof *sp)) == NULL) err(1, "spawn_insert: calloc"); if ((sp->name = strdup(name)) == NULL) err(1, "spawn_insert: strdup"); /* Convert the arguments to an argument list. */ if ((ptr = cp = strdup(args)) == NULL) err(1, "spawn_insert: strdup"); while ((arg = argsep(&ptr)) != NULL) { /* Null argument; skip it. */ if (*arg == '\0') continue; sp->argc++; if ((sp->argv = realloc(sp->argv, sp->argc * sizeof *sp->argv)) == NULL) err(1, "spawn_insert: realloc"); if ((sp->argv[sp->argc - 1] = strdup(arg)) == NULL) err(1, "spawn_insert: strdup"); } free(cp); sp->flags = flags; DNPRINTF(SWM_D_SPAWN, "arg %d: [%s]\n", sp->argc, sp->argv[sp->argc-1]); TAILQ_INSERT_TAIL(&spawns, sp, entry); DNPRINTF(SWM_D_SPAWN, "spawn_insert: leave\n"); } void spawn_remove(struct spawn_prog *sp) { int i; DNPRINTF(SWM_D_SPAWN, "spawn_remove: %s\n", sp->name); TAILQ_REMOVE(&spawns, sp, entry); for (i = 0; i < sp->argc; i++) free(sp->argv[i]); free(sp->argv); free(sp->name); free(sp); DNPRINTF(SWM_D_SPAWN, "spawn_remove: leave\n"); } struct spawn_prog* spawn_find(const char *name) { struct spawn_prog *sp; TAILQ_FOREACH(sp, &spawns, entry) if (strcasecmp(sp->name, name) == 0) return sp; return NULL; } void setspawn(const char *name, const char *args, int flags) { struct spawn_prog *sp; DNPRINTF(SWM_D_SPAWN, "setspawn: %s\n", name); if (name == NULL) return; /* Remove any old spawn under the same name. */ if ((sp = spawn_find(name)) != NULL) spawn_remove(sp); if (*args != '\0') spawn_insert(name, args, flags); else warnx("error: setspawn: cannot find program: %s", name); DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n"); } int setconfspawn(const char *selector, const char *value, int flags) { char *args; if (selector == NULL || strlen(selector) == 0) return (1); args = expand_tilde(value); DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, args); setspawn(selector, args, flags); free(args); DNPRINTF(SWM_D_SPAWN, "setconfspawn: done.\n"); return (0); } void validate_spawns(void) { struct spawn_prog *sp; char which[PATH_MAX]; size_t i; struct key *kp; RB_FOREACH(kp, key_tree, &keys) { if (kp->funcid != KF_SPAWN_CUSTOM) continue; /* find program */ sp = spawn_find(kp->spawn_name); if (sp == NULL || sp->flags & SWM_SPAWN_OPTIONAL) continue; /* verify we have the goods */ snprintf(which, sizeof which, "which %s", sp->argv[0]); DNPRINTF(SWM_D_CONF, "validate_spawns: which %s\n", sp->argv[0]); for (i = strlen("which "); i < strlen(which); i++) if (which[i] == ' ') { which[i] = '\0'; break; } if (system(which) != 0) add_startup_exception("could not find %s", &which[strlen("which ")]); } } void setup_spawn(void) { setconfspawn("lock", "xlock", 0); setconfspawn("term", "xterm", 0); setconfspawn("spawn_term", "xterm", 0); setconfspawn("menu", "dmenu_run" " $dmenu_bottom" " -fn $bar_font" " -nb $bar_color" " -nf $bar_font_color" " -sb $bar_border" " -sf $bar_color", 0); setconfspawn("search", "dmenu" " $dmenu_bottom" " -i" " -fn $bar_font" " -nb $bar_color" " -nf $bar_font_color" " -sb $bar_border" " -sf $bar_color", 0); setconfspawn("name_workspace", "dmenu" " $dmenu_bottom" " -p Workspace" " -fn $bar_font" " -nb $bar_color" " -nf $bar_font_color" " -sb $bar_border" " -sf $bar_color", 0); /* These are not verified for existence, even with a binding set. */ setconfspawn("screenshot_all", "screenshot.sh full", SWM_SPAWN_OPTIONAL); setconfspawn("screenshot_wind", "screenshot.sh window", SWM_SPAWN_OPTIONAL); setconfspawn("initscr", "initscreen.sh", SWM_SPAWN_OPTIONAL); } /* key bindings */ #define SWM_MODNAME_SIZE 32 #define SWM_KEY_WS "\n+ \t" int parsekeys(const char *keystr, unsigned int currmod, unsigned int *mod, KeySym *ks) { char *str, *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); } if ((cp = str = strdup(keystr)) == NULL) err(1, "parsekeys: strdup"); *ks = XCB_NO_SYMBOL; *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) == 0) *mod |= XCB_MOD_MASK_1; else if (strncasecmp(name, "Mod2", SWM_MODNAME_SIZE) == 0) *mod += XCB_MOD_MASK_2; else if (strncmp(name, "Mod3", SWM_MODNAME_SIZE) == 0) *mod |= XCB_MOD_MASK_3; else if (strncmp(name, "Mod4", SWM_MODNAME_SIZE) == 0) *mod |= XCB_MOD_MASK_4; else if (strncasecmp(name, "SHIFT", SWM_MODNAME_SIZE) == 0) *mod |= XCB_MOD_MASK_SHIFT; else if (strncasecmp(name, "CONTROL", SWM_MODNAME_SIZE) == 0) *mod |= XCB_MOD_MASK_CONTROL; else { *ks = XStringToKeysym(name); XConvertCase(*ks, ks, &uks); if (ks == XCB_NO_SYMBOL) { DNPRINTF(SWM_D_KEY, "parsekeys: invalid key %s\n", name); free(str); return (1); } } } free(str); DNPRINTF(SWM_D_KEY, "parsekeys: leave ok\n"); return (0); } char * strdupsafe(const char *str) { if (str == NULL) return (NULL); else return (strdup(str)); } void key_insert(unsigned int mod, KeySym ks, enum keyfuncid kfid, const 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_tree, &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_tree, &keys, &kp)); } void key_remove(struct key *kp) { DNPRINTF(SWM_D_KEY, "key_remove: %s\n", keyfuncs[kp->funcid].name); RB_REMOVE(key_tree, &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, const 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, const 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("bind: Key combination already unbound."); DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); return; } key_insert(mod, ks, kfid, spawn_name); DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); } int setconfbinding(const char *selector, const char *value, int flags) { enum keyfuncid kfid; unsigned int mod; KeySym ks; struct spawn_prog *sp; /* suppress unused warning since var is needed */ (void)flags; DNPRINTF(SWM_D_KEY, "setconfbinding: enter selector: [%s], " "value: [%s]\n", selector, value); if (selector == NULL || strlen(selector) == 0) { DNPRINTF(SWM_D_KEY, "setconfbinding: unbind %s\n", value); if (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 " "keyfunc\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 */ if ((sp = spawn_find(selector)) != NULL) { DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match " "spawn\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) { #define MODKEY_SHIFT MODKEY | XCB_MOD_MASK_SHIFT setkeybinding(MODKEY, XK_b, KF_BAR_TOGGLE, NULL); setkeybinding(MODKEY_SHIFT, XK_b, KF_BAR_TOGGLE_WS,NULL); setkeybinding(MODKEY, XK_v, KF_BUTTON2, NULL); setkeybinding(MODKEY, XK_space, KF_CYCLE_LAYOUT,NULL); setkeybinding(MODKEY_SHIFT, XK_backslash, KF_FLIP_LAYOUT, NULL); setkeybinding(MODKEY, XK_t, KF_FLOAT_TOGGLE,NULL); setkeybinding(MODKEY, XK_m, KF_FOCUS_MAIN, NULL); setkeybinding(MODKEY, XK_j, KF_FOCUS_NEXT, NULL); setkeybinding(MODKEY, XK_Tab, KF_FOCUS_NEXT, NULL); setkeybinding(MODKEY, XK_k, KF_FOCUS_PREV, NULL); setkeybinding(MODKEY_SHIFT, XK_Tab, KF_FOCUS_PREV, NULL); setkeybinding(MODKEY, XK_u, KF_FOCUS_URGENT,NULL); setkeybinding(MODKEY, XK_e, KF_MAXIMIZE_TOGGLE,NULL); setkeybinding(MODKEY_SHIFT, XK_equal, KF_HEIGHT_GROW,NULL); setkeybinding(MODKEY_SHIFT, XK_minus, KF_HEIGHT_SHRINK,NULL); setkeybinding(MODKEY, XK_w, KF_ICONIFY, 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_SHIFT, XK_bracketright,KF_MOVE_DOWN,NULL); setkeybinding(MODKEY, XK_bracketleft, KF_MOVE_LEFT,NULL); setkeybinding(MODKEY, XK_bracketright,KF_MOVE_RIGHT,NULL); setkeybinding(MODKEY_SHIFT, XK_bracketleft, KF_MOVE_UP, NULL); setkeybinding(MODKEY_SHIFT, XK_KP_End, KF_MVRG_1, NULL); setkeybinding(MODKEY_SHIFT, XK_KP_Down, KF_MVRG_2, NULL); setkeybinding(MODKEY_SHIFT, XK_KP_Next, KF_MVRG_3, NULL); setkeybinding(MODKEY_SHIFT, XK_KP_Left, KF_MVRG_4, NULL); setkeybinding(MODKEY_SHIFT, XK_KP_Begin, KF_MVRG_5, NULL); setkeybinding(MODKEY_SHIFT, XK_KP_Right, KF_MVRG_6, NULL); setkeybinding(MODKEY_SHIFT, XK_KP_Home, KF_MVRG_7, NULL); setkeybinding(MODKEY_SHIFT, XK_KP_Up, KF_MVRG_8, NULL); setkeybinding(MODKEY_SHIFT, XK_KP_Prior, KF_MVRG_9, NULL); setkeybinding(MODKEY_SHIFT, XK_1, KF_MVWS_1, NULL); setkeybinding(MODKEY_SHIFT, XK_2, KF_MVWS_2, NULL); setkeybinding(MODKEY_SHIFT, XK_3, KF_MVWS_3, NULL); setkeybinding(MODKEY_SHIFT, XK_4, KF_MVWS_4, NULL); setkeybinding(MODKEY_SHIFT, XK_5, KF_MVWS_5, NULL); setkeybinding(MODKEY_SHIFT, XK_6, KF_MVWS_6, NULL); setkeybinding(MODKEY_SHIFT, XK_7, KF_MVWS_7, NULL); setkeybinding(MODKEY_SHIFT, XK_8, KF_MVWS_8, NULL); setkeybinding(MODKEY_SHIFT, XK_9, KF_MVWS_9, NULL); setkeybinding(MODKEY_SHIFT, XK_0, KF_MVWS_10, NULL); setkeybinding(MODKEY_SHIFT, XK_F1, KF_MVWS_11, NULL); setkeybinding(MODKEY_SHIFT, XK_F2, KF_MVWS_12, NULL); setkeybinding(MODKEY_SHIFT, XK_F3, KF_MVWS_13, NULL); setkeybinding(MODKEY_SHIFT, XK_F4, KF_MVWS_14, NULL); setkeybinding(MODKEY_SHIFT, XK_F5, KF_MVWS_15, NULL); setkeybinding(MODKEY_SHIFT, XK_F6, KF_MVWS_16, NULL); setkeybinding(MODKEY_SHIFT, XK_F7, KF_MVWS_17, NULL); setkeybinding(MODKEY_SHIFT, XK_F8, KF_MVWS_18, NULL); setkeybinding(MODKEY_SHIFT, XK_F9, KF_MVWS_19, NULL); setkeybinding(MODKEY_SHIFT, XK_F10, KF_MVWS_20, NULL); setkeybinding(MODKEY_SHIFT, XK_F11, KF_MVWS_21, NULL); setkeybinding(MODKEY_SHIFT, XK_F12, KF_MVWS_22, NULL); setkeybinding(MODKEY_SHIFT, XK_slash, KF_NAME_WORKSPACE,NULL); setkeybinding(MODKEY_SHIFT, XK_q, KF_QUIT, NULL); setkeybinding(MODKEY_SHIFT, XK_r, KF_RAISE_TOGGLE,NULL); setkeybinding(MODKEY, XK_q, KF_RESTART, NULL); setkeybinding(MODKEY, XK_KP_End, KF_RG_1, NULL); setkeybinding(MODKEY, XK_KP_Down, KF_RG_2, NULL); setkeybinding(MODKEY, XK_KP_Next, KF_RG_3, NULL); setkeybinding(MODKEY, XK_KP_Left, KF_RG_4, NULL); setkeybinding(MODKEY, XK_KP_Begin, KF_RG_5, NULL); setkeybinding(MODKEY, XK_KP_Right, KF_RG_6, NULL); setkeybinding(MODKEY, XK_KP_Home, KF_RG_7, NULL); setkeybinding(MODKEY, XK_KP_Up, KF_RG_8, NULL); setkeybinding(MODKEY, XK_KP_Prior, KF_RG_9, NULL); setkeybinding(MODKEY_SHIFT, XK_Right, KF_RG_NEXT, NULL); setkeybinding(MODKEY_SHIFT, XK_Left, KF_RG_PREV, NULL); setkeybinding(MODKEY, XK_f, KF_SEARCH_WIN, NULL); setkeybinding(MODKEY, XK_slash, KF_SEARCH_WORKSPACE,NULL); setkeybinding(MODKEY_SHIFT, XK_i, KF_SPAWN_CUSTOM,"initscr"); setkeybinding(MODKEY_SHIFT, XK_Delete, KF_SPAWN_CUSTOM,"lock"); setkeybinding(MODKEY, XK_p, KF_SPAWN_CUSTOM,"menu"); setkeybinding(MODKEY, XK_s, KF_SPAWN_CUSTOM,"screenshot_all"); setkeybinding(MODKEY_SHIFT, XK_s, KF_SPAWN_CUSTOM,"screenshot_wind"); setkeybinding(MODKEY_SHIFT, XK_Return, KF_SPAWN_CUSTOM,"term"); setkeybinding(MODKEY_SHIFT, XK_comma, KF_STACK_INC, NULL); setkeybinding(MODKEY_SHIFT, XK_period, KF_STACK_DEC, NULL); setkeybinding(MODKEY_SHIFT, XK_space, KF_STACK_RESET, NULL); setkeybinding(MODKEY, XK_Return, KF_SWAP_MAIN, NULL); setkeybinding(MODKEY_SHIFT, XK_j, KF_SWAP_NEXT, NULL); setkeybinding(MODKEY_SHIFT, XK_k, KF_SWAP_PREV, NULL); setkeybinding(MODKEY_SHIFT, XK_w, KF_UNICONIFY, NULL); setkeybinding(MODKEY_SHIFT, XK_v, KF_VERSION, NULL); setkeybinding(MODKEY, XK_equal, KF_WIDTH_GROW, NULL); setkeybinding(MODKEY, XK_minus, KF_WIDTH_SHRINK,NULL); setkeybinding(MODKEY, XK_x, KF_WIND_DEL, NULL); setkeybinding(MODKEY_SHIFT, XK_x, KF_WIND_KILL, 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_F1, KF_WS_11, NULL); setkeybinding(MODKEY, XK_F2, KF_WS_12, NULL); setkeybinding(MODKEY, XK_F3, KF_WS_13, NULL); setkeybinding(MODKEY, XK_F4, KF_WS_14, NULL); setkeybinding(MODKEY, XK_F5, KF_WS_15, NULL); setkeybinding(MODKEY, XK_F6, KF_WS_16, NULL); setkeybinding(MODKEY, XK_F7, KF_WS_17, NULL); setkeybinding(MODKEY, XK_F8, KF_WS_18, NULL); setkeybinding(MODKEY, XK_F9, KF_WS_19, NULL); setkeybinding(MODKEY, XK_F10, KF_WS_20, NULL); setkeybinding(MODKEY, XK_F11, KF_WS_21, NULL); setkeybinding(MODKEY, XK_F12, KF_WS_22, 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_SHIFT, XK_Up, KF_WS_NEXT_MOVE,NULL); setkeybinding(MODKEY_SHIFT, XK_Down, KF_WS_PREV_MOVE,NULL); setkeybinding(MODKEY, XK_a, KF_WS_PRIOR, NULL); #ifdef SWM_DEBUG setkeybinding(MODKEY_SHIFT, XK_d, KF_DUMPWINS, NULL); #endif #undef MODKEY_SHIFT } void clear_keys(void) { struct key *kp; while (RB_EMPTY(&keys) == 0) { kp = RB_ROOT(&keys); key_remove(kp); } } int setkeymapping(const char *selector, const char *value, int flags) { char *keymapping_file; /* suppress unused warnings since vars are needed */ (void)selector; (void)flags; DNPRINTF(SWM_D_KEY, "setkeymapping: enter\n"); keymapping_file = expand_tilde(value); clear_keys(); /* load new key bindings; if it fails, revert to default bindings */ if (conf_load(keymapping_file, SWM_CONF_KEYMAPPING)) { clear_keys(); setup_keys(); } free(keymapping_file); DNPRINTF(SWM_D_KEY, "setkeymapping: leave\n"); return (0); } void updatenumlockmask(void) { unsigned int i, j; xcb_get_modifier_mapping_reply_t *modmap_r; xcb_keycode_t *modmap, kc, *keycode; numlockmask = 0; modmap_r = xcb_get_modifier_mapping_reply(conn, xcb_get_modifier_mapping(conn), NULL); if (modmap_r) { modmap = xcb_get_modifier_mapping_keycodes(modmap_r); for (i = 0; i < 8; i++) { for (j = 0; j < modmap_r->keycodes_per_modifier; j++) { kc = modmap[i * modmap_r->keycodes_per_modifier + j]; keycode = xcb_key_symbols_get_keycode(syms, XK_Num_Lock); if (keycode) { if (kc == *keycode) numlockmask = (1 << i); free(keycode); } } } free(modmap_r); } DNPRINTF(SWM_D_MISC, "updatenumlockmask: %d\n", numlockmask); } void grabkeys(void) { struct key *kp; int num_screens, k, j; unsigned int modifiers[4]; xcb_keycode_t *code; DNPRINTF(SWM_D_MISC, "grabkeys\n"); updatenumlockmask(); modifiers[0] = 0; modifiers[1] = numlockmask; modifiers[2] = XCB_MOD_MASK_LOCK; modifiers[3] = numlockmask | XCB_MOD_MASK_LOCK; num_screens = get_screen_count(); for (k = 0; k < num_screens; k++) { if (TAILQ_EMPTY(&screens[k].rl)) continue; xcb_ungrab_key(conn, XCB_GRAB_ANY, screens[k].root, XCB_MOD_MASK_ANY); RB_FOREACH(kp, key_tree, &keys) { /* Skip unused ws binds. */ if ((int)kp->funcid > KF_WS_1 + workspace_limit - 1 && kp->funcid <= KF_WS_22) continue; /* Skip unused mvws binds. */ if ((int)kp->funcid > KF_MVWS_1 + workspace_limit - 1 && kp->funcid <= KF_MVWS_22) continue; if ((code = xcb_key_symbols_get_keycode(syms, kp->keysym))) { for (j = 0; j < LENGTH(modifiers); j++) xcb_grab_key(conn, 1, screens[k].root, kp->mod | modifiers[j], *code, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_SYNC); free(code); } } } } void grabbuttons(struct ws_win *win) { unsigned int modifiers[4]; int i, j; DNPRINTF(SWM_D_MOUSE, "grabbuttons: win %#x\n", win->id); updatenumlockmask(); modifiers[0] = 0; modifiers[1] = numlockmask; modifiers[2] = XCB_MOD_MASK_LOCK; modifiers[3] = numlockmask | XCB_MOD_MASK_LOCK; for (i = 0; i < LENGTH(buttons); i++) if (buttons[i].action == client_click) for (j = 0; j < LENGTH(modifiers); ++j) xcb_grab_button(conn, 0, win->id, BUTTONMASK, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, XCB_CURSOR_NONE, buttons[i].button, buttons[i].mask | modifiers[j]); } const char *quirkname[] = { "NONE", /* config string for "no value" */ "FLOAT", "TRANSSZ", "ANYWHERE", "XTERM_FONTADJ", "FULLSCREEN", "FOCUSPREV", "NOFOCUSONMAP", "FOCUSONMAP_SINGLE", "OBEYAPPFOCUSREQ", "IGNOREPID", "IGNORESPAWNWS", }; /* SWM_Q_DELIM: retain '|' for back compat for now (2009-08-11) */ #define SWM_Q_DELIM "\n|+ \t" int parsequirks(const char *qstr, uint32_t *quirk, int *ws) { char *str, *cp, *name; int i; if (quirk == NULL || qstr == NULL) return (1); if ((str = strdup(qstr)) == NULL) err(1, "parsequirks: strdup"); cp = str; *quirk = 0; while ((name = strsep(&cp, SWM_Q_DELIM)) != NULL) { if (cp) cp += (long)strspn(cp, SWM_Q_DELIM); if (sscanf(name, "WS[%d]", ws) == 1) { if (*ws > 0) *ws -= 1; continue; } for (i = 0; i < LENGTH(quirkname); i++) { if (strncasecmp(name, quirkname[i], SWM_QUIRK_LEN) == 0) { DNPRINTF(SWM_D_QUIRK, "parsequirks: %s\n", name); if (i == 0) { *quirk = 0; free(str); return (0); } *quirk |= 1 << (i-1); break; } } if (i >= LENGTH(quirkname)) { DNPRINTF(SWM_D_QUIRK, "parsequirks: invalid quirk [%s]\n", name); free(str); return (1); } } free(str); return (0); } void quirk_insert(const char *class, const char *instance, const char *name, uint32_t quirk, int ws) { struct quirk *qp; char *str; bool failed = false; DNPRINTF(SWM_D_QUIRK, "quirk_insert: class: %s, instance: %s, name: %s," " value: %u, ws: %d\n", class, instance, name, quirk, ws); if ((qp = malloc(sizeof *qp)) == NULL) err(1, "quirk_insert: malloc"); if ((qp->class = strdup(class)) == NULL) err(1, "quirk_insert: strdup"); if ((qp->instance = strdup(instance)) == NULL) err(1, "quirk_insert: strdup"); if ((qp->name = strdup(name)) == NULL) err(1, "quirk_insert: strdup"); if (asprintf(&str, "^%s$", class) == -1) err(1, "quirk_insert: asprintf"); if (regcomp(&qp->regex_class, str, REG_EXTENDED | REG_NOSUB)) { add_startup_exception("regex failed to compile quirk 'class' " "field: %s", class); failed = true; } DNPRINTF(SWM_D_QUIRK, "quirk_insert: compiled: %s\n", str); free(str); if (asprintf(&str, "^%s$", instance) == -1) err(1, "quirk_insert: asprintf"); if (regcomp(&qp->regex_instance, str, REG_EXTENDED | REG_NOSUB)) { add_startup_exception("regex failed to compile quirk 'instance'" " field: %s", instance); failed = true; } DNPRINTF(SWM_D_QUIRK, "quirk_insert: compiled: %s\n", str); free(str); if (asprintf(&str, "^%s$", name) == -1) err(1, "quirk_insert: asprintf"); if (regcomp(&qp->regex_name, str, REG_EXTENDED | REG_NOSUB)) { add_startup_exception("regex failed to compile quirk 'name' " "field: %s", name); failed = true; } DNPRINTF(SWM_D_QUIRK, "quirk_insert: compiled: %s\n", str); free(str); if (failed) { DNPRINTF(SWM_D_QUIRK, "quirk_insert: regex error; skipping.\n"); quirk_free(qp); } else { qp->quirk = quirk; qp->ws = ws; TAILQ_INSERT_TAIL(&quirks, qp, entry); } DNPRINTF(SWM_D_QUIRK, "quirk_insert: leave\n"); } void quirk_remove(struct quirk *qp) { DNPRINTF(SWM_D_QUIRK, "quirk_remove: %s:%s [%u]\n", qp->class, qp->name, qp->quirk); TAILQ_REMOVE(&quirks, qp, entry); quirk_free(qp); DNPRINTF(SWM_D_QUIRK, "quirk_remove: leave\n"); } void quirk_free(struct quirk *qp) { regfree(&qp->regex_class); regfree(&qp->regex_instance); regfree(&qp->regex_name); free(qp->class); free(qp->instance); free(qp->name); free(qp); } void quirk_replace(struct quirk *qp, const char *class, const char *instance, const char *name, uint32_t quirk, int ws) { DNPRINTF(SWM_D_QUIRK, "quirk_replace: %s:%s:%s [%u], ws: %d\n", qp->class, qp->instance, qp->name, qp->quirk, qp->ws); quirk_remove(qp); quirk_insert(class, instance, name, quirk, ws); DNPRINTF(SWM_D_QUIRK, "quirk_replace: leave\n"); } void setquirk(const char *class, const char *instance, const char *name, uint32_t quirk, int ws) { struct quirk *qp; DNPRINTF(SWM_D_QUIRK, "setquirk: enter %s:%s:%s [%u], ws: %d\n", class, instance, name, quirk, ws); /* Remove/replace existing quirk. */ TAILQ_FOREACH(qp, &quirks, entry) { if (strcmp(qp->class, class) == 0 && strcmp(qp->instance, instance) == 0 && strcmp(qp->name, name) == 0) { if (quirk == 0 && ws == -1) quirk_remove(qp); else quirk_replace(qp, class, instance, name, quirk, ws); DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n"); return; } } /* Only insert if quirk is not NONE or forced ws is set. */ if (quirk || ws != -1) quirk_insert(class, instance, name, quirk, ws); DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n"); } /* Eat '\' in str used to escape square brackets and colon. */ void unescape_selector(char *str) { char *cp; for (cp = str; *str != '\0'; ++str, ++cp) { if (*str == '\\' && (*(str + 1) == ':' || *(str + 1) == ']' || *(str + 1) == '[')) ++str; *cp = *str; } *cp = '\0'; } int setconfquirk(const char *selector, const char *value, int flags) { char *str, *cp, *class; char *instance = NULL, *name = NULL; int retval, count = 0, ws = -1; uint32_t qrks; /* suppress unused warning since var is needed */ (void)flags; if (selector == NULL || strlen(selector) == 0) return (0); if ((str = strdup(selector)) == NULL) err(1, "setconfquirk: strdup"); /* Find non-escaped colon. */ class = cp = str; if (*cp == ':') { *cp = '\0'; ++count; } for (++cp; *cp != '\0'; ++cp) { if (*cp == ':' && *(cp - 1) != '\\') { *cp = '\0'; ++count; } } unescape_selector(class); if (count) { instance = class + strlen(class) + 1; unescape_selector(instance); } else { instance = ".*"; } if (count > 1) { name = instance + strlen(instance) + 1; unescape_selector(name); } else { name = ".*"; } DNPRINTF(SWM_D_CONF, "setconfquirk: class: %s, instance: %s, " "name: %s\n", class, instance, name); if ((retval = parsequirks(value, &qrks, &ws)) == 0) setquirk(class, instance, name, qrks, ws); free(str); return (retval); } void setup_quirks(void) { setquirk("MPlayer", "xv", ".*", SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV, -1); setquirk("OpenOffice.org 3.2", "VCLSalFrame", ".*", SWM_Q_FLOAT, -1); setquirk("Firefox-bin", "firefox-bin", ".*", SWM_Q_TRANSSZ, -1); setquirk("Firefox", "Dialog", ".*", SWM_Q_FLOAT, -1); setquirk("Gimp", "gimp", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1); setquirk("XTerm", "xterm", ".*", SWM_Q_XTERM_FONTADJ, -1); setquirk("xine", "Xine Window", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1); setquirk("Xitk", "Xitk Combo", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1); setquirk("xine", "xine Panel", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1); setquirk("Xitk", "Xine Window", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1); setquirk("xine", "xine Video Fullscreen Window", ".*", SWM_Q_FULLSCREEN | SWM_Q_FLOAT, -1); setquirk("pcb", "pcb", ".*", SWM_Q_FLOAT, -1); setquirk("SDL_App", "SDL_App", ".*", SWM_Q_FLOAT | SWM_Q_FULLSCREEN, -1); } /* conf file stuff */ #define SWM_CONF_FILE "spectrwm.conf" #define SWM_CONF_FILE_OLD "scrotwm.conf" enum { SWM_S_BAR_ACTION, SWM_S_BAR_AT_BOTTOM, SWM_S_BAR_BORDER_WIDTH, SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_BAR_ENABLED_WS, SWM_S_BAR_FONT, SWM_S_BAR_FORMAT, SWM_S_BAR_JUSTIFY, SWM_S_BORDER_WIDTH, SWM_S_BOUNDARY_WIDTH, SWM_S_CLOCK_ENABLED, SWM_S_CLOCK_FORMAT, SWM_S_CYCLE_EMPTY, SWM_S_CYCLE_VISIBLE, SWM_S_DIALOG_RATIO, SWM_S_DISABLE_BORDER, SWM_S_FOCUS_CLOSE, SWM_S_FOCUS_CLOSE_WRAP, SWM_S_FOCUS_DEFAULT, SWM_S_FOCUS_MODE, SWM_S_ICONIC_ENABLED, SWM_S_JAVA_WORKAROUND, SWM_S_REGION_PADDING, SWM_S_SPAWN_ORDER, SWM_S_SPAWN_TERM, SWM_S_SS_APP, SWM_S_SS_ENABLED, SWM_S_STACK_ENABLED, SWM_S_TERM_WIDTH, SWM_S_TILE_GAP, SWM_S_URGENT_COLLAPSE, SWM_S_URGENT_ENABLED, SWM_S_VERBOSE_LAYOUT, SWM_S_WARP_POINTER, SWM_S_WINDOW_CLASS_ENABLED, SWM_S_WINDOW_INSTANCE_ENABLED, SWM_S_WINDOW_NAME_ENABLED, SWM_S_WORKSPACE_LIMIT, SWM_S_WORKSPACE_NAME, }; int setconfvalue(const char *selector, const char *value, int flags) { struct workspace *ws; int i, ws_id, num_screens; char *b, *str, s[1024]; switch (flags) { case SWM_S_BAR_ACTION: free(bar_argv[0]); if ((bar_argv[0] = expand_tilde(value)) == NULL) err(1, "setconfvalue: bar_action"); break; case SWM_S_BAR_AT_BOTTOM: bar_at_bottom = (atoi(value) != 0); break; case SWM_S_BAR_BORDER_WIDTH: bar_border_width = atoi(value); if (bar_border_width < 0) bar_border_width = 0; break; case SWM_S_BAR_DELAY: /* No longer needed; leave to not break old conf files. */ break; case SWM_S_BAR_ENABLED: bar_enabled = (atoi(value) != 0); break; case SWM_S_BAR_ENABLED_WS: ws_id = atoi(selector) - 1; if (ws_id < 0 || ws_id >= workspace_limit) errx(1, "setconfvalue: bar_enabled_ws: invalid " "workspace %d.", ws_id + 1); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { ws = (struct workspace *)&screens[i].ws; ws[ws_id].bar_enabled = (atoi(value) != 0); } break; case SWM_S_BAR_FONT: b = bar_fonts; if (asprintf(&bar_fonts, "%s,%s", value, bar_fonts) == -1) err(1, "setconfvalue: asprintf: failed to allocate " "memory for bar_fonts."); free(b); /* If already in xft mode, then we are done. */ if (!bar_font_legacy) break; if ((str = strdup(value)) == NULL) err(1, "setconfvalue: strdup"); /* If there are any non-XLFD entries, switch to Xft mode. */ while ((b = strsep(&str, ",")) != NULL) { if (*b == '\0') continue; if (!isxlfd(b)) { bar_font_legacy = false; break; } } free(str); break; case SWM_S_BAR_FORMAT: free(bar_format); if ((bar_format = strdup(value)) == NULL) err(1, "setconfvalue: bar_format"); break; case SWM_S_BAR_JUSTIFY: if (strcmp(value, "left") == 0) bar_justify = SWM_BAR_JUSTIFY_LEFT; else if (strcmp(value, "center") == 0) bar_justify = SWM_BAR_JUSTIFY_CENTER; else if (strcmp(value, "right") == 0) bar_justify = SWM_BAR_JUSTIFY_RIGHT; else errx(1, "invalid bar_justify"); break; case SWM_S_BORDER_WIDTH: border_width = atoi(value); if (border_width < 0) border_width = 0; break; case SWM_S_BOUNDARY_WIDTH: boundary_width = atoi(value); if (boundary_width < 0) boundary_width = 0; break; case SWM_S_CLOCK_ENABLED: clock_enabled = (atoi(value) != 0); break; case SWM_S_CLOCK_FORMAT: #ifndef SWM_DENY_CLOCK_FORMAT free(clock_format); if ((clock_format = strdup(value)) == NULL) err(1, "setconfvalue: clock_format"); #endif break; case SWM_S_CYCLE_EMPTY: cycle_empty = (atoi(value) != 0); break; case SWM_S_CYCLE_VISIBLE: cycle_visible = (atoi(value) != 0); break; case SWM_S_DIALOG_RATIO: dialog_ratio = atof(value); if (dialog_ratio > 1.0 || dialog_ratio <= .3) dialog_ratio = .6; break; case SWM_S_DISABLE_BORDER: disable_border = (atoi(value) != 0); break; case SWM_S_FOCUS_CLOSE: if (strcmp(value, "first") == 0) focus_close = SWM_STACK_BOTTOM; else if (strcmp(value, "last") == 0) focus_close = SWM_STACK_TOP; else if (strcmp(value, "next") == 0) focus_close = SWM_STACK_ABOVE; else if (strcmp(value, "previous") == 0) focus_close = SWM_STACK_BELOW; else errx(1, "focus_close"); break; case SWM_S_FOCUS_CLOSE_WRAP: focus_close_wrap = (atoi(value) != 0); break; case SWM_S_FOCUS_DEFAULT: if (strcmp(value, "last") == 0) focus_default = SWM_STACK_TOP; else if (strcmp(value, "first") == 0) focus_default = SWM_STACK_BOTTOM; else errx(1, "focus_default"); break; case SWM_S_FOCUS_MODE: if (strcmp(value, "default") == 0) focus_mode = SWM_FOCUS_DEFAULT; else if (strcmp(value, "follow") == 0 || strcmp(value, "follow_cursor") == 0) focus_mode = SWM_FOCUS_FOLLOW; else if (strcmp(value, "manual") == 0) focus_mode = SWM_FOCUS_MANUAL; else errx(1, "focus_mode"); break; case SWM_S_ICONIC_ENABLED: iconic_enabled = (atoi(value) != 0); break; case SWM_S_JAVA_WORKAROUND: java_workaround = (atoi(value) != 0); break; case SWM_S_REGION_PADDING: region_padding = atoi(value); if (region_padding < 0) region_padding = 0; break; case SWM_S_SPAWN_ORDER: if (strcmp(value, "first") == 0) spawn_position = SWM_STACK_BOTTOM; else if (strcmp(value, "last") == 0) spawn_position = SWM_STACK_TOP; else if (strcmp(value, "next") == 0) spawn_position = SWM_STACK_ABOVE; else if (strcmp(value, "previous") == 0) spawn_position = SWM_STACK_BELOW; else errx(1, "spawn_position"); break; case SWM_S_SPAWN_TERM: setconfspawn("term", value, 0); setconfspawn("spawn_term", value, 0); break; case SWM_S_SS_APP: /* No longer needed; leave to not break old conf files. */ break; case SWM_S_SS_ENABLED: /* No longer needed; leave to not break old conf files. */ break; case SWM_S_STACK_ENABLED: stack_enabled = (atoi(value) != 0); break; case SWM_S_TERM_WIDTH: term_width = atoi(value); if (term_width < 0) term_width = 0; break; case SWM_S_TILE_GAP: tile_gap = atoi(value); break; case SWM_S_URGENT_COLLAPSE: urgent_collapse = (atoi(value) != 0); break; case SWM_S_URGENT_ENABLED: urgent_enabled = (atoi(value) != 0); break; case SWM_S_VERBOSE_LAYOUT: verbose_layout = (atoi(value) != 0); for (i = 0; layouts[i].l_stack != NULL; i++) { if (verbose_layout) layouts[i].l_string = fancy_stacker; else layouts[i].l_string = plain_stacker; } break; case SWM_S_WARP_POINTER: warp_pointer = (atoi(value) != 0); break; case SWM_S_WINDOW_CLASS_ENABLED: window_class_enabled = (atoi(value) != 0); break; case SWM_S_WINDOW_INSTANCE_ENABLED: window_instance_enabled = (atoi(value) != 0); break; case SWM_S_WINDOW_NAME_ENABLED: window_name_enabled = (atoi(value) != 0); break; case SWM_S_WORKSPACE_LIMIT: workspace_limit = atoi(value); if (workspace_limit > SWM_WS_MAX) workspace_limit = SWM_WS_MAX; else if (workspace_limit < 1) workspace_limit = 1; ewmh_update_desktops(); break; case SWM_S_WORKSPACE_NAME: if (getenv("SWM_STARTED") != NULL) return (0); bzero(s, sizeof s); if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2) errx(1, "invalid entry, should be 'ws[]:name'"); ws_id--; if (ws_id < 0 || ws_id >= workspace_limit) errx(1, "setconfvalue: workspace_name: invalid " "workspace %d.", ws_id + 1); num_screens = get_screen_count(); for (i = 0; i < num_screens; ++i) { ws = (struct workspace *)&screens[i].ws; if (strlen(s) > 0) { free(ws[ws_id].name); if ((ws[ws_id].name = strdup(s)) == NULL) err(1, "setconfvalue: workspace_name."); ewmh_update_desktop_names(); ewmh_get_desktop_names(); } } break; default: return (1); } return (0); } int setconfmodkey(const char *selector, const char *value, int flags) { /* suppress unused warnings since vars are needed */ (void)selector; (void)flags; if (strncasecmp(value, "Mod1", strlen("Mod1")) == 0) update_modkey(XCB_MOD_MASK_1); else if (strncasecmp(value, "Mod2", strlen("Mod2")) == 0) update_modkey(XCB_MOD_MASK_2); else if (strncasecmp(value, "Mod3", strlen("Mod3")) == 0) update_modkey(XCB_MOD_MASK_3); else if (strncasecmp(value, "Mod4", strlen("Mod4")) == 0) update_modkey(XCB_MOD_MASK_4); else return (1); return (0); } int setconfcolor(const char *selector, const char *value, int flags) { int first, last, i = 0, num_screens; num_screens = get_screen_count(); /* conf screen indices begin at 1; treat vals <= 0 as 'all screens.' */ if (selector == NULL || strlen(selector) == 0 || (last = atoi(selector) - 1) < 0) { first = 0; last = num_screens - 1; } else { first = last; } if (last >= num_screens) { add_startup_exception("invalid screen index: %d out of bounds " "(maximum %d)", last + 1, num_screens); return (1); } for (i = first; i <= last; ++i) { setscreencolor(value, i, flags); /* * When setting focus/unfocus colors, we need to also * set maximize colors to match if they haven't been customized. */ if (flags == SWM_S_COLOR_FOCUS && !screens[i].c[SWM_S_COLOR_FOCUS_MAXIMIZED].manual) setscreencolor(value, i, SWM_S_COLOR_FOCUS_MAXIMIZED); else if (flags == SWM_S_COLOR_UNFOCUS && !screens[i].c[SWM_S_COLOR_UNFOCUS_MAXIMIZED].manual) setscreencolor(value, i, SWM_S_COLOR_UNFOCUS_MAXIMIZED); screens[i].c[flags].manual = 1; } return (0); } int setconfregion(const char *selector, const char *value, int flags) { /* suppress unused warnings since vars are needed */ (void)selector; (void)flags; custom_region(value); return (0); } int setautorun(const char *selector, const char *value, int flags) { int ws_id; char s[1024]; char *ap, *sp; union arg a; int argc = 0; pid_t pid; struct pid_e *p; /* suppress unused warnings since vars are needed */ (void)selector; (void)flags; if (getenv("SWM_STARTED")) return (0); bzero(s, sizeof s); if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2) errx(1, "invalid autorun entry, should be 'ws[]:command'"); ws_id--; if (ws_id < 0 || ws_id >= workspace_limit) errx(1, "autorun: invalid workspace %d", ws_id + 1); sp = expand_tilde((char *)&s); /* * This is a little intricate * * If the pid already exists we simply reuse it because it means it was * used before AND not claimed by manage_window. We get away with * altering it in the parent after INSERT because this can not be a race */ a.argv = NULL; while ((ap = strsep(&sp, " \t")) != NULL) { if (*ap == '\0') continue; DNPRINTF(SWM_D_SPAWN, "setautorun: arg [%s]\n", ap); argc++; if ((a.argv = realloc(a.argv, argc * sizeof(char *))) == NULL) err(1, "setautorun: realloc"); a.argv[argc - 1] = ap; } free(sp); if ((a.argv = realloc(a.argv, (argc + 1) * sizeof(char *))) == NULL) err(1, "setautorun: realloc"); a.argv[argc] = NULL; if ((pid = fork()) == 0) { spawn(ws_id, &a, true); /* NOTREACHED */ _exit(1); } free(a.argv); /* parent */ p = find_pid(pid); if (p == NULL) { p = calloc(1, sizeof *p); if (p == NULL) return (1); TAILQ_INSERT_TAIL(&pidlist, p, entry); } p->pid = pid; p->ws = ws_id; return (0); } int setlayout(const char *selector, const char *value, int flags) { struct workspace *ws; int ws_id, i, x, mg, ma, si, ar; int st = SWM_V_STACK, num_screens; char s[1024]; bool f = false; /* suppress unused warnings since vars are needed */ (void)selector; (void)flags; if (getenv("SWM_STARTED")) return (0); bzero(s, sizeof s); if (sscanf(value, "ws[%d]:%d:%d:%d:%d:%1023c", &ws_id, &mg, &ma, &si, &ar, s) != 6) errx(1, "invalid layout entry, should be 'ws[]:" "::::" "'"); ws_id--; if (ws_id < 0 || ws_id >= workspace_limit) errx(1, "layout: invalid workspace %d", ws_id + 1); if (strcasecmp(s, "vertical") == 0) st = SWM_V_STACK; else if (strcasecmp(s, "vertical_flip") == 0) { st = SWM_V_STACK; f = true; } else if (strcasecmp(s, "horizontal") == 0) st = SWM_H_STACK; else if (strcasecmp(s, "horizontal_flip") == 0) { st = SWM_H_STACK; f = true; } else if (strcasecmp(s, "fullscreen") == 0) st = SWM_MAX_STACK; else errx(1, "invalid layout entry, should be 'ws[]:" "::::" "'"); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { ws = (struct workspace *)&screens[i].ws; ws[ws_id].cur_layout = &layouts[st]; ws[ws_id].always_raise = (ar != 0); if (st == SWM_MAX_STACK) continue; /* master grow */ for (x = 0; x < abs(mg); x++) { ws[ws_id].cur_layout->l_config(&ws[ws_id], mg >= 0 ? SWM_ARG_ID_MASTERGROW : SWM_ARG_ID_MASTERSHRINK); } /* master add */ for (x = 0; x < abs(ma); x++) { ws[ws_id].cur_layout->l_config(&ws[ws_id], ma >= 0 ? SWM_ARG_ID_MASTERADD : SWM_ARG_ID_MASTERDEL); } /* stack inc */ for (x = 0; x < abs(si); x++) { ws[ws_id].cur_layout->l_config(&ws[ws_id], si >= 0 ? SWM_ARG_ID_STACKINC : SWM_ARG_ID_STACKDEC); } /* Apply flip */ if (f) { ws[ws_id].cur_layout->l_config(&ws[ws_id], SWM_ARG_ID_FLIPLAYOUT); } } return (0); } /* config options */ struct config_option { char *optname; int (*func)(const char*, const char*, int); int funcflags; }; struct config_option configopt[] = { { "autorun", setautorun, 0 }, { "bar_action", setconfvalue, SWM_S_BAR_ACTION }, { "bar_at_bottom", setconfvalue, SWM_S_BAR_AT_BOTTOM }, { "bar_border", setconfcolor, SWM_S_COLOR_BAR_BORDER }, { "bar_border_unfocus", setconfcolor, SWM_S_COLOR_BAR_BORDER_UNFOCUS }, { "bar_border_width", setconfvalue, SWM_S_BAR_BORDER_WIDTH }, { "bar_color", setconfcolor, SWM_S_COLOR_BAR }, { "bar_delay", setconfvalue, SWM_S_BAR_DELAY }, { "bar_enabled", setconfvalue, SWM_S_BAR_ENABLED }, { "bar_enabled_ws", setconfvalue, SWM_S_BAR_ENABLED_WS }, { "bar_font", setconfvalue, SWM_S_BAR_FONT }, { "bar_font_color", setconfcolor, SWM_S_COLOR_BAR_FONT }, { "bar_format", setconfvalue, SWM_S_BAR_FORMAT }, { "bar_justify", setconfvalue, SWM_S_BAR_JUSTIFY }, { "bind", setconfbinding, 0 }, { "border_width", setconfvalue, SWM_S_BORDER_WIDTH }, { "boundary_width", setconfvalue, SWM_S_BOUNDARY_WIDTH }, { "clock_enabled", setconfvalue, SWM_S_CLOCK_ENABLED }, { "clock_format", setconfvalue, SWM_S_CLOCK_FORMAT }, { "color_focus", setconfcolor, SWM_S_COLOR_FOCUS }, { "color_focus_maximized", setconfcolor, SWM_S_COLOR_FOCUS_MAXIMIZED }, { "color_unfocus", setconfcolor, SWM_S_COLOR_UNFOCUS }, { "color_unfocus_maximized", setconfcolor, SWM_S_COLOR_UNFOCUS_MAXIMIZED }, { "cycle_empty", setconfvalue, SWM_S_CYCLE_EMPTY }, { "cycle_visible", setconfvalue, SWM_S_CYCLE_VISIBLE }, { "dialog_ratio", setconfvalue, SWM_S_DIALOG_RATIO }, { "disable_border", setconfvalue, SWM_S_DISABLE_BORDER }, { "focus_close", setconfvalue, SWM_S_FOCUS_CLOSE }, { "focus_close_wrap", setconfvalue, SWM_S_FOCUS_CLOSE_WRAP }, { "focus_default", setconfvalue, SWM_S_FOCUS_DEFAULT }, { "focus_mode", setconfvalue, SWM_S_FOCUS_MODE }, { "iconic_enabled", setconfvalue, SWM_S_ICONIC_ENABLED }, { "java_workaround", setconfvalue, SWM_S_JAVA_WORKAROUND }, { "keyboard_mapping", setkeymapping, 0 }, { "layout", setlayout, 0 }, { "modkey", setconfmodkey, 0 }, { "program", setconfspawn, 0 }, { "quirk", setconfquirk, 0 }, { "region", setconfregion, 0 }, { "region_padding", setconfvalue, SWM_S_REGION_PADDING }, { "screenshot_app", setconfvalue, SWM_S_SS_APP }, { "screenshot_enabled", setconfvalue, SWM_S_SS_ENABLED }, { "spawn_position", setconfvalue, SWM_S_SPAWN_ORDER }, { "spawn_term", setconfvalue, SWM_S_SPAWN_TERM }, { "stack_enabled", setconfvalue, SWM_S_STACK_ENABLED }, { "term_width", setconfvalue, SWM_S_TERM_WIDTH }, { "tile_gap", setconfvalue, SWM_S_TILE_GAP }, { "title_class_enabled", setconfvalue, SWM_S_WINDOW_CLASS_ENABLED }, /* For backwards compat. */ { "title_name_enabled", setconfvalue, SWM_S_WINDOW_INSTANCE_ENABLED }, /* For backwards compat. */ { "urgent_collapse", setconfvalue, SWM_S_URGENT_COLLAPSE }, { "urgent_enabled", setconfvalue, SWM_S_URGENT_ENABLED }, { "verbose_layout", setconfvalue, SWM_S_VERBOSE_LAYOUT }, { "warp_pointer", setconfvalue, SWM_S_WARP_POINTER }, { "window_class_enabled", setconfvalue, SWM_S_WINDOW_CLASS_ENABLED }, { "window_instance_enabled", setconfvalue, SWM_S_WINDOW_INSTANCE_ENABLED }, { "window_name_enabled", setconfvalue, SWM_S_WINDOW_NAME_ENABLED }, { "workspace_limit", setconfvalue, SWM_S_WORKSPACE_LIMIT }, { "name", setconfvalue, SWM_S_WORKSPACE_NAME }, }; void _add_startup_exception(const char *fmt, va_list ap) { if (vasprintf(&startup_exception, fmt, ap) == -1) warn("%s: asprintf", __func__); } void add_startup_exception(const char *fmt, ...) { va_list ap; nr_exceptions++; if (startup_exception) return; /* force bar to be enabled due to exception */ bar_enabled = true; va_start(ap, fmt); _add_startup_exception(fmt, ap); va_end(ap); } int conf_load(const char *filename, int keymapping) { FILE *config; char *line = NULL, *cp, *ce, *optsub, *optval = NULL; size_t linelen, lineno = 0; int wordlen, i, optidx, count; struct config_option *opt = NULL; DNPRINTF(SWM_D_CONF, "conf_load: begin\n"); if (filename == NULL) { warnx("conf_load: no filename"); return (1); } DNPRINTF(SWM_D_CONF, "conf_load: open %s\n", filename); if ((config = fopen(filename, "r")) == NULL) { warn("conf_load: fopen: %s", filename); return (1); } while (!feof(config)) { if (line) free(line); if ((line = fparseln(config, &linelen, &lineno, NULL, FPARSELN_UNESCCOMM | FPARSELN_UNESCCONT)) == NULL) { if (ferror(config)) err(1, "%s", filename); else continue; } cp = line; cp += strspn(cp, " \t\n"); /* eat whitespace */ if (cp[0] == '\0') { /* empty line */ continue; } /* get config option */ wordlen = strcspn(cp, "=[ \t\n"); if (wordlen == 0) { add_startup_exception("%s: line %zd: no option found", filename, lineno); continue; } optidx = -1; for (i = 0; i < LENGTH(configopt); i++) { opt = &configopt[i]; if (strncasecmp(cp, opt->optname, wordlen) == 0 && (int)strlen(opt->optname) == wordlen) { optidx = i; break; } } if (optidx == -1) { add_startup_exception("%s: line %zd: unknown option " "%.*s", filename, lineno, wordlen, cp); continue; } if (keymapping && opt && strcmp(opt->optname, "bind")) { add_startup_exception("%s: line %zd: invalid option " "%.*s", filename, lineno, wordlen, cp); continue; } cp += wordlen; cp += strspn(cp, " \t\n"); /* eat whitespace */ /* get [selector] if any */ optsub = NULL; count = 0; if (*cp == '[') { ++count; /* Get length of selector. */ for (ce = ++cp; *ce != '\0'; ++ce) { /* Find matching (not escaped) bracket. */ if (*ce == ']' && *(ce - 1) != '\\') { --count; break; } } if (count > 0) { add_startup_exception("%s: line %zd: syntax " "error: unterminated selector", filename, lineno); continue; } /* ce points at ']'; terminate optsub. */ *ce = '\0'; optsub = cp; cp = ce + 1; } cp += strspn(cp, "= \t\n"); /* eat trailing */ /* get RHS value */ optval = cp; if (strlen(optval) == 0) { add_startup_exception("%s: line %zd: must supply value " "to %s", filename, lineno, configopt[optidx].optname); continue; } /* call function to deal with it all */ if (configopt[optidx].func(optsub, optval, configopt[optidx].funcflags) != 0) { add_startup_exception("%s: line %zd: invalid data for " "%s", filename, lineno, configopt[optidx].optname); continue; } } if (line) free(line); fclose(config); DNPRINTF(SWM_D_CONF, "conf_load: end\n"); return (0); } void set_child_transient(struct ws_win *win, xcb_window_t *trans) { struct ws_win *parent, *w; struct swm_region *r; struct workspace *ws; xcb_icccm_wm_hints_t wmh; parent = find_window(win->transient); if (parent) parent->focus_child = win; else { DNPRINTF(SWM_D_MISC, "set_child_transient: parent doesn't exist" " for %#x trans %#x\n", win->id, win->transient); r = root_to_region(win->s->root, SWM_CK_ALL); ws = r->ws; /* parent doen't exist in our window list */ TAILQ_FOREACH(w, &ws->winlist, entry) { if (xcb_icccm_get_wm_hints_reply(conn, xcb_icccm_get_wm_hints(conn, w->id), &wmh, NULL) != 1) { warnx("can't get hints for %#x", w->id); continue; } if (win->hints.window_group != wmh.window_group) continue; w->focus_child = win; win->transient = w->id; *trans = w->id; DNPRINTF(SWM_D_MISC, "set_child_transient: adjusting " "transient to %#x\n", win->transient); break; } } } pid_t window_get_pid(xcb_window_t win) { pid_t ret = 0; const char *errstr; xcb_atom_t apid; xcb_get_property_cookie_t pc; xcb_get_property_reply_t *pr; apid = get_atom_from_string("_NET_WM_PID"); if (apid == XCB_ATOM_NONE) goto tryharder; pc = xcb_get_property(conn, 0, win, apid, XCB_ATOM_CARDINAL, 0, 1); pr = xcb_get_property_reply(conn, pc, NULL); if (pr == NULL) goto tryharder; if (pr->type != XCB_ATOM_CARDINAL) { free(pr); goto tryharder; } if (pr->type == apid && pr->format == 32) ret = *((pid_t *)xcb_get_property_value(pr)); free(pr); return (ret); tryharder: apid = get_atom_from_string("_SWM_PID"); pc = xcb_get_property(conn, 0, win, apid, XCB_ATOM_STRING, 0, SWM_PROPLEN); pr = xcb_get_property_reply(conn, pc, NULL); if (pr == NULL) return (0); if (pr->type != apid) { free(pr); return (0); } ret = (pid_t)strtonum(xcb_get_property_value(pr), 0, INT_MAX, &errstr); free(pr); return (ret); } int get_swm_ws(xcb_window_t id) { int ws_idx = -1; char *prop = NULL; size_t proplen; const char *errstr; xcb_get_property_reply_t *gpr; gpr = xcb_get_property_reply(conn, xcb_get_property(conn, 0, id, a_swm_ws, XCB_ATOM_STRING, 0, SWM_PROPLEN), NULL); if (gpr == NULL) return (-1); if (gpr->type) { proplen = xcb_get_property_value_length(gpr); if (proplen > 0) { prop = malloc(proplen + 1); if (prop) { memcpy(prop, xcb_get_property_value(gpr), proplen); prop[proplen] = '\0'; } } } free(gpr); if (prop) { DNPRINTF(SWM_D_PROP, "get_swm_ws: _SWM_WS: %s\n", prop); ws_idx = (int)strtonum(prop, 0, workspace_limit - 1, &errstr); if (errstr) { DNPRINTF(SWM_D_PROP, "get_swm_ws: win #%s: %s", errstr, prop); } free(prop); } return ws_idx; } int get_ws_idx(struct ws_win *win) { xcb_get_property_reply_t *gpr; int ws_idx = -1; if (win == NULL) return -1; gpr = xcb_get_property_reply(conn, xcb_get_property(conn, 0, win->id, ewmh[_NET_WM_DESKTOP].atom, XCB_ATOM_CARDINAL, 0, 1), NULL); if (gpr) { if (gpr->type == XCB_ATOM_CARDINAL && gpr->format == 32) ws_idx = *((int *)xcb_get_property_value(gpr)); free(gpr); } if (ws_idx == -1 && !(win->quirks & SWM_Q_IGNORESPAWNWS)) ws_idx = get_swm_ws(win->id); if (ws_idx > workspace_limit - 1 || ws_idx < -1) ws_idx = -1; DNPRINTF(SWM_D_PROP, "get_ws_idx: win %#x, ws_idx: %d\n", win->id, ws_idx); return ws_idx; } struct ws_win * manage_window(xcb_window_t id, int spawn_pos, bool mapped) { struct ws_win *win, *ww; struct swm_region *r; struct pid_e *p; struct quirk *qp; xcb_get_geometry_reply_t *gr; xcb_window_t trans = XCB_WINDOW_NONE; uint32_t i, wa[2], new_flags; int ws_idx, force_ws = -1; char *class, *instance, *name; if ((win = find_window(id)) != NULL) { DNPRINTF(SWM_D_MISC, "manage_window: win %#x already " "managed; skipping.)\n", id); return (win); /* Already managed. */ } /* See if window is on the unmanaged list. */ if ((win = find_unmanaged_window(id)) != NULL) { DNPRINTF(SWM_D_MISC, "manage_window: win %#x found on " "unmanaged list.\n", id); TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry); if (TRANS(win)) set_child_transient(win, &trans); goto out; } else { DNPRINTF(SWM_D_MISC, "manage_window: win %#x is new.\n", id); } /* Try to get initial window geometry. */ gr = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, id), NULL); if (gr == NULL) { DNPRINTF(SWM_D_MISC, "manage_window: get geometry failed.\n"); return (NULL); } /* Create and initialize ws_win object. */ if ((win = calloc(1, sizeof(struct ws_win))) == NULL) err(1, "manage_window: calloc: failed to allocate memory for " "new window"); win->id = id; /* Figureout which region the window belongs to. */ r = root_to_region(gr->root, SWM_CK_ALL); /* Ignore window border if there is one. */ WIDTH(win) = gr->width; HEIGHT(win) = gr->height; X(win) = gr->x + gr->border_width - border_width; Y(win) = gr->y + gr->border_width - border_width; win->bordered = true; win->mapped = mapped; win->s = r->s; /* this never changes */ free(gr); /* Select which X events to monitor and set border pixel color. */ wa[0] = win->s->c[SWM_S_COLOR_UNFOCUS].pixel; wa[1] = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY; #ifdef SWM_DEBUG wa[1] |= XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE; #endif xcb_change_window_attributes(conn, win->id, XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK, wa); /* Get WM_SIZE_HINTS. */ xcb_icccm_get_wm_normal_hints_reply(conn, xcb_icccm_get_wm_normal_hints(conn, win->id), &win->sh, NULL); /* Get WM_HINTS. */ xcb_icccm_get_wm_hints_reply(conn, xcb_icccm_get_wm_hints(conn, win->id), &win->hints, NULL); /* Get WM_TRANSIENT_FOR; see if window is a transient. */ xcb_icccm_get_wm_transient_for_reply(conn, xcb_icccm_get_wm_transient_for(conn, win->id), &trans, NULL); if (trans) { win->transient = trans; set_child_transient(win, &win->transient); } /* Get WM_PROTOCOLS. */ get_wm_protocols(win); /* Set initial quirks based on EWMH. */ ewmh_autoquirk(win); /* Determine initial quirks. */ xcb_icccm_get_wm_class_reply(conn, xcb_icccm_get_wm_class(conn, win->id), &win->ch, NULL); class = win->ch.class_name ? win->ch.class_name : ""; instance = win->ch.instance_name ? win->ch.instance_name : ""; name = get_win_name(win->id); DNPRINTF(SWM_D_CLASS, "manage_window: class: %s, instance: %s, " "name: %s\n", class, instance, name); /* java is retarded so treat it special */ if (strstr(instance, "sun-awt")) { DNPRINTF(SWM_D_CLASS, "manage_window: java window detected.\n"); win->java = true; } TAILQ_FOREACH(qp, &quirks, entry) { if (regexec(&qp->regex_class, class, 0, NULL, 0) == 0 && regexec(&qp->regex_instance, instance, 0, NULL, 0) == 0 && regexec(&qp->regex_name, name, 0, NULL, 0) == 0) { DNPRINTF(SWM_D_CLASS, "manage_window: matched " "quirk: %s:%s:%s mask: %#x, ws: %d\n", qp->class, qp->instance, qp->name, qp->quirk, qp->ws); win->quirks = qp->quirk; if (qp->ws >= 0 && qp->ws < workspace_limit) force_ws = qp->ws; } } free(name); /* Reset font sizes (the bruteforce way; no default keybinding). */ if (win->quirks & SWM_Q_XTERM_FONTADJ) { for (i = 0; i < SWM_MAX_FONT_STEPS; i++) fake_keypress(win, XK_KP_Subtract, XCB_MOD_MASK_SHIFT); for (i = 0; i < SWM_MAX_FONT_STEPS; i++) fake_keypress(win, XK_KP_Add, XCB_MOD_MASK_SHIFT); } /* Figure out which workspace the window belongs to. */ if (!(win->quirks & SWM_Q_IGNOREPID) && (p = find_pid(window_get_pid(win->id))) != NULL) { win->ws = &r->s->ws[p->ws]; TAILQ_REMOVE(&pidlist, p, entry); free(p); p = NULL; } else if ((ws_idx = get_ws_idx(win)) != -1 && !TRANS(win)) { /* _SWM_WS is set; use that. */ win->ws = &r->s->ws[ws_idx]; } else if (trans && (ww = find_window(trans)) != NULL) { /* Launch transients in the same ws as parent. */ win->ws = ww->ws; } else { win->ws = r->ws; } if (force_ws != -1) win->ws = &r->s->ws[force_ws]; /* Set the _NET_WM_DESKTOP atom. */ DNPRINTF(SWM_D_PROP, "manage_window: set _NET_WM_DESKTOP: %d\n", win->ws->idx); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id, ewmh[_NET_WM_DESKTOP].atom, XCB_ATOM_CARDINAL, 32, 1, &win->ws->idx); /* Remove any _SWM_WS now that we set _NET_WM_DESKTOP. */ xcb_delete_property(conn, win->id, a_swm_ws); /* WS must already be set for this to work. */ store_float_geom(win); /* Make sure window is positioned inside its region, if its active. */ if (win->ws->r) { region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_HARDBOUNDARY); update_window(win); } out: /* Figure out where to stack the window in the workspace. */ if (trans && (ww = find_window(trans))) TAILQ_INSERT_AFTER(&win->ws->winlist, ww, win, entry); else if (win->ws->focus && spawn_pos == SWM_STACK_ABOVE) TAILQ_INSERT_AFTER(&win->ws->winlist, win->ws->focus, win, entry); else if (win->ws->focus && spawn_pos == SWM_STACK_BELOW) TAILQ_INSERT_BEFORE(win->ws->focus, win, entry); else switch (spawn_pos) { default: case SWM_STACK_TOP: case SWM_STACK_ABOVE: TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry); break; case SWM_STACK_BOTTOM: case SWM_STACK_BELOW: TAILQ_INSERT_HEAD(&win->ws->winlist, win, entry); } ewmh_update_client_list(); TAILQ_INSERT_TAIL(&win->ws->stack, win, stack_entry); /* Get/apply initial _NET_WM_STATE */ ewmh_get_wm_state(win); /* Apply quirks. */ new_flags = win->ewmh_flags; if (win->quirks & SWM_Q_FLOAT) new_flags |= EWMH_F_ABOVE; if (win->quirks & SWM_Q_ANYWHERE) new_flags |= SWM_F_MANUAL; ewmh_apply_flags(win, new_flags); ewmh_update_wm_state(win); /* Set initial _NET_WM_ALLOWED_ACTIONS */ ewmh_update_actions(win); grabbuttons(win); DNPRINTF(SWM_D_MISC, "manage_window: done. win %#x, (x,y) w x h: " "(%d,%d) %d x %d, ws: %d, iconic: %s, transient: %#x\n", win->id, X(win), Y(win), WIDTH(win), HEIGHT(win), win->ws->idx, YESNO(ICONIC(win)), win->transient); return (win); } void free_window(struct ws_win *win) { DNPRINTF(SWM_D_MISC, "free_window: win %#x\n", win->id); if (win == NULL) return; TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry); xcb_icccm_get_wm_class_reply_wipe(&win->ch); kill_refs(win); /* paint memory */ memset(win, 0xff, sizeof *win); /* XXX kill later */ free(win); DNPRINTF(SWM_D_MISC, "free_window: done.\n"); } void unmanage_window(struct ws_win *win) { struct ws_win *parent; if (win == NULL) return; DNPRINTF(SWM_D_MISC, "unmanage_window: win %#x\n", win->id); if (TRANS(win)) { parent = find_window(win->transient); if (parent) parent->focus_child = NULL; } TAILQ_REMOVE(&win->ws->stack, win, stack_entry); TAILQ_REMOVE(&win->ws->winlist, win, entry); TAILQ_INSERT_TAIL(&win->ws->unmanagedlist, win, entry); ewmh_update_client_list(); } void expose(xcb_expose_event_t *e) { int i, num_screens; struct swm_region *r; DNPRINTF(SWM_D_EVENT, "expose: win %#x\n", e->window); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) if (e->window == WINID(r->bar)) bar_draw(); xcb_flush(conn); } #ifdef SWM_DEBUG void focusin(xcb_focus_in_event_t *e) { DNPRINTF(SWM_D_EVENT, "focusin: win %#x, mode: %s(%u), " "detail: %s(%u)\n", e->event, get_notify_mode_label(e->mode), e->mode, get_notify_detail_label(e->detail), e->detail); } void focusout(xcb_focus_out_event_t *e) { DNPRINTF(SWM_D_EVENT, "focusout: win %#x, mode: %s(%u), " "detail: %s(%u)\n", e->event, get_notify_mode_label(e->mode), e->mode, get_notify_detail_label(e->detail), e->detail); } #endif void keypress(xcb_key_press_event_t *e) { xcb_keysym_t keysym; struct key *kp; keysym = xcb_key_press_lookup_keysym(syms, e, 0); DNPRINTF(SWM_D_EVENT, "keypress: keysym: %u, win (x,y): %#x (%d,%d), " "detail: %u, time: %u, root (x,y): %#x (%d,%d), child: %#x, " "state: %u, same_screen: %s\n", keysym, e->event, e->event_x, e->event_y, e->detail, e->time, e->root, e->root_x, e->root_y, e->child, e->state, YESNO(e->same_screen)); if ((kp = key_lookup(CLEANMASK(e->state), keysym)) == NULL) goto out; last_event_time = e->time; if (kp->funcid == KF_SPAWN_CUSTOM) spawn_custom(root_to_region(e->root, SWM_CK_ALL), &(keyfuncs[kp->funcid].args), kp->spawn_name); else if (keyfuncs[kp->funcid].func) keyfuncs[kp->funcid].func(root_to_region(e->root, SWM_CK_ALL), &(keyfuncs[kp->funcid].args)); out: /* Unfreeze grab events. */ xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, e->time); xcb_flush(conn); DNPRINTF(SWM_D_EVENT, "keypress: done.\n"); } void buttonpress(xcb_button_press_event_t *e) { struct ws_win *win = NULL; struct swm_region *r, *old_r; int i; bool handled = false; DNPRINTF(SWM_D_EVENT, "buttonpress: win (x,y): %#x (%d,%d), " "detail: %u, time: %u, root (x,y): %#x (%d,%d), child: %#x, " "state: %u, same_screen: %s\n", e->event, e->event_x, e->event_y, e->detail, e->time, e->root, e->root_x, e->root_y, e->child, e->state, YESNO(e->same_screen)); if (e->event == e->root) { if (e->child != 0) { win = find_window(e->child); /* Pass ButtonPress to window if it isn't managed. */ if (win == NULL) goto out; } else { /* Focus on empty region */ /* If no windows on region if its empty. */ r = root_to_region(e->root, SWM_CK_POINTER); if (r == NULL) { DNPRINTF(SWM_D_EVENT, "buttonpress: " "NULL region; ignoring.\n"); goto out; } if (TAILQ_EMPTY(&r->ws->winlist)) { old_r = root_to_region(e->root, SWM_CK_FOCUS); if (old_r && old_r != r) unfocus_win(old_r->ws->focus); xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, e->root, e->time); /* Clear bar since empty. */ bar_draw(); handled = true; goto out; } } } else { win = find_window(e->event); } if (win == NULL) goto out; last_event_time = e->time; focus_win(get_focus_magic(win)); for (i = 0; i < LENGTH(buttons); i++) if (client_click == buttons[i].action && buttons[i].func && buttons[i].button == e->detail && CLEANMASK(buttons[i].mask) == CLEANMASK(e->state)) { buttons[i].func(win, &buttons[i].args); handled = true; } out: if (!handled) { DNPRINTF(SWM_D_EVENT, "buttonpress: passing to window.\n"); /* Replay event to event window */ xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, e->time); } else { DNPRINTF(SWM_D_EVENT, "buttonpress: handled.\n"); /* Unfreeze grab events. */ xcb_allow_events(conn, XCB_ALLOW_SYNC_POINTER, e->time); } xcb_flush(conn); } #ifdef SWM_DEBUG void print_win_geom(xcb_window_t w) { xcb_get_geometry_reply_t *wa; wa = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, w), NULL); if (wa == NULL) { DNPRINTF(SWM_D_MISC, "print_win_geom: window not found: %#x\n", w); return; } DNPRINTF(SWM_D_MISC, "print_win_geom: win %#x, root: %#x, " "depth: %u, (x,y) w x h: (%d,%d) %d x %d, border: %d\n", w, wa->root, wa->depth, wa->x, wa->y, wa->width, wa->height, wa->border_width); free(wa); } #endif #ifdef SWM_DEBUG char * get_stack_mode_name(uint8_t mode) { char *name; switch(mode) { case XCB_STACK_MODE_ABOVE: name = "Above"; break; case XCB_STACK_MODE_BELOW: name = "Below"; break; case XCB_STACK_MODE_TOP_IF: name = "TopIf"; break; case XCB_STACK_MODE_BOTTOM_IF: name = "BottomIf"; break; case XCB_STACK_MODE_OPPOSITE: name = "Opposite"; break; default: name = "Unknown"; } return name; } #endif void configurerequest(xcb_configure_request_event_t *e) { struct ws_win *win; struct swm_region *r = NULL; int i = 0; uint32_t wc[7] = {0}; uint16_t mask = 0; bool new = false; if ((win = find_window(e->window)) == NULL) if ((win = find_unmanaged_window(e->window)) == NULL) new = true; #ifdef SWM_DEBUG if (swm_debug & SWM_D_EVENT) { print_win_geom(e->window); DNPRINTF(SWM_D_EVENT, "configurerequest: win %#x, " "parent: %#x, new: %s, value_mask: %u { ", e->window, e->parent, YESNO(new), e->value_mask); if (e->value_mask & XCB_CONFIG_WINDOW_X) DPRINTF("X: %d ", e->x); if (e->value_mask & XCB_CONFIG_WINDOW_Y) DPRINTF("Y: %d ", e->y); if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) DPRINTF("W: %u ", e->width); if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) DPRINTF("H: %u ", e->height); if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) DPRINTF("Border: %u ", e->border_width); if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) DPRINTF("Sibling: %#x ", e->sibling); if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) DPRINTF("StackMode: %s(%u) ", get_stack_mode_name(e->stack_mode), e->stack_mode); DPRINTF("}\n"); } #endif if (new) { if (e->value_mask & XCB_CONFIG_WINDOW_X) { mask |= XCB_CONFIG_WINDOW_X; wc[i++] = e->x; } if (e->value_mask & XCB_CONFIG_WINDOW_Y) { mask |= XCB_CONFIG_WINDOW_Y; wc[i++] = e->y; } if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) { mask |= XCB_CONFIG_WINDOW_WIDTH; wc[i++] = e->width; } if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) { mask |= XCB_CONFIG_WINDOW_HEIGHT; wc[i++] = e->height; } if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) { mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH; wc[i++] = e->border_width; } if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) { mask |= XCB_CONFIG_WINDOW_SIBLING; wc[i++] = e->sibling; } if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) { mask |= XCB_CONFIG_WINDOW_STACK_MODE; wc[i++] = e->stack_mode; } if (mask != 0) { xcb_configure_window(conn, e->window, mask, wc); xcb_flush(conn); } } else if ((!MANUAL(win) || win->quirks & SWM_Q_ANYWHERE) && !FULLSCREEN(win) && !MAXIMIZED(win)) { if (win->ws->r) r = win->ws->r; else if (win->ws->old_r) r = win->ws->old_r; /* windows are centered unless ANYWHERE quirk is set. */ if (win->quirks & SWM_Q_ANYWHERE) { if (e->value_mask & XCB_CONFIG_WINDOW_X) { win->g_float.x = e->x; if (r) win->g_float.x -= X(r); } if (e->value_mask & XCB_CONFIG_WINDOW_Y) { win->g_float.y = e->y; if (r) win->g_float.y -= Y(r); } } if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) win->g_float.w = e->width; if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) win->g_float.h = e->height; win->g_floatvalid = true; if (!MAXIMIZED(win) && !FULLSCREEN(win) && (TRANS(win) || (ABOVE(win) && win->ws->cur_layout != &layouts[SWM_MAX_STACK]))) { WIDTH(win) = win->g_float.w; HEIGHT(win) = win->g_float.h; if (r != NULL) { update_floater(win); focus_flush(); } else { config_win(win, e); xcb_flush(conn); } } else { config_win(win, e); xcb_flush(conn); } } else { config_win(win, e); xcb_flush(conn); } DNPRINTF(SWM_D_EVENT, "configurerequest: done.\n"); } void configurenotify(xcb_configure_notify_event_t *e) { struct ws_win *win; DNPRINTF(SWM_D_EVENT, "configurenotify: win %#x, event win: %#x, " "(x,y) WxH: (%d,%d) %ux%u, border: %u, above_sibling: %#x, " "override_redirect: %s\n", e->window, e->event, e->x, e->y, e->width, e->height, e->border_width, e->above_sibling, YESNO(e->override_redirect)); win = find_window(e->window); if (win) { xcb_icccm_get_wm_normal_hints_reply(conn, xcb_icccm_get_wm_normal_hints(conn, win->id), &win->sh, NULL); adjust_font(win); if (font_adjusted) { stack(); xcb_flush(conn); } } } void destroynotify(xcb_destroy_notify_event_t *e) { struct ws_win *win; DNPRINTF(SWM_D_EVENT, "destroynotify: win %#x\n", e->window); if ((win = find_window(e->window)) == NULL) { if ((win = find_unmanaged_window(e->window)) == NULL) return; free_window(win); return; } if (focus_mode != SWM_FOCUS_FOLLOW) { /* If we were focused, make sure we focus on something else. */ if (win == win->ws->focus) win->ws->focus_pending = get_focus_prev(win); } unmanage_window(win); stack(); if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(win->ws)) { if (win->ws->focus_pending) { focus_win(win->ws->focus_pending); win->ws->focus_pending = NULL; } else if (win == win->ws->focus) { xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, win->ws->r->id, XCB_CURRENT_TIME); } } free_window(win); focus_flush(); } #ifdef SWM_DEBUG char * get_notify_detail_label(uint8_t detail) { char *label; switch (detail) { case XCB_NOTIFY_DETAIL_ANCESTOR: label = "Ancestor"; break; case XCB_NOTIFY_DETAIL_VIRTUAL: label = "Virtual"; break; case XCB_NOTIFY_DETAIL_INFERIOR: label = "Inferior"; break; case XCB_NOTIFY_DETAIL_NONLINEAR: label = "Nonlinear"; break; case XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL: label = "NonlinearVirtual"; break; case XCB_NOTIFY_DETAIL_POINTER: label = "Pointer"; break; case XCB_NOTIFY_DETAIL_POINTER_ROOT: label = "PointerRoot"; break; case XCB_NOTIFY_DETAIL_NONE: label = "None"; break; default: label = "Unknown"; } return label; } char * get_notify_mode_label(uint8_t mode) { char *label; switch (mode) { case XCB_NOTIFY_MODE_NORMAL: label = "Normal"; break; case XCB_NOTIFY_MODE_GRAB: label = "Grab"; break; case XCB_NOTIFY_MODE_UNGRAB: label = "Ungrab"; break; case XCB_NOTIFY_MODE_WHILE_GRABBED: label = "WhileGrabbed"; break; default: label = "Unknown"; } return label; } #endif void enternotify(xcb_enter_notify_event_t *e) { struct ws_win *win; struct swm_region *r; DNPRINTF(SWM_D_FOCUS, "enternotify: time: %u, win (x,y): %#x " "(%d,%d), mode: %s(%d), detail: %s(%d), root (x,y): %#x (%d,%d), " "child: %#x, same_screen_focus: %s, state: %d\n", e->time, e->event, e->event_x, e->event_y, get_notify_mode_label(e->mode), e->mode, get_notify_detail_label(e->detail), e->detail, e->root, e->root_x, e->root_y, e->child, YESNO(e->same_screen_focus), e->state); if (focus_mode == SWM_FOCUS_MANUAL && e->mode == XCB_NOTIFY_MODE_NORMAL) { DNPRINTF(SWM_D_EVENT, "enternotify: manual focus; ignoring.\n"); return; } last_event_time = e->time; if ((win = find_window(e->event)) == NULL) { if (e->event == e->root) { /* If no windows on pointer region, then focus root. */ r = root_to_region(e->root, SWM_CK_POINTER); if (r == NULL) { DNPRINTF(SWM_D_EVENT, "enternotify: " "NULL region; ignoring.\n"); return; } focus_region(r); } else { DNPRINTF(SWM_D_EVENT, "enternotify: window is NULL; " "ignoring\n"); return; } } else { if (e->mode == XCB_NOTIFY_MODE_NORMAL && e->detail == XCB_NOTIFY_DETAIL_INFERIOR) { DNPRINTF(SWM_D_EVENT, "enternotify: entering from " "inferior; ignoring\n"); return; } focus_win(get_focus_magic(win)); } xcb_flush(conn); } #ifdef SWM_DEBUG void leavenotify(xcb_leave_notify_event_t *e) { DNPRINTF(SWM_D_FOCUS, "leavenotify: time: %u, win (x,y): %#x " "(%d,%d), mode: %s(%d), detail: %s(%d), root (x,y): %#x (%d,%d), " "child: %#x, same_screen_focus: %s, state: %d\n", e->time, e->event, e->event_x, e->event_y, get_notify_mode_label(e->mode), e->mode, get_notify_detail_label(e->detail), e->detail, e->root, e->root_x, e->root_y, e->child, YESNO(e->same_screen_focus), e->state); } #endif void mapnotify(xcb_map_notify_event_t *e) { struct ws_win *win, *parent = NULL; struct workspace *ws; DNPRINTF(SWM_D_EVENT, "mapnotify: win %#x\n", e->window); if ((win = manage_window(e->window, spawn_position, true)) == NULL) return; ws = win->ws; /* Need to know if win was mapped due to ws switch. */ if (ws->state == SWM_WS_STATE_MAPPED) { if (ws->focus_pending && TRANS(ws->focus_pending)) parent = find_window(win->transient); /* If window's parent is maximized, don't clear it. */ if ((parent == NULL) || !MAXIMIZED(parent)) if (clear_maximized(ws) > 0) stack(); } win->mapped = true; set_win_state(win, XCB_ICCCM_WM_STATE_NORMAL); if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(win->ws)) { if (ws->focus_pending == win) { focus_win(win); ws->focus_pending = NULL; center_pointer(win->ws->r); focus_flush(); } } xcb_flush(conn); } void mappingnotify(xcb_mapping_notify_event_t *e) { struct ws_win *w; int i, j, num_screens; xcb_refresh_keyboard_mapping(syms, e); if (e->request == XCB_MAPPING_KEYBOARD) { grabkeys(); /* Regrab buttons on all managed windows. */ num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) TAILQ_FOREACH(w, &screens[i].ws[j].winlist, entry) grabbuttons(w); } } void maprequest(xcb_map_request_event_t *e) { struct ws_win *win, *w = NULL; xcb_get_window_attributes_reply_t *war; DNPRINTF(SWM_D_EVENT, "maprequest: win %#x\n", e->window); war = xcb_get_window_attributes_reply(conn, xcb_get_window_attributes(conn, e->window), NULL); if (war == NULL) { DNPRINTF(SWM_D_EVENT, "maprequest: window lost.\n"); goto out; } if (war->override_redirect) { DNPRINTF(SWM_D_EVENT, "maprequest: override_redirect; " "skipping.\n"); goto out; } win = manage_window(e->window, spawn_position, (war->map_state == XCB_MAP_STATE_VIEWABLE)); if (win == NULL) goto out; /* The new window should get focus; prepare. */ if (focus_mode != SWM_FOCUS_FOLLOW && !(win->quirks & SWM_Q_NOFOCUSONMAP) && (!(win->hints.flags & XCB_ICCCM_WM_HINT_INPUT) || (win->hints.flags & XCB_ICCCM_WM_HINT_INPUT && win->hints.input))) { if (win->quirks & SWM_Q_FOCUSONMAP_SINGLE) { /* See if other wins of same type are already mapped. */ TAILQ_FOREACH(w, &win->ws->winlist, entry) { if (w == win || !w->mapped) continue; if (w->ch.class_name && win->ch.class_name && strcmp(w->ch.class_name, win->ch.class_name) == 0 && w->ch.instance_name && win->ch.instance_name && strcmp(w->ch.instance_name, win->ch.instance_name) == 0) break; } } if (w == NULL) win->ws->focus_pending = get_focus_magic(win); } /* All windows need to be mapped if they are in the current workspace.*/ if (win->ws->r) stack(); /* Ignore EnterNotify to handle the mapnotify without interference. */ if (focus_mode == SWM_FOCUS_DEFAULT) event_drain(XCB_ENTER_NOTIFY); out: free(war); DNPRINTF(SWM_D_EVENT, "maprequest: done.\n"); } void motionnotify(xcb_motion_notify_event_t *e) { struct swm_region *r = NULL; int i, num_screens; DNPRINTF(SWM_D_FOCUS, "motionnotify: time: %u, win (x,y): %#x " "(%d,%d), detail: %s(%d), root (x,y): %#x (%d,%d), " "child: %#x, same_screen_focus: %s, state: %d\n", e->time, e->event, e->event_x, e->event_y, get_notify_detail_label(e->detail), e->detail, e->root, e->root_x, e->root_y, e->child, YESNO(e->same_screen), e->state); last_event_time = e->time; if (focus_mode == SWM_FOCUS_MANUAL) return; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) if (screens[i].root == e->root) break; TAILQ_FOREACH(r, &screens[i].rl, entry) if (X(r) <= e->root_x && e->root_x < MAX_X(r) && Y(r) <= e->root_y && e->root_y < MAX_Y(r)) break; focus_region(r); } #ifdef SWM_DEBUG char * get_atom_name(xcb_atom_t atom) { char *name = NULL; #ifdef SWM_DEBUG_ATOM_NAMES /* * This should be disabled during most debugging since * xcb_get_* causes an xcb_flush. */ size_t len; xcb_get_atom_name_reply_t *r; r = xcb_get_atom_name_reply(conn, xcb_get_atom_name(conn, atom), NULL); if (r) { len = xcb_get_atom_name_name_length(r); if (len > 0) { name = malloc(len + 1); if (name) { memcpy(name, xcb_get_atom_name_name(r), len); name[len] = '\0'; } } free(r); } #else (void)atom; #endif return (name); } #endif void propertynotify(xcb_property_notify_event_t *e) { struct ws_win *win; struct workspace *ws; #ifdef SWM_DEBUG char *name; name = get_atom_name(e->atom); DNPRINTF(SWM_D_EVENT, "propertynotify: win %#x, atom: %s(%u), " "time: %#x, state: %u\n", e->window, name, e->atom, e->time, e->state); free(name); #endif win = find_window(e->window); if (win == NULL) return; ws = win->ws; last_event_time = e->time; if (e->atom == a_state) { /* State just changed, make sure it gets focused if mapped. */ if (e->state == XCB_PROPERTY_NEW_VALUE) { if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(ws)) { if (win->mapped && ws->focus_pending == win) { focus_win(ws->focus_pending); ws->focus_pending = NULL; } } } } else if (e->atom == XCB_ATOM_WM_CLASS || e->atom == XCB_ATOM_WM_NAME) { bar_draw(); } else if (e->atom == a_prot) { get_wm_protocols(win); } xcb_flush(conn); } void unmapnotify(xcb_unmap_notify_event_t *e) { struct ws_win *win; struct workspace *ws; DNPRINTF(SWM_D_EVENT, "unmapnotify: win %#x\n", e->window); /* If we aren't managing the window, then ignore. */ win = find_window(e->window); if (win == NULL || win->id != e->window) return; /* Do nothing if already withdrawn. */ if (!win->mapped && !ICONIC(win)) return; ws = win->ws; win->mapped = false; /* If win was focused, make sure to focus on something else. */ if (win == ws->focus) { if (focus_mode != SWM_FOCUS_FOLLOW) { ws->focus_pending = get_focus_prev(win); DNPRINTF(SWM_D_EVENT, "unmapnotify: " "focus_pending: %#x\n", WINID(ws->focus_pending)); } unfocus_win(win); } if (ICONIC(win)) { /* Iconify. */ set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC); } else { /* Withdraw. */ set_win_state(win, XCB_ICCCM_WM_STATE_WITHDRAWN); unmanage_window(win); } if (ws->r) stack(); /* Update focus if ws is active. */ if (WS_FOCUSED(ws)) { if (focus_mode == SWM_FOCUS_FOLLOW) { focus_win(get_pointer_win(ws->r->s->root)); } else if (ws->focus_pending) { focus_win(ws->focus_pending); ws->focus_pending = NULL; } else if (ws->focus == NULL) { xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, ws->r->id, XCB_CURRENT_TIME); } } center_pointer(ws->r); focus_flush(); } #ifdef SWM_DEBUG char * get_source_type_label(uint32_t type) { char *label; switch (type) { case EWMH_SOURCE_TYPE_NONE: label = "None"; break; case EWMH_SOURCE_TYPE_NORMAL: label = "Normal"; break; case EWMH_SOURCE_TYPE_OTHER: label = "Other"; break; default: label = "Invalid"; } return label; } #endif void clientmessage(xcb_client_message_event_t *e) { struct ws_win *win; struct swm_region *r = NULL; union arg a; uint32_t val[2]; int num_screens, i; xcb_map_request_event_t mre; #ifdef SWM_DEBUG char *name; name = get_atom_name(e->type); DNPRINTF(SWM_D_EVENT, "clientmessage: win %#x, atom: %s(%u)\n", e->window, name, e->type); free(name); #endif if (e->type == ewmh[_NET_CURRENT_DESKTOP].atom) { num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) if (screens[i].root == e->window) { r = screens[i].r_focus; break; } if (r && e->data.data32[0] < (uint32_t)workspace_limit) { a.id = e->data.data32[0]; switchws(r, &a); focus_flush(); } return; } win = find_window(e->window); if (win == NULL) { if (e->type == ewmh[_NET_ACTIVE_WINDOW].atom) { /* Manage the window with maprequest. */ DNPRINTF(SWM_D_EVENT, "clientmessage: request focus on " "unmanaged window.\n"); mre.window = e->window; maprequest(&mre); } return; } if (e->type == ewmh[_NET_ACTIVE_WINDOW].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_ACTIVE_WINDOW, " "source_type: %s(%d)\n", get_source_type_label(e->data.data32[0]), e->data.data32[0]); /* * Allow focus changes that are a result of direct user * action and from applications that use the old EWMH spec. */ if (e->data.data32[0] != EWMH_SOURCE_TYPE_NORMAL || win->quirks & SWM_Q_OBEYAPPFOCUSREQ) { if (WS_FOCUSED(win->ws)) focus_win(win); else win->ws->focus_pending = win; } } else if (e->type == ewmh[_NET_CLOSE_WINDOW].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_CLOSE_WINDOW\n"); if (win->can_delete) client_msg(win, a_delete, 0); else xcb_kill_client(conn, win->id); } else if (e->type == ewmh[_NET_MOVERESIZE_WINDOW].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_MOVERESIZE_WINDOW\n"); if (ABOVE(win)) { if (e->data.data32[0] & (1<<8)) /* x */ X(win) = e->data.data32[1]; if (e->data.data32[0] & (1<<9)) /* y */ Y(win) = e->data.data32[2]; if (e->data.data32[0] & (1<<10)) /* width */ WIDTH(win) = e->data.data32[3]; if (e->data.data32[0] & (1<<11)) /* height */ HEIGHT(win) = e->data.data32[4]; update_window(win); } else { /* Notify no change was made. */ config_win(win, NULL); /* TODO: Change stack sizes */ } } else if (e->type == ewmh[_NET_RESTACK_WINDOW].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_RESTACK_WINDOW\n"); val[0] = e->data.data32[1]; /* Sibling window. */ val[1] = e->data.data32[2]; /* Stack mode detail. */ xcb_configure_window(conn, win->id, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, val); } else if (e->type == ewmh[_NET_WM_STATE].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_STATE\n"); ewmh_change_wm_state(win, e->data.data32[1], e->data.data32[0]); if (e->data.data32[2]) ewmh_change_wm_state(win, e->data.data32[2], e->data.data32[0]); ewmh_update_wm_state(win); stack(); } else if (e->type == ewmh[_NET_WM_DESKTOP].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_DESKTOP\n"); r = win->ws->r; win_to_ws(win, e->data.data32[0], true); /* Restack if either the source or destination ws is mapped. */ if (r != NULL || win->ws->r != NULL) { if (FLOATING(win)) load_float_geom(win); stack(); } } focus_flush(); } void check_conn(void) { int errcode = xcb_connection_has_error(conn); #ifdef XCB_CONN_ERROR char *s; switch (errcode) { case XCB_CONN_ERROR: s = "Socket error, pipe error or other stream error."; break; case XCB_CONN_CLOSED_EXT_NOTSUPPORTED: s = "Extension not supported."; break; case XCB_CONN_CLOSED_MEM_INSUFFICIENT: s = "Insufficient memory."; break; case XCB_CONN_CLOSED_REQ_LEN_EXCEED: s = "Request length was exceeded."; break; case XCB_CONN_CLOSED_PARSE_ERR: s = "Error parsing display string."; break; default: s = "Unknown error."; } if (errcode) errx(errcode, "X CONNECTION ERROR: %s", s); #else if (errcode) errx(errcode, "X CONNECTION ERROR"); #endif } int enable_wm(void) { int num_screens, i; const uint32_t val = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_ENTER_WINDOW; xcb_screen_t *sc; xcb_void_cookie_t wac; xcb_generic_error_t *error; /* this causes an error if some other window manager is running */ num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { if ((sc = get_screen(i)) == NULL) errx(1, "ERROR: can't get screen %d.", i); DNPRINTF(SWM_D_INIT, "enable_wm: screen %d, root: %#x\n", i, sc->root); wac = xcb_change_window_attributes_checked(conn, sc->root, XCB_CW_EVENT_MASK, &val); if ((error = xcb_request_check(conn, wac))) { DNPRINTF(SWM_D_INIT, "enable_wm: error_code: %u\n", error->error_code); free(error); return 1; } /* click to focus on empty region */ xcb_grab_button(conn, 1, sc->root, BUTTONMASK, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, XCB_CURSOR_NONE, XCB_BUTTON_INDEX_1, XCB_BUTTON_MASK_ANY); } return 0; } void new_region(struct swm_screen *s, int x, int y, int w, int h) { struct swm_region *r = NULL, *n; struct workspace *ws = NULL; int i; uint32_t wa[1]; DNPRINTF(SWM_D_MISC, "new region: screen[%d]:%dx%d+%d+%d\n", s->idx, w, h, x, y); /* remove any conflicting regions */ n = TAILQ_FIRST(&s->rl); while (n) { r = n; n = TAILQ_NEXT(r, entry); if (X(r) < (x + w) && (X(r) + WIDTH(r)) > x && Y(r) < (y + h) && (Y(r) + HEIGHT(r)) > y) { if (r->ws->r != NULL) r->ws->old_r = r->ws->r; r->ws->r = NULL; bar_cleanup(r); xcb_destroy_window(conn, r->id); TAILQ_REMOVE(&s->rl, r, entry); TAILQ_INSERT_TAIL(&s->orl, r, entry); } } /* search old regions for one to reuse */ /* size + location match */ TAILQ_FOREACH(r, &s->orl, entry) if (X(r) == x && Y(r) == y && HEIGHT(r) == h && WIDTH(r) == w) break; /* size match */ TAILQ_FOREACH(r, &s->orl, entry) if (HEIGHT(r) == h && WIDTH(r) == w) break; if (r != NULL) { TAILQ_REMOVE(&s->orl, r, entry); /* try to use old region's workspace */ if (r->ws->r == NULL) ws = r->ws; } else if ((r = calloc(1, sizeof(struct swm_region))) == NULL) err(1, "new_region: calloc: failed to allocate memory " "for screen"); /* if we don't have a workspace already, find one */ if (ws == NULL) { for (i = 0; i < workspace_limit; i++) if (s->ws[i].r == NULL) { ws = &s->ws[i]; break; } } if (ws == NULL) errx(1, "new_region: no free workspaces"); if (ws->state == SWM_WS_STATE_HIDDEN) ws->state = SWM_WS_STATE_MAPPING; X(r) = x; Y(r) = y; WIDTH(r) = w; HEIGHT(r) = h; r->s = s; r->ws = ws; r->ws_prior = NULL; ws->r = r; outputs++; TAILQ_INSERT_TAIL(&s->rl, r, entry); /* Invisible region window to detect pointer events on empty regions. */ r->id = xcb_generate_id(conn); wa[0] = XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_POINTER_MOTION_HINT; xcb_create_window(conn, XCB_COPY_FROM_PARENT, r->id, r->s->root, X(r), Y(r), WIDTH(r), HEIGHT(r), 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, wa); /* Make sure region input is at the bottom. */ wa[0] = XCB_STACK_MODE_BELOW; xcb_configure_window(conn, r->id, XCB_CONFIG_WINDOW_STACK_MODE, wa); xcb_map_window(conn, r->id); } void scan_randr(int idx) { #ifdef SWM_XRR_HAS_CRTC int c; int ncrtc = 0; #endif /* SWM_XRR_HAS_CRTC */ struct swm_region *r; struct ws_win *win; int num_screens; xcb_randr_get_screen_resources_current_cookie_t src; xcb_randr_get_screen_resources_current_reply_t *srr; xcb_randr_get_crtc_info_cookie_t cic; xcb_randr_get_crtc_info_reply_t *cir = NULL; xcb_randr_crtc_t *crtc; xcb_screen_t *screen; DNPRINTF(SWM_D_MISC, "scan_randr: screen: %d\n", idx); if ((screen = get_screen(idx)) == NULL) errx(1, "ERROR: can't get screen %d.", idx); num_screens = get_screen_count(); if (idx >= num_screens) errx(1, "scan_randr: invalid screen"); /* remove any old regions */ while ((r = TAILQ_FIRST(&screens[idx].rl)) != NULL) { r->ws->old_r = r->ws->r = NULL; bar_cleanup(r); xcb_destroy_window(conn, r->id); TAILQ_REMOVE(&screens[idx].rl, r, entry); TAILQ_INSERT_TAIL(&screens[idx].orl, r, entry); } outputs = 0; /* map virtual screens onto physical screens */ #ifdef SWM_XRR_HAS_CRTC if (randr_support) { src = xcb_randr_get_screen_resources_current(conn, screens[idx].root); srr = xcb_randr_get_screen_resources_current_reply(conn, src, NULL); if (srr == NULL) { new_region(&screens[idx], 0, 0, screen->width_in_pixels, screen->height_in_pixels); goto out; } else ncrtc = srr->num_crtcs; crtc = xcb_randr_get_screen_resources_current_crtcs(srr); for (c = 0; c < ncrtc; c++) { cic = xcb_randr_get_crtc_info(conn, crtc[c], XCB_CURRENT_TIME); cir = xcb_randr_get_crtc_info_reply(conn, cic, NULL); if (cir == NULL) continue; if (cir->num_outputs == 0) { free(cir); continue; } if (cir->mode == 0) new_region(&screens[idx], 0, 0, screen->width_in_pixels, screen->height_in_pixels); else new_region(&screens[idx], cir->x, cir->y, cir->width, cir->height); free(cir); } free(srr); } #endif /* SWM_XRR_HAS_CRTC */ /* If detection failed, create a single region that spans the screen. */ if (TAILQ_EMPTY(&screens[idx].rl)) new_region(&screens[idx], 0, 0, screen->width_in_pixels, screen->height_in_pixels); out: /* Cleanup unused previously visible workspaces. */ TAILQ_FOREACH(r, &screens[idx].orl, entry) { TAILQ_FOREACH(win, &r->ws->winlist, entry) unmap_window(win); r->ws->state = SWM_WS_STATE_HIDDEN; /* The screen shouldn't focus on an unused region. */ if (screens[idx].r_focus == r) screens[idx].r_focus = NULL; } DNPRINTF(SWM_D_MISC, "scan_randr: done.\n"); } void screenchange(xcb_randr_screen_change_notify_event_t *e) { struct swm_region *r; int i, num_screens; DNPRINTF(SWM_D_EVENT, "screenchange: root: %#x\n", e->root); num_screens = get_screen_count(); /* silly event doesn't include the screen index */ for (i = 0; i < num_screens; i++) if (screens[i].root == e->root) break; if (i >= num_screens) errx(1, "screenchange: screen not found"); /* brute force for now, just re-enumerate the regions */ scan_randr(i); #ifdef SWM_DEBUG print_win_geom(e->root); #endif /* add bars to all regions */ for (i = 0; i < num_screens; i++) { TAILQ_FOREACH(r, &screens[i].rl, entry) bar_setup(r); } stack(); /* Make sure a region has focus on each screen. */ for (i = 0; i < num_screens; i++) { if (screens[i].r_focus == NULL) { r = TAILQ_FIRST(&screens[i].rl); if (r != NULL) focus_region(r); } } bar_draw(); focus_flush(); /* Update workspace state on all regions. */ for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) r->ws->state = SWM_WS_STATE_MAPPED; } void grab_windows(void) { struct swm_region *r = NULL; xcb_window_t *wins = NULL, trans, *cwins = NULL; int i, j, k, n, no, num_screens; uint8_t state; bool manage, mapped; xcb_query_tree_cookie_t qtc; xcb_query_tree_reply_t *qtr; xcb_get_window_attributes_cookie_t gac; xcb_get_window_attributes_reply_t *gar; xcb_get_property_cookie_t pc; xcb_get_property_reply_t *pr; DNPRINTF(SWM_D_INIT, "grab_windows: begin\n"); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { qtc = xcb_query_tree(conn, screens[i].root); qtr = xcb_query_tree_reply(conn, qtc, NULL); if (qtr == NULL) continue; wins = xcb_query_tree_children(qtr); no = xcb_query_tree_children_length(qtr); /* Try to sort windows according to _NET_CLIENT_LIST. */ pr = xcb_get_property_reply(conn, xcb_get_property(conn, 0, screens[i].root, ewmh[_NET_CLIENT_LIST].atom, XCB_ATOM_WINDOW, 0, UINT32_MAX), NULL); if (pr != NULL) { cwins = xcb_get_property_value(pr); n = xcb_get_property_value_length(pr) / sizeof(xcb_atom_t); for (j = 0; j < n; ++j) { for (k = j; k < no; ++k) { if (wins[k] == cwins[j]) { /* Swap wins j and k. */ wins[k] = wins[j]; wins[j] = cwins[j]; } } } free(pr); } /* attach windows to a region */ /* normal windows */ DNPRINTF(SWM_D_INIT, "grab_windows: grab top level windows.\n"); for (j = 0; j < no; j++) { TAILQ_FOREACH(r, &screens[i].rl, entry) { if (r->id == wins[j]) { DNPRINTF(SWM_D_INIT, "grab_windows: " "skip %#x; region input window.\n", wins[j]); break; } else if (r->bar->id == wins[j]) { DNPRINTF(SWM_D_INIT, "grab_windows: " "skip %#x; region bar.\n", wins[j]); break; } } if (r) continue; gac = xcb_get_window_attributes(conn, wins[j]); gar = xcb_get_window_attributes_reply(conn, gac, NULL); if (gar == NULL) { DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " "doesn't exist.\n", wins[j]); continue; } if (gar->override_redirect) { DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " "override_redirect set.\n", wins[j]); free(gar); continue; } pc = xcb_icccm_get_wm_transient_for(conn, wins[j]); if (xcb_icccm_get_wm_transient_for_reply(conn, pc, &trans, NULL)) { DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " "is transient for %#x.\n", wins[j], trans); free(gar); continue; } state = get_win_state(wins[j]); manage = state != XCB_ICCCM_WM_STATE_WITHDRAWN; mapped = gar->map_state == XCB_MAP_STATE_VIEWABLE; if (mapped || manage) manage_window(wins[j], SWM_STACK_TOP, mapped); free(gar); } /* transient windows */ DNPRINTF(SWM_D_INIT, "grab_windows: grab transient windows.\n"); for (j = 0; j < no; j++) { gac = xcb_get_window_attributes(conn, wins[j]); gar = xcb_get_window_attributes_reply(conn, gac, NULL); if (gar == NULL) { DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " "doesn't exist.\n", wins[j]); continue; } if (gar->override_redirect) { DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " "override_redirect set.\n", wins[j]); free(gar); continue; } state = get_win_state(wins[j]); manage = state != XCB_ICCCM_WM_STATE_WITHDRAWN; mapped = gar->map_state == XCB_MAP_STATE_VIEWABLE; pc = xcb_icccm_get_wm_transient_for(conn, wins[j]); if (xcb_icccm_get_wm_transient_for_reply(conn, pc, &trans, NULL) && manage) manage_window(wins[j], SWM_STACK_TOP, mapped); free(gar); } free(qtr); } DNPRINTF(SWM_D_INIT, "grab_windows: done.\n"); } void setup_screens(void) { int i, j, k, num_screens; struct workspace *ws; uint32_t gcv[1], wa[1]; const xcb_query_extension_reply_t *qep; xcb_screen_t *screen; xcb_randr_query_version_cookie_t c; xcb_randr_query_version_reply_t *r; num_screens = get_screen_count(); if ((screens = calloc(num_screens, sizeof(struct swm_screen))) == NULL) err(1, "setup_screens: calloc: failed to allocate memory for " "screens"); /* Initial RandR setup. */ randr_support = false; qep = xcb_get_extension_data(conn, &xcb_randr_id); if (qep->present) { c = xcb_randr_query_version(conn, 1, 1); r = xcb_randr_query_version_reply(conn, c, NULL); if (r) { if (r->major_version >= 1) { randr_support = true; randr_eventbase = qep->first_event; } free(r); } } wa[0] = cursors[XC_LEFT_PTR].cid; /* map physical screens */ for (i = 0; i < num_screens; i++) { DNPRINTF(SWM_D_WS, "setup_screens: init screen: %d\n", i); screens[i].idx = i; screens[i].r_focus = NULL; TAILQ_INIT(&screens[i].rl); TAILQ_INIT(&screens[i].orl); if ((screen = get_screen(i)) == NULL) errx(1, "ERROR: can't get screen %d.", i); screens[i].root = screen->root; /* set default colors */ setscreencolor("red", i, SWM_S_COLOR_FOCUS); setscreencolor("rgb:88/88/88", i, SWM_S_COLOR_UNFOCUS); setscreencolor("rgb:00/80/80", i, SWM_S_COLOR_BAR_BORDER); setscreencolor("rgb:00/40/40", i, SWM_S_COLOR_BAR_BORDER_UNFOCUS); setscreencolor("black", i, SWM_S_COLOR_BAR); setscreencolor("rgb:a0/a0/a0", i, SWM_S_COLOR_BAR_FONT); setscreencolor("red", i, SWM_S_COLOR_FOCUS_MAXIMIZED); setscreencolor("rgb:88/88/88", i, SWM_S_COLOR_UNFOCUS_MAXIMIZED); /* create graphics context on screen */ screens[i].bar_gc = xcb_generate_id(conn); gcv[0] = 0; xcb_create_gc(conn, screens[i].bar_gc, screens[i].root, XCB_GC_GRAPHICS_EXPOSURES, gcv); /* set default cursor */ xcb_change_window_attributes(conn, screens[i].root, XCB_CW_CURSOR, wa); /* init all workspaces */ /* XXX these should be dynamically allocated too */ for (j = 0; j < SWM_WS_MAX; j++) { ws = &screens[i].ws[j]; ws->idx = j; ws->name = NULL; ws->bar_enabled = true; ws->focus = NULL; ws->focus_prev = NULL; ws->focus_pending = NULL; ws->r = NULL; ws->old_r = NULL; ws->state = SWM_WS_STATE_HIDDEN; TAILQ_INIT(&ws->stack); TAILQ_INIT(&ws->winlist); TAILQ_INIT(&ws->unmanagedlist); for (k = 0; layouts[k].l_stack != NULL; k++) if (layouts[k].l_config != NULL) layouts[k].l_config(ws, SWM_ARG_ID_STACKINIT); ws->cur_layout = &layouts[0]; ws->cur_layout->l_string(ws); } scan_randr(i); if (randr_support) xcb_randr_select_input(conn, screens[i].root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE); } } void setup_globals(void) { if ((bar_fonts = strdup(SWM_BAR_FONTS)) == NULL) err(1, "setup_globals: strdup: failed to allocate memory."); if ((clock_format = strdup("%a %b %d %R %Z %Y")) == NULL) err(1, "setup_globals: strdup: failed to allocate memory."); if ((syms = xcb_key_symbols_alloc(conn)) == NULL) errx(1, "unable to allocate key symbols"); a_state = get_atom_from_string("WM_STATE"); a_prot = get_atom_from_string("WM_PROTOCOLS"); a_delete = get_atom_from_string("WM_DELETE_WINDOW"); a_net_supported = get_atom_from_string("_NET_SUPPORTED"); a_net_wm_check = get_atom_from_string("_NET_SUPPORTING_WM_CHECK"); a_takefocus = get_atom_from_string("WM_TAKE_FOCUS"); a_utf8_string = get_atom_from_string("UTF8_STRING"); a_swm_ws = get_atom_from_string("_SWM_WS"); } void shutdown_cleanup(void) { int i, num_screens; /* disable alarm because the following code may not be interrupted */ alarm(0); if (signal(SIGALRM, SIG_IGN) == SIG_ERR) err(1, "can't disable alarm"); bar_extra_stop(); unmap_all(); cursors_cleanup(); teardown_ewmh(); num_screens = get_screen_count(); for (i = 0; i < num_screens; ++i) { xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, screens[i].root, XCB_CURRENT_TIME); if (screens[i].bar_gc != XCB_NONE) xcb_free_gc(conn, screens[i].bar_gc); if (!bar_font_legacy) { XftColorFree(display, DefaultVisual(display, i), DefaultColormap(display, i), &bar_font_color); XftColorFree(display, DefaultVisual(display, i), DefaultColormap(display, i), &search_font_color); } } if (bar_font_legacy) XFreeFontSet(display, bar_fs); else { XftFontClose(display, bar_font); } xcb_key_symbols_free(syms); xcb_flush(conn); xcb_disconnect(conn); } void event_error(xcb_generic_error_t *e) { (void)e; DNPRINTF(SWM_D_EVENT, "event_error: %s(%u) from %s(%u), sequence: %u, " "resource_id: %u, minor_code: %u\n", xcb_event_get_error_label(e->error_code), e->error_code, xcb_event_get_request_label(e->major_code), e->major_code, e->sequence, e->resource_id, e->minor_code); } void event_handle(xcb_generic_event_t *evt) { uint8_t type = XCB_EVENT_RESPONSE_TYPE(evt); DNPRINTF(SWM_D_EVENT, "XCB Event: %s(%d), seq %u\n", xcb_event_get_label(XCB_EVENT_RESPONSE_TYPE(evt)), XCB_EVENT_RESPONSE_TYPE(evt), evt->sequence); switch (type) { #define EVENT(type, callback) case type: callback((void *)evt); return EVENT(0, event_error); EVENT(XCB_BUTTON_PRESS, buttonpress); /*EVENT(XCB_BUTTON_RELEASE, buttonpress);*/ /*EVENT(XCB_CIRCULATE_NOTIFY, );*/ /*EVENT(XCB_CIRCULATE_REQUEST, );*/ EVENT(XCB_CLIENT_MESSAGE, clientmessage); /*EVENT(XCB_COLORMAP_NOTIFY, );*/ EVENT(XCB_CONFIGURE_NOTIFY, configurenotify); EVENT(XCB_CONFIGURE_REQUEST, configurerequest); /*EVENT(XCB_CREATE_NOTIFY, );*/ EVENT(XCB_DESTROY_NOTIFY, destroynotify); EVENT(XCB_ENTER_NOTIFY, enternotify); EVENT(XCB_EXPOSE, expose); #ifdef SWM_DEBUG EVENT(XCB_FOCUS_IN, focusin); EVENT(XCB_FOCUS_OUT, focusout); #endif /*EVENT(XCB_GRAPHICS_EXPOSURE, );*/ /*EVENT(XCB_GRAVITY_NOTIFY, );*/ EVENT(XCB_KEY_PRESS, keypress); /*EVENT(XCB_KEY_RELEASE, keypress);*/ /*EVENT(XCB_KEYMAP_NOTIFY, );*/ #ifdef SWM_DEBUG EVENT(XCB_LEAVE_NOTIFY, leavenotify); #endif EVENT(XCB_MAP_NOTIFY, mapnotify); EVENT(XCB_MAP_REQUEST, maprequest); EVENT(XCB_MAPPING_NOTIFY, mappingnotify); EVENT(XCB_MOTION_NOTIFY, motionnotify); /*EVENT(XCB_NO_EXPOSURE, );*/ EVENT(XCB_PROPERTY_NOTIFY, propertynotify); /*EVENT(XCB_REPARENT_NOTIFY, );*/ /*EVENT(XCB_RESIZE_REQUEST, );*/ /*EVENT(XCB_SELECTION_CLEAR, );*/ /*EVENT(XCB_SELECTION_NOTIFY, );*/ /*EVENT(XCB_SELECTION_REQUEST, );*/ EVENT(XCB_UNMAP_NOTIFY, unmapnotify); /*EVENT(XCB_VISIBILITY_NOTIFY, );*/ #undef EVENT } if (type - randr_eventbase == XCB_RANDR_SCREEN_CHANGE_NOTIFY) screenchange((void *)evt); } int main(int argc, char *argv[]) { struct swm_region *r; char conf[PATH_MAX], *cfile = NULL; struct stat sb; int xfd, i, num_screens; struct sigaction sact; xcb_generic_event_t *evt; struct timeval tv; fd_set rd; int rd_max; int num_readable; bool stdin_ready = false, startup = true; /* suppress unused warning since var is needed */ (void)argc; #ifdef SWM_DEBUG time_started = time(NULL); #endif start_argv = argv; warnx("Welcome to spectrwm V%s Build: %s", SPECTRWM_VERSION, buildstr); if (setlocale(LC_CTYPE, "") == NULL || setlocale(LC_TIME, "") == NULL) warnx("no locale support"); /* handle some signals */ bzero(&sact, sizeof(sact)); sigemptyset(&sact.sa_mask); sact.sa_flags = 0; sact.sa_handler = sighdlr; sigaction(SIGINT, &sact, NULL); sigaction(SIGQUIT, &sact, NULL); sigaction(SIGTERM, &sact, NULL); sigaction(SIGHUP, &sact, NULL); sact.sa_handler = sighdlr; sact.sa_flags = SA_NOCLDSTOP; sigaction(SIGCHLD, &sact, NULL); if ((display = XOpenDisplay(0)) == NULL) errx(1, "can not open display"); conn = XGetXCBConnection(display); if (xcb_connection_has_error(conn)) errx(1, "can not get XCB connection"); XSetEventQueueOwner(display, XCBOwnsEventQueue); xcb_prefetch_extension_data(conn, &xcb_randr_id); xfd = xcb_get_file_descriptor(conn); /* look for local and global conf file */ pwd = getpwuid(getuid()); if (pwd == NULL) errx(1, "invalid user: %d", getuid()); xcb_grab_server(conn); xcb_aux_sync(conn); /* flush all events */ while ((evt = xcb_poll_for_event(conn))) { if (XCB_EVENT_RESPONSE_TYPE(evt) == 0) event_handle(evt); free(evt); } if (enable_wm()) errx(1, "another window manager is currently running"); /* Load Xcursors and/or cursorfont glyph cursors. */ cursors_load(); xcb_aux_sync(conn); setup_globals(); setup_screens(); setup_ewmh(); setup_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) */ if (cfile) conf_load(cfile, SWM_CONF_DEFAULT); validate_spawns(); if (getenv("SWM_STARTED") == NULL) setenv("SWM_STARTED", "YES", 1); /* setup all bars */ num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) bar_setup(r); /* Manage existing windows. */ grab_windows(); grabkeys(); stack(); bar_draw(); xcb_ungrab_server(conn); xcb_flush(conn); /* Update state of each newly mapped workspace. */ for (i = 0; i < num_screens; i++) TAILQ_FOREACH(r, &screens[i].rl, entry) r->ws->state = SWM_WS_STATE_MAPPED; rd_max = xfd > STDIN_FILENO ? xfd : STDIN_FILENO; while (running) { while ((evt = xcb_poll_for_event(conn))) { if (!running) goto done; event_handle(evt); free(evt); } /* If just (re)started, set default focus if needed. */ if (startup) { startup = false; if (focus_mode != SWM_FOCUS_FOLLOW) { r = TAILQ_FIRST(&screens[0].rl); if (r) { focus_region(r); focus_flush(); } continue; } } FD_ZERO(&rd); if (bar_extra) FD_SET(STDIN_FILENO, &rd); FD_SET(xfd, &rd); tv.tv_sec = 1; tv.tv_usec = 0; num_readable = select(rd_max + 1, &rd, NULL, NULL, &tv); if (num_readable == -1 && errno != EINTR) { DNPRINTF(SWM_D_MISC, "select failed"); } else if (num_readable > 0 && FD_ISSET(STDIN_FILENO, &rd)) { stdin_ready = true; } if (restart_wm) restart(NULL, NULL); if (search_resp) search_do_resp(); if (!running) goto done; if (stdin_ready) { stdin_ready = false; bar_extra_update(); } bar_draw(); xcb_flush(conn); } done: shutdown_cleanup(); return (0); } spectrwm-2.6.0/spectrwm.conf010064400017500000000000000074651237571307300151350ustar00marcowheel# PLEASE READ THE MAN PAGE BEFORE EDITING THIS FILE! # http://opensource.conformal.com/cgi-bin/man-cgi?spectrwm # NOTE: all rgb color values in this file are in hex! see XQueryColor for examples # workspace_limit = 22 # focus_mode = default # focus_close = previous # focus_close_wrap = 1 # focus_default = last # spawn_position = next # warp_pointer = 1 # Window Decoration # border_width = 1 # color_focus = red # color_focus_maximized = yellow # color_unfocus = rgb:88/88/88 # color_unfocus_maximized = rgb:88/88/00 # region_padding = 0 # tile_gap = 0 # Region containment # Distance window must be dragged/resized beyond the region edge before it is # allowed outside the region. # boundary_width = 50 # Remove window border when bar is disabled and there is only one window in workspace # disable_border = 1 # Bar Settings # bar_enabled = 1 # bar_border_width = 1 # bar_border[1] = rgb:00/80/80 # bar_border_unfocus[1] = rgb:00/40/40 # bar_color[1] = black # bar_font_color[1] = rgb:a0/a0/a0 # bar_font = -*-terminus-medium-*-*-*-*-*-*-*-*-*-*-* # bar_action = baraction.sh # bar_justify = left # bar_format = +N:+I +S <+D>+4<%a %b %d %R %Z %Y+8<+A+4<+V # bar_at_bottom = 1 # stack_enabled = 1 # clock_enabled = 1 # clock_format = %a %b %d %R %Z %Y # iconic_enabled = 0 # window_class_enabled = 0 # window_instance_enabled = 0 # window_name_enabled = 0 # verbose_layout = 1 # urgent_enabled = 1 # Dialog box size ratio when using TRANSSZ quirk; 0.3 < dialog_ratio <= 1.0 # dialog_ratio = 0.6 # Split a non-RandR dual head setup into one region per monitor # (non-standard driver-based multihead is not seen by spectrwm) # region = screen[1]:1280x1024+0+0 # region = screen[1]:1280x1024+1280+0 # Launch applications in a workspace of choice # autorun = ws[1]:xterm # autorun = ws[2]:xombrero http://www.openbsd.org # Customize workspace layout at start # layout = ws[1]:4:0:0:0:vertical # layout = ws[2]:0:0:0:0:horizontal # layout = ws[3]:0:0:0:0:fullscreen # layout = ws[4]:4:0:0:0:vertical_flip # layout = ws[5]:0:0:0:0:horizontal_flip # Set workspace name at start # name = ws[1]:IRC # name = ws[2]:Email # name = ws[3]:Browse # name = ws[10]:Music # Mod key, (Windows key is Mod4) (Apple key on OSX is Mod2) # modkey = Mod1 # This allows you to include pre-defined key bindings for your keyboard layout. # keyboard_mapping = ~/.spectrwm_us.conf # PROGRAMS # Validated default programs: # program[lock] = xlock # program[term] = xterm # program[menu] = dmenu_run $dmenu_bottom -fn $bar_font -nb $bar_color -nf $bar_font_color -sb $bar_border -sf $bar_color # To disable validation of the above, free the respective binding(s): # bind[] = MOD+Shift+Delete # disable lock # bind[] = MOD+Shift+Enter # disable term # bind[] = MOD+p # disable menu # Optional default programs that will only be validated if you override: # program[screenshot_all] = screenshot.sh full # optional # program[screenshot_wind] = screenshot.sh window # optional # program[initscr] = initscreen.sh # optional # EXAMPLE: Define 'firefox' action and bind to key. # program[firefox] = firefox http://spectrwm.org/ # bind[firefox] = MOD+Shift+b # QUIRKS # Default quirks, remove with: quirk[class:name] = NONE # quirk[MPlayer:xv] = FLOAT + FULLSCREEN + FOCUSPREV # quirk[OpenOffice.org 2.4:VCLSalFrame] = FLOAT # quirk[OpenOffice.org 3.0:VCLSalFrame] = FLOAT # quirk[OpenOffice.org 3.1:VCLSalFrame] = FLOAT # quirk[Firefox-bin:firefox-bin] = TRANSSZ # quirk[Firefox:Dialog] = FLOAT # quirk[Gimp:gimp] = FLOAT + ANYWHERE # quirk[XTerm:xterm] = XTERM_FONTADJ # quirk[xine:Xine Window] = FLOAT + ANYWHERE # quirk[Xitk:Xitk Combo] = FLOAT + ANYWHERE # quirk[xine:xine Panel] = FLOAT + ANYWHERE # quirk[Xitk:Xine Window] = FLOAT + ANYWHERE # quirk[xine:xine Video Fullscreen Window] = FULLSCREEN + FLOAT # quirk[pcb:pcb] = FLOAT spectrwm-2.6.0/spectrwm_cz.conf010064400017500000000000000066451237571307300156300ustar00marcowheel# 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[term] = MOD+Shift+Return bind[menu] = MOD+p bind[quit] = MOD+Shift+q bind[restart] = MOD+q bind[focus_main] = MOD+m bind[focus_urgent] = MOD+u bind[ws_1] = MOD+plus bind[ws_2] = MOD+ecaron bind[ws_3] = MOD+scaron bind[ws_4] = MOD+ccaron bind[ws_5] = MOD+rcaron bind[ws_6] = MOD+zcaron bind[ws_7] = MOD+yacute bind[ws_8] = MOD+aacute bind[ws_9] = MOD+iacute bind[ws_10] = MOD+eacute bind[ws_11] = MOD+F1 bind[ws_12] = MOD+F2 bind[ws_13] = MOD+F3 bind[ws_14] = MOD+F4 bind[ws_15] = MOD+F5 bind[ws_16] = MOD+F6 bind[ws_17] = MOD+F7 bind[ws_18] = MOD+F8 bind[ws_19] = MOD+F9 bind[ws_20] = MOD+F10 bind[ws_21] = MOD+F11 bind[ws_22] = MOD+F12 bind[ws_next] = MOD+Right bind[ws_prev] = MOD+Left bind[ws_next_all] = MOD+Up bind[ws_prev_all] = MOD+Down bind[ws_next_move] = MOD+Shift+Up bind[ws_prev_move] = MOD+Shift+Down bind[ws_prior] = MOD+a bind[mvws_1] = MOD+Shift+plus bind[mvws_2] = MOD+Shift+ecaron bind[mvws_3] = MOD+Shift+scaron bind[mvws_4] = MOD+Shift+ccaron bind[mvws_5] = MOD+Shift+rcaron bind[mvws_6] = MOD+Shift+zcaron bind[mvws_7] = MOD+Shift+yacute bind[mvws_8] = MOD+Shift+aacute bind[mvws_9] = MOD+Shift+iacute bind[mvws_10] = MOD+Shift+eacute bind[mvws_11] = MOD+Shift+F1 bind[mvws_12] = MOD+Shift+F2 bind[mvws_13] = MOD+Shift+F3 bind[mvws_14] = MOD+Shift+F4 bind[mvws_15] = MOD+Shift+F5 bind[mvws_16] = MOD+Shift+F6 bind[mvws_17] = MOD+Shift+F7 bind[mvws_18] = MOD+Shift+F8 bind[mvws_19] = MOD+Shift+F9 bind[mvws_20] = MOD+Shift+F10 bind[mvws_21] = MOD+Shift+F11 bind[mvws_22] = MOD+Shift+F12 bind[rg_1] = MOD+KP_End bind[rg_2] = MOD+KP_Down bind[rg_3] = MOD+KP_Next bind[rg_4] = MOD+KP_Left bind[rg_5] = MOD+KP_Begin bind[rg_6] = MOD+KP_Right bind[rg_7] = MOD+KP_Home bind[rg_8] = MOD+KP_Up bind[rg_9] = MOD+KP_Prior bind[rg_next] = MOD+Shift+Right bind[rg_prev] = MOD+Shift+Left bind[mvrg_1] = MOD+Shift+KP_End bind[mvrg_2] = MOD+Shift+KP_Down bind[mvrg_3] = MOD+Shift+KP_Next bind[mvrg_4] = MOD+Shift+KP_Left bind[mvrg_5] = MOD+Shift+KP_Begin bind[mvrg_6] = MOD+Shift+KP_Right bind[mvrg_7] = MOD+Shift+KP_Home bind[mvrg_8] = MOD+Shift+KP_Up bind[mvrg_9] = MOD+Shift+KP_Prior bind[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-2.6.0/spectrwm_es.1010064400017500000000000000470251237571307300150330ustar00marcowheel.\" Copyright (c) 2009 Marco Peereboom .\" Copyright (c) 2009 Darrin Chandler .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate: February 15 2012 $ .Dt SPECTRWM 1 .Os .Sh NOMBRE .Nm spectrwm .Nd es un manejador de ventanas para X11 .Sh SYNOPSIS .Nm spectrwm .Sh 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_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 .Pf ws_ Ns Ar n .It Cm M-S- Ns Aq Ar n .Pf mvws_ Ns Ar n .It Cm M- Ns Aq Cm Right ws_next .It Cm M- Ns Aq Cm Left ws_prev .It Cm M-a ws_prior .It Cm M-S- Ns Aq Cm Right screen_next .It Cm M-S- Ns Aq Cm Left screen_prev .It Cm M-s screenshot_all .It Cm M-S-s screenshot_wind .It Cm M-S-v version .It Cm M-t float_toggle .It Cm M-S Aq Cm Delete lock .It Cm M-S-i initscr .It Cm M-w iconify .It Cm M-S-w uniconify .It Cm M-S-r always_raise .It Cm M-v button2 .It Cm M-- width_shrink .It Cm M-= width_grow .It Cm M-S- height_shrink .It Cm M-S-= height_grow .It Cm M-[ move_left .It Cm M-] move_right .It Cm M-S-[ move_up .It Cm M-S-] move_down .El .Pp El nombre de las 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. .Sh SIGNALS Enviando .Nm una senal de HUP reinicia spectrwm. .Sh ARCHIVOS .Bl -tag -width "/etc/spectrwm.confXXX" -compact .It Pa ~/.spectrwm.conf .Nm archivo de 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 .Nm fue escrito por .Pp .Bl -tag -width "Ryan Thomas McBride Aq mcbride@countersiege.com " -offset indent -compact .It Cm Marco Peereboom Aq marco@peereboom.us .It Cm Ryan Thomas McBride Aq mcbride@countersiege.com .It Cm Darrin Chandler Aq dwchandler@stilyagin.com .It Cm Pierre-Yves Ritschard Aq pyr@spootnik.org .It Cm Tuukka Kataja Aq stuge@xor.fi .It Cm Jason L. Wright Aq jason@thought.net .It Cm Reginald Kennedy Aq rk@rejii.com .It Cm Lawrence Teo Aq lteo@lteo.net .It Cm Tiago Cunha Aq tcunha@gmx.com .El spectrwm-2.6.0/spectrwm_es.conf010064400017500000000000000065041237571307300156150ustar00marcowheel# 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[term] = MOD+Shift+Return bind[menu] = MOD+p bind[quit] = MOD+Shift+q bind[restart] = MOD+q bind[focus_main] = MOD+m bind[focus_urgent] = MOD+u bind[ws_1] = MOD+1 bind[ws_2] = MOD+2 bind[ws_3] = MOD+3 bind[ws_4] = MOD+4 bind[ws_5] = MOD+5 bind[ws_6] = MOD+6 bind[ws_7] = MOD+7 bind[ws_8] = MOD+8 bind[ws_9] = MOD+9 bind[ws_10] = MOD+0 bind[ws_11] = MOD+F1 bind[ws_12] = MOD+F2 bind[ws_13] = MOD+F3 bind[ws_14] = MOD+F4 bind[ws_15] = MOD+F5 bind[ws_16] = MOD+F6 bind[ws_17] = MOD+F7 bind[ws_18] = MOD+F8 bind[ws_19] = MOD+F9 bind[ws_20] = MOD+F10 bind[ws_21] = MOD+F11 bind[ws_22] = MOD+F12 bind[ws_next] = MOD+Right bind[ws_prev] = MOD+Left bind[ws_next_all] = MOD+Up bind[ws_prev_all] = MOD+Down bind[ws_next_move] = MOD+Shift+Up bind[ws_prev_move] = MOD+Shift+Down bind[ws_prior] = MOD+a bind[mvws_1] = MOD+Shift+1 bind[mvws_2] = MOD+Shift+2 bind[mvws_3] = MOD+Shift+3 bind[mvws_4] = MOD+Shift+4 bind[mvws_5] = MOD+Shift+5 bind[mvws_6] = MOD+Shift+6 bind[mvws_7] = MOD+Shift+7 bind[mvws_8] = MOD+Shift+8 bind[mvws_9] = MOD+Shift+9 bind[mvws_10] = MOD+Shift+0 bind[mvws_11] = MOD+Shift+F1 bind[mvws_12] = MOD+Shift+F2 bind[mvws_13] = MOD+Shift+F3 bind[mvws_14] = MOD+Shift+F4 bind[mvws_15] = MOD+Shift+F5 bind[mvws_16] = MOD+Shift+F6 bind[mvws_17] = MOD+Shift+F7 bind[mvws_18] = MOD+Shift+F8 bind[mvws_19] = MOD+Shift+F9 bind[mvws_20] = MOD+Shift+F10 bind[mvws_21] = MOD+Shift+F11 bind[mvws_22] = MOD+Shift+F12 bind[rg_1] = MOD+KP_End bind[rg_2] = MOD+KP_Down bind[rg_3] = MOD+KP_Next bind[rg_4] = MOD+KP_Left bind[rg_5] = MOD+KP_Begin bind[rg_6] = MOD+KP_Right bind[rg_7] = MOD+KP_Home bind[rg_8] = MOD+KP_Up bind[rg_9] = MOD+KP_Prior bind[rg_next] = MOD+Shift+Right bind[rg_prev] = MOD+Shift+Left bind[mvrg_1] = MOD+Shift+KP_End bind[mvrg_2] = MOD+Shift+KP_Down bind[mvrg_3] = MOD+Shift+KP_Next bind[mvrg_4] = MOD+Shift+KP_Left bind[mvrg_5] = MOD+Shift+KP_Begin bind[mvrg_6] = MOD+Shift+KP_Right bind[mvrg_7] = MOD+Shift+KP_Home bind[mvrg_8] = MOD+Shift+KP_Up bind[mvrg_9] = MOD+Shift+KP_Prior bind[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-2.6.0/spectrwm_fr.conf010064400017500000000000000066751237571307300156260ustar00marcowheel# 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[term] = MOD+Shift+Return bind[menu] = MOD+p bind[quit] = MOD+Shift+q bind[restart] = MOD+q bind[focus_main] = MOD+m bind[focus_urgent] = MOD+u bind[ws_1] = MOD+ampersand bind[ws_2] = MOD+eacute bind[ws_3] = MOD+quotedbl bind[ws_4] = MOD+apostrophe bind[ws_5] = MOD+parenleft bind[ws_6] = MOD+hyphen bind[ws_7] = MOD+egrave bind[ws_8] = MOD+underscore bind[ws_9] = MOD+ccedilla bind[ws_10] = MOD+agrave bind[ws_11] = MOD+F1 bind[ws_12] = MOD+F2 bind[ws_13] = MOD+F3 bind[ws_14] = MOD+F4 bind[ws_15] = MOD+F5 bind[ws_16] = MOD+F6 bind[ws_17] = MOD+F7 bind[ws_18] = MOD+F8 bind[ws_19] = MOD+F9 bind[ws_20] = MOD+F10 bind[ws_21] = MOD+F11 bind[ws_22] = MOD+F12 bind[ws_next] = MOD+Right bind[ws_prev] = MOD+Left bind[ws_next_all] = MOD+Up bind[ws_prev_all] = MOD+Down bind[ws_next_move] = MOD+Shift+Up bind[ws_prev_move] = MOD+Shift+Down bind[ws_prior] = MOD+a bind[mvws_1] = MOD+Shift+ampersand bind[mvws_2] = MOD+Shift+eacute bind[mvws_3] = MOD+Shift+quotedbl bind[mvws_4] = MOD+Shift+apostrophe bind[mvws_5] = MOD+Shift+parenleft bind[mvws_6] = MOD+Shift+hyphen bind[mvws_7] = MOD+Shift+egrave bind[mvws_8] = MOD+Shift+underscore bind[mvws_9] = MOD+Shift+ccedilla bind[mvws_10] = MOD+Shift+agrave bind[mvws_11] = MOD+Shift+F1 bind[mvws_12] = MOD+Shift+F2 bind[mvws_13] = MOD+Shift+F3 bind[mvws_14] = MOD+Shift+F4 bind[mvws_15] = MOD+Shift+F5 bind[mvws_16] = MOD+Shift+F6 bind[mvws_17] = MOD+Shift+F7 bind[mvws_18] = MOD+Shift+F8 bind[mvws_19] = MOD+Shift+F9 bind[mvws_20] = MOD+Shift+F10 bind[mvws_21] = MOD+Shift+F11 bind[mvws_22] = MOD+Shift+F12 bind[rg_1] = MOD+KP_End bind[rg_2] = MOD+KP_Down bind[rg_3] = MOD+KP_Next bind[rg_4] = MOD+KP_Left bind[rg_5] = MOD+KP_Begin bind[rg_6] = MOD+KP_Right bind[rg_7] = MOD+KP_Home bind[rg_8] = MOD+KP_Up bind[rg_9] = MOD+KP_Prior bind[rg_next] = MOD+Shift+Right bind[rg_prev] = MOD+Shift+Left bind[mvrg_1] = MOD+Shift+KP_End bind[mvrg_2] = MOD+Shift+KP_Down bind[mvrg_3] = MOD+Shift+KP_Next bind[mvrg_4] = MOD+Shift+KP_Left bind[mvrg_5] = MOD+Shift+KP_Begin bind[mvrg_6] = MOD+Shift+KP_Right bind[mvrg_7] = MOD+Shift+KP_Home bind[mvrg_8] = MOD+Shift+KP_Up bind[mvrg_9] = MOD+Shift+KP_Prior bind[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-2.6.0/spectrwm_fr_ch.conf010064400017500000000000000065001237571307300162630ustar00marcowheel# 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[term] = MOD+Shift+Return bind[menu] = MOD+p bind[quit] = MOD+Shift+q bind[restart] = MOD+q bind[focus_main] = MOD+m bind[focus_urgent] = MOD+u bind[ws_1] = MOD+1 bind[ws_2] = MOD+2 bind[ws_3] = MOD+3 bind[ws_4] = MOD+4 bind[ws_5] = MOD+5 bind[ws_6] = MOD+6 bind[ws_7] = MOD+7 bind[ws_8] = MOD+8 bind[ws_9] = MOD+9 bind[ws_10] = MOD+0 bind[ws_11] = MOD+F1 bind[ws_12] = MOD+F2 bind[ws_13] = MOD+F3 bind[ws_14] = MOD+F4 bind[ws_15] = MOD+F5 bind[ws_16] = MOD+F6 bind[ws_17] = MOD+F7 bind[ws_18] = MOD+F8 bind[ws_19] = MOD+F9 bind[ws_20] = MOD+F10 bind[ws_21] = MOD+F11 bind[ws_22] = MOD+F12 bind[ws_next] = MOD+Right bind[ws_prev] = MOD+Left bind[ws_next_all] = MOD+Up bind[ws_prev_all] = MOD+Down bind[ws_next_move] = MOD+Shift+Up bind[ws_prev_move] = MOD+Shift+Down bind[ws_prior] = MOD+a bind[mvws_1] = MOD+Shift+1 bind[mvws_2] = MOD+Shift+2 bind[mvws_3] = MOD+Shift+3 bind[mvws_4] = MOD+Shift+4 bind[mvws_5] = MOD+Shift+5 bind[mvws_6] = MOD+Shift+6 bind[mvws_7] = MOD+Shift+7 bind[mvws_8] = MOD+Shift+8 bind[mvws_9] = MOD+Shift+9 bind[mvws_10] = MOD+Shift+0 bind[mvws_11] = MOD+Shift+F1 bind[mvws_12] = MOD+Shift+F2 bind[mvws_13] = MOD+Shift+F3 bind[mvws_14] = MOD+Shift+F4 bind[mvws_15] = MOD+Shift+F5 bind[mvws_16] = MOD+Shift+F6 bind[mvws_17] = MOD+Shift+F7 bind[mvws_18] = MOD+Shift+F8 bind[mvws_19] = MOD+Shift+F9 bind[mvws_20] = MOD+Shift+F10 bind[mvws_21] = MOD+Shift+F11 bind[mvws_22] = MOD+Shift+F12 bind[rg_1] = MOD+KP_End bind[rg_2] = MOD+KP_Down bind[rg_3] = MOD+KP_Next bind[rg_4] = MOD+KP_Left bind[rg_5] = MOD+KP_Begin bind[rg_6] = MOD+KP_Right bind[rg_7] = MOD+KP_Home bind[rg_8] = MOD+KP_Up bind[rg_9] = MOD+KP_Prior bind[rg_next] = MOD+Shift+Right bind[rg_prev] = MOD+Shift+Left bind[mvrg_1] = MOD+Shift+KP_End bind[mvrg_2] = MOD+Shift+KP_Down bind[mvrg_3] = MOD+Shift+KP_Next bind[mvrg_4] = MOD+Shift+KP_Left bind[mvrg_5] = MOD+Shift+KP_Begin bind[mvrg_6] = MOD+Shift+KP_Right bind[mvrg_7] = MOD+Shift+KP_Home bind[mvrg_8] = MOD+Shift+KP_Up bind[mvrg_9] = MOD+Shift+KP_Prior bind[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-2.6.0/spectrwm_it.1010064400017500000000000000506101237571307300150320ustar00marcowheel.\" 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_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-2.6.0/version.h010064400017500000000000000022541237571307300142470ustar00marcowheel/* * 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 2 #define SPECTRWM_MINOR 6 #define SPECTRWM_PATCH 0 #define SPECTRWM_VERSION SPECTRWM_STRINGIZE(SPECTRWM_MAJOR) "." \ SPECTRWM_STRINGIZE(SPECTRWM_MINOR) "." \ SPECTRWM_STRINGIZE(SPECTRWM_PATCH) #endif /* SPECTRWM_VERSION_H */ spectrwm-2.6.0/spectrwm_pt.1010064400017500000000000000360631237571307300150470ustar00marcowheel.\" Copyright (c) 2009 Marco Peereboom .\" Copyright (c) 2009 Darrin Chandler .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate$ .Dt SPECTRWM 1 .Os .Sh NOME .Nm spectrwm .Nd gerenciador de janela para o X11 .Sh SINOPSE .Nm spectrwm .Sh DESCRI\(,C\(~AO .Nm \('e um gerenciador de janela minimalista que tenta n\(~ao atrapalhar a valorosa forma real da tela para que essa possa ser usada para coisas muito mais importantes. Tem sensatos defaults e n\(~ao requer que algu\('em aprenda uma linguagem de programa\(,c\(~ao para fazer qualquer configura\(,c\(~ao. Ele foi escrito por hackers para hackers e esfor\(,ca-se em ser pequeno, compacto e r\('apido. .Pp Quando o .Nm inicia, ele l\(^e as configura\(,c\(~oes do seu arquivo de configura\(,c\(~ao, .Pa spectrwm.conf . Veja a se\(,c\(~ao .Sx ARQUIVOS DE CONFIGURA\(,C\(~AO logo abaixo. .Pp A seguinte nota\(,c\(~ao \('e usada por toda essa p\('agina: .Pp .Bl -tag -width Ds -offset indent -compact .It Cm M Meta .It Cm S Shift .It Aq Cm Name Nome da tecla .It Cm M1 Bot\(~ao 1 do mouse .It Cm M3 Bot\(~ao 3 do mouse .El .Pp .Nm \('e muito simples de usar. Muitas das a\(,c\(~oes s\(~ao iniciadas por atalhos do mouse ou do teclado. Veja a se\(,c\(~ao .Sx ATALHOS logo abaixo para os defaults e as personaliza\(,c\(~oes. .Sh ARQUIVOS DE CONFIGURA\(,C\(~AO .Nm primeiro tenta abrir o arquivo de configura\(,c\(~ao no diret\('orio do usu\('ario, .Pa ~/.spectrwm.conf . Se o arquivo n\(~ao estiver dispon\('ivel, ent\(~ao tenta abrir o arquivo de configura\(,c\(~ao global .Pa /etc/spectrwm.conf . .Pp Assim \('e o formato do arquivo:\*(Ltpalavra-chave\*(Gt = \*(Ltconfigura\(,c\(~ao\*(Gt. Por exemplo: .Pp .Dl color_focus = red .Pp Para habilitar ou desabilitar uma op\(,c\(~ao usa-se o 1 ou 0, respectivamente. .Pp O arquivo suporta as seguintes palavras-chave: .Pp .Bl -tag -width "title_class_enabledXXX" -offset indent -compact .It Cm color_focus Cor da borda da janela atualmente focada. .It Cm color_unfocus Cor da borda das janelas fora de foco. .It Cm bar_enabled Habilita ou desabilita a barra de status. .It Cm bar_border Ns Bq Ar x Cor da borda da barra de status na tela .Ar x . .It Cm bar_color Ns Bq Ar x Cor da janela da barra de status na tela .Ar x . .It Cm bar_font_color Ns Bq Ar x Cor da fonte na barra de status na tela .Ar x . .It Cm bar_font Fonte da barra de status. .It Cm bar_action Script externo que preenche a barra de status com informa\(,c\(~oes adicionais, como tempo de vida da bateria. .It Cm bar_at_bottom Coloca a barra de status na parte inferior de cada regi\(~ao, ao inv\('es da parte superior. .It Cm stack_enabled Habilita ou desabilita mostrar o atual algor\('itmo de empilhamento na barra de status. .It Cm clock_enabled Habilita ou desabilita mostrar o rel\('ogio na barra de status. Desabilite configurando para 0, ent\(~ao um rel\('ogio personalizado pode ser usado no script bar_action. .It Cm dialog_ratio Algumas aplica\(,c\(~oes tem janelas de di\('alogo que s\(~ao muito pequenas para serem \('uteis. Essa taxa \('e o tamanho da tela para o qual elas ser\(~ao redimencionadas. Por exemplo, 0.6 equivale a 60% do tamanho da tela f\('isica. .It Cm region Aloca uma regi\(~ao personalizada, removendo qualquer regi\(~ao automaticamente detectada que ocupe o mesmo espa\(,co na tela. Definido no formato screen[]:WIDTHxHEIGHT+X+Y, e.g.\& screen[1]:800x1200+0+0. .It Cm term_width Configura a largura m\('inima preferida para o terminal Se esse valor for maior do que 0, .Nm vai tentar ajustar os tamanhos da fonte no terminal para manter a largura do terminal acima desse n\('umero enquanto a janela \('e redimencionada. Apenas o .Xr xterm 1 \('e suportado atualmente. O bin\('ario do .Xr xterm 1 n\(~ao deve ser setuid ou setgid, que \('e o default em muitos sistemas. Os usu\('arios podem precisar de configurar program[term] (veja a se\(,c\(~ao .Sx PROGRAMAS ) para usar uma c\('opia alternativa do bin\('ario do .Xr xterm 1 sem o bit setgid ativado. .It Cm title_class_enabled Habilita ou desabilita mostrar a classe da janela na barra de status. Habilite configurando para 1. .It Cm title_name_enabled Habilita ou desabilita mostrar o t\('itulo da janela na barra de status. Habilite configurando para 1. .It Cm window_name_enabled Habilita ou desabilita mostrar a nome da janela na barra de status. Habilite configurando para 1. .It Cm modkey Muda a tecla de modifica\(,c\(~ao. Mod1 \('e geralmente a tecla ALT e Mod4 \('e a tecla windows em um PC. .It Cm focus_mode Usar um valor de follow_cursor vai fazer o gerenciador de janela focar a janela sob o mouse quando trocando \('areas de trabalho e criando janelas. .It Cm disable_border Remove a borda quando a barra estiver desabilitada e houver apenas uma janela na tela. .It Cm program Ns Bq Ar p Define uma nova a\(,c\(~ao para executar um programa .Ar p . Veja a se\(,c\(~ao .Sx PROGRAMAS logo abaixo. .It Cm bind Ns Bq Ar x Cria uma combina\(,c\(~ao de teclas de atalho para a a\(,c\(~ao .Ar x . Veja a se\(,c\(~ao .Sx ATALHOS logo abaixo. .It Cm quirk Ns Bq Ar c:n Adicione "quirk" para janelas com classe .Ar c e nome .Ar n . Veja a se\(,c\(~ao .Sx QUIRKS logo abaixo. .El .Pp Cores precisam ser especificadas pela especifica\(,c\(~ao .Xr XQueryColor 3 e fontes pela especifica\(,c\(~ao .Xr XQueryFont 3 . .Pp Para listar as fontes dispon\('iveis em seu sistema veja o manual do .Xr fc-list 1 ou do .Xr xlsfonts 1 . A aplica\(,c\(~ao .Xr xfontsel 1 pode te ajudar a mostrar a X Logical Font Description ("XLFD") usada na configura\(,c\(~ao da palavra-chave bar_font. .Sh PROGRAMAS .Nm te permite definir a\(,c\(~oes personalizadas para executar programas de sua escolha e ent\(~ao criar um atalho para elas da mesma forma que as a\(,c\(~oes embutidas. Veja a se\(,c\(~ao .Sx ATALHOS logo abaixo. .Pp Os programas default s\(~ao descritos abaixo: .Pp .Bl -tag -width "screenshot_wind" -offset indent -compact .It Cm term xterm .It Cm screenshot_all screenshot.sh full .It Cm screenshot_wind screenshot.sh window .It Cm lock xlock .It Cm initscr initscreen.sh .It Cm menu dmenu_run \-fn $bar_font \-nb $bar_color \-nf $bar_font_color \-sb $bar_border \-sf $bar_color .El .Pp Programas personalizados no arquivo de configura\(,c\(~ao s\(~ao especificados da seguinte maneira: .Pp .Dl program[] = [ [... ]] .Pp .Aq name \('e um identificador qualquer que n\(~ao conflite com uma a\(,c\(~ao ou palavra-chave embutida, .Aq progpath \('e o programa desejado, e .Aq arg \('e zero ou mais argumentos para o programa. .Pp As seguintes vari\('aveis representam valores configur\('aveis no .Nm (veja a se\(,c\(~ao .Sx ARQUIVOS DE CONFIGURA\(,C\(~AO logo acima), e podem ser usadas nos campos .Aq arg e ser\(~ao substitu\('idas pelos valores na hora em que o programa for executado: .Pp .Bl -tag -width "$bar_font_color" -offset indent -compact .It Cm $bar_border .It Cm $bar_color .It Cm $bar_font .It Cm $bar_font_color .It Cm $color_focus .It Cm $color_unfocus .El .Pp Exemplo: .Bd -literal -offset indent program[ff] = /usr/local/bin/firefox http://spectrwm.org/ bind[ff] = Mod+f # Agora Mod+F executa o firefox .Ed .Pp Para desfazer a configura\(,c\(~ao anterior: .Bd -literal -offset indent bind[] = Mod+f program[ff] = .Ed .Pp .Sh ATALHOS .Nm prov\(^e muitas fun\(,c\(~oes (ou a\(,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-2.6.0/spectrwm_ru.1010064400017500000000000000430771237571307300150550ustar00marcowheel.\" Copyright (c) 2009 Marco Peereboom .\" Copyright (c) 2009 Darrin Chandler .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate$ .Dt SPECTRWM 1 .Os .Sh НАЗВАНИЕ .Nm spectrwm .Nd Оконный менеджер для X11 .Sh ИСПОЛЬЗОВАНИЕ .Nm spectrwm .Sh ОПИСАНИЕ .Nm это минималистичный менеджер окон, ставящий своей целью не мешать вам и не занимать ценное пространство экрана. Его настройки по-умолчанию разумны и, кроме того, он не требует знания языков программирования для работы с конфигурационным файлом. Он написан хакерами для хакеров и старается быть легким, компактным и быстрым. .Pp Когда .Nm запускается, он читает настройки из своего конфигурационного файла, .Pa spectrwm.conf . Смотрите секцию .Sx КОНФИГУРАЦИОННЫЕ ФАЙЛЫ ниже. .Pp На этой странице используются следующие обозначения: .Pp .Bl -tag -width Ds -offset indent -compact .It Cm M Мета-клавиша .It Cm S Shift .It Aq Cm Name Имя клавиши .It Cm M1 Кнопка мыши 1 .It Cm M3 Кнопка мыши 3 .El .Pp .Nm должен быть понятным и очевидным. Большинство действий выполняется комбинациями клавиш. Смотрите секцию .Sx ПРИВЯЗКИ ниже, чтобы узнать о стандартных настройках. .Sh КОНФИГУРАЦИОННЫЕ ФАЙЛЫ .Nm пытается прочитать файл в домашнем каталоге, .Pa ~/.spectrwm.conf . В случае, если он недоступен, происходит обращение к глобальному файлу настроек, .Pa /etc/spectrwm.conf . .Pp Формат файла следующий: \*(Ltключ\*(Gt = \*(Ltзначение\*(Gt. Например: .Pp .Dl color_focus = red .Pp Однозначное включение и выключение задается значениями 1 и 0. .Pp Поддерживаются следующие ключевые слова: .Pp .Bl -tag -width "title_class_enabledXXX" -offset indent -compact .It Cm color_focus Цвет рамки окна в фокусе. .It Cm color_unfocus Цвет рамки окон не в фокусе. .It Cm bar_enabled Включение статусной строки. .It Cm bar_border Ns Bq Ar x Цвет рамки статусной строки .Ar x . .It Cm bar_color Ns Bq Ar x Цвет статусной строки .Ar x . .It Cm bar_font_color Ns Bq Ar x Цвет шрифта статусной строки .Ar x . .It Cm bar_font Тип шрифта статусной строки. .It Cm bar_action Внешний файл скрипта для статусной строки, выводящий туда информацию, например, уровень заряда батарей. .It Cm stack_enabled Включить отображение способа укладки окон в статусной строке. .It Cm clock_enabled Включить часы в статусной строке. Можно отключить, установив 0, и Вы сможете использовать собственные часы из внешнего скрипта. .It Cm dialog_ratio Ряд приложений имеет слишком маленькие диалоговые окна. Это значение - доля размера экрана, к которой они будут приведены. Например, значение 0.6 будет соответствовать 60% от реального размера экрана. .It Cm region Выделяет область экрана на Ваше усмотрение, уничтожает все перекрытые области экрана, определенные автоматически. Формат: screen[]:WIDTHxHEIGHT+X+Y, например\& screen[1]:1280x800+0+0. .It Cm term_width Установить минимальную допустимую ширину эмулятора терминала. Если это значение больше 0, .Nm попытается отмасштабировать шрифты в терминале, чтобы ширина была больше этого значения . Поодерживается только .Xr xterm 1 . Также .Xr xterm 1 не может быть с setuid или setgid, хотя это так на многих системах. Возможно необходимо задать program[term] (Смотрите секцию .Sx ПРОГРАММЫ ) чтобы использовалась другая копия .Xr xterm 1 без заданного бита setgid. .It Cm title_class_enabled Отображать класс окна в статусной строке. Обычно выключено .It Cm title_name_enabled Отображать заголовок окна в статусной строке. Обычно выключено .It Cm modkey Назначить Мета-клавишу, клавишу-модификатор. Mod1 соответствует клавише ALT, а Mod4 соответствует клавише WIN на PC. .It Cm program Ns Bq Ar p Добавить пользовательскую программу для назначения привязки .Ar p . Смотрите секцию .Sx ПРОГРАММЫ ниже. .It Cm bind Ns Bq Ar x Назначить привязку на действие .Ar x . Смотрите секцию .Sx ПРИВЯЗКИ ниже. .It Cm quirk Ns Bq Ar c:n Добавить костыль для окон с классом .Ar c и именем .Ar n . Смотрите секцию .Sx КОСТЫЛИ ниже. .El .Pp Цвета задаются с помощью .Xr XQueryColor 3 А шрифты задаются с использованием .Xr XQueryFont 3 . .Sh ПРОГРАММЫ .Nm позволяет Вам добавлять Ваши собственные действия для запуска программ и делать к ним привязки как ко всем остальным действиям Смотрите секцию .Sx ПРИВЯЗКИ ниже. .Pp Стандартные программы: .Pp .Bl -tag -width "screenshot_wind" -offset indent -compact .It Cm term xterm .It Cm screenshot_all screenshot.sh full .It Cm screenshot_wind screenshot.sh window .It Cm lock xlock .It Cm initscr initscreen.sh .It Cm menu dmenu_run \-fn $bar_font \-nb $bar_color \-nf $bar_font_color \-sb $bar_border \-sf $bar_color .El .Pp Ваши собственные программы задаются следующим образом: .Pp .Dl program[] = [ [... ]] .Pp .Aq name это любой идентификатор, не мешающийся с уже существующими, .Aq progpath это собственно путь к программе, .Aq arg это список передаваемых аргументов или оставьте пустым. .Pp Следующие переменные можно получать из .Nm (Смотрите секцию .Sx КОНФИГУРАЦИОННЫЕ ФАЙЛЫ выше), и их можно использовать как .Aq arg (в момент запуска программы будет выполнена подстановка значений): .Pp .Bl -tag -width "$bar_font_color" -offset indent -compact .It Cm $bar_border .It Cm $bar_color .It Cm $bar_font .It Cm $bar_font_color .It Cm $color_focus .It Cm $color_unfocus .El .Pp Например: .Bd -literal -offset indent program[ff] = /usr/local/bin/firefox http://spectrwm.org/ bind[ff] = Mod+f # Значит Mod+F запускает firefox .Ed .Pp Чтобы отменить назначение: .Bd -literal -offset indent bind[] = Mod+f program[ff] = .Ed .Pp .Sh ПРИВЯЗКИ .Nm предоставляет доступ к действиям с помощью клавиатурных комбинаций. .Pp Установленные привязки для мыши: .Pp .Bl -tag -width "M-j, M-XXX" -offset indent -compact .It Cm M1 Сфокусироваться на окне .It Cm M-M1 Переместить окно .It Cm M-M3 Изменить размер окна .It Cm M-S-M3 Изменить размер окна, удерживая его в центре .El .Pp Стандартные клавиатурные привязки: .Pp .Bl -tag -width "M-j, M-XXX" -offset indent -compact .It Cm M-S- Ns Aq Cm Return term .It Cm M-p menu .It Cm M-S-q quit .It Cm M-q restart .Nm .It Cm M- Ns Aq Cm Space cycle_layout .It Cm M-S- Ns Aq Cm Space reset_layout .It Cm M-h master_shrink .It Cm M-l master_grow .It Cm M-, master_add .It Cm M-. master_del .It Cm M-S-, stack_inc .It Cm M-S-. stack_del .It Cm M- Ns Aq Cm Return swap_main .It Xo .Cm M-j , .Cm M- Ns Aq Cm TAB .Xc focus_next .It Xo .Cm M-k , .Cm M-S- Ns Aq Cm TAB .Xc focus_prev .It Cm M-m focus_main .It Cm M-S-j swap_next .It Cm M-S-k swap_prev .It Cm M-b bar_toggle .It Cm M-x wind_del .It Cm M-S-x wind_kill .It Cm M- Ns Aq Ar n .Ns ws_ Ns Ar n .It Cm M-S- Ns Aq Ar n .Ns mvws_ Ns Ar n .It Cm M- Ns Aq Cm Right ws_next .It Cm M- Ns Aq Cm Left ws_prev .It Cm M-S- Ns Aq Cm Right screen_next .It Cm M-S- Ns Aq Cm Left screen_prev .It Cm M-s screenshot_all .It Cm M-S-s screenshot_wind .It Cm M-S-v version .It Cm M-t float_toggle .It Cm M-S Aq Cm Delete lock .It Cm M-S-i initscr .El .Pp Описания действий перечислены ниже: .Pp .Bl -tag -width "M-j, M-XXX" -offset indent -compact .It Cm term Запустить эмулятор терминала (Смотрите секцию .Sx ПРОГРАММЫ выше) .It Cm menu Меню (Смотрите секцию .Sx ПРОГРАММЫ выше) .It Cm quit Выйти .Nm .It Cm restart Перезапустить .Nm .It Cm cycle_layout Менять укладку окон .It Cm reset_layout Стандартная укладка .It Cm master_shrink Сжать область главного окна .It Cm master_grow Расширить область главного окна .It Cm master_add Добавить окна в главную область .It Cm master_del Убрать окна из главной области .It Cm stack_inc Увеличить число столбцов или рядов в текущей укладке .It Cm stack_del Уменьшить число столбцов или рядов в текущей укладке .It Cm swap_main Отправить текущее окно в главную область, сделать главным .It Cm focus_next Фокусироваться на следующем окне .It Cm focus_prev Фокусироваться на предыдущем окне .It Cm focus_main Фокусироваться на главном окне .It Cm swap_next Поменять со следующим окном .It Cm swap_prev Поменять со предыдущим окном .It Cm bar_toggle Выключить статусную строку на всех рабочих столах .It Cm wind_del Закрыть фокусированное окно .It Cm wind_kill Грохнуть фокусированное окно .It Cm ws_ Ns Ar n Переключиться на рабочий стол .Ar n , где .Ar n от 1 до 10 .It Cm mvws_ Ns Ar n Переместить фокусированное окно в рабочий стол .Ar n , где .Ar n от 1 до 10 .It Cm ws_next Перейти к следующему не пустому рабочему столу .It Cm ws_prev Перейти к следующему не пустому рабочему столу .It Cm screen_next Переместить указатель в следующую область .It Cm screen_prev Переместить указатель в следующую область .It Cm screenshot_all Сделать снимок всего экрана (если возможно) (Смотрите секцию .Sx ПРОГРАММЫ выше) .It Cm screenshot_wind Сделать снимок окна (если возможно) (Смотрите секцию .Sx ПРОГРАММЫ выше) .It Cm version Показать версию в статусной строке .It Cm float_toggle Переключить окно в фокусе в плавающий режим, float .It Cm lock Заблокировать экран (Смотрите секцию .Sx ПРОГРАММЫ выше) .It Cm initscr Инициализировать экран еще раз (Смотрите секцию .Sx ПРОГРАММЫ выше) .El .Pp Собственные привязки назначаются следующим образом: .Pp .Dl bind[] = .Pp .Aq action это действие из списка программ .Aq keys это не более одной клавиши-модификатора (MOD, Mod1, Shift, и.т.п.) и обычные клавиши (b, space, и.т.п.), разделенные "+". Например: .Bd -literal -offset indent bind[reset] = Mod4+q # назначить WIN + q на действие reset bind[] = Mod1+q # снять все действия с Alt + q .Ed .Pp На одно действие можно назначить несколько комбинаций. .Sh КОСТЫЛИ .Nm позволяет настроить костыли, нужные для специальной работы spectrwm с рядом приложений, который вы определяете сами. То есть, Вы можете принудительно установить способ тайлинга для какого-нибудь приложения .Pp Список стандартных костылей: .Pp .Bl -tag -width "OpenOffice.org N.M:VCLSalFrameXXX" -offset indent -compact .It Firefox\-bin:firefox\-bin TRANSSZ .It Firefox:Dialog FLOAT .It Gimp:gimp FLOAT + ANYWHERE .It MPlayer:xv FLOAT + FULLSCREEN .It OpenOffice.org 2.4:VCLSalFrame FLOAT .It OpenOffice.org 3.1:VCLSalFrame FLOAT .It pcb:pcb FLOAT .It xine:Xine Window FLOAT + ANYWHERE .It xine:xine Panel FLOAT + ANYWHERE .It xine:xine Video Fullscreen Window FULLSCREEN + FLOAT .It Xitk:Xitk Combo FLOAT + ANYWHERE .It Xitk:Xine Window FLOAT + ANYWHERE .It XTerm:xterm XTERM_FONTADJ .El .Pp Описание: .Pp .Bl -tag -width "XTERM_FONTADJXXX" -offset indent -compact .It FLOAT Такое окно не нужно тайлить вообще, разрешить ему float .It TRANSSZ Тразиентое окно (Смотрите секцию .Sx КОНФИГУРАЦИОННЫЕ ФАЙЛЫ) . .It ANYWHERE Позволить окну самостоятельно выбрать местоположение .It XTERM_FONTADJ Изменять шрифты xterm при изменении размеров окна .It FULLSCREEN Позволить окну запускаться в полноэкранном режиме .El .Pp Назначать костыли можно следующим образом: .Pp .Dl quirk[:] = [ + ... ] .Pp .Aq class и .Aq name определяют к какому окну будет применяться костыль, а .Aq quirk один из вышеперечисленных способов. Например: .Bd -literal -offset indent quirk[MPlayer:xv] = FLOAT + FULLSCREEN # mplayer настроен quirk[pcb:pcb] = NONE # убрать существующий костыль .Ed .Pp Вы можете узнать .Aq class и .Aq name запустив xprop и нажав в интересующее окно. Вот как будет выглядеть вывод для Firefox: .Bd -literal -offset indent $ xprop | grep WM_CLASS WM_CLASS(STRING) = "Navigator", "Firefox" .Ed .Pp Обратите внимание, класс и имя меняются местами, правильный костыль будет выглядеть так: .Bd -literal -offset indent quirk[Firefox:Navigator] = FLOAT .Ed .Sh ФАЙЛЫ .Bl -tag -width "/etc/spectrwm.confXXX" -compact .It Pa ~/.spectrwm.conf .Nm Личные настройки пользователя. .It Pa /etc/spectrwm.conf .Nm Глобавльные настройки. .El .Sh ИСТОРИЯ .Nm идейно основан на dwm и xmonad .Sh АВТОРЫ .An -nosplit .Pp .Nm написан: .An Marco Peereboom Aq marco@peereboom.us , .An Ryan Thomas McBride Aq mcbride@countersiege.com and .An Darrin Chandler Aq dwchandler@stilyagin.com . .Sh БАГИ При вызове меню с помощью .Cm M-p , необходима корректная работа dmenu. spectrwm-2.6.0/spectrwm_se.conf010064400017500000000000000065031237571307300156140ustar00marcowheel# 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[term] = MOD+Shift+Return bind[menu] = MOD+p bind[quit] = MOD+Shift+q bind[restart] = MOD+q bind[focus_main] = MOD+m bind[focus_urgent] = MOD+u bind[ws_1] = MOD+1 bind[ws_2] = MOD+2 bind[ws_3] = MOD+3 bind[ws_4] = MOD+4 bind[ws_5] = MOD+5 bind[ws_6] = MOD+6 bind[ws_7] = MOD+7 bind[ws_8] = MOD+8 bind[ws_9] = MOD+9 bind[ws_10] = MOD+0 bind[ws_11] = MOD+F1 bind[ws_12] = MOD+F2 bind[ws_13] = MOD+F3 bind[ws_14] = MOD+F4 bind[ws_15] = MOD+F5 bind[ws_16] = MOD+F6 bind[ws_17] = MOD+F7 bind[ws_18] = MOD+F8 bind[ws_19] = MOD+F9 bind[ws_20] = MOD+F10 bind[ws_21] = MOD+F11 bind[ws_22] = MOD+F12 bind[ws_next] = MOD+Right bind[ws_prev] = MOD+Left bind[ws_next_all] = MOD+Up bind[ws_prev_all] = MOD+Down bind[ws_next_move] = MOD+Shift+Up bind[ws_prev_move] = MOD+Shift+Down bind[ws_prior] = MOD+a bind[mvws_1] = MOD+Shift+1 bind[mvws_2] = MOD+Shift+2 bind[mvws_3] = MOD+Shift+3 bind[mvws_4] = MOD+Shift+4 bind[mvws_5] = MOD+Shift+5 bind[mvws_6] = MOD+Shift+6 bind[mvws_7] = MOD+Shift+7 bind[mvws_8] = MOD+Shift+8 bind[mvws_9] = MOD+Shift+9 bind[mvws_10] = MOD+Shift+0 bind[mvws_11] = MOD+Shift+F1 bind[mvws_12] = MOD+Shift+F2 bind[mvws_13] = MOD+Shift+F3 bind[mvws_14] = MOD+Shift+F4 bind[mvws_15] = MOD+Shift+F5 bind[mvws_16] = MOD+Shift+F6 bind[mvws_17] = MOD+Shift+F7 bind[mvws_18] = MOD+Shift+F8 bind[mvws_19] = MOD+Shift+F9 bind[mvws_20] = MOD+Shift+F10 bind[mvws_21] = MOD+Shift+F11 bind[mvws_22] = MOD+Shift+F12 bind[rg_1] = MOD+KP_End bind[rg_2] = MOD+KP_Down bind[rg_3] = MOD+KP_Next bind[rg_4] = MOD+KP_Left bind[rg_5] = MOD+KP_Begin bind[rg_6] = MOD+KP_Right bind[rg_7] = MOD+KP_Home bind[rg_8] = MOD+KP_Up bind[rg_9] = MOD+KP_Prior bind[rg_next] = MOD+Shift+Right bind[rg_prev] = MOD+Shift+Left bind[mvrg_1] = MOD+Shift+KP_End bind[mvrg_2] = MOD+Shift+KP_Down bind[mvrg_3] = MOD+Shift+KP_Next bind[mvrg_4] = MOD+Shift+KP_Left bind[mvrg_5] = MOD+Shift+KP_Begin bind[mvrg_6] = MOD+Shift+KP_Right bind[mvrg_7] = MOD+Shift+KP_Home bind[mvrg_8] = MOD+Shift+KP_Up bind[mvrg_9] = MOD+Shift+KP_Prior bind[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-2.6.0/spectrwm_us.conf010064400017500000000000000065101237571307300156320ustar00marcowheel# 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[term] = MOD+Shift+Return bind[menu] = MOD+p bind[quit] = MOD+Shift+q bind[restart] = MOD+q bind[focus_main] = MOD+m bind[focus_urgent] = MOD+u bind[ws_1] = MOD+1 bind[ws_2] = MOD+2 bind[ws_3] = MOD+3 bind[ws_4] = MOD+4 bind[ws_5] = MOD+5 bind[ws_6] = MOD+6 bind[ws_7] = MOD+7 bind[ws_8] = MOD+8 bind[ws_9] = MOD+9 bind[ws_10] = MOD+0 bind[ws_11] = MOD+F1 bind[ws_12] = MOD+F2 bind[ws_13] = MOD+F3 bind[ws_14] = MOD+F4 bind[ws_15] = MOD+F5 bind[ws_16] = MOD+F6 bind[ws_17] = MOD+F7 bind[ws_18] = MOD+F8 bind[ws_19] = MOD+F9 bind[ws_20] = MOD+F10 bind[ws_21] = MOD+F11 bind[ws_22] = MOD+F12 bind[ws_next] = MOD+Right bind[ws_prev] = MOD+Left bind[ws_next_all] = MOD+Up bind[ws_prev_all] = MOD+Down bind[ws_next_move] = MOD+Shift+Up bind[ws_prev_move] = MOD+Shift+Down bind[ws_prior] = MOD+a bind[mvws_1] = MOD+Shift+1 bind[mvws_2] = MOD+Shift+2 bind[mvws_3] = MOD+Shift+3 bind[mvws_4] = MOD+Shift+4 bind[mvws_5] = MOD+Shift+5 bind[mvws_6] = MOD+Shift+6 bind[mvws_7] = MOD+Shift+7 bind[mvws_8] = MOD+Shift+8 bind[mvws_9] = MOD+Shift+9 bind[mvws_10] = MOD+Shift+0 bind[mvws_11] = MOD+Shift+F1 bind[mvws_12] = MOD+Shift+F2 bind[mvws_13] = MOD+Shift+F3 bind[mvws_14] = MOD+Shift+F4 bind[mvws_15] = MOD+Shift+F5 bind[mvws_16] = MOD+Shift+F6 bind[mvws_17] = MOD+Shift+F7 bind[mvws_18] = MOD+Shift+F8 bind[mvws_19] = MOD+Shift+F9 bind[mvws_20] = MOD+Shift+F10 bind[mvws_21] = MOD+Shift+F11 bind[mvws_22] = MOD+Shift+F12 bind[rg_1] = MOD+KP_End bind[rg_2] = MOD+KP_Down bind[rg_3] = MOD+KP_Next bind[rg_4] = MOD+KP_Left bind[rg_5] = MOD+KP_Begin bind[rg_6] = MOD+KP_Right bind[rg_7] = MOD+KP_Home bind[rg_8] = MOD+KP_Up bind[rg_9] = MOD+KP_Prior bind[rg_next] = MOD+Shift+Right bind[rg_prev] = MOD+Shift+Left bind[mvrg_1] = MOD+Shift+KP_End bind[mvrg_2] = MOD+Shift+KP_Down bind[mvrg_3] = MOD+Shift+KP_Next bind[mvrg_4] = MOD+Shift+KP_Left bind[mvrg_5] = MOD+Shift+KP_Begin bind[mvrg_6] = MOD+Shift+KP_Right bind[mvrg_7] = MOD+Shift+KP_Home bind[mvrg_8] = MOD+Shift+KP_Up bind[mvrg_9] = MOD+Shift+KP_Prior bind[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