pax_global_header00006660000000000000000000000064124607533500014517gustar00rootroot0000000000000052 comment=b7a8c11750d11721a897fdb8442d52f15e7a24a0 cwm-5.6/000077500000000000000000000000001246075335000121575ustar00rootroot00000000000000cwm-5.6/Makefile000066400000000000000000000027221246075335000136220ustar00rootroot00000000000000# 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 mousefunc.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 mousefunc.o strlcpy.o strlcat.o y.tab.o \ strtonum.o fgetln.o CPPFLAGS+= `pkg-config --cflags fontconfig x11 xft xinerama xrandr` CFLAGS?= -Wall -O2 -g -D_GNU_SOURCE LDFLAGS+= `pkg-config --libs fontconfig x11 xft xinerama xrandr` MANPREFIX?= ${PREFIX}/share/man all: ${PROG} clean: rm -f ${OBJS} ${PROG} y.tab.c y.tab.c: parse.y yacc parse.y ${PROG}: ${OBJS} y.tab.o ${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/-[^.]*$$//') && \ gpg --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 cwm-5.6/README000066400000000000000000000076201246075335000130440ustar00rootroot00000000000000This 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/chneukirchen/cwm Releases can be found at http://chneukirchen.org/releases You are welcome to join the IRC channel ##cwm on Freenode 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. --Christian Neukirchen [0]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/xenocara/app/cwm/ cwm-5.6/calmwm.c000066400000000000000000000123421246075335000136050ustar00rootroot00000000000000/* * 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_ctx_q Screenq = TAILQ_HEAD_INITIALIZER(Screenq); int HasRandr, Randr_ev; struct conf Conf; const char *homedir; volatile sig_atomic_t cwm_status; static void sighdlr(int); static int x_errorhandler(Display *, XErrorEvent *); static void x_init(const char *); static void x_restart(char **); static void x_teardown(void); static int x_wmerrorhandler(Display *, XErrorEvent *); int main(int argc, char **argv) { const char *conf_file = NULL; char *conf_path, *display_name = NULL; char **cwm_argv; int ch; struct passwd *pw; if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) warnx("no locale support"); mbtowc(NULL, NULL, MB_CUR_MAX); cwm_argv = argv; while ((ch = getopt(argc, argv, "c:d:")) != -1) { switch (ch) { case 'c': conf_file = optarg; break; case 'd': display_name = optarg; break; default: usage(); } } argc -= optind; argv += optind; if (signal(SIGCHLD, sighdlr) == SIG_ERR) err(1, "signal"); if ((homedir = getenv("HOME")) == NULL || *homedir == '\0') { pw = getpwuid(getuid()); if (pw != NULL && pw->pw_dir != NULL && *pw->pw_dir != '\0') homedir = pw->pw_dir; else homedir = "/"; } if (conf_file == NULL) xasprintf(&conf_path, "%s/%s", homedir, CONFFILE); else conf_path = xstrdup(conf_file); if (access(conf_path, R_OK) != 0) { if (conf_file != NULL) warn("%s", conf_file); free(conf_path); conf_path = NULL; } conf_init(&Conf); if (conf_path && (parse_config(conf_path, &Conf) == -1)) warnx("config file %s has errors", conf_path); free(conf_path); x_init(display_name); cwm_status = CWM_RUNNING; while (cwm_status == CWM_RUNNING) xev_process(); x_teardown(); if (cwm_status == CWM_RESTART) x_restart(cwm_argv); return(0); } static void 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); HasRandr = XRRQueryExtension(X_Dpy, &Randr_ev, &i); conf_atoms(); conf_cursor(&Conf); for (i = 0; i < ScreenCount(X_Dpy); i++) screen_init(i); } static void x_restart(char **args) { (void)setsid(); (void)execvp(args[0], args); } 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]); XftDrawDestroy(sc->xftdraw); XftFontClose(X_Dpy, sc->xftfont); XUnmapWindow(X_Dpy, sc->menuwin); XDestroyWindow(X_Dpy, sc->menuwin); 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(WAIT_ANY, &status, WNOHANG)) > 0 || (pid < 0 && errno == EINTR)) ; break; } errno = save_errno; } void usage(void) { extern char *__progname; (void)fprintf(stderr, "usage: %s [-c file] [-d display]\n", __progname); exit(1); } cwm-5.6/calmwm.h000066400000000000000000000417441246075335000136220ustar00rootroot00000000000000/* * 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 **); #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 #include #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 CONFFILE ".cwmrc" #define WMNAME "CWM" #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) #define MOUSEMASK (BUTTONMASK|PointerMotionMask) #define MENUMASK (MOUSEMASK|ButtonMotionMask|ExposureMask) #define MENUGRABMASK (MOUSEMASK|ButtonMotionMask|StructureNotifyMask) #define KEYMASK (KeyPressMask|ExposureMask) #define IGNOREMODMASK (LockMask|Mod2Mask) /* kb movement */ #define CWM_MOVE 0x0001 #define CWM_RESIZE 0x0002 #define CWM_PTRMOVE 0x0004 #define CWM_BIGMOVE 0x0008 #define CWM_UP 0x0010 #define CWM_DOWN 0x0020 #define CWM_LEFT 0x0040 #define CWM_RIGHT 0x0080 /* exec */ #define CWM_EXEC_PROGRAM 0x0001 #define CWM_EXEC_WM 0x0002 /* cycle */ #define CWM_CYCLE 0x0001 #define CWM_RCYCLE 0x0002 #define CWM_INGROUP 0x0004 /* menu */ #define CWM_MENU_DUMMY 0x0001 #define CWM_MENU_FILE 0x0002 #define ARG_CHAR 0x0001 #define ARG_INT 0x0002 #define CWM_TILE_HORIZ 0x0001 #define CWM_TILE_VERT 0x0002 #define CWM_GAP 0x0001 #define CWM_NOGAP 0x0002 #define CWM_WIN 0x0001 #define CWM_QUIT 0x0000 #define CWM_RUNNING 0x0001 #define CWM_RESTART 0x0002 union arg { char *c; int i; }; union press { KeySym keysym; unsigned int button; }; enum cursor_font { CF_DEFAULT, CF_MOVE, CF_NORMAL, CF_QUESTION, CF_RESIZE, 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(winname_q, winname); TAILQ_HEAD(ignore_q, winname); struct client_ctx { TAILQ_ENTRY(client_ctx) entry; TAILQ_ENTRY(client_ctx) group_entry; struct screen_ctx *sc; Window win; Colormap colormap; unsigned int bwidth; /* 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; #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_HIGHLIGHT (CLIENT_GROUP | CLIENT_UNGROUP) #define CLIENT_MAXFLAGS (CLIENT_VMAXIMIZED | CLIENT_HMAXIMIZED) #define CLIENT_MAXIMIZED (CLIENT_VMAXIMIZED | CLIENT_HMAXIMIZED) int flags; int stackingorder; struct winname_q nameq; #define CLIENT_MAXNAMEQLEN 5 int nameqlen; char *name; char *label; char *matchname; struct group_ctx *group; XClassHint ch; XWMHints *wmh; }; TAILQ_HEAD(client_ctx_q, client_ctx); struct group_ctx { TAILQ_ENTRY(group_ctx) entry; struct screen_ctx *sc; char *name; int num; struct client_ctx_q clientq; }; TAILQ_HEAD(group_ctx_q, group_ctx); struct autogroupwin { TAILQ_ENTRY(autogroupwin) entry; char *class; char *name; int num; }; TAILQ_HEAD(autogroupwin_q, autogroupwin); struct region_ctx { TAILQ_ENTRY(region_ctx) entry; int num; struct geom area; }; TAILQ_HEAD(region_ctx_q, region_ctx); struct screen_ctx { TAILQ_ENTRY(screen_ctx) entry; int which; Window rootwin; Window menuwin; int cycling; int hideall; int snapdist; struct geom view; /* viewable area */ struct geom work; /* workable area, gap-applied */ struct gap gap; struct client_ctx_q clientq; struct region_ctx_q regionq; #define CALMWM_NGROUPS 10 struct group_ctx_q groupq; struct group_ctx *group_active; XftColor xftcolor[CWM_COLOR_NITEMS]; XftDraw *xftdraw; XftFont *xftfont; }; TAILQ_HEAD(screen_ctx_q, screen_ctx); struct binding { TAILQ_ENTRY(binding) entry; void (*callback)(struct client_ctx *, union arg *); union arg argument; unsigned int modmask; union press press; int flags; int argtype; }; TAILQ_HEAD(keybinding_q, binding); TAILQ_HEAD(mousebinding_q, binding); struct cmd { TAILQ_ENTRY(cmd) entry; char *name; char path[PATH_MAX]; }; TAILQ_HEAD(cmd_q, cmd); 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 keybinding_q keybindingq; struct mousebinding_q mousebindingq; struct autogroupwin_q autogroupq; struct ignore_q ignoreq; struct cmd_q cmdq; #define CONF_STICKY_GROUPS 0x0001 int flags; #define CONF_BWIDTH 1 int bwidth; #define CONF_MAMOUNT 1 int mamount; #define CONF_SNAPDIST 0 int snapdist; struct gap gap; char *color[CWM_COLOR_NITEMS]; char known_hosts[PATH_MAX]; #define CONF_FONT "sans-serif:pixelsize=14:bold" char *font; Cursor cursor[CF_NITEMS]; }; /* MWM hints */ struct mwm_hints { unsigned long flags; unsigned long functions; unsigned long decorations; }; #define MWM_NUMHINTS 3 #define PROP_MWM_HINTS_ELEMENTS 3 #define MWM_HINTS_DECORATIONS (1<<1) #define MWM_DECOR_ALL (1<<0) #define MWM_DECOR_BORDER (1<<1) extern Display *X_Dpy; extern Time Last_Event_Time; extern struct screen_ctx_q Screenq; extern struct conf Conf; extern const char *homedir; extern int HasRandr, Randr_ev; enum { WM_STATE, WM_DELETE_WINDOW, WM_TAKE_FOCUS, WM_PROTOCOLS, _MOTIF_WM_HINTS, UTF8_STRING, WM_CHANGE_STATE, CWMH_NITEMS }; enum { _NET_SUPPORTED, _NET_SUPPORTING_WM_CHECK, _NET_ACTIVE_WINDOW, _NET_CLIENT_LIST, _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 6 _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, EWMH_NITEMS }; enum { _NET_WM_STATE_REMOVE, _NET_WM_STATE_ADD, _NET_WM_STATE_TOGGLE }; extern Atom cwmh[CWMH_NITEMS]; extern Atom ewmh[EWMH_NITEMS]; void usage(void); void client_applysizehints(struct client_ctx *); void client_config(struct client_ctx *); struct client_ctx *client_current(void); void client_cycle(struct screen_ctx *, int); void client_cycle_leave(struct screen_ctx *); void client_delete(struct client_ctx *); void client_draw_border(struct client_ctx *); struct client_ctx *client_find(Window); long client_get_wm_state(struct client_ctx *); void client_getsizehints(struct client_ctx *); void client_hide(struct client_ctx *); void client_htile(struct client_ctx *); void client_lower(struct client_ctx *); void client_map(struct client_ctx *); void client_msg(struct client_ctx *, Atom, Time); void client_move(struct client_ctx *); struct client_ctx *client_init(Window, struct screen_ctx *); void client_ptrsave(struct client_ctx *); void client_ptrwarp(struct client_ctx *); void client_raise(struct client_ctx *); void client_resize(struct client_ctx *, int); void client_send_delete(struct client_ctx *); void client_set_wm_state(struct client_ctx *, long); void client_setactive(struct client_ctx *); void client_setname(struct client_ctx *); int client_snapcalc(int, int, int, int, int); void client_toggle_freeze(struct client_ctx *); void client_toggle_fullscreen(struct client_ctx *); void client_toggle_hidden(struct client_ctx *); void client_toggle_hmaximize(struct client_ctx *); void client_toggle_maximize(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_unhide(struct client_ctx *); void client_urgency(struct client_ctx *); void client_vtile(struct client_ctx *); void client_warp(struct client_ctx *); void client_wm_hints(struct client_ctx *); void group_alltoggle(struct screen_ctx *); void group_autogroup(struct client_ctx *); void group_cycle(struct screen_ctx *, int); void group_hide(struct group_ctx *); void group_hidetoggle(struct screen_ctx *, int); int group_holds_only_hidden(struct group_ctx *); int group_holds_only_sticky(struct group_ctx *); void group_init(struct screen_ctx *, int); void group_movetogroup(struct client_ctx *, int); void group_only(struct screen_ctx *, int); void group_show(struct group_ctx *); void group_toggle_membership_enter(struct client_ctx *); void group_toggle_membership_leave(struct client_ctx *); void group_update_names(struct screen_ctx *); void search_match_client(struct menu_q *, struct menu_q *, char *); void search_match_exec(struct menu_q *, struct menu_q *, char *); void search_match_exec_path(struct menu_q *, struct menu_q *, char *); void search_match_path_any(struct menu_q *, struct menu_q *, char *); void search_match_text(struct menu_q *, struct menu_q *, char *); void search_print_client(struct menu *, int); struct screen_ctx *screen_find(Window); struct geom screen_find_xinerama(struct screen_ctx *, int, int, int); void screen_init(int); void screen_update_geometry(struct screen_ctx *); void screen_updatestackingorder(struct screen_ctx *); void kbfunc_client_cycle(struct client_ctx *, union arg *); void kbfunc_client_cyclegroup(struct client_ctx *, union arg *); void kbfunc_client_delete(struct client_ctx *, union arg *); void kbfunc_client_group(struct client_ctx *, union arg *); void kbfunc_client_grouponly(struct client_ctx *, union arg *); void kbfunc_client_grouptoggle(struct client_ctx *, union arg *); void kbfunc_client_hide(struct client_ctx *, union arg *); void kbfunc_client_label(struct client_ctx *, union arg *); void kbfunc_client_lower(struct client_ctx *, union arg *); void kbfunc_client_moveresize(struct client_ctx *, union arg *); void kbfunc_client_movetogroup(struct client_ctx *, union arg *); void kbfunc_client_nogroup(struct client_ctx *, union arg *); void kbfunc_client_raise(struct client_ctx *, union arg *); void kbfunc_client_rcycle(struct client_ctx *, union arg *); void kbfunc_client_search(struct client_ctx *, union arg *); void kbfunc_client_toggle_freeze(struct client_ctx *, union arg *); void kbfunc_client_toggle_fullscreen(struct client_ctx *, union arg *); void kbfunc_client_toggle_hmaximize(struct client_ctx *, union arg *); void kbfunc_client_toggle_maximize(struct client_ctx *, union arg *); void kbfunc_client_toggle_sticky(struct client_ctx *, union arg *); void kbfunc_client_toggle_vmaximize(struct client_ctx *, union arg *); void kbfunc_cmdexec(struct client_ctx *, union arg *); void kbfunc_cwm_status(struct client_ctx *, union arg *); void kbfunc_exec(struct client_ctx *, union arg *); void kbfunc_lock(struct client_ctx *, union arg *); void kbfunc_menu_cmd(struct client_ctx *, union arg *); void kbfunc_ssh(struct client_ctx *, union arg *); void kbfunc_term(struct client_ctx *, union arg *); void kbfunc_tile(struct client_ctx *, union arg *); void mousefunc_client_grouptoggle(struct client_ctx *, union arg *); void mousefunc_client_move(struct client_ctx *, union arg *); void mousefunc_client_resize(struct client_ctx *, union arg *); void mousefunc_menu_cmd(struct client_ctx *, union arg *); void mousefunc_menu_group(struct client_ctx *, union arg *); void mousefunc_menu_unhide(struct client_ctx *, union arg *); 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 *, ...); void menuq_clear(struct menu_q *); int parse_config(const char *, struct conf *); void conf_atoms(void); void conf_autogroup(struct conf *, int, const char *); int conf_bind_kbd(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 *); int conf_cmd_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 xev_process(void); void xu_btn_grab(Window, int, unsigned int); void xu_btn_ungrab(Window); int xu_getprop(Window, Atom, Atom, long, unsigned char **); int xu_getstrprop(Window, Atom, char **); void xu_key_grab(Window, unsigned int, KeySym); void xu_key_ungrab(Window); void xu_ptr_getpos(Window, int *, int *); int xu_ptr_grab(Window, unsigned int, Cursor); int xu_ptr_regrab(unsigned int, Cursor); void xu_ptr_setpos(Window, int, int); void xu_ptr_ungrab(void); void xu_xft_draw(struct screen_ctx *, const char *, int, int, int); int xu_xft_width(XftFont *, const char *, int); void xu_xorcolor(XftColor, XftColor, XftColor *); 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_workarea(struct screen_ctx *); void xu_ewmh_net_client_list(struct screen_ctx *); void xu_ewmh_net_active_window(struct screen_ctx *, Window); void xu_ewmh_net_wm_desktop_viewport(struct screen_ctx *); void xu_ewmh_net_wm_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 *); void xu_ewmh_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 *); void u_exec(char *); void u_spawn(char *); void *xcalloc(size_t, size_t); void *xmalloc(size_t); char *xstrdup(const char *); int xasprintf(char **, const char *, ...) __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); #endif /* _CALMWM_H_ */ cwm-5.6/client.c000066400000000000000000000550331246075335000136070ustar00rootroot00000000000000/* * 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 client_ctx *client_next(struct client_ctx *); static struct client_ctx *client_prev(struct client_ctx *); static void client_mtf(struct client_ctx *); static void client_none(struct screen_ctx *); static void client_placecalc(struct client_ctx *); static void client_wm_protocols(struct client_ctx *); static void client_mwm_hints(struct client_ctx *); static int client_inbound(struct client_ctx *, int, int); struct client_ctx *curcc = 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_init(Window win, struct screen_ctx *sc) { struct client_ctx *cc; XWindowAttributes wattr; int mapped; if (win == None) return(NULL); if (!XGetWindowAttributes(X_Dpy, win, &wattr)) return(NULL); if (sc == NULL) { sc = screen_find(wattr.root); mapped = 1; } else { if (wattr.override_redirect || wattr.map_state != IsViewable) return(NULL); mapped = wattr.map_state != IsUnmapped; } cc = xcalloc(1, sizeof(*cc)); XGrabServer(X_Dpy); cc->sc = sc; cc->win = win; TAILQ_INIT(&cc->nameq); client_setname(cc); conf_client(cc); XGetClassHint(X_Dpy, cc->win, &cc->ch); client_wm_hints(cc); client_wm_protocols(cc); client_getsizehints(cc); client_mwm_hints(cc); /* Saved pointer position */ cc->ptr.x = -1; cc->ptr.y = -1; cc->geom.x = wattr.x; cc->geom.y = wattr.y; cc->geom.w = wattr.width; cc->geom.h = wattr.height; cc->colormap = wattr.colormap; if (wattr.map_state != IsViewable) { client_placecalc(cc); client_move(cc); if ((cc->wmh) && (cc->wmh->flags & StateHint)) client_set_wm_state(cc, cc->wmh->initial_state); } XSelectInput(X_Dpy, cc->win, ColormapChangeMask | EnterWindowMask | PropertyChangeMask | KeyReleaseMask); XAddToSaveSet(X_Dpy, cc->win); client_transient(cc); /* Notify client of its configuration. */ client_config(cc); TAILQ_INSERT_TAIL(&sc->clientq, cc, entry); xu_ewmh_net_client_list(sc); xu_ewmh_restore_net_wm_state(cc); if (client_get_wm_state(cc) == IconicState) client_hide(cc); else client_unhide(cc); if (mapped) group_autogroup(cc); XSync(X_Dpy, False); XUngrabServer(X_Dpy); return(cc); } void client_delete(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); if (cc->group != NULL) TAILQ_REMOVE(&cc->group->clientq, cc, group_entry); if (cc == client_current()) client_none(sc); while ((wn = TAILQ_FIRST(&cc->nameq)) != NULL) { TAILQ_REMOVE(&cc->nameq, wn, entry); free(wn->name); free(wn); } if (cc->ch.res_class) XFree(cc->ch.res_class); if (cc->ch.res_name) XFree(cc->ch.res_name); if (cc->wmh) XFree(cc->wmh); free(cc); } void client_setactive(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) client_msg(cc, cwmh[WM_TAKE_FOCUS], Last_Event_Time); if ((oldcc = client_current())) { oldcc->flags &= ~CLIENT_ACTIVE; client_draw_border(oldcc); } /* If we're in the middle of cycing, don't change the order. */ if (!sc->cycling) client_mtf(cc); curcc = 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); } /* * set when there is no active client */ static void client_none(struct screen_ctx *sc) { Window none = None; xu_ewmh_net_active_window(sc, none); curcc = NULL; } struct client_ctx * client_current(void) { return(curcc); } void client_toggle_freeze(struct client_ctx *cc) { if (cc->flags & CLIENT_FREEZE) cc->flags &= ~CLIENT_FREEZE; else cc->flags |= CLIENT_FREEZE; } void client_toggle_hidden(struct client_ctx *cc) { if (cc->flags & CLIENT_HIDDEN) cc->flags &= ~CLIENT_HIDDEN; else cc->flags |= CLIENT_HIDDEN; xu_ewmh_set_net_wm_state(cc); } void client_toggle_sticky(struct client_ctx *cc) { if (cc->flags & CLIENT_STICKY) cc->flags &= ~CLIENT_STICKY; else 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 xine; if ((cc->flags & CLIENT_FREEZE) && !(cc->flags & CLIENT_FULLSCREEN)) return; if (cc->flags & CLIENT_FULLSCREEN) { cc->bwidth = Conf.bwidth; cc->geom = cc->fullgeom; cc->flags &= ~(CLIENT_FULLSCREEN | CLIENT_FREEZE); goto resize; } cc->fullgeom = cc->geom; xine = screen_find_xinerama(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, CWM_NOGAP); cc->bwidth = 0; cc->geom = xine; cc->flags |= (CLIENT_FULLSCREEN | CLIENT_FREEZE); resize: client_resize(cc, 0); xu_ewmh_set_net_wm_state(cc); } void client_toggle_maximize(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct geom xine; if (cc->flags & (CLIENT_FREEZE|CLIENT_STICKY)) 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; } /* * pick screen that the middle of the window is on. * that's probably more fair than if just the origin of * a window is poking over a boundary */ xine = screen_find_xinerama(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, CWM_GAP); cc->geom.x = xine.x; cc->geom.y = xine.y; cc->geom.w = xine.w - (cc->bwidth * 2); cc->geom.h = xine.h - (cc->bwidth * 2); cc->flags |= CLIENT_MAXIMIZED; resize: client_resize(cc, 0); xu_ewmh_set_net_wm_state(cc); } void client_toggle_vmaximize(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct geom xine; if (cc->flags & (CLIENT_FREEZE|CLIENT_STICKY)) 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; xine = screen_find_xinerama(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, CWM_GAP); cc->geom.y = xine.y; cc->geom.h = xine.h - (cc->bwidth * 2); cc->flags |= CLIENT_VMAXIMIZED; resize: client_resize(cc, 0); xu_ewmh_set_net_wm_state(cc); } void client_toggle_hmaximize(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct geom xine; if (cc->flags & (CLIENT_FREEZE|CLIENT_STICKY)) 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; xine = screen_find_xinerama(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, CWM_GAP); cc->geom.x = xine.x; cc->geom.w = xine.w - (cc->bwidth * 2); cc->flags |= CLIENT_HMAXIMIZED; resize: client_resize(cc, 0); xu_ewmh_set_net_wm_state(cc); } 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); 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_ptrwarp(struct client_ctx *cc) { int x = cc->ptr.x, y = cc->ptr.y; if (x == -1 || y == -1) { x = cc->geom.w / 2; y = cc->geom.h / 2; } if (cc->flags & CLIENT_HIDDEN) client_unhide(cc); else client_raise(cc); xu_ptr_setpos(cc->win, x, y); } void client_ptrsave(struct client_ctx *cc) { int x, y; xu_ptr_getpos(cc->win, &x, &y); if (client_inbound(cc, x, y)) { cc->ptr.x = x; cc->ptr.y = y; } else { cc->ptr.x = -1; cc->ptr.y = -1; } } void client_hide(struct client_ctx *cc) { if (cc->flags & CLIENT_STICKY) return; XUnmapWindow(X_Dpy, cc->win); cc->flags &= ~CLIENT_ACTIVE; cc->flags |= CLIENT_HIDDEN; client_set_wm_state(cc, IconicState); if (cc == client_current()) client_none(cc->sc); } void client_unhide(struct client_ctx *cc) { if (cc->flags & CLIENT_STICKY) return; XMapRaised(X_Dpy, cc->win); cc->flags &= ~CLIENT_HIDDEN; client_set_wm_state(cc, 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, cc->bwidth); XSetWindowBorder(X_Dpy, cc->win, pixel); } 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) { if ((cc->wmh = XGetWMHints(X_Dpy, cc->win)) == NULL) return; if ((cc->wmh->flags & InputHint) && (cc->wmh->input)) cc->flags |= CLIENT_INPUT; if ((cc->wmh->flags & XUrgencyHint)) client_urgency(cc); } void client_msg(struct client_ctx *cc, Atom proto, Time ts) { XClientMessageEvent cm; (void)memset(&cm, 0, sizeof(cm)); cm.type = ClientMessage; cm.window = cc->win; cm.message_type = cwmh[WM_PROTOCOLS]; cm.format = 32; cm.data.l[0] = proto; cm.data.l[1] = ts; XSendEvent(X_Dpy, cc->win, False, NoEventMask, (XEvent *)&cm); } void client_send_delete(struct client_ctx *cc) { if (cc->flags & CLIENT_WM_DELETE_WINDOW) client_msg(cc, cwmh[WM_DELETE_WINDOW], CurrentTime); else XKillClient(X_Dpy, cc->win); } void client_setname(struct client_ctx *cc) { struct winname *wn; char *newname; if (!xu_getstrprop(cc->win, ewmh[_NET_WM_NAME], &newname)) if (!xu_getstrprop(cc->win, XA_WM_NAME, &newname)) newname = xstrdup(""); TAILQ_FOREACH(wn, &cc->nameq, entry) { if (strcmp(wn->name, newname) == 0) { /* Move to the last since we got a hit. */ TAILQ_REMOVE(&cc->nameq, wn, entry); TAILQ_INSERT_TAIL(&cc->nameq, wn, entry); goto match; } } wn = xmalloc(sizeof(*wn)); wn->name = newname; TAILQ_INSERT_TAIL(&cc->nameq, wn, entry); cc->nameqlen++; match: cc->name = wn->name; /* Now, do some garbage collection. */ if (cc->nameqlen > CLIENT_MAXNAMEQLEN) { wn = TAILQ_FIRST(&cc->nameq); assert(wn != NULL); TAILQ_REMOVE(&cc->nameq, wn, entry); free(wn->name); free(wn); cc->nameqlen--; } } void client_cycle(struct screen_ctx *sc, int flags) { struct client_ctx *oldcc, *newcc; int again = 1; if (TAILQ_EMPTY(&sc->clientq)) return; oldcc = client_current(); if (oldcc == NULL) oldcc = (flags & CWM_RCYCLE ? TAILQ_LAST(&sc->clientq, client_ctx_q) : TAILQ_FIRST(&sc->clientq)); newcc = oldcc; while (again) { again = 0; newcc = (flags & CWM_RCYCLE ? client_prev(newcc) : client_next(newcc)); /* Only cycle visible and non-ignored windows. */ if ((newcc->flags & (CLIENT_HIDDEN|CLIENT_IGNORE)) || ((flags & CWM_INGROUP) && (newcc->group != oldcc->group))) 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_ptrsave(oldcc); client_ptrwarp(newcc); } void client_cycle_leave(struct screen_ctx *sc) { struct client_ctx *cc; sc->cycling = 0; if ((cc = client_current())) { client_mtf(cc); group_toggle_membership_leave(cc); XUngrabKeyboard(X_Dpy, CurrentTime); } } static struct client_ctx * client_next(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct client_ctx *ccc; return((ccc = TAILQ_NEXT(cc, entry)) != NULL ? ccc : TAILQ_FIRST(&sc->clientq)); } static struct client_ctx * client_prev(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct client_ctx *ccc; return((ccc = TAILQ_PREV(cc, client_ctx_q, entry)) != NULL ? ccc : TAILQ_LAST(&sc->clientq, client_ctx_q)); } static void client_placecalc(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; int xslack, yslack; if (cc->hint.flags & (USPosition|PPosition)) { /* * Ignore XINERAMA screens, just make sure it's somewhere * in the virtual desktop. else it stops people putting xterms * at startup in the screen the mouse doesn't start in *sigh*. * XRandR bits mean that {x,y}max shouldn't be outside what's * currently there. */ xslack = sc->view.w - cc->geom.w - cc->bwidth * 2; yslack = sc->view.h - cc->geom.h - cc->bwidth * 2; cc->geom.x = MIN(cc->geom.x, xslack); cc->geom.y = MIN(cc->geom.y, yslack); } else { struct geom xine; int xmouse, ymouse; xu_ptr_getpos(sc->rootwin, &xmouse, &ymouse); xine = screen_find_xinerama(sc, xmouse, ymouse, CWM_GAP); xine.w += xine.x; xine.h += xine.y; xmouse = MAX(xmouse, xine.x) - cc->geom.w / 2; ymouse = MAX(ymouse, xine.y) - cc->geom.h / 2; xmouse = MAX(xmouse, xine.x); ymouse = MAX(ymouse, xine.y); xslack = xine.w - cc->geom.w - cc->bwidth * 2; yslack = xine.h - cc->geom.h - cc->bwidth * 2; if (xslack >= xine.x) { cc->geom.x = MAX(MIN(xmouse, xslack), xine.x); } else { cc->geom.x = xine.x; cc->geom.w = xine.w; } if (yslack >= xine.y) { cc->geom.y = MAX(MIN(ymouse, yslack), xine.y); } else { cc->geom.y = xine.y; cc->geom.h = xine.h; } } } static 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_getsizehints(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); 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_applysizehints(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); cc->geom.w = MAX(cc->geom.w, 1); cc->geom.h = MAX(cc->geom.h, 1); } static void client_mwm_hints(struct client_ctx *cc) { struct mwm_hints *mwmh; if (xu_getprop(cc->win, cwmh[_MOTIF_WM_HINTS], cwmh[_MOTIF_WM_HINTS], PROP_MWM_HINTS_ELEMENTS, (unsigned char **)&mwmh) == MWM_NUMHINTS) { if (mwmh->flags & MWM_HINTS_DECORATIONS && !(mwmh->decorations & MWM_DECOR_ALL) && !(mwmh->decorations & MWM_DECOR_BORDER)) cc->bwidth = 0; } } 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)) && tc->group) { group_movetogroup(cc, tc->group->num); if (tc->flags & CLIENT_IGNORE) cc->flags |= CLIENT_IGNORE; } } } static 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 group_ctx *gc = cc->group; struct screen_ctx *sc = cc->sc; struct geom xine; int i, n, mh, x, h, w; if (!gc) return; i = n = 0; TAILQ_FOREACH(ci, &gc->clientq, group_entry) { if (ci->flags & CLIENT_HIDDEN || ci->flags & CLIENT_IGNORE || (ci == cc)) continue; n++; } if (n == 0) return; xine = screen_find_xinerama(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, CWM_GAP); if (cc->flags & CLIENT_VMAXIMIZED || cc->geom.h + (cc->bwidth * 2) >= xine.h) return; cc->flags &= ~CLIENT_HMAXIMIZED; cc->geom.x = xine.x; cc->geom.y = xine.y; cc->geom.w = xine.w - (cc->bwidth * 2); client_resize(cc, 1); client_ptrwarp(cc); mh = cc->geom.h + (cc->bwidth * 2); x = xine.x; w = xine.w / n; h = xine.h - mh; TAILQ_FOREACH(ci, &gc->clientq, group_entry) { if (ci->flags & CLIENT_HIDDEN || ci->flags & CLIENT_IGNORE || (ci == cc)) continue; ci->bwidth = Conf.bwidth; ci->geom.y = xine.y + mh; ci->geom.x = x; ci->geom.h = h - (ci->bwidth * 2); ci->geom.w = w - (ci->bwidth * 2); if (i + 1 == n) ci->geom.w = xine.x + xine.w - ci->geom.x - (ci->bwidth * 2); x += w; client_resize(ci, 1); i++; } } void client_vtile(struct client_ctx *cc) { struct client_ctx *ci; struct group_ctx *gc = cc->group; struct screen_ctx *sc = cc->sc; struct geom xine; int i, n, mw, y, h, w; if (!gc) return; i = n = 0; TAILQ_FOREACH(ci, &gc->clientq, group_entry) { if (ci->flags & CLIENT_HIDDEN || ci->flags & CLIENT_IGNORE || (ci == cc)) continue; n++; } if (n == 0) return; xine = screen_find_xinerama(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, CWM_GAP); if (cc->flags & CLIENT_HMAXIMIZED || cc->geom.w + (cc->bwidth * 2) >= xine.w) return; cc->flags &= ~CLIENT_VMAXIMIZED; cc->geom.x = xine.x; cc->geom.y = xine.y; cc->geom.h = xine.h - (cc->bwidth * 2); client_resize(cc, 1); client_ptrwarp(cc); mw = cc->geom.w + (cc->bwidth * 2); y = xine.y; h = xine.h / n; w = xine.w - mw; TAILQ_FOREACH(ci, &gc->clientq, group_entry) { if (ci->flags & CLIENT_HIDDEN || ci->flags & CLIENT_IGNORE || (ci == cc)) continue; ci->bwidth = Conf.bwidth; ci->geom.y = y; ci->geom.x = xine.x + mw; ci->geom.h = h - (ci->bwidth * 2); ci->geom.w = w - (ci->bwidth * 2); if (i + 1 == n) ci->geom.h = xine.y + xine.h - ci->geom.y - (ci->bwidth * 2); y += h; client_resize(ci, 1); i++; } } long client_get_wm_state(struct client_ctx *cc) { long *p, state = -1; if (xu_getprop(cc->win, cwmh[WM_STATE], cwmh[WM_STATE], 2L, (unsigned char **)&p) > 0) { state = *p; XFree(p); } return(state); } void client_set_wm_state(struct client_ctx *cc, long state) { long data[] = { state, None }; XChangeProperty(X_Dpy, cc->win, cwmh[WM_STATE], cwmh[WM_STATE], 32, PropModeReplace, (unsigned char *)data, 2); } cwm-5.6/conf.c000066400000000000000000000453431246075335000132610ustar00rootroot00000000000000/* * 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_getmask(const char *, unsigned int *); static void conf_cmd_remove(struct conf *, const char *); static void conf_unbind_kbd(struct conf *, struct binding *); static void conf_unbind_mouse(struct conf *, struct binding *); int conf_cmd_add(struct conf *c, const char *name, const char *path) { struct cmd *cmd; cmd = xmalloc(sizeof(*cmd)); cmd->name = xstrdup(name); if (strlcpy(cmd->path, path, sizeof(cmd->path)) >= sizeof(cmd->path)) { free(cmd->name); free(cmd); return(0); } conf_cmd_remove(c, name); TAILQ_INSERT_TAIL(&c->cmdq, cmd, entry); return(1); } static void conf_cmd_remove(struct conf *c, const char *name) { struct cmd *cmd = NULL, *cmdnxt; TAILQ_FOREACH_SAFE(cmd, &c->cmdq, entry, cmdnxt) { if (strcmp(cmd->name, name) == 0) { TAILQ_REMOVE(&c->cmdq, cmd, entry); free(cmd->name); free(cmd); } } } void conf_autogroup(struct conf *c, int num, const char *val) { struct autogroupwin *aw; char *p; aw = xcalloc(1, sizeof(*aw)); if ((p = strchr(val, ',')) == NULL) { aw->name = NULL; aw->class = xstrdup(val); } else { *(p++) = '\0'; aw->name = xstrdup(val); aw->class = xstrdup(p); } aw->num = num; TAILQ_INSERT_TAIL(&c->autogroupq, aw, 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); } 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 */ }; void conf_screen(struct screen_ctx *sc) { unsigned int i; XftColor xc; Colormap colormap = DefaultColormap(X_Dpy, sc->which); Visual *visual = DefaultVisual(X_Dpy, sc->which); 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, "XftFontOpenName"); } 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, visual, colormap, &xc.color, &sc->xftcolor[CWM_COLOR_MENU_FONT_SEL])) warnx("XftColorAllocValue: %s", Conf.color[i]); break; } if (XftColorAllocName(X_Dpy, visual, colormap, Conf.color[i], &xc)) { sc->xftcolor[i] = xc; XftColorFree(X_Dpy, visual, colormap, &xc); } else { warnx("XftColorAllocName: %s", Conf.color[i]); XftColorAllocName(X_Dpy, visual, colormap, color_binds[i], &sc->xftcolor[i]); } } sc->menuwin = 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); sc->xftdraw = XftDrawCreate(X_Dpy, sc->menuwin, visual, colormap); if (sc->xftdraw == NULL) errx(1, "XftDrawCreate"); conf_grab_kbd(sc->rootwin); } static const struct { const char *key; const char *func; } kbd_binds[] = { { "CM-Return", "terminal" }, { "CM-Delete", "lock" }, { "M-question", "exec" }, { "CM-w", "exec_wm" }, { "M-period", "ssh" }, { "M-Return", "hide" }, { "M-Down", "lower" }, { "M-Up", "raise" }, { "M-slash", "search" }, { "C-slash", "menusearch" }, { "M-Tab", "cycle" }, { "MS-Tab", "rcycle" }, { "CM-n", "label" }, { "CM-x", "delete" }, { "CM-0", "nogroup" }, { "CM-1", "group1" }, { "CM-2", "group2" }, { "CM-3", "group3" }, { "CM-4", "group4" }, { "CM-5", "group5" }, { "CM-6", "group6" }, { "CM-7", "group7" }, { "CM-8", "group8" }, { "CM-9", "group9" }, { "M-Right", "cyclegroup" }, { "M-Left", "rcyclegroup" }, { "CM-g", "grouptoggle" }, { "CM-f", "fullscreen" }, { "CM-m", "maximize" }, { "CM-s", "sticky" }, { "CM-equal", "vmaximize" }, { "CMS-equal", "hmaximize" }, { "CMS-f", "freeze" }, { "CMS-r", "restart" }, { "CMS-q", "quit" }, { "M-h", "moveleft" }, { "M-j", "movedown" }, { "M-k", "moveup" }, { "M-l", "moveright" }, { "M-H", "bigmoveleft" }, { "M-J", "bigmovedown" }, { "M-K", "bigmoveup" }, { "M-L", "bigmoveright" }, { "CM-h", "resizeleft" }, { "CM-j", "resizedown" }, { "CM-k", "resizeup" }, { "CM-l", "resizeright" }, { "CM-H", "bigresizeleft" }, { "CM-J", "bigresizedown" }, { "CM-K", "bigresizeup" }, { "CM-L", "bigresizeright" }, { "C-Left", "ptrmoveleft" }, { "C-Down", "ptrmovedown" }, { "C-Up", "ptrmoveup" }, { "C-Right", "ptrmoveright" }, { "CS-Left", "bigptrmoveleft" }, { "CS-Down", "bigptrmovedown" }, { "CS-Up", "bigptrmoveup" }, { "CS-Right", "bigptrmoveright" }, }, mouse_binds[] = { { "1", "menu_unhide" }, { "2", "menu_group" }, { "3", "menu_cmd" }, { "M-1", "window_move" }, { "CM-1", "window_grouptoggle" }, { "M-2", "window_resize" }, { "M-3", "window_lower" }, { "CMS-3", "window_hide" }, }; void conf_init(struct conf *c) { unsigned int i; c->bwidth = CONF_BWIDTH; c->mamount = CONF_MAMOUNT; c->snapdist = CONF_SNAPDIST; TAILQ_INIT(&c->ignoreq); TAILQ_INIT(&c->cmdq); TAILQ_INIT(&c->keybindingq); TAILQ_INIT(&c->autogroupq); TAILQ_INIT(&c->mousebindingq); for (i = 0; i < nitems(kbd_binds); i++) conf_bind_kbd(c, kbd_binds[i].key, kbd_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"); (void)snprintf(c->known_hosts, sizeof(c->known_hosts), "%s/%s", homedir, ".ssh/known_hosts"); c->font = xstrdup(CONF_FONT); } void conf_clear(struct conf *c) { struct autogroupwin *aw; struct binding *kb, *mb; struct winname *wn; struct cmd *cmd; int i; while ((cmd = TAILQ_FIRST(&c->cmdq)) != NULL) { TAILQ_REMOVE(&c->cmdq, cmd, entry); free(cmd->name); free(cmd); } while ((kb = TAILQ_FIRST(&c->keybindingq)) != NULL) { TAILQ_REMOVE(&c->keybindingq, kb, entry); free(kb); } while ((aw = TAILQ_FIRST(&c->autogroupq)) != NULL) { TAILQ_REMOVE(&c->autogroupq, aw, entry); free(aw->class); free(aw->name); free(aw); } while ((wn = TAILQ_FIRST(&c->ignoreq)) != NULL) { TAILQ_REMOVE(&c->ignoreq, wn, entry); free(wn->name); free(wn); } while ((mb = TAILQ_FIRST(&c->mousebindingq)) != NULL) { TAILQ_REMOVE(&c->mousebindingq, mb, entry); free(mb); } for (i = 0; i < CWM_COLOR_NITEMS; i++) free(c->color[i]); free(c->font); } void conf_client(struct client_ctx *cc) { struct winname *wn; int ignore = 0; TAILQ_FOREACH(wn, &Conf.ignoreq, entry) { if (strncasecmp(wn->name, cc->name, strlen(wn->name)) == 0) { ignore = 1; break; } } cc->bwidth = ignore ? 0 : Conf.bwidth; cc->flags |= ignore ? CLIENT_IGNORE : 0; } static const struct { const char *tag; void (*handler)(struct client_ctx *, union arg *); int flags; union arg argument; } name_to_func[] = { { "lower", kbfunc_client_lower, CWM_WIN, {0} }, { "raise", kbfunc_client_raise, CWM_WIN, {0} }, { "search", kbfunc_client_search, 0, {0} }, { "menusearch", kbfunc_menu_cmd, 0, {0} }, { "hide", kbfunc_client_hide, CWM_WIN, {0} }, { "cycle", kbfunc_client_cycle, 0, {.i = CWM_CYCLE} }, { "rcycle", kbfunc_client_cycle, 0, {.i = CWM_RCYCLE} }, { "label", kbfunc_client_label, CWM_WIN, {0} }, { "delete", kbfunc_client_delete, CWM_WIN, {0} }, { "group1", kbfunc_client_group, 0, {.i = 1} }, { "group2", kbfunc_client_group, 0, {.i = 2} }, { "group3", kbfunc_client_group, 0, {.i = 3} }, { "group4", kbfunc_client_group, 0, {.i = 4} }, { "group5", kbfunc_client_group, 0, {.i = 5} }, { "group6", kbfunc_client_group, 0, {.i = 6} }, { "group7", kbfunc_client_group, 0, {.i = 7} }, { "group8", kbfunc_client_group, 0, {.i = 8} }, { "group9", kbfunc_client_group, 0, {.i = 9} }, { "grouponly1", kbfunc_client_grouponly, 0, {.i = 1} }, { "grouponly2", kbfunc_client_grouponly, 0, {.i = 2} }, { "grouponly3", kbfunc_client_grouponly, 0, {.i = 3} }, { "grouponly4", kbfunc_client_grouponly, 0, {.i = 4} }, { "grouponly5", kbfunc_client_grouponly, 0, {.i = 5} }, { "grouponly6", kbfunc_client_grouponly, 0, {.i = 6} }, { "grouponly7", kbfunc_client_grouponly, 0, {.i = 7} }, { "grouponly8", kbfunc_client_grouponly, 0, {.i = 8} }, { "grouponly9", kbfunc_client_grouponly, 0, {.i = 9} }, { "movetogroup1", kbfunc_client_movetogroup, CWM_WIN, {.i = 1} }, { "movetogroup2", kbfunc_client_movetogroup, CWM_WIN, {.i = 2} }, { "movetogroup3", kbfunc_client_movetogroup, CWM_WIN, {.i = 3} }, { "movetogroup4", kbfunc_client_movetogroup, CWM_WIN, {.i = 4} }, { "movetogroup5", kbfunc_client_movetogroup, CWM_WIN, {.i = 5} }, { "movetogroup6", kbfunc_client_movetogroup, CWM_WIN, {.i = 6} }, { "movetogroup7", kbfunc_client_movetogroup, CWM_WIN, {.i = 7} }, { "movetogroup8", kbfunc_client_movetogroup, CWM_WIN, {.i = 8} }, { "movetogroup9", kbfunc_client_movetogroup, CWM_WIN, {.i = 9} }, { "nogroup", kbfunc_client_nogroup, 0, {0} }, { "cyclegroup", kbfunc_client_cyclegroup, 0, {.i = CWM_CYCLE} }, { "rcyclegroup", kbfunc_client_cyclegroup, 0, {.i = CWM_RCYCLE} }, { "cycleingroup", kbfunc_client_cycle, CWM_WIN, {.i = CWM_CYCLE|CWM_INGROUP} }, { "rcycleingroup", kbfunc_client_cycle, CWM_WIN, {.i = CWM_RCYCLE|CWM_INGROUP} }, { "grouptoggle", kbfunc_client_grouptoggle, CWM_WIN, {0}}, { "sticky", kbfunc_client_toggle_sticky, CWM_WIN, {0} }, { "fullscreen", kbfunc_client_toggle_fullscreen, CWM_WIN, {0} }, { "maximize", kbfunc_client_toggle_maximize, CWM_WIN, {0} }, { "vmaximize", kbfunc_client_toggle_vmaximize, CWM_WIN, {0} }, { "hmaximize", kbfunc_client_toggle_hmaximize, CWM_WIN, {0} }, { "freeze", kbfunc_client_toggle_freeze, CWM_WIN, {0} }, { "restart", kbfunc_cwm_status, 0, {.i = CWM_RESTART} }, { "quit", kbfunc_cwm_status, 0, {.i = CWM_QUIT} }, { "exec", kbfunc_exec, 0, {.i = CWM_EXEC_PROGRAM} }, { "exec_wm", kbfunc_exec, 0, {.i = CWM_EXEC_WM} }, { "ssh", kbfunc_ssh, 0, {0} }, { "terminal", kbfunc_term, 0, {0} }, { "lock", kbfunc_lock, 0, {0} }, { "moveup", kbfunc_client_moveresize, CWM_WIN, {.i = (CWM_UP|CWM_MOVE)} }, { "movedown", kbfunc_client_moveresize, CWM_WIN, {.i = (CWM_DOWN|CWM_MOVE)} }, { "moveright", kbfunc_client_moveresize, CWM_WIN, {.i = (CWM_RIGHT|CWM_MOVE)} }, { "moveleft", kbfunc_client_moveresize, CWM_WIN, {.i = (CWM_LEFT|CWM_MOVE)} }, { "bigmoveup", kbfunc_client_moveresize, CWM_WIN, {.i = (CWM_UP|CWM_MOVE|CWM_BIGMOVE)} }, { "bigmovedown", kbfunc_client_moveresize, CWM_WIN, {.i = (CWM_DOWN|CWM_MOVE|CWM_BIGMOVE)} }, { "bigmoveright", kbfunc_client_moveresize, CWM_WIN, {.i = (CWM_RIGHT|CWM_MOVE|CWM_BIGMOVE)} }, { "bigmoveleft", kbfunc_client_moveresize, CWM_WIN, {.i = (CWM_LEFT|CWM_MOVE|CWM_BIGMOVE)} }, { "resizeup", kbfunc_client_moveresize, CWM_WIN, {.i = (CWM_UP|CWM_RESIZE)} }, { "resizedown", kbfunc_client_moveresize, CWM_WIN, {.i = (CWM_DOWN|CWM_RESIZE)} }, { "resizeright", kbfunc_client_moveresize, CWM_WIN, {.i = (CWM_RIGHT|CWM_RESIZE)} }, { "resizeleft", kbfunc_client_moveresize, CWM_WIN, {.i = (CWM_LEFT|CWM_RESIZE)} }, { "bigresizeup", kbfunc_client_moveresize, CWM_WIN, {.i = (CWM_UP|CWM_RESIZE|CWM_BIGMOVE)} }, { "bigresizedown", kbfunc_client_moveresize, CWM_WIN, {.i = (CWM_DOWN|CWM_RESIZE|CWM_BIGMOVE)} }, { "bigresizeright", kbfunc_client_moveresize, CWM_WIN, {.i = (CWM_RIGHT|CWM_RESIZE|CWM_BIGMOVE)} }, { "bigresizeleft", kbfunc_client_moveresize, CWM_WIN, {.i = (CWM_LEFT|CWM_RESIZE|CWM_BIGMOVE)} }, { "ptrmoveup", kbfunc_client_moveresize, 0, {.i = (CWM_UP|CWM_PTRMOVE)} }, { "ptrmovedown", kbfunc_client_moveresize, 0, {.i = (CWM_DOWN|CWM_PTRMOVE)} }, { "ptrmoveleft", kbfunc_client_moveresize, 0, {.i = (CWM_LEFT|CWM_PTRMOVE)} }, { "ptrmoveright", kbfunc_client_moveresize, 0, {.i = (CWM_RIGHT|CWM_PTRMOVE)} }, { "bigptrmoveup", kbfunc_client_moveresize, 0, {.i = (CWM_UP|CWM_PTRMOVE|CWM_BIGMOVE)} }, { "bigptrmovedown", kbfunc_client_moveresize, 0, {.i = (CWM_DOWN|CWM_PTRMOVE|CWM_BIGMOVE)} }, { "bigptrmoveleft", kbfunc_client_moveresize, 0, {.i = (CWM_LEFT|CWM_PTRMOVE|CWM_BIGMOVE)} }, { "bigptrmoveright", kbfunc_client_moveresize, 0, {.i = (CWM_RIGHT|CWM_PTRMOVE|CWM_BIGMOVE)} }, { "htile", kbfunc_tile, CWM_WIN, {.i = CWM_TILE_HORIZ} }, { "vtile", kbfunc_tile, CWM_WIN, {.i = CWM_TILE_VERT} }, { "window_lower", kbfunc_client_lower, CWM_WIN, {0} }, { "window_raise", kbfunc_client_raise, CWM_WIN, {0} }, { "window_hide", kbfunc_client_hide, CWM_WIN, {0} }, { "window_move", mousefunc_client_move, CWM_WIN, {0} }, { "window_resize", mousefunc_client_resize, CWM_WIN, {0} }, { "window_grouptoggle", mousefunc_client_grouptoggle, CWM_WIN, {0} }, { "menu_group", mousefunc_menu_group, 0, {0} }, { "menu_unhide", mousefunc_menu_unhide, 0, {0} }, { "menu_cmd", mousefunc_menu_cmd, 0, {0} }, }; static const struct { const char ch; int mask; } bind_mods[] = { { 'C', ControlMask }, { 'M', Mod1Mask }, { '4', Mod4Mask }, { 'S', ShiftMask }, }; static const char * conf_bind_getmask(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_kbd(struct conf *c, const char *bind, const char *cmd) { struct binding *kb; const char *key; unsigned int i, mask; kb = xcalloc(1, sizeof(*kb)); key = conf_bind_getmask(bind, &mask); kb->modmask |= mask; kb->press.keysym = XStringToKeysym(key); if (kb->press.keysym == NoSymbol) { warnx("unknown symbol: %s", key); free(kb); return(0); } /* We now have the correct binding, remove duplicates. */ conf_unbind_kbd(c, kb); if (strcmp("unmap", cmd) == 0) { free(kb); return(1); } 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->flags = name_to_func[i].flags; kb->argument = name_to_func[i].argument; kb->argtype |= ARG_INT; TAILQ_INSERT_TAIL(&c->keybindingq, kb, entry); return(1); } kb->callback = kbfunc_cmdexec; kb->flags = 0; kb->argument.c = xstrdup(cmd); kb->argtype |= ARG_CHAR; TAILQ_INSERT_TAIL(&c->keybindingq, kb, entry); return(1); } static void conf_unbind_kbd(struct conf *c, struct binding *unbind) { struct binding *key = NULL, *keynxt; TAILQ_FOREACH_SAFE(key, &c->keybindingq, entry, keynxt) { if (key->modmask != unbind->modmask) continue; if (key->press.keysym == unbind->press.keysym) { TAILQ_REMOVE(&c->keybindingq, key, entry); if (key->argtype & ARG_CHAR) free(key->argument.c); free(key); } } } int conf_bind_mouse(struct conf *c, const char *bind, const char *cmd) { struct binding *mb; const char *button, *errstr; unsigned int i, mask; mb = xcalloc(1, sizeof(*mb)); button = conf_bind_getmask(bind, &mask); mb->modmask |= mask; mb->press.button = strtonum(button, Button1, Button5, &errstr); if (errstr) { warnx("button number is %s: %s", errstr, button); free(mb); return(0); } /* We now have the correct binding, remove duplicates. */ conf_unbind_mouse(c, mb); if (strcmp("unmap", cmd) == 0) { free(mb); return(1); } 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->flags = name_to_func[i].flags; mb->argument = name_to_func[i].argument; TAILQ_INSERT_TAIL(&c->mousebindingq, mb, entry); return(1); } return(0); } static void conf_unbind_mouse(struct conf *c, struct binding *unbind) { struct binding *mb = NULL, *mbnxt; TAILQ_FOREACH_SAFE(mb, &c->mousebindingq, entry, mbnxt) { if (mb->modmask != unbind->modmask) continue; if (mb->press.button == unbind->press.button) { TAILQ_REMOVE(&c->mousebindingq, mb, entry); free(mb); } } } static int cursor_binds[] = { XC_X_cursor, /* CF_DEFAULT */ XC_fleur, /* CF_MOVE */ XC_left_ptr, /* CF_NORMAL */ XC_question_arrow, /* CF_QUESTION */ XC_bottom_right_corner, /* CF_RESIZE */ }; 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_grab_mouse(Window win) { struct binding *mb; xu_btn_ungrab(win); TAILQ_FOREACH(mb, &Conf.mousebindingq, entry) { if (mb->flags & CWM_WIN) xu_btn_grab(win, mb->modmask, mb->press.button); } } void conf_grab_kbd(Window win) { struct binding *kb; xu_key_ungrab(win); TAILQ_FOREACH(kb, &Conf.keybindingq, entry) xu_key_grab(win, kb->modmask, kb->press.keysym); } static char *cwmhints[] = { "WM_STATE", "WM_DELETE_WINDOW", "WM_TAKE_FOCUS", "WM_PROTOCOLS", "_MOTIF_WM_HINTS", "UTF8_STRING", "WM_CHANGE_STATE", }; static char *ewmhints[] = { "_NET_SUPPORTED", "_NET_SUPPORTING_WM_CHECK", "_NET_ACTIVE_WINDOW", "_NET_CLIENT_LIST", "_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", }; void conf_atoms(void) { XInternAtoms(X_Dpy, cwmhints, nitems(cwmhints), False, cwmh); XInternAtoms(X_Dpy, ewmhints, nitems(ewmhints), False, ewmh); } cwm-5.6/cwm.1000066400000000000000000000164751246075335000130440ustar00rootroot00000000000000.\" $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 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. .Nm also aims to maintain the simplest and most pleasant aesthetic. .Pp The following notation is used throughout this page: .Pp .Bl -tag -width Ds -offset indent -compact .It Ic C Control. .It Ic M Meta. .It Ic S Shift. .It Ic M1 Left mouse button. .It Ic M2 Middle mouse button. .It Ic M3 Right mouse button. .El .Pp .Nm is very simple in its use. Most of the actions are initiated via key bindings. The default key bindings are described below; their functionality is described in more detail later. .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-/ Search for windows. .It Ic C-/ 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 Delete current window. .It Ic CM-[n] Select group n, where n is 1-9. .It Ic CM-0 Select 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-= Toggle vertical maximization of current window. .It Ic CMS-= Toggle horizontal maximization of current window. .It Ic M-? Spawn .Dq exec program dialog. .It Ic M-. 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 dialog; allows you to switch from .Nm to another window manager without restarting the X server. .It Ic CMS-r Restart the running .Xr cwm 1 . .It Ic CMS-q Quit .Nm . .El .Pp The default mouse bindings are: .Pp .Bl -tag -width Ds -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 options for .Nm 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. .El .Sh POINTER MOVEMENT The pointer can be moved with the use of the keyboard through bindings. .Ic C-[Up|Down|Left|Right] moves the pointer a small amount, while .Ic CS-[Up|Down|Left|Right] moves the pointer a larger amount. For example, to move the pointer to the left by a small amount, press .Ic C-Left . To move the pointer down by a larger amount, press .Ic CS-Down . .Sh WINDOW MOVEMENT AND RESIZING .Nm windows can be moved with the use of the keyboard through .Cm vi Ns -like bindings. .Ic M-[hjkl] moves the current window a small amount, while .Ic MS-[hjkl] moves the current window a larger amount. For example, to move the current window to the left a small amount, press .Ic M-h . To move the current window down by a larger amount, press .Ic MS-j . .Pp Similarly, windows may be resized with the same key bindings with the addition of the Control key. .Ic CM-[hjkl] resizes the window a small amount and .Ic CMS-[hjkl] resizes by a larger increment. .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 10n -offset indent -compact .It ! The window is the currently focused window. .It & The window is hidden. .El .Pp The following key bindings may be used to navigate the result list: .Pp .Bl -tag -width "[Down] or C-s or M-j" -offset indent -compact .It Ic [Down], C-s No or Ic M-j Select the next window in the list. .It Ic [Up], C-r No or Ic M-k Select the previous window in the list. .It Ic [Backspace] No or Ic C-h Backspace. .It Ic C-u Clear the input. .It Ic [Return] Focus the selected window. .It Ic [Esc] Cancel. .It Ic C-a Whenever there are no matching windows, list every window. .El .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. Currently, the only operation that is supported is to hide and unhide the grouped windows. 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 10n -offset indent -compact .It Ic M1 Show list of currently hidden windows. Clicking on an item will unhide that window. .It Ic M2 Show list of currently defined groups. Clicking on an item will hide/unhide that group. .It Ic M3 Show list of applications as defined in .Pa ~/.cwmrc . Clicking on an item will spawn that application. .El .Sh ENVIRONMENT .Bl -tag -width "DISPLAYXXX" .It DISPLAY .Nm starts on this display unless the .Fl d option is given. .El .Sh FILES .Bl -tag -width Ds .It Pa ~/.cwmrc .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-5.6/cwm.pub000066400000000000000000000001601246075335000134520ustar00rootroot00000000000000untrusted comment: portable cwm release key public key RWTdOib0PoIM0pmDAPnV2S9/AMRqTOVfTY/KAkFemdH13cqBDHdduTas cwm-5.6/cwmrc.5000066400000000000000000000243271246075335000133700ustar00rootroot00000000000000.\" $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: .Pp .Bl -tag -width Ds -compact .It Ic autogroup Ar group windowclass .It Ic autogroup Ar group windowname,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 . .Pp .It Ic bind Ar keys command Cause the creation of a key binding, or replacement of a default key binding. The modifier keys come first, followed by a .Sq - . .Pp The following modifiers are recognised: .Pp .Bl -tag -width Ds -offset indent -compact .It C The Control key. .It M The Meta key. .It S The Shift key. .It 4 The Mod4 key (normally the windows key). .El .Pp The .Sq - should be followed by a keysym name, taken from .Pa /usr/X11R6/include/X11/keysymdef.h . The .Ar command may either be one from the .Sx BIND COMMAND LIST (see below) or the command line that is to be executed. .Pp A special .Ar command keyword .Dq unmap can be used to remove the named key binding. This can be used to remove a binding which conflicts with an application. .Pp .It Ic borderwidth Ar pixels Set the window border width to .Ar pixels . .Pp .It Ic color activeborder Ar color Set the color of the active border. .Pp .It Ic color font Ar color Set menu font color. .Pp .It Ic color selfont Ar color Set font color for selected menu item. .Pp .It Ic color groupborder Ar color Set the color of the border while grouping a window. .Pp .It Ic color inactiveborder Ar color Set the color of the inactive border. .Pp .It Ic color menubg Ar color Set menu background color. .Pp .It Ic color menufg Ar color Set menu foreground color. .Pp .It Ic color urgencyborder Ar color Set the color of the border of a window indicating urgency. .Pp .It Ic color ungroupborder Ar color Set the color of the border while ungrouping a window. .Pp .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 execve 2 . .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. .Pp .It Ic fontname Ar font Change the default .Ar font for .Xr Xft 3 . .Pp .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. .Pp .It Ic ignore Ar windowname Ignore, and do not warp to, windows with the name .Ar windowname when drawing borders and cycling through windows. .Pp .It Ic mousebind Ar buttons command Cause the creation of a mouse binding, or replacement of a default mouse binding. The modifier keys come first, followed by a .Sq - . .Pp The following modifiers are recognised: .Pp .Bl -tag -width Ds -offset indent -compact .It C The Control key. .It M The Meta key. .It S The Shift key. .It 4 The Mod4 key (normally the windows key). .El .Pp The .Sq - should be followed by number: .Pp .Bl -tag -width Ds -offset indent -compact .It 1 Left mouse button. .It 2 Middle mouse button. .It 3 Right mouse button. .It 4 Scroll up mouse button. .It 5 Scroll down mouse button. .El .Pp The .Ar command may be taken from the .Sx MOUSEBIND COMMAND LIST (see below). .Pp .It Ic moveamount Ar pixels Set a default size for the keyboard movement bindings, in pixels. The default is 1. .Pp .It Ic snapdist Ar pixels Minimum distance to snap-to adjacent edge, in pixels. The default is 0. .Pp .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. .El .Sh EXAMPLE CONFIGURATION .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 CM-r label bind CS-Return "xterm -e top" bind 4-o unmap bind CM-equal unmap bind CMS-equal unmap bind C4-equal vmaximize bind C4S-equal hmaximize bind M-1 grouponly1 bind M-2 grouponly2 bind M-3 grouponly3 bind MS-1 movetogroup1 bind MS-2 movetogroup2 bind MS-3 movetogroup3 # Mouse bindings mousebind M-2 window_lower mousebind M-3 window_resize .Ed .Sh BIND COMMAND LIST .Bl -tag -width 18n -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 search Launch window search menu. .It menusearch Launch application search menu. .It exec Launch .Dq exec program menu. .It exec_wm Launch .Dq exec WindowManager menu. .It ssh Launch .Dq ssh menu. .It group[n] Select group n, where n is 1-9. .It grouponly[n] Like .Ar group[n] but also hides the other groups. .It nogroup Select all groups. .It grouptoggle Toggle group membership of current window. .It movetogroup[n] Hide current window from display and move to group n, where n is 1-9. .It cyclegroup Forward cycle through groups. .It rcyclegroup Reverse cycle through groups. .It cycle Forward cycle through windows. .It rcycle Reverse cycle through windows. .It cycleingroup Forward cycle through windows in current group. .It rcycleingroup Reverse cycle through windows in current group. .It delete Delete current window. .It hide Hide current window. .It lower Lower current window. .It raise Raise current window. .It label Label current window. .It freeze Freeze current window geometry. .It sticky Stick current window to all groups (same as assigning to nogroup). .It fullscreen Full-screen current window (gap + border removed). .It maximize Maximize current window (gap + border honored). .It vmaximize Vertically maximize current window (gap + border honored). .It hmaximize Horizontally maximize current window (gap + border honored). .It moveup Move window .Ar moveamount pixels up. .It movedown Move window .Ar moveamount pixels down. .It moveright Move window .Ar moveamount pixels right. .It moveleft Move window .Ar moveamount pixels left. .It bigmoveup Move window 10 times .Ar moveamount pixels up. .It bigmovedown Move window 10 times .Ar moveamount pixels down. .It bigmoveright Move window 10 times .Ar moveamount pixels right. .It bigmoveleft Move window 10 times .Ar moveamount pixels left. .It resizeup Resize window .Ar moveamount pixels up. .It resizedown Resize window .Ar moveamount pixels down. .It resizeright Resize window .Ar moveamount pixels right. .It resizeleft Resize window .Ar moveamount pixels left. .It bigresizeup Resize window 10 times .Ar moveamount pixels up. .It bigresizedown Resize window 10 times .Ar moveamount pixels down. .It bigresizeright Resize window 10 times .Ar moveamount pixels right. .It bigresizeleft Resize window 10 times .Ar moveamount pixels left. .It ptrmoveup Move pointer .Ar moveamount pixels up. .It ptrmovedown Move pointer .Ar moveamount pixels down. .It ptrmoveright Move pointer .Ar moveamount pixels right. .It ptrmoveleft Move pointer .Ar moveamount pixels left. .It bigptrmoveup Move pointer 10 times .Ar moveamount pixels up. .It bigptrmovedown Move pointer 10 times .Ar moveamount pixels down. .It bigptrmoveright Move pointer 10 times .Ar moveamount pixels right. .It bigptrmoveleft Move pointer 10 times .Ar moveamount pixels left. .It htile Current window is placed at the top of the screen and maximized horizontally, other windows in its group share remaining screen space. .It vtile Current window is placed on the left of the screen and maximized vertically, other windows in its group share remaining screen space. .El .Sh MOUSEBIND COMMAND LIST .Bl -tag -width 18n -compact .It window_move Move current window. .It window_resize Resize current window. .It window_lower Lower current window. .It window_raise Raise current window. .It window_hide Hide current window. .It window_grouptoggle Toggle group membership of current window. .It cyclegroup Forward cycle through groups. .It rcyclegroup Reverse cycle through groups. .It menu_group Launch group list. .It menu_unhide Launch hidden window list. .It menu_cmd Launch command list. .El .Sh FILES .Bl -tag -width "~/.cwmrcXXX" -compact .It Pa ~/.cwmrc default .Xr cwm 1 configuration file .El .Sh SEE ALSO .Xr cwm 1 .Sh HISTORY The .Nm file format first appeared in .Ox 4.4 . cwm-5.6/fgetln.c000066400000000000000000000053041246075335000136040ustar00rootroot00000000000000/* $NetBSD: fgetln.c,v 1.9 2008/04/29 06:53:03 martin Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Christos Zoulas. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #if !HAVE_FGETLN #include #ifndef HAVE_NBTOOL_CONFIG_H /* These headers are required, but included from nbtool_config.h */ #include #include #include #include #endif char * fgetln(FILE *fp, size_t *len) { static char *buf = NULL; static size_t bufsiz = 0; char *ptr; if (buf == NULL) { bufsiz = BUFSIZ; if ((buf = malloc(bufsiz)) == NULL) return NULL; } if (fgets(buf, bufsiz, fp) == NULL) return NULL; *len = 0; while ((ptr = strchr(&buf[*len], '\n')) == NULL) { size_t nbufsiz = bufsiz + BUFSIZ; char *nbuf = realloc(buf, nbufsiz); if (nbuf == NULL) { int oerrno = errno; free(buf); errno = oerrno; buf = NULL; return NULL; } else buf = nbuf; if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL) { buf[bufsiz] = '\0'; *len = strlen(buf); return buf; } *len = bufsiz; bufsiz = nbufsiz; } *len = (ptr - buf) + 1; return buf; } #endif #ifdef TEST int main(int argc, char *argv[]) { char *p; size_t len; while ((p = fgetln(stdin, &len)) != NULL) { (void)printf("%zu %s", len, p); free(p); } return 0; } #endif cwm-5.6/group.c000066400000000000000000000164321246075335000134650ustar00rootroot00000000000000/* * 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 #include "calmwm.h" static void group_assign(struct group_ctx *, struct client_ctx *); static void group_restack(struct group_ctx *); static void group_setactive(struct group_ctx *); const char *num_to_name[] = { "nogroup", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" }; static void group_assign(struct group_ctx *gc, struct client_ctx *cc) { if (cc->group != NULL) TAILQ_REMOVE(&cc->group->clientq, cc, group_entry); cc->group = gc; if (cc->group != NULL) TAILQ_INSERT_TAIL(&gc->clientq, cc, group_entry); xu_ewmh_net_wm_desktop(cc); } void group_hide(struct group_ctx *gc) { struct client_ctx *cc; screen_updatestackingorder(gc->sc); TAILQ_FOREACH(cc, &gc->clientq, group_entry) client_hide(cc); } void group_show(struct group_ctx *gc) { struct client_ctx *cc; TAILQ_FOREACH(cc, &gc->clientq, group_entry) client_unhide(cc); group_restack(gc); group_setactive(gc); } static void group_restack(struct group_ctx *gc) { struct client_ctx *cc; Window *winlist; int i, lastempty = -1; int nwins = 0, highstack = 0; TAILQ_FOREACH(cc, &gc->clientq, group_entry) { if (cc->stackingorder > highstack) highstack = cc->stackingorder; } winlist = xcalloc((highstack + 1), sizeof(*winlist)); /* Invert the stacking order for XRestackWindows(). */ TAILQ_FOREACH(cc, &gc->clientq, group_entry) { 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) { struct group_ctx *gc; gc = xmalloc(sizeof(*gc)); gc->sc = sc; gc->name = xstrdup(num_to_name[num]); gc->num = num; TAILQ_INIT(&gc->clientq); TAILQ_INSERT_TAIL(&sc->groupq, gc, entry); if (num == 1) group_setactive(gc); } void group_setactive(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; if (idx < 0 || idx >= CALMWM_NGROUPS) errx(1, "group_movetogroup: index out of range (%d)", idx); TAILQ_FOREACH(gc, &sc->groupq, entry) { if (gc->num == idx) break; } if (cc->group == gc) return; if (group_holds_only_hidden(gc)) client_hide(cc); group_assign(gc, cc); } void group_toggle_membership_enter(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct group_ctx *gc = sc->group_active; if (gc == cc->group) { group_assign(NULL, cc); cc->flags |= CLIENT_UNGROUP; } else { group_assign(gc, cc); cc->flags |= CLIENT_GROUP; } client_draw_border(cc); } void group_toggle_membership_leave(struct client_ctx *cc) { cc->flags &= ~CLIENT_HIGHLIGHT; client_draw_border(cc); } int group_holds_only_sticky(struct group_ctx *gc) { struct client_ctx *cc; TAILQ_FOREACH(cc, &gc->clientq, group_entry) { if (!(cc->flags & CLIENT_STICKY)) return(0); } return(1); } int group_holds_only_hidden(struct group_ctx *gc) { struct client_ctx *cc; int hidden = 0, same = 0; TAILQ_FOREACH(cc, &gc->clientq, group_entry) { if (cc->flags & CLIENT_STICKY) continue; if (hidden == ((cc->flags & CLIENT_HIDDEN) ? 1 : 0)) same++; } if (same == 0) hidden = !hidden; return(hidden); } void group_hidetoggle(struct screen_ctx *sc, int idx) { struct group_ctx *gc; if (idx < 0 || idx >= CALMWM_NGROUPS) errx(1, "group_hidetoggle: index out of range (%d)", idx); TAILQ_FOREACH(gc, &sc->groupq, entry) { if (gc->num == idx) break; } if (group_holds_only_hidden(gc)) group_show(gc); else { group_hide(gc); /* make clients stick to empty group */ if (TAILQ_EMPTY(&gc->clientq)) group_setactive(gc); } } void group_only(struct screen_ctx *sc, int idx) { struct group_ctx *gc; if (idx < 0 || idx >= CALMWM_NGROUPS) errx(1, "group_only: index out of range (%d)", idx); TAILQ_FOREACH(gc, &sc->groupq, entry) { if (gc->num == idx) group_show(gc); else group_hide(gc); } } /* * Cycle through active groups. If none exist, then just stay put. */ void group_cycle(struct screen_ctx *sc, int flags) { struct group_ctx *gc, *showgroup = NULL; assert(sc->group_active != NULL); gc = sc->group_active; for (;;) { gc = (flags & CWM_RCYCLE) ? TAILQ_PREV(gc, group_ctx_q, entry) : TAILQ_NEXT(gc, entry); if (gc == NULL) gc = (flags & CWM_RCYCLE) ? TAILQ_LAST(&sc->groupq, group_ctx_q) : TAILQ_FIRST(&sc->groupq); if (gc == sc->group_active) break; if (!group_holds_only_sticky(gc) && showgroup == NULL) showgroup = gc; else if (!group_holds_only_hidden(gc)) group_hide(gc); } if (showgroup == NULL) return; group_hide(sc->group_active); if (group_holds_only_hidden(showgroup)) group_show(showgroup); else group_setactive(showgroup); } void group_alltoggle(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_autogroup(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; struct autogroupwin *aw; struct group_ctx *gc; int num = -2, both_match = 0; long *grpnum; if (cc->ch.res_class == NULL || cc->ch.res_name == NULL) return; if (xu_getprop(cc->win, ewmh[_NET_WM_DESKTOP], XA_CARDINAL, 1, (unsigned char **)&grpnum) > 0) { num = *grpnum; if (num > CALMWM_NGROUPS || num < -1) num = CALMWM_NGROUPS - 1; XFree(grpnum); } else { TAILQ_FOREACH(aw, &Conf.autogroupq, entry) { if (strcmp(aw->class, cc->ch.res_class) == 0) { if ((aw->name != NULL) && (strcmp(aw->name, cc->ch.res_name) == 0)) { num = aw->num; both_match = 1; } else if (aw->name == NULL && !both_match) num = aw->num; } } } if ((num == -1) || (num == 0)) { group_assign(NULL, cc); return; } TAILQ_FOREACH(gc, &sc->groupq, entry) { if (gc->num == num) { group_assign(gc, cc); return; } } if (Conf.flags & CONF_STICKY_GROUPS) group_assign(sc->group_active, cc); else group_assign(NULL, cc); } cwm-5.6/kbfunc.c000066400000000000000000000250621246075335000136000ustar00rootroot00000000000000/* * 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$ */ #include #include "queue.h" #include #include #include #include #include #include #include #include #include #include #include "calmwm.h" #define HASH_MARKER "|1|" extern sig_atomic_t cwm_status; void kbfunc_client_lower(struct client_ctx *cc, union arg *arg) { client_ptrsave(cc); client_lower(cc); } void kbfunc_client_raise(struct client_ctx *cc, union arg *arg) { client_raise(cc); } #define TYPEMASK (CWM_MOVE | CWM_RESIZE | CWM_PTRMOVE) #define MOVEMASK (CWM_UP | CWM_DOWN | CWM_LEFT | CWM_RIGHT) void kbfunc_client_moveresize(struct client_ctx *cc, union arg *arg) { struct screen_ctx *sc = cc->sc; struct geom xine; int x, y, flags, amt; unsigned int mx, my; if (cc->flags & (CLIENT_FREEZE|CLIENT_STICKY)) return; mx = my = 0; flags = arg->i; amt = Conf.mamount; if (flags & CWM_BIGMOVE) { flags -= CWM_BIGMOVE; amt = amt * 10; } switch (flags & MOVEMASK) { case CWM_UP: my -= amt; break; case CWM_DOWN: my += amt; break; case CWM_RIGHT: mx += amt; break; case CWM_LEFT: mx -= amt; break; } switch (flags & TYPEMASK) { case CWM_MOVE: cc->geom.x += mx; if (cc->geom.x + cc->geom.w < 0) cc->geom.x = -cc->geom.w; if (cc->geom.x > sc->view.w - 1) cc->geom.x = sc->view.w - 1; cc->geom.y += my; if (cc->geom.y + cc->geom.h < 0) cc->geom.y = -cc->geom.h; if (cc->geom.y > sc->view.h - 1) cc->geom.y = sc->view.h - 1; xine = screen_find_xinerama(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, CWM_GAP); cc->geom.x += client_snapcalc(cc->geom.x, cc->geom.x + cc->geom.w + (cc->bwidth * 2), xine.x, xine.x + xine.w, sc->snapdist); cc->geom.y += client_snapcalc(cc->geom.y, cc->geom.y + cc->geom.h + (cc->bwidth * 2), xine.y, xine.y + xine.h, sc->snapdist); client_move(cc); xu_ptr_getpos(cc->win, &x, &y); cc->ptr.x = x + mx; cc->ptr.y = y + my; client_ptrwarp(cc); break; case CWM_RESIZE: if ((cc->geom.w += mx) < 1) cc->geom.w = 1; if ((cc->geom.h += my) < 1) cc->geom.h = 1; client_resize(cc, 1); /* Make sure the pointer stays within the window. */ xu_ptr_getpos(cc->win, &cc->ptr.x, &cc->ptr.y); if (cc->ptr.x > cc->geom.w) cc->ptr.x = cc->geom.w - cc->bwidth; if (cc->ptr.y > cc->geom.h) cc->ptr.y = cc->geom.h - cc->bwidth; client_ptrwarp(cc); break; case CWM_PTRMOVE: xu_ptr_getpos(sc->rootwin, &x, &y); xu_ptr_setpos(sc->rootwin, x + mx, y + my); break; default: warnx("invalid flags passed to kbfunc_client_moveresize"); } } void kbfunc_client_search(struct client_ctx *cc, union arg *arg) { struct screen_ctx *sc = cc->sc; struct client_ctx *old_cc; struct menu *mi; struct menu_q menuq; old_cc = client_current(); TAILQ_INIT(&menuq); TAILQ_FOREACH(cc, &sc->clientq, entry) menuq_add(&menuq, cc, "%s", cc->name); if ((mi = menu_filter(sc, &menuq, "window", NULL, 0, search_match_client, search_print_client)) != NULL) { cc = (struct client_ctx *)mi->ctx; if (cc->flags & CLIENT_HIDDEN) client_unhide(cc); if (old_cc) client_ptrsave(old_cc); client_ptrwarp(cc); } menuq_clear(&menuq); } void kbfunc_menu_cmd(struct client_ctx *cc, union arg *arg) { struct screen_ctx *sc = cc->sc; struct cmd *cmd; struct menu *mi; struct menu_q menuq; TAILQ_INIT(&menuq); TAILQ_FOREACH(cmd, &Conf.cmdq, entry) menuq_add(&menuq, cmd, "%s", cmd->name); if ((mi = menu_filter(sc, &menuq, "application", NULL, 0, search_match_text, NULL)) != NULL) u_spawn(((struct cmd *)mi->ctx)->path); menuq_clear(&menuq); } void kbfunc_client_cycle(struct client_ctx *cc, union arg *arg) { struct screen_ctx *sc = cc->sc; /* XXX for X apps that ignore events */ XGrabKeyboard(X_Dpy, sc->rootwin, True, GrabModeAsync, GrabModeAsync, CurrentTime); client_cycle(sc, arg->i); } void kbfunc_client_hide(struct client_ctx *cc, union arg *arg) { client_hide(cc); } void kbfunc_cmdexec(struct client_ctx *cc, union arg *arg) { u_spawn(arg->c); } void kbfunc_term(struct client_ctx *cc, union arg *arg) { struct cmd *cmd; TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { if (strcmp(cmd->name, "term") == 0) u_spawn(cmd->path); } } void kbfunc_lock(struct client_ctx *cc, union arg *arg) { struct cmd *cmd; TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { if (strcmp(cmd->name, "lock") == 0) u_spawn(cmd->path); } } void kbfunc_exec(struct client_ctx *cc, union arg *arg) { #define NPATHS 256 struct screen_ctx *sc = cc->sc; char **ap, *paths[NPATHS], *path, *pathcpy; char tpath[PATH_MAX]; const char *label; DIR *dirp; struct dirent *dp; struct menu *mi; struct menu_q menuq; int l, i, cmd = arg->i; switch (cmd) { case CWM_EXEC_PROGRAM: label = "exec"; break; case CWM_EXEC_WM: label = "wm"; break; default: errx(1, "kbfunc_exec: invalid cmd %d", cmd); /*NOTREACHED*/ } 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) { /* skip everything but regular files and symlinks */ if (dp->d_type != DT_REG && dp->d_type != DT_LNK) continue; (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; 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, label, NULL, CWM_MENU_DUMMY | CWM_MENU_FILE, search_match_exec_path, NULL)) != NULL) { if (mi->text[0] == '\0') goto out; switch (cmd) { case CWM_EXEC_PROGRAM: u_spawn(mi->text); break; case CWM_EXEC_WM: u_exec(mi->text); warn("%s", mi->text); break; default: errx(1, "kb_func: egad, cmd changed value!"); break; } } out: if (mi != NULL && mi->dummy) free(mi); menuq_clear(&menuq); } void kbfunc_ssh(struct client_ctx *cc, union arg *arg) { struct screen_ctx *sc = cc->sc; struct cmd *cmd; struct menu *mi; struct menu_q menuq; FILE *fp; char *buf, *lbuf, *p; char hostbuf[HOST_NAME_MAX+1]; char path[PATH_MAX]; int l; size_t len; if ((fp = fopen(Conf.known_hosts, "r")) == NULL) { warn("kbfunc_ssh: %s", Conf.known_hosts); return; } TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { if (strcmp(cmd->name, "term") == 0) break; } TAILQ_INIT(&menuq); lbuf = NULL; while ((buf = fgetln(fp, &len))) { if (buf[len - 1] == '\n') buf[len - 1] = '\0'; else { /* EOF without EOL, copy and add the NUL */ lbuf = xmalloc(len + 1); (void)memcpy(lbuf, buf, len); lbuf[len] = '\0'; buf = lbuf; } /* skip hashed hosts */ if (strncmp(buf, HASH_MARKER, strlen(HASH_MARKER)) == 0) continue; for (p = buf; *p != ',' && *p != ' ' && p != buf + len; p++) { /* do nothing */ } /* ignore badness */ if (p - buf + 1 > sizeof(hostbuf)) continue; (void)strlcpy(hostbuf, buf, p - buf + 1); menuq_add(&menuq, NULL, hostbuf); } free(lbuf); (void)fclose(fp); if ((mi = menu_filter(sc, &menuq, "ssh", NULL, CWM_MENU_DUMMY, search_match_exec, NULL)) != 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_label(struct client_ctx *cc, union arg *arg) { struct menu *mi; struct menu_q menuq; TAILQ_INIT(&menuq); /* dummy is set, so this will always return */ mi = menu_filter(cc->sc, &menuq, "label", cc->label, CWM_MENU_DUMMY, search_match_text, NULL); if (!mi->abort) { free(cc->label); cc->label = xstrdup(mi->text); } free(mi); } void kbfunc_client_delete(struct client_ctx *cc, union arg *arg) { client_send_delete(cc); } void kbfunc_client_group(struct client_ctx *cc, union arg *arg) { group_hidetoggle(cc->sc, arg->i); } void kbfunc_client_grouponly(struct client_ctx *cc, union arg *arg) { group_only(cc->sc, arg->i); } void kbfunc_client_cyclegroup(struct client_ctx *cc, union arg *arg) { group_cycle(cc->sc, arg->i); } void kbfunc_client_nogroup(struct client_ctx *cc, union arg *arg) { group_alltoggle(cc->sc); } void kbfunc_client_grouptoggle(struct client_ctx *cc, union arg *arg) { /* XXX for stupid X apps like xpdf and gvim */ XGrabKeyboard(X_Dpy, cc->win, True, GrabModeAsync, GrabModeAsync, CurrentTime); group_toggle_membership_enter(cc); } void kbfunc_client_movetogroup(struct client_ctx *cc, union arg *arg) { group_movetogroup(cc, arg->i); } void kbfunc_client_toggle_sticky(struct client_ctx *cc, union arg *arg) { client_toggle_sticky(cc); } void kbfunc_client_toggle_fullscreen(struct client_ctx *cc, union arg *arg) { client_toggle_fullscreen(cc); } void kbfunc_client_toggle_maximize(struct client_ctx *cc, union arg *arg) { client_toggle_maximize(cc); } void kbfunc_client_toggle_vmaximize(struct client_ctx *cc, union arg *arg) { client_toggle_vmaximize(cc); } void kbfunc_client_toggle_hmaximize(struct client_ctx *cc, union arg *arg) { client_toggle_hmaximize(cc); } void kbfunc_client_toggle_freeze(struct client_ctx *cc, union arg *arg) { client_toggle_freeze(cc); } void kbfunc_cwm_status(struct client_ctx *cc, union arg *arg) { cwm_status = arg->i; } void kbfunc_tile(struct client_ctx *cc, union arg *arg) { switch (arg->i) { case CWM_TILE_HORIZ: client_htile(cc); break; case CWM_TILE_VERT: client_vtile(cc); break; } } cwm-5.6/menu.c000066400000000000000000000341331246075335000132730ustar00rootroot00000000000000/* * 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" 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; char searchstr[MENU_MAXENTRY + 1]; char dispstr[MENU_MAXENTRY*2 + 1]; char promptstr[MENU_MAXENTRY + 1]; int hasprompt; int list; int listing; int changed; int noresult; int prev; int entry; int height; int width; int num; int flags; int x; int y; 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(XEvent *, struct menu_ctx *, struct menu_q *); static struct menu *menu_handle_release(XEvent *, struct menu_ctx *, struct menu_q *); 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 evmask, focusrevert; int xsave, ysave, xcur, ycur; TAILQ_INIT(&resultq); (void)memset(&mc, 0, sizeof(mc)); xu_ptr_getpos(sc->rootwin, &mc.x, &mc.y); xsave = mc.x; ysave = mc.y; mc.sc = sc; mc.flags = flags; if (prompt != NULL) { evmask = MENUMASK | KEYMASK; /* accept keys as well */ (void)strlcpy(mc.promptstr, prompt, sizeof(mc.promptstr)); mc.hasprompt = 1; } else { evmask = MENUMASK; mc.list = 1; } if (initial != NULL) (void)strlcpy(mc.searchstr, initial, sizeof(mc.searchstr)); else mc.searchstr[0] = '\0'; mc.match = match; mc.print = print; mc.entry = mc.prev = -1; XSelectInput(X_Dpy, sc->menuwin, evmask); XMapRaised(X_Dpy, sc->menuwin); if (xu_ptr_grab(sc->menuwin, MENUGRABMASK, Conf.cursor[CF_QUESTION]) < 0) { XUnmapWindow(X_Dpy, sc->menuwin); return(NULL); } XGetInputFocus(X_Dpy, &focuswin, &focusrevert); XSetInputFocus(X_Dpy, sc->menuwin, RevertToPointerRoot, CurrentTime); /* make sure keybindings don't remove keys from the menu stream */ XGrabKeyboard(X_Dpy, sc->menuwin, True, GrabModeAsync, GrabModeAsync, CurrentTime); for (;;) { mc.changed = 0; XWindowEvent(X_Dpy, sc->menuwin, evmask, &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(&e, &mc, &resultq); break; case ButtonRelease: if ((mi = menu_handle_release(&e, &mc, &resultq)) != NULL) goto out; break; default: break; } } out: if ((mc.flags & CWM_MENU_DUMMY) == 0 && mi->dummy) { /* no mouse based match */ free(mi); mi = NULL; } XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime); /* restore if user didn't move */ xu_ptr_getpos(sc->rootwin, &xcur, &ycur); if (xcur == mc.x && ycur == mc.y) xu_ptr_setpos(sc->rootwin, xsave, ysave); xu_ptr_ungrab(); XMoveResizeWindow(X_Dpy, sc->menuwin, 0, 0, 1, 1); XUnmapWindow(X_Dpy, sc->menuwin); XUngrabKeyboard(X_Dpy, CurrentTime); return(mi); } static struct menu * menu_complete_path(struct menu_ctx *mc) { struct menu *mi, *mr; struct menu_q menuq; char *path = NULL; path = xcalloc(1, sizeof(mr->text)); mr = xcalloc(1, sizeof(*mr)); TAILQ_INIT(&menuq); if ((mi = menu_filter(mc->sc, &menuq, mc->searchstr, NULL, CWM_MENU_DUMMY, search_match_path_any, NULL)) != NULL) { mr->abort = mi->abort; mr->dummy = mi->dummy; strlcpy(path, mi->text, sizeof(mi->text)); } menuq_clear(&menuq); if (path[0] != '\0') snprintf(mr->text, sizeof(mr->text), "%s \"%s\"", mc->searchstr, path); else if (!mr->abort) strlcpy(mr->text, mc->searchstr, sizeof(mr->text)); free(path); 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)); } mc->noresult = 0; if (mc->changed && mc->searchstr[0] != '\0') { (*mc->match)(menuq, resultq, mc->searchstr); /* If menuq is empty, never show we've failed */ mc->noresult = TAILQ_EMPTY(resultq) && !TAILQ_EMPTY(menuq); } else if (mc->changed) TAILQ_INIT(resultq); if (!mc->list && mc->listing && !mc->changed) { 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 xine; int n, xsave, ysave; if (mc->list) { if (TAILQ_EMPTY(resultq) && mc->list) { /* 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; } mc->num = 0; mc->width = 0; mc->height = 0; if (mc->hasprompt) { (void)snprintf(mc->dispstr, sizeof(mc->dispstr), "%s%s%s%s", mc->promptstr, PROMPT_SCHAR, mc->searchstr, PROMPT_ECHAR); mc->width = xu_xft_width(sc->xftfont, mc->dispstr, strlen(mc->dispstr)); mc->height = sc->xftfont->height + 1; mc->num = 1; } TAILQ_FOREACH(mi, resultq, resultentry) { char *text; if (mc->print != NULL) { (*mc->print)(mi, mc->listing); text = mi->print; } else { mi->print[0] = '\0'; text = mi->text; } mc->width = MAX(mc->width, xu_xft_width(sc->xftfont, text, MIN(strlen(text), MENU_MAXENTRY))); mc->height += sc->xftfont->height + 1; mc->num++; } xine = screen_find_xinerama(sc, mc->x, mc->y, CWM_GAP); xine.w += xine.x - Conf.bwidth * 2; xine.h += xine.y - Conf.bwidth * 2; xsave = mc->x; ysave = mc->y; /* Never hide the top, or left side, of the menu. */ if (mc->x + mc->width >= xine.w) mc->x = xine.w - mc->width; if (mc->x < xine.x) { mc->x = xine.x; mc->width = MIN(mc->width, (xine.w - xine.x)); } if (mc->y + mc->height >= xine.h) mc->y = xine.h - mc->height; if (mc->y < xine.y) { mc->y = xine.y; mc->height = MIN(mc->height, (xine.h - xine.y)); } if (mc->x != xsave || mc->y != ysave) xu_ptr_setpos(sc->rootwin, mc->x, mc->y); XClearWindow(X_Dpy, sc->menuwin); XMoveResizeWindow(X_Dpy, sc->menuwin, mc->x, mc->y, mc->width, mc->height); if (mc->hasprompt) { xu_xft_draw(sc, mc->dispstr, CWM_COLOR_MENU_FONT, 0, sc->xftfont->ascent); n = 1; } else n = 0; TAILQ_FOREACH(mi, resultq, resultentry) { char *text = mi->print[0] != '\0' ? mi->print : mi->text; int y = n * (sc->xftfont->height + 1) + sc->xftfont->ascent + 1; /* Stop drawing when menu doesn't fit inside the screen. */ if (mc->y + y > xine.h) break; xu_xft_draw(sc, text, CWM_COLOR_MENU_FONT, 0, y); n++; } if (mc->hasprompt && 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; char *text; int color, i = 0; if (mc->hasprompt) 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; text = mi->print[0] != '\0' ? mi->print : mi->text; XftDrawRect(sc->xftdraw, &sc->xftcolor[color], 0, (sc->xftfont->height + 1) * entry, mc->width, (sc->xftfont->height + 1) + sc->xftfont->descent); color = active ? CWM_COLOR_MENU_FONT_SEL : CWM_COLOR_MENU_FONT; xu_xft_draw(sc, text, color, 0, (sc->xftfont->height + 1) * entry + sc->xftfont->ascent + 1); } static void menu_handle_move(XEvent *e, struct menu_ctx *mc, struct menu_q *resultq) { mc->prev = mc->entry; mc->entry = menu_calc_entry(mc, e->xbutton.x, e->xbutton.y); if (mc->prev == mc->entry) return; if (mc->prev != -1) menu_draw_entry(mc, resultq, mc->prev, 0); if (mc->entry != -1) { (void)xu_ptr_regrab(MENUGRABMASK, Conf.cursor[CF_NORMAL]); menu_draw_entry(mc, resultq, mc->entry, 1); } else (void)xu_ptr_regrab(MENUGRABMASK, Conf.cursor[CF_DEFAULT]); } static struct menu * menu_handle_release(XEvent *e, struct menu_ctx *mc, struct menu_q *resultq) { struct menu *mi; int entry, i = 0; entry = menu_calc_entry(mc, e->xbutton.x, e->xbutton.y); if (mc->hasprompt) i = 1; 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->width || y < 0 || y > (sc->xftfont->height + 1) * mc->num || entry < 0 || entry >= mc->num) entry = -1; if (mc->hasprompt && entry == 0) entry = -1; return(entry); } static int menu_keycode(XKeyEvent *ev, enum ctltype *ctl, char *chr) { KeySym ks; unsigned int state = ev->state; *ctl = CTL_NONE; chr[0] = '\0'; ks = XkbKeycodeToKeysym(X_Dpy, ev->keycode, 0, (state & ShiftMask) ? 1 : 0); /* Look for control characters. */ switch (ks) { case XK_BackSpace: *ctl = CTL_ERASEONE; break; 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 && (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; } } if (*ctl == CTL_NONE && (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); (void)vsnprintf(mi->text, sizeof(mi->text), fmt, ap); 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-5.6/mousefunc.c000066400000000000000000000146231246075335000143350ustar00rootroot00000000000000/* * calmwm - the calm window manager * * Copyright (c) 2004 Marius Aamodt Eriksen * Copyright (c) 2008 rivo nurges * * 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 mousefunc_sweep_calc(struct client_ctx *, int, int, int, int); static void mousefunc_sweep_draw(struct client_ctx *); static void mousefunc_sweep_calc(struct client_ctx *cc, int x, int y, int mx, int my) { cc->geom.w = abs(x - mx) - cc->bwidth; cc->geom.h = abs(y - my) - cc->bwidth; client_applysizehints(cc); cc->geom.x = x <= mx ? x : x - cc->geom.w; cc->geom.y = y <= my ? y : y - cc->geom.h; } static void mousefunc_sweep_draw(struct client_ctx *cc) { struct screen_ctx *sc = cc->sc; char s[14]; /* fits " nnnn x nnnn \0" */ (void)snprintf(s, sizeof(s), " %4d x %-4d ", (cc->geom.w - cc->hint.basew) / cc->hint.incw, (cc->geom.h - cc->hint.baseh) / cc->hint.inch); XReparentWindow(X_Dpy, sc->menuwin, cc->win, 0, 0); XMoveResizeWindow(X_Dpy, sc->menuwin, 0, 0, xu_xft_width(sc->xftfont, s, strlen(s)), sc->xftfont->height); XMapWindow(X_Dpy, sc->menuwin); XClearWindow(X_Dpy, sc->menuwin); xu_xft_draw(sc, s, CWM_COLOR_MENU_FONT, 0, sc->xftfont->ascent + 1); } void mousefunc_client_resize(struct client_ctx *cc, union arg *arg) { XEvent ev; Time ltime = 0; struct screen_ctx *sc = cc->sc; int x = cc->geom.x, y = cc->geom.y; if (cc->flags & (CLIENT_FREEZE|CLIENT_STICKY)) return; client_raise(cc); client_ptrsave(cc); if (xu_ptr_grab(cc->win, MOUSEMASK, Conf.cursor[CF_RESIZE]) < 0) return; xu_ptr_setpos(cc->win, cc->geom.w, cc->geom.h); mousefunc_sweep_draw(cc); for (;;) { 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; mousefunc_sweep_calc(cc, x, y, ev.xmotion.x_root, ev.xmotion.y_root); client_resize(cc, 1); mousefunc_sweep_draw(cc); break; case ButtonRelease: client_resize(cc, 1); XUnmapWindow(X_Dpy, sc->menuwin); XReparentWindow(X_Dpy, sc->menuwin, sc->rootwin, 0, 0); xu_ptr_ungrab(); /* Make sure the pointer stays within the window. */ if (cc->ptr.x > cc->geom.w) cc->ptr.x = cc->geom.w - cc->bwidth; if (cc->ptr.y > cc->geom.h) cc->ptr.y = cc->geom.h - cc->bwidth; client_ptrwarp(cc); return; } } /* NOTREACHED */ } void mousefunc_client_move(struct client_ctx *cc, union arg *arg) { XEvent ev; Time ltime = 0; struct screen_ctx *sc = cc->sc; struct geom xine; int px, py; client_raise(cc); if (cc->flags & (CLIENT_FREEZE|CLIENT_STICKY)) return; if (xu_ptr_grab(cc->win, MOUSEMASK, Conf.cursor[CF_MOVE]) < 0) return; xu_ptr_getpos(cc->win, &px, &py); for (;;) { 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 - px - cc->bwidth; cc->geom.y = ev.xmotion.y_root - py - cc->bwidth; xine = screen_find_xinerama(sc, cc->geom.x + cc->geom.w / 2, cc->geom.y + cc->geom.h / 2, CWM_GAP); cc->geom.x += client_snapcalc(cc->geom.x, cc->geom.x + cc->geom.w + (cc->bwidth * 2), xine.x, xine.x + xine.w, sc->snapdist); cc->geom.y += client_snapcalc(cc->geom.y, cc->geom.y + cc->geom.h + (cc->bwidth * 2), xine.y, xine.y + xine.h, sc->snapdist); client_move(cc); break; case ButtonRelease: client_move(cc); xu_ptr_ungrab(); return; } } /* NOTREACHED */ } void mousefunc_client_grouptoggle(struct client_ctx *cc, union arg *arg) { group_toggle_membership_enter(cc); } void mousefunc_menu_group(struct client_ctx *cc, union arg *arg) { struct screen_ctx *sc = cc->sc; struct group_ctx *gc; struct menu *mi; struct menu_q menuq; TAILQ_INIT(&menuq); TAILQ_FOREACH(gc, &sc->groupq, entry) { if (group_holds_only_sticky(gc)) continue; menuq_add(&menuq, gc, group_holds_only_hidden(gc) ? "%d: [%s]" : "%d: %s", gc->num, gc->name); } if (TAILQ_EMPTY(&menuq)) return; if ((mi = menu_filter(sc, &menuq, NULL, NULL, 0, NULL, NULL)) != NULL) { gc = (struct group_ctx *)mi->ctx; (group_holds_only_hidden(gc)) ? group_show(gc) : group_hide(gc); } menuq_clear(&menuq); } void mousefunc_menu_unhide(struct client_ctx *cc, union arg *arg) { struct screen_ctx *sc = cc->sc; struct client_ctx *old_cc; struct menu *mi; struct menu_q menuq; char *wname; old_cc = client_current(); TAILQ_INIT(&menuq); TAILQ_FOREACH(cc, &sc->clientq, entry) { if (cc->flags & CLIENT_HIDDEN) { wname = (cc->label) ? cc->label : cc->name; if (wname == NULL) continue; menuq_add(&menuq, cc, "(%d) %s", cc->group ? cc->group->num : 0, wname); } } if (TAILQ_EMPTY(&menuq)) return; if ((mi = menu_filter(sc, &menuq, NULL, NULL, 0, NULL, NULL)) != NULL) { cc = (struct client_ctx *)mi->ctx; client_unhide(cc); if (old_cc != NULL) client_ptrsave(old_cc); client_ptrwarp(cc); } menuq_clear(&menuq); } void mousefunc_menu_cmd(struct client_ctx *cc, union arg *arg) { struct screen_ctx *sc = cc->sc; struct cmd *cmd; struct menu *mi; struct menu_q menuq; TAILQ_INIT(&menuq); TAILQ_FOREACH(cmd, &Conf.cmdq, entry) menuq_add(&menuq, cmd, "%s", cmd->name); if (TAILQ_EMPTY(&menuq)) return; if ((mi = menu_filter(sc, &menuq, NULL, NULL, 0, NULL, NULL)) != NULL) u_spawn(((struct cmd *)mi->ctx)->path); menuq_clear(&menuq); } cwm-5.6/parse.y000066400000000000000000000271461246075335000134750ustar00rootroot00000000000000/* $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 #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 *); 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 FONTNAME STICKY GAP MOUSEBIND %token AUTOGROUP BIND COMMAND IGNORE %token YES NO BORDERWIDTH MOVEAMOUNT %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 %% 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 ; yesno : YES { $$ = 1; } | NO { $$ = 0; } ; main : FONTNAME STRING { free(conf->font); conf->font = $2; } | STICKY yesno { if ($2 == 0) conf->flags &= ~CONF_STICKY_GROUPS; else conf->flags |= CONF_STICKY_GROUPS; } | BORDERWIDTH NUMBER { if ($2 < 0 || $2 > UINT_MAX) { yyerror("invalid borderwidth: %lld", $2); YYERROR; } conf->bwidth = $2; } | MOVEAMOUNT NUMBER { if ($2 < 0 || $2 > INT_MAX) { yyerror("invalid movemount: %lld", $2); YYERROR; } conf->mamount = $2; } | SNAPDIST NUMBER { if ($2 < 0 || $2 > INT_MAX) { yyerror("invalid snapdist: %lld", $2); YYERROR; } conf->snapdist = $2; } | COMMAND STRING string { if (!conf_cmd_add(conf, $2, $3)) { yyerror("command name/path too long"); free($2); free($3); YYERROR; } free($2); free($3); } | AUTOGROUP NUMBER STRING { if ($2 < 0 || $2 > 9) { free($3); yyerror("invalid autogroup: %lld", $2); YYERROR; } conf_autogroup(conf, $2, $3); free($3); } | IGNORE STRING { conf_ignore(conf, $2); free($2); } | BIND STRING string { if (!conf_bind_kbd(conf, $2, $3)) { yyerror("invalid bind: %s %s", $2, $3); free($2); free($3); YYERROR; } free($2); free($3); } | 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: %lld %lld %lld %lld", $2, $3, $4, $5); YYERROR; } conf->gap.top = $2; conf->gap.bottom = $3; conf->gap.left = $4; conf->gap.right = $5; } | MOUSEBIND STRING string { if (!conf_bind_mouse(conf, $2, $3)) { yyerror("invalid mousebind: %s %s", $2, $3); free($2); free($3); YYERROR; } free($2); free($3); } ; 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", BIND}, { "borderwidth", BORDERWIDTH}, { "color", COLOR}, { "command", COMMAND}, { "font", FONTCOLOR}, { "fontname", FONTNAME}, { "gap", GAP}, { "groupborder", GROUPBORDER}, { "ignore", IGNORE}, { "inactiveborder", INACTIVEBORDER}, { "menubg", MENUBG}, { "menufg", MENUFG}, { "mousebind", MOUSEBIND}, { "moveamount", MOVEAMOUNT}, { "no", NO}, { "selfont", FONTSELCOLOR}, { "snapdist", SNAPDIST}, { "sticky", STICKY}, { "ungroupborder", UNGROUPBORDER}, { "urgencyborder", URGENCYBORDER}, { "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 = parsebuf[parseindex++]; if (c != '\0') return (c); parsebuf = NULL; } else parseindex++; } if (pushback_index) return (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 < MAXPUSHBACK-1) return (pushback_buffer[pushback_index++] = c); else return (EOF); } int findeol(void) { int c; parsebuf = NULL; /* skip to either EOF or the first real EOL */ while (1) { if (pushback_index) c = 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 || c == ' ' || c == '\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 ((unsigned)(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(*--p); c = *--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 ((unsigned)(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) { struct file *nfile; nfile = xcalloc(1, sizeof(struct file)); nfile->name = xstrdup(name); if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { warn("%s", nfile->name); free(nfile->name); free(nfile); return (NULL); } 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) { int errors = 0; conf = xconf; if ((file = pushfile(filename)) == NULL) { return (-1); } topfile = file; yyparse(); errors = file->errors; popfile(); return (errors ? -1 : 0); } cwm-5.6/queue.h000066400000000000000000000461231246075335000134620ustar00rootroot00000000000000/* $OpenBSD: "queue.h",v 1.36 2012/04/11 13:29:14 naddy 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) /* * 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-5.6/screen.c000066400000000000000000000112141246075335000136010ustar00rootroot00000000000000/* * 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 screen_init(int which) { struct screen_ctx *sc; Window *wins, w0, w1; XSetWindowAttributes rootattr; unsigned int nwins, i; 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->cycling = 0; sc->hideall = 0; conf_screen(sc); xu_ewmh_net_supported(sc); xu_ewmh_net_supported_wm_check(sc); screen_update_geometry(sc); for (i = 0; i < CALMWM_NGROUPS; i++) group_init(sc, i); xu_ewmh_net_desktop_names(sc); xu_ewmh_net_wm_desktop_viewport(sc); xu_ewmh_net_wm_number_of_desktops(sc); xu_ewmh_net_showing_desktop(sc); xu_ewmh_net_virtual_roots(sc); rootattr.cursor = Conf.cursor[CF_NORMAL]; rootattr.event_mask = SubstructureRedirectMask|SubstructureNotifyMask| PropertyChangeMask|EnterWindowMask|LeaveWindowMask| ColormapChangeMask|BUTTONMASK; XChangeWindowAttributes(X_Dpy, sc->rootwin, CWEventMask|CWCursor, &rootattr); /* Deal with existing clients. */ if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) { for (i = 0; i < nwins; i++) (void)client_init(wins[i], sc); XFree(wins); } screen_updatestackingorder(sc); if (HasRandr) XRRSelectInput(X_Dpy, sc->rootwin, RRScreenChangeNotifyMask); TAILQ_INSERT_TAIL(&Screenq, sc, entry); XSync(X_Dpy, False); } struct screen_ctx * screen_find(Window win) { struct screen_ctx *sc; TAILQ_FOREACH(sc, &Screenq, entry) { if (sc->rootwin == win) return(sc); } /* XXX FAIL HERE */ return(TAILQ_FIRST(&Screenq)); } 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); } } /* * Find which xinerama screen the coordinates (x,y) is on. */ struct geom screen_find_xinerama(struct screen_ctx *sc, int x, int y, int flags) { struct region_ctx *region; struct geom geom = sc->work; TAILQ_FOREACH(region, &sc->regionq, entry) { if (x >= region->area.x && x < region->area.x+region->area.w && y >= region->area.y && y < region->area.y+region->area.h) { geom = region->area; break; } } if (flags & CWM_GAP) { 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); } void screen_update_geometry(struct screen_ctx *sc) { XineramaScreenInfo *info = NULL; struct region_ctx *region; int info_num = 0, i; 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.x = sc->view.x + sc->gap.left; sc->work.y = sc->view.y + sc->gap.top; sc->work.w = sc->view.w - (sc->gap.left + sc->gap.right); sc->work.h = sc->view.h - (sc->gap.top + sc->gap.bottom); /* RandR event may have a CTRC added or removed. */ if (XineramaIsActive(X_Dpy)) info = XineramaQueryScreens(X_Dpy, &info_num); while ((region = TAILQ_FIRST(&sc->regionq)) != NULL) { TAILQ_REMOVE(&sc->regionq, region, entry); free(region); } for (i = 0; i < info_num; i++) { region = xmalloc(sizeof(*region)); region->num = i; region->area.x = info[i].x_org; region->area.y = info[i].y_org; region->area.w = info[i].width; region->area.h = info[i].height; TAILQ_INSERT_TAIL(&sc->regionq, region, entry); } if (info) XFree(info); xu_ewmh_net_desktop_geometry(sc); xu_ewmh_net_workarea(sc); } cwm-5.6/search.c000066400000000000000000000144221246075335000135730ustar00rootroot00000000000000/* * 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 "calmwm.h" #define PATH_ANY 0x0001 #define PATH_EXEC 0x0002 static void search_match_path(struct menu_q *, struct menu_q *, char *, int); static void search_match_path_exec(struct menu_q *, struct menu_q *, char *); static int strsubmatch(char *, char *, int); /* * Match: label, title, class. */ void search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search) { struct winname *wn; struct menu *mi, *tierp[4], *before = NULL; TAILQ_INIT(resultq); (void)memset(tierp, 0, sizeof(tierp)); /* * In order of rank: * * 1. Look through labels. * 2. Look at title history, from present to past. * 3. Look at window class name. */ TAILQ_FOREACH(mi, menuq, entry) { int tier = -1, t; struct client_ctx *cc = mi->ctx; /* First, try to match on labels. */ if (cc->label != NULL && strsubmatch(search, cc->label, 0)) { cc->matchname = cc->label; tier = 0; } /* Then, on window names. */ if (tier < 0) { TAILQ_FOREACH_REVERSE(wn, &cc->nameq, winname_q, entry) if (strsubmatch(search, wn->name, 0)) { cc->matchname = wn->name; tier = 2; break; } } /* Then if there is a match on the window class name. */ if (tier < 0 && strsubmatch(search, cc->ch.res_class, 0)) { cc->matchname = cc->ch.res_class; tier = 3; } if (tier < 0) continue; /* * De-rank a client one tier if it's the current * window. Furthermore, this is denoted by a "!" when * printing the window name in the search menu. */ if (cc == client_current() && tier < nitems(tierp) - 1) tier++; /* Clients that are hidden get ranked one up. */ if ((cc->flags & CLIENT_HIDDEN) && (tier > 0)) tier--; assert(tier < nitems(tierp)); /* * 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_print_client(struct menu *mi, int list) { struct client_ctx *cc; char flag = ' '; cc = mi->ctx; if (cc == client_current()) flag = '!'; else if (cc->flags & CLIENT_HIDDEN) flag = '&'; if (list) cc->matchname = cc->name; (void)snprintf(mi->print, sizeof(mi->print), "(%d) %c%s", cc->group ? cc->group->num : 0, flag, cc->matchname); if (!list && cc->matchname != cc->name && strlen(mi->print) < sizeof(mi->print) - 1) { const char *marker = ""; char buf[MENU_MAXENTRY + 1]; int diff; diff = sizeof(mi->print) - 1 - strlen(mi->print); /* One for the ':' */ diff -= 1; if (strlen(cc->name) > diff) { marker = ".."; diff -= 2; } else { diff = strlen(cc->name); } (void)strlcpy(buf, mi->print, sizeof(buf)); (void)snprintf(mi->print, sizeof(mi->print), "%s:%.*s%s", buf, diff, cc->name, marker); } } static void search_match_path(struct menu_q *menuq, struct menu_q *resultq, char *search, int flag) { char pattern[PATH_MAX]; glob_t g; int i; TAILQ_INIT(resultq); (void)strlcpy(pattern, search, sizeof(pattern)); (void)strlcat(pattern, "*", sizeof(pattern)); 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; menuq_add(resultq, NULL, "%s", g.gl_pathv[i]); } globfree(&g); } static void search_match_path_exec(struct menu_q *menuq, struct menu_q *resultq, char *search) { return(search_match_path(menuq, resultq, search, PATH_EXEC)); } void search_match_path_any(struct menu_q *menuq, struct menu_q *resultq, char *search) { return(search_match_path(menuq, 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 (strsubmatch(search, mi->text, 0)) TAILQ_INSERT_TAIL(resultq, mi, resultentry); } 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 (strsubmatch(search, mi->text, 1) == 0 && fnmatch(search, mi->text, 0) == FNM_NOMATCH) continue; TAILQ_FOREACH(mj, resultq, resultentry) { r = strcasecmp(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); } } void search_match_exec_path(struct menu_q *menuq, struct menu_q *resultq, char *search) { search_match_exec(menuq, resultq, search); if (TAILQ_EMPTY(resultq)) search_match_path_exec(menuq, resultq, search); } static int strsubmatch(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 = len - sublen; else flen = 0; for (n = 0; n <= flen; n++) if (strncasecmp(sub, str + n, sublen) == 0) return(1); return(0); } cwm-5.6/strlcat.c000066400000000000000000000034141246075335000140010ustar00rootroot00000000000000/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */ #include #include #include "calmwm.h" #ifndef HAVE_STRLCAT /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). * Returns strlen(src) + MIN(siz, strlen(initial dst)). * If retval >= siz, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ } #endif /* !HAVE_STRLCAT */ cwm-5.6/strlcpy.c000066400000000000000000000032351246075335000140260ustar00rootroot00000000000000/* $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */ #include #include #include "calmwm.h" #ifndef HAVE_STRLCPY /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0 && --n != 0) { do { if ((*d++ = *s++) == 0) break; } while (--n != 0); } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } #endif /* !HAVE_STRLCPY */ cwm-5.6/strtonum.c000066400000000000000000000035171246075335000142240ustar00rootroot00000000000000/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */ /* * Copyright (c) 2004 Ted Unangst and Todd Miller * All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* 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; char *ep; int error = 0; struct errval { const char *errstr; int err; } ev[4] = { { NULL, 0 }, { "invalid", EINVAL }, { "too small", ERANGE }, { "too large", ERANGE }, }; ev[0].err = errno; errno = 0; if (minval > maxval) error = INVALID; else { ll = strtoll(numstr, &ep, 10); if (numstr == ep || *ep != '\0') error = INVALID; else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) error = TOOSMALL; else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) error = TOOLARGE; } if (errstrp != NULL) *errstrp = ev[error].errstr; errno = ev[error].err; if (error) ll = 0; return (ll); } #endif /* HAVE_STRTONUM */ cwm-5.6/update.sh000077500000000000000000000001521246075335000137760ustar00rootroot00000000000000GIT_MERGE_AUTOEDIT=no git cvsimport -o master -v -k -m -d anoncvs@openbsd.cs.fau.de:/cvs xenocara/app/cwm cwm-5.6/util.c000066400000000000000000000034121246075335000133000ustar00rootroot00000000000000/* * 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" #define MAXARGLEN 20 void u_spawn(char *argstr) { switch (fork()) { case 0: u_exec(argstr); err(1, "%s", argstr); break; case -1: warn("fork"); default: break; } } void u_exec(char *argstr) { char *args[MAXARGLEN], **ap = args; char **end = &args[MAXARGLEN - 1], *tmp; 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); } cwm-5.6/xevents.c000066400000000000000000000235261246075335000140270ustar00rootroot00000000000000/* * 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 }; static void xev_handle_maprequest(XEvent *ee) { XMapRequestEvent *e = &ee->xmaprequest; struct client_ctx *cc = NULL, *old_cc; if ((old_cc = client_current())) client_ptrsave(old_cc); if ((cc = client_find(e->window)) == NULL) cc = client_init(e->window, NULL); if ((cc != NULL) && (!(cc->flags & CLIENT_IGNORE))) client_ptrwarp(cc); } static void xev_handle_unmapnotify(XEvent *ee) { XUnmapEvent *e = &ee->xunmap; struct client_ctx *cc; if ((cc = client_find(e->window)) != NULL) { if (e->send_event) { client_set_wm_state(cc, WithdrawnState); } else { if (!(cc->flags & CLIENT_HIDDEN)) client_delete(cc); } } } static void xev_handle_destroynotify(XEvent *ee) { XDestroyWindowEvent *e = &ee->xdestroywindow; struct client_ctx *cc; if ((cc = client_find(e->window)) != NULL) client_delete(cc); } static void xev_handle_configurerequest(XEvent *ee) { XConfigureRequestEvent *e = &ee->xconfigurerequest; struct client_ctx *cc; struct screen_ctx *sc; XWindowChanges wc; 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; if ((cc = client_find(e->window)) != NULL) { switch (e->atom) { case XA_WM_NORMAL_HINTS: client_getsizehints(cc); break; case XA_WM_NAME: client_setname(cc); break; case XA_WM_HINTS: client_wm_hints(cc); client_draw_border(cc); break; case XA_WM_TRANSIENT_FOR: client_transient(cc); break; default: /* do nothing */ break; } } else { TAILQ_FOREACH(sc, &Screenq, entry) { if (sc->rootwin == e->window) { if (e->atom == ewmh[_NET_DESKTOP_NAMES]) xu_ewmh_net_desktop_names(sc); } } } } static void xev_handle_enternotify(XEvent *ee) { XCrossingEvent *e = &ee->xcrossing; struct client_ctx *cc; Last_Event_Time = e->time; if ((cc = client_find(e->window)) != NULL) client_setactive(cc); } /* We can split this into two event handlers. */ static void xev_handle_buttonpress(XEvent *ee) { XButtonEvent *e = &ee->xbutton; struct client_ctx *cc, fakecc; struct binding *mb; e->state &= ~IGNOREMODMASK; TAILQ_FOREACH(mb, &Conf.mousebindingq, entry) { if (e->button == mb->press.button && e->state == mb->modmask) break; } if (mb == NULL) return; if (mb->flags & CWM_WIN) { if (((cc = client_find(e->window)) == NULL) && (cc = client_current()) == NULL) return; } else { if (e->window != e->root) return; cc = &fakecc; cc->sc = screen_find(e->window); } (*mb->callback)(cc, &mb->argument); } static void xev_handle_buttonrelease(XEvent *ee) { struct client_ctx *cc; if ((cc = client_current())) group_toggle_membership_leave(cc); } static void xev_handle_keypress(XEvent *ee) { XKeyEvent *e = &ee->xkey; struct client_ctx *cc = NULL, fakecc; struct binding *kb; KeySym keysym, skeysym; unsigned int modshift; keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0); skeysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 1); e->state &= ~IGNOREMODMASK; TAILQ_FOREACH(kb, &Conf.keybindingq, 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; if (kb->flags & CWM_WIN) { if (((cc = client_find(e->window)) == NULL) && (cc = client_current()) == NULL) return; } else { cc = &fakecc; cc->sc = screen_find(e->window); } (*kb->callback)(cc, &kb->argument); } /* * This is only used for the modifier suppression detection. */ static void xev_handle_keyrelease(XEvent *ee) { XKeyEvent *e = &ee->xkey; struct screen_ctx *sc; KeySym keysym; unsigned int i; sc = screen_find(e->root); keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0); for (i = 0; i < nitems(modkeys); i++) { if (keysym == modkeys[i]) { client_cycle_leave(sc); break; } } } static void xev_handle_clientmessage(XEvent *ee) { XClientMessageEvent *e = &ee->xclient; struct client_ctx *cc, *old_cc; struct screen_ctx *sc; sc = screen_find(e->window); if ((cc = client_find(e->window)) == NULL && e->window != sc->rootwin) return; if (e->message_type == cwmh[WM_CHANGE_STATE] && e->format == 32 && e->data.l[0] == IconicState) client_hide(cc); if (e->message_type == ewmh[_NET_CLOSE_WINDOW]) client_send_delete(cc); if (e->message_type == ewmh[_NET_ACTIVE_WINDOW] && e->format == 32) { if ((old_cc = client_current())) client_ptrsave(old_cc); client_ptrwarp(cc); } if (e->message_type == ewmh[_NET_WM_DESKTOP] && e->format == 32) { /* * The EWMH spec states that if the cardinal returned is * 0xFFFFFFFF (-1) then the window should appear on all * desktops, which in our case is assigned to group 0. */ if (e->data.l[0] == (unsigned long)-1) group_movetogroup(cc, 0); else group_movetogroup(cc, e->data.l[0]); } if (e->message_type == ewmh[_NET_WM_STATE] && e->format == 32) xu_ewmh_handle_net_wm_state_msg(cc, e->data.l[0], e->data.l[1], e->data.l[2]); if (e->message_type == ewmh[_NET_CURRENT_DESKTOP] && e->format == 32) group_only(sc, e->data.l[0]); } static void xev_handle_randr(XEvent *ee) { XRRScreenChangeNotifyEvent *rev = (XRRScreenChangeNotifyEvent *)ee; struct screen_ctx *sc; int i; i = XRRRootToScreen(X_Dpy, rev->root); TAILQ_FOREACH(sc, &Screenq, entry) { if (sc->which == i) { XRRUpdateConfiguration(ee); screen_update_geometry(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; 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; if ((cc = client_find(e->window)) != NULL && e->count == 0) client_draw_border(cc); } void xev_process(void) { XEvent e; XNextEvent(X_Dpy, &e); if (e.type - Randr_ev == RRScreenChangeNotify) xev_handle_randr(&e); else if (e.type < LASTEvent && xev_handlers[e.type] != NULL) (*xev_handlers[e.type])(&e); } cwm-5.6/xmalloc.c000066400000000000000000000034331246075335000137650ustar00rootroot00000000000000/* * 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); } 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 = vasprintf(ret, fmt, ap); va_end(ap); if (i < 0 || *ret == NULL) err(1, "asprintf"); return(i); } cwm-5.6/xutil.c000066400000000000000000000304511246075335000134730ustar00rootroot00000000000000/* * 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 unsigned int ign_mods[] = { 0, LockMask, Mod2Mask, Mod2Mask | LockMask }; void xu_btn_grab(Window win, int mask, unsigned int btn) { unsigned int i; for (i = 0; i < nitems(ign_mods); i++) XGrabButton(X_Dpy, btn, (mask | ign_mods[i]), win, False, BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); } void xu_btn_ungrab(Window win) { XUngrabButton(X_Dpy, AnyButton, AnyModifier, win); } void xu_key_grab(Window win, unsigned int mask, KeySym keysym) { KeyCode code; unsigned int i; code = XKeysymToKeycode(X_Dpy, keysym); if ((XkbKeycodeToKeysym(X_Dpy, code, 0, 0) != keysym) && (XkbKeycodeToKeysym(X_Dpy, code, 0, 1) == keysym)) mask |= ShiftMask; for (i = 0; i < nitems(ign_mods); i++) XGrabKey(X_Dpy, code, (mask | ign_mods[i]), win, True, GrabModeAsync, GrabModeAsync); } void xu_key_ungrab(Window win) { XUngrabKey(X_Dpy, AnyKey, AnyModifier, win); } int xu_ptr_grab(Window win, unsigned int mask, Cursor curs) { return(XGrabPointer(X_Dpy, win, False, mask, GrabModeAsync, GrabModeAsync, None, curs, CurrentTime) == GrabSuccess ? 0 : -1); } int xu_ptr_regrab(unsigned int mask, Cursor curs) { return(XChangeActivePointerGrab(X_Dpy, mask, curs, CurrentTime) == GrabSuccess ? 0 : -1); } void xu_ptr_ungrab(void) { XUngrabPointer(X_Dpy, CurrentTime); } void xu_ptr_getpos(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_setpos(Window win, int x, int y) { XWarpPointer(X_Dpy, None, win, 0, 0, 0, 0, x, y); } int xu_getprop(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_getstrprop(Window win, Atom atm, char **text) { XTextProperty prop; char **list; int nitems = 0; *text = NULL; XGetTextProperty(X_Dpy, win, &prop, atm); if (!prop.nitems) 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); } /* 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 *)WMNAME, strlen(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_workarea(struct screen_ctx *sc) { long workareas[CALMWM_NGROUPS][4]; int i; for (i = 0; i < CALMWM_NGROUPS; i++) { workareas[i][0] = sc->work.x; workareas[i][1] = sc->work.y; workareas[i][2] = sc->work.w; workareas[i][3] = sc->work.h; } XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_WORKAREA], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)workareas, CALMWM_NGROUPS * 4); } 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 = xcalloc(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_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_wm_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_wm_number_of_desktops(struct screen_ctx *sc) { long ndesks = CALMWM_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_getprop(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 = xcalloc(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); } /* Application Window Properties */ void xu_ewmh_net_wm_desktop(struct client_ctx *cc) { long num = 0xffffffff; if (cc->group) num = cc->group->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_getprop(cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 64L, (unsigned char **)&p)) <= 0) return(NULL); state = xcalloc(*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; static struct handlers { int atom; int property; 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 }, }; 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].property)) handlers[i].toggle(cc); break; case _NET_WM_STATE_REMOVE: if (cc->flags & handlers[i].property) 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_HORZ]) client_toggle_hmaximize(cc); if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_VERT]) client_toggle_vmaximize(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); } 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 = xcalloc((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_HORZ] && oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_VERT] && oatoms[i] != ewmh[_NET_WM_STATE_HIDDEN] && oatoms[i] != ewmh[_NET_WM_STATE_FULLSCREEN] && oatoms[i] != ewmh[_NET_WM_STATE_DEMANDS_ATTENTION]) 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_HMAXIMIZED) atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_HORZ]; if (cc->flags & CLIENT_VMAXIMIZED) atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_VERT]; } if (cc->flags & CLIENT_URGENCY) atoms[j++] = ewmh[_NET_WM_STATE_DEMANDS_ATTENTION]; 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); } 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; } int xu_xft_width(XftFont *xftfont, const char *text, int len) { XGlyphInfo extents; XftTextExtentsUtf8(X_Dpy, xftfont, (const FcChar8*)text, len, &extents); return(extents.xOff); } void xu_xft_draw(struct screen_ctx *sc, const char *text, int color, int x, int y) { XftDrawStringUtf8(sc->xftdraw, &sc->xftcolor[color], sc->xftfont, x, y, (const FcChar8*)text, strlen(text)); }