├── Makefile ├── README ├── calmwm.c ├── calmwm.h ├── client.c ├── conf.c ├── cwm.1 ├── cwm.pub ├── cwmrc.5 ├── group.c ├── kbfunc.c ├── menu.c ├── migrate-config.pl ├── parse.y ├── queue.h ├── reallocarray.c ├── screen.c ├── search.c ├── strlcat.c ├── strlcpy.c ├── strtonum.c ├── update.sh ├── util.c ├── xevents.c ├── xmalloc.c └── xutil.c /Makefile: -------------------------------------------------------------------------------- 1 | # cwm makefile for BSD make and GNU make 2 | # uses pkg-config, DESTDIR and PREFIX 3 | 4 | PROG= cwm 5 | 6 | PREFIX?= /usr/local 7 | 8 | SRCS= calmwm.c screen.c xmalloc.c client.c menu.c \ 9 | search.c util.c xutil.c conf.c xevents.c group.c \ 10 | kbfunc.c parse.y 11 | 12 | OBJS= calmwm.o screen.o xmalloc.o client.o menu.o \ 13 | search.o util.o xutil.o conf.o xevents.o group.o \ 14 | kbfunc.o strlcpy.o strlcat.o parse.o \ 15 | strtonum.o reallocarray.o 16 | 17 | PKG_CONFIG?= pkg-config 18 | 19 | CPPFLAGS+= `${PKG_CONFIG} --cflags x11 xft xrandr` 20 | 21 | CFLAGS?= -Wall -O2 -g -D_GNU_SOURCE 22 | 23 | LDFLAGS+= `${PKG_CONFIG} --libs x11 xft xrandr` 24 | 25 | MANPREFIX?= ${PREFIX}/share/man 26 | 27 | all: ${PROG} 28 | 29 | clean: 30 | rm -f ${OBJS} ${PROG} parse.c 31 | 32 | ${PROG}: ${OBJS} 33 | ${CC} ${OBJS} ${LDFLAGS} -o ${PROG} 34 | 35 | .c.o: 36 | ${CC} -c ${CFLAGS} ${CPPFLAGS} $< 37 | 38 | install: ${PROG} 39 | install -d ${DESTDIR}${PREFIX}/bin ${DESTDIR}${MANPREFIX}/man1 ${DESTDIR}${MANPREFIX}/man5 40 | install -m 755 cwm ${DESTDIR}${PREFIX}/bin 41 | install -m 644 cwm.1 ${DESTDIR}${MANPREFIX}/man1 42 | install -m 644 cwmrc.5 ${DESTDIR}${MANPREFIX}/man5 43 | 44 | release: 45 | VERSION=$$(git describe --tags | sed 's/^v//;s/-[^.]*$$//') && \ 46 | git archive --prefix=cwm-$$VERSION/ -o cwm-$$VERSION.tar.gz HEAD 47 | 48 | sign: 49 | VERSION=$$(git describe --tags | sed 's/^v//;s/-[^.]*$$//') && \ 50 | gpg2 --armor --detach-sign cwm-$$VERSION.tar.gz && \ 51 | signify -S -s ~/.signify/cwm.sec -m cwm-$$VERSION.tar.gz && \ 52 | sed -i '1cuntrusted comment: verify with cwm.pub' cwm-$$VERSION.tar.gz.sig 53 | 54 | .PRECIOUS: parse.c 55 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is a port of OpenBSD's excellent cwm[0] to Linux and other Unices. 2 | 3 | cwm is a window manager for X11 which contains many features that 4 | concentrate on the efficiency and transparency of window 5 | management. cwm also aims to maintain the simplest and most 6 | pleasant aesthetic. 7 | 8 | This port requires pkg-config, Xft, Xinerama and Xrandr. The included Makefile 9 | should work with both GNU make and BSD make. It has been built successfully on 10 | OpenBSD, FreeBSD, NetBSD, OS X 10.9 and Linux. 11 | 12 | This version actively tracks changes in the OpenBSD CVS repository. 13 | Releases are roughly coordinated. 14 | 15 | The revision controlled version is at https://github.com/leahneukirchen/cwm 16 | Releases can be found at http://leahneukirchen.org/releases 17 | 18 | You are welcome to join the IRC channel ##cwm on irc.libera.chat 19 | to talk about cwm. 20 | 21 | 22 | ChangeLog: 23 | 24 | 2012-05-02: First public release 5.1 of portable cwm. 25 | 26 | 2014-04-13: Second public release 5.5 of portable cwm. 27 | 28 | User visible changes (for a full list including smaller bug fixes, see 29 | http://www.openbsd.org/plus.html ff.) 30 | 31 | Changes made between OpenBSD 5.1 and 5.2 32 | * Fixed cwm(1) atom (WM_PROTOCOLS) style handing; closing a window will no 33 | longer close entire application if the client supports CLIENT_PROTO_DELETE. 34 | * Re-implement atom handing for more consistent separation of cwm(1) and 35 | Extended Window Manager Hints. 36 | * cwm(1) better integrated into the freedesktop.org Window Manager 37 | Specification Project. 38 | 39 | Changes made between OpenBSD 5.2 and 5.3 40 | * Set the initial cwm(1) group to "1". 41 | * Added cwm(1) per-group vert/horiz tiling support with new bind commands 42 | "vtile" and "htile." 43 | * Made cwm(1) screen font an Xft(3) font. 44 | * Specific last match for autogroup in cwmrc(5). 45 | * Tab completion support for cwm(1) menus. 46 | * Allow cwm(1) clients to be resized from a max state. 47 | * Multibyte input to cwm(1) menu code now possible. 48 | 49 | Changes made between OpenBSD 5.3 and 5.4 50 | * Added support for mouse based group {,r}cycle to cwmrc(5). 51 | * Allow mouse button4 and button5 in cwmrc(5). 52 | * Made cwm(1) check for, and honour, CWStackMode and CWSibling change requests 53 | during a ConfigureRequest event. 54 | * Honour PATH search order for cwm(1)'s exec. 55 | 56 | Changes made between OpenBSD 5.5 and 5.4 57 | * Only set the cwm(1) urgency flag if the client is not active. 58 | * Allow the cwm(1) config parser continue parsing even after encountering an 59 | error. 60 | * cwm(1) now follows the EWMH spec: if the cardinal returned is 0xFFFFFFFF (-1) 61 | then the window should appear on all desktops. 62 | * Made cwm(1) supply a more useful title for windows launched via the ssh(1) 63 | command menu ("[ssh] "). 64 | * Allowed cwm(1) to accept _NET_WM_DESKTOP and _NET_CURRENT_DESKTOP 65 | ClientMessage. 66 | * Implemented cwm(1) support for _NET_WM_STATE_FULLSCREEN hint, with keybinding 67 | changes: CM-f "fullscreen", CM-m "maximize". 68 | * Instead of using the work area, use the Xinerama area for cwm(1) snap 69 | calculations. 70 | * Save-set when cwm(1) is re-exec'ing so as to not lose State on our hidden 71 | clients. 72 | * Added cwmrc(5) support for XUrgency and matching 73 | _NET_WM_STATE_DEMANDS_ATTENTION ewmh hint, with configurable urgencyborder. 74 | * Prepend the group shortcut in the cwm(1) client search menu; 75 | prepend shortcut in unhide menu. 76 | * If not hidden during an UnmapNotify event, cwm(1) will now un-manage the 77 | client. 78 | * Like "gap", made cwm(1) "snapdist" per-screen. 79 | * Removed cwmrc(5) option to bind a key by keycode with brackets, which never 80 | worked. Users should be using keysym names not keycodes. 81 | * Re-added cwm(1) support for WM_TAKE_FOCUS. Solves keyboard input focus loss 82 | for java apps. 83 | * For cwm(1) clients that support WM_TAKE_FOCUS in their WM_PROTOCOLS property, 84 | send a ClientMessage event. 85 | 86 | 2015-01-24: Third public release 5.6 of portable cwm. 87 | 88 | * Support building on FreeBSD and OS X. 89 | * Support for sticky windows (_NET_WM_STATE_STICKY). 90 | * Internal cleanups and bug fixes. 91 | 92 | Changes made between OpenBSD 5.6 and 5.7 93 | * Implemented _NET_WM_STATE_STICKY in cwm(1). Allows client to "stick" 94 | to all desktops or groups. 95 | * Ensure cwm(1) client that wants to be in nogroup stays in nogroup 96 | (thus stays in view), even when (re)reading NET_WM_DESKTOP. 97 | 98 | Changes made between OpenBSD 5.7 and 5.8 99 | * In cwm(1), introduce "groupsearch" for group menu search. 100 | * In cwm(1), show an empty "ssh to" menu if the known_hosts file is missing. 101 | * In cwm(1), replace screen region info gathering with XRandR 102 | equivalent of Xinerama queries. 103 | 104 | Changes made between OpenBSD 5.8 and 5.9 105 | * Don't allow freeze operations on fullscreen. 106 | * Implement _NET_CLIENT_LIST_STACKING. 107 | 108 | 2017-10-17: Fourth public release 6.2 of portable cwm. 109 | 110 | Changes made between OpenBSD 6.2 and 6.3 111 | * Fix blocking bug during moving or resizing. 112 | * window-snap-* commands to move windows to edges and corners. 113 | * Add support for _NET_WM_STATE_SKIP_PAGER and _NET_WM_STATE_SKIP_TASKBAR. 114 | * Add support for re-exec'ing with SIGHUP. 115 | 116 | 2018-05-14: Fifth public release 6.3 of portable cwm. 117 | 118 | 2020-01-04: Sixth public release 6.6 of portable cwm. 119 | 120 | Changes made between OpenBSD 6.4 and 6.5 121 | * Added a configtest flag (-n) to cwm(1). 122 | * Introduced 'group-close-[n]' action to cwm(1) to close all windows 123 | within a specified group. 124 | 125 | 2020-05-22: Seventh public release 6.7 of portable cwm. 126 | 127 | Changes made between OpenBSD 6.6 and 6.7 128 | * Allowed cwm(1) configuration of window size based on percentage of 129 | the master window during horizontal and vertical tiling actions. 130 | * Allowed use of window-htile and window-vtile with the "empty" group 131 | clients in cwm(1). 132 | 133 | 2022-04-30: Eighth public release 7.1 of portable cwm. 134 | 135 | Changes made between OpenBSD 6.9 and 7.0 136 | * Changed cwm(1) maximization and full-screen mode toggling to keep 137 | the cursor within the window, preventing focus loss. 138 | 139 | Changes made between OpenBSD 7.0 and 7.1 140 | * Added a cwm(1) "group-last" command that shows only the previously 141 | active group. 142 | * Allowed bare numbers for key and mouse bindings in cwm(1). 143 | 144 | 2023-10-20: Ninth public release 7.4 of portable cwm. 145 | 146 | Changes made between OpenBSD 7.3 and 7.4: 147 | * Allow cwm(1) to cycle through windows of the same window class as 148 | the active window, default key binding to M-grave, respectively 149 | Alt-Tilde, like with other window managers. 150 | 151 | --Leah Neukirchen 152 | 153 | [0]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/xenocara/app/cwm/ 154 | -------------------------------------------------------------------------------- /calmwm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * calmwm - the calm window manager 3 | * 4 | * Copyright (c) 2004 Marius Aamodt Eriksen 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * $OpenBSD$ 19 | */ 20 | 21 | #include 22 | #include "queue.h" 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "calmwm.h" 38 | 39 | Display *X_Dpy; 40 | Time Last_Event_Time = CurrentTime; 41 | Atom cwmh[CWMH_NITEMS]; 42 | Atom ewmh[EWMH_NITEMS]; 43 | struct screen_q Screenq = TAILQ_HEAD_INITIALIZER(Screenq); 44 | struct conf Conf; 45 | volatile sig_atomic_t cwm_status; 46 | 47 | void usage(void); 48 | static void sighdlr(int); 49 | static int x_errorhandler(Display *, XErrorEvent *); 50 | static int x_init(const char *); 51 | static void x_teardown(void); 52 | static int x_wmerrorhandler(Display *, XErrorEvent *); 53 | 54 | int 55 | main(int argc, char **argv) 56 | { 57 | char *display_name = NULL; 58 | char *fallback; 59 | int ch, xfd, nflag = 0; 60 | struct pollfd pfd[1]; 61 | 62 | if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 63 | warnx("no locale support"); 64 | mbtowc(NULL, NULL, MB_CUR_MAX); 65 | 66 | conf_init(&Conf); 67 | 68 | fallback = u_argv(argv); 69 | Conf.wm_argv = u_argv(argv); 70 | while ((ch = getopt(argc, argv, "c:d:nv")) != -1) { 71 | switch (ch) { 72 | case 'c': 73 | free(Conf.conf_file); 74 | Conf.conf_file = xstrdup(optarg); 75 | break; 76 | case 'd': 77 | display_name = optarg; 78 | break; 79 | case 'n': 80 | nflag = 1; 81 | break; 82 | case 'v': 83 | Conf.debug++; 84 | break; 85 | default: 86 | usage(); 87 | } 88 | } 89 | argc -= optind; 90 | argv += optind; 91 | 92 | if (signal(SIGCHLD, sighdlr) == SIG_ERR || 93 | signal(SIGHUP, sighdlr) == SIG_ERR || 94 | signal(SIGINT, sighdlr) == SIG_ERR || 95 | signal(SIGTERM, sighdlr) == SIG_ERR) 96 | err(1, "signal"); 97 | 98 | if (parse_config(Conf.conf_file, &Conf) == -1) { 99 | warnx("error parsing config file"); 100 | if (nflag) 101 | return 1; 102 | } 103 | if (nflag) 104 | return 0; 105 | 106 | xfd = x_init(display_name); 107 | cwm_status = CWM_RUNNING; 108 | 109 | #ifdef __OpenBSD__ 110 | if (pledge("stdio rpath proc exec", NULL) == -1) 111 | err(1, "pledge"); 112 | #endif 113 | 114 | memset(&pfd, 0, sizeof(pfd)); 115 | pfd[0].fd = xfd; 116 | pfd[0].events = POLLIN; 117 | while (cwm_status == CWM_RUNNING) { 118 | xev_process(); 119 | if (poll(pfd, 1, -1) == -1) { 120 | if (errno != EINTR) 121 | warn("poll"); 122 | } 123 | } 124 | x_teardown(); 125 | if (cwm_status == CWM_EXEC_WM) { 126 | u_exec(Conf.wm_argv); 127 | warnx("'%s' failed to start, starting fallback", Conf.wm_argv); 128 | u_exec(fallback); 129 | } 130 | 131 | return 0; 132 | } 133 | 134 | static int 135 | x_init(const char *dpyname) 136 | { 137 | int i; 138 | 139 | if ((X_Dpy = XOpenDisplay(dpyname)) == NULL) 140 | errx(1, "unable to open display \"%s\"", XDisplayName(dpyname)); 141 | 142 | XSetErrorHandler(x_wmerrorhandler); 143 | XSelectInput(X_Dpy, DefaultRootWindow(X_Dpy), SubstructureRedirectMask); 144 | XSync(X_Dpy, False); 145 | XSetErrorHandler(x_errorhandler); 146 | 147 | Conf.xrandr = XRRQueryExtension(X_Dpy, &Conf.xrandr_event_base, &i); 148 | 149 | xu_atom_init(); 150 | conf_cursor(&Conf); 151 | 152 | for (i = 0; i < ScreenCount(X_Dpy); i++) 153 | screen_init(i); 154 | 155 | return ConnectionNumber(X_Dpy); 156 | } 157 | 158 | static void 159 | x_teardown(void) 160 | { 161 | struct screen_ctx *sc; 162 | unsigned int i; 163 | 164 | conf_clear(&Conf); 165 | 166 | TAILQ_FOREACH(sc, &Screenq, entry) { 167 | for (i = 0; i < CWM_COLOR_NITEMS; i++) 168 | XftColorFree(X_Dpy, DefaultVisual(X_Dpy, sc->which), 169 | DefaultColormap(X_Dpy, sc->which), 170 | &sc->xftcolor[i]); 171 | XftFontClose(X_Dpy, sc->xftfont); 172 | XUngrabKey(X_Dpy, AnyKey, AnyModifier, sc->rootwin); 173 | } 174 | XUngrabPointer(X_Dpy, CurrentTime); 175 | XUngrabKeyboard(X_Dpy, CurrentTime); 176 | for (i = 0; i < CF_NITEMS; i++) 177 | XFreeCursor(X_Dpy, Conf.cursor[i]); 178 | XSync(X_Dpy, False); 179 | XSetInputFocus(X_Dpy, PointerRoot, RevertToPointerRoot, CurrentTime); 180 | XCloseDisplay(X_Dpy); 181 | } 182 | 183 | static int 184 | x_wmerrorhandler(Display *dpy, XErrorEvent *e) 185 | { 186 | errx(1, "root window unavailable - perhaps another wm is running?"); 187 | return 0; 188 | } 189 | 190 | static int 191 | x_errorhandler(Display *dpy, XErrorEvent *e) 192 | { 193 | #ifdef DEBUG 194 | char msg[80], number[80], req[80]; 195 | 196 | XGetErrorText(X_Dpy, e->error_code, msg, sizeof(msg)); 197 | (void)snprintf(number, sizeof(number), "%d", e->request_code); 198 | XGetErrorDatabaseText(X_Dpy, "XRequest", number, 199 | "", req, sizeof(req)); 200 | 201 | warnx("%s(0x%x): %s", req, (unsigned int)e->resourceid, msg); 202 | #endif 203 | return 0; 204 | } 205 | 206 | static void 207 | sighdlr(int sig) 208 | { 209 | pid_t pid; 210 | int save_errno = errno, status; 211 | 212 | switch (sig) { 213 | case SIGCHLD: 214 | /* Collect dead children. */ 215 | while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || 216 | (pid < 0 && errno == EINTR)) 217 | ; 218 | break; 219 | case SIGHUP: 220 | cwm_status = CWM_EXEC_WM; 221 | break; 222 | case SIGINT: 223 | case SIGTERM: 224 | cwm_status = CWM_QUIT; 225 | break; 226 | } 227 | 228 | errno = save_errno; 229 | } 230 | 231 | void 232 | usage(void) 233 | { 234 | extern char *__progname; 235 | 236 | (void)fprintf(stderr, "usage: %s [-nv] [-c file] [-d display]\n", 237 | __progname); 238 | exit(1); 239 | } 240 | -------------------------------------------------------------------------------- /calmwm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * calmwm - the calm window manager 3 | * 4 | * Copyright (c) 2004 Marius Aamodt Eriksen 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * $OpenBSD$ 19 | */ 20 | 21 | #ifndef _CALMWM_H_ 22 | #define _CALMWM_H_ 23 | 24 | #include 25 | #include 26 | #include "queue.h" 27 | 28 | /* prototypes for portable-included functions */ 29 | char *fgetln(FILE *, size_t *); 30 | long long strtonum(const char *, long long, long long, const char **); 31 | void *reallocarray(void *, size_t, size_t); 32 | 33 | 34 | #ifdef strlcat 35 | #define HAVE_STRLCAT 36 | #else 37 | size_t strlcat(char *, const char *, size_t); 38 | #endif 39 | #ifdef strlcpy 40 | #define HAVE_STRLCPY 41 | #else 42 | size_t strlcpy(char *, const char *, size_t); 43 | #endif 44 | 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | #define LOG_DEBUG0(...) log_debug(0, __func__, __VA_ARGS__) 56 | #define LOG_DEBUG1(...) log_debug(1, __func__, __VA_ARGS__) 57 | #define LOG_DEBUG2(...) log_debug(2, __func__, __VA_ARGS__) 58 | #define LOG_DEBUG3(...) log_debug(3, __func__, __VA_ARGS__) 59 | 60 | #undef MIN 61 | #undef MAX 62 | #define MIN(x, y) ((x) < (y) ? (x) : (y)) 63 | #define MAX(x, y) ((x) > (y) ? (x) : (y)) 64 | 65 | #ifndef nitems 66 | #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) 67 | #endif 68 | 69 | #define BUTTONMASK (ButtonPressMask | ButtonReleaseMask) 70 | #define MOUSEMASK (BUTTONMASK | PointerMotionMask) 71 | #define IGNOREMODMASK (LockMask | Mod2Mask | 0x2000) 72 | 73 | /* direction/amount */ 74 | #define CWM_UP 0x0001 75 | #define CWM_DOWN 0x0002 76 | #define CWM_LEFT 0x0004 77 | #define CWM_RIGHT 0x0008 78 | #define CWM_BIGAMOUNT 0x0010 79 | #define CWM_UP_BIG (CWM_UP | CWM_BIGAMOUNT) 80 | #define CWM_DOWN_BIG (CWM_DOWN | CWM_BIGAMOUNT) 81 | #define CWM_LEFT_BIG (CWM_LEFT | CWM_BIGAMOUNT) 82 | #define CWM_RIGHT_BIG (CWM_RIGHT | CWM_BIGAMOUNT) 83 | #define CWM_UP_RIGHT (CWM_UP | CWM_RIGHT) 84 | #define CWM_UP_LEFT (CWM_UP | CWM_LEFT) 85 | #define CWM_DOWN_RIGHT (CWM_DOWN | CWM_RIGHT) 86 | #define CWM_DOWN_LEFT (CWM_DOWN | CWM_LEFT) 87 | 88 | #define CWM_CYCLE_FORWARD 0x0001 89 | #define CWM_CYCLE_REVERSE 0x0002 90 | #define CWM_CYCLE_INGROUP 0x0004 91 | #define CWM_CYCLE_INCLASS 0x0008 92 | 93 | enum cwm_status { 94 | CWM_QUIT, 95 | CWM_RUNNING, 96 | CWM_EXEC_WM 97 | }; 98 | enum cursor_font { 99 | CF_NORMAL, 100 | CF_MOVE, 101 | CF_RESIZE, 102 | CF_QUESTION, 103 | CF_NITEMS 104 | }; 105 | enum color { 106 | CWM_COLOR_BORDER_ACTIVE, 107 | CWM_COLOR_BORDER_INACTIVE, 108 | CWM_COLOR_BORDER_URGENCY, 109 | CWM_COLOR_BORDER_GROUP, 110 | CWM_COLOR_BORDER_UNGROUP, 111 | CWM_COLOR_MENU_FG, 112 | CWM_COLOR_MENU_BG, 113 | CWM_COLOR_MENU_FONT, 114 | CWM_COLOR_MENU_FONT_SEL, 115 | CWM_COLOR_NITEMS 116 | }; 117 | 118 | struct geom { 119 | int x; 120 | int y; 121 | int w; 122 | int h; 123 | }; 124 | struct gap { 125 | int top; 126 | int bottom; 127 | int left; 128 | int right; 129 | }; 130 | 131 | struct winname { 132 | TAILQ_ENTRY(winname) entry; 133 | char *name; 134 | }; 135 | TAILQ_HEAD(name_q, winname); 136 | TAILQ_HEAD(ignore_q, winname); 137 | 138 | struct client_ctx { 139 | TAILQ_ENTRY(client_ctx) entry; 140 | struct screen_ctx *sc; 141 | struct group_ctx *gc; 142 | Window win; 143 | Colormap colormap; 144 | int bwidth; /* border width */ 145 | int obwidth; /* original border width */ 146 | struct geom geom, savegeom, fullgeom; 147 | struct { 148 | long flags; /* defined hints */ 149 | int basew; /* desired width */ 150 | int baseh; /* desired height */ 151 | int minw; /* minimum width */ 152 | int minh; /* minimum height */ 153 | int maxw; /* maximum width */ 154 | int maxh; /* maximum height */ 155 | int incw; /* width increment progression */ 156 | int inch; /* height increment progression */ 157 | float mina; /* minimum aspect ratio */ 158 | float maxa; /* maximum aspect ratio */ 159 | } hint; 160 | struct { 161 | int x; /* x position */ 162 | int y; /* y position */ 163 | } ptr; 164 | struct { 165 | int h; /* height */ 166 | int w; /* width */ 167 | } dim; 168 | #define CLIENT_HIDDEN 0x0001 169 | #define CLIENT_IGNORE 0x0002 170 | #define CLIENT_VMAXIMIZED 0x0004 171 | #define CLIENT_HMAXIMIZED 0x0008 172 | #define CLIENT_FREEZE 0x0010 173 | #define CLIENT_GROUP 0x0020 174 | #define CLIENT_UNGROUP 0x0040 175 | #define CLIENT_INPUT 0x0080 176 | #define CLIENT_WM_DELETE_WINDOW 0x0100 177 | #define CLIENT_WM_TAKE_FOCUS 0x0200 178 | #define CLIENT_URGENCY 0x0400 179 | #define CLIENT_FULLSCREEN 0x0800 180 | #define CLIENT_STICKY 0x1000 181 | #define CLIENT_ACTIVE 0x2000 182 | #define CLIENT_SKIP_PAGER 0x4000 183 | #define CLIENT_SKIP_TASKBAR 0x8000 184 | 185 | #define CLIENT_SKIP_CYCLE (CLIENT_HIDDEN | CLIENT_IGNORE | \ 186 | CLIENT_SKIP_TASKBAR | CLIENT_SKIP_PAGER) 187 | #define CLIENT_HIGHLIGHT (CLIENT_GROUP | CLIENT_UNGROUP) 188 | #define CLIENT_MAXFLAGS (CLIENT_VMAXIMIZED | CLIENT_HMAXIMIZED) 189 | #define CLIENT_MAXIMIZED (CLIENT_VMAXIMIZED | CLIENT_HMAXIMIZED) 190 | int flags; 191 | int stackingorder; 192 | struct name_q nameq; 193 | char *name; 194 | char *label; 195 | char *res_class; /* class hint */ 196 | char *res_name; /* class hint */ 197 | int initial_state; /* wm hint */ 198 | }; 199 | TAILQ_HEAD(client_q, client_ctx); 200 | 201 | struct group_ctx { 202 | TAILQ_ENTRY(group_ctx) entry; 203 | struct screen_ctx *sc; 204 | char *name; 205 | int num; 206 | }; 207 | TAILQ_HEAD(group_q, group_ctx); 208 | 209 | struct autogroup { 210 | TAILQ_ENTRY(autogroup) entry; 211 | char *class; 212 | char *name; 213 | int num; 214 | }; 215 | TAILQ_HEAD(autogroup_q, autogroup); 216 | 217 | struct region_ctx { 218 | TAILQ_ENTRY(region_ctx) entry; 219 | int num; 220 | struct geom view; /* viewable area */ 221 | struct geom work; /* workable area, gap-applied */ 222 | }; 223 | TAILQ_HEAD(region_q, region_ctx); 224 | 225 | struct screen_ctx { 226 | TAILQ_ENTRY(screen_ctx) entry; 227 | int which; 228 | Window rootwin; 229 | int cycling; 230 | int hideall; 231 | int snapdist; 232 | struct geom view; /* viewable area */ 233 | struct geom work; /* workable area, gap-applied */ 234 | struct gap gap; 235 | struct client_q clientq; 236 | struct region_q regionq; 237 | struct group_q groupq; 238 | struct group_ctx *group_active; 239 | struct group_ctx *group_last; 240 | Colormap colormap; 241 | Visual *visual; 242 | struct { 243 | Window win; 244 | XftDraw *xftdraw; 245 | } prop; 246 | XftColor xftcolor[CWM_COLOR_NITEMS]; 247 | XftFont *xftfont; 248 | }; 249 | TAILQ_HEAD(screen_q, screen_ctx); 250 | 251 | struct cargs { 252 | char *cmd; 253 | int flag; 254 | enum { 255 | CWM_XEV_KEY, 256 | CWM_XEV_BTN 257 | } xev; 258 | }; 259 | enum context { 260 | CWM_CONTEXT_NONE = 0, 261 | CWM_CONTEXT_CC, 262 | CWM_CONTEXT_SC 263 | }; 264 | struct bind_ctx { 265 | TAILQ_ENTRY(bind_ctx) entry; 266 | void (*callback)(void *, struct cargs *); 267 | struct cargs *cargs; 268 | enum context context; 269 | unsigned int modmask; 270 | union { 271 | KeySym keysym; 272 | unsigned int button; 273 | } press; 274 | }; 275 | TAILQ_HEAD(keybind_q, bind_ctx); 276 | TAILQ_HEAD(mousebind_q, bind_ctx); 277 | 278 | struct cmd_ctx { 279 | TAILQ_ENTRY(cmd_ctx) entry; 280 | char *name; 281 | char *path; 282 | }; 283 | TAILQ_HEAD(cmd_q, cmd_ctx); 284 | TAILQ_HEAD(wm_q, cmd_ctx); 285 | 286 | #define CWM_MENU_DUMMY 0x0001 287 | #define CWM_MENU_FILE 0x0002 288 | #define CWM_MENU_LIST 0x0004 289 | #define CWM_MENU_WINDOW_ALL 0x0008 290 | #define CWM_MENU_WINDOW_HIDDEN 0x0010 291 | 292 | struct menu { 293 | TAILQ_ENTRY(menu) entry; 294 | TAILQ_ENTRY(menu) resultentry; 295 | #define MENU_MAXENTRY 200 296 | char text[MENU_MAXENTRY + 1]; 297 | char print[MENU_MAXENTRY + 1]; 298 | void *ctx; 299 | short dummy; 300 | short abort; 301 | }; 302 | TAILQ_HEAD(menu_q, menu); 303 | 304 | struct conf { 305 | struct keybind_q keybindq; 306 | struct mousebind_q mousebindq; 307 | struct autogroup_q autogroupq; 308 | struct ignore_q ignoreq; 309 | struct cmd_q cmdq; 310 | struct wm_q wmq; 311 | int ngroups; 312 | int stickygroups; 313 | int nameqlen; 314 | int bwidth; 315 | int mamount; 316 | int snapdist; 317 | int htile; 318 | int vtile; 319 | struct gap gap; 320 | char *color[CWM_COLOR_NITEMS]; 321 | char *font; 322 | char *wmname; 323 | Cursor cursor[CF_NITEMS]; 324 | int xrandr; 325 | int xrandr_event_base; 326 | char *conf_file; 327 | char *known_hosts; 328 | char *wm_argv; 329 | int debug; 330 | }; 331 | 332 | /* MWM hints */ 333 | struct mwm_hints { 334 | #define MWM_HINTS_ELEMENTS 5L 335 | 336 | #define MWM_HINTS_FUNCTIONS (1L << 0) 337 | #define MWM_HINTS_DECORATIONS (1L << 1) 338 | #define MWM_HINTS_INPUT_MODE (1L << 2) 339 | #define MWM_HINTS_STATUS (1L << 3) 340 | unsigned long flags; 341 | 342 | #define MWM_FUNC_ALL (1L << 0) 343 | #define MWM_FUNC_RESIZE (1L << 1) 344 | #define MWM_FUNC_MOVE (1L << 2) 345 | #define MWM_FUNC_MINIMIZE (1L << 3) 346 | #define MWM_FUNC_MAXIMIZE (1L << 4) 347 | #define MWM_FUNC_CLOSE (1L << 5) 348 | unsigned long functions; 349 | 350 | #define MWM_DECOR_ALL (1L << 0) 351 | #define MWM_DECOR_BORDER (1L << 1) 352 | #define MWM_DECOR_RESIZEH (1L << 2) 353 | #define MWM_DECOR_TITLE (1L << 3) 354 | #define MWM_DECOR_MENU (1L << 4) 355 | #define MWM_DECOR_MINIMIZE (1L << 5) 356 | #define MWM_DECOR_MAXIMIZE (1L << 6) 357 | unsigned long decorations; 358 | 359 | #define MWM_INPUT_MODELESS 0 360 | #define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1 361 | #define MWM_INPUT_SYSTEM_MODAL 2 362 | #define MWM_INPUT_FULL_APPLICATION_MODAL 3 363 | long inputMode; 364 | 365 | #define MWM_TEAROFF_WINDOW (1L << 0) 366 | unsigned long status; 367 | }; 368 | 369 | enum cwmh { 370 | WM_STATE, 371 | WM_DELETE_WINDOW, 372 | WM_TAKE_FOCUS, 373 | WM_PROTOCOLS, 374 | _MOTIF_WM_HINTS, 375 | UTF8_STRING, 376 | WM_CHANGE_STATE, 377 | CWMH_NITEMS 378 | }; 379 | enum ewmh { 380 | _NET_SUPPORTED, 381 | _NET_SUPPORTING_WM_CHECK, 382 | _NET_ACTIVE_WINDOW, 383 | _NET_CLIENT_LIST, 384 | _NET_CLIENT_LIST_STACKING, 385 | _NET_NUMBER_OF_DESKTOPS, 386 | _NET_CURRENT_DESKTOP, 387 | _NET_DESKTOP_VIEWPORT, 388 | _NET_DESKTOP_GEOMETRY, 389 | _NET_VIRTUAL_ROOTS, 390 | _NET_SHOWING_DESKTOP, 391 | _NET_DESKTOP_NAMES, 392 | _NET_WORKAREA, 393 | _NET_WM_NAME, 394 | _NET_WM_DESKTOP, 395 | _NET_CLOSE_WINDOW, 396 | _NET_WM_STATE, 397 | #define _NET_WM_STATES_NITEMS 9 398 | _NET_WM_STATE_STICKY, 399 | _NET_WM_STATE_MAXIMIZED_VERT, 400 | _NET_WM_STATE_MAXIMIZED_HORZ, 401 | _NET_WM_STATE_HIDDEN, 402 | _NET_WM_STATE_FULLSCREEN, 403 | _NET_WM_STATE_DEMANDS_ATTENTION, 404 | _NET_WM_STATE_SKIP_PAGER, 405 | _NET_WM_STATE_SKIP_TASKBAR, 406 | _CWM_WM_STATE_FREEZE, 407 | EWMH_NITEMS 408 | }; 409 | enum net_wm_state { 410 | _NET_WM_STATE_REMOVE, 411 | _NET_WM_STATE_ADD, 412 | _NET_WM_STATE_TOGGLE 413 | }; 414 | 415 | extern Display *X_Dpy; 416 | extern Time Last_Event_Time; 417 | extern Atom cwmh[CWMH_NITEMS]; 418 | extern Atom ewmh[EWMH_NITEMS]; 419 | extern struct screen_q Screenq; 420 | extern struct conf Conf; 421 | 422 | void usage(void); 423 | 424 | void client_apply_sizehints(struct client_ctx *); 425 | void client_close(struct client_ctx *); 426 | void client_config(struct client_ctx *); 427 | struct client_ctx *client_current(struct screen_ctx *); 428 | void client_draw_border(struct client_ctx *); 429 | struct client_ctx *client_find(Window); 430 | void client_get_sizehints(struct client_ctx *); 431 | void client_hide(struct client_ctx *); 432 | void client_htile(struct client_ctx *); 433 | int client_inbound(struct client_ctx *, int, int); 434 | struct client_ctx *client_init(Window, struct screen_ctx *); 435 | void client_lower(struct client_ctx *); 436 | void client_move(struct client_ctx *); 437 | void client_mtf(struct client_ctx *); 438 | struct client_ctx *client_next(struct client_ctx *); 439 | struct client_ctx *client_prev(struct client_ctx *); 440 | void client_ptr_inbound(struct client_ctx *, int); 441 | void client_ptr_save(struct client_ctx *); 442 | void client_ptr_warp(struct client_ctx *); 443 | void client_raise(struct client_ctx *); 444 | void client_remove(struct client_ctx *); 445 | void client_resize(struct client_ctx *, int); 446 | void client_set_active(struct client_ctx *); 447 | void client_set_name(struct client_ctx *); 448 | void client_show(struct client_ctx *); 449 | int client_snapcalc(int, int, int, int, int); 450 | void client_toggle_hidden(struct client_ctx *); 451 | void client_toggle_hmaximize(struct client_ctx *); 452 | void client_toggle_fullscreen(struct client_ctx *); 453 | void client_toggle_freeze(struct client_ctx *); 454 | void client_toggle_maximize(struct client_ctx *); 455 | void client_toggle_skip_pager(struct client_ctx *); 456 | void client_toggle_skip_taskbar(struct client_ctx *); 457 | void client_toggle_sticky(struct client_ctx *); 458 | void client_toggle_vmaximize(struct client_ctx *); 459 | void client_transient(struct client_ctx *); 460 | void client_urgency(struct client_ctx *); 461 | void client_vtile(struct client_ctx *); 462 | void client_wm_hints(struct client_ctx *); 463 | 464 | void group_assign(struct group_ctx *, struct client_ctx *); 465 | int group_autogroup(struct client_ctx *); 466 | void group_cycle(struct screen_ctx *, int); 467 | void group_hide(struct group_ctx *); 468 | int group_holds_only_hidden(struct group_ctx *); 469 | int group_holds_only_sticky(struct group_ctx *); 470 | void group_init(struct screen_ctx *, int, const char *); 471 | void group_movetogroup(struct client_ctx *, int); 472 | void group_only(struct screen_ctx *, int); 473 | void group_close(struct screen_ctx *, int); 474 | int group_restore(struct client_ctx *); 475 | void group_show(struct group_ctx *); 476 | void group_toggle(struct screen_ctx *, int); 477 | void group_toggle_all(struct screen_ctx *); 478 | void group_toggle_membership(struct client_ctx *); 479 | void group_update_names(struct screen_ctx *); 480 | 481 | void search_match_client(struct menu_q *, struct menu_q *, 482 | char *); 483 | void search_match_cmd(struct menu_q *, struct menu_q *, 484 | char *); 485 | void search_match_exec(struct menu_q *, struct menu_q *, 486 | char *); 487 | void search_match_group(struct menu_q *, struct menu_q *, 488 | char *); 489 | void search_match_path(struct menu_q *, struct menu_q *, 490 | char *); 491 | void search_match_text(struct menu_q *, struct menu_q *, 492 | char *); 493 | void search_match_wm(struct menu_q *, struct menu_q *, 494 | char *); 495 | void search_print_client(struct menu *, int); 496 | void search_print_cmd(struct menu *, int); 497 | void search_print_group(struct menu *, int); 498 | void search_print_text(struct menu *, int); 499 | void search_print_wm(struct menu *, int); 500 | 501 | struct region_ctx *region_find(struct screen_ctx *, int, int); 502 | void screen_assert_clients_within(struct screen_ctx *); 503 | struct geom screen_area(struct screen_ctx *, int, int, int); 504 | struct screen_ctx *screen_find(Window); 505 | void screen_init(int); 506 | void screen_prop_win_create(struct screen_ctx *, Window); 507 | void screen_prop_win_destroy(struct screen_ctx *); 508 | void screen_prop_win_draw(struct screen_ctx *, 509 | const char *, ...) 510 | __attribute__((__format__ (printf, 2, 3))) 511 | __attribute__((__nonnull__ (2))); 512 | void screen_update_geometry(struct screen_ctx *); 513 | void screen_updatestackingorder(struct screen_ctx *); 514 | 515 | void kbfunc_cwm_status(void *, struct cargs *); 516 | void kbfunc_ptrmove(void *, struct cargs *); 517 | void kbfunc_client_snap(void *, struct cargs *); 518 | void kbfunc_client_move(void *, struct cargs *); 519 | void kbfunc_client_resize(void *, struct cargs *); 520 | void kbfunc_client_close(void *, struct cargs *); 521 | void kbfunc_client_lower(void *, struct cargs *); 522 | void kbfunc_client_raise(void *, struct cargs *); 523 | void kbfunc_client_hide(void *, struct cargs *); 524 | void kbfunc_client_toggle_freeze(void *, struct cargs *); 525 | void kbfunc_client_toggle_sticky(void *, struct cargs *); 526 | void kbfunc_client_toggle_fullscreen(void *, 527 | struct cargs *); 528 | void kbfunc_client_toggle_maximize(void *, struct cargs *); 529 | void kbfunc_client_toggle_hmaximize(void *, struct cargs *); 530 | void kbfunc_client_toggle_vmaximize(void *, struct cargs *); 531 | void kbfunc_client_htile(void *, struct cargs *); 532 | void kbfunc_client_vtile(void *, struct cargs *); 533 | void kbfunc_client_cycle(void *, struct cargs *); 534 | void kbfunc_client_toggle_group(void *, struct cargs *); 535 | void kbfunc_client_movetogroup(void *, struct cargs *); 536 | void kbfunc_group_toggle(void *, struct cargs *); 537 | void kbfunc_group_only(void *, struct cargs *); 538 | void kbfunc_group_last(void *, struct cargs *); 539 | void kbfunc_group_close(void *, struct cargs *); 540 | void kbfunc_group_cycle(void *, struct cargs *); 541 | void kbfunc_group_toggle_all(void *, struct cargs *); 542 | void kbfunc_menu_client(void *, struct cargs *); 543 | void kbfunc_menu_cmd(void *, struct cargs *); 544 | void kbfunc_menu_group(void *, struct cargs *); 545 | void kbfunc_menu_wm(void *, struct cargs *); 546 | void kbfunc_menu_exec(void *, struct cargs *); 547 | void kbfunc_menu_ssh(void *, struct cargs *); 548 | void kbfunc_client_menu_label(void *, struct cargs *); 549 | void kbfunc_exec_cmd(void *, struct cargs *); 550 | void kbfunc_exec_lock(void *, struct cargs *); 551 | void kbfunc_exec_term(void *, struct cargs *); 552 | 553 | struct menu *menu_filter(struct screen_ctx *, struct menu_q *, 554 | const char *, const char *, int, 555 | void (*)(struct menu_q *, struct menu_q *, char *), 556 | void (*)(struct menu *, int)); 557 | void menuq_add(struct menu_q *, void *, const char *, ...) 558 | __attribute__((__format__ (printf, 3, 4))); 559 | void menuq_clear(struct menu_q *); 560 | 561 | int parse_config(const char *, struct conf *); 562 | 563 | void conf_autogroup(struct conf *, int, const char *, 564 | const char *); 565 | int conf_bind_key(struct conf *, const char *, 566 | const char *); 567 | int conf_bind_mouse(struct conf *, const char *, 568 | const char *); 569 | void conf_clear(struct conf *); 570 | void conf_client(struct client_ctx *); 571 | void conf_cmd_add(struct conf *, const char *, 572 | const char *); 573 | void conf_wm_add(struct conf *, const char *, 574 | const char *); 575 | void conf_cursor(struct conf *); 576 | void conf_grab_kbd(Window); 577 | void conf_grab_mouse(Window); 578 | void conf_init(struct conf *); 579 | void conf_ignore(struct conf *, const char *); 580 | void conf_screen(struct screen_ctx *); 581 | void conf_group(struct screen_ctx *); 582 | 583 | void xev_process(void); 584 | 585 | int xu_get_prop(Window, Atom, Atom, long, unsigned char **); 586 | int xu_get_strprop(Window, Atom, char **); 587 | void xu_ptr_get(Window, int *, int *); 588 | void xu_ptr_set(Window, int, int); 589 | void xu_get_wm_state(Window, long *); 590 | void xu_set_wm_state(Window, long); 591 | void xu_send_clientmsg(Window, Atom, Time); 592 | void xu_xorcolor(XftColor, XftColor, XftColor *); 593 | 594 | void xu_atom_init(void); 595 | void xu_ewmh_net_supported(struct screen_ctx *); 596 | void xu_ewmh_net_supported_wm_check(struct screen_ctx *); 597 | void xu_ewmh_net_desktop_geometry(struct screen_ctx *); 598 | void xu_ewmh_net_desktop_viewport(struct screen_ctx *); 599 | void xu_ewmh_net_workarea(struct screen_ctx *); 600 | void xu_ewmh_net_client_list(struct screen_ctx *); 601 | void xu_ewmh_net_client_list_stacking(struct screen_ctx *); 602 | void xu_ewmh_net_active_window(struct screen_ctx *, Window); 603 | void xu_ewmh_net_number_of_desktops(struct screen_ctx *); 604 | void xu_ewmh_net_showing_desktop(struct screen_ctx *); 605 | void xu_ewmh_net_virtual_roots(struct screen_ctx *); 606 | void xu_ewmh_net_current_desktop(struct screen_ctx *); 607 | void xu_ewmh_net_desktop_names(struct screen_ctx *); 608 | int xu_ewmh_get_net_wm_desktop(struct client_ctx *, long *); 609 | void xu_ewmh_set_net_wm_desktop(struct client_ctx *); 610 | Atom *xu_ewmh_get_net_wm_state(struct client_ctx *, int *); 611 | void xu_ewmh_handle_net_wm_state_msg(struct client_ctx *, 612 | int, Atom, Atom); 613 | void xu_ewmh_set_net_wm_state(struct client_ctx *); 614 | void xu_ewmh_restore_net_wm_state(struct client_ctx *); 615 | 616 | char *u_argv(char * const *); 617 | void u_exec(char *); 618 | void u_spawn(char *); 619 | void log_debug(int, const char *, const char *, ...) 620 | __attribute__((__format__ (printf, 3, 4))) 621 | __attribute__((__nonnull__ (3))); 622 | 623 | void *xcalloc(size_t, size_t); 624 | void *xmalloc(size_t); 625 | void *xreallocarray(void *, size_t, size_t); 626 | char *xstrdup(const char *); 627 | int xasprintf(char **, const char *, ...) 628 | __attribute__((__format__ (printf, 2, 3))) 629 | __attribute__((__nonnull__ (2))); 630 | int xvasprintf(char **, const char *, va_list) 631 | __attribute__((__nonnull__ (2))); 632 | 633 | #endif /* _CALMWM_H_ */ 634 | -------------------------------------------------------------------------------- /conf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * calmwm - the calm window manager 3 | * 4 | * Copyright (c) 2004 Marius Aamodt Eriksen 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * $OpenBSD$ 19 | */ 20 | 21 | #include 22 | #include "queue.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "calmwm.h" 34 | 35 | static const char *conf_bind_mask(const char *, unsigned int *); 36 | static void conf_unbind_key(struct conf *, struct bind_ctx *); 37 | static void conf_unbind_mouse(struct conf *, struct bind_ctx *); 38 | 39 | static const struct { 40 | int num; 41 | const char *name; 42 | } group_binds[] = { 43 | { 0, "nogroup" }, 44 | { 1, "one" }, 45 | { 2, "two" }, 46 | { 3, "three" }, 47 | { 4, "four" }, 48 | { 5, "five" }, 49 | { 6, "six" }, 50 | { 7, "seven" }, 51 | { 8, "eight" }, 52 | { 9, "nine" }, 53 | }; 54 | static int cursor_binds[] = { 55 | XC_left_ptr, /* CF_NORMAL */ 56 | XC_fleur, /* CF_MOVE */ 57 | XC_bottom_right_corner, /* CF_RESIZE */ 58 | XC_question_arrow, /* CF_QUESTION */ 59 | }; 60 | static const char *color_binds[] = { 61 | "#CCCCCC", /* CWM_COLOR_BORDER_ACTIVE */ 62 | "#666666", /* CWM_COLOR_BORDER_INACTIVE */ 63 | "#FC8814", /* CWM_COLOR_BORDER_URGENCY */ 64 | "blue", /* CWM_COLOR_BORDER_GROUP */ 65 | "red", /* CWM_COLOR_BORDER_UNGROUP */ 66 | "black", /* CWM_COLOR_MENU_FG */ 67 | "white", /* CWM_COLOR_MENU_BG */ 68 | "black", /* CWM_COLOR_MENU_FONT */ 69 | "", /* CWM_COLOR_MENU_FONT_SEL */ 70 | }; 71 | static const struct { 72 | const char *tag; 73 | void (*handler)(void *, struct cargs *); 74 | enum context context; 75 | int flag; 76 | } name_to_func[] = { 77 | #define FUNC_CC(t, h, n) \ 78 | #t, kbfunc_ ## h, CWM_CONTEXT_CC, n 79 | #define FUNC_SC(t, h, n) \ 80 | #t, kbfunc_ ## h, CWM_CONTEXT_SC, n 81 | 82 | { FUNC_CC(window-lower, client_lower, 0) }, 83 | { FUNC_CC(window-raise, client_raise, 0) }, 84 | { FUNC_CC(window-hide, client_hide, 0) }, 85 | { FUNC_CC(window-close, client_close, 0) }, 86 | { FUNC_CC(window-delete, client_close, 0) }, 87 | { FUNC_CC(window-htile, client_htile, 0) }, 88 | { FUNC_CC(window-vtile, client_vtile, 0) }, 89 | { FUNC_CC(window-stick, client_toggle_sticky, 0) }, 90 | { FUNC_CC(window-fullscreen, client_toggle_fullscreen, 0) }, 91 | { FUNC_CC(window-maximize, client_toggle_maximize, 0) }, 92 | { FUNC_CC(window-vmaximize, client_toggle_vmaximize, 0) }, 93 | { FUNC_CC(window-hmaximize, client_toggle_hmaximize, 0) }, 94 | { FUNC_CC(window-freeze, client_toggle_freeze, 0) }, 95 | { FUNC_CC(window-group, client_toggle_group, 0) }, 96 | { FUNC_CC(window-movetogroup-1, client_movetogroup, 1) }, 97 | { FUNC_CC(window-movetogroup-2, client_movetogroup, 2) }, 98 | { FUNC_CC(window-movetogroup-3, client_movetogroup, 3) }, 99 | { FUNC_CC(window-movetogroup-4, client_movetogroup, 4) }, 100 | { FUNC_CC(window-movetogroup-5, client_movetogroup, 5) }, 101 | { FUNC_CC(window-movetogroup-6, client_movetogroup, 6) }, 102 | { FUNC_CC(window-movetogroup-7, client_movetogroup, 7) }, 103 | { FUNC_CC(window-movetogroup-8, client_movetogroup, 8) }, 104 | { FUNC_CC(window-movetogroup-9, client_movetogroup, 9) }, 105 | { FUNC_CC(window-snap-up, client_snap, (CWM_UP)) }, 106 | { FUNC_CC(window-snap-down, client_snap, (CWM_DOWN)) }, 107 | { FUNC_CC(window-snap-right, client_snap, (CWM_RIGHT)) }, 108 | { FUNC_CC(window-snap-left, client_snap, (CWM_LEFT)) }, 109 | { FUNC_CC(window-snap-up-right, client_snap, (CWM_UP_RIGHT)) }, 110 | { FUNC_CC(window-snap-up-left, client_snap, (CWM_UP_LEFT)) }, 111 | { FUNC_CC(window-snap-down-right, client_snap, (CWM_DOWN_RIGHT)) }, 112 | { FUNC_CC(window-snap-down-left, client_snap, (CWM_DOWN_LEFT)) }, 113 | { FUNC_CC(window-move, client_move, 0) }, 114 | { FUNC_CC(window-move-up, client_move, (CWM_UP)) }, 115 | { FUNC_CC(window-move-down, client_move, (CWM_DOWN)) }, 116 | { FUNC_CC(window-move-right, client_move, (CWM_RIGHT)) }, 117 | { FUNC_CC(window-move-left, client_move, (CWM_LEFT)) }, 118 | { FUNC_CC(window-move-up-big, client_move, (CWM_UP_BIG)) }, 119 | { FUNC_CC(window-move-down-big, client_move, (CWM_DOWN_BIG)) }, 120 | { FUNC_CC(window-move-right-big, client_move, (CWM_RIGHT_BIG)) }, 121 | { FUNC_CC(window-move-left-big, client_move, (CWM_LEFT_BIG)) }, 122 | { FUNC_CC(window-resize, client_resize, 0) }, 123 | { FUNC_CC(window-resize-up, client_resize, (CWM_UP)) }, 124 | { FUNC_CC(window-resize-down, client_resize, (CWM_DOWN)) }, 125 | { FUNC_CC(window-resize-right, client_resize, (CWM_RIGHT)) }, 126 | { FUNC_CC(window-resize-left, client_resize, (CWM_LEFT)) }, 127 | { FUNC_CC(window-resize-up-big, client_resize, (CWM_UP_BIG)) }, 128 | { FUNC_CC(window-resize-down-big, client_resize, (CWM_DOWN_BIG)) }, 129 | { FUNC_CC(window-resize-right-big, client_resize, (CWM_RIGHT_BIG)) }, 130 | { FUNC_CC(window-resize-left-big, client_resize, (CWM_LEFT_BIG)) }, 131 | { FUNC_CC(window-menu-label, client_menu_label, 0) }, 132 | 133 | { FUNC_SC(window-cycle, client_cycle, (CWM_CYCLE_FORWARD)) }, 134 | { FUNC_SC(window-rcycle, client_cycle, (CWM_CYCLE_REVERSE)) }, 135 | { FUNC_SC(window-cycle-ingroup, client_cycle, 136 | (CWM_CYCLE_FORWARD | CWM_CYCLE_INGROUP)) }, 137 | { FUNC_SC(window-rcycle-ingroup, client_cycle, 138 | (CWM_CYCLE_REVERSE | CWM_CYCLE_INGROUP)) }, 139 | { FUNC_SC(window-cycle-inclass, client_cycle, 140 | (CWM_CYCLE_FORWARD | CWM_CYCLE_INCLASS)) }, 141 | { FUNC_SC(window-rcycle-inclass, client_cycle, 142 | (CWM_CYCLE_REVERSE | CWM_CYCLE_INCLASS)) }, 143 | 144 | { FUNC_SC(group-cycle, group_cycle, (CWM_CYCLE_FORWARD)) }, 145 | { FUNC_SC(group-rcycle, group_cycle, (CWM_CYCLE_REVERSE)) }, 146 | { FUNC_SC(group-last, group_last, 0) }, 147 | { FUNC_SC(group-toggle-all, group_toggle_all, 0) }, 148 | { FUNC_SC(group-toggle-1, group_toggle, 1) }, 149 | { FUNC_SC(group-toggle-2, group_toggle, 2) }, 150 | { FUNC_SC(group-toggle-3, group_toggle, 3) }, 151 | { FUNC_SC(group-toggle-4, group_toggle, 4) }, 152 | { FUNC_SC(group-toggle-5, group_toggle, 5) }, 153 | { FUNC_SC(group-toggle-6, group_toggle, 6) }, 154 | { FUNC_SC(group-toggle-7, group_toggle, 7) }, 155 | { FUNC_SC(group-toggle-8, group_toggle, 8) }, 156 | { FUNC_SC(group-toggle-9, group_toggle, 9) }, 157 | { FUNC_SC(group-only-1, group_only, 1) }, 158 | { FUNC_SC(group-only-2, group_only, 2) }, 159 | { FUNC_SC(group-only-3, group_only, 3) }, 160 | { FUNC_SC(group-only-4, group_only, 4) }, 161 | { FUNC_SC(group-only-5, group_only, 5) }, 162 | { FUNC_SC(group-only-6, group_only, 6) }, 163 | { FUNC_SC(group-only-7, group_only, 7) }, 164 | { FUNC_SC(group-only-8, group_only, 8) }, 165 | { FUNC_SC(group-only-9, group_only, 9) }, 166 | { FUNC_SC(group-close-1, group_close, 1) }, 167 | { FUNC_SC(group-close-2, group_close, 2) }, 168 | { FUNC_SC(group-close-3, group_close, 3) }, 169 | { FUNC_SC(group-close-4, group_close, 4) }, 170 | { FUNC_SC(group-close-5, group_close, 5) }, 171 | { FUNC_SC(group-close-6, group_close, 6) }, 172 | { FUNC_SC(group-close-7, group_close, 7) }, 173 | { FUNC_SC(group-close-8, group_close, 8) }, 174 | { FUNC_SC(group-close-9, group_close, 9) }, 175 | 176 | { FUNC_SC(pointer-move-up, ptrmove, (CWM_UP)) }, 177 | { FUNC_SC(pointer-move-down, ptrmove, (CWM_DOWN)) }, 178 | { FUNC_SC(pointer-move-left, ptrmove, (CWM_LEFT)) }, 179 | { FUNC_SC(pointer-move-right, ptrmove, (CWM_RIGHT)) }, 180 | { FUNC_SC(pointer-move-up-big, ptrmove, (CWM_UP_BIG)) }, 181 | { FUNC_SC(pointer-move-down-big, ptrmove, (CWM_DOWN_BIG)) }, 182 | { FUNC_SC(pointer-move-left-big, ptrmove, (CWM_LEFT_BIG)) }, 183 | { FUNC_SC(pointer-move-right-big, ptrmove, (CWM_RIGHT_BIG)) }, 184 | 185 | { FUNC_SC(menu-cmd, menu_cmd, 0) }, 186 | { FUNC_SC(menu-group, menu_group, 0) }, 187 | { FUNC_SC(menu-ssh, menu_ssh, 0) }, 188 | { FUNC_SC(menu-window, menu_client, CWM_MENU_WINDOW_ALL) }, 189 | { FUNC_SC(menu-window-hidden, menu_client, CWM_MENU_WINDOW_HIDDEN) }, 190 | { FUNC_SC(menu-exec, menu_exec, 0) }, 191 | { FUNC_SC(menu-exec-wm, menu_wm, 0) }, 192 | 193 | { FUNC_SC(terminal, exec_term, 0) }, 194 | { FUNC_SC(lock, exec_lock, 0) }, 195 | { FUNC_SC(restart, cwm_status, CWM_EXEC_WM) }, 196 | { FUNC_SC(quit, cwm_status, CWM_QUIT) }, 197 | }; 198 | static unsigned int ignore_mods[] = { 199 | 0, LockMask, Mod2Mask, Mod2Mask | LockMask 200 | }; 201 | static const struct { 202 | const char ch; 203 | int mask; 204 | } bind_mods[] = { 205 | { 'S', ShiftMask }, 206 | { 'C', ControlMask }, 207 | { 'M', Mod1Mask }, 208 | { '4', Mod4Mask }, 209 | { '5', Mod5Mask }, 210 | }; 211 | static const struct { 212 | const char *key; 213 | const char *func; 214 | } key_binds[] = { 215 | { "CM-Return", "terminal" }, 216 | { "CM-Delete", "lock" }, 217 | { "M-question", "menu-exec" }, 218 | { "CM-w", "menu-exec-wm" }, 219 | { "M-period", "menu-ssh" }, 220 | { "M-Return", "window-hide" }, 221 | { "M-Down", "window-lower" }, 222 | { "M-Up", "window-raise" }, 223 | { "M-slash", "menu-window" }, 224 | { "C-slash", "menu-cmd" }, 225 | { "M-Tab", "window-cycle" }, 226 | { "MS-Tab", "window-rcycle" }, 227 | { "M-grave", "window-cycle-inclass" }, 228 | { "MS-grave", "window-rcycle-inclass" }, 229 | { "CM-n", "window-menu-label" }, 230 | { "CM-x", "window-close" }, 231 | { "CM-a", "group-toggle-all" }, 232 | { "CM-0", "group-toggle-all" }, 233 | { "CM-1", "group-toggle-1" }, 234 | { "CM-2", "group-toggle-2" }, 235 | { "CM-3", "group-toggle-3" }, 236 | { "CM-4", "group-toggle-4" }, 237 | { "CM-5", "group-toggle-5" }, 238 | { "CM-6", "group-toggle-6" }, 239 | { "CM-7", "group-toggle-7" }, 240 | { "CM-8", "group-toggle-8" }, 241 | { "CM-9", "group-toggle-9" }, 242 | { "M-Right", "group-cycle" }, 243 | { "M-Left", "group-rcycle" }, 244 | { "CM-g", "window-group" }, 245 | { "CM-f", "window-fullscreen" }, 246 | { "CM-m", "window-maximize" }, 247 | { "CM-s", "window-stick" }, 248 | { "CM-equal", "window-vmaximize" }, 249 | { "CMS-equal", "window-hmaximize" }, 250 | { "CMS-f", "window-freeze" }, 251 | { "CMS-r", "restart" }, 252 | { "CMS-q", "quit" }, 253 | { "M-h", "window-move-left" }, 254 | { "M-j", "window-move-down" }, 255 | { "M-k", "window-move-up" }, 256 | { "M-l", "window-move-right" }, 257 | { "MS-h", "window-move-left-big" }, 258 | { "MS-j", "window-move-down-big" }, 259 | { "MS-k", "window-move-up-big" }, 260 | { "MS-l", "window-move-right-big" }, 261 | { "CM-h", "window-resize-left" }, 262 | { "CM-j", "window-resize-down" }, 263 | { "CM-k", "window-resize-up" }, 264 | { "CM-l", "window-resize-right" }, 265 | { "CMS-h", "window-resize-left-big" }, 266 | { "CMS-j", "window-resize-down-big" }, 267 | { "CMS-k", "window-resize-up-big" }, 268 | { "CMS-l", "window-resize-right-big" }, 269 | }, 270 | mouse_binds[] = { 271 | { "1", "menu-window" }, 272 | { "2", "menu-group" }, 273 | { "3", "menu-cmd" }, 274 | { "M-1", "window-move" }, 275 | { "CM-1", "window-group" }, 276 | { "M-2", "window-resize" }, 277 | { "M-3", "window-lower" }, 278 | { "CMS-3", "window-hide" }, 279 | }; 280 | 281 | void 282 | conf_init(struct conf *c) 283 | { 284 | const char *home; 285 | struct passwd *pw; 286 | unsigned int i; 287 | 288 | c->stickygroups = 0; 289 | c->bwidth = 1; 290 | c->mamount = 1; 291 | c->htile = 50; 292 | c->vtile = 50; 293 | c->snapdist = 0; 294 | c->ngroups = 0; 295 | c->nameqlen = 5; 296 | 297 | TAILQ_INIT(&c->ignoreq); 298 | TAILQ_INIT(&c->autogroupq); 299 | TAILQ_INIT(&c->keybindq); 300 | TAILQ_INIT(&c->mousebindq); 301 | TAILQ_INIT(&c->cmdq); 302 | TAILQ_INIT(&c->wmq); 303 | 304 | for (i = 0; i < nitems(key_binds); i++) 305 | conf_bind_key(c, key_binds[i].key, key_binds[i].func); 306 | 307 | for (i = 0; i < nitems(mouse_binds); i++) 308 | conf_bind_mouse(c, mouse_binds[i].key, mouse_binds[i].func); 309 | 310 | for (i = 0; i < nitems(color_binds); i++) 311 | c->color[i] = xstrdup(color_binds[i]); 312 | 313 | conf_cmd_add(c, "lock", "xlock"); 314 | conf_cmd_add(c, "term", "xterm"); 315 | conf_wm_add(c, "cwm", "cwm"); 316 | 317 | c->font = xstrdup("sans-serif:pixelsize=14:bold"); 318 | c->wmname = xstrdup("CWM"); 319 | 320 | home = getenv("HOME"); 321 | if ((home == NULL) || (*home == '\0')) { 322 | pw = getpwuid(getuid()); 323 | if (pw != NULL && pw->pw_dir != NULL && *pw->pw_dir != '\0') 324 | home = pw->pw_dir; 325 | else 326 | home = "/"; 327 | } 328 | xasprintf(&c->conf_file, "%s/%s", home, ".cwmrc"); 329 | xasprintf(&c->known_hosts, "%s/%s", home, ".ssh/known_hosts"); 330 | } 331 | 332 | void 333 | conf_clear(struct conf *c) 334 | { 335 | struct autogroup *ag; 336 | struct bind_ctx *kb, *mb; 337 | struct winname *wn; 338 | struct cmd_ctx *cmd, *wm; 339 | int i; 340 | 341 | while ((cmd = TAILQ_FIRST(&c->cmdq)) != NULL) { 342 | TAILQ_REMOVE(&c->cmdq, cmd, entry); 343 | free(cmd->name); 344 | free(cmd->path); 345 | free(cmd); 346 | } 347 | while ((wm = TAILQ_FIRST(&c->wmq)) != NULL) { 348 | TAILQ_REMOVE(&c->wmq, wm, entry); 349 | free(wm->name); 350 | free(wm->path); 351 | free(wm); 352 | } 353 | while ((kb = TAILQ_FIRST(&c->keybindq)) != NULL) { 354 | TAILQ_REMOVE(&c->keybindq, kb, entry); 355 | free(kb); 356 | } 357 | while ((ag = TAILQ_FIRST(&c->autogroupq)) != NULL) { 358 | TAILQ_REMOVE(&c->autogroupq, ag, entry); 359 | free(ag->class); 360 | free(ag->name); 361 | free(ag); 362 | } 363 | while ((wn = TAILQ_FIRST(&c->ignoreq)) != NULL) { 364 | TAILQ_REMOVE(&c->ignoreq, wn, entry); 365 | free(wn->name); 366 | free(wn); 367 | } 368 | while ((mb = TAILQ_FIRST(&c->mousebindq)) != NULL) { 369 | TAILQ_REMOVE(&c->mousebindq, mb, entry); 370 | free(mb); 371 | } 372 | for (i = 0; i < CWM_COLOR_NITEMS; i++) 373 | free(c->color[i]); 374 | 375 | free(c->conf_file); 376 | free(c->known_hosts); 377 | free(c->font); 378 | free(c->wmname); 379 | } 380 | 381 | void 382 | conf_cmd_add(struct conf *c, const char *name, const char *path) 383 | { 384 | struct cmd_ctx *cmd, *cmdtmp = NULL, *cmdnxt; 385 | 386 | cmd = xmalloc(sizeof(*cmd)); 387 | cmd->name = xstrdup(name); 388 | cmd->path = xstrdup(path); 389 | 390 | TAILQ_FOREACH_SAFE(cmdtmp, &c->cmdq, entry, cmdnxt) { 391 | if (strcmp(cmdtmp->name, name) == 0) { 392 | TAILQ_REMOVE(&c->cmdq, cmdtmp, entry); 393 | free(cmdtmp->name); 394 | free(cmdtmp->path); 395 | free(cmdtmp); 396 | } 397 | } 398 | TAILQ_INSERT_TAIL(&c->cmdq, cmd, entry); 399 | } 400 | 401 | void 402 | conf_wm_add(struct conf *c, const char *name, const char *path) 403 | { 404 | struct cmd_ctx *wm, *wmtmp = NULL, *wmnxt; 405 | 406 | wm = xmalloc(sizeof(*wm)); 407 | wm->name = xstrdup(name); 408 | wm->path = xstrdup(path); 409 | 410 | TAILQ_FOREACH_SAFE(wmtmp, &c->cmdq, entry, wmnxt) { 411 | if (strcmp(wmtmp->name, name) == 0) { 412 | TAILQ_REMOVE(&c->wmq, wmtmp, entry); 413 | free(wmtmp->name); 414 | free(wmtmp->path); 415 | free(wmtmp); 416 | } 417 | } 418 | TAILQ_INSERT_TAIL(&c->wmq, wm, entry); 419 | } 420 | 421 | void 422 | conf_autogroup(struct conf *c, int num, const char *name, const char *class) 423 | { 424 | struct autogroup *ag; 425 | char *p; 426 | 427 | ag = xmalloc(sizeof(*ag)); 428 | if ((p = strchr(class, ',')) == NULL) { 429 | if (name == NULL) 430 | ag->name = NULL; 431 | else 432 | ag->name = xstrdup(name); 433 | 434 | ag->class = xstrdup(class); 435 | } else { 436 | *(p++) = '\0'; 437 | if (name == NULL) 438 | ag->name = xstrdup(class); 439 | else 440 | ag->name = xstrdup(name); 441 | 442 | ag->class = xstrdup(p); 443 | } 444 | ag->num = num; 445 | TAILQ_INSERT_TAIL(&c->autogroupq, ag, entry); 446 | } 447 | 448 | void 449 | conf_ignore(struct conf *c, const char *name) 450 | { 451 | struct winname *wn; 452 | 453 | wn = xmalloc(sizeof(*wn)); 454 | wn->name = xstrdup(name); 455 | TAILQ_INSERT_TAIL(&c->ignoreq, wn, entry); 456 | } 457 | 458 | void 459 | conf_cursor(struct conf *c) 460 | { 461 | unsigned int i; 462 | 463 | for (i = 0; i < nitems(cursor_binds); i++) 464 | c->cursor[i] = XCreateFontCursor(X_Dpy, cursor_binds[i]); 465 | } 466 | 467 | void 468 | conf_client(struct client_ctx *cc) 469 | { 470 | struct winname *wn; 471 | 472 | TAILQ_FOREACH(wn, &Conf.ignoreq, entry) { 473 | if (strncasecmp(wn->name, cc->name, strlen(wn->name)) == 0) { 474 | cc->flags |= CLIENT_IGNORE; 475 | break; 476 | } 477 | } 478 | } 479 | 480 | void 481 | conf_screen(struct screen_ctx *sc) 482 | { 483 | unsigned int i; 484 | XftColor xc; 485 | 486 | sc->gap = Conf.gap; 487 | sc->snapdist = Conf.snapdist; 488 | 489 | sc->xftfont = XftFontOpenXlfd(X_Dpy, sc->which, Conf.font); 490 | if (sc->xftfont == NULL) { 491 | sc->xftfont = XftFontOpenName(X_Dpy, sc->which, Conf.font); 492 | if (sc->xftfont == NULL) 493 | errx(1, "%s: XftFontOpenName: %s", __func__, Conf.font); 494 | } 495 | 496 | for (i = 0; i < nitems(color_binds); i++) { 497 | if (i == CWM_COLOR_MENU_FONT_SEL && *Conf.color[i] == '\0') { 498 | xu_xorcolor(sc->xftcolor[CWM_COLOR_MENU_BG], 499 | sc->xftcolor[CWM_COLOR_MENU_FG], &xc); 500 | xu_xorcolor(sc->xftcolor[CWM_COLOR_MENU_FONT], xc, &xc); 501 | if (!XftColorAllocValue(X_Dpy, sc->visual, sc->colormap, 502 | &xc.color, &sc->xftcolor[CWM_COLOR_MENU_FONT_SEL])) 503 | warnx("XftColorAllocValue: %s", Conf.color[i]); 504 | break; 505 | } 506 | if (!XftColorAllocName(X_Dpy, sc->visual, sc->colormap, 507 | Conf.color[i], &sc->xftcolor[i])) { 508 | warnx("XftColorAllocName: %s", Conf.color[i]); 509 | XftColorAllocName(X_Dpy, sc->visual, sc->colormap, 510 | color_binds[i], &sc->xftcolor[i]); 511 | } 512 | } 513 | 514 | conf_grab_kbd(sc->rootwin); 515 | } 516 | 517 | void 518 | conf_group(struct screen_ctx *sc) 519 | { 520 | unsigned int i; 521 | 522 | for (i = 0; i < nitems(group_binds); i++) { 523 | group_init(sc, group_binds[i].num, group_binds[i].name); 524 | Conf.ngroups++; 525 | } 526 | } 527 | 528 | static const char * 529 | conf_bind_mask(const char *name, unsigned int *mask) 530 | { 531 | char *dash; 532 | const char *ch; 533 | unsigned int i; 534 | 535 | *mask = 0; 536 | if ((dash = strchr(name, '-')) == NULL) 537 | return name; 538 | for (i = 0; i < nitems(bind_mods); i++) { 539 | if ((ch = strchr(name, bind_mods[i].ch)) != NULL && ch < dash) 540 | *mask |= bind_mods[i].mask; 541 | } 542 | /* Skip past modifiers. */ 543 | return (dash + 1); 544 | } 545 | 546 | int 547 | conf_bind_key(struct conf *c, const char *bind, const char *cmd) 548 | { 549 | struct bind_ctx *kb; 550 | struct cargs *cargs; 551 | const char *key; 552 | unsigned int i; 553 | 554 | if ((strcmp(bind, "all") == 0) && (cmd == NULL)) { 555 | conf_unbind_key(c, NULL); 556 | return 1; 557 | } 558 | kb = xmalloc(sizeof(*kb)); 559 | key = conf_bind_mask(bind, &kb->modmask); 560 | kb->press.keysym = XStringToKeysym(key); 561 | if (kb->press.keysym == NoSymbol) { 562 | warnx("unknown symbol: %s", key); 563 | free(kb); 564 | return 0; 565 | } 566 | conf_unbind_key(c, kb); 567 | if (cmd == NULL) { 568 | free(kb); 569 | return 1; 570 | } 571 | cargs = xcalloc(1, sizeof(*cargs)); 572 | for (i = 0; i < nitems(name_to_func); i++) { 573 | if (strcmp(name_to_func[i].tag, cmd) != 0) 574 | continue; 575 | kb->callback = name_to_func[i].handler; 576 | kb->context = name_to_func[i].context; 577 | cargs->flag = name_to_func[i].flag; 578 | goto out; 579 | } 580 | kb->callback = kbfunc_exec_cmd; 581 | kb->context = CWM_CONTEXT_NONE; 582 | cargs->flag = 0; 583 | cargs->cmd = xstrdup(cmd); 584 | out: 585 | kb->cargs = cargs; 586 | TAILQ_INSERT_TAIL(&c->keybindq, kb, entry); 587 | return 1; 588 | } 589 | 590 | static void 591 | conf_unbind_key(struct conf *c, struct bind_ctx *unbind) 592 | { 593 | struct bind_ctx *key = NULL, *keynxt; 594 | 595 | TAILQ_FOREACH_SAFE(key, &c->keybindq, entry, keynxt) { 596 | if ((unbind == NULL) || 597 | ((key->modmask == unbind->modmask) && 598 | (key->press.keysym == unbind->press.keysym))) { 599 | TAILQ_REMOVE(&c->keybindq, key, entry); 600 | free(key->cargs->cmd); 601 | free(key->cargs); 602 | free(key); 603 | } 604 | } 605 | } 606 | 607 | int 608 | conf_bind_mouse(struct conf *c, const char *bind, const char *cmd) 609 | { 610 | struct bind_ctx *mb; 611 | struct cargs *cargs; 612 | const char *button, *errstr; 613 | unsigned int i; 614 | 615 | if ((strcmp(bind, "all") == 0) && (cmd == NULL)) { 616 | conf_unbind_mouse(c, NULL); 617 | return 1; 618 | } 619 | mb = xmalloc(sizeof(*mb)); 620 | button = conf_bind_mask(bind, &mb->modmask); 621 | mb->press.button = strtonum(button, Button1, Button5, &errstr); 622 | if (errstr) { 623 | warnx("button number is %s: %s", errstr, button); 624 | free(mb); 625 | return 0; 626 | } 627 | conf_unbind_mouse(c, mb); 628 | if (cmd == NULL) { 629 | free(mb); 630 | return 1; 631 | } 632 | cargs = xcalloc(1, sizeof(*cargs)); 633 | for (i = 0; i < nitems(name_to_func); i++) { 634 | if (strcmp(name_to_func[i].tag, cmd) != 0) 635 | continue; 636 | mb->callback = name_to_func[i].handler; 637 | mb->context = name_to_func[i].context; 638 | cargs->flag = name_to_func[i].flag; 639 | goto out; 640 | } 641 | mb->callback = kbfunc_exec_cmd; 642 | mb->context = CWM_CONTEXT_NONE; 643 | cargs->flag = 0; 644 | cargs->cmd = xstrdup(cmd); 645 | out: 646 | mb->cargs = cargs; 647 | TAILQ_INSERT_TAIL(&c->mousebindq, mb, entry); 648 | return 1; 649 | } 650 | 651 | static void 652 | conf_unbind_mouse(struct conf *c, struct bind_ctx *unbind) 653 | { 654 | struct bind_ctx *mb = NULL, *mbnxt; 655 | 656 | TAILQ_FOREACH_SAFE(mb, &c->mousebindq, entry, mbnxt) { 657 | if ((unbind == NULL) || 658 | ((mb->modmask == unbind->modmask) && 659 | (mb->press.button == unbind->press.button))) { 660 | TAILQ_REMOVE(&c->mousebindq, mb, entry); 661 | free(mb->cargs->cmd); 662 | free(mb->cargs); 663 | free(mb); 664 | } 665 | } 666 | } 667 | 668 | void 669 | conf_grab_kbd(Window win) 670 | { 671 | struct bind_ctx *kb; 672 | KeyCode kc; 673 | unsigned int i; 674 | 675 | XUngrabKey(X_Dpy, AnyKey, AnyModifier, win); 676 | 677 | TAILQ_FOREACH(kb, &Conf.keybindq, entry) { 678 | kc = XKeysymToKeycode(X_Dpy, kb->press.keysym); 679 | if (kc == 0) 680 | continue; 681 | if ((XkbKeycodeToKeysym(X_Dpy, kc, 0, 0) != kb->press.keysym) && 682 | (XkbKeycodeToKeysym(X_Dpy, kc, 0, 1) == kb->press.keysym)) 683 | kb->modmask |= ShiftMask; 684 | 685 | for (i = 0; i < nitems(ignore_mods); i++) 686 | XGrabKey(X_Dpy, kc, (kb->modmask | ignore_mods[i]), win, 687 | True, GrabModeAsync, GrabModeAsync); 688 | } 689 | } 690 | 691 | void 692 | conf_grab_mouse(Window win) 693 | { 694 | struct bind_ctx *mb; 695 | unsigned int i; 696 | 697 | XUngrabButton(X_Dpy, AnyButton, AnyModifier, win); 698 | 699 | TAILQ_FOREACH(mb, &Conf.mousebindq, entry) { 700 | if (mb->context != CWM_CONTEXT_CC) 701 | continue; 702 | for (i = 0; i < nitems(ignore_mods); i++) { 703 | XGrabButton(X_Dpy, mb->press.button, 704 | (mb->modmask | ignore_mods[i]), win, False, 705 | BUTTONMASK, GrabModeAsync, GrabModeSync, 706 | None, None); 707 | } 708 | } 709 | } 710 | -------------------------------------------------------------------------------- /cwm.1: -------------------------------------------------------------------------------- 1 | .\" $OpenBSD$ 2 | .\" 3 | .\" Copyright (c) 2004,2005 Marius Aamodt Eriksen 4 | .\" 5 | .\" Permission to use, copy, modify, and distribute this software for any 6 | .\" purpose with or without fee is hereby granted, provided that the above 7 | .\" copyright notice and this permission notice appear in all copies. 8 | .\" 9 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | .\" 17 | .Dd $Mdocdate$ 18 | .Dt CWM 1 19 | .Os 20 | .Sh NAME 21 | .Nm cwm 22 | .Nd a lightweight and efficient window manager for X11 23 | .Sh SYNOPSIS 24 | .\" For a program: program [-abc] file ... 25 | .Nm cwm 26 | .Op Fl nv 27 | .Op Fl c Ar file 28 | .Op Fl d Ar display 29 | .Sh DESCRIPTION 30 | .Nm 31 | is a window manager for X11 which contains many features that 32 | concentrate on the efficiency and transparency of window management, 33 | while maintaining the simplest and most pleasant aesthetic. 34 | .Pp 35 | The options are as follows: 36 | .Bl -tag -width Ds 37 | .It Fl c Ar file 38 | Specify an alternative configuration file. 39 | By default, 40 | .Nm 41 | loads 42 | .Pa ~/.cwmrc , 43 | if present. 44 | Any error messages from lines in the configuration file will be sent to 45 | .Em stderr ; 46 | however, 47 | .Nm 48 | will continue to process the rest of the configuration file. 49 | .It Fl d Ar display 50 | Specify the display to use. 51 | .It Fl n 52 | Configtest mode. 53 | Only check the configuration file for validity. 54 | .It Fl v 55 | Verbose mode. 56 | Multiple 57 | .Fl v 58 | options increase the verbosity. 59 | .El 60 | .Pp 61 | .Nm 62 | actions are initiated either via key or mouse bindings. 63 | The following notations are used throughout this page: 64 | .Pp 65 | .Bl -tag -width Ds -offset indent -compact 66 | .It Ic C 67 | Control key. 68 | .It Ic M 69 | Meta key. 70 | .It Ic S 71 | Shift key. 72 | .It Ic 4 73 | Mod4 (windows) key. 74 | .It Ic M1 75 | Left mouse button. 76 | .It Ic M2 77 | Middle mouse button. 78 | .It Ic M3 79 | Right mouse button. 80 | .El 81 | .Pp 82 | The default key bindings are: 83 | .Pp 84 | .Bl -tag -width "CM-EscapeXXXXX" -offset indent -compact 85 | .It Ic CM-Return 86 | Spawn a new terminal. 87 | .It Ic CM-Delete 88 | Lock the screen. 89 | .It Ic M-Return 90 | Hide current window. 91 | .It Ic M-Down 92 | Lower current window. 93 | .It Ic M-Up 94 | Raise current window. 95 | .It Ic M-slash 96 | Search for windows. 97 | .It Ic C-slash 98 | Search for applications. 99 | .It Ic CM-n 100 | Label current window. 101 | .It Ic M-Tab 102 | Cycle through currently visible windows. 103 | .It Ic MS-Tab 104 | Reverse cycle through currently visible windows. 105 | .It Ic M-grave 106 | Cycle through currently visible windows of the same window class. 107 | .It Ic MS-grave 108 | Reverse cycle through currently visible windows of the same window class. 109 | .It Ic CM-x 110 | Close current window. 111 | .It Ic CM-[n] 112 | Toggle visibility of group n, where n is 1-9. 113 | .It Ic CM-a 114 | Toggle visibility of all groups. 115 | .It Ic CM-g 116 | Toggle group membership of current window. 117 | .It Ic M-Right 118 | Cycle through active groups. 119 | .It Ic M-Left 120 | Reverse cycle through active groups. 121 | .It Ic CMS-f 122 | Toggle freezing geometry of current window. 123 | .It Ic CM-s 124 | Toggle stickiness of current window. 125 | .It Ic CM-f 126 | Toggle full-screen mode of current window. 127 | .It Ic CM-m 128 | Toggle maximization of current window. 129 | .It Ic CM-equal 130 | Toggle vertical maximization of current window. 131 | .It Ic CMS-equal 132 | Toggle horizontal maximization of current window. 133 | .It Ic M-[hjkl] 134 | Move window by a small amount. 135 | .It Ic MS-[hjkl] 136 | Move window by a large amount; see 137 | .Xr cwmrc 5 . 138 | .It Ic CM-[hjkl] 139 | Resize window by a small amount. 140 | .It Ic CMS-[hjkl] 141 | Resize window by a large amount; see 142 | .Xr cwmrc 5 . 143 | .It Ic M-question 144 | Spawn 145 | .Dq exec program 146 | dialog. 147 | .It Ic M-period 148 | Spawn 149 | .Dq ssh to 150 | dialog. 151 | This parses 152 | .Pa $HOME/.ssh/known_hosts 153 | to provide host auto-completion. 154 | .Xr ssh 1 155 | will be executed via the configured terminal emulator. 156 | .It Ic CM-w 157 | Spawn 158 | .Dq exec WindowManager 159 | menu, allowing a switch to another window manager. 160 | .It Ic CMS-r 161 | Restart. 162 | .It Ic CMS-q 163 | Quit. 164 | .El 165 | .Pp 166 | The default mouse bindings are: 167 | .Pp 168 | .Bl -tag -width "CM-EscapeXXXXX" -offset indent -compact 169 | .It Ic M-M1 170 | Move current window. 171 | .It Ic CM-M1 172 | Toggle group membership of current window. 173 | .It Ic M-M2 174 | Resize current window 175 | .It Ic M-M3 176 | Lower current window. 177 | .It Ic CMS-M3 178 | Hide current window. 179 | .El 180 | .Pp 181 | The following key bindings may be used to navigate 182 | search and exec dialogs: 183 | .Pp 184 | .Bl -tag -width "[Down] or C-s or M-j" -offset indent -compact 185 | .It Ic [Return] 186 | Select item. 187 | .It Ic [Down], C-s No or Ic M-j 188 | Next item. 189 | .It Ic [Up], C-r No or Ic M-k 190 | Previous item. 191 | .It Ic [Backspace] No or Ic C-h 192 | Backspace. 193 | .It Ic C-u 194 | Clear input. 195 | .It Ic C-a 196 | List all available items. 197 | .It Ic [Esc] 198 | Cancel. 199 | .El 200 | .Pp 201 | .Nm 202 | rereads its configuration file when it receives a hangup signal, 203 | .Dv SIGHUP , 204 | by executing itself with the name and arguments with which it was started. 205 | This is equivalent to the 206 | .Ar restart 207 | function. 208 | .Sh SEARCH 209 | .Nm 210 | features the ability to search for windows by their current title, 211 | old titles, and by their label. 212 | The priority for the search results are: label, current title, 213 | old titles in reverse order, and finally window class name. 214 | .Nm 215 | keeps a history of the 5 previous titles of a window. 216 | .Pp 217 | When searching, the leftmost character of the result list may show a 218 | flag: 219 | .Pp 220 | .Bl -tag -width Ds -offset indent -compact 221 | .It ! 222 | Window is currently focused. 223 | .It & 224 | Window is hidden. 225 | .El 226 | .Sh APPLICATIONS 227 | .Nm 228 | manages a list of applications defined with the 229 | .Cm command 230 | configuration option. 231 | .Sh GROUPS 232 | .Nm 233 | has the ability to group windows together, and use the groups to 234 | perform operations on the entire group instead of just one window. 235 | Together with the 236 | .Pa sticky 237 | option, this can be used to emulate virtual desktops. 238 | .Pp 239 | To edit groups, use the group selection commands to toggle membership 240 | of a group. 241 | A blue border will be shown briefly on windows added to the current group, 242 | and a red border will be shown on those just removed. 243 | .Sh MENUS 244 | Menus are recalled by clicking the mouse on the root window: 245 | .Pp 246 | .Bl -tag -width Ds -offset indent -compact 247 | .It Ic M1 248 | Show list of currently defined windows. 249 | Selecting an item will warp to that window, unhiding it if necessary. 250 | .It Ic M2 251 | Show list of currently defined groups. 252 | Selecting an item will hide/unhide that group. 253 | .It Ic M3 254 | Show list of applications as defined in the configuration file. 255 | Selecting an item will spawn that application. 256 | .El 257 | .Sh ENVIRONMENT 258 | .Bl -tag -width "DISPLAYXXX" -compact 259 | .It DISPLAY 260 | .Nm 261 | starts on this display unless the 262 | .Fl d 263 | option is given. 264 | .El 265 | .Sh FILES 266 | .Bl -tag -width "~/.cwmrcXX" -compact 267 | .It Pa ~/.cwmrc 268 | Default 269 | .Nm 270 | configuration file. 271 | .El 272 | .Sh SEE ALSO 273 | .Xr cwmrc 5 274 | .Sh HISTORY 275 | .Nm 276 | was originally inspired by evilwm, but was rewritten from scratch 277 | due to limitations in the evilwm codebase. 278 | The from-scratch rewrite borrowed some code from 9wm, however that code 279 | has since been removed or rewritten. 280 | .Pp 281 | .Nm 282 | first appeared in 283 | .Ox 4.2 . 284 | .Sh AUTHORS 285 | .An -nosplit 286 | .Nm 287 | was developed by 288 | .An Marius Aamodt Eriksen Aq marius@monkey.org 289 | with contributions from 290 | .An Andy Adamson Aq dros@monkey.org , 291 | .An Niels Provos Aq provos@monkey.org , 292 | and 293 | .An Antti Nyk\(:anen Aq aon@iki.fi . 294 | Ideas, discussion with many others. 295 | -------------------------------------------------------------------------------- /cwm.pub: -------------------------------------------------------------------------------- 1 | untrusted comment: portable cwm release key public key 2 | RWTdOib0PoIM0pmDAPnV2S9/AMRqTOVfTY/KAkFemdH13cqBDHdduTas 3 | -------------------------------------------------------------------------------- /cwmrc.5: -------------------------------------------------------------------------------- 1 | .\" $OpenBSD$ 2 | .\" 3 | .\" Copyright (c) 2004,2005 Marius Aamodt Eriksen 4 | .\" 5 | .\" Permission to use, copy, modify, and distribute this software for any 6 | .\" purpose with or without fee is hereby granted, provided that the above 7 | .\" copyright notice and this permission notice appear in all copies. 8 | .\" 9 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | .\" 17 | .Dd $Mdocdate$ 18 | .Dt CWMRC 5 19 | .Os 20 | .Sh NAME 21 | .Nm cwmrc 22 | .Nd calm window manager configuration file 23 | .Sh DESCRIPTION 24 | This manual page describes the 25 | .Xr cwm 1 26 | configuration file. 27 | .Pp 28 | The current line can be extended over multiple lines using a backslash 29 | .Pq Sq \e . 30 | Comments can be put anywhere in the file using a hash mark 31 | .Pq Sq # , 32 | and extend to the end of the current line. 33 | Care should be taken when commenting out multi-line text: 34 | the comment is effective until the end of the entire block. 35 | .Pp 36 | Arguments containing whitespace should be surrounded by double quotes 37 | .Pq \&" . 38 | .Pp 39 | The following options are accepted: 40 | .Bl -tag -width Ds 41 | .It Ic autogroup Ar group Oo Ar windowname , Oc Ns Ar windowclass 42 | Automatically add new windows to 43 | .Ar group 44 | if their class property matches 45 | .Ar windowclass , 46 | or if their name and class properties match 47 | .Ar windowname 48 | and 49 | .Ar windowclass , 50 | respectively. 51 | The more specific last match wins. 52 | .Ar group 53 | is a number between 0 and 9. 54 | If 55 | .Ar group 56 | is 0, matching windows will not be added to any group; this may be 57 | used to override 58 | .Dq sticky group mode . 59 | .Pp 60 | The name and class values, respectively, for existing windows 61 | are both set in the WM_CLASS property and may be obtained using 62 | .Xr xprop 1 . 63 | .It Ic bind-key Ar key function 64 | Bind or rebind key 65 | .Ar key 66 | to 67 | .Ar function . 68 | The modifier keys come first, followed by a 69 | .Sq - , 70 | then a keysym name, taken from 71 | .Pa /usr/X11R6/include/X11/keysymdef.h . 72 | .Pp 73 | The following modifiers are recognised: 74 | .Pp 75 | .Bl -tag -width Ds -offset indent -compact 76 | .It Ic C 77 | Control key. 78 | .It Ic M 79 | Meta key. 80 | .It Ic S 81 | Shift key. 82 | .It Ic 4 83 | Mod4 (windows) key. 84 | .It Ic 5 85 | Mod5 (AltGr) key. 86 | .El 87 | .Pp 88 | The 89 | .Ar function 90 | may either be one from the 91 | .Sx BIND FUNCTION LIST 92 | (see below) or the command line that is to be executed. 93 | .It Ic bind-mouse Ar button function 94 | Bind or rebind button 95 | .Ar button 96 | to 97 | .Ar function . 98 | The modifier keys come first, followed by a 99 | .Sq - , 100 | then the button number. 101 | .Pp 102 | The same modifiers are recognised as for 103 | .Ar key 104 | in 105 | .Nm bind-key . 106 | .Pp 107 | The following buttons are recognised: 108 | .Pp 109 | .Bl -tag -width Ds -offset indent -compact 110 | .It Ic 1 111 | Left mouse button. 112 | .It Ic 2 113 | Middle mouse button. 114 | .It Ic 3 115 | Right mouse button. 116 | .It Ic 4 117 | Scroll up mouse button. 118 | .It Ic 5 119 | Scroll down mouse button. 120 | .El 121 | .Pp 122 | The 123 | .Ar function 124 | may be taken from the 125 | .Sx BIND FUNCTION LIST 126 | (see below) or the command line that is to be executed. 127 | .It Ic borderwidth Ar pixels 128 | Set the window border width to 129 | .Ar pixels . 130 | .It Ic color activeborder Ar color 131 | Set the color of the active border. 132 | .It Ic color font Ar color 133 | Set menu font color. 134 | .It Ic color selfont Ar color 135 | Set font color for selected menu item. 136 | .It Ic color groupborder Ar color 137 | Set the color of the border while grouping a window. 138 | .It Ic color inactiveborder Ar color 139 | Set the color of the inactive border. 140 | .It Ic color menubg Ar color 141 | Set menu background color. 142 | .It Ic color menufg Ar color 143 | Set menu foreground color. 144 | .It Ic color urgencyborder Ar color 145 | Set the color of the border of a window indicating urgency. 146 | .It Ic color ungroupborder Ar color 147 | Set the color of the border while ungrouping a window. 148 | .It Ic command Ar name path 149 | Every 150 | .Ar name 151 | entry is shown in the application menu. 152 | When selected, the defined 153 | .Ar path 154 | is executed with 155 | .Xr execvp 3 . 156 | .Pp 157 | The 158 | .Ar name 159 | entries 160 | .Nm term 161 | and 162 | .Nm lock 163 | have a special meaning. 164 | They point to the terminal and screen locking programs specified by 165 | key bindings. 166 | The defaults are 167 | .Xr xterm 1 168 | and 169 | .Xr xlock 1 , 170 | respectively. 171 | .It Ic fontname Ar font 172 | Change the default 173 | .Ar font 174 | for 175 | .Xr Xft 3 . 176 | .It Ic gap Ar top bottom left right 177 | Define a 178 | .Dq gap 179 | in pixels at the edge of the screen, so that when a 180 | window is maximized it will not overlap this area. 181 | This 182 | .Dq gap 183 | can be used for applications such as 184 | .Xr xclock 1 , 185 | where the user may wish to remain visible. 186 | .It Ic htile Ar percent 187 | Set the percentage of screen the master window should occupy 188 | after calling 189 | .Ic window-htile . 190 | If set to 0, the horizontal size of the master window will 191 | remain unchanged. 192 | The default is 50. 193 | .It Ic ignore Ar windowname 194 | Ignore, and do not warp to, windows with the name 195 | .Ar windowname 196 | when drawing borders and cycling through windows. 197 | .It Ic moveamount Ar pixels 198 | Set a default size for the keyboard movement bindings, 199 | in pixels. 200 | The default is 1. 201 | .It Ic snapdist Ar pixels 202 | Minimum distance to snap-to adjacent edge, in pixels. 203 | The default is 0. 204 | .It Ic sticky Ic yes Ns \&| Ns Ic no 205 | Toggle sticky group mode. 206 | The default behavior for new windows is to not assign any group. 207 | By enabling sticky group mode, 208 | .Xr cwm 1 209 | will assign new windows to the currently selected group. 210 | .It Ic unbind-key Ar key 211 | Unbind function bound to 212 | .Ar key . 213 | A special 214 | .Ar key 215 | keyword 216 | .Dq all 217 | can be used to unbind all keys. 218 | .It Ic unbind-mouse Ar button 219 | Unbind function bound to 220 | .Ar button . 221 | A special 222 | .Ar button 223 | keyword 224 | .Dq all 225 | can be used to unbind all buttons. 226 | .It Ic vtile Ar percent 227 | Set the percentage of screen the master window should occupy 228 | after calling 229 | .Ic window-vtile . 230 | If set to 0, the vertical size of the master window will 231 | remain unchanged. 232 | The default is 50. 233 | .It Ic wm Ar name path 234 | Every 235 | .Ar name 236 | entry is shown in the wm menu. 237 | When selected, the window manager is replaced by 238 | .Ar path . 239 | .El 240 | .Sh BIND FUNCTION LIST 241 | .Bl -tag -width 23n -compact 242 | .It restart 243 | Restart the running 244 | .Xr cwm 1 . 245 | .It quit 246 | Quit 247 | .Xr cwm 1 . 248 | .It terminal 249 | Spawn a new terminal. 250 | .It lock 251 | Lock the screen. 252 | .It menu-window 253 | Launch window search menu. 254 | .It menu-window-hidden 255 | Launch hidden window search menu. 256 | .It menu-cmd 257 | Launch application search menu. 258 | .It menu-group 259 | Launch group search menu. 260 | .It menu-exec 261 | Launch 262 | .Dq exec program 263 | menu. 264 | .It menu-exec-wm 265 | Launch 266 | .Dq exec WindowManager 267 | menu. 268 | .It menu-ssh 269 | Launch 270 | .Dq ssh 271 | menu. 272 | .It group-toggle-[n] 273 | Toggle visibility of group n, where n is 1-9. 274 | .It group-only-[n] 275 | Show only group n, where n is 1-9, hiding other groups. 276 | .It group-last 277 | Show only the previously active group. 278 | .It group-close-[n] 279 | Close all windows in group n, where n is 1-9. 280 | .It group-toggle-all 281 | Toggle visibility of all groups. 282 | .It window-group 283 | Toggle group membership of current window. 284 | .It window-movetogroup-[n] 285 | Hide current window from display and move to group n, where n is 1-9. 286 | .It group-cycle 287 | Forward cycle through groups. 288 | .It group-rcycle 289 | Reverse cycle through groups. 290 | .It window-cycle 291 | Forward cycle through windows. 292 | .It window-rcycle 293 | Reverse cycle through windows. 294 | .It window-cycle-ingroup 295 | Forward cycle through windows in current group. 296 | .It window-rcycle-ingroup 297 | Reverse cycle through windows in current group. 298 | .It window-cycle-inclass 299 | Forward cycle through windows of the current window class. 300 | .It window-rcycle-inclass 301 | Reverse cycle through windows of the current window class. 302 | .It window-close 303 | Close current window. 304 | .It window-hide 305 | Hide current window. 306 | .It window-lower 307 | Lower current window. 308 | .It window-raise 309 | Raise current window. 310 | .It window-menu-label 311 | Label current window. 312 | .It window-freeze 313 | Freeze current window geometry. 314 | .It window-stick 315 | Stick current window to all groups (same as assigning to nogroup). 316 | .It window-fullscreen 317 | Full-screen current window (gap + border removed). 318 | .It window-maximize 319 | Maximize current window (gap + border honored). 320 | .It window-vmaximize 321 | Vertically maximize current window (gap + border honored). 322 | .It window-hmaximize 323 | Horizontally maximize current window (gap + border honored). 324 | .It window-htile 325 | Current window is placed at the top of the screen, maximized 326 | horizontally and resized to 327 | .Ar htile 328 | (default half) of the vertical screen space. 329 | Other windows in its group share remaining screen space. 330 | .It window-vtile 331 | Current window is placed on the left of the screen, maximized vertically 332 | and resized to 333 | .Ar vtile 334 | (default half) of the horizontal screen space. 335 | Other windows in its group share remaining screen space. 336 | .It window-move 337 | Move current window. 338 | .It window-resize 339 | Resize current window. 340 | .It window-move-up 341 | Move window 342 | .Ar moveamount 343 | pixels up. 344 | .It window-move-down 345 | Move window 346 | .Ar moveamount 347 | pixels down. 348 | .It window-move-right 349 | Move window 350 | .Ar moveamount 351 | pixels right. 352 | .It window-move-left 353 | Move window 354 | .Ar moveamount 355 | pixels left. 356 | .It window-move-up-big 357 | Move window 10 times 358 | .Ar moveamount 359 | pixels up. 360 | .It window-move-down-big 361 | Move window 10 times 362 | .Ar moveamount 363 | pixels down. 364 | .It window-move-right-big 365 | Move window 10 times 366 | .Ar moveamount 367 | pixels right. 368 | .It window-move-left-big 369 | Move window 10 times 370 | .Ar moveamount 371 | pixels left. 372 | .It window-resize-up 373 | Resize window 374 | .Ar moveamount 375 | pixels up. 376 | .It window-resize-down 377 | Resize window 378 | .Ar moveamount 379 | pixels down. 380 | .It window-resize-right 381 | Resize window 382 | .Ar moveamount 383 | pixels right. 384 | .It window-resize-left 385 | Resize window 386 | .Ar moveamount 387 | pixels left. 388 | .It window-resize-up-big 389 | Resize window 10 times 390 | .Ar moveamount 391 | pixels up. 392 | .It window-resize-down-big 393 | Resize window 10 times 394 | .Ar moveamount 395 | pixels down. 396 | .It window-resize-right-big 397 | Resize window 10 times 398 | .Ar moveamount 399 | pixels right. 400 | .It window-resize-left-big 401 | Resize window 10 times 402 | .Ar moveamount 403 | pixels left. 404 | .It window-snap-up 405 | Snap window to top edge. 406 | .It window-snap-down 407 | Snap window to bottom edge. 408 | .It window-snap-right 409 | Snap window to right edge. 410 | .It window-snap-left 411 | Snap window to left edge. 412 | .It window-snap-up-right 413 | Snap window to top-right corner. 414 | .It window-snap-up-left 415 | Snap window to top-left corner. 416 | .It window-snap-down-right 417 | Snap window to bottom-right corner. 418 | .It window-snap-down-left 419 | Snap window to bottom-left corner. 420 | .It pointer-move-up 421 | Move pointer 422 | .Ar moveamount 423 | pixels up. 424 | .It pointer-move-down 425 | Move pointer 426 | .Ar moveamount 427 | pixels down. 428 | .It pointer-move-right 429 | Move pointer 430 | .Ar moveamount 431 | pixels right. 432 | .It pointer-move-left 433 | Move pointer 434 | .Ar moveamount 435 | pixels left. 436 | .It pointer-move-up-big 437 | Move pointer 10 times 438 | .Ar moveamount 439 | pixels up. 440 | .It pointer-move-down-big 441 | Move pointer 10 times 442 | .Ar moveamount 443 | pixels down. 444 | .It pointer-move-right-big 445 | Move pointer 10 times 446 | .Ar moveamount 447 | pixels right. 448 | .It pointer-move-left-big 449 | Move pointer 10 times 450 | .Ar moveamount 451 | pixels left. 452 | .El 453 | .Sh FILES 454 | .Bl -tag -width "~/.cwmrcXXX" -compact 455 | .It Pa ~/.cwmrc 456 | Default 457 | .Xr cwm 1 458 | configuration file. 459 | .El 460 | .Sh EXAMPLES 461 | .Bd -literal 462 | # Set default Xft(3) font 463 | fontname "sans-serif:pixelsize=14:bold" 464 | 465 | # Turn on sticky-group mode 466 | sticky yes 467 | 468 | # Any entry here is shown in the application menu 469 | command firefox firefox 470 | command xmms xmms 471 | command top "xterm -e top" 472 | 473 | # Autogroup definitions 474 | autogroup 3 "aterm,XTerm" 475 | autogroup 3 "xterm,XTerm" 476 | 477 | # Ignore programs by that name by not drawing borders around them. 478 | ignore XMMS 479 | ignore xwi 480 | ignore xapm 481 | ignore xclock 482 | 483 | # Key bindings 484 | bind-key CM-r window-menu-label 485 | bind-key CS-Return "xterm -e top" 486 | bind-key C4-equal window-vmaximize 487 | bind-key C4S-equal window-hmaximize 488 | bind-key M-1 group-only-1 489 | bind-key M-2 group-only-2 490 | bind-key M-3 group-only-3 491 | bind-key MS-1 window-movetogroup-1 492 | bind-key MS-2 window-movetogroup-2 493 | bind-key MS-3 window-movetogroup-3 494 | unbind-key 4-o 495 | unbind-key CM-equal 496 | unbind-key CMS-equal 497 | 498 | # Mouse bindings 499 | bind-mouse M-2 window-lower 500 | bind-mouse M-3 window-resize 501 | .Ed 502 | .Sh SEE ALSO 503 | .Xr cwm 1 504 | .Sh HISTORY 505 | The 506 | .Nm 507 | file format first appeared in 508 | .Ox 4.4 . 509 | -------------------------------------------------------------------------------- /group.c: -------------------------------------------------------------------------------- 1 | /* 2 | * calmwm - the calm window manager 3 | * 4 | * Copyright (c) 2004 Andy Adamson 5 | * Copyright (c) 2004,2005 Marius Aamodt Eriksen 6 | * 7 | * Permission to use, copy, modify, and distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | * 19 | * $OpenBSD$ 20 | */ 21 | 22 | #include 23 | #include "queue.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "calmwm.h" 34 | 35 | static struct group_ctx *group_next(struct group_ctx *); 36 | static struct group_ctx *group_prev(struct group_ctx *); 37 | static void group_restack(struct group_ctx *); 38 | static void group_set_active(struct group_ctx *); 39 | 40 | void 41 | group_assign(struct group_ctx *gc, struct client_ctx *cc) 42 | { 43 | if ((gc != NULL) && (gc->num == 0)) 44 | gc = NULL; 45 | 46 | cc->gc = gc; 47 | 48 | xu_ewmh_set_net_wm_desktop(cc); 49 | } 50 | 51 | void 52 | group_hide(struct group_ctx *gc) 53 | { 54 | struct screen_ctx *sc = gc->sc; 55 | struct client_ctx *cc; 56 | 57 | screen_updatestackingorder(gc->sc); 58 | 59 | TAILQ_FOREACH(cc, &sc->clientq, entry) { 60 | if (cc->gc != gc) 61 | continue; 62 | if (!(cc->flags & CLIENT_STICKY) && 63 | !(cc->flags & CLIENT_HIDDEN)) 64 | client_hide(cc); 65 | } 66 | } 67 | 68 | void 69 | group_show(struct group_ctx *gc) 70 | { 71 | struct screen_ctx *sc = gc->sc; 72 | struct client_ctx *cc; 73 | 74 | TAILQ_FOREACH(cc, &sc->clientq, entry) { 75 | if (cc->gc != gc) 76 | continue; 77 | if (!(cc->flags & CLIENT_STICKY) && 78 | (cc->flags & CLIENT_HIDDEN)) 79 | client_show(cc); 80 | } 81 | group_restack(gc); 82 | group_set_active(gc); 83 | } 84 | 85 | static void 86 | group_restack(struct group_ctx *gc) 87 | { 88 | struct screen_ctx *sc = gc->sc; 89 | struct client_ctx *cc; 90 | Window *winlist; 91 | int i, lastempty = -1; 92 | int nwins = 0, highstack = 0; 93 | 94 | TAILQ_FOREACH(cc, &sc->clientq, entry) { 95 | if (cc->gc != gc) 96 | continue; 97 | if (cc->stackingorder > highstack) 98 | highstack = cc->stackingorder; 99 | } 100 | winlist = xreallocarray(NULL, (highstack + 1), sizeof(*winlist)); 101 | 102 | /* Invert the stacking order for XRestackWindows(). */ 103 | TAILQ_FOREACH(cc, &sc->clientq, entry) { 104 | if (cc->gc != gc) 105 | continue; 106 | winlist[highstack - cc->stackingorder] = cc->win; 107 | nwins++; 108 | } 109 | 110 | /* Un-sparseify */ 111 | for (i = 0; i <= highstack; i++) { 112 | if (!winlist[i] && lastempty == -1) 113 | lastempty = i; 114 | else if (winlist[i] && lastempty != -1) { 115 | winlist[lastempty] = winlist[i]; 116 | if (++lastempty == i) 117 | lastempty = -1; 118 | } 119 | } 120 | 121 | XRestackWindows(X_Dpy, winlist, nwins); 122 | free(winlist); 123 | } 124 | 125 | void 126 | group_init(struct screen_ctx *sc, int num, const char *name) 127 | { 128 | struct group_ctx *gc; 129 | 130 | gc = xmalloc(sizeof(*gc)); 131 | gc->sc = sc; 132 | gc->name = xstrdup(name); 133 | gc->num = num; 134 | TAILQ_INSERT_TAIL(&sc->groupq, gc, entry); 135 | 136 | if (num == 1) 137 | group_set_active(gc); 138 | } 139 | 140 | void 141 | group_set_active(struct group_ctx *gc) 142 | { 143 | struct screen_ctx *sc = gc->sc; 144 | 145 | sc->group_active = gc; 146 | 147 | xu_ewmh_net_current_desktop(sc); 148 | } 149 | 150 | void 151 | group_movetogroup(struct client_ctx *cc, int idx) 152 | { 153 | struct screen_ctx *sc = cc->sc; 154 | struct group_ctx *gc; 155 | 156 | TAILQ_FOREACH(gc, &sc->groupq, entry) { 157 | if (gc->num == idx) { 158 | if (cc->gc == gc) 159 | return; 160 | if (gc->num != 0 && group_holds_only_hidden(gc)) 161 | client_hide(cc); 162 | group_assign(gc, cc); 163 | } 164 | } 165 | } 166 | 167 | void 168 | group_toggle_membership(struct client_ctx *cc) 169 | { 170 | struct screen_ctx *sc = cc->sc; 171 | struct group_ctx *gc = sc->group_active; 172 | 173 | if (cc->gc == gc) { 174 | group_assign(NULL, cc); 175 | cc->flags |= CLIENT_UNGROUP; 176 | } else { 177 | group_assign(gc, cc); 178 | cc->flags |= CLIENT_GROUP; 179 | } 180 | client_draw_border(cc); 181 | } 182 | 183 | int 184 | group_holds_only_sticky(struct group_ctx *gc) 185 | { 186 | struct screen_ctx *sc = gc->sc; 187 | struct client_ctx *cc; 188 | 189 | TAILQ_FOREACH(cc, &sc->clientq, entry) { 190 | if (cc->gc != gc) 191 | continue; 192 | if (!(cc->flags & CLIENT_STICKY)) 193 | return 0; 194 | } 195 | return 1; 196 | } 197 | 198 | int 199 | group_holds_only_hidden(struct group_ctx *gc) 200 | { 201 | struct screen_ctx *sc = gc->sc; 202 | struct client_ctx *cc; 203 | 204 | TAILQ_FOREACH(cc, &sc->clientq, entry) { 205 | if (cc->gc != gc) 206 | continue; 207 | if (!(cc->flags & (CLIENT_HIDDEN | CLIENT_STICKY))) 208 | return 0; 209 | } 210 | return 1; 211 | } 212 | 213 | void 214 | group_only(struct screen_ctx *sc, int idx) 215 | { 216 | struct group_ctx *gc; 217 | 218 | if (sc->group_last != sc->group_active) 219 | sc->group_last = sc->group_active; 220 | 221 | TAILQ_FOREACH(gc, &sc->groupq, entry) { 222 | if (gc->num == idx) 223 | group_show(gc); 224 | else 225 | group_hide(gc); 226 | } 227 | } 228 | 229 | void 230 | group_toggle(struct screen_ctx *sc, int idx) 231 | { 232 | struct group_ctx *gc; 233 | 234 | TAILQ_FOREACH(gc, &sc->groupq, entry) { 235 | if (gc->num == idx) { 236 | if (group_holds_only_hidden(gc)) 237 | group_show(gc); 238 | else 239 | group_hide(gc); 240 | } 241 | } 242 | } 243 | 244 | void 245 | group_toggle_all(struct screen_ctx *sc) 246 | { 247 | struct group_ctx *gc; 248 | 249 | TAILQ_FOREACH(gc, &sc->groupq, entry) { 250 | if (sc->hideall) 251 | group_show(gc); 252 | else 253 | group_hide(gc); 254 | } 255 | sc->hideall = !sc->hideall; 256 | } 257 | 258 | void 259 | group_close(struct screen_ctx *sc, int idx) 260 | { 261 | struct group_ctx *gc; 262 | struct client_ctx *cc; 263 | 264 | TAILQ_FOREACH(gc, &sc->groupq, entry) { 265 | if (gc->num == idx) { 266 | TAILQ_FOREACH(cc, &sc->clientq, entry) { 267 | if (cc->gc != gc) 268 | continue; 269 | client_close(cc); 270 | } 271 | } 272 | } 273 | } 274 | 275 | void 276 | group_cycle(struct screen_ctx *sc, int flags) 277 | { 278 | struct group_ctx *newgc, *oldgc, *showgroup = NULL; 279 | 280 | oldgc = sc->group_active; 281 | 282 | newgc = oldgc; 283 | for (;;) { 284 | newgc = (flags & CWM_CYCLE_REVERSE) ? group_prev(newgc) : 285 | group_next(newgc); 286 | 287 | if (newgc == oldgc) 288 | break; 289 | 290 | if (!group_holds_only_sticky(newgc) && showgroup == NULL) 291 | showgroup = newgc; 292 | else if (!group_holds_only_hidden(newgc)) 293 | group_hide(newgc); 294 | } 295 | if (showgroup == NULL) 296 | return; 297 | 298 | group_hide(oldgc); 299 | 300 | if (group_holds_only_hidden(showgroup)) 301 | group_show(showgroup); 302 | else 303 | group_set_active(showgroup); 304 | } 305 | 306 | static struct group_ctx * 307 | group_next(struct group_ctx *gc) 308 | { 309 | struct screen_ctx *sc = gc->sc; 310 | struct group_ctx *newgc; 311 | 312 | return(((newgc = TAILQ_NEXT(gc, entry)) != NULL) ? 313 | newgc : TAILQ_FIRST(&sc->groupq)); 314 | } 315 | 316 | static struct group_ctx * 317 | group_prev(struct group_ctx *gc) 318 | { 319 | struct screen_ctx *sc = gc->sc; 320 | struct group_ctx *newgc; 321 | 322 | return(((newgc = TAILQ_PREV(gc, group_q, entry)) != NULL) ? 323 | newgc : TAILQ_LAST(&sc->groupq, group_q)); 324 | } 325 | 326 | int 327 | group_restore(struct client_ctx *cc) 328 | { 329 | struct screen_ctx *sc = cc->sc; 330 | struct group_ctx *gc; 331 | int num; 332 | long grpnum; 333 | 334 | if (!xu_ewmh_get_net_wm_desktop(cc, &grpnum)) 335 | return 0; 336 | 337 | num = (grpnum == -1) ? 0 : grpnum; 338 | num = MIN(num, (Conf.ngroups - 1)); 339 | 340 | TAILQ_FOREACH(gc, &sc->groupq, entry) { 341 | if (gc->num == num) { 342 | group_assign(gc, cc); 343 | return 1; 344 | } 345 | } 346 | return 0; 347 | } 348 | 349 | int 350 | group_autogroup(struct client_ctx *cc) 351 | { 352 | struct screen_ctx *sc = cc->sc; 353 | struct autogroup *ag; 354 | struct group_ctx *gc; 355 | int num = -1, both_match = 0; 356 | 357 | if (cc->res_class == NULL || cc->res_name == NULL) 358 | return 0; 359 | 360 | TAILQ_FOREACH(ag, &Conf.autogroupq, entry) { 361 | if (strcmp(ag->class, cc->res_class) == 0) { 362 | if ((ag->name != NULL) && 363 | (strcmp(ag->name, cc->res_name) == 0)) { 364 | num = ag->num; 365 | both_match = 1; 366 | } else if (ag->name == NULL && !both_match) 367 | num = ag->num; 368 | } 369 | } 370 | 371 | TAILQ_FOREACH(gc, &sc->groupq, entry) { 372 | if (gc->num == num) { 373 | group_assign(gc, cc); 374 | return 1; 375 | } 376 | } 377 | return 0; 378 | } 379 | -------------------------------------------------------------------------------- /kbfunc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * calmwm - the calm window manager 3 | * 4 | * Copyright (c) 2004 Martin Murray 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * $OpenBSD$ 19 | */ 20 | 21 | /* For FreeBSD. */ 22 | #define _WITH_GETLINE 23 | 24 | #include 25 | #include "queue.h" 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "calmwm.h" 40 | 41 | #define HASH_MARKER "|1|" 42 | 43 | extern sig_atomic_t cwm_status; 44 | 45 | static void kbfunc_amount(int, int, int *, int *); 46 | static void kbfunc_client_move_kb(void *, struct cargs *); 47 | static void kbfunc_client_move_mb(void *, struct cargs *); 48 | static void kbfunc_client_resize_kb(void *, struct cargs *); 49 | static void kbfunc_client_resize_mb(void *, struct cargs *); 50 | 51 | void 52 | kbfunc_cwm_status(void *ctx, struct cargs *cargs) 53 | { 54 | cwm_status = cargs->flag; 55 | } 56 | 57 | static void 58 | kbfunc_amount(int flags, int amt, int *mx, int *my) 59 | { 60 | #define CWM_FACTOR 10 61 | 62 | if (flags & CWM_BIGAMOUNT) 63 | amt *= CWM_FACTOR; 64 | 65 | switch (flags & (CWM_UP | CWM_DOWN | CWM_LEFT | CWM_RIGHT)) { 66 | case CWM_UP: 67 | *my -= amt; 68 | break; 69 | case CWM_DOWN: 70 | *my += amt; 71 | break; 72 | case CWM_RIGHT: 73 | *mx += amt; 74 | break; 75 | case CWM_LEFT: 76 | *mx -= amt; 77 | break; 78 | } 79 | } 80 | 81 | void 82 | kbfunc_ptrmove(void *ctx, struct cargs *cargs) 83 | { 84 | struct screen_ctx *sc = ctx; 85 | int x, y; 86 | int mx = 0, my = 0; 87 | 88 | kbfunc_amount(cargs->flag, Conf.mamount, &mx, &my); 89 | 90 | xu_ptr_get(sc->rootwin, &x, &y); 91 | xu_ptr_set(sc->rootwin, x + mx, y + my); 92 | } 93 | 94 | void 95 | kbfunc_client_move(void *ctx, struct cargs *cargs) 96 | { 97 | if (cargs->xev == CWM_XEV_BTN) 98 | kbfunc_client_move_mb(ctx, cargs); 99 | else 100 | kbfunc_client_move_kb(ctx, cargs); 101 | } 102 | 103 | void 104 | kbfunc_client_resize(void *ctx, struct cargs *cargs) 105 | { 106 | if (cargs->xev == CWM_XEV_BTN) 107 | kbfunc_client_resize_mb(ctx, cargs); 108 | else 109 | kbfunc_client_resize_kb(ctx, cargs); 110 | } 111 | 112 | static void 113 | kbfunc_client_move_kb(void *ctx, struct cargs *cargs) 114 | { 115 | struct client_ctx *cc = ctx; 116 | struct screen_ctx *sc = cc->sc; 117 | struct geom area; 118 | int mx = 0, my = 0; 119 | 120 | if (cc->flags & CLIENT_FREEZE) 121 | return; 122 | 123 | kbfunc_amount(cargs->flag, Conf.mamount, &mx, &my); 124 | 125 | cc->geom.x += mx; 126 | if (cc->geom.x < -(cc->geom.w + cc->bwidth - 1)) 127 | cc->geom.x = -(cc->geom.w + cc->bwidth - 1); 128 | if (cc->geom.x > (sc->view.w - cc->bwidth - 1)) 129 | cc->geom.x = sc->view.w - cc->bwidth - 1; 130 | cc->geom.y += my; 131 | if (cc->geom.y < -(cc->geom.h + cc->bwidth - 1)) 132 | cc->geom.y = -(cc->geom.h + cc->bwidth - 1); 133 | if (cc->geom.y > (sc->view.h - cc->bwidth - 1)) 134 | cc->geom.y = sc->view.h - cc->bwidth - 1; 135 | 136 | area = screen_area(sc, 137 | cc->geom.x + cc->geom.w / 2, 138 | cc->geom.y + cc->geom.h / 2, 1); 139 | cc->geom.x += client_snapcalc(cc->geom.x, 140 | cc->geom.x + cc->geom.w + (cc->bwidth * 2), 141 | area.x, area.x + area.w, sc->snapdist); 142 | cc->geom.y += client_snapcalc(cc->geom.y, 143 | cc->geom.y + cc->geom.h + (cc->bwidth * 2), 144 | area.y, area.y + area.h, sc->snapdist); 145 | 146 | client_move(cc); 147 | client_ptr_inbound(cc, 1); 148 | XSync(X_Dpy, True); 149 | } 150 | 151 | static void 152 | kbfunc_client_move_mb(void *ctx, struct cargs *cargs) 153 | { 154 | struct client_ctx *cc = ctx; 155 | XEvent ev; 156 | Time ltime = 0; 157 | struct screen_ctx *sc = cc->sc; 158 | struct geom area; 159 | int move = 1; 160 | 161 | client_raise(cc); 162 | 163 | if (cc->flags & CLIENT_FREEZE) 164 | return; 165 | 166 | client_ptr_inbound(cc, 1); 167 | 168 | if (XGrabPointer(X_Dpy, sc->rootwin, False, MOUSEMASK, 169 | GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_MOVE], 170 | CurrentTime) != GrabSuccess) 171 | return; 172 | 173 | screen_prop_win_create(sc, cc->win); 174 | screen_prop_win_draw(sc, "%+5d%+5d", cc->geom.x, cc->geom.y); 175 | while (move) { 176 | XMaskEvent(X_Dpy, MOUSEMASK, &ev); 177 | switch (ev.type) { 178 | case MotionNotify: 179 | /* not more than 60 times / second */ 180 | if ((ev.xmotion.time - ltime) <= (1000 / 60)) 181 | continue; 182 | ltime = ev.xmotion.time; 183 | 184 | cc->geom.x = ev.xmotion.x_root - cc->ptr.x - cc->bwidth; 185 | cc->geom.y = ev.xmotion.y_root - cc->ptr.y - cc->bwidth; 186 | 187 | area = screen_area(sc, 188 | cc->geom.x + cc->geom.w / 2, 189 | cc->geom.y + cc->geom.h / 2, 1); 190 | cc->geom.x += client_snapcalc(cc->geom.x, 191 | cc->geom.x + cc->geom.w + (cc->bwidth * 2), 192 | area.x, area.x + area.w, sc->snapdist); 193 | cc->geom.y += client_snapcalc(cc->geom.y, 194 | cc->geom.y + cc->geom.h + (cc->bwidth * 2), 195 | area.y, area.y + area.h, sc->snapdist); 196 | client_move(cc); 197 | screen_prop_win_draw(sc, 198 | "%+5d%+5d", cc->geom.x, cc->geom.y); 199 | break; 200 | case ButtonRelease: 201 | move = 0; 202 | break; 203 | } 204 | } 205 | if (ltime) 206 | client_move(cc); 207 | screen_prop_win_destroy(sc); 208 | XUngrabPointer(X_Dpy, CurrentTime); 209 | } 210 | 211 | static void 212 | kbfunc_client_resize_kb(void *ctx, struct cargs *cargs) 213 | { 214 | struct client_ctx *cc = ctx; 215 | int mx = 0, my = 0; 216 | int amt = 1; 217 | 218 | if (cc->flags & CLIENT_FREEZE) 219 | return; 220 | 221 | if (!(cc->hint.flags & PResizeInc)) 222 | amt = Conf.mamount; 223 | 224 | kbfunc_amount(cargs->flag, amt, &mx, &my); 225 | 226 | if ((cc->geom.w += mx * cc->hint.incw) < cc->hint.minw) 227 | cc->geom.w = cc->hint.minw; 228 | if ((cc->geom.h += my * cc->hint.inch) < cc->hint.minh) 229 | cc->geom.h = cc->hint.minh; 230 | if (cc->geom.x + cc->geom.w + cc->bwidth - 1 < 0) 231 | cc->geom.x = -(cc->geom.w + cc->bwidth - 1); 232 | if (cc->geom.y + cc->geom.h + cc->bwidth - 1 < 0) 233 | cc->geom.y = -(cc->geom.h + cc->bwidth - 1); 234 | 235 | client_resize(cc, 1); 236 | client_ptr_inbound(cc, 1); 237 | XSync(X_Dpy, True); 238 | } 239 | 240 | static void 241 | kbfunc_client_resize_mb(void *ctx, struct cargs *cargs) 242 | { 243 | struct client_ctx *cc = ctx; 244 | XEvent ev; 245 | Time ltime = 0; 246 | struct screen_ctx *sc = cc->sc; 247 | int resize = 1; 248 | 249 | if (cc->flags & CLIENT_FREEZE) 250 | return; 251 | 252 | client_raise(cc); 253 | client_ptr_save(cc); 254 | 255 | xu_ptr_set(cc->win, cc->geom.w, cc->geom.h); 256 | 257 | if (XGrabPointer(X_Dpy, sc->rootwin, False, MOUSEMASK, 258 | GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_RESIZE], 259 | CurrentTime) != GrabSuccess) 260 | return; 261 | 262 | screen_prop_win_create(sc, cc->win); 263 | screen_prop_win_draw(sc, "%4d x %-4d", cc->dim.w, cc->dim.h); 264 | while (resize) { 265 | XMaskEvent(X_Dpy, MOUSEMASK, &ev); 266 | switch (ev.type) { 267 | case MotionNotify: 268 | /* not more than 60 times / second */ 269 | if ((ev.xmotion.time - ltime) <= (1000 / 60)) 270 | continue; 271 | ltime = ev.xmotion.time; 272 | 273 | cc->geom.w = ev.xmotion.x - cc->geom.x - cc->bwidth; 274 | cc->geom.h = ev.xmotion.y - cc->geom.y - cc->bwidth; 275 | client_apply_sizehints(cc); 276 | client_resize(cc, 1); 277 | screen_prop_win_draw(sc, 278 | "%4d x %-4d", cc->dim.w, cc->dim.h); 279 | break; 280 | case ButtonRelease: 281 | resize = 0; 282 | break; 283 | } 284 | } 285 | if (ltime) 286 | client_resize(cc, 1); 287 | screen_prop_win_destroy(sc); 288 | XUngrabPointer(X_Dpy, CurrentTime); 289 | 290 | /* Make sure the pointer stays within the window. */ 291 | client_ptr_inbound(cc, 0); 292 | } 293 | 294 | void 295 | kbfunc_client_snap(void *ctx, struct cargs *cargs) 296 | { 297 | struct client_ctx *cc = ctx; 298 | struct screen_ctx *sc = cc->sc; 299 | struct geom area; 300 | int flags; 301 | 302 | area = screen_area(sc, 303 | cc->geom.x + cc->geom.w / 2, 304 | cc->geom.y + cc->geom.h / 2, 1); 305 | 306 | flags = cargs->flag; 307 | while (flags) { 308 | if (flags & CWM_UP) { 309 | cc->geom.y = area.y; 310 | flags &= ~CWM_UP; 311 | } 312 | if (flags & CWM_LEFT) { 313 | cc->geom.x = area.x; 314 | flags &= ~CWM_LEFT; 315 | } 316 | if (flags & CWM_RIGHT) { 317 | cc->geom.x = area.x + area.w - cc->geom.w - 318 | (cc->bwidth * 2); 319 | flags &= ~CWM_RIGHT; 320 | } 321 | if (flags & CWM_DOWN) { 322 | cc->geom.y = area.y + area.h - cc->geom.h - 323 | (cc->bwidth * 2); 324 | flags &= ~CWM_DOWN; 325 | } 326 | } 327 | client_move(cc); 328 | client_ptr_inbound(cc, 1); 329 | } 330 | 331 | void 332 | kbfunc_client_close(void *ctx, struct cargs *cargs) 333 | { 334 | client_close(ctx); 335 | } 336 | 337 | void 338 | kbfunc_client_lower(void *ctx, struct cargs *cargs) 339 | { 340 | client_ptr_save(ctx); 341 | client_lower(ctx); 342 | } 343 | 344 | void 345 | kbfunc_client_raise(void *ctx, struct cargs *cargs) 346 | { 347 | client_raise(ctx); 348 | } 349 | 350 | void 351 | kbfunc_client_hide(void *ctx, struct cargs *cargs) 352 | { 353 | client_hide(ctx); 354 | } 355 | 356 | void 357 | kbfunc_client_toggle_freeze(void *ctx, struct cargs *cargs) 358 | { 359 | client_toggle_freeze(ctx); 360 | } 361 | 362 | void 363 | kbfunc_client_toggle_sticky(void *ctx, struct cargs *cargs) 364 | { 365 | client_toggle_sticky(ctx); 366 | } 367 | 368 | void 369 | kbfunc_client_toggle_fullscreen(void *ctx, struct cargs *cargs) 370 | { 371 | client_toggle_fullscreen(ctx); 372 | } 373 | 374 | void 375 | kbfunc_client_toggle_maximize(void *ctx, struct cargs *cargs) 376 | { 377 | client_toggle_maximize(ctx); 378 | } 379 | 380 | void 381 | kbfunc_client_toggle_hmaximize(void *ctx, struct cargs *cargs) 382 | { 383 | client_toggle_hmaximize(ctx); 384 | } 385 | 386 | void 387 | kbfunc_client_toggle_vmaximize(void *ctx, struct cargs *cargs) 388 | { 389 | client_toggle_vmaximize(ctx); 390 | } 391 | 392 | void 393 | kbfunc_client_htile(void *ctx, struct cargs *cargs) 394 | { 395 | client_htile(ctx); 396 | } 397 | 398 | void 399 | kbfunc_client_vtile(void *ctx, struct cargs *cargs) 400 | { 401 | client_vtile(ctx); 402 | } 403 | 404 | void 405 | kbfunc_client_cycle(void *ctx, struct cargs *cargs) 406 | { 407 | struct screen_ctx *sc = ctx; 408 | struct client_ctx *newcc, *oldcc, *prevcc; 409 | int again = 1, flags = cargs->flag; 410 | 411 | /* For X apps that ignore/steal events. */ 412 | if (cargs->xev == CWM_XEV_KEY) 413 | XGrabKeyboard(X_Dpy, sc->rootwin, True, 414 | GrabModeAsync, GrabModeAsync, CurrentTime); 415 | 416 | if (TAILQ_EMPTY(&sc->clientq)) 417 | return; 418 | 419 | prevcc = TAILQ_FIRST(&sc->clientq); 420 | oldcc = client_current(sc); 421 | if (oldcc == NULL) 422 | oldcc = (flags & CWM_CYCLE_REVERSE) ? 423 | TAILQ_LAST(&sc->clientq, client_q) : 424 | TAILQ_FIRST(&sc->clientq); 425 | 426 | newcc = oldcc; 427 | while (again) { 428 | again = 0; 429 | 430 | newcc = (flags & CWM_CYCLE_REVERSE) ? client_prev(newcc) : 431 | client_next(newcc); 432 | 433 | /* Only cycle visible and non-ignored windows. */ 434 | if ((newcc->flags & (CLIENT_SKIP_CYCLE)) || 435 | ((flags & CWM_CYCLE_INGROUP) && 436 | (newcc->gc != oldcc->gc)) || 437 | ((flags & CWM_CYCLE_INCLASS) && 438 | strcmp(newcc->res_class, oldcc->res_class) != 0)) 439 | again = 1; 440 | 441 | /* Is oldcc the only non-hidden window? */ 442 | if (newcc == oldcc) { 443 | if (again) 444 | return; /* No windows visible. */ 445 | break; 446 | } 447 | } 448 | 449 | /* Reset when cycling mod is released. XXX I hate this hack */ 450 | sc->cycling = 1; 451 | client_ptr_save(oldcc); 452 | client_raise(prevcc); 453 | client_raise(newcc); 454 | if (!client_inbound(newcc, newcc->ptr.x, newcc->ptr.y)) { 455 | newcc->ptr.x = newcc->geom.w / 2; 456 | newcc->ptr.y = newcc->geom.h / 2; 457 | } 458 | 459 | /* When no client is active, warp pointer to last active. */ 460 | if (oldcc->flags & (CLIENT_ACTIVE)) 461 | client_ptr_warp(newcc); 462 | else if (oldcc->flags & (CLIENT_SKIP_CYCLE)) 463 | client_ptr_warp(newcc); 464 | else { 465 | client_raise(oldcc); 466 | client_ptr_warp(oldcc); 467 | } 468 | } 469 | 470 | void 471 | kbfunc_client_toggle_group(void *ctx, struct cargs *cargs) 472 | { 473 | struct client_ctx *cc = ctx; 474 | 475 | /* For X apps that ignore/steal events. */ 476 | if (cargs->xev == CWM_XEV_KEY) 477 | XGrabKeyboard(X_Dpy, cc->win, True, 478 | GrabModeAsync, GrabModeAsync, CurrentTime); 479 | 480 | group_toggle_membership(cc); 481 | } 482 | 483 | void 484 | kbfunc_client_movetogroup(void *ctx, struct cargs *cargs) 485 | { 486 | group_movetogroup(ctx, cargs->flag); 487 | } 488 | 489 | void 490 | kbfunc_group_only(void *ctx, struct cargs *cargs) 491 | { 492 | group_only(ctx, cargs->flag); 493 | } 494 | 495 | void 496 | kbfunc_group_last(void *ctx, struct cargs *cargs) 497 | { 498 | struct screen_ctx *sc = ctx; 499 | 500 | group_only(ctx, sc->group_last->num); 501 | } 502 | 503 | void 504 | kbfunc_group_toggle(void *ctx, struct cargs *cargs) 505 | { 506 | group_toggle(ctx, cargs->flag); 507 | } 508 | 509 | void 510 | kbfunc_group_toggle_all(void *ctx, struct cargs *cargs) 511 | { 512 | group_toggle_all(ctx); 513 | } 514 | 515 | void 516 | kbfunc_group_close(void *ctx, struct cargs *cargs) 517 | { 518 | group_close(ctx, cargs->flag); 519 | } 520 | 521 | void 522 | kbfunc_group_cycle(void *ctx, struct cargs *cargs) 523 | { 524 | group_cycle(ctx, cargs->flag); 525 | } 526 | 527 | void 528 | kbfunc_menu_client(void *ctx, struct cargs *cargs) 529 | { 530 | struct screen_ctx *sc = ctx; 531 | struct client_ctx *cc, *old_cc; 532 | struct menu *mi; 533 | struct menu_q menuq; 534 | int mflags = 0; 535 | 536 | if (cargs->xev == CWM_XEV_BTN) 537 | mflags |= CWM_MENU_LIST; 538 | 539 | TAILQ_INIT(&menuq); 540 | TAILQ_FOREACH(cc, &sc->clientq, entry) { 541 | if ((cargs->flag & CWM_MENU_WINDOW_ALL) || 542 | (cc->flags & CLIENT_HIDDEN)) 543 | menuq_add(&menuq, cc, NULL); 544 | } 545 | 546 | if ((mi = menu_filter(sc, &menuq, "window", NULL, mflags, 547 | search_match_client, search_print_client)) != NULL) { 548 | cc = (struct client_ctx *)mi->ctx; 549 | client_show(cc); 550 | if ((old_cc = client_current(sc)) != NULL) 551 | client_ptr_save(old_cc); 552 | client_ptr_warp(cc); 553 | } 554 | 555 | menuq_clear(&menuq); 556 | } 557 | 558 | void 559 | kbfunc_menu_cmd(void *ctx, struct cargs *cargs) 560 | { 561 | struct screen_ctx *sc = ctx; 562 | struct cmd_ctx *cmd; 563 | struct menu *mi; 564 | struct menu_q menuq; 565 | int mflags = 0; 566 | 567 | if (cargs->xev == CWM_XEV_BTN) 568 | mflags |= CWM_MENU_LIST; 569 | 570 | TAILQ_INIT(&menuq); 571 | TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { 572 | if ((strcmp(cmd->name, "lock") == 0) || 573 | (strcmp(cmd->name, "term") == 0)) 574 | continue; 575 | menuq_add(&menuq, cmd, NULL); 576 | } 577 | 578 | if ((mi = menu_filter(sc, &menuq, "application", NULL, mflags, 579 | search_match_cmd, search_print_cmd)) != NULL) { 580 | cmd = (struct cmd_ctx *)mi->ctx; 581 | u_spawn(cmd->path); 582 | } 583 | 584 | menuq_clear(&menuq); 585 | } 586 | 587 | void 588 | kbfunc_menu_group(void *ctx, struct cargs *cargs) 589 | { 590 | struct screen_ctx *sc = ctx; 591 | struct group_ctx *gc; 592 | struct menu *mi; 593 | struct menu_q menuq; 594 | int mflags = 0; 595 | 596 | if (cargs->xev == CWM_XEV_BTN) 597 | mflags |= CWM_MENU_LIST; 598 | 599 | TAILQ_INIT(&menuq); 600 | TAILQ_FOREACH(gc, &sc->groupq, entry) { 601 | if (group_holds_only_sticky(gc)) 602 | continue; 603 | menuq_add(&menuq, gc, NULL); 604 | } 605 | 606 | if ((mi = menu_filter(sc, &menuq, "group", NULL, mflags, 607 | search_match_group, search_print_group)) != NULL) { 608 | gc = (struct group_ctx *)mi->ctx; 609 | (group_holds_only_hidden(gc)) ? 610 | group_show(gc) : group_hide(gc); 611 | } 612 | 613 | menuq_clear(&menuq); 614 | } 615 | 616 | void 617 | kbfunc_menu_wm(void *ctx, struct cargs *cargs) 618 | { 619 | struct screen_ctx *sc = ctx; 620 | struct cmd_ctx *wm; 621 | struct menu *mi; 622 | struct menu_q menuq; 623 | int mflags = 0; 624 | 625 | if (cargs->xev == CWM_XEV_BTN) 626 | mflags |= CWM_MENU_LIST; 627 | 628 | TAILQ_INIT(&menuq); 629 | TAILQ_FOREACH(wm, &Conf.wmq, entry) 630 | menuq_add(&menuq, wm, NULL); 631 | 632 | if ((mi = menu_filter(sc, &menuq, "wm", NULL, mflags, 633 | search_match_wm, search_print_wm)) != NULL) { 634 | wm = (struct cmd_ctx *)mi->ctx; 635 | free(Conf.wm_argv); 636 | Conf.wm_argv = xstrdup(wm->path); 637 | cwm_status = CWM_EXEC_WM; 638 | } 639 | 640 | menuq_clear(&menuq); 641 | } 642 | 643 | void 644 | kbfunc_menu_exec(void *ctx, struct cargs *cargs) 645 | { 646 | #define NPATHS 256 647 | struct screen_ctx *sc = ctx; 648 | char **ap, *paths[NPATHS], *path, *pathcpy; 649 | char tpath[PATH_MAX]; 650 | struct stat sb; 651 | DIR *dirp; 652 | struct dirent *dp; 653 | struct menu *mi; 654 | struct menu_q menuq; 655 | int l, i; 656 | int mflags = (CWM_MENU_DUMMY | CWM_MENU_FILE); 657 | 658 | TAILQ_INIT(&menuq); 659 | 660 | if ((path = getenv("PATH")) == NULL) 661 | path = _PATH_DEFPATH; 662 | pathcpy = path = xstrdup(path); 663 | 664 | for (ap = paths; ap < &paths[NPATHS - 1] && 665 | (*ap = strsep(&pathcpy, ":")) != NULL;) { 666 | if (**ap != '\0') 667 | ap++; 668 | } 669 | *ap = NULL; 670 | for (i = 0; i < NPATHS && paths[i] != NULL; i++) { 671 | if ((dirp = opendir(paths[i])) == NULL) 672 | continue; 673 | 674 | while ((dp = readdir(dirp)) != NULL) { 675 | (void)memset(tpath, '\0', sizeof(tpath)); 676 | l = snprintf(tpath, sizeof(tpath), "%s/%s", paths[i], 677 | dp->d_name); 678 | if (l == -1 || l >= sizeof(tpath)) 679 | continue; 680 | /* Skip everything but regular files and symlinks. */ 681 | if (dp->d_type != DT_REG && dp->d_type != DT_LNK) { 682 | /* lstat(2) in case d_type isn't supported. */ 683 | if (lstat(tpath, &sb) == -1) 684 | continue; 685 | if (!S_ISREG(sb.st_mode) && 686 | !S_ISLNK(sb.st_mode)) 687 | continue; 688 | } 689 | if (access(tpath, X_OK) == 0) 690 | menuq_add(&menuq, NULL, "%s", dp->d_name); 691 | } 692 | (void)closedir(dirp); 693 | } 694 | free(path); 695 | 696 | if ((mi = menu_filter(sc, &menuq, "exec", NULL, mflags, 697 | search_match_exec, search_print_text)) != NULL) { 698 | if (mi->text[0] == '\0') 699 | goto out; 700 | u_spawn(mi->text); 701 | } 702 | out: 703 | if (mi != NULL && mi->dummy) 704 | free(mi); 705 | menuq_clear(&menuq); 706 | } 707 | 708 | void 709 | kbfunc_menu_ssh(void *ctx, struct cargs *cargs) 710 | { 711 | struct screen_ctx *sc = ctx; 712 | struct cmd_ctx *cmd; 713 | struct menu *mi; 714 | struct menu_q menuq; 715 | FILE *fp; 716 | char *buf, *lbuf, *p; 717 | char hostbuf[_POSIX_HOST_NAME_MAX+1]; 718 | char path[PATH_MAX]; 719 | int l; 720 | size_t len; 721 | ssize_t slen; 722 | int mflags = (CWM_MENU_DUMMY); 723 | 724 | TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { 725 | if (strcmp(cmd->name, "term") == 0) 726 | break; 727 | } 728 | TAILQ_INIT(&menuq); 729 | 730 | if ((fp = fopen(Conf.known_hosts, "r")) == NULL) { 731 | warn("%s: %s", __func__, Conf.known_hosts); 732 | goto menu; 733 | } 734 | 735 | lbuf = NULL; 736 | len = 0; 737 | while ((slen = getline(&lbuf, &len, fp)) != -1) { 738 | buf = lbuf; 739 | if (buf[slen - 1] == '\n') 740 | buf[slen - 1] = '\0'; 741 | 742 | /* skip hashed hosts */ 743 | if (strncmp(buf, HASH_MARKER, strlen(HASH_MARKER)) == 0) 744 | continue; 745 | for (p = buf; *p != ',' && *p != ' ' && p != buf + slen; p++) 746 | ; 747 | /* ignore badness */ 748 | if (p - buf + 1 > sizeof(hostbuf)) 749 | continue; 750 | (void)strlcpy(hostbuf, buf, p - buf + 1); 751 | menuq_add(&menuq, NULL, "%s", hostbuf); 752 | } 753 | free(lbuf); 754 | if (ferror(fp)) 755 | err(1, "%s", path); 756 | (void)fclose(fp); 757 | menu: 758 | if ((mi = menu_filter(sc, &menuq, "ssh", NULL, mflags, 759 | search_match_text, search_print_text)) != NULL) { 760 | if (mi->text[0] == '\0') 761 | goto out; 762 | l = snprintf(path, sizeof(path), "%s -T '[ssh] %s' -e ssh %s", 763 | cmd->path, mi->text, mi->text); 764 | if (l == -1 || l >= sizeof(path)) 765 | goto out; 766 | u_spawn(path); 767 | } 768 | out: 769 | if (mi != NULL && mi->dummy) 770 | free(mi); 771 | menuq_clear(&menuq); 772 | } 773 | 774 | void 775 | kbfunc_client_menu_label(void *ctx, struct cargs *cargs) 776 | { 777 | struct client_ctx *cc = ctx; 778 | struct menu *mi; 779 | struct menu_q menuq; 780 | int mflags = (CWM_MENU_DUMMY); 781 | 782 | TAILQ_INIT(&menuq); 783 | 784 | /* dummy is set, so this will always return */ 785 | mi = menu_filter(cc->sc, &menuq, "label", cc->label, mflags, 786 | search_match_text, search_print_text); 787 | 788 | if (!mi->abort) { 789 | free(cc->label); 790 | cc->label = xstrdup(mi->text); 791 | } 792 | free(mi); 793 | } 794 | 795 | void 796 | kbfunc_exec_cmd(void *ctx, struct cargs *cargs) 797 | { 798 | u_spawn(cargs->cmd); 799 | } 800 | 801 | void 802 | kbfunc_exec_term(void *ctx, struct cargs *cargs) 803 | { 804 | struct cmd_ctx *cmd; 805 | 806 | TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { 807 | if (strcmp(cmd->name, "term") == 0) 808 | u_spawn(cmd->path); 809 | } 810 | } 811 | 812 | void 813 | kbfunc_exec_lock(void *ctx, struct cargs *cargs) 814 | { 815 | struct cmd_ctx *cmd; 816 | 817 | TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { 818 | if (strcmp(cmd->name, "lock") == 0) 819 | u_spawn(cmd->path); 820 | } 821 | } 822 | -------------------------------------------------------------------------------- /menu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * calmwm - the calm window manager 3 | * 4 | * Copyright (c) 2008 Owain G. Ainsworth 5 | * Copyright (c) 2004 Marius Aamodt Eriksen 6 | * 7 | * Permission to use, copy, modify, and distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | * 19 | * $OpenBSD$ 20 | */ 21 | 22 | #include 23 | #include "queue.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "calmwm.h" 36 | 37 | #define PROMPT_SCHAR "\xc2\xbb" 38 | #define PROMPT_ECHAR "\xc2\xab" 39 | 40 | #define MENUMASK (MOUSEMASK | ButtonMotionMask | KeyPressMask | \ 41 | ExposureMask) 42 | #define MENUGRABMASK (MOUSEMASK | ButtonMotionMask | StructureNotifyMask) 43 | 44 | enum ctltype { 45 | CTL_NONE = -1, 46 | CTL_ERASEONE = 0, CTL_WIPE, CTL_UP, CTL_DOWN, CTL_RETURN, 47 | CTL_TAB, CTL_ABORT, CTL_ALL 48 | }; 49 | 50 | struct menu_ctx { 51 | struct screen_ctx *sc; 52 | Window win; 53 | XftDraw *xftdraw; 54 | struct geom geom; 55 | char searchstr[MENU_MAXENTRY + 1]; 56 | char dispstr[MENU_MAXENTRY*2 + 1]; 57 | char promptstr[MENU_MAXENTRY + 1]; 58 | int list; 59 | int listing; 60 | int changed; 61 | int prev; 62 | int entry; 63 | int num; 64 | int flags; 65 | void (*match)(struct menu_q *, struct menu_q *, char *); 66 | void (*print)(struct menu *, int); 67 | }; 68 | static struct menu *menu_handle_key(XEvent *, struct menu_ctx *, 69 | struct menu_q *, struct menu_q *); 70 | static void menu_handle_move(struct menu_ctx *, 71 | struct menu_q *, int, int); 72 | static struct menu *menu_handle_release(struct menu_ctx *, 73 | struct menu_q *, int, int); 74 | static void menu_draw(struct menu_ctx *, struct menu_q *, 75 | struct menu_q *); 76 | static void menu_draw_entry(struct menu_ctx *, struct menu_q *, 77 | int, int); 78 | static int menu_calc_entry(struct menu_ctx *, int, int); 79 | static struct menu *menu_complete_path(struct menu_ctx *); 80 | static int menu_keycode(XKeyEvent *, enum ctltype *, char *); 81 | 82 | struct menu * 83 | menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt, 84 | const char *initial, int flags, 85 | void (*match)(struct menu_q *, struct menu_q *, char *), 86 | void (*print)(struct menu *, int)) 87 | { 88 | struct menu_ctx mc; 89 | struct menu_q resultq; 90 | struct menu *mi = NULL; 91 | XEvent e; 92 | Window focuswin; 93 | int focusrevert, xsave, ysave, xcur, ycur; 94 | 95 | TAILQ_INIT(&resultq); 96 | 97 | xu_ptr_get(sc->rootwin, &xsave, &ysave); 98 | 99 | (void)memset(&mc, 0, sizeof(mc)); 100 | mc.sc = sc; 101 | mc.flags = flags; 102 | mc.match = match; 103 | mc.print = print; 104 | mc.entry = mc.prev = -1; 105 | mc.geom.x = xsave; 106 | mc.geom.y = ysave; 107 | 108 | if (mc.flags & CWM_MENU_LIST) 109 | mc.list = 1; 110 | 111 | (void)strlcpy(mc.promptstr, prompt, sizeof(mc.promptstr)); 112 | if (initial != NULL) 113 | (void)strlcpy(mc.searchstr, initial, sizeof(mc.searchstr)); 114 | else 115 | mc.searchstr[0] = '\0'; 116 | 117 | mc.win = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0, 1, 1, 118 | Conf.bwidth, 119 | sc->xftcolor[CWM_COLOR_MENU_FG].pixel, 120 | sc->xftcolor[CWM_COLOR_MENU_BG].pixel); 121 | mc.xftdraw = XftDrawCreate(X_Dpy, mc.win, 122 | sc->visual, sc->colormap); 123 | 124 | XSelectInput(X_Dpy, mc.win, MENUMASK); 125 | XMapRaised(X_Dpy, mc.win); 126 | 127 | if (XGrabPointer(X_Dpy, mc.win, False, MENUGRABMASK, 128 | GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_QUESTION], 129 | CurrentTime) != GrabSuccess) { 130 | XftDrawDestroy(mc.xftdraw); 131 | XDestroyWindow(X_Dpy, mc.win); 132 | return NULL; 133 | } 134 | 135 | XGetInputFocus(X_Dpy, &focuswin, &focusrevert); 136 | XSetInputFocus(X_Dpy, mc.win, RevertToPointerRoot, CurrentTime); 137 | 138 | /* make sure keybindings don't remove keys from the menu stream */ 139 | XGrabKeyboard(X_Dpy, mc.win, True, 140 | GrabModeAsync, GrabModeAsync, CurrentTime); 141 | 142 | for (;;) { 143 | mc.changed = 0; 144 | 145 | XWindowEvent(X_Dpy, mc.win, MENUMASK, &e); 146 | 147 | switch (e.type) { 148 | case KeyPress: 149 | if ((mi = menu_handle_key(&e, &mc, menuq, &resultq)) 150 | != NULL) 151 | goto out; 152 | /* FALLTHROUGH */ 153 | case Expose: 154 | menu_draw(&mc, menuq, &resultq); 155 | break; 156 | case MotionNotify: 157 | menu_handle_move(&mc, &resultq, 158 | e.xbutton.x, e.xbutton.y); 159 | break; 160 | case ButtonRelease: 161 | if ((mi = menu_handle_release(&mc, &resultq, 162 | e.xbutton.x, e.xbutton.y)) != NULL) 163 | goto out; 164 | break; 165 | default: 166 | break; 167 | } 168 | } 169 | out: 170 | if ((mc.flags & CWM_MENU_DUMMY) == 0 && mi->dummy) { 171 | /* no mouse based match */ 172 | free(mi); 173 | mi = NULL; 174 | } 175 | 176 | XftDrawDestroy(mc.xftdraw); 177 | XDestroyWindow(X_Dpy, mc.win); 178 | 179 | XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime); 180 | /* restore if user didn't move */ 181 | xu_ptr_get(sc->rootwin, &xcur, &ycur); 182 | if (xcur == mc.geom.x && ycur == mc.geom.y) 183 | xu_ptr_set(sc->rootwin, xsave, ysave); 184 | 185 | XUngrabPointer(X_Dpy, CurrentTime); 186 | XUngrabKeyboard(X_Dpy, CurrentTime); 187 | 188 | return mi; 189 | } 190 | 191 | static struct menu * 192 | menu_complete_path(struct menu_ctx *mc) 193 | { 194 | struct screen_ctx *sc = mc->sc; 195 | struct menu *mi, *mr; 196 | struct menu_q menuq; 197 | int mflags = (CWM_MENU_DUMMY); 198 | 199 | mr = xcalloc(1, sizeof(*mr)); 200 | 201 | TAILQ_INIT(&menuq); 202 | 203 | if ((mi = menu_filter(sc, &menuq, mc->searchstr, NULL, mflags, 204 | search_match_path, search_print_text)) != NULL) { 205 | mr->abort = mi->abort; 206 | mr->dummy = mi->dummy; 207 | if (mi->text[0] != '\0') 208 | snprintf(mr->text, sizeof(mr->text), "%s \"%s\"", 209 | mc->searchstr, mi->text); 210 | else if (!mr->abort) 211 | strlcpy(mr->text, mc->searchstr, sizeof(mr->text)); 212 | } 213 | 214 | menuq_clear(&menuq); 215 | 216 | return mr; 217 | } 218 | 219 | static struct menu * 220 | menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq, 221 | struct menu_q *resultq) 222 | { 223 | struct menu *mi; 224 | enum ctltype ctl; 225 | char chr[32]; 226 | size_t len; 227 | int clen, i; 228 | wchar_t wc; 229 | 230 | if (menu_keycode(&e->xkey, &ctl, chr) < 0) 231 | return NULL; 232 | 233 | switch (ctl) { 234 | case CTL_ERASEONE: 235 | if ((len = strlen(mc->searchstr)) > 0) { 236 | clen = 1; 237 | while (mbtowc(&wc, &mc->searchstr[len-clen], MB_CUR_MAX) == -1) 238 | clen++; 239 | for (i = 1; i <= clen; i++) 240 | mc->searchstr[len - i] = '\0'; 241 | mc->changed = 1; 242 | } 243 | break; 244 | case CTL_UP: 245 | mi = TAILQ_LAST(resultq, menu_q); 246 | if (mi == NULL) 247 | break; 248 | 249 | TAILQ_REMOVE(resultq, mi, resultentry); 250 | TAILQ_INSERT_HEAD(resultq, mi, resultentry); 251 | break; 252 | case CTL_DOWN: 253 | mi = TAILQ_FIRST(resultq); 254 | if (mi == NULL) 255 | break; 256 | 257 | TAILQ_REMOVE(resultq, mi, resultentry); 258 | TAILQ_INSERT_TAIL(resultq, mi, resultentry); 259 | break; 260 | case CTL_RETURN: 261 | /* 262 | * Return whatever the cursor is currently on. Else 263 | * even if dummy is zero, we need to return something. 264 | */ 265 | if ((mi = TAILQ_FIRST(resultq)) == NULL) { 266 | mi = xmalloc(sizeof(*mi)); 267 | (void)strlcpy(mi->text, 268 | mc->searchstr, sizeof(mi->text)); 269 | mi->dummy = 1; 270 | } 271 | mi->abort = 0; 272 | return mi; 273 | case CTL_WIPE: 274 | mc->searchstr[0] = '\0'; 275 | mc->changed = 1; 276 | break; 277 | case CTL_TAB: 278 | if ((mi = TAILQ_FIRST(resultq)) != NULL) { 279 | /* 280 | * - We are in exec_path menu mode 281 | * - It is equal to the input 282 | * We got a command, launch the file menu 283 | */ 284 | if ((mc->flags & CWM_MENU_FILE) && 285 | (strncmp(mc->searchstr, mi->text, 286 | strlen(mi->text))) == 0) 287 | return menu_complete_path(mc); 288 | 289 | /* 290 | * Put common prefix of the results into searchstr 291 | */ 292 | (void)strlcpy(mc->searchstr, 293 | mi->text, sizeof(mc->searchstr)); 294 | while ((mi = TAILQ_NEXT(mi, resultentry)) != NULL) { 295 | i = 0; 296 | while (tolower(mc->searchstr[i]) == 297 | tolower(mi->text[i])) 298 | i++; 299 | mc->searchstr[i] = '\0'; 300 | } 301 | mc->changed = 1; 302 | } 303 | break; 304 | case CTL_ALL: 305 | mc->list = !mc->list; 306 | break; 307 | case CTL_ABORT: 308 | mi = xmalloc(sizeof(*mi)); 309 | mi->text[0] = '\0'; 310 | mi->dummy = 1; 311 | mi->abort = 1; 312 | return mi; 313 | default: 314 | break; 315 | } 316 | 317 | if (chr[0] != '\0') { 318 | mc->changed = 1; 319 | (void)strlcat(mc->searchstr, chr, sizeof(mc->searchstr)); 320 | } 321 | 322 | if (mc->changed) { 323 | if (mc->searchstr[0] != '\0') 324 | (*mc->match)(menuq, resultq, mc->searchstr); 325 | } else if (!mc->list && mc->listing) { 326 | TAILQ_INIT(resultq); 327 | mc->listing = 0; 328 | } 329 | 330 | return NULL; 331 | } 332 | 333 | static void 334 | menu_draw(struct menu_ctx *mc, struct menu_q *menuq, struct menu_q *resultq) 335 | { 336 | struct screen_ctx *sc = mc->sc; 337 | struct menu *mi; 338 | struct geom area; 339 | int n, xsave, ysave; 340 | XGlyphInfo extents; 341 | 342 | if (mc->list) { 343 | if (TAILQ_EMPTY(resultq)) { 344 | /* Copy them all over. */ 345 | TAILQ_FOREACH(mi, menuq, entry) 346 | TAILQ_INSERT_TAIL(resultq, mi, resultentry); 347 | 348 | mc->listing = 1; 349 | } else if (mc->changed) 350 | mc->listing = 0; 351 | } 352 | 353 | (void)snprintf(mc->dispstr, sizeof(mc->dispstr), "%s%s%s%s", 354 | mc->promptstr, PROMPT_SCHAR, mc->searchstr, PROMPT_ECHAR); 355 | XftTextExtentsUtf8(X_Dpy, sc->xftfont, 356 | (const FcChar8*)mc->dispstr, strlen(mc->dispstr), &extents); 357 | mc->geom.w = extents.xOff; 358 | mc->geom.h = sc->xftfont->ascent + sc->xftfont->descent; 359 | mc->num = 1; 360 | 361 | TAILQ_FOREACH(mi, resultq, resultentry) { 362 | (*mc->print)(mi, mc->listing); 363 | XftTextExtentsUtf8(X_Dpy, sc->xftfont, 364 | (const FcChar8*)mi->print, 365 | MIN(strlen(mi->print), MENU_MAXENTRY), &extents); 366 | mc->geom.w = MAX(mc->geom.w, extents.xOff); 367 | mc->geom.h += sc->xftfont->ascent + sc->xftfont->descent; 368 | mc->num++; 369 | } 370 | 371 | area = screen_area(sc, mc->geom.x, mc->geom.y, 1); 372 | area.w += area.x - Conf.bwidth * 2; 373 | area.h += area.y - Conf.bwidth * 2; 374 | 375 | xsave = mc->geom.x; 376 | ysave = mc->geom.y; 377 | 378 | /* Never hide the top, or left side, of the menu. */ 379 | if (mc->geom.x + mc->geom.w >= area.w) 380 | mc->geom.x = area.w - mc->geom.w; 381 | if (mc->geom.x < area.x) { 382 | mc->geom.x = area.x; 383 | mc->geom.w = MIN(mc->geom.w, (area.w - area.x)); 384 | } 385 | if (mc->geom.y + mc->geom.h >= area.h) 386 | mc->geom.y = area.h - mc->geom.h; 387 | if (mc->geom.y < area.y) { 388 | mc->geom.y = area.y; 389 | mc->geom.h = MIN(mc->geom.h, (area.h - area.y)); 390 | } 391 | 392 | if (mc->geom.x != xsave || mc->geom.y != ysave) 393 | xu_ptr_set(sc->rootwin, mc->geom.x, mc->geom.y); 394 | 395 | XClearWindow(X_Dpy, mc->win); 396 | XMoveResizeWindow(X_Dpy, mc->win, mc->geom.x, mc->geom.y, 397 | mc->geom.w, mc->geom.h); 398 | 399 | n = 1; 400 | XftDrawStringUtf8(mc->xftdraw, 401 | &sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont, 402 | 0, sc->xftfont->ascent, 403 | (const FcChar8*)mc->dispstr, strlen(mc->dispstr)); 404 | 405 | TAILQ_FOREACH(mi, resultq, resultentry) { 406 | int y = n * (sc->xftfont->ascent + sc->xftfont->descent); 407 | 408 | /* Stop drawing when menu doesn't fit inside the screen. */ 409 | if (mc->geom.y + y >= area.h) 410 | break; 411 | 412 | XftDrawStringUtf8(mc->xftdraw, 413 | &sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont, 414 | 0, y + sc->xftfont->ascent, 415 | (const FcChar8*)mi->print, strlen(mi->print)); 416 | n++; 417 | } 418 | if (n > 1) 419 | menu_draw_entry(mc, resultq, 1, 1); 420 | } 421 | 422 | static void 423 | menu_draw_entry(struct menu_ctx *mc, struct menu_q *resultq, 424 | int entry, int active) 425 | { 426 | struct screen_ctx *sc = mc->sc; 427 | struct menu *mi; 428 | int color, i = 1, y; 429 | 430 | TAILQ_FOREACH(mi, resultq, resultentry) 431 | if (entry == i++) 432 | break; 433 | if (mi == NULL) 434 | return; 435 | 436 | y = entry * (sc->xftfont->ascent + sc->xftfont->descent); 437 | color = (active) ? CWM_COLOR_MENU_FG : CWM_COLOR_MENU_BG; 438 | XftDrawRect(mc->xftdraw, &sc->xftcolor[color], 0, y, 439 | mc->geom.w, sc->xftfont->ascent + sc->xftfont->descent); 440 | color = (active) ? CWM_COLOR_MENU_FONT_SEL : CWM_COLOR_MENU_FONT; 441 | XftDrawStringUtf8(mc->xftdraw, 442 | &sc->xftcolor[color], sc->xftfont, 0, y + sc->xftfont->ascent, 443 | (const FcChar8*)mi->print, strlen(mi->print)); 444 | } 445 | 446 | static void 447 | menu_handle_move(struct menu_ctx *mc, struct menu_q *resultq, int x, int y) 448 | { 449 | mc->prev = mc->entry; 450 | mc->entry = menu_calc_entry(mc, x, y); 451 | 452 | if (mc->prev == mc->entry) 453 | return; 454 | 455 | if (mc->prev != -1) 456 | menu_draw_entry(mc, resultq, mc->prev, 0); 457 | if (mc->entry != -1) { 458 | XChangeActivePointerGrab(X_Dpy, MENUGRABMASK, 459 | Conf.cursor[CF_NORMAL], CurrentTime); 460 | menu_draw_entry(mc, resultq, mc->entry, 1); 461 | } 462 | } 463 | 464 | static struct menu * 465 | menu_handle_release(struct menu_ctx *mc, struct menu_q *resultq, int x, int y) 466 | { 467 | struct menu *mi; 468 | int entry, i = 1; 469 | 470 | entry = menu_calc_entry(mc, x, y); 471 | 472 | TAILQ_FOREACH(mi, resultq, resultentry) 473 | if (entry == i++) 474 | break; 475 | if (mi == NULL) { 476 | mi = xmalloc(sizeof(*mi)); 477 | mi->text[0] = '\0'; 478 | mi->dummy = 1; 479 | } 480 | return mi; 481 | } 482 | 483 | static int 484 | menu_calc_entry(struct menu_ctx *mc, int x, int y) 485 | { 486 | struct screen_ctx *sc = mc->sc; 487 | int entry; 488 | 489 | entry = y / (sc->xftfont->ascent + sc->xftfont->descent); 490 | 491 | /* in bounds? */ 492 | if (x < 0 || x > mc->geom.w || y < 0 || 493 | y > (sc->xftfont->ascent + sc->xftfont->descent) * mc->num || 494 | entry < 0 || entry >= mc->num) 495 | entry = -1; 496 | 497 | if (entry == 0) 498 | entry = -1; 499 | 500 | return entry; 501 | } 502 | 503 | static int 504 | menu_keycode(XKeyEvent *ev, enum ctltype *ctl, char *chr) 505 | { 506 | KeySym ks; 507 | 508 | *ctl = CTL_NONE; 509 | chr[0] = '\0'; 510 | 511 | ks = XkbKeycodeToKeysym(X_Dpy, ev->keycode, 0, 512 | (ev->state & ShiftMask) ? 1 : 0); 513 | 514 | /* Look for control characters. */ 515 | switch (ks) { 516 | case XK_BackSpace: 517 | *ctl = CTL_ERASEONE; 518 | break; 519 | case XK_KP_Enter: 520 | case XK_Return: 521 | *ctl = CTL_RETURN; 522 | break; 523 | case XK_Tab: 524 | *ctl = CTL_TAB; 525 | break; 526 | case XK_Up: 527 | *ctl = CTL_UP; 528 | break; 529 | case XK_Down: 530 | *ctl = CTL_DOWN; 531 | break; 532 | case XK_Escape: 533 | *ctl = CTL_ABORT; 534 | break; 535 | } 536 | 537 | if (*ctl == CTL_NONE && (ev->state & ControlMask)) { 538 | switch (ks) { 539 | case XK_s: 540 | case XK_S: 541 | /* Emacs "next" */ 542 | *ctl = CTL_DOWN; 543 | break; 544 | case XK_r: 545 | case XK_R: 546 | /* Emacs "previous" */ 547 | *ctl = CTL_UP; 548 | break; 549 | case XK_u: 550 | case XK_U: 551 | *ctl = CTL_WIPE; 552 | break; 553 | case XK_h: 554 | case XK_H: 555 | *ctl = CTL_ERASEONE; 556 | break; 557 | case XK_a: 558 | case XK_A: 559 | *ctl = CTL_ALL; 560 | break; 561 | case XK_bracketleft: 562 | *ctl = CTL_ABORT; 563 | break; 564 | } 565 | } 566 | 567 | if (*ctl == CTL_NONE && (ev->state & Mod1Mask)) { 568 | switch (ks) { 569 | case XK_j: 570 | case XK_J: 571 | /* Vi "down" */ 572 | *ctl = CTL_DOWN; 573 | break; 574 | case XK_k: 575 | case XK_K: 576 | /* Vi "up" */ 577 | *ctl = CTL_UP; 578 | break; 579 | } 580 | } 581 | 582 | if (*ctl != CTL_NONE) 583 | return 0; 584 | 585 | if (XLookupString(ev, chr, 32, &ks, NULL) < 0) 586 | return -1; 587 | 588 | return 0; 589 | } 590 | 591 | void 592 | menuq_add(struct menu_q *mq, void *ctx, const char *fmt, ...) 593 | { 594 | va_list ap; 595 | struct menu *mi; 596 | 597 | mi = xcalloc(1, sizeof(*mi)); 598 | mi->ctx = ctx; 599 | 600 | va_start(ap, fmt); 601 | if (fmt != NULL) 602 | (void)vsnprintf(mi->text, sizeof(mi->text), fmt, ap); 603 | else 604 | mi->text[0] = '\0'; 605 | va_end(ap); 606 | 607 | TAILQ_INSERT_TAIL(mq, mi, entry); 608 | } 609 | 610 | void 611 | menuq_clear(struct menu_q *mq) 612 | { 613 | struct menu *mi; 614 | 615 | while ((mi = TAILQ_FIRST(mq)) != NULL) { 616 | TAILQ_REMOVE(mq, mi, entry); 617 | free(mi); 618 | } 619 | } 620 | -------------------------------------------------------------------------------- /migrate-config.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -p 2 | # Originally by Stuart Henderson in 3 | # <20161206120601.vmimohqh4nafaeah@symphytum.spacehopper.org> 4 | # Ported to Perl and slightly tweaked by Leah Neukirchen. 5 | 6 | s/\bbind\b(.*)\bunmap/unbind-key \1/; 7 | s/\bmousebind\b(.*)unmap/unbind-mouse \1/; 8 | s/\bresize\b/window-resize/; 9 | s/\bresizedown\b/window-resize-down/; 10 | s/\bresizeleft\b/window-resize-left/; 11 | s/\bresizeright\b/window-resize-right/; 12 | s/\bresizeup\b/window-resize-up/; 13 | s/\bmove\b/window-move/; 14 | s/\bmovedown\b/window-move-down/; 15 | s/\bmoveleft\b/window-move-left/; 16 | s/\bmoveright\b/window-move-right/; 17 | s/\bmoveup\b/window-move-up/; 18 | s/\bbigmovedown\b/window-move-down-big/; 19 | s/\bbigmoveleft\b/window-move-left-big/; 20 | s/\bbigmoveright\b/window-move-right-big/; 21 | s/\bbigmoveup\b/window-move-up-big/; 22 | s/\bbigptrmovedown\b/pointer-move-down-big/; 23 | s/\bbigptrmoveleft\b/pointer-move-left-big/; 24 | s/\bbigptrmoveright\b/pointer-move-right-big/; 25 | s/\bbigptrmoveup\b/pointer-move-up-big/; 26 | s/\bbigresizedown\b/window-resize-down-big/; 27 | s/\bbigresizeleft\b/window-resize-left-big/; 28 | s/\bbigresizeright\b/window-resize-right-big/; 29 | s/\bbigresizeup\b/window-resize-up-big/; 30 | s/\bbind\b/bind-key/; 31 | s/\bcycle\b/window-cycle/; 32 | s/\bcyclegroup\b/group-cycle/; 33 | s/\bcycleingroup\b/window-cycle-ingroup/; 34 | s/\bdelete\b/window-delete/; 35 | s/\bexec\b/menu-exec/; 36 | s/\bexec_wm\b/menu-exec-wm/; 37 | s/\bfreeze\b/window-freeze/; 38 | s/\bfullscreen\b/window-fullscreen/; 39 | s/\bgroupsearch\b/menu-group/; 40 | s/\bgrouptoggle\b/window-group/; 41 | s/\bhide\b/window-hide/; 42 | s/\bhmaximize\b/window-hmaximize/; 43 | s/\bhtile\b/window-htile/; 44 | s/\blabel\b/window-menu-label/; 45 | s/\blower\b/window-lower/; 46 | s/\bmaximize\b/window-maximize/; 47 | s/\bmenu_cmd\b/menu-cmd/; 48 | s/\bmenu_group\b/menu-group/; 49 | s/\bmenu_unhide\b/menu-window/; 50 | s/\bmenusearch\b/menu-cmd/; 51 | s/\bmousebind\b/bind-mouse/; 52 | s/\bnogroup\b/group-toggle-all/; 53 | s/\bptrmovedown\b/pointer-move-down/; 54 | s/\bptrmoveleft\b/pointer-move-left/; 55 | s/\bptrmoveright\b/pointer-move-right/; 56 | s/\bptrmoveup\b/pointer-move-up/; 57 | s/\braise\b/window-raise/; 58 | s/\brcycle\b/window-rcycle/; 59 | s/\brcyclegroup\b/group-rcycle/; 60 | s/\brcycleingroup\b/window-rcycle-ingroup/; 61 | s/\bsearch\b/menu-window/; 62 | s/\bssh\b/menu-ssh/; 63 | s/\bstick\b/window-stick/; 64 | s/\bvmaximize\b/window-vmaximize/; 65 | s/\bvtile\b/window-vtile/; 66 | s/\bwindow_grouptoggle\b/window-group/; 67 | s/\bwindow_move\b/window-move/; 68 | s/\bwindow_resize\b/window-resize/; 69 | s/\bwindow_hide\b/window-hide/; 70 | s/\bwindow_lower\b/window-lower/; 71 | s/\bwindow_raise\b/window-raise/; 72 | s/\bmovetogroup([1-9])\b/window-movetogroup-\1/; 73 | s/\bgrouponly([1-9])\b/group-only-\1/; 74 | s/\bgroup([1-9])\b/group-toggle-\1/; 75 | -------------------------------------------------------------------------------- /parse.y: -------------------------------------------------------------------------------- 1 | /* $OpenBSD$ */ 2 | 3 | /* 4 | * Copyright (c) 2002, 2003, 2004 Henning Brauer 5 | * Copyright (c) 2001 Markus Friedl. All rights reserved. 6 | * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 7 | * Copyright (c) 2001 Theo de Raadt. All rights reserved. 8 | * 9 | * Permission to use, copy, modify, and distribute this software for any 10 | * purpose with or without fee is hereby granted, provided that the above 11 | * copyright notice and this permission notice appear in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | %{ 23 | 24 | #include 25 | #include "queue.h" 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "calmwm.h" 37 | 38 | #define YYSTYPE_IS_DECLARED 39 | 40 | TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 41 | static struct file { 42 | TAILQ_ENTRY(file) entry; 43 | FILE *stream; 44 | char *name; 45 | int lineno; 46 | int errors; 47 | } *file, *topfile; 48 | struct file *pushfile(const char *, FILE *); 49 | int popfile(void); 50 | int yyparse(void); 51 | int yylex(void); 52 | int yyerror(const char *, ...) 53 | __attribute__((__format__ (printf, 1, 2))) 54 | __attribute__((__nonnull__ (1))); 55 | int kw_cmp(const void *, const void *); 56 | int lookup(char *); 57 | int lgetc(int); 58 | int lungetc(int); 59 | int findeol(void); 60 | 61 | static struct conf *conf; 62 | 63 | typedef struct { 64 | union { 65 | int64_t number; 66 | char *string; 67 | } v; 68 | int lineno; 69 | } YYSTYPE; 70 | 71 | %} 72 | 73 | %token BINDKEY UNBINDKEY BINDMOUSE UNBINDMOUSE 74 | %token FONTNAME STICKY GAP 75 | %token AUTOGROUP COMMAND IGNORE WM 76 | %token YES NO BORDERWIDTH MOVEAMOUNT HTILE VTILE 77 | %token COLOR SNAPDIST 78 | %token ACTIVEBORDER INACTIVEBORDER URGENCYBORDER 79 | %token GROUPBORDER UNGROUPBORDER 80 | %token MENUBG MENUFG 81 | %token FONTCOLOR FONTSELCOLOR 82 | %token ERROR 83 | %token STRING 84 | %token NUMBER 85 | %type yesno 86 | %type string numberstring 87 | %% 88 | 89 | grammar : /* empty */ 90 | | grammar '\n' 91 | | grammar main '\n' 92 | | grammar color '\n' 93 | | grammar error '\n' { file->errors++; } 94 | ; 95 | 96 | string : string STRING { 97 | if (asprintf(&$$, "%s %s", $1, $2) == -1) { 98 | free($1); 99 | free($2); 100 | yyerror("string: asprintf"); 101 | YYERROR; 102 | } 103 | free($1); 104 | free($2); 105 | } 106 | | STRING 107 | ; 108 | 109 | numberstring : NUMBER { 110 | char *s; 111 | if (asprintf(&s, "%lld", $1) == -1) { 112 | yyerror("string: asprintf"); 113 | YYERROR; 114 | } 115 | $$ = s; 116 | } 117 | | STRING 118 | ; 119 | 120 | yesno : YES { $$ = 1; } 121 | | NO { $$ = 0; } 122 | ; 123 | 124 | main : FONTNAME STRING { 125 | free(conf->font); 126 | conf->font = $2; 127 | } 128 | | STICKY yesno { 129 | conf->stickygroups = $2; 130 | } 131 | | BORDERWIDTH NUMBER { 132 | if ($2 < 0 || $2 > INT_MAX) { 133 | yyerror("invalid borderwidth"); 134 | YYERROR; 135 | } 136 | conf->bwidth = $2; 137 | } 138 | | HTILE NUMBER { 139 | if ($2 < 0 || $2 > 99) { 140 | yyerror("invalid htile percent"); 141 | YYERROR; 142 | } 143 | conf->htile = $2; 144 | } 145 | | VTILE NUMBER { 146 | if ($2 < 0 || $2 > 99) { 147 | yyerror("invalid vtile percent"); 148 | YYERROR; 149 | } 150 | conf->vtile = $2; 151 | } 152 | | MOVEAMOUNT NUMBER { 153 | if ($2 < 0 || $2 > INT_MAX) { 154 | yyerror("invalid movemount"); 155 | YYERROR; 156 | } 157 | conf->mamount = $2; 158 | } 159 | | SNAPDIST NUMBER { 160 | if ($2 < 0 || $2 > INT_MAX) { 161 | yyerror("invalid snapdist"); 162 | YYERROR; 163 | } 164 | conf->snapdist = $2; 165 | } 166 | | COMMAND STRING string { 167 | if (strlen($3) >= PATH_MAX) { 168 | yyerror("%s command path too long", $2); 169 | free($2); 170 | free($3); 171 | YYERROR; 172 | } 173 | conf_cmd_add(conf, $2, $3); 174 | free($2); 175 | free($3); 176 | } 177 | | WM STRING string { 178 | if (strlen($3) >= PATH_MAX) { 179 | yyerror("%s wm path too long", $2); 180 | free($2); 181 | free($3); 182 | YYERROR; 183 | } 184 | conf_wm_add(conf, $2, $3); 185 | free($2); 186 | free($3); 187 | } 188 | | AUTOGROUP NUMBER STRING { 189 | if ($2 < 0 || $2 > 9) { 190 | yyerror("invalid autogroup"); 191 | free($3); 192 | YYERROR; 193 | } 194 | conf_autogroup(conf, $2, NULL, $3); 195 | free($3); 196 | } 197 | | AUTOGROUP NUMBER STRING ',' STRING { 198 | if ($2 < 0 || $2 > 9) { 199 | yyerror("invalid autogroup"); 200 | free($3); 201 | free($5); 202 | YYERROR; 203 | } 204 | conf_autogroup(conf, $2, $3, $5); 205 | free($3); 206 | free($5); 207 | } 208 | | IGNORE STRING { 209 | conf_ignore(conf, $2); 210 | free($2); 211 | } 212 | | GAP NUMBER NUMBER NUMBER NUMBER { 213 | if ($2 < 0 || $2 > INT_MAX || 214 | $3 < 0 || $3 > INT_MAX || 215 | $4 < 0 || $4 > INT_MAX || 216 | $5 < 0 || $5 > INT_MAX) { 217 | yyerror("invalid gap"); 218 | YYERROR; 219 | } 220 | conf->gap.top = $2; 221 | conf->gap.bottom = $3; 222 | conf->gap.left = $4; 223 | conf->gap.right = $5; 224 | } 225 | | BINDKEY numberstring string { 226 | if (!conf_bind_key(conf, $2, $3)) { 227 | yyerror("invalid bind-key: %s %s", $2, $3); 228 | free($2); 229 | free($3); 230 | YYERROR; 231 | } 232 | free($2); 233 | free($3); 234 | } 235 | | UNBINDKEY numberstring { 236 | if (!conf_bind_key(conf, $2, NULL)) { 237 | yyerror("invalid unbind-key: %s", $2); 238 | free($2); 239 | YYERROR; 240 | } 241 | free($2); 242 | } 243 | | BINDMOUSE numberstring string { 244 | if (!conf_bind_mouse(conf, $2, $3)) { 245 | yyerror("invalid bind-mouse: %s %s", $2, $3); 246 | free($2); 247 | free($3); 248 | YYERROR; 249 | } 250 | free($2); 251 | free($3); 252 | } 253 | | UNBINDMOUSE numberstring { 254 | if (!conf_bind_mouse(conf, $2, NULL)) { 255 | yyerror("invalid unbind-mouse: %s", $2); 256 | free($2); 257 | YYERROR; 258 | } 259 | free($2); 260 | } 261 | ; 262 | 263 | color : COLOR colors 264 | ; 265 | 266 | colors : ACTIVEBORDER STRING { 267 | free(conf->color[CWM_COLOR_BORDER_ACTIVE]); 268 | conf->color[CWM_COLOR_BORDER_ACTIVE] = $2; 269 | } 270 | | INACTIVEBORDER STRING { 271 | free(conf->color[CWM_COLOR_BORDER_INACTIVE]); 272 | conf->color[CWM_COLOR_BORDER_INACTIVE] = $2; 273 | } 274 | | URGENCYBORDER STRING { 275 | free(conf->color[CWM_COLOR_BORDER_URGENCY]); 276 | conf->color[CWM_COLOR_BORDER_URGENCY] = $2; 277 | } 278 | | GROUPBORDER STRING { 279 | free(conf->color[CWM_COLOR_BORDER_GROUP]); 280 | conf->color[CWM_COLOR_BORDER_GROUP] = $2; 281 | } 282 | | UNGROUPBORDER STRING { 283 | free(conf->color[CWM_COLOR_BORDER_UNGROUP]); 284 | conf->color[CWM_COLOR_BORDER_UNGROUP] = $2; 285 | } 286 | | MENUBG STRING { 287 | free(conf->color[CWM_COLOR_MENU_BG]); 288 | conf->color[CWM_COLOR_MENU_BG] = $2; 289 | } 290 | | MENUFG STRING { 291 | free(conf->color[CWM_COLOR_MENU_FG]); 292 | conf->color[CWM_COLOR_MENU_FG] = $2; 293 | } 294 | | FONTCOLOR STRING { 295 | free(conf->color[CWM_COLOR_MENU_FONT]); 296 | conf->color[CWM_COLOR_MENU_FONT] = $2; 297 | } 298 | | FONTSELCOLOR STRING { 299 | free(conf->color[CWM_COLOR_MENU_FONT_SEL]); 300 | conf->color[CWM_COLOR_MENU_FONT_SEL] = $2; 301 | } 302 | ; 303 | %% 304 | 305 | struct keywords { 306 | const char *k_name; 307 | int k_val; 308 | }; 309 | 310 | int 311 | yyerror(const char *fmt, ...) 312 | { 313 | va_list ap; 314 | 315 | file->errors++; 316 | va_start(ap, fmt); 317 | fprintf(stderr, "%s:%d: ", file->name, yylval.lineno); 318 | vfprintf(stderr, fmt, ap); 319 | fprintf(stderr, "\n"); 320 | va_end(ap); 321 | return (0); 322 | } 323 | 324 | int 325 | kw_cmp(const void *k, const void *e) 326 | { 327 | return (strcmp(k, ((const struct keywords *)e)->k_name)); 328 | } 329 | 330 | int 331 | lookup(char *s) 332 | { 333 | /* this has to be sorted always */ 334 | static const struct keywords keywords[] = { 335 | { "activeborder", ACTIVEBORDER}, 336 | { "autogroup", AUTOGROUP}, 337 | { "bind-key", BINDKEY}, 338 | { "bind-mouse", BINDMOUSE}, 339 | { "borderwidth", BORDERWIDTH}, 340 | { "color", COLOR}, 341 | { "command", COMMAND}, 342 | { "font", FONTCOLOR}, 343 | { "fontname", FONTNAME}, 344 | { "gap", GAP}, 345 | { "groupborder", GROUPBORDER}, 346 | { "htile", HTILE}, 347 | { "ignore", IGNORE}, 348 | { "inactiveborder", INACTIVEBORDER}, 349 | { "menubg", MENUBG}, 350 | { "menufg", MENUFG}, 351 | { "moveamount", MOVEAMOUNT}, 352 | { "no", NO}, 353 | { "selfont", FONTSELCOLOR}, 354 | { "snapdist", SNAPDIST}, 355 | { "sticky", STICKY}, 356 | { "unbind-key", UNBINDKEY}, 357 | { "unbind-mouse", UNBINDMOUSE}, 358 | { "ungroupborder", UNGROUPBORDER}, 359 | { "urgencyborder", URGENCYBORDER}, 360 | { "vtile", VTILE}, 361 | { "wm", WM}, 362 | { "yes", YES} 363 | }; 364 | const struct keywords *p; 365 | 366 | p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 367 | sizeof(keywords[0]), kw_cmp); 368 | 369 | if (p) 370 | return (p->k_val); 371 | else 372 | return (STRING); 373 | } 374 | 375 | #define MAXPUSHBACK 128 376 | 377 | char *parsebuf; 378 | int parseindex; 379 | char pushback_buffer[MAXPUSHBACK]; 380 | int pushback_index = 0; 381 | 382 | int 383 | lgetc(int quotec) 384 | { 385 | int c, next; 386 | 387 | if (parsebuf) { 388 | /* Read character from the parsebuffer instead of input. */ 389 | if (parseindex >= 0) { 390 | c = (unsigned char)parsebuf[parseindex++]; 391 | if (c != '\0') 392 | return (c); 393 | parsebuf = NULL; 394 | } else 395 | parseindex++; 396 | } 397 | 398 | if (pushback_index) 399 | return ((unsigned char)pushback_buffer[--pushback_index]); 400 | 401 | if (quotec) { 402 | if ((c = getc(file->stream)) == EOF) { 403 | yyerror("reached end of file while parsing " 404 | "quoted string"); 405 | if (file == topfile || popfile() == EOF) 406 | return (EOF); 407 | return (quotec); 408 | } 409 | return (c); 410 | } 411 | 412 | while ((c = getc(file->stream)) == '\\') { 413 | next = getc(file->stream); 414 | if (next != '\n') { 415 | c = next; 416 | break; 417 | } 418 | yylval.lineno = file->lineno; 419 | file->lineno++; 420 | } 421 | 422 | while (c == EOF) { 423 | if (file == topfile || popfile() == EOF) 424 | return (EOF); 425 | c = getc(file->stream); 426 | } 427 | return (c); 428 | } 429 | 430 | int 431 | lungetc(int c) 432 | { 433 | if (c == EOF) 434 | return (EOF); 435 | if (parsebuf) { 436 | parseindex--; 437 | if (parseindex >= 0) 438 | return (c); 439 | } 440 | if (pushback_index + 1 >= MAXPUSHBACK) 441 | return (EOF); 442 | pushback_buffer[pushback_index++] = c; 443 | return (c); 444 | } 445 | 446 | int 447 | findeol(void) 448 | { 449 | int c; 450 | 451 | parsebuf = NULL; 452 | 453 | /* skip to either EOF or the first real EOL */ 454 | while (1) { 455 | if (pushback_index) 456 | c = (unsigned char)pushback_buffer[--pushback_index]; 457 | else 458 | c = lgetc(0); 459 | if (c == '\n') { 460 | file->lineno++; 461 | break; 462 | } 463 | if (c == EOF) 464 | break; 465 | } 466 | return (ERROR); 467 | } 468 | 469 | int 470 | yylex(void) 471 | { 472 | char buf[8096]; 473 | char *p; 474 | int quotec, next, c; 475 | int token; 476 | 477 | p = buf; 478 | while ((c = lgetc(0)) == ' ' || c == '\t') 479 | ; /* nothing */ 480 | 481 | yylval.lineno = file->lineno; 482 | if (c == '#') 483 | while ((c = lgetc(0)) != '\n' && c != EOF) 484 | ; /* nothing */ 485 | 486 | switch (c) { 487 | case '\'': 488 | case '"': 489 | quotec = c; 490 | while (1) { 491 | if ((c = lgetc(quotec)) == EOF) 492 | return (0); 493 | if (c == '\n') { 494 | file->lineno++; 495 | continue; 496 | } else if (c == '\\') { 497 | if ((next = lgetc(quotec)) == EOF) 498 | return (0); 499 | if (next == quotec || next == ' ' || 500 | next == '\t') 501 | c = next; 502 | else if (next == '\n') { 503 | file->lineno++; 504 | continue; 505 | } else 506 | lungetc(next); 507 | } else if (c == quotec) { 508 | *p = '\0'; 509 | break; 510 | } else if (c == '\0') { 511 | yyerror("syntax error"); 512 | return (findeol()); 513 | } 514 | if (p + 1 >= buf + sizeof(buf) - 1) { 515 | yyerror("string too long"); 516 | return (findeol()); 517 | } 518 | *p++ = (char)c; 519 | } 520 | yylval.v.string = xstrdup(buf); 521 | return (STRING); 522 | } 523 | 524 | #define allowed_to_end_number(x) \ 525 | (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 526 | 527 | if (c == '-' || isdigit(c)) { 528 | do { 529 | *p++ = c; 530 | if ((size_t)(p-buf) >= sizeof(buf)) { 531 | yyerror("string too long"); 532 | return (findeol()); 533 | } 534 | } while ((c = lgetc(0)) != EOF && isdigit(c)); 535 | lungetc(c); 536 | if (p == buf + 1 && buf[0] == '-') 537 | goto nodigits; 538 | if (c == EOF || allowed_to_end_number(c)) { 539 | const char *errstr = NULL; 540 | 541 | *p = '\0'; 542 | yylval.v.number = strtonum(buf, LLONG_MIN, 543 | LLONG_MAX, &errstr); 544 | if (errstr) { 545 | yyerror("\"%s\" invalid number: %s", 546 | buf, errstr); 547 | return (findeol()); 548 | } 549 | return (NUMBER); 550 | } else { 551 | nodigits: 552 | while (p > buf + 1) 553 | lungetc((unsigned char)*--p); 554 | c = (unsigned char)*--p; 555 | if (c == '-') 556 | return (c); 557 | } 558 | } 559 | 560 | /* Similar to other parse.y copies, but also allows '/' in strings */ 561 | #define allowed_in_string(x) \ 562 | (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 563 | x != '{' && x != '}' && x != '<' && x != '>' && \ 564 | x != '!' && x != '=' && x != '#' && x != ',')) 565 | 566 | if (isalnum(c) || c == ':' || c == '_' || c == '*' || c == '/') { 567 | do { 568 | *p++ = c; 569 | if ((size_t)(p-buf) >= sizeof(buf)) { 570 | yyerror("string too long"); 571 | return (findeol()); 572 | } 573 | } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 574 | lungetc(c); 575 | *p = '\0'; 576 | if ((token = lookup(buf)) == STRING) 577 | yylval.v.string = xstrdup(buf); 578 | return (token); 579 | } 580 | if (c == '\n') { 581 | yylval.lineno = file->lineno; 582 | file->lineno++; 583 | } 584 | if (c == EOF) 585 | return (0); 586 | return (c); 587 | } 588 | 589 | struct file * 590 | pushfile(const char *name, FILE *stream) 591 | { 592 | struct file *nfile; 593 | 594 | nfile = xcalloc(1, sizeof(struct file)); 595 | nfile->name = xstrdup(name); 596 | nfile->stream = stream; 597 | nfile->lineno = 1; 598 | TAILQ_INSERT_TAIL(&files, nfile, entry); 599 | return (nfile); 600 | } 601 | 602 | int 603 | popfile(void) 604 | { 605 | struct file *prev; 606 | 607 | if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 608 | prev->errors += file->errors; 609 | 610 | TAILQ_REMOVE(&files, file, entry); 611 | fclose(file->stream); 612 | free(file->name); 613 | free(file); 614 | file = prev; 615 | return (file ? 0 : EOF); 616 | } 617 | 618 | int 619 | parse_config(const char *filename, struct conf *xconf) 620 | { 621 | FILE *stream; 622 | int errors = 0; 623 | 624 | conf = xconf; 625 | 626 | stream = fopen(filename, "r"); 627 | if (stream == NULL) { 628 | if (errno == ENOENT) 629 | return (0); 630 | warn("%s", filename); 631 | return (-1); 632 | } 633 | file = pushfile(filename, stream); 634 | topfile = file; 635 | 636 | yyparse(); 637 | errors = file->errors; 638 | popfile(); 639 | 640 | return (errors ? -1 : 0); 641 | } 642 | -------------------------------------------------------------------------------- /reallocarray.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: reallocarray.c,v 1.2 2014/12/08 03:45:00 bcook Exp $ */ 2 | /* 3 | * Copyright (c) 2008 Otto Moerbeek 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | /* 24 | * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX 25 | * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW 26 | */ 27 | #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) 28 | 29 | void * 30 | reallocarray(void *optr, size_t nmemb, size_t size) 31 | { 32 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && 33 | nmemb > 0 && SIZE_MAX / nmemb < size) { 34 | errno = ENOMEM; 35 | return NULL; 36 | } 37 | return realloc(optr, size * nmemb); 38 | } 39 | -------------------------------------------------------------------------------- /screen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * calmwm - the calm window manager 3 | * 4 | * Copyright (c) 2004 Marius Aamodt Eriksen 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * $OpenBSD$ 19 | */ 20 | 21 | #include 22 | #include "queue.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "calmwm.h" 34 | 35 | static struct geom screen_apply_gap(struct screen_ctx *, struct geom); 36 | static void screen_scan(struct screen_ctx *); 37 | 38 | void 39 | screen_init(int which) 40 | { 41 | struct screen_ctx *sc; 42 | XSetWindowAttributes attr; 43 | 44 | sc = xmalloc(sizeof(*sc)); 45 | 46 | TAILQ_INIT(&sc->clientq); 47 | TAILQ_INIT(&sc->regionq); 48 | TAILQ_INIT(&sc->groupq); 49 | 50 | sc->which = which; 51 | sc->rootwin = RootWindow(X_Dpy, sc->which); 52 | sc->colormap = DefaultColormap(X_Dpy, sc->which); 53 | sc->visual = DefaultVisual(X_Dpy, sc->which); 54 | sc->cycling = 0; 55 | sc->hideall = 0; 56 | 57 | conf_screen(sc); 58 | 59 | xu_ewmh_net_supported(sc); 60 | xu_ewmh_net_supported_wm_check(sc); 61 | 62 | conf_group(sc); 63 | sc->group_last = sc->group_active; 64 | screen_update_geometry(sc); 65 | 66 | xu_ewmh_net_desktop_names(sc); 67 | xu_ewmh_net_number_of_desktops(sc); 68 | xu_ewmh_net_showing_desktop(sc); 69 | xu_ewmh_net_virtual_roots(sc); 70 | 71 | attr.cursor = Conf.cursor[CF_NORMAL]; 72 | attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask | 73 | EnterWindowMask | PropertyChangeMask | ButtonPressMask; 74 | XChangeWindowAttributes(X_Dpy, sc->rootwin, (CWEventMask | CWCursor), &attr); 75 | 76 | if (Conf.xrandr) 77 | XRRSelectInput(X_Dpy, sc->rootwin, RRScreenChangeNotifyMask); 78 | 79 | screen_scan(sc); 80 | screen_updatestackingorder(sc); 81 | 82 | TAILQ_INSERT_TAIL(&Screenq, sc, entry); 83 | 84 | XSync(X_Dpy, False); 85 | } 86 | 87 | static void 88 | screen_scan(struct screen_ctx *sc) 89 | { 90 | struct client_ctx *cc, *active = NULL; 91 | Window *wins, w0, w1, rwin, cwin; 92 | unsigned int nwins, i, mask; 93 | int rx, ry, wx, wy; 94 | 95 | XQueryPointer(X_Dpy, sc->rootwin, &rwin, &cwin, 96 | &rx, &ry, &wx, &wy, &mask); 97 | 98 | if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) { 99 | for (i = 0; i < nwins; i++) { 100 | if ((cc = client_init(wins[i], sc)) != NULL) 101 | if (cc->win == cwin) 102 | active = cc; 103 | } 104 | XFree(wins); 105 | } 106 | if (active) 107 | client_set_active(active); 108 | } 109 | 110 | struct screen_ctx * 111 | screen_find(Window win) 112 | { 113 | struct screen_ctx *sc; 114 | 115 | TAILQ_FOREACH(sc, &Screenq, entry) { 116 | if (sc->rootwin == win) 117 | return sc; 118 | } 119 | warnx("%s: failure win 0x%lx", __func__, win); 120 | return NULL; 121 | } 122 | 123 | void 124 | screen_updatestackingorder(struct screen_ctx *sc) 125 | { 126 | Window *wins, w0, w1; 127 | struct client_ctx *cc; 128 | unsigned int nwins, i, s; 129 | 130 | if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) { 131 | for (s = 0, i = 0; i < nwins; i++) { 132 | /* Skip hidden windows */ 133 | if ((cc = client_find(wins[i])) == NULL || 134 | cc->flags & CLIENT_HIDDEN) 135 | continue; 136 | 137 | cc->stackingorder = s++; 138 | } 139 | XFree(wins); 140 | } 141 | } 142 | 143 | struct region_ctx * 144 | region_find(struct screen_ctx *sc, int x, int y) 145 | { 146 | struct region_ctx *rc; 147 | 148 | TAILQ_FOREACH(rc, &sc->regionq, entry) { 149 | if ((x >= rc->view.x) && (x < (rc->view.x + rc->view.w)) && 150 | (y >= rc->view.y) && (y < (rc->view.y + rc->view.h))) { 151 | break; 152 | } 153 | } 154 | return rc; 155 | } 156 | 157 | struct geom 158 | screen_area(struct screen_ctx *sc, int x, int y, int apply_gap) 159 | { 160 | struct region_ctx *rc; 161 | struct geom area = sc->view; 162 | 163 | TAILQ_FOREACH(rc, &sc->regionq, entry) { 164 | if ((x >= rc->view.x) && (x < (rc->view.x + rc->view.w)) && 165 | (y >= rc->view.y) && (y < (rc->view.y + rc->view.h))) { 166 | area = rc->view; 167 | break; 168 | } 169 | } 170 | if (apply_gap) 171 | area = screen_apply_gap(sc, area); 172 | return area; 173 | } 174 | 175 | void 176 | screen_update_geometry(struct screen_ctx *sc) 177 | { 178 | struct region_ctx *rc; 179 | 180 | sc->view.x = 0; 181 | sc->view.y = 0; 182 | sc->view.w = DisplayWidth(X_Dpy, sc->which); 183 | sc->view.h = DisplayHeight(X_Dpy, sc->which); 184 | sc->work = screen_apply_gap(sc, sc->view); 185 | 186 | while ((rc = TAILQ_FIRST(&sc->regionq)) != NULL) { 187 | TAILQ_REMOVE(&sc->regionq, rc, entry); 188 | free(rc); 189 | } 190 | 191 | if (Conf.xrandr) { 192 | XRRScreenResources *sr; 193 | XRRCrtcInfo *ci; 194 | int i; 195 | 196 | sr = XRRGetScreenResources(X_Dpy, sc->rootwin); 197 | for (i = 0, ci = NULL; i < sr->ncrtc; i++) { 198 | ci = XRRGetCrtcInfo(X_Dpy, sr, sr->crtcs[i]); 199 | if (ci == NULL) 200 | continue; 201 | if (ci->noutput == 0) { 202 | XRRFreeCrtcInfo(ci); 203 | continue; 204 | } 205 | 206 | rc = xmalloc(sizeof(*rc)); 207 | rc->num = i; 208 | rc->view.x = ci->x; 209 | rc->view.y = ci->y; 210 | rc->view.w = ci->width; 211 | rc->view.h = ci->height; 212 | rc->work = screen_apply_gap(sc, rc->view); 213 | TAILQ_INSERT_TAIL(&sc->regionq, rc, entry); 214 | 215 | XRRFreeCrtcInfo(ci); 216 | } 217 | XRRFreeScreenResources(sr); 218 | } else { 219 | rc = xmalloc(sizeof(*rc)); 220 | rc->num = 0; 221 | rc->view.x = 0; 222 | rc->view.y = 0; 223 | rc->view.w = DisplayWidth(X_Dpy, sc->which); 224 | rc->view.h = DisplayHeight(X_Dpy, sc->which); 225 | rc->work = screen_apply_gap(sc, rc->view); 226 | TAILQ_INSERT_TAIL(&sc->regionq, rc, entry); 227 | } 228 | 229 | xu_ewmh_net_desktop_geometry(sc); 230 | xu_ewmh_net_desktop_viewport(sc); 231 | xu_ewmh_net_workarea(sc); 232 | } 233 | 234 | static struct geom 235 | screen_apply_gap(struct screen_ctx *sc, struct geom geom) 236 | { 237 | geom.x += sc->gap.left; 238 | geom.y += sc->gap.top; 239 | geom.w -= (sc->gap.left + sc->gap.right); 240 | geom.h -= (sc->gap.top + sc->gap.bottom); 241 | 242 | return geom; 243 | } 244 | 245 | /* Bring back clients which are beyond the screen. */ 246 | void 247 | screen_assert_clients_within(struct screen_ctx *sc) 248 | { 249 | struct client_ctx *cc; 250 | int top, left, right, bottom; 251 | 252 | TAILQ_FOREACH(cc, &sc->clientq, entry) { 253 | if (cc->sc != sc) 254 | continue; 255 | top = cc->geom.y; 256 | left = cc->geom.x; 257 | right = cc->geom.x + cc->geom.w + (cc->bwidth * 2) - 1; 258 | bottom = cc->geom.y + cc->geom.h + (cc->bwidth * 2) - 1; 259 | if ((top > sc->view.h || left > sc->view.w) || 260 | (bottom < 0 || right < 0)) { 261 | cc->geom.x = sc->gap.left; 262 | cc->geom.y = sc->gap.top; 263 | client_move(cc); 264 | } 265 | } 266 | } 267 | 268 | void 269 | screen_prop_win_create(struct screen_ctx *sc, Window win) 270 | { 271 | sc->prop.win = XCreateSimpleWindow(X_Dpy, win, 0, 0, 1, 1, 0, 272 | sc->xftcolor[CWM_COLOR_MENU_BG].pixel, 273 | sc->xftcolor[CWM_COLOR_MENU_BG].pixel); 274 | sc->prop.xftdraw = XftDrawCreate(X_Dpy, sc->prop.win, 275 | sc->visual, sc->colormap); 276 | 277 | XMapWindow(X_Dpy, sc->prop.win); 278 | } 279 | 280 | void 281 | screen_prop_win_destroy(struct screen_ctx *sc) 282 | { 283 | XftDrawDestroy(sc->prop.xftdraw); 284 | XDestroyWindow(X_Dpy, sc->prop.win); 285 | } 286 | 287 | void 288 | screen_prop_win_draw(struct screen_ctx *sc, const char *fmt, ...) 289 | { 290 | va_list ap; 291 | char *text; 292 | XGlyphInfo extents; 293 | 294 | va_start(ap, fmt); 295 | xvasprintf(&text, fmt, ap); 296 | va_end(ap); 297 | 298 | XftTextExtentsUtf8(X_Dpy, sc->xftfont, (const FcChar8*)text, 299 | strlen(text), &extents); 300 | XResizeWindow(X_Dpy, sc->prop.win, extents.xOff, sc->xftfont->height); 301 | XClearWindow(X_Dpy, sc->prop.win); 302 | XftDrawStringUtf8(sc->prop.xftdraw, &sc->xftcolor[CWM_COLOR_MENU_FONT], 303 | sc->xftfont, 0, sc->xftfont->ascent + 1, 304 | (const FcChar8*)text, strlen(text)); 305 | 306 | free(text); 307 | } 308 | -------------------------------------------------------------------------------- /search.c: -------------------------------------------------------------------------------- 1 | /* 2 | * calmwm - the calm window manager 3 | * 4 | * Copyright (c) 2004 Marius Aamodt Eriksen 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * $OpenBSD$ 19 | */ 20 | 21 | #include 22 | #include "queue.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "calmwm.h" 35 | 36 | #define PATH_ANY 0x0001 37 | #define PATH_EXEC 0x0002 38 | 39 | static void match_path_type(struct menu_q *, char *, int); 40 | static int match_substr(char *, char *, int); 41 | 42 | static int 43 | match_substr(char *sub, char *str, int zeroidx) 44 | { 45 | size_t len, sublen; 46 | unsigned int n, flen; 47 | 48 | if (sub == NULL || str == NULL) 49 | return 0; 50 | 51 | len = strlen(str); 52 | sublen = strlen(sub); 53 | 54 | if (sublen > len) 55 | return 0; 56 | 57 | if (zeroidx) 58 | flen = 0; 59 | else 60 | flen = len - sublen; 61 | 62 | for (n = 0; n <= flen; n++) 63 | if (strncasecmp(sub, str + n, sublen) == 0) 64 | return 1; 65 | 66 | return 0; 67 | } 68 | 69 | void 70 | search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search) 71 | { 72 | struct menu *mi, *tierp[3], *before = NULL; 73 | struct client_ctx *cc; 74 | struct winname *wn; 75 | 76 | (void)memset(tierp, 0, sizeof(tierp)); 77 | 78 | TAILQ_INIT(resultq); 79 | TAILQ_FOREACH(mi, menuq, entry) { 80 | int tier = -1, t; 81 | cc = (struct client_ctx *)mi->ctx; 82 | 83 | /* Match on label. */ 84 | if (match_substr(search, cc->label, 0)) 85 | tier = 0; 86 | 87 | /* Match on window name history, from present to past. */ 88 | if (tier < 0) { 89 | TAILQ_FOREACH_REVERSE(wn, &cc->nameq, name_q, entry) 90 | if (match_substr(search, wn->name, 0)) { 91 | tier = 1; 92 | break; 93 | } 94 | } 95 | 96 | /* Match on window resource class. */ 97 | if ((tier < 0) && match_substr(search, cc->res_class, 0)) 98 | tier = 2; 99 | 100 | if (tier < 0) 101 | continue; 102 | 103 | /* Current window is ranked down. */ 104 | if ((tier < nitems(tierp) - 1) && (cc->flags & CLIENT_ACTIVE)) 105 | tier++; 106 | 107 | /* Hidden window is ranked up. */ 108 | if ((tier > 0) && (cc->flags & CLIENT_HIDDEN)) 109 | tier--; 110 | 111 | /* 112 | * If you have a tierp, insert after it, and make it 113 | * the new tierp. If you don't have a tierp, find the 114 | * first nonzero tierp above you, insert after it. 115 | * Always make your current tierp the newly inserted 116 | * entry. 117 | */ 118 | for (t = tier; t >= 0 && ((before = tierp[t]) == NULL); t--) 119 | ; 120 | 121 | if (before == NULL) 122 | TAILQ_INSERT_HEAD(resultq, mi, resultentry); 123 | else 124 | TAILQ_INSERT_AFTER(resultq, before, mi, resultentry); 125 | 126 | tierp[tier] = mi; 127 | } 128 | } 129 | 130 | void 131 | search_match_cmd(struct menu_q *menuq, struct menu_q *resultq, char *search) 132 | { 133 | struct menu *mi; 134 | struct cmd_ctx *cmd; 135 | 136 | TAILQ_INIT(resultq); 137 | TAILQ_FOREACH(mi, menuq, entry) { 138 | cmd = (struct cmd_ctx *)mi->ctx; 139 | if (match_substr(search, cmd->name, 0)) 140 | TAILQ_INSERT_TAIL(resultq, mi, resultentry); 141 | } 142 | } 143 | 144 | void 145 | search_match_group(struct menu_q *menuq, struct menu_q *resultq, char *search) 146 | { 147 | struct menu *mi; 148 | struct group_ctx *gc; 149 | char *s; 150 | 151 | TAILQ_INIT(resultq); 152 | TAILQ_FOREACH(mi, menuq, entry) { 153 | gc = (struct group_ctx *)mi->ctx; 154 | xasprintf(&s, "%d %s", gc->num, gc->name); 155 | if (match_substr(search, s, 0)) 156 | TAILQ_INSERT_TAIL(resultq, mi, resultentry); 157 | free(s); 158 | } 159 | } 160 | 161 | static void 162 | match_path_type(struct menu_q *resultq, char *search, int flag) 163 | { 164 | struct menu *mi; 165 | char *pattern; 166 | glob_t g; 167 | int i; 168 | 169 | xasprintf(&pattern, "%s*", search); 170 | if (glob(pattern, GLOB_MARK, NULL, &g) != 0) 171 | return; 172 | for (i = 0; i < g.gl_pathc; i++) { 173 | if ((flag & PATH_EXEC) && access(g.gl_pathv[i], X_OK)) 174 | continue; 175 | mi = xcalloc(1, sizeof(*mi)); 176 | (void)strlcpy(mi->text, g.gl_pathv[i], sizeof(mi->text)); 177 | TAILQ_INSERT_TAIL(resultq, mi, resultentry); 178 | } 179 | globfree(&g); 180 | free(pattern); 181 | } 182 | 183 | void 184 | search_match_exec(struct menu_q *menuq, struct menu_q *resultq, char *search) 185 | { 186 | struct menu *mi, *mj; 187 | int r; 188 | 189 | TAILQ_INIT(resultq); 190 | TAILQ_FOREACH(mi, menuq, entry) { 191 | if (match_substr(search, mi->text, 1) == 0 && 192 | fnmatch(search, mi->text, 0) == FNM_NOMATCH) 193 | continue; 194 | TAILQ_FOREACH(mj, resultq, resultentry) { 195 | r = strcmp(mi->text, mj->text); 196 | if (r < 0) 197 | TAILQ_INSERT_BEFORE(mj, mi, resultentry); 198 | if (r <= 0) 199 | break; 200 | } 201 | if (mj == NULL) 202 | TAILQ_INSERT_TAIL(resultq, mi, resultentry); 203 | } 204 | if (TAILQ_EMPTY(resultq)) 205 | match_path_type(resultq, search, PATH_EXEC); 206 | } 207 | 208 | void 209 | search_match_path(struct menu_q *menuq, struct menu_q *resultq, char *search) 210 | { 211 | TAILQ_INIT(resultq); 212 | match_path_type(resultq, search, PATH_ANY); 213 | } 214 | 215 | void 216 | search_match_text(struct menu_q *menuq, struct menu_q *resultq, char *search) 217 | { 218 | struct menu *mi; 219 | 220 | TAILQ_INIT(resultq); 221 | TAILQ_FOREACH(mi, menuq, entry) { 222 | if (match_substr(search, mi->text, 0)) 223 | TAILQ_INSERT_TAIL(resultq, mi, resultentry); 224 | } 225 | } 226 | 227 | void 228 | search_match_wm(struct menu_q *menuq, struct menu_q *resultq, char *search) 229 | { 230 | struct menu *mi; 231 | struct cmd_ctx *wm; 232 | 233 | TAILQ_INIT(resultq); 234 | TAILQ_FOREACH(mi, menuq, entry) { 235 | wm = (struct cmd_ctx *)mi->ctx; 236 | if ((match_substr(search, wm->name, 0)) || 237 | (match_substr(search, wm->path, 0))) 238 | TAILQ_INSERT_TAIL(resultq, mi, resultentry); 239 | } 240 | } 241 | 242 | void 243 | search_print_client(struct menu *mi, int listing) 244 | { 245 | struct client_ctx *cc = (struct client_ctx *)mi->ctx; 246 | char flag = ' '; 247 | 248 | if (cc->flags & CLIENT_ACTIVE) 249 | flag = '!'; 250 | else if (cc->flags & CLIENT_HIDDEN) 251 | flag = '&'; 252 | 253 | (void)snprintf(mi->print, sizeof(mi->print), "(%d) %c[%s] %s", 254 | (cc->gc) ? cc->gc->num : 0, flag, 255 | (cc->label) ? cc->label : "", cc->name); 256 | } 257 | 258 | void 259 | search_print_cmd(struct menu *mi, int listing) 260 | { 261 | struct cmd_ctx *cmd = (struct cmd_ctx *)mi->ctx; 262 | 263 | (void)snprintf(mi->print, sizeof(mi->print), "%s", cmd->name); 264 | } 265 | 266 | void 267 | search_print_group(struct menu *mi, int listing) 268 | { 269 | struct group_ctx *gc = (struct group_ctx *)mi->ctx; 270 | 271 | (void)snprintf(mi->print, sizeof(mi->print), 272 | (group_holds_only_hidden(gc)) ? "%d: [%s]" : "%d: %s", 273 | gc->num, gc->name); 274 | } 275 | 276 | void 277 | search_print_text(struct menu *mi, int listing) 278 | { 279 | (void)snprintf(mi->print, sizeof(mi->print), "%s", mi->text); 280 | } 281 | 282 | void 283 | search_print_wm(struct menu *mi, int listing) 284 | { 285 | struct cmd_ctx *wm = (struct cmd_ctx *)mi->ctx; 286 | 287 | (void)snprintf(mi->print, sizeof(mi->print), "%s [%s]", 288 | wm->name, wm->path); 289 | } 290 | -------------------------------------------------------------------------------- /strlcat.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strlcat.c,v 1.14 2015/01/15 03:54:12 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1998, 2015 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */ 20 | 21 | #include 22 | #include 23 | #include "calmwm.h" 24 | 25 | #ifndef HAVE_STRLCAT 26 | 27 | /* 28 | * Appends src to string dst of size dsize (unlike strncat, dsize is the 29 | * full size of dst, not space left). At most dsize-1 characters 30 | * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). 31 | * Returns strlen(src) + MIN(dsize, strlen(initial dst)). 32 | * If retval >= siz, truncation occurred. 33 | */ 34 | size_t 35 | strlcat(char *dst, const char *src, size_t dsize) 36 | { 37 | const char *odst = dst; 38 | const char *osrc = src; 39 | size_t n = dsize; 40 | size_t dlen; 41 | 42 | /* Find the end of dst and adjust bytes left but don't go past end. */ 43 | while (n-- != 0 && *dst != '\0') 44 | dst++; 45 | dlen = dst - odst; 46 | n = dsize - dlen; 47 | 48 | if (n-- == 0) 49 | return(dlen + strlen(src)); 50 | while (*src != '\0') { 51 | if (n != 0) { 52 | *dst++ = *src; 53 | n--; 54 | } 55 | src++; 56 | } 57 | *dst = '\0'; 58 | 59 | return(dlen + (src - osrc)); /* count does not include NUL */ 60 | } 61 | 62 | #endif /* !HAVE_STRLCAT */ 63 | -------------------------------------------------------------------------------- /strlcpy.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1998, 2015 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */ 20 | 21 | #include 22 | #include 23 | #include "calmwm.h" 24 | 25 | #ifndef HAVE_STRLCPY 26 | 27 | /* 28 | * Copy string src to buffer dst of size dsize. At most dsize-1 29 | * chars will be copied. Always NUL terminates (unless dsize == 0). 30 | * Returns strlen(src); if retval >= dsize, truncation occurred. 31 | */ 32 | size_t 33 | strlcpy(char *dst, const char *src, size_t dsize) 34 | { 35 | const char *osrc = src; 36 | size_t nleft = dsize; 37 | 38 | /* Copy as many bytes as will fit. */ 39 | if (nleft != 0) { 40 | while (--nleft != 0) { 41 | if ((*dst++ = *src++) == '\0') 42 | break; 43 | } 44 | } 45 | 46 | /* Not enough room in dst, add NUL and traverse rest of src. */ 47 | if (nleft == 0) { 48 | if (dsize != 0) 49 | *dst = '\0'; /* NUL-terminate dst */ 50 | while (*src++) 51 | ; 52 | } 53 | 54 | return(src - osrc - 1); /* count does not include NUL */ 55 | } 56 | 57 | #endif /* !HAVE_STRLCPY */ 58 | -------------------------------------------------------------------------------- /strtonum.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strtonum.c,v 1.7 2013/04/17 18:40:58 tedu Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2004 Ted Unangst and Todd Miller 5 | * All rights reserved. 6 | * 7 | * Permission to use, copy, modify, and distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | */ 19 | 20 | /* OPENBSD ORIGINAL: lib/libc/stdlib/strtonum.c */ 21 | 22 | #ifndef HAVE_STRTONUM 23 | #include 24 | #include 25 | #include 26 | 27 | #define INVALID 1 28 | #define TOOSMALL 2 29 | #define TOOLARGE 3 30 | 31 | long long 32 | strtonum(const char *numstr, long long minval, long long maxval, 33 | const char **errstrp) 34 | { 35 | long long ll = 0; 36 | int error = 0; 37 | char *ep; 38 | struct errval { 39 | const char *errstr; 40 | int err; 41 | } ev[4] = { 42 | { NULL, 0 }, 43 | { "invalid", EINVAL }, 44 | { "too small", ERANGE }, 45 | { "too large", ERANGE }, 46 | }; 47 | 48 | ev[0].err = errno; 49 | errno = 0; 50 | if (minval > maxval) { 51 | error = INVALID; 52 | } else { 53 | ll = strtoll(numstr, &ep, 10); 54 | if (numstr == ep || *ep != '\0') 55 | error = INVALID; 56 | else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) 57 | error = TOOSMALL; 58 | else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) 59 | error = TOOLARGE; 60 | } 61 | if (errstrp != NULL) 62 | *errstrp = ev[error].errstr; 63 | errno = ev[error].err; 64 | if (error) 65 | ll = 0; 66 | 67 | return (ll); 68 | } 69 | 70 | #endif /* HAVE_STRTONUM */ 71 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #GIT_MERGE_AUTOEDIT=no git cvsimport -o master -v -k -m -d anoncvs@openbsd.cs.fau.de:/cvs xenocara/app/cwm 2 | GIT_MERGE_AUTOEDIT=no git cvsimport -o master -v -k -m -d anoncvs@mirror.osn.de:/cvs xenocara/app/cwm 3 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * calmwm - the calm window manager 3 | * 4 | * Copyright (c) 2004 Marius Aamodt Eriksen 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * $OpenBSD$ 19 | */ 20 | 21 | #include 22 | #include "queue.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "calmwm.h" 34 | 35 | static void log_msg(const char *, va_list); 36 | 37 | void 38 | u_spawn(char *argstr) 39 | { 40 | switch (fork()) { 41 | case 0: 42 | u_exec(argstr); 43 | exit(1); 44 | case -1: 45 | warn("fork"); 46 | default: 47 | break; 48 | } 49 | } 50 | 51 | void 52 | u_exec(char *argstr) 53 | { 54 | #define MAXARGLEN 20 55 | char *args[MAXARGLEN], **ap = args; 56 | char **end = &args[MAXARGLEN - 2], *tmp; 57 | char *s = argstr; 58 | 59 | while (ap < end && (*ap = strsep(&argstr, " \t")) != NULL) { 60 | if (**ap == '\0') 61 | continue; 62 | ap++; 63 | if (argstr != NULL) { 64 | /* deal with quoted strings */ 65 | switch(argstr[0]) { 66 | case '"': 67 | case '\'': 68 | if ((tmp = strchr(argstr + 1, argstr[0])) 69 | != NULL) { 70 | *(tmp++) = '\0'; 71 | *(ap++) = ++argstr; 72 | argstr = tmp; 73 | } 74 | break; 75 | default: 76 | break; 77 | } 78 | } 79 | } 80 | *ap = NULL; 81 | 82 | (void)setsid(); 83 | (void)execvp(args[0], args); 84 | warn("%s", s); 85 | } 86 | 87 | char * 88 | u_argv(char * const *argv) 89 | { 90 | size_t siz = 0; 91 | int i; 92 | char *p; 93 | 94 | if (argv == 0) 95 | return NULL; 96 | 97 | for (i = 0; argv[i]; i++) 98 | siz += strlen(argv[i]) + 1; 99 | if (siz == 0) 100 | return NULL; 101 | 102 | p = xmalloc(siz); 103 | strlcpy(p, argv[0], siz); 104 | for (i = 1; argv[i]; i++) { 105 | strlcat(p, " ", siz); 106 | strlcat(p, argv[i], siz); 107 | } 108 | return p; 109 | } 110 | 111 | static void 112 | log_msg(const char *msg, va_list ap) 113 | { 114 | char *fmt; 115 | 116 | if (asprintf(&fmt, "%s\n", msg) == -1) { 117 | vfprintf(stderr, msg, ap); 118 | fprintf(stderr, "\n"); 119 | } else { 120 | vfprintf(stderr, fmt, ap); 121 | free(fmt); 122 | } 123 | fflush(stderr); 124 | } 125 | 126 | void 127 | log_debug(int level, const char *func, const char *msg, ...) 128 | { 129 | char *fmt; 130 | va_list ap; 131 | 132 | if (Conf.debug < level) 133 | return; 134 | 135 | va_start(ap, msg); 136 | xasprintf(&fmt, "debug%d: %s: %s", level, func, msg); 137 | log_msg(fmt, ap); 138 | free(fmt); 139 | va_end(ap); 140 | } 141 | -------------------------------------------------------------------------------- /xevents.c: -------------------------------------------------------------------------------- 1 | /* 2 | * calmwm - the calm window manager 3 | * 4 | * Copyright (c) 2004 Marius Aamodt Eriksen 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * $OpenBSD$ 19 | */ 20 | 21 | /* 22 | * NOTE: 23 | * It is the responsibility of the caller to deal with memory 24 | * management of the xevent's. 25 | */ 26 | 27 | #include 28 | #include "queue.h" 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "calmwm.h" 39 | 40 | static void xev_handle_maprequest(XEvent *); 41 | static void xev_handle_unmapnotify(XEvent *); 42 | static void xev_handle_destroynotify(XEvent *); 43 | static void xev_handle_configurerequest(XEvent *); 44 | static void xev_handle_propertynotify(XEvent *); 45 | static void xev_handle_enternotify(XEvent *); 46 | static void xev_handle_buttonpress(XEvent *); 47 | static void xev_handle_buttonrelease(XEvent *); 48 | static void xev_handle_keypress(XEvent *); 49 | static void xev_handle_keyrelease(XEvent *); 50 | static void xev_handle_clientmessage(XEvent *); 51 | static void xev_handle_randr(XEvent *); 52 | static void xev_handle_mappingnotify(XEvent *); 53 | static void xev_handle_expose(XEvent *); 54 | 55 | void (*xev_handlers[LASTEvent])(XEvent *) = { 56 | [MapRequest] = xev_handle_maprequest, 57 | [UnmapNotify] = xev_handle_unmapnotify, 58 | [DestroyNotify] = xev_handle_destroynotify, 59 | [ConfigureRequest] = xev_handle_configurerequest, 60 | [PropertyNotify] = xev_handle_propertynotify, 61 | [EnterNotify] = xev_handle_enternotify, 62 | [ButtonPress] = xev_handle_buttonpress, 63 | [ButtonRelease] = xev_handle_buttonrelease, 64 | [KeyPress] = xev_handle_keypress, 65 | [KeyRelease] = xev_handle_keyrelease, 66 | [ClientMessage] = xev_handle_clientmessage, 67 | [MappingNotify] = xev_handle_mappingnotify, 68 | [Expose] = xev_handle_expose, 69 | }; 70 | 71 | static KeySym modkeys[] = { XK_Alt_L, XK_Alt_R, XK_Super_L, XK_Super_R, 72 | XK_Control_L, XK_Control_R, XK_ISO_Level3_Shift }; 73 | 74 | static void 75 | xev_handle_maprequest(XEvent *ee) 76 | { 77 | XMapRequestEvent *e = &ee->xmaprequest; 78 | struct screen_ctx *sc; 79 | struct client_ctx *cc, *old_cc; 80 | 81 | LOG_DEBUG3("parent: 0x%lx window: 0x%lx", e->parent, e->window); 82 | 83 | if ((sc = screen_find(e->parent)) == NULL) 84 | return; 85 | 86 | if ((old_cc = client_current(sc)) != NULL) 87 | client_ptr_save(old_cc); 88 | 89 | if ((cc = client_find(e->window)) == NULL) 90 | cc = client_init(e->window, NULL); 91 | 92 | if ((cc != NULL) && (!(cc->flags & CLIENT_IGNORE))) 93 | client_ptr_warp(cc); 94 | } 95 | 96 | static void 97 | xev_handle_unmapnotify(XEvent *ee) 98 | { 99 | XUnmapEvent *e = &ee->xunmap; 100 | struct client_ctx *cc; 101 | 102 | LOG_DEBUG3("window: 0x%lx", e->window); 103 | 104 | if ((cc = client_find(e->window)) != NULL) { 105 | if (e->send_event) { 106 | xu_set_wm_state(cc->win, WithdrawnState); 107 | } else { 108 | if (!(cc->flags & CLIENT_HIDDEN)) 109 | client_remove(cc); 110 | } 111 | } 112 | } 113 | 114 | static void 115 | xev_handle_destroynotify(XEvent *ee) 116 | { 117 | XDestroyWindowEvent *e = &ee->xdestroywindow; 118 | struct client_ctx *cc; 119 | 120 | LOG_DEBUG3("window: 0x%lx", e->window); 121 | 122 | if ((cc = client_find(e->window)) != NULL) 123 | client_remove(cc); 124 | } 125 | 126 | static void 127 | xev_handle_configurerequest(XEvent *ee) 128 | { 129 | XConfigureRequestEvent *e = &ee->xconfigurerequest; 130 | struct client_ctx *cc; 131 | struct screen_ctx *sc; 132 | XWindowChanges wc; 133 | 134 | LOG_DEBUG3("window: 0x%lx", e->window); 135 | 136 | if ((cc = client_find(e->window)) != NULL) { 137 | sc = cc->sc; 138 | 139 | if (e->value_mask & CWWidth) 140 | cc->geom.w = e->width; 141 | if (e->value_mask & CWHeight) 142 | cc->geom.h = e->height; 143 | if (e->value_mask & CWX) 144 | cc->geom.x = e->x; 145 | if (e->value_mask & CWY) 146 | cc->geom.y = e->y; 147 | if (e->value_mask & CWBorderWidth) 148 | cc->bwidth = e->border_width; 149 | if (e->value_mask & CWSibling) 150 | wc.sibling = e->above; 151 | if (e->value_mask & CWStackMode) 152 | wc.stack_mode = e->detail; 153 | 154 | if (cc->geom.x == 0 && cc->geom.w >= sc->view.w) 155 | cc->geom.x -= cc->bwidth; 156 | 157 | if (cc->geom.y == 0 && cc->geom.h >= sc->view.h) 158 | cc->geom.y -= cc->bwidth; 159 | 160 | wc.x = cc->geom.x; 161 | wc.y = cc->geom.y; 162 | wc.width = cc->geom.w; 163 | wc.height = cc->geom.h; 164 | wc.border_width = cc->bwidth; 165 | 166 | XConfigureWindow(X_Dpy, cc->win, e->value_mask, &wc); 167 | client_config(cc); 168 | } else { 169 | /* let it do what it wants, it'll be ours when we map it. */ 170 | wc.x = e->x; 171 | wc.y = e->y; 172 | wc.width = e->width; 173 | wc.height = e->height; 174 | wc.border_width = e->border_width; 175 | wc.stack_mode = Above; 176 | e->value_mask &= ~CWStackMode; 177 | 178 | XConfigureWindow(X_Dpy, e->window, e->value_mask, &wc); 179 | } 180 | } 181 | 182 | static void 183 | xev_handle_propertynotify(XEvent *ee) 184 | { 185 | XPropertyEvent *e = &ee->xproperty; 186 | struct screen_ctx *sc; 187 | struct client_ctx *cc; 188 | 189 | LOG_DEBUG3("window: 0x%lx", e->window); 190 | 191 | if ((cc = client_find(e->window)) != NULL) { 192 | switch (e->atom) { 193 | case XA_WM_NORMAL_HINTS: 194 | client_get_sizehints(cc); 195 | break; 196 | case XA_WM_NAME: 197 | client_set_name(cc); 198 | break; 199 | case XA_WM_HINTS: 200 | client_wm_hints(cc); 201 | client_draw_border(cc); 202 | break; 203 | case XA_WM_TRANSIENT_FOR: 204 | client_transient(cc); 205 | client_draw_border(cc); 206 | if (cc->gc) 207 | group_movetogroup(cc, cc->gc->num); 208 | break; 209 | default: 210 | if (e->atom == ewmh[_NET_WM_NAME]) 211 | client_set_name(cc); 212 | break; 213 | } 214 | } else { 215 | if (e->atom == ewmh[_NET_DESKTOP_NAMES]) { 216 | if ((sc = screen_find(e->window)) != NULL) 217 | xu_ewmh_net_desktop_names(sc); 218 | } 219 | } 220 | } 221 | 222 | static void 223 | xev_handle_enternotify(XEvent *ee) 224 | { 225 | XCrossingEvent *e = &ee->xcrossing; 226 | struct client_ctx *cc; 227 | 228 | LOG_DEBUG3("window: 0x%lx", e->window); 229 | 230 | Last_Event_Time = e->time; 231 | 232 | if ((cc = client_find(e->window)) != NULL) 233 | client_set_active(cc); 234 | } 235 | 236 | static void 237 | xev_handle_buttonpress(XEvent *ee) 238 | { 239 | XButtonEvent *e = &ee->xbutton; 240 | struct client_ctx *cc; 241 | struct screen_ctx *sc; 242 | struct bind_ctx *mb; 243 | 244 | LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx", 245 | e->root, e->window, e->subwindow); 246 | 247 | if ((sc = screen_find(e->root)) == NULL) 248 | return; 249 | 250 | e->state &= ~IGNOREMODMASK; 251 | 252 | TAILQ_FOREACH(mb, &Conf.mousebindq, entry) { 253 | if (e->button == mb->press.button && e->state == mb->modmask) 254 | break; 255 | } 256 | if (mb == NULL) 257 | return; 258 | mb->cargs->xev = CWM_XEV_BTN; 259 | switch (mb->context) { 260 | case CWM_CONTEXT_CC: 261 | if (((cc = client_find(e->window)) == NULL) && 262 | ((cc = client_current(sc)) == NULL)) 263 | return; 264 | (*mb->callback)(cc, mb->cargs); 265 | break; 266 | case CWM_CONTEXT_SC: 267 | (*mb->callback)(sc, mb->cargs); 268 | break; 269 | case CWM_CONTEXT_NONE: 270 | (*mb->callback)(NULL, mb->cargs); 271 | break; 272 | } 273 | } 274 | 275 | static void 276 | xev_handle_buttonrelease(XEvent *ee) 277 | { 278 | XButtonEvent *e = &ee->xbutton; 279 | struct client_ctx *cc; 280 | 281 | LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx", 282 | e->root, e->window, e->subwindow); 283 | 284 | if ((cc = client_find(e->window)) != NULL) { 285 | if (cc->flags & (CLIENT_ACTIVE | CLIENT_HIGHLIGHT)) { 286 | cc->flags &= ~CLIENT_HIGHLIGHT; 287 | client_draw_border(cc); 288 | } 289 | } 290 | } 291 | 292 | static void 293 | xev_handle_keypress(XEvent *ee) 294 | { 295 | XKeyEvent *e = &ee->xkey; 296 | struct client_ctx *cc; 297 | struct screen_ctx *sc; 298 | struct bind_ctx *kb; 299 | KeySym keysym, skeysym; 300 | unsigned int modshift; 301 | 302 | LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx", 303 | e->root, e->window, e->subwindow); 304 | 305 | if ((sc = screen_find(e->root)) == NULL) 306 | return; 307 | 308 | keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0); 309 | skeysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 1); 310 | 311 | e->state &= ~IGNOREMODMASK; 312 | 313 | TAILQ_FOREACH(kb, &Conf.keybindq, entry) { 314 | if (keysym != kb->press.keysym && skeysym == kb->press.keysym) 315 | modshift = ShiftMask; 316 | else 317 | modshift = 0; 318 | 319 | if ((kb->modmask | modshift) != e->state) 320 | continue; 321 | 322 | if (kb->press.keysym == ((modshift == 0) ? keysym : skeysym)) 323 | break; 324 | } 325 | if (kb == NULL) 326 | return; 327 | kb->cargs->xev = CWM_XEV_KEY; 328 | switch (kb->context) { 329 | case CWM_CONTEXT_CC: 330 | if (((cc = client_find(e->subwindow)) == NULL) && 331 | ((cc = client_current(sc)) == NULL)) 332 | return; 333 | (*kb->callback)(cc, kb->cargs); 334 | break; 335 | case CWM_CONTEXT_SC: 336 | (*kb->callback)(sc, kb->cargs); 337 | break; 338 | case CWM_CONTEXT_NONE: 339 | (*kb->callback)(NULL, kb->cargs); 340 | break; 341 | } 342 | } 343 | 344 | /* 345 | * This is only used for the modifier suppression detection. 346 | */ 347 | static void 348 | xev_handle_keyrelease(XEvent *ee) 349 | { 350 | XKeyEvent *e = &ee->xkey; 351 | struct screen_ctx *sc; 352 | struct client_ctx *cc; 353 | KeySym keysym; 354 | unsigned int i; 355 | 356 | LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx", 357 | e->root, e->window, e->subwindow); 358 | 359 | if ((sc = screen_find(e->root)) == NULL) 360 | return; 361 | 362 | keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0); 363 | for (i = 0; i < nitems(modkeys); i++) { 364 | if (keysym == modkeys[i]) { 365 | if ((cc = client_current(sc)) != NULL) { 366 | if (sc->cycling) { 367 | sc->cycling = 0; 368 | client_mtf(cc); 369 | } 370 | if (cc->flags & CLIENT_HIGHLIGHT) { 371 | cc->flags &= ~CLIENT_HIGHLIGHT; 372 | client_draw_border(cc); 373 | } 374 | } 375 | XUngrabKeyboard(X_Dpy, CurrentTime); 376 | break; 377 | } 378 | } 379 | } 380 | 381 | static void 382 | xev_handle_clientmessage(XEvent *ee) 383 | { 384 | XClientMessageEvent *e = &ee->xclient; 385 | struct client_ctx *cc, *old_cc; 386 | struct screen_ctx *sc; 387 | 388 | LOG_DEBUG3("window: 0x%lx", e->window); 389 | 390 | if (e->message_type == cwmh[WM_CHANGE_STATE]) { 391 | if ((cc = client_find(e->window)) != NULL) { 392 | if (e->data.l[0] == IconicState) 393 | client_hide(cc); 394 | } 395 | } else if (e->message_type == ewmh[_NET_CLOSE_WINDOW]) { 396 | if ((cc = client_find(e->window)) != NULL) { 397 | client_close(cc); 398 | } 399 | } else if (e->message_type == ewmh[_NET_ACTIVE_WINDOW]) { 400 | if ((cc = client_find(e->window)) != NULL) { 401 | if ((old_cc = client_current(NULL)) != NULL) 402 | client_ptr_save(old_cc); 403 | client_show(cc); 404 | client_ptr_warp(cc); 405 | } 406 | } else if (e->message_type == ewmh[_NET_WM_DESKTOP]) { 407 | if ((cc = client_find(e->window)) != NULL) { 408 | /* 409 | * The EWMH spec states that if the cardinal returned 410 | * is 0xFFFFFFFF (-1) then the window should appear 411 | * on all desktops, in our case, group 0. 412 | */ 413 | if (e->data.l[0] == (unsigned long)-1) 414 | group_movetogroup(cc, 0); 415 | else 416 | if (e->data.l[0] >= 0 && 417 | e->data.l[0] < Conf.ngroups) 418 | group_movetogroup(cc, e->data.l[0]); 419 | } 420 | } else if (e->message_type == ewmh[_NET_WM_STATE]) { 421 | if ((cc = client_find(e->window)) != NULL) { 422 | xu_ewmh_handle_net_wm_state_msg(cc, 423 | e->data.l[0], e->data.l[1], e->data.l[2]); 424 | } 425 | } else if (e->message_type == ewmh[_NET_CURRENT_DESKTOP]) { 426 | if ((sc = screen_find(e->window)) != NULL) { 427 | if (e->data.l[0] >= 0 && 428 | e->data.l[0] < Conf.ngroups) 429 | group_only(sc, e->data.l[0]); 430 | } 431 | } 432 | } 433 | 434 | static void 435 | xev_handle_randr(XEvent *ee) 436 | { 437 | XRRScreenChangeNotifyEvent *e = (XRRScreenChangeNotifyEvent *)ee; 438 | struct screen_ctx *sc; 439 | 440 | LOG_DEBUG3("size: %d/%d", e->width, e->height); 441 | 442 | if ((sc = screen_find(e->root)) == NULL) 443 | return; 444 | 445 | XRRUpdateConfiguration(ee); 446 | screen_update_geometry(sc); 447 | screen_assert_clients_within(sc); 448 | } 449 | 450 | /* 451 | * Called when the keymap has changed. 452 | * Ungrab all keys, reload keymap and then regrab 453 | */ 454 | static void 455 | xev_handle_mappingnotify(XEvent *ee) 456 | { 457 | XMappingEvent *e = &ee->xmapping; 458 | struct screen_ctx *sc; 459 | 460 | LOG_DEBUG3("window: 0x%lx", e->window); 461 | 462 | XRefreshKeyboardMapping(e); 463 | if (e->request == MappingKeyboard) { 464 | TAILQ_FOREACH(sc, &Screenq, entry) 465 | conf_grab_kbd(sc->rootwin); 466 | } 467 | } 468 | 469 | static void 470 | xev_handle_expose(XEvent *ee) 471 | { 472 | XExposeEvent *e = &ee->xexpose; 473 | struct client_ctx *cc; 474 | 475 | LOG_DEBUG3("window: 0x%lx", e->window); 476 | 477 | if ((cc = client_find(e->window)) != NULL && e->count == 0) 478 | client_draw_border(cc); 479 | } 480 | 481 | void 482 | xev_process(void) 483 | { 484 | XEvent e; 485 | 486 | while (XPending(X_Dpy)) { 487 | XNextEvent(X_Dpy, &e); 488 | if ((e.type - Conf.xrandr_event_base) == RRScreenChangeNotify) 489 | xev_handle_randr(&e); 490 | else if ((e.type < LASTEvent) && (xev_handlers[e.type] != NULL)) 491 | (*xev_handlers[e.type])(&e); 492 | } 493 | } 494 | -------------------------------------------------------------------------------- /xmalloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * calmwm - the calm window manager 3 | * 4 | * Copyright (c) 2004 Marius Aamodt Eriksen 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * $OpenBSD$ 19 | */ 20 | 21 | #include 22 | #include "queue.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "calmwm.h" 35 | 36 | void * 37 | xmalloc(size_t siz) 38 | { 39 | void *p; 40 | 41 | if (siz == 0) 42 | errx(1, "xmalloc: zero size"); 43 | if ((p = malloc(siz)) == NULL) 44 | err(1, "malloc"); 45 | 46 | return p; 47 | } 48 | 49 | void * 50 | xcalloc(size_t no, size_t siz) 51 | { 52 | void *p; 53 | 54 | if (siz == 0 || no == 0) 55 | errx(1, "xcalloc: zero size"); 56 | if (SIZE_MAX / no < siz) 57 | errx(1, "xcalloc: no * siz > SIZE_MAX"); 58 | if ((p = calloc(no, siz)) == NULL) 59 | err(1, "calloc"); 60 | 61 | return p; 62 | } 63 | 64 | void * 65 | xreallocarray(void *ptr, size_t nmemb, size_t size) 66 | { 67 | void *p; 68 | 69 | p = reallocarray(ptr, nmemb, size); 70 | if (p == NULL) 71 | errx(1, "xreallocarray: out of memory (new_size %zu bytes)", 72 | nmemb * size); 73 | return p; 74 | } 75 | 76 | char * 77 | xstrdup(const char *str) 78 | { 79 | char *p; 80 | 81 | if ((p = strdup(str)) == NULL) 82 | err(1, "strdup"); 83 | 84 | return p; 85 | } 86 | 87 | int 88 | xasprintf(char **ret, const char *fmt, ...) 89 | { 90 | va_list ap; 91 | int i; 92 | 93 | va_start(ap, fmt); 94 | i = xvasprintf(ret, fmt, ap); 95 | va_end(ap); 96 | 97 | return i; 98 | } 99 | 100 | int 101 | xvasprintf(char **ret, const char *fmt, va_list ap) 102 | { 103 | int i; 104 | 105 | i = vasprintf(ret, fmt, ap); 106 | if (i == -1) 107 | err(1, "vasprintf"); 108 | 109 | return i; 110 | } 111 | -------------------------------------------------------------------------------- /xutil.c: -------------------------------------------------------------------------------- 1 | /* 2 | * calmwm - the calm window manager 3 | * 4 | * Copyright (c) 2004 Marius Aamodt Eriksen 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * $OpenBSD$ 19 | */ 20 | 21 | #include 22 | #include "queue.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "calmwm.h" 33 | 34 | void 35 | xu_ptr_get(Window win, int *x, int *y) 36 | { 37 | Window w0, w1; 38 | int tmp0, tmp1; 39 | unsigned int tmp2; 40 | 41 | XQueryPointer(X_Dpy, win, &w0, &w1, &tmp0, &tmp1, x, y, &tmp2); 42 | } 43 | 44 | void 45 | xu_ptr_set(Window win, int x, int y) 46 | { 47 | XWarpPointer(X_Dpy, None, win, 0, 0, 0, 0, x, y); 48 | } 49 | 50 | int 51 | xu_get_prop(Window win, Atom atm, Atom type, long len, unsigned char **p) 52 | { 53 | Atom realtype; 54 | unsigned long n, extra; 55 | int format; 56 | 57 | if (XGetWindowProperty(X_Dpy, win, atm, 0L, len, False, type, 58 | &realtype, &format, &n, &extra, p) != Success || *p == NULL) 59 | return -1; 60 | 61 | if (n == 0) 62 | XFree(*p); 63 | 64 | return n; 65 | } 66 | 67 | int 68 | xu_get_strprop(Window win, Atom atm, char **text) { 69 | XTextProperty prop; 70 | char **list; 71 | int nitems = 0; 72 | 73 | *text = NULL; 74 | 75 | XGetTextProperty(X_Dpy, win, &prop, atm); 76 | if (!prop.nitems) { 77 | XFree(prop.value); 78 | return 0; 79 | } 80 | 81 | if (Xutf8TextPropertyToTextList(X_Dpy, &prop, &list, 82 | &nitems) == Success && nitems > 0 && *list) { 83 | if (nitems > 1) { 84 | XTextProperty prop2; 85 | if (Xutf8TextListToTextProperty(X_Dpy, list, nitems, 86 | XUTF8StringStyle, &prop2) == Success) { 87 | *text = xstrdup((const char *)prop2.value); 88 | XFree(prop2.value); 89 | } 90 | } else { 91 | *text = xstrdup(*list); 92 | } 93 | XFreeStringList(list); 94 | } 95 | XFree(prop.value); 96 | 97 | return nitems; 98 | } 99 | 100 | void 101 | xu_send_clientmsg(Window win, Atom proto, Time ts) 102 | { 103 | XClientMessageEvent cm; 104 | 105 | (void)memset(&cm, 0, sizeof(cm)); 106 | cm.type = ClientMessage; 107 | cm.window = win; 108 | cm.message_type = cwmh[WM_PROTOCOLS]; 109 | cm.format = 32; 110 | cm.data.l[0] = proto; 111 | cm.data.l[1] = ts; 112 | 113 | XSendEvent(X_Dpy, win, False, NoEventMask, (XEvent *)&cm); 114 | } 115 | 116 | void 117 | xu_get_wm_state(Window win, long *state) 118 | { 119 | long *p; 120 | 121 | *state = -1; 122 | if (xu_get_prop(win, cwmh[WM_STATE], cwmh[WM_STATE], 2L, 123 | (unsigned char **)&p) > 0) { 124 | *state = *p; 125 | XFree(p); 126 | } 127 | } 128 | 129 | void 130 | xu_set_wm_state(Window win, long state) 131 | { 132 | long data[] = { state, None }; 133 | 134 | XChangeProperty(X_Dpy, win, cwmh[WM_STATE], cwmh[WM_STATE], 32, 135 | PropModeReplace, (unsigned char *)data, 2); 136 | } 137 | void 138 | xu_xorcolor(XftColor a, XftColor b, XftColor *r) 139 | { 140 | r->pixel = a.pixel ^ b.pixel; 141 | r->color.red = a.color.red ^ b.color.red; 142 | r->color.green = a.color.green ^ b.color.green; 143 | r->color.blue = a.color.blue ^ b.color.blue; 144 | r->color.alpha = 0xffff; 145 | } 146 | 147 | void 148 | xu_atom_init(void) 149 | { 150 | char *cwmhints[] = { 151 | "WM_STATE", 152 | "WM_DELETE_WINDOW", 153 | "WM_TAKE_FOCUS", 154 | "WM_PROTOCOLS", 155 | "_MOTIF_WM_HINTS", 156 | "UTF8_STRING", 157 | "WM_CHANGE_STATE", 158 | }; 159 | char *ewmhints[] = { 160 | "_NET_SUPPORTED", 161 | "_NET_SUPPORTING_WM_CHECK", 162 | "_NET_ACTIVE_WINDOW", 163 | "_NET_CLIENT_LIST", 164 | "_NET_CLIENT_LIST_STACKING", 165 | "_NET_NUMBER_OF_DESKTOPS", 166 | "_NET_CURRENT_DESKTOP", 167 | "_NET_DESKTOP_VIEWPORT", 168 | "_NET_DESKTOP_GEOMETRY", 169 | "_NET_VIRTUAL_ROOTS", 170 | "_NET_SHOWING_DESKTOP", 171 | "_NET_DESKTOP_NAMES", 172 | "_NET_WORKAREA", 173 | "_NET_WM_NAME", 174 | "_NET_WM_DESKTOP", 175 | "_NET_CLOSE_WINDOW", 176 | "_NET_WM_STATE", 177 | "_NET_WM_STATE_STICKY", 178 | "_NET_WM_STATE_MAXIMIZED_VERT", 179 | "_NET_WM_STATE_MAXIMIZED_HORZ", 180 | "_NET_WM_STATE_HIDDEN", 181 | "_NET_WM_STATE_FULLSCREEN", 182 | "_NET_WM_STATE_DEMANDS_ATTENTION", 183 | "_NET_WM_STATE_SKIP_PAGER", 184 | "_NET_WM_STATE_SKIP_TASKBAR", 185 | "_CWM_WM_STATE_FREEZE", 186 | }; 187 | 188 | XInternAtoms(X_Dpy, cwmhints, nitems(cwmhints), False, cwmh); 189 | XInternAtoms(X_Dpy, ewmhints, nitems(ewmhints), False, ewmh); 190 | } 191 | 192 | /* Root Window Properties */ 193 | void 194 | xu_ewmh_net_supported(struct screen_ctx *sc) 195 | { 196 | XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTED], 197 | XA_ATOM, 32, PropModeReplace, (unsigned char *)ewmh, EWMH_NITEMS); 198 | } 199 | 200 | void 201 | xu_ewmh_net_supported_wm_check(struct screen_ctx *sc) 202 | { 203 | Window w; 204 | 205 | w = XCreateSimpleWindow(X_Dpy, sc->rootwin, -1, -1, 1, 1, 0, 0, 0); 206 | XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTING_WM_CHECK], 207 | XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1); 208 | XChangeProperty(X_Dpy, w, ewmh[_NET_SUPPORTING_WM_CHECK], 209 | XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1); 210 | XChangeProperty(X_Dpy, w, ewmh[_NET_WM_NAME], 211 | cwmh[UTF8_STRING], 8, PropModeReplace, 212 | (unsigned char *)Conf.wmname, strlen(Conf.wmname)); 213 | } 214 | 215 | void 216 | xu_ewmh_net_desktop_geometry(struct screen_ctx *sc) 217 | { 218 | long geom[2] = { sc->view.w, sc->view.h }; 219 | 220 | XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_GEOMETRY], 221 | XA_CARDINAL, 32, PropModeReplace, (unsigned char *)geom , 2); 222 | } 223 | 224 | void 225 | xu_ewmh_net_desktop_viewport(struct screen_ctx *sc) 226 | { 227 | long viewports[2] = {0, 0}; 228 | 229 | /* We don't support large desktops, so this is (0, 0). */ 230 | XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_VIEWPORT], 231 | XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2); 232 | } 233 | 234 | void 235 | xu_ewmh_net_workarea(struct screen_ctx *sc) 236 | { 237 | unsigned long *workarea; 238 | int i, ngroups = Conf.ngroups; 239 | 240 | workarea = xreallocarray(NULL, ngroups * 4, sizeof(unsigned long)); 241 | for (i = 0; i < ngroups; i++) { 242 | workarea[4 * i + 0] = sc->work.x; 243 | workarea[4 * i + 1] = sc->work.y; 244 | workarea[4 * i + 2] = sc->work.w; 245 | workarea[4 * i + 3] = sc->work.h; 246 | } 247 | XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_WORKAREA], 248 | XA_CARDINAL, 32, PropModeReplace, (unsigned char *)workarea, 249 | ngroups * 4); 250 | free(workarea); 251 | } 252 | 253 | void 254 | xu_ewmh_net_client_list(struct screen_ctx *sc) 255 | { 256 | struct client_ctx *cc; 257 | Window *winlist; 258 | int i = 0, j = 0; 259 | 260 | TAILQ_FOREACH(cc, &sc->clientq, entry) 261 | i++; 262 | if (i == 0) 263 | return; 264 | 265 | winlist = xreallocarray(NULL, i, sizeof(*winlist)); 266 | TAILQ_FOREACH(cc, &sc->clientq, entry) 267 | winlist[j++] = cc->win; 268 | XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CLIENT_LIST], 269 | XA_WINDOW, 32, PropModeReplace, (unsigned char *)winlist, i); 270 | free(winlist); 271 | } 272 | 273 | void 274 | xu_ewmh_net_client_list_stacking(struct screen_ctx *sc) 275 | { 276 | struct client_ctx *cc; 277 | Window *winlist; 278 | int i = 0, j; 279 | 280 | TAILQ_FOREACH(cc, &sc->clientq, entry) 281 | i++; 282 | if (i == 0) 283 | return; 284 | 285 | j = i; 286 | winlist = xreallocarray(NULL, i, sizeof(*winlist)); 287 | TAILQ_FOREACH(cc, &sc->clientq, entry) 288 | winlist[--j] = cc->win; 289 | XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CLIENT_LIST_STACKING], 290 | XA_WINDOW, 32, PropModeReplace, (unsigned char *)winlist, i); 291 | free(winlist); 292 | } 293 | 294 | void 295 | xu_ewmh_net_active_window(struct screen_ctx *sc, Window w) 296 | { 297 | XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_ACTIVE_WINDOW], 298 | XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1); 299 | } 300 | 301 | void 302 | xu_ewmh_net_number_of_desktops(struct screen_ctx *sc) 303 | { 304 | long ndesks = Conf.ngroups; 305 | 306 | XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_NUMBER_OF_DESKTOPS], 307 | XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&ndesks, 1); 308 | } 309 | 310 | void 311 | xu_ewmh_net_showing_desktop(struct screen_ctx *sc) 312 | { 313 | long zero = 0; 314 | 315 | /* We don't support `showing desktop' mode, so this is zero. 316 | * Note that when we hide all groups, or when all groups are 317 | * hidden we could technically set this later on. 318 | */ 319 | XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SHOWING_DESKTOP], 320 | XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&zero, 1); 321 | } 322 | 323 | void 324 | xu_ewmh_net_virtual_roots(struct screen_ctx *sc) 325 | { 326 | /* We don't support virtual roots, so delete if set by previous wm. */ 327 | XDeleteProperty(X_Dpy, sc->rootwin, ewmh[_NET_VIRTUAL_ROOTS]); 328 | } 329 | 330 | void 331 | xu_ewmh_net_current_desktop(struct screen_ctx *sc) 332 | { 333 | long num = sc->group_active->num; 334 | 335 | XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CURRENT_DESKTOP], 336 | XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1); 337 | } 338 | 339 | void 340 | xu_ewmh_net_desktop_names(struct screen_ctx *sc) 341 | { 342 | struct group_ctx *gc; 343 | char *p, *q; 344 | unsigned char *prop_ret; 345 | int i = 0, j = 0, nstrings = 0, n = 0; 346 | size_t len = 0, tlen, slen; 347 | 348 | /* Let group names be overwritten if _NET_DESKTOP_NAMES is set. */ 349 | 350 | if ((j = xu_get_prop(sc->rootwin, ewmh[_NET_DESKTOP_NAMES], 351 | cwmh[UTF8_STRING], 0xffffff, (unsigned char **)&prop_ret)) > 0) { 352 | prop_ret[j - 1] = '\0'; /* paranoia */ 353 | while (i < j) { 354 | if (prop_ret[i++] == '\0') 355 | nstrings++; 356 | } 357 | } 358 | 359 | p = (char *)prop_ret; 360 | while (n < nstrings) { 361 | TAILQ_FOREACH(gc, &sc->groupq, entry) { 362 | if (gc->num == n) { 363 | free(gc->name); 364 | gc->name = xstrdup(p); 365 | p += strlen(p) + 1; 366 | break; 367 | } 368 | } 369 | n++; 370 | } 371 | if (prop_ret != NULL) 372 | XFree(prop_ret); 373 | 374 | TAILQ_FOREACH(gc, &sc->groupq, entry) 375 | len += strlen(gc->name) + 1; 376 | q = p = xreallocarray(NULL, len, sizeof(*p)); 377 | 378 | tlen = len; 379 | TAILQ_FOREACH(gc, &sc->groupq, entry) { 380 | slen = strlen(gc->name) + 1; 381 | (void)strlcpy(q, gc->name, tlen); 382 | tlen -= slen; 383 | q += slen; 384 | } 385 | 386 | XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_NAMES], 387 | cwmh[UTF8_STRING], 8, PropModeReplace, (unsigned char *)p, len); 388 | free(p); 389 | } 390 | 391 | /* Application Window Properties */ 392 | int 393 | xu_ewmh_get_net_wm_desktop(struct client_ctx *cc, long *n) 394 | { 395 | long *p; 396 | 397 | if (xu_get_prop(cc->win, ewmh[_NET_WM_DESKTOP], XA_CARDINAL, 1L, 398 | (unsigned char **)&p) <= 0) 399 | return 0; 400 | *n = *p; 401 | XFree(p); 402 | return 1; 403 | } 404 | 405 | void 406 | xu_ewmh_set_net_wm_desktop(struct client_ctx *cc) 407 | { 408 | long num = 0xffffffff; 409 | 410 | if (cc->gc) 411 | num = cc->gc->num; 412 | 413 | XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_DESKTOP], 414 | XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1); 415 | } 416 | 417 | Atom * 418 | xu_ewmh_get_net_wm_state(struct client_ctx *cc, int *n) 419 | { 420 | Atom *state, *p = NULL; 421 | 422 | if ((*n = xu_get_prop(cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 64L, 423 | (unsigned char **)&p)) <= 0) 424 | return NULL; 425 | 426 | state = xreallocarray(NULL, *n, sizeof(Atom)); 427 | (void)memcpy(state, p, *n * sizeof(Atom)); 428 | XFree((char *)p); 429 | 430 | return state; 431 | } 432 | 433 | void 434 | xu_ewmh_handle_net_wm_state_msg(struct client_ctx *cc, int action, 435 | Atom first, Atom second) 436 | { 437 | unsigned int i; 438 | struct handlers { 439 | Atom atom; 440 | int flag; 441 | void (*toggle)(struct client_ctx *); 442 | } handlers[] = { 443 | { _NET_WM_STATE_STICKY, 444 | CLIENT_STICKY, 445 | client_toggle_sticky }, 446 | { _NET_WM_STATE_MAXIMIZED_VERT, 447 | CLIENT_VMAXIMIZED, 448 | client_toggle_vmaximize }, 449 | { _NET_WM_STATE_MAXIMIZED_HORZ, 450 | CLIENT_HMAXIMIZED, 451 | client_toggle_hmaximize }, 452 | { _NET_WM_STATE_HIDDEN, 453 | CLIENT_HIDDEN, 454 | client_toggle_hidden }, 455 | { _NET_WM_STATE_FULLSCREEN, 456 | CLIENT_FULLSCREEN, 457 | client_toggle_fullscreen }, 458 | { _NET_WM_STATE_DEMANDS_ATTENTION, 459 | CLIENT_URGENCY, 460 | client_urgency }, 461 | { _NET_WM_STATE_SKIP_PAGER, 462 | CLIENT_SKIP_PAGER, 463 | client_toggle_skip_pager}, 464 | { _NET_WM_STATE_SKIP_TASKBAR, 465 | CLIENT_SKIP_TASKBAR, 466 | client_toggle_skip_taskbar}, 467 | { _CWM_WM_STATE_FREEZE, 468 | CLIENT_FREEZE, 469 | client_toggle_freeze }, 470 | }; 471 | 472 | for (i = 0; i < nitems(handlers); i++) { 473 | if (first != ewmh[handlers[i].atom] && 474 | second != ewmh[handlers[i].atom]) 475 | continue; 476 | switch (action) { 477 | case _NET_WM_STATE_ADD: 478 | if (!(cc->flags & handlers[i].flag)) 479 | handlers[i].toggle(cc); 480 | break; 481 | case _NET_WM_STATE_REMOVE: 482 | if (cc->flags & handlers[i].flag) 483 | handlers[i].toggle(cc); 484 | break; 485 | case _NET_WM_STATE_TOGGLE: 486 | handlers[i].toggle(cc); 487 | } 488 | } 489 | } 490 | 491 | void 492 | xu_ewmh_restore_net_wm_state(struct client_ctx *cc) 493 | { 494 | Atom *atoms; 495 | int i, n; 496 | 497 | atoms = xu_ewmh_get_net_wm_state(cc, &n); 498 | for (i = 0; i < n; i++) { 499 | if (atoms[i] == ewmh[_NET_WM_STATE_STICKY]) 500 | client_toggle_sticky(cc); 501 | if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_VERT]) 502 | client_toggle_vmaximize(cc); 503 | if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_HORZ]) 504 | client_toggle_hmaximize(cc); 505 | if (atoms[i] == ewmh[_NET_WM_STATE_HIDDEN]) 506 | client_toggle_hidden(cc); 507 | if (atoms[i] == ewmh[_NET_WM_STATE_FULLSCREEN]) 508 | client_toggle_fullscreen(cc); 509 | if (atoms[i] == ewmh[_NET_WM_STATE_DEMANDS_ATTENTION]) 510 | client_urgency(cc); 511 | if (atoms[i] == ewmh[_NET_WM_STATE_SKIP_PAGER]) 512 | client_toggle_skip_pager(cc); 513 | if (atoms[i] == ewmh[_NET_WM_STATE_SKIP_TASKBAR]) 514 | client_toggle_skip_taskbar(cc); 515 | if (atoms[i] == ewmh[_CWM_WM_STATE_FREEZE]) 516 | client_toggle_freeze(cc); 517 | } 518 | free(atoms); 519 | } 520 | 521 | void 522 | xu_ewmh_set_net_wm_state(struct client_ctx *cc) 523 | { 524 | Atom *atoms, *oatoms; 525 | int n, i, j; 526 | 527 | oatoms = xu_ewmh_get_net_wm_state(cc, &n); 528 | atoms = xreallocarray(NULL, (n + _NET_WM_STATES_NITEMS), sizeof(Atom)); 529 | for (i = j = 0; i < n; i++) { 530 | if (oatoms[i] != ewmh[_NET_WM_STATE_STICKY] && 531 | oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_VERT] && 532 | oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_HORZ] && 533 | oatoms[i] != ewmh[_NET_WM_STATE_HIDDEN] && 534 | oatoms[i] != ewmh[_NET_WM_STATE_FULLSCREEN] && 535 | oatoms[i] != ewmh[_NET_WM_STATE_DEMANDS_ATTENTION] && 536 | oatoms[i] != ewmh[_NET_WM_STATE_SKIP_PAGER] && 537 | oatoms[i] != ewmh[_NET_WM_STATE_SKIP_TASKBAR] && 538 | oatoms[i] != ewmh[_CWM_WM_STATE_FREEZE]) 539 | atoms[j++] = oatoms[i]; 540 | } 541 | free(oatoms); 542 | if (cc->flags & CLIENT_STICKY) 543 | atoms[j++] = ewmh[_NET_WM_STATE_STICKY]; 544 | if (cc->flags & CLIENT_HIDDEN) 545 | atoms[j++] = ewmh[_NET_WM_STATE_HIDDEN]; 546 | if (cc->flags & CLIENT_FULLSCREEN) 547 | atoms[j++] = ewmh[_NET_WM_STATE_FULLSCREEN]; 548 | else { 549 | if (cc->flags & CLIENT_VMAXIMIZED) 550 | atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_VERT]; 551 | if (cc->flags & CLIENT_HMAXIMIZED) 552 | atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_HORZ]; 553 | } 554 | if (cc->flags & CLIENT_URGENCY) 555 | atoms[j++] = ewmh[_NET_WM_STATE_DEMANDS_ATTENTION]; 556 | if (cc->flags & CLIENT_SKIP_PAGER) 557 | atoms[j++] = ewmh[_NET_WM_STATE_SKIP_PAGER]; 558 | if (cc->flags & CLIENT_SKIP_TASKBAR) 559 | atoms[j++] = ewmh[_NET_WM_STATE_SKIP_TASKBAR]; 560 | if (cc->flags & CLIENT_FREEZE) 561 | atoms[j++] = ewmh[_CWM_WM_STATE_FREEZE]; 562 | if (j > 0) 563 | XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE], 564 | XA_ATOM, 32, PropModeReplace, (unsigned char *)atoms, j); 565 | else 566 | XDeleteProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE]); 567 | free(atoms); 568 | } 569 | --------------------------------------------------------------------------------