tabbed.c (34312B)
1 /* 2 * See LICENSE file for copyright and license details. 3 */ 4 5 #include <sys/wait.h> 6 #include <locale.h> 7 #include <signal.h> 8 #include <stdarg.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <unistd.h> 13 #include <X11/Xatom.h> 14 #include <X11/Xlib.h> 15 #include <X11/Xproto.h> 16 #include <X11/Xutil.h> 17 #include <X11/XKBlib.h> 18 #include <X11/Xft/Xft.h> 19 #include <X11/Xresource.h> 20 21 #include "arg.h" 22 23 /* XEMBED messages */ 24 #define XEMBED_EMBEDDED_NOTIFY 0 25 #define XEMBED_WINDOW_ACTIVATE 1 26 #define XEMBED_WINDOW_DEACTIVATE 2 27 #define XEMBED_REQUEST_FOCUS 3 28 #define XEMBED_FOCUS_IN 4 29 #define XEMBED_FOCUS_OUT 5 30 #define XEMBED_FOCUS_NEXT 6 31 #define XEMBED_FOCUS_PREV 7 32 /* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */ 33 #define XEMBED_MODALITY_ON 10 34 #define XEMBED_MODALITY_OFF 11 35 #define XEMBED_REGISTER_ACCELERATOR 12 36 #define XEMBED_UNREGISTER_ACCELERATOR 13 37 #define XEMBED_ACTIVATE_ACCELERATOR 14 38 39 /* Details for XEMBED_FOCUS_IN: */ 40 #define XEMBED_FOCUS_CURRENT 0 41 #define XEMBED_FOCUS_FIRST 1 42 #define XEMBED_FOCUS_LAST 2 43 44 /* Macros */ 45 #define MAX(a, b) ((a) > (b) ? (a) : (b)) 46 #define MIN(a, b) ((a) < (b) ? (a) : (b)) 47 #define LENGTH(x) (sizeof((x)) / sizeof(*(x))) 48 #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) 49 #define TEXTW(x) (textnw(x, strlen(x)) + dc.font.height) 50 51 #define XRESOURCE_LOAD_META(NAME) \ 52 if(!XrmGetResource(xrdb, "tabbed." NAME, "tabbed." NAME, &type, &ret)) \ 53 XrmGetResource(xrdb, "*." NAME, "*." NAME, &type, &ret); \ 54 if (ret.addr != NULL && !strncmp("String", type, 64)) 55 56 #define XRESOURCE_LOAD_STRING(NAME, DST) \ 57 XRESOURCE_LOAD_META(NAME) \ 58 DST = ret.addr; 59 60 enum { ColFG, ColBG, ColLast }; /* color */ 61 enum { WMProtocols, WMDelete, WMName, WMState, WMFullscreen, 62 XEmbed, WMSelectTab, WMLast }; /* default atoms */ 63 64 typedef union { 65 int i; 66 const void *v; 67 } Arg; 68 69 typedef struct { 70 unsigned int mod; 71 KeySym keysym; 72 void (*func)(const Arg *); 73 const Arg arg; 74 } Key; 75 76 typedef struct { 77 int x, y, w, h; 78 XftColor norm[ColLast]; 79 XftColor sel[ColLast]; 80 XftColor urg[ColLast]; 81 Drawable drawable; 82 GC gc; 83 struct { 84 int ascent; 85 int descent; 86 int height; 87 XftFont *xfont; 88 } font; 89 } DC; /* draw context */ 90 91 typedef struct { 92 char name[256]; 93 Window win; 94 int tabx; 95 Bool urgent; 96 Bool closed; 97 } Client; 98 99 /* function declarations */ 100 static void buttonpress(const XEvent *e); 101 static void cleanup(void); 102 static void clientmessage(const XEvent *e); 103 static void configurenotify(const XEvent *e); 104 static void configurerequest(const XEvent *e); 105 static void createnotify(const XEvent *e); 106 static void destroynotify(const XEvent *e); 107 static void die(const char *errstr, ...); 108 static void drawbar(void); 109 static void drawtext(const char *text, XftColor col[ColLast]); 110 static void *ecalloc(size_t n, size_t size); 111 static void *erealloc(void *o, size_t size); 112 static void expose(const XEvent *e); 113 static void focus(int c); 114 static void focusin(const XEvent *e); 115 static void focusonce(const Arg *arg); 116 static void focusurgent(const Arg *arg); 117 static void fullscreen(const Arg *arg); 118 static char *getatom(int a); 119 static int getclient(Window w); 120 static XftColor getcolor(const char *colstr); 121 static int getfirsttab(void); 122 static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size); 123 static void initfont(const char *fontstr); 124 static Bool isprotodel(int c); 125 static void keypress(const XEvent *e); 126 static void keyrelease(const XEvent *e); 127 static void killclient(const Arg *arg); 128 static void manage(Window win); 129 static void maprequest(const XEvent *e); 130 static void move(const Arg *arg); 131 static void movetab(const Arg *arg); 132 static void propertynotify(const XEvent *e); 133 static void resize(int c, int w, int h); 134 static void rotate(const Arg *arg); 135 static void run(void); 136 static void sendxembed(int c, long msg, long detail, long d1, long d2); 137 static void setcmd(int argc, char *argv[], int); 138 static void setup(void); 139 static void sigchld(int unused); 140 static void showbar(const Arg *arg); 141 static void spawn(const Arg *arg); 142 static int textnw(const char *text, unsigned int len); 143 static void toggle(const Arg *arg); 144 static void unmanage(int c); 145 static void unmapnotify(const XEvent *e); 146 static void updatenumlockmask(void); 147 static void updatetitle(int c); 148 static int xerror(Display *dpy, XErrorEvent *ee); 149 static void xsettitle(Window w, const char *str); 150 static void xrdb_load(void); 151 static void reload(int sig); 152 static void writecolors(void); 153 154 /* variables */ 155 static int screen; 156 static void (*handler[LASTEvent]) (const XEvent *) = { 157 [ButtonPress] = buttonpress, 158 [ClientMessage] = clientmessage, 159 [ConfigureNotify] = configurenotify, 160 [ConfigureRequest] = configurerequest, 161 [CreateNotify] = createnotify, 162 [UnmapNotify] = unmapnotify, 163 [DestroyNotify] = destroynotify, 164 [Expose] = expose, 165 [FocusIn] = focusin, 166 [KeyPress] = keypress, 167 [KeyRelease] = keyrelease, 168 [MapRequest] = maprequest, 169 [PropertyNotify] = propertynotify, 170 }; 171 static int bh, obh, wx, wy, ww, wh, vbh; 172 static unsigned int numlockmask; 173 static Bool running = True, nextfocus, doinitspawn = True, 174 fillagain = False, closelastclient = False, 175 killclientsfirst = False; 176 static Display *dpy; 177 static DC dc; 178 static Atom wmatom[WMLast]; 179 static Window root, win; 180 static Client **clients; 181 static int nclients, sel = -1, lastsel = -1; 182 static int (*xerrorxlib)(Display *, XErrorEvent *); 183 static int cmd_append_pos; 184 static char winid[64]; 185 static char **cmd; 186 static char *wmname = "tabbed"; 187 static const char *geometry; 188 #if HIDETABS 189 static Bool barvisibility = False; 190 #else 191 static Bool barvisibility = True; 192 #endif 193 194 static Colormap cmap; 195 static Visual *visual = NULL; 196 197 char *argv0; 198 199 static int colors_changed = 0; 200 201 /* configuration, allows nested code to access above variables */ 202 #include "config.h" 203 204 void 205 buttonpress(const XEvent *e) 206 { 207 const XButtonPressedEvent *ev = &e->xbutton; 208 int i, fc; 209 Arg arg; 210 211 if (ev->y < 0 || ev->y > bh) 212 return; 213 214 if (((fc = getfirsttab()) > 0 && ev->x < TEXTW(before)) || ev->x < 0) 215 return; 216 217 for (i = fc; i < nclients; i++) { 218 if (clients[i]->tabx > ev->x) { 219 switch (ev->button) { 220 case Button1: 221 focus(i); 222 break; 223 case Button2: 224 focus(i); 225 killclient(NULL); 226 break; 227 case Button4: /* FALLTHROUGH */ 228 case Button5: 229 arg.i = ev->button == Button4 ? -1 : 1; 230 rotate(&arg); 231 break; 232 } 233 break; 234 } 235 } 236 } 237 238 void 239 cleanup(void) 240 { 241 int i; 242 243 for (i = 0; i < nclients; i++) { 244 focus(i); 245 killclient(NULL); 246 XReparentWindow(dpy, clients[i]->win, root, 0, 0); 247 unmanage(i); 248 } 249 free(clients); 250 clients = NULL; 251 252 XFreePixmap(dpy, dc.drawable); 253 XFreeGC(dpy, dc.gc); 254 XDestroyWindow(dpy, win); 255 XSync(dpy, False); 256 free(cmd); 257 } 258 259 void 260 clientmessage(const XEvent *e) 261 { 262 const XClientMessageEvent *ev = &e->xclient; 263 264 if (ev->message_type == wmatom[WMProtocols] && 265 ev->data.l[0] == wmatom[WMDelete]) { 266 if (nclients > 1 && killclientsfirst) { 267 killclient(0); 268 return; 269 } 270 running = False; 271 } 272 } 273 274 void 275 configurenotify(const XEvent *e) 276 { 277 const XConfigureEvent *ev = &e->xconfigure; 278 279 if (ev->window == win && (ev->width != ww || ev->height != wh)) { 280 ww = ev->width; 281 wh = ev->height; 282 XFreePixmap(dpy, dc.drawable); 283 dc.drawable = XCreatePixmap(dpy, win, ww, wh, 32); 284 285 if (!obh && (wh <= bh)) { 286 obh = bh; 287 bh = 0; 288 } else if (!bh && (wh > obh)) { 289 bh = obh; 290 obh = 0; 291 } 292 293 if (sel > -1) 294 resize(sel, ww, wh - bh); 295 XSync(dpy, False); 296 } 297 } 298 299 void 300 configurerequest(const XEvent *e) 301 { 302 const XConfigureRequestEvent *ev = &e->xconfigurerequest; 303 XWindowChanges wc; 304 int c; 305 306 if ((c = getclient(ev->window)) > -1) { 307 wc.x = 0; 308 wc.y = bh; 309 wc.width = ww; 310 wc.height = wh - bh; 311 wc.border_width = 0; 312 wc.sibling = ev->above; 313 wc.stack_mode = ev->detail; 314 XConfigureWindow(dpy, clients[c]->win, ev->value_mask, &wc); 315 } 316 } 317 318 void 319 createnotify(const XEvent *e) 320 { 321 const XCreateWindowEvent *ev = &e->xcreatewindow; 322 323 if (ev->window != win && getclient(ev->window) < 0) 324 manage(ev->window); 325 } 326 327 void 328 destroynotify(const XEvent *e) 329 { 330 const XDestroyWindowEvent *ev = &e->xdestroywindow; 331 int c; 332 333 if ((c = getclient(ev->window)) > -1) 334 unmanage(c); 335 } 336 337 void 338 die(const char *errstr, ...) 339 { 340 va_list ap; 341 342 va_start(ap, errstr); 343 vfprintf(stderr, errstr, ap); 344 va_end(ap); 345 exit(EXIT_FAILURE); 346 } 347 348 void 349 drawbar(void) 350 { 351 XftColor *col; 352 int c, cc, fc, width, nbh; 353 char *name = NULL; 354 char tabtitle[256]; 355 356 #if HIDETABS 357 nbh = barvisibility && nclients > 1 ? vbh : 0; 358 #else 359 nbh = nclients > 1 ? vbh : 0; 360 #endif 361 if (nbh != bh) { 362 bh = nbh; 363 for (c = 0; c < nclients; c++) 364 XMoveResizeWindow(dpy, clients[c]->win, 0, bh, ww, wh-bh); 365 } 366 367 if (bh == 0) return; 368 369 if (colors_changed == 1) writecolors(); 370 371 if (nclients == 0) { 372 dc.x = 0; 373 dc.w = ww; 374 XFetchName(dpy, win, &name); 375 drawtext(name ? name : "", dc.norm); 376 XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, 0); 377 XSync(dpy, False); 378 379 return; 380 } 381 382 width = ww; 383 cc = ww / tabwidth; 384 if (nclients > cc) 385 cc = (ww - TEXTW(before) - TEXTW(after)) / tabwidth; 386 387 if ((fc = getfirsttab()) + cc < nclients) { 388 dc.w = TEXTW(after); 389 dc.x = width - dc.w; 390 drawtext(after, dc.sel); 391 width -= dc.w; 392 } 393 dc.x = 0; 394 395 if (fc > 0) { 396 dc.w = TEXTW(before); 397 drawtext(before, dc.sel); 398 dc.x += dc.w; 399 width -= dc.w; 400 } 401 402 cc = MIN(cc, nclients); 403 for (c = fc; c < fc + cc; c++) { 404 dc.w = width / cc; 405 if (c == sel) { 406 col = dc.sel; 407 dc.w += width % cc; 408 } else { 409 col = clients[c]->urgent ? dc.urg : dc.norm; 410 } 411 if (clientNumber) { 412 snprintf(tabtitle, sizeof(tabtitle), "%d: %s", 413 c + 1, clients[c]->name); 414 drawtext(tabtitle, col); 415 } else 416 drawtext(clients[c]->name, col); 417 dc.x += dc.w; 418 clients[c]->tabx = dc.x; 419 } 420 XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, 0); 421 XSync(dpy, False); 422 } 423 424 void 425 drawtext(const char *text, XftColor col[ColLast]) 426 { 427 int i, j, x, y, h, len, olen; 428 char buf[256]; 429 XftDraw *d; 430 XRectangle r = { dc.x, dc.y, dc.w, dc.h }; 431 432 XSetForeground(dpy, dc.gc, col[ColBG].pixel); 433 XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); 434 if (!text) 435 return; 436 437 olen = strlen(text); 438 h = dc.font.ascent + dc.font.descent; 439 y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent; 440 x = dc.x + (h / 2); 441 442 /* shorten text if necessary */ 443 for (len = MIN(olen, sizeof(buf)); 444 len && textnw(text, len) > dc.w - h; len--); 445 446 if (!len) 447 return; 448 449 memcpy(buf, text, len); 450 if (len < olen) { 451 for (i = len, j = strlen(titletrim); j && i; 452 buf[--i] = titletrim[--j]) 453 ; 454 } 455 456 d = XftDrawCreate(dpy, dc.drawable, visual, cmap); 457 XftDrawStringUtf8(d, &col[ColFG], dc.font.xfont, x, y, (XftChar8 *) buf, len); 458 XftDrawDestroy(d); 459 } 460 461 void * 462 ecalloc(size_t n, size_t size) 463 { 464 void *p; 465 466 if (!(p = calloc(n, size))) 467 die("%s: cannot calloc\n", argv0); 468 return p; 469 } 470 471 void * 472 erealloc(void *o, size_t size) 473 { 474 void *p; 475 476 if (!(p = realloc(o, size))) 477 die("%s: cannot realloc\n", argv0); 478 return p; 479 } 480 481 void 482 expose(const XEvent *e) 483 { 484 const XExposeEvent *ev = &e->xexpose; 485 486 if (ev->count == 0 && win == ev->window) 487 drawbar(); 488 } 489 490 void 491 focus(int c) 492 { 493 char buf[BUFSIZ] = "tabbed-"VERSION" ::"; 494 size_t i, n; 495 XWMHints* wmh; 496 497 /* If c, sel and clients are -1, raise tabbed-win itself */ 498 if (nclients == 0) { 499 cmd[cmd_append_pos] = NULL; 500 for(i = 0, n = strlen(buf); cmd[i] && n < sizeof(buf); i++) 501 n += snprintf(&buf[n], sizeof(buf) - n, " %s", cmd[i]); 502 503 xsettitle(win, buf); 504 XRaiseWindow(dpy, win); 505 506 return; 507 } 508 509 if (c < 0 || c >= nclients) 510 return; 511 512 resize(c, ww, wh - bh); 513 XRaiseWindow(dpy, clients[c]->win); 514 XSetInputFocus(dpy, clients[c]->win, RevertToParent, CurrentTime); 515 sendxembed(c, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0); 516 sendxembed(c, XEMBED_WINDOW_ACTIVATE, 0, 0, 0); 517 xsettitle(win, clients[c]->name); 518 519 if (sel != c) { 520 lastsel = sel; 521 sel = c; 522 } 523 524 if (clients[c]->urgent && (wmh = XGetWMHints(dpy, clients[c]->win))) { 525 wmh->flags &= ~XUrgencyHint; 526 XSetWMHints(dpy, clients[c]->win, wmh); 527 clients[c]->urgent = False; 528 XFree(wmh); 529 } 530 531 drawbar(); 532 XSync(dpy, False); 533 } 534 535 void 536 focusin(const XEvent *e) 537 { 538 const XFocusChangeEvent *ev = &e->xfocus; 539 int dummy; 540 Window focused; 541 542 if (ev->mode != NotifyUngrab) { 543 XGetInputFocus(dpy, &focused, &dummy); 544 if (focused == win) 545 focus(sel); 546 } 547 } 548 549 void 550 focusonce(const Arg *arg) 551 { 552 nextfocus = True; 553 } 554 555 void 556 focusurgent(const Arg *arg) 557 { 558 int c; 559 560 if (sel < 0) 561 return; 562 563 for (c = (sel + 1) % nclients; c != sel; c = (c + 1) % nclients) { 564 if (clients[c]->urgent) { 565 focus(c); 566 return; 567 } 568 } 569 } 570 571 void 572 fullscreen(const Arg *arg) 573 { 574 XEvent e; 575 576 e.type = ClientMessage; 577 e.xclient.window = win; 578 e.xclient.message_type = wmatom[WMState]; 579 e.xclient.format = 32; 580 e.xclient.data.l[0] = 2; 581 e.xclient.data.l[1] = wmatom[WMFullscreen]; 582 e.xclient.data.l[2] = 0; 583 XSendEvent(dpy, root, False, SubstructureNotifyMask, &e); 584 } 585 586 char * 587 getatom(int a) 588 { 589 static char buf[BUFSIZ]; 590 Atom adummy; 591 int idummy; 592 unsigned long ldummy; 593 unsigned char *p = NULL; 594 595 XGetWindowProperty(dpy, win, wmatom[a], 0L, BUFSIZ, False, XA_STRING, 596 &adummy, &idummy, &ldummy, &ldummy, &p); 597 if (p) 598 strncpy(buf, (char *)p, LENGTH(buf)-1); 599 else 600 buf[0] = '\0'; 601 XFree(p); 602 603 return buf; 604 } 605 606 int 607 getclient(Window w) 608 { 609 int i; 610 611 for (i = 0; i < nclients; i++) { 612 if (clients[i]->win == w) 613 return i; 614 } 615 616 return -1; 617 } 618 619 XftColor 620 getcolor(const char *colstr) 621 { 622 XftColor color; 623 624 if (!XftColorAllocName(dpy, visual, cmap, colstr, &color)) 625 die("%s: cannot allocate color '%s'\n", argv0, colstr); 626 627 return color; 628 } 629 630 int 631 getfirsttab(void) 632 { 633 int cc, ret; 634 635 if (sel < 0) 636 return 0; 637 638 cc = ww / tabwidth; 639 if (nclients > cc) 640 cc = (ww - TEXTW(before) - TEXTW(after)) / tabwidth; 641 642 ret = sel - cc / 2 + (cc + 1) % 2; 643 return ret < 0 ? 0 : 644 ret + cc > nclients ? MAX(0, nclients - cc) : 645 ret; 646 } 647 648 Bool 649 gettextprop(Window w, Atom atom, char *text, unsigned int size) 650 { 651 char **list = NULL; 652 int n; 653 XTextProperty name; 654 655 if (!text || size == 0) 656 return False; 657 658 text[0] = '\0'; 659 XGetTextProperty(dpy, w, &name, atom); 660 if (!name.nitems) 661 return False; 662 663 if (name.encoding == XA_STRING) { 664 strncpy(text, (char *)name.value, size - 1); 665 } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success 666 && n > 0 && *list) { 667 strncpy(text, *list, size - 1); 668 XFreeStringList(list); 669 } 670 text[size - 1] = '\0'; 671 XFree(name.value); 672 673 return True; 674 } 675 676 void 677 initfont(const char *fontstr) 678 { 679 if (!(dc.font.xfont = XftFontOpenName(dpy, screen, fontstr)) 680 && !(dc.font.xfont = XftFontOpenName(dpy, screen, "fixed"))) 681 die("error, cannot load font: '%s'\n", fontstr); 682 683 dc.font.ascent = dc.font.xfont->ascent; 684 dc.font.descent = dc.font.xfont->descent; 685 dc.font.height = dc.font.ascent + dc.font.descent; 686 } 687 688 Bool 689 isprotodel(int c) 690 { 691 int i, n; 692 Atom *protocols; 693 Bool ret = False; 694 695 if (XGetWMProtocols(dpy, clients[c]->win, &protocols, &n)) { 696 for (i = 0; !ret && i < n; i++) { 697 if (protocols[i] == wmatom[WMDelete]) 698 ret = True; 699 } 700 XFree(protocols); 701 } 702 703 return ret; 704 } 705 706 void 707 keypress(const XEvent *e) 708 { 709 const XKeyEvent *ev = &e->xkey; 710 unsigned int i; 711 KeySym keysym; 712 713 keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0); 714 for (i = 0; i < LENGTH(keys); i++) { 715 if (keysym == keys[i].keysym && 716 CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) && 717 keys[i].func) 718 keys[i].func(&(keys[i].arg)); 719 } 720 } 721 722 void 723 keyrelease(const XEvent *e) 724 { 725 const XKeyEvent *ev = &e->xkey; 726 unsigned int i; 727 KeySym keysym; 728 729 keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0); 730 for (i = 0; i < LENGTH(keyreleases); i++) { 731 if (keysym == keyreleases[i].keysym && 732 CLEANMASK(keyreleases[i].mod) == CLEANMASK(ev->state) && 733 keyreleases[i].func) 734 keyreleases[i].func(&(keyreleases[i].arg)); 735 } 736 } 737 738 void 739 killclient(const Arg *arg) 740 { 741 XEvent ev; 742 743 if (sel < 0) 744 return; 745 746 if (isprotodel(sel) && !clients[sel]->closed) { 747 ev.type = ClientMessage; 748 ev.xclient.window = clients[sel]->win; 749 ev.xclient.message_type = wmatom[WMProtocols]; 750 ev.xclient.format = 32; 751 ev.xclient.data.l[0] = wmatom[WMDelete]; 752 ev.xclient.data.l[1] = CurrentTime; 753 XSendEvent(dpy, clients[sel]->win, False, NoEventMask, &ev); 754 clients[sel]->closed = True; 755 } else { 756 XKillClient(dpy, clients[sel]->win); 757 } 758 } 759 760 void 761 manage(Window w) 762 { 763 updatenumlockmask(); 764 { 765 int i, j, nextpos; 766 unsigned int modifiers[] = { 0, LockMask, numlockmask, 767 numlockmask | LockMask }; 768 KeyCode code; 769 Client *c; 770 XEvent e; 771 772 XWithdrawWindow(dpy, w, 0); 773 XReparentWindow(dpy, w, win, 0, bh); 774 XSelectInput(dpy, w, PropertyChangeMask | 775 StructureNotifyMask | EnterWindowMask); 776 XSync(dpy, False); 777 778 for (i = 0; i < LENGTH(keys); i++) { 779 if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) { 780 for (j = 0; j < LENGTH(modifiers); j++) { 781 XGrabKey(dpy, code, keys[i].mod | 782 modifiers[j], w, True, 783 GrabModeAsync, GrabModeAsync); 784 } 785 } 786 } 787 788 for (i = 0; i < LENGTH(keyreleases); i++) { 789 if ((code = XKeysymToKeycode(dpy, keyreleases[i].keysym))) { 790 for (j = 0; j < LENGTH(modifiers); j++) { 791 XGrabKey(dpy, code, keyreleases[i].mod | 792 modifiers[j], w, True, 793 GrabModeAsync, GrabModeAsync); 794 } 795 } 796 } 797 798 c = ecalloc(1, sizeof *c); 799 c->win = w; 800 801 nclients++; 802 clients = erealloc(clients, sizeof(Client *) * nclients); 803 804 if(npisrelative) { 805 nextpos = sel + newposition; 806 } else { 807 if (newposition < 0) 808 nextpos = nclients - newposition; 809 else 810 nextpos = newposition; 811 } 812 if (nextpos >= nclients) 813 nextpos = nclients - 1; 814 if (nextpos < 0) 815 nextpos = 0; 816 817 if (nclients > 1 && nextpos < nclients - 1) 818 memmove(&clients[nextpos + 1], &clients[nextpos], 819 sizeof(Client *) * (nclients - nextpos - 1)); 820 821 clients[nextpos] = c; 822 updatetitle(nextpos); 823 824 XLowerWindow(dpy, w); 825 XMapWindow(dpy, w); 826 827 e.xclient.window = w; 828 e.xclient.type = ClientMessage; 829 e.xclient.message_type = wmatom[XEmbed]; 830 e.xclient.format = 32; 831 e.xclient.data.l[0] = CurrentTime; 832 e.xclient.data.l[1] = XEMBED_EMBEDDED_NOTIFY; 833 e.xclient.data.l[2] = 0; 834 e.xclient.data.l[3] = win; 835 e.xclient.data.l[4] = 0; 836 XSendEvent(dpy, root, False, NoEventMask, &e); 837 838 XSync(dpy, False); 839 840 /* Adjust sel before focus does set it to lastsel. */ 841 if (sel >= nextpos) 842 sel++; 843 focus(nextfocus ? nextpos : 844 sel < 0 ? 0 : 845 sel); 846 nextfocus = foreground; 847 } 848 } 849 850 void 851 maprequest(const XEvent *e) 852 { 853 const XMapRequestEvent *ev = &e->xmaprequest; 854 855 if (getclient(ev->window) < 0) 856 manage(ev->window); 857 } 858 859 void 860 move(const Arg *arg) 861 { 862 if (arg->i >= 0 && arg->i < nclients) 863 focus(arg->i); 864 } 865 866 void 867 movetab(const Arg *arg) 868 { 869 int c; 870 Client *new; 871 872 if (sel < 0) 873 return; 874 875 c = (sel + arg->i) % nclients; 876 if (c < 0) 877 c += nclients; 878 879 if (c == sel) 880 return; 881 882 new = clients[sel]; 883 if (sel < c) 884 memmove(&clients[sel], &clients[sel+1], 885 sizeof(Client *) * (c - sel)); 886 else 887 memmove(&clients[c+1], &clients[c], 888 sizeof(Client *) * (sel - c)); 889 clients[c] = new; 890 sel = c; 891 892 drawbar(); 893 } 894 895 void 896 propertynotify(const XEvent *e) 897 { 898 const XPropertyEvent *ev = &e->xproperty; 899 XWMHints *wmh; 900 int c; 901 char* selection = NULL; 902 Arg arg; 903 904 if (ev->state == PropertyNewValue && ev->atom == wmatom[WMSelectTab]) { 905 selection = getatom(WMSelectTab); 906 if (!strncmp(selection, "0x", 2)) { 907 arg.i = getclient(strtoul(selection, NULL, 0)); 908 move(&arg); 909 } else { 910 cmd[cmd_append_pos] = selection; 911 arg.v = cmd; 912 spawn(&arg); 913 } 914 } else if (ev->state == PropertyNewValue && ev->atom == XA_WM_HINTS && 915 (c = getclient(ev->window)) > -1 && 916 (wmh = XGetWMHints(dpy, clients[c]->win))) { 917 if (wmh->flags & XUrgencyHint) { 918 XFree(wmh); 919 wmh = XGetWMHints(dpy, win); 920 if (c != sel) { 921 if (urgentswitch && wmh && 922 !(wmh->flags & XUrgencyHint)) { 923 /* only switch, if tabbed was focused 924 * since last urgency hint if WMHints 925 * could not be received, 926 * default to no switch */ 927 focus(c); 928 } else { 929 /* if no switch should be performed, 930 * mark tab as urgent */ 931 clients[c]->urgent = True; 932 drawbar(); 933 } 934 } 935 if (wmh && !(wmh->flags & XUrgencyHint)) { 936 /* update tabbed urgency hint 937 * if not set already */ 938 wmh->flags |= XUrgencyHint; 939 XSetWMHints(dpy, win, wmh); 940 } 941 } 942 XFree(wmh); 943 } else if (ev->state != PropertyDelete && ev->atom == XA_WM_NAME && 944 (c = getclient(ev->window)) > -1) { 945 updatetitle(c); 946 } 947 } 948 949 void 950 resize(int c, int w, int h) 951 { 952 XConfigureEvent ce; 953 XWindowChanges wc; 954 955 ce.x = 0; 956 ce.y = wc.y = bh; 957 ce.width = wc.width = w; 958 ce.height = wc.height = h; 959 ce.type = ConfigureNotify; 960 ce.display = dpy; 961 ce.event = clients[c]->win; 962 ce.window = clients[c]->win; 963 ce.above = None; 964 ce.override_redirect = False; 965 ce.border_width = 0; 966 967 XConfigureWindow(dpy, clients[c]->win, CWY | CWWidth | CWHeight, &wc); 968 XSendEvent(dpy, clients[c]->win, False, StructureNotifyMask, 969 (XEvent *)&ce); 970 } 971 972 void 973 rotate(const Arg *arg) 974 { 975 int nsel = -1; 976 977 if (sel < 0) 978 return; 979 980 if (arg->i == 0) { 981 if (lastsel > -1) 982 focus(lastsel); 983 } else if (sel > -1) { 984 /* Rotating in an arg->i step around the clients. */ 985 nsel = sel + arg->i; 986 while (nsel >= nclients) 987 nsel -= nclients; 988 while (nsel < 0) 989 nsel += nclients; 990 focus(nsel); 991 } 992 } 993 994 void 995 run(void) 996 { 997 XEvent ev; 998 999 /* main event loop */ 1000 XSync(dpy, False); 1001 drawbar(); 1002 if (doinitspawn == True) 1003 spawn(NULL); 1004 1005 while (running) { 1006 XNextEvent(dpy, &ev); 1007 if (handler[ev.type]) 1008 (handler[ev.type])(&ev); /* call handler */ 1009 } 1010 } 1011 1012 void 1013 sendxembed(int c, long msg, long detail, long d1, long d2) 1014 { 1015 XEvent e = { 0 }; 1016 1017 e.xclient.window = clients[c]->win; 1018 e.xclient.type = ClientMessage; 1019 e.xclient.message_type = wmatom[XEmbed]; 1020 e.xclient.format = 32; 1021 e.xclient.data.l[0] = CurrentTime; 1022 e.xclient.data.l[1] = msg; 1023 e.xclient.data.l[2] = detail; 1024 e.xclient.data.l[3] = d1; 1025 e.xclient.data.l[4] = d2; 1026 XSendEvent(dpy, clients[c]->win, False, NoEventMask, &e); 1027 } 1028 1029 void 1030 setcmd(int argc, char *argv[], int replace) 1031 { 1032 int i; 1033 1034 cmd = ecalloc(argc + 3, sizeof(*cmd)); 1035 if (argc == 0) 1036 return; 1037 for (i = 0; i < argc; i++) 1038 cmd[i] = argv[i]; 1039 cmd[replace > 0 ? replace : argc] = winid; 1040 cmd_append_pos = argc + !replace; 1041 cmd[cmd_append_pos] = cmd[cmd_append_pos + 1] = NULL; 1042 } 1043 1044 void 1045 setup(void) 1046 { 1047 int bitm, tx, ty, tw, th, dh, dw, isfixed; 1048 XWMHints *wmh; 1049 XClassHint class_hint; 1050 XSizeHints *size_hint; 1051 1052 /* clean up any zombies immediately */ 1053 sigchld(0); 1054 1055 /* init screen */ 1056 screen = DefaultScreen(dpy); 1057 root = RootWindow(dpy, screen); 1058 initfont(font); 1059 if (barHeight) 1060 vbh = dc.h = barHeight; 1061 else 1062 vbh = dc.h = dc.font.height + 2; 1063 1064 /* init atoms */ 1065 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 1066 wmatom[WMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", 1067 False); 1068 wmatom[WMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 1069 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); 1070 wmatom[WMSelectTab] = XInternAtom(dpy, "_TABBED_SELECT_TAB", False); 1071 wmatom[WMState] = XInternAtom(dpy, "_NET_WM_STATE", False); 1072 wmatom[XEmbed] = XInternAtom(dpy, "_XEMBED", False); 1073 1074 /* init appearance */ 1075 wx = 0; 1076 wy = 0; 1077 ww = 800; 1078 wh = 600; 1079 isfixed = 0; 1080 1081 if (geometry) { 1082 tx = ty = tw = th = 0; 1083 bitm = XParseGeometry(geometry, &tx, &ty, (unsigned *)&tw, 1084 (unsigned *)&th); 1085 if (bitm & XValue) 1086 wx = tx; 1087 if (bitm & YValue) 1088 wy = ty; 1089 if (bitm & WidthValue) 1090 ww = tw; 1091 if (bitm & HeightValue) 1092 wh = th; 1093 if (bitm & XNegative && wx == 0) 1094 wx = -1; 1095 if (bitm & YNegative && wy == 0) 1096 wy = -1; 1097 if (bitm & (HeightValue | WidthValue)) 1098 isfixed = 1; 1099 1100 dw = DisplayWidth(dpy, screen); 1101 dh = DisplayHeight(dpy, screen); 1102 if (wx < 0) 1103 wx = dw + wx - ww - 1; 1104 if (wy < 0) 1105 wy = dh + wy - wh - 1; 1106 } 1107 1108 XVisualInfo *vis; 1109 XRenderPictFormat *fmt; 1110 int nvi; 1111 int i; 1112 1113 XVisualInfo tpl = { 1114 .screen = screen, 1115 .depth = 32, 1116 .class = TrueColor 1117 }; 1118 1119 vis = XGetVisualInfo(dpy, VisualScreenMask | VisualDepthMask | VisualClassMask, &tpl, &nvi); 1120 for(i = 0; i < nvi; i ++) { 1121 fmt = XRenderFindVisualFormat(dpy, vis[i].visual); 1122 if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) { 1123 visual = vis[i].visual; 1124 break; 1125 } 1126 } 1127 1128 XFree(vis); 1129 1130 if (! visual) { 1131 fprintf(stderr, "Couldn't find ARGB visual.\n"); 1132 exit(1); 1133 } 1134 1135 cmap = XCreateColormap( dpy, root, visual, None); 1136 dc.norm[ColBG] = getcolor(normbgcolor); 1137 dc.norm[ColFG] = getcolor(normfgcolor); 1138 dc.sel[ColBG] = getcolor(selbgcolor); 1139 dc.sel[ColFG] = getcolor(selfgcolor); 1140 dc.urg[ColBG] = getcolor(urgbgcolor); 1141 dc.urg[ColFG] = getcolor(urgfgcolor); 1142 XSetWindowAttributes attrs; 1143 attrs.background_pixel = dc.norm[ColBG].pixel; 1144 attrs.border_pixel = dc.norm[ColFG].pixel; 1145 attrs.bit_gravity = NorthWestGravity; 1146 attrs.event_mask = FocusChangeMask | KeyPressMask 1147 | ExposureMask | VisibilityChangeMask | StructureNotifyMask 1148 | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; 1149 attrs.background_pixmap = None ; 1150 attrs.colormap = cmap; 1151 1152 win = XCreateWindow(dpy, root, wx, wy, 1153 ww, wh, 0, 32, InputOutput, 1154 visual, CWBackPixmap | CWBorderPixel | CWBitGravity 1155 | CWEventMask | CWColormap, &attrs); 1156 1157 dc.drawable = XCreatePixmap(dpy, win, ww, wh, 1158 32); 1159 dc.gc = XCreateGC(dpy, dc.drawable, 0, 0); 1160 1161 XMapRaised(dpy, win); 1162 XSelectInput(dpy, win, SubstructureNotifyMask | FocusChangeMask | 1163 ButtonPressMask | ExposureMask | KeyPressMask | 1164 KeyReleaseMask | PropertyChangeMask | StructureNotifyMask | 1165 SubstructureRedirectMask); 1166 xerrorxlib = XSetErrorHandler(xerror); 1167 1168 class_hint.res_name = wmname; 1169 class_hint.res_class = "tabbed"; 1170 XSetClassHint(dpy, win, &class_hint); 1171 1172 size_hint = XAllocSizeHints(); 1173 if (!isfixed) { 1174 size_hint->flags = PSize | PMinSize; 1175 size_hint->height = wh; 1176 size_hint->width = ww; 1177 size_hint->min_height = bh + 1; 1178 } else { 1179 size_hint->flags = PMaxSize | PMinSize; 1180 size_hint->min_width = size_hint->max_width = ww; 1181 size_hint->min_height = size_hint->max_height = wh; 1182 } 1183 wmh = XAllocWMHints(); 1184 XSetWMProperties(dpy, win, NULL, NULL, NULL, 0, size_hint, wmh, NULL); 1185 XFree(size_hint); 1186 XFree(wmh); 1187 1188 XSetWMProtocols(dpy, win, &wmatom[WMDelete], 1); 1189 1190 snprintf(winid, sizeof(winid), "%lu", win); 1191 setenv("XEMBED", winid, 1); 1192 1193 nextfocus = foreground; 1194 focus(-1); 1195 } 1196 1197 void 1198 showbar(const Arg *arg) 1199 { 1200 barvisibility = arg->i; 1201 drawbar(); 1202 } 1203 1204 void 1205 sigchld(int unused) 1206 { 1207 if (signal(SIGCHLD, sigchld) == SIG_ERR) 1208 die("%s: cannot install SIGCHLD handler", argv0); 1209 1210 while (0 < waitpid(-1, NULL, WNOHANG)); 1211 } 1212 1213 void 1214 spawn(const Arg *arg) 1215 { 1216 if (fork() == 0) { 1217 if(dpy) 1218 close(ConnectionNumber(dpy)); 1219 1220 setsid(); 1221 if (arg && arg->v) { 1222 execvp(((char **)arg->v)[0], (char **)arg->v); 1223 fprintf(stderr, "%s: execvp %s", argv0, 1224 ((char **)arg->v)[0]); 1225 } else { 1226 cmd[cmd_append_pos] = NULL; 1227 execvp(cmd[0], cmd); 1228 fprintf(stderr, "%s: execvp %s", argv0, cmd[0]); 1229 } 1230 perror(" failed"); 1231 exit(0); 1232 } 1233 } 1234 1235 int 1236 textnw(const char *text, unsigned int len) 1237 { 1238 XGlyphInfo ext; 1239 XftTextExtentsUtf8(dpy, dc.font.xfont, (XftChar8 *) text, len, &ext); 1240 return ext.xOff; 1241 } 1242 1243 void 1244 toggle(const Arg *arg) 1245 { 1246 *(Bool*) arg->v = !*(Bool*) arg->v; 1247 } 1248 1249 void 1250 unmanage(int c) 1251 { 1252 if (c < 0 || c >= nclients) { 1253 drawbar(); 1254 XSync(dpy, False); 1255 return; 1256 } 1257 1258 if (!nclients) 1259 return; 1260 1261 if (c == 0) { 1262 /* First client. */ 1263 nclients--; 1264 free(clients[0]); 1265 memmove(&clients[0], &clients[1], sizeof(Client *) * nclients); 1266 } else if (c == nclients - 1) { 1267 /* Last client. */ 1268 nclients--; 1269 free(clients[c]); 1270 clients = erealloc(clients, sizeof(Client *) * nclients); 1271 } else { 1272 /* Somewhere inbetween. */ 1273 free(clients[c]); 1274 memmove(&clients[c], &clients[c+1], 1275 sizeof(Client *) * (nclients - (c + 1))); 1276 nclients--; 1277 } 1278 1279 if (nclients <= 0) { 1280 lastsel = sel = -1; 1281 1282 if (closelastclient) 1283 running = False; 1284 else if (fillagain && running) 1285 spawn(NULL); 1286 } else { 1287 if (lastsel >= nclients) 1288 lastsel = nclients - 1; 1289 else if (lastsel > c) 1290 lastsel--; 1291 1292 if (c == sel && lastsel >= 0) { 1293 focus(lastsel); 1294 } else { 1295 if (sel > c) 1296 sel--; 1297 if (sel >= nclients) 1298 sel = nclients - 1; 1299 1300 focus(sel); 1301 } 1302 } 1303 1304 drawbar(); 1305 XSync(dpy, False); 1306 } 1307 1308 void 1309 unmapnotify(const XEvent *e) 1310 { 1311 const XUnmapEvent *ev = &e->xunmap; 1312 int c; 1313 1314 if ((c = getclient(ev->window)) > -1) 1315 unmanage(c); 1316 } 1317 1318 void 1319 updatenumlockmask(void) 1320 { 1321 unsigned int i, j; 1322 XModifierKeymap *modmap; 1323 1324 numlockmask = 0; 1325 modmap = XGetModifierMapping(dpy); 1326 for (i = 0; i < 8; i++) { 1327 for (j = 0; j < modmap->max_keypermod; j++) { 1328 if (modmap->modifiermap[i * modmap->max_keypermod + j] 1329 == XKeysymToKeycode(dpy, XK_Num_Lock)) 1330 numlockmask = (1 << i); 1331 } 1332 } 1333 XFreeModifiermap(modmap); 1334 } 1335 1336 void 1337 updatetitle(int c) 1338 { 1339 if (!gettextprop(clients[c]->win, wmatom[WMName], clients[c]->name, 1340 sizeof(clients[c]->name))) 1341 gettextprop(clients[c]->win, XA_WM_NAME, clients[c]->name, 1342 sizeof(clients[c]->name)); 1343 if (sel == c) 1344 xsettitle(win, clients[c]->name); 1345 drawbar(); 1346 } 1347 1348 /* There's no way to check accesses to destroyed windows, thus those cases are 1349 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs 1350 * default error handler, which may call exit. */ 1351 int 1352 xerror(Display *dpy, XErrorEvent *ee) 1353 { 1354 if (ee->error_code == BadWindow 1355 || (ee->request_code == X_SetInputFocus && 1356 ee->error_code == BadMatch) 1357 || (ee->request_code == X_PolyText8 && 1358 ee->error_code == BadDrawable) 1359 || (ee->request_code == X_PolyFillRectangle && 1360 ee->error_code == BadDrawable) 1361 || (ee->request_code == X_PolySegment && 1362 ee->error_code == BadDrawable) 1363 || (ee->request_code == X_ConfigureWindow && 1364 ee->error_code == BadMatch) 1365 || (ee->request_code == X_GrabButton && 1366 ee->error_code == BadAccess) 1367 || (ee->request_code == X_GrabKey && 1368 ee->error_code == BadAccess) 1369 || (ee->request_code == X_CopyArea && 1370 ee->error_code == BadDrawable)) 1371 return 0; 1372 1373 fprintf(stderr, "%s: fatal error: request code=%d, error code=%d\n", 1374 argv0, ee->request_code, ee->error_code); 1375 return xerrorxlib(dpy, ee); /* may call exit */ 1376 } 1377 1378 void 1379 xsettitle(Window w, const char *str) 1380 { 1381 XTextProperty xtp; 1382 1383 if (XmbTextListToTextProperty(dpy, (char **)&str, 1, 1384 XCompoundTextStyle, &xtp) == Success) { 1385 XSetTextProperty(dpy, w, &xtp, wmatom[WMName]); 1386 XSetTextProperty(dpy, w, &xtp, XA_WM_NAME); 1387 XFree(xtp.value); 1388 } 1389 } 1390 1391 void 1392 usage(void) 1393 { 1394 die("usage: %s [-dfksv] [-g geometry] [-n name] [-p [s+/-]pos]\n" 1395 " [-r narg] [-o color] [-O color] [-t color] [-T color]\n" 1396 " [-u color] [-U color] command...\n", argv0); 1397 } 1398 1399 void 1400 xrdb_load(void) 1401 { 1402 /* XXX */ 1403 char *xrm; 1404 char *type; 1405 XrmDatabase xrdb; 1406 XrmValue ret; 1407 Display *dpy; 1408 1409 if(!(dpy = XOpenDisplay(NULL))) 1410 die("Can't open display\n"); 1411 1412 XrmInitialize(); 1413 xrm = XResourceManagerString(dpy); 1414 1415 if (xrm != NULL) { 1416 xrdb = XrmGetStringDatabase(xrm); 1417 1418 XRESOURCE_LOAD_STRING("color0", normbgcolor); 1419 XRESOURCE_LOAD_STRING("color12", normfgcolor); 1420 1421 XRESOURCE_LOAD_STRING("color12", selbgcolor); 1422 XRESOURCE_LOAD_STRING("color0", selfgcolor); 1423 1424 XRESOURCE_LOAD_STRING("color0", urgbgcolor); 1425 XRESOURCE_LOAD_STRING("color1", urgfgcolor); 1426 1427 XRESOURCE_LOAD_STRING("font", font); 1428 } 1429 XFlush(dpy); 1430 } 1431 1432 void 1433 reload(int sig) { 1434 xrdb_load(); 1435 colors_changed=1; 1436 signal(SIGUSR1, reload); 1437 } 1438 1439 void 1440 writecolors(void) { 1441 dc.norm[ColBG] = getcolor(normbgcolor); 1442 dc.norm[ColFG] = getcolor(normfgcolor); 1443 dc.sel[ColBG] = getcolor(selbgcolor); 1444 dc.sel[ColFG] = getcolor(selfgcolor); 1445 dc.urg[ColBG] = getcolor(urgbgcolor); 1446 dc.urg[ColFG] = getcolor(urgfgcolor); 1447 1448 colors_changed = 0; 1449 } 1450 1451 int 1452 main(int argc, char *argv[]) 1453 { 1454 Bool detach = False; 1455 int replace = 0; 1456 char *pstr; 1457 1458 ARGBEGIN { 1459 case 'c': 1460 closelastclient = True; 1461 fillagain = False; 1462 break; 1463 case 'd': 1464 detach = True; 1465 break; 1466 case 'f': 1467 fillagain = True; 1468 break; 1469 case 'g': 1470 geometry = EARGF(usage()); 1471 break; 1472 case 'k': 1473 killclientsfirst = True; 1474 break; 1475 case 'n': 1476 wmname = EARGF(usage()); 1477 break; 1478 case 'O': 1479 normfgcolor = EARGF(usage()); 1480 break; 1481 case 'o': 1482 normbgcolor = EARGF(usage()); 1483 break; 1484 case 'p': 1485 pstr = EARGF(usage()); 1486 if (pstr[0] == 's') { 1487 npisrelative = True; 1488 newposition = atoi(&pstr[1]); 1489 } else { 1490 newposition = atoi(pstr); 1491 } 1492 break; 1493 case 'r': 1494 replace = atoi(EARGF(usage())); 1495 break; 1496 case 's': 1497 doinitspawn = False; 1498 break; 1499 case 'T': 1500 selfgcolor = EARGF(usage()); 1501 break; 1502 case 't': 1503 selbgcolor = EARGF(usage()); 1504 break; 1505 case 'U': 1506 urgfgcolor = EARGF(usage()); 1507 break; 1508 case 'u': 1509 urgbgcolor = EARGF(usage()); 1510 break; 1511 case 'v': 1512 die("tabbed-"VERSION", © 2009-2016 tabbed engineers, " 1513 "see LICENSE for details.\n"); 1514 break; 1515 default: 1516 usage(); 1517 break; 1518 } ARGEND; 1519 1520 if (argc < 1) { 1521 doinitspawn = False; 1522 fillagain = False; 1523 } 1524 1525 setcmd(argc, argv, replace); 1526 1527 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 1528 fprintf(stderr, "%s: no locale support\n", argv0); 1529 if (!(dpy = XOpenDisplay(NULL))) 1530 die("%s: cannot open display\n", argv0); 1531 1532 xrdb_load(); 1533 signal(SIGUSR1, reload); 1534 setup(); 1535 printf("0x%lx\n", win); 1536 fflush(NULL); 1537 1538 if (detach) { 1539 if (fork() == 0) { 1540 fclose(stdout); 1541 } else { 1542 if (dpy) 1543 close(ConnectionNumber(dpy)); 1544 return EXIT_SUCCESS; 1545 } 1546 } 1547 1548 run(); 1549 cleanup(); 1550 XCloseDisplay(dpy); 1551 1552 return EXIT_SUCCESS; 1553 } 1554