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.
 
 
 
 
 
 

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