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