My dmenu build
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.
 
 
 
 
 
 

472 lines
9.7 KiB

  1. /* See LICENSE file for copyright and license details. */
  2. #include <ctype.h>
  3. #include <locale.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <unistd.h>
  8. #include <X11/keysym.h>
  9. #include <X11/Xlib.h>
  10. #include <X11/Xutil.h>
  11. #include "dmenu.h"
  12. typedef struct Item Item;
  13. struct Item {
  14. char *text;
  15. Item *next; /* traverses all items */
  16. Item *left, *right; /* traverses items matching current search pattern */
  17. };
  18. /* forward declarations */
  19. static void appenditem(Item *i, Item **list, Item **last);
  20. static void calcoffsetsh(void);
  21. static void calcoffsetsv(void);
  22. static char *cistrstr(const char *s, const char *sub);
  23. static void cleanup(void);
  24. static void dinput(void);
  25. static void drawmenuh(void);
  26. static void drawmenuv(void);
  27. static void match(void);
  28. static void readstdin(void);
  29. /* variables */
  30. static char **argp = NULL;
  31. static char *maxname = NULL;
  32. static unsigned int cmdw = 0;
  33. static unsigned int lines = 0;
  34. static Item *allitems = NULL; /* first of all items */
  35. static Item *item = NULL; /* first of pattern matching items */
  36. static Item *sel = NULL;
  37. static Item *next = NULL;
  38. static Item *prev = NULL;
  39. static Item *curr = NULL;
  40. static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
  41. static char *(*fstrstr)(const char *, const char *) = strstr;
  42. static void (*calcoffsets)(void) = calcoffsetsh;
  43. void
  44. appenditem(Item *i, Item **list, Item **last) {
  45. if(!(*last))
  46. *list = i;
  47. else
  48. (*last)->right = i;
  49. i->left = *last;
  50. i->right = NULL;
  51. *last = i;
  52. }
  53. void
  54. calcoffsetsh(void) {
  55. unsigned int w, x;
  56. w = promptw + cmdw + textw(&dc, "<") + textw(&dc, ">");
  57. for(x = w, next = curr; next; next = next->right)
  58. if((x += MIN(textw(&dc, next->text), mw / 3)) > mw)
  59. break;
  60. for(x = w, prev = curr; prev && prev->left; prev = prev->left)
  61. if((x += MIN(textw(&dc, prev->left->text), mw / 3)) > mw)
  62. break;
  63. }
  64. void
  65. calcoffsetsv(void) {
  66. unsigned int i;
  67. next = prev = curr;
  68. for(i = 0; i < lines && next; i++)
  69. next = next->right;
  70. mh = (dc.font.height + 2) * (i + 1);
  71. for(i = 0; i < lines && prev && prev->left; i++)
  72. prev = prev->left;
  73. }
  74. char *
  75. cistrstr(const char *s, const char *sub) {
  76. int c, csub;
  77. unsigned int len;
  78. if(!sub)
  79. return (char *)s;
  80. if((c = tolower(*sub++)) != '\0') {
  81. len = strlen(sub);
  82. do {
  83. do {
  84. if((csub = *s++) == '\0')
  85. return NULL;
  86. }
  87. while(tolower(csub) != c);
  88. }
  89. while(strncasecmp(s, sub, len) != 0);
  90. s--;
  91. }
  92. return (char *)s;
  93. }
  94. void
  95. cleanup(void) {
  96. Item *itm;
  97. while(allitems) {
  98. itm = allitems->next;
  99. free(allitems->text);
  100. free(allitems);
  101. allitems = itm;
  102. }
  103. cleanupdraw(&dc);
  104. XDestroyWindow(dpy, win);
  105. XUngrabKeyboard(dpy, CurrentTime);
  106. XCloseDisplay(dpy);
  107. }
  108. void
  109. dinput(void) {
  110. cleanup();
  111. argp[0] = "dinput";
  112. argp[1] = text;
  113. execvp("dinput", argp);
  114. eprint("cannot exec dinput\n");
  115. }
  116. void
  117. drawbar(void) {
  118. dc.x = 0;
  119. dc.y = 0;
  120. dc.w = mw;
  121. dc.h = mh;
  122. drawbox(&dc, normcol);
  123. dc.h = dc.font.height + 2;
  124. dc.y = topbar ? 0 : mh - dc.h;
  125. /* print prompt? */
  126. if(prompt) {
  127. dc.w = promptw;
  128. drawtext(&dc, prompt, selcol);
  129. dc.x += dc.w;
  130. }
  131. dc.w = mw - dc.x;
  132. /* print command */
  133. if(cmdw && item && lines == 0)
  134. dc.w = cmdw;
  135. drawtext(&dc, text, normcol);
  136. if(lines > 0)
  137. drawmenuv();
  138. else if(curr)
  139. drawmenuh();
  140. commitdraw(&dc, win);
  141. }
  142. void
  143. drawmenuh(void) {
  144. unsigned long *col;
  145. Item *i;
  146. dc.x += cmdw;
  147. dc.w = textw(&dc, "<");
  148. drawtext(&dc, curr->left ? "<" : NULL, normcol);
  149. dc.x += dc.w;
  150. for(i = curr; i != next; i = i->right) {
  151. dc.w = MIN(textw(&dc, i->text), mw / 3);
  152. col = (sel == i) ? selcol : normcol;
  153. drawbox(&dc, col);
  154. drawtext(&dc, i->text, col);
  155. dc.x += dc.w;
  156. }
  157. dc.w = textw(&dc, ">");
  158. dc.x = mw - dc.w;
  159. drawtext(&dc, next ? ">" : NULL, normcol);
  160. }
  161. void
  162. drawmenuv(void) {
  163. Item *i;
  164. XWindowAttributes wa;
  165. dc.y = topbar ? dc.h : 0;
  166. dc.w = mw - dc.x;
  167. for(i = curr; i != next; i = i->right) {
  168. drawtext(&dc, i->text, (sel == i) ? selcol : normcol);
  169. dc.y += dc.h;
  170. }
  171. if(!XGetWindowAttributes(dpy, win, &wa))
  172. eprint("cannot get window attributes");
  173. XMoveResizeWindow(dpy, win, wa.x, wa.y + (topbar ? 0 : wa.height - mh), mw, mh);
  174. }
  175. void
  176. kpress(XKeyEvent *e) {
  177. char buf[sizeof text];
  178. int num;
  179. unsigned int i, len;
  180. KeySym ksym;
  181. len = strlen(text);
  182. num = XLookupString(e, buf, sizeof buf, &ksym, NULL);
  183. if(ksym == XK_KP_Enter)
  184. ksym = XK_Return;
  185. else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
  186. ksym = (ksym - XK_KP_0) + XK_0;
  187. else if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
  188. || IsMiscFunctionKey(ksym) || IsPFKey(ksym)
  189. || IsPrivateKeypadKey(ksym))
  190. return;
  191. /* first check if a control mask is omitted */
  192. if(e->state & ControlMask) {
  193. switch(tolower(ksym)) {
  194. default:
  195. return;
  196. case XK_a:
  197. ksym = XK_Home;
  198. break;
  199. case XK_b:
  200. ksym = XK_Left;
  201. break;
  202. case XK_c:
  203. ksym = XK_Escape;
  204. break;
  205. case XK_e:
  206. ksym = XK_End;
  207. break;
  208. case XK_f:
  209. ksym = XK_Right;
  210. break;
  211. case XK_h:
  212. ksym = XK_BackSpace;
  213. break;
  214. case XK_i:
  215. ksym = XK_Tab;
  216. break;
  217. case XK_j:
  218. case XK_m:
  219. ksym = XK_Return;
  220. break;
  221. case XK_n:
  222. ksym = XK_Down;
  223. break;
  224. case XK_p:
  225. ksym = XK_Up;
  226. break;
  227. case XK_u:
  228. text[0] = '\0';
  229. match();
  230. break;
  231. case XK_w:
  232. if(len == 0)
  233. return;
  234. i = len;
  235. while(i-- > 0 && text[i] == ' ');
  236. while(i-- > 0 && text[i] != ' ');
  237. text[++i] = '\0';
  238. match();
  239. break;
  240. }
  241. }
  242. switch(ksym) {
  243. default:
  244. num = MIN(num, sizeof text);
  245. if(num && !iscntrl((int) buf[0])) {
  246. memcpy(text + len, buf, num + 1);
  247. len += num;
  248. match();
  249. }
  250. break;
  251. case XK_BackSpace:
  252. if(len == 0)
  253. return;
  254. for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[len - i]); i++);
  255. len -= i;
  256. text[len] = '\0';
  257. match();
  258. break;
  259. case XK_End:
  260. while(next) {
  261. sel = curr = next;
  262. calcoffsets();
  263. }
  264. while(sel && sel->right)
  265. sel = sel->right;
  266. break;
  267. case XK_Escape:
  268. exit(EXIT_FAILURE);
  269. case XK_Home:
  270. sel = curr = item;
  271. calcoffsets();
  272. break;
  273. case XK_Left:
  274. case XK_Up:
  275. if(!sel || !sel->left)
  276. return;
  277. sel = sel->left;
  278. if(sel->right == curr) {
  279. curr = prev;
  280. calcoffsets();
  281. }
  282. break;
  283. case XK_Next:
  284. if(!next)
  285. return;
  286. sel = curr = next;
  287. calcoffsets();
  288. break;
  289. case XK_Prior:
  290. if(!prev)
  291. return;
  292. sel = curr = prev;
  293. calcoffsets();
  294. break;
  295. case XK_Return:
  296. if(e->state & ShiftMask)
  297. dinput();
  298. fprintf(stdout, "%s", sel ? sel->text : text);
  299. fflush(stdout);
  300. exit(EXIT_SUCCESS);
  301. case XK_Right:
  302. case XK_Down:
  303. if(!sel || !sel->right)
  304. return;
  305. sel = sel->right;
  306. if(sel == next) {
  307. curr = next;
  308. calcoffsets();
  309. }
  310. break;
  311. case XK_Tab:
  312. if(sel)
  313. strncpy(text, sel->text, sizeof text);
  314. dinput();
  315. break;
  316. }
  317. drawbar();
  318. }
  319. void
  320. match(void) {
  321. unsigned int len;
  322. Item *i, *itemend, *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend;
  323. len = strlen(text);
  324. item = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL;
  325. for(i = allitems; i; i = i->next)
  326. if(!fstrncmp(text, i->text, len + 1))
  327. appenditem(i, &lexact, &exactend);
  328. else if(!fstrncmp(text, i->text, len))
  329. appenditem(i, &lprefix, &prefixend);
  330. else if(fstrstr(i->text, text))
  331. appenditem(i, &lsubstr, &substrend);
  332. if(lexact) {
  333. item = lexact;
  334. itemend = exactend;
  335. }
  336. if(lprefix) {
  337. if(itemend) {
  338. itemend->right = lprefix;
  339. lprefix->left = itemend;
  340. }
  341. else
  342. item = lprefix;
  343. itemend = prefixend;
  344. }
  345. if(lsubstr) {
  346. if(itemend) {
  347. itemend->right = lsubstr;
  348. lsubstr->left = itemend;
  349. }
  350. else
  351. item = lsubstr;
  352. }
  353. curr = prev = next = sel = item;
  354. calcoffsets();
  355. }
  356. void
  357. readstdin(void) {
  358. char *p, buf[sizeof text];
  359. unsigned int len = 0, max = 0;
  360. Item *i, *new;
  361. i = NULL;
  362. while(fgets(buf, sizeof buf, stdin)) {
  363. len = strlen(buf);
  364. if(buf[len-1] == '\n')
  365. buf[--len] = '\0';
  366. if(!(p = strdup(buf)))
  367. eprint("cannot strdup %u bytes\n", len);
  368. if((max = MAX(max, len)) == len)
  369. maxname = p;
  370. if(!(new = malloc(sizeof *new)))
  371. eprint("cannot malloc %u bytes\n", sizeof *new);
  372. new->next = new->left = new->right = NULL;
  373. new->text = p;
  374. if(!i)
  375. allitems = new;
  376. else
  377. i->next = new;
  378. i = new;
  379. }
  380. }
  381. int
  382. main(int argc, char *argv[]) {
  383. unsigned int i;
  384. /* command line args */
  385. progname = "dmenu";
  386. for(i = 1; i < argc; i++)
  387. if(!strcmp(argv[i], "-i")) {
  388. fstrncmp = strncasecmp;
  389. fstrstr = cistrstr;
  390. }
  391. else if(!strcmp(argv[i], "-b"))
  392. topbar = False;
  393. else if(!strcmp(argv[i], "-l")) {
  394. if(++i < argc) lines = atoi(argv[i]);
  395. if(lines > 0)
  396. calcoffsets = calcoffsetsv;
  397. }
  398. else if(!strcmp(argv[i], "-fn")) {
  399. if(++i < argc) font = argv[i];
  400. }
  401. else if(!strcmp(argv[i], "-nb")) {
  402. if(++i < argc) normbgcolor = argv[i];
  403. }
  404. else if(!strcmp(argv[i], "-nf")) {
  405. if(++i < argc) normfgcolor = argv[i];
  406. }
  407. else if(!strcmp(argv[i], "-p")) {
  408. if(++i < argc) prompt = argv[i];
  409. }
  410. else if(!strcmp(argv[i], "-sb")) {
  411. if(++i < argc) selbgcolor = argv[i];
  412. }
  413. else if(!strcmp(argv[i], "-sf")) {
  414. if(++i < argc) selfgcolor = argv[i];
  415. }
  416. else if(!strcmp(argv[i], "-v")) {
  417. printf("dmenu-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n");
  418. exit(EXIT_SUCCESS);
  419. }
  420. else {
  421. fputs("usage: dmenu [-i] [-b] [-l <lines>] [-fn <font>] [-nb <color>]\n"
  422. " [-nf <color>] [-p <prompt>] [-sb <color>] [-sf <color>] [-v]\n", stderr);
  423. exit(EXIT_FAILURE);
  424. }
  425. if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
  426. fprintf(stderr, "dmenu: warning: no locale support\n");
  427. if(!(dpy = XOpenDisplay(NULL)))
  428. eprint("cannot open display\n");
  429. if(atexit(&cleanup) != 0)
  430. eprint("cannot register cleanup\n");
  431. screen = DefaultScreen(dpy);
  432. root = RootWindow(dpy, screen);
  433. if(!(argp = malloc(sizeof *argp * (argc+2))))
  434. eprint("cannot malloc %u bytes\n", sizeof *argp * (argc+2));
  435. memcpy(argp + 2, argv + 1, sizeof *argp * argc);
  436. readstdin();
  437. grabkeyboard();
  438. setup(lines);
  439. if(maxname)
  440. cmdw = MIN(textw(&dc, maxname), mw / 3);
  441. match();
  442. run();
  443. return 0;
  444. }