Another copy of my dotfiles. Because I don't completely trust GitHub.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1385 lines
29 KiB

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