tabbed.c.orig 29 KB

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