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.
 
 
 
 
 
 

469 lines
9.6 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. drawtext(&dc, NULL, 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. Item *i;
  145. dc.x += cmdw;
  146. dc.w = textw(&dc, "<");
  147. drawtext(&dc, curr->left ? "<" : NULL, normcol);
  148. dc.x += dc.w;
  149. for(i = curr; i != next; i = i->right) {
  150. dc.w = MIN(textw(&dc, i->text), mw / 3);
  151. drawtext(&dc, i->text, (sel == i) ? selcol : normcol);
  152. dc.x += dc.w;
  153. }
  154. dc.w = textw(&dc, ">");
  155. dc.x = mw - dc.w;
  156. drawtext(&dc, next ? ">" : NULL, normcol);
  157. }
  158. void
  159. drawmenuv(void) {
  160. Item *i;
  161. XWindowAttributes wa;
  162. dc.y = topbar ? dc.h : 0;
  163. dc.w = mw - dc.x;
  164. for(i = curr; i != next; i = i->right) {
  165. drawtext(&dc, i->text, (sel == i) ? selcol : normcol);
  166. dc.y += dc.h;
  167. }
  168. if(!XGetWindowAttributes(dpy, win, &wa))
  169. eprint("cannot get window attributes");
  170. XMoveResizeWindow(dpy, win, wa.x, wa.y + (topbar ? 0 : wa.height - mh), mw, mh);
  171. }
  172. void
  173. kpress(XKeyEvent *e) {
  174. char buf[sizeof text];
  175. int num;
  176. unsigned int i, len;
  177. KeySym ksym;
  178. len = strlen(text);
  179. num = XLookupString(e, buf, sizeof buf, &ksym, NULL);
  180. if(ksym == XK_KP_Enter)
  181. ksym = XK_Return;
  182. else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
  183. ksym = (ksym - XK_KP_0) + XK_0;
  184. else if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
  185. || IsMiscFunctionKey(ksym) || IsPFKey(ksym)
  186. || IsPrivateKeypadKey(ksym))
  187. return;
  188. /* first check if a control mask is omitted */
  189. if(e->state & ControlMask) {
  190. switch(tolower(ksym)) {
  191. default:
  192. return;
  193. case XK_a:
  194. ksym = XK_Home;
  195. break;
  196. case XK_b:
  197. ksym = XK_Left;
  198. break;
  199. case XK_c:
  200. ksym = XK_Escape;
  201. break;
  202. case XK_e:
  203. ksym = XK_End;
  204. break;
  205. case XK_f:
  206. ksym = XK_Right;
  207. break;
  208. case XK_h:
  209. ksym = XK_BackSpace;
  210. break;
  211. case XK_i:
  212. ksym = XK_Tab;
  213. break;
  214. case XK_j:
  215. case XK_m:
  216. ksym = XK_Return;
  217. break;
  218. case XK_n:
  219. ksym = XK_Down;
  220. break;
  221. case XK_p:
  222. ksym = XK_Up;
  223. break;
  224. case XK_u:
  225. text[0] = '\0';
  226. match();
  227. break;
  228. case XK_w:
  229. if(len == 0)
  230. return;
  231. i = len;
  232. while(i-- > 0 && text[i] == ' ');
  233. while(i-- > 0 && text[i] != ' ');
  234. text[++i] = '\0';
  235. match();
  236. break;
  237. }
  238. }
  239. switch(ksym) {
  240. default:
  241. num = MIN(num, sizeof text);
  242. if(num && !iscntrl((int) buf[0])) {
  243. memcpy(text + len, buf, num + 1);
  244. len += num;
  245. match();
  246. }
  247. break;
  248. case XK_BackSpace:
  249. if(len == 0)
  250. return;
  251. for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[len - i]); i++);
  252. len -= i;
  253. text[len] = '\0';
  254. match();
  255. break;
  256. case XK_End:
  257. while(next) {
  258. sel = curr = next;
  259. calcoffsets();
  260. }
  261. while(sel && sel->right)
  262. sel = sel->right;
  263. break;
  264. case XK_Escape:
  265. exit(EXIT_FAILURE);
  266. case XK_Home:
  267. sel = curr = item;
  268. calcoffsets();
  269. break;
  270. case XK_Left:
  271. case XK_Up:
  272. if(!sel || !sel->left)
  273. return;
  274. sel = sel->left;
  275. if(sel->right == curr) {
  276. curr = prev;
  277. calcoffsets();
  278. }
  279. break;
  280. case XK_Next:
  281. if(!next)
  282. return;
  283. sel = curr = next;
  284. calcoffsets();
  285. break;
  286. case XK_Prior:
  287. if(!prev)
  288. return;
  289. sel = curr = prev;
  290. calcoffsets();
  291. break;
  292. case XK_Return:
  293. if(e->state & ShiftMask)
  294. dinput();
  295. fprintf(stdout, "%s", sel ? sel->text : text);
  296. fflush(stdout);
  297. exit(EXIT_SUCCESS);
  298. case XK_Right:
  299. case XK_Down:
  300. if(!sel || !sel->right)
  301. return;
  302. sel = sel->right;
  303. if(sel == next) {
  304. curr = next;
  305. calcoffsets();
  306. }
  307. break;
  308. case XK_Tab:
  309. if(sel)
  310. strncpy(text, sel->text, sizeof text);
  311. dinput();
  312. break;
  313. }
  314. drawbar();
  315. }
  316. void
  317. match(void) {
  318. unsigned int len;
  319. Item *i, *itemend, *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend;
  320. len = strlen(text);
  321. item = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL;
  322. for(i = allitems; i; i = i->next)
  323. if(!fstrncmp(text, i->text, len + 1))
  324. appenditem(i, &lexact, &exactend);
  325. else if(!fstrncmp(text, i->text, len))
  326. appenditem(i, &lprefix, &prefixend);
  327. else if(fstrstr(i->text, text))
  328. appenditem(i, &lsubstr, &substrend);
  329. if(lexact) {
  330. item = lexact;
  331. itemend = exactend;
  332. }
  333. if(lprefix) {
  334. if(itemend) {
  335. itemend->right = lprefix;
  336. lprefix->left = itemend;
  337. }
  338. else
  339. item = lprefix;
  340. itemend = prefixend;
  341. }
  342. if(lsubstr) {
  343. if(itemend) {
  344. itemend->right = lsubstr;
  345. lsubstr->left = itemend;
  346. }
  347. else
  348. item = lsubstr;
  349. }
  350. curr = prev = next = sel = item;
  351. calcoffsets();
  352. }
  353. void
  354. readstdin(void) {
  355. char *p, buf[sizeof text];
  356. unsigned int len = 0, max = 0;
  357. Item *i, *new;
  358. i = NULL;
  359. while(fgets(buf, sizeof buf, stdin)) {
  360. len = strlen(buf);
  361. if(buf[len-1] == '\n')
  362. buf[--len] = '\0';
  363. if(!(p = strdup(buf)))
  364. eprint("cannot strdup %u bytes\n", len);
  365. if((max = MAX(max, len)) == len)
  366. maxname = p;
  367. if(!(new = malloc(sizeof *new)))
  368. eprint("cannot malloc %u bytes\n", sizeof *new);
  369. new->next = new->left = new->right = NULL;
  370. new->text = p;
  371. if(!i)
  372. allitems = new;
  373. else
  374. i->next = new;
  375. i = new;
  376. }
  377. }
  378. int
  379. main(int argc, char *argv[]) {
  380. unsigned int i;
  381. /* command line args */
  382. progname = "dmenu";
  383. for(i = 1; i < argc; i++)
  384. if(!strcmp(argv[i], "-i")) {
  385. fstrncmp = strncasecmp;
  386. fstrstr = cistrstr;
  387. }
  388. else if(!strcmp(argv[i], "-b"))
  389. topbar = False;
  390. else if(!strcmp(argv[i], "-l")) {
  391. if(++i < argc) lines = atoi(argv[i]);
  392. if(lines > 0)
  393. calcoffsets = calcoffsetsv;
  394. }
  395. else if(!strcmp(argv[i], "-fn")) {
  396. if(++i < argc) font = argv[i];
  397. }
  398. else if(!strcmp(argv[i], "-nb")) {
  399. if(++i < argc) normbgcolor = argv[i];
  400. }
  401. else if(!strcmp(argv[i], "-nf")) {
  402. if(++i < argc) normfgcolor = argv[i];
  403. }
  404. else if(!strcmp(argv[i], "-p")) {
  405. if(++i < argc) prompt = argv[i];
  406. }
  407. else if(!strcmp(argv[i], "-sb")) {
  408. if(++i < argc) selbgcolor = argv[i];
  409. }
  410. else if(!strcmp(argv[i], "-sf")) {
  411. if(++i < argc) selfgcolor = argv[i];
  412. }
  413. else if(!strcmp(argv[i], "-v")) {
  414. printf("dmenu-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n");
  415. exit(EXIT_SUCCESS);
  416. }
  417. else {
  418. fputs("usage: dmenu [-i] [-b] [-l <lines>] [-fn <font>] [-nb <color>]\n"
  419. " [-nf <color>] [-p <prompt>] [-sb <color>] [-sf <color>] [-v]\n", stderr);
  420. exit(EXIT_FAILURE);
  421. }
  422. if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
  423. fprintf(stderr, "dmenu: warning: no locale support\n");
  424. if(!(dpy = XOpenDisplay(NULL)))
  425. eprint("cannot open display\n");
  426. if(atexit(&cleanup) != 0)
  427. eprint("cannot register cleanup\n");
  428. screen = DefaultScreen(dpy);
  429. root = RootWindow(dpy, screen);
  430. if(!(argp = malloc(sizeof *argp * (argc+2))))
  431. eprint("cannot malloc %u bytes\n", sizeof *argp * (argc+2));
  432. memcpy(argp + 2, argv + 1, sizeof *argp * argc);
  433. readstdin();
  434. grabkeyboard();
  435. setup(lines);
  436. if(maxname)
  437. cmdw = MIN(textw(&dc, maxname), mw / 3);
  438. match();
  439. run();
  440. return 0;
  441. }