pax_global_header00006660000000000000000000000064142332506330014513gustar00rootroot0000000000000052 comment=c55191fdc5a92fd003c5789bbb27dcbb1ff32b1b cwm-7.1/000077500000000000000000000000001423325063300121505ustar00rootroot00000000000000cwm-7.1/Makefile000066400000000000000000000026461423325063300136200ustar00rootroot00000000000000# cwm makefile for BSD make and GNU make # uses pkg-config, DESTDIR and PREFIX PROG= cwm PREFIX?= /usr/local SRCS= calmwm.c screen.c xmalloc.c client.c menu.c \ search.c util.c xutil.c conf.c xevents.c group.c \ kbfunc.c parse.y OBJS= calmwm.o screen.o xmalloc.o client.o menu.o \ search.o util.o xutil.o conf.o xevents.o group.o \ kbfunc.o strlcpy.o strlcat.o parse.o \ strtonum.o reallocarray.o PKG_CONFIG?= pkg-config CPPFLAGS+= `${PKG_CONFIG} --cflags x11 xft xrandr` CFLAGS?= -Wall -O2 -g -D_GNU_SOURCE LDFLAGS+= `${PKG_CONFIG} --libs x11 xft xrandr` MANPREFIX?= ${PREFIX}/share/man all: ${PROG} clean: rm -f ${OBJS} ${PROG} parse.c ${PROG}: ${OBJS} ${CC} ${OBJS} ${LDFLAGS} -o ${PROG} .c.o: ${CC} -c ${CFLAGS} ${CPPFLAGS} $< install: ${PROG} install -d ${DESTDIR}${PREFIX}/bin ${DESTDIR}${MANPREFIX}/man1 ${DESTDIR}${MANPREFIX}/man5 install -m 755 cwm ${DESTDIR}${PREFIX}/bin install -m 644 cwm.1 ${DESTDIR}${MANPREFIX}/man1 install -m 644 cwmrc.5 ${DESTDIR}${MANPREFIX}/man5 release: VERSION=$$(git describe --tags | sed 's/^v//;s/-[^.]*$$//') && \ git archive --prefix=cwm-$$VERSION/ -o cwm-$$VERSION.tar.gz HEAD sign: VERSION=$$(git describe --tags | sed 's/^v//;s/-[^.]*$$//') && \ gpg2 --armor --detach-sign cwm-$$VERSION.tar.gz && \ signify -S -s ~/.signify/cwm.sec -m cwm-$$VERSION.tar.gz && \ sed -i '1cuntrusted comment: verify with cwm.pub' cwm-$$VERSION.tar.gz.sig .PRECIOUS: parse.c cwm-7.1/README000066400000000000000000000135751423325063300130430ustar00rootroot00000000000000This is a port of OpenBSD's excellent cwm[0] to Linux and other Unices. cwm is a window manager for X11 which contains many features that concentrate on the efficiency and transparency of window management. cwm also aims to maintain the simplest and most pleasant aesthetic. This port requires pkg-config, Xft, Xinerama and Xrandr. The included Makefile should work with both GNU make and BSD make. It has been built successfully on OpenBSD, FreeBSD, NetBSD, OS X 10.9 and Linux. This version actively tracks changes in the OpenBSD CVS repository. Releases are roughly coordinated. The revision controlled version is at https://github.com/leahneukirchen/cwm Releases can be found at http://leahneukirchen.org/releases You are welcome to join the IRC channel ##cwm on irc.libera.chat to talk about cwm. ChangeLog: 2012-05-02: First public release 5.1 of portable cwm. 2014-04-13: Second public release 5.5 of portable cwm. User visible changes (for a full list including smaller bug fixes, see http://www.openbsd.org/plus.html ff.) Changes made between OpenBSD 5.1 and 5.2 * Fixed cwm(1) atom (WM_PROTOCOLS) style handing; closing a window will no longer close entire application if the client supports CLIENT_PROTO_DELETE. * Re-implement atom handing for more consistent separation of cwm(1) and Extended Window Manager Hints. * cwm(1) better integrated into the freedesktop.org Window Manager Specification Project. Changes made between OpenBSD 5.2 and 5.3 * Set the initial cwm(1) group to "1". * Added cwm(1) per-group vert/horiz tiling support with new bind commands "vtile" and "htile." * Made cwm(1) screen font an Xft(3) font. * Specific last match for autogroup in cwmrc(5). * Tab completion support for cwm(1) menus. * Allow cwm(1) clients to be resized from a max state. * Multibyte input to cwm(1) menu code now possible. Changes made between OpenBSD 5.3 and 5.4 * Added support for mouse based group {,r}cycle to cwmrc(5). * Allow mouse button4 and button5 in cwmrc(5). * Made cwm(1) check for, and honour, CWStackMode and CWSibling change requests during a ConfigureRequest event. * Honour PATH search order for cwm(1)'s exec. Changes made between OpenBSD 5.5 and 5.4 * Only set the cwm(1) urgency flag if the client is not active. * Allow the cwm(1) config parser continue parsing even after encountering an error. * cwm(1) now follows the EWMH spec: if the cardinal returned is 0xFFFFFFFF (-1) then the window should appear on all desktops. * Made cwm(1) supply a more useful title for windows launched via the ssh(1) command menu ("[ssh] "). * Allowed cwm(1) to accept _NET_WM_DESKTOP and _NET_CURRENT_DESKTOP ClientMessage. * Implemented cwm(1) support for _NET_WM_STATE_FULLSCREEN hint, with keybinding changes: CM-f "fullscreen", CM-m "maximize". * Instead of using the work area, use the Xinerama area for cwm(1) snap calculations. * Save-set when cwm(1) is re-exec'ing so as to not lose State on our hidden clients. * Added cwmrc(5) support for XUrgency and matching _NET_WM_STATE_DEMANDS_ATTENTION ewmh hint, with configurable urgencyborder. * Prepend the group shortcut in the cwm(1) client search menu; prepend shortcut in unhide menu. * If not hidden during an UnmapNotify event, cwm(1) will now un-manage the client. * Like "gap", made cwm(1) "snapdist" per-screen. * Removed cwmrc(5) option to bind a key by keycode with brackets, which never worked. Users should be using keysym names not keycodes. * Re-added cwm(1) support for WM_TAKE_FOCUS. Solves keyboard input focus loss for java apps. * For cwm(1) clients that support WM_TAKE_FOCUS in their WM_PROTOCOLS property, send a ClientMessage event. 2015-01-24: Third public release 5.6 of portable cwm. * Support building on FreeBSD and OS X. * Support for sticky windows (_NET_WM_STATE_STICKY). * Internal cleanups and bug fixes. Changes made between OpenBSD 5.6 and 5.7 * Implemented _NET_WM_STATE_STICKY in cwm(1). Allows client to "stick" to all desktops or groups. * Ensure cwm(1) client that wants to be in nogroup stays in nogroup (thus stays in view), even when (re)reading NET_WM_DESKTOP. Changes made between OpenBSD 5.7 and 5.8 * In cwm(1), introduce "groupsearch" for group menu search. * In cwm(1), show an empty "ssh to" menu if the known_hosts file is missing. * In cwm(1), replace screen region info gathering with XRandR equivalent of Xinerama queries. Changes made between OpenBSD 5.8 and 5.9 * Don't allow freeze operations on fullscreen. * Implement _NET_CLIENT_LIST_STACKING. 2017-10-17: Fourth public release 6.2 of portable cwm. Changes made between OpenBSD 6.2 and 6.3 * Fix blocking bug during moving or resizing. * window-snap-* commands to move windows to edges and corners. * Add support for _NET_WM_STATE_SKIP_PAGER and _NET_WM_STATE_SKIP_TASKBAR. * Add support for re-exec'ing with SIGHUP. 2018-05-14: Fifth public release 6.3 of portable cwm. 2020-01-04: Sixth public release 6.6 of portable cwm. Changes made between OpenBSD 6.4 and 6.5 * Added a configtest flag (-n) to cwm(1). * Introduced 'group-close-[n]' action to cwm(1) to close all windows within a specified group. 2020-05-22: Seventh public release 6.7 of portable cwm. Changes made between OpenBSD 6.6 and 6.7 * Allowed cwm(1) configuration of window size based on percentage of the master window during horizontal and vertical tiling actions. * Allowed use of window-htile and window-vtile with the "empty" group clients in cwm(1). 2022-04-30: Eighth public release 7.1 of portable cwm. Changes made between OpenBSD 6.9 and 7.0 * Changed cwm(1) maximization and full-screen mode toggling to keep the cursor within the window, preventing focus loss. Changes made between OpenBSD 7.0 and 7.1 * Added a cwm(1) "group-last" command that shows only the previously active group. * Allowed bare numbers for key and mouse bindings in cwm(1). --Leah Neukirchen [0]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/xenocara/app/cwm/ cwm-7.1/calmwm.c000066400000000000000000000124441423325063300136010ustar00rootroot00000000000000/* * calmwm - the calm window manager * * Copyright (c) 2004 Marius Aamodt Eriksen * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD$ */ #include #include "queue.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "calmwm.h" Display *X_Dpy; Time Last_Event_Time = CurrentTime; Atom cwmh[CWMH_NITEMS]; Atom ewmh[EWMH_NITEMS]; struct screen_q Screenq = TAILQ_HEAD_INITIALIZER(Screenq); struct conf Conf; volatile sig_atomic_t cwm_status; void usage(void); static void sighdlr(int); static int x_errorhandler(Display *, XErrorEvent *); static int x_init(const char *); static void x_teardown(void); static int x_wmerrorhandler(Display *, XErrorEvent *); int main(int argc, char **argv) { char *display_name = NULL; char *fallback; int ch, xfd, nflag = 0; struct pollfd pfd[1]; if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) warnx("no locale support"); mbtowc(NULL, NULL, MB_CUR_MAX); conf_init(&Conf); fallback = u_argv(argv); Conf.wm_argv = u_argv(argv); while ((ch = getopt(argc, argv, "c:d:nv")) != -1) { switch (ch) { case 'c': free(Conf.conf_file); Conf.conf_file = xstrdup(optarg); break; case 'd': display_name = optarg; break; case 'n': nflag = 1; break; case 'v': Conf.debug++; break; default: usage(); } } argc -= optind; argv += optind; if (signal(SIGCHLD, sighdlr) == SIG_ERR || signal(SIGHUP, sighdlr) == SIG_ERR || signal(SIGINT, sighdlr) == SIG_ERR || signal(SIGTERM, sighdlr) == SIG_ERR) err(1, "signal"); if (parse_config(Conf.conf_file, &Conf) == -1) { warnx("error parsing config file"); if (nflag) return 1; } if (nflag) return 0; xfd = x_init(display_name); cwm_status = CWM_RUNNING; #ifdef __OpenBSD__ if (pledge("stdio rpath proc exec", NULL) == -1) err(1, "pledge"); #endif memset(&pfd, 0, sizeof(pfd)); pfd[0].fd = xfd; pfd[0].events = POLLIN; while (cwm_status == CWM_RUNNING) { xev_process(); if (poll(pfd, 1, -1) == -1) { if (errno != EINTR) warn("poll"); } } x_teardown(); if (cwm_status == CWM_EXEC_WM) { u_exec(Conf.wm_argv); warnx("'%s' failed to start, starting fallback", Conf.wm_argv); u_exec(fallback); } return 0; } static int x_init(const char *dpyname) { int i; if ((X_Dpy = XOpenDisplay(dpyname)) == NULL) errx(1, "unable to open display \"%s\"", XDisplayName(dpyname)); XSetErrorHandler(x_wmerrorhandler); XSelectInput(X_Dpy, DefaultRootWindow(X_Dpy), SubstructureRedirectMask); XSync(X_Dpy, False); XSetErrorHandler(x_errorhandler); Conf.xrandr = XRRQueryExtension(X_Dpy, &Conf.xrandr_event_base, &i); xu_atom_init(); conf_cursor(&Conf); for (i = 0; i < ScreenCount(X_Dpy); i++) screen_init(i); return ConnectionNumber(X_Dpy); } static void x_teardown(void) { struct screen_ctx *sc; unsigned int i; conf_clear(&Conf); TAILQ_FOREACH(sc, &Screenq, entry) { for (i = 0; i < CWM_COLOR_NITEMS; i++) XftColorFree(X_Dpy, DefaultVisual(X_Dpy, sc->which), DefaultColormap(X_Dpy, sc->which), &sc->xftcolor[i]); XftFontClose(X_Dpy, sc->xftfont); XUngrabKey(X_Dpy, AnyKey, AnyModifier, sc->rootwin); } XUngrabPointer(X_Dpy, CurrentTime); XUngrabKeyboard(X_Dpy, CurrentTime); for (i = 0; i < CF_NITEMS; i++) XFreeCursor(X_Dpy, Conf.cursor[i]); XSync(X_Dpy, False); XSetInputFocus(X_Dpy, PointerRoot, RevertToPointerRoot, CurrentTime); XCloseDisplay(X_Dpy); } static int x_wmerrorhandler(Display *dpy, XErrorEvent *e) { errx(1, "root window unavailable - perhaps another wm is running?"); return 0; } static int x_errorhandler(Display *dpy, XErrorEvent *e) { #ifdef DEBUG char msg[80], number[80], req[80]; XGetErrorText(X_Dpy, e->error_code, msg, sizeof(msg)); (void)snprintf(number, sizeof(number), "%d", e->request_code); XGetErrorDatabaseText(X_Dpy, "XRequest", number, "", req, sizeof(req)); warnx("%s(0x%x): %s", req, (unsigned int)e->resourceid, msg); #endif return 0; } static void sighdlr(int sig) { pid_t pid; int save_errno = errno, status; switch (sig) { case SIGCHLD: /* Collect dead children. */ while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || (pid < 0 && errno == EINTR)) ; break; case SIGHUP: cwm_status = CWM_EXEC_WM; break; case SIGINT: case SIGTERM: cwm_status = CWM_QUIT; break; } errno = save_errno; } void usage(void) { extern char *__progname; (void)fprintf(stderr, "usage: %s [-nv] [-c file] [-d display]\n", __progname); exit(1); } cwm-7.1/calmwm.h000066400000000000000000000460161423325063300136100ustar00rootroot00000000000000/* * calmwm - the calm window manager * * Copyright (c) 2004 Marius Aamodt Eriksen * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD$ */ #ifndef _CALMWM_H_ #define _CALMWM_H_ #include #include #include "queue.h" /* prototypes for portable-included functions */ char *fgetln(FILE *, size_t *); long long strtonum(const char *, long long, long long, const char **); void *reallocarray(void *, size_t, size_t); #ifdef strlcat #define HAVE_STRLCAT #else size_t strlcat(char *, const char *, size_t); #endif #ifdef strlcpy #define HAVE_STRLCPY #else size_t strlcpy(char *, const char *, size_t); #endif #include #include #include #include #include #include #include #include #include #define LOG_DEBUG0(...) log_debug(0, __func__, __VA_ARGS__) #define LOG_DEBUG1(...) log_debug(1, __func__, __VA_ARGS__) #define LOG_DEBUG2(...) log_debug(2, __func__, __VA_ARGS__) #define LOG_DEBUG3(...) log_debug(3, __func__, __VA_ARGS__) #undef MIN #undef MAX #define MIN(x, y) ((x) < (y) ? (x) : (y)) #define MAX(x, y) ((x) > (y) ? (x) : (y)) #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif #define BUTTONMASK (ButtonPressMask | ButtonReleaseMask) #define MOUSEMASK (BUTTONMASK | PointerMotionMask) #define IGNOREMODMASK (LockMask | Mod2Mask | 0x2000) /* direction/amount */ #define CWM_UP 0x0001 #define CWM_DOWN 0x0002 #define CWM_LEFT 0x0004 #define CWM_RIGHT 0x0008 #define CWM_BIGAMOUNT 0x0010 #define CWM_UP_BIG (CWM_UP | CWM_BIGAMOUNT) #define CWM_DOWN_BIG (CWM_DOWN | CWM_BIGAMOUNT) #define CWM_LEFT_BIG (CWM_LEFT | CWM_BIGAMOUNT) #define CWM_RIGHT_BIG (CWM_RIGHT | CWM_BIGAMOUNT) #define CWM_UP_RIGHT (CWM_UP | CWM_RIGHT) #define CWM_UP_LEFT (CWM_UP | CWM_LEFT) #define CWM_DOWN_RIGHT (CWM_DOWN | CWM_RIGHT) #define CWM_DOWN_LEFT (CWM_DOWN | CWM_LEFT) #define CWM_CYCLE_FORWARD 0x0001 #define CWM_CYCLE_REVERSE 0x0002 #define CWM_CYCLE_INGROUP 0x0004 enum cwm_status { CWM_QUIT, CWM_RUNNING, CWM_EXEC_WM }; enum cursor_font { CF_NORMAL, CF_MOVE, CF_RESIZE, CF_QUESTION, CF_NITEMS }; enum color { CWM_COLOR_BORDER_ACTIVE, CWM_COLOR_BORDER_INACTIVE, CWM_COLOR_BORDER_URGENCY, CWM_COLOR_BORDER_GROUP, CWM_COLOR_BORDER_UNGROUP, CWM_COLOR_MENU_FG, CWM_COLOR_MENU_BG, CWM_COLOR_MENU_FONT, CWM_COLOR_MENU_FONT_SEL, CWM_COLOR_NITEMS }; struct geom { int x; int y; int w; int h; }; struct gap { int top; int bottom; int left; int right; }; struct winname { TAILQ_ENTRY(winname) entry; char *name; }; TAILQ_HEAD(name_q, winname); TAILQ_HEAD(ignore_q, winname); struct client_ctx { TAILQ_ENTRY(client_ctx) entry; struct screen_ctx *sc; struct group_ctx *gc; Window win; Colormap colormap; int bwidth; /* border width */ int obwidth; /* original border width */ struct geom geom, savegeom, fullgeom; struct { long flags; /* defined hints */ int basew; /* desired width */ int baseh; /* desired height */ int minw; /* minimum width */ int minh; /* minimum height */ int maxw; /* maximum width */ int maxh; /* maximum height */ int incw; /* width increment progression */ int inch; /* height increment progression */ float mina; /* minimum aspect ratio */ float maxa; /* maximum aspect ratio */ } hint; struct { int x; /* x position */ int y; /* y position */ } ptr; struct { int h; /* height */ int w; /* width */ } dim; #define CLIENT_HIDDEN 0x0001 #define CLIENT_IGNORE 0x0002 #define CLIENT_VMAXIMIZED 0x0004 #define CLIENT_HMAXIMIZED 0x0008 #define CLIENT_FREEZE 0x0010 #define CLIENT_GROUP 0x0020 #define CLIENT_UNGROUP 0x0040 #define CLIENT_INPUT 0x0080 #define CLIENT_WM_DELETE_WINDOW 0x0100 #define CLIENT_WM_TAKE_FOCUS 0x0200 #define CLIENT_URGENCY 0x0400 #define CLIENT_FULLSCREEN 0x0800 #define CLIENT_STICKY 0x1000 #define CLIENT_ACTIVE 0x2000 #define CLIENT_SKIP_PAGER 0x4000 #define CLIENT_SKIP_TASKBAR 0x8000 #define CLIENT_SKIP_CYCLE (CLIENT_HIDDEN | CLIENT_IGNORE | \ CLIENT_SKIP_TASKBAR | CLIENT_SKIP_PAGER) #define CLIENT_HIGHLIGHT (CLIENT_GROUP | CLIENT_UNGROUP) #define CLIENT_MAXFLAGS (CLIENT_VMAXIMIZED | CLIENT_HMAXIMIZED) #define CLIENT_MAXIMIZED (CLIENT_VMAXIMIZED | CLIENT_HMAXIMIZED) int flags; int stackingorder; struct name_q nameq; char *name; char *label; char *res_class; /* class hint */ char *res_name; /* class hint */ int initial_state; /* wm hint */ }; TAILQ_HEAD(client_q, client_ctx); struct group_ctx { TAILQ_ENTRY(group_ctx) entry; struct screen_ctx *sc; char *name; int num; }; TAILQ_HEAD(group_q, group_ctx); struct autogroup { TAILQ_ENTRY(autogroup) entry; char *class; char *name; int num; }; TAILQ_HEAD(autogroup_q, autogroup); struct region_ctx { TAILQ_ENTRY(region_ctx) entry; int num; struct geom view; /* viewable area */ struct geom work; /* workable area, gap-applied */ }; TAILQ_HEAD(region_q, region_ctx); struct screen_ctx { TAILQ_ENTRY(screen_ctx) entry; int which; Window rootwin; int cycling; int hideall; int snapdist; struct geom view; /* viewable area */ struct geom work; /* workable area, gap-applied */ struct gap gap; struct client_q clientq; struct region_q regionq; struct group_q groupq; struct group_ctx *group_active; struct group_ctx *group_last; Colormap colormap; Visual *visual; struct { Window win; XftDraw *xftdraw; } prop; XftColor xftcolor[CWM_COLOR_NITEMS]; XftFont *xftfont; }; TAILQ_HEAD(screen_q, screen_ctx); struct cargs { char *cmd; int flag; enum { CWM_XEV_KEY, CWM_XEV_BTN } xev; }; enum context { CWM_CONTEXT_NONE = 0, CWM_CONTEXT_CC, CWM_CONTEXT_SC }; struct bind_ctx { TAILQ_ENTRY(bind_ctx) entry; void (*callback)(void *, struct cargs *); struct cargs *cargs; enum context context; unsigned int modmask; union { KeySym keysym; unsigned int button; } press; }; TAILQ_HEAD(keybind_q, bind_ctx); TAILQ_HEAD(mousebind_q, bind_ctx); struct cmd_ctx { TAILQ_ENTRY(cmd_ctx) entry; char *name; char *path; }; TAILQ_HEAD(cmd_q, cmd_ctx); TAILQ_HEAD(wm_q, cmd_ctx); #define CWM_MENU_DUMMY 0x0001 #define CWM_MENU_FILE 0x0002 #define CWM_MENU_LIST 0x0004 #define CWM_MENU_WINDOW_ALL 0x0008 #define CWM_MENU_WINDOW_HIDDEN 0x0010 struct menu { TAILQ_ENTRY(menu) entry; TAILQ_ENTRY(menu) resultentry; #define MENU_MAXENTRY 200 char text[MENU_MAXENTRY + 1]; char print[MENU_MAXENTRY + 1]; void *ctx; short dummy; short abort; }; TAILQ_HEAD(menu_q, menu); struct conf { struct keybind_q keybindq; struct mousebind_q mousebindq; struct autogroup_q autogroupq; struct ignore_q ignoreq; struct cmd_q cmdq; struct wm_q wmq; int ngroups; int stickygroups; int nameqlen; int bwidth; int mamount; int snapdist; int htile; int vtile; struct gap gap; char *color[CWM_COLOR_NITEMS]; char *font; char *wmname; Cursor cursor[CF_NITEMS]; int xrandr; int xrandr_event_base; char *conf_file; char *known_hosts; char *wm_argv; int debug; }; /* MWM hints */ struct mwm_hints { #define MWM_HINTS_ELEMENTS 5L #define MWM_HINTS_FUNCTIONS (1L << 0) #define MWM_HINTS_DECORATIONS (1L << 1) #define MWM_HINTS_INPUT_MODE (1L << 2) #define MWM_HINTS_STATUS (1L << 3) unsigned long flags; #define MWM_FUNC_ALL (1L << 0) #define MWM_FUNC_RESIZE (1L << 1) #define MWM_FUNC_MOVE (1L << 2) #define MWM_FUNC_MINIMIZE (1L << 3) #define MWM_FUNC_MAXIMIZE (1L << 4) #define MWM_FUNC_CLOSE (1L << 5) unsigned long functions; #define MWM_DECOR_ALL (1L << 0) #define MWM_DECOR_BORDER (1L << 1) #define MWM_DECOR_RESIZEH (1L << 2) #define MWM_DECOR_TITLE (1L << 3) #define MWM_DECOR_MENU (1L << 4) #define MWM_DECOR_MINIMIZE (1L << 5) #define MWM_DECOR_MAXIMIZE (1L << 6) unsigned long decorations; #define MWM_INPUT_MODELESS 0 #define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1 #define MWM_INPUT_SYSTEM_MODAL 2 #define MWM_INPUT_FULL_APPLICATION_MODAL 3 long inputMode; #define MWM_TEAROFF_WINDOW (1L << 0) unsigned long status; }; enum cwmh { WM_STATE, WM_DELETE_WINDOW, WM_TAKE_FOCUS, WM_PROTOCOLS, _MOTIF_WM_HINTS, UTF8_STRING, WM_CHANGE_STATE, CWMH_NITEMS }; enum ewmh { _NET_SUPPORTED, _NET_SUPPORTING_WM_CHECK, _NET_ACTIVE_WINDOW, _NET_CLIENT_LIST, _NET_CLIENT_LIST_STACKING, _NET_NUMBER_OF_DESKTOPS, _NET_CURRENT_DESKTOP, _NET_DESKTOP_VIEWPORT, _NET_DESKTOP_GEOMETRY, _NET_VIRTUAL_ROOTS, _NET_SHOWING_DESKTOP, _NET_DESKTOP_NAMES, _NET_WORKAREA, _NET_WM_NAME, _NET_WM_DESKTOP, _NET_CLOSE_WINDOW, _NET_WM_STATE, #define _NET_WM_STATES_NITEMS 9 _NET_WM_STATE_STICKY, _NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_HIDDEN, _NET_WM_STATE_FULLSCREEN, _NET_WM_STATE_DEMANDS_ATTENTION, _NET_WM_STATE_SKIP_PAGER, _NET_WM_STATE_SKIP_TASKBAR, _CWM_WM_STATE_FREEZE, EWMH_NITEMS }; enum net_wm_state { _NET_WM_STATE_REMOVE, _NET_WM_STATE_ADD, _NET_WM_STATE_TOGGLE }; extern Display *X_Dpy; extern Time Last_Event_Time; extern Atom cwmh[CWMH_NITEMS]; extern Atom ewmh[EWMH_NITEMS]; extern struct screen_q Screenq; extern struct conf Conf; void usage(void); void client_apply_sizehints(struct client_ctx *); void client_close(struct client_ctx *); void client_config(struct client_ctx *); struct client_ctx *client_current(struct screen_ctx *); void client_draw_border(struct client_ctx *); struct client_ctx *client_find(Window); void client_get_sizehints(struct client_ctx *); void client_hide(struct client_ctx *); void client_htile(struct client_ctx *); int client_inbound(struct client_ctx *, int, int); struct client_ctx *client_init(Window, struct screen_ctx *); void client_lower(struct client_ctx *); void client_move(struct client_ctx *); void client_mtf(struct client_ctx *); struct client_ctx *client_next(struct client_ctx *); struct client_ctx *client_prev(struct client_ctx *); void client_ptr_inbound(struct client_ctx *, int); void client_ptr_save(struct client_ctx *); void client_ptr_warp(struct client_ctx *); void client_raise(struct client_ctx *); void client_remove(struct client_ctx *); void client_resize(struct client_ctx *, int); void client_set_active(struct client_ctx *); void client_set_name(struct client_ctx *); void client_show(struct client_ctx *); int client_snapcalc(int, int, int, int, int); void client_toggle_hidden(struct client_ctx *); void client_toggle_hmaximize(struct client_ctx *); void client_toggle_fullscreen(struct client_ctx *); void client_toggle_freeze(struct client_ctx *); void client_toggle_maximize(struct client_ctx *); void client_toggle_skip_pager(struct client_ctx *); void client_toggle_skip_taskbar(struct client_ctx *); void client_toggle_sticky(struct client_ctx *); void client_toggle_vmaximize(struct client_ctx *); void client_transient(struct client_ctx *); void client_urgency(struct client_ctx *); void client_vtile(struct client_ctx *); void client_wm_hints(struct client_ctx *); void group_assign(struct group_ctx *, struct client_ctx *); int group_autogroup(struct client_ctx *); void group_cycle(struct screen_ctx *, int); void group_hide(struct group_ctx *); int group_holds_only_hidden(struct group_ctx *); int group_holds_only_sticky(struct group_ctx *); void group_init(struct screen_ctx *, int, const char *); void group_movetogroup(struct client_ctx *, int); void group_only(struct screen_ctx *, int); void group_close(struct screen_ctx *, int); int group_restore(struct client_ctx *); void group_show(struct group_ctx *); void group_toggle(struct screen_ctx *, int); void group_toggle_all(struct screen_ctx *); void group_toggle_membership(struct client_ctx *); void group_update_names(struct screen_ctx *); void search_match_client(struct menu_q *, struct menu_q *, char *); void search_match_cmd(struct menu_q *, struct menu_q *, char *); void search_match_exec(struct menu_q *, struct menu_q *, char *); void search_match_group(struct menu_q *, struct menu_q *, char *); void search_match_path(struct menu_q *, struct menu_q *, char *); void search_match_text(struct menu_q *, struct menu_q *, char *); void search_match_wm(struct menu_q *, struct menu_q *, char *); void search_print_client(struct menu *, int); void search_print_cmd(struct menu *, int); void search_print_group(struct menu *, int); void search_print_text(struct menu *, int); void search_print_wm(struct menu *, int); struct region_ctx *region_find(struct screen_ctx *, int, int); void screen_assert_clients_within(struct screen_ctx *); struct geom screen_area(struct screen_ctx *, int, int, int); struct screen_ctx *screen_find(Window); void screen_init(int); void screen_prop_win_create(struct screen_ctx *, Window); void screen_prop_win_destroy(struct screen_ctx *); void screen_prop_win_draw(struct screen_ctx *, const char *, ...) __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); void screen_update_geometry(struct screen_ctx *); void screen_updatestackingorder(struct screen_ctx *); void kbfunc_cwm_status(void *, struct cargs *); void kbfunc_ptrmove(void *, struct cargs *); void kbfunc_client_snap(void *, struct cargs *); void kbfunc_client_move(void *, struct cargs *); void kbfunc_client_resize(void *, struct cargs *); void kbfunc_client_close(void *, struct cargs *); void kbfunc_client_lower(void *, struct cargs *); void kbfunc_client_raise(void *, struct cargs *); void kbfunc_client_hide(void *, struct cargs *); void kbfunc_client_toggle_freeze(void *, struct cargs *); void kbfunc_client_toggle_sticky(void *, struct cargs *); void kbfunc_client_toggle_fullscreen(void *, struct cargs *); void kbfunc_client_toggle_maximize(void *, struct cargs *); void kbfunc_client_toggle_hmaximize(void *, struct cargs *); void kbfunc_client_toggle_vmaximize(void *, struct cargs *); void kbfunc_client_htile(void *, struct cargs *); void kbfunc_client_vtile(void *, struct cargs *); void kbfunc_client_cycle(void *, struct cargs *); void kbfunc_client_toggle_group(void *, struct cargs *); void kbfunc_client_movetogroup(void *, struct cargs *); void kbfunc_group_toggle(void *, struct cargs *); void kbfunc_group_only(void *, struct cargs *); void kbfunc_group_last(void *, struct cargs *); void kbfunc_group_close(void *, struct cargs *); void kbfunc_group_cycle(void *, struct cargs *); void kbfunc_group_toggle_all(void *, struct cargs *); void kbfunc_menu_client(void *, struct cargs *); void kbfunc_menu_cmd(void *, struct cargs *); void kbfunc_menu_group(void *, struct cargs *); void kbfunc_menu_wm(void *, struct cargs *); void kbfunc_menu_exec(void *, struct cargs *); void kbfunc_menu_ssh(void *, struct cargs *); void kbfunc_client_menu_label(void *, struct cargs *); void kbfunc_exec_cmd(void *, struct cargs *); void kbfunc_exec_lock(void *, struct cargs *); void kbfunc_exec_term(void *, struct cargs *); struct menu *menu_filter(struct screen_ctx *, struct menu_q *, const char *, const char *, int, void (*)(struct menu_q *, struct menu_q *, char *), void (*)(struct menu *, int)); void menuq_add(struct menu_q *, void *, const char *, ...) __attribute__((__format__ (printf, 3, 4))); void menuq_clear(struct menu_q *); int parse_config(const char *, struct conf *); void conf_autogroup(struct conf *, int, const char *, const char *); int conf_bind_key(struct conf *, const char *, const char *); int conf_bind_mouse(struct conf *, const char *, const char *); void conf_clear(struct conf *); void conf_client(struct client_ctx *); void conf_cmd_add(struct conf *, const char *, const char *); void conf_wm_add(struct conf *, const char *, const char *); void conf_cursor(struct conf *); void conf_grab_kbd(Window); void conf_grab_mouse(Window); void conf_init(struct conf *); void conf_ignore(struct conf *, const char *); void conf_screen(struct screen_ctx *); void conf_group(struct screen_ctx *); void xev_process(void); int xu_get_prop(Window, Atom, Atom, long, unsigned char **); int xu_get_strprop(Window, Atom, char **); void xu_ptr_get(Window, int *, int *); void xu_ptr_set(Window, int, int); void xu_get_wm_state(Window, long *); void xu_set_wm_state(Window, long); void xu_send_clientmsg(Window, Atom, Time); void xu_xorcolor(XftColor, XftColor, XftColor *); void xu_atom_init(void); void xu_ewmh_net_supported(struct screen_ctx *); void xu_ewmh_net_supported_wm_check(struct screen_ctx *); void xu_ewmh_net_desktop_geometry(struct screen_ctx *); void xu_ewmh_net_desktop_viewport(struct screen_ctx *); void xu_ewmh_net_workarea(struct screen_ctx *); void xu_ewmh_net_client_list(struct screen_ctx *); void xu_ewmh_net_client_list_stacking(struct screen_ctx *); void xu_ewmh_net_active_window(struct screen_ctx *, Window); void xu_ewmh_net_number_of_desktops(struct screen_ctx *); void xu_ewmh_net_showing_desktop(struct screen_ctx *); void xu_ewmh_net_virtual_roots(struct screen_ctx *); void xu_ewmh_net_current_desktop(struct screen_ctx *); void xu_ewmh_net_desktop_names(struct screen_ctx *); int xu_ewmh_get_net_wm_desktop(struct client_ctx *, long *); void xu_ewmh_set_net_wm_desktop(struct client_ctx *); Atom *xu_ewmh_get_net_wm_state(struct client_ctx *, int *); void xu_ewmh_handle_net_wm_state_msg(struct client_ctx *, int, Atom, Atom); void xu_ewmh_set_net_wm_state(struct client_ctx *); void xu_ewmh_restore_net_wm_state(struct client_ctx *); char *u_argv(char * const *); void u_exec(char *); void u_spawn(char *); void log_debug(int, const char *, const char *, ...) __attribute__((__format__ (printf, 3, 4))) __attribute__((__nonnull__ (3))); void *xcalloc(size_t, size_t); void *xmalloc(size_t); void *xreallocarray(void *, size_t, size_t); char *xstrdup(const char *); int xasprintf(char **, const char *, ...) __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); int xvasprintf(char **, const char *, va_list) __attribute__((__nonnull__ (2))); #endif /* _CALMWM_H_ */ cwm-7.1/client.c000066400000000000000000000546711423325063300136070ustar00rootroot00000000000000/* * calmwm - the calm window manager * * Copyright (c) 2004 Marius Aamodt Eriksen * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD$ */ #include #include "queue.h" #include #include #include #include #include #include #include #include "calmwm.h" static void client_class_hint(struct client_ctx *); static void client_placement(struct client_ctx *); static void client_mwm_hints(struct client_ctx *); static void client_wm_protocols(struct client_ctx *); struct client_ctx * client_init(Window win, struct screen_ctx *sc) { struct client_ctx *cc; XWindowAttributes wattr; int mapped; long state; if (win == None) return NULL; if (!XGetWindowAttributes(X_Dpy, win, &wattr)) return NULL; if (sc == NULL) { if ((sc = screen_find(wattr.root)) == NULL) return NULL; mapped = 1; } else { if (wattr.override_redirect || wattr.map_state != IsViewable) return NULL; mapped = wattr.map_state != IsUnmapped; } XGrabServer(X_Dpy); cc = xmalloc(sizeof(*cc)); cc->sc = sc; cc->win = win; cc->name = NULL; cc->label = NULL; cc->gc = NULL; cc->res_class = NULL; cc->res_name = NULL; cc->flags = 0; cc->stackingorder = 0; cc->initial_state = 0; memset(&cc->hint, 0, sizeof(cc->hint)); TAILQ_INIT(&cc->nameq); cc->geom.x = wattr.x; cc->geom.y = wattr.y; cc->geom.w = wattr.width; cc->geom.h = wattr.height; cc->colormap = wattr.colormap; cc->obwidth = wattr.border_width; cc->bwidth = Conf.bwidth; client_set_name(cc); conf_client(cc); client_wm_hints(cc); client_class_hint(cc); client_wm_protocols(cc); client_get_sizehints(cc); client_transient(cc); client_mwm_hints(cc); if ((cc->flags & CLIENT_IGNORE)) cc->bwidth = 0; cc->dim.w = (cc->geom.w - cc->hint.basew) / cc->hint.incw; cc->dim.h = (cc->geom.h - cc->hint.baseh) / cc->hint.inch; cc->ptr.x = cc->geom.w / 2; cc->ptr.y = cc->geom.h / 2; if (wattr.map_state != IsViewable) { client_placement(cc); client_resize(cc, 0); if (cc->initial_state) xu_set_wm_state(cc->win, cc->initial_state); } XSelectInput(X_Dpy, cc->win, EnterWindowMask | PropertyChangeMask | KeyReleaseMask); XAddToSaveSet(X_Dpy, cc->win); /* Notify client of its configuration. */ client_config(cc); TAILQ_INSERT_TAIL(&sc->clientq, cc, entry); xu_ewmh_net_client_list(sc); xu_ewmh_net_client_list_stacking(sc); xu_ewmh_restore_net_wm_state(cc); xu_get_wm_state(cc->win, &state); if (state == IconicState) client_hide(cc); else client_show(cc); if (mapped) { if (cc->gc) { group_movetogroup(cc, cc->gc->num); goto out; } if (group_restore(cc)) goto out; if (group_autogroup(cc)) goto out; if (Conf.stickygroups) group_assign(sc->group_active, cc); else group_assign(NULL, cc); } out: XSync(X_Dpy, False); XUngrabServer(X_Dpy); return cc; } struct client_ctx * client_current(struct screen_ctx *sc) { struct screen_ctx *_sc; struct client_ctx *cc; if (sc) { TAILQ_FOREACH(cc, &sc->clientq, entry) { if (cc->flags & CLIENT_ACTIVE) return cc; } } else { TAILQ_FOREACH(_sc, &Screenq, entry) { TAILQ_FOREACH(cc, &_sc->clientq, entry) { if (cc->flags & CLIENT_ACTIVE) return cc; } } } return NULL; } struct client_ctx * client_find(Window win) { struct screen_ctx *sc; struct client_ctx *cc; TAILQ_FOREACH(sc, &Screenq, entry) { TAILQ_FOREACH(cc, &sc->clientq, entry) { if (cc->win == win) return cc; } } return NULL; } struct client_ctx * client_next(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct client_ctx *newcc; return(((newcc = TAILQ_NEXT(cc, entry)) != NULL) ? newcc : TAILQ_FIRST(&sc->clientq)); } struct client_ctx * client_prev(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct client_ctx *newcc; return(((newcc = TAILQ_PREV(cc, client_q, entry)) != NULL) ? newcc : TAILQ_LAST(&sc->clientq, client_q)); } void client_remove(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct winname *wn; TAILQ_REMOVE(&sc->clientq, cc, entry); xu_ewmh_net_client_list(sc); xu_ewmh_net_client_list_stacking(sc); if (cc->flags & CLIENT_ACTIVE) xu_ewmh_net_active_window(sc, None); while ((wn = TAILQ_FIRST(&cc->nameq)) != NULL) { TAILQ_REMOVE(&cc->nameq, wn, entry); free(wn->name); free(wn); } free(cc->name); free(cc->label); free(cc->res_class); free(cc->res_name); free(cc); } void client_set_active(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct client_ctx *oldcc; if (cc->flags & CLIENT_HIDDEN) return; XInstallColormap(X_Dpy, cc->colormap); if ((cc->flags & CLIENT_INPUT) || (!(cc->flags & CLIENT_WM_TAKE_FOCUS))) { XSetInputFocus(X_Dpy, cc->win, RevertToPointerRoot, CurrentTime); } if (cc->flags & CLIENT_WM_TAKE_FOCUS) xu_send_clientmsg(cc->win, cwmh[WM_TAKE_FOCUS], Last_Event_Time); if ((oldcc = client_current(sc)) != NULL) { oldcc->flags &= ~CLIENT_ACTIVE; client_draw_border(oldcc); } /* If we're in the middle of cycling, don't change the order. */ if (!sc->cycling) client_mtf(cc); cc->flags |= CLIENT_ACTIVE; cc->flags &= ~CLIENT_URGENCY; client_draw_border(cc); conf_grab_mouse(cc->win); xu_ewmh_net_active_window(sc, cc->win); } void client_toggle_freeze(struct client_ctx *cc) { if (cc->flags & CLIENT_FULLSCREEN) return; cc->flags ^= CLIENT_FREEZE; xu_ewmh_set_net_wm_state(cc); } void client_toggle_hidden(struct client_ctx *cc) { cc->flags ^= CLIENT_HIDDEN; xu_ewmh_set_net_wm_state(cc); } void client_toggle_skip_pager(struct client_ctx *cc) { cc->flags ^= CLIENT_SKIP_PAGER; xu_ewmh_set_net_wm_state(cc); } void client_toggle_skip_taskbar(struct client_ctx *cc) { cc->flags ^= CLIENT_SKIP_TASKBAR; xu_ewmh_set_net_wm_state(cc); } void client_toggle_sticky(struct client_ctx *cc) { cc->flags ^= CLIENT_STICKY; xu_ewmh_set_net_wm_state(cc); } void client_toggle_fullscreen(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct geom area; if ((cc->flags & CLIENT_FREEZE) && !(cc->flags & CLIENT_FULLSCREEN)) return; if (cc->flags & CLIENT_FULLSCREEN) { if (!(cc->flags & CLIENT_IGNORE)) cc->bwidth = Conf.bwidth; cc->geom = cc->fullgeom; cc->flags &= ~(CLIENT_FULLSCREEN | CLIENT_FREEZE); goto resize; } cc->fullgeom = cc->geom; area = screen_area(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, 0); cc->bwidth = 0; cc->geom = area; cc->flags |= (CLIENT_FULLSCREEN | CLIENT_FREEZE); resize: client_resize(cc, 0); xu_ewmh_set_net_wm_state(cc); client_ptr_inbound(cc, 1); } void client_toggle_maximize(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct geom area; if (cc->flags & CLIENT_FREEZE) return; if ((cc->flags & CLIENT_MAXFLAGS) == CLIENT_MAXIMIZED) { cc->geom = cc->savegeom; cc->flags &= ~CLIENT_MAXIMIZED; goto resize; } if (!(cc->flags & CLIENT_VMAXIMIZED)) { cc->savegeom.h = cc->geom.h; cc->savegeom.y = cc->geom.y; } if (!(cc->flags & CLIENT_HMAXIMIZED)) { cc->savegeom.w = cc->geom.w; cc->savegeom.x = cc->geom.x; } area = screen_area(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, 1); cc->geom.x = area.x; cc->geom.y = area.y; cc->geom.w = area.w - (cc->bwidth * 2); cc->geom.h = area.h - (cc->bwidth * 2); cc->flags |= CLIENT_MAXIMIZED; resize: client_resize(cc, 0); xu_ewmh_set_net_wm_state(cc); client_ptr_inbound(cc, 1); } void client_toggle_vmaximize(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct geom area; if (cc->flags & CLIENT_FREEZE) return; if (cc->flags & CLIENT_VMAXIMIZED) { cc->geom.y = cc->savegeom.y; cc->geom.h = cc->savegeom.h; cc->flags &= ~CLIENT_VMAXIMIZED; goto resize; } cc->savegeom.y = cc->geom.y; cc->savegeom.h = cc->geom.h; area = screen_area(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, 1); cc->geom.y = area.y; cc->geom.h = area.h - (cc->bwidth * 2); cc->flags |= CLIENT_VMAXIMIZED; resize: client_resize(cc, 0); xu_ewmh_set_net_wm_state(cc); client_ptr_inbound(cc, 1); } void client_toggle_hmaximize(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct geom area; if (cc->flags & CLIENT_FREEZE) return; if (cc->flags & CLIENT_HMAXIMIZED) { cc->geom.x = cc->savegeom.x; cc->geom.w = cc->savegeom.w; cc->flags &= ~CLIENT_HMAXIMIZED; goto resize; } cc->savegeom.x = cc->geom.x; cc->savegeom.w = cc->geom.w; area = screen_area(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, 1); cc->geom.x = area.x; cc->geom.w = area.w - (cc->bwidth * 2); cc->flags |= CLIENT_HMAXIMIZED; resize: client_resize(cc, 0); xu_ewmh_set_net_wm_state(cc); client_ptr_inbound(cc, 1); } void client_resize(struct client_ctx *cc, int reset) { if (reset) { cc->flags &= ~CLIENT_MAXIMIZED; xu_ewmh_set_net_wm_state(cc); } client_draw_border(cc); XMoveResizeWindow(X_Dpy, cc->win, cc->geom.x, cc->geom.y, cc->geom.w, cc->geom.h); cc->dim.w = (cc->geom.w - cc->hint.basew) / cc->hint.incw; cc->dim.h = (cc->geom.h - cc->hint.baseh) / cc->hint.inch; client_config(cc); } void client_move(struct client_ctx *cc) { XMoveWindow(X_Dpy, cc->win, cc->geom.x, cc->geom.y); client_config(cc); } void client_lower(struct client_ctx *cc) { XLowerWindow(X_Dpy, cc->win); } void client_raise(struct client_ctx *cc) { XRaiseWindow(X_Dpy, cc->win); } void client_config(struct client_ctx *cc) { XConfigureEvent cn; (void)memset(&cn, 0, sizeof(cn)); cn.type = ConfigureNotify; cn.event = cc->win; cn.window = cc->win; cn.x = cc->geom.x; cn.y = cc->geom.y; cn.width = cc->geom.w; cn.height = cc->geom.h; cn.border_width = cc->bwidth; cn.above = None; cn.override_redirect = 0; XSendEvent(X_Dpy, cc->win, False, StructureNotifyMask, (XEvent *)&cn); } void client_ptr_inbound(struct client_ctx *cc, int getpos) { if (getpos) xu_ptr_get(cc->win, &cc->ptr.x, &cc->ptr.y); if (cc->ptr.x < 0) cc->ptr.x = 0; else if (cc->ptr.x > cc->geom.w - 1) cc->ptr.x = cc->geom.w - 1; if (cc->ptr.y < 0) cc->ptr.y = 0; else if (cc->ptr.y > cc->geom.h - 1) cc->ptr.y = cc->geom.h - 1; client_ptr_warp(cc); } void client_ptr_warp(struct client_ctx *cc) { xu_ptr_set(cc->win, cc->ptr.x, cc->ptr.y); } void client_ptr_save(struct client_ctx *cc) { int x, y; xu_ptr_get(cc->win, &x, &y); if (client_inbound(cc, x, y)) { cc->ptr.x = x; cc->ptr.y = y; } else { cc->ptr.x = cc->geom.w / 2; cc->ptr.y = cc->geom.h / 2; } } void client_hide(struct client_ctx *cc) { XUnmapWindow(X_Dpy, cc->win); if (cc->flags & CLIENT_ACTIVE) { cc->flags &= ~CLIENT_ACTIVE; xu_ewmh_net_active_window(cc->sc, None); } cc->flags |= CLIENT_HIDDEN; xu_set_wm_state(cc->win, IconicState); } void client_show(struct client_ctx *cc) { XMapRaised(X_Dpy, cc->win); cc->flags &= ~CLIENT_HIDDEN; xu_set_wm_state(cc->win, NormalState); client_draw_border(cc); } void client_urgency(struct client_ctx *cc) { if (!(cc->flags & CLIENT_ACTIVE)) cc->flags |= CLIENT_URGENCY; } void client_draw_border(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; unsigned long pixel; if (cc->flags & CLIENT_ACTIVE) switch (cc->flags & CLIENT_HIGHLIGHT) { case CLIENT_GROUP: pixel = sc->xftcolor[CWM_COLOR_BORDER_GROUP].pixel; break; case CLIENT_UNGROUP: pixel = sc->xftcolor[CWM_COLOR_BORDER_UNGROUP].pixel; break; default: pixel = sc->xftcolor[CWM_COLOR_BORDER_ACTIVE].pixel; break; } else pixel = sc->xftcolor[CWM_COLOR_BORDER_INACTIVE].pixel; if (cc->flags & CLIENT_URGENCY) pixel = sc->xftcolor[CWM_COLOR_BORDER_URGENCY].pixel; XSetWindowBorderWidth(X_Dpy, cc->win, (unsigned int)cc->bwidth); XSetWindowBorder(X_Dpy, cc->win, pixel); } static void client_class_hint(struct client_ctx *cc) { XClassHint ch; if (XGetClassHint(X_Dpy, cc->win, &ch)) { if (ch.res_class) { cc->res_class = xstrdup(ch.res_class); XFree(ch.res_class); } if (ch.res_name) { cc->res_name = xstrdup(ch.res_name); XFree(ch.res_name); } } } static void client_wm_protocols(struct client_ctx *cc) { Atom *p; int i, j; if (XGetWMProtocols(X_Dpy, cc->win, &p, &j)) { for (i = 0; i < j; i++) { if (p[i] == cwmh[WM_DELETE_WINDOW]) cc->flags |= CLIENT_WM_DELETE_WINDOW; else if (p[i] == cwmh[WM_TAKE_FOCUS]) cc->flags |= CLIENT_WM_TAKE_FOCUS; } XFree(p); } } void client_wm_hints(struct client_ctx *cc) { XWMHints *wmh; if ((wmh = XGetWMHints(X_Dpy, cc->win)) != NULL) { if ((wmh->flags & InputHint) && (wmh->input)) cc->flags |= CLIENT_INPUT; if ((wmh->flags & XUrgencyHint)) client_urgency(cc); if ((wmh->flags & StateHint)) cc->initial_state = wmh->initial_state; XFree(wmh); } } void client_close(struct client_ctx *cc) { if (cc->flags & CLIENT_WM_DELETE_WINDOW) xu_send_clientmsg(cc->win, cwmh[WM_DELETE_WINDOW], CurrentTime); else XKillClient(X_Dpy, cc->win); } void client_set_name(struct client_ctx *cc) { struct winname *wn, *wnnxt; int i = 0; free(cc->name); if (!xu_get_strprop(cc->win, ewmh[_NET_WM_NAME], &cc->name)) if (!xu_get_strprop(cc->win, XA_WM_NAME, &cc->name)) cc->name = xstrdup(""); TAILQ_FOREACH_SAFE(wn, &cc->nameq, entry, wnnxt) { if (strcmp(wn->name, cc->name) == 0) { TAILQ_REMOVE(&cc->nameq, wn, entry); free(wn->name); free(wn); } i++; } wn = xmalloc(sizeof(*wn)); wn->name = xstrdup(cc->name); TAILQ_INSERT_TAIL(&cc->nameq, wn, entry); /* Garbage collection. */ if ((i + 1) > Conf.nameqlen) { wn = TAILQ_FIRST(&cc->nameq); TAILQ_REMOVE(&cc->nameq, wn, entry); free(wn->name); free(wn); } } static void client_placement(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; if (cc->hint.flags & (USPosition | PPosition)) { if (cc->geom.x >= sc->view.w) cc->geom.x = sc->view.w - cc->bwidth - 1; if (cc->geom.x + cc->geom.w + cc->bwidth <= 0) cc->geom.x = -(cc->geom.w + cc->bwidth - 1); if (cc->geom.y >= sc->view.h) cc->geom.x = sc->view.h - cc->bwidth - 1; if (cc->geom.y + cc->geom.h + cc->bwidth <= 0) cc->geom.y = -(cc->geom.h + cc->bwidth - 1); if (cc->flags & CLIENT_IGNORE) { if (((cc->obwidth * 2) + cc->geom.x + cc->geom.w) == sc->view.w) cc->geom.x += cc->obwidth * 2; if (((cc->obwidth * 2) + cc->geom.y + cc->geom.h) == sc->view.h) cc->geom.y += cc->obwidth * 2; } } else { struct geom area; int xmouse, ymouse, xslack, yslack; xu_ptr_get(sc->rootwin, &xmouse, &ymouse); area = screen_area(sc, xmouse, ymouse, 1); xmouse = MAX(MAX(xmouse, area.x) - cc->geom.w / 2, area.x); ymouse = MAX(MAX(ymouse, area.y) - cc->geom.h / 2, area.y); xslack = area.x + area.w - cc->geom.w - cc->bwidth * 2; yslack = area.y + area.h - cc->geom.h - cc->bwidth * 2; if (xslack >= area.x) { cc->geom.x = MAX(MIN(xmouse, xslack), area.x); } else { cc->geom.x = area.x; cc->geom.w = area.x + area.w; } if (yslack >= area.y) { cc->geom.y = MAX(MIN(ymouse, yslack), area.y); } else { cc->geom.y = area.y; cc->geom.h = area.y + area.h; } } } void client_mtf(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; TAILQ_REMOVE(&sc->clientq, cc, entry); TAILQ_INSERT_HEAD(&sc->clientq, cc, entry); } void client_get_sizehints(struct client_ctx *cc) { long tmp; XSizeHints size; if (!XGetWMNormalHints(X_Dpy, cc->win, &size, &tmp)) size.flags = 0; cc->hint.flags = size.flags; if (size.flags & PBaseSize) { cc->hint.basew = size.base_width; cc->hint.baseh = size.base_height; } else if (size.flags & PMinSize) { cc->hint.basew = size.min_width; cc->hint.baseh = size.min_height; } if (size.flags & PMinSize) { cc->hint.minw = size.min_width; cc->hint.minh = size.min_height; } else if (size.flags & PBaseSize) { cc->hint.minw = size.base_width; cc->hint.minh = size.base_height; } if (size.flags & PMaxSize) { cc->hint.maxw = size.max_width; cc->hint.maxh = size.max_height; } if (size.flags & PResizeInc) { cc->hint.incw = size.width_inc; cc->hint.inch = size.height_inc; } cc->hint.incw = MAX(1, cc->hint.incw); cc->hint.inch = MAX(1, cc->hint.inch); cc->hint.minw = MAX(1, cc->hint.minw); cc->hint.minh = MAX(1, cc->hint.minh); if (size.flags & PAspect) { if (size.min_aspect.x > 0) cc->hint.mina = (float)size.min_aspect.y / size.min_aspect.x; if (size.max_aspect.y > 0) cc->hint.maxa = (float)size.max_aspect.x / size.max_aspect.y; } } void client_apply_sizehints(struct client_ctx *cc) { Bool baseismin; baseismin = (cc->hint.basew == cc->hint.minw) && (cc->hint.baseh == cc->hint.minh); /* temporarily remove base dimensions, ICCCM 4.1.2.3 */ if (!baseismin) { cc->geom.w -= cc->hint.basew; cc->geom.h -= cc->hint.baseh; } /* adjust for aspect limits */ if (cc->hint.mina && cc->hint.maxa) { if (cc->hint.maxa < (float)cc->geom.w / cc->geom.h) cc->geom.w = cc->geom.h * cc->hint.maxa; else if (cc->hint.mina < (float)cc->geom.h / cc->geom.w) cc->geom.h = cc->geom.w * cc->hint.mina; } /* remove base dimensions for increment */ if (baseismin) { cc->geom.w -= cc->hint.basew; cc->geom.h -= cc->hint.baseh; } /* adjust for increment value */ cc->geom.w -= cc->geom.w % cc->hint.incw; cc->geom.h -= cc->geom.h % cc->hint.inch; /* restore base dimensions */ cc->geom.w += cc->hint.basew; cc->geom.h += cc->hint.baseh; /* adjust for min width/height */ cc->geom.w = MAX(cc->geom.w, cc->hint.minw); cc->geom.h = MAX(cc->geom.h, cc->hint.minh); /* adjust for max width/height */ if (cc->hint.maxw) cc->geom.w = MIN(cc->geom.w, cc->hint.maxw); if (cc->hint.maxh) cc->geom.h = MIN(cc->geom.h, cc->hint.maxh); } static void client_mwm_hints(struct client_ctx *cc) { struct mwm_hints *mwmh; if (xu_get_prop(cc->win, cwmh[_MOTIF_WM_HINTS], cwmh[_MOTIF_WM_HINTS], MWM_HINTS_ELEMENTS, (unsigned char **)&mwmh) <= 0) return; if ((mwmh->flags & MWM_HINTS_DECORATIONS) && !(mwmh->decorations & MWM_DECOR_ALL)) { if (!(mwmh->decorations & MWM_DECOR_BORDER)) cc->bwidth = 0; } XFree(mwmh); } void client_transient(struct client_ctx *cc) { struct client_ctx *tc; Window trans; if (XGetTransientForHint(X_Dpy, cc->win, &trans)) { if ((tc = client_find(trans)) != NULL) { if (tc->flags & CLIENT_IGNORE) { cc->flags |= CLIENT_IGNORE; cc->bwidth = tc->bwidth; } } } } int client_inbound(struct client_ctx *cc, int x, int y) { return(x < cc->geom.w && x >= 0 && y < cc->geom.h && y >= 0); } int client_snapcalc(int n0, int n1, int e0, int e1, int snapdist) { int s0, s1; s0 = s1 = 0; if (abs(e0 - n0) <= snapdist) s0 = e0 - n0; if (abs(e1 - n1) <= snapdist) s1 = e1 - n1; /* possible to snap in both directions */ if (s0 != 0 && s1 != 0) if (abs(s0) < abs(s1)) return s0; else return s1; else if (s0 != 0) return s0; else if (s1 != 0) return s1; else return 0; } void client_htile(struct client_ctx *cc) { struct client_ctx *ci; struct screen_ctx *sc = cc->sc; struct geom area; int i, n, mh, x, w, h; i = n = 0; area = screen_area(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, 1); TAILQ_FOREACH(ci, &sc->clientq, entry) { if (ci->gc != cc->gc) continue; if (ci->flags & CLIENT_HIDDEN || ci->flags & CLIENT_IGNORE || (ci == cc) || ci->geom.x < area.x || ci->geom.x > (area.x + area.w) || ci->geom.y < area.y || ci->geom.y > (area.y + area.h)) continue; n++; } if (n == 0) return; if (cc->flags & CLIENT_VMAXIMIZED || cc->geom.h + (cc->bwidth * 2) >= area.h) return; cc->flags &= ~CLIENT_HMAXIMIZED; cc->geom.x = area.x; cc->geom.y = area.y; cc->geom.w = area.w - (cc->bwidth * 2); if (Conf.htile > 0) cc->geom.h = ((area.h - (cc->bwidth * 2)) * Conf.htile) / 100; client_resize(cc, 1); client_ptr_warp(cc); mh = cc->geom.h + (cc->bwidth * 2); x = area.x; w = area.w / n; h = area.h - mh; TAILQ_FOREACH(ci, &sc->clientq, entry) { if (ci->gc != cc->gc) continue; if (ci->flags & CLIENT_HIDDEN || ci->flags & CLIENT_IGNORE || (ci == cc) || ci->geom.x < area.x || ci->geom.x > (area.x + area.w) || ci->geom.y < area.y || ci->geom.y > (area.y + area.h)) continue; ci->bwidth = Conf.bwidth; ci->geom.x = x; ci->geom.y = area.y + mh; ci->geom.w = w - (ci->bwidth * 2); ci->geom.h = h - (ci->bwidth * 2); if (i + 1 == n) ci->geom.w = area.x + area.w - ci->geom.x - (ci->bwidth * 2); x += w; i++; client_resize(ci, 1); } } void client_vtile(struct client_ctx *cc) { struct client_ctx *ci; struct screen_ctx *sc = cc->sc; struct geom area; int i, n, mw, y, w, h; i = n = 0; area = screen_area(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, 1); TAILQ_FOREACH(ci, &sc->clientq, entry) { if (ci->gc != cc->gc) continue; if (ci->flags & CLIENT_HIDDEN || ci->flags & CLIENT_IGNORE || (ci == cc) || ci->geom.x < area.x || ci->geom.x > (area.x + area.w) || ci->geom.y < area.y || ci->geom.y > (area.y + area.h)) continue; n++; } if (n == 0) return; if (cc->flags & CLIENT_HMAXIMIZED || cc->geom.w + (cc->bwidth * 2) >= area.w) return; cc->flags &= ~CLIENT_VMAXIMIZED; cc->geom.x = area.x; cc->geom.y = area.y; if (Conf.vtile > 0) cc->geom.w = ((area.w - (cc->bwidth * 2)) * Conf.vtile) / 100; cc->geom.h = area.h - (cc->bwidth * 2); client_resize(cc, 1); client_ptr_warp(cc); mw = cc->geom.w + (cc->bwidth * 2); y = area.y; h = area.h / n; w = area.w - mw; TAILQ_FOREACH(ci, &sc->clientq, entry) { if (ci->gc != cc->gc) continue; if (ci->flags & CLIENT_HIDDEN || ci->flags & CLIENT_IGNORE || (ci == cc) || ci->geom.x < area.x || ci->geom.x > (area.x + area.w) || ci->geom.y < area.y || ci->geom.y > (area.y + area.h)) continue; ci->bwidth = Conf.bwidth; ci->geom.x = area.x + mw; ci->geom.y = y; ci->geom.w = w - (ci->bwidth * 2); ci->geom.h = h - (ci->bwidth * 2); if (i + 1 == n) ci->geom.h = area.y + area.h - ci->geom.y - (ci->bwidth * 2); y += h; i++; client_resize(ci, 1); } } cwm-7.1/conf.c000066400000000000000000000467521423325063300132570ustar00rootroot00000000000000/* * calmwm - the calm window manager * * Copyright (c) 2004 Marius Aamodt Eriksen * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD$ */ #include #include "queue.h" #include #include #include #include #include #include #include #include #include "calmwm.h" static const char *conf_bind_mask(const char *, unsigned int *); static void conf_unbind_key(struct conf *, struct bind_ctx *); static void conf_unbind_mouse(struct conf *, struct bind_ctx *); static const struct { int num; const char *name; } group_binds[] = { { 0, "nogroup" }, { 1, "one" }, { 2, "two" }, { 3, "three" }, { 4, "four" }, { 5, "five" }, { 6, "six" }, { 7, "seven" }, { 8, "eight" }, { 9, "nine" }, }; static int cursor_binds[] = { XC_left_ptr, /* CF_NORMAL */ XC_fleur, /* CF_MOVE */ XC_bottom_right_corner, /* CF_RESIZE */ XC_question_arrow, /* CF_QUESTION */ }; static const char *color_binds[] = { "#CCCCCC", /* CWM_COLOR_BORDER_ACTIVE */ "#666666", /* CWM_COLOR_BORDER_INACTIVE */ "#FC8814", /* CWM_COLOR_BORDER_URGENCY */ "blue", /* CWM_COLOR_BORDER_GROUP */ "red", /* CWM_COLOR_BORDER_UNGROUP */ "black", /* CWM_COLOR_MENU_FG */ "white", /* CWM_COLOR_MENU_BG */ "black", /* CWM_COLOR_MENU_FONT */ "", /* CWM_COLOR_MENU_FONT_SEL */ }; static const struct { const char *tag; void (*handler)(void *, struct cargs *); enum context context; int flag; } name_to_func[] = { #define FUNC_CC(t, h, n) \ #t, kbfunc_ ## h, CWM_CONTEXT_CC, n #define FUNC_SC(t, h, n) \ #t, kbfunc_ ## h, CWM_CONTEXT_SC, n { FUNC_CC(window-lower, client_lower, 0) }, { FUNC_CC(window-raise, client_raise, 0) }, { FUNC_CC(window-hide, client_hide, 0) }, { FUNC_CC(window-close, client_close, 0) }, { FUNC_CC(window-delete, client_close, 0) }, { FUNC_CC(window-htile, client_htile, 0) }, { FUNC_CC(window-vtile, client_vtile, 0) }, { FUNC_CC(window-stick, client_toggle_sticky, 0) }, { FUNC_CC(window-fullscreen, client_toggle_fullscreen, 0) }, { FUNC_CC(window-maximize, client_toggle_maximize, 0) }, { FUNC_CC(window-vmaximize, client_toggle_vmaximize, 0) }, { FUNC_CC(window-hmaximize, client_toggle_hmaximize, 0) }, { FUNC_CC(window-freeze, client_toggle_freeze, 0) }, { FUNC_CC(window-group, client_toggle_group, 0) }, { FUNC_CC(window-movetogroup-1, client_movetogroup, 1) }, { FUNC_CC(window-movetogroup-2, client_movetogroup, 2) }, { FUNC_CC(window-movetogroup-3, client_movetogroup, 3) }, { FUNC_CC(window-movetogroup-4, client_movetogroup, 4) }, { FUNC_CC(window-movetogroup-5, client_movetogroup, 5) }, { FUNC_CC(window-movetogroup-6, client_movetogroup, 6) }, { FUNC_CC(window-movetogroup-7, client_movetogroup, 7) }, { FUNC_CC(window-movetogroup-8, client_movetogroup, 8) }, { FUNC_CC(window-movetogroup-9, client_movetogroup, 9) }, { FUNC_CC(window-snap-up, client_snap, (CWM_UP)) }, { FUNC_CC(window-snap-down, client_snap, (CWM_DOWN)) }, { FUNC_CC(window-snap-right, client_snap, (CWM_RIGHT)) }, { FUNC_CC(window-snap-left, client_snap, (CWM_LEFT)) }, { FUNC_CC(window-snap-up-right, client_snap, (CWM_UP_RIGHT)) }, { FUNC_CC(window-snap-up-left, client_snap, (CWM_UP_LEFT)) }, { FUNC_CC(window-snap-down-right, client_snap, (CWM_DOWN_RIGHT)) }, { FUNC_CC(window-snap-down-left, client_snap, (CWM_DOWN_LEFT)) }, { FUNC_CC(window-move, client_move, 0) }, { FUNC_CC(window-move-up, client_move, (CWM_UP)) }, { FUNC_CC(window-move-down, client_move, (CWM_DOWN)) }, { FUNC_CC(window-move-right, client_move, (CWM_RIGHT)) }, { FUNC_CC(window-move-left, client_move, (CWM_LEFT)) }, { FUNC_CC(window-move-up-big, client_move, (CWM_UP_BIG)) }, { FUNC_CC(window-move-down-big, client_move, (CWM_DOWN_BIG)) }, { FUNC_CC(window-move-right-big, client_move, (CWM_RIGHT_BIG)) }, { FUNC_CC(window-move-left-big, client_move, (CWM_LEFT_BIG)) }, { FUNC_CC(window-resize, client_resize, 0) }, { FUNC_CC(window-resize-up, client_resize, (CWM_UP)) }, { FUNC_CC(window-resize-down, client_resize, (CWM_DOWN)) }, { FUNC_CC(window-resize-right, client_resize, (CWM_RIGHT)) }, { FUNC_CC(window-resize-left, client_resize, (CWM_LEFT)) }, { FUNC_CC(window-resize-up-big, client_resize, (CWM_UP_BIG)) }, { FUNC_CC(window-resize-down-big, client_resize, (CWM_DOWN_BIG)) }, { FUNC_CC(window-resize-right-big, client_resize, (CWM_RIGHT_BIG)) }, { FUNC_CC(window-resize-left-big, client_resize, (CWM_LEFT_BIG)) }, { FUNC_CC(window-menu-label, client_menu_label, 0) }, { FUNC_SC(window-cycle, client_cycle, (CWM_CYCLE_FORWARD)) }, { FUNC_SC(window-rcycle, client_cycle, (CWM_CYCLE_REVERSE)) }, { FUNC_SC(window-cycle-ingroup, client_cycle, (CWM_CYCLE_FORWARD | CWM_CYCLE_INGROUP)) }, { FUNC_SC(window-rcycle-ingroup, client_cycle, (CWM_CYCLE_REVERSE | CWM_CYCLE_INGROUP)) }, { FUNC_SC(group-cycle, group_cycle, (CWM_CYCLE_FORWARD)) }, { FUNC_SC(group-rcycle, group_cycle, (CWM_CYCLE_REVERSE)) }, { FUNC_SC(group-last, group_last, 0) }, { FUNC_SC(group-toggle-all, group_toggle_all, 0) }, { FUNC_SC(group-toggle-1, group_toggle, 1) }, { FUNC_SC(group-toggle-2, group_toggle, 2) }, { FUNC_SC(group-toggle-3, group_toggle, 3) }, { FUNC_SC(group-toggle-4, group_toggle, 4) }, { FUNC_SC(group-toggle-5, group_toggle, 5) }, { FUNC_SC(group-toggle-6, group_toggle, 6) }, { FUNC_SC(group-toggle-7, group_toggle, 7) }, { FUNC_SC(group-toggle-8, group_toggle, 8) }, { FUNC_SC(group-toggle-9, group_toggle, 9) }, { FUNC_SC(group-only-1, group_only, 1) }, { FUNC_SC(group-only-2, group_only, 2) }, { FUNC_SC(group-only-3, group_only, 3) }, { FUNC_SC(group-only-4, group_only, 4) }, { FUNC_SC(group-only-5, group_only, 5) }, { FUNC_SC(group-only-6, group_only, 6) }, { FUNC_SC(group-only-7, group_only, 7) }, { FUNC_SC(group-only-8, group_only, 8) }, { FUNC_SC(group-only-9, group_only, 9) }, { FUNC_SC(group-close-1, group_close, 1) }, { FUNC_SC(group-close-2, group_close, 2) }, { FUNC_SC(group-close-3, group_close, 3) }, { FUNC_SC(group-close-4, group_close, 4) }, { FUNC_SC(group-close-5, group_close, 5) }, { FUNC_SC(group-close-6, group_close, 6) }, { FUNC_SC(group-close-7, group_close, 7) }, { FUNC_SC(group-close-8, group_close, 8) }, { FUNC_SC(group-close-9, group_close, 9) }, { FUNC_SC(pointer-move-up, ptrmove, (CWM_UP)) }, { FUNC_SC(pointer-move-down, ptrmove, (CWM_DOWN)) }, { FUNC_SC(pointer-move-left, ptrmove, (CWM_LEFT)) }, { FUNC_SC(pointer-move-right, ptrmove, (CWM_RIGHT)) }, { FUNC_SC(pointer-move-up-big, ptrmove, (CWM_UP_BIG)) }, { FUNC_SC(pointer-move-down-big, ptrmove, (CWM_DOWN_BIG)) }, { FUNC_SC(pointer-move-left-big, ptrmove, (CWM_LEFT_BIG)) }, { FUNC_SC(pointer-move-right-big, ptrmove, (CWM_RIGHT_BIG)) }, { FUNC_SC(menu-cmd, menu_cmd, 0) }, { FUNC_SC(menu-group, menu_group, 0) }, { FUNC_SC(menu-ssh, menu_ssh, 0) }, { FUNC_SC(menu-window, menu_client, CWM_MENU_WINDOW_ALL) }, { FUNC_SC(menu-window-hidden, menu_client, CWM_MENU_WINDOW_HIDDEN) }, { FUNC_SC(menu-exec, menu_exec, 0) }, { FUNC_SC(menu-exec-wm, menu_wm, 0) }, { FUNC_SC(terminal, exec_term, 0) }, { FUNC_SC(lock, exec_lock, 0) }, { FUNC_SC(restart, cwm_status, CWM_EXEC_WM) }, { FUNC_SC(quit, cwm_status, CWM_QUIT) }, }; static unsigned int ignore_mods[] = { 0, LockMask, Mod2Mask, Mod2Mask | LockMask }; static const struct { const char ch; int mask; } bind_mods[] = { { 'S', ShiftMask }, { 'C', ControlMask }, { 'M', Mod1Mask }, { '4', Mod4Mask }, { '5', Mod5Mask }, }; static const struct { const char *key; const char *func; } key_binds[] = { { "CM-Return", "terminal" }, { "CM-Delete", "lock" }, { "M-question", "menu-exec" }, { "CM-w", "menu-exec-wm" }, { "M-period", "menu-ssh" }, { "M-Return", "window-hide" }, { "M-Down", "window-lower" }, { "M-Up", "window-raise" }, { "M-slash", "menu-window" }, { "C-slash", "menu-cmd" }, { "M-Tab", "window-cycle" }, { "MS-Tab", "window-rcycle" }, { "CM-n", "window-menu-label" }, { "CM-x", "window-close" }, { "CM-a", "group-toggle-all" }, { "CM-0", "group-toggle-all" }, { "CM-1", "group-toggle-1" }, { "CM-2", "group-toggle-2" }, { "CM-3", "group-toggle-3" }, { "CM-4", "group-toggle-4" }, { "CM-5", "group-toggle-5" }, { "CM-6", "group-toggle-6" }, { "CM-7", "group-toggle-7" }, { "CM-8", "group-toggle-8" }, { "CM-9", "group-toggle-9" }, { "M-Right", "group-cycle" }, { "M-Left", "group-rcycle" }, { "CM-g", "window-group" }, { "CM-f", "window-fullscreen" }, { "CM-m", "window-maximize" }, { "CM-s", "window-stick" }, { "CM-equal", "window-vmaximize" }, { "CMS-equal", "window-hmaximize" }, { "CMS-f", "window-freeze" }, { "CMS-r", "restart" }, { "CMS-q", "quit" }, { "M-h", "window-move-left" }, { "M-j", "window-move-down" }, { "M-k", "window-move-up" }, { "M-l", "window-move-right" }, { "MS-h", "window-move-left-big" }, { "MS-j", "window-move-down-big" }, { "MS-k", "window-move-up-big" }, { "MS-l", "window-move-right-big" }, { "CM-h", "window-resize-left" }, { "CM-j", "window-resize-down" }, { "CM-k", "window-resize-up" }, { "CM-l", "window-resize-right" }, { "CMS-h", "window-resize-left-big" }, { "CMS-j", "window-resize-down-big" }, { "CMS-k", "window-resize-up-big" }, { "CMS-l", "window-resize-right-big" }, }, mouse_binds[] = { { "1", "menu-window" }, { "2", "menu-group" }, { "3", "menu-cmd" }, { "M-1", "window-move" }, { "CM-1", "window-group" }, { "M-2", "window-resize" }, { "M-3", "window-lower" }, { "CMS-3", "window-hide" }, }; void conf_init(struct conf *c) { const char *home; struct passwd *pw; unsigned int i; c->stickygroups = 0; c->bwidth = 1; c->mamount = 1; c->htile = 50; c->vtile = 50; c->snapdist = 0; c->ngroups = 0; c->nameqlen = 5; TAILQ_INIT(&c->ignoreq); TAILQ_INIT(&c->autogroupq); TAILQ_INIT(&c->keybindq); TAILQ_INIT(&c->mousebindq); TAILQ_INIT(&c->cmdq); TAILQ_INIT(&c->wmq); for (i = 0; i < nitems(key_binds); i++) conf_bind_key(c, key_binds[i].key, key_binds[i].func); for (i = 0; i < nitems(mouse_binds); i++) conf_bind_mouse(c, mouse_binds[i].key, mouse_binds[i].func); for (i = 0; i < nitems(color_binds); i++) c->color[i] = xstrdup(color_binds[i]); conf_cmd_add(c, "lock", "xlock"); conf_cmd_add(c, "term", "xterm"); conf_wm_add(c, "cwm", "cwm"); c->font = xstrdup("sans-serif:pixelsize=14:bold"); c->wmname = xstrdup("CWM"); home = getenv("HOME"); if ((home == NULL) || (*home == '\0')) { pw = getpwuid(getuid()); if (pw != NULL && pw->pw_dir != NULL && *pw->pw_dir != '\0') home = pw->pw_dir; else home = "/"; } xasprintf(&c->conf_file, "%s/%s", home, ".cwmrc"); xasprintf(&c->known_hosts, "%s/%s", home, ".ssh/known_hosts"); } void conf_clear(struct conf *c) { struct autogroup *ag; struct bind_ctx *kb, *mb; struct winname *wn; struct cmd_ctx *cmd, *wm; int i; while ((cmd = TAILQ_FIRST(&c->cmdq)) != NULL) { TAILQ_REMOVE(&c->cmdq, cmd, entry); free(cmd->name); free(cmd->path); free(cmd); } while ((wm = TAILQ_FIRST(&c->wmq)) != NULL) { TAILQ_REMOVE(&c->wmq, wm, entry); free(wm->name); free(wm->path); free(wm); } while ((kb = TAILQ_FIRST(&c->keybindq)) != NULL) { TAILQ_REMOVE(&c->keybindq, kb, entry); free(kb); } while ((ag = TAILQ_FIRST(&c->autogroupq)) != NULL) { TAILQ_REMOVE(&c->autogroupq, ag, entry); free(ag->class); free(ag->name); free(ag); } while ((wn = TAILQ_FIRST(&c->ignoreq)) != NULL) { TAILQ_REMOVE(&c->ignoreq, wn, entry); free(wn->name); free(wn); } while ((mb = TAILQ_FIRST(&c->mousebindq)) != NULL) { TAILQ_REMOVE(&c->mousebindq, mb, entry); free(mb); } for (i = 0; i < CWM_COLOR_NITEMS; i++) free(c->color[i]); free(c->conf_file); free(c->known_hosts); free(c->font); free(c->wmname); } void conf_cmd_add(struct conf *c, const char *name, const char *path) { struct cmd_ctx *cmd, *cmdtmp = NULL, *cmdnxt; cmd = xmalloc(sizeof(*cmd)); cmd->name = xstrdup(name); cmd->path = xstrdup(path); TAILQ_FOREACH_SAFE(cmdtmp, &c->cmdq, entry, cmdnxt) { if (strcmp(cmdtmp->name, name) == 0) { TAILQ_REMOVE(&c->cmdq, cmdtmp, entry); free(cmdtmp->name); free(cmdtmp->path); free(cmdtmp); } } TAILQ_INSERT_TAIL(&c->cmdq, cmd, entry); } void conf_wm_add(struct conf *c, const char *name, const char *path) { struct cmd_ctx *wm, *wmtmp = NULL, *wmnxt; wm = xmalloc(sizeof(*wm)); wm->name = xstrdup(name); wm->path = xstrdup(path); TAILQ_FOREACH_SAFE(wmtmp, &c->cmdq, entry, wmnxt) { if (strcmp(wmtmp->name, name) == 0) { TAILQ_REMOVE(&c->wmq, wmtmp, entry); free(wmtmp->name); free(wmtmp->path); free(wmtmp); } } TAILQ_INSERT_TAIL(&c->wmq, wm, entry); } void conf_autogroup(struct conf *c, int num, const char *name, const char *class) { struct autogroup *ag; char *p; ag = xmalloc(sizeof(*ag)); if ((p = strchr(class, ',')) == NULL) { if (name == NULL) ag->name = NULL; else ag->name = xstrdup(name); ag->class = xstrdup(class); } else { *(p++) = '\0'; if (name == NULL) ag->name = xstrdup(class); else ag->name = xstrdup(name); ag->class = xstrdup(p); } ag->num = num; TAILQ_INSERT_TAIL(&c->autogroupq, ag, entry); } void conf_ignore(struct conf *c, const char *name) { struct winname *wn; wn = xmalloc(sizeof(*wn)); wn->name = xstrdup(name); TAILQ_INSERT_TAIL(&c->ignoreq, wn, entry); } void conf_cursor(struct conf *c) { unsigned int i; for (i = 0; i < nitems(cursor_binds); i++) c->cursor[i] = XCreateFontCursor(X_Dpy, cursor_binds[i]); } void conf_client(struct client_ctx *cc) { struct winname *wn; TAILQ_FOREACH(wn, &Conf.ignoreq, entry) { if (strncasecmp(wn->name, cc->name, strlen(wn->name)) == 0) { cc->flags |= CLIENT_IGNORE; break; } } } void conf_screen(struct screen_ctx *sc) { unsigned int i; XftColor xc; sc->gap = Conf.gap; sc->snapdist = Conf.snapdist; sc->xftfont = XftFontOpenXlfd(X_Dpy, sc->which, Conf.font); if (sc->xftfont == NULL) { sc->xftfont = XftFontOpenName(X_Dpy, sc->which, Conf.font); if (sc->xftfont == NULL) errx(1, "%s: XftFontOpenName: %s", __func__, Conf.font); } for (i = 0; i < nitems(color_binds); i++) { if (i == CWM_COLOR_MENU_FONT_SEL && *Conf.color[i] == '\0') { xu_xorcolor(sc->xftcolor[CWM_COLOR_MENU_BG], sc->xftcolor[CWM_COLOR_MENU_FG], &xc); xu_xorcolor(sc->xftcolor[CWM_COLOR_MENU_FONT], xc, &xc); if (!XftColorAllocValue(X_Dpy, sc->visual, sc->colormap, &xc.color, &sc->xftcolor[CWM_COLOR_MENU_FONT_SEL])) warnx("XftColorAllocValue: %s", Conf.color[i]); break; } if (!XftColorAllocName(X_Dpy, sc->visual, sc->colormap, Conf.color[i], &sc->xftcolor[i])) { warnx("XftColorAllocName: %s", Conf.color[i]); XftColorAllocName(X_Dpy, sc->visual, sc->colormap, color_binds[i], &sc->xftcolor[i]); } } conf_grab_kbd(sc->rootwin); } void conf_group(struct screen_ctx *sc) { unsigned int i; for (i = 0; i < nitems(group_binds); i++) { group_init(sc, group_binds[i].num, group_binds[i].name); Conf.ngroups++; } } static const char * conf_bind_mask(const char *name, unsigned int *mask) { char *dash; const char *ch; unsigned int i; *mask = 0; if ((dash = strchr(name, '-')) == NULL) return name; for (i = 0; i < nitems(bind_mods); i++) { if ((ch = strchr(name, bind_mods[i].ch)) != NULL && ch < dash) *mask |= bind_mods[i].mask; } /* Skip past modifiers. */ return (dash + 1); } int conf_bind_key(struct conf *c, const char *bind, const char *cmd) { struct bind_ctx *kb; struct cargs *cargs; const char *key; unsigned int i; if ((strcmp(bind, "all") == 0) && (cmd == NULL)) { conf_unbind_key(c, NULL); return 1; } kb = xmalloc(sizeof(*kb)); key = conf_bind_mask(bind, &kb->modmask); kb->press.keysym = XStringToKeysym(key); if (kb->press.keysym == NoSymbol) { warnx("unknown symbol: %s", key); free(kb); return 0; } conf_unbind_key(c, kb); if (cmd == NULL) { free(kb); return 1; } cargs = xcalloc(1, sizeof(*cargs)); for (i = 0; i < nitems(name_to_func); i++) { if (strcmp(name_to_func[i].tag, cmd) != 0) continue; kb->callback = name_to_func[i].handler; kb->context = name_to_func[i].context; cargs->flag = name_to_func[i].flag; goto out; } kb->callback = kbfunc_exec_cmd; kb->context = CWM_CONTEXT_NONE; cargs->flag = 0; cargs->cmd = xstrdup(cmd); out: kb->cargs = cargs; TAILQ_INSERT_TAIL(&c->keybindq, kb, entry); return 1; } static void conf_unbind_key(struct conf *c, struct bind_ctx *unbind) { struct bind_ctx *key = NULL, *keynxt; TAILQ_FOREACH_SAFE(key, &c->keybindq, entry, keynxt) { if ((unbind == NULL) || ((key->modmask == unbind->modmask) && (key->press.keysym == unbind->press.keysym))) { TAILQ_REMOVE(&c->keybindq, key, entry); free(key->cargs->cmd); free(key->cargs); free(key); } } } int conf_bind_mouse(struct conf *c, const char *bind, const char *cmd) { struct bind_ctx *mb; struct cargs *cargs; const char *button, *errstr; unsigned int i; if ((strcmp(bind, "all") == 0) && (cmd == NULL)) { conf_unbind_mouse(c, NULL); return 1; } mb = xmalloc(sizeof(*mb)); button = conf_bind_mask(bind, &mb->modmask); mb->press.button = strtonum(button, Button1, Button5, &errstr); if (errstr) { warnx("button number is %s: %s", errstr, button); free(mb); return 0; } conf_unbind_mouse(c, mb); if (cmd == NULL) { free(mb); return 1; } cargs = xcalloc(1, sizeof(*cargs)); for (i = 0; i < nitems(name_to_func); i++) { if (strcmp(name_to_func[i].tag, cmd) != 0) continue; mb->callback = name_to_func[i].handler; mb->context = name_to_func[i].context; cargs->flag = name_to_func[i].flag; goto out; } mb->callback = kbfunc_exec_cmd; mb->context = CWM_CONTEXT_NONE; cargs->flag = 0; cargs->cmd = xstrdup(cmd); out: mb->cargs = cargs; TAILQ_INSERT_TAIL(&c->mousebindq, mb, entry); return 1; } static void conf_unbind_mouse(struct conf *c, struct bind_ctx *unbind) { struct bind_ctx *mb = NULL, *mbnxt; TAILQ_FOREACH_SAFE(mb, &c->mousebindq, entry, mbnxt) { if ((unbind == NULL) || ((mb->modmask == unbind->modmask) && (mb->press.button == unbind->press.button))) { TAILQ_REMOVE(&c->mousebindq, mb, entry); free(mb->cargs->cmd); free(mb->cargs); free(mb); } } } void conf_grab_kbd(Window win) { struct bind_ctx *kb; KeyCode kc; unsigned int i; XUngrabKey(X_Dpy, AnyKey, AnyModifier, win); TAILQ_FOREACH(kb, &Conf.keybindq, entry) { kc = XKeysymToKeycode(X_Dpy, kb->press.keysym); if (kc == 0) continue; if ((XkbKeycodeToKeysym(X_Dpy, kc, 0, 0) != kb->press.keysym) && (XkbKeycodeToKeysym(X_Dpy, kc, 0, 1) == kb->press.keysym)) kb->modmask |= ShiftMask; for (i = 0; i < nitems(ignore_mods); i++) XGrabKey(X_Dpy, kc, (kb->modmask | ignore_mods[i]), win, True, GrabModeAsync, GrabModeAsync); } } void conf_grab_mouse(Window win) { struct bind_ctx *mb; unsigned int i; XUngrabButton(X_Dpy, AnyButton, AnyModifier, win); TAILQ_FOREACH(mb, &Conf.mousebindq, entry) { if (mb->context != CWM_CONTEXT_CC) continue; for (i = 0; i < nitems(ignore_mods); i++) { XGrabButton(X_Dpy, mb->press.button, (mb->modmask | ignore_mods[i]), win, False, BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); } } } cwm-7.1/cwm.1000066400000000000000000000155731423325063300130330ustar00rootroot00000000000000.\" $OpenBSD$ .\" .\" Copyright (c) 2004,2005 Marius Aamodt Eriksen .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate$ .Dt CWM 1 .Os .Sh NAME .Nm cwm .Nd a lightweight and efficient window manager for X11 .Sh SYNOPSIS .\" For a program: program [-abc] file ... .Nm cwm .Op Fl nv .Op Fl c Ar file .Op Fl d Ar display .Sh DESCRIPTION .Nm is a window manager for X11 which contains many features that concentrate on the efficiency and transparency of window management, while maintaining the simplest and most pleasant aesthetic. .Pp The options are as follows: .Bl -tag -width Ds .It Fl c Ar file Specify an alternative configuration file. By default, .Nm loads .Pa ~/.cwmrc , if present. Any error messages from lines in the configuration file will be sent to .Em stderr ; however, .Nm will continue to process the rest of the configuration file. .It Fl d Ar display Specify the display to use. .It Fl n Configtest mode. Only check the configuration file for validity. .It Fl v Verbose mode. Multiple .Fl v options increase the verbosity. .El .Pp .Nm actions are initiated either via key or mouse bindings. The following notations are used throughout this page: .Pp .Bl -tag -width Ds -offset indent -compact .It Ic C Control key. .It Ic M Meta key. .It Ic S Shift key. .It Ic 4 Mod4 (windows) key. .It Ic M1 Left mouse button. .It Ic M2 Middle mouse button. .It Ic M3 Right mouse button. .El .Pp The default key bindings are: .Pp .Bl -tag -width "CM-EscapeXXXXX" -offset indent -compact .It Ic CM-Return Spawn a new terminal. .It Ic CM-Delete Lock the screen. .It Ic M-Return Hide current window. .It Ic M-Down Lower current window. .It Ic M-Up Raise current window. .It Ic M-slash Search for windows. .It Ic C-slash Search for applications. .It Ic CM-n Label current window. .It Ic M-Tab Cycle through currently visible windows. .It Ic MS-Tab Reverse cycle through currently visible windows. .It Ic CM-x Close current window. .It Ic CM-[n] Toggle visibility of group n, where n is 1-9. .It Ic CM-a Toggle visibility of all groups. .It Ic CM-g Toggle group membership of current window. .It Ic M-Right Cycle through active groups. .It Ic M-Left Reverse cycle through active groups. .It Ic CMS-f Toggle freezing geometry of current window. .It Ic CM-s Toggle stickiness of current window. .It Ic CM-f Toggle full-screen mode of current window. .It Ic CM-m Toggle maximization of current window. .It Ic CM-equal Toggle vertical maximization of current window. .It Ic CMS-equal Toggle horizontal maximization of current window. .It Ic M-[hjkl] Move window by a small amount. .It Ic MS-[hjkl] Move window by a large amount; see .Xr cwmrc 5 . .It Ic CM-[hjkl] Resize window by a small amount. .It Ic CMS-[hjkl] Resize window by a large amount; see .Xr cwmrc 5 . .It Ic M-question Spawn .Dq exec program dialog. .It Ic M-period Spawn .Dq ssh to dialog. This parses .Pa $HOME/.ssh/known_hosts to provide host auto-completion. .Xr ssh 1 will be executed via the configured terminal emulator. .It Ic CM-w Spawn .Dq exec WindowManager menu, allowing a switch to another window manager. .It Ic CMS-r Restart. .It Ic CMS-q Quit. .El .Pp The default mouse bindings are: .Pp .Bl -tag -width "CM-EscapeXXXXX" -offset indent -compact .It Ic M-M1 Move current window. .It Ic CM-M1 Toggle group membership of current window. .It Ic M-M2 Resize current window .It Ic M-M3 Lower current window. .It Ic CMS-M3 Hide current window. .El .Pp The following key bindings may be used to navigate search and exec dialogs: .Pp .Bl -tag -width "[Down] or C-s or M-j" -offset indent -compact .It Ic [Return] Select item. .It Ic [Down], C-s No or Ic M-j Next item. .It Ic [Up], C-r No or Ic M-k Previous item. .It Ic [Backspace] No or Ic C-h Backspace. .It Ic C-u Clear input. .It Ic C-a List all available items. .It Ic [Esc] Cancel. .El .Pp .Nm rereads its configuration file when it receives a hangup signal, .Dv SIGHUP , by executing itself with the name and arguments with which it was started. This is equivalent to the .Ar restart function. .Sh SEARCH .Nm features the ability to search for windows by their current title, old titles, and by their label. The priority for the search results are: label, current title, old titles in reverse order, and finally window class name. .Nm keeps a history of the 5 previous titles of a window. .Pp When searching, the leftmost character of the result list may show a flag: .Pp .Bl -tag -width Ds -offset indent -compact .It ! Window is currently focused. .It & Window is hidden. .El .Sh APPLICATIONS .Nm manages a list of applications defined with the .Cm command configuration option. .Sh GROUPS .Nm has the ability to group windows together, and use the groups to perform operations on the entire group instead of just one window. Together with the .Pa sticky option, this can be used to emulate virtual desktops. .Pp To edit groups, use the group selection commands to toggle membership of a group. A blue border will be shown briefly on windows added to the current group, and a red border will be shown on those just removed. .Sh MENUS Menus are recalled by clicking the mouse on the root window: .Pp .Bl -tag -width Ds -offset indent -compact .It Ic M1 Show list of currently defined windows. Selecting an item will warp to that window, unhiding it if necessary. .It Ic M2 Show list of currently defined groups. Selecting an item will hide/unhide that group. .It Ic M3 Show list of applications as defined in the configuration file. Selecting an item will spawn that application. .El .Sh ENVIRONMENT .Bl -tag -width "DISPLAYXXX" -compact .It DISPLAY .Nm starts on this display unless the .Fl d option is given. .El .Sh FILES .Bl -tag -width "~/.cwmrcXX" -compact .It Pa ~/.cwmrc Default .Nm configuration file. .El .Sh SEE ALSO .Xr cwmrc 5 .Sh HISTORY .Nm was originally inspired by evilwm, but was rewritten from scratch due to limitations in the evilwm codebase. The from-scratch rewrite borrowed some code from 9wm, however that code has since been removed or rewritten. .Pp .Nm first appeared in .Ox 4.2 . .Sh AUTHORS .An -nosplit .Nm was developed by .An Marius Aamodt Eriksen Aq marius@monkey.org with contributions from .An Andy Adamson Aq dros@monkey.org , .An Niels Provos Aq provos@monkey.org , and .An Antti Nyk\(:anen Aq aon@iki.fi . Ideas, discussion with many others. cwm-7.1/cwm.pub000066400000000000000000000001601423325063300134430ustar00rootroot00000000000000untrusted comment: portable cwm release key public key RWTdOib0PoIM0pmDAPnV2S9/AMRqTOVfTY/KAkFemdH13cqBDHdduTas cwm-7.1/cwmrc.5000066400000000000000000000266531423325063300133650ustar00rootroot00000000000000.\" $OpenBSD$ .\" .\" Copyright (c) 2004,2005 Marius Aamodt Eriksen .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate$ .Dt CWMRC 5 .Os .Sh NAME .Nm cwmrc .Nd calm window manager configuration file .Sh DESCRIPTION This manual page describes the .Xr cwm 1 configuration file. .Pp The current line can be extended over multiple lines using a backslash .Pq Sq \e . Comments can be put anywhere in the file using a hash mark .Pq Sq # , and extend to the end of the current line. Care should be taken when commenting out multi-line text: the comment is effective until the end of the entire block. .Pp Arguments containing whitespace should be surrounded by double quotes .Pq \&" . .Pp The following options are accepted: .Bl -tag -width Ds .It Ic autogroup Ar group Oo Ar windowname , Oc Ns Ar windowclass Automatically add new windows to .Ar group if their class property matches .Ar windowclass , or if their name and class properties match .Ar windowname and .Ar windowclass , respectively. The more specific last match wins. .Ar group is a number between 0 and 9. If .Ar group is 0, matching windows will not be added to any group; this may be used to override .Dq sticky group mode . .Pp The name and class values, respectively, for existing windows are both set in the WM_CLASS property and may be obtained using .Xr xprop 1 . .It Ic bind-key Ar key function Bind or rebind key .Ar key to .Ar function . The modifier keys come first, followed by a .Sq - , then a keysym name, taken from .Pa /usr/X11R6/include/X11/keysymdef.h . .Pp The following modifiers are recognised: .Pp .Bl -tag -width Ds -offset indent -compact .It Ic C Control key. .It Ic M Meta key. .It Ic S Shift key. .It Ic 4 Mod4 (windows) key. .It Ic 5 Mod5 (AltGr) key. .El .Pp The .Ar function may either be one from the .Sx BIND FUNCTION LIST (see below) or the command line that is to be executed. .It Ic bind-mouse Ar button function Bind or rebind button .Ar button to .Ar function . The modifier keys come first, followed by a .Sq - , then the button number. .Pp The same modifiers are recognised as for .Ar key in .Nm bind-key . .Pp The following buttons are recognised: .Pp .Bl -tag -width Ds -offset indent -compact .It Ic 1 Left mouse button. .It Ic 2 Middle mouse button. .It Ic 3 Right mouse button. .It Ic 4 Scroll up mouse button. .It Ic 5 Scroll down mouse button. .El .Pp The .Ar function may be taken from the .Sx BIND FUNCTION LIST (see below) or the command line that is to be executed. .It Ic borderwidth Ar pixels Set the window border width to .Ar pixels . .It Ic color activeborder Ar color Set the color of the active border. .It Ic color font Ar color Set menu font color. .It Ic color selfont Ar color Set font color for selected menu item. .It Ic color groupborder Ar color Set the color of the border while grouping a window. .It Ic color inactiveborder Ar color Set the color of the inactive border. .It Ic color menubg Ar color Set menu background color. .It Ic color menufg Ar color Set menu foreground color. .It Ic color urgencyborder Ar color Set the color of the border of a window indicating urgency. .It Ic color ungroupborder Ar color Set the color of the border while ungrouping a window. .It Ic command Ar name path Every .Ar name entry is shown in the application menu. When selected, the defined .Ar path is executed with .Xr execvp 3 . .Pp The .Ar name entries .Nm term and .Nm lock have a special meaning. They point to the terminal and screen locking programs specified by key bindings. The defaults are .Xr xterm 1 and .Xr xlock 1 , respectively. .It Ic fontname Ar font Change the default .Ar font for .Xr Xft 3 . .It Ic gap Ar top bottom left right Define a .Dq gap in pixels at the edge of the screen, so that when a window is maximized it will not overlap this area. This .Dq gap can be used for applications such as .Xr xclock 1 , where the user may wish to remain visible. .It Ic htile Ar percent Set the percentage of screen the master window should occupy after calling .Ic window-htile . If set to 0, the horizontal size of the master window will remain unchanged. The default is 50. .It Ic ignore Ar windowname Ignore, and do not warp to, windows with the name .Ar windowname when drawing borders and cycling through windows. .It Ic moveamount Ar pixels Set a default size for the keyboard movement bindings, in pixels. The default is 1. .It Ic snapdist Ar pixels Minimum distance to snap-to adjacent edge, in pixels. The default is 0. .It Ic sticky Ic yes Ns \&| Ns Ic no Toggle sticky group mode. The default behavior for new windows is to not assign any group. By enabling sticky group mode, .Xr cwm 1 will assign new windows to the currently selected group. .It Ic unbind-key Ar key Unbind function bound to .Ar key . A special .Ar key keyword .Dq all can be used to unbind all keys. .It Ic unbind-mouse Ar button Unbind function bound to .Ar button . A special .Ar button keyword .Dq all can be used to unbind all buttons. .It Ic vtile Ar percent Set the percentage of screen the master window should occupy after calling .Ic window-vtile . If set to 0, the vertical size of the master window will remain unchanged. The default is 50. .It Ic wm Ar name path Every .Ar name entry is shown in the wm menu. When selected, the window manager is replaced by .Ar path . .El .Sh BIND FUNCTION LIST .Bl -tag -width 23n -compact .It restart Restart the running .Xr cwm 1 . .It quit Quit .Xr cwm 1 . .It terminal Spawn a new terminal. .It lock Lock the screen. .It menu-window Launch window search menu. .It menu-window-hidden Launch hidden window search menu. .It menu-cmd Launch application search menu. .It menu-group Launch group search menu. .It menu-exec Launch .Dq exec program menu. .It menu-exec-wm Launch .Dq exec WindowManager menu. .It menu-ssh Launch .Dq ssh menu. .It group-toggle-[n] Toggle visibility of group n, where n is 1-9. .It group-only-[n] Show only group n, where n is 1-9, hiding other groups. .It group-last Show only the previously active group. .It group-close-[n] Close all windows in group n, where n is 1-9. .It group-toggle-all Toggle visibility of all groups. .It window-group Toggle group membership of current window. .It window-movetogroup-[n] Hide current window from display and move to group n, where n is 1-9. .It group-cycle Forward cycle through groups. .It group-rcycle Reverse cycle through groups. .It window-cycle Forward cycle through windows. .It window-rcycle Reverse cycle through windows. .It window-cycle-ingroup Forward cycle through windows in current group. .It window-rcycle-ingroup Reverse cycle through windows in current group. .It window-close Close current window. .It window-hide Hide current window. .It window-lower Lower current window. .It window-raise Raise current window. .It window-menu-label Label current window. .It window-freeze Freeze current window geometry. .It window-stick Stick current window to all groups (same as assigning to nogroup). .It window-fullscreen Full-screen current window (gap + border removed). .It window-maximize Maximize current window (gap + border honored). .It window-vmaximize Vertically maximize current window (gap + border honored). .It window-hmaximize Horizontally maximize current window (gap + border honored). .It window-htile Current window is placed at the top of the screen, maximized horizontally and resized to .Ar htile (default half) of the vertical screen space. Other windows in its group share remaining screen space. .It window-vtile Current window is placed on the left of the screen, maximized vertically and resized to .Ar vtile (default half) of the horizontal screen space. Other windows in its group share remaining screen space. .It window-move Move current window. .It window-resize Resize current window. .It window-move-up Move window .Ar moveamount pixels up. .It window-move-down Move window .Ar moveamount pixels down. .It window-move-right Move window .Ar moveamount pixels right. .It window-move-left Move window .Ar moveamount pixels left. .It window-move-up-big Move window 10 times .Ar moveamount pixels up. .It window-move-down-big Move window 10 times .Ar moveamount pixels down. .It window-move-right-big Move window 10 times .Ar moveamount pixels right. .It window-move-left-big Move window 10 times .Ar moveamount pixels left. .It window-resize-up Resize window .Ar moveamount pixels up. .It window-resize-down Resize window .Ar moveamount pixels down. .It window-resize-right Resize window .Ar moveamount pixels right. .It window-resize-left Resize window .Ar moveamount pixels left. .It window-resize-up-big Resize window 10 times .Ar moveamount pixels up. .It window-resize-down-big Resize window 10 times .Ar moveamount pixels down. .It window-resize-right-big Resize window 10 times .Ar moveamount pixels right. .It window-resize-left-big Resize window 10 times .Ar moveamount pixels left. .It window-snap-up Snap window to top edge. .It window-snap-down Snap window to bottom edge. .It window-snap-right Snap window to right edge. .It window-snap-left Snap window to left edge. .It window-snap-up-right Snap window to top-right corner. .It window-snap-up-left Snap window to top-left corner. .It window-snap-down-right Snap window to bottom-right corner. .It window-snap-down-left Snap window to bottom-left corner. .It pointer-move-up Move pointer .Ar moveamount pixels up. .It pointer-move-down Move pointer .Ar moveamount pixels down. .It pointer-move-right Move pointer .Ar moveamount pixels right. .It pointer-move-left Move pointer .Ar moveamount pixels left. .It pointer-move-up-big Move pointer 10 times .Ar moveamount pixels up. .It pointer-move-down-big Move pointer 10 times .Ar moveamount pixels down. .It pointer-move-right-big Move pointer 10 times .Ar moveamount pixels right. .It pointer-move-left-big Move pointer 10 times .Ar moveamount pixels left. .El .Sh FILES .Bl -tag -width "~/.cwmrcXXX" -compact .It Pa ~/.cwmrc Default .Xr cwm 1 configuration file. .El .Sh EXAMPLES .Bd -literal # Set default Xft(3) font fontname "sans-serif:pixelsize=14:bold" # Turn on sticky-group mode sticky yes # Any entry here is shown in the application menu command firefox firefox command xmms xmms command top "xterm -e top" # Autogroup definitions autogroup 3 "aterm,XTerm" autogroup 3 "xterm,XTerm" # Ignore programs by that name by not drawing borders around them. ignore XMMS ignore xwi ignore xapm ignore xclock # Key bindings bind-key CM-r window-menu-label bind-key CS-Return "xterm -e top" bind-key C4-equal window-vmaximize bind-key C4S-equal window-hmaximize bind-key M-1 group-only-1 bind-key M-2 group-only-2 bind-key M-3 group-only-3 bind-key MS-1 window-movetogroup-1 bind-key MS-2 window-movetogroup-2 bind-key MS-3 window-movetogroup-3 unbind-key 4-o unbind-key CM-equal unbind-key CMS-equal # Mouse bindings bind-mouse M-2 window-lower bind-mouse M-3 window-resize .Ed .Sh SEE ALSO .Xr cwm 1 .Sh HISTORY The .Nm file format first appeared in .Ox 4.4 . cwm-7.1/group.c000066400000000000000000000167261423325063300134640ustar00rootroot00000000000000/* * calmwm - the calm window manager * * Copyright (c) 2004 Andy Adamson * Copyright (c) 2004,2005 Marius Aamodt Eriksen * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD$ */ #include #include "queue.h" #include #include #include #include #include #include #include #include "calmwm.h" static struct group_ctx *group_next(struct group_ctx *); static struct group_ctx *group_prev(struct group_ctx *); static void group_restack(struct group_ctx *); static void group_set_active(struct group_ctx *); void group_assign(struct group_ctx *gc, struct client_ctx *cc) { if ((gc != NULL) && (gc->num == 0)) gc = NULL; cc->gc = gc; xu_ewmh_set_net_wm_desktop(cc); } void group_hide(struct group_ctx *gc) { struct screen_ctx *sc = gc->sc; struct client_ctx *cc; screen_updatestackingorder(gc->sc); TAILQ_FOREACH(cc, &sc->clientq, entry) { if (cc->gc != gc) continue; if (!(cc->flags & CLIENT_STICKY) && !(cc->flags & CLIENT_HIDDEN)) client_hide(cc); } } void group_show(struct group_ctx *gc) { struct screen_ctx *sc = gc->sc; struct client_ctx *cc; TAILQ_FOREACH(cc, &sc->clientq, entry) { if (cc->gc != gc) continue; if (!(cc->flags & CLIENT_STICKY) && (cc->flags & CLIENT_HIDDEN)) client_show(cc); } group_restack(gc); group_set_active(gc); } static void group_restack(struct group_ctx *gc) { struct screen_ctx *sc = gc->sc; struct client_ctx *cc; Window *winlist; int i, lastempty = -1; int nwins = 0, highstack = 0; TAILQ_FOREACH(cc, &sc->clientq, entry) { if (cc->gc != gc) continue; if (cc->stackingorder > highstack) highstack = cc->stackingorder; } winlist = xreallocarray(NULL, (highstack + 1), sizeof(*winlist)); /* Invert the stacking order for XRestackWindows(). */ TAILQ_FOREACH(cc, &sc->clientq, entry) { if (cc->gc != gc) continue; winlist[highstack - cc->stackingorder] = cc->win; nwins++; } /* Un-sparseify */ for (i = 0; i <= highstack; i++) { if (!winlist[i] && lastempty == -1) lastempty = i; else if (winlist[i] && lastempty != -1) { winlist[lastempty] = winlist[i]; if (++lastempty == i) lastempty = -1; } } XRestackWindows(X_Dpy, winlist, nwins); free(winlist); } void group_init(struct screen_ctx *sc, int num, const char *name) { struct group_ctx *gc; gc = xmalloc(sizeof(*gc)); gc->sc = sc; gc->name = xstrdup(name); gc->num = num; TAILQ_INSERT_TAIL(&sc->groupq, gc, entry); if (num == 1) group_set_active(gc); } void group_set_active(struct group_ctx *gc) { struct screen_ctx *sc = gc->sc; sc->group_active = gc; xu_ewmh_net_current_desktop(sc); } void group_movetogroup(struct client_ctx *cc, int idx) { struct screen_ctx *sc = cc->sc; struct group_ctx *gc; TAILQ_FOREACH(gc, &sc->groupq, entry) { if (gc->num == idx) { if (cc->gc == gc) return; if (gc->num != 0 && group_holds_only_hidden(gc)) client_hide(cc); group_assign(gc, cc); } } } void group_toggle_membership(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct group_ctx *gc = sc->group_active; if (cc->gc == gc) { group_assign(NULL, cc); cc->flags |= CLIENT_UNGROUP; } else { group_assign(gc, cc); cc->flags |= CLIENT_GROUP; } client_draw_border(cc); } int group_holds_only_sticky(struct group_ctx *gc) { struct screen_ctx *sc = gc->sc; struct client_ctx *cc; TAILQ_FOREACH(cc, &sc->clientq, entry) { if (cc->gc != gc) continue; if (!(cc->flags & CLIENT_STICKY)) return 0; } return 1; } int group_holds_only_hidden(struct group_ctx *gc) { struct screen_ctx *sc = gc->sc; struct client_ctx *cc; TAILQ_FOREACH(cc, &sc->clientq, entry) { if (cc->gc != gc) continue; if (!(cc->flags & (CLIENT_HIDDEN | CLIENT_STICKY))) return 0; } return 1; } void group_only(struct screen_ctx *sc, int idx) { struct group_ctx *gc; if (sc->group_last != sc->group_active) sc->group_last = sc->group_active; TAILQ_FOREACH(gc, &sc->groupq, entry) { if (gc->num == idx) group_show(gc); else group_hide(gc); } } void group_toggle(struct screen_ctx *sc, int idx) { struct group_ctx *gc; TAILQ_FOREACH(gc, &sc->groupq, entry) { if (gc->num == idx) { if (group_holds_only_hidden(gc)) group_show(gc); else group_hide(gc); } } } void group_toggle_all(struct screen_ctx *sc) { struct group_ctx *gc; TAILQ_FOREACH(gc, &sc->groupq, entry) { if (sc->hideall) group_show(gc); else group_hide(gc); } sc->hideall = !sc->hideall; } void group_close(struct screen_ctx *sc, int idx) { struct group_ctx *gc; struct client_ctx *cc; TAILQ_FOREACH(gc, &sc->groupq, entry) { if (gc->num == idx) { TAILQ_FOREACH(cc, &sc->clientq, entry) { if (cc->gc != gc) continue; client_close(cc); } } } } void group_cycle(struct screen_ctx *sc, int flags) { struct group_ctx *newgc, *oldgc, *showgroup = NULL; oldgc = sc->group_active; newgc = oldgc; for (;;) { newgc = (flags & CWM_CYCLE_REVERSE) ? group_prev(newgc) : group_next(newgc); if (newgc == oldgc) break; if (!group_holds_only_sticky(newgc) && showgroup == NULL) showgroup = newgc; else if (!group_holds_only_hidden(newgc)) group_hide(newgc); } if (showgroup == NULL) return; group_hide(oldgc); if (group_holds_only_hidden(showgroup)) group_show(showgroup); else group_set_active(showgroup); } static struct group_ctx * group_next(struct group_ctx *gc) { struct screen_ctx *sc = gc->sc; struct group_ctx *newgc; return(((newgc = TAILQ_NEXT(gc, entry)) != NULL) ? newgc : TAILQ_FIRST(&sc->groupq)); } static struct group_ctx * group_prev(struct group_ctx *gc) { struct screen_ctx *sc = gc->sc; struct group_ctx *newgc; return(((newgc = TAILQ_PREV(gc, group_q, entry)) != NULL) ? newgc : TAILQ_LAST(&sc->groupq, group_q)); } int group_restore(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct group_ctx *gc; int num; long grpnum; if (!xu_ewmh_get_net_wm_desktop(cc, &grpnum)) return 0; num = (grpnum == -1) ? 0 : grpnum; num = MIN(num, (Conf.ngroups - 1)); TAILQ_FOREACH(gc, &sc->groupq, entry) { if (gc->num == num) { group_assign(gc, cc); return 1; } } return 0; } int group_autogroup(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct autogroup *ag; struct group_ctx *gc; int num = -1, both_match = 0; if (cc->res_class == NULL || cc->res_name == NULL) return 0; TAILQ_FOREACH(ag, &Conf.autogroupq, entry) { if (strcmp(ag->class, cc->res_class) == 0) { if ((ag->name != NULL) && (strcmp(ag->name, cc->res_name) == 0)) { num = ag->num; both_match = 1; } else if (ag->name == NULL && !both_match) num = ag->num; } } TAILQ_FOREACH(gc, &sc->groupq, entry) { if (gc->num == num) { group_assign(gc, cc); return 1; } } return 0; } cwm-7.1/kbfunc.c000066400000000000000000000435321423325063300135730ustar00rootroot00000000000000/* * calmwm - the calm window manager * * Copyright (c) 2004 Martin Murray * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD$ */ /* For FreeBSD. */ #define _WITH_GETLINE #include #include "queue.h" #include #include #include #include #include #include #include #include #include #include #include #include "calmwm.h" #define HASH_MARKER "|1|" extern sig_atomic_t cwm_status; static void kbfunc_amount(int, int, int *, int *); static void kbfunc_client_move_kb(void *, struct cargs *); static void kbfunc_client_move_mb(void *, struct cargs *); static void kbfunc_client_resize_kb(void *, struct cargs *); static void kbfunc_client_resize_mb(void *, struct cargs *); void kbfunc_cwm_status(void *ctx, struct cargs *cargs) { cwm_status = cargs->flag; } static void kbfunc_amount(int flags, int amt, int *mx, int *my) { #define CWM_FACTOR 10 if (flags & CWM_BIGAMOUNT) amt *= CWM_FACTOR; switch (flags & (CWM_UP | CWM_DOWN | CWM_LEFT | CWM_RIGHT)) { case CWM_UP: *my -= amt; break; case CWM_DOWN: *my += amt; break; case CWM_RIGHT: *mx += amt; break; case CWM_LEFT: *mx -= amt; break; } } void kbfunc_ptrmove(void *ctx, struct cargs *cargs) { struct screen_ctx *sc = ctx; int x, y; int mx = 0, my = 0; kbfunc_amount(cargs->flag, Conf.mamount, &mx, &my); xu_ptr_get(sc->rootwin, &x, &y); xu_ptr_set(sc->rootwin, x + mx, y + my); } void kbfunc_client_move(void *ctx, struct cargs *cargs) { if (cargs->xev == CWM_XEV_BTN) kbfunc_client_move_mb(ctx, cargs); else kbfunc_client_move_kb(ctx, cargs); } void kbfunc_client_resize(void *ctx, struct cargs *cargs) { if (cargs->xev == CWM_XEV_BTN) kbfunc_client_resize_mb(ctx, cargs); else kbfunc_client_resize_kb(ctx, cargs); } static void kbfunc_client_move_kb(void *ctx, struct cargs *cargs) { struct client_ctx *cc = ctx; struct screen_ctx *sc = cc->sc; struct geom area; int mx = 0, my = 0; if (cc->flags & CLIENT_FREEZE) return; kbfunc_amount(cargs->flag, Conf.mamount, &mx, &my); cc->geom.x += mx; if (cc->geom.x < -(cc->geom.w + cc->bwidth - 1)) cc->geom.x = -(cc->geom.w + cc->bwidth - 1); if (cc->geom.x > (sc->view.w - cc->bwidth - 1)) cc->geom.x = sc->view.w - cc->bwidth - 1; cc->geom.y += my; if (cc->geom.y < -(cc->geom.h + cc->bwidth - 1)) cc->geom.y = -(cc->geom.h + cc->bwidth - 1); if (cc->geom.y > (sc->view.h - cc->bwidth - 1)) cc->geom.y = sc->view.h - cc->bwidth - 1; area = screen_area(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, 1); cc->geom.x += client_snapcalc(cc->geom.x, cc->geom.x + cc->geom.w + (cc->bwidth * 2), area.x, area.x + area.w, sc->snapdist); cc->geom.y += client_snapcalc(cc->geom.y, cc->geom.y + cc->geom.h + (cc->bwidth * 2), area.y, area.y + area.h, sc->snapdist); client_move(cc); client_ptr_inbound(cc, 1); XSync(X_Dpy, True); } static void kbfunc_client_move_mb(void *ctx, struct cargs *cargs) { struct client_ctx *cc = ctx; XEvent ev; Time ltime = 0; struct screen_ctx *sc = cc->sc; struct geom area; int move = 1; client_raise(cc); if (cc->flags & CLIENT_FREEZE) return; client_ptr_inbound(cc, 1); if (XGrabPointer(X_Dpy, cc->win, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_MOVE], CurrentTime) != GrabSuccess) return; screen_prop_win_create(sc, cc->win); screen_prop_win_draw(sc, "%+5d%+5d", cc->geom.x, cc->geom.y); while (move) { XMaskEvent(X_Dpy, MOUSEMASK, &ev); switch (ev.type) { case MotionNotify: /* not more than 60 times / second */ if ((ev.xmotion.time - ltime) <= (1000 / 60)) continue; ltime = ev.xmotion.time; cc->geom.x = ev.xmotion.x_root - cc->ptr.x - cc->bwidth; cc->geom.y = ev.xmotion.y_root - cc->ptr.y - cc->bwidth; area = screen_area(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, 1); cc->geom.x += client_snapcalc(cc->geom.x, cc->geom.x + cc->geom.w + (cc->bwidth * 2), area.x, area.x + area.w, sc->snapdist); cc->geom.y += client_snapcalc(cc->geom.y, cc->geom.y + cc->geom.h + (cc->bwidth * 2), area.y, area.y + area.h, sc->snapdist); client_move(cc); screen_prop_win_draw(sc, "%+5d%+5d", cc->geom.x, cc->geom.y); break; case ButtonRelease: move = 0; break; } } if (ltime) client_move(cc); screen_prop_win_destroy(sc); XUngrabPointer(X_Dpy, CurrentTime); } static void kbfunc_client_resize_kb(void *ctx, struct cargs *cargs) { struct client_ctx *cc = ctx; int mx = 0, my = 0; int amt = 1; if (cc->flags & CLIENT_FREEZE) return; if (!(cc->hint.flags & PResizeInc)) amt = Conf.mamount; kbfunc_amount(cargs->flag, amt, &mx, &my); if ((cc->geom.w += mx * cc->hint.incw) < cc->hint.minw) cc->geom.w = cc->hint.minw; if ((cc->geom.h += my * cc->hint.inch) < cc->hint.minh) cc->geom.h = cc->hint.minh; if (cc->geom.x + cc->geom.w + cc->bwidth - 1 < 0) cc->geom.x = -(cc->geom.w + cc->bwidth - 1); if (cc->geom.y + cc->geom.h + cc->bwidth - 1 < 0) cc->geom.y = -(cc->geom.h + cc->bwidth - 1); client_resize(cc, 1); client_ptr_inbound(cc, 1); XSync(X_Dpy, True); } static void kbfunc_client_resize_mb(void *ctx, struct cargs *cargs) { struct client_ctx *cc = ctx; XEvent ev; Time ltime = 0; struct screen_ctx *sc = cc->sc; int resize = 1; if (cc->flags & CLIENT_FREEZE) return; client_raise(cc); client_ptr_save(cc); xu_ptr_set(cc->win, cc->geom.w, cc->geom.h); if (XGrabPointer(X_Dpy, cc->win, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_RESIZE], CurrentTime) != GrabSuccess) return; screen_prop_win_create(sc, cc->win); screen_prop_win_draw(sc, "%4d x %-4d", cc->dim.w, cc->dim.h); while (resize) { XMaskEvent(X_Dpy, MOUSEMASK, &ev); switch (ev.type) { case MotionNotify: /* not more than 60 times / second */ if ((ev.xmotion.time - ltime) <= (1000 / 60)) continue; ltime = ev.xmotion.time; cc->geom.w = ev.xmotion.x; cc->geom.h = ev.xmotion.y; client_apply_sizehints(cc); client_resize(cc, 1); screen_prop_win_draw(sc, "%4d x %-4d", cc->dim.w, cc->dim.h); break; case ButtonRelease: resize = 0; break; } } if (ltime) client_resize(cc, 1); screen_prop_win_destroy(sc); XUngrabPointer(X_Dpy, CurrentTime); /* Make sure the pointer stays within the window. */ client_ptr_inbound(cc, 0); } void kbfunc_client_snap(void *ctx, struct cargs *cargs) { struct client_ctx *cc = ctx; struct screen_ctx *sc = cc->sc; struct geom area; int flags; area = screen_area(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, 1); flags = cargs->flag; while (flags) { if (flags & CWM_UP) { cc->geom.y = area.y; flags &= ~CWM_UP; } if (flags & CWM_LEFT) { cc->geom.x = area.x; flags &= ~CWM_LEFT; } if (flags & CWM_RIGHT) { cc->geom.x = area.x + area.w - cc->geom.w - (cc->bwidth * 2); flags &= ~CWM_RIGHT; } if (flags & CWM_DOWN) { cc->geom.y = area.y + area.h - cc->geom.h - (cc->bwidth * 2); flags &= ~CWM_DOWN; } } client_move(cc); client_ptr_inbound(cc, 1); } void kbfunc_client_close(void *ctx, struct cargs *cargs) { client_close(ctx); } void kbfunc_client_lower(void *ctx, struct cargs *cargs) { client_ptr_save(ctx); client_lower(ctx); } void kbfunc_client_raise(void *ctx, struct cargs *cargs) { client_raise(ctx); } void kbfunc_client_hide(void *ctx, struct cargs *cargs) { client_hide(ctx); } void kbfunc_client_toggle_freeze(void *ctx, struct cargs *cargs) { client_toggle_freeze(ctx); } void kbfunc_client_toggle_sticky(void *ctx, struct cargs *cargs) { client_toggle_sticky(ctx); } void kbfunc_client_toggle_fullscreen(void *ctx, struct cargs *cargs) { client_toggle_fullscreen(ctx); } void kbfunc_client_toggle_maximize(void *ctx, struct cargs *cargs) { client_toggle_maximize(ctx); } void kbfunc_client_toggle_hmaximize(void *ctx, struct cargs *cargs) { client_toggle_hmaximize(ctx); } void kbfunc_client_toggle_vmaximize(void *ctx, struct cargs *cargs) { client_toggle_vmaximize(ctx); } void kbfunc_client_htile(void *ctx, struct cargs *cargs) { client_htile(ctx); } void kbfunc_client_vtile(void *ctx, struct cargs *cargs) { client_vtile(ctx); } void kbfunc_client_cycle(void *ctx, struct cargs *cargs) { struct screen_ctx *sc = ctx; struct client_ctx *newcc, *oldcc, *prevcc; int again = 1, flags = cargs->flag; /* For X apps that ignore/steal events. */ if (cargs->xev == CWM_XEV_KEY) XGrabKeyboard(X_Dpy, sc->rootwin, True, GrabModeAsync, GrabModeAsync, CurrentTime); if (TAILQ_EMPTY(&sc->clientq)) return; prevcc = TAILQ_FIRST(&sc->clientq); oldcc = client_current(sc); if (oldcc == NULL) oldcc = (flags & CWM_CYCLE_REVERSE) ? TAILQ_LAST(&sc->clientq, client_q) : TAILQ_FIRST(&sc->clientq); newcc = oldcc; while (again) { again = 0; newcc = (flags & CWM_CYCLE_REVERSE) ? client_prev(newcc) : client_next(newcc); /* Only cycle visible and non-ignored windows. */ if ((newcc->flags & (CLIENT_SKIP_CYCLE)) || ((flags & CWM_CYCLE_INGROUP) && (newcc->gc != oldcc->gc))) again = 1; /* Is oldcc the only non-hidden window? */ if (newcc == oldcc) { if (again) return; /* No windows visible. */ break; } } /* Reset when cycling mod is released. XXX I hate this hack */ sc->cycling = 1; client_ptr_save(oldcc); client_raise(prevcc); client_raise(newcc); if (!client_inbound(newcc, newcc->ptr.x, newcc->ptr.y)) { newcc->ptr.x = newcc->geom.w / 2; newcc->ptr.y = newcc->geom.h / 2; } /* When no client is active, warp pointer to last active. */ if (oldcc->flags & (CLIENT_ACTIVE)) client_ptr_warp(newcc); else if (oldcc->flags & (CLIENT_SKIP_CYCLE)) client_ptr_warp(newcc); else { client_raise(oldcc); client_ptr_warp(oldcc); } } void kbfunc_client_toggle_group(void *ctx, struct cargs *cargs) { struct client_ctx *cc = ctx; /* For X apps that ignore/steal events. */ if (cargs->xev == CWM_XEV_KEY) XGrabKeyboard(X_Dpy, cc->win, True, GrabModeAsync, GrabModeAsync, CurrentTime); group_toggle_membership(cc); } void kbfunc_client_movetogroup(void *ctx, struct cargs *cargs) { group_movetogroup(ctx, cargs->flag); } void kbfunc_group_only(void *ctx, struct cargs *cargs) { group_only(ctx, cargs->flag); } void kbfunc_group_last(void *ctx, struct cargs *cargs) { struct screen_ctx *sc = ctx; group_only(ctx, sc->group_last->num); } void kbfunc_group_toggle(void *ctx, struct cargs *cargs) { group_toggle(ctx, cargs->flag); } void kbfunc_group_toggle_all(void *ctx, struct cargs *cargs) { group_toggle_all(ctx); } void kbfunc_group_close(void *ctx, struct cargs *cargs) { group_close(ctx, cargs->flag); } void kbfunc_group_cycle(void *ctx, struct cargs *cargs) { group_cycle(ctx, cargs->flag); } void kbfunc_menu_client(void *ctx, struct cargs *cargs) { struct screen_ctx *sc = ctx; struct client_ctx *cc, *old_cc; struct menu *mi; struct menu_q menuq; int mflags = 0; if (cargs->xev == CWM_XEV_BTN) mflags |= CWM_MENU_LIST; TAILQ_INIT(&menuq); TAILQ_FOREACH(cc, &sc->clientq, entry) { if ((cargs->flag & CWM_MENU_WINDOW_ALL) || (cc->flags & CLIENT_HIDDEN)) menuq_add(&menuq, cc, NULL); } if ((mi = menu_filter(sc, &menuq, "window", NULL, mflags, search_match_client, search_print_client)) != NULL) { cc = (struct client_ctx *)mi->ctx; client_show(cc); if ((old_cc = client_current(sc)) != NULL) client_ptr_save(old_cc); client_ptr_warp(cc); } menuq_clear(&menuq); } void kbfunc_menu_cmd(void *ctx, struct cargs *cargs) { struct screen_ctx *sc = ctx; struct cmd_ctx *cmd; struct menu *mi; struct menu_q menuq; int mflags = 0; if (cargs->xev == CWM_XEV_BTN) mflags |= CWM_MENU_LIST; TAILQ_INIT(&menuq); TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { if ((strcmp(cmd->name, "lock") == 0) || (strcmp(cmd->name, "term") == 0)) continue; menuq_add(&menuq, cmd, NULL); } if ((mi = menu_filter(sc, &menuq, "application", NULL, mflags, search_match_cmd, search_print_cmd)) != NULL) { cmd = (struct cmd_ctx *)mi->ctx; u_spawn(cmd->path); } menuq_clear(&menuq); } void kbfunc_menu_group(void *ctx, struct cargs *cargs) { struct screen_ctx *sc = ctx; struct group_ctx *gc; struct menu *mi; struct menu_q menuq; int mflags = 0; if (cargs->xev == CWM_XEV_BTN) mflags |= CWM_MENU_LIST; TAILQ_INIT(&menuq); TAILQ_FOREACH(gc, &sc->groupq, entry) { if (group_holds_only_sticky(gc)) continue; menuq_add(&menuq, gc, NULL); } if ((mi = menu_filter(sc, &menuq, "group", NULL, mflags, search_match_group, search_print_group)) != NULL) { gc = (struct group_ctx *)mi->ctx; (group_holds_only_hidden(gc)) ? group_show(gc) : group_hide(gc); } menuq_clear(&menuq); } void kbfunc_menu_wm(void *ctx, struct cargs *cargs) { struct screen_ctx *sc = ctx; struct cmd_ctx *wm; struct menu *mi; struct menu_q menuq; int mflags = 0; if (cargs->xev == CWM_XEV_BTN) mflags |= CWM_MENU_LIST; TAILQ_INIT(&menuq); TAILQ_FOREACH(wm, &Conf.wmq, entry) menuq_add(&menuq, wm, NULL); if ((mi = menu_filter(sc, &menuq, "wm", NULL, mflags, search_match_wm, search_print_wm)) != NULL) { wm = (struct cmd_ctx *)mi->ctx; free(Conf.wm_argv); Conf.wm_argv = xstrdup(wm->path); cwm_status = CWM_EXEC_WM; } menuq_clear(&menuq); } void kbfunc_menu_exec(void *ctx, struct cargs *cargs) { #define NPATHS 256 struct screen_ctx *sc = ctx; char **ap, *paths[NPATHS], *path, *pathcpy; char tpath[PATH_MAX]; struct stat sb; DIR *dirp; struct dirent *dp; struct menu *mi; struct menu_q menuq; int l, i; int mflags = (CWM_MENU_DUMMY | CWM_MENU_FILE); TAILQ_INIT(&menuq); if ((path = getenv("PATH")) == NULL) path = _PATH_DEFPATH; pathcpy = path = xstrdup(path); for (ap = paths; ap < &paths[NPATHS - 1] && (*ap = strsep(&pathcpy, ":")) != NULL;) { if (**ap != '\0') ap++; } *ap = NULL; for (i = 0; i < NPATHS && paths[i] != NULL; i++) { if ((dirp = opendir(paths[i])) == NULL) continue; while ((dp = readdir(dirp)) != NULL) { (void)memset(tpath, '\0', sizeof(tpath)); l = snprintf(tpath, sizeof(tpath), "%s/%s", paths[i], dp->d_name); if (l == -1 || l >= sizeof(tpath)) continue; /* Skip everything but regular files and symlinks. */ if (dp->d_type != DT_REG && dp->d_type != DT_LNK) { /* lstat(2) in case d_type isn't supported. */ if (lstat(tpath, &sb) == -1) continue; if (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode)) continue; } if (access(tpath, X_OK) == 0) menuq_add(&menuq, NULL, "%s", dp->d_name); } (void)closedir(dirp); } free(path); if ((mi = menu_filter(sc, &menuq, "exec", NULL, mflags, search_match_exec, search_print_text)) != NULL) { if (mi->text[0] == '\0') goto out; u_spawn(mi->text); } out: if (mi != NULL && mi->dummy) free(mi); menuq_clear(&menuq); } void kbfunc_menu_ssh(void *ctx, struct cargs *cargs) { struct screen_ctx *sc = ctx; struct cmd_ctx *cmd; struct menu *mi; struct menu_q menuq; FILE *fp; char *buf, *lbuf, *p; char hostbuf[_POSIX_HOST_NAME_MAX+1]; char path[PATH_MAX]; int l; size_t len; ssize_t slen; int mflags = (CWM_MENU_DUMMY); TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { if (strcmp(cmd->name, "term") == 0) break; } TAILQ_INIT(&menuq); if ((fp = fopen(Conf.known_hosts, "r")) == NULL) { warn("%s: %s", __func__, Conf.known_hosts); goto menu; } lbuf = NULL; len = 0; while ((slen = getline(&lbuf, &len, fp)) != -1) { buf = lbuf; if (buf[slen - 1] == '\n') buf[slen - 1] = '\0'; /* skip hashed hosts */ if (strncmp(buf, HASH_MARKER, strlen(HASH_MARKER)) == 0) continue; for (p = buf; *p != ',' && *p != ' ' && p != buf + slen; p++) ; /* ignore badness */ if (p - buf + 1 > sizeof(hostbuf)) continue; (void)strlcpy(hostbuf, buf, p - buf + 1); menuq_add(&menuq, NULL, "%s", hostbuf); } free(lbuf); if (ferror(fp)) err(1, "%s", path); (void)fclose(fp); menu: if ((mi = menu_filter(sc, &menuq, "ssh", NULL, mflags, search_match_text, search_print_text)) != NULL) { if (mi->text[0] == '\0') goto out; l = snprintf(path, sizeof(path), "%s -T '[ssh] %s' -e ssh %s", cmd->path, mi->text, mi->text); if (l == -1 || l >= sizeof(path)) goto out; u_spawn(path); } out: if (mi != NULL && mi->dummy) free(mi); menuq_clear(&menuq); } void kbfunc_client_menu_label(void *ctx, struct cargs *cargs) { struct client_ctx *cc = ctx; struct menu *mi; struct menu_q menuq; int mflags = (CWM_MENU_DUMMY); TAILQ_INIT(&menuq); /* dummy is set, so this will always return */ mi = menu_filter(cc->sc, &menuq, "label", cc->label, mflags, search_match_text, search_print_text); if (!mi->abort) { free(cc->label); cc->label = xstrdup(mi->text); } free(mi); } void kbfunc_exec_cmd(void *ctx, struct cargs *cargs) { u_spawn(cargs->cmd); } void kbfunc_exec_term(void *ctx, struct cargs *cargs) { struct cmd_ctx *cmd; TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { if (strcmp(cmd->name, "term") == 0) u_spawn(cmd->path); } } void kbfunc_exec_lock(void *ctx, struct cargs *cargs) { struct cmd_ctx *cmd; TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { if (strcmp(cmd->name, "lock") == 0) u_spawn(cmd->path); } } cwm-7.1/menu.c000066400000000000000000000344411423325063300132660ustar00rootroot00000000000000/* * calmwm - the calm window manager * * Copyright (c) 2008 Owain G. Ainsworth * Copyright (c) 2004 Marius Aamodt Eriksen * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD$ */ #include #include "queue.h" #include #include #include #include #include #include #include #include #include #include "calmwm.h" #define PROMPT_SCHAR "\xc2\xbb" #define PROMPT_ECHAR "\xc2\xab" #define MENUMASK (MOUSEMASK | ButtonMotionMask | KeyPressMask | \ ExposureMask) #define MENUGRABMASK (MOUSEMASK | ButtonMotionMask | StructureNotifyMask) enum ctltype { CTL_NONE = -1, CTL_ERASEONE = 0, CTL_WIPE, CTL_UP, CTL_DOWN, CTL_RETURN, CTL_TAB, CTL_ABORT, CTL_ALL }; struct menu_ctx { struct screen_ctx *sc; Window win; XftDraw *xftdraw; struct geom geom; char searchstr[MENU_MAXENTRY + 1]; char dispstr[MENU_MAXENTRY*2 + 1]; char promptstr[MENU_MAXENTRY + 1]; int list; int listing; int changed; int prev; int entry; int num; int flags; void (*match)(struct menu_q *, struct menu_q *, char *); void (*print)(struct menu *, int); }; static struct menu *menu_handle_key(XEvent *, struct menu_ctx *, struct menu_q *, struct menu_q *); static void menu_handle_move(struct menu_ctx *, struct menu_q *, int, int); static struct menu *menu_handle_release(struct menu_ctx *, struct menu_q *, int, int); static void menu_draw(struct menu_ctx *, struct menu_q *, struct menu_q *); static void menu_draw_entry(struct menu_ctx *, struct menu_q *, int, int); static int menu_calc_entry(struct menu_ctx *, int, int); static struct menu *menu_complete_path(struct menu_ctx *); static int menu_keycode(XKeyEvent *, enum ctltype *, char *); struct menu * menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt, const char *initial, int flags, void (*match)(struct menu_q *, struct menu_q *, char *), void (*print)(struct menu *, int)) { struct menu_ctx mc; struct menu_q resultq; struct menu *mi = NULL; XEvent e; Window focuswin; int focusrevert, xsave, ysave, xcur, ycur; TAILQ_INIT(&resultq); xu_ptr_get(sc->rootwin, &xsave, &ysave); (void)memset(&mc, 0, sizeof(mc)); mc.sc = sc; mc.flags = flags; mc.match = match; mc.print = print; mc.entry = mc.prev = -1; mc.geom.x = xsave; mc.geom.y = ysave; if (mc.flags & CWM_MENU_LIST) mc.list = 1; (void)strlcpy(mc.promptstr, prompt, sizeof(mc.promptstr)); if (initial != NULL) (void)strlcpy(mc.searchstr, initial, sizeof(mc.searchstr)); else mc.searchstr[0] = '\0'; mc.win = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0, 1, 1, Conf.bwidth, sc->xftcolor[CWM_COLOR_MENU_FG].pixel, sc->xftcolor[CWM_COLOR_MENU_BG].pixel); mc.xftdraw = XftDrawCreate(X_Dpy, mc.win, sc->visual, sc->colormap); XSelectInput(X_Dpy, mc.win, MENUMASK); XMapRaised(X_Dpy, mc.win); if (XGrabPointer(X_Dpy, mc.win, False, MENUGRABMASK, GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_QUESTION], CurrentTime) != GrabSuccess) { XftDrawDestroy(mc.xftdraw); XDestroyWindow(X_Dpy, mc.win); return NULL; } XGetInputFocus(X_Dpy, &focuswin, &focusrevert); XSetInputFocus(X_Dpy, mc.win, RevertToPointerRoot, CurrentTime); /* make sure keybindings don't remove keys from the menu stream */ XGrabKeyboard(X_Dpy, mc.win, True, GrabModeAsync, GrabModeAsync, CurrentTime); for (;;) { mc.changed = 0; XWindowEvent(X_Dpy, mc.win, MENUMASK, &e); switch (e.type) { case KeyPress: if ((mi = menu_handle_key(&e, &mc, menuq, &resultq)) != NULL) goto out; /* FALLTHROUGH */ case Expose: menu_draw(&mc, menuq, &resultq); break; case MotionNotify: menu_handle_move(&mc, &resultq, e.xbutton.x, e.xbutton.y); break; case ButtonRelease: if ((mi = menu_handle_release(&mc, &resultq, e.xbutton.x, e.xbutton.y)) != NULL) goto out; break; default: break; } } out: if ((mc.flags & CWM_MENU_DUMMY) == 0 && mi->dummy) { /* no mouse based match */ free(mi); mi = NULL; } XftDrawDestroy(mc.xftdraw); XDestroyWindow(X_Dpy, mc.win); XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime); /* restore if user didn't move */ xu_ptr_get(sc->rootwin, &xcur, &ycur); if (xcur == mc.geom.x && ycur == mc.geom.y) xu_ptr_set(sc->rootwin, xsave, ysave); XUngrabPointer(X_Dpy, CurrentTime); XUngrabKeyboard(X_Dpy, CurrentTime); return mi; } static struct menu * menu_complete_path(struct menu_ctx *mc) { struct screen_ctx *sc = mc->sc; struct menu *mi, *mr; struct menu_q menuq; int mflags = (CWM_MENU_DUMMY); mr = xcalloc(1, sizeof(*mr)); TAILQ_INIT(&menuq); if ((mi = menu_filter(sc, &menuq, mc->searchstr, NULL, mflags, search_match_path, search_print_text)) != NULL) { mr->abort = mi->abort; mr->dummy = mi->dummy; if (mi->text[0] != '\0') snprintf(mr->text, sizeof(mr->text), "%s \"%s\"", mc->searchstr, mi->text); else if (!mr->abort) strlcpy(mr->text, mc->searchstr, sizeof(mr->text)); } menuq_clear(&menuq); return mr; } static struct menu * menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq, struct menu_q *resultq) { struct menu *mi; enum ctltype ctl; char chr[32]; size_t len; int clen, i; wchar_t wc; if (menu_keycode(&e->xkey, &ctl, chr) < 0) return NULL; switch (ctl) { case CTL_ERASEONE: if ((len = strlen(mc->searchstr)) > 0) { clen = 1; while (mbtowc(&wc, &mc->searchstr[len-clen], MB_CUR_MAX) == -1) clen++; for (i = 1; i <= clen; i++) mc->searchstr[len - i] = '\0'; mc->changed = 1; } break; case CTL_UP: mi = TAILQ_LAST(resultq, menu_q); if (mi == NULL) break; TAILQ_REMOVE(resultq, mi, resultentry); TAILQ_INSERT_HEAD(resultq, mi, resultentry); break; case CTL_DOWN: mi = TAILQ_FIRST(resultq); if (mi == NULL) break; TAILQ_REMOVE(resultq, mi, resultentry); TAILQ_INSERT_TAIL(resultq, mi, resultentry); break; case CTL_RETURN: /* * Return whatever the cursor is currently on. Else * even if dummy is zero, we need to return something. */ if ((mi = TAILQ_FIRST(resultq)) == NULL) { mi = xmalloc(sizeof(*mi)); (void)strlcpy(mi->text, mc->searchstr, sizeof(mi->text)); mi->dummy = 1; } mi->abort = 0; return mi; case CTL_WIPE: mc->searchstr[0] = '\0'; mc->changed = 1; break; case CTL_TAB: if ((mi = TAILQ_FIRST(resultq)) != NULL) { /* * - We are in exec_path menu mode * - It is equal to the input * We got a command, launch the file menu */ if ((mc->flags & CWM_MENU_FILE) && (strncmp(mc->searchstr, mi->text, strlen(mi->text))) == 0) return menu_complete_path(mc); /* * Put common prefix of the results into searchstr */ (void)strlcpy(mc->searchstr, mi->text, sizeof(mc->searchstr)); while ((mi = TAILQ_NEXT(mi, resultentry)) != NULL) { i = 0; while (tolower(mc->searchstr[i]) == tolower(mi->text[i])) i++; mc->searchstr[i] = '\0'; } mc->changed = 1; } break; case CTL_ALL: mc->list = !mc->list; break; case CTL_ABORT: mi = xmalloc(sizeof(*mi)); mi->text[0] = '\0'; mi->dummy = 1; mi->abort = 1; return mi; default: break; } if (chr[0] != '\0') { mc->changed = 1; (void)strlcat(mc->searchstr, chr, sizeof(mc->searchstr)); } if (mc->changed) { if (mc->searchstr[0] != '\0') (*mc->match)(menuq, resultq, mc->searchstr); } else if (!mc->list && mc->listing) { TAILQ_INIT(resultq); mc->listing = 0; } return NULL; } static void menu_draw(struct menu_ctx *mc, struct menu_q *menuq, struct menu_q *resultq) { struct screen_ctx *sc = mc->sc; struct menu *mi; struct geom area; int n, xsave, ysave; XGlyphInfo extents; if (mc->list) { if (TAILQ_EMPTY(resultq)) { /* Copy them all over. */ TAILQ_FOREACH(mi, menuq, entry) TAILQ_INSERT_TAIL(resultq, mi, resultentry); mc->listing = 1; } else if (mc->changed) mc->listing = 0; } (void)snprintf(mc->dispstr, sizeof(mc->dispstr), "%s%s%s%s", mc->promptstr, PROMPT_SCHAR, mc->searchstr, PROMPT_ECHAR); XftTextExtentsUtf8(X_Dpy, sc->xftfont, (const FcChar8*)mc->dispstr, strlen(mc->dispstr), &extents); mc->geom.w = extents.xOff; mc->geom.h = sc->xftfont->height + 1; mc->num = 1; TAILQ_FOREACH(mi, resultq, resultentry) { (*mc->print)(mi, mc->listing); XftTextExtentsUtf8(X_Dpy, sc->xftfont, (const FcChar8*)mi->print, MIN(strlen(mi->print), MENU_MAXENTRY), &extents); mc->geom.w = MAX(mc->geom.w, extents.xOff); mc->geom.h += sc->xftfont->height + 1; mc->num++; } area = screen_area(sc, mc->geom.x, mc->geom.y, 1); area.w += area.x - Conf.bwidth * 2; area.h += area.y - Conf.bwidth * 2; xsave = mc->geom.x; ysave = mc->geom.y; /* Never hide the top, or left side, of the menu. */ if (mc->geom.x + mc->geom.w >= area.w) mc->geom.x = area.w - mc->geom.w; if (mc->geom.x < area.x) { mc->geom.x = area.x; mc->geom.w = MIN(mc->geom.w, (area.w - area.x)); } if (mc->geom.y + mc->geom.h >= area.h) mc->geom.y = area.h - mc->geom.h; if (mc->geom.y < area.y) { mc->geom.y = area.y; mc->geom.h = MIN(mc->geom.h, (area.h - area.y)); } if (mc->geom.x != xsave || mc->geom.y != ysave) xu_ptr_set(sc->rootwin, mc->geom.x, mc->geom.y); XClearWindow(X_Dpy, mc->win); XMoveResizeWindow(X_Dpy, mc->win, mc->geom.x, mc->geom.y, mc->geom.w, mc->geom.h); n = 1; XftDrawStringUtf8(mc->xftdraw, &sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont, 0, sc->xftfont->ascent, (const FcChar8*)mc->dispstr, strlen(mc->dispstr)); TAILQ_FOREACH(mi, resultq, resultentry) { int y = n * (sc->xftfont->height + 1) + sc->xftfont->ascent + 1; /* Stop drawing when menu doesn't fit inside the screen. */ if (mc->geom.y + y > area.h) break; XftDrawStringUtf8(mc->xftdraw, &sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont, 0, y, (const FcChar8*)mi->print, strlen(mi->print)); n++; } if (n > 1) menu_draw_entry(mc, resultq, 1, 1); } static void menu_draw_entry(struct menu_ctx *mc, struct menu_q *resultq, int entry, int active) { struct screen_ctx *sc = mc->sc; struct menu *mi; int color, i = 1; TAILQ_FOREACH(mi, resultq, resultentry) if (entry == i++) break; if (mi == NULL) return; color = (active) ? CWM_COLOR_MENU_FG : CWM_COLOR_MENU_BG; XftDrawRect(mc->xftdraw, &sc->xftcolor[color], 0, (sc->xftfont->height + 1) * entry, mc->geom.w, (sc->xftfont->height + 1) + sc->xftfont->descent); color = (active) ? CWM_COLOR_MENU_FONT_SEL : CWM_COLOR_MENU_FONT; XftDrawStringUtf8(mc->xftdraw, &sc->xftcolor[color], sc->xftfont, 0, (sc->xftfont->height + 1) * entry + sc->xftfont->ascent + 1, (const FcChar8*)mi->print, strlen(mi->print)); } static void menu_handle_move(struct menu_ctx *mc, struct menu_q *resultq, int x, int y) { mc->prev = mc->entry; mc->entry = menu_calc_entry(mc, x, y); if (mc->prev == mc->entry) return; if (mc->prev != -1) menu_draw_entry(mc, resultq, mc->prev, 0); if (mc->entry != -1) { XChangeActivePointerGrab(X_Dpy, MENUGRABMASK, Conf.cursor[CF_NORMAL], CurrentTime); menu_draw_entry(mc, resultq, mc->entry, 1); } } static struct menu * menu_handle_release(struct menu_ctx *mc, struct menu_q *resultq, int x, int y) { struct menu *mi; int entry, i = 1; entry = menu_calc_entry(mc, x, y); TAILQ_FOREACH(mi, resultq, resultentry) if (entry == i++) break; if (mi == NULL) { mi = xmalloc(sizeof(*mi)); mi->text[0] = '\0'; mi->dummy = 1; } return mi; } static int menu_calc_entry(struct menu_ctx *mc, int x, int y) { struct screen_ctx *sc = mc->sc; int entry; entry = y / (sc->xftfont->height + 1); /* in bounds? */ if (x < 0 || x > mc->geom.w || y < 0 || y > (sc->xftfont->height + 1) * mc->num || entry < 0 || entry >= mc->num) entry = -1; if (entry == 0) entry = -1; return entry; } static int menu_keycode(XKeyEvent *ev, enum ctltype *ctl, char *chr) { KeySym ks; *ctl = CTL_NONE; chr[0] = '\0'; ks = XkbKeycodeToKeysym(X_Dpy, ev->keycode, 0, (ev->state & ShiftMask) ? 1 : 0); /* Look for control characters. */ switch (ks) { case XK_BackSpace: *ctl = CTL_ERASEONE; break; case XK_KP_Enter: case XK_Return: *ctl = CTL_RETURN; break; case XK_Tab: *ctl = CTL_TAB; break; case XK_Up: *ctl = CTL_UP; break; case XK_Down: *ctl = CTL_DOWN; break; case XK_Escape: *ctl = CTL_ABORT; break; } if (*ctl == CTL_NONE && (ev->state & ControlMask)) { switch (ks) { case XK_s: case XK_S: /* Emacs "next" */ *ctl = CTL_DOWN; break; case XK_r: case XK_R: /* Emacs "previous" */ *ctl = CTL_UP; break; case XK_u: case XK_U: *ctl = CTL_WIPE; break; case XK_h: case XK_H: *ctl = CTL_ERASEONE; break; case XK_a: case XK_A: *ctl = CTL_ALL; break; case XK_bracketleft: *ctl = CTL_ABORT; break; } } if (*ctl == CTL_NONE && (ev->state & Mod1Mask)) { switch (ks) { case XK_j: case XK_J: /* Vi "down" */ *ctl = CTL_DOWN; break; case XK_k: case XK_K: /* Vi "up" */ *ctl = CTL_UP; break; } } if (*ctl != CTL_NONE) return 0; if (XLookupString(ev, chr, 32, &ks, NULL) < 0) return -1; return 0; } void menuq_add(struct menu_q *mq, void *ctx, const char *fmt, ...) { va_list ap; struct menu *mi; mi = xcalloc(1, sizeof(*mi)); mi->ctx = ctx; va_start(ap, fmt); if (fmt != NULL) (void)vsnprintf(mi->text, sizeof(mi->text), fmt, ap); else mi->text[0] = '\0'; va_end(ap); TAILQ_INSERT_TAIL(mq, mi, entry); } void menuq_clear(struct menu_q *mq) { struct menu *mi; while ((mi = TAILQ_FIRST(mq)) != NULL) { TAILQ_REMOVE(mq, mi, entry); free(mi); } } cwm-7.1/migrate-config.pl000077500000000000000000000047741423325063300154170ustar00rootroot00000000000000#!/usr/bin/perl -p # Originally by Stuart Henderson in # <20161206120601.vmimohqh4nafaeah@symphytum.spacehopper.org> # Ported to Perl and slightly tweaked by Leah Neukirchen. s/\bbind\b(.*)\bunmap/unbind-key \1/; s/\bmousebind\b(.*)unmap/unbind-mouse \1/; s/\bresize\b/window-resize/; s/\bresizedown\b/window-resize-down/; s/\bresizeleft\b/window-resize-left/; s/\bresizeright\b/window-resize-right/; s/\bresizeup\b/window-resize-up/; s/\bmove\b/window-move/; s/\bmovedown\b/window-move-down/; s/\bmoveleft\b/window-move-left/; s/\bmoveright\b/window-move-right/; s/\bmoveup\b/window-move-up/; s/\bbigmovedown\b/window-move-down-big/; s/\bbigmoveleft\b/window-move-left-big/; s/\bbigmoveright\b/window-move-right-big/; s/\bbigmoveup\b/window-move-up-big/; s/\bbigptrmovedown\b/pointer-move-down-big/; s/\bbigptrmoveleft\b/pointer-move-left-big/; s/\bbigptrmoveright\b/pointer-move-right-big/; s/\bbigptrmoveup\b/pointer-move-up-big/; s/\bbigresizedown\b/window-resize-down-big/; s/\bbigresizeleft\b/window-resize-left-big/; s/\bbigresizeright\b/window-resize-right-big/; s/\bbigresizeup\b/window-resize-up-big/; s/\bbind\b/bind-key/; s/\bcycle\b/window-cycle/; s/\bcyclegroup\b/group-cycle/; s/\bcycleingroup\b/window-cycle-ingroup/; s/\bdelete\b/window-delete/; s/\bexec\b/menu-exec/; s/\bexec_wm\b/menu-exec-wm/; s/\bfreeze\b/window-freeze/; s/\bfullscreen\b/window-fullscreen/; s/\bgroupsearch\b/menu-group/; s/\bgrouptoggle\b/window-group/; s/\bhide\b/window-hide/; s/\bhmaximize\b/window-hmaximize/; s/\bhtile\b/window-htile/; s/\blabel\b/window-menu-label/; s/\blower\b/window-lower/; s/\bmaximize\b/window-maximize/; s/\bmenu_cmd\b/menu-cmd/; s/\bmenu_group\b/menu-group/; s/\bmenu_unhide\b/menu-window/; s/\bmenusearch\b/menu-cmd/; s/\bmousebind\b/bind-mouse/; s/\bnogroup\b/group-toggle-all/; s/\bptrmovedown\b/pointer-move-down/; s/\bptrmoveleft\b/pointer-move-left/; s/\bptrmoveright\b/pointer-move-right/; s/\bptrmoveup\b/pointer-move-up/; s/\braise\b/window-raise/; s/\brcycle\b/window-rcycle/; s/\brcyclegroup\b/group-rcycle/; s/\brcycleingroup\b/window-rcycle-ingroup/; s/\bsearch\b/menu-window/; s/\bssh\b/menu-ssh/; s/\bstick\b/window-stick/; s/\bvmaximize\b/window-vmaximize/; s/\bvtile\b/window-vtile/; s/\bwindow_grouptoggle\b/window-group/; s/\bwindow_move\b/window-move/; s/\bwindow_resize\b/window-resize/; s/\bwindow_hide\b/window-hide/; s/\bwindow_lower\b/window-lower/; s/\bwindow_raise\b/window-raise/; s/\bmovetogroup([1-9])\b/window-movetogroup-\1/; s/\bgrouponly([1-9])\b/group-only-\1/; s/\bgroup([1-9])\b/group-toggle-\1/; cwm-7.1/parse.y000066400000000000000000000316701423325063300134630ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. * Copyright (c) 2001 Theo de Raadt. All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ %{ #include #include "queue.h" #include #include #include #include #include #include #include #include #include "calmwm.h" #define YYSTYPE_IS_DECLARED TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); static struct file { TAILQ_ENTRY(file) entry; FILE *stream; char *name; int lineno; int errors; } *file, *topfile; struct file *pushfile(const char *, FILE *); int popfile(void); int yyparse(void); int yylex(void); int yyerror(const char *, ...) __attribute__((__format__ (printf, 1, 2))) __attribute__((__nonnull__ (1))); int kw_cmp(const void *, const void *); int lookup(char *); int lgetc(int); int lungetc(int); int findeol(void); static struct conf *conf; typedef struct { union { int64_t number; char *string; } v; int lineno; } YYSTYPE; %} %token BINDKEY UNBINDKEY BINDMOUSE UNBINDMOUSE %token FONTNAME STICKY GAP %token AUTOGROUP COMMAND IGNORE WM %token YES NO BORDERWIDTH MOVEAMOUNT HTILE VTILE %token COLOR SNAPDIST %token ACTIVEBORDER INACTIVEBORDER URGENCYBORDER %token GROUPBORDER UNGROUPBORDER %token MENUBG MENUFG %token FONTCOLOR FONTSELCOLOR %token ERROR %token STRING %token NUMBER %type yesno %type string numberstring %% grammar : /* empty */ | grammar '\n' | grammar main '\n' | grammar color '\n' | grammar error '\n' { file->errors++; } ; string : string STRING { if (asprintf(&$$, "%s %s", $1, $2) == -1) { free($1); free($2); yyerror("string: asprintf"); YYERROR; } free($1); free($2); } | STRING ; numberstring : NUMBER { char *s; if (asprintf(&s, "%lld", $1) == -1) { yyerror("string: asprintf"); YYERROR; } $$ = s; } | STRING ; yesno : YES { $$ = 1; } | NO { $$ = 0; } ; main : FONTNAME STRING { free(conf->font); conf->font = $2; } | STICKY yesno { conf->stickygroups = $2; } | BORDERWIDTH NUMBER { if ($2 < 0 || $2 > INT_MAX) { yyerror("invalid borderwidth"); YYERROR; } conf->bwidth = $2; } | HTILE NUMBER { if ($2 < 0 || $2 > 99) { yyerror("invalid htile percent"); YYERROR; } conf->htile = $2; } | VTILE NUMBER { if ($2 < 0 || $2 > 99) { yyerror("invalid vtile percent"); YYERROR; } conf->vtile = $2; } | MOVEAMOUNT NUMBER { if ($2 < 0 || $2 > INT_MAX) { yyerror("invalid movemount"); YYERROR; } conf->mamount = $2; } | SNAPDIST NUMBER { if ($2 < 0 || $2 > INT_MAX) { yyerror("invalid snapdist"); YYERROR; } conf->snapdist = $2; } | COMMAND STRING string { if (strlen($3) >= PATH_MAX) { yyerror("%s command path too long", $2); free($2); free($3); YYERROR; } conf_cmd_add(conf, $2, $3); free($2); free($3); } | WM STRING string { if (strlen($3) >= PATH_MAX) { yyerror("%s wm path too long", $2); free($2); free($3); YYERROR; } conf_wm_add(conf, $2, $3); free($2); free($3); } | AUTOGROUP NUMBER STRING { if ($2 < 0 || $2 > 9) { yyerror("invalid autogroup"); free($3); YYERROR; } conf_autogroup(conf, $2, NULL, $3); free($3); } | AUTOGROUP NUMBER STRING ',' STRING { if ($2 < 0 || $2 > 9) { yyerror("invalid autogroup"); free($3); free($5); YYERROR; } conf_autogroup(conf, $2, $3, $5); free($3); free($5); } | IGNORE STRING { conf_ignore(conf, $2); free($2); } | GAP NUMBER NUMBER NUMBER NUMBER { if ($2 < 0 || $2 > INT_MAX || $3 < 0 || $3 > INT_MAX || $4 < 0 || $4 > INT_MAX || $5 < 0 || $5 > INT_MAX) { yyerror("invalid gap"); YYERROR; } conf->gap.top = $2; conf->gap.bottom = $3; conf->gap.left = $4; conf->gap.right = $5; } | BINDKEY numberstring string { if (!conf_bind_key(conf, $2, $3)) { yyerror("invalid bind-key: %s %s", $2, $3); free($2); free($3); YYERROR; } free($2); free($3); } | UNBINDKEY numberstring { if (!conf_bind_key(conf, $2, NULL)) { yyerror("invalid unbind-key: %s", $2); free($2); YYERROR; } free($2); } | BINDMOUSE numberstring string { if (!conf_bind_mouse(conf, $2, $3)) { yyerror("invalid bind-mouse: %s %s", $2, $3); free($2); free($3); YYERROR; } free($2); free($3); } | UNBINDMOUSE numberstring { if (!conf_bind_mouse(conf, $2, NULL)) { yyerror("invalid unbind-mouse: %s", $2); free($2); YYERROR; } free($2); } ; color : COLOR colors ; colors : ACTIVEBORDER STRING { free(conf->color[CWM_COLOR_BORDER_ACTIVE]); conf->color[CWM_COLOR_BORDER_ACTIVE] = $2; } | INACTIVEBORDER STRING { free(conf->color[CWM_COLOR_BORDER_INACTIVE]); conf->color[CWM_COLOR_BORDER_INACTIVE] = $2; } | URGENCYBORDER STRING { free(conf->color[CWM_COLOR_BORDER_URGENCY]); conf->color[CWM_COLOR_BORDER_URGENCY] = $2; } | GROUPBORDER STRING { free(conf->color[CWM_COLOR_BORDER_GROUP]); conf->color[CWM_COLOR_BORDER_GROUP] = $2; } | UNGROUPBORDER STRING { free(conf->color[CWM_COLOR_BORDER_UNGROUP]); conf->color[CWM_COLOR_BORDER_UNGROUP] = $2; } | MENUBG STRING { free(conf->color[CWM_COLOR_MENU_BG]); conf->color[CWM_COLOR_MENU_BG] = $2; } | MENUFG STRING { free(conf->color[CWM_COLOR_MENU_FG]); conf->color[CWM_COLOR_MENU_FG] = $2; } | FONTCOLOR STRING { free(conf->color[CWM_COLOR_MENU_FONT]); conf->color[CWM_COLOR_MENU_FONT] = $2; } | FONTSELCOLOR STRING { free(conf->color[CWM_COLOR_MENU_FONT_SEL]); conf->color[CWM_COLOR_MENU_FONT_SEL] = $2; } ; %% struct keywords { const char *k_name; int k_val; }; int yyerror(const char *fmt, ...) { va_list ap; file->errors++; va_start(ap, fmt); fprintf(stderr, "%s:%d: ", file->name, yylval.lineno); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); return (0); } int kw_cmp(const void *k, const void *e) { return (strcmp(k, ((const struct keywords *)e)->k_name)); } int lookup(char *s) { /* this has to be sorted always */ static const struct keywords keywords[] = { { "activeborder", ACTIVEBORDER}, { "autogroup", AUTOGROUP}, { "bind-key", BINDKEY}, { "bind-mouse", BINDMOUSE}, { "borderwidth", BORDERWIDTH}, { "color", COLOR}, { "command", COMMAND}, { "font", FONTCOLOR}, { "fontname", FONTNAME}, { "gap", GAP}, { "groupborder", GROUPBORDER}, { "htile", HTILE}, { "ignore", IGNORE}, { "inactiveborder", INACTIVEBORDER}, { "menubg", MENUBG}, { "menufg", MENUFG}, { "moveamount", MOVEAMOUNT}, { "no", NO}, { "selfont", FONTSELCOLOR}, { "snapdist", SNAPDIST}, { "sticky", STICKY}, { "unbind-key", UNBINDKEY}, { "unbind-mouse", UNBINDMOUSE}, { "ungroupborder", UNGROUPBORDER}, { "urgencyborder", URGENCYBORDER}, { "vtile", VTILE}, { "wm", WM}, { "yes", YES} }; const struct keywords *p; p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), sizeof(keywords[0]), kw_cmp); if (p) return (p->k_val); else return (STRING); } #define MAXPUSHBACK 128 char *parsebuf; int parseindex; char pushback_buffer[MAXPUSHBACK]; int pushback_index = 0; int lgetc(int quotec) { int c, next; if (parsebuf) { /* Read character from the parsebuffer instead of input. */ if (parseindex >= 0) { c = (unsigned char)parsebuf[parseindex++]; if (c != '\0') return (c); parsebuf = NULL; } else parseindex++; } if (pushback_index) return ((unsigned char)pushback_buffer[--pushback_index]); if (quotec) { if ((c = getc(file->stream)) == EOF) { yyerror("reached end of file while parsing " "quoted string"); if (file == topfile || popfile() == EOF) return (EOF); return (quotec); } return (c); } while ((c = getc(file->stream)) == '\\') { next = getc(file->stream); if (next != '\n') { c = next; break; } yylval.lineno = file->lineno; file->lineno++; } while (c == EOF) { if (file == topfile || popfile() == EOF) return (EOF); c = getc(file->stream); } return (c); } int lungetc(int c) { if (c == EOF) return (EOF); if (parsebuf) { parseindex--; if (parseindex >= 0) return (c); } if (pushback_index + 1 >= MAXPUSHBACK) return (EOF); pushback_buffer[pushback_index++] = c; return (c); } int findeol(void) { int c; parsebuf = NULL; /* skip to either EOF or the first real EOL */ while (1) { if (pushback_index) c = (unsigned char)pushback_buffer[--pushback_index]; else c = lgetc(0); if (c == '\n') { file->lineno++; break; } if (c == EOF) break; } return (ERROR); } int yylex(void) { char buf[8096]; char *p; int quotec, next, c; int token; p = buf; while ((c = lgetc(0)) == ' ' || c == '\t') ; /* nothing */ yylval.lineno = file->lineno; if (c == '#') while ((c = lgetc(0)) != '\n' && c != EOF) ; /* nothing */ switch (c) { case '\'': case '"': quotec = c; while (1) { if ((c = lgetc(quotec)) == EOF) return (0); if (c == '\n') { file->lineno++; continue; } else if (c == '\\') { if ((next = lgetc(quotec)) == EOF) return (0); if (next == quotec || next == ' ' || next == '\t') c = next; else if (next == '\n') { file->lineno++; continue; } else lungetc(next); } else if (c == quotec) { *p = '\0'; break; } else if (c == '\0') { yyerror("syntax error"); return (findeol()); } if (p + 1 >= buf + sizeof(buf) - 1) { yyerror("string too long"); return (findeol()); } *p++ = (char)c; } yylval.v.string = xstrdup(buf); return (STRING); } #define allowed_to_end_number(x) \ (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') if (c == '-' || isdigit(c)) { do { *p++ = c; if ((size_t)(p-buf) >= sizeof(buf)) { yyerror("string too long"); return (findeol()); } } while ((c = lgetc(0)) != EOF && isdigit(c)); lungetc(c); if (p == buf + 1 && buf[0] == '-') goto nodigits; if (c == EOF || allowed_to_end_number(c)) { const char *errstr = NULL; *p = '\0'; yylval.v.number = strtonum(buf, LLONG_MIN, LLONG_MAX, &errstr); if (errstr) { yyerror("\"%s\" invalid number: %s", buf, errstr); return (findeol()); } return (NUMBER); } else { nodigits: while (p > buf + 1) lungetc((unsigned char)*--p); c = (unsigned char)*--p; if (c == '-') return (c); } } /* Similar to other parse.y copies, but also allows '/' in strings */ #define allowed_in_string(x) \ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ x != '{' && x != '}' && x != '<' && x != '>' && \ x != '!' && x != '=' && x != '#' && x != ',')) if (isalnum(c) || c == ':' || c == '_' || c == '*' || c == '/') { do { *p++ = c; if ((size_t)(p-buf) >= sizeof(buf)) { yyerror("string too long"); return (findeol()); } } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); lungetc(c); *p = '\0'; if ((token = lookup(buf)) == STRING) yylval.v.string = xstrdup(buf); return (token); } if (c == '\n') { yylval.lineno = file->lineno; file->lineno++; } if (c == EOF) return (0); return (c); } struct file * pushfile(const char *name, FILE *stream) { struct file *nfile; nfile = xcalloc(1, sizeof(struct file)); nfile->name = xstrdup(name); nfile->stream = stream; nfile->lineno = 1; TAILQ_INSERT_TAIL(&files, nfile, entry); return (nfile); } int popfile(void) { struct file *prev; if ((prev = TAILQ_PREV(file, files, entry)) != NULL) prev->errors += file->errors; TAILQ_REMOVE(&files, file, entry); fclose(file->stream); free(file->name); free(file); file = prev; return (file ? 0 : EOF); } int parse_config(const char *filename, struct conf *xconf) { FILE *stream; int errors = 0; conf = xconf; stream = fopen(filename, "r"); if (stream == NULL) { if (errno == ENOENT) return (0); warn("%s", filename); return (-1); } file = pushfile(filename, stream); topfile = file; yyparse(); errors = file->errors; popfile(); return (errors ? -1 : 0); } cwm-7.1/queue.h000066400000000000000000000534741423325063300134620ustar00rootroot00000000000000/* $OpenBSD: queue.h,v 1.38 2013/07/03 15:05:21 fgsch Exp $ */ /* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ /* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 */ #ifndef _SYS_QUEUE_H_ #define _SYS_QUEUE_H_ /* * This file defines five types of data structures: singly-linked lists, * lists, simple queues, tail queues, and circular queues. * * * A singly-linked list is headed by a single forward pointer. The elements * are singly linked for minimum space and pointer manipulation overhead at * the expense of O(n) removal for arbitrary elements. New elements can be * added to the list after an existing element or at the head of the list. * Elements being removed from the head of the list should use the explicit * macro for this purpose for optimum efficiency. A singly-linked list may * only be traversed in the forward direction. Singly-linked lists are ideal * for applications with large datasets and few or no removals or for * implementing a LIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A simple queue is headed by a pair of pointers, one the head of the * list and the other to the tail of the list. The elements are singly * linked to save space, so elements can only be removed from the * head of the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the * list. A simple queue may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * A circle queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the list. * A circle queue may be traversed in either direction, but has a more * complex end of list detection. * * For details on the use of these macros, see the queue(3) manual page. */ #if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) #define _Q_INVALIDATE(a) (a) = ((void *)-1) #else #define _Q_INVALIDATE(a) #endif /* * Singly-linked List definitions. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } /* * Singly-linked List access methods. */ #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_END(head) NULL #define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) #define SLIST_FOREACH(var, head, field) \ for((var) = SLIST_FIRST(head); \ (var) != SLIST_END(head); \ (var) = SLIST_NEXT(var, field)) #define SLIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SLIST_FIRST(head); \ (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ (var) = (tvar)) /* * Singly-linked List functions. */ #define SLIST_INIT(head) { \ SLIST_FIRST(head) = SLIST_END(head); \ } #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ (elm)->field.sle_next = (slistelm)->field.sle_next; \ (slistelm)->field.sle_next = (elm); \ } while (0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ (elm)->field.sle_next = (head)->slh_first; \ (head)->slh_first = (elm); \ } while (0) #define SLIST_REMOVE_AFTER(elm, field) do { \ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ } while (0) #define SLIST_REMOVE_HEAD(head, field) do { \ (head)->slh_first = (head)->slh_first->field.sle_next; \ } while (0) #define SLIST_REMOVE(head, elm, type, field) do { \ if ((head)->slh_first == (elm)) { \ SLIST_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->slh_first; \ \ while (curelm->field.sle_next != (elm)) \ curelm = curelm->field.sle_next; \ curelm->field.sle_next = \ curelm->field.sle_next->field.sle_next; \ _Q_INVALIDATE((elm)->field.sle_next); \ } \ } while (0) /* * List definitions. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List access methods */ #define LIST_FIRST(head) ((head)->lh_first) #define LIST_END(head) NULL #define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) #define LIST_NEXT(elm, field) ((elm)->field.le_next) #define LIST_FOREACH(var, head, field) \ for((var) = LIST_FIRST(head); \ (var)!= LIST_END(head); \ (var) = LIST_NEXT(var, field)) #define LIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = LIST_FIRST(head); \ (var) && ((tvar) = LIST_NEXT(var, field), 1); \ (var) = (tvar)) /* * List functions. */ #define LIST_INIT(head) do { \ LIST_FIRST(head) = LIST_END(head); \ } while (0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ &(elm)->field.le_next; \ (listelm)->field.le_next = (elm); \ (elm)->field.le_prev = &(listelm)->field.le_next; \ } while (0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.le_prev = (listelm)->field.le_prev; \ (elm)->field.le_next = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &(elm)->field.le_next; \ } while (0) #define LIST_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } while (0) #define LIST_REMOVE(elm, field) do { \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ _Q_INVALIDATE((elm)->field.le_prev); \ _Q_INVALIDATE((elm)->field.le_next); \ } while (0) #define LIST_REPLACE(elm, elm2, field) do { \ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ (elm2)->field.le_next->field.le_prev = \ &(elm2)->field.le_next; \ (elm2)->field.le_prev = (elm)->field.le_prev; \ *(elm2)->field.le_prev = (elm2); \ _Q_INVALIDATE((elm)->field.le_prev); \ _Q_INVALIDATE((elm)->field.le_next); \ } while (0) /* * Simple queue definitions. */ #define SIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqh_first; /* first element */ \ struct type **sqh_last; /* addr of last next element */ \ } #define SIMPLEQ_HEAD_INITIALIZER(head) \ { NULL, &(head).sqh_first } #define SIMPLEQ_ENTRY(type) \ struct { \ struct type *sqe_next; /* next element */ \ } /* * Simple queue access methods. */ #define SIMPLEQ_FIRST(head) ((head)->sqh_first) #define SIMPLEQ_END(head) NULL #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) #define SIMPLEQ_FOREACH(var, head, field) \ for((var) = SIMPLEQ_FIRST(head); \ (var) != SIMPLEQ_END(head); \ (var) = SIMPLEQ_NEXT(var, field)) #define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SIMPLEQ_FIRST(head); \ (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ (var) = (tvar)) /* * Simple queue functions. */ #define SIMPLEQ_INIT(head) do { \ (head)->sqh_first = NULL; \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ (head)->sqh_first = (elm); \ } while (0) #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqe_next = NULL; \ *(head)->sqh_last = (elm); \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ (head)->sqh_last = &(elm)->field.sqe_next; \ (listelm)->field.sqe_next = (elm); \ } while (0) #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) /* * XOR Simple queue definitions. */ #define XSIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqx_first; /* first element */ \ struct type **sqx_last; /* addr of last next element */ \ unsigned long sqx_cookie; \ } #define XSIMPLEQ_ENTRY(type) \ struct { \ struct type *sqx_next; /* next element */ \ } /* * XOR Simple queue access methods. */ #define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ (unsigned long)(ptr))) #define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) #define XSIMPLEQ_END(head) NULL #define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) #define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) #define XSIMPLEQ_FOREACH(var, head, field) \ for ((var) = XSIMPLEQ_FIRST(head); \ (var) != XSIMPLEQ_END(head); \ (var) = XSIMPLEQ_NEXT(head, var, field)) #define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = XSIMPLEQ_FIRST(head); \ (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ (var) = (tvar)) /* * XOR Simple queue functions. */ #define XSIMPLEQ_INIT(head) do { \ arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ } while (0) #define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqx_next = (head)->sqx_first) == \ XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ } while (0) #define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ } while (0) #define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ } while (0) #define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ } while (0) #define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ (elm)->field.sqx_next)->field.sqx_next) \ == XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = \ XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ } while (0) /* * Tail queue definitions. */ #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).tqh_first } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ } /* * tail queue access methods */ #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_END(head) NULL #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) /* XXX */ #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_EMPTY(head) \ (TAILQ_FIRST(head) == TAILQ_END(head)) #define TAILQ_FOREACH(var, head, field) \ for((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head); \ (var) = TAILQ_NEXT(var, field)) #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head) && \ ((tvar) = TAILQ_NEXT(var, field), 1); \ (var) = (tvar)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head); \ (var) = TAILQ_PREV(var, headname, field)) #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ for ((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head) && \ ((tvar) = TAILQ_PREV(var, headname, field), 1); \ (var) = (tvar)) /* * Tail queue functions. */ #define TAILQ_INIT(head) do { \ (head)->tqh_first = NULL; \ (head)->tqh_last = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ (elm)->field.tqe_next = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_REMOVE(head, elm, field) do { \ if (((elm)->field.tqe_next) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ _Q_INVALIDATE((elm)->field.tqe_prev); \ _Q_INVALIDATE((elm)->field.tqe_next); \ } while (0) #define TAILQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ (elm2)->field.tqe_next->field.tqe_prev = \ &(elm2)->field.tqe_next; \ else \ (head)->tqh_last = &(elm2)->field.tqe_next; \ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ *(elm2)->field.tqe_prev = (elm2); \ _Q_INVALIDATE((elm)->field.tqe_prev); \ _Q_INVALIDATE((elm)->field.tqe_next); \ } while (0) /* * Circular queue definitions. */ #define CIRCLEQ_HEAD(name, type) \ struct name { \ struct type *cqh_first; /* first element */ \ struct type *cqh_last; /* last element */ \ } #define CIRCLEQ_HEAD_INITIALIZER(head) \ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } #define CIRCLEQ_ENTRY(type) \ struct { \ struct type *cqe_next; /* next element */ \ struct type *cqe_prev; /* previous element */ \ } /* * Circular queue access methods */ #define CIRCLEQ_FIRST(head) ((head)->cqh_first) #define CIRCLEQ_LAST(head) ((head)->cqh_last) #define CIRCLEQ_END(head) ((void *)(head)) #define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) #define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) #define CIRCLEQ_EMPTY(head) \ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) #define CIRCLEQ_FOREACH(var, head, field) \ for((var) = CIRCLEQ_FIRST(head); \ (var) != CIRCLEQ_END(head); \ (var) = CIRCLEQ_NEXT(var, field)) #define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = CIRCLEQ_FIRST(head); \ (var) != CIRCLEQ_END(head) && \ ((tvar) = CIRCLEQ_NEXT(var, field), 1); \ (var) = (tvar)) #define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ for((var) = CIRCLEQ_LAST(head); \ (var) != CIRCLEQ_END(head); \ (var) = CIRCLEQ_PREV(var, field)) #define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ for ((var) = CIRCLEQ_LAST(head, headname); \ (var) != CIRCLEQ_END(head) && \ ((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \ (var) = (tvar)) /* * Circular queue functions. */ #define CIRCLEQ_INIT(head) do { \ (head)->cqh_first = CIRCLEQ_END(head); \ (head)->cqh_last = CIRCLEQ_END(head); \ } while (0) #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm)->field.cqe_next; \ (elm)->field.cqe_prev = (listelm); \ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm); \ else \ (listelm)->field.cqe_next->field.cqe_prev = (elm); \ (listelm)->field.cqe_next = (elm); \ } while (0) #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm); \ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm); \ else \ (listelm)->field.cqe_prev->field.cqe_next = (elm); \ (listelm)->field.cqe_prev = (elm); \ } while (0) #define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ (elm)->field.cqe_next = (head)->cqh_first; \ (elm)->field.cqe_prev = CIRCLEQ_END(head); \ if ((head)->cqh_last == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm); \ else \ (head)->cqh_first->field.cqe_prev = (elm); \ (head)->cqh_first = (elm); \ } while (0) #define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.cqe_next = CIRCLEQ_END(head); \ (elm)->field.cqe_prev = (head)->cqh_last; \ if ((head)->cqh_first == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm); \ else \ (head)->cqh_last->field.cqe_next = (elm); \ (head)->cqh_last = (elm); \ } while (0) #define CIRCLEQ_REMOVE(head, elm, field) do { \ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm)->field.cqe_prev; \ else \ (elm)->field.cqe_next->field.cqe_prev = \ (elm)->field.cqe_prev; \ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm)->field.cqe_next; \ else \ (elm)->field.cqe_prev->field.cqe_next = \ (elm)->field.cqe_next; \ _Q_INVALIDATE((elm)->field.cqe_prev); \ _Q_INVALIDATE((elm)->field.cqe_next); \ } while (0) #define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ CIRCLEQ_END(head)) \ (head)->cqh_last = (elm2); \ else \ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ CIRCLEQ_END(head)) \ (head)->cqh_first = (elm2); \ else \ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ _Q_INVALIDATE((elm)->field.cqe_prev); \ _Q_INVALIDATE((elm)->field.cqe_next); \ } while (0) #endif /* !_SYS_QUEUE_H_ */ cwm-7.1/reallocarray.c000066400000000000000000000025201423325063300147730ustar00rootroot00000000000000/* $OpenBSD: reallocarray.c,v 1.2 2014/12/08 03:45:00 bcook Exp $ */ /* * Copyright (c) 2008 Otto Moerbeek * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include /* * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW */ #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) void * reallocarray(void *optr, size_t nmemb, size_t size) { if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && nmemb > 0 && SIZE_MAX / nmemb < size) { errno = ENOMEM; return NULL; } return realloc(optr, size * nmemb); } cwm-7.1/screen.c000066400000000000000000000163671423325063300136100ustar00rootroot00000000000000/* * calmwm - the calm window manager * * Copyright (c) 2004 Marius Aamodt Eriksen * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD$ */ #include #include "queue.h" #include #include #include #include #include #include #include #include #include "calmwm.h" static struct geom screen_apply_gap(struct screen_ctx *, struct geom); static void screen_scan(struct screen_ctx *); void screen_init(int which) { struct screen_ctx *sc; XSetWindowAttributes attr; sc = xmalloc(sizeof(*sc)); TAILQ_INIT(&sc->clientq); TAILQ_INIT(&sc->regionq); TAILQ_INIT(&sc->groupq); sc->which = which; sc->rootwin = RootWindow(X_Dpy, sc->which); sc->colormap = DefaultColormap(X_Dpy, sc->which); sc->visual = DefaultVisual(X_Dpy, sc->which); sc->cycling = 0; sc->hideall = 0; conf_screen(sc); xu_ewmh_net_supported(sc); xu_ewmh_net_supported_wm_check(sc); conf_group(sc); sc->group_last = sc->group_active; screen_update_geometry(sc); xu_ewmh_net_desktop_names(sc); xu_ewmh_net_number_of_desktops(sc); xu_ewmh_net_showing_desktop(sc); xu_ewmh_net_virtual_roots(sc); attr.cursor = Conf.cursor[CF_NORMAL]; attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask | EnterWindowMask | PropertyChangeMask | ButtonPressMask; XChangeWindowAttributes(X_Dpy, sc->rootwin, (CWEventMask | CWCursor), &attr); if (Conf.xrandr) XRRSelectInput(X_Dpy, sc->rootwin, RRScreenChangeNotifyMask); screen_scan(sc); screen_updatestackingorder(sc); TAILQ_INSERT_TAIL(&Screenq, sc, entry); XSync(X_Dpy, False); } static void screen_scan(struct screen_ctx *sc) { struct client_ctx *cc, *active = NULL; Window *wins, w0, w1, rwin, cwin; unsigned int nwins, i, mask; int rx, ry, wx, wy; XQueryPointer(X_Dpy, sc->rootwin, &rwin, &cwin, &rx, &ry, &wx, &wy, &mask); if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) { for (i = 0; i < nwins; i++) { if ((cc = client_init(wins[i], sc)) != NULL) if (cc->win == cwin) active = cc; } XFree(wins); } if (active) client_set_active(active); } struct screen_ctx * screen_find(Window win) { struct screen_ctx *sc; TAILQ_FOREACH(sc, &Screenq, entry) { if (sc->rootwin == win) return sc; } warnx("%s: failure win 0x%lx", __func__, win); return NULL; } void screen_updatestackingorder(struct screen_ctx *sc) { Window *wins, w0, w1; struct client_ctx *cc; unsigned int nwins, i, s; if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) { for (s = 0, i = 0; i < nwins; i++) { /* Skip hidden windows */ if ((cc = client_find(wins[i])) == NULL || cc->flags & CLIENT_HIDDEN) continue; cc->stackingorder = s++; } XFree(wins); } } struct region_ctx * region_find(struct screen_ctx *sc, int x, int y) { struct region_ctx *rc; TAILQ_FOREACH(rc, &sc->regionq, entry) { if ((x >= rc->view.x) && (x < (rc->view.x + rc->view.w)) && (y >= rc->view.y) && (y < (rc->view.y + rc->view.h))) { break; } } return rc; } struct geom screen_area(struct screen_ctx *sc, int x, int y, int apply_gap) { struct region_ctx *rc; struct geom area = sc->view; TAILQ_FOREACH(rc, &sc->regionq, entry) { if ((x >= rc->view.x) && (x < (rc->view.x + rc->view.w)) && (y >= rc->view.y) && (y < (rc->view.y + rc->view.h))) { area = rc->view; break; } } if (apply_gap) area = screen_apply_gap(sc, area); return area; } void screen_update_geometry(struct screen_ctx *sc) { struct region_ctx *rc; sc->view.x = 0; sc->view.y = 0; sc->view.w = DisplayWidth(X_Dpy, sc->which); sc->view.h = DisplayHeight(X_Dpy, sc->which); sc->work = screen_apply_gap(sc, sc->view); while ((rc = TAILQ_FIRST(&sc->regionq)) != NULL) { TAILQ_REMOVE(&sc->regionq, rc, entry); free(rc); } if (Conf.xrandr) { XRRScreenResources *sr; XRRCrtcInfo *ci; int i; sr = XRRGetScreenResources(X_Dpy, sc->rootwin); for (i = 0, ci = NULL; i < sr->ncrtc; i++) { ci = XRRGetCrtcInfo(X_Dpy, sr, sr->crtcs[i]); if (ci == NULL) continue; if (ci->noutput == 0) { XRRFreeCrtcInfo(ci); continue; } rc = xmalloc(sizeof(*rc)); rc->num = i; rc->view.x = ci->x; rc->view.y = ci->y; rc->view.w = ci->width; rc->view.h = ci->height; rc->work = screen_apply_gap(sc, rc->view); TAILQ_INSERT_TAIL(&sc->regionq, rc, entry); XRRFreeCrtcInfo(ci); } XRRFreeScreenResources(sr); } else { rc = xmalloc(sizeof(*rc)); rc->num = 0; rc->view.x = 0; rc->view.y = 0; rc->view.w = DisplayWidth(X_Dpy, sc->which); rc->view.h = DisplayHeight(X_Dpy, sc->which); rc->work = screen_apply_gap(sc, rc->view); TAILQ_INSERT_TAIL(&sc->regionq, rc, entry); } xu_ewmh_net_desktop_geometry(sc); xu_ewmh_net_desktop_viewport(sc); xu_ewmh_net_workarea(sc); } static struct geom screen_apply_gap(struct screen_ctx *sc, struct geom geom) { geom.x += sc->gap.left; geom.y += sc->gap.top; geom.w -= (sc->gap.left + sc->gap.right); geom.h -= (sc->gap.top + sc->gap.bottom); return geom; } /* Bring back clients which are beyond the screen. */ void screen_assert_clients_within(struct screen_ctx *sc) { struct client_ctx *cc; int top, left, right, bottom; TAILQ_FOREACH(cc, &sc->clientq, entry) { if (cc->sc != sc) continue; top = cc->geom.y; left = cc->geom.x; right = cc->geom.x + cc->geom.w + (cc->bwidth * 2) - 1; bottom = cc->geom.y + cc->geom.h + (cc->bwidth * 2) - 1; if ((top > sc->view.h || left > sc->view.w) || (bottom < 0 || right < 0)) { cc->geom.x = sc->gap.left; cc->geom.y = sc->gap.top; client_move(cc); } } } void screen_prop_win_create(struct screen_ctx *sc, Window win) { sc->prop.win = XCreateSimpleWindow(X_Dpy, win, 0, 0, 1, 1, 0, sc->xftcolor[CWM_COLOR_MENU_BG].pixel, sc->xftcolor[CWM_COLOR_MENU_BG].pixel); sc->prop.xftdraw = XftDrawCreate(X_Dpy, sc->prop.win, sc->visual, sc->colormap); XMapWindow(X_Dpy, sc->prop.win); } void screen_prop_win_destroy(struct screen_ctx *sc) { XftDrawDestroy(sc->prop.xftdraw); XDestroyWindow(X_Dpy, sc->prop.win); } void screen_prop_win_draw(struct screen_ctx *sc, const char *fmt, ...) { va_list ap; char *text; XGlyphInfo extents; va_start(ap, fmt); xvasprintf(&text, fmt, ap); va_end(ap); XftTextExtentsUtf8(X_Dpy, sc->xftfont, (const FcChar8*)text, strlen(text), &extents); XResizeWindow(X_Dpy, sc->prop.win, extents.xOff, sc->xftfont->height); XClearWindow(X_Dpy, sc->prop.win); XftDrawStringUtf8(sc->prop.xftdraw, &sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont, 0, sc->xftfont->ascent + 1, (const FcChar8*)text, strlen(text)); free(text); } cwm-7.1/search.c000066400000000000000000000152171423325063300135670ustar00rootroot00000000000000/* * calmwm - the calm window manager * * Copyright (c) 2004 Marius Aamodt Eriksen * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD$ */ #include #include "queue.h" #include #include #include #include #include #include #include #include #include #include "calmwm.h" #define PATH_ANY 0x0001 #define PATH_EXEC 0x0002 static void match_path_type(struct menu_q *, char *, int); static int match_substr(char *, char *, int); static int match_substr(char *sub, char *str, int zeroidx) { size_t len, sublen; unsigned int n, flen; if (sub == NULL || str == NULL) return 0; len = strlen(str); sublen = strlen(sub); if (sublen > len) return 0; if (zeroidx) flen = 0; else flen = len - sublen; for (n = 0; n <= flen; n++) if (strncasecmp(sub, str + n, sublen) == 0) return 1; return 0; } void search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search) { struct menu *mi, *tierp[3], *before = NULL; struct client_ctx *cc; struct winname *wn; (void)memset(tierp, 0, sizeof(tierp)); TAILQ_INIT(resultq); TAILQ_FOREACH(mi, menuq, entry) { int tier = -1, t; cc = (struct client_ctx *)mi->ctx; /* Match on label. */ if (match_substr(search, cc->label, 0)) tier = 0; /* Match on window name history, from present to past. */ if (tier < 0) { TAILQ_FOREACH_REVERSE(wn, &cc->nameq, name_q, entry) if (match_substr(search, wn->name, 0)) { tier = 1; break; } } /* Match on window resource class. */ if ((tier < 0) && match_substr(search, cc->res_class, 0)) tier = 2; if (tier < 0) continue; /* Current window is ranked down. */ if ((tier < nitems(tierp) - 1) && (cc->flags & CLIENT_ACTIVE)) tier++; /* Hidden window is ranked up. */ if ((tier > 0) && (cc->flags & CLIENT_HIDDEN)) tier--; /* * If you have a tierp, insert after it, and make it * the new tierp. If you don't have a tierp, find the * first nonzero tierp above you, insert after it. * Always make your current tierp the newly inserted * entry. */ for (t = tier; t >= 0 && ((before = tierp[t]) == NULL); t--) ; if (before == NULL) TAILQ_INSERT_HEAD(resultq, mi, resultentry); else TAILQ_INSERT_AFTER(resultq, before, mi, resultentry); tierp[tier] = mi; } } void search_match_cmd(struct menu_q *menuq, struct menu_q *resultq, char *search) { struct menu *mi; struct cmd_ctx *cmd; TAILQ_INIT(resultq); TAILQ_FOREACH(mi, menuq, entry) { cmd = (struct cmd_ctx *)mi->ctx; if (match_substr(search, cmd->name, 0)) TAILQ_INSERT_TAIL(resultq, mi, resultentry); } } void search_match_group(struct menu_q *menuq, struct menu_q *resultq, char *search) { struct menu *mi; struct group_ctx *gc; char *s; TAILQ_INIT(resultq); TAILQ_FOREACH(mi, menuq, entry) { gc = (struct group_ctx *)mi->ctx; xasprintf(&s, "%d %s", gc->num, gc->name); if (match_substr(search, s, 0)) TAILQ_INSERT_TAIL(resultq, mi, resultentry); free(s); } } static void match_path_type(struct menu_q *resultq, char *search, int flag) { struct menu *mi; char *pattern; glob_t g; int i; xasprintf(&pattern, "%s*", search); if (glob(pattern, GLOB_MARK, NULL, &g) != 0) return; for (i = 0; i < g.gl_pathc; i++) { if ((flag & PATH_EXEC) && access(g.gl_pathv[i], X_OK)) continue; mi = xcalloc(1, sizeof(*mi)); (void)strlcpy(mi->text, g.gl_pathv[i], sizeof(mi->text)); TAILQ_INSERT_TAIL(resultq, mi, resultentry); } globfree(&g); free(pattern); } void search_match_exec(struct menu_q *menuq, struct menu_q *resultq, char *search) { struct menu *mi, *mj; int r; TAILQ_INIT(resultq); TAILQ_FOREACH(mi, menuq, entry) { if (match_substr(search, mi->text, 1) == 0 && fnmatch(search, mi->text, 0) == FNM_NOMATCH) continue; TAILQ_FOREACH(mj, resultq, resultentry) { r = strcmp(mi->text, mj->text); if (r < 0) TAILQ_INSERT_BEFORE(mj, mi, resultentry); if (r <= 0) break; } if (mj == NULL) TAILQ_INSERT_TAIL(resultq, mi, resultentry); } if (TAILQ_EMPTY(resultq)) match_path_type(resultq, search, PATH_EXEC); } void search_match_path(struct menu_q *menuq, struct menu_q *resultq, char *search) { TAILQ_INIT(resultq); match_path_type(resultq, search, PATH_ANY); } void search_match_text(struct menu_q *menuq, struct menu_q *resultq, char *search) { struct menu *mi; TAILQ_INIT(resultq); TAILQ_FOREACH(mi, menuq, entry) { if (match_substr(search, mi->text, 0)) TAILQ_INSERT_TAIL(resultq, mi, resultentry); } } void search_match_wm(struct menu_q *menuq, struct menu_q *resultq, char *search) { struct menu *mi; struct cmd_ctx *wm; TAILQ_INIT(resultq); TAILQ_FOREACH(mi, menuq, entry) { wm = (struct cmd_ctx *)mi->ctx; if ((match_substr(search, wm->name, 0)) || (match_substr(search, wm->path, 0))) TAILQ_INSERT_TAIL(resultq, mi, resultentry); } } void search_print_client(struct menu *mi, int listing) { struct client_ctx *cc = (struct client_ctx *)mi->ctx; char flag = ' '; if (cc->flags & CLIENT_ACTIVE) flag = '!'; else if (cc->flags & CLIENT_HIDDEN) flag = '&'; (void)snprintf(mi->print, sizeof(mi->print), "(%d) %c[%s] %s", (cc->gc) ? cc->gc->num : 0, flag, (cc->label) ? cc->label : "", cc->name); } void search_print_cmd(struct menu *mi, int listing) { struct cmd_ctx *cmd = (struct cmd_ctx *)mi->ctx; (void)snprintf(mi->print, sizeof(mi->print), "%s", cmd->name); } void search_print_group(struct menu *mi, int listing) { struct group_ctx *gc = (struct group_ctx *)mi->ctx; (void)snprintf(mi->print, sizeof(mi->print), (group_holds_only_hidden(gc)) ? "%d: [%s]" : "%d: %s", gc->num, gc->name); } void search_print_text(struct menu *mi, int listing) { (void)snprintf(mi->print, sizeof(mi->print), "%s", mi->text); } void search_print_wm(struct menu *mi, int listing) { struct cmd_ctx *wm = (struct cmd_ctx *)mi->ctx; (void)snprintf(mi->print, sizeof(mi->print), "%s [%s]", wm->name, wm->path); } cwm-7.1/strlcat.c000066400000000000000000000035111423325063300137700ustar00rootroot00000000000000/* $OpenBSD: strlcat.c,v 1.14 2015/01/15 03:54:12 millert Exp $ */ /* * Copyright (c) 1998, 2015 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */ #include #include #include "calmwm.h" #ifndef HAVE_STRLCAT /* * Appends src to string dst of size dsize (unlike strncat, dsize is the * full size of dst, not space left). At most dsize-1 characters * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). * Returns strlen(src) + MIN(dsize, strlen(initial dst)). * If retval >= siz, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t dsize) { const char *odst = dst; const char *osrc = src; size_t n = dsize; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end. */ while (n-- != 0 && *dst != '\0') dst++; dlen = dst - odst; n = dsize - dlen; if (n-- == 0) return(dlen + strlen(src)); while (*src != '\0') { if (n != 0) { *dst++ = *src; n--; } src++; } *dst = '\0'; return(dlen + (src - osrc)); /* count does not include NUL */ } #endif /* !HAVE_STRLCAT */ cwm-7.1/strlcpy.c000066400000000000000000000032701423325063300140160ustar00rootroot00000000000000/* $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $ */ /* * Copyright (c) 1998, 2015 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */ #include #include #include "calmwm.h" #ifndef HAVE_STRLCPY /* * Copy string src to buffer dst of size dsize. At most dsize-1 * chars will be copied. Always NUL terminates (unless dsize == 0). * Returns strlen(src); if retval >= dsize, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t dsize) { const char *osrc = src; size_t nleft = dsize; /* Copy as many bytes as will fit. */ if (nleft != 0) { while (--nleft != 0) { if ((*dst++ = *src++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src. */ if (nleft == 0) { if (dsize != 0) *dst = '\0'; /* NUL-terminate dst */ while (*src++) ; } return(src - osrc - 1); /* count does not include NUL */ } #endif /* !HAVE_STRLCPY */ cwm-7.1/strtonum.c000066400000000000000000000035161423325063300142140ustar00rootroot00000000000000/* $OpenBSD: strtonum.c,v 1.7 2013/04/17 18:40:58 tedu Exp $ */ /* * Copyright (c) 2004 Ted Unangst and Todd Miller * All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* OPENBSD ORIGINAL: lib/libc/stdlib/strtonum.c */ #ifndef HAVE_STRTONUM #include #include #include #define INVALID 1 #define TOOSMALL 2 #define TOOLARGE 3 long long strtonum(const char *numstr, long long minval, long long maxval, const char **errstrp) { long long ll = 0; int error = 0; char *ep; struct errval { const char *errstr; int err; } ev[4] = { { NULL, 0 }, { "invalid", EINVAL }, { "too small", ERANGE }, { "too large", ERANGE }, }; ev[0].err = errno; errno = 0; if (minval > maxval) { error = INVALID; } else { ll = strtoll(numstr, &ep, 10); if (numstr == ep || *ep != '\0') error = INVALID; else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) error = TOOSMALL; else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) error = TOOLARGE; } if (errstrp != NULL) *errstrp = ev[error].errstr; errno = ev[error].err; if (error) ll = 0; return (ll); } #endif /* HAVE_STRTONUM */ cwm-7.1/update.sh000077500000000000000000000003211423325063300137650ustar00rootroot00000000000000#GIT_MERGE_AUTOEDIT=no git cvsimport -o master -v -k -m -d anoncvs@openbsd.cs.fau.de:/cvs xenocara/app/cwm GIT_MERGE_AUTOEDIT=no git cvsimport -o master -v -k -m -d anoncvs@mirror.osn.de:/cvs xenocara/app/cwm cwm-7.1/util.c000066400000000000000000000052221423325063300132720ustar00rootroot00000000000000/* * calmwm - the calm window manager * * Copyright (c) 2004 Marius Aamodt Eriksen * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD$ */ #include #include "queue.h" #include #include #include #include #include #include #include #include #include "calmwm.h" static void log_msg(const char *, va_list); void u_spawn(char *argstr) { switch (fork()) { case 0: u_exec(argstr); exit(1); case -1: warn("fork"); default: break; } } void u_exec(char *argstr) { #define MAXARGLEN 20 char *args[MAXARGLEN], **ap = args; char **end = &args[MAXARGLEN - 2], *tmp; char *s = argstr; while (ap < end && (*ap = strsep(&argstr, " \t")) != NULL) { if (**ap == '\0') continue; ap++; if (argstr != NULL) { /* deal with quoted strings */ switch(argstr[0]) { case '"': case '\'': if ((tmp = strchr(argstr + 1, argstr[0])) != NULL) { *(tmp++) = '\0'; *(ap++) = ++argstr; argstr = tmp; } break; default: break; } } } *ap = NULL; (void)setsid(); (void)execvp(args[0], args); warn("%s", s); } char * u_argv(char * const *argv) { size_t siz = 0; int i; char *p; if (argv == 0) return NULL; for (i = 0; argv[i]; i++) siz += strlen(argv[i]) + 1; if (siz == 0) return NULL; p = xmalloc(siz); strlcpy(p, argv[0], siz); for (i = 1; argv[i]; i++) { strlcat(p, " ", siz); strlcat(p, argv[i], siz); } return p; } static void log_msg(const char *msg, va_list ap) { char *fmt; if (asprintf(&fmt, "%s\n", msg) == -1) { vfprintf(stderr, msg, ap); fprintf(stderr, "\n"); } else { vfprintf(stderr, fmt, ap); free(fmt); } fflush(stderr); } void log_debug(int level, const char *func, const char *msg, ...) { char *fmt; va_list ap; if (Conf.debug < level) return; va_start(ap, msg); xasprintf(&fmt, "debug%d: %s: %s", level, func, msg); log_msg(fmt, ap); free(fmt); va_end(ap); } cwm-7.1/xevents.c000066400000000000000000000276061423325063300140230ustar00rootroot00000000000000/* * calmwm - the calm window manager * * Copyright (c) 2004 Marius Aamodt Eriksen * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD$ */ /* * NOTE: * It is the responsibility of the caller to deal with memory * management of the xevent's. */ #include #include "queue.h" #include #include #include #include #include #include #include #include "calmwm.h" static void xev_handle_maprequest(XEvent *); static void xev_handle_unmapnotify(XEvent *); static void xev_handle_destroynotify(XEvent *); static void xev_handle_configurerequest(XEvent *); static void xev_handle_propertynotify(XEvent *); static void xev_handle_enternotify(XEvent *); static void xev_handle_buttonpress(XEvent *); static void xev_handle_buttonrelease(XEvent *); static void xev_handle_keypress(XEvent *); static void xev_handle_keyrelease(XEvent *); static void xev_handle_clientmessage(XEvent *); static void xev_handle_randr(XEvent *); static void xev_handle_mappingnotify(XEvent *); static void xev_handle_expose(XEvent *); void (*xev_handlers[LASTEvent])(XEvent *) = { [MapRequest] = xev_handle_maprequest, [UnmapNotify] = xev_handle_unmapnotify, [DestroyNotify] = xev_handle_destroynotify, [ConfigureRequest] = xev_handle_configurerequest, [PropertyNotify] = xev_handle_propertynotify, [EnterNotify] = xev_handle_enternotify, [ButtonPress] = xev_handle_buttonpress, [ButtonRelease] = xev_handle_buttonrelease, [KeyPress] = xev_handle_keypress, [KeyRelease] = xev_handle_keyrelease, [ClientMessage] = xev_handle_clientmessage, [MappingNotify] = xev_handle_mappingnotify, [Expose] = xev_handle_expose, }; static KeySym modkeys[] = { XK_Alt_L, XK_Alt_R, XK_Super_L, XK_Super_R, XK_Control_L, XK_Control_R, XK_ISO_Level3_Shift }; static void xev_handle_maprequest(XEvent *ee) { XMapRequestEvent *e = &ee->xmaprequest; struct screen_ctx *sc; struct client_ctx *cc, *old_cc; LOG_DEBUG3("parent: 0x%lx window: 0x%lx", e->parent, e->window); if ((sc = screen_find(e->parent)) == NULL) return; if ((old_cc = client_current(sc)) != NULL) client_ptr_save(old_cc); if ((cc = client_find(e->window)) == NULL) cc = client_init(e->window, NULL); if ((cc != NULL) && (!(cc->flags & CLIENT_IGNORE))) client_ptr_warp(cc); } static void xev_handle_unmapnotify(XEvent *ee) { XUnmapEvent *e = &ee->xunmap; struct client_ctx *cc; LOG_DEBUG3("window: 0x%lx", e->window); if ((cc = client_find(e->window)) != NULL) { if (e->send_event) { xu_set_wm_state(cc->win, WithdrawnState); } else { if (!(cc->flags & CLIENT_HIDDEN)) client_remove(cc); } } } static void xev_handle_destroynotify(XEvent *ee) { XDestroyWindowEvent *e = &ee->xdestroywindow; struct client_ctx *cc; LOG_DEBUG3("window: 0x%lx", e->window); if ((cc = client_find(e->window)) != NULL) client_remove(cc); } static void xev_handle_configurerequest(XEvent *ee) { XConfigureRequestEvent *e = &ee->xconfigurerequest; struct client_ctx *cc; struct screen_ctx *sc; XWindowChanges wc; LOG_DEBUG3("window: 0x%lx", e->window); if ((cc = client_find(e->window)) != NULL) { sc = cc->sc; if (e->value_mask & CWWidth) cc->geom.w = e->width; if (e->value_mask & CWHeight) cc->geom.h = e->height; if (e->value_mask & CWX) cc->geom.x = e->x; if (e->value_mask & CWY) cc->geom.y = e->y; if (e->value_mask & CWBorderWidth) cc->bwidth = e->border_width; if (e->value_mask & CWSibling) wc.sibling = e->above; if (e->value_mask & CWStackMode) wc.stack_mode = e->detail; if (cc->geom.x == 0 && cc->geom.w >= sc->view.w) cc->geom.x -= cc->bwidth; if (cc->geom.y == 0 && cc->geom.h >= sc->view.h) cc->geom.y -= cc->bwidth; wc.x = cc->geom.x; wc.y = cc->geom.y; wc.width = cc->geom.w; wc.height = cc->geom.h; wc.border_width = cc->bwidth; XConfigureWindow(X_Dpy, cc->win, e->value_mask, &wc); client_config(cc); } else { /* let it do what it wants, it'll be ours when we map it. */ wc.x = e->x; wc.y = e->y; wc.width = e->width; wc.height = e->height; wc.border_width = e->border_width; wc.stack_mode = Above; e->value_mask &= ~CWStackMode; XConfigureWindow(X_Dpy, e->window, e->value_mask, &wc); } } static void xev_handle_propertynotify(XEvent *ee) { XPropertyEvent *e = &ee->xproperty; struct screen_ctx *sc; struct client_ctx *cc; LOG_DEBUG3("window: 0x%lx", e->window); if ((cc = client_find(e->window)) != NULL) { switch (e->atom) { case XA_WM_NORMAL_HINTS: client_get_sizehints(cc); break; case XA_WM_NAME: client_set_name(cc); break; case XA_WM_HINTS: client_wm_hints(cc); client_draw_border(cc); break; case XA_WM_TRANSIENT_FOR: client_transient(cc); client_draw_border(cc); if (cc->gc) group_movetogroup(cc, cc->gc->num); break; default: if (e->atom == ewmh[_NET_WM_NAME]) client_set_name(cc); break; } } else { if (e->atom == ewmh[_NET_DESKTOP_NAMES]) { if ((sc = screen_find(e->window)) != NULL) xu_ewmh_net_desktop_names(sc); } } } static void xev_handle_enternotify(XEvent *ee) { XCrossingEvent *e = &ee->xcrossing; struct client_ctx *cc; LOG_DEBUG3("window: 0x%lx", e->window); Last_Event_Time = e->time; if ((cc = client_find(e->window)) != NULL) client_set_active(cc); } static void xev_handle_buttonpress(XEvent *ee) { XButtonEvent *e = &ee->xbutton; struct client_ctx *cc; struct screen_ctx *sc; struct bind_ctx *mb; LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx", e->root, e->window, e->subwindow); if ((sc = screen_find(e->root)) == NULL) return; e->state &= ~IGNOREMODMASK; TAILQ_FOREACH(mb, &Conf.mousebindq, entry) { if (e->button == mb->press.button && e->state == mb->modmask) break; } if (mb == NULL) return; mb->cargs->xev = CWM_XEV_BTN; switch (mb->context) { case CWM_CONTEXT_CC: if (((cc = client_find(e->window)) == NULL) && ((cc = client_current(sc)) == NULL)) return; (*mb->callback)(cc, mb->cargs); break; case CWM_CONTEXT_SC: (*mb->callback)(sc, mb->cargs); break; case CWM_CONTEXT_NONE: (*mb->callback)(NULL, mb->cargs); break; } } static void xev_handle_buttonrelease(XEvent *ee) { XButtonEvent *e = &ee->xbutton; struct client_ctx *cc; LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx", e->root, e->window, e->subwindow); if ((cc = client_find(e->window)) != NULL) { if (cc->flags & (CLIENT_ACTIVE | CLIENT_HIGHLIGHT)) { cc->flags &= ~CLIENT_HIGHLIGHT; client_draw_border(cc); } } } static void xev_handle_keypress(XEvent *ee) { XKeyEvent *e = &ee->xkey; struct client_ctx *cc; struct screen_ctx *sc; struct bind_ctx *kb; KeySym keysym, skeysym; unsigned int modshift; LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx", e->root, e->window, e->subwindow); if ((sc = screen_find(e->root)) == NULL) return; keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0); skeysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 1); e->state &= ~IGNOREMODMASK; TAILQ_FOREACH(kb, &Conf.keybindq, entry) { if (keysym != kb->press.keysym && skeysym == kb->press.keysym) modshift = ShiftMask; else modshift = 0; if ((kb->modmask | modshift) != e->state) continue; if (kb->press.keysym == ((modshift == 0) ? keysym : skeysym)) break; } if (kb == NULL) return; kb->cargs->xev = CWM_XEV_KEY; switch (kb->context) { case CWM_CONTEXT_CC: if (((cc = client_find(e->subwindow)) == NULL) && ((cc = client_current(sc)) == NULL)) return; (*kb->callback)(cc, kb->cargs); break; case CWM_CONTEXT_SC: (*kb->callback)(sc, kb->cargs); break; case CWM_CONTEXT_NONE: (*kb->callback)(NULL, kb->cargs); break; } } /* * This is only used for the modifier suppression detection. */ static void xev_handle_keyrelease(XEvent *ee) { XKeyEvent *e = &ee->xkey; struct screen_ctx *sc; struct client_ctx *cc; KeySym keysym; unsigned int i; LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx", e->root, e->window, e->subwindow); if ((sc = screen_find(e->root)) == NULL) return; keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0); for (i = 0; i < nitems(modkeys); i++) { if (keysym == modkeys[i]) { if ((cc = client_current(sc)) != NULL) { if (sc->cycling) { sc->cycling = 0; client_mtf(cc); } if (cc->flags & CLIENT_HIGHLIGHT) { cc->flags &= ~CLIENT_HIGHLIGHT; client_draw_border(cc); } } XUngrabKeyboard(X_Dpy, CurrentTime); break; } } } static void xev_handle_clientmessage(XEvent *ee) { XClientMessageEvent *e = &ee->xclient; struct client_ctx *cc, *old_cc; struct screen_ctx *sc; LOG_DEBUG3("window: 0x%lx", e->window); if (e->message_type == cwmh[WM_CHANGE_STATE]) { if ((cc = client_find(e->window)) != NULL) { if (e->data.l[0] == IconicState) client_hide(cc); } } else if (e->message_type == ewmh[_NET_CLOSE_WINDOW]) { if ((cc = client_find(e->window)) != NULL) { client_close(cc); } } else if (e->message_type == ewmh[_NET_ACTIVE_WINDOW]) { if ((cc = client_find(e->window)) != NULL) { if ((old_cc = client_current(NULL)) != NULL) client_ptr_save(old_cc); client_show(cc); client_ptr_warp(cc); } } else if (e->message_type == ewmh[_NET_WM_DESKTOP]) { if ((cc = client_find(e->window)) != NULL) { /* * The EWMH spec states that if the cardinal returned * is 0xFFFFFFFF (-1) then the window should appear * on all desktops, in our case, group 0. */ if (e->data.l[0] == (unsigned long)-1) group_movetogroup(cc, 0); else if (e->data.l[0] >= 0 && e->data.l[0] < Conf.ngroups) group_movetogroup(cc, e->data.l[0]); } } else if (e->message_type == ewmh[_NET_WM_STATE]) { if ((cc = client_find(e->window)) != NULL) { xu_ewmh_handle_net_wm_state_msg(cc, e->data.l[0], e->data.l[1], e->data.l[2]); } } else if (e->message_type == ewmh[_NET_CURRENT_DESKTOP]) { if ((sc = screen_find(e->window)) != NULL) { if (e->data.l[0] >= 0 && e->data.l[0] < Conf.ngroups) group_only(sc, e->data.l[0]); } } } static void xev_handle_randr(XEvent *ee) { XRRScreenChangeNotifyEvent *e = (XRRScreenChangeNotifyEvent *)ee; struct screen_ctx *sc; LOG_DEBUG3("size: %d/%d", e->width, e->height); if ((sc = screen_find(e->root)) == NULL) return; XRRUpdateConfiguration(ee); screen_update_geometry(sc); screen_assert_clients_within(sc); } /* * Called when the keymap has changed. * Ungrab all keys, reload keymap and then regrab */ static void xev_handle_mappingnotify(XEvent *ee) { XMappingEvent *e = &ee->xmapping; struct screen_ctx *sc; LOG_DEBUG3("window: 0x%lx", e->window); XRefreshKeyboardMapping(e); if (e->request == MappingKeyboard) { TAILQ_FOREACH(sc, &Screenq, entry) conf_grab_kbd(sc->rootwin); } } static void xev_handle_expose(XEvent *ee) { XExposeEvent *e = &ee->xexpose; struct client_ctx *cc; LOG_DEBUG3("window: 0x%lx", e->window); if ((cc = client_find(e->window)) != NULL && e->count == 0) client_draw_border(cc); } void xev_process(void) { XEvent e; while (XPending(X_Dpy)) { XNextEvent(X_Dpy, &e); if ((e.type - Conf.xrandr_event_base) == RRScreenChangeNotify) xev_handle_randr(&e); else if ((e.type < LASTEvent) && (xev_handlers[e.type] != NULL)) (*xev_handlers[e.type])(&e); } } cwm-7.1/xmalloc.c000066400000000000000000000041321423325063300137530ustar00rootroot00000000000000/* * calmwm - the calm window manager * * Copyright (c) 2004 Marius Aamodt Eriksen * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD$ */ #include #include "queue.h" #include #include #include #include #include #include #include #include #include #include "calmwm.h" void * xmalloc(size_t siz) { void *p; if (siz == 0) errx(1, "xmalloc: zero size"); if ((p = malloc(siz)) == NULL) err(1, "malloc"); return p; } void * xcalloc(size_t no, size_t siz) { void *p; if (siz == 0 || no == 0) errx(1, "xcalloc: zero size"); if (SIZE_MAX / no < siz) errx(1, "xcalloc: no * siz > SIZE_MAX"); if ((p = calloc(no, siz)) == NULL) err(1, "calloc"); return p; } void * xreallocarray(void *ptr, size_t nmemb, size_t size) { void *p; p = reallocarray(ptr, nmemb, size); if (p == NULL) errx(1, "xreallocarray: out of memory (new_size %zu bytes)", nmemb * size); return p; } char * xstrdup(const char *str) { char *p; if ((p = strdup(str)) == NULL) err(1, "strdup"); return p; } int xasprintf(char **ret, const char *fmt, ...) { va_list ap; int i; va_start(ap, fmt); i = xvasprintf(ret, fmt, ap); va_end(ap); return i; } int xvasprintf(char **ret, const char *fmt, va_list ap) { int i; i = vasprintf(ret, fmt, ap); if (i == -1) err(1, "vasprintf"); return i; } cwm-7.1/xutil.c000066400000000000000000000340171423325063300134660ustar00rootroot00000000000000/* * calmwm - the calm window manager * * Copyright (c) 2004 Marius Aamodt Eriksen * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD$ */ #include #include "queue.h" #include #include #include #include #include #include #include #include "calmwm.h" void xu_ptr_get(Window win, int *x, int *y) { Window w0, w1; int tmp0, tmp1; unsigned int tmp2; XQueryPointer(X_Dpy, win, &w0, &w1, &tmp0, &tmp1, x, y, &tmp2); } void xu_ptr_set(Window win, int x, int y) { XWarpPointer(X_Dpy, None, win, 0, 0, 0, 0, x, y); } int xu_get_prop(Window win, Atom atm, Atom type, long len, unsigned char **p) { Atom realtype; unsigned long n, extra; int format; if (XGetWindowProperty(X_Dpy, win, atm, 0L, len, False, type, &realtype, &format, &n, &extra, p) != Success || *p == NULL) return -1; if (n == 0) XFree(*p); return n; } int xu_get_strprop(Window win, Atom atm, char **text) { XTextProperty prop; char **list; int nitems = 0; *text = NULL; XGetTextProperty(X_Dpy, win, &prop, atm); if (!prop.nitems) { XFree(prop.value); return 0; } if (Xutf8TextPropertyToTextList(X_Dpy, &prop, &list, &nitems) == Success && nitems > 0 && *list) { if (nitems > 1) { XTextProperty prop2; if (Xutf8TextListToTextProperty(X_Dpy, list, nitems, XUTF8StringStyle, &prop2) == Success) { *text = xstrdup((const char *)prop2.value); XFree(prop2.value); } } else { *text = xstrdup(*list); } XFreeStringList(list); } XFree(prop.value); return nitems; } void xu_send_clientmsg(Window win, Atom proto, Time ts) { XClientMessageEvent cm; (void)memset(&cm, 0, sizeof(cm)); cm.type = ClientMessage; cm.window = win; cm.message_type = cwmh[WM_PROTOCOLS]; cm.format = 32; cm.data.l[0] = proto; cm.data.l[1] = ts; XSendEvent(X_Dpy, win, False, NoEventMask, (XEvent *)&cm); } void xu_get_wm_state(Window win, long *state) { long *p; *state = -1; if (xu_get_prop(win, cwmh[WM_STATE], cwmh[WM_STATE], 2L, (unsigned char **)&p) > 0) { *state = *p; XFree(p); } } void xu_set_wm_state(Window win, long state) { long data[] = { state, None }; XChangeProperty(X_Dpy, win, cwmh[WM_STATE], cwmh[WM_STATE], 32, PropModeReplace, (unsigned char *)data, 2); } void xu_xorcolor(XftColor a, XftColor b, XftColor *r) { r->pixel = a.pixel ^ b.pixel; r->color.red = a.color.red ^ b.color.red; r->color.green = a.color.green ^ b.color.green; r->color.blue = a.color.blue ^ b.color.blue; r->color.alpha = 0xffff; } void xu_atom_init(void) { char *cwmhints[] = { "WM_STATE", "WM_DELETE_WINDOW", "WM_TAKE_FOCUS", "WM_PROTOCOLS", "_MOTIF_WM_HINTS", "UTF8_STRING", "WM_CHANGE_STATE", }; char *ewmhints[] = { "_NET_SUPPORTED", "_NET_SUPPORTING_WM_CHECK", "_NET_ACTIVE_WINDOW", "_NET_CLIENT_LIST", "_NET_CLIENT_LIST_STACKING", "_NET_NUMBER_OF_DESKTOPS", "_NET_CURRENT_DESKTOP", "_NET_DESKTOP_VIEWPORT", "_NET_DESKTOP_GEOMETRY", "_NET_VIRTUAL_ROOTS", "_NET_SHOWING_DESKTOP", "_NET_DESKTOP_NAMES", "_NET_WORKAREA", "_NET_WM_NAME", "_NET_WM_DESKTOP", "_NET_CLOSE_WINDOW", "_NET_WM_STATE", "_NET_WM_STATE_STICKY", "_NET_WM_STATE_MAXIMIZED_VERT", "_NET_WM_STATE_MAXIMIZED_HORZ", "_NET_WM_STATE_HIDDEN", "_NET_WM_STATE_FULLSCREEN", "_NET_WM_STATE_DEMANDS_ATTENTION", "_NET_WM_STATE_SKIP_PAGER", "_NET_WM_STATE_SKIP_TASKBAR", "_CWM_WM_STATE_FREEZE", }; XInternAtoms(X_Dpy, cwmhints, nitems(cwmhints), False, cwmh); XInternAtoms(X_Dpy, ewmhints, nitems(ewmhints), False, ewmh); } /* Root Window Properties */ void xu_ewmh_net_supported(struct screen_ctx *sc) { XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTED], XA_ATOM, 32, PropModeReplace, (unsigned char *)ewmh, EWMH_NITEMS); } void xu_ewmh_net_supported_wm_check(struct screen_ctx *sc) { Window w; w = XCreateSimpleWindow(X_Dpy, sc->rootwin, -1, -1, 1, 1, 0, 0, 0); XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTING_WM_CHECK], XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1); XChangeProperty(X_Dpy, w, ewmh[_NET_SUPPORTING_WM_CHECK], XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1); XChangeProperty(X_Dpy, w, ewmh[_NET_WM_NAME], cwmh[UTF8_STRING], 8, PropModeReplace, (unsigned char *)Conf.wmname, strlen(Conf.wmname)); } void xu_ewmh_net_desktop_geometry(struct screen_ctx *sc) { long geom[2] = { sc->view.w, sc->view.h }; XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_GEOMETRY], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)geom , 2); } void xu_ewmh_net_desktop_viewport(struct screen_ctx *sc) { long viewports[2] = {0, 0}; /* We don't support large desktops, so this is (0, 0). */ XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_VIEWPORT], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2); } void xu_ewmh_net_workarea(struct screen_ctx *sc) { unsigned long *workarea; int i, ngroups = Conf.ngroups; workarea = xreallocarray(NULL, ngroups * 4, sizeof(unsigned long)); for (i = 0; i < ngroups; i++) { workarea[4 * i + 0] = sc->work.x; workarea[4 * i + 1] = sc->work.y; workarea[4 * i + 2] = sc->work.w; workarea[4 * i + 3] = sc->work.h; } XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_WORKAREA], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)workarea, ngroups * 4); free(workarea); } void xu_ewmh_net_client_list(struct screen_ctx *sc) { struct client_ctx *cc; Window *winlist; int i = 0, j = 0; TAILQ_FOREACH(cc, &sc->clientq, entry) i++; if (i == 0) return; winlist = xreallocarray(NULL, i, sizeof(*winlist)); TAILQ_FOREACH(cc, &sc->clientq, entry) winlist[j++] = cc->win; XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CLIENT_LIST], XA_WINDOW, 32, PropModeReplace, (unsigned char *)winlist, i); free(winlist); } void xu_ewmh_net_client_list_stacking(struct screen_ctx *sc) { struct client_ctx *cc; Window *winlist; int i = 0, j; TAILQ_FOREACH(cc, &sc->clientq, entry) i++; if (i == 0) return; j = i; winlist = xreallocarray(NULL, i, sizeof(*winlist)); TAILQ_FOREACH(cc, &sc->clientq, entry) winlist[--j] = cc->win; XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CLIENT_LIST_STACKING], XA_WINDOW, 32, PropModeReplace, (unsigned char *)winlist, i); free(winlist); } void xu_ewmh_net_active_window(struct screen_ctx *sc, Window w) { XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_ACTIVE_WINDOW], XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1); } void xu_ewmh_net_number_of_desktops(struct screen_ctx *sc) { long ndesks = Conf.ngroups; XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_NUMBER_OF_DESKTOPS], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&ndesks, 1); } void xu_ewmh_net_showing_desktop(struct screen_ctx *sc) { long zero = 0; /* We don't support `showing desktop' mode, so this is zero. * Note that when we hide all groups, or when all groups are * hidden we could technically set this later on. */ XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SHOWING_DESKTOP], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&zero, 1); } void xu_ewmh_net_virtual_roots(struct screen_ctx *sc) { /* We don't support virtual roots, so delete if set by previous wm. */ XDeleteProperty(X_Dpy, sc->rootwin, ewmh[_NET_VIRTUAL_ROOTS]); } void xu_ewmh_net_current_desktop(struct screen_ctx *sc) { long num = sc->group_active->num; XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CURRENT_DESKTOP], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1); } void xu_ewmh_net_desktop_names(struct screen_ctx *sc) { struct group_ctx *gc; char *p, *q; unsigned char *prop_ret; int i = 0, j = 0, nstrings = 0, n = 0; size_t len = 0, tlen, slen; /* Let group names be overwritten if _NET_DESKTOP_NAMES is set. */ if ((j = xu_get_prop(sc->rootwin, ewmh[_NET_DESKTOP_NAMES], cwmh[UTF8_STRING], 0xffffff, (unsigned char **)&prop_ret)) > 0) { prop_ret[j - 1] = '\0'; /* paranoia */ while (i < j) { if (prop_ret[i++] == '\0') nstrings++; } } p = (char *)prop_ret; while (n < nstrings) { TAILQ_FOREACH(gc, &sc->groupq, entry) { if (gc->num == n) { free(gc->name); gc->name = xstrdup(p); p += strlen(p) + 1; break; } } n++; } if (prop_ret != NULL) XFree(prop_ret); TAILQ_FOREACH(gc, &sc->groupq, entry) len += strlen(gc->name) + 1; q = p = xreallocarray(NULL, len, sizeof(*p)); tlen = len; TAILQ_FOREACH(gc, &sc->groupq, entry) { slen = strlen(gc->name) + 1; (void)strlcpy(q, gc->name, tlen); tlen -= slen; q += slen; } XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_NAMES], cwmh[UTF8_STRING], 8, PropModeReplace, (unsigned char *)p, len); free(p); } /* Application Window Properties */ int xu_ewmh_get_net_wm_desktop(struct client_ctx *cc, long *n) { long *p; if (xu_get_prop(cc->win, ewmh[_NET_WM_DESKTOP], XA_CARDINAL, 1L, (unsigned char **)&p) <= 0) return 0; *n = *p; XFree(p); return 1; } void xu_ewmh_set_net_wm_desktop(struct client_ctx *cc) { long num = 0xffffffff; if (cc->gc) num = cc->gc->num; XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_DESKTOP], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1); } Atom * xu_ewmh_get_net_wm_state(struct client_ctx *cc, int *n) { Atom *state, *p = NULL; if ((*n = xu_get_prop(cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 64L, (unsigned char **)&p)) <= 0) return NULL; state = xreallocarray(NULL, *n, sizeof(Atom)); (void)memcpy(state, p, *n * sizeof(Atom)); XFree((char *)p); return state; } void xu_ewmh_handle_net_wm_state_msg(struct client_ctx *cc, int action, Atom first, Atom second) { unsigned int i; struct handlers { Atom atom; int flag; void (*toggle)(struct client_ctx *); } handlers[] = { { _NET_WM_STATE_STICKY, CLIENT_STICKY, client_toggle_sticky }, { _NET_WM_STATE_MAXIMIZED_VERT, CLIENT_VMAXIMIZED, client_toggle_vmaximize }, { _NET_WM_STATE_MAXIMIZED_HORZ, CLIENT_HMAXIMIZED, client_toggle_hmaximize }, { _NET_WM_STATE_HIDDEN, CLIENT_HIDDEN, client_toggle_hidden }, { _NET_WM_STATE_FULLSCREEN, CLIENT_FULLSCREEN, client_toggle_fullscreen }, { _NET_WM_STATE_DEMANDS_ATTENTION, CLIENT_URGENCY, client_urgency }, { _NET_WM_STATE_SKIP_PAGER, CLIENT_SKIP_PAGER, client_toggle_skip_pager}, { _NET_WM_STATE_SKIP_TASKBAR, CLIENT_SKIP_TASKBAR, client_toggle_skip_taskbar}, { _CWM_WM_STATE_FREEZE, CLIENT_FREEZE, client_toggle_freeze }, }; for (i = 0; i < nitems(handlers); i++) { if (first != ewmh[handlers[i].atom] && second != ewmh[handlers[i].atom]) continue; switch (action) { case _NET_WM_STATE_ADD: if (!(cc->flags & handlers[i].flag)) handlers[i].toggle(cc); break; case _NET_WM_STATE_REMOVE: if (cc->flags & handlers[i].flag) handlers[i].toggle(cc); break; case _NET_WM_STATE_TOGGLE: handlers[i].toggle(cc); } } } void xu_ewmh_restore_net_wm_state(struct client_ctx *cc) { Atom *atoms; int i, n; atoms = xu_ewmh_get_net_wm_state(cc, &n); for (i = 0; i < n; i++) { if (atoms[i] == ewmh[_NET_WM_STATE_STICKY]) client_toggle_sticky(cc); if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_VERT]) client_toggle_vmaximize(cc); if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_HORZ]) client_toggle_hmaximize(cc); if (atoms[i] == ewmh[_NET_WM_STATE_HIDDEN]) client_toggle_hidden(cc); if (atoms[i] == ewmh[_NET_WM_STATE_FULLSCREEN]) client_toggle_fullscreen(cc); if (atoms[i] == ewmh[_NET_WM_STATE_DEMANDS_ATTENTION]) client_urgency(cc); if (atoms[i] == ewmh[_NET_WM_STATE_SKIP_PAGER]) client_toggle_skip_pager(cc); if (atoms[i] == ewmh[_NET_WM_STATE_SKIP_TASKBAR]) client_toggle_skip_taskbar(cc); if (atoms[i] == ewmh[_CWM_WM_STATE_FREEZE]) client_toggle_freeze(cc); } free(atoms); } void xu_ewmh_set_net_wm_state(struct client_ctx *cc) { Atom *atoms, *oatoms; int n, i, j; oatoms = xu_ewmh_get_net_wm_state(cc, &n); atoms = xreallocarray(NULL, (n + _NET_WM_STATES_NITEMS), sizeof(Atom)); for (i = j = 0; i < n; i++) { if (oatoms[i] != ewmh[_NET_WM_STATE_STICKY] && oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_VERT] && oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_HORZ] && oatoms[i] != ewmh[_NET_WM_STATE_HIDDEN] && oatoms[i] != ewmh[_NET_WM_STATE_FULLSCREEN] && oatoms[i] != ewmh[_NET_WM_STATE_DEMANDS_ATTENTION] && oatoms[i] != ewmh[_NET_WM_STATE_SKIP_PAGER] && oatoms[i] != ewmh[_NET_WM_STATE_SKIP_TASKBAR] && oatoms[i] != ewmh[_CWM_WM_STATE_FREEZE]) atoms[j++] = oatoms[i]; } free(oatoms); if (cc->flags & CLIENT_STICKY) atoms[j++] = ewmh[_NET_WM_STATE_STICKY]; if (cc->flags & CLIENT_HIDDEN) atoms[j++] = ewmh[_NET_WM_STATE_HIDDEN]; if (cc->flags & CLIENT_FULLSCREEN) atoms[j++] = ewmh[_NET_WM_STATE_FULLSCREEN]; else { if (cc->flags & CLIENT_VMAXIMIZED) atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_VERT]; if (cc->flags & CLIENT_HMAXIMIZED) atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_HORZ]; } if (cc->flags & CLIENT_URGENCY) atoms[j++] = ewmh[_NET_WM_STATE_DEMANDS_ATTENTION]; if (cc->flags & CLIENT_SKIP_PAGER) atoms[j++] = ewmh[_NET_WM_STATE_SKIP_PAGER]; if (cc->flags & CLIENT_SKIP_TASKBAR) atoms[j++] = ewmh[_NET_WM_STATE_SKIP_TASKBAR]; if (cc->flags & CLIENT_FREEZE) atoms[j++] = ewmh[_CWM_WM_STATE_FREEZE]; if (j > 0) XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 32, PropModeReplace, (unsigned char *)atoms, j); else XDeleteProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE]); free(atoms); }