wmii

git clone git://oldgit.suckless.org/wmii/
Log | Files | Refs | README | LICENSE

main.c (9402B)


      1 /* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com>
      2  * Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
      3  * See LICENSE file for license details.
      4  */
      5 #define EXTERN
      6 #include "dat.h"
      7 #include <X11/Xproto.h>
      8 #include <X11/cursorfont.h>
      9 #include <errno.h>
     10 #include <fcntl.h>
     11 #include <locale.h>
     12 #include <pwd.h>
     13 #include <sys/signal.h>
     14 #include <sys/stat.h>
     15 #include "fns.h"
     16 
     17 static const char
     18 	version[] = "wmii-"VERSION", "COPYRIGHT"\n";
     19 
     20 static char*	address;
     21 static char*	ns_path;
     22 static int	sleeperfd;
     23 static int	sock;
     24 static int	exitsignal;
     25 
     26 static struct sigaction	sa;
     27 static struct passwd*	passwd;
     28 
     29 static void
     30 usage(void) {
     31 	fatal("usage: wmii [-a <address>] [-r <wmiirc>] [-v]\n");
     32 }
     33 
     34 static int
     35 errfmt(Fmt *f) {
     36 	return fmtstrcpy(f, ixp_errbuf());
     37 }
     38 
     39 static void
     40 scan_wins(void) {
     41 	int i;
     42 	uint num;
     43 	XWindow *wins;
     44 	XWindowAttributes wa;
     45 	XWindow root, parent;
     46 
     47 	if(XQueryTree(display, scr.root.xid, &root, &parent, &wins, &num)) {
     48 		for(i = 0; i < num; i++) {
     49 			if(!XGetWindowAttributes(display, wins[i], &wa) || wa.override_redirect)
     50 				continue;
     51 			if(!XGetTransientForHint(display, wins[i], &parent))
     52 			if(wa.map_state == IsViewable)
     53 				client_create(wins[i], &wa);
     54 		}
     55 		/* Manage transients. */
     56 		for(i = 0; i < num; i++) {
     57 			if(!XGetWindowAttributes(display, wins[i], &wa) || wa.override_redirect)
     58 				continue;
     59 			if(XGetTransientForHint(display, wins[i], &parent))
     60 			if(wa.map_state == IsViewable)
     61 				client_create(wins[i], &wa);
     62 		}
     63 	}
     64 	if(wins)
     65 		XFree(wins);
     66 }
     67 
     68 static void
     69 init_ns(void) {
     70 	char *s;
     71 
     72 	if(address && strncmp(address, "unix!", 5) == 0) {
     73 		ns_path = estrdup(&address[5]);
     74 		s = strrchr(ns_path, '/');
     75 		if(s != nil)
     76 			*s = '\0';
     77 		if(ns_path[0] != '/' || ns_path[0] == '\0')
     78 			fatal("address %q is not an absolute path", address);
     79 		setenv("NAMESPACE", ns_path, true);
     80 	}else
     81 		ns_path = ixp_namespace();
     82 
     83 	if(ns_path == nil)
     84 		fatal("Bad namespace path: %r\n");
     85 }
     86 
     87 static void
     88 init_environment(void) {
     89 	init_ns();
     90 
     91 	if(address)
     92 		setenv("WMII_ADDRESS", address, true);
     93 	else
     94 		address = smprint("unix!%s/wmii", ns_path);
     95 	setenv("WMII_CONFPATH",
     96 	       sxprint("%s/.%s:%s", getenv("HOME"), CONFDIR, GLOBALCONF),
     97 	       true);
     98 }
     99 
    100 static void
    101 create_cursor(int ident, uint shape) {
    102 	cursor[ident] = XCreateFontCursor(display, shape);
    103 }
    104 
    105 static void
    106 init_cursors(void) {
    107 	static char zchar[1];
    108 	Pixmap pix;
    109 	XColor black, dummy;
    110 
    111 	create_cursor(CurNormal, XC_left_ptr);
    112 	create_cursor(CurNECorner, XC_top_right_corner);
    113 	create_cursor(CurNWCorner, XC_top_left_corner);
    114 	create_cursor(CurSECorner, XC_bottom_right_corner);
    115 	create_cursor(CurSWCorner, XC_bottom_left_corner);
    116 	create_cursor(CurMove, XC_fleur);
    117 	create_cursor(CurDHArrow, XC_sb_h_double_arrow);
    118 	create_cursor(CurDVArrow, XC_sb_v_double_arrow);
    119 	create_cursor(CurInput, XC_xterm);
    120 	create_cursor(CurSizing, XC_sizing);
    121 	create_cursor(CurIcon, XC_icon);
    122 	create_cursor(CurTCross, XC_tcross);
    123 
    124 	XAllocNamedColor(display, scr.colormap,
    125 			"black", &black, &dummy);
    126 	pix = XCreateBitmapFromData(
    127 			display, scr.root.xid,
    128 			zchar, 1, 1);
    129 
    130 	cursor[CurNone] = XCreatePixmapCursor(display,
    131 			pix, pix,
    132 			&black, &black,
    133 			0, 0);
    134 
    135 	XFreePixmap(display, pix);
    136 }
    137 
    138 /*
    139  * There's no way to check accesses to destroyed windows, thus
    140  * those cases are ignored (especially on UnmapNotifies).
    141  * Other types of errors call Xlib's default error handler, which
    142  * calls exit().
    143  */
    144 ErrorCode ignored_xerrors[] = {
    145 	{ 0, BadWindow },
    146 	{ X_SetInputFocus, BadMatch },
    147 	{ X_PolyText8, BadDrawable },
    148 	{ X_PolyFillRectangle, BadDrawable },
    149 	{ X_PolySegment, BadDrawable },
    150 	{ X_ConfigureWindow, BadMatch },
    151 	{ X_GrabKey, BadAccess },
    152 	{ X_GetAtomName, BadAtom },
    153 	{ 0, }
    154 };
    155 
    156 void
    157 regerror(char *err) {
    158 	fprint(2, "%s: %s\n", argv0, err);
    159 }
    160 
    161 void
    162 init_screens(void) {
    163 	Rectangle *rects;
    164 	View *v;
    165 	int i, n, m;
    166 
    167 #ifdef notdef
    168 	d.x = Dx(scr.rect) - Dx(screen->r);
    169 	d.y = Dy(scr.rect) - Dy(screen->r);
    170 	for(v=view; v; v=v->next) {
    171 		v->r.max.x += d.x;
    172 		v->r.max.y += d.y;
    173 	}
    174 #endif
    175 
    176 	/* Reallocate screens, zero any new ones. */
    177 	rects = xinerama_screens(&n);
    178 	m = nscreens;
    179 	nscreens = max(n, nscreens);
    180 	screens = erealloc(screens, (nscreens + 1) * sizeof *screens);
    181 	screens[nscreens] = nil;
    182 	for(v=view; v; v=v->next) {
    183 		v->areas = erealloc(v->areas, nscreens * sizeof *v->areas);
    184 		v->r = erealloc(v->r, nscreens * sizeof *v->r);
    185 		v->pad = erealloc(v->pad, nscreens * sizeof *v->pad);
    186 	}
    187 
    188 	/* Reallocate buffers. */
    189 	freeimage(disp.ibuf);
    190 	freeimage(disp.ibuf32);
    191 	disp.ibuf = allocimage(Dx(scr.rect), Dy(scr.rect), scr.depth);
    192 	disp.ibuf32 = nil; /* Probably shouldn't do this until it's needed. */
    193 	if(render_visual)
    194 		disp.ibuf32 = allocimage(Dx(scr.rect), Dy(scr.rect), 32);
    195 
    196 	/* Resize and initialize screens. */
    197 	for(i=0; i < nscreens; i++) {
    198 		if(i >= m)
    199 			screens[i] = emallocz(sizeof *screens[i]);
    200 
    201 		screen = screens[i];
    202 		screen->idx = i;
    203 
    204 		screen->showing = i < n;
    205 		if(screen->showing)
    206 			screen->r = rects[i];
    207 		else
    208 			screen->r = rectsetorigin(screen->r, scr.rect.max);
    209 		if(i >= m)
    210 			for(v=view; v; v=v->next)
    211 				view_init(v, i);
    212 		def.snap = Dy(screen->r) / 63;
    213 		bar_init(screens[i]);
    214 	}
    215 	screen = screens[0];
    216 	if(selview)
    217 		view_update(selview);
    218 }
    219 
    220 static void
    221 cleanup(void) {
    222 	starting = -1;
    223 	while(client)
    224 		client_destroy(client);
    225 	ixp_server_close(&srv);
    226 	close(sleeperfd);
    227 }
    228 
    229 static void
    230 cleanup_handler(int signal) {
    231 	sa.sa_handler = SIG_DFL;
    232 	sigaction(signal, &sa, nil);
    233 
    234 	srv.running = false;
    235 
    236 	switch(signal) {
    237 	case SIGTERM:
    238 		sa.sa_handler = cleanup_handler;
    239 		sigaction(SIGALRM, &sa, nil);
    240 		alarm(1);
    241 	default:
    242 		exitsignal = signal;
    243 		break;
    244 	case SIGALRM:
    245 		raise(SIGTERM);
    246 	case SIGINT:
    247 		break;
    248 	}
    249 }
    250 
    251 static void
    252 init_traps(void) {
    253 	char buf[1];
    254 	int fd[2];
    255 
    256 	if(pipe(fd) != 0)
    257 		fatal("Can't pipe(): %r");
    258 
    259 	if(doublefork() == 0) {
    260 		close(fd[1]);
    261 		close(ConnectionNumber(display));
    262 		setsid();
    263 
    264 		display = XOpenDisplay(nil);
    265 		if(!display)
    266 			fatal("Can't open display");
    267 
    268 		/* Wait for parent to exit */
    269 		read(fd[0], buf, 1);
    270 
    271 		setfocus(pointerwin, RevertToPointerRoot);
    272 		XCloseDisplay(display);
    273 		exit(0);
    274 	}
    275 
    276 	close(fd[0]);
    277 	sleeperfd = fd[1];
    278 
    279 	sa.sa_flags = 0;
    280 	sa.sa_handler = cleanup_handler;
    281 	sigaction(SIGINT, &sa, nil);
    282 	sigaction(SIGTERM, &sa, nil);
    283 	sigaction(SIGQUIT, &sa, nil);
    284 	sigaction(SIGHUP, &sa, nil);
    285 	sigaction(SIGUSR1, &sa, nil);
    286 	sigaction(SIGUSR2, &sa, nil);
    287 }
    288 
    289 void
    290 spawn_command(const char *cmd) {
    291 	char *shell, *p;
    292 
    293 
    294 	if(doublefork() == 0) {
    295 		if((p = pathsearch(getenv("WMII_CONFPATH"), cmd, true)))
    296 			cmd = p;
    297 
    298 		if(setsid() == -1)
    299 			fatal("Can't setsid: %r");
    300 
    301 		/* Run through the user's shell as a login shell */
    302 		shell = passwd->pw_shell;
    303 		if(shell[0] != '/')
    304 			fatal("Shell is not an absolute path: %s", shell);
    305 		p = smprint("-%s", strrchr(shell, '/') + 1);
    306 
    307 		close(0);
    308 		open("/dev/null", O_RDONLY);
    309 
    310 		execl(shell, p, "-c", cmd, nil);
    311 		fatal("Can't exec '%s': %r", cmd);
    312 		/* NOTREACHED */
    313 	}
    314 }
    315 
    316 static void
    317 closedisplay(IxpConn *c) {
    318 	USED(c);
    319 
    320 	XCloseDisplay(display);
    321 }
    322 
    323 static void
    324 printfcall(IxpFcall *f) {
    325 	Dprint(D9p, "%F\n", f);
    326 }
    327 
    328 int
    329 main(int argc, char *argv[]) {
    330 	char **oargv;
    331 	char *wmiirc;
    332 	int i;
    333 
    334 	IXP_ASSERT_VERSION;
    335 
    336 	setlocale(LC_CTYPE, "");
    337 	fmtinstall('r', errfmt);
    338 	fmtinstall('a', afmt);
    339 	fmtinstall('C', Cfmt);
    340 	fmtinstall('E', fmtevent);
    341 	quotefmtinstall();
    342 
    343 	wmiirc = "wmiirc";
    344 
    345 	oargv = argv;
    346 	ARGBEGIN{
    347 	case 'a':
    348 		address = EARGF(usage());
    349 		break;
    350 	case 'r':
    351 		wmiirc = EARGF(usage());
    352 		break;
    353 	case 'v':
    354 		lprint(1, "%s", version);
    355 		exit(0);
    356 	case 'D':
    357 		if(waserror())
    358 			fatal("parsing debug flags: %r");
    359 		msg_debug(EARGF(usage()));
    360 		poperror();
    361 		break;
    362 	default:
    363 		usage();
    364 		break;
    365 	}ARGEND;
    366 
    367 	if(argc)
    368 		usage();
    369 
    370 	starting = true;
    371 
    372 	initdisplay();
    373 
    374 	traperrors(true);
    375 	selectinput(&scr.root, SubstructureRedirectMask);
    376 	if(traperrors(false))
    377 		fatal("another window manager is already running");
    378 
    379 	passwd = getpwuid(getuid());
    380 	user = estrdup(passwd->pw_name);
    381 	gethostname(hostname, sizeof(hostname) - 1);
    382 
    383 	init_environment();
    384 
    385 	fmtinstall('F', Ffmt);
    386 	ixp_printfcall = printfcall;
    387 
    388 	sock = ixp_announce(address);
    389 	if(sock < 0)
    390 		fatal("Can't create socket %q: %r", address);
    391 	closeexec(ConnectionNumber(display));
    392 	closeexec(sock);
    393 
    394 	if(wmiirc[0])
    395 		spawn_command(wmiirc);
    396 
    397 	init_traps();
    398 	init_cursors();
    399 	update_keys();
    400 	ewmh_init();
    401 	xext_init();
    402 
    403 	event_debug = debug_event;
    404 
    405 	srv.preselect = event_preselect;
    406 	ixp_listen(&srv, sock, &p9srv, ixp_serve9conn, nil);
    407 	ixp_listen(&srv, ConnectionNumber(display), nil, event_fdready, closedisplay);
    408 
    409 	def.border = 1;
    410 	def.colmode = Colstack;
    411 	def.font = loadfont(FONT);
    412 	def.incmode = ISqueeze;
    413 
    414 	def.mod = Mod1Mask;
    415 
    416 	loadcolor(&def.focuscolor, FOCUSCOLORS, nil);
    417 	loadcolor(&def.normcolor, NORMCOLORS, nil);
    418 
    419 	disp.sel = pointerscreen();
    420 
    421 	init_screens();
    422 	root_init();
    423 
    424 	disp.focus = nil;
    425 	setfocus(screen->barwin, RevertToParent);
    426 	view_select("1");
    427 
    428 	scan_wins();
    429 	starting = false;
    430 
    431 	view_update_all();
    432 	ewmh_updateviews();
    433 
    434 	event("FocusTag %s\n", selview->name);
    435 
    436 	i = ixp_serverloop(&srv);
    437 	if(i)
    438 		fprint(2, "%s: error: %r\n", argv0);
    439 	else
    440 		event("Quit");
    441 
    442 	cleanup();
    443 
    444 	if(exitsignal)
    445 		raise(exitsignal);
    446 	if(execstr) {
    447 		char *toks[32];
    448 		int n;
    449 
    450 		n = unquote(strdup(execstr), toks, nelem(toks)-1);
    451 		toks[n] = nil;
    452 		execvp(toks[0], toks);
    453 		fprint(2, "%s: failed to exec %q: %r\n", argv0, execstr);
    454 		execvp(argv0, oargv);
    455 		fatal("failed to exec myself");
    456 	}
    457 	return i;
    458 }
    459