tabbed.c 30 KB

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