summary refs log tree commit diff
path: root/calmwm.c
diff options
context:
space:
mode:
Diffstat (limited to 'calmwm.c')
-rw-r--r--calmwm.c476
1 files changed, 189 insertions, 287 deletions
diff --git a/calmwm.c b/calmwm.c
index ab0142e..3601d6c 100644
--- a/calmwm.c
+++ b/calmwm.c
@@ -2,344 +2,246 @@
  * calmwm - the calm window manager
  *
  * Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
- * All rights reserved.
  *
- * $Id$
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $OpenBSD$
  */
 
-#include "headers.h"
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <locale.h>
+#include <poll.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
 #include "calmwm.h"
 
-Display				*G_dpy;
-XFontStruct			*G_font;
-
-Cursor				 G_cursor_move;
-Cursor				 G_cursor_resize;
-Cursor				 G_cursor_select;
-Cursor				 G_cursor_default;
-Cursor				 G_cursor_question;
-
-struct screen_ctx_q		 G_screenq;
-struct screen_ctx		*G_curscreen;
-u_int				 G_nscreens;
-
-struct client_ctx_q		 G_clientq;
-
-int				 G_doshape, G_shape_ev;
-int				 G_starting;
-struct conf			 G_conf;
-struct fontdesc                 *DefaultFont;
-char                            *DefaultFontName;
-
-/* From TWM */
-#define gray_width 2
-#define gray_height 2
-static char gray_bits[] = {0x02, 0x01};
-
-/* List borrowed from 9wm/rio */
-char *tryfonts[] = {
-	"9x15bold",
-	"blit",
-	"*-lucidatypewriter-bold-*-14-*-75-*",
-	"*-lucidatypewriter-medium-*-12-*-75-*",
-	"fixed",
-	"*",
-	NULL,
-};
-
-static void _sigchld_cb(int);
+Display			*X_Dpy;
+Time			 Last_Event_Time = CurrentTime;
+Atom			 cwmh[CWMH_NITEMS];
+Atom			 ewmh[EWMH_NITEMS];
+struct screen_q		 Screenq = TAILQ_HEAD_INITIALIZER(Screenq);
+struct conf		 Conf;
+volatile sig_atomic_t	 cwm_status;
+
+static void	sighdlr(int);
+static int	x_errorhandler(Display *, XErrorEvent *);
+static int	x_init(const char *);
+static void	x_teardown(void);
+static int	x_wmerrorhandler(Display *, XErrorEvent *);
 
 int
 main(int argc, char **argv)
 {
-	int ch;
-	int conf_flags = 0;
-
-	DefaultFontName = "sans-serif:pixelsize=14:bold";
-
-	while ((ch = getopt(argc, argv, "sf:")) != -1) {
+	const char	*conf_file = NULL;
+	char		*conf_path, *display_name = NULL;
+	char		*fallback;
+	int		 ch, xfd;
+	struct pollfd	 pfd[1];
+	struct passwd	*pw;
+
+	if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
+		warnx("no locale support");
+	mbtowc(NULL, NULL, MB_CUR_MAX);
+
+	fallback = u_argv(argv);
+	Conf.wm_argv = u_argv(argv);
+	while ((ch = getopt(argc, argv, "c:d:")) != -1) {
 		switch (ch) {
-		case 's':
-			conf_flags |= CONF_STICKY_GROUPS;
+		case 'c':
+			conf_file = optarg;
 			break;
-		case 'f':
-			DefaultFontName = xstrdup(optarg);
+		case 'd':
+			display_name = optarg;
 			break;
 		default:
-			errx(1, "Unknown option '%c'", ch);
+			usage();
 		}
 	}
+	argc -= optind;
+	argv += optind;
+
+	if (signal(SIGCHLD, sighdlr) == SIG_ERR)
+		err(1, "signal");
+	if (signal(SIGHUP, sighdlr) == SIG_ERR)
+		err(1, "signal");
+
+	Conf.homedir = getenv("HOME");
+	if ((Conf.homedir == NULL) || (Conf.homedir[0] == '\0')) {
+		pw = getpwuid(getuid());
+		if (pw != NULL && pw->pw_dir != NULL && *pw->pw_dir != '\0')
+			Conf.homedir = pw->pw_dir;
+		else
+			Conf.homedir = "/";
+	}
 
-	/* Ignore a few signals. */
-        if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
-                err(1, "signal");
-
-        if (signal(SIGCHLD, _sigchld_cb) == SIG_ERR)
-                err(1, "signal");
-
-	group_init();
-
-	G_starting = 1;
-	conf_setup(&G_conf);
-	G_conf.flags |= conf_flags;
-	client_setup();
-	x_setup();
-	G_starting = 0;
-
-	xev_init();
-	XEV_QUICK(NULL, NULL, MapRequest, xev_handle_maprequest, NULL);
-	XEV_QUICK(NULL, NULL, UnmapNotify, xev_handle_unmapnotify, NULL);
-	XEV_QUICK(NULL, NULL, ConfigureRequest,
-	    xev_handle_configurerequest, NULL);
-	XEV_QUICK(NULL, NULL, PropertyNotify, xev_handle_propertynotify, NULL);
-	XEV_QUICK(NULL, NULL, EnterNotify, xev_handle_enternotify, NULL);
-	XEV_QUICK(NULL, NULL, LeaveNotify, xev_handle_leavenotify, NULL);
-	XEV_QUICK(NULL, NULL, ButtonPress, xev_handle_buttonpress, NULL);
-	XEV_QUICK(NULL, NULL, ButtonRelease, xev_handle_buttonrelease, NULL);
-	XEV_QUICK(NULL, NULL, KeyPress, xev_handle_keypress, NULL);
-	XEV_QUICK(NULL, NULL, KeyRelease, xev_handle_keyrelease, NULL);
-	XEV_QUICK(NULL, NULL, Expose, xev_handle_expose, NULL);
-	XEV_QUICK(NULL, NULL, DestroyNotify, xev_handle_destroynotify, NULL);
-	XEV_QUICK(NULL, NULL, ClientMessage, xev_handle_clientmessage, NULL);
-
-	xev_loop();
-
-	return (0);
-}
+	if (conf_file == NULL)
+		xasprintf(&conf_path, "%s/%s", Conf.homedir, CONFFILE);
+	else
+		conf_path = xstrdup(conf_file);
 
-void
-x_setup(void)
-{
-	int i;
-	struct screen_ctx *sc;
-	char *fontname;
+	if (access(conf_path, R_OK) != 0) {
+		if (conf_file != NULL)
+			warn("%s", conf_file);
+		free(conf_path);
+		conf_path = NULL;
+	}
 
-	TAILQ_INIT(&G_screenq);
+	conf_init(&Conf);
 
-	if ((G_dpy = XOpenDisplay("")) == NULL)
-		errx(1, "%s:%d XOpenDisplay()", __FILE__, __LINE__);
+	if (conf_path && (parse_config(conf_path, &Conf) == -1))
+		warnx("config file %s has errors", conf_path);
+	free(conf_path);
 
-	XSetErrorHandler(x_errorhandler);
+	xfd = x_init(display_name);
+	cwm_status = CWM_RUNNING;
 
-	G_doshape = XShapeQueryExtension(G_dpy, &G_shape_ev, &i);
+	if (pledge("stdio rpath proc exec", NULL) == -1)
+		err(1, "pledge");
 
-	i = 0;
-	while ((fontname = tryfonts[i++]) != NULL) {
-		if ((G_font = XLoadQueryFont(G_dpy, fontname)) != NULL)
-			break;
+	memset(&pfd, 0, sizeof(pfd));
+	pfd[0].fd = xfd;
+	pfd[0].events = POLLIN;
+	while (cwm_status == CWM_RUNNING) {
+		xev_process();
+		if (poll(pfd, 1, INFTIM) == -1) {
+			if (errno != EINTR)
+				warn("poll");
+		}
 	}
+	x_teardown();
+	if (cwm_status == CWM_EXEC_WM)
+		u_exec(Conf.wm_argv);
 
-	if (fontname == NULL)
-		errx(1, "Couldn't load any fonts.");
+	warnx("'%s' failed to start, restarting fallback", Conf.wm_argv);
+	u_exec(fallback);
 
-	G_nscreens = ScreenCount(G_dpy);
-	for (i = 0; i < (int)G_nscreens; i++) {
-		XMALLOC(sc, struct screen_ctx);
-		x_setupscreen(sc, i);
-		TAILQ_INSERT_TAIL(&G_screenq, sc, entry);
-	}
-
-	G_cursor_move = XCreateFontCursor(G_dpy, XC_fleur);
-	G_cursor_resize = XCreateFontCursor(G_dpy, XC_bottom_right_corner);
-	/* (used to be) XCreateFontCursor(G_dpy, XC_hand1); */
-	G_cursor_select = XCreateFontCursor(G_dpy, XC_hand1);
-/* 	G_cursor_select = cursor_bigarrow(G_curscreen); */
-	G_cursor_default = XCreateFontCursor(G_dpy, XC_X_cursor);
-/* 	G_cursor_default = cursor_bigarrow(G_curscreen); */
-	G_cursor_question = XCreateFontCursor(G_dpy, XC_question_arrow);
+	return(0);
 }
 
-int
-x_setupscreen(struct screen_ctx *sc, u_int which)
+static int
+x_init(const char *dpyname)
 {
-	XColor tmp;
-	XGCValues gv, gv1/* , gv2 */;
-	Window *wins, w0, w1;
-	u_int nwins, i = 0;
-	XWindowAttributes winattr;
-	XSetWindowAttributes rootattr;
-	struct keybinding *kb;
-
-	sc->display = x_screenname(which);
-	sc->which = which;
-	sc->rootwin = RootWindow(G_dpy, which);
-	XAllocNamedColor(G_dpy, DefaultColormap(G_dpy, which),
-	    "black", &sc->fgcolor, &tmp);
-	XAllocNamedColor(G_dpy, DefaultColormap(G_dpy, which),
-	    "#00cc00", &sc->bgcolor, &tmp);
-	XAllocNamedColor(G_dpy,DefaultColormap(G_dpy, which),
-	    "blue", &sc->fccolor, &tmp);
-	XAllocNamedColor(G_dpy, DefaultColormap(G_dpy, which),
-	    "red", &sc->redcolor, &tmp);
-	XAllocNamedColor(G_dpy, DefaultColormap(G_dpy, which),
-	    "#00ccc8", &sc->cyancolor, &tmp);
-	XAllocNamedColor(G_dpy, DefaultColormap(G_dpy, which),
-	    "white", &sc->whitecolor, &tmp);
-	XAllocNamedColor(G_dpy, DefaultColormap(G_dpy, which),
-	    "black", &sc->blackcolor, &tmp);
-
-	TAILQ_FOREACH(kb, &G_conf.keybindingq, entry)
-		xu_key_grab(sc->rootwin, kb->modmask, kb->keysym);
-
-	/* Special -- for alt state. */
-/* 	xu_key_grab(sc->rootwin, 0, XK_Alt_L); */
-/* 	xu_key_grab(sc->rootwin, 0, XK_Alt_R); */
-
-	sc->blackpixl = BlackPixel(G_dpy, sc->which);
-	sc->whitepixl = WhitePixel(G_dpy, sc->which);
-	sc->bluepixl = sc->fccolor.pixel;
-	sc->redpixl = sc->redcolor.pixel;
-	sc->cyanpixl = sc->cyancolor.pixel;
-
-        sc->gray = XCreatePixmapFromBitmapData(G_dpy, sc->rootwin,
-            gray_bits, gray_width, gray_height,
-            sc->blackpixl, sc->whitepixl, DefaultDepth(G_dpy, sc->which));
-
-        sc->blue = XCreatePixmapFromBitmapData(G_dpy, sc->rootwin,
-            gray_bits, gray_width, gray_height,
-            sc->bluepixl, sc->whitepixl, DefaultDepth(G_dpy, sc->which));
-
-        sc->red = XCreatePixmapFromBitmapData(G_dpy, sc->rootwin,
-            gray_bits, gray_width, gray_height,
-	    sc->redpixl, sc->whitepixl, DefaultDepth(G_dpy, sc->which));
-
-	gv.foreground = sc->blackpixl^sc->whitepixl;
-	gv.background = sc->whitepixl;
-	gv.function = GXxor;
-	gv.line_width = 1;
-	gv.subwindow_mode = IncludeInferiors;
-	gv.font = G_font->fid;
-
-	sc->gc = XCreateGC(G_dpy, sc->rootwin,
-	    GCForeground|GCBackground|GCFunction|
-	    GCLineWidth|GCSubwindowMode|GCFont, &gv);
-
-#ifdef notyet
-	gv2.foreground = sc->blackpixl^sc->cyanpixl;
-	gv2.background = sc->cyanpixl;
-	gv2.function = GXxor;
-	gv2.line_width = 1;
-	gv2.subwindow_mode = IncludeInferiors;
-	gv2.font = G_font->fid;
-#endif
-
-	sc->hlgc = XCreateGC(G_dpy, sc->rootwin,
-	    GCForeground|GCBackground|GCFunction|
-	    GCLineWidth|GCSubwindowMode|GCFont, &gv);
-
-	gv1.function = GXinvert;
-	gv1.subwindow_mode = IncludeInferiors;
-	gv1.line_width = 1;
-	gv1.font = G_font->fid;
-
-	sc->invgc = XCreateGC(G_dpy, sc->rootwin,
-	    GCFunction|GCSubwindowMode|GCLineWidth|GCFont, &gv1);
-
-	font_init(sc);
-	DefaultFont = font_getx(sc, DefaultFontName);
-
-	/*
-	 * XXX - this should *really* be in screen_init().  ordering
-	 * problem.
-	 */
-	TAILQ_INIT(&sc->mruq);
-
-	/* Initialize menu window. */
-	grab_menuinit(sc);
-	search_init(sc);
-
-	/* Deal with existing clients. */
-	XQueryTree(G_dpy, sc->rootwin, &w0, &w1, &wins, &nwins);	
-
-	for (i = 0; i < nwins; i++) {
-		XGetWindowAttributes(G_dpy, wins[i], &winattr);
-		if (winattr.override_redirect ||
-		    winattr.map_state != IsViewable) {
-			char *name;
-			XFetchName(G_dpy, wins[i], &name);
-			continue;
-		}
-		client_new(wins[i], sc, winattr.map_state != IsUnmapped);
-	}
-	XFree(wins);
+	int	i;
 
-	G_curscreen = sc;	/* XXX */
-	screen_init();
-	screen_updatestackingorder();
+	if ((X_Dpy = XOpenDisplay(dpyname)) == NULL)
+		errx(1, "unable to open display \"%s\"", XDisplayName(dpyname));
 
-	rootattr.event_mask = ChildMask|PropertyChangeMask|EnterWindowMask|
-	    LeaveWindowMask|ColormapChangeMask|ButtonMask;
+	XSetErrorHandler(x_wmerrorhandler);
+	XSelectInput(X_Dpy, DefaultRootWindow(X_Dpy), SubstructureRedirectMask);
+	XSync(X_Dpy, False);
+	XSetErrorHandler(x_errorhandler);
 
-	/* Set the root cursor to a nice obnoxious arrow :-) */
-/* 	rootattr.cursor = cursor_bigarrow(sc); */
+	Conf.xrandr = XRRQueryExtension(X_Dpy, &Conf.xrandr_event_base, &i);
 
-	XChangeWindowAttributes(G_dpy, sc->rootwin,
-	    /* CWCursor| */CWEventMask, &rootattr);
+	conf_atoms();
+	conf_cursor(&Conf);
 
-	XSync(G_dpy, False);
+	for (i = 0; i < ScreenCount(X_Dpy); i++)
+		screen_init(i);
 
-	return (0);
+	return ConnectionNumber(X_Dpy);
 }
 
-char *
-x_screenname(int which)
+static void
+x_teardown(void)
 {
-	char *cp, *dstr, *sn;
-	size_t snlen;
-
-	if (which > 9)
-		errx(1, "Can't handle more than 9 screens.  If you need it, "
-		    "tell <marius@monkey.org>.  It's a trivial fix.");
-
-	dstr = xstrdup(DisplayString(G_dpy));
-
-	if ((cp = rindex(dstr, ':')) == NULL)
-		return (NULL);
-
-	if ((cp = index(cp, '.')) != NULL)
-		*cp = '\0';
+	struct screen_ctx	*sc;
+	unsigned int		 i;
+
+	conf_clear(&Conf);
+
+	TAILQ_FOREACH(sc, &Screenq, entry) {
+		for (i = 0; i < CWM_COLOR_NITEMS; i++)
+			XftColorFree(X_Dpy, DefaultVisual(X_Dpy, sc->which),
+			    DefaultColormap(X_Dpy, sc->which),
+			    &sc->xftcolor[i]);
+		XftFontClose(X_Dpy, sc->xftfont);
+		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);
+}
 
-	snlen = strlen(dstr) + 3; /* string, dot, number, null */
-	sn = (char *)xmalloc(snlen);
-	snprintf(sn, snlen, "%s.%d", dstr, which);
-	free(dstr);
+static int
+x_wmerrorhandler(Display *dpy, XErrorEvent *e)
+{
+	errx(1, "root window unavailable - perhaps another wm is running?");
 
-	return (sn);
+	return(0);
 }
 
-int
+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(G_dpy, e->error_code, msg, sizeof(msg));
-		snprintf(number, sizeof(number), "%d", e->request_code);
-		XGetErrorDatabaseText(G_dpy, "XRequest", number,
-		    "<unknown>", req, sizeof(req));
+	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));
 
-		warnx("%s(0x%x): %s", req, (u_int)e->resourceid, msg);
-	}
+	warnx("%s(0x%x): %s", req, (unsigned int)e->resourceid, msg);
 #endif
+	return(0);
+}
 
-	if (G_starting && 
-	    e->error_code == BadAccess &&                                       
-	    e->request_code == X_GrabKey)                        
-		errx(1, "root window unavailable - perhaps another "
-		    "wm is running?");                                      
+static void
+sighdlr(int sig)
+{
+	pid_t	 pid;
+	int	 save_errno = errno, status;
+
+	switch (sig) {
+	case SIGCHLD:
+		/* Collect dead children. */
+		while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0 ||
+		    (pid < 0 && errno == EINTR))
+			;
+		break;
+	case SIGHUP:
+		cwm_status = CWM_EXEC_WM;
+		break;
+	}
 
-	return (0);
+	errno = save_errno;
 }
 
-static void
-_sigchld_cb(int which)
+__dead void
+usage(void)
 {
-	pid_t pid;
-	int status;
+	extern char	*__progname;
 
-/* 	Collect dead children. */
-        while ((pid = waitpid(-1, &status, WNOHANG)) > 0 ||
-            (pid < 0 && errno == EINTR))
-		;
+	(void)fprintf(stderr, "usage: %s [-c file] [-d display]\n",
+	    __progname);
+	exit(1);
 }