2 Commits

Author SHA1 Message Date
a119fe5240 cvsimport 2010-09-25 21:58:18 +00:00
f2e8fad75c cvsimport 2010-09-25 22:21:23 +00:00
31 changed files with 4312 additions and 5622 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,53 +1,54 @@
# cwm makefile for BSD make and GNU make
# uses pkg-config, DESTDIR and PREFIX
# $OpenBSD$
#.include <bsd.xconf.mk>
PROG= cwm
PREFIX?= /usr/local
BINDIR= /usr/bin
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
search.c util.c xutil.c conf.c input.c xevents.c group.c \
kbfunc.c mousefunc.c font.c parse.y
OBJS= calmwm.o screen.o xmalloc.o client.o menu.o \
search.o util.o xutil.o conf.o xevents.o group.o \
kbfunc.o strlcpy.o strlcat.o y.tab.o \
strtonum.o reallocarray.o
search.o util.o xutil.o conf.o input.o xevents.o group.o \
kbfunc.o mousefunc.o font.o strlcpy.o strlcat.o y.tab.o \
strtonum.o fgetln.o
CPPFLAGS+= `pkg-config --cflags fontconfig x11 xft xrandr`
X11BASE= /usr
CFLAGS?= -Wall -O2 -g -D_GNU_SOURCE
CPPFLAGS+= -I${X11BASE}/include -I${X11BASE}/include/freetype2 -I.
LDFLAGS+= `pkg-config --libs fontconfig x11 xft xrandr`
CFLAGS+= -Wall
MANPREFIX?= ${PREFIX}/share/man
LDADD+= -L${X11BASE}/lib -lXft -lXrender -lX11 -lXau -lXdmcp \
-lfontconfig -lexpat -lfreetype -lz -lXinerama -lXrandr -lXext
all: ${PROG}
MANDIR= ${X11BASE}/man/cat
MAN= cwm.1 cwmrc.5
CLEANFILES= cwm.cat1 cwmrc.cat5
all: $(PROG)
clean:
rm -f ${OBJS} ${PROG} y.tab.c
rm -rf $(OBJS) $(PROG) y.tab.c
y.tab.c: parse.y
yacc parse.y
byacc parse.y
${PROG}: ${OBJS} y.tab.o
${CC} ${OBJS} ${LDFLAGS} -o ${PROG}
.c.o:
${CC} -c ${CFLAGS} ${CPPFLAGS} $<
$(PROG): $(OBJS) y.tab.o
$(CC) $(OBJS) ${LDADD} -o ${PROG}
$(OBJS): %.o: %.c
$(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
install -m 755 cwm /usr/local/bin/
install -m 644 cwm.1 /usr/local/man/man1
install -m 644 cwmrc.5 /usr/local/man/man5
release:
VERSION=$$(git describe --tags | sed 's/^v//;s/-[^.]*$$//') && \
git archive --prefix=cwm-$$VERSION/ -o cwm-$$VERSION.tar.gz HEAD
sign:
VERSION=$$(git describe --tags | sed 's/^v//;s/-[^.]*$$//') && \
gpg --armor --detach-sign cwm-$$VERSION.tar.gz && \
signify -S -s ~/.signify/cwm.sec -m cwm-$$VERSION.tar.gz && \
sed -i '1cuntrusted comment: verify with cwm.pub' cwm-$$VERSION.tar.gz.sig
#.include <bsd.prog.mk>
#.include <bsd.xorg.mk>

112
README
View File

@ -1,112 +0,0 @@
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. It has been built successfully on
OpenBSD, FreeBSD, NetBSD, OS X 10.9 and Linux.
This version actively tracks changes in the OpenBSD CVS repository.
Releases are roughly coordinated.
The revision controlled version is at https://github.com/chneukirchen/cwm
Releases can be found at http://chneukirchen.org/releases
You are welcome to join the IRC channel ##cwm on Freenode to talk about cwm.
ChangeLog:
2012-05-02: First public release 5.1 of portable cwm.
2014-04-13: Second public release 5.5 of portable cwm.
User visible changes (for a full list including smaller bug fixes, see
http://www.openbsd.org/plus.html ff.)
Changes made between OpenBSD 5.1 and 5.2
* Fixed cwm(1) atom (WM_PROTOCOLS) style handing; closing a window will no
longer close entire application if the client supports CLIENT_PROTO_DELETE.
* Re-implement atom handing for more consistent separation of cwm(1) and
Extended Window Manager Hints.
* cwm(1) better integrated into the freedesktop.org Window Manager
Specification Project.
Changes made between OpenBSD 5.2 and 5.3
* Set the initial cwm(1) group to "1".
* Added cwm(1) per-group vert/horiz tiling support with new bind commands
"vtile" and "htile."
* Made cwm(1) screen font an Xft(3) font.
* Specific last match for autogroup in cwmrc(5).
* Tab completion support for cwm(1) menus.
* Allow cwm(1) clients to be resized from a max state.
* Multibyte input to cwm(1) menu code now possible.
Changes made between OpenBSD 5.3 and 5.4
* Added support for mouse based group {,r}cycle to cwmrc(5).
* Allow mouse button4 and button5 in cwmrc(5).
* Made cwm(1) check for, and honour, CWStackMode and CWSibling change requests
during a ConfigureRequest event.
* Honour PATH search order for cwm(1)'s exec.
Changes made between OpenBSD 5.5 and 5.4
* Only set the cwm(1) urgency flag if the client is not active.
* Allow the cwm(1) config parser continue parsing even after encountering an
error.
* cwm(1) now follows the EWMH spec: if the cardinal returned is 0xFFFFFFFF (-1)
then the window should appear on all desktops.
* Made cwm(1) supply a more useful title for windows launched via the ssh(1)
command menu ("[ssh] ").
* Allowed cwm(1) to accept _NET_WM_DESKTOP and _NET_CURRENT_DESKTOP
ClientMessage.
* Implemented cwm(1) support for _NET_WM_STATE_FULLSCREEN hint, with keybinding
changes: CM-f "fullscreen", CM-m "maximize".
* Instead of using the work area, use the Xinerama area for cwm(1) snap
calculations.
* Save-set when cwm(1) is re-exec'ing so as to not lose State on our hidden
clients.
* Added cwmrc(5) support for XUrgency and matching
_NET_WM_STATE_DEMANDS_ATTENTION ewmh hint, with configurable urgencyborder.
* Prepend the group shortcut in the cwm(1) client search menu;
prepend shortcut in unhide menu.
* If not hidden during an UnmapNotify event, cwm(1) will now un-manage the
client.
* Like "gap", made cwm(1) "snapdist" per-screen.
* Removed cwmrc(5) option to bind a key by keycode with brackets, which never
worked. Users should be using keysym names not keycodes.
* Re-added cwm(1) support for WM_TAKE_FOCUS. Solves keyboard input focus loss
for java apps.
* For cwm(1) clients that support WM_TAKE_FOCUS in their WM_PROTOCOLS property,
send a ClientMessage event.
2015-01-24: Third public release 5.6 of portable cwm.
* Support building on FreeBSD and OS X.
* Support for sticky windows (_NET_WM_STATE_STICKY).
* Internal cleanups and bug fixes.
Changes made between OpenBSD 5.6 and 5.7
* Implemented _NET_WM_STATE_STICKY in cwm(1). Allows client to "stick"
to all desktops or groups.
* Ensure cwm(1) client that wants to be in nogroup stays in nogroup
(thus stays in view), even when (re)reading NET_WM_DESKTOP.
Changes made between OpenBSD 5.7 and 5.8
* In cwm(1), introduce "groupsearch" for group menu search.
* In cwm(1), show an empty "ssh to" menu if the known_hosts file is missing.
* In cwm(1), replace screen region info gathering with XRandR
equivalent of Xinerama queries.
Changes made between OpenBSD 5.8 and 5.9
* Don't allow freeze operations on fullscreen.
* Implement _NET_CLIENT_LIST_STACKING.
2017-10-17: Fourth public release 6.2 of portable cwm.
--Leah Neukirchen <leah@vuxu.org>
[0]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/xenocara/app/cwm/

262
calmwm.c
View File

@ -15,55 +15,53 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $OpenBSD$
* $Id$
*/
#include <sys/types.h>
#include "queue.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 <pwd.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;
const char *homedir;
volatile sig_atomic_t cwm_status;
Display *X_Dpy;
static void sighdlr(int);
Cursor Cursor_move;
Cursor Cursor_resize;
Cursor Cursor_select;
Cursor Cursor_default;
Cursor Cursor_question;
struct screen_ctx_q Screenq = TAILQ_HEAD_INITIALIZER(Screenq);
struct client_ctx_q Clientq = TAILQ_HEAD_INITIALIZER(Clientq);
int HasXinerama, HasRandr, Randr_ev;
int Starting;
struct conf Conf;
static void sigchld_cb(int);
static void dpy_init(const char *);
static int x_errorhandler(Display *, XErrorEvent *);
static void x_init(const char *);
static void x_setup(void);
static void x_setupscreen(struct screen_ctx *, u_int);
static void x_teardown(void);
static int x_wmerrorhandler(Display *, XErrorEvent *);
int
main(int argc, char **argv)
{
const char *conf_file = NULL;
char *conf_path, *display_name = NULL;
char *display_name = NULL;
int ch;
struct passwd *pw;
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
warnx("no locale support");
mbtowc(NULL, NULL, MB_CUR_MAX);
Conf.wm_argv = u_argv(argv);
while ((ch = getopt(argc, argv, "c:d:")) != -1) {
switch (ch) {
case 'c':
@ -79,149 +77,185 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
if (signal(SIGCHLD, sighdlr) == SIG_ERR)
if (signal(SIGCHLD, sigchld_cb) == SIG_ERR)
err(1, "signal");
if ((homedir = getenv("HOME")) == NULL || *homedir == '\0') {
pw = getpwuid(getuid());
if (pw != NULL && pw->pw_dir != NULL && *pw->pw_dir != '\0')
homedir = pw->pw_dir;
else
homedir = "/";
}
Starting = 1;
dpy_init(display_name);
if (conf_file == NULL)
xasprintf(&conf_path, "%s/%s", homedir, CONFFILE);
else
conf_path = xstrdup(conf_file);
bzero(&Conf, sizeof(Conf));
conf_setup(&Conf, conf_file);
xu_getatoms();
x_setup();
Starting = 0;
if (access(conf_path, R_OK) != 0) {
if (conf_file != NULL)
warn("%s", conf_file);
free(conf_path);
conf_path = NULL;
}
xev_loop();
conf_init(&Conf);
if (conf_path && (parse_config(conf_path, &Conf) == -1))
warnx("config file %s has errors", conf_path);
free(conf_path);
x_init(display_name);
cwm_status = CWM_RUNNING;
#ifdef __OpenBSD__
if (pledge("stdio rpath proc exec", NULL) == -1)
err(1, "pledge");
#endif
while (cwm_status == CWM_RUNNING)
xev_process();
x_teardown();
if (cwm_status == CWM_EXEC_WM)
u_exec(Conf.wm_argv);
return(0);
return (0);
}
static void
x_init(const char *dpyname)
dpy_init(const char *dpyname)
{
int i;
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);
}
conf_atoms();
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);
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);
Cursor_move = XCreateFontCursor(X_Dpy, XC_fleur);
Cursor_resize = XCreateFontCursor(X_Dpy, XC_bottom_right_corner);
Cursor_select = XCreateFontCursor(X_Dpy, XC_hand1);
Cursor_default = XCreateFontCursor(X_Dpy, XC_X_cursor);
Cursor_question = XCreateFontCursor(X_Dpy, XC_question_arrow);
}
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);
XftDrawDestroy(sc->menu.xftdraw);
XDestroyWindow(X_Dpy, sc->menu.win);
XUngrabKey(X_Dpy, AnyKey, AnyModifier, sc->rootwin);
}
XUngrabPointer(X_Dpy, CurrentTime);
XUngrabKeyboard(X_Dpy, CurrentTime);
for (i = 0; i < CF_NITEMS; i++)
XFreeCursor(X_Dpy, Conf.cursor[i]);
XSync(X_Dpy, False);
XSetInputFocus(X_Dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
XCloseDisplay(X_Dpy);
}
static int
x_wmerrorhandler(Display *dpy, XErrorEvent *e)
static void
x_setupscreen(struct screen_ctx *sc, u_int which)
{
errx(1, "root window unavailable - perhaps another wm is running?");
Window *wins, w0, w1;
XWindowAttributes winattr;
XSetWindowAttributes rootattr;
int fake;
u_int nwins, i;
sc->which = which;
sc->rootwin = RootWindow(X_Dpy, sc->which);
return(0);
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);
font_init(sc);
conf_font(&Conf, sc);
TAILQ_INIT(&sc->mruq);
/* Initialize menu window. */
menu_init(sc);
xu_setwmname(sc);
rootattr.event_mask = ChildMask|PropertyChangeMask|EnterWindowMask|
LeaveWindowMask|ColormapChangeMask|ButtonMask;
XChangeWindowAttributes(X_Dpy, sc->rootwin,
CWEventMask, &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;
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_errorhandler(Display *dpy, XErrorEvent *e)
{
#ifdef DEBUG
char msg[80], number[80], req[80];
{
char msg[80], number[80], req[80];
XGetErrorText(X_Dpy, e->error_code, msg, sizeof(msg));
(void)snprintf(number, sizeof(number), "%d", e->request_code);
XGetErrorDatabaseText(X_Dpy, "XRequest", number,
"<unknown>", req, sizeof(req));
XGetErrorText(X_Dpy, e->error_code, msg, sizeof(msg));
snprintf(number, sizeof(number), "%d", e->request_code);
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);
if (Starting &&
e->error_code == BadAccess &&
e->request_code == X_GrabKey)
errx(1, "root window unavailable - perhaps another "
"wm is running?");
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(-1, &status, WNOHANG)) > 0 ||
(pid < 0 && errno == EINTR))
;
break;
}
/* Collect dead children. */
while ((pid = waitpid(-1, &status, WNOHANG)) > 0 ||
(pid < 0 && errno == EINTR))
;
errno = save_errno;
}
void
__dead void
usage(void)
{
extern char *__progname;
(void)fprintf(stderr, "usage: %s [-c file] [-d display]\n",
__progname);
fprintf(stderr, "usage: %s [-c file] [-d display]\n", __progname);
exit(1);
}

927
calmwm.h

File diff suppressed because it is too large Load Diff

1254
client.c

File diff suppressed because it is too large Load Diff

1197
conf.c

File diff suppressed because it is too large Load Diff

217
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: August 24 2009 $
.Dt CWM 1
.Os
.Sh NAME
@ -28,40 +28,19 @@
.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.
.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
@ -70,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
@ -83,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.
@ -96,42 +79,26 @@ Reverse cycle through currently visible windows.
.It Ic CM-x
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
Cycle through active groups.
.It Ic M-Left
Reverse cycle through active groups.
.It Ic CMS-f
Toggle freezing geometry of current window.
.It Ic CM-s
Toggle stickiness of current window.
.It Ic CM-f
Toggle full-screen mode of current window.
.It Ic CM-m
Toggle maximization of current window.
.It Ic CM-equal
Toggle 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.
@ -143,16 +110,19 @@ will be executed via the configured terminal emulator.
.It Ic CM-w
Spawn
.Dq exec WindowManager
dialog, 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
@ -165,25 +135,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.
The options for
.Nm
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,
@ -196,16 +188,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 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.
@ -217,20 +230,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
.Pa ~/.cwmrc .
Selecting an item will spawn that application.
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
@ -238,14 +251,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
@ -256,14 +278,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.

View File

@ -1,2 +0,0 @@
untrusted comment: portable cwm release key public key
RWTdOib0PoIM0pmDAPnV2S9/AMRqTOVfTY/KAkFemdH13cqBDHdduTas

616
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 25 2010 $
.Dt CWMRC 5
.Os
.Sh NAME
@ -24,116 +24,61 @@
This manual page describes the
.Xr cwm 1
configuration file.
.Pp
The current line can be extended over multiple lines using a backslash
.Pq Sq \e .
Comments can be put anywhere in the file using a hash mark
.Pq Sq # ,
and extend to the end of the current line.
Care should be taken when commenting out multi-line text:
the comment is effective until the end of the entire block.
.Pp
Arguments containing whitespace should be surrounded by double quotes
.Pq \&" .
.Pp
The following options are accepted:
The following options are accepted in the configuration file:
.Pp
.Bl -tag -width Ds -compact
.It Ic autogroup Ar group windowclass
.It Ic autogroup Ar group windowname
.It Ic autogroup Ar group windowname,windowclass
Automatically add new windows to
.Ar group
if their class property matches
.Ar windowclass ,
or if their name and class properties match
.Ar windowname
and
.Ar windowclass ,
respectively.
The more specific last match wins.
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 .
.Pp
.It Ic bind-key Ar key function
Bind or rebind key
.Ar key
to
.Ar function .
.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 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.
.Pp
.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 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.
.El
.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.
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
.It Ic borderwidth Ar pixels
Set the window border width to
@ -142,27 +87,12 @@ Set the window border width to
.It Ic color activeborder Ar color
Set the color of the active border.
.Pp
.It Ic color font Ar color
Set menu font color.
.Pp
.It Ic color selfont Ar color
Set font color for selected menu item.
.Pp
.It Ic color groupborder Ar color
Set the color of the border while grouping a window.
.Pp
.It Ic color inactiveborder Ar color
Set the color of the inactive border.
.Pp
.It Ic color menubg Ar color
Set menu background color.
.Pp
.It Ic color menufg Ar color
Set menu foreground color.
.Pp
.It Ic color urgencyborder Ar color
Set the color of the border of a window indicating urgency.
.Pp
.It Ic color ungroupborder Ar color
Set the color of the border while ungrouping a window.
.Pp
@ -183,7 +113,7 @@ 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
@ -212,233 +142,57 @@ 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.
.Pp
.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.
.Pp
.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.
.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-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-delete
Delete 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 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 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 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"
@ -461,25 +215,207 @@ 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 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 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_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

99
font.c Normal file
View File

@ -0,0 +1,99 @@
/*
* font.c - cwm font abstraction
*
* 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.
*/
#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->fontheight);
}
void
font_init(struct screen_ctx *sc)
{
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 (!XftColorAllocName(X_Dpy, DefaultVisual(X_Dpy, sc->which),
DefaultColormap(X_Dpy, sc->which), "black", &sc->xftcolor))
errx(1, "XftColorAllocName");
}
int
font_width(struct screen_ctx *sc, const char *text, int len)
{
XGlyphInfo extents;
XftTextExtents8(X_Dpy, sc->font, (const XftChar8*)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);
/* Really needs to be UTF8'd. */
XftDrawString8(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);
}

575
group.c
View File

@ -16,98 +16,111 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $OpenBSD$
* $Id$
*/
#include <sys/types.h>
#include "queue.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_setactive(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 *);
const char *num_to_name[] = {
const char *shortcut_to_name[] = {
"nogroup", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine"
};
void
group_assign(struct group_ctx *gc, struct client_ctx *cc)
static void
group_add(struct group_ctx *gc, struct client_ctx *cc)
{
if (cc->gc != NULL)
TAILQ_REMOVE(&cc->gc->clientq, cc, group_entry);
long no;
if (cc == NULL || gc == NULL)
errx(1, "group_add: a ctx is NULL");
if ((gc != NULL) && (gc->num == 0))
gc = NULL;
no = gc->shortcut - 1;
cc->gc = gc;
if (cc->group == gc)
return;
if (cc->gc != NULL)
TAILQ_INSERT_TAIL(&gc->clientq, cc, group_entry);
if (cc->group != NULL)
TAILQ_REMOVE(&cc->group->clients, cc, group_entry);
xu_ewmh_net_wm_desktop(cc);
}
XChangeProperty(X_Dpy, cc->win, _NET_WM_DESKTOP, XA_CARDINAL,
32, PropModeReplace, (unsigned char *)&no, 1);
void
group_hide(struct group_ctx *gc)
{
struct client_ctx *cc;
screen_updatestackingorder(gc->sc);
TAILQ_FOREACH(cc, &gc->clientq, group_entry) {
if (!(cc->flags & CLIENT_STICKY))
client_hide(cc);
}
}
void
group_show(struct group_ctx *gc)
{
struct client_ctx *cc;
TAILQ_FOREACH(cc, &gc->clientq, group_entry) {
if (!(cc->flags & CLIENT_STICKY))
client_unhide(cc);
}
group_restack(gc);
group_setactive(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 client_ctx *cc;
Window *winlist;
int i, lastempty = -1;
int nwins = 0, highstack = 0;
u_int i;
int lastempty = -1;
TAILQ_FOREACH(cc, &gc->clientq, group_entry) {
if (cc->stackingorder > highstack)
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, &gc->clientq, group_entry) {
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) {
@ -117,104 +130,141 @@ 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)
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(num_to_name[num]);
gc->num = num;
TAILQ_INIT(&gc->clientq);
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);
TAILQ_INSERT_TAIL(&sc->groupq, gc, entry);
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);
}
if (num == 1)
group_setactive(gc);
/* 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_setactive(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
group_movetogroup(struct client_ctx *cc, int idx)
{
struct screen_ctx *sc = cc->sc;
struct group_ctx *gc;
if (idx < 0 || idx >= Conf.ngroups)
errx(1, "%s: index out of range (%d)", __func__, idx);
if (idx < 0 || idx >= CALMWM_NGROUPS)
err(1, "group_movetogroup: index out of range (%d)", idx);
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx)
break;
}
if (cc->gc == gc)
return;
if (gc->num != 0 && group_holds_only_hidden(gc))
if(sc->group_active != &sc->groups[idx])
client_hide(cc);
group_assign(gc, cc);
group_add(&sc->groups[idx], cc);
}
/*
* Colouring for groups upon add/remove.
*/
void
group_toggle_membership_enter(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 (gc == cc->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);
}
void
group_toggle_membership_leave(struct client_ctx *cc)
group_sticky_toggle_exit(struct client_ctx *cc)
{
cc->flags &= ~CLIENT_HIGHLIGHT;
cc->highlight = 0;
client_draw_border(cc);
}
int
group_holds_only_sticky(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 client_ctx *cc;
int same = 0;
TAILQ_FOREACH(cc, &gc->clientq, group_entry) {
if (!(cc->flags & CLIENT_STICKY))
return(0);
TAILQ_FOREACH(cc, &gc->clients, group_entry) {
if (gc->hidden == ((cc->flags & CLIENT_HIDDEN) ? 1 : 0))
same++;
}
return(1);
}
int
group_holds_only_hidden(struct group_ctx *gc)
{
struct client_ctx *cc;
TAILQ_FOREACH(cc, &gc->clientq, group_entry) {
if (!(cc->flags & (CLIENT_HIDDEN | CLIENT_STICKY)))
return(0);
}
return(1);
if (same == 0)
gc->hidden = !gc->hidden;
}
void
@ -222,158 +272,267 @@ group_hidetoggle(struct screen_ctx *sc, int idx)
{
struct group_ctx *gc;
if (idx < 0 || idx >= Conf.ngroups)
errx(1, "%s: index out of range (%d)", __func__, idx);
if (idx < 0 || idx >= CALMWM_NGROUPS)
err(1, "group_hidetoggle: index out of range (%d)", idx);
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == idx)
break;
}
gc = &sc->groups[idx];
group_fix_hidden_state(gc);
if (group_holds_only_hidden(gc))
group_show(gc);
if (gc->hidden)
group_show(sc, gc);
else {
group_hide(gc);
/* make clients stick to empty group */
if (TAILQ_EMPTY(&gc->clientq))
group_setactive(gc);
group_hide(sc, gc);
/* XXX wtf? */
if (TAILQ_EMPTY(&gc->clients))
group_setactive(sc, idx);
}
}
void
group_only(struct screen_ctx *sc, int idx)
{
struct group_ctx *gc;
int i;
if (idx < 0 || idx >= Conf.ngroups)
errx(1, "%s: index out of range (%d)", __func__, idx);
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);
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)
group_cycle(struct screen_ctx *sc, int reverse)
{
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 = reverse ? TAILQ_PREV(gc, group_ctx_q, entry) :
TAILQ_NEXT(gc, entry);
if (gc == NULL)
gc = reverse ? 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_setactive(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;
struct screen_ctx *sc;
struct group_ctx *gc;
struct menu *mi;
struct menu_q menuq;
int i;
return(((newgc = TAILQ_PREV(gc, group_q, entry)) != NULL) ?
newgc : TAILQ_LAST(&sc->groupq, group_q));
sc = screen_fromroot(e->root);
TAILQ_INIT(&menuq);
for (i = 0; i < CALMWM_NGROUPS; i++) {
gc = &sc->groups[i];
if (TAILQ_EMPTY(&gc->clients))
continue;
mi = xcalloc(1, sizeof(*mi));
if (gc->hidden)
snprintf(mi->text, sizeof(mi->text), "%d: [%s]",
gc->shortcut, sc->group_names[i]);
else
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);
}
}
void
group_alltoggle(struct screen_ctx *sc)
{
struct group_ctx *gc;
int i;
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (sc->hideall)
group_show(gc);
for (i = 0; i < CALMWM_NGROUPS; i++) {
if (sc->group_hideall)
group_show(sc, &sc->groups[i]);
else
group_hide(gc);
group_hide(sc, &sc->groups[i]);
}
sc->hideall = !sc->hideall;
sc->group_hideall = (!sc->group_hideall);
}
int
group_restore(struct client_ctx *cc)
{
struct screen_ctx *sc = cc->sc;
struct group_ctx *gc;
int num;
long *grpnum;
if (xu_getprop(cc->win, ewmh[_NET_WM_DESKTOP], XA_CARDINAL, 1L,
(unsigned char **)&grpnum) <= 0)
return(0);
num = (*grpnum == -1) ? 0 : *grpnum;
num = MIN(num, (Conf.ngroups - 1));
XFree(grpnum);
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == num) {
group_assign(gc, cc);
return(1);
}
}
return(0);
}
int
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->ch.res_class == NULL || cc->ch.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->ch.res_class) == 0) {
if ((ag->name != NULL) &&
(strcmp(ag->name, cc->ch.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)
free(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;
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);
}

117
input.c Normal file
View File

@ -0,0 +1,117 @@
/*
* calmwm - the calm window manager
*
* Copyright (c) 2004 Marius Aamodt 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.
*
* $Id$
*/
#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
input_keycodetrans(KeyCode kc, u_int state, enum ctltype *ctl, char *chr)
{
int ks;
*ctl = CTL_NONE;
*chr = '\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_Return:
*ctl = CTL_RETURN;
break;
case XK_Up:
*ctl = CTL_UP;
break;
case XK_Down:
*ctl = CTL_DOWN;
break;
case XK_Escape:
*ctl = CTL_ABORT;
break;
}
if (*ctl == CTL_NONE && (state & ControlMask)) {
switch (ks) {
case XK_s:
case XK_S:
/* Emacs "next" */
*ctl = CTL_DOWN;
break;
case XK_r:
case XK_R:
/* Emacs "previous" */
*ctl = CTL_UP;
break;
case XK_u:
case XK_U:
*ctl = CTL_WIPE;
break;
case XK_h:
case XK_H:
*ctl = CTL_ERASEONE;
break;
case XK_a:
case XK_A:
*ctl = CTL_ALL;
break;
}
}
if (*ctl == CTL_NONE && (state & Mod1Mask)) {
switch (ks) {
case XK_j:
case XK_J:
/* Vi "down" */
*ctl = CTL_DOWN;
break;
case XK_k:
case XK_K:
/* Vi "up" */
*ctl = CTL_UP;
break;
}
}
if (*ctl != CTL_NONE)
return (0);
/*
* For regular characters, only (part of, actually) Latin 1
* for now.
*/
if (ks < 0x20 || ks > 0x07e)
return (-1);
*chr = (char)ks;
return (0);
}

824
kbfunc.c
View File

@ -1,7 +1,7 @@
/*
* calmwm - the calm window manager
* calmwm - the calm window manager
*
* Copyright (c) 2004 Martin Murray <mmurray@monkey.org>
* Copyright (c) 2004 Martin Murray <mmurray@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
@ -15,521 +15,250 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $OpenBSD$
* $Id$
*/
/* For FreeBSD. */
#define _WITH_GETLINE
#include <sys/types.h>
#include "queue.h"
#include <sys/param.h>
#include <sys/queue.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "calmwm.h"
#define KNOWN_HOSTS ".ssh/known_hosts"
#define HASH_MARKER "|1|"
extern sig_atomic_t cwm_status;
static void kbfunc_amount(int, int, int *, int *);
static void kbfunc_client_move_kb(void *, struct cargs *);
static void kbfunc_client_move_mb(void *, struct cargs *);
static void kbfunc_client_resize_kb(void *, struct cargs *);
static void kbfunc_client_resize_mb(void *, struct cargs *);
extern int _xev_quit;
void
kbfunc_cwm_status(void *ctx, struct cargs *cargs)
kbfunc_client_lower(struct client_ctx *cc, union arg *arg)
{
cwm_status = cargs->flag;
client_lower(cc);
}
static void
kbfunc_amount(int flags, int amt, int *mx, int *my)
void
kbfunc_client_raise(struct client_ctx *cc, union arg *arg)
{
#define CWM_FACTOR 10
client_raise(cc);
}
if (flags & CWM_BIGAMOUNT)
amt *= CWM_FACTOR;
#define typemask (CWM_MOVE | CWM_RESIZE | CWM_PTRMOVE)
#define movemask (CWM_UP | CWM_DOWN | CWM_LEFT | CWM_RIGHT)
void
kbfunc_moveresize(struct client_ctx *cc, union arg *arg)
{
struct screen_ctx *sc;
int x, y, flags, amt;
u_int mx, my;
switch (flags & DIRECTIONMASK) {
sc = cc->sc;
mx = my = 0;
flags = arg->i;
amt = Conf.mamount;
if (flags & CWM_BIGMOVE) {
flags -= CWM_BIGMOVE;
amt = amt * 10;
}
switch (flags & movemask) {
case CWM_UP:
*my -= amt;
my -= amt;
break;
case CWM_DOWN:
*my += amt;
my += amt;
break;
case CWM_RIGHT:
*mx += amt;
mx += amt;
break;
case CWM_LEFT:
*mx -= amt;
mx -= amt;
break;
}
}
switch (flags & typemask) {
case CWM_MOVE:
cc->geom.y += my;
if (cc->geom.y + cc->geom.height < 0)
cc->geom.y = -cc->geom.height;
if (cc->geom.y > cc->sc->ymax - 1)
cc->geom.y = cc->sc->ymax - 1;
void
kbfunc_ptrmove(void *ctx, struct cargs *cargs)
{
struct screen_ctx *sc = ctx;
int x, y;
int mx = 0, my = 0;
cc->geom.x += mx;
if (cc->geom.x + cc->geom.width < 0)
cc->geom.x = -cc->geom.width;
if (cc->geom.x > cc->sc->xmax - 1)
cc->geom.x = cc->sc->xmax - 1;
kbfunc_amount(cargs->flag, Conf.mamount, &mx, &my);
xu_ptr_getpos(sc->rootwin, &x, &y);
xu_ptr_setpos(sc->rootwin, x + mx, y + my);
}
void
kbfunc_client_move(void *ctx, struct cargs *cargs)
{
if (cargs->xev == CWM_XEV_BTN)
kbfunc_client_move_mb(ctx, cargs);
else
kbfunc_client_move_kb(ctx, cargs);
}
void
kbfunc_client_resize(void *ctx, struct cargs *cargs)
{
if (cargs->xev == CWM_XEV_BTN)
kbfunc_client_resize_mb(ctx, cargs);
else
kbfunc_client_resize_kb(ctx, cargs);
}
static void
kbfunc_client_move_kb(void *ctx, struct cargs *cargs)
{
struct client_ctx *cc = ctx;
struct screen_ctx *sc = cc->sc;
struct geom area;
int mx = 0, my = 0;
if (cc->flags & CLIENT_FREEZE)
return;
kbfunc_amount(cargs->flag, Conf.mamount, &mx, &my);
cc->geom.x += mx;
if (cc->geom.x < -(cc->geom.w + cc->bwidth - 1))
cc->geom.x = -(cc->geom.w + cc->bwidth - 1);
if (cc->geom.x > (sc->view.w - cc->bwidth - 1))
cc->geom.x = sc->view.w - cc->bwidth - 1;
cc->geom.y += my;
if (cc->geom.y < -(cc->geom.h + cc->bwidth - 1))
cc->geom.y = -(cc->geom.h + cc->bwidth - 1);
if (cc->geom.y > (sc->view.h - cc->bwidth - 1))
cc->geom.y = sc->view.h - cc->bwidth - 1;
area = screen_area(sc,
cc->geom.x + cc->geom.w / 2,
cc->geom.y + cc->geom.h / 2, CWM_GAP);
cc->geom.x += client_snapcalc(cc->geom.x,
cc->geom.x + cc->geom.w + (cc->bwidth * 2),
area.x, area.x + area.w, sc->snapdist);
cc->geom.y += client_snapcalc(cc->geom.y,
cc->geom.y + cc->geom.h + (cc->bwidth * 2),
area.y, area.y + area.h, sc->snapdist);
client_move(cc);
client_ptr_inbound(cc, 1);
}
static void
kbfunc_client_move_mb(void *ctx, struct cargs *cargs)
{
struct client_ctx *cc = ctx;
XEvent ev;
Time ltime = 0;
struct screen_ctx *sc = cc->sc;
struct geom area;
int move = 1;
client_raise(cc);
if (cc->flags & CLIENT_FREEZE)
return;
client_ptr_inbound(cc, 1);
if (XGrabPointer(X_Dpy, cc->win, False, MOUSEMASK,
GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_MOVE],
CurrentTime) != GrabSuccess)
return;
menu_windraw(sc, cc->win, "%4d, %-4d", cc->geom.x, cc->geom.y);
while (move) {
XWindowEvent(X_Dpy, cc->win, MOUSEMASK, &ev);
switch (ev.type) {
case MotionNotify:
/* not more than 60 times / second */
if ((ev.xmotion.time - ltime) <= (1000 / 60))
continue;
ltime = ev.xmotion.time;
cc->geom.x = ev.xmotion.x_root - cc->ptr.x - cc->bwidth;
cc->geom.y = ev.xmotion.y_root - cc->ptr.y - cc->bwidth;
area = screen_area(sc,
cc->geom.x + cc->geom.w / 2,
cc->geom.y + cc->geom.h / 2, CWM_GAP);
cc->geom.x += client_snapcalc(cc->geom.x,
cc->geom.x + cc->geom.w + (cc->bwidth * 2),
area.x, area.x + area.w, sc->snapdist);
cc->geom.y += client_snapcalc(cc->geom.y,
cc->geom.y + cc->geom.h + (cc->bwidth * 2),
area.y, area.y + area.h, sc->snapdist);
client_move(cc);
menu_windraw(sc, cc->win,
"%4d, %-4d", cc->geom.x, cc->geom.y);
break;
case ButtonRelease:
move = 0;
break;
}
}
if (ltime)
client_move(cc);
XUnmapWindow(X_Dpy, sc->menu.win);
XReparentWindow(X_Dpy, sc->menu.win, sc->rootwin, 0, 0);
XUngrabPointer(X_Dpy, CurrentTime);
}
xu_ptr_getpos(cc->win, &x, &y);
cc->ptr.y = y + my;
cc->ptr.x = x + mx;
client_ptrwarp(cc);
break;
case CWM_RESIZE:
if ((cc->geom.height += my) < 1)
cc->geom.height = 1;
if ((cc->geom.width += mx) < 1)
cc->geom.width = 1;
client_resize(cc);
static void
kbfunc_client_resize_kb(void *ctx, struct cargs *cargs)
{
struct client_ctx *cc = ctx;
int mx = 0, my = 0;
int amt = 1;
if (cc->flags & CLIENT_FREEZE)
return;
if (!(cc->hint.flags & PResizeInc))
amt = Conf.mamount;
kbfunc_amount(cargs->flag, amt, &mx, &my);
if ((cc->geom.w += mx * cc->hint.incw) < cc->hint.minw)
cc->geom.w = cc->hint.minw;
if ((cc->geom.h += my * cc->hint.inch) < cc->hint.minh)
cc->geom.h = cc->hint.minh;
if (cc->geom.x + cc->geom.w + cc->bwidth - 1 < 0)
cc->geom.x = -(cc->geom.w + cc->bwidth - 1);
if (cc->geom.y + cc->geom.h + cc->bwidth - 1 < 0)
cc->geom.y = -(cc->geom.h + cc->bwidth - 1);
client_resize(cc, 1);
client_ptr_inbound(cc, 1);
}
static void
kbfunc_client_resize_mb(void *ctx, struct cargs *cargs)
{
struct client_ctx *cc = ctx;
XEvent ev;
Time ltime = 0;
struct screen_ctx *sc = cc->sc;
int resize = 1;
if (cc->flags & CLIENT_FREEZE)
return;
client_raise(cc);
client_ptrsave(cc);
xu_ptr_setpos(cc->win, cc->geom.w, cc->geom.h);
if (XGrabPointer(X_Dpy, cc->win, False, MOUSEMASK,
GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_RESIZE],
CurrentTime) != GrabSuccess)
return;
menu_windraw(sc, cc->win, "%4d x %-4d", cc->dim.w, cc->dim.h);
while (resize) {
XWindowEvent(X_Dpy, cc->win, MOUSEMASK, &ev);
switch (ev.type) {
case MotionNotify:
/* not more than 60 times / second */
if ((ev.xmotion.time - ltime) <= (1000 / 60))
continue;
ltime = ev.xmotion.time;
cc->geom.w = ev.xmotion.x;
cc->geom.h = ev.xmotion.y;
client_applysizehints(cc);
client_resize(cc, 1);
menu_windraw(sc, cc->win,
"%4d x %-4d", cc->dim.w, cc->dim.h);
break;
case ButtonRelease:
resize = 0;
break;
/* Make sure the pointer stays within the window. */
xu_ptr_getpos(cc->win, &cc->ptr.x, &cc->ptr.y);
if (cc->ptr.x > cc->geom.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);
break;
case CWM_PTRMOVE:
if (cc) {
xu_ptr_getpos(cc->win, &x, &y);
xu_ptr_setpos(cc->win, x + mx, y + my);
} else {
xu_ptr_getpos(sc->rootwin, &x, &y);
xu_ptr_setpos(sc->rootwin, x + mx, y + my);
}
break;
default:
warnx("invalid flags passed to kbfunc_client_moveresize");
}
if (ltime)
client_resize(cc, 1);
XUnmapWindow(X_Dpy, sc->menu.win);
XReparentWindow(X_Dpy, sc->menu.win, sc->rootwin, 0, 0);
XUngrabPointer(X_Dpy, CurrentTime);
/* Make sure the pointer stays within the window. */
client_ptr_inbound(cc, 0);
}
void
kbfunc_client_delete(void *ctx, struct cargs *cargs)
kbfunc_client_search(struct client_ctx *cc, union arg *arg)
{
client_send_delete(ctx);
}
void
kbfunc_client_lower(void *ctx, struct cargs *cargs)
{
client_ptrsave(ctx);
client_lower(ctx);
}
void
kbfunc_client_raise(void *ctx, struct cargs *cargs)
{
client_raise(ctx);
}
void
kbfunc_client_hide(void *ctx, struct cargs *cargs)
{
client_hide(ctx);
}
void
kbfunc_client_toggle_freeze(void *ctx, struct cargs *cargs)
{
client_toggle_freeze(ctx);
}
void
kbfunc_client_toggle_sticky(void *ctx, struct cargs *cargs)
{
client_toggle_sticky(ctx);
}
void
kbfunc_client_toggle_fullscreen(void *ctx, struct cargs *cargs)
{
client_toggle_fullscreen(ctx);
}
void
kbfunc_client_toggle_maximize(void *ctx, struct cargs *cargs)
{
client_toggle_maximize(ctx);
}
void
kbfunc_client_toggle_hmaximize(void *ctx, struct cargs *cargs)
{
client_toggle_hmaximize(ctx);
}
void
kbfunc_client_toggle_vmaximize(void *ctx, struct cargs *cargs)
{
client_toggle_vmaximize(ctx);
}
void
kbfunc_client_htile(void *ctx, struct cargs *cargs)
{
client_htile(ctx);
}
void
kbfunc_client_vtile(void *ctx, struct cargs *cargs)
{
client_vtile(ctx);
}
void
kbfunc_client_cycle(void *ctx, struct cargs *cargs)
{
client_cycle(ctx, cargs->flag);
}
void
kbfunc_client_toggle_group(void *ctx, struct cargs *cargs)
{
struct client_ctx *cc = ctx;
/* For X apps that steal events. */
if (cargs->xev == CWM_XEV_KEY)
XGrabKeyboard(X_Dpy, cc->win, True,
GrabModeAsync, GrabModeAsync, CurrentTime);
group_toggle_membership_enter(cc);
}
void
kbfunc_client_movetogroup(void *ctx, struct cargs *cargs)
{
group_movetogroup(ctx, cargs->flag);
}
void
kbfunc_group_toggle(void *ctx, struct cargs *cargs)
{
group_hidetoggle(ctx, cargs->flag);
}
void
kbfunc_group_only(void *ctx, struct cargs *cargs)
{
group_only(ctx, cargs->flag);
}
void
kbfunc_group_cycle(void *ctx, struct cargs *cargs)
{
group_cycle(ctx, cargs->flag);
}
void
kbfunc_group_alltoggle(void *ctx, struct cargs *cargs)
{
group_alltoggle(ctx);
}
void
kbfunc_menu_client(void *ctx, struct cargs *cargs)
{
struct screen_ctx *sc = ctx;
struct client_ctx *cc, *old_cc;
struct screen_ctx *sc;
struct client_ctx *old_cc;
struct menu *mi;
struct menu_q menuq;
int m = (cargs->xev == CWM_XEV_BTN);
int all = (cargs->flag & CWM_MENU_WINDOW_ALL);
sc = cc->sc;
old_cc = client_current();
TAILQ_INIT(&menuq);
TAILQ_FOREACH(cc, &sc->clientq, entry) {
if (!all) {
if (cc->flags & CLIENT_HIDDEN)
menuq_add(&menuq, cc, NULL);
} else
menuq_add(&menuq, cc, NULL);
TAILQ_FOREACH(cc, &Clientq, entry) {
mi = xcalloc(1, sizeof(*mi));
strlcpy(mi->text, cc->name, sizeof(mi->text));
mi->ctx = cc;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
if ((mi = menu_filter(sc, &menuq,
(m) ? NULL : "window", NULL,
((m) ? CWM_MENU_LIST : 0),
if ((mi = menu_filter(sc, &menuq, "window", NULL, 0,
search_match_client, search_print_client)) != NULL) {
cc = (struct client_ctx *)mi->ctx;
if (cc->flags & CLIENT_HIDDEN)
client_unhide(cc);
else
client_raise(cc);
if (old_cc)
client_ptrsave(old_cc);
client_ptrwarp(cc);
}
menuq_clear(&menuq);
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
}
void
kbfunc_menu_cmd(void *ctx, struct cargs *cargs)
kbfunc_menu_search(struct client_ctx *cc, union arg *arg)
{
struct screen_ctx *sc = ctx;
struct cmd_ctx *cmd;
struct screen_ctx *sc;
struct cmd *cmd;
struct menu *mi;
struct menu_q menuq;
int m = (cargs->xev == CWM_XEV_BTN);
sc = cc->sc;
TAILQ_INIT(&menuq);
TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
if ((strcmp(cmd->name, "lock") == 0) ||
(strcmp(cmd->name, "term") == 0))
continue;
/* search_match_text() needs mi->text */
menuq_add(&menuq, cmd, "%s", cmd->name);
mi = xcalloc(1, sizeof(*mi));
strlcpy(mi->text, cmd->label, sizeof(mi->text));
mi->ctx = cmd;
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
if ((mi = menu_filter(sc, &menuq,
(m) ? NULL : "application", NULL,
((m) ? CWM_MENU_LIST : 0),
search_match_text, search_print_cmd)) != NULL) {
cmd = (struct cmd_ctx *)mi->ctx;
u_spawn(cmd->path);
}
if ((mi = menu_filter(sc, &menuq, "application", NULL, 0,
search_match_text, NULL)) != NULL)
u_spawn(((struct cmd *)mi->ctx)->image);
menuq_clear(&menuq);
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
}
void
kbfunc_menu_group(void *ctx, struct cargs *cargs)
kbfunc_client_cycle(struct client_ctx *cc, union arg *arg)
{
struct screen_ctx *sc = ctx;
struct group_ctx *gc;
struct menu *mi;
struct menu_q menuq;
int m = (cargs->xev == CWM_XEV_BTN);
struct screen_ctx *sc;
TAILQ_INIT(&menuq);
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (group_holds_only_sticky(gc))
continue;
menuq_add(&menuq, gc, "%d %s", gc->num, gc->name);
}
sc = cc->sc;
if ((mi = menu_filter(sc, &menuq,
(m) ? NULL : "group", NULL, (CWM_MENU_LIST),
search_match_text, search_print_group)) != NULL) {
gc = (struct group_ctx *)mi->ctx;
(group_holds_only_hidden(gc)) ?
group_show(gc) : group_hide(gc);
}
/* XXX for X apps that ignore events */
XGrabKeyboard(X_Dpy, sc->rootwin, True,
GrabModeAsync, GrabModeAsync, CurrentTime);
menuq_clear(&menuq);
client_cycle(sc, arg->i);
}
void
kbfunc_menu_exec(void *ctx, struct cargs *cargs)
kbfunc_client_hide(struct client_ctx *cc, union arg *arg)
{
client_hide(cc);
}
void
kbfunc_cmdexec(struct client_ctx *cc, union arg *arg)
{
u_spawn(arg->c);
}
void
kbfunc_term(struct client_ctx *cc, union arg *arg)
{
u_spawn(Conf.termpath);
}
void
kbfunc_lock(struct client_ctx *cc, union arg *arg)
{
u_spawn(Conf.lockpath);
}
void
kbfunc_exec(struct client_ctx *cc, union arg *arg)
{
#define NPATHS 256
struct screen_ctx *sc = ctx;
char **ap, *paths[NPATHS], *path, *pathcpy;
char tpath[PATH_MAX];
struct stat sb;
const char *label;
struct screen_ctx *sc;
char **ap, *paths[NPATHS], *path, *pathcpy, *label;
char tpath[MAXPATHLEN];
DIR *dirp;
struct dirent *dp;
struct menu *mi;
struct menu_q menuq;
int l, i, cmd = cargs->flag;
int l, i, cmd = arg->i;
sc = cc->sc;
switch (cmd) {
case CWM_MENU_EXEC_EXEC:
label = "exec";
break;
case CWM_MENU_EXEC_WM:
label = "wm";
break;
default:
errx(1, "%s: invalid cmd %d", __func__, cmd);
/* NOTREACHED */
case CWM_EXEC_PROGRAM:
label = "exec";
break;
case CWM_EXEC_WM:
label = "wm";
break;
default:
err(1, "kbfunc_exec: invalid cmd %d", cmd);
/*NOTREACHED*/
}
TAILQ_INIT(&menuq);
@ -549,162 +278,215 @@ kbfunc_menu_exec(void *ctx, struct cargs *cargs)
continue;
while ((dp = readdir(dirp)) != NULL) {
(void)memset(tpath, '\0', sizeof(tpath));
/* skip everything but regular files and symlinks */
if (dp->d_type != DT_REG && dp->d_type != DT_LNK)
continue;
memset(tpath, '\0', sizeof(tpath));
l = snprintf(tpath, sizeof(tpath), "%s/%s", paths[i],
dp->d_name);
if (l == -1 || l >= sizeof(tpath))
/* check for truncation etc */
if (l == -1 || l >= (int)sizeof(tpath))
continue;
/* Skip everything but regular files and symlinks. */
if (dp->d_type != DT_REG && dp->d_type != DT_LNK) {
/* lstat(2) in case d_type isn't supported. */
if (lstat(tpath, &sb) == -1)
continue;
if (!S_ISREG(sb.st_mode) &&
!S_ISLNK(sb.st_mode))
continue;
if (access(tpath, X_OK) == 0) {
mi = xcalloc(1, sizeof(*mi));
strlcpy(mi->text, dp->d_name, sizeof(mi->text));
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
if (access(tpath, X_OK) == 0)
menuq_add(&menuq, NULL, "%s", dp->d_name);
}
(void)closedir(dirp);
}
free(path);
xfree(path);
if ((mi = menu_filter(sc, &menuq, label, NULL,
(CWM_MENU_DUMMY | CWM_MENU_FILE),
search_match_exec, search_print_text)) != NULL) {
if ((mi = menu_filter(sc, &menuq, label, NULL, 1,
search_match_exec, NULL)) != NULL) {
if (mi->text[0] == '\0')
goto out;
switch (cmd) {
case CWM_MENU_EXEC_EXEC:
u_spawn(mi->text);
break;
case CWM_MENU_EXEC_WM:
cwm_status = CWM_EXEC_WM;
free(Conf.wm_argv);
Conf.wm_argv = xstrdup(mi->text);
break;
default:
errx(1, "%s: egad, cmd changed value!", __func__);
/* NOTREACHED */
case CWM_EXEC_PROGRAM:
u_spawn(mi->text);
break;
case CWM_EXEC_WM:
u_exec(mi->text);
warn("%s", mi->text);
break;
default:
err(1, "kb_func: egad, cmd changed value!");
break;
}
}
out:
if (mi != NULL && mi->dummy)
free(mi);
menuq_clear(&menuq);
xfree(mi);
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
}
void
kbfunc_menu_ssh(void *ctx, struct cargs *cargs)
kbfunc_ssh(struct client_ctx *cc, union arg *arg)
{
struct screen_ctx *sc = ctx;
struct cmd_ctx *cmd;
struct screen_ctx *sc;
struct menu *mi;
struct menu_q menuq;
FILE *fp;
char *buf, *lbuf, *p;
char hostbuf[_POSIX_HOST_NAME_MAX+1];
char path[PATH_MAX];
char *buf, *lbuf, *p, *home;
char hostbuf[MAXHOSTNAMELEN], filename[MAXPATHLEN];
char cmd[256];
int l;
size_t len;
ssize_t slen;
TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
if (strcmp(cmd->name, "term") == 0)
break;
}
sc = cc->sc;
if ((home = getenv("HOME")) == NULL)
return;
l = snprintf(filename, sizeof(filename), "%s/%s", home, KNOWN_HOSTS);
if (l == -1 || l >= sizeof(filename))
return;
if ((fp = fopen(filename, "r")) == NULL)
return;
TAILQ_INIT(&menuq);
if ((fp = fopen(Conf.known_hosts, "r")) == NULL) {
warn("%s: %s", __func__, Conf.known_hosts);
goto menu;
}
lbuf = NULL;
len = 0;
while ((slen = getline(&lbuf, &len, fp)) != -1) {
buf = lbuf;
if (buf[slen - 1] == '\n')
buf[slen - 1] = '\0';
while ((buf = fgetln(fp, &len))) {
if (buf[len - 1] == '\n')
buf[len - 1] = '\0';
else {
/* EOF without EOL, copy and add the NUL */
lbuf = xmalloc(len + 1);
memcpy(lbuf, buf, len);
lbuf[len] = '\0';
buf = lbuf;
}
/* skip hashed hosts */
if (strncmp(buf, HASH_MARKER, strlen(HASH_MARKER)) == 0)
continue;
for (p = buf; *p != ',' && *p != ' ' && p != buf + slen; p++) {
for (p = buf; *p != ',' && *p != ' ' && p != buf + len; p++) {
/* do nothing */
}
/* ignore badness */
if (p - buf + 1 > sizeof(hostbuf))
continue;
(void)strlcpy(hostbuf, buf, p - buf + 1);
menuq_add(&menuq, NULL, "%s", hostbuf);
(void) strlcpy(hostbuf, buf, p - buf + 1);
mi = xcalloc(1, sizeof(*mi));
(void) strlcpy(mi->text, hostbuf, sizeof(mi->text));
TAILQ_INSERT_TAIL(&menuq, mi, entry);
}
free(lbuf);
if (ferror(fp))
err(1, "%s", path);
(void)fclose(fp);
menu:
if ((mi = menu_filter(sc, &menuq, "ssh", NULL, (CWM_MENU_DUMMY),
search_match_text, search_print_text)) != NULL) {
xfree(lbuf);
fclose(fp);
if ((mi = menu_filter(sc, &menuq, "ssh", NULL, 1,
search_match_exec, NULL)) != NULL) {
if (mi->text[0] == '\0')
goto out;
l = snprintf(path, sizeof(path), "%s -T '[ssh] %s' -e ssh %s",
cmd->path, mi->text, mi->text);
if (l == -1 || l >= sizeof(path))
goto out;
u_spawn(path);
l = snprintf(cmd, sizeof(cmd), "%s -e ssh %s", Conf.termpath,
mi->text);
if (l != -1 && l < sizeof(cmd))
u_spawn(cmd);
}
out:
if (mi != NULL && mi->dummy)
free(mi);
menuq_clear(&menuq);
xfree(mi);
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
TAILQ_REMOVE(&menuq, mi, entry);
xfree(mi);
}
}
void
kbfunc_client_menu_label(void *ctx, struct cargs *cargs)
kbfunc_client_label(struct client_ctx *cc, union arg *arg)
{
struct client_ctx *cc = ctx;
struct menu *mi;
struct menu_q menuq;
struct menu *mi;
struct menu_q menuq;
TAILQ_INIT(&menuq);
/* dummy is set, so this will always return */
mi = menu_filter(cc->sc, &menuq, "label", cc->label, (CWM_MENU_DUMMY),
search_match_text, search_print_text);
mi = menu_filter(cc->sc, &menuq, "label", cc->label, 1,
search_match_text, NULL);
if (!mi->abort) {
free(cc->label);
if (cc->label != NULL)
xfree(cc->label);
cc->label = xstrdup(mi->text);
}
free(mi);
xfree(mi);
}
void
kbfunc_exec_cmd(void *ctx, struct cargs *cargs)
kbfunc_client_delete(struct client_ctx *cc, union arg *arg)
{
u_spawn(cargs->cmd);
client_send_delete(cc);
}
void
kbfunc_exec_term(void *ctx, struct cargs *cargs)
kbfunc_client_group(struct client_ctx *cc, union arg *arg)
{
struct cmd_ctx *cmd;
TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
if (strcmp(cmd->name, "term") == 0)
u_spawn(cmd->path);
}
group_hidetoggle(cc->sc, KBTOGROUP(arg->i));
}
void
kbfunc_exec_lock(void *ctx, struct cargs *cargs)
kbfunc_client_grouponly(struct client_ctx *cc, union arg *arg)
{
struct cmd_ctx *cmd;
TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
if (strcmp(cmd->name, "lock") == 0)
u_spawn(cmd->path);
}
group_only(cc->sc, KBTOGROUP(arg->i));
}
void
kbfunc_client_cyclegroup(struct client_ctx *cc, union arg *arg)
{
group_cycle(cc->sc, arg->i);
}
void
kbfunc_client_nogroup(struct client_ctx *cc, union arg *arg)
{
group_alltoggle(cc->sc);
}
void
kbfunc_client_grouptoggle(struct client_ctx *cc, union arg *arg)
{
/* XXX for stupid X apps like xpdf and gvim */
XGrabKeyboard(X_Dpy, cc->win, True,
GrabModeAsync, GrabModeAsync, CurrentTime);
group_sticky_toggle_enter(cc);
}
void
kbfunc_client_movetogroup(struct client_ctx *cc, union arg *arg)
{
group_movetogroup(cc, KBTOGROUP(arg->i));
}
void
kbfunc_client_maximize(struct client_ctx *cc, union arg *arg)
{
client_maximize(cc);
}
void
kbfunc_client_vmaximize(struct client_ctx *cc, union arg *arg)
{
client_vertmaximize(cc);
}
void
kbfunc_client_hmaximize(struct client_ctx *cc, union arg *arg)
{
client_horizmaximize(cc);
}
void
kbfunc_quit_wm(struct client_ctx *cc, union arg *arg)
{
_xev_quit = 1;
}
void
kbfunc_reload(struct client_ctx *cc, union arg *arg)
{
conf_reload(&Conf);
}

610
menu.c
View File

@ -1,6 +1,4 @@
/*
* calmwm - the calm window manager
*
* Copyright (c) 2008 Owain G. Ainsworth <oga@openbsd.org>
* Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
*
@ -15,36 +13,24 @@
* 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/types.h>
#include "queue.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"
#define PROMPT_SCHAR "\xc2\xbb"
#define PROMPT_ECHAR "\xc2\xab"
enum ctltype {
CTL_NONE = -1,
CTL_ERASEONE = 0, CTL_WIPE, CTL_UP, CTL_DOWN, CTL_RETURN,
CTL_TAB, CTL_ABORT, CTL_ALL
};
#define PROMPT_SCHAR '<27>'
#define PROMPT_ECHAR '<27>'
struct menu_ctx {
struct screen_ctx *sc;
char searchstr[MENU_MAXENTRY + 1];
char dispstr[MENU_MAXENTRY*2 + 1];
char promptstr[MENU_MAXENTRY + 1];
@ -52,31 +38,48 @@ struct menu_ctx {
int list;
int listing;
int changed;
int noresult;
int prev;
int entry;
int width;
int num;
int flags;
struct geom geom;
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 *);
void
menu_init(struct screen_ctx *sc)
{
XGCValues gv;
sc->menuwin = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0, 1, 1, 0,
sc->color[CWM_COLOR_BG_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;
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))
{
@ -86,127 +89,91 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt,
XEvent e;
Window focuswin;
int evmask, focusrevert;
int xsave, ysave, xcur, ycur;
TAILQ_INIT(&resultq);
(void)memset(&mc, 0, sizeof(mc));
bzero(&mc, sizeof(mc));
xu_ptr_getpos(sc->rootwin, &xsave, &ysave);
xu_ptr_getpos(sc->rootwin, &mc.x, &mc.y);
mc.sc = sc;
mc.flags = flags;
mc.match = match;
mc.print = print;
mc.entry = mc.prev = -1;
mc.geom.x = xsave;
mc.geom.y = ysave;
if (mc.flags & CWM_MENU_LIST)
if (prompt == NULL) {
evmask = MenuMask;
mc.promptstr[0] = '\0';
mc.list = 1;
if (initial != NULL)
(void)strlcpy(mc.searchstr, initial, sizeof(mc.searchstr));
else
mc.searchstr[0] = '\0';
evmask = MENUMASK;
if (prompt != NULL) {
evmask |= KEYMASK; /* accept keys as well */
(void)strlcpy(mc.promptstr, prompt, sizeof(mc.promptstr));
} else {
evmask = MenuMask | KeyMask; /* only accept keys if prompt */
snprintf(mc.promptstr, sizeof(mc.promptstr), "%s%c", prompt,
PROMPT_SCHAR);
snprintf(mc.dispstr, sizeof(mc.dispstr), "%s%s%c", mc.promptstr,
mc.searchstr, PROMPT_ECHAR);
mc.width = font_width(sc, mc.dispstr, strlen(mc.dispstr));
mc.hasprompt = 1;
}
XSelectInput(X_Dpy, sc->menu.win, evmask);
XMapRaised(X_Dpy, sc->menu.win);
if (initial != NULL)
strlcpy(mc.searchstr, initial, sizeof(mc.searchstr));
else
mc.searchstr[0] = '\0';
if (XGrabPointer(X_Dpy, sc->menu.win, False, MENUGRABMASK,
GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_QUESTION],
CurrentTime) != GrabSuccess) {
XUnmapWindow(X_Dpy, sc->menu.win);
return(NULL);
mc.match = match;
mc.print = print;
mc.entry = mc.prev = -1;
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 (xu_ptr_grab(sc->menuwin, MenuGrabMask, Cursor_question) < 0) {
XUnmapWindow(X_Dpy, sc->menuwin);
return (NULL);
}
XGetInputFocus(X_Dpy, &focuswin, &focusrevert);
XSetInputFocus(X_Dpy, sc->menu.win, RevertToPointerRoot, CurrentTime);
XSetInputFocus(X_Dpy, sc->menuwin, RevertToPointerRoot, CurrentTime);
/* make sure keybindings don't remove keys from the menu stream */
XGrabKeyboard(X_Dpy, sc->menu.win, True,
XGrabKeyboard(X_Dpy, sc->menuwin, True,
GrabModeAsync, GrabModeAsync, CurrentTime);
for (;;) {
mc.changed = 0;
XWindowEvent(X_Dpy, sc->menu.win, evmask, &e);
XWindowEvent(X_Dpy, sc->menuwin, evmask, &e);
switch (e.type) {
default:
break;
case KeyPress:
if ((mi = menu_handle_key(&e, &mc, menuq, &resultq))
!= NULL)
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:
break;
}
}
out:
if ((mc.flags & CWM_MENU_DUMMY) == 0 && mi->dummy) {
/* no mouse based match */
free(mi);
if (dummy == 0 && mi->dummy) { /* no match */
xfree (mi);
mi = NULL;
xu_ptr_ungrab();
XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime);
}
XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime);
/* restore if user didn't move */
xu_ptr_getpos(sc->rootwin, &xcur, &ycur);
if (xcur == mc.geom.x && ycur == mc.geom.y)
xu_ptr_setpos(sc->rootwin, xsave, ysave);
XUngrabPointer(X_Dpy, CurrentTime);
XMoveResizeWindow(X_Dpy, sc->menu.win, 0, 0, 1, 1);
XUnmapWindow(X_Dpy, sc->menu.win);
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;
mr = xcalloc(1, sizeof(*mr));
TAILQ_INIT(&menuq);
if ((mi = menu_filter(sc, &menuq, mc->searchstr, NULL,
(CWM_MENU_DUMMY), 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 *
@ -215,22 +182,17 @@ 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 (input_keycodetrans(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;
@ -256,87 +218,68 @@ 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));
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;
struct menu *mi;
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)
@ -344,139 +287,103 @@ menu_draw(struct menu_ctx *mc, struct menu_q *menuq, struct menu_q *resultq)
}
mc->num = 0;
mc->geom.w = 0;
mc->geom.h = 0;
mc->width = 0;
dy = 0;
if (mc->hasprompt) {
(void)snprintf(mc->dispstr, sizeof(mc->dispstr), "%s%s%s%s",
mc->promptstr, PROMPT_SCHAR, mc->searchstr, PROMPT_ECHAR);
XftTextExtentsUtf8(X_Dpy, sc->xftfont,
(const FcChar8*)mc->dispstr, strlen(mc->dispstr), &extents);
mc->geom.w = extents.xOff;
mc->geom.h = sc->xftfont->height + 1;
snprintf(mc->dispstr, sizeof(mc->dispstr), "%s%s%c",
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);
char *text;
XftTextExtentsUtf8(X_Dpy, sc->xftfont,
(const FcChar8*)mi->print,
MIN(strlen(mi->print), MENU_MAXENTRY), &extents);
if (mc->print != NULL) {
(*mc->print)(mi, mc->listing);
text = mi->print;
} else {
mi->print[0] = '\0';
text = mi->text;
}
mc->geom.w = MAX(mc->geom.w, extents.xOff);
mc->geom.h += sc->xftfont->height + 1;
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, CWM_GAP);
area.w += area.x - Conf.bwidth * 2;
area.h += area.y - Conf.bwidth * 2;
xsave = mc->x;
ysave = mc->y;
if (mc->x < 0)
mc->x = 0;
else if (mc->x + mc->width >= sc->xmax)
mc->x = sc->xmax - mc->width;
xsave = mc->geom.x;
ysave = mc->geom.y;
if (mc->y + dy >= sc->ymax)
mc->y = sc->ymax - dy;
/* never hide the top of the menu */
if (mc->y < 0)
mc->y = 0;
/* Never hide the top, or left side, of the menu. */
if (mc->geom.x + mc->geom.w >= area.w)
mc->geom.x = area.w - mc->geom.w;
if (mc->geom.x < area.x) {
mc->geom.x = area.x;
mc->geom.w = MIN(mc->geom.w, (area.w - area.x));
}
if (mc->geom.y + mc->geom.h >= area.h)
mc->geom.y = area.h - mc->geom.h;
if (mc->geom.y < area.y) {
mc->geom.y = area.y;
mc->geom.h = MIN(mc->geom.h, (area.h - area.y));
}
if (mc->x != xsave || mc->y != ysave)
xu_ptr_setpos(sc->rootwin, mc->x, mc->y);
if (mc->geom.x != xsave || mc->geom.y != ysave)
xu_ptr_setpos(sc->rootwin, mc->geom.x, mc->geom.y);
XClearWindow(X_Dpy, sc->menuwin);
XMoveResizeWindow(X_Dpy, sc->menuwin, mc->x, mc->y, mc->width, dy);
XClearWindow(X_Dpy, sc->menu.win);
XMoveResizeWindow(X_Dpy, sc->menu.win, mc->geom.x, mc->geom.y,
mc->geom.w, mc->geom.h);
n = 0;
if (mc->hasprompt) {
XftDrawStringUtf8(sc->menu.xftdraw,
&sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont,
0, sc->xftfont->ascent,
(const FcChar8*)mc->dispstr, strlen(mc->dispstr));
n++;
}
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->height + 1) + sc->xftfont->ascent + 1;
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(sc->menu.xftdraw,
&sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont,
0, y,
(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 (mc->hasprompt && n > 1)
menu_draw_entry(mc, resultq, 1, 1);
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 = 0;
if (mc->hasprompt)
i = 1;
TAILQ_FOREACH(mi, resultq, resultentry)
if (entry == i++)
break;
if (mi == NULL)
return;
color = (active) ? CWM_COLOR_MENU_FG : CWM_COLOR_MENU_BG;
XftDrawRect(sc->menu.xftdraw, &sc->xftcolor[color], 0,
(sc->xftfont->height + 1) * entry, mc->geom.w,
(sc->xftfont->height + 1) + sc->xftfont->descent);
color = (active) ? CWM_COLOR_MENU_FONT_SEL : CWM_COLOR_MENU_FONT;
XftDrawStringUtf8(sc->menu.xftdraw,
&sc->xftcolor[color], sc->xftfont,
0, (sc->xftfont->height + 1) * entry + sc->xftfont->ascent + 1,
(const FcChar8*)mi->print, strlen(mi->print));
}
static void
menu_handle_move(struct menu_ctx *mc, struct menu_q *resultq, int x, int y)
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);
}
xu_ptr_regrab(MenuGrabMask, Cursor_select);
XFillRectangle(X_Dpy, sc->menuwin, sc->gc, 0,
font_height(sc) * mc->entry, mc->width, font_height(sc));
} else
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 = 0;
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);
xu_ptr_ungrab();
if (mc->hasprompt)
i = 1;
@ -489,174 +396,23 @@ 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->height + 1);
entry = y / font_height(sc);
/* in bounds? */
if (x < 0 || x > mc->geom.w || y < 0 ||
y > (sc->xftfont->height + 1) * 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 (mc->hasprompt && entry == 0)
entry = -1;
return(entry);
}
static int
menu_keycode(XKeyEvent *ev, enum ctltype *ctl, char *chr)
{
KeySym ks;
*ctl = CTL_NONE;
chr[0] = '\0';
ks = XkbKeycodeToKeysym(X_Dpy, ev->keycode, 0,
(ev->state & ShiftMask) ? 1 : 0);
/* Look for control characters. */
switch (ks) {
case XK_BackSpace:
*ctl = CTL_ERASEONE;
break;
case XK_KP_Enter:
case XK_Return:
*ctl = CTL_RETURN;
break;
case XK_Tab:
*ctl = CTL_TAB;
break;
case XK_Up:
*ctl = CTL_UP;
break;
case XK_Down:
*ctl = CTL_DOWN;
break;
case XK_Escape:
*ctl = CTL_ABORT;
break;
}
if (*ctl == CTL_NONE && (ev->state & ControlMask)) {
switch (ks) {
case XK_s:
case XK_S:
/* Emacs "next" */
*ctl = CTL_DOWN;
break;
case XK_r:
case XK_R:
/* Emacs "previous" */
*ctl = CTL_UP;
break;
case XK_u:
case XK_U:
*ctl = CTL_WIPE;
break;
case XK_h:
case XK_H:
*ctl = CTL_ERASEONE;
break;
case XK_a:
case XK_A:
*ctl = CTL_ALL;
break;
case XK_bracketleft:
*ctl = CTL_ABORT;
break;
}
}
if (*ctl == CTL_NONE && (ev->state & Mod1Mask)) {
switch (ks) {
case XK_j:
case XK_J:
/* Vi "down" */
*ctl = CTL_DOWN;
break;
case XK_k:
case XK_K:
/* Vi "up" */
*ctl = CTL_UP;
break;
}
}
if (*ctl != CTL_NONE)
return(0);
if (XLookupString(ev, chr, 32, &ks, NULL) < 0)
return(-1);
return(0);
}
void
menuq_add(struct menu_q *mq, void *ctx, const char *fmt, ...)
{
va_list ap;
struct menu *mi;
mi = xcalloc(1, sizeof(*mi));
mi->ctx = ctx;
va_start(ap, fmt);
if (fmt != NULL)
(void)vsnprintf(mi->text, sizeof(mi->text), fmt, ap);
else
mi->text[0] = '\0';
va_end(ap);
TAILQ_INSERT_TAIL(mq, mi, entry);
}
void
menuq_clear(struct menu_q *mq)
{
struct menu *mi;
while ((mi = TAILQ_FIRST(mq)) != NULL) {
TAILQ_REMOVE(mq, mi, entry);
free(mi);
}
}
void
menu_windraw(struct screen_ctx *sc, Window win, const char *fmt, ...)
{
va_list ap;
int i;
char *text;
XGlyphInfo extents;
va_start(ap, fmt);
i = vasprintf(&text, fmt, ap);
va_end(ap);
if (i < 0 || text == NULL)
err(1, "vasprintf");
XftTextExtentsUtf8(X_Dpy, sc->xftfont, (const FcChar8*)text,
strlen(text), &extents);
XReparentWindow(X_Dpy, sc->menu.win, win, 0, 0);
XMoveResizeWindow(X_Dpy, sc->menu.win, 0, 0,
extents.xOff, sc->xftfont->height);
XMapWindow(X_Dpy, sc->menu.win);
XClearWindow(X_Dpy, sc->menu.win);
XftDrawStringUtf8(sc->menu.xftdraw, &sc->xftcolor[CWM_COLOR_MENU_FONT],
sc->xftfont, 0, sc->xftfont->ascent + 1,
(const FcChar8*)text, strlen(text));
free(text);
return (entry);
}

View File

@ -1,74 +0,0 @@
#!/usr/bin/perl -p
# Originally by Stuart Henderson in
# <20161206120601.vmimohqh4nafaeah@symphytum.spacehopper.org>
# Ported to Perl and slightly tweaked by Leah Neukirchen.
s/\bbind\b(.*)\bunmap/unbind-key \1/;
s/\bmousebind\b(.*)unmap/unbind-mouse \1/;
s/\bresize\b/window-resize/;
s/\bresizedown\b/window-resize-down/;
s/\bresizeleft\b/window-resize-left/;
s/\bresizeright\b/window-resize-right/;
s/\bresizeup\b/window-resize-up/;
s/\bmove\b/window-move/;
s/\bmovedown\b/window-move-down/;
s/\bmoveleft\b/window-move-left/;
s/\bmoveright\b/window-move-right/;
s/\bmoveup\b/window-move-up/;
s/\bbigmovedown\b/window-move-down-big/;
s/\bbigmoveleft\b/window-move-left-big/;
s/\bbigmoveright\b/window-move-right-big/;
s/\bbigmoveup\b/window-move-up-big/;
s/\bbigptrmovedown\b/pointer-move-down-big/;
s/\bbigptrmoveleft\b/pointer-move-left-big/;
s/\bbigptrmoveright\b/pointer-move-right-big/;
s/\bbigptrmoveup\b/pointer-move-up-big/;
s/\bbigresizedown\b/window-resize-down-big/;
s/\bbigresizeleft\b/window-resize-left-big/;
s/\bbigresizeright\b/window-resize-right-big/;
s/\bbigresizeup\b/window-resize-up-big/;
s/\bbind\b/bind-key/;
s/\bcycle\b/window-cycle/;
s/\bcyclegroup\b/group-cycle/;
s/\bcycleingroup\b/window-cycle-ingroup/;
s/\bdelete\b/window-delete/;
s/\bexec\b/menu-exec/;
s/\bexec_wm\b/menu-exec-wm/;
s/\bfreeze\b/window-freeze/;
s/\bfullscreen\b/window-fullscreen/;
s/\bgroupsearch\b/menu-group/;
s/\bgrouptoggle\b/window-group/;
s/\bhide\b/window-hide/;
s/\bhmaximize\b/window-hmaximize/;
s/\bhtile\b/window-htile/;
s/\blabel\b/window-menu-label/;
s/\blower\b/window-lower/;
s/\bmaximize\b/window-maximize/;
s/\bmenu_cmd\b/menu-cmd/;
s/\bmenu_group\b/menu-group/;
s/\bmenu_unhide\b/menu-window/;
s/\bmenusearch\b/menu-cmd/;
s/\bmousebind\b/bind-mouse/;
s/\bnogroup\b/group-toggle-all/;
s/\bptrmovedown\b/pointer-move-down/;
s/\bptrmoveleft\b/pointer-move-left/;
s/\bptrmoveright\b/pointer-move-right/;
s/\bptrmoveup\b/pointer-move-up/;
s/\braise\b/window-raise/;
s/\brcycle\b/window-rcycle/;
s/\brcyclegroup\b/group-rcycle/;
s/\brcycleingroup\b/window-rcycle-ingroup/;
s/\bsearch\b/menu-window/;
s/\bssh\b/menu-ssh/;
s/\bstick\b/window-stick/;
s/\bvmaximize\b/window-vmaximize/;
s/\bvtile\b/window-vtile/;
s/\bwindow_grouptoggle\b/window-group/;
s/\bwindow_move\b/window-move/;
s/\bwindow_resize\b/window-resize/;
s/\bwindow_hide\b/window-hide/;
s/\bwindow_lower\b/window-lower/;
s/\bwindow_raise\b/window-raise/;
s/\bmovetogroup([1-9])\b/window-movetogroup-\1/;
s/\bgrouponly([1-9])\b/group-only-\1/;
s/\bgroup([1-9])\b/group-toggle-\1/;

279
mousefunc.c Normal file
View File

@ -0,0 +1,279 @@
/*
* 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.
*
* $Id$
*/
#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, height, width_size, width_name;
snprintf(asize, sizeof(asize), "%dx%d",
(cc->geom.width - cc->geom.basew) / cc->geom.incw,
(cc->geom.height - cc->geom.baseh) / cc->geom.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);
height = font_ascent(sc) + font_descent(sc) + 1;
XMoveResizeWindow(X_Dpy, sc->menuwin, cc->geom.x, cc->geom.y,
width, height * 2);
XMapWindow(X_Dpy, sc->menuwin);
XReparentWindow(X_Dpy, sc->menuwin, cc->win, 0, 0);
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, height + font_ascent(sc) + 1);
}
void
mousefunc_window_resize(struct client_ctx *cc, void *arg)
{
XEvent ev;
Time time = 0;
struct screen_ctx *sc = cc->sc;
int x = cc->geom.x, y = cc->geom.y;
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 sync more than 60 times / second */
if ((ev.xmotion.time - time) > (1000 / 60)) {
time = ev.xmotion.time;
XSync(X_Dpy, False);
client_resize(cc);
}
break;
case ButtonRelease:
if (time) {
XSync(X_Dpy, False);
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 time = 0;
int px, py;
client_raise(cc);
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->geom.y = ev.xmotion.y_root - py;
/* don't sync more than 60 times / second */
if ((ev.xmotion.time - time) > (1000 / 60)) {
time = ev.xmotion.time;
XSync(X_Dpy, False);
client_move(cc);
}
break;
case ButtonRelease:
if (time) {
XSync(X_Dpy, False);
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_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));
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));
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);
}
}

295
parse.y
View File

@ -21,7 +21,7 @@
%{
#include <sys/types.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <ctype.h>
@ -35,8 +35,6 @@
#include "calmwm.h"
#define YYSTYPE_IS_DECLARED
TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
static struct file {
TAILQ_ENTRY(file) entry;
@ -44,19 +42,18 @@ static struct file {
char *name;
int lineno;
int errors;
} *file, *topfile;
struct file *pushfile(const char *);
int popfile(void);
int yyparse(void);
int yylex(void);
int yyerror(const char *, ...)
__attribute__((__format__ (printf, 1, 2)))
__attribute__((__nonnull__ (1)));
int kw_cmp(const void *, const void *);
int lookup(char *);
int lgetc(int);
int lungetc(int);
int findeol(void);
} *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;
@ -70,15 +67,12 @@ typedef struct {
%}
%token BINDKEY UNBINDKEY BINDMOUSE UNBINDMOUSE
%token FONTNAME STICKY GAP
%token AUTOGROUP COMMAND IGNORE
%token FONTNAME STICKY GAP MOUSEBIND
%token AUTOGROUP BIND COMMAND IGNORE
%token YES NO BORDERWIDTH MOVEAMOUNT
%token COLOR SNAPDIST
%token ACTIVEBORDER INACTIVEBORDER URGENCYBORDER
%token COLOR
%token ACTIVEBORDER INACTIVEBORDER
%token GROUPBORDER UNGROUPBORDER
%token MENUBG MENUFG
%token FONTCOLOR FONTSELCOLOR
%token ERROR
%token <v.string> STRING
%token <v.number> NUMBER
@ -111,156 +105,81 @@ yesno : YES { $$ = 1; }
;
main : FONTNAME STRING {
free(conf->font);
conf->font = $2;
free(conf->DefaultFontName);
conf->DefaultFontName = $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;
}
| 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 (!conf_cmd_add(conf, $2, $3)) {
yyerror("command name/path too long");
free($2);
free($3);
YYERROR;
}
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));
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 STRING 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 STRING {
if (!conf_bind_key(conf, $2, NULL)) {
yyerror("invalid unbind-key: %s", $2);
free($2);
YYERROR;
}
free($2);
}
| BINDMOUSE STRING 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 STRING {
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_BORDOR_ACTIVE].name);
conf->color[CWM_COLOR_BORDOR_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;
}
| MENUBG STRING {
free(conf->color[CWM_COLOR_MENU_BG]);
conf->color[CWM_COLOR_MENU_BG] = $2;
}
| MENUFG STRING {
free(conf->color[CWM_COLOR_MENU_FG]);
conf->color[CWM_COLOR_MENU_FG] = $2;
}
| FONTCOLOR STRING {
free(conf->color[CWM_COLOR_MENU_FONT]);
conf->color[CWM_COLOR_MENU_FONT] = $2;
}
| FONTSELCOLOR STRING {
free(conf->color[CWM_COLOR_MENU_FONT_SEL]);
conf->color[CWM_COLOR_MENU_FONT_SEL] = $2;
free(conf->color[CWM_COLOR_BORDER_UNGROUP].name);
conf->color[CWM_COLOR_BORDER_UNGROUP].name = $2;
}
;
%%
@ -273,7 +192,7 @@ struct keywords {
int
yyerror(const char *fmt, ...)
{
va_list ap;
va_list ap;
file->errors++;
va_start(ap, fmt);
@ -297,28 +216,20 @@ 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},
{ "font", FONTCOLOR},
{ "fontname", FONTNAME},
{ "gap", GAP},
{ "groupborder", GROUPBORDER},
{ "ignore", IGNORE},
{ "inactiveborder", INACTIVEBORDER},
{ "menubg", MENUBG},
{ "menufg", MENUFG},
{ "mousebind", MOUSEBIND},
{ "moveamount", MOVEAMOUNT},
{ "no", NO},
{ "selfont", FONTSELCOLOR},
{ "snapdist", SNAPDIST},
{ "sticky", STICKY},
{ "unbind-key", UNBINDKEY},
{ "unbind-mouse", UNBINDMOUSE},
{ "ungroupborder", UNGROUPBORDER},
{ "urgencyborder", URGENCYBORDER},
{ "yes", YES}
};
const struct keywords *p;
@ -360,9 +271,8 @@ lgetc(int quotec)
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);
}
@ -380,7 +290,7 @@ lgetc(int quotec)
}
while (c == EOF) {
if (file == topfile || popfile() == EOF)
if (popfile() == EOF)
return (EOF);
c = getc(file->stream);
}
@ -409,13 +319,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 = pushback_buffer[--pushback_index];
else
c = lgetc(0);
c = lgetc(0);
if (c == '\n') {
file->lineno++;
break;
@ -466,9 +374,6 @@ 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");
@ -516,7 +421,6 @@ nodigits:
}
}
/* 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 != '>' && \
@ -554,7 +458,8 @@ pushfile(const char *name)
nfile->name = xstrdup(name);
if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
warn("%s", nfile->name);
if (errno != ENOENT)
warn("%s", nfile->name);
free(nfile->name);
free(nfile);
return (NULL);
@ -569,32 +474,94 @@ 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)
{
int errors = 0;
int errors = 0;
conf = xconf;
conf = xcalloc(1, sizeof(*conf));
if ((file = pushfile(filename)) == NULL) {
free(conf);
return (-1);
}
topfile = file;
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->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);
}
strlcpy(xconf->termpath, conf->termpath,
sizeof(xconf->termpath));
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->DefaultFontName = conf->DefaultFontName;
}
free(conf);
return (errors ? -1 : 0);
}

648
queue.h
View File

@ -1,648 +0,0 @@
/* $OpenBSD: queue.h,v 1.38 2013/07/03 15:05:21 fgsch Exp $ */
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
*/
#ifndef _SYS_QUEUE_H_
#define _SYS_QUEUE_H_
/*
* This file defines five types of data structures: singly-linked lists,
* lists, simple queues, tail queues, and circular queues.
*
*
* A singly-linked list is headed by a single forward pointer. The elements
* are singly linked for minimum space and pointer manipulation overhead at
* the expense of O(n) removal for arbitrary elements. New elements can be
* added to the list after an existing element or at the head of the list.
* Elements being removed from the head of the list should use the explicit
* macro for this purpose for optimum efficiency. A singly-linked list may
* only be traversed in the forward direction. Singly-linked lists are ideal
* for applications with large datasets and few or no removals or for
* implementing a LIFO queue.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before
* or after an existing element or at the head of the list. A list
* may only be traversed in the forward direction.
*
* A simple queue is headed by a pair of pointers, one the head of the
* list and the other to the tail of the list. The elements are singly
* linked to save space, so elements can only be removed from the
* head of the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the
* list. A simple queue may only be traversed in the forward direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*
* A circle queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the list.
* A circle queue may be traversed in either direction, but has a more
* complex end of list detection.
*
* For details on the use of these macros, see the queue(3) manual page.
*/
#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
#define _Q_INVALIDATE(a) (a) = ((void *)-1)
#else
#define _Q_INVALIDATE(a)
#endif
/*
* Singly-linked List definitions.
*/
#define SLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define SLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
}
/*
* Singly-linked List access methods.
*/
#define SLIST_FIRST(head) ((head)->slh_first)
#define SLIST_END(head) NULL
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
#define SLIST_FOREACH(var, head, field) \
for((var) = SLIST_FIRST(head); \
(var) != SLIST_END(head); \
(var) = SLIST_NEXT(var, field))
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SLIST_FIRST(head); \
(var) && ((tvar) = SLIST_NEXT(var, field), 1); \
(var) = (tvar))
/*
* Singly-linked List functions.
*/
#define SLIST_INIT(head) { \
SLIST_FIRST(head) = SLIST_END(head); \
}
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
(elm)->field.sle_next = (slistelm)->field.sle_next; \
(slistelm)->field.sle_next = (elm); \
} while (0)
#define SLIST_INSERT_HEAD(head, elm, field) do { \
(elm)->field.sle_next = (head)->slh_first; \
(head)->slh_first = (elm); \
} while (0)
#define SLIST_REMOVE_AFTER(elm, field) do { \
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
} while (0)
#define SLIST_REMOVE_HEAD(head, field) do { \
(head)->slh_first = (head)->slh_first->field.sle_next; \
} while (0)
#define SLIST_REMOVE(head, elm, type, field) do { \
if ((head)->slh_first == (elm)) { \
SLIST_REMOVE_HEAD((head), field); \
} else { \
struct type *curelm = (head)->slh_first; \
\
while (curelm->field.sle_next != (elm)) \
curelm = curelm->field.sle_next; \
curelm->field.sle_next = \
curelm->field.sle_next->field.sle_next; \
_Q_INVALIDATE((elm)->field.sle_next); \
} \
} while (0)
/*
* List definitions.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
/*
* List access methods
*/
#define LIST_FIRST(head) ((head)->lh_first)
#define LIST_END(head) NULL
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_FOREACH(var, head, field) \
for((var) = LIST_FIRST(head); \
(var)!= LIST_END(head); \
(var) = LIST_NEXT(var, field))
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = LIST_FIRST(head); \
(var) && ((tvar) = LIST_NEXT(var, field), 1); \
(var) = (tvar))
/*
* List functions.
*/
#define LIST_INIT(head) do { \
LIST_FIRST(head) = LIST_END(head); \
} while (0)
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
(listelm)->field.le_next->field.le_prev = \
&(elm)->field.le_next; \
(listelm)->field.le_next = (elm); \
(elm)->field.le_prev = &(listelm)->field.le_next; \
} while (0)
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.le_prev = (listelm)->field.le_prev; \
(elm)->field.le_next = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &(elm)->field.le_next; \
} while (0)
#define LIST_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
(head)->lh_first = (elm); \
(elm)->field.le_prev = &(head)->lh_first; \
} while (0)
#define LIST_REMOVE(elm, field) do { \
if ((elm)->field.le_next != NULL) \
(elm)->field.le_next->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = (elm)->field.le_next; \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
#define LIST_REPLACE(elm, elm2, field) do { \
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
(elm2)->field.le_next->field.le_prev = \
&(elm2)->field.le_next; \
(elm2)->field.le_prev = (elm)->field.le_prev; \
*(elm2)->field.le_prev = (elm2); \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
/*
* Simple queue definitions.
*/
#define SIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqh_first; /* first element */ \
struct type **sqh_last; /* addr of last next element */ \
}
#define SIMPLEQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).sqh_first }
#define SIMPLEQ_ENTRY(type) \
struct { \
struct type *sqe_next; /* next element */ \
}
/*
* Simple queue access methods.
*/
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
#define SIMPLEQ_END(head) NULL
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
#define SIMPLEQ_FOREACH(var, head, field) \
for((var) = SIMPLEQ_FIRST(head); \
(var) != SIMPLEQ_END(head); \
(var) = SIMPLEQ_NEXT(var, field))
#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SIMPLEQ_FIRST(head); \
(var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
(var) = (tvar))
/*
* Simple queue functions.
*/
#define SIMPLEQ_INIT(head) do { \
(head)->sqh_first = NULL; \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
(head)->sqh_first = (elm); \
} while (0)
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqe_next = NULL; \
*(head)->sqh_last = (elm); \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
(head)->sqh_last = &(elm)->field.sqe_next; \
(listelm)->field.sqe_next = (elm); \
} while (0)
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
== NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
/*
* XOR Simple queue definitions.
*/
#define XSIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqx_first; /* first element */ \
struct type **sqx_last; /* addr of last next element */ \
unsigned long sqx_cookie; \
}
#define XSIMPLEQ_ENTRY(type) \
struct { \
struct type *sqx_next; /* next element */ \
}
/*
* XOR Simple queue access methods.
*/
#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \
(unsigned long)(ptr)))
#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first))
#define XSIMPLEQ_END(head) NULL
#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head))
#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next))
#define XSIMPLEQ_FOREACH(var, head, field) \
for ((var) = XSIMPLEQ_FIRST(head); \
(var) != XSIMPLEQ_END(head); \
(var) = XSIMPLEQ_NEXT(head, var, field))
#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = XSIMPLEQ_FIRST(head); \
(var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \
(var) = (tvar))
/*
* XOR Simple queue functions.
*/
#define XSIMPLEQ_INIT(head) do { \
arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \
(head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
} while (0)
#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqx_next = (head)->sqx_first) == \
XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
(head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \
} while (0)
#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \
*(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
} while (0)
#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \
XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
(listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \
} while (0)
#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqx_first = XSIMPLEQ_XOR(head, \
(head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
} while (0)
#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \
(elm)->field.sqx_next)->field.sqx_next) \
== XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = \
XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
} while (0)
/*
* Tail queue definitions.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
/*
* tail queue access methods
*/
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_END(head) NULL
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
/* XXX */
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_EMPTY(head) \
(TAILQ_FIRST(head) == TAILQ_END(head))
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head) && \
((tvar) = TAILQ_NEXT(var, field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head); \
(var) = TAILQ_PREV(var, headname, field))
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head) && \
((tvar) = TAILQ_PREV(var, headname, field), 1); \
(var) = (tvar))
/*
* Tail queue functions.
*/
#define TAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(head)->tqh_first->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_REMOVE(head, elm, field) do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
(elm2)->field.tqe_next->field.tqe_prev = \
&(elm2)->field.tqe_next; \
else \
(head)->tqh_last = &(elm2)->field.tqe_next; \
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
*(elm2)->field.tqe_prev = (elm2); \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
/*
* Circular queue definitions.
*/
#define CIRCLEQ_HEAD(name, type) \
struct name { \
struct type *cqh_first; /* first element */ \
struct type *cqh_last; /* last element */ \
}
#define CIRCLEQ_HEAD_INITIALIZER(head) \
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
#define CIRCLEQ_ENTRY(type) \
struct { \
struct type *cqe_next; /* next element */ \
struct type *cqe_prev; /* previous element */ \
}
/*
* Circular queue access methods
*/
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
#define CIRCLEQ_END(head) ((void *)(head))
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
#define CIRCLEQ_EMPTY(head) \
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
#define CIRCLEQ_FOREACH(var, head, field) \
for((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_NEXT(var, field))
#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head) && \
((tvar) = CIRCLEQ_NEXT(var, field), 1); \
(var) = (tvar))
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
for((var) = CIRCLEQ_LAST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_PREV(var, field))
#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = CIRCLEQ_LAST(head, headname); \
(var) != CIRCLEQ_END(head) && \
((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \
(var) = (tvar))
/*
* Circular queue functions.
*/
#define CIRCLEQ_INIT(head) do { \
(head)->cqh_first = CIRCLEQ_END(head); \
(head)->cqh_last = CIRCLEQ_END(head); \
} while (0)
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
(elm)->field.cqe_prev = (listelm); \
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
(listelm)->field.cqe_next = (elm); \
} while (0)
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm); \
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
(listelm)->field.cqe_prev = (elm); \
} while (0)
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
(elm)->field.cqe_next = (head)->cqh_first; \
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
if ((head)->cqh_last == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(head)->cqh_first->field.cqe_prev = (elm); \
(head)->cqh_first = (elm); \
} while (0)
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.cqe_next = CIRCLEQ_END(head); \
(elm)->field.cqe_prev = (head)->cqh_last; \
if ((head)->cqh_first == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(head)->cqh_last->field.cqe_next = (elm); \
(head)->cqh_last = (elm); \
} while (0)
#define CIRCLEQ_REMOVE(head, elm, field) do { \
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm)->field.cqe_prev; \
else \
(elm)->field.cqe_next->field.cqe_prev = \
(elm)->field.cqe_prev; \
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm)->field.cqe_next; \
else \
(elm)->field.cqe_prev->field.cqe_next = \
(elm)->field.cqe_next; \
_Q_INVALIDATE((elm)->field.cqe_prev); \
_Q_INVALIDATE((elm)->field.cqe_next); \
} while (0)
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
CIRCLEQ_END(head)) \
(head)->cqh_last = (elm2); \
else \
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
CIRCLEQ_END(head)) \
(head)->cqh_first = (elm2); \
else \
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
_Q_INVALIDATE((elm)->field.cqe_prev); \
_Q_INVALIDATE((elm)->field.cqe_next); \
} while (0)
#endif /* !_SYS_QUEUE_H_ */

View File

@ -1,38 +0,0 @@
/* $OpenBSD: reallocarray.c,v 1.2 2014/12/08 03:45:00 bcook Exp $ */
/*
* Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
/*
* This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
* if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
*/
#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
void *
reallocarray(void *optr, size_t nmemb, size_t size)
{
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
nmemb > 0 && SIZE_MAX / nmemb < size) {
errno = ENOMEM;
return NULL;
}
return realloc(optr, size * nmemb);
}

299
screen.c
View File

@ -15,94 +15,32 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $OpenBSD$
* $Id$
*/
#include <sys/types.h>
#include "queue.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
screen_init(int which)
{
struct screen_ctx *sc;
Window *wins, w0, w1, active = None;
XSetWindowAttributes rootattr;
unsigned int nwins, i;
sc = xmalloc(sizeof(*sc));
TAILQ_INIT(&sc->clientq);
TAILQ_INIT(&sc->regionq);
TAILQ_INIT(&sc->groupq);
sc->which = which;
sc->rootwin = RootWindow(X_Dpy, sc->which);
sc->cycling = 0;
sc->hideall = 0;
conf_screen(sc);
xu_ewmh_net_supported(sc);
xu_ewmh_net_supported_wm_check(sc);
screen_update_geometry(sc);
for (i = 0; i < Conf.ngroups; i++)
group_init(sc, i);
xu_ewmh_net_desktop_names(sc);
xu_ewmh_net_wm_desktop_viewport(sc);
xu_ewmh_net_wm_number_of_desktops(sc);
xu_ewmh_net_showing_desktop(sc);
xu_ewmh_net_virtual_roots(sc);
active = xu_ewmh_get_net_active_window(sc);
rootattr.cursor = Conf.cursor[CF_NORMAL];
rootattr.event_mask = SubstructureRedirectMask |
SubstructureNotifyMask | PropertyChangeMask | EnterWindowMask |
LeaveWindowMask | ColormapChangeMask | BUTTONMASK;
XChangeWindowAttributes(X_Dpy, sc->rootwin,
(CWEventMask | CWCursor), &rootattr);
/* Deal with existing clients. */
if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) {
for (i = 0; i < nwins; i++)
(void)client_init(wins[i], sc, (active == wins[i]));
XFree(wins);
}
screen_updatestackingorder(sc);
if (Conf.xrandr)
XRRSelectInput(X_Dpy, sc->rootwin, RRScreenChangeNotifyMask);
TAILQ_INSERT_TAIL(&Screenq, sc, entry);
XSync(X_Dpy, False);
}
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%lu\n", __func__, win);
return(NULL);
TAILQ_FOREACH(sc, &Screenq, entry)
if (sc->rootwin == rootwin)
return (sc);
/* XXX FAIL HERE */
return (TAILQ_FIRST(&Screenq));
}
void
@ -110,145 +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, enum apply_gap apply_gap)
{
struct region_ctx *rc;
struct geom area = sc->work;
TAILQ_FOREACH(rc, &sc->regionq, entry) {
if ((x >= rc->area.x) && (x < (rc->area.x + rc->area.w)) &&
(y >= rc->area.y) && (y < (rc->area.y + rc->area.h))) {
area = rc->area;
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->area.x = ci->x;
rc->area.y = ci->y;
rc->area.w = ci->width;
rc->area.h = ci->height;
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_workarea(sc);
}
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_init_xinerama(struct screen_ctx *sc)
{
XineramaScreenInfo *info;
int no;
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_update_geometry(struct screen_ctx *sc, int width, int height)
{
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);
}

180
search.c
View File

@ -2,7 +2,6 @@
* calmwm - the calm window manager
*
* Copyright (c) 2004 Marius Aamodt 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.
@ -15,75 +14,92 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $OpenBSD$
* $Id$
*/
#include <sys/types.h>
#include "queue.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 void search_match_path_type(struct menu_q *, struct menu_q *,
char *, int);
static int strsubmatch(char *, char *, int);
/*
* Match: label, title, class.
*/
void
search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct winname *wn;
struct menu *mi, *tierp[4], *before = NULL;
int ntiers;
ntiers = sizeof(tierp) / sizeof(tierp[0]);
TAILQ_INIT(resultq);
(void)memset(tierp, 0, sizeof(tierp));
memset(tierp, 0, sizeof(tierp));
/*
* In order of rank:
*
* 1. Look through labels.
* 2. Look at title history, from present to past.
* 3. Look at window class name.
*/
TAILQ_FOREACH(mi, menuq, entry) {
int tier = -1, t;
struct client_ctx *cc = (struct client_ctx *)mi->ctx;
struct client_ctx *cc = mi->ctx;
/* Match on label. */
if (strsubmatch(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)
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) && strsubmatch(search, cc->ch.res_class, 0))
/* 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 < ntiers - 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--;
if (tier >= nitems(tierp))
errx(1, "%s: invalid tier", __func__);
assert(tier < ntiers);
/*
* If you have a tierp, insert after it, and make it
@ -105,75 +121,45 @@ search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
}
void
search_print_text(struct menu *mi, int listing)
search_print_client(struct menu *mi, int list)
{
(void)snprintf(mi->print, sizeof(mi->print), "%s", mi->text);
}
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_client(struct menu *mi, int listing)
{
struct client_ctx *cc = (struct client_ctx *)mi->ctx;
struct client_ctx *cc;
char flag = ' ';
if (cc->flags & CLIENT_ACTIVE)
cc = mi->ctx;
if (cc == client_current())
flag = '!';
else if (cc->flags & CLIENT_HIDDEN)
flag = '&';
(void)snprintf(mi->print, sizeof(mi->print), "(%d) %c[%s] %s",
(cc->gc) ? cc->gc->num : 0, flag,
(cc->label) ? cc->label : "", cc->name);
}
if (list)
cc->matchname = cc->name;
static void
search_match_path_type(struct menu_q *menuq, struct menu_q *resultq,
char *search, int flag)
{
struct menu *mi;
char pattern[PATH_MAX];
glob_t g;
int i;
snprintf(mi->print, sizeof(mi->print), "%c%s", flag, cc->matchname);
(void)strlcpy(pattern, search, sizeof(pattern));
(void)strlcat(pattern, "*", sizeof(pattern));
if (!list && cc->matchname != cc->name &&
strlen(mi->print) < sizeof(mi->print) - 1) {
const char *marker = "";
char buf[MENU_MAXENTRY + 1];
int diff;
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);
diff = sizeof(mi->print) - 1 - strlen(mi->print);
/* One for the ':' */
diff -= 1;
if (strlen(cc->name) > diff) {
marker = "..";
diff -= 2;
} else {
diff = strlen(cc->name);
}
strlcpy(buf, mi->print, sizeof(buf));
snprintf(mi->print, sizeof(mi->print),
"%s:%.*s%s", buf, diff, cc->name, marker);
}
globfree(&g);
}
void
search_match_path(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
TAILQ_INIT(resultq);
search_match_path_type(menuq, resultq, search, PATH_ANY);
}
void
@ -192,43 +178,39 @@ void
search_match_exec(struct menu_q *menuq, struct menu_q *resultq, char *search)
{
struct menu *mi, *mj;
int r;
TAILQ_INIT(resultq);
TAILQ_FOREACH(mi, menuq, entry) {
if (strsubmatch(search, mi->text, 1) == 0 &&
fnmatch(search, mi->text, 0) == FNM_NOMATCH)
continue;
TAILQ_FOREACH(mj, resultq, resultentry) {
r = strcmp(mi->text, mj->text);
if (r < 0)
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);
if (r <= 0)
break;
}
}
if (mj == NULL)
TAILQ_INSERT_TAIL(resultq, mi, resultentry);
}
if (TAILQ_EMPTY(resultq))
search_match_path_type(menuq, resultq, search, PATH_EXEC);
}
static int
strsubmatch(char *sub, char *str, int zeroidx)
{
size_t len, sublen;
unsigned int n, flen;
size_t len, sublen;
u_int n, flen;
if (sub == NULL || str == NULL)
return(0);
return (0);
len = strlen(str);
sublen = strlen(sub);
if (sublen > len)
return(0);
return (0);
if (!zeroidx)
flen = len - sublen;
@ -237,7 +219,7 @@ strsubmatch(char *sub, char *str, int zeroidx)
for (n = 0; n <= flen; n++)
if (strncasecmp(sub, str + n, sublen) == 0)
return(1);
return (1);
return(0);
return (0);
}

View File

@ -1,7 +1,7 @@
/* $OpenBSD: strlcat.c,v 1.14 2015/01/15 03:54:12 millert Exp $ */
/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */
/*
* Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
* 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
@ -18,45 +18,44 @@
/* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */
#include <sys/types.h>
#include <string.h>
#include "calmwm.h"
#ifndef HAVE_STRLCAT
#include <sys/types.h>
#include <string.h>
/*
* Appends src to string dst of size dsize (unlike strncat, dsize is the
* full size of dst, not space left). At most dsize-1 characters
* will be copied. Always NUL terminates (unless dsize <= strlen(dst)).
* Returns strlen(src) + MIN(dsize, strlen(initial dst)).
* 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 dsize)
strlcat(char *dst, const char *src, size_t siz)
{
const char *odst = dst;
const char *osrc = src;
size_t n = dsize;
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 && *dst != '\0')
dst++;
dlen = dst - odst;
n = dsize - 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(src));
while (*src != '\0') {
if (n != 0) {
*dst++ = *src;
if (n == 0)
return(dlen + strlen(s));
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
n--;
}
src++;
s++;
}
*dst = '\0';
*d = '\0';
return(dlen + (src - osrc)); /* count does not include NUL */
return(dlen + (s - src)); /* count does not include NUL */
}
#endif /* !HAVE_STRLCAT */

View File

@ -1,7 +1,7 @@
/* $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $ */
/* $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */
/*
* Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
* 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
@ -18,40 +18,40 @@
/* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */
#include <sys/types.h>
#include <string.h>
#include "calmwm.h"
#ifndef HAVE_STRLCPY
#include <sys/types.h>
#include <string.h>
/*
* Copy string src to buffer dst of size dsize. At most dsize-1
* chars will be copied. Always NUL terminates (unless dsize == 0).
* Returns strlen(src); if retval >= dsize, truncation occurred.
* 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 dsize)
strlcpy(char *dst, const char *src, size_t siz)
{
const char *osrc = src;
size_t nleft = dsize;
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit. */
if (nleft != 0) {
while (--nleft != 0) {
if ((*dst++ = *src++) == '\0')
/* 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 (nleft == 0) {
if (dsize != 0)
*dst = '\0'; /* NUL-terminate dst */
while (*src++)
/* 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(src - osrc - 1); /* count does not include NUL */
return(s - src - 1); /* count does not include NUL */
}
#endif /* !HAVE_STRLCPY */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: strtonum.c,v 1.7 2013/04/17 18:40:58 tedu Exp $ */
/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */
/*
* Copyright (c) 2004 Ted Unangst and Todd Miller
@ -20,21 +20,21 @@
/* OPENBSD ORIGINAL: lib/libc/stdlib/strtonum.c */
#ifndef HAVE_STRTONUM
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#define INVALID 1
#define TOOSMALL 2
#define TOOLARGE 3
#define INVALID 1
#define TOOSMALL 2
#define TOOLARGE 3
long long
strtonum(const char *numstr, long long minval, long long maxval,
const char **errstrp)
{
long long ll = 0;
int error = 0;
char *ep;
int error = 0;
struct errval {
const char *errstr;
int err;
@ -47,9 +47,9 @@ strtonum(const char *numstr, long long minval, long long maxval,
ev[0].err = errno;
errno = 0;
if (minval > maxval) {
if (minval > maxval)
error = INVALID;
} else {
else {
ll = strtoll(numstr, &ep, 10);
if (numstr == ep || *ep != '\0')
error = INVALID;

View File

@ -1,2 +0,0 @@
#GIT_MERGE_AUTOEDIT=no git cvsimport -o master -v -k -m -d anoncvs@openbsd.cs.fau.de:/cvs xenocara/app/cwm
GIT_MERGE_AUTOEDIT=no git cvsimport -o master -v -k -m -d anoncvs@mirror.osn.de:/cvs xenocara/app/cwm

50
util.c
View File

@ -15,43 +15,46 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $OpenBSD$
* $Id$
*/
#include <sys/types.h>
#include "queue.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
#define MAXARGLEN 20
int
u_spawn(char *argstr)
{
switch (fork()) {
case 0:
u_exec(argstr);
err(1, "%s", argstr);
break;
case -1:
warn("fork");
return (-1);
default:
break;
}
return (0);
}
void
u_exec(char *argstr)
{
#define MAXARGLEN 20
char *args[MAXARGLEN], **ap = args;
char **end = &args[MAXARGLEN - 1], *tmp;
char *s = argstr;
while (ap < end && (*ap = strsep(&argstr, " \t")) != NULL) {
if (**ap == '\0')
@ -74,33 +77,8 @@ u_exec(char *argstr)
}
}
}
*ap = NULL;
(void)setsid();
(void)execvp(args[0], args);
err(1, "%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);
setsid();
execvp(args[0], args);
}

340
xevents.c
View File

@ -15,7 +15,7 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $OpenBSD$
* $Id$
*/
/*
@ -24,15 +24,15 @@
* management of the xevent's.
*/
#include <sys/types.h>
#include "queue.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,64 +43,83 @@ 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 };
static void
xev_handle_maprequest(XEvent *ee)
{
XMapRequestEvent *e = &ee->xmaprequest;
struct client_ctx *cc = NULL, *old_cc;
XWindowAttributes xattr;
struct winmatch *wm;
if ((old_cc = client_current()) != NULL)
client_ptrsave(old_cc);
if ((cc = client_find(e->window)) == NULL)
cc = client_init(e->window, NULL, 0);
if ((cc = client_find(e->window)) == NULL) {
XGetWindowAttributes(X_Dpy, e->window, &xattr);
cc = client_new(e->window, screen_fromroot(xattr.root), 1);
}
if ((cc != NULL) && (!(cc->flags & CLIENT_IGNORE)))
client_ptrwarp(cc);
TAILQ_FOREACH(wm, &Conf.ignoreq, entry) {
if (strncasecmp(wm->title, cc->name, strlen(wm->title)) == 0)
return;
}
client_ptrwarp(cc);
}
static void
xev_handle_unmapnotify(XEvent *ee)
{
XUnmapEvent *e = &ee->xunmap;
XEvent ev;
struct client_ctx *cc;
/* XXX, we need a recursive locking wrapper around grab server */
XGrabServer(X_Dpy);
if ((cc = client_find(e->window)) != NULL) {
if (e->send_event) {
client_set_wm_state(cc, WithdrawnState);
} else {
if (!(cc->flags & CLIENT_HIDDEN))
client_delete(cc);
}
/*
* 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
@ -125,34 +144,30 @@ xev_handle_configurerequest(XEvent *ee)
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);
xev_reconfig(cc);
} else {
/* let it do what it wants, it'll be ours when we map it. */
wc.x = e->x;
@ -182,25 +197,40 @@ xev_handle_propertynotify(XEvent *ee)
case XA_WM_NAME:
client_setname(cc);
break;
case XA_WM_HINTS:
client_wm_hints(cc);
client_draw_border(cc);
break;
case XA_WM_TRANSIENT_FOR:
client_transient(cc);
break;
default:
/* do nothing */
break;
}
} else {
TAILQ_FOREACH(sc, &Screenq, entry) {
if (sc->rootwin == e->window) {
if (e->atom == ewmh[_NET_DESKTOP_NAMES])
xu_ewmh_net_desktop_names(sc);
}
}
TAILQ_FOREACH(sc, &Screenq, entry)
if (sc->rootwin == e->window)
goto test;
return;
test:
if (e->atom == _NET_DESKTOP_NAMES)
group_update_names(sc);
}
}
void
xev_reconfig(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);
}
static void
@ -209,79 +239,75 @@ xev_handle_enternotify(XEvent *ee)
XCrossingEvent *e = &ee->xcrossing;
struct client_ctx *cc;
Last_Event_Time = e->time;
if ((cc = client_find(e->window)) != NULL)
client_setactive(cc);
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;
e->state &= ~IGNOREMODMASK;
sc = screen_fromroot(e->root);
cc = client_find(e->window);
TAILQ_FOREACH(mb, &Conf.mousebindq, entry) {
if (e->button == mb->press.button && e->state == mb->modmask)
/* Ignore caps lock and numlock */
e->state &= ~(Mod2Mask | LockMask);
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()) == NULL)
if (mb->context == MOUSEBIND_CTX_ROOT) {
if (e->window != sc->rootwin)
return;
(*mb->callback)(cc, mb->cargs);
break;
case CWM_CONTEXT_SC:
if (e->window != e->root)
return;
if ((sc = screen_find(e->window)) == NULL)
return;
(*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;
if ((cc = client_find(e->window)) != NULL) {
if (cc->flags & CLIENT_ACTIVE)
group_toggle_membership_leave(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;
keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
skeysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 1);
keysym = XKeycodeToKeysym(X_Dpy, e->keycode, 0);
skeysym = XKeycodeToKeysym(X_Dpy, e->keycode, 1);
e->state &= ~IGNOREMODMASK;
/* we don't care about caps lock and numlock here */
e->state &= ~(LockMask | Mod2Mask);
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;
@ -289,51 +315,55 @@ 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 (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:
if ((sc = screen_find(e->window)) == NULL)
return;
(*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)
{
XKeyEvent *e = &ee->xkey;
struct screen_ctx *sc;
KeySym keysym;
unsigned int i;
struct client_ctx *cc;
int keysym;
if ((sc = screen_find(e->root)) == NULL)
sc = screen_fromroot(e->root);
cc = client_current();
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]) {
client_cycle_leave(sc);
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);
}
}
@ -341,50 +371,17 @@ 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;
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_send_delete(cc);
}
} else if (e->message_type == ewmh[_NET_ACTIVE_WINDOW]) {
if ((cc = client_find(e->window)) != NULL) {
if ((old_cc = client_current()) != NULL)
client_ptrsave(old_cc);
if (cc->flags & CLIENT_HIDDEN)
client_unhide(cc);
else
client_raise(cc);
client_ptrwarp(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
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) {
group_only(sc, e->data.l[0]);
}
}
xa_wm_change_state = XInternAtom(X_Dpy, "WM_CHANGE_STATE", False);
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
@ -396,10 +393,10 @@ xev_handle_randr(XEvent *ee)
i = XRRRootToScreen(X_Dpy, rev->root);
TAILQ_FOREACH(sc, &Screenq, entry) {
if (sc->which == i) {
if (sc->which == (u_int)i) {
XRRUpdateConfiguration(ee);
screen_update_geometry(sc);
screen_assert_clients_within(sc);
screen_update_geometry(sc, rev->width, rev->height);
screen_init_xinerama(sc);
}
}
}
@ -412,13 +409,15 @@ static void
xev_handle_mappingnotify(XEvent *ee)
{
XMappingEvent *e = &ee->xmapping;
struct screen_ctx *sc;
struct keybinding *kb;
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
@ -431,14 +430,19 @@ xev_handle_expose(XEvent *ee)
client_draw_border(cc);
}
volatile sig_atomic_t _xev_quit = 0;
void
xev_process(void)
xev_loop(void)
{
XEvent e;
XNextEvent(X_Dpy, &e);
if (e.type - Conf.xrandr_event_base == RRScreenChangeNotify)
xev_handle_randr(&e);
else if (e.type < LASTEvent && xev_handlers[e.type] != NULL)
(*xev_handlers[e.type])(&e);
while (_xev_quit == 0) {
XNextEvent(X_Dpy, &e);
if (e.type - Randr_ev == RRScreenChangeNotify)
xev_handle_randr(&e);
else if (e.type < LASTEvent && xev_handlers[e.type] != NULL)
(*xev_handlers[e.type])(&e);
}
}

View File

@ -15,20 +15,17 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $OpenBSD$
* $Id$
*/
#include <sys/types.h>
#include "queue.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,21 +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 = vasprintf(ret, fmt, ap);
va_end(ap);
if (i < 0 || *ret == NULL)
err(1, "asprintf");
return(i);
return (p);
}

564
xutil.c
View File

@ -15,30 +15,70 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $OpenBSD$
* $Id$
*/
#include <sys/types.h>
#include "queue.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_getpos(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_ungrab(void)
{
XUngrabPointer(X_Dpy, CurrentTime);
}
void
xu_btn_grab(Window win, int mask, u_int btn)
{
int i;
for (i = 0; i < sizeof(ign_mods)/sizeof(*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 < sizeof(ign_mods)/sizeof(*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
@ -47,21 +87,68 @@ xu_ptr_setpos(Window win, int x, int y)
XWarpPointer(X_Dpy, None, win, 0, 0, 0, 0, x, y);
}
int
xu_getprop(Window win, Atom atm, Atom type, long len, unsigned char **p)
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 < sizeof(ign_mods)/sizeof(*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 < sizeof(ign_mods)/sizeof(*ign_mods); i++)
XUngrabKey(X_Dpy, code, (mask | ign_mods[i]), win);
}
void
xu_sendmsg(Window win, Atom atm, long val)
{
XEvent e;
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
@ -74,7 +161,7 @@ xu_getstrprop(Window win, Atom atm, char **text) {
XGetTextProperty(X_Dpy, win, &prop, atm);
if (!prop.nitems)
return(0);
return (0);
if (Xutf8TextPropertyToTextList(X_Dpy, &prop, &list,
&nitems) == Success && nitems > 0 && *list) {
@ -82,7 +169,7 @@ xu_getstrprop(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 {
@ -93,380 +180,107 @@ xu_getstrprop(Window win, Atom atm, char **text) {
XFree(prop.value);
return(nitems);
return (nitems);
}
/* Root Window Properties */
void
xu_ewmh_net_supported(struct screen_ctx *sc)
int
xu_getstate(struct client_ctx *cc, int *state)
{
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTED],
XA_ATOM, 32, PropModeReplace, (unsigned char *)ewmh, EWMH_NITEMS);
}
long *p = NULL;
void
xu_ewmh_net_supported_wm_check(struct screen_ctx *sc)
{
Window w;
if (xu_getprop(cc->win, WM_STATE, WM_STATE, 2L, (u_char **)&p) <= 0)
return (-1);
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_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);
}
Window
xu_ewmh_get_net_active_window(struct screen_ctx *sc)
{
long *p;
Window win;
if ((xu_getprop(sc->rootwin, ewmh[_NET_ACTIVE_WINDOW],
XA_WINDOW, 32, (unsigned char **)&p)) <= 0)
return(None);
win = (Window)*p;
XFree(p);
return(win);
}
void
xu_ewmh_net_wm_desktop_viewport(struct screen_ctx *sc)
{
long viewports[2] = {0, 0};
/* We don't support large desktops, so this is (0, 0). */
XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_VIEWPORT],
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2);
}
void
xu_ewmh_net_wm_number_of_desktops(struct screen_ctx *sc)
{
long ndesks = 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_getprop(sc->rootwin, ewmh[_NET_DESKTOP_NAMES],
cwmh[UTF8_STRING], 0xffffff, (unsigned char **)&prop_ret)) > 0) {
prop_ret[j - 1] = '\0'; /* paranoia */
while (i < j) {
if (prop_ret[i++] == '\0')
nstrings++;
}
}
p = (char *)prop_ret;
while (n < nstrings) {
TAILQ_FOREACH(gc, &sc->groupq, entry) {
if (gc->num == n) {
free(gc->name);
gc->name = xstrdup(p);
p += strlen(p) + 1;
break;
}
}
n++;
}
if (prop_ret != NULL)
XFree(prop_ret);
TAILQ_FOREACH(gc, &sc->groupq, entry)
len += strlen(gc->name) + 1;
q = p = 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 */
void
xu_ewmh_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_getprop(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;
static struct handlers {
int atom;
int property;
void (*toggle)(struct client_ctx *);
} handlers[] = {
{ _NET_WM_STATE_STICKY,
CLIENT_STICKY,
client_toggle_sticky },
{ _NET_WM_STATE_MAXIMIZED_VERT,
CLIENT_VMAXIMIZED,
client_toggle_vmaximize },
{ _NET_WM_STATE_MAXIMIZED_HORZ,
CLIENT_HMAXIMIZED,
client_toggle_hmaximize },
{ _NET_WM_STATE_HIDDEN,
CLIENT_HIDDEN,
client_toggle_hidden },
{ _NET_WM_STATE_FULLSCREEN,
CLIENT_FULLSCREEN,
client_toggle_fullscreen },
{ _NET_WM_STATE_DEMANDS_ATTENTION,
CLIENT_URGENCY,
client_urgency },
{ _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].property))
handlers[i].toggle(cc);
break;
case _NET_WM_STATE_REMOVE:
if (cc->flags & handlers[i].property)
handlers[i].toggle(cc);
break;
case _NET_WM_STATE_TOGGLE:
handlers[i].toggle(cc);
}
}
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[_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[_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_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);
}
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;
XFreeColors(X_Dpy, DefaultColormap(X_Dpy, sc->which), &pixel, 1, 0L);
}