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.
 
 
 
 
 
 

363 lines
8.1 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. #ifdef XINERAMA
  12. #include <X11/extensions/Xinerama.h>
  13. #endif
  14. #include <draw.h>
  15. /* macros */
  16. #define INRECT(X,Y,RX,RY,RW,RH) ((X) >= (RX) && (X) < (RX) + (RW) && (Y) >= (RY) && (Y) < (RY) + (RH))
  17. #define MIN(a, b) ((a) < (b) ? (a) : (b))
  18. #define MAX(a, b) ((a) > (b) ? (a) : (b))
  19. #define IS_UTF8_1ST_CHAR(c) ((((c) & 0xc0) == 0xc0) || !((c) & 0x80))
  20. /* forward declarations */
  21. static void cleanup(void);
  22. static void drawinput(void);
  23. static void grabkeyboard(void);
  24. static void kpress(XKeyEvent *e);
  25. static void run(void);
  26. static void setup(void);
  27. #include "config.h"
  28. /* variables */
  29. static char *prompt = NULL;
  30. static char text[4096];
  31. static int promptw = 0;
  32. static int screen;
  33. static size_t cursor = 0;
  34. static unsigned int numlockmask = 0;
  35. static unsigned int mw, mh;
  36. static unsigned long normcol[ColLast];
  37. static unsigned long selcol[ColLast];
  38. static Bool topbar = True;
  39. static DC dc;
  40. static Display *dpy;
  41. static Window win, root;
  42. void
  43. cleanup(void) {
  44. cleanupdraw(&dc);
  45. XDestroyWindow(dpy, win);
  46. XUngrabKeyboard(dpy, CurrentTime);
  47. XCloseDisplay(dpy);
  48. }
  49. void
  50. drawinput(void)
  51. {
  52. dc.x = 0;
  53. dc.y = 0;
  54. dc.w = mw;
  55. dc.h = mh;
  56. drawtext(&dc, NULL, normcol);
  57. /* print prompt? */
  58. if(prompt) {
  59. dc.w = promptw;
  60. drawtext(&dc, prompt, selcol);
  61. dc.x += dc.w;
  62. }
  63. dc.w = mw - dc.x;
  64. drawtext(&dc, text, normcol);
  65. drawcursor(&dc, text, cursor, normcol);
  66. commitdraw(&dc, win);
  67. }
  68. void
  69. grabkeyboard(void) {
  70. unsigned int len;
  71. for(len = 1000; len; len--) {
  72. if(XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)
  73. == GrabSuccess)
  74. return;
  75. usleep(1000);
  76. }
  77. exit(EXIT_FAILURE);
  78. }
  79. void
  80. kpress(XKeyEvent *e) {
  81. char buf[sizeof text];
  82. int num;
  83. unsigned int i, len;
  84. KeySym ksym;
  85. len = strlen(text);
  86. num = XLookupString(e, buf, sizeof buf, &ksym, NULL);
  87. if(ksym == XK_KP_Enter)
  88. ksym = XK_Return;
  89. else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
  90. ksym = (ksym - XK_KP_0) + XK_0;
  91. else if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
  92. || IsMiscFunctionKey(ksym) || IsPFKey(ksym)
  93. || IsPrivateKeypadKey(ksym))
  94. return;
  95. /* first check if a control mask is omitted */
  96. if(e->state & ControlMask) {
  97. switch(tolower(ksym)) {
  98. default:
  99. return;
  100. case XK_a:
  101. ksym = XK_Home;
  102. break;
  103. case XK_b:
  104. ksym = XK_Left;
  105. break;
  106. case XK_c:
  107. ksym = XK_Escape;
  108. break;
  109. case XK_e:
  110. ksym = XK_End;
  111. break;
  112. case XK_f:
  113. ksym = XK_Right;
  114. break;
  115. case XK_h:
  116. ksym = XK_BackSpace;
  117. break;
  118. case XK_j:
  119. case XK_m:
  120. ksym = XK_Return;
  121. break;
  122. case XK_k:
  123. text[cursor] = '\0';
  124. break;
  125. case XK_u:
  126. memmove(text, text + cursor, sizeof text - cursor + 1);
  127. cursor = 0;
  128. break;
  129. case XK_w:
  130. if(cursor > 0) {
  131. i = cursor;
  132. while(i-- > 0 && text[i] == ' ');
  133. while(i-- > 0 && text[i] != ' ');
  134. memmove(text + i + 1, text + cursor, sizeof text - cursor + 1);
  135. cursor = i + 1;
  136. }
  137. break;
  138. case XK_y:
  139. {
  140. FILE *fp;
  141. char *s;
  142. if(!(fp = popen("sselp", "r")))
  143. eprint("cannot popen sselp\n");
  144. s = fgets(buf, sizeof buf, fp);
  145. pclose(fp);
  146. if(s == NULL)
  147. return;
  148. }
  149. num = strlen(buf);
  150. if(num && buf[num-1] == '\n')
  151. buf[--num] = '\0';
  152. break;
  153. }
  154. }
  155. switch(ksym) {
  156. default:
  157. num = MIN(num, sizeof text - cursor);
  158. if(num && !iscntrl((int) buf[0])) {
  159. memmove(text + cursor + num, text + cursor, sizeof text - cursor - num);
  160. memcpy(text + cursor, buf, num);
  161. cursor += num;
  162. }
  163. break;
  164. case XK_BackSpace:
  165. if(cursor == 0)
  166. return;
  167. for(i = 1; cursor - i > 0 && !IS_UTF8_1ST_CHAR(text[cursor - i]); i++);
  168. memmove(text + cursor - i, text + cursor, sizeof text - cursor + i);
  169. cursor -= i;
  170. break;
  171. case XK_Delete:
  172. if(cursor == len)
  173. return;
  174. for(i = 1; cursor + i < len && !IS_UTF8_1ST_CHAR(text[cursor + i]); i++);
  175. memmove(text + cursor, text + cursor + i, sizeof text - cursor);
  176. break;
  177. case XK_End:
  178. cursor = len;
  179. break;
  180. case XK_Escape:
  181. exit(EXIT_FAILURE);
  182. case XK_Home:
  183. cursor = 0;
  184. break;
  185. case XK_Left:
  186. if(cursor == 0)
  187. return;
  188. while(cursor-- > 0 && !IS_UTF8_1ST_CHAR(text[cursor]));
  189. break;
  190. case XK_Return:
  191. fprintf(stdout, "%s", text);
  192. fflush(stdout);
  193. exit(EXIT_SUCCESS);
  194. case XK_Right:
  195. if(cursor == len)
  196. return;
  197. while(cursor++ < len && !IS_UTF8_1ST_CHAR(text[cursor]));
  198. break;
  199. }
  200. drawinput();
  201. }
  202. void
  203. run(void) {
  204. XEvent ev;
  205. /* main event loop */
  206. XSync(dpy, False);
  207. while(!XNextEvent(dpy, &ev))
  208. switch(ev.type) {
  209. case KeyPress:
  210. kpress(&ev.xkey);
  211. break;
  212. case Expose:
  213. if(ev.xexpose.count == 0)
  214. drawinput();
  215. break;
  216. case VisibilityNotify:
  217. if(ev.xvisibility.state != VisibilityUnobscured)
  218. XRaiseWindow(dpy, win);
  219. break;
  220. }
  221. exit(EXIT_FAILURE);
  222. }
  223. void
  224. setup(void) {
  225. int i, j, x, y;
  226. #if XINERAMA
  227. int n;
  228. XineramaScreenInfo *info = NULL;
  229. #endif
  230. XModifierKeymap *modmap;
  231. XSetWindowAttributes wa;
  232. /* init modifier map */
  233. modmap = XGetModifierMapping(dpy);
  234. for(i = 0; i < 8; i++)
  235. for(j = 0; j < modmap->max_keypermod; j++) {
  236. if(modmap->modifiermap[i * modmap->max_keypermod + j]
  237. == XKeysymToKeycode(dpy, XK_Num_Lock))
  238. numlockmask = (1 << i);
  239. }
  240. XFreeModifiermap(modmap);
  241. dc.dpy = dpy;
  242. normcol[ColBG] = getcolor(&dc, normbgcolor);
  243. normcol[ColFG] = getcolor(&dc, normfgcolor);
  244. selcol[ColBG] = getcolor(&dc, selbgcolor);
  245. selcol[ColFG] = getcolor(&dc, selfgcolor);
  246. initfont(&dc, font);
  247. /* input window */
  248. wa.override_redirect = True;
  249. wa.background_pixmap = ParentRelative;
  250. wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
  251. /* input window geometry */
  252. mh = dc.font.height + 2;
  253. #if XINERAMA
  254. if(XineramaIsActive(dpy) && (info = XineramaQueryScreens(dpy, &n))) {
  255. i = 0;
  256. if(n > 1) {
  257. int di;
  258. unsigned int dui;
  259. Window dummy;
  260. if(XQueryPointer(dpy, root, &dummy, &dummy, &x, &y, &di, &di, &dui))
  261. for(i = 0; i < n; i++)
  262. if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height))
  263. break;
  264. }
  265. x = info[i].x_org;
  266. y = topbar ? info[i].y_org : info[i].y_org + info[i].height - mh;
  267. mw = info[i].width;
  268. XFree(info);
  269. }
  270. else
  271. #endif
  272. {
  273. x = 0;
  274. y = topbar ? 0 : DisplayHeight(dpy, screen) - mh;
  275. mw = DisplayWidth(dpy, screen);
  276. }
  277. win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
  278. DefaultDepth(dpy, screen), CopyFromParent,
  279. DefaultVisual(dpy, screen),
  280. CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
  281. setupdraw(&dc, win);
  282. if(prompt)
  283. promptw = MIN(textw(&dc, prompt), mw / 5);
  284. cursor = strlen(text);
  285. XMapRaised(dpy, win);
  286. }
  287. int
  288. main(int argc, char *argv[]) {
  289. unsigned int i;
  290. /* command line args */
  291. progname = "dinput";
  292. for(i = 1; i < argc; i++)
  293. if(!strcmp(argv[i], "-i"))
  294. ; /* ignore flag */
  295. else if(!strcmp(argv[i], "-b"))
  296. topbar = False;
  297. else if(!strcmp(argv[i], "-l"))
  298. i++; /* ignore flag */
  299. else if(!strcmp(argv[i], "-fn")) {
  300. if(++i < argc) font = argv[i];
  301. }
  302. else if(!strcmp(argv[i], "-nb")) {
  303. if(++i < argc) normbgcolor = argv[i];
  304. }
  305. else if(!strcmp(argv[i], "-nf")) {
  306. if(++i < argc) normfgcolor = argv[i];
  307. }
  308. else if(!strcmp(argv[i], "-p")) {
  309. if(++i < argc) prompt = argv[i];
  310. }
  311. else if(!strcmp(argv[i], "-sb")) {
  312. if(++i < argc) selbgcolor = argv[i];
  313. }
  314. else if(!strcmp(argv[i], "-sf")) {
  315. if(++i < argc) selfgcolor = argv[i];
  316. }
  317. else if(!strcmp(argv[i], "-v")) {
  318. printf("dinput-"VERSION", © 2006-2010 dinput engineers, see LICENSE for details\n");
  319. exit(EXIT_SUCCESS);
  320. }
  321. else if(!*text)
  322. strncpy(text, argv[i], sizeof text);
  323. else {
  324. fputs("usage: dinput [-b] [-fn <font>] [-nb <color>] [-nf <color>]\n"
  325. " [-p <prompt>] [-sb <color>] [-sf <color>] [-v] [<text>]\n", stderr);
  326. exit(EXIT_FAILURE);
  327. }
  328. if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
  329. fprintf(stderr, "dinput: warning: no locale support\n");
  330. if(!(dpy = XOpenDisplay(NULL)))
  331. eprint("cannot open display\n");
  332. if(atexit(&cleanup) != 0)
  333. eprint("cannot register cleanup\n");
  334. screen = DefaultScreen(dpy);
  335. root = RootWindow(dpy, screen);
  336. grabkeyboard();
  337. setup();
  338. run();
  339. return 0;
  340. }