28 Commits
master ... v5.1

Author SHA1 Message Date
601779351c Version 5.1 2012-05-02 14:26:57 +02:00
5f79bace21 Add release target to Makefile 2012-05-02 14:26:36 +02:00
de5efc6a54 Define compatibility macros only if they are needed 2012-05-02 14:26:14 +02:00
1603d18560 cvsimport 2011-12-29 20:48:38 +00:00
96ebac8b04 fix pkg-config packages 2011-12-12 16:17:39 +01:00
cbe46b651f cvsimport 2011-11-06 02:03:47 +00:00
901a75d3c6 cvsimport 2011-10-17 18:18:38 +00:00
58077c157b cvsimport 2011-10-12 15:43:50 +00:00
ce34d0066e Add README 2011-10-05 17:48:47 +02:00
7c7a5bdcdc rewrite Makefile 2011-10-05 17:48:33 +02:00
54c1b90725 cvsimport 2011-09-19 07:23:03 +00:00
3c67ec8ccc cvsimport 2011-09-13 09:17:30 +00:00
b1d81b7eb6 cvsimport 2011-09-08 12:35:33 +00:00
513c35dca9 cvsimport 2011-08-04 20:53:37 +02:00
f067809af4 cvsimport 2011-05-15 17:58:47 +00:00
a90a0b5705 cvsimport 2011-05-07 17:15:37 +00:00
2bac9a6be9 cvsimport 2011-05-03 19:28:18 +02:00
9eaece0541 cvsimport 2011-02-18 11:50:01 +00:00
5bc15cdb58 cvsimport 2010-12-24 23:42:39 +01:00
10622cf40d Add protoypes, fixes segfaults on x86 2010-09-26 14:51:23 +02:00
a64855f9cc Improve CFLAGS 2010-09-26 14:51:02 +02:00
e6d9effa8d Use yacc instead of byacc 2010-09-26 01:46:05 +02:00
b11c12e99b Merge branch 'origin' into linux 2010-09-26 01:43:12 +02:00
c9a14a8f40 cvsimport 2010-07-30 12:28:19 +00:00
e0c9657773 cvsimport 2010-05-22 22:32:08 +00:00
oga
f769df540d cvsimport 2010-04-12 16:17:46 +00:00
d90ab51111 Add missing functions 2010-02-21 19:18:12 +01:00
50aff37f50 Import linux.patch
Patch largely based on cwmbuild.sh from http://tamentis.com/hacks/cwm/
written by Bertrand Janin, updated by Christian Neukirchen.
2010-02-21 19:15:27 +01:00
25 changed files with 4469 additions and 4929 deletions

14
LICENSE Normal file
View File

@ -0,0 +1,14 @@
Copyright (c) 2004,2005 Marius Aamodt Eriksen <marius@monkey.org>
Copyright (c) 2004 Andy Adamson <dros@monkey.org>
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.

View File

@ -1,24 +1,47 @@
# $OpenBSD$
.include <bsd.xconf.mk>
# cwm makefile for BSD make and GNU make
# uses pkg-config, DESTDIR and PREFIX
PROG= cwm
PREFIX= /usr/local
SRCS= calmwm.c screen.c xmalloc.c client.c menu.c \
search.c util.c xutil.c conf.c xevents.c group.c \
kbfunc.c parse.y
kbfunc.c mousefunc.c font.c parse.y
CPPFLAGS+= -I${X11BASE}/include -I${X11BASE}/include/freetype2 -I${.CURDIR}
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 font.o strlcpy.o strlcat.o y.tab.o \
strtonum.o fgetln.o
CFLAGS+= -Wall
YFLAGS=
LDADD+= -L${X11BASE}/lib -lXft -lXrender -lX11 -lxcb -lXau -lXdmcp \
-lfontconfig -lexpat -lfreetype -lz -lXrandr -lXext
CPPFLAGS+= `pkg-config --cflags fontconfig x11 xft xinerama xrandr`
MANDIR= ${X11BASE}/man/man
MAN= cwm.1 cwmrc.5
CFLAGS= -Wall -O2 -g
obj: _xenocara_obj
LDFLAGS+= `pkg-config --libs fontconfig x11 xft xinerama xrandr`
.include <bsd.prog.mk>
.include <bsd.xorg.mk>
MANPREFIX= ${PREFIX}/share/man
all: ${PROG}
clean:
rm -rf ${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

24
README Normal file
View File

@ -0,0 +1,24 @@
This 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.
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
ChangeLog:
2012-05-02: First public release 5.1 of portable cwm.
--Christian Neukirchen <chneukirchen@gmail.com>
[0]: http://www.openbsd.org/cgi-bin/cvsweb/xenocara/app/cwm/

256
calmwm.c
View File

@ -18,70 +18,58 @@
* $OpenBSD$
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/wait.h>
#include <err.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <locale.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
Display *X_Dpy;
Time Last_Event_Time = CurrentTime;
Atom cwmh[CWMH_NITEMS];
Atom ewmh[EWMH_NITEMS];
struct screen_q Screenq = TAILQ_HEAD_INITIALIZER(Screenq);
struct conf Conf;
volatile sig_atomic_t cwm_status;
Display *X_Dpy;
__dead void usage(void);
static void sighdlr(int);
Cursor Cursor_default;
Cursor Cursor_move;
Cursor Cursor_normal;
Cursor Cursor_question;
Cursor Cursor_resize;
struct screen_ctx_q Screenq = TAILQ_HEAD_INITIALIZER(Screenq);
struct client_ctx_q Clientq = TAILQ_HEAD_INITIALIZER(Clientq);
int HasXinerama, HasRandr, Randr_ev;
struct conf Conf;
static void sigchld_cb(int);
static void dpy_init(const char *);
static int x_errorhandler(Display *, XErrorEvent *);
static int x_init(const char *);
static void x_teardown(void);
static int x_wmerrorhandler(Display *, XErrorEvent *);
static void x_setup(void);
static void x_setupscreen(struct screen_ctx *, u_int);
static void x_teardown(void);
int
main(int argc, char **argv)
{
const char *conf_file = NULL;
char *display_name = NULL;
char *fallback;
int ch, xfd, nflag = 0;
struct pollfd pfd[1];
int ch;
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
warnx("no locale support");
mbtowc(NULL, NULL, MB_CUR_MAX);
conf_init(&Conf);
fallback = u_argv(argv);
Conf.wm_argv = u_argv(argv);
while ((ch = getopt(argc, argv, "c:d:nv")) != -1) {
while ((ch = getopt(argc, argv, "c:d:")) != -1) {
switch (ch) {
case 'c':
free(Conf.conf_file);
Conf.conf_file = xstrdup(optarg);
conf_file = optarg;
break;
case 'd':
display_name = optarg;
break;
case 'n':
nflag = 1;
break;
case 'v':
Conf.debug++;
break;
default:
usage();
}
@ -89,106 +77,154 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
if (signal(SIGCHLD, sighdlr) == SIG_ERR ||
signal(SIGHUP, sighdlr) == SIG_ERR ||
signal(SIGINT, sighdlr) == SIG_ERR ||
signal(SIGTERM, sighdlr) == SIG_ERR)
if (signal(SIGCHLD, sigchld_cb) == SIG_ERR)
err(1, "signal");
if (parse_config(Conf.conf_file, &Conf) == -1) {
warnx("error parsing config file");
if (nflag)
return 1;
}
if (nflag)
return 0;
dpy_init(display_name);
xfd = x_init(display_name);
cwm_status = CWM_RUNNING;
bzero(&Conf, sizeof(Conf));
conf_setup(&Conf, conf_file);
xu_getatoms();
x_setup();
if (pledge("stdio rpath proc exec", NULL) == -1)
err(1, "pledge");
xev_loop();
memset(&pfd, 0, sizeof(pfd));
pfd[0].fd = xfd;
pfd[0].events = POLLIN;
while (cwm_status == CWM_RUNNING) {
xev_process();
if (poll(pfd, 1, INFTIM) == -1) {
if (errno != EINTR)
warn("poll");
}
}
x_teardown();
if (cwm_status == CWM_EXEC_WM) {
u_exec(Conf.wm_argv);
warnx("'%s' failed to start, starting fallback", Conf.wm_argv);
u_exec(fallback);
}
return 0;
return (0);
}
static int
x_init(const char *dpyname)
static void
dpy_init(const char *dpyname)
{
int i;
XSetErrorHandler(x_errorhandler);
if ((X_Dpy = XOpenDisplay(dpyname)) == NULL)
errx(1, "unable to open display \"%s\"", XDisplayName(dpyname));
errx(1, "unable to open display \"%s\"",
XDisplayName(dpyname));
XSetErrorHandler(x_wmerrorhandler);
XSelectInput(X_Dpy, DefaultRootWindow(X_Dpy), SubstructureRedirectMask);
XSync(X_Dpy, False);
XSetErrorHandler(x_errorhandler);
Conf.xrandr = XRRQueryExtension(X_Dpy, &Conf.xrandr_event_base, &i);
HasRandr = XRRQueryExtension(X_Dpy, &Randr_ev, &i);
}
xu_atom_init();
conf_cursor(&Conf);
static void
x_setup(void)
{
struct screen_ctx *sc;
struct keybinding *kb;
int i;
for (i = 0; i < ScreenCount(X_Dpy); i++)
screen_init(i);
Cursor_default = XCreateFontCursor(X_Dpy, XC_X_cursor);
Cursor_move = XCreateFontCursor(X_Dpy, XC_fleur);
Cursor_normal = XCreateFontCursor(X_Dpy, XC_left_ptr);
Cursor_question = XCreateFontCursor(X_Dpy, XC_question_arrow);
Cursor_resize = XCreateFontCursor(X_Dpy, XC_bottom_right_corner);
return ConnectionNumber(X_Dpy);
for (i = 0; i < ScreenCount(X_Dpy); i++) {
sc = xcalloc(1, sizeof(*sc));
x_setupscreen(sc, i);
TAILQ_INSERT_TAIL(&Screenq, sc, entry);
}
/*
* XXX key grabs weren't done before, since Screenq was empty,
* do them here for now (this needs changing).
*/
TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
conf_grab(&Conf, kb);
}
static void
x_teardown(void)
{
struct screen_ctx *sc;
unsigned int i;
conf_clear(&Conf);
TAILQ_FOREACH(sc, &Screenq, entry)
XFreeGC(X_Dpy, sc->gc);
TAILQ_FOREACH(sc, &Screenq, entry) {
for (i = 0; i < CWM_COLOR_NITEMS; i++)
XftColorFree(X_Dpy, DefaultVisual(X_Dpy, sc->which),
DefaultColormap(X_Dpy, sc->which),
&sc->xftcolor[i]);
XftFontClose(X_Dpy, sc->xftfont);
XUngrabKey(X_Dpy, AnyKey, AnyModifier, sc->rootwin);
}
XUngrabPointer(X_Dpy, CurrentTime);
XUngrabKeyboard(X_Dpy, CurrentTime);
for (i = 0; i < CF_NITEMS; i++)
XFreeCursor(X_Dpy, Conf.cursor[i]);
XSync(X_Dpy, False);
XSetInputFocus(X_Dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
XCloseDisplay(X_Dpy);
}
static void
x_setupscreen(struct screen_ctx *sc, u_int which)
{
Window *wins, w0, w1;
XWindowAttributes winattr;
XSetWindowAttributes rootattr;
int fake;
u_int nwins, i;
sc->which = which;
sc->rootwin = RootWindow(X_Dpy, sc->which);
conf_gap(&Conf, sc);
screen_update_geometry(sc, DisplayWidth(X_Dpy, sc->which),
DisplayHeight(X_Dpy, sc->which));
conf_color(&Conf, sc);
group_init(sc);
conf_font(&Conf, sc);
TAILQ_INIT(&sc->mruq);
/* Initialize menu window. */
menu_init(sc);
xu_setwmname(sc);
rootattr.cursor = Cursor_normal;
rootattr.event_mask = CHILDMASK|PropertyChangeMask|EnterWindowMask|
LeaveWindowMask|ColormapChangeMask|BUTTONMASK;
XChangeWindowAttributes(X_Dpy, sc->rootwin,
CWEventMask|CWCursor, &rootattr);
/* Deal with existing clients. */
XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins);
for (i = 0; i < nwins; i++) {
XGetWindowAttributes(X_Dpy, wins[i], &winattr);
if (winattr.override_redirect ||
winattr.map_state != IsViewable)
continue;
(void)client_new(wins[i], sc, winattr.map_state != IsUnmapped);
}
XFree(wins);
screen_updatestackingorder(sc);
if (XineramaQueryExtension(X_Dpy, &fake, &fake) == 1 &&
((HasXinerama = XineramaIsActive(X_Dpy)) == 1))
HasXinerama = 1;
if (HasRandr)
XRRSelectInput(X_Dpy, sc->rootwin, RRScreenChangeNotifyMask);
/*
* initial setup of xinerama screens, if we're using RandR then we'll
* redo this whenever the screen changes since a CTRC may have been
* added or removed
*/
screen_init_xinerama(sc);
XSync(X_Dpy, False);
}
static int
x_wmerrorhandler(Display *dpy, XErrorEvent *e)
{
errx(1, "root window unavailable - perhaps another wm is running?");
return 0;
}
return (0);
}
static int
x_errorhandler(Display *dpy, XErrorEvent *e)
{
#ifdef DEBUG
#if DEBUG
char msg[80], number[80], req[80];
XGetErrorText(X_Dpy, e->error_code, msg, sizeof(msg));
@ -196,32 +232,22 @@ x_errorhandler(Display *dpy, XErrorEvent *e)
XGetErrorDatabaseText(X_Dpy, "XRequest", number,
"<unknown>", req, sizeof(req));
warnx("%s(0x%x): %s", req, (unsigned int)e->resourceid, msg);
warnx("%s(0x%x): %s", req, (u_int)e->resourceid, msg);
#endif
return 0;
return (0);
}
static void
sighdlr(int sig)
sigchld_cb(int which)
{
pid_t pid;
int save_errno = errno, status;
int save_errno = errno;
int status;
switch (sig) {
case SIGCHLD:
/* Collect dead children. */
while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0 ||
(pid < 0 && errno == EINTR))
;
break;
case SIGHUP:
cwm_status = CWM_EXEC_WM;
break;
case SIGINT:
case SIGTERM:
cwm_status = CWM_QUIT;
break;
}
/* Collect dead children. */
while ((pid = waitpid(-1, &status, WNOHANG)) > 0 ||
(pid < 0 && errno == EINTR))
;
errno = save_errno;
}
@ -231,7 +257,7 @@ usage(void)
{
extern char *__progname;
(void)fprintf(stderr, "usage: %s [-nv] [-c file] [-d display]\n",
(void)fprintf(stderr, "usage: %s [-c file] [-d display]\n",
__progname);
exit(1);
}

766
calmwm.h
View File

@ -21,21 +21,28 @@
#ifndef _CALMWM_H_
#define _CALMWM_H_
#include <X11/XKBlib.h>
/* ugly stuff */
#ifndef TAILQ_END
#define TAILQ_END(head) NULL
#endif
#ifndef __dead
#define __dead
#endif
char *fgetln(FILE *, size_t *);
long long strtonum(const char *, long long, long long, const char **);
size_t strlcpy(char *, const char *, size_t);
size_t strlcat(char *, const char *, size_t);
#include <X11/Xatom.h>
#include <X11/Xft/Xft.h>
#include <X11/Xlib.h>
#include <X11/Xproto.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
#include <X11/extensions/Xinerama.h>
#include <X11/extensions/Xrandr.h>
#include <X11/keysym.h>
#define LOG_DEBUG0(...) log_debug(0, __func__, __VA_ARGS__)
#define LOG_DEBUG1(...) log_debug(1, __func__, __VA_ARGS__)
#define LOG_DEBUG2(...) log_debug(2, __func__, __VA_ARGS__)
#define LOG_DEBUG3(...) log_debug(3, __func__, __VA_ARGS__)
#undef MIN
#undef MAX
#define MIN(x, y) ((x) < (y) ? (x) : (y))
@ -45,60 +52,61 @@
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
#define BUTTONMASK (ButtonPressMask | ButtonReleaseMask)
#define MOUSEMASK (BUTTONMASK | PointerMotionMask)
#define IGNOREMODMASK (LockMask | Mod2Mask | 0x2000)
#define CONFFILE ".cwmrc"
#define WMNAME "CWM"
/* direction/amount */
#define CWM_UP 0x0001
#define CWM_DOWN 0x0002
#define CWM_LEFT 0x0004
#define CWM_RIGHT 0x0008
#define CWM_BIGAMOUNT 0x0010
#define CWM_UP_BIG (CWM_UP | CWM_BIGAMOUNT)
#define CWM_DOWN_BIG (CWM_DOWN | CWM_BIGAMOUNT)
#define CWM_LEFT_BIG (CWM_LEFT | CWM_BIGAMOUNT)
#define CWM_RIGHT_BIG (CWM_RIGHT | CWM_BIGAMOUNT)
#define CWM_UP_RIGHT (CWM_UP | CWM_RIGHT)
#define CWM_UP_LEFT (CWM_UP | CWM_LEFT)
#define CWM_DOWN_RIGHT (CWM_DOWN | CWM_RIGHT)
#define CWM_DOWN_LEFT (CWM_DOWN | CWM_LEFT)
#define CHILDMASK (SubstructureRedirectMask|SubstructureNotifyMask)
#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
#define KEYMASK (KeyPressMask|ExposureMask)
#define MENUMASK (BUTTONMASK|ButtonMotionMask|ExposureMask| \
PointerMotionMask)
#define MENUGRABMASK (BUTTONMASK|ButtonMotionMask|StructureNotifyMask|\
PointerMotionMask)
#define SEARCHMASK (KeyPressMask|ExposureMask)
#define CWM_CYCLE_FORWARD 0x0001
#define CWM_CYCLE_REVERSE 0x0002
#define CWM_CYCLE_INGROUP 0x0004
/* 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
enum cwm_status {
CWM_QUIT,
CWM_RUNNING,
CWM_EXEC_WM
/* exec */
#define CWM_EXEC_PROGRAM 0x0001
#define CWM_EXEC_WM 0x0002
/* cycle */
#define CWM_CYCLE 0x0001
#define CWM_RCYCLE 0x0002
#define CWM_INGROUP 0x0004
#define KBTOGROUP(X) ((X) - 1)
union arg {
char *c;
int i;
};
enum cursor_font {
CF_NORMAL,
CF_MOVE,
CF_RESIZE,
CF_QUESTION,
CF_NITEMS
};
enum color {
enum cwmcolor {
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
CWM_COLOR_FG_MENU,
CWM_COLOR_BG_MENU,
CWM_COLOR_FONT,
CWM_COLOR_MAX
};
struct geom {
int x;
int y;
int w;
int h;
struct color {
char *name;
unsigned long pixel;
};
struct gap {
int top;
int bottom;
@ -110,20 +118,24 @@ struct winname {
TAILQ_ENTRY(winname) entry;
char *name;
};
TAILQ_HEAD(name_q, winname);
TAILQ_HEAD(ignore_q, winname);
TAILQ_HEAD(winname_q, winname);
struct client_ctx {
TAILQ_ENTRY(client_ctx) entry;
TAILQ_ENTRY(client_ctx) entry;
TAILQ_ENTRY(client_ctx) group_entry;
TAILQ_ENTRY(client_ctx) mru_entry;
struct screen_ctx *sc;
struct group_ctx *gc;
Window win;
Colormap colormap;
int bwidth; /* border width */
int obwidth; /* original border width */
struct geom geom, savegeom, fullgeom;
XSizeHints *size;
Colormap cmap;
u_int bwidth; /* border width */
struct {
int x; /* x position */
int y; /* y position */
int width; /* width */
int height;/* height */
} geom, savegeom;
struct {
long flags; /* defined hints */
int basew; /* desired width */
int baseh; /* desired height */
int minw; /* minimum width */
@ -139,138 +151,125 @@ struct client_ctx {
int x; /* x position */
int y; /* y position */
} ptr;
struct {
int h; /* height */
int w; /* width */
} dim;
#define CLIENT_PROTO_DELETE 0x0001
#define CLIENT_PROTO_TAKEFOCUS 0x0002
int xproto;
#define CLIENT_HIDDEN 0x0001
#define CLIENT_IGNORE 0x0002
#define CLIENT_VMAXIMIZED 0x0004
#define CLIENT_HMAXIMIZED 0x0008
#define CLIENT_FREEZE 0x0010
#define CLIENT_GROUP 0x0020
#define CLIENT_UNGROUP 0x0040
#define CLIENT_INPUT 0x0080
#define CLIENT_WM_DELETE_WINDOW 0x0100
#define CLIENT_WM_TAKE_FOCUS 0x0200
#define CLIENT_URGENCY 0x0400
#define CLIENT_FULLSCREEN 0x0800
#define CLIENT_STICKY 0x1000
#define CLIENT_ACTIVE 0x2000
#define CLIENT_SKIP_PAGER 0x4000
#define CLIENT_SKIP_TASKBAR 0x8000
#define CLIENT_SKIP_CYCLE (CLIENT_HIDDEN | CLIENT_IGNORE | \
CLIENT_SKIP_TASKBAR | CLIENT_SKIP_PAGER)
#define CLIENT_HIGHLIGHT (CLIENT_GROUP | CLIENT_UNGROUP)
#define CLIENT_MAXFLAGS (CLIENT_VMAXIMIZED | CLIENT_HMAXIMIZED)
#define CLIENT_MAXIMIZED (CLIENT_VMAXIMIZED | CLIENT_HMAXIMIZED)
int flags;
int state;
int active;
int stackingorder;
struct name_q nameq;
#define CLIENT_HIGHLIGHT_GROUP 0x0001
#define CLIENT_HIGHLIGHT_UNGROUP 0x0002
int highlight;
struct winname_q nameq;
#define CLIENT_MAXNAMEQLEN 5
int nameqlen;
char *name;
char *label;
char *res_class; /* class hint */
char *res_name; /* class hint */
int initial_state; /* wm hint */
char *matchname;
struct group_ctx *group;
char *app_class;
char *app_name;
};
TAILQ_HEAD(client_q, client_ctx);
TAILQ_HEAD(client_ctx_q, client_ctx);
TAILQ_HEAD(cycle_entry_q, client_ctx);
struct winmatch {
TAILQ_ENTRY(winmatch) entry;
#define WIN_MAXTITLELEN 256
char title[WIN_MAXTITLELEN];
};
TAILQ_HEAD(winmatch_q, winmatch);
struct group_ctx {
TAILQ_ENTRY(group_ctx) entry;
struct screen_ctx *sc;
char *name;
int num;
struct client_ctx_q clients;
int shortcut;
int hidden;
int nhidden;
int highstack;
};
TAILQ_HEAD(group_q, group_ctx);
TAILQ_HEAD(group_ctx_q, group_ctx);
struct autogroup {
TAILQ_ENTRY(autogroup) entry;
char *class;
char *name;
int num;
struct autogroupwin {
TAILQ_ENTRY(autogroupwin) entry;
char *class;
char *name;
int num;
};
TAILQ_HEAD(autogroup_q, autogroup);
struct region_ctx {
TAILQ_ENTRY(region_ctx) entry;
int num;
struct geom view; /* viewable area */
struct geom work; /* workable area, gap-applied */
};
TAILQ_HEAD(region_q, region_ctx);
TAILQ_HEAD(autogroupwin_q, autogroupwin);
struct screen_ctx {
TAILQ_ENTRY(screen_ctx) entry;
int which;
u_int which;
Window rootwin;
int cycling;
int hideall;
int snapdist;
struct geom view; /* viewable area */
struct geom work; /* workable area, gap-applied */
Window menuwin;
struct color color[CWM_COLOR_MAX];
GC gc;
int altpersist;
int xmax;
int ymax;
struct gap gap;
struct client_q clientq;
struct region_q regionq;
struct group_q groupq;
struct cycle_entry_q mruq;
XftColor xftcolor;
XftDraw *xftdraw;
XftFont *font;
int xinerama_no;
XineramaScreenInfo *xinerama;
#define CALMWM_NGROUPS 9
struct group_ctx groups[CALMWM_NGROUPS];
struct group_ctx_q groupq;
int group_hideall;
int group_nonames;
struct group_ctx *group_active;
struct group_ctx *group_last;
Colormap colormap;
Visual *visual;
struct {
Window win;
XftDraw *xftdraw;
} prop;
XftColor xftcolor[CWM_COLOR_NITEMS];
XftFont *xftfont;
char **group_names;
};
TAILQ_HEAD(screen_q, screen_ctx);
TAILQ_HEAD(screen_ctx_q, screen_ctx);
struct cargs {
char *cmd;
int flag;
enum {
CWM_XEV_KEY,
CWM_XEV_BTN
} xev;
struct keybinding {
TAILQ_ENTRY(keybinding) entry;
void (*callback)(struct client_ctx *, union arg *);
union arg argument;
int modmask;
int keysym;
int keycode;
#define KBFLAG_NEEDCLIENT 0x0001
int flags;
};
enum context {
CWM_CONTEXT_NONE = 0,
CWM_CONTEXT_CC,
CWM_CONTEXT_SC
};
struct bind_ctx {
TAILQ_ENTRY(bind_ctx) entry;
void (*callback)(void *, struct cargs *);
struct cargs *cargs;
enum context context;
unsigned int modmask;
union {
KeySym keysym;
unsigned int button;
} press;
};
TAILQ_HEAD(keybind_q, bind_ctx);
TAILQ_HEAD(mousebind_q, bind_ctx);
TAILQ_HEAD(keybinding_q, keybinding);
struct cmd_ctx {
TAILQ_ENTRY(cmd_ctx) entry;
char *name;
char *path;
struct mousebinding {
TAILQ_ENTRY(mousebinding) entry;
void (*callback)(struct client_ctx *, void *);
int modmask;
int button;
#define MOUSEBIND_CTX_ROOT 0x0001
#define MOUSEBIND_CTX_WIN 0x0002
int context;
};
TAILQ_HEAD(cmd_q, cmd_ctx);
TAILQ_HEAD(wm_q, cmd_ctx);
TAILQ_HEAD(mousebinding_q, mousebinding);
#define CWM_MENU_DUMMY 0x0001
#define CWM_MENU_FILE 0x0002
#define CWM_MENU_LIST 0x0004
#define CWM_MENU_WINDOW_ALL 0x0008
#define CWM_MENU_WINDOW_HIDDEN 0x0010
struct cmd {
TAILQ_ENTRY(cmd) entry;
int flags;
char image[MAXPATHLEN];
#define CMD_MAXLABELLEN 256
char label[CMD_MAXLABELLEN];
};
TAILQ_HEAD(cmd_q, cmd);
struct menu {
TAILQ_ENTRY(menu) entry;
TAILQ_ENTRY(menu) resultentry;
#define MENU_MAXENTRY 200
#define MENU_MAXENTRY 50
char text[MENU_MAXENTRY + 1];
char print[MENU_MAXENTRY + 1];
void *ctx;
@ -280,330 +279,253 @@ struct menu {
TAILQ_HEAD(menu_q, menu);
struct conf {
struct keybind_q keybindq;
struct mousebind_q mousebindq;
struct autogroup_q autogroupq;
struct ignore_q ignoreq;
struct keybinding_q keybindingq;
struct autogroupwin_q autogroupq;
struct winmatch_q ignoreq;
char conf_path[MAXPATHLEN];
struct cmd_q cmdq;
struct wm_q wmq;
int ngroups;
int stickygroups;
int nameqlen;
struct mousebinding_q mousebindingq;
#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;
int htile;
int vtile;
struct gap gap;
char *color[CWM_COLOR_NITEMS];
struct color color[CWM_COLOR_MAX];
char termpath[MAXPATHLEN];
char lockpath[MAXPATHLEN];
#define CONF_FONT "sans-serif:pixelsize=14:bold"
char *font;
char *wmname;
Cursor cursor[CF_NITEMS];
int xrandr;
int xrandr_event_base;
char *conf_file;
char *known_hosts;
char *wm_argv;
int debug;
};
/* MWM hints */
struct mwm_hints {
#define MWM_HINTS_ELEMENTS 5L
#define MWM_HINTS_FUNCTIONS (1L << 0)
#define MWM_HINTS_DECORATIONS (1L << 1)
#define MWM_HINTS_INPUT_MODE (1L << 2)
#define MWM_HINTS_STATUS (1L << 3)
unsigned long flags;
#define MWM_FUNC_ALL (1L << 0)
#define MWM_FUNC_RESIZE (1L << 1)
#define MWM_FUNC_MOVE (1L << 2)
#define MWM_FUNC_MINIMIZE (1L << 3)
#define MWM_FUNC_MAXIMIZE (1L << 4)
#define MWM_FUNC_CLOSE (1L << 5)
unsigned long functions;
#define MWM_DECOR_ALL (1L << 0)
#define MWM_DECOR_BORDER (1L << 1)
#define MWM_DECOR_RESIZEH (1L << 2)
#define MWM_DECOR_TITLE (1L << 3)
#define MWM_DECOR_MENU (1L << 4)
#define MWM_DECOR_MINIMIZE (1L << 5)
#define MWM_DECOR_MAXIMIZE (1L << 6)
unsigned long decorations;
#define MWM_INPUT_MODELESS 0
#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1
#define MWM_INPUT_SYSTEM_MODAL 2
#define MWM_INPUT_FULL_APPLICATION_MODAL 3
long inputMode;
#define MWM_TEAROFF_WINDOW (1L << 0)
unsigned long status;
u_long flags;
u_long functions;
u_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)
enum cwmh {
WM_STATE,
WM_DELETE_WINDOW,
WM_TAKE_FOCUS,
WM_PROTOCOLS,
_MOTIF_WM_HINTS,
UTF8_STRING,
WM_CHANGE_STATE,
CWMH_NITEMS
};
enum ewmh {
_NET_SUPPORTED,
_NET_SUPPORTING_WM_CHECK,
_NET_ACTIVE_WINDOW,
_NET_CLIENT_LIST,
_NET_CLIENT_LIST_STACKING,
_NET_NUMBER_OF_DESKTOPS,
_NET_CURRENT_DESKTOP,
_NET_DESKTOP_VIEWPORT,
_NET_DESKTOP_GEOMETRY,
_NET_VIRTUAL_ROOTS,
_NET_SHOWING_DESKTOP,
_NET_DESKTOP_NAMES,
_NET_WORKAREA,
_NET_WM_NAME,
_NET_WM_DESKTOP,
_NET_CLOSE_WINDOW,
_NET_WM_STATE,
#define _NET_WM_STATES_NITEMS 9
_NET_WM_STATE_STICKY,
_NET_WM_STATE_MAXIMIZED_VERT,
_NET_WM_STATE_MAXIMIZED_HORZ,
_NET_WM_STATE_HIDDEN,
_NET_WM_STATE_FULLSCREEN,
_NET_WM_STATE_DEMANDS_ATTENTION,
_NET_WM_STATE_SKIP_PAGER,
_NET_WM_STATE_SKIP_TASKBAR,
_CWM_WM_STATE_FREEZE,
EWMH_NITEMS
};
enum net_wm_state {
_NET_WM_STATE_REMOVE,
_NET_WM_STATE_ADD,
_NET_WM_STATE_TOGGLE
};
__dead void usage(void);
extern Display *X_Dpy;
extern Time Last_Event_Time;
extern Atom cwmh[CWMH_NITEMS];
extern Atom ewmh[EWMH_NITEMS];
extern struct screen_q Screenq;
extern struct conf Conf;
void client_apply_sizehints(struct client_ctx *);
void client_close(struct client_ctx *);
void client_config(struct client_ctx *);
struct client_ctx *client_current(struct screen_ctx *);
void client_applysizehints(struct client_ctx *);
struct client_ctx *client_current(void);
void client_cycle(struct screen_ctx *, int);
void client_delete(struct client_ctx *);
void client_draw_border(struct client_ctx *);
struct client_ctx *client_find(Window);
void client_get_sizehints(struct client_ctx *);
void client_freeze(struct client_ctx *);
void client_getsizehints(struct client_ctx *);
void client_hide(struct client_ctx *);
void client_htile(struct client_ctx *);
int client_inbound(struct client_ctx *, int, int);
struct client_ctx *client_init(Window, struct screen_ctx *);
void client_horizmaximize(struct client_ctx *);
void client_leave(struct client_ctx *);
void client_lower(struct client_ctx *);
void client_map(struct client_ctx *);
void client_maximize(struct client_ctx *);
void client_move(struct client_ctx *);
void client_mtf(struct client_ctx *);
struct client_ctx *client_next(struct client_ctx *);
struct client_ctx *client_prev(struct client_ctx *);
void client_ptr_inbound(struct client_ctx *, int);
void client_ptr_save(struct client_ctx *);
void client_ptr_warp(struct client_ctx *);
struct client_ctx *client_new(Window, struct screen_ctx *, int);
void client_ptrsave(struct client_ctx *);
void client_ptrwarp(struct client_ctx *);
void client_raise(struct client_ctx *);
void client_remove(struct client_ctx *);
void client_resize(struct client_ctx *, int);
void client_set_active(struct client_ctx *);
void client_set_name(struct client_ctx *);
void client_show(struct client_ctx *);
void client_resize(struct client_ctx *);
void client_send_delete(struct client_ctx *);
void client_setactive(struct client_ctx *, int);
void client_setname(struct client_ctx *);
int client_snapcalc(int, int, int, int, int);
void client_toggle_hidden(struct client_ctx *);
void client_toggle_hmaximize(struct client_ctx *);
void client_toggle_fullscreen(struct client_ctx *);
void client_toggle_freeze(struct client_ctx *);
void client_toggle_maximize(struct client_ctx *);
void client_toggle_skip_pager(struct client_ctx *);
void client_toggle_skip_taskbar(struct client_ctx *);
void client_toggle_sticky(struct client_ctx *);
void client_toggle_vmaximize(struct client_ctx *);
void client_transient(struct client_ctx *);
void client_urgency(struct client_ctx *);
void client_vtile(struct client_ctx *);
void client_wm_hints(struct client_ctx *);
void client_unhide(struct client_ctx *);
void client_vertmaximize(struct client_ctx *);
void client_warp(struct client_ctx *);
void group_assign(struct group_ctx *, struct client_ctx *);
int group_autogroup(struct client_ctx *);
void group_alltoggle(struct screen_ctx *);
void group_autogroup(struct client_ctx *);
void group_client_delete(struct client_ctx *);
void group_cycle(struct screen_ctx *, int);
void group_hide(struct group_ctx *);
int group_holds_only_hidden(struct group_ctx *);
int group_holds_only_sticky(struct group_ctx *);
void group_init(struct screen_ctx *, int, const char *);
void group_hidetoggle(struct screen_ctx *, int);
void group_init(struct screen_ctx *);
void group_make_autogroup(struct conf *, char *, int);
void group_menu(XButtonEvent *);
void group_movetogroup(struct client_ctx *, int);
void group_only(struct screen_ctx *, int);
void group_close(struct screen_ctx *, int);
int group_restore(struct client_ctx *);
void group_show(struct group_ctx *);
void group_toggle(struct screen_ctx *, int);
void group_toggle_all(struct screen_ctx *);
void group_toggle_membership(struct client_ctx *);
void group_sticky(struct client_ctx *);
void group_sticky_toggle_enter(struct client_ctx *);
void group_sticky_toggle_exit(struct client_ctx *);
void group_update_names(struct screen_ctx *);
void search_match_client(struct menu_q *, struct menu_q *,
char *);
void search_match_cmd(struct menu_q *, struct menu_q *,
char *);
void search_match_exec(struct menu_q *, struct menu_q *,
char *);
void search_match_group(struct menu_q *, struct menu_q *,
char *);
void search_match_path(struct menu_q *, struct menu_q *,
char *);
void search_match_text(struct menu_q *, struct menu_q *,
char *);
void search_match_wm(struct menu_q *, struct menu_q *,
char *);
void search_print_client(struct menu *, int);
void search_print_cmd(struct menu *, int);
void search_print_group(struct menu *, int);
void search_print_text(struct menu *, int);
void search_print_wm(struct menu *, int);
struct region_ctx *region_find(struct screen_ctx *, int, int);
void screen_assert_clients_within(struct screen_ctx *);
struct geom screen_area(struct screen_ctx *, int, int, int);
struct screen_ctx *screen_find(Window);
void screen_init(int);
void screen_prop_win_create(struct screen_ctx *, Window);
void screen_prop_win_destroy(struct screen_ctx *);
void screen_prop_win_draw(struct screen_ctx *,
const char *, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
void screen_update_geometry(struct screen_ctx *);
XineramaScreenInfo *screen_find_xinerama(struct screen_ctx *, int, int);
struct screen_ctx *screen_fromroot(Window);
void screen_init_xinerama(struct screen_ctx *);
void screen_update_geometry(struct screen_ctx *, int, int);
void screen_updatestackingorder(struct screen_ctx *);
void kbfunc_cwm_status(void *, struct cargs *);
void kbfunc_ptrmove(void *, struct cargs *);
void kbfunc_client_snap(void *, struct cargs *);
void kbfunc_client_move(void *, struct cargs *);
void kbfunc_client_resize(void *, struct cargs *);
void kbfunc_client_close(void *, struct cargs *);
void kbfunc_client_lower(void *, struct cargs *);
void kbfunc_client_raise(void *, struct cargs *);
void kbfunc_client_hide(void *, struct cargs *);
void kbfunc_client_toggle_freeze(void *, struct cargs *);
void kbfunc_client_toggle_sticky(void *, struct cargs *);
void kbfunc_client_toggle_fullscreen(void *,
struct cargs *);
void kbfunc_client_toggle_maximize(void *, struct cargs *);
void kbfunc_client_toggle_hmaximize(void *, struct cargs *);
void kbfunc_client_toggle_vmaximize(void *, struct cargs *);
void kbfunc_client_htile(void *, struct cargs *);
void kbfunc_client_vtile(void *, struct cargs *);
void kbfunc_client_cycle(void *, struct cargs *);
void kbfunc_client_toggle_group(void *, struct cargs *);
void kbfunc_client_movetogroup(void *, struct cargs *);
void kbfunc_group_toggle(void *, struct cargs *);
void kbfunc_group_only(void *, struct cargs *);
void kbfunc_group_last(void *, struct cargs *);
void kbfunc_group_close(void *, struct cargs *);
void kbfunc_group_cycle(void *, struct cargs *);
void kbfunc_group_toggle_all(void *, struct cargs *);
void kbfunc_menu_client(void *, struct cargs *);
void kbfunc_menu_cmd(void *, struct cargs *);
void kbfunc_menu_group(void *, struct cargs *);
void kbfunc_menu_wm(void *, struct cargs *);
void kbfunc_menu_exec(void *, struct cargs *);
void kbfunc_menu_ssh(void *, struct cargs *);
void kbfunc_client_menu_label(void *, struct cargs *);
void kbfunc_exec_cmd(void *, struct cargs *);
void kbfunc_exec_lock(void *, struct cargs *);
void kbfunc_exec_term(void *, struct cargs *);
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_freeze(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_hmaximize(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_maximize(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_vmaximize(struct client_ctx *,
union arg *);
void kbfunc_cmdexec(struct client_ctx *, union arg *);
void kbfunc_exec(struct client_ctx *, union arg *);
void kbfunc_lock(struct client_ctx *, union arg *);
void kbfunc_menu_search(struct client_ctx *, union arg *);
void kbfunc_moveresize(struct client_ctx *, union arg *);
void kbfunc_quit_wm(struct client_ctx *, union arg *);
void kbfunc_reload(struct client_ctx *, union arg *);
void kbfunc_ssh(struct client_ctx *, union arg *);
void kbfunc_term(struct client_ctx *, union arg *);
void mousefunc_menu_cmd(struct client_ctx *, void *);
void mousefunc_menu_group(struct client_ctx *, void *);
void mousefunc_menu_unhide(struct client_ctx *, void *);
void mousefunc_window_grouptoggle(struct client_ctx *,
void *);
void mousefunc_window_hide(struct client_ctx *, void *);
void mousefunc_window_lower(struct client_ctx *, void *);
void mousefunc_window_move(struct client_ctx *, void *);
void mousefunc_window_raise(struct client_ctx *, void *);
void mousefunc_window_resize(struct client_ctx *, void *);
struct menu *menu_filter(struct screen_ctx *, struct menu_q *,
const char *, const char *, int,
char *, char *, int,
void (*)(struct menu_q *, struct menu_q *, char *),
void (*)(struct menu *, int));
void menuq_add(struct menu_q *, void *, const char *, ...)
__attribute__((__format__ (printf, 3, 4)));
void menuq_clear(struct menu_q *);
void menu_init(struct screen_ctx *);
int parse_config(const char *, struct conf *);
void conf_autogroup(struct conf *, int, const char *,
const char *);
int conf_bind_key(struct conf *, const char *,
const char *);
int conf_bind_mouse(struct conf *, const char *,
const char *);
void conf_bindname(struct conf *, char *, char *);
void conf_clear(struct conf *);
void conf_client(struct client_ctx *);
void conf_cmd_add(struct conf *, const char *,
const char *);
void conf_wm_add(struct conf *, const char *,
const char *);
void conf_cursor(struct conf *);
void conf_grab_kbd(Window);
void conf_grab_mouse(Window);
void conf_cmd_add(struct conf *, char *, char *, int);
void conf_color(struct conf *, struct screen_ctx *);
void conf_font(struct conf *, struct screen_ctx *);
void conf_gap(struct conf *, struct screen_ctx *);
void conf_grab(struct conf *, struct keybinding *);
void conf_grab_mouse(struct client_ctx *);
void conf_init(struct conf *);
void conf_ignore(struct conf *, const char *);
void conf_screen(struct screen_ctx *);
void conf_group(struct screen_ctx *);
void conf_mousebind(struct conf *, char *, char *);
void conf_reload(struct conf *);
void conf_setup(struct conf *, const char *);
void conf_ungrab(struct conf *, struct keybinding *);
void xev_process(void);
int font_ascent(struct screen_ctx *);
int font_descent(struct screen_ctx *);
void font_draw(struct screen_ctx *, const char *, int,
Drawable, int, int);
u_int font_height(struct screen_ctx *);
void font_init(struct screen_ctx *, const char *);
int font_width(struct screen_ctx *, const char *, int);
XftFont *font_make(struct screen_ctx *, const char *);
int xu_get_prop(Window, Atom, Atom, long, unsigned char **);
int xu_get_strprop(Window, Atom, char **);
void xu_ptr_get(Window, int *, int *);
void xu_ptr_set(Window, int, int);
void xu_get_wm_state(Window, long *);
void xu_set_wm_state(Window, long);
void xu_send_clientmsg(Window, Atom, Time);
void xu_xorcolor(XftColor, XftColor, XftColor *);
void xev_loop(void);
void xu_atom_init(void);
void xu_ewmh_net_supported(struct screen_ctx *);
void xu_ewmh_net_supported_wm_check(struct screen_ctx *);
void xu_ewmh_net_desktop_geometry(struct screen_ctx *);
void xu_ewmh_net_desktop_viewport(struct screen_ctx *);
void xu_ewmh_net_workarea(struct screen_ctx *);
void xu_ewmh_net_client_list(struct screen_ctx *);
void xu_ewmh_net_client_list_stacking(struct screen_ctx *);
void xu_ewmh_net_active_window(struct screen_ctx *, Window);
void xu_ewmh_net_number_of_desktops(struct screen_ctx *);
void xu_ewmh_net_showing_desktop(struct screen_ctx *);
void xu_ewmh_net_virtual_roots(struct screen_ctx *);
void xu_ewmh_net_current_desktop(struct screen_ctx *);
void xu_ewmh_net_desktop_names(struct screen_ctx *);
int xu_ewmh_get_net_wm_desktop(struct client_ctx *, long *);
void xu_ewmh_set_net_wm_desktop(struct client_ctx *);
Atom *xu_ewmh_get_net_wm_state(struct client_ctx *, int *);
void xu_ewmh_handle_net_wm_state_msg(struct client_ctx *,
int, Atom, Atom);
void xu_ewmh_set_net_wm_state(struct client_ctx *);
void xu_ewmh_restore_net_wm_state(struct client_ctx *);
void xu_btn_grab(Window, int, u_int);
void xu_btn_ungrab(Window, int, u_int);
void xu_configure(struct client_ctx *);
void xu_freecolor(struct screen_ctx *, unsigned long);
void xu_getatoms(void);
unsigned long xu_getcolor(struct screen_ctx *, char *);
int xu_getprop(Window, Atom, Atom, long, u_char **);
int xu_getstate(struct client_ctx *, int *);
int xu_getstrprop(Window, Atom, char **);
void xu_key_grab(Window, int, int);
void xu_key_ungrab(Window, int, int);
void xu_ptr_getpos(Window, int *, int *);
int xu_ptr_grab(Window, int, Cursor);
int xu_ptr_regrab(int, Cursor);
void xu_ptr_setpos(Window, int, int);
void xu_ptr_ungrab(void);
void xu_sendmsg(Window, Atom, long);
void xu_setstate(struct client_ctx *, int);
void xu_setwmname(struct screen_ctx *);
char *u_argv(char * const *);
void u_exec(char *);
void u_spawn(char *);
void log_debug(int, const char *, const char *, ...)
__attribute__((__format__ (printf, 3, 4)))
__attribute__((__nonnull__ (3)));
void *xcalloc(size_t, size_t);
void xfree(void *);
void *xmalloc(size_t);
void *xreallocarray(void *, size_t, size_t);
char *xstrdup(const char *);
int xasprintf(char **, const char *, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
int xvasprintf(char **, const char *, va_list)
__attribute__((__nonnull__ (2)));
/* Externs */
extern Display *X_Dpy;
extern Cursor Cursor_default;
extern Cursor Cursor_move;
extern Cursor Cursor_normal;
extern Cursor Cursor_question;
extern Cursor Cursor_resize;
extern struct screen_ctx_q Screenq;
extern struct client_ctx_q Clientq;
extern struct conf Conf;
extern int HasXinerama, HasRandr, Randr_ev;
#define WM_STATE cwm_atoms[0]
#define WM_DELETE_WINDOW cwm_atoms[1]
#define WM_TAKE_FOCUS cwm_atoms[2]
#define WM_PROTOCOLS cwm_atoms[3]
#define _MOTIF_WM_HINTS cwm_atoms[4]
#define UTF8_STRING cwm_atoms[5]
/*
* please make all hints below this point netwm hints, starting with
* _NET_SUPPORTED. If you change other hints make sure you update
* CWM_NETWM_START
*/
#define _NET_SUPPORTED cwm_atoms[6]
#define _NET_SUPPORTING_WM_CHECK cwm_atoms[7]
#define _NET_WM_NAME cwm_atoms[8]
#define _NET_ACTIVE_WINDOW cwm_atoms[9]
#define _NET_CLIENT_LIST cwm_atoms[10]
#define _NET_NUMBER_OF_DESKTOPS cwm_atoms[11]
#define _NET_CURRENT_DESKTOP cwm_atoms[12]
#define _NET_DESKTOP_VIEWPORT cwm_atoms[13]
#define _NET_DESKTOP_GEOMETRY cwm_atoms[14]
#define _NET_VIRTUAL_ROOTS cwm_atoms[15]
#define _NET_SHOWING_DESKTOP cwm_atoms[16]
#define _NET_DESKTOP_NAMES cwm_atoms[17]
#define _NET_WM_DESKTOP cwm_atoms[18]
#define _NET_WORKAREA cwm_atoms[19]
#define CWM_NO_ATOMS 20
#define CWM_NETWM_START 6
extern Atom cwm_atoms[CWM_NO_ATOMS];
#endif /* _CALMWM_H_ */

1274
client.c

File diff suppressed because it is too large Load Diff

1154
conf.c

File diff suppressed because it is too large Load Diff

242
cwm.1
View File

@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate$
.Dd $Mdocdate: September 25 2010 $
.Dt CWM 1
.Os
.Sh NAME
@ -23,54 +23,24 @@
.Sh SYNOPSIS
.\" For a program: program [-abc] file ...
.Nm cwm
.Op Fl nv
.Op Fl c Ar file
.Op Fl d Ar display
.Sh DESCRIPTION
.Nm
is a window manager for X11 which contains many features that
concentrate on the efficiency and transparency of window management,
while maintaining the simplest and most pleasant aesthetic.
concentrate on the efficiency and transparency of window management.
.Nm
also aims to maintain the simplest and most pleasant aesthetic.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl c Ar file
Specify an alternative configuration file.
By default,
.Nm
loads
.Pa ~/.cwmrc ,
if present.
Any error messages from lines in the configuration file will be sent to
.Em stderr ;
however,
.Nm
will continue to process the rest of the configuration file.
.It Fl d Ar display
Specify the display to use.
.It Fl n
Configtest mode.
Only check the configuration file for validity.
.It Fl v
Verbose mode.
Multiple
.Fl v
options increase the verbosity.
.El
.Pp
.Nm
actions are initiated either via key or mouse bindings.
The following notations are used throughout this page:
The following notation is used throughout this page:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It Ic C
Control key.
Control.
.It Ic M
Meta key.
Meta.
.It Ic S
Shift key.
.It Ic 4
Mod4 (windows) key.
Shift.
.It Ic M1
Left mouse button.
.It Ic M2
@ -79,7 +49,11 @@ Middle mouse button.
Right mouse button.
.El
.Pp
The default key bindings are:
.Nm
is very simple in its use.
Most of the actions are initiated via key bindings.
The current 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
@ -92,9 +66,9 @@ Hide current window.
Lower current window.
.It Ic M-Up
Raise current window.
.It Ic M-slash
.It Ic M-/
Search for windows.
.It Ic C-slash
.It Ic C-/
Search for applications.
.It Ic CM-n
Label current window.
@ -103,11 +77,11 @@ Cycle through currently visible windows.
.It Ic MS-Tab
Reverse cycle through currently visible windows.
.It Ic CM-x
Close current window.
Delete current window.
.It Ic CM-[n]
Toggle visibility of group n, where n is 1-9.
.It Ic CM-a
Toggle visibility of all groups.
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
@ -116,31 +90,17 @@ Cycle through active groups.
Reverse cycle through active groups.
.It Ic CMS-f
Toggle freezing geometry of current window.
.It Ic CM-s
Toggle stickiness of current window.
.It Ic CM-f
Toggle full-screen mode of current window.
.It Ic CM-m
Toggle maximization of current window.
.It Ic CM-equal
Toggle full-screen size of current window.
.It Ic CM-=
Toggle vertical maximization of current window.
.It Ic CMS-equal
.It Ic CMS-=
Toggle horizontal maximization of current window.
.It Ic M-[hjkl]
Move window by a small amount.
.It Ic MS-[hjkl]
Move window by a large amount; see
.Xr cwmrc 5 .
.It Ic CM-[hjkl]
Resize window by a small amount.
.It Ic CMS-[hjkl]
Resize window by a large amount; see
.Xr cwmrc 5 .
.It Ic M-question
.It Ic M-?
Spawn
.Dq exec program
dialog.
.It Ic M-period
.It Ic M-.
Spawn
.Dq ssh to
dialog.
@ -152,16 +112,19 @@ will be executed via the configured terminal emulator.
.It Ic CM-w
Spawn
.Dq exec WindowManager
menu, allowing a switch to another window manager.
dialog; allows you to switch from
.Nm
to another window manager without restarting the X server.
.It Ic CMS-r
Restart.
Reload configuration.
.It Ic CMS-q
Quit.
Quit
.Nm .
.El
.Pp
The default mouse bindings are:
The mouse bindings are also important, they are:
.Pp
.Bl -tag -width "CM-EscapeXXXXX" -offset indent -compact
.Bl -tag -width Ds -offset indent -compact
.It Ic M-M1
Move current window.
.It Ic CM-M1
@ -174,33 +137,47 @@ Lower current window.
Hide current window.
.El
.Pp
The following key bindings may be used to navigate
search and exec dialogs:
.Pp
.Bl -tag -width "[Down] or C-s or M-j" -offset indent -compact
.It Ic [Return]
Select item.
.It Ic [Down], C-s No or Ic M-j
Next item.
.It Ic [Up], C-r No or Ic M-k
Previous item.
.It Ic [Backspace] No or Ic C-h
Backspace.
.It Ic C-u
Clear input.
.It Ic C-a
List all available items.
.It Ic [Esc]
Cancel.
.El
.Pp
The options for
.Nm
rereads its configuration file when it receives a hangup signal,
.Dv SIGHUP ,
by executing itself with the name and arguments with which it was started.
This is equivalent to the
.Ar restart
function.
are as follows:
.Bl -tag -width Ds
.It Fl c Ar file
Specify the config file to use. Defaults to
.Pa ~/.cwmrc .
.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,
@ -213,21 +190,37 @@ keeps a history of the 5 previous titles of a window.
When searching, the leftmost character of the result list may show a
flag:
.Pp
.Bl -tag -width Ds -offset indent -compact
.Bl -tag -width 10n -offset indent -compact
.It !
Window is currently focused.
The window is the currently focused window.
.It &
Window is hidden.
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 APPLICATIONS
.Nm
manages a list of applications defined with the
.Cm command
configuration option.
.Sh GROUPS
.Nm
has the ability to group windows together, and use the groups to
perform operations on the entire group instead of just one window.
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.
@ -239,19 +232,20 @@ and a red border will be shown on those just removed.
.Sh MENUS
Menus are recalled by clicking the mouse on the root window:
.Pp
.Bl -tag -width Ds -offset indent -compact
.Bl -tag -width 10n -offset indent -compact
.It Ic M1
Show list of currently defined windows.
Selecting an item will warp to that window, unhiding it if necessary.
Show list of currently hidden windows.
Clicking on an item will unhide that window.
.It Ic M2
Show list of currently defined groups.
Selecting an item will hide/unhide that group.
Clicking on an item will hide/unhide that group.
.It Ic M3
Show list of applications as defined in the configuration file.
Selecting an item will spawn that application.
Show list of applications as defined in
.Pa ~/.cwmrc .
Clicking on an item will spawn that application.
.El
.Sh ENVIRONMENT
.Bl -tag -width "DISPLAYXXX" -compact
.Bl -tag -width "DISPLAYXXX"
.It DISPLAY
.Nm
starts on this display unless the
@ -259,14 +253,23 @@ starts on this display unless the
option is given.
.El
.Sh FILES
.Bl -tag -width "~/.cwmrcXX" -compact
.Bl -tag -width Ds
.It Pa ~/.cwmrc
Default
.Nm
configuration file.
.El
.Sh SEE ALSO
.Xr cwmrc 5
.Sh AUTHORS
.An -nosplit
.Pp
.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.
.Sh HISTORY
.Nm
was originally inspired by evilwm, but was rewritten from scratch
@ -277,14 +280,3 @@ has since been removed or rewritten.
.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.

650
cwmrc.5
View File

@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate$
.Dd $Mdocdate: September 8 2011 $
.Dt CWMRC 5
.Os
.Sh NAME
@ -37,114 +37,86 @@ Arguments containing whitespace should be surrounded by double quotes
.Pq \&" .
.Pp
The following options are accepted:
.Bl -tag -width Ds
.It Ic autogroup Ar group Oo Ar windowname , Oc Ns Ar windowclass
Automatically add new windows to
.Ar group
if their class property matches
.Ar windowclass ,
or if their name and class properties match
.Ar windowname
and
.Ar windowclass ,
respectively.
The more specific last match wins.
.Pp
.Bl -tag -width Ds -compact
.It Ic autogroup Ar group windowname
.It Ic autogroup Ar group windowname,windowclass
Control automatic window grouping, based on the name and/or class
properties, where
.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 .
If the group number is 0, then the window will not be grouped; this to
allow for
.Dq sticky
windows in 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
The name and class of a window may be obtained using
.Xr xprop 1 .
.It Ic bind-key Ar key function
Bind or rebind key
.Ar key
to
.Ar function .
.Pp
.It Ic bind Ar keys command
Cause the creation of a keybinding, or replacement of a default
keybinding.
The modifier keys come first, followed by a
.Sq - ,
then a keysym name, taken from
.Pa /usr/X11R6/include/X11/keysymdef.h .
.Sq - .
.Pp
The following modifiers are recognised:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It Ic C
Control key.
.It Ic M
Meta key.
.It Ic S
Shift key.
.It Ic 4
Mod4 (windows) key.
.It Ic 5
Mod5 (AltGr) key.
.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
.Ar function
.Sq -
should be followed by either a keysym name, taken from
.Pa /usr/X11R6/include/X11/keysymdef.h ,
or a numerical keycode value enclosed in
.Dq [] .
The
.Ar command
may either be one from the
.Sx BIND FUNCTION LIST
.Sx BIND COMMAND LIST
(see below) or the command line that is to be executed.
.It Ic bind-mouse Ar button function
Bind or rebind button
.Ar button
to
.Ar function .
The modifier keys come first, followed by a
.Sq - ,
then the button number.
.Pp
The same modifiers are recognised as for
.Ar key
in
.Nm bind-key .
A special
.Ar command
keyword
.Dq unmap
can be used to remove the named keybinding.
This can be used to remove a binding which conflicts with an
application.
.Pp
The following buttons are recognised:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It Ic 1
Left mouse button.
.It Ic 2
Middle mouse button.
.It Ic 3
Right mouse button.
.It Ic 4
Scroll up mouse button.
.It Ic 5
Scroll down mouse button.
.El
.Pp
The
.Ar function
may be taken from the
.Sx BIND FUNCTION LIST
(see below) or the command line that is to be executed.
.It Ic borderwidth Ar pixels
Set the window border width to
.Ar pixels .
.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.
.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.
.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
@ -152,7 +124,7 @@ entry is shown in the application menu.
When selected, the defined
.Ar path
is executed with
.Xr execvp 3 .
.Xr execve 2 .
.Pp
The
.Ar name
@ -162,17 +134,19 @@ and
.Nm lock
have a special meaning.
They point to the terminal and screen locking programs specified by
key bindings.
keybindings.
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
@ -183,277 +157,67 @@ This
can be used for applications such as
.Xr xclock 1 ,
where the user may wish to remain visible.
.It Ic htile Ar percent
Set the percentage of screen the master window should occupy
after calling
.Ic window-htile .
If set to 0, the horizontal size of the master window will
remain unchanged.
The default is 50.
.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.
.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.
.It Ic unbind-key Ar key
Unbind function bound to
.Ar key .
A special
.Ar key
keyword
.Dq all
can be used to unbind all keys.
.It Ic unbind-mouse Ar button
Unbind function bound to
.Ar button .
A special
.Ar button
keyword
.Dq all
can be used to unbind all buttons.
.It Ic vtile Ar percent
Set the percentage of screen the master window should occupy
after calling
.Ic window-vtile .
If set to 0, the vertical size of the master window will
remain unchanged.
The default is 50.
.It Ic wm Ar name path
Every
.Ar name
entry is shown in the wm menu.
When selected, the window manager is replaced by
.Ar path .
.El
.Sh BIND FUNCTION LIST
.Bl -tag -width 23n -compact
.It restart
Restart the running
.Xr cwm 1 .
.It quit
Quit
.Xr cwm 1 .
.It terminal
Spawn a new terminal.
.It lock
Lock the screen.
.It menu-window
Launch window search menu.
.It menu-window-hidden
Launch hidden window search menu.
.It menu-cmd
Launch application search menu.
.It menu-group
Launch group search menu.
.It menu-exec
Launch
.Dq exec program
menu.
.It menu-exec-wm
Launch
.Dq exec WindowManager
menu.
.It menu-ssh
Launch
.Dq ssh
menu.
.It group-toggle-[n]
Toggle visibility of group n, where n is 1-9.
.It group-only-[n]
Show only group n, where n is 1-9, hiding other groups.
.It group-last
Show only the previously active group.
.It group-close-[n]
Close all windows in group n, where n is 1-9.
.It group-toggle-all
Toggle visibility of all groups.
.It window-group
Toggle group membership of current window.
.It window-movetogroup-[n]
Hide current window from display and move to group n, where n is 1-9.
.It group-cycle
Forward cycle through groups.
.It group-rcycle
Reverse cycle through groups.
.It window-cycle
Forward cycle through windows.
.It window-rcycle
Reverse cycle through windows.
.It window-cycle-ingroup
Forward cycle through windows in current group.
.It window-rcycle-ingroup
Reverse cycle through windows in current group.
.It window-close
Close current window.
.It window-hide
Hide current window.
.It window-lower
Lower current window.
.It window-raise
Raise current window.
.It window-menu-label
Label current window.
.It window-freeze
Freeze current window geometry.
.It window-stick
Stick current window to all groups (same as assigning to nogroup).
.It window-fullscreen
Full-screen current window (gap + border removed).
.It window-maximize
Maximize current window (gap + border honored).
.It window-vmaximize
Vertically maximize current window (gap + border honored).
.It window-hmaximize
Horizontally maximize current window (gap + border honored).
.It window-htile
Current window is placed at the top of the screen, maximized
horizontally and resized to
.Ar htile
(default half) of the vertical screen space.
Other windows in its group share remaining screen space.
.It window-vtile
Current window is placed on the left of the screen, maximized vertically
and resized to
.Ar vtile
(default half) of the horizontal screen space.
Other windows in its group share remaining screen space.
.It window-move
Move current window.
.It window-resize
Resize current window.
.It window-move-up
Move window
.Ar moveamount
pixels up.
.It window-move-down
Move window
.Ar moveamount
pixels down.
.It window-move-right
Move window
.Ar moveamount
pixels right.
.It window-move-left
Move window
.Ar moveamount
pixels left.
.It window-move-up-big
Move window 10 times
.Ar moveamount
pixels up.
.It window-move-down-big
Move window 10 times
.Ar moveamount
pixels down.
.It window-move-right-big
Move window 10 times
.Ar moveamount
pixels right.
.It window-move-left-big
Move window 10 times
.Ar moveamount
pixels left.
.It window-resize-up
Resize window
.Ar moveamount
pixels up.
.It window-resize-down
Resize window
.Ar moveamount
pixels down.
.It window-resize-right
Resize window
.Ar moveamount
pixels right.
.It window-resize-left
Resize window
.Ar moveamount
pixels left.
.It window-resize-up-big
Resize window 10 times
.Ar moveamount
pixels up.
.It window-resize-down-big
Resize window 10 times
.Ar moveamount
pixels down.
.It window-resize-right-big
Resize window 10 times
.Ar moveamount
pixels right.
.It window-resize-left-big
Resize window 10 times
.Ar moveamount
pixels left.
.It window-snap-up
Snap window to top edge.
.It window-snap-down
Snap window to bottom edge.
.It window-snap-right
Snap window to right edge.
.It window-snap-left
Snap window to left edge.
.It window-snap-up-right
Snap window to top-right corner.
.It window-snap-up-left
Snap window to top-left corner.
.It window-snap-down-right
Snap window to bottom-right corner.
.It window-snap-down-left
Snap window to bottom-left corner.
.It pointer-move-up
Move pointer
.Ar moveamount
pixels up.
.It pointer-move-down
Move pointer
.Ar moveamount
pixels down.
.It pointer-move-right
Move pointer
.Ar moveamount
pixels right.
.It pointer-move-left
Move pointer
.Ar moveamount
pixels left.
.It pointer-move-up-big
Move pointer 10 times
.Ar moveamount
pixels up.
.It pointer-move-down-big
Move pointer 10 times
.Ar moveamount
pixels down.
.It pointer-move-right-big
Move pointer 10 times
.Ar moveamount
pixels right.
.It pointer-move-left-big
Move pointer 10 times
.Ar moveamount
pixels left.
.El
.Sh FILES
.Bl -tag -width "~/.cwmrcXXX" -compact
.It Pa ~/.cwmrc
Default
.Xr cwm 1
configuration file.
.El
.Sh EXAMPLES
.Sh EXAMPLE CONFIGURATION
.Bd -literal
# Set default Xft(3) font
fontname "sans-serif:pixelsize=14:bold"
@ -476,25 +240,215 @@ ignore xwi
ignore xapm
ignore xclock
# Key bindings
bind-key CM-r window-menu-label
bind-key CS-Return "xterm -e top"
bind-key C4-equal window-vmaximize
bind-key C4S-equal window-hmaximize
bind-key M-1 group-only-1
bind-key M-2 group-only-2
bind-key M-3 group-only-3
bind-key MS-1 window-movetogroup-1
bind-key MS-2 window-movetogroup-2
bind-key MS-3 window-movetogroup-3
unbind-key 4-o
unbind-key CM-equal
unbind-key CMS-equal
# Keybindings
bind CM-r label
bind CS-Return "xterm -e top"
bind 4-o unmap
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
bind-mouse M-2 window-lower
bind-mouse M-3 window-resize
# Mousebindings
mousebind M-2 window_lower
mousebind M-3 window_resize
.Ed
.Sh BIND COMMAND LIST
.Bl -tag -width 18n -compact
.It reload
Reload configuration.
.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 maximize
Maximize current window full-screen.
.It vmaximize
Maximize current window vertically.
.It hmaximize
Maximize current window horizontally.
.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.
.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 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

106
fgetln.c Normal file
View File

@ -0,0 +1,106 @@
/* $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 <stdlib.h>
#ifndef HAVE_NBTOOL_CONFIG_H
/* These headers are required, but included from nbtool_config.h */
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#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

105
font.c Normal file
View File

@ -0,0 +1,105 @@
/*
* calmwm - the calm window manager
*
* Copyright (c) 2005 Marius Eriksen <marius@monkey.org>
*
* 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 <sys/param.h>
#include <sys/queue.h>
#include <err.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
int
font_ascent(struct screen_ctx *sc)
{
return (sc->font->ascent);
}
int
font_descent(struct screen_ctx *sc)
{
return (sc->font->descent);
}
u_int
font_height(struct screen_ctx *sc)
{
return (sc->font->height + 1);
}
void
font_init(struct screen_ctx *sc, const char *color)
{
if (sc->xftdraw)
XftDrawDestroy(sc->xftdraw);
sc->xftdraw = XftDrawCreate(X_Dpy, sc->rootwin,
DefaultVisual(X_Dpy, sc->which), DefaultColormap(X_Dpy, sc->which));
if (sc->xftdraw == NULL)
errx(1, "XftDrawCreate");
if (sc->xftcolor.pixel)
XftColorFree(X_Dpy, DefaultVisual(X_Dpy, sc->which),
DefaultColormap(X_Dpy, sc->which), &sc->xftcolor);
if (!XftColorAllocName(X_Dpy, DefaultVisual(X_Dpy, sc->which),
DefaultColormap(X_Dpy, sc->which), color, &sc->xftcolor))
errx(1, "XftColorAllocName");
}
int
font_width(struct screen_ctx *sc, const char *text, int len)
{
XGlyphInfo extents;
XftTextExtentsUtf8(X_Dpy, sc->font, (const FcChar8*)text,
len, &extents);
return (extents.xOff);
}
void
font_draw(struct screen_ctx *sc, const char *text, int len,
Drawable d, int x, int y)
{
XftDrawChange(sc->xftdraw, d);
XftDrawStringUtf8(sc->xftdraw, &sc->xftcolor, sc->font, x, y,
(const FcChar8*)text, len);
}
XftFont *
font_make(struct screen_ctx *sc, const char *name)
{
XftFont *fn = NULL;
FcPattern *pat, *patx;
XftResult res;
if ((pat = FcNameParse((const FcChar8*)name)) == NULL)
return (NULL);
if ((patx = XftFontMatch(X_Dpy, sc->which, pat, &res)) != NULL)
fn = XftFontOpenPattern(X_Dpy, patx);
FcPatternDestroy(pat);
return (fn);
}

627
group.c
View File

@ -19,96 +19,113 @@
* $OpenBSD$
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
static struct group_ctx *group_next(struct group_ctx *);
static struct group_ctx *group_prev(struct group_ctx *);
static void group_restack(struct group_ctx *);
static void group_set_active(struct group_ctx *);
static void group_add(struct group_ctx *, struct client_ctx *);
static void group_remove(struct client_ctx *);
static void group_hide(struct screen_ctx *, struct group_ctx *);
static void group_show(struct screen_ctx *, struct group_ctx *);
static void group_fix_hidden_state(struct group_ctx *);
static void group_setactive(struct screen_ctx *, long);
static void group_set_names(struct screen_ctx *);
void
group_assign(struct group_ctx *gc, struct client_ctx *cc)
const char *shortcut_to_name[] = {
"nogroup", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine"
};
static void
group_add(struct group_ctx *gc, struct client_ctx *cc)
{
if ((gc != NULL) && (gc->num == 0))
gc = NULL;
long no;
if (cc == NULL || gc == NULL)
errx(1, "group_add: a ctx is NULL");
cc->gc = gc;
no = gc->shortcut - 1;
xu_ewmh_set_net_wm_desktop(cc);
}
if (cc->group == gc)
return;
void
group_hide(struct group_ctx *gc)
{
struct screen_ctx *sc = gc->sc;
struct client_ctx *cc;
if (cc->group != NULL)
TAILQ_REMOVE(&cc->group->clients, cc, group_entry);
screen_updatestackingorder(gc->sc);
XChangeProperty(X_Dpy, cc->win, _NET_WM_DESKTOP, XA_CARDINAL,
32, PropModeReplace, (unsigned char *)&no, 1);
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
if (!(cc->flags & CLIENT_STICKY) &&
!(cc->flags & CLIENT_HIDDEN))
client_hide(cc);
}
}
void
group_show(struct group_ctx *gc)
{
struct screen_ctx *sc = gc->sc;
struct client_ctx *cc;
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
if (!(cc->flags & CLIENT_STICKY) &&
(cc->flags & CLIENT_HIDDEN))
client_show(cc);
}
group_restack(gc);
group_set_active(gc);
TAILQ_INSERT_TAIL(&gc->clients, cc, group_entry);
cc->group = gc;
}
static void
group_restack(struct group_ctx *gc)
group_remove(struct client_ctx *cc)
{
long no = 0xffffffff;
if (cc == NULL || cc->group == NULL)
errx(1, "group_remove: a ctx is NULL");
XChangeProperty(X_Dpy, cc->win, _NET_WM_DESKTOP, XA_CARDINAL,
32, PropModeReplace, (unsigned char *)&no, 1);
TAILQ_REMOVE(&cc->group->clients, cc, group_entry);
cc->group = NULL;
}
static void
group_hide(struct screen_ctx *sc, struct group_ctx *gc)
{
struct client_ctx *cc;
screen_updatestackingorder(sc);
gc->nhidden = 0;
gc->highstack = 0;
TAILQ_FOREACH(cc, &gc->clients, group_entry) {
client_hide(cc);
gc->nhidden++;
if (cc->stackingorder > gc->highstack)
gc->highstack = cc->stackingorder;
}
gc->hidden = 1; /* XXX: equivalent to gc->nhidden > 0 */
}
static void
group_show(struct screen_ctx *sc, struct group_ctx *gc)
{
struct screen_ctx *sc = gc->sc;
struct client_ctx *cc;
Window *winlist;
int i, lastempty = -1;
int nwins = 0, highstack = 0;
u_int i;
int lastempty = -1;
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
if (cc->stackingorder > highstack)
highstack = cc->stackingorder;
gc->highstack = 0;
TAILQ_FOREACH(cc, &gc->clients, group_entry) {
if (cc->stackingorder > gc->highstack)
gc->highstack = cc->stackingorder;
}
winlist = xreallocarray(NULL, (highstack + 1), sizeof(*winlist));
winlist = (Window *) xcalloc(sizeof(*winlist), (gc->highstack + 1));
/* Invert the stacking order for XRestackWindows(). */
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
winlist[highstack - cc->stackingorder] = cc->win;
nwins++;
/*
* Invert the stacking order as XRestackWindows() expects them
* top-to-bottom.
*/
TAILQ_FOREACH(cc, &gc->clients, group_entry) {
winlist[gc->highstack - cc->stackingorder] = cc->win;
client_unhide(cc);
}
/* Un-sparseify */
for (i = 0; i <= highstack; i++) {
for (i = 0; i <= gc->highstack; i++) {
if (!winlist[i] && lastempty == -1)
lastempty = i;
else if (winlist[i] && lastempty != -1) {
@ -118,33 +135,81 @@ group_restack(struct group_ctx *gc)
}
}
XRestackWindows(X_Dpy, winlist, nwins);
free(winlist);
XRestackWindows(X_Dpy, winlist, gc->nhidden);
xfree(winlist);
gc->hidden = 0;
group_setactive(sc, gc->shortcut - 1);
}
void
group_init(struct screen_ctx *sc, int num, const char *name)
group_init(struct screen_ctx *sc)
{
struct group_ctx *gc;
int i;
long viewports[2] = {0, 0};
long ndesks = CALMWM_NGROUPS, zero = 0;
gc = xmalloc(sizeof(*gc));
gc->sc = sc;
gc->name = xstrdup(name);
gc->num = num;
TAILQ_INSERT_TAIL(&sc->groupq, gc, entry);
TAILQ_INIT(&sc->groupq);
sc->group_hideall = 0;
/* see if any group names have already been set and update the property
* with ours if they'll have changed.
*/
group_update_names(sc);
if (num == 1)
group_set_active(gc);
for (i = 0; i < CALMWM_NGROUPS; i++) {
TAILQ_INIT(&sc->groups[i].clients);
sc->groups[i].hidden = 0;
sc->groups[i].shortcut = i + 1;
TAILQ_INSERT_TAIL(&sc->groupq, &sc->groups[i], entry);
}
/* we don't support large desktops, so this is always (0, 0) */
XChangeProperty(X_Dpy, sc->rootwin, _NET_DESKTOP_VIEWPORT,
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2);
XChangeProperty(X_Dpy, sc->rootwin, _NET_NUMBER_OF_DESKTOPS,
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&ndesks, 1);
/*
* we don't use virtual roots, so make sure it's not there from a
* previous wm.
*/
XDeleteProperty(X_Dpy, sc->rootwin, _NET_VIRTUAL_ROOTS);
/*
* We don't really have a ``showing desktop'' mode, so this is zero
* always. XXX 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, _NET_SHOWING_DESKTOP,
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&zero, 1);
group_setactive(sc, 0);
}
void
group_set_active(struct group_ctx *gc)
group_make_autogroup(struct conf *conf, char *val, int no)
{
struct screen_ctx *sc = gc->sc;
struct autogroupwin *aw;
char *p;
sc->group_active = gc;
aw = xcalloc(1, sizeof(*aw));
xu_ewmh_net_current_desktop(sc);
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 = no;
TAILQ_INSERT_TAIL(&conf->autogroupq, aw, entry);
}
static void
group_setactive(struct screen_ctx *sc, long idx)
{
sc->group_active = &sc->groups[idx];
XChangeProperty(X_Dpy, sc->rootwin, _NET_CURRENT_DESKTOP,
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&idx, 1);
}
void
@ -153,226 +218,332 @@ group_movetogroup(struct client_ctx *cc, int idx)
struct screen_ctx *sc = cc->sc;
struct group_ctx *gc;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx) {
if (cc->gc == gc)
return;
if (gc->num != 0 && group_holds_only_hidden(gc))
client_hide(cc);
group_assign(gc, cc);
}
if (idx < 0 || idx >= CALMWM_NGROUPS)
err(1, "group_movetogroup: index out of range (%d)", idx);
gc = &sc->groups[idx];
if (cc->group == gc)
return;
if (gc->hidden) {
client_hide(cc);
gc->nhidden++;
}
group_add(gc, cc);
}
/*
* Colouring for groups upon add/remove.
*/
void
group_toggle_membership(struct client_ctx *cc)
group_sticky_toggle_enter(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
struct group_ctx *gc = sc->group_active;
struct group_ctx *gc;
if (cc->gc == gc) {
group_assign(NULL, cc);
cc->flags |= CLIENT_UNGROUP;
gc = sc->group_active;
if (gc == cc->group) {
group_remove(cc);
cc->highlight = CLIENT_HIGHLIGHT_UNGROUP;
} else {
group_assign(gc, cc);
cc->flags |= CLIENT_GROUP;
group_add(gc, cc);
cc->highlight = CLIENT_HIGHLIGHT_GROUP;
}
client_draw_border(cc);
}
int
group_holds_only_sticky(struct group_ctx *gc)
void
group_sticky_toggle_exit(struct client_ctx *cc)
{
struct screen_ctx *sc = gc->sc;
struct client_ctx *cc;
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
if (!(cc->flags & CLIENT_STICKY))
return 0;
}
return 1;
cc->highlight = 0;
client_draw_border(cc);
}
int
group_holds_only_hidden(struct group_ctx *gc)
/*
* if group_hidetoggle would produce no effect, toggle the group's hidden state
*/
static void
group_fix_hidden_state(struct group_ctx *gc)
{
struct screen_ctx *sc = gc->sc;
struct client_ctx *cc;
int same = 0;
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
if (!(cc->flags & (CLIENT_HIDDEN | CLIENT_STICKY)))
return 0;
TAILQ_FOREACH(cc, &gc->clients, group_entry) {
if (gc->hidden == ((cc->flags & CLIENT_HIDDEN) ? 1 : 0))
same++;
}
if (same == 0)
gc->hidden = !gc->hidden;
}
void
group_hidetoggle(struct screen_ctx *sc, int idx)
{
struct group_ctx *gc;
if (idx < 0 || idx >= CALMWM_NGROUPS)
err(1, "group_hidetoggle: index out of range (%d)", idx);
gc = &sc->groups[idx];
group_fix_hidden_state(gc);
if (gc->hidden)
group_show(sc, gc);
else {
group_hide(sc, gc);
/* XXX wtf? */
if (TAILQ_EMPTY(&gc->clients))
group_setactive(sc, idx);
}
return 1;
}
void
group_only(struct screen_ctx *sc, int idx)
{
struct group_ctx *gc;
int i;
if (sc->group_last != sc->group_active)
sc->group_last = sc->group_active;
if (idx < 0 || idx >= CALMWM_NGROUPS)
err(1, "group_only: index out of range (%d)", idx);
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx)
group_show(gc);
for (i = 0; i < CALMWM_NGROUPS; i++) {
if (i == idx)
group_show(sc, &sc->groups[i]);
else
group_hide(gc);
}
}
void
group_toggle(struct screen_ctx *sc, int idx)
{
struct group_ctx *gc;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx) {
if (group_holds_only_hidden(gc))
group_show(gc);
else
group_hide(gc);
}
}
}
void
group_toggle_all(struct screen_ctx *sc)
{
struct group_ctx *gc;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (sc->hideall)
group_show(gc);
else
group_hide(gc);
}
sc->hideall = !sc->hideall;
}
void
group_close(struct screen_ctx *sc, int idx)
{
struct group_ctx *gc;
struct client_ctx *cc;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx) {
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->gc != gc)
continue;
client_close(cc);
}
}
group_hide(sc, &sc->groups[i]);
}
}
/*
* Cycle through active groups. If none exist, then just stay put.
*/
void
group_cycle(struct screen_ctx *sc, int flags)
{
struct group_ctx *newgc, *oldgc, *showgroup = NULL;
struct group_ctx *gc, *showgroup = NULL;
oldgc = sc->group_active;
assert(sc->group_active != NULL);
newgc = oldgc;
gc = sc->group_active;
for (;;) {
newgc = (flags & CWM_CYCLE_REVERSE) ? group_prev(newgc) :
group_next(newgc);
if (newgc == oldgc)
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(newgc) && showgroup == NULL)
showgroup = newgc;
else if (!group_holds_only_hidden(newgc))
group_hide(newgc);
if (!TAILQ_EMPTY(&gc->clients) && showgroup == NULL)
showgroup = gc;
else if (!gc->hidden)
group_hide(sc, gc);
}
if (showgroup == NULL)
return;
group_hide(oldgc);
group_hide(sc, sc->group_active);
if (group_holds_only_hidden(showgroup))
group_show(showgroup);
if (showgroup->hidden)
group_show(sc, showgroup);
else
group_set_active(showgroup);
group_setactive(sc, showgroup->shortcut - 1);
}
static struct group_ctx *
group_next(struct group_ctx *gc)
/* called when a client is deleted */
void
group_client_delete(struct client_ctx *cc)
{
struct screen_ctx *sc = gc->sc;
struct group_ctx *newgc;
if (cc->group == NULL)
return;
return(((newgc = TAILQ_NEXT(gc, entry)) != NULL) ?
newgc : TAILQ_FIRST(&sc->groupq));
TAILQ_REMOVE(&cc->group->clients, cc, group_entry);
cc->group = NULL; /* he he */
}
static struct group_ctx *
group_prev(struct group_ctx *gc)
void
group_menu(XButtonEvent *e)
{
struct screen_ctx *sc = gc->sc;
struct group_ctx *newgc;
return(((newgc = TAILQ_PREV(gc, group_q, entry)) != NULL) ?
newgc : TAILQ_LAST(&sc->groupq, group_q));
}
int
group_restore(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
struct screen_ctx *sc;
struct group_ctx *gc;
int num;
long grpnum;
struct menu *mi;
struct menu_q menuq;
int i;
if (!xu_ewmh_get_net_wm_desktop(cc, &grpnum))
return 0;
sc = screen_fromroot(e->root);
TAILQ_INIT(&menuq);
num = (grpnum == -1) ? 0 : grpnum;
num = MIN(num, (Conf.ngroups - 1));
for (i = 0; i < CALMWM_NGROUPS; i++) {
gc = &sc->groups[i];
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == num) {
group_assign(gc, cc);
return 1;
}
if (TAILQ_EMPTY(&gc->clients))
continue;
mi = xcalloc(1, sizeof(*mi));
if (gc->hidden)
(void)snprintf(mi->text, sizeof(mi->text), "%d: [%s]",
gc->shortcut, sc->group_names[i]);
else
(void)snprintf(mi->text, sizeof(mi->text), "%d: %s",
gc->shortcut, sc->group_names[i]);
mi->ctx = gc;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
if (TAILQ_EMPTY(&menuq))
return;
mi = menu_filter(sc, &menuq, NULL, NULL, 0, NULL, NULL);
if (mi == NULL || mi->ctx == NULL)
goto cleanup;
gc = (struct group_ctx *)mi->ctx;
(gc->hidden) ? group_show(sc, gc) : group_hide(sc, gc);
cleanup:
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
return 0;
}
int
void
group_alltoggle(struct screen_ctx *sc)
{
int i;
for (i = 0; i < CALMWM_NGROUPS; i++) {
if (sc->group_hideall)
group_show(sc, &sc->groups[i]);
else
group_hide(sc, &sc->groups[i]);
}
sc->group_hideall = (!sc->group_hideall);
}
void
group_autogroup(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
struct autogroup *ag;
struct autogroupwin *aw;
struct group_ctx *gc;
int num = -1, both_match = 0;
int no = -1;
long *grpno;
if (cc->res_class == NULL || cc->res_name == NULL)
return 0;
if (cc->app_class == NULL || cc->app_name == NULL)
return;
TAILQ_FOREACH(ag, &Conf.autogroupq, entry) {
if (strcmp(ag->class, cc->res_class) == 0) {
if ((ag->name != NULL) &&
(strcmp(ag->name, cc->res_name) == 0)) {
num = ag->num;
both_match = 1;
} else if (ag->name == NULL && !both_match)
num = ag->num;
if (xu_getprop(cc->win, _NET_WM_DESKTOP, XA_CARDINAL,
1, (unsigned char **)&grpno) > 0) {
if (*grpno == 0xffffffff)
no = 0;
else if (*grpno > CALMWM_NGROUPS || *grpno < 0)
no = CALMWM_NGROUPS - 1;
else
no = *grpno + 1;
XFree(grpno);
} else {
TAILQ_FOREACH(aw, &Conf.autogroupq, entry) {
if (strcmp(aw->class, cc->app_class) == 0 &&
(aw->name == NULL ||
strcmp(aw->name, cc->app_name) == 0)) {
no = aw->num;
break;
}
}
}
/* no group please */
if (no == 0)
return;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == num) {
group_assign(gc, cc);
return 1;
if (gc->shortcut == no) {
group_add(gc, cc);
return;
}
}
return 0;
if (Conf.flags & CONF_STICKY_GROUPS)
group_add(sc->group_active, cc);
}
void
group_update_names(struct screen_ctx *sc)
{
char **strings, *p;
unsigned char *prop_ret;
Atom type_ret;
int format_ret, i = 0, nstrings = 0, n, setnames = 0;
unsigned long bytes_after, num_ret;
if (XGetWindowProperty(X_Dpy, sc->rootwin, _NET_DESKTOP_NAMES, 0,
0xffffff, False, UTF8_STRING, &type_ret, &format_ret,
&num_ret, &bytes_after, &prop_ret) == Success &&
prop_ret != NULL && format_ret == 8) {
/* failure, just set defaults */
prop_ret[num_ret - 1] = '\0'; /* paranoia */
while (i < num_ret) {
if (prop_ret[i++] == '\0')
nstrings++;
}
}
strings = xmalloc((nstrings < CALMWM_NGROUPS ? CALMWM_NGROUPS :
nstrings) * sizeof(*strings));
i = n = 0;
p = prop_ret;
while (n < nstrings) {
strings[n++] = xstrdup(p);
p += strlen(p) + 1;
}
/*
* make sure we always set our defaults if nothing is there to
* replace them.
*/
if (n < CALMWM_NGROUPS) {
setnames = 1;
i = 1;
while (n < CALMWM_NGROUPS)
strings[n++] = xstrdup(shortcut_to_name[i++]);
}
if (prop_ret != NULL)
XFree(prop_ret);
if (sc->group_nonames != 0)
xfree(sc->group_names);
sc->group_names = strings;
sc->group_nonames = n;
if (setnames)
group_set_names(sc);
}
static void
group_set_names(struct screen_ctx *sc)
{
unsigned char *p, *q;
size_t len = 0, tlen, slen;
int i;
for (i = 0; i < sc->group_nonames; i++)
len += strlen(sc->group_names[i]) + 1;
q = p = xcalloc(len, sizeof(*p));
tlen = len;
for (i = 0; i < sc->group_nonames; i++) {
slen = strlen(sc->group_names[i]) + 1;
(void)strlcpy(q, sc->group_names[i], tlen);
tlen -= slen;
q += slen;
}
XChangeProperty(X_Dpy, sc->rootwin, _NET_DESKTOP_NAMES,
UTF8_STRING, 8, PropModeReplace, p, len);
}

945
kbfunc.c

File diff suppressed because it is too large Load Diff

538
menu.c
View File

@ -19,17 +19,14 @@
* $OpenBSD$
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
@ -37,51 +34,69 @@
#define PROMPT_SCHAR "\xc2\xbb"
#define PROMPT_ECHAR "\xc2\xab"
#define MENUMASK (MOUSEMASK | ButtonMotionMask | KeyPressMask | \
ExposureMask)
#define MENUGRABMASK (MOUSEMASK | ButtonMotionMask | StructureNotifyMask)
enum ctltype {
CTL_NONE = -1,
CTL_ERASEONE = 0, CTL_WIPE, CTL_UP, CTL_DOWN, CTL_RETURN,
CTL_TAB, CTL_ABORT, CTL_ALL
CTL_ABORT, CTL_ALL
};
struct menu_ctx {
struct screen_ctx *sc;
Window win;
XftDraw *xftdraw;
struct geom geom;
char searchstr[MENU_MAXENTRY + 1];
char dispstr[MENU_MAXENTRY*2 + 1];
char promptstr[MENU_MAXENTRY + 1];
int hasprompt;
int list;
int listing;
int changed;
int noresult;
int prev;
int entry;
int width;
int num;
int flags;
void (*match)(struct menu_q *, struct menu_q *, char *);
void (*print)(struct menu *, int);
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(struct menu_ctx *,
struct menu_q *, int, int);
static struct menu *menu_handle_release(struct menu_ctx *,
struct menu_q *, int, int);
static void menu_draw(struct menu_ctx *, struct menu_q *,
struct menu_q *);
static void menu_draw_entry(struct menu_ctx *, struct menu_q *,
static void menu_handle_move(XEvent *, struct menu_ctx *,
struct screen_ctx *);
static struct menu *menu_handle_release(XEvent *, struct menu_ctx *,
struct screen_ctx *, struct menu_q *);
static void menu_draw(struct screen_ctx *, struct menu_ctx *,
struct menu_q *, struct menu_q *);
static int menu_calc_entry(struct screen_ctx *, struct menu_ctx *,
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 *);
static int menu_keycode(KeyCode, u_int, enum ctltype *,
char *);
void
menu_init(struct screen_ctx *sc)
{
XGCValues gv;
if (sc->menuwin)
XDestroyWindow(X_Dpy, sc->menuwin);
sc->menuwin = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0, 1, 1,
Conf.bwidth,
sc->color[CWM_COLOR_FG_MENU].pixel,
sc->color[CWM_COLOR_BG_MENU].pixel);
gv.foreground =
sc->color[CWM_COLOR_FG_MENU].pixel^sc->color[CWM_COLOR_BG_MENU].pixel;
gv.background = sc->color[CWM_COLOR_BG_MENU].pixel;
gv.function = GXxor;
if (sc->gc)
XFreeGC(X_Dpy, sc->gc);
sc->gc = XCreateGC(X_Dpy, sc->menuwin,
GCForeground|GCBackground|GCFunction, &gv);
}
struct menu *
menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt,
const char *initial, int flags,
menu_filter(struct screen_ctx *sc, struct menu_q *menuq, char *prompt,
char *initial, int dummy,
void (*match)(struct menu_q *, struct menu_q *, char *),
void (*print)(struct menu *, int))
{
@ -90,59 +105,62 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt,
struct menu *mi = NULL;
XEvent e;
Window focuswin;
int focusrevert, xsave, ysave, xcur, ycur;
int evmask, focusrevert;
int xsave, ysave, xcur, ycur;
TAILQ_INIT(&resultq);
xu_ptr_get(sc->rootwin, &xsave, &ysave);
bzero(&mc, sizeof(mc));
(void)memset(&mc, 0, sizeof(mc));
mc.sc = sc;
mc.flags = flags;
mc.match = match;
mc.print = print;
mc.entry = mc.prev = -1;
mc.geom.x = xsave;
mc.geom.y = ysave;
xu_ptr_getpos(sc->rootwin, &mc.x, &mc.y);
if (mc.flags & CWM_MENU_LIST)
xsave = mc.x;
ysave = mc.y;
if (prompt == NULL) {
evmask = MENUMASK;
mc.promptstr[0] = '\0';
mc.list = 1;
} else {
evmask = MENUMASK | KEYMASK; /* only accept keys if prompt */
(void)snprintf(mc.promptstr, sizeof(mc.promptstr), "%s%s",
prompt, PROMPT_SCHAR);
(void)snprintf(mc.dispstr, sizeof(mc.dispstr), "%s%s%s",
mc.promptstr, mc.searchstr, PROMPT_ECHAR);
mc.width = font_width(sc, mc.dispstr, strlen(mc.dispstr));
mc.hasprompt = 1;
}
(void)strlcpy(mc.promptstr, prompt, sizeof(mc.promptstr));
if (initial != NULL)
(void)strlcpy(mc.searchstr, initial, sizeof(mc.searchstr));
else
mc.searchstr[0] = '\0';
mc.win = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0, 1, 1,
Conf.bwidth,
sc->xftcolor[CWM_COLOR_MENU_FG].pixel,
sc->xftcolor[CWM_COLOR_MENU_BG].pixel);
mc.xftdraw = XftDrawCreate(X_Dpy, mc.win,
sc->visual, sc->colormap);
mc.match = match;
mc.print = print;
mc.entry = mc.prev = -1;
XSelectInput(X_Dpy, mc.win, MENUMASK);
XMapRaised(X_Dpy, mc.win);
XMoveResizeWindow(X_Dpy, sc->menuwin, mc.x, mc.y, mc.width,
font_height(sc));
XSelectInput(X_Dpy, sc->menuwin, evmask);
XMapRaised(X_Dpy, sc->menuwin);
if (XGrabPointer(X_Dpy, mc.win, False, MENUGRABMASK,
GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_QUESTION],
CurrentTime) != GrabSuccess) {
XftDrawDestroy(mc.xftdraw);
XDestroyWindow(X_Dpy, mc.win);
return NULL;
if (xu_ptr_grab(sc->menuwin, MENUGRABMASK, Cursor_question) < 0) {
XUnmapWindow(X_Dpy, sc->menuwin);
return (NULL);
}
XGetInputFocus(X_Dpy, &focuswin, &focusrevert);
XSetInputFocus(X_Dpy, mc.win, RevertToPointerRoot, CurrentTime);
XSetInputFocus(X_Dpy, sc->menuwin, RevertToPointerRoot, CurrentTime);
/* make sure keybindings don't remove keys from the menu stream */
XGrabKeyboard(X_Dpy, mc.win, True,
XGrabKeyboard(X_Dpy, sc->menuwin, True,
GrabModeAsync, GrabModeAsync, CurrentTime);
for (;;) {
mc.changed = 0;
XWindowEvent(X_Dpy, mc.win, MENUMASK, &e);
XWindowEvent(X_Dpy, sc->menuwin, evmask, &e);
switch (e.type) {
case KeyPress:
@ -151,15 +169,14 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt,
goto out;
/* FALLTHROUGH */
case Expose:
menu_draw(&mc, menuq, &resultq);
menu_draw(sc, &mc, menuq, &resultq);
break;
case MotionNotify:
menu_handle_move(&mc, &resultq,
e.xbutton.x, e.xbutton.y);
menu_handle_move(&e, &mc, sc);
break;
case ButtonRelease:
if ((mi = menu_handle_release(&mc, &resultq,
e.xbutton.x, e.xbutton.y)) != NULL)
if ((mi = menu_handle_release(&e, &mc, sc, &resultq))
!= NULL)
goto out;
break;
default:
@ -167,53 +184,22 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt,
}
}
out:
if ((mc.flags & CWM_MENU_DUMMY) == 0 && mi->dummy) {
/* no mouse based match */
free(mi);
if (dummy == 0 && mi->dummy) { /* no mouse based match */
xfree(mi);
mi = NULL;
}
XftDrawDestroy(mc.xftdraw);
XDestroyWindow(X_Dpy, mc.win);
XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime);
/* restore if user didn't move */
xu_ptr_get(sc->rootwin, &xcur, &ycur);
if (xcur == mc.geom.x && ycur == mc.geom.y)
xu_ptr_set(sc->rootwin, xsave, ysave);
xu_ptr_getpos(sc->rootwin, &xcur, &ycur);
if (xcur == mc.x && ycur == mc.y)
xu_ptr_setpos(sc->rootwin, xsave, ysave);
xu_ptr_ungrab();
XUngrabPointer(X_Dpy, CurrentTime);
XUnmapWindow(X_Dpy, sc->menuwin);
XUngrabKeyboard(X_Dpy, CurrentTime);
return mi;
}
static struct menu *
menu_complete_path(struct menu_ctx *mc)
{
struct screen_ctx *sc = mc->sc;
struct menu *mi, *mr;
struct menu_q menuq;
int mflags = (CWM_MENU_DUMMY);
mr = xcalloc(1, sizeof(*mr));
TAILQ_INIT(&menuq);
if ((mi = menu_filter(sc, &menuq, mc->searchstr, NULL, mflags,
search_match_path, search_print_text)) != NULL) {
mr->abort = mi->abort;
mr->dummy = mi->dummy;
if (mi->text[0] != '\0')
snprintf(mr->text, sizeof(mr->text), "%s \"%s\"",
mc->searchstr, mi->text);
else if (!mr->abort)
strlcpy(mr->text, mc->searchstr, sizeof(mr->text));
}
menuq_clear(&menuq);
return mr;
return (mi);
}
static struct menu *
@ -222,22 +208,16 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
{
struct menu *mi;
enum ctltype ctl;
char chr[32];
char chr;
size_t len;
int clen, i;
wchar_t wc;
if (menu_keycode(&e->xkey, &ctl, chr) < 0)
return NULL;
if (menu_keycode(e->xkey.keycode, e->xkey.state, &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->searchstr[len - 1] = '\0';
mc->changed = 1;
}
break;
@ -263,211 +243,191 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
* even if dummy is zero, we need to return something.
*/
if ((mi = TAILQ_FIRST(resultq)) == NULL) {
mi = xmalloc(sizeof(*mi));
mi = xmalloc(sizeof *mi);
(void)strlcpy(mi->text,
mc->searchstr, sizeof(mi->text));
mi->dummy = 1;
}
mi->abort = 0;
return mi;
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 = xmalloc(sizeof *mi);
mi->text[0] = '\0';
mi->dummy = 1;
mi->abort = 1;
return mi;
return (mi);
default:
break;
}
if (chr[0] != '\0') {
if (chr != '\0') {
char str[2];
str[0] = chr;
str[1] = '\0';
mc->changed = 1;
(void)strlcat(mc->searchstr, chr, sizeof(mc->searchstr));
(void)strlcat(mc->searchstr, str, sizeof(mc->searchstr));
}
if (mc->changed) {
if (mc->searchstr[0] != '\0')
(*mc->match)(menuq, resultq, mc->searchstr);
} else if (!mc->list && mc->listing) {
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;
return (NULL);
}
static void
menu_draw(struct menu_ctx *mc, struct menu_q *menuq, struct menu_q *resultq)
menu_draw(struct screen_ctx *sc, struct menu_ctx *mc, struct menu_q *menuq,
struct menu_q *resultq)
{
struct screen_ctx *sc = mc->sc;
struct menu *mi;
struct geom area;
int n, xsave, ysave;
XGlyphInfo extents;
XineramaScreenInfo *xine;
int xmin, xmax, ymin, ymax;
int n, dy, xsave, ysave;
if (mc->list) {
if (TAILQ_EMPTY(resultq)) {
if (TAILQ_EMPTY(resultq) && mc->list) {
/* Copy them all over. */
TAILQ_FOREACH(mi, menuq, entry)
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
TAILQ_INSERT_TAIL(resultq, mi,
resultentry);
mc->listing = 1;
} else if (mc->changed)
mc->listing = 0;
}
(void)snprintf(mc->dispstr, sizeof(mc->dispstr), "%s%s%s%s",
mc->promptstr, PROMPT_SCHAR, mc->searchstr, PROMPT_ECHAR);
XftTextExtentsUtf8(X_Dpy, sc->xftfont,
(const FcChar8*)mc->dispstr, strlen(mc->dispstr), &extents);
mc->geom.w = extents.xOff;
mc->geom.h = sc->xftfont->ascent + sc->xftfont->descent;
mc->num = 1;
mc->num = 0;
mc->width = 0;
dy = 0;
if (mc->hasprompt) {
(void)snprintf(mc->dispstr, sizeof(mc->dispstr), "%s%s%s",
mc->promptstr, mc->searchstr, PROMPT_ECHAR);
mc->width = font_width(sc, mc->dispstr, strlen(mc->dispstr));
dy = font_height(sc);
mc->num = 1;
}
TAILQ_FOREACH(mi, resultq, resultentry) {
(*mc->print)(mi, mc->listing);
XftTextExtentsUtf8(X_Dpy, sc->xftfont,
(const FcChar8*)mi->print,
MIN(strlen(mi->print), MENU_MAXENTRY), &extents);
mc->geom.w = MAX(mc->geom.w, extents.xOff);
mc->geom.h += sc->xftfont->ascent + sc->xftfont->descent;
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, font_width(sc, text,
MIN(strlen(text), MENU_MAXENTRY)));
dy += font_height(sc);
mc->num++;
}
area = screen_area(sc, mc->geom.x, mc->geom.y, 1);
area.w += area.x - Conf.bwidth * 2;
area.h += area.y - Conf.bwidth * 2;
xsave = mc->geom.x;
ysave = mc->geom.y;
/* Never hide the top, or left side, of the menu. */
if (mc->geom.x + mc->geom.w >= area.w)
mc->geom.x = area.w - mc->geom.w;
if (mc->geom.x < area.x) {
mc->geom.x = area.x;
mc->geom.w = MIN(mc->geom.w, (area.w - area.x));
}
if (mc->geom.y + mc->geom.h >= area.h)
mc->geom.y = area.h - mc->geom.h;
if (mc->geom.y < area.y) {
mc->geom.y = area.y;
mc->geom.h = MIN(mc->geom.h, (area.h - area.y));
xine = screen_find_xinerama(sc, mc->x, mc->y);
if (xine) {
xmin = xine->x_org;
xmax = xine->x_org + xine->width;
ymin = xine->y_org;
ymax = xine->y_org + xine->height;
} else {
xmin = ymin = 0;
xmax = sc->xmax;
ymax = sc->ymax;
}
if (mc->geom.x != xsave || mc->geom.y != ysave)
xu_ptr_set(sc->rootwin, mc->geom.x, mc->geom.y);
xsave = mc->x;
ysave = mc->y;
XClearWindow(X_Dpy, mc->win);
XMoveResizeWindow(X_Dpy, mc->win, mc->geom.x, mc->geom.y,
mc->geom.w, mc->geom.h);
if (mc->x < xmin)
mc->x = xmin;
else if (mc->x + mc->width >= xmax)
mc->x = xmax - mc->width;
n = 1;
XftDrawStringUtf8(mc->xftdraw,
&sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont,
0, sc->xftfont->ascent,
(const FcChar8*)mc->dispstr, strlen(mc->dispstr));
if (mc->y + dy >= ymax)
mc->y = ymax - dy;
/* never hide the top of the menu */
if (mc->y < ymin) {
mc->y = ymin;
dy = ymax - ymin;
}
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, dy);
if (mc->hasprompt) {
font_draw(sc, mc->dispstr, strlen(mc->dispstr), sc->menuwin,
0, font_ascent(sc) + 1);
n = 1;
} else
n = 0;
TAILQ_FOREACH(mi, resultq, resultentry) {
int y = n * (sc->xftfont->ascent + sc->xftfont->descent);
char *text = mi->print[0] != '\0' ?
mi->print : mi->text;
/* Stop drawing when menu doesn't fit inside the screen. */
if (mc->geom.y + y >= area.h)
break;
XftDrawStringUtf8(mc->xftdraw,
&sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont,
0, y + sc->xftfont->ascent,
(const FcChar8*)mi->print, strlen(mi->print));
font_draw(sc, text, MIN(strlen(text), MENU_MAXENTRY),
sc->menuwin, 0, n * font_height(sc) + font_ascent(sc) + 1);
n++;
}
if (n > 1)
menu_draw_entry(mc, resultq, 1, 1);
if (mc->hasprompt && n > 1 && (mc->searchstr[0] != '\0'))
XFillRectangle(X_Dpy, sc->menuwin, sc->gc,
0, font_height(sc), mc->width, font_height(sc));
if (mc->noresult)
XFillRectangle(X_Dpy, sc->menuwin, sc->gc,
0, 0, mc->width, font_height(sc));
}
static void
menu_draw_entry(struct menu_ctx *mc, struct menu_q *resultq,
int entry, int active)
{
struct screen_ctx *sc = mc->sc;
struct menu *mi;
int color, i = 1, y;
TAILQ_FOREACH(mi, resultq, resultentry)
if (entry == i++)
break;
if (mi == NULL)
return;
y = entry * (sc->xftfont->ascent + sc->xftfont->descent);
color = (active) ? CWM_COLOR_MENU_FG : CWM_COLOR_MENU_BG;
XftDrawRect(mc->xftdraw, &sc->xftcolor[color], 0, y,
mc->geom.w, sc->xftfont->ascent + sc->xftfont->descent);
color = (active) ? CWM_COLOR_MENU_FONT_SEL : CWM_COLOR_MENU_FONT;
XftDrawStringUtf8(mc->xftdraw,
&sc->xftcolor[color], sc->xftfont, 0, y + sc->xftfont->ascent,
(const FcChar8*)mi->print, strlen(mi->print));
}
static void
menu_handle_move(struct menu_ctx *mc, struct menu_q *resultq, int x, int y)
menu_handle_move(XEvent *e, struct menu_ctx *mc, struct screen_ctx *sc)
{
mc->prev = mc->entry;
mc->entry = menu_calc_entry(mc, x, y);
if (mc->prev == mc->entry)
return;
mc->entry = menu_calc_entry(sc, mc, e->xbutton.x, e->xbutton.y);
if (mc->prev != -1)
menu_draw_entry(mc, resultq, mc->prev, 0);
XFillRectangle(X_Dpy, sc->menuwin, sc->gc, 0,
font_height(sc) * mc->prev, mc->width, font_height(sc));
if (mc->entry != -1) {
XChangeActivePointerGrab(X_Dpy, MENUGRABMASK,
Conf.cursor[CF_NORMAL], CurrentTime);
menu_draw_entry(mc, resultq, mc->entry, 1);
}
(void)xu_ptr_regrab(MENUGRABMASK, Cursor_normal);
XFillRectangle(X_Dpy, sc->menuwin, sc->gc, 0,
font_height(sc) * mc->entry, mc->width, font_height(sc));
} else
(void)xu_ptr_regrab(MENUGRABMASK, Cursor_default);
}
static struct menu *
menu_handle_release(struct menu_ctx *mc, struct menu_q *resultq, int x, int y)
menu_handle_release(XEvent *e, struct menu_ctx *mc, struct screen_ctx *sc,
struct menu_q *resultq)
{
struct menu *mi;
int entry, i = 1;
struct menu *mi;
int entry, i = 0;
entry = menu_calc_entry(mc, x, y);
entry = menu_calc_entry(sc, mc, e->xbutton.x, e->xbutton.y);
if (mc->hasprompt)
i = 1;
TAILQ_FOREACH(mi, resultq, resultentry)
if (entry == i++)
@ -477,52 +437,45 @@ menu_handle_release(struct menu_ctx *mc, struct menu_q *resultq, int x, int y)
mi->text[0] = '\0';
mi->dummy = 1;
}
return mi;
return (mi);
}
static int
menu_calc_entry(struct menu_ctx *mc, int x, int y)
menu_calc_entry(struct screen_ctx *sc, struct menu_ctx *mc, int x, int y)
{
struct screen_ctx *sc = mc->sc;
int entry;
int entry;
entry = y / (sc->xftfont->ascent + sc->xftfont->descent);
entry = y / font_height(sc);
/* in bounds? */
if (x < 0 || x > mc->geom.w || y < 0 ||
y > (sc->xftfont->ascent + sc->xftfont->descent) * mc->num ||
entry < 0 || entry >= mc->num)
if (x <= 0 || x > mc->width || y <= 0 ||
y > font_height(sc) * mc->num || entry < 0 || entry >= mc->num)
entry = -1;
if (entry == 0)
if (mc->hasprompt && entry == 0)
entry = -1;
return entry;
return (entry);
}
static int
menu_keycode(XKeyEvent *ev, enum ctltype *ctl, char *chr)
menu_keycode(KeyCode kc, u_int state, enum ctltype *ctl, char *chr)
{
KeySym ks;
int ks;
*ctl = CTL_NONE;
chr[0] = '\0';
*chr = '\0';
ks = XkbKeycodeToKeysym(X_Dpy, ev->keycode, 0,
(ev->state & ShiftMask) ? 1 : 0);
ks = XKeycodeToKeysym(X_Dpy, kc, (state & ShiftMask) ? 1 : 0);
/* Look for control characters. */
switch (ks) {
case XK_BackSpace:
*ctl = CTL_ERASEONE;
break;
case XK_KP_Enter:
case XK_Return:
*ctl = CTL_RETURN;
break;
case XK_Tab:
*ctl = CTL_TAB;
break;
case XK_Up:
*ctl = CTL_UP;
break;
@ -534,7 +487,7 @@ menu_keycode(XKeyEvent *ev, enum ctltype *ctl, char *chr)
break;
}
if (*ctl == CTL_NONE && (ev->state & ControlMask)) {
if (*ctl == CTL_NONE && (state & ControlMask)) {
switch (ks) {
case XK_s:
case XK_S:
@ -558,13 +511,10 @@ menu_keycode(XKeyEvent *ev, enum ctltype *ctl, char *chr)
case XK_A:
*ctl = CTL_ALL;
break;
case XK_bracketleft:
*ctl = CTL_ABORT;
break;
}
}
if (*ctl == CTL_NONE && (ev->state & Mod1Mask)) {
if (*ctl == CTL_NONE && (state & Mod1Mask)) {
switch (ks) {
case XK_j:
case XK_J:
@ -580,40 +530,16 @@ menu_keycode(XKeyEvent *ev, enum ctltype *ctl, char *chr)
}
if (*ctl != CTL_NONE)
return 0;
return (0);
if (XLookupString(ev, chr, 32, &ks, NULL) < 0)
return -1;
/*
* For regular characters, only (part of, actually) Latin 1
* for now.
*/
if (ks < 0x20 || ks > 0x07e)
return (-1);
return 0;
}
void
menuq_add(struct menu_q *mq, void *ctx, const char *fmt, ...)
{
va_list ap;
struct menu *mi;
mi = xcalloc(1, sizeof(*mi));
mi->ctx = ctx;
va_start(ap, fmt);
if (fmt != NULL)
(void)vsnprintf(mi->text, sizeof(mi->text), fmt, ap);
else
mi->text[0] = '\0';
va_end(ap);
TAILQ_INSERT_TAIL(mq, mi, entry);
}
void
menuq_clear(struct menu_q *mq)
{
struct menu *mi;
while ((mi = TAILQ_FIRST(mq)) != NULL) {
TAILQ_REMOVE(mq, mi, entry);
free(mi);
}
*chr = (char)ks;
return (0);
}

289
mousefunc.c Normal file
View File

@ -0,0 +1,289 @@
/*
* calmwm - the calm window manager
*
* Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
* Copyright (c) 2008 rivo nurges <rix@estpak.ee>
*
* 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 <sys/param.h>
#include <sys/queue.h>
#include <err.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
static int mousefunc_sweep_calc(struct client_ctx *, int, int, int, int);
static void mousefunc_sweep_draw(struct client_ctx *);
static int
mousefunc_sweep_calc(struct client_ctx *cc, int x, int y, int mx, int my)
{
int width = cc->geom.width, height = cc->geom.height;
cc->geom.width = abs(x - mx) - cc->bwidth;
cc->geom.height = abs(y - my) - cc->bwidth;
client_applysizehints(cc);
cc->geom.x = x <= mx ? x : x - cc->geom.width;
cc->geom.y = y <= my ? y : y - cc->geom.height;
return (width != cc->geom.width || height != cc->geom.height);
}
static void
mousefunc_sweep_draw(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
char asize[10]; /* fits "nnnnxnnnn\0" */
int width, width_size, width_name;
(void)snprintf(asize, sizeof(asize), "%dx%d",
(cc->geom.width - cc->hint.basew) / cc->hint.incw,
(cc->geom.height - cc->hint.baseh) / cc->hint.inch);
width_size = font_width(sc, asize, strlen(asize)) + 4;
width_name = font_width(sc, cc->name, strlen(cc->name)) + 4;
width = MAX(width_size, width_name);
XReparentWindow(X_Dpy, sc->menuwin, cc->win, 0, 0);
XMoveResizeWindow(X_Dpy, sc->menuwin, 0, 0, width, font_height(sc) * 2);
XMapWindow(X_Dpy, sc->menuwin);
XClearWindow(X_Dpy, sc->menuwin);
font_draw(sc, cc->name, strlen(cc->name), sc->menuwin,
2, font_ascent(sc) + 1);
font_draw(sc, asize, strlen(asize), sc->menuwin,
width / 2 - width_size / 2, font_height(sc) + font_ascent(sc) + 1);
}
void
mousefunc_window_resize(struct client_ctx *cc, void *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)
return;
client_raise(cc);
client_ptrsave(cc);
if (xu_ptr_grab(cc->win, MOUSEMASK, Cursor_resize) < 0)
return;
xu_ptr_setpos(cc->win, cc->geom.width, cc->geom.height);
mousefunc_sweep_draw(cc);
for (;;) {
XMaskEvent(X_Dpy, MOUSEMASK|ExposureMask, &ev);
switch (ev.type) {
case Expose:
client_draw_border(cc);
break;
case MotionNotify:
if (mousefunc_sweep_calc(cc, x, y,
ev.xmotion.x_root, ev.xmotion.y_root))
/* Recompute window output */
mousefunc_sweep_draw(cc);
/* don't resize more than 60 times / second */
if ((ev.xmotion.time - ltime) > (1000 / 60)) {
ltime = ev.xmotion.time;
client_resize(cc);
}
break;
case ButtonRelease:
if (ltime)
client_resize(cc);
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.width)
cc->ptr.x = cc->geom.width - cc->bwidth;
if (cc->ptr.y > cc->geom.height)
cc->ptr.y = cc->geom.height - cc->bwidth;
client_ptrwarp(cc);
return;
}
}
/* NOTREACHED */
}
void
mousefunc_window_move(struct client_ctx *cc, void *arg)
{
XEvent ev;
Time ltime = 0;
int px, py;
client_raise(cc);
if (cc->flags & CLIENT_FREEZE)
return;
if (xu_ptr_grab(cc->win, MOUSEMASK, Cursor_move) < 0)
return;
xu_ptr_getpos(cc->win, &px, &py);
for (;;) {
XMaskEvent(X_Dpy, MOUSEMASK|ExposureMask, &ev);
switch (ev.type) {
case Expose:
client_draw_border(cc);
break;
case MotionNotify:
cc->geom.x = ev.xmotion.x_root - px - cc->bwidth;
cc->geom.y = ev.xmotion.y_root - py - cc->bwidth;
cc->geom.x += client_snapcalc(cc->geom.x,
cc->geom.width, cc->sc->xmax,
cc->bwidth, Conf.snapdist);
cc->geom.y += client_snapcalc(cc->geom.y,
cc->geom.height, cc->sc->ymax,
cc->bwidth, Conf.snapdist);
/* don't move more than 60 times / second */
if ((ev.xmotion.time - ltime) > (1000 / 60)) {
ltime = ev.xmotion.time;
client_move(cc);
}
break;
case ButtonRelease:
if (ltime)
client_move(cc);
xu_ptr_ungrab();
return;
}
}
/* NOTREACHED */
}
void
mousefunc_window_grouptoggle(struct client_ctx *cc, void *arg)
{
group_sticky_toggle_enter(cc);
}
void
mousefunc_window_lower(struct client_ctx *cc, void *arg)
{
client_ptrsave(cc);
client_lower(cc);
}
void
mousefunc_window_raise(struct client_ctx *cc, void *arg)
{
client_raise(cc);
}
void
mousefunc_window_hide(struct client_ctx *cc, void *arg)
{
client_hide(cc);
}
void
mousefunc_menu_group(struct client_ctx *cc, void *arg)
{
group_menu(arg);
}
void
mousefunc_menu_unhide(struct client_ctx *cc, void *arg)
{
struct screen_ctx *sc;
struct client_ctx *old_cc;
struct menu *mi;
struct menu_q menuq;
char *wname;
sc = cc->sc;
old_cc = client_current();
TAILQ_INIT(&menuq);
TAILQ_FOREACH(cc, &Clientq, entry)
if (cc->flags & CLIENT_HIDDEN) {
wname = (cc->label) ? cc->label : cc->name;
if (wname == NULL)
continue;
mi = xcalloc(1, sizeof(*mi));
(void)strlcpy(mi->text, wname, sizeof(mi->text));
mi->ctx = cc;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
if (TAILQ_EMPTY(&menuq))
return;
mi = menu_filter(sc, &menuq, NULL, NULL, 0, NULL, NULL);
if (mi != NULL) {
cc = (struct client_ctx *)mi->ctx;
client_unhide(cc);
if (old_cc != NULL)
client_ptrsave(old_cc);
client_ptrwarp(cc);
} else {
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
}
}
void
mousefunc_menu_cmd(struct client_ctx *cc, void *arg)
{
struct screen_ctx *sc;
struct menu *mi;
struct menu_q menuq;
struct cmd *cmd;
sc = cc->sc;
TAILQ_INIT(&menuq);
TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
mi = xcalloc(1, sizeof(*mi));
(void)strlcpy(mi->text, cmd->label, sizeof(mi->text));
mi->ctx = cmd;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
if (TAILQ_EMPTY(&menuq))
return;
mi = menu_filter(sc, &menuq, NULL, NULL, 0, NULL, NULL);
if (mi != NULL)
u_spawn(((struct cmd *)mi->ctx)->image);
else
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
}

363
parse.y
View File

@ -21,7 +21,7 @@
%{
#include <sys/types.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <ctype.h>
@ -42,19 +42,18 @@ static struct file {
char *name;
int lineno;
int errors;
} *file, *topfile;
struct file *pushfile(const char *, FILE *);
int popfile(void);
int yyparse(void);
int yylex(void);
int yyerror(const char *, ...)
__attribute__((__format__ (printf, 1, 2)))
__attribute__((__nonnull__ (1)));
int kw_cmp(const void *, const void *);
int lookup(char *);
int lgetc(int);
int lungetc(int);
int findeol(void);
} *file;
struct file *pushfile(const char *);
int popfile(void);
int yyparse(void);
int yylex(void);
int yyerror(const char *, ...);
int kw_cmp(const void *, const void *);
int lookup(char *);
int lgetc(int);
int lungetc(int);
int findeol(void);
static struct conf *conf;
@ -68,20 +67,18 @@ typedef struct {
%}
%token BINDKEY UNBINDKEY BINDMOUSE UNBINDMOUSE
%token FONTNAME STICKY GAP
%token AUTOGROUP COMMAND IGNORE WM
%token YES NO BORDERWIDTH MOVEAMOUNT HTILE VTILE
%token FONTNAME STICKY GAP MOUSEBIND
%token AUTOGROUP BIND COMMAND IGNORE
%token YES NO BORDERWIDTH MOVEAMOUNT
%token COLOR SNAPDIST
%token ACTIVEBORDER INACTIVEBORDER URGENCYBORDER
%token ACTIVEBORDER INACTIVEBORDER
%token GROUPBORDER UNGROUPBORDER
%token MENUBG MENUFG
%token FONTCOLOR FONTSELCOLOR
%token MENUBG MENUFG FONTCOLOR
%token ERROR
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.number> yesno
%type <v.string> string numberstring
%type <v.string> string
%%
grammar : /* empty */
@ -104,17 +101,6 @@ string : string STRING {
| STRING
;
numberstring : NUMBER {
char *s;
if (asprintf(&s, "%lld", $1) == -1) {
yyerror("string: asprintf");
YYERROR;
}
$$ = s;
}
| STRING
;
yesno : YES { $$ = 1; }
| NO { $$ = 0; }
;
@ -124,178 +110,92 @@ main : FONTNAME STRING {
conf->font = $2;
}
| STICKY yesno {
conf->stickygroups = $2;
if ($2 == 0)
conf->flags &= ~CONF_STICKY_GROUPS;
else
conf->flags |= CONF_STICKY_GROUPS;
}
| BORDERWIDTH NUMBER {
if ($2 < 0 || $2 > INT_MAX) {
yyerror("invalid borderwidth");
YYERROR;
}
conf->bwidth = $2;
}
| HTILE NUMBER {
if ($2 < 0 || $2 > 99) {
yyerror("invalid htile percent");
YYERROR;
}
conf->htile = $2;
}
| VTILE NUMBER {
if ($2 < 0 || $2 > 99) {
yyerror("invalid vtile percent");
YYERROR;
}
conf->vtile = $2;
}
| MOVEAMOUNT NUMBER {
if ($2 < 0 || $2 > INT_MAX) {
yyerror("invalid movemount");
YYERROR;
}
conf->mamount = $2;
}
| SNAPDIST NUMBER {
if ($2 < 0 || $2 > INT_MAX) {
yyerror("invalid snapdist");
YYERROR;
}
conf->snapdist = $2;
}
| COMMAND STRING string {
if (strlen($3) >= PATH_MAX) {
yyerror("%s command path too long", $2);
free($2);
free($3);
YYERROR;
}
conf_cmd_add(conf, $2, $3);
free($2);
free($3);
}
| WM STRING string {
if (strlen($3) >= PATH_MAX) {
yyerror("%s wm path too long", $2);
free($2);
free($3);
YYERROR;
}
conf_wm_add(conf, $2, $3);
conf_cmd_add(conf, $3, $2, 0);
free($2);
free($3);
}
| AUTOGROUP NUMBER STRING {
if ($2 < 0 || $2 > 9) {
yyerror("invalid autogroup");
free($3);
yyerror("autogroup number out of range: %d", $2);
YYERROR;
}
conf_autogroup(conf, $2, NULL, $3);
group_make_autogroup(conf, $3, $2);
free($3);
}
| AUTOGROUP NUMBER STRING ',' STRING {
if ($2 < 0 || $2 > 9) {
yyerror("invalid autogroup");
free($3);
free($5);
YYERROR;
}
conf_autogroup(conf, $2, $3, $5);
free($3);
free($5);
}
| IGNORE STRING {
conf_ignore(conf, $2);
struct winmatch *wm;
wm = xcalloc(1, sizeof(*wm));
(void)strlcpy(wm->title, $2, sizeof(wm->title));
TAILQ_INSERT_TAIL(&conf->ignoreq, wm, entry);
free($2);
}
| BIND STRING string {
conf_bindname(conf, $2, $3);
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");
YYERROR;
}
conf->gap.top = $2;
conf->gap.bottom = $3;
conf->gap.left = $4;
conf->gap.right = $5;
}
| BINDKEY numberstring string {
if (!conf_bind_key(conf, $2, $3)) {
yyerror("invalid bind-key: %s %s", $2, $3);
free($2);
free($3);
YYERROR;
}
| MOUSEBIND STRING string {
conf_mousebind(conf, $2, $3);
free($2);
free($3);
}
| UNBINDKEY numberstring {
if (!conf_bind_key(conf, $2, NULL)) {
yyerror("invalid unbind-key: %s", $2);
free($2);
YYERROR;
}
free($2);
}
| BINDMOUSE numberstring string {
if (!conf_bind_mouse(conf, $2, $3)) {
yyerror("invalid bind-mouse: %s %s", $2, $3);
free($2);
free($3);
YYERROR;
}
free($2);
free($3);
}
| UNBINDMOUSE numberstring {
if (!conf_bind_mouse(conf, $2, NULL)) {
yyerror("invalid unbind-mouse: %s", $2);
free($2);
YYERROR;
}
free($2);
}
;
color : COLOR colors
;
colors : ACTIVEBORDER STRING {
free(conf->color[CWM_COLOR_BORDER_ACTIVE]);
conf->color[CWM_COLOR_BORDER_ACTIVE] = $2;
free(conf->color[CWM_COLOR_BORDER_ACTIVE].name);
conf->color[CWM_COLOR_BORDER_ACTIVE].name = $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;
free(conf->color[CWM_COLOR_BORDER_INACTIVE].name);
conf->color[CWM_COLOR_BORDER_INACTIVE].name = $2;
}
| GROUPBORDER STRING {
free(conf->color[CWM_COLOR_BORDER_GROUP]);
conf->color[CWM_COLOR_BORDER_GROUP] = $2;
free(conf->color[CWM_COLOR_BORDER_GROUP].name);
conf->color[CWM_COLOR_BORDER_GROUP].name = $2;
}
| UNGROUPBORDER STRING {
free(conf->color[CWM_COLOR_BORDER_UNGROUP]);
conf->color[CWM_COLOR_BORDER_UNGROUP] = $2;
free(conf->color[CWM_COLOR_BORDER_UNGROUP].name);
conf->color[CWM_COLOR_BORDER_UNGROUP].name = $2;
}
| MENUBG STRING {
free(conf->color[CWM_COLOR_MENU_BG]);
conf->color[CWM_COLOR_MENU_BG] = $2;
free(conf->color[CWM_COLOR_BG_MENU].name);
conf->color[CWM_COLOR_BG_MENU].name = $2;
}
| MENUFG STRING {
free(conf->color[CWM_COLOR_MENU_FG]);
conf->color[CWM_COLOR_MENU_FG] = $2;
free(conf->color[CWM_COLOR_FG_MENU].name);
conf->color[CWM_COLOR_FG_MENU].name = $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;
free(conf->color[CWM_COLOR_FONT].name);
conf->color[CWM_COLOR_FONT].name = $2;
}
;
%%
@ -308,7 +208,7 @@ struct keywords {
int
yyerror(const char *fmt, ...)
{
va_list ap;
va_list ap;
file->errors++;
va_start(ap, fmt);
@ -332,8 +232,7 @@ lookup(char *s)
static const struct keywords keywords[] = {
{ "activeborder", ACTIVEBORDER},
{ "autogroup", AUTOGROUP},
{ "bind-key", BINDKEY},
{ "bind-mouse", BINDMOUSE},
{ "bind", BIND},
{ "borderwidth", BORDERWIDTH},
{ "color", COLOR},
{ "command", COMMAND},
@ -341,22 +240,16 @@ lookup(char *s)
{ "fontname", FONTNAME},
{ "gap", GAP},
{ "groupborder", GROUPBORDER},
{ "htile", HTILE},
{ "ignore", IGNORE},
{ "inactiveborder", INACTIVEBORDER},
{ "menubg", MENUBG},
{ "menufg", MENUFG},
{ "mousebind", MOUSEBIND},
{ "moveamount", MOVEAMOUNT},
{ "no", NO},
{ "selfont", FONTSELCOLOR},
{ "snapdist", SNAPDIST},
{ "sticky", STICKY},
{ "unbind-key", UNBINDKEY},
{ "unbind-mouse", UNBINDMOUSE},
{ "ungroupborder", UNGROUPBORDER},
{ "urgencyborder", URGENCYBORDER},
{ "vtile", VTILE},
{ "wm", WM},
{ "yes", YES}
};
const struct keywords *p;
@ -385,7 +278,7 @@ lgetc(int quotec)
if (parsebuf) {
/* Read character from the parsebuffer instead of input. */
if (parseindex >= 0) {
c = (unsigned char)parsebuf[parseindex++];
c = parsebuf[parseindex++];
if (c != '\0')
return (c);
parsebuf = NULL;
@ -394,13 +287,12 @@ lgetc(int quotec)
}
if (pushback_index)
return ((unsigned char)pushback_buffer[--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)
yyerror("reached end of file while parsing quoted string");
if (popfile() == EOF)
return (EOF);
return (quotec);
}
@ -418,7 +310,7 @@ lgetc(int quotec)
}
while (c == EOF) {
if (file == topfile || popfile() == EOF)
if (popfile() == EOF)
return (EOF);
c = getc(file->stream);
}
@ -435,10 +327,10 @@ lungetc(int c)
if (parseindex >= 0)
return (c);
}
if (pushback_index + 1 >= MAXPUSHBACK)
if (pushback_index < MAXPUSHBACK-1)
return (pushback_buffer[pushback_index++] = c);
else
return (EOF);
pushback_buffer[pushback_index++] = c;
return (c);
}
int
@ -447,13 +339,11 @@ findeol(void)
int c;
parsebuf = NULL;
pushback_index = 0;
/* skip to either EOF or the first real EOL */
while (1) {
if (pushback_index)
c = (unsigned char)pushback_buffer[--pushback_index];
else
c = lgetc(0);
c = lgetc(0);
if (c == '\n') {
file->lineno++;
break;
@ -494,8 +384,7 @@ yylex(void)
} else if (c == '\\') {
if ((next = lgetc(quotec)) == EOF)
return (0);
if (next == quotec || next == ' ' ||
next == '\t')
if (next == quotec || c == ' ' || c == '\t')
c = next;
else if (next == '\n') {
file->lineno++;
@ -505,15 +394,12 @@ yylex(void)
} 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++ = c;
*p++ = (char)c;
}
yylval.v.string = xstrdup(buf);
return (STRING);
@ -525,7 +411,7 @@ yylex(void)
if (c == '-' || isdigit(c)) {
do {
*p++ = c;
if ((size_t)(p-buf) >= sizeof(buf)) {
if ((unsigned)(p-buf) >= sizeof(buf)) {
yyerror("string too long");
return (findeol());
}
@ -548,14 +434,13 @@ yylex(void)
} else {
nodigits:
while (p > buf + 1)
lungetc((unsigned char)*--p);
c = (unsigned char)*--p;
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 != '>' && \
@ -564,7 +449,7 @@ nodigits:
if (isalnum(c) || c == ':' || c == '_' || c == '*' || c == '/') {
do {
*p++ = c;
if ((size_t)(p-buf) >= sizeof(buf)) {
if ((unsigned)(p-buf) >= sizeof(buf)) {
yyerror("string too long");
return (findeol());
}
@ -585,13 +470,18 @@ nodigits:
}
struct file *
pushfile(const char *name, FILE *stream)
pushfile(const char *name)
{
struct file *nfile;
nfile = xcalloc(1, sizeof(struct file));
nfile->name = xstrdup(name);
nfile->stream = stream;
if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
free(nfile->name);
free(nfile);
return (NULL);
}
nfile->lineno = 1;
TAILQ_INSERT_TAIL(&files, nfile, entry);
return (nfile);
@ -602,38 +492,95 @@ popfile(void)
{
struct file *prev;
if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
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);
TAILQ_REMOVE(&files, file, entry);
fclose(file->stream);
free(file->name);
free(file);
file = prev;
return (0);
}
return (EOF);
}
int
parse_config(const char *filename, struct conf *xconf)
{
FILE *stream;
int errors = 0;
int errors = 0;
conf = xconf;
conf = xcalloc(1, sizeof(*conf));
stream = fopen(filename, "r");
if (stream == NULL) {
if (errno == ENOENT)
return (0);
warn("%s", filename);
if ((file = pushfile(filename)) == NULL) {
free(conf);
return (-1);
}
file = pushfile(filename, stream);
topfile = file;
(void)strlcpy(conf->conf_path, filename, sizeof(conf->conf_path));
conf_init(conf);
yyparse();
errors = file->errors;
file->errors = 0;
popfile();
if (errors) {
conf_clear(conf);
}
else {
struct autogroupwin *ag;
struct keybinding *kb;
struct winmatch *wm;
struct cmd *cmd;
struct mousebinding *mb;
int i;
conf_clear(xconf);
xconf->flags = conf->flags;
xconf->bwidth = conf->bwidth;
xconf->mamount = conf->mamount;
xconf->snapdist = conf->snapdist;
xconf->gap = conf->gap;
while ((cmd = TAILQ_FIRST(&conf->cmdq)) != NULL) {
TAILQ_REMOVE(&conf->cmdq, cmd, entry);
TAILQ_INSERT_TAIL(&xconf->cmdq, cmd, entry);
}
while ((kb = TAILQ_FIRST(&conf->keybindingq)) != NULL) {
TAILQ_REMOVE(&conf->keybindingq, kb, entry);
TAILQ_INSERT_TAIL(&xconf->keybindingq, kb, entry);
}
while ((ag = TAILQ_FIRST(&conf->autogroupq)) != NULL) {
TAILQ_REMOVE(&conf->autogroupq, ag, entry);
TAILQ_INSERT_TAIL(&xconf->autogroupq, ag, entry);
}
while ((wm = TAILQ_FIRST(&conf->ignoreq)) != NULL) {
TAILQ_REMOVE(&conf->ignoreq, wm, entry);
TAILQ_INSERT_TAIL(&xconf->ignoreq, wm, entry);
}
while ((mb = TAILQ_FIRST(&conf->mousebindingq)) != NULL) {
TAILQ_REMOVE(&conf->mousebindingq, mb, entry);
TAILQ_INSERT_TAIL(&xconf->mousebindingq, mb, entry);
}
(void)strlcpy(xconf->termpath, conf->termpath,
sizeof(xconf->termpath));
(void)strlcpy(xconf->lockpath, conf->lockpath,
sizeof(xconf->lockpath));
for (i = 0; i < CWM_COLOR_MAX; i++)
xconf->color[i].name = conf->color[i].name;
xconf->font = conf->font;
}
free(conf);
return (errors ? -1 : 0);
}

330
screen.c
View File

@ -18,106 +18,29 @@
* $OpenBSD$
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
static struct geom screen_apply_gap(struct screen_ctx *, struct geom);
static void screen_scan(struct screen_ctx *);
void
screen_init(int which)
{
struct screen_ctx *sc;
XSetWindowAttributes attr;
sc = xmalloc(sizeof(*sc));
TAILQ_INIT(&sc->clientq);
TAILQ_INIT(&sc->regionq);
TAILQ_INIT(&sc->groupq);
sc->which = which;
sc->rootwin = RootWindow(X_Dpy, sc->which);
sc->colormap = DefaultColormap(X_Dpy, sc->which);
sc->visual = DefaultVisual(X_Dpy, sc->which);
sc->cycling = 0;
sc->hideall = 0;
conf_screen(sc);
xu_ewmh_net_supported(sc);
xu_ewmh_net_supported_wm_check(sc);
conf_group(sc);
sc->group_last = sc->group_active;
screen_update_geometry(sc);
xu_ewmh_net_desktop_names(sc);
xu_ewmh_net_number_of_desktops(sc);
xu_ewmh_net_showing_desktop(sc);
xu_ewmh_net_virtual_roots(sc);
attr.cursor = Conf.cursor[CF_NORMAL];
attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
EnterWindowMask | PropertyChangeMask | ButtonPressMask;
XChangeWindowAttributes(X_Dpy, sc->rootwin, (CWEventMask | CWCursor), &attr);
if (Conf.xrandr)
XRRSelectInput(X_Dpy, sc->rootwin, RRScreenChangeNotifyMask);
screen_scan(sc);
screen_updatestackingorder(sc);
TAILQ_INSERT_TAIL(&Screenq, sc, entry);
XSync(X_Dpy, False);
}
static void
screen_scan(struct screen_ctx *sc)
{
struct client_ctx *cc, *active = NULL;
Window *wins, w0, w1, rwin, cwin;
unsigned int nwins, i, mask;
int rx, ry, wx, wy;
XQueryPointer(X_Dpy, sc->rootwin, &rwin, &cwin,
&rx, &ry, &wx, &wy, &mask);
if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) {
for (i = 0; i < nwins; i++) {
if ((cc = client_init(wins[i], sc)) != NULL)
if (cc->win == cwin)
active = cc;
}
XFree(wins);
}
if (active)
client_set_active(active);
}
struct screen_ctx *
screen_find(Window win)
screen_fromroot(Window rootwin)
{
struct screen_ctx *sc;
TAILQ_FOREACH(sc, &Screenq, entry) {
if (sc->rootwin == win)
return sc;
}
warnx("%s: failure win 0x%lx", __func__, win);
return NULL;
TAILQ_FOREACH(sc, &Screenq, entry)
if (sc->rootwin == rootwin)
return (sc);
/* XXX FAIL HERE */
return (TAILQ_FIRST(&Screenq));
}
void
@ -125,183 +48,86 @@ screen_updatestackingorder(struct screen_ctx *sc)
{
Window *wins, w0, w1;
struct client_ctx *cc;
unsigned int nwins, i, s;
u_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;
if (!XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins))
return;
cc->stackingorder = s++;
}
XFree(wins);
}
}
struct region_ctx *
region_find(struct screen_ctx *sc, int x, int y)
{
struct region_ctx *rc;
TAILQ_FOREACH(rc, &sc->regionq, entry) {
if ((x >= rc->view.x) && (x < (rc->view.x + rc->view.w)) &&
(y >= rc->view.y) && (y < (rc->view.y + rc->view.h))) {
break;
}
}
return rc;
}
struct geom
screen_area(struct screen_ctx *sc, int x, int y, int apply_gap)
{
struct region_ctx *rc;
struct geom area = sc->view;
TAILQ_FOREACH(rc, &sc->regionq, entry) {
if ((x >= rc->view.x) && (x < (rc->view.x + rc->view.w)) &&
(y >= rc->view.y) && (y < (rc->view.y + rc->view.h))) {
area = rc->view;
break;
}
}
if (apply_gap)
area = screen_apply_gap(sc, area);
return area;
}
void
screen_update_geometry(struct screen_ctx *sc)
{
struct region_ctx *rc;
sc->view.x = 0;
sc->view.y = 0;
sc->view.w = DisplayWidth(X_Dpy, sc->which);
sc->view.h = DisplayHeight(X_Dpy, sc->which);
sc->work = screen_apply_gap(sc, sc->view);
while ((rc = TAILQ_FIRST(&sc->regionq)) != NULL) {
TAILQ_REMOVE(&sc->regionq, rc, entry);
free(rc);
}
if (Conf.xrandr) {
XRRScreenResources *sr;
XRRCrtcInfo *ci;
int i;
sr = XRRGetScreenResources(X_Dpy, sc->rootwin);
for (i = 0, ci = NULL; i < sr->ncrtc; i++) {
ci = XRRGetCrtcInfo(X_Dpy, sr, sr->crtcs[i]);
if (ci == NULL)
continue;
if (ci->noutput == 0) {
XRRFreeCrtcInfo(ci);
continue;
}
rc = xmalloc(sizeof(*rc));
rc->num = i;
rc->view.x = ci->x;
rc->view.y = ci->y;
rc->view.w = ci->width;
rc->view.h = ci->height;
rc->work = screen_apply_gap(sc, rc->view);
TAILQ_INSERT_TAIL(&sc->regionq, rc, entry);
XRRFreeCrtcInfo(ci);
}
XRRFreeScreenResources(sr);
} else {
rc = xmalloc(sizeof(*rc));
rc->num = 0;
rc->view.x = 0;
rc->view.y = 0;
rc->view.w = DisplayWidth(X_Dpy, sc->which);
rc->view.h = DisplayHeight(X_Dpy, sc->which);
rc->work = screen_apply_gap(sc, rc->view);
TAILQ_INSERT_TAIL(&sc->regionq, rc, entry);
}
xu_ewmh_net_desktop_geometry(sc);
xu_ewmh_net_desktop_viewport(sc);
xu_ewmh_net_workarea(sc);
}
static struct geom
screen_apply_gap(struct screen_ctx *sc, struct geom geom)
{
geom.x += sc->gap.left;
geom.y += sc->gap.top;
geom.w -= (sc->gap.left + sc->gap.right);
geom.h -= (sc->gap.top + sc->gap.bottom);
return geom;
}
/* Bring back clients which are beyond the screen. */
void
screen_assert_clients_within(struct screen_ctx *sc)
{
struct client_ctx *cc;
int top, left, right, bottom;
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (cc->sc != sc)
for (s = 0, i = 0; i < nwins; i++) {
/* Skip hidden windows */
if ((cc = client_find(wins[i])) == NULL ||
cc->flags & CLIENT_HIDDEN)
continue;
top = cc->geom.y;
left = cc->geom.x;
right = cc->geom.x + cc->geom.w + (cc->bwidth * 2) - 1;
bottom = cc->geom.y + cc->geom.h + (cc->bwidth * 2) - 1;
if ((top > sc->view.h || left > sc->view.w) ||
(bottom < 0 || right < 0)) {
cc->geom.x = sc->gap.left;
cc->geom.y = sc->gap.top;
client_move(cc);
}
cc->stackingorder = s++;
}
XFree(wins);
}
void
screen_prop_win_create(struct screen_ctx *sc, Window win)
screen_init_xinerama(struct screen_ctx *sc)
{
sc->prop.win = XCreateSimpleWindow(X_Dpy, win, 0, 0, 1, 1, 0,
sc->xftcolor[CWM_COLOR_MENU_BG].pixel,
sc->xftcolor[CWM_COLOR_MENU_BG].pixel);
sc->prop.xftdraw = XftDrawCreate(X_Dpy, sc->prop.win,
sc->visual, sc->colormap);
XineramaScreenInfo *info;
int no;
XMapWindow(X_Dpy, sc->prop.win);
if (HasXinerama == 0 || XineramaIsActive(X_Dpy) == 0) {
HasXinerama = 0;
sc->xinerama_no = 0;
}
info = XineramaQueryScreens(X_Dpy, &no);
if (info == NULL) {
/* Is xinerama actually off, instead of a malloc failure? */
if (sc->xinerama == NULL)
HasXinerama = 0;
return;
}
if (sc->xinerama != NULL)
XFree(sc->xinerama);
sc->xinerama = info;
sc->xinerama_no = no;
}
/*
* Find which xinerama screen the coordinates (x,y) is on.
*/
XineramaScreenInfo *
screen_find_xinerama(struct screen_ctx *sc, int x, int y)
{
XineramaScreenInfo *info;
int i;
for (i = 0; i < sc->xinerama_no; i++) {
info = &sc->xinerama[i];
if (x >= info->x_org && x < info->x_org + info->width &&
y >= info->y_org && y < info->y_org + info->height)
return (info);
}
return (NULL);
}
void
screen_prop_win_destroy(struct screen_ctx *sc)
screen_update_geometry(struct screen_ctx *sc, int width, int height)
{
XftDrawDestroy(sc->prop.xftdraw);
XDestroyWindow(X_Dpy, sc->prop.win);
}
void
screen_prop_win_draw(struct screen_ctx *sc, const char *fmt, ...)
{
va_list ap;
char *text;
XGlyphInfo extents;
va_start(ap, fmt);
xvasprintf(&text, fmt, ap);
va_end(ap);
XftTextExtentsUtf8(X_Dpy, sc->xftfont, (const FcChar8*)text,
strlen(text), &extents);
XResizeWindow(X_Dpy, sc->prop.win, extents.xOff, sc->xftfont->height);
XClearWindow(X_Dpy, sc->prop.win);
XftDrawStringUtf8(sc->prop.xftdraw, &sc->xftcolor[CWM_COLOR_MENU_FONT],
sc->xftfont, 0, sc->xftfont->ascent + 1,
(const FcChar8*)text, strlen(text));
free(text);
long geom[2], workareas[CALMWM_NGROUPS][4];
int i;
sc->xmax = geom[0] = width;
sc->ymax = geom[1] = height;
XChangeProperty(X_Dpy, sc->rootwin, _NET_DESKTOP_GEOMETRY,
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)geom , 2);
/* x, y, width, height. */
for (i = 0; i < CALMWM_NGROUPS; i++) {
workareas[i][0] = sc->gap.left;
workareas[i][1] = sc->gap.top;
workareas[i][2] = width - (sc->gap.left + sc->gap.right);
workareas[i][3] = height - (sc->gap.top + sc->gap.bottom);
}
XChangeProperty(X_Dpy, sc->rootwin, _NET_WORKAREA,
XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)workareas, CALMWM_NGROUPS * 4);
}

291
search.c
View File

@ -18,96 +18,87 @@
* $OpenBSD$
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fnmatch.h>
#include <glob.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
#define PATH_ANY 0x0001
#define PATH_EXEC 0x0002
static int strsubmatch(char *, char *, int);
static void match_path_type(struct menu_q *, char *, int);
static int match_substr(char *, char *, int);
static int
match_substr(char *sub, char *str, int zeroidx)
{
size_t len, sublen;
unsigned int n, flen;
if (sub == NULL || str == NULL)
return 0;
len = strlen(str);
sublen = strlen(sub);
if (sublen > len)
return 0;
if (zeroidx)
flen = 0;
else
flen = len - sublen;
for (n = 0; n <= flen; n++)
if (strncasecmp(sub, str + n, sublen) == 0)
return 1;
return 0;
}
/*
* Match: label, title, class.
*/
void
search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct menu *mi, *tierp[3], *before = NULL;
struct client_ctx *cc;
struct winname *wn;
struct winname *wn;
struct menu *mi, *tierp[4], *before = NULL;
TAILQ_INIT(resultq);
(void)memset(tierp, 0, sizeof(tierp));
TAILQ_INIT(resultq);
/*
* 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;
cc = (struct client_ctx *)mi->ctx;
struct client_ctx *cc = mi->ctx;
/* Match on label. */
if (match_substr(search, cc->label, 0))
/* First, try to match on labels. */
if (cc->label != NULL && strsubmatch(search, cc->label, 0)) {
cc->matchname = cc->label;
tier = 0;
}
/* Match on window name history, from present to past. */
/* Then, on window names. */
if (tier < 0) {
TAILQ_FOREACH_REVERSE(wn, &cc->nameq, name_q, entry)
if (match_substr(search, wn->name, 0)) {
tier = 1;
TAILQ_FOREACH_REVERSE(wn, &cc->nameq, winname_q, entry)
if (strsubmatch(search, wn->name, 0)) {
cc->matchname = wn->name;
tier = 2;
break;
}
}
/* Match on window resource class. */
if ((tier < 0) && match_substr(search, cc->res_class, 0))
tier = 2;
/* Then if there is a match on the window class name. */
if (tier < 0 && strsubmatch(search, cc->app_class, 0)) {
cc->matchname = cc->app_class;
tier = 3;
}
if (tier < 0)
continue;
/* Current window is ranked down. */
if ((tier < nitems(tierp) - 1) && (cc->flags & CLIENT_ACTIVE))
/*
* 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++;
/* Hidden window is ranked up. */
if ((tier > 0) && (cc->flags & CLIENT_HIDDEN))
/* 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
@ -128,88 +119,46 @@ search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
}
void
search_match_cmd(struct menu_q *menuq, struct menu_q *resultq, char *search)
search_print_client(struct menu *mi, int list)
{
struct menu *mi;
struct cmd_ctx *cmd;
struct client_ctx *cc;
char flag = ' ';
TAILQ_INIT(resultq);
TAILQ_FOREACH(mi, menuq, entry) {
cmd = (struct cmd_ctx *)mi->ctx;
if (match_substr(search, cmd->name, 0))
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
}
}
cc = mi->ctx;
void
search_match_group(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct menu *mi;
struct group_ctx *gc;
char *s;
if (cc == client_current())
flag = '!';
else if (cc->flags & CLIENT_HIDDEN)
flag = '&';
TAILQ_INIT(resultq);
TAILQ_FOREACH(mi, menuq, entry) {
gc = (struct group_ctx *)mi->ctx;
xasprintf(&s, "%d %s", gc->num, gc->name);
if (match_substr(search, s, 0))
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
free(s);
}
}
if (list)
cc->matchname = cc->name;
static void
match_path_type(struct menu_q *resultq, char *search, int flag)
{
struct menu *mi;
char *pattern;
glob_t g;
int i;
(void)snprintf(mi->print, sizeof(mi->print), "%c%s", flag,
cc->matchname);
xasprintf(&pattern, "%s*", search);
if (glob(pattern, GLOB_MARK, NULL, &g) != 0)
return;
for (i = 0; i < g.gl_pathc; i++) {
if ((flag & PATH_EXEC) && access(g.gl_pathv[i], X_OK))
continue;
mi = xcalloc(1, sizeof(*mi));
(void)strlcpy(mi->text, g.gl_pathv[i], sizeof(mi->text));
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
}
globfree(&g);
free(pattern);
}
if (!list && cc->matchname != cc->name &&
strlen(mi->print) < sizeof(mi->print) - 1) {
const char *marker = "";
char buf[MENU_MAXENTRY + 1];
int diff;
void
search_match_exec(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct menu *mi, *mj;
int r;
diff = sizeof(mi->print) - 1 - strlen(mi->print);
TAILQ_INIT(resultq);
TAILQ_FOREACH(mi, menuq, entry) {
if (match_substr(search, mi->text, 1) == 0 &&
fnmatch(search, mi->text, 0) == FNM_NOMATCH)
continue;
TAILQ_FOREACH(mj, resultq, resultentry) {
r = strcmp(mi->text, mj->text);
if (r < 0)
TAILQ_INSERT_BEFORE(mj, mi, resultentry);
if (r <= 0)
break;
/* One for the ':' */
diff -= 1;
if (strlen(cc->name) > diff) {
marker = "..";
diff -= 2;
} else {
diff = strlen(cc->name);
}
if (mj == NULL)
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
}
if (TAILQ_EMPTY(resultq))
match_path_type(resultq, search, PATH_EXEC);
}
void
search_match_path(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
TAILQ_INIT(resultq);
match_path_type(resultq, search, PATH_ANY);
(void)strlcpy(buf, mi->print, sizeof(buf));
(void)snprintf(mi->print, sizeof(mi->print),
"%s:%.*s%s", buf, diff, cc->name, marker);
}
}
void
@ -218,72 +167,58 @@ search_match_text(struct menu_q *menuq, struct menu_q *resultq, char *search)
struct menu *mi;
TAILQ_INIT(resultq);
TAILQ_FOREACH(mi, menuq, entry) {
if (match_substr(search, mi->text, 0))
TAILQ_FOREACH(mi, menuq, entry)
if (strsubmatch(search, mi->text, 0))
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
}
}
void
search_match_wm(struct menu_q *menuq, struct menu_q *resultq, char *search)
search_match_exec(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct menu *mi;
struct cmd_ctx *wm;
struct menu *mi, *mj;
TAILQ_INIT(resultq);
TAILQ_FOREACH(mi, menuq, entry) {
wm = (struct cmd_ctx *)mi->ctx;
if ((match_substr(search, wm->name, 0)) ||
(match_substr(search, wm->path, 0)))
if (strsubmatch(search, mi->text, 1) == 0 &&
fnmatch(search, mi->text, 0) == FNM_NOMATCH)
continue;
for (mj = TAILQ_FIRST(resultq); mj != NULL;
mj = TAILQ_NEXT(mj, resultentry)) {
if (strcasecmp(mi->text, mj->text) < 0) {
TAILQ_INSERT_BEFORE(mj, mi, resultentry);
break;
}
}
if (mj == NULL)
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
}
}
void
search_print_client(struct menu *mi, int listing)
static int
strsubmatch(char *sub, char *str, int zeroidx)
{
struct client_ctx *cc = (struct client_ctx *)mi->ctx;
char flag = ' ';
size_t len, sublen;
u_int n, flen;
if (cc->flags & CLIENT_ACTIVE)
flag = '!';
else if (cc->flags & CLIENT_HIDDEN)
flag = '&';
if (sub == NULL || str == NULL)
return (0);
(void)snprintf(mi->print, sizeof(mi->print), "(%d) %c[%s] %s",
(cc->gc) ? cc->gc->num : 0, flag,
(cc->label) ? cc->label : "", cc->name);
}
void
search_print_cmd(struct menu *mi, int listing)
{
struct cmd_ctx *cmd = (struct cmd_ctx *)mi->ctx;
(void)snprintf(mi->print, sizeof(mi->print), "%s", cmd->name);
}
void
search_print_group(struct menu *mi, int listing)
{
struct group_ctx *gc = (struct group_ctx *)mi->ctx;
(void)snprintf(mi->print, sizeof(mi->print),
(group_holds_only_hidden(gc)) ? "%d: [%s]" : "%d: %s",
gc->num, gc->name);
}
void
search_print_text(struct menu *mi, int listing)
{
(void)snprintf(mi->print, sizeof(mi->print), "%s", mi->text);
}
void
search_print_wm(struct menu *mi, int listing)
{
struct cmd_ctx *wm = (struct cmd_ctx *)mi->ctx;
(void)snprintf(mi->print, sizeof(mi->print), "%s [%s]",
wm->name, wm->path);
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);
}

61
strlcat.c Normal file
View File

@ -0,0 +1,61 @@
/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* 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 */
#ifndef HAVE_STRLCAT
#include <sys/types.h>
#include <string.h>
/*
* 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 */

57
strlcpy.c Normal file
View File

@ -0,0 +1,57 @@
/* $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* 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 */
#ifndef HAVE_STRLCPY
#include <sys/types.h>
#include <string.h>
/*
* 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 */

70
strtonum.c Normal file
View File

@ -0,0 +1,70 @@
/* $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 <stdlib.h>
#include <limits.h>
#include <errno.h>
#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 */

73
util.c
View File

@ -18,21 +18,19 @@
* $OpenBSD$
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
static void log_msg(const char *, va_list);
#define MAXARGLEN 20
void
u_spawn(char *argstr)
@ -40,7 +38,8 @@ u_spawn(char *argstr)
switch (fork()) {
case 0:
u_exec(argstr);
exit(1);
err(1, "%s", argstr);
break;
case -1:
warn("fork");
default:
@ -51,10 +50,8 @@ u_spawn(char *argstr)
void
u_exec(char *argstr)
{
#define MAXARGLEN 20
char *args[MAXARGLEN], **ap = args;
char **end = &args[MAXARGLEN - 2], *tmp;
char *s = argstr;
char **end = &args[MAXARGLEN - 1], *tmp;
while (ap < end && (*ap = strsep(&argstr, " \t")) != NULL) {
if (**ap == '\0')
@ -77,64 +74,8 @@ u_exec(char *argstr)
}
}
}
*ap = NULL;
*ap = NULL;
(void)setsid();
(void)execvp(args[0], args);
warn("%s", s);
}
char *
u_argv(char * const *argv)
{
size_t siz = 0;
int i;
char *p;
if (argv == 0)
return NULL;
for (i = 0; argv[i]; i++)
siz += strlen(argv[i]) + 1;
if (siz == 0)
return NULL;
p = xmalloc(siz);
strlcpy(p, argv[0], siz);
for (i = 1; argv[i]; i++) {
strlcat(p, " ", siz);
strlcat(p, argv[i], siz);
}
return p;
}
static void
log_msg(const char *msg, va_list ap)
{
char *fmt;
if (asprintf(&fmt, "%s\n", msg) == -1) {
vfprintf(stderr, msg, ap);
fprintf(stderr, "\n");
} else {
vfprintf(stderr, fmt, ap);
free(fmt);
}
fflush(stderr);
}
void
log_debug(int level, const char *func, const char *msg, ...)
{
char *fmt;
va_list ap;
if (Conf.debug < level)
return;
va_start(ap, msg);
xasprintf(&fmt, "debug%d: %s: %s", level, func, msg);
log_msg(fmt, ap);
free(fmt);
va_end(ap);
}

375
xevents.c
View File

@ -24,15 +24,15 @@
* management of the xevent's.
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
@ -43,72 +43,78 @@ 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_leavenotify(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_expose(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,
[LeaveNotify] = xev_handle_leavenotify,
[ButtonPress] = xev_handle_buttonpress,
[ButtonRelease] = xev_handle_buttonrelease,
[KeyPress] = xev_handle_keypress,
[KeyRelease] = xev_handle_keyrelease,
[Expose] = xev_handle_expose,
[DestroyNotify] = xev_handle_destroynotify,
[ClientMessage] = xev_handle_clientmessage,
[MappingNotify] = xev_handle_mappingnotify,
[Expose] = xev_handle_expose,
};
static KeySym modkeys[] = { XK_Alt_L, XK_Alt_R, XK_Super_L, XK_Super_R,
XK_Control_L, XK_Control_R, XK_ISO_Level3_Shift };
static void
xev_handle_maprequest(XEvent *ee)
{
XMapRequestEvent *e = &ee->xmaprequest;
struct screen_ctx *sc;
struct client_ctx *cc, *old_cc;
struct client_ctx *cc = NULL, *old_cc;
XWindowAttributes xattr;
LOG_DEBUG3("parent: 0x%lx window: 0x%lx", e->parent, e->window);
if ((old_cc = client_current()) != NULL)
client_ptrsave(old_cc);
if ((sc = screen_find(e->parent)) == NULL)
return;
if ((cc = client_find(e->window)) == NULL) {
XGetWindowAttributes(X_Dpy, e->window, &xattr);
cc = client_new(e->window, screen_fromroot(xattr.root), 1);
}
if ((old_cc = client_current(sc)) != NULL)
client_ptr_save(old_cc);
if ((cc = client_find(e->window)) == NULL)
cc = client_init(e->window, NULL);
if ((cc != NULL) && (!(cc->flags & CLIENT_IGNORE)))
client_ptr_warp(cc);
if ((cc->flags & CLIENT_IGNORE) == 0)
client_ptrwarp(cc);
}
static void
xev_handle_unmapnotify(XEvent *ee)
{
XUnmapEvent *e = &ee->xunmap;
XEvent ev;
struct client_ctx *cc;
LOG_DEBUG3("window: 0x%lx", e->window);
/* XXX, we need a recursive locking wrapper around grab server */
XGrabServer(X_Dpy);
if ((cc = client_find(e->window)) != NULL) {
if (e->send_event) {
xu_set_wm_state(cc->win, WithdrawnState);
} else {
if (!(cc->flags & CLIENT_HIDDEN))
client_remove(cc);
}
/*
* If it's going to die anyway, nuke it.
*
* Else, if it's a synthetic event delete state, since they
* want it to be withdrawn. ICCM recommends you withdraw on
* this even if we haven't alredy been told to iconify, to
* deal with legacy clients.
*/
if (XCheckTypedWindowEvent(X_Dpy, cc->win,
DestroyNotify, &ev) || e->send_event != 0) {
client_delete(cc);
} else
client_hide(cc);
}
XUngrabServer(X_Dpy);
}
static void
@ -117,10 +123,8 @@ xev_handle_destroynotify(XEvent *ee)
XDestroyWindowEvent *e = &ee->xdestroywindow;
struct client_ctx *cc;
LOG_DEBUG3("window: 0x%lx", e->window);
if ((cc = client_find(e->window)) != NULL)
client_remove(cc);
client_delete(cc);
}
static void
@ -131,40 +135,34 @@ xev_handle_configurerequest(XEvent *ee)
struct screen_ctx *sc;
XWindowChanges wc;
LOG_DEBUG3("window: 0x%lx", e->window);
if ((cc = client_find(e->window)) != NULL) {
sc = cc->sc;
if (e->value_mask & CWWidth)
cc->geom.w = e->width;
cc->geom.width = e->width;
if (e->value_mask & CWHeight)
cc->geom.h = e->height;
cc->geom.height = 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;
wc.border_width = e->border_width;
if (cc->geom.x == 0 && cc->geom.w >= sc->view.w)
if (cc->geom.x == 0 && cc->geom.width >= sc->xmax)
cc->geom.x -= cc->bwidth;
if (cc->geom.y == 0 && cc->geom.h >= sc->view.h)
if (cc->geom.y == 0 && cc->geom.height >= sc->ymax)
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.width = cc->geom.width;
wc.height = cc->geom.height;
wc.border_width = cc->bwidth;
XConfigureWindow(X_Dpy, cc->win, e->value_mask, &wc);
client_config(cc);
xu_configure(cc);
} else {
/* let it do what it wants, it'll be ours when we map it. */
wc.x = e->x;
@ -186,37 +184,32 @@ xev_handle_propertynotify(XEvent *ee)
struct screen_ctx *sc;
struct client_ctx *cc;
LOG_DEBUG3("window: 0x%lx", e->window);
if ((cc = client_find(e->window)) != NULL) {
switch (e->atom) {
case XA_WM_NORMAL_HINTS:
client_get_sizehints(cc);
client_getsizehints(cc);
break;
case XA_WM_NAME:
client_set_name(cc);
break;
case XA_WM_HINTS:
client_wm_hints(cc);
client_draw_border(cc);
client_setname(cc);
break;
case XA_WM_TRANSIENT_FOR:
client_transient(cc);
client_draw_border(cc);
if (cc->gc)
group_movetogroup(cc, cc->gc->num);
break;
default:
if (e->atom == ewmh[_NET_WM_NAME])
client_set_name(cc);
/* do nothing */
break;
}
} else {
if (e->atom == ewmh[_NET_DESKTOP_NAMES]) {
if ((sc = screen_find(e->window)) != NULL)
xu_ewmh_net_desktop_names(sc);
}
TAILQ_FOREACH(sc, &Screenq, entry)
if (sc->rootwin == e->window)
goto test;
return;
test:
if (e->atom == _NET_DESKTOP_NAMES)
group_update_names(sc);
}
}
static void
@ -225,93 +218,75 @@ xev_handle_enternotify(XEvent *ee)
XCrossingEvent *e = &ee->xcrossing;
struct client_ctx *cc;
LOG_DEBUG3("window: 0x%lx", e->window);
Last_Event_Time = e->time;
if ((cc = client_find(e->window)) != NULL)
client_set_active(cc);
client_setactive(cc, 1);
}
static void
xev_handle_leavenotify(XEvent *ee)
{
client_leave(NULL);
}
/* We can split this into two event handlers. */
static void
xev_handle_buttonpress(XEvent *ee)
{
XButtonEvent *e = &ee->xbutton;
struct client_ctx *cc;
struct client_ctx *cc, fakecc;
struct screen_ctx *sc;
struct bind_ctx *mb;
struct mousebinding *mb;
LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
e->root, e->window, e->subwindow);
sc = screen_fromroot(e->root);
cc = client_find(e->window);
if ((sc = screen_find(e->root)) == NULL)
return;
/* Ignore caps lock and numlock */
e->state &= ~(Mod2Mask | LockMask);
e->state &= ~IGNOREMODMASK;
TAILQ_FOREACH(mb, &Conf.mousebindq, entry) {
if (e->button == mb->press.button && e->state == mb->modmask)
TAILQ_FOREACH(mb, &Conf.mousebindingq, entry) {
if (e->button == mb->button && e->state == mb->modmask)
break;
}
if (mb == NULL)
return;
mb->cargs->xev = CWM_XEV_BTN;
switch (mb->context) {
case CWM_CONTEXT_CC:
if (((cc = client_find(e->window)) == NULL) &&
((cc = client_current(sc)) == NULL))
if (mb->context == MOUSEBIND_CTX_ROOT) {
if (e->window != sc->rootwin)
return;
(*mb->callback)(cc, mb->cargs);
break;
case CWM_CONTEXT_SC:
(*mb->callback)(sc, mb->cargs);
break;
case CWM_CONTEXT_NONE:
(*mb->callback)(NULL, mb->cargs);
break;
}
cc = &fakecc;
cc->sc = screen_fromroot(e->window);
} else if (cc == NULL) /* (mb->context == MOUSEBIND_CTX_WIN */
return;
(*mb->callback)(cc, e);
}
static void
xev_handle_buttonrelease(XEvent *ee)
{
XButtonEvent *e = &ee->xbutton;
struct client_ctx *cc;
struct client_ctx *cc;
LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
e->root, e->window, e->subwindow);
if ((cc = client_find(e->window)) != NULL) {
if (cc->flags & (CLIENT_ACTIVE | CLIENT_HIGHLIGHT)) {
cc->flags &= ~CLIENT_HIGHLIGHT;
client_draw_border(cc);
}
}
if ((cc = client_current()) != NULL)
group_sticky_toggle_exit(cc);
}
static void
xev_handle_keypress(XEvent *ee)
{
XKeyEvent *e = &ee->xkey;
struct client_ctx *cc;
struct screen_ctx *sc;
struct bind_ctx *kb;
struct client_ctx *cc = NULL, fakecc;
struct keybinding *kb;
KeySym keysym, skeysym;
unsigned int modshift;
int modshift;
LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
e->root, e->window, e->subwindow);
keysym = XKeycodeToKeysym(X_Dpy, e->keycode, 0);
skeysym = XKeycodeToKeysym(X_Dpy, e->keycode, 1);
if ((sc = screen_find(e->root)) == NULL)
return;
/* we don't care about caps lock and numlock here */
e->state &= ~(LockMask | Mod2Mask);
keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
skeysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 1);
e->state &= ~IGNOREMODMASK;
TAILQ_FOREACH(kb, &Conf.keybindq, entry) {
if (keysym != kb->press.keysym && skeysym == kb->press.keysym)
TAILQ_FOREACH(kb, &Conf.keybindingq, entry) {
if (keysym != kb->keysym && skeysym == kb->keysym)
modshift = ShiftMask;
else
modshift = 0;
@ -319,30 +294,28 @@ xev_handle_keypress(XEvent *ee)
if ((kb->modmask | modshift) != e->state)
continue;
if (kb->press.keysym == ((modshift == 0) ? keysym : skeysym))
if ((kb->keycode != 0 && kb->keysym == NoSymbol &&
kb->keycode == e->keycode) || kb->keysym ==
(modshift == 0 ? keysym : skeysym))
break;
}
if (kb == NULL)
return;
kb->cargs->xev = CWM_XEV_KEY;
switch (kb->context) {
case CWM_CONTEXT_CC:
if (((cc = client_find(e->subwindow)) == NULL) &&
((cc = client_current(sc)) == NULL))
if (kb->flags & KBFLAG_NEEDCLIENT) {
if (((cc = client_find(e->window)) == NULL) &&
(cc = client_current()) == NULL)
return;
(*kb->callback)(cc, kb->cargs);
break;
case CWM_CONTEXT_SC:
(*kb->callback)(sc, kb->cargs);
break;
case CWM_CONTEXT_NONE:
(*kb->callback)(NULL, kb->cargs);
break;
} else {
cc = &fakecc;
cc->sc = screen_fromroot(e->window);
}
(*kb->callback)(cc, &kb->argument);
}
/*
* This is only used for the modifier suppression detection.
* This is only used for the alt suppression detection.
*/
static void
xev_handle_keyrelease(XEvent *ee)
@ -350,31 +323,26 @@ xev_handle_keyrelease(XEvent *ee)
XKeyEvent *e = &ee->xkey;
struct screen_ctx *sc;
struct client_ctx *cc;
KeySym keysym;
unsigned int i;
int keysym;
LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
e->root, e->window, e->subwindow);
sc = screen_fromroot(e->root);
cc = client_current();
if ((sc = screen_find(e->root)) == NULL)
keysym = XKeycodeToKeysym(X_Dpy, e->keycode, 0);
if (keysym != XK_Alt_L && keysym != XK_Alt_R)
return;
keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
for (i = 0; i < nitems(modkeys); i++) {
if (keysym == modkeys[i]) {
if ((cc = client_current(sc)) != NULL) {
if (sc->cycling) {
sc->cycling = 0;
client_mtf(cc);
}
if (cc->flags & CLIENT_HIGHLIGHT) {
cc->flags &= ~CLIENT_HIGHLIGHT;
client_draw_border(cc);
}
}
XUngrabKeyboard(X_Dpy, CurrentTime);
break;
}
sc->altpersist = 0;
/*
* XXX - better interface... xevents should not know about
* how/when to mtf.
*/
client_mtf(NULL);
if (cc != NULL) {
group_sticky_toggle_exit(cc);
XUngrabKeyboard(X_Dpy, CurrentTime);
}
}
@ -382,69 +350,34 @@ static void
xev_handle_clientmessage(XEvent *ee)
{
XClientMessageEvent *e = &ee->xclient;
struct client_ctx *cc, *old_cc;
struct screen_ctx *sc;
Atom xa_wm_change_state;
struct client_ctx *cc;
LOG_DEBUG3("window: 0x%lx", e->window);
xa_wm_change_state = XInternAtom(X_Dpy, "WM_CHANGE_STATE", False);
if (e->message_type == cwmh[WM_CHANGE_STATE]) {
if ((cc = client_find(e->window)) != NULL) {
if (e->data.l[0] == IconicState)
client_hide(cc);
}
} else if (e->message_type == ewmh[_NET_CLOSE_WINDOW]) {
if ((cc = client_find(e->window)) != NULL) {
client_close(cc);
}
} else if (e->message_type == ewmh[_NET_ACTIVE_WINDOW]) {
if ((cc = client_find(e->window)) != NULL) {
if ((old_cc = client_current(NULL)) != NULL)
client_ptr_save(old_cc);
client_show(cc);
client_ptr_warp(cc);
}
} else if (e->message_type == ewmh[_NET_WM_DESKTOP]) {
if ((cc = client_find(e->window)) != NULL) {
/*
* The EWMH spec states that if the cardinal returned
* is 0xFFFFFFFF (-1) then the window should appear
* on all desktops, in our case, group 0.
*/
if (e->data.l[0] == (unsigned long)-1)
group_movetogroup(cc, 0);
else
if (e->data.l[0] >= 0 &&
e->data.l[0] < Conf.ngroups)
group_movetogroup(cc, e->data.l[0]);
}
} else if (e->message_type == ewmh[_NET_WM_STATE]) {
if ((cc = client_find(e->window)) != NULL) {
xu_ewmh_handle_net_wm_state_msg(cc,
e->data.l[0], e->data.l[1], e->data.l[2]);
}
} else if (e->message_type == ewmh[_NET_CURRENT_DESKTOP]) {
if ((sc = screen_find(e->window)) != NULL) {
if (e->data.l[0] >= 0 &&
e->data.l[0] < Conf.ngroups)
group_only(sc, e->data.l[0]);
}
}
if ((cc = client_find(e->window)) == NULL)
return;
if (e->message_type == xa_wm_change_state && e->format == 32 &&
e->data.l[0] == IconicState)
client_hide(cc);
}
static void
xev_handle_randr(XEvent *ee)
{
XRRScreenChangeNotifyEvent *e = (XRRScreenChangeNotifyEvent *)ee;
XRRScreenChangeNotifyEvent *rev = (XRRScreenChangeNotifyEvent *)ee;
struct screen_ctx *sc;
int i;
LOG_DEBUG3("size: %d/%d", e->width, e->height);
if ((sc = screen_find(e->root)) == NULL)
return;
XRRUpdateConfiguration(ee);
screen_update_geometry(sc);
screen_assert_clients_within(sc);
i = XRRRootToScreen(X_Dpy, rev->root);
TAILQ_FOREACH(sc, &Screenq, entry) {
if (sc->which == (u_int)i) {
XRRUpdateConfiguration(ee);
screen_update_geometry(sc, rev->width, rev->height);
screen_init_xinerama(sc);
}
}
}
/*
@ -455,15 +388,15 @@ static void
xev_handle_mappingnotify(XEvent *ee)
{
XMappingEvent *e = &ee->xmapping;
struct screen_ctx *sc;
struct keybinding *kb;
LOG_DEBUG3("window: 0x%lx", e->window);
TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
conf_ungrab(&Conf, kb);
XRefreshKeyboardMapping(e);
if (e->request == MappingKeyboard) {
TAILQ_FOREACH(sc, &Screenq, entry)
conf_grab_kbd(sc->rootwin);
}
TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
conf_grab(&Conf, kb);
}
static void
@ -472,22 +405,22 @@ xev_handle_expose(XEvent *ee)
XExposeEvent *e = &ee->xexpose;
struct client_ctx *cc;
LOG_DEBUG3("window: 0x%lx", e->window);
if ((cc = client_find(e->window)) != NULL && e->count == 0)
client_draw_border(cc);
}
volatile sig_atomic_t xev_quit = 0;
void
xev_process(void)
xev_loop(void)
{
XEvent e;
while (XPending(X_Dpy)) {
while (xev_quit == 0) {
XNextEvent(X_Dpy, &e);
if ((e.type - Conf.xrandr_event_base) == RRScreenChangeNotify)
if (e.type - Randr_ev == RRScreenChangeNotify)
xev_handle_randr(&e);
else if ((e.type < LASTEvent) && (xev_handlers[e.type] != NULL))
else if (e.type < LASTEvent && xev_handlers[e.type] != NULL)
(*xev_handlers[e.type])(&e);
}
}

View File

@ -18,17 +18,14 @@
* $OpenBSD$
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
@ -38,12 +35,10 @@ xmalloc(size_t siz)
{
void *p;
if (siz == 0)
errx(1, "xmalloc: zero size");
if ((p = malloc(siz)) == NULL)
err(1, "malloc");
return p;
return (p);
}
void *
@ -51,26 +46,16 @@ 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;
return (p);
}
void *
xreallocarray(void *ptr, size_t nmemb, size_t size)
void
xfree(void *p)
{
void *p;
p = reallocarray(ptr, nmemb, size);
if (p == NULL)
errx(1, "xreallocarray: out of memory (new_size %zu bytes)",
nmemb * size);
return p;
free(p);
}
char *
@ -81,30 +66,5 @@ xstrdup(const char *str)
if ((p = strdup(str)) == NULL)
err(1, "strdup");
return p;
}
int
xasprintf(char **ret, const char *fmt, ...)
{
va_list ap;
int i;
va_start(ap, fmt);
i = xvasprintf(ret, fmt, ap);
va_end(ap);
return i;
}
int
xvasprintf(char **ret, const char *fmt, va_list ap)
{
int i;
i = vasprintf(ret, fmt, ap);
if (i == -1)
err(1, "vasprintf");
return i;
return (p);
}

681
xutil.c
View File

@ -18,54 +18,160 @@
* $OpenBSD$
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
void
xu_ptr_get(Window win, int *x, int *y)
{
Window w0, w1;
int tmp0, tmp1;
unsigned int tmp2;
static unsigned int ign_mods[] = { 0, LockMask, Mod2Mask, Mod2Mask | LockMask };
XQueryPointer(X_Dpy, win, &w0, &w1, &tmp0, &tmp1, x, y, &tmp2);
int
xu_ptr_grab(Window win, int mask, Cursor curs)
{
return (XGrabPointer(X_Dpy, win, False, mask,
GrabModeAsync, GrabModeAsync,
None, curs, CurrentTime) == GrabSuccess ? 0 : -1);
}
int
xu_ptr_regrab(int mask, Cursor curs)
{
return (XChangeActivePointerGrab(X_Dpy, mask,
curs, CurrentTime) == GrabSuccess ? 0 : -1);
}
void
xu_ptr_set(Window win, int x, int y)
xu_ptr_ungrab(void)
{
XUngrabPointer(X_Dpy, CurrentTime);
}
void
xu_btn_grab(Window win, int mask, u_int btn)
{
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, int mask, u_int btn)
{
int i;
for (i = 0; i < nitems(ign_mods); i++)
XUngrabButton(X_Dpy, btn, (mask | ign_mods[i]), win);
}
void
xu_ptr_getpos(Window rootwin, int *x, int *y)
{
Window w0, w1;
int tmp0, tmp1;
u_int tmp2;
XQueryPointer(X_Dpy, rootwin, &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_get_prop(Window win, Atom atm, Atom type, long len, unsigned char **p)
void
xu_key_grab(Window win, int mask, int keysym)
{
Atom realtype;
unsigned long n, extra;
int format;
KeyCode code;
int i;
code = XKeysymToKeycode(X_Dpy, keysym);
if ((XKeycodeToKeysym(X_Dpy, code, 0) != keysym) &&
(XKeycodeToKeysym(X_Dpy, code, 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, int mask, int keysym)
{
KeyCode code;
int i;
code = XKeysymToKeycode(X_Dpy, keysym);
if ((XKeycodeToKeysym(X_Dpy, code, 0) != keysym) &&
(XKeycodeToKeysym(X_Dpy, code, 1) == keysym))
mask |= ShiftMask;
for (i = 0; i < nitems(ign_mods); i++)
XUngrabKey(X_Dpy, code, (mask | ign_mods[i]), win);
}
void
xu_configure(struct client_ctx *cc)
{
XConfigureEvent ce;
ce.type = ConfigureNotify;
ce.event = cc->win;
ce.window = cc->win;
ce.x = cc->geom.x;
ce.y = cc->geom.y;
ce.width = cc->geom.width;
ce.height = cc->geom.height;
ce.border_width = cc->bwidth;
ce.above = None;
ce.override_redirect = 0;
XSendEvent(X_Dpy, cc->win, False, StructureNotifyMask, (XEvent *)&ce);
}
void
xu_sendmsg(Window win, Atom atm, long val)
{
XEvent e;
(void)memset(&e, 0, sizeof(e));
e.xclient.type = ClientMessage;
e.xclient.window = win;
e.xclient.message_type = atm;
e.xclient.format = 32;
e.xclient.data.l[0] = val;
e.xclient.data.l[1] = CurrentTime;
XSendEvent(X_Dpy, win, False, 0, &e);
}
int
xu_getprop(Window win, Atom atm, Atom type, long len, u_char **p)
{
Atom realtype;
u_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;
return (-1);
if (n == 0)
XFree(*p);
return n;
return (n);
}
int
xu_get_strprop(Window win, Atom atm, char **text) {
xu_getstrprop(Window win, Atom atm, char **text) {
XTextProperty prop;
char **list;
int nitems = 0;
@ -73,10 +179,8 @@ xu_get_strprop(Window win, Atom atm, char **text) {
*text = NULL;
XGetTextProperty(X_Dpy, win, &prop, atm);
if (!prop.nitems) {
XFree(prop.value);
return 0;
}
if (!prop.nitems)
return (0);
if (Xutf8TextPropertyToTextList(X_Dpy, &prop, &list,
&nitems) == Success && nitems > 0 && *list) {
@ -84,7 +188,7 @@ xu_get_strprop(Window win, Atom atm, char **text) {
XTextProperty prop2;
if (Xutf8TextListToTextProperty(X_Dpy, list, nitems,
XUTF8StringStyle, &prop2) == Success) {
*text = xstrdup((const char *)prop2.value);
*text = xstrdup(prop2.value);
XFree(prop2.value);
}
} else {
@ -92,477 +196,110 @@ xu_get_strprop(Window win, Atom atm, char **text) {
}
XFreeStringList(list);
}
XFree(prop.value);
return nitems;
return (nitems);
}
void
xu_send_clientmsg(Window win, Atom proto, Time ts)
{
XClientMessageEvent cm;
(void)memset(&cm, 0, sizeof(cm));
cm.type = ClientMessage;
cm.window = win;
cm.message_type = cwmh[WM_PROTOCOLS];
cm.format = 32;
cm.data.l[0] = proto;
cm.data.l[1] = ts;
XSendEvent(X_Dpy, win, False, NoEventMask, (XEvent *)&cm);
}
void
xu_get_wm_state(Window win, long *state)
{
long *p;
*state = -1;
if (xu_get_prop(win, cwmh[WM_STATE], cwmh[WM_STATE], 2L,
(unsigned char **)&p) > 0) {
*state = *p;
XFree(p);
}
}
void
xu_set_wm_state(Window win, long state)
{
long data[] = { state, None };
XChangeProperty(X_Dpy, win, cwmh[WM_STATE], cwmh[WM_STATE], 32,
PropModeReplace, (unsigned char *)data, 2);
}
void
xu_xorcolor(XftColor a, XftColor b, XftColor *r)
{
r->pixel = a.pixel ^ b.pixel;
r->color.red = a.color.red ^ b.color.red;
r->color.green = a.color.green ^ b.color.green;
r->color.blue = a.color.blue ^ b.color.blue;
r->color.alpha = 0xffff;
}
void
xu_atom_init(void)
{
char *cwmhints[] = {
"WM_STATE",
"WM_DELETE_WINDOW",
"WM_TAKE_FOCUS",
"WM_PROTOCOLS",
"_MOTIF_WM_HINTS",
"UTF8_STRING",
"WM_CHANGE_STATE",
};
char *ewmhints[] = {
"_NET_SUPPORTED",
"_NET_SUPPORTING_WM_CHECK",
"_NET_ACTIVE_WINDOW",
"_NET_CLIENT_LIST",
"_NET_CLIENT_LIST_STACKING",
"_NET_NUMBER_OF_DESKTOPS",
"_NET_CURRENT_DESKTOP",
"_NET_DESKTOP_VIEWPORT",
"_NET_DESKTOP_GEOMETRY",
"_NET_VIRTUAL_ROOTS",
"_NET_SHOWING_DESKTOP",
"_NET_DESKTOP_NAMES",
"_NET_WORKAREA",
"_NET_WM_NAME",
"_NET_WM_DESKTOP",
"_NET_CLOSE_WINDOW",
"_NET_WM_STATE",
"_NET_WM_STATE_STICKY",
"_NET_WM_STATE_MAXIMIZED_VERT",
"_NET_WM_STATE_MAXIMIZED_HORZ",
"_NET_WM_STATE_HIDDEN",
"_NET_WM_STATE_FULLSCREEN",
"_NET_WM_STATE_DEMANDS_ATTENTION",
"_NET_WM_STATE_SKIP_PAGER",
"_NET_WM_STATE_SKIP_TASKBAR",
"_CWM_WM_STATE_FREEZE",
};
XInternAtoms(X_Dpy, cwmhints, nitems(cwmhints), False, cwmh);
XInternAtoms(X_Dpy, ewmhints, nitems(ewmhints), False, ewmh);
}
/* Root Window Properties */
void
xu_ewmh_net_supported(struct screen_ctx *sc)
{
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTED],
XA_ATOM, 32, PropModeReplace, (unsigned char *)ewmh, EWMH_NITEMS);
}
void
xu_ewmh_net_supported_wm_check(struct screen_ctx *sc)
{
Window w;
w = XCreateSimpleWindow(X_Dpy, sc->rootwin, -1, -1, 1, 1, 0, 0, 0);
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTING_WM_CHECK],
XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
XChangeProperty(X_Dpy, w, ewmh[_NET_SUPPORTING_WM_CHECK],
XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
XChangeProperty(X_Dpy, w, ewmh[_NET_WM_NAME],
cwmh[UTF8_STRING], 8, PropModeReplace,
(unsigned char *)Conf.wmname, strlen(Conf.wmname));
}
void
xu_ewmh_net_desktop_geometry(struct screen_ctx *sc)
{
long geom[2] = { sc->view.w, sc->view.h };
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_GEOMETRY],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)geom , 2);
}
void
xu_ewmh_net_desktop_viewport(struct screen_ctx *sc)
{
long viewports[2] = {0, 0};
/* We don't support large desktops, so this is (0, 0). */
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_VIEWPORT],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2);
}
void
xu_ewmh_net_workarea(struct screen_ctx *sc)
{
unsigned long *workarea;
int i, ngroups = Conf.ngroups;
workarea = xreallocarray(NULL, ngroups * 4, sizeof(unsigned long));
for (i = 0; i < ngroups; i++) {
workarea[4 * i + 0] = sc->work.x;
workarea[4 * i + 1] = sc->work.y;
workarea[4 * i + 2] = sc->work.w;
workarea[4 * i + 3] = sc->work.h;
}
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_WORKAREA],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)workarea,
ngroups * 4);
free(workarea);
}
void
xu_ewmh_net_client_list(struct screen_ctx *sc)
{
struct client_ctx *cc;
Window *winlist;
int i = 0, j = 0;
TAILQ_FOREACH(cc, &sc->clientq, entry)
i++;
if (i == 0)
return;
winlist = xreallocarray(NULL, i, sizeof(*winlist));
TAILQ_FOREACH(cc, &sc->clientq, entry)
winlist[j++] = cc->win;
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CLIENT_LIST],
XA_WINDOW, 32, PropModeReplace, (unsigned char *)winlist, i);
free(winlist);
}
void
xu_ewmh_net_client_list_stacking(struct screen_ctx *sc)
{
struct client_ctx *cc;
Window *winlist;
int i = 0, j;
TAILQ_FOREACH(cc, &sc->clientq, entry)
i++;
if (i == 0)
return;
j = i;
winlist = xreallocarray(NULL, i, sizeof(*winlist));
TAILQ_FOREACH(cc, &sc->clientq, entry)
winlist[--j] = cc->win;
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CLIENT_LIST_STACKING],
XA_WINDOW, 32, PropModeReplace, (unsigned char *)winlist, i);
free(winlist);
}
void
xu_ewmh_net_active_window(struct screen_ctx *sc, Window w)
{
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_ACTIVE_WINDOW],
XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
}
void
xu_ewmh_net_number_of_desktops(struct screen_ctx *sc)
{
long ndesks = Conf.ngroups;
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_NUMBER_OF_DESKTOPS],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&ndesks, 1);
}
void
xu_ewmh_net_showing_desktop(struct screen_ctx *sc)
{
long zero = 0;
/* We don't support `showing desktop' mode, so this is zero.
* Note that when we hide all groups, or when all groups are
* hidden we could technically set this later on.
*/
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SHOWING_DESKTOP],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&zero, 1);
}
void
xu_ewmh_net_virtual_roots(struct screen_ctx *sc)
{
/* We don't support virtual roots, so delete if set by previous wm. */
XDeleteProperty(X_Dpy, sc->rootwin, ewmh[_NET_VIRTUAL_ROOTS]);
}
void
xu_ewmh_net_current_desktop(struct screen_ctx *sc)
{
long num = sc->group_active->num;
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CURRENT_DESKTOP],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1);
}
void
xu_ewmh_net_desktop_names(struct screen_ctx *sc)
{
struct group_ctx *gc;
char *p, *q;
unsigned char *prop_ret;
int i = 0, j = 0, nstrings = 0, n = 0;
size_t len = 0, tlen, slen;
/* Let group names be overwritten if _NET_DESKTOP_NAMES is set. */
if ((j = xu_get_prop(sc->rootwin, ewmh[_NET_DESKTOP_NAMES],
cwmh[UTF8_STRING], 0xffffff, (unsigned char **)&prop_ret)) > 0) {
prop_ret[j - 1] = '\0'; /* paranoia */
while (i < j) {
if (prop_ret[i++] == '\0')
nstrings++;
}
}
p = (char *)prop_ret;
while (n < nstrings) {
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == n) {
free(gc->name);
gc->name = xstrdup(p);
p += strlen(p) + 1;
break;
}
}
n++;
}
if (prop_ret != NULL)
XFree(prop_ret);
TAILQ_FOREACH(gc, &sc->groupq, entry)
len += strlen(gc->name) + 1;
q = p = xreallocarray(NULL, len, sizeof(*p));
tlen = len;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
slen = strlen(gc->name) + 1;
(void)strlcpy(q, gc->name, tlen);
tlen -= slen;
q += slen;
}
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_NAMES],
cwmh[UTF8_STRING], 8, PropModeReplace, (unsigned char *)p, len);
free(p);
}
/* Application Window Properties */
int
xu_ewmh_get_net_wm_desktop(struct client_ctx *cc, long *n)
xu_getstate(struct client_ctx *cc, int *state)
{
long *p;
long *p = NULL;
if (xu_get_prop(cc->win, ewmh[_NET_WM_DESKTOP], XA_CARDINAL, 1L,
(unsigned char **)&p) <= 0)
return 0;
*n = *p;
XFree(p);
return 1;
}
if (xu_getprop(cc->win, WM_STATE, WM_STATE, 2L, (u_char **)&p) <= 0)
return (-1);
void
xu_ewmh_set_net_wm_desktop(struct client_ctx *cc)
{
long num = 0xffffffff;
if (cc->gc)
num = cc->gc->num;
XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_DESKTOP],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1);
}
Atom *
xu_ewmh_get_net_wm_state(struct client_ctx *cc, int *n)
{
Atom *state, *p = NULL;
if ((*n = xu_get_prop(cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 64L,
(unsigned char **)&p)) <= 0)
return NULL;
state = xreallocarray(NULL, *n, sizeof(Atom));
(void)memcpy(state, p, *n * sizeof(Atom));
*state = (int)*p;
XFree((char *)p);
return state;
return (0);
}
void
xu_ewmh_handle_net_wm_state_msg(struct client_ctx *cc, int action,
Atom first, Atom second)
xu_setstate(struct client_ctx *cc, int state)
{
unsigned int i;
struct handlers {
Atom atom;
int flag;
void (*toggle)(struct client_ctx *);
} handlers[] = {
{ _NET_WM_STATE_STICKY,
CLIENT_STICKY,
client_toggle_sticky },
{ _NET_WM_STATE_MAXIMIZED_VERT,
CLIENT_VMAXIMIZED,
client_toggle_vmaximize },
{ _NET_WM_STATE_MAXIMIZED_HORZ,
CLIENT_HMAXIMIZED,
client_toggle_hmaximize },
{ _NET_WM_STATE_HIDDEN,
CLIENT_HIDDEN,
client_toggle_hidden },
{ _NET_WM_STATE_FULLSCREEN,
CLIENT_FULLSCREEN,
client_toggle_fullscreen },
{ _NET_WM_STATE_DEMANDS_ATTENTION,
CLIENT_URGENCY,
client_urgency },
{ _NET_WM_STATE_SKIP_PAGER,
CLIENT_SKIP_PAGER,
client_toggle_skip_pager},
{ _NET_WM_STATE_SKIP_TASKBAR,
CLIENT_SKIP_TASKBAR,
client_toggle_skip_taskbar},
{ _CWM_WM_STATE_FREEZE,
CLIENT_FREEZE,
client_toggle_freeze },
};
long dat[2];
for (i = 0; i < nitems(handlers); i++) {
if (first != ewmh[handlers[i].atom] &&
second != ewmh[handlers[i].atom])
continue;
switch (action) {
case _NET_WM_STATE_ADD:
if (!(cc->flags & handlers[i].flag))
handlers[i].toggle(cc);
break;
case _NET_WM_STATE_REMOVE:
if (cc->flags & handlers[i].flag)
handlers[i].toggle(cc);
break;
case _NET_WM_STATE_TOGGLE:
handlers[i].toggle(cc);
}
}
dat[0] = state;
dat[1] = None;
cc->state = state;
XChangeProperty(X_Dpy, cc->win, WM_STATE, WM_STATE, 32,
PropModeReplace, (unsigned char *)dat, 2);
}
Atom cwm_atoms[CWM_NO_ATOMS];
char *atoms[CWM_NO_ATOMS] = {
"WM_STATE",
"WM_DELETE_WINDOW",
"WM_TAKE_FOCUS",
"WM_PROTOCOLS",
"_MOTIF_WM_HINTS",
"UTF8_STRING",
"_NET_SUPPORTED",
"_NET_SUPPORTING_WM_CHECK",
"_NET_WM_NAME",
"_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_WM_DESKTOP",
"_NET_WORKAREA",
};
void
xu_getatoms(void)
{
XInternAtoms(X_Dpy, atoms, CWM_NO_ATOMS, False, cwm_atoms);
}
void
xu_ewmh_restore_net_wm_state(struct client_ctx *cc)
xu_setwmname(struct screen_ctx *sc)
{
Atom *atoms;
int i, n;
/*
* set up the _NET_SUPPORTED hint with all netwm atoms that we
* know about.
*/
XChangeProperty(X_Dpy, sc->rootwin, _NET_SUPPORTED, XA_ATOM, 32,
PropModeReplace, (unsigned char *)&_NET_SUPPORTED,
CWM_NO_ATOMS - CWM_NETWM_START);
/*
* netwm spec says that to prove that the hint is not stale you must
* provide _NET_SUPPORTING_WM_CHECK containing a window (we use the
* menu window). The property must be set on the root window and the
* window itself, the window also must have _NET_WM_NAME set with the
* window manager name.
*/
XChangeProperty(X_Dpy, sc->rootwin, _NET_SUPPORTING_WM_CHECK,
XA_WINDOW, 32, PropModeReplace, (unsigned char *)&sc->menuwin, 1);
XChangeProperty(X_Dpy, sc->menuwin, _NET_SUPPORTING_WM_CHECK,
XA_WINDOW, 32, PropModeReplace, (unsigned char *)&sc->menuwin, 1);
XChangeProperty(X_Dpy, sc->menuwin, _NET_WM_NAME, UTF8_STRING,
8, PropModeReplace, WMNAME, strlen(WMNAME));
}
atoms = xu_ewmh_get_net_wm_state(cc, &n);
for (i = 0; i < n; i++) {
if (atoms[i] == ewmh[_NET_WM_STATE_STICKY])
client_toggle_sticky(cc);
if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_VERT])
client_toggle_vmaximize(cc);
if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_HORZ])
client_toggle_hmaximize(cc);
if (atoms[i] == ewmh[_NET_WM_STATE_HIDDEN])
client_toggle_hidden(cc);
if (atoms[i] == ewmh[_NET_WM_STATE_FULLSCREEN])
client_toggle_fullscreen(cc);
if (atoms[i] == ewmh[_NET_WM_STATE_DEMANDS_ATTENTION])
client_urgency(cc);
if (atoms[i] == ewmh[_NET_WM_STATE_SKIP_PAGER])
client_toggle_skip_pager(cc);
if (atoms[i] == ewmh[_NET_WM_STATE_SKIP_TASKBAR])
client_toggle_skip_taskbar(cc);
if (atoms[i] == ewmh[_CWM_WM_STATE_FREEZE])
client_toggle_freeze(cc);
unsigned long
xu_getcolor(struct screen_ctx *sc, char *name)
{
XColor color, tmp;
if (!XAllocNamedColor(X_Dpy, DefaultColormap(X_Dpy, sc->which),
name, &color, &tmp)) {
warnx("XAllocNamedColor error: '%s'", name);
return 0;
}
free(atoms);
return color.pixel;
}
void
xu_ewmh_set_net_wm_state(struct client_ctx *cc)
xu_freecolor(struct screen_ctx *sc, unsigned long pixel)
{
Atom *atoms, *oatoms;
int n, i, j;
oatoms = xu_ewmh_get_net_wm_state(cc, &n);
atoms = xreallocarray(NULL, (n + _NET_WM_STATES_NITEMS), sizeof(Atom));
for (i = j = 0; i < n; i++) {
if (oatoms[i] != ewmh[_NET_WM_STATE_STICKY] &&
oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_VERT] &&
oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_HORZ] &&
oatoms[i] != ewmh[_NET_WM_STATE_HIDDEN] &&
oatoms[i] != ewmh[_NET_WM_STATE_FULLSCREEN] &&
oatoms[i] != ewmh[_NET_WM_STATE_DEMANDS_ATTENTION] &&
oatoms[i] != ewmh[_NET_WM_STATE_SKIP_PAGER] &&
oatoms[i] != ewmh[_NET_WM_STATE_SKIP_TASKBAR] &&
oatoms[i] != ewmh[_CWM_WM_STATE_FREEZE])
atoms[j++] = oatoms[i];
}
free(oatoms);
if (cc->flags & CLIENT_STICKY)
atoms[j++] = ewmh[_NET_WM_STATE_STICKY];
if (cc->flags & CLIENT_HIDDEN)
atoms[j++] = ewmh[_NET_WM_STATE_HIDDEN];
if (cc->flags & CLIENT_FULLSCREEN)
atoms[j++] = ewmh[_NET_WM_STATE_FULLSCREEN];
else {
if (cc->flags & CLIENT_VMAXIMIZED)
atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_VERT];
if (cc->flags & CLIENT_HMAXIMIZED)
atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_HORZ];
}
if (cc->flags & CLIENT_URGENCY)
atoms[j++] = ewmh[_NET_WM_STATE_DEMANDS_ATTENTION];
if (cc->flags & CLIENT_SKIP_PAGER)
atoms[j++] = ewmh[_NET_WM_STATE_SKIP_PAGER];
if (cc->flags & CLIENT_SKIP_TASKBAR)
atoms[j++] = ewmh[_NET_WM_STATE_SKIP_TASKBAR];
if (cc->flags & CLIENT_FREEZE)
atoms[j++] = ewmh[_CWM_WM_STATE_FREEZE];
if (j > 0)
XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE],
XA_ATOM, 32, PropModeReplace, (unsigned char *)atoms, j);
else
XDeleteProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE]);
free(atoms);
XFreeColors(X_Dpy, DefaultColormap(X_Dpy, sc->which), &pixel, 1, 0L);
}