main.c (12855B)
1 /* 2 * Copyright (C) Raymond Cole <rc@wolog.xyz> 3 * 4 * Licensed under the GNU General Public License; either version 3 of 5 * the License, or (at your option) any later version. See the LICENSE 6 * file for details. 7 * 8 * Portions of the code originally belong to dwm as developed by Anselm 9 * R. Garbe et al. See LICENSE-dwm for the copyright and license details 10 * of those portions. 11 */ 12 #include <errno.h> 13 #include <locale.h> 14 #include <poll.h> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <unistd.h> 19 #include <sys/wait.h> 20 #include <X11/cursorfont.h> 21 #include <X11/Xatom.h> 22 #include <X11/Xlib.h> 23 #include <X11/Xproto.h> 24 #include <X11/Xutil.h> 25 26 #include "swm.h" 27 #include "util.h" 28 29 extern char **environ; 30 31 int running = 1; 32 int restart; 33 int pending; 34 Display *dpy; 35 Window root; 36 int screen; 37 int sw, sh; 38 Monitor *mons, *selmon; 39 Atom wmatom[WMLast], netatom[NetLast]; 40 const char *tags[] = TAGS; 41 unsigned long pixelfocus, pixelunfocus, pixelurgent; 42 Cursor curnormal, curresize, curmove; 43 unsigned lockmasks[4]; 44 45 static int xerror(Display *dpy, XErrorEvent *ee); 46 static int xerrorstart(Display *dpy, XErrorEvent *ee); 47 static void checkotherwm(void); 48 static void initatoms(void); 49 static unsigned long *getpixel(const char *name, unsigned long *pixel); 50 static unsigned getnumlockmask(void); 51 static void dumpmonitors(char *var); 52 static void loadmonitors(char *s); 53 static void dumpclients(char *var, Monitor *m); 54 static void loadclients(int n, char *s); 55 static void dumpworkspaces(char *var, Monitor *m); 56 static void loadworkspaces(int n, char *s); 57 static void dumpstate(void); 58 static void loadstate(void); 59 static void setup(void); 60 static void scan(void); 61 static void run(void); 62 static void cleanup(void); 63 64 static int (*xerrorxlib)(Display *, XErrorEvent *); 65 66 int 67 xerror(Display *dpy, XErrorEvent *ee) 68 { 69 unsigned char r = ee->request_code; 70 unsigned char e = ee->error_code; 71 72 warn("Xlib error: request code=%d, error code=%d", r, e); 73 switch (e) { 74 case BadAccess: 75 if (r == X_GrabButton 76 || r == X_GrabKey) 77 return 0; 78 break; 79 case BadDrawable: 80 if (r == X_PolyText8 81 || r == X_PolyFillRectangle 82 || r == X_PolySegment 83 || r == X_CopyArea) 84 return 0; 85 break; 86 case BadMatch: 87 if (r == X_SetInputFocus 88 || r == X_ConfigureWindow) 89 return 0; 90 break; 91 case BadWindow: 92 return 0; 93 default: 94 break; 95 } 96 return xerrorxlib(dpy, ee); /* might call exit */ 97 } 98 99 int 100 xerrorstart(Display *dpy, XErrorEvent *ee) 101 { 102 die("another window manager is already running"); 103 return -1; 104 } 105 106 void 107 checkotherwm(void) 108 { 109 xerrorxlib = XSetErrorHandler(xerrorstart); 110 XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); 111 XSync(dpy, False); 112 XSetErrorHandler(xerror); 113 XSync(dpy, False); 114 } 115 116 void 117 initatoms(void) 118 { 119 static const struct { 120 Atom *p; 121 const char *s; 122 } a[] = { 123 { wmatom + WMProtocols, "WM_PROTOCOLS" }, 124 { wmatom + WMDeleteWindow, "WM_DELETE_WINDOW" }, 125 { wmatom + WMTakeFocus, "WM_TAKE_FOCUS" }, 126 { wmatom + WMState, "WM_STATE" }, 127 { netatom + NetSupported, "_NET_SUPPORTED" }, 128 { netatom + NetActiveWindow, "_NET_ACTIVE_WINDOW" }, 129 { netatom + NetWMName, "_NET_WM_NAME" }, 130 { netatom + NetWMWindowType, "_NET_WM_WINDOW_TYPE" }, 131 { netatom + NetWMState, "_NET_WM_STATE" }, 132 { netatom + NetWMWindowTypeDialog,"_NET_WM_WINDOW_TYPE_DIALOG"}, 133 { netatom + NetWMStateFullscreen, "_NET_WM_STATE_FULLSCREEN" }, 134 }; 135 int i; 136 137 for (i = 0; i < LENGTH(a); ++i) 138 *a[i].p = XInternAtom(dpy, a[i].s, False); 139 } 140 141 unsigned long * 142 getpixel(const char *name, unsigned long *pixel) 143 { 144 Colormap cmap = DefaultColormap(dpy, screen); 145 XColor color[2]; 146 147 if (!XAllocNamedColor(dpy, cmap, name, color, color + 1)) { 148 warn("cannot allocate color `%s'", name); 149 return NULL; 150 } 151 *pixel = color->pixel; 152 return pixel; 153 } 154 155 unsigned 156 getnumlockmask(void) 157 { 158 XModifierKeymap *modmap = XGetModifierMapping(dpy); 159 KeyCode k; 160 int i, n; 161 162 if (!modmap) { 163 warn("cannot get modifier mapping"); 164 return 0; 165 } 166 k = XKeysymToKeycode(dpy, XK_Num_Lock); 167 n = modmap->max_keypermod; 168 for (i = 0; i < 8 * n && modmap->modifiermap[i] != k; ++i) 169 ; 170 XFreeModifiermap(modmap); 171 return i < 8 * n ? 1 << (i / n) : 0; 172 } 173 174 void 175 dumpmonitors(char *var) 176 { 177 char *buf, *p; 178 Monitor *m; 179 int n; 180 181 n = 0; 182 for (m = mons; m; m = m->next) 183 n += 6; 184 if (!n) 185 return; 186 if (!(buf = malloc(n))) { 187 warn("cannot dump state of clients:"); 188 return; 189 } 190 p = buf; 191 for (m = mons; m; m = m->next) { 192 sprintf(p, "%d %c%c", m->selws, m->showbar ? 'B' : 'b', 193 m->nogaps ? 'g' : 'G'); 194 p += strlen(p); 195 *p++ = ':'; 196 } 197 *--p = '\0'; 198 if (setenv(var, buf, 1) < 0) 199 warn("setenv `%s':", var); 200 free(buf); 201 } 202 203 void 204 loadmonitors(char *val) 205 { 206 Monitor *m = mons; 207 int i, l; 208 209 for (val = strtok(val, ":"); val && m; val = strtok(NULL, ":")) { 210 if (sscanf(val, "%i %n", &i, &l) != 1) 211 continue; 212 if ((unsigned)i < LENGTH(tags)) { 213 wssync(m); 214 m->selws = i; 215 wsload(m); 216 } 217 for (; val[l]; ++l) { 218 switch (val[l]) { 219 case 'b': 220 case 'B': 221 m->showbar = val[l] == 'B'; 222 XMoveWindow(dpy, m->barwin, m->x, BY(m)); 223 break; 224 case 'g': 225 case 'G': 226 m->nogaps = val[l] == 'g'; 227 break; 228 } 229 } 230 m = m->next; 231 } 232 } 233 234 void 235 dumpclients(char *var, Monitor *m) 236 { 237 char *buf, *p; 238 Client *c; 239 size_t n; 240 241 n = 0; 242 for (c = m->clients; c; c = c->next) 243 n += 18 + (LENGTH(tags) + 3) / 4; 244 if (!n) 245 return; 246 if (!(buf = malloc(n))) { 247 warn("cannot dump state of clients:"); 248 return; 249 } 250 p = buf; 251 for (c = m->clients; c; c = c->next) { 252 sprintf(p, "0x%x 0x%x ", (unsigned)c->win, c->tags); 253 p += strlen(p); 254 if (c->isfloating) 255 *p++ = 'f'; 256 if (c->isfullscreen) 257 *p++ = 'F'; 258 if (c->isscratch) 259 *p++ = 's'; 260 if (p[-1] == ' ') 261 --p; 262 *p++ = ':'; 263 } 264 *--p = '\0'; 265 if (setenv(var, buf, 1) < 0) 266 warn("setenv `%s':", var); 267 free(buf); 268 } 269 270 void 271 loadclients(int n, char *val) 272 { 273 Monitor *m; 274 Client *c, **tc; 275 int win, t, l; 276 277 for (m = mons; m && m->num != n; m = m->next) 278 ; 279 if (!m) 280 m = mons; 281 for (val = strtok(val, ":"); val; val = strtok(NULL, ":")) { 282 if (sscanf(val, "%i %i %n", &win, &t, &l) != 2 283 || !(c = wintoclient(win))) 284 continue; 285 if (c->mon != m) 286 sendtomon(c, m); 287 detach(c); 288 c->next = NULL; 289 for (tc = &m->clients; *tc; tc = &(*tc)->next) 290 ; 291 *tc = c; 292 tc = &c->next; 293 for (; val[l]; ++l) { 294 switch (val[l]) { 295 case 'f': 296 setfloating(c, 1); 297 break; 298 case 'F': 299 setfullscreen(c, 1); 300 break; 301 case 's': 302 setscratch(c, 1); 303 break; 304 } 305 } 306 if ((t &= TAGMASK) || c->isscratch) 307 c->tags = t; 308 } 309 } 310 311 void 312 dumpworkspaces(char *var, Monitor *m) 313 { 314 char *buf, *p; 315 int i; 316 317 buf = malloc(LENGTH(tags) 318 * ((LENGTH(tags) + 3) / 4 + 1 + sizeof(m->lytdesc))); 319 if (!buf) { 320 warn("cannot dump state of workspaces:", var); 321 return; 322 } 323 wssync(m); 324 p = buf; 325 i = m->selws; 326 for (m->selws = 0; m->selws < LENGTH(tags); ++m->selws) { 327 wsload(m); 328 sprintf(p, "0x%x %s", m->tagset, m->lytdesc); 329 p += strlen(p); 330 *p++ = ';'; 331 } 332 m->selws = i; 333 wsload(m); 334 *--p = '\0'; 335 if (setenv(var, buf, 1) < 0) 336 warn("setenv `%s':", var); 337 free(buf); 338 } 339 340 void 341 loadworkspaces(int n, char *s) 342 { 343 Monitor *m; 344 LytNode *lyt; 345 int i, t, l; 346 347 for (m = mons; m && m->num != n; m = m->next) 348 ; 349 if (!m) 350 return; 351 wssync(m); 352 i = m->selws; 353 for (s = strtok(s, ";"), m->selws = 0; 354 s && m->selws < LENGTH(tags); 355 s = strtok(NULL, ";"), ++m->selws) { 356 if (sscanf(s, "%i %n", &t, &l) != 1) 357 continue; 358 wsload(m); 359 m->tagset = t & TAGMASK; 360 if ((lyt = lytparse(s + l))) { 361 lytfree(m->lyt); 362 m->lyt = lyt; 363 } 364 wssync(m); 365 } 366 m->selws = i; 367 wsload(m); 368 } 369 370 void 371 dumpstate(void) 372 { 373 char s[32]; 374 Monitor *m; 375 376 strcpy(s, "SWM_MONITORS"); 377 dumpmonitors(s); 378 for (m = mons; m; m = m->next) { 379 sprintf(s, "SWM_MON%d_CLIENTS", m->num); 380 dumpclients(s, m); 381 sprintf(s, "SWM_MON%d_WORKSPACES", m->num); 382 dumpworkspaces(s, m); 383 } 384 if (selmon->sel) { 385 sprintf(s, "0x%x", (unsigned)selmon->sel->win); 386 if (setenv("SWM_FOCUS", s, 1)) 387 warn("setenv `%s':", s); 388 } 389 } 390 391 void 392 loadstate(void) 393 { 394 int i, n, l; 395 char *entry, *val; 396 Client *c; 397 398 if (!environ) 399 return; 400 for (i = 0; environ[i]; ++i) { 401 if (strncmp(environ[i], "SWM_", 4) 402 || !strncmp(environ[i], "SWM_FOCUS=", 10)) 403 continue; 404 if (!(entry = strdup(environ[i]))) { 405 warn("cannot load state from `%s':", environ[i]); 406 continue; 407 } 408 if (!(val = strchr(entry, '='))) { 409 free(entry); 410 continue; 411 } 412 *val++ = '\0'; 413 l = 0; 414 if (!strcmp(entry, "SWM_MONITORS")) 415 loadmonitors(val); 416 else if (sscanf(entry, "SWM_MON%d_CLIENTS%n", &n, &l) == 1 417 && !entry[l]) 418 loadclients(n, val); 419 else if (sscanf(entry, "SWM_MON%d_WORKSPACES%n", &n, &l) == 1 420 && !entry[l]) 421 loadworkspaces(n, val); 422 unsetenv(entry); 423 free(entry); 424 --i; 425 } 426 val = getenv("SWM_FOCUS"); 427 if (val && sscanf(val, "%i", &i) == 1 && (c = wintoclient(i))) 428 focus(c); 429 else 430 focus(selmon->stack); 431 unsetenv("SWM_FOCUS"); 432 } 433 434 void 435 setup(void) 436 { 437 XSetWindowAttributes wa; 438 struct sigaction sa; 439 Monitor *m; 440 441 sigemptyset(&sa.sa_mask); 442 sa.sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART; 443 sa.sa_handler = SIG_IGN; 444 sigaction(SIGCHLD, &sa, NULL); 445 while (waitpid(-1, NULL, WNOHANG) > 0) 446 ; 447 screen = DefaultScreen(dpy); 448 sw = DisplayWidth(dpy, screen); 449 sh = DisplayHeight(dpy, screen); 450 root = RootWindow(dpy, screen); 451 initatoms(); 452 if (updategeom() < 0) 453 die("cannot set up monitor objects"); 454 curnormal = XCreateFontCursor(dpy, XC_left_ptr); 455 curresize = XCreateFontCursor(dpy, XC_sizing); 456 curmove = XCreateFontCursor(dpy, XC_fleur); 457 if (!getpixel(colfocus, &pixelfocus) 458 || !getpixel(colunfocus, &pixelunfocus) 459 || !getpixel(colurgent, &pixelurgent)) 460 die("cannot initialize border pixels"); 461 barsetup(); 462 for (m = mons; m; m = m->next) 463 initbar(m); 464 XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, 465 PropModeReplace, (unsigned char *)netatom, NetLast); 466 wa.cursor = curnormal; 467 wa.event_mask = SubstructureRedirectMask 468 |StructureNotifyMask|SubstructureNotifyMask 469 |PointerMotionMask|ButtonPressMask 470 |EnterWindowMask|LeaveWindowMask; 471 XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); 472 XSelectInput(dpy, root, wa.event_mask); 473 lockmasks[1] = LockMask; 474 lockmasks[2] = getnumlockmask(); 475 lockmasks[3] = lockmasks[1]|lockmasks[2]; 476 grabkeys(); 477 focus(selmon->stack); 478 } 479 480 void 481 scan(void) 482 { 483 Window t, *wins = NULL; 484 XWindowAttributes wa; 485 unsigned n, i, j; 486 487 if (!XQueryTree(dpy, root, &t, &t, &wins, &n) || !wins) 488 return; 489 for (i = j = 0; i < n; ++i) { 490 if (!XGetWindowAttributes(dpy, wins[i], &wa) 491 || wa.override_redirect 492 || (wa.map_state != IsViewable 493 && getstate(wins[i]) != IconicState)) 494 continue; 495 if (XGetTransientForHint(dpy, wins[i], &t)) 496 wins[j++] = wins[i]; 497 else 498 manage(wins[i], &wa); 499 } 500 while (j--) { 501 if (!XGetWindowAttributes(dpy, wins[j], &wa)) 502 continue; 503 manage(wins[j], &wa); 504 } 505 XFree(wins); 506 loadstate(); 507 } 508 509 void 510 run(void) 511 { 512 struct pollfd fds[2]; 513 XEvent ev; 514 Monitor *m; 515 516 runsprog(); 517 fds[0].fd = ConnectionNumber(dpy); 518 fds[0].events = fds[1].events = POLLIN; 519 for (;;) { 520 while (XPending(dpy)) { 521 XNextEvent(dpy, &ev); 522 if (!handler[ev.type]) 523 continue; 524 handler[ev.type](&ev); 525 if (!running) 526 return; 527 } 528 do { 529 if (pending) { 530 for (m = mons; m; m = m->next) { 531 if (m->pending & PArrange) 532 arrange(m); 533 if (m->pending & PDrawbar) 534 drawbar(m); 535 m->pending = 0; 536 } 537 XSync(dpy, False); 538 pending = 0; 539 } 540 fds[1].fd = sfdr; 541 if (poll(fds, 2, -1) < 0) { 542 if (errno == EINTR) 543 continue; 544 die("poll:"); 545 } 546 if (fds[1].revents) 547 updatestatus(); 548 } while (!fds[0].revents); 549 if (fds[0].revents != POLLIN) 550 die("X connection broken"); 551 } 552 } 553 554 void 555 cleanup(void) 556 { 557 Monitor *m; 558 559 if (restart) 560 dumpstate(); 561 for (m = mons; m; m = m->next) 562 while (m->stack) 563 unmanage(m->stack, 0); 564 XUngrabKey(dpy, AnyKey, AnyModifier, root); 565 endsprog(); 566 barcleanup(); 567 XFreeCursor(dpy, curnormal); 568 XFreeCursor(dpy, curresize); 569 XFreeCursor(dpy, curmove); 570 cleanupgeom(); 571 XSync(dpy, False); 572 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); 573 XDeleteProperty(dpy, root, netatom[NetActiveWindow]); 574 } 575 576 int 577 main(int argc, char *argv[]) 578 { 579 if (argc > 1) 580 die("excessive arguments"); 581 progname = argv[0]; 582 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 583 die("no locale support"); 584 if (!(dpy = XOpenDisplay(NULL))) 585 die("cannot open display"); 586 checkotherwm(); 587 setup(); 588 #ifdef __OpenBSD__ 589 if (pledge("stdio rpath proc exec", NULL) == -1) 590 die("pledge:"); 591 #endif 592 scan(); 593 run(); 594 cleanup(); 595 XCloseDisplay(dpy); 596 if (restart) { 597 execvp(argv[0], argv); 598 die("execvp '%s':", argv[0]); 599 } 600 return 0; 601 }