shod.c (11270B)
1 #include <sys/wait.h> 2 3 #include <err.h> 4 #include <limits.h> 5 #include <locale.h> 6 #include <signal.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <unistd.h> 11 12 #include <X11/extensions/Xrandr.h> 13 14 #include "shod.h" 15 16 #define WMNAME "shod" 17 #define ROOT_EVENTS (SubstructureRedirectMask | SubstructureNotifyMask \ 18 | StructureNotifyMask | ButtonPressMask \ 19 | PropertyChangeMask) 20 21 static int (*xerrorxlib)(Display *, XErrorEvent *); 22 23 /* shared variables */ 24 unsigned long clientmask = CWEventMask | CWColormap | CWBackPixel | CWBorderPixel; 25 XSetWindowAttributes clientswa = { 26 .event_mask = SubstructureNotifyMask | ButtonReleaseMask 27 | SubstructureRedirectMask | ButtonPressMask | FocusChangeMask 28 | Button1MotionMask 29 }; 30 struct WM wm = { .running = 1 }; 31 struct Dock dock; 32 33 /* show usage and exit */ 34 static void 35 usage(void) 36 { 37 (void)fprintf(stderr, "usage: shod [-AcdhSstW] [file]\n"); 38 exit(1); 39 } 40 41 /* call fork checking for error; exit on error */ 42 static pid_t 43 efork(void) 44 { 45 pid_t pid; 46 47 if ((pid = fork()) < 0) 48 err(1, "fork"); 49 return pid; 50 } 51 52 /* call execlp checking for error; exit on error */ 53 static void 54 execshell(char *filename) 55 { 56 char *argv[3]; 57 58 if ((argv[0] = getenv(SHELL)) == NULL) 59 argv[0] = DEF_SHELL; 60 if (filename[0] == '-' && filename[1] == '\0') 61 argv[1] = NULL; /* read commands from stdin */ 62 else 63 argv[1] = filename; /* read commands from file */ 64 argv[2] = NULL; 65 if (execvp(argv[0], argv) == -1) { 66 err(1, "%s", argv[0]); 67 } 68 } 69 70 /* error handler */ 71 static int 72 xerror(Display *dpy, XErrorEvent *e) 73 { 74 /* stolen from berry, which stole from katriawm, which stole from dwm lol */ 75 76 /* There's no way to check accesses to destroyed windows, thus those 77 * cases are ignored (especially on UnmapNotify's). Other types of 78 * errors call Xlibs default error handler, which may call exit. */ 79 if (e->error_code == BadWindow || 80 (e->request_code == X_SetInputFocus && e->error_code == BadMatch) || 81 (e->request_code == X_PolyText8 && e->error_code == BadDrawable) || 82 (e->request_code == X_PolyFillRectangle && e->error_code == BadDrawable) || 83 (e->request_code == X_PolySegment && e->error_code == BadDrawable) || 84 (e->request_code == X_ConfigureWindow && e->error_code == BadMatch) || 85 (e->request_code == X_ConfigureWindow && e->error_code == BadValue) || 86 (e->request_code == X_GrabButton && e->error_code == BadAccess) || 87 (e->request_code == X_GrabKey && e->error_code == BadAccess) || 88 (e->request_code == X_CopyArea && e->error_code == BadDrawable) || 89 (e->request_code == 139 && e->error_code == BadDrawable) || 90 (e->request_code == 139 && e->error_code == 143)) 91 return 0; 92 93 fprintf(stderr, "shod: "); 94 return xerrorxlib(dpy, e); 95 exit(1); /* unreached */ 96 } 97 98 /* startup error handler to check if another window manager is already running */ 99 static int 100 xerrorstart(Display *dpy, XErrorEvent *e) 101 { 102 (void)dpy; 103 (void)e; 104 errx(1, "another window manager is already running"); 105 } 106 107 /* read command-line options */ 108 static char * 109 getoptions(int argc, char *argv[]) 110 { 111 int c; 112 113 while ((c = getopt(argc, argv, "AcdhSstW")) != -1) { 114 switch (c) { 115 case 'A' : 116 config.altkeysym = XK_Alt_L; 117 config.modifier = Mod1Mask; 118 break; 119 case 'c': 120 config.honorconfig = 1; 121 break; 122 case 'd': 123 config.floatdialog = 1; 124 break; 125 case 'h': 126 config.disablehidden = 1; 127 break; 128 case 'S': 129 config.sloppytiles = 1; 130 break; 131 case 's': 132 config.sloppyfocus = 1; 133 break; 134 case 't': 135 config.disablealttab = 1; 136 break; 137 case 'W' : 138 config.altkeysym = XK_Super_L; 139 config.modifier = Mod4Mask; 140 break; 141 default: 142 usage(); 143 break; 144 } 145 } 146 argc -= optind; 147 argv += optind; 148 if (argc > 1) 149 usage(); 150 return *argv; 151 } 152 153 /* initialize signals */ 154 static void 155 initsignal(void) 156 { 157 struct sigaction sa; 158 159 /* remove zombies, we may inherit children when exec'ing shod in .xinitrc */ 160 sa.sa_handler = SIG_IGN; 161 sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; 162 sigemptyset(&sa.sa_mask); 163 if (sigaction(SIGCHLD, &sa, NULL) == -1) 164 err(1, "sigaction"); 165 while (waitpid(-1, NULL, WNOHANG) > 0) 166 ; 167 } 168 169 /* initialize cursors */ 170 static void 171 initcursors(void) 172 { 173 wm.cursors[CURSOR_NORMAL] = XCreateFontCursor(dpy, XC_left_ptr); 174 wm.cursors[CURSOR_MOVE] = XCreateFontCursor(dpy, XC_fleur); 175 wm.cursors[CURSOR_NW] = XCreateFontCursor(dpy, XC_top_left_corner); 176 wm.cursors[CURSOR_NE] = XCreateFontCursor(dpy, XC_top_right_corner); 177 wm.cursors[CURSOR_SW] = XCreateFontCursor(dpy, XC_bottom_left_corner); 178 wm.cursors[CURSOR_SE] = XCreateFontCursor(dpy, XC_bottom_right_corner); 179 wm.cursors[CURSOR_N] = XCreateFontCursor(dpy, XC_top_side); 180 wm.cursors[CURSOR_S] = XCreateFontCursor(dpy, XC_bottom_side); 181 wm.cursors[CURSOR_W] = XCreateFontCursor(dpy, XC_left_side); 182 wm.cursors[CURSOR_E] = XCreateFontCursor(dpy, XC_right_side); 183 wm.cursors[CURSOR_V] = XCreateFontCursor(dpy, XC_sb_v_double_arrow); 184 wm.cursors[CURSOR_H] = XCreateFontCursor(dpy, XC_sb_h_double_arrow); 185 wm.cursors[CURSOR_HAND] = XCreateFontCursor(dpy, XC_hand2); 186 wm.cursors[CURSOR_PIRATE] = XCreateFontCursor(dpy, XC_pirate); 187 } 188 189 static void 190 initxrm(void) 191 { 192 static struct { 193 const char *class, *name; 194 } resourceids[NRESOURCES] = { 195 #define X(res, s1, s2) [res] = { .class = s1, .name = s2, }, 196 RESOURCES 197 #undef X 198 }; 199 long n; 200 int i; 201 char *value; 202 203 XrmInitialize(); 204 wm.application.class = XrmPermStringToQuark("Shod"); 205 wm.application.name = XrmPermStringToQuark("shod"); 206 wm.anyresource = XrmPermStringToQuark("?"); 207 for (i = 0; i < NRESOURCES; i++) { 208 wm.resources[i].class = XrmPermStringToQuark(resourceids[i].class); 209 wm.resources[i].name = XrmPermStringToQuark(resourceids[i].name); 210 } 211 if (!settheme()) 212 exit(EXIT_FAILURE); 213 setresources(XResourceManagerString(dpy)); 214 if (xdb != NULL) { 215 value = getresource( 216 xdb, 217 (XrmClass[]){ 218 wm.application.class, 219 wm.resources[RES_NDESKTOPS].class, 220 NULLQUARK, 221 }, 222 (XrmName[]){ 223 wm.application.name, 224 wm.resources[RES_NDESKTOPS].name, 225 NULLQUARK, 226 } 227 ); 228 if (value != NULL && (n = strtol(value, NULL, 10)) > 0 && n < 100) { 229 config.ndesktops = n; 230 } 231 } 232 } 233 234 /* set up root window */ 235 static void 236 initroot(void) 237 { 238 /* change default cursor */ 239 XDefineCursor(dpy, root, wm.cursors[CURSOR_NORMAL]); 240 241 /* Set focus to root window */ 242 XSetInputFocus(dpy, root, RevertToParent, CurrentTime); 243 } 244 245 /* create dock window */ 246 static void 247 initdock(void) 248 { 249 XSetWindowAttributes swa; 250 251 TAILQ_INIT(&dock.dappq); 252 dock.pix = None; 253 swa.event_mask = SubstructureNotifyMask | SubstructureRedirectMask; 254 swa.background_pixel = BlackPixel(dpy, screen); 255 swa.border_pixel = BlackPixel(dpy, screen); 256 swa.colormap = colormap; 257 dock.win = XCreateWindow(dpy, root, 0, 0, 1, 1, 0, 258 depth, InputOutput, visual, clientmask, &swa); 259 } 260 261 /* create dummy windows used for controlling focus and the layer of clients */ 262 static void 263 initdummywindows(void) 264 { 265 int i; 266 XSetWindowAttributes swa; 267 268 for (i = 0; i < LAYER_LAST; i++) { 269 wm.layers[i].ncols = 0; 270 wm.layers[i].frame = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); 271 XRaiseWindow(dpy, wm.layers[i].frame); 272 TAILQ_INSERT_HEAD(&wm.stackq, &wm.layers[i], raiseentry); 273 } 274 swa = clientswa; 275 swa.event_mask |= KeyPressMask; 276 wm.checkwin = wm.focuswin = wm.dragwin = wm.restackwin = XCreateWindow( 277 dpy, root, 278 - (2 * config.borderwidth + config.titlewidth), 279 - (2 * config.borderwidth + config.titlewidth), 280 2 * config.borderwidth + config.titlewidth, 281 2 * config.borderwidth + config.titlewidth, 282 0, depth, CopyFromParent, visual, 283 clientmask, 284 &swa 285 ); 286 } 287 288 /* map and hide dummy windows */ 289 static void 290 mapdummywins(void) 291 { 292 XMapWindow(dpy, wm.focuswin); 293 XSetInputFocus(dpy, wm.focuswin, RevertToParent, CurrentTime); 294 } 295 296 /* run stdin on sh */ 297 static void 298 autostart(char *filename) 299 { 300 pid_t pid; 301 302 if (filename == NULL) 303 return; 304 if ((pid = efork()) == 0) { 305 if (efork() == 0) 306 execshell(filename); 307 exit(0); 308 } 309 waitpid(pid, NULL, 0); 310 } 311 312 static void 313 checkotherwm(void) 314 { 315 xerrorxlib = XSetErrorHandler(xerrorstart); 316 XSelectInput(dpy, root, ROOT_EVENTS); 317 XSync(dpy, False); 318 (void)XSetErrorHandler(xerror); 319 XSync(dpy, False); 320 } 321 322 /* destroy dummy windows */ 323 static void 324 cleandummywindows(void) 325 { 326 int i; 327 328 XDestroyWindow(dpy, wm.checkwin); 329 for (i = 0; i < LAYER_LAST; i++) { 330 XDestroyWindow(dpy, wm.layers[i].frame); 331 } 332 } 333 334 /* free cursors */ 335 static void 336 cleancursors(void) 337 { 338 size_t i; 339 340 for (i = 0; i < CURSOR_LAST; i++) { 341 XFreeCursor(dpy, wm.cursors[i]); 342 } 343 } 344 345 /* clean window manager structures */ 346 static void 347 cleanwm(void) 348 { 349 struct Monitor *mon; 350 struct Object *obj; 351 struct Container *c; 352 353 while ((c = TAILQ_FIRST(&wm.focusq)) != NULL) 354 containerdel(c); 355 while ((obj = TAILQ_FIRST(&wm.notifq)) != NULL) 356 (void)unmanagenotif(obj, 0); 357 while ((obj = TAILQ_FIRST(&wm.barq)) != NULL) 358 (void)unmanagebar(obj, 0); 359 while ((obj = TAILQ_FIRST(&wm.splashq)) != NULL) 360 (void)unmanagesplash(obj, 0); 361 while ((obj = TAILQ_FIRST(&dock.dappq)) != NULL) 362 (void)unmanagedockapp(obj, 0); 363 while ((mon = TAILQ_FIRST(&wm.monq)) != NULL) 364 mondel(mon); 365 if (dock.pix != None) 366 XFreePixmap(dpy, dock.pix); 367 XDestroyWindow(dpy, dock.win); 368 } 369 370 /* shod window manager */ 371 int 372 main(int argc, char *argv[]) 373 { 374 XEvent ev; 375 char *filename, *wmname; 376 377 if (!setlocale(LC_ALL, "") || !XSupportsLocale()) 378 warnx("warning: no locale support"); 379 xinit(); 380 checkotherwm(); 381 moninit(); 382 xinitvisual(); 383 clientswa.colormap = colormap; 384 clientswa.border_pixel = BlackPixel(dpy, screen); 385 clientswa.background_pixel = BlackPixel(dpy, screen); 386 387 /* get configuration */ 388 if ((wmname = strrchr(argv[0], '/')) != NULL) 389 wmname++; 390 else if (argv[0] != NULL && argv[0][0] != '\0') 391 wmname = argv[0]; 392 else 393 wmname = WMNAME; 394 filename = getoptions(argc, argv); 395 396 /* check sloppy focus */ 397 if (config.sloppyfocus || config.sloppytiles) 398 clientswa.event_mask |= EnterWindowMask; 399 400 /* initialize queues */ 401 TAILQ_INIT(&wm.monq); 402 TAILQ_INIT(&wm.barq); 403 TAILQ_INIT(&wm.splashq); 404 TAILQ_INIT(&wm.notifq); 405 TAILQ_INIT(&wm.focusq); 406 TAILQ_INIT(&wm.stackq); 407 408 /* initialize */ 409 initsignal(); 410 initcursors(); 411 initatoms(); 412 initroot(); 413 initdummywindows(); 414 initdock(); 415 initxrm(); 416 417 /* set up list of monitors */ 418 monupdate(); 419 wm.selmon = TAILQ_FIRST(&wm.monq); 420 421 /* initialize ewmh hints */ 422 ewmhinit(wmname); 423 ewmhsetcurrentdesktop(0); 424 ewmhsetshowingdesktop(0); 425 ewmhsetclients(); 426 ewmhsetactivewindow(None); 427 428 /* run sh script */ 429 autostart(filename); 430 431 /* scan windows */ 432 scan(); 433 mapdummywins(); 434 435 /* set modifier key and grab alt key */ 436 setmod(); 437 438 /* run main event loop */ 439 while (!XNextEvent(dpy, &ev)) { 440 wm.setclientlist = 0; 441 if (wm.xrandr && ev.type - wm.xrandrev == RRScreenChangeNotify) 442 monevent(&ev); 443 else if (ev.type < LASTEvent && xevents[ev.type] != NULL) 444 (*xevents[ev.type])(&ev); 445 if (!wm.running) 446 break; 447 if (wm.setclientlist) { 448 ewmhsetclients(); 449 } 450 } 451 452 /* clean up */ 453 cleandummywindows(); 454 cleancursors(); 455 cleanwm(); 456 cleantheme(); 457 458 /* clear ewmh hints */ 459 ewmhsetclients(); 460 ewmhsetactivewindow(None); 461 462 /* close connection to server */ 463 XUngrabPointer(dpy, CurrentTime); 464 XUngrabKeyboard(dpy, CurrentTime); 465 XCloseDisplay(dpy); 466 467 return 0; 468 }