aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dmenu.c417
1 files changed, 213 insertions, 204 deletions
diff --git a/dmenu.c b/dmenu.c
index 9e78e83..49a6583 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -35,16 +35,16 @@ struct Item {
35 bool out; 35 bool out;
36}; 36};
37 37
38static void appenditem(Item *item, Item **list, Item **last); 38static void appenditem(Item *, Item **, Item **);
39static void calcoffsets(void); 39static void calcoffsets(void);
40static char *cistrstr(const char *s, const char *sub); 40static char *cistrstr(const char *, const char *);
41static void cleanup(void); 41static void cleanup(void);
42static void drawmenu(void); 42static void drawmenu(void);
43static void grabkeyboard(void); 43static void grabkeyboard(void);
44static void insert(const char *str, ssize_t n); 44static void insert(const char *, ssize_t);
45static void keypress(XKeyEvent *ev); 45static void keypress(XKeyEvent *);
46static void match(void); 46static void match(void);
47static size_t nextrune(int inc); 47static size_t nextrune(int);
48static void paste(void); 48static void paste(void);
49static void readstdin(void); 49static void readstdin(void);
50static void run(void); 50static void run(void);
@@ -53,100 +53,31 @@ static void usage(void);
53 53
54static char text[BUFSIZ] = ""; 54static char text[BUFSIZ] = "";
55static int bh, mw, mh; 55static int bh, mw, mh;
56static int sw, sh; /* X display screen geometry width, height */
56static int inputw, promptw; 57static int inputw, promptw;
57static size_t cursor = 0; 58static size_t cursor;
58static Atom clip, utf8;
59static Item *items = NULL; 59static Item *items = NULL;
60static Item *matches, *matchend; 60static Item *matches, *matchend;
61static Item *prev, *curr, *next, *sel; 61static Item *prev, *curr, *next, *sel;
62static Window win; 62static int mon = -1, screen;
63
64static Atom clip, utf8;
65static Display *dpy;
66static Window root, win;
63static XIC xic; 67static XIC xic;
64static int mon = -1;
65 68
66static ClrScheme scheme[SchemeLast]; 69static ClrScheme scheme[SchemeLast];
67static Display *dpy;
68static int screen;
69static Window root;
70static Drw *drw; 70static Drw *drw;
71static int sw, sh; /* X display screen geometry width, height */
72 71
73#include "config.h" 72#include "config.h"
74 73
75static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; 74static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
76static char *(*fstrstr)(const char *, const char *) = strstr; 75static char *(*fstrstr)(const char *, const char *) = strstr;
77 76
78int 77static void
79main(int argc, char *argv[]) { 78appenditem(Item *item, Item **list, Item **last)
80 bool fast = false; 79{
81 int i; 80 if (*last)
82
83 for(i = 1; i < argc; i++)
84 /* these options take no arguments */
85 if(!strcmp(argv[i], "-v")) { /* prints version information */
86 puts("dmenu-"VERSION", © 2006-2015 dmenu engineers, see LICENSE for details");
87 exit(0);
88 }
89 else if(!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */
90 topbar = false;
91 else if(!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
92 fast = true;
93 else if(!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
94 fstrncmp = strncasecmp;
95 fstrstr = cistrstr;
96 }
97 else if(i+1 == argc)
98 usage();
99 /* these options take one argument */
100 else if(!strcmp(argv[i], "-l")) /* number of lines in vertical list */
101 lines = atoi(argv[++i]);
102 else if(!strcmp(argv[i], "-m"))
103 mon = atoi(argv[++i]);
104 else if(!strcmp(argv[i], "-p")) /* adds prompt to left of input field */
105 prompt = argv[++i];
106 else if(!strcmp(argv[i], "-fn")) /* font or font set */
107 fonts[0] = argv[++i];
108 else if(!strcmp(argv[i], "-nb")) /* normal background color */
109 normbgcolor = argv[++i];
110 else if(!strcmp(argv[i], "-nf")) /* normal foreground color */
111 normfgcolor = argv[++i];
112 else if(!strcmp(argv[i], "-sb")) /* selected background color */
113 selbgcolor = argv[++i];
114 else if(!strcmp(argv[i], "-sf")) /* selected foreground color */
115 selfgcolor = argv[++i];
116 else
117 usage();
118
119 if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
120 fputs("warning: no locale support\n", stderr);
121 if(!(dpy = XOpenDisplay(NULL)))
122 die("dmenu: cannot open display\n");
123 screen = DefaultScreen(dpy);
124 root = RootWindow(dpy, screen);
125 sw = DisplayWidth(dpy, screen);
126 sh = DisplayHeight(dpy, screen);
127 drw = drw_create(dpy, screen, root, sw, sh);
128 drw_load_fonts(drw, fonts, LENGTH(fonts));
129 if(!drw->fontcount)
130 die("No fonts could be loaded.\n");
131 drw_setscheme(drw, &scheme[SchemeNorm]);
132
133 if(fast) {
134 grabkeyboard();
135 readstdin();
136 }
137 else {
138 readstdin();
139 grabkeyboard();
140 }
141 setup();
142 run();
143
144 return 1; /* unreachable */
145}
146
147void
148appenditem(Item *item, Item **list, Item **last) {
149 if(*last)
150 (*last)->right = item; 81 (*last)->right = item;
151 else 82 else
152 *list = item; 83 *list = item;
@@ -156,25 +87,27 @@ appenditem(Item *item, Item **list, Item **last) {
156 *last = item; 87 *last = item;
157} 88}
158 89
159void 90static void
160calcoffsets(void) { 91calcoffsets(void)
92{
161 int i, n; 93 int i, n;
162 94
163 if(lines > 0) 95 if (lines > 0)
164 n = lines * bh; 96 n = lines * bh;
165 else 97 else
166 n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); 98 n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
167 /* calculate which items will begin the next page and previous page */ 99 /* calculate which items will begin the next page and previous page */
168 for(i = 0, next = curr; next; next = next->right) 100 for (i = 0, next = curr; next; next = next->right)
169 if((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n) 101 if ((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n)
170 break; 102 break;
171 for(i = 0, prev = curr; prev && prev->left; prev = prev->left) 103 for (i = 0, prev = curr; prev && prev->left; prev = prev->left)
172 if((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) 104 if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n)
173 break; 105 break;
174} 106}
175 107
176void 108static void
177cleanup(void) { 109cleanup(void)
110{
178 XUngrabKey(dpy, AnyKey, AnyModifier, root); 111 XUngrabKey(dpy, AnyKey, AnyModifier, root);
179 drw_clr_free(scheme[SchemeNorm].bg); 112 drw_clr_free(scheme[SchemeNorm].bg);
180 drw_clr_free(scheme[SchemeNorm].fg); 113 drw_clr_free(scheme[SchemeNorm].fg);
@@ -187,18 +120,20 @@ cleanup(void) {
187 XCloseDisplay(dpy); 120 XCloseDisplay(dpy);
188} 121}
189 122
190char * 123static char *
191cistrstr(const char *s, const char *sub) { 124cistrstr(const char *s, const char *sub)
125{
192 size_t len; 126 size_t len;
193 127
194 for(len = strlen(sub); *s; s++) 128 for (len = strlen(sub); *s; s++)
195 if(!strncasecmp(s, sub, len)) 129 if (!strncasecmp(s, sub, len))
196 return (char *)s; 130 return (char *)s;
197 return NULL; 131 return NULL;
198} 132}
199 133
200void 134static void
201drawmenu(void) { 135drawmenu(void)
136{
202 int curpos; 137 int curpos;
203 Item *item; 138 Item *item;
204 int x = 0, y = 0, h = bh, w; 139 int x = 0, y = 0, h = bh, w;
@@ -206,7 +141,7 @@ drawmenu(void) {
206 drw_setscheme(drw, &scheme[SchemeNorm]); 141 drw_setscheme(drw, &scheme[SchemeNorm]);
207 drw_rect(drw, 0, 0, mw, mh, 1, 1, 1); 142 drw_rect(drw, 0, 0, mw, mh, 1, 1, 1);
208 143
209 if(prompt && *prompt) { 144 if (prompt && *prompt) {
210 drw_setscheme(drw, &scheme[SchemeSel]); 145 drw_setscheme(drw, &scheme[SchemeSel]);
211 drw_text(drw, x, 0, promptw, bh, prompt, 0); 146 drw_text(drw, x, 0, promptw, bh, prompt, 0);
212 x += promptw; 147 x += promptw;
@@ -216,41 +151,40 @@ drawmenu(void) {
216 drw_setscheme(drw, &scheme[SchemeNorm]); 151 drw_setscheme(drw, &scheme[SchemeNorm]);
217 drw_text(drw, x, 0, w, bh, text, 0); 152 drw_text(drw, x, 0, w, bh, text, 0);
218 153
219 if((curpos = TEXTNW(text, cursor) + bh/2 - 2) < w) { 154 if ((curpos = TEXTNW(text, cursor) + bh / 2 - 2) < w) {
220 drw_setscheme(drw, &scheme[SchemeNorm]); 155 drw_setscheme(drw, &scheme[SchemeNorm]);
221 drw_rect(drw, x + curpos + 2, 2, 1, bh - 4, 1, 1, 0); 156 drw_rect(drw, x + curpos + 2, 2, 1, bh - 4, 1, 1, 0);
222 } 157 }
223 158
224 if(lines > 0) { 159 if (lines > 0) {
225 /* draw vertical list */ 160 /* draw vertical list */
226 w = mw - x; 161 w = mw - x;
227 for(item = curr; item != next; item = item->right) { 162 for (item = curr; item != next; item = item->right) {
228 y += h; 163 y += h;
229 if(item == sel) 164 if (item == sel)
230 drw_setscheme(drw, &scheme[SchemeSel]); 165 drw_setscheme(drw, &scheme[SchemeSel]);
231 else if(item->out) 166 else if (item->out)
232 drw_setscheme(drw, &scheme[SchemeOut]); 167 drw_setscheme(drw, &scheme[SchemeOut]);
233 else 168 else
234 drw_setscheme(drw, &scheme[SchemeNorm]); 169 drw_setscheme(drw, &scheme[SchemeNorm]);
235 170
236 drw_text(drw, x, y, w, bh, item->text, 0); 171 drw_text(drw, x, y, w, bh, item->text, 0);
237 } 172 }
238 } 173 } else if (matches) {
239 else if(matches) {
240 /* draw horizontal list */ 174 /* draw horizontal list */
241 x += inputw; 175 x += inputw;
242 w = TEXTW("<"); 176 w = TEXTW("<");
243 if(curr->left) { 177 if (curr->left) {
244 drw_setscheme(drw, &scheme[SchemeNorm]); 178 drw_setscheme(drw, &scheme[SchemeNorm]);
245 drw_text(drw, x, 0, w, bh, "<", 0); 179 drw_text(drw, x, 0, w, bh, "<", 0);
246 } 180 }
247 for(item = curr; item != next; item = item->right) { 181 for (item = curr; item != next; item = item->right) {
248 x += w; 182 x += w;
249 w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); 183 w = MIN(TEXTW(item->text), mw - x - TEXTW(">"));
250 184
251 if(item == sel) 185 if (item == sel)
252 drw_setscheme(drw, &scheme[SchemeSel]); 186 drw_setscheme(drw, &scheme[SchemeSel]);
253 else if(item->out) 187 else if (item->out)
254 drw_setscheme(drw, &scheme[SchemeOut]); 188 drw_setscheme(drw, &scheme[SchemeOut]);
255 else 189 else
256 drw_setscheme(drw, &scheme[SchemeNorm]); 190 drw_setscheme(drw, &scheme[SchemeNorm]);
@@ -258,7 +192,7 @@ drawmenu(void) {
258 } 192 }
259 w = TEXTW(">"); 193 w = TEXTW(">");
260 x = mw - w; 194 x = mw - w;
261 if(next) { 195 if (next) {
262 drw_setscheme(drw, &scheme[SchemeNorm]); 196 drw_setscheme(drw, &scheme[SchemeNorm]);
263 drw_text(drw, x, 0, w, bh, ">", 0); 197 drw_text(drw, x, 0, w, bh, ">", 0);
264 } 198 }
@@ -266,13 +200,14 @@ drawmenu(void) {
266 drw_map(drw, win, 0, 0, mw, mh); 200 drw_map(drw, win, 0, 0, mw, mh);
267} 201}
268 202
269void 203static void
270grabkeyboard(void) { 204grabkeyboard(void)
205{
271 int i; 206 int i;
272 207
273 /* try to grab keyboard, we may have to wait for another process to ungrab */ 208 /* try to grab keyboard, we may have to wait for another process to ungrab */
274 for(i = 0; i < 1000; i++) { 209 for (i = 0; i < 1000; i++) {
275 if(XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, 210 if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True,
276 GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) 211 GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess)
277 return; 212 return;
278 usleep(1000); 213 usleep(1000);
@@ -280,29 +215,31 @@ grabkeyboard(void) {
280 die("cannot grab keyboard\n"); 215 die("cannot grab keyboard\n");
281} 216}
282 217
283void 218static void
284insert(const char *str, ssize_t n) { 219insert(const char *str, ssize_t n)
285 if(strlen(text) + n > sizeof text - 1) 220{
221 if (strlen(text) + n > sizeof text - 1)
286 return; 222 return;
287 /* move existing text out of the way, insert new text, and update cursor */ 223 /* move existing text out of the way, insert new text, and update cursor */
288 memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); 224 memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0));
289 if(n > 0) 225 if (n > 0)
290 memcpy(&text[cursor], str, n); 226 memcpy(&text[cursor], str, n);
291 cursor += n; 227 cursor += n;
292 match(); 228 match();
293} 229}
294 230
295void 231static void
296keypress(XKeyEvent *ev) { 232keypress(XKeyEvent *ev)
233{
297 char buf[32]; 234 char buf[32];
298 int len; 235 int len;
299 KeySym ksym = NoSymbol; 236 KeySym ksym = NoSymbol;
300 Status status; 237 Status status;
301 238
302 len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); 239 len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
303 if(status == XBufferOverflow) 240 if (status == XBufferOverflow)
304 return; 241 return;
305 if(ev->state & ControlMask) 242 if (ev->state & ControlMask)
306 switch(ksym) { 243 switch(ksym) {
307 case XK_a: ksym = XK_Home; break; 244 case XK_a: ksym = XK_Home; break;
308 case XK_b: ksym = XK_Left; break; 245 case XK_b: ksym = XK_Left; break;
@@ -328,9 +265,9 @@ keypress(XKeyEvent *ev) {
328 insert(NULL, 0 - cursor); 265 insert(NULL, 0 - cursor);
329 break; 266 break;
330 case XK_w: /* delete word */ 267 case XK_w: /* delete word */
331 while(cursor > 0 && text[nextrune(-1)] == ' ') 268 while (cursor > 0 && text[nextrune(-1)] == ' ')
332 insert(NULL, nextrune(-1) - cursor); 269 insert(NULL, nextrune(-1) - cursor);
333 while(cursor > 0 && text[nextrune(-1)] != ' ') 270 while (cursor > 0 && text[nextrune(-1)] != ' ')
334 insert(NULL, nextrune(-1) - cursor); 271 insert(NULL, nextrune(-1) - cursor);
335 break; 272 break;
336 case XK_y: /* paste selection */ 273 case XK_y: /* paste selection */
@@ -346,7 +283,7 @@ keypress(XKeyEvent *ev) {
346 default: 283 default:
347 return; 284 return;
348 } 285 }
349 else if(ev->state & Mod1Mask) 286 else if (ev->state & Mod1Mask)
350 switch(ksym) { 287 switch(ksym) {
351 case XK_g: ksym = XK_Home; break; 288 case XK_g: ksym = XK_Home; break;
352 case XK_G: ksym = XK_End; break; 289 case XK_G: ksym = XK_End; break;
@@ -359,31 +296,31 @@ keypress(XKeyEvent *ev) {
359 } 296 }
360 switch(ksym) { 297 switch(ksym) {
361 default: 298 default:
362 if(!iscntrl(*buf)) 299 if (!iscntrl(*buf))
363 insert(buf, len); 300 insert(buf, len);
364 break; 301 break;
365 case XK_Delete: 302 case XK_Delete:
366 if(text[cursor] == '\0') 303 if (text[cursor] == '\0')
367 return; 304 return;
368 cursor = nextrune(+1); 305 cursor = nextrune(+1);
369 /* fallthrough */ 306 /* fallthrough */
370 case XK_BackSpace: 307 case XK_BackSpace:
371 if(cursor == 0) 308 if (cursor == 0)
372 return; 309 return;
373 insert(NULL, nextrune(-1) - cursor); 310 insert(NULL, nextrune(-1) - cursor);
374 break; 311 break;
375 case XK_End: 312 case XK_End:
376 if(text[cursor] != '\0') { 313 if (text[cursor] != '\0') {
377 cursor = strlen(text); 314 cursor = strlen(text);
378 break; 315 break;
379 } 316 }
380 if(next) { 317 if (next) {
381 /* jump to end of list and position items in reverse */ 318 /* jump to end of list and position items in reverse */
382 curr = matchend; 319 curr = matchend;
383 calcoffsets(); 320 calcoffsets();
384 curr = prev; 321 curr = prev;
385 calcoffsets(); 322 calcoffsets();
386 while(next && (curr = curr->right)) 323 while (next && (curr = curr->right))
387 calcoffsets(); 324 calcoffsets();
388 } 325 }
389 sel = matchend; 326 sel = matchend;
@@ -392,7 +329,7 @@ keypress(XKeyEvent *ev) {
392 cleanup(); 329 cleanup();
393 exit(1); 330 exit(1);
394 case XK_Home: 331 case XK_Home:
395 if(sel == matches) { 332 if (sel == matches) {
396 cursor = 0; 333 cursor = 0;
397 break; 334 break;
398 } 335 }
@@ -400,27 +337,27 @@ keypress(XKeyEvent *ev) {
400 calcoffsets(); 337 calcoffsets();
401 break; 338 break;
402 case XK_Left: 339 case XK_Left:
403 if(cursor > 0 && (!sel || !sel->left || lines > 0)) { 340 if (cursor > 0 && (!sel || !sel->left || lines > 0)) {
404 cursor = nextrune(-1); 341 cursor = nextrune(-1);
405 break; 342 break;
406 } 343 }
407 if(lines > 0) 344 if (lines > 0)
408 return; 345 return;
409 /* fallthrough */ 346 /* fallthrough */
410 case XK_Up: 347 case XK_Up:
411 if(sel && sel->left && (sel = sel->left)->right == curr) { 348 if (sel && sel->left && (sel = sel->left)->right == curr) {
412 curr = prev; 349 curr = prev;
413 calcoffsets(); 350 calcoffsets();
414 } 351 }
415 break; 352 break;
416 case XK_Next: 353 case XK_Next:
417 if(!next) 354 if (!next)
418 return; 355 return;
419 sel = curr = next; 356 sel = curr = next;
420 calcoffsets(); 357 calcoffsets();
421 break; 358 break;
422 case XK_Prior: 359 case XK_Prior:
423 if(!prev) 360 if (!prev)
424 return; 361 return;
425 sel = curr = prev; 362 sel = curr = prev;
426 calcoffsets(); 363 calcoffsets();
@@ -428,29 +365,29 @@ keypress(XKeyEvent *ev) {
428 case XK_Return: 365 case XK_Return:
429 case XK_KP_Enter: 366 case XK_KP_Enter:
430 puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); 367 puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
431 if(!(ev->state & ControlMask)) { 368 if (!(ev->state & ControlMask)) {
432 cleanup(); 369 cleanup();
433 exit(0); 370 exit(0);
434 } 371 }
435 if(sel) 372 if (sel)
436 sel->out = true; 373 sel->out = true;
437 break; 374 break;
438 case XK_Right: 375 case XK_Right:
439 if(text[cursor] != '\0') { 376 if (text[cursor] != '\0') {
440 cursor = nextrune(+1); 377 cursor = nextrune(+1);
441 break; 378 break;
442 } 379 }
443 if(lines > 0) 380 if (lines > 0)
444 return; 381 return;
445 /* fallthrough */ 382 /* fallthrough */
446 case XK_Down: 383 case XK_Down:
447 if(sel && sel->right && (sel = sel->right) == next) { 384 if (sel && sel->right && (sel = sel->right) == next) {
448 curr = next; 385 curr = next;
449 calcoffsets(); 386 calcoffsets();
450 } 387 }
451 break; 388 break;
452 case XK_Tab: 389 case XK_Tab:
453 if(!sel) 390 if (!sel)
454 return; 391 return;
455 strncpy(text, sel->text, sizeof text - 1); 392 strncpy(text, sel->text, sizeof text - 1);
456 text[sizeof text - 1] = '\0'; 393 text[sizeof text - 1] = '\0';
@@ -461,8 +398,9 @@ keypress(XKeyEvent *ev) {
461 drawmenu(); 398 drawmenu();
462} 399}
463 400
464void 401static void
465match(void) { 402match(void)
403{
466 static char **tokv = NULL; 404 static char **tokv = NULL;
467 static int tokn = 0; 405 static int tokn = 0;
468 406
@@ -473,41 +411,39 @@ match(void) {
473 411
474 strcpy(buf, text); 412 strcpy(buf, text);
475 /* separate input text into tokens to be matched individually */ 413 /* separate input text into tokens to be matched individually */
476 for(s = strtok(buf, " "); s; tokv[tokc-1] = s, s = strtok(NULL, " ")) 414 for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " "))
477 if(++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) 415 if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv)))
478 die("cannot realloc %u bytes\n", tokn * sizeof *tokv); 416 die("cannot realloc %u bytes\n", tokn * sizeof *tokv);
479 len = tokc ? strlen(tokv[0]) : 0; 417 len = tokc ? strlen(tokv[0]) : 0;
480 418
481 matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; 419 matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
482 for(item = items; item && item->text; item++) { 420 for (item = items; item && item->text; item++) {
483 for(i = 0; i < tokc; i++) 421 for (i = 0; i < tokc; i++)
484 if(!fstrstr(item->text, tokv[i])) 422 if (!fstrstr(item->text, tokv[i]))
485 break; 423 break;
486 if(i != tokc) /* not all tokens match */ 424 if (i != tokc) /* not all tokens match */
487 continue; 425 continue;
488 /* exact matches go first, then prefixes, then substrings */ 426 /* exact matches go first, then prefixes, then substrings */
489 if(!tokc || !fstrncmp(tokv[0], item->text, len+1)) 427 if (!tokc || !fstrncmp(tokv[0], item->text, len + 1))
490 appenditem(item, &matches, &matchend); 428 appenditem(item, &matches, &matchend);
491 else if(!fstrncmp(tokv[0], item->text, len)) 429 else if (!fstrncmp(tokv[0], item->text, len))
492 appenditem(item, &lprefix, &prefixend); 430 appenditem(item, &lprefix, &prefixend);
493 else 431 else
494 appenditem(item, &lsubstr, &substrend); 432 appenditem(item, &lsubstr, &substrend);
495 } 433 }
496 if(lprefix) { 434 if (lprefix) {
497 if(matches) { 435 if (matches) {
498 matchend->right = lprefix; 436 matchend->right = lprefix;
499 lprefix->left = matchend; 437 lprefix->left = matchend;
500 } 438 } else
501 else
502 matches = lprefix; 439 matches = lprefix;
503 matchend = prefixend; 440 matchend = prefixend;
504 } 441 }
505 if(lsubstr) { 442 if (lsubstr) {
506 if(matches) { 443 if (matches) {
507 matchend->right = lsubstr; 444 matchend->right = lsubstr;
508 lsubstr->left = matchend; 445 lsubstr->left = matchend;
509 } 446 } else
510 else
511 matches = lsubstr; 447 matches = lsubstr;
512 matchend = substrend; 448 matchend = substrend;
513 } 449 }
@@ -515,17 +451,20 @@ match(void) {
515 calcoffsets(); 451 calcoffsets();
516} 452}
517 453
518size_t 454static size_t
519nextrune(int inc) { 455nextrune(int inc)
456{
520 ssize_t n; 457 ssize_t n;
521 458
522 /* return location of next utf8 rune in the given direction (+1 or -1) */ 459 /* return location of next utf8 rune in the given direction (+1 or -1) */
523 for(n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc); 460 for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc)
461 ;
524 return n; 462 return n;
525} 463}
526 464
527void 465static void
528paste(void) { 466paste(void)
467{
529 char *p, *q; 468 char *p, *q;
530 int di; 469 int di;
531 unsigned long dl; 470 unsigned long dl;
@@ -534,64 +473,67 @@ paste(void) {
534 /* we have been given the current selection, now insert it into input */ 473 /* we have been given the current selection, now insert it into input */
535 XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, 474 XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False,
536 utf8, &da, &di, &dl, &dl, (unsigned char **)&p); 475 utf8, &da, &di, &dl, &dl, (unsigned char **)&p);
537 insert(p, (q = strchr(p, '\n')) ? q-p : (ssize_t)strlen(p)); 476 insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p));
538 XFree(p); 477 XFree(p);
539 drawmenu(); 478 drawmenu();
540} 479}
541 480
542void 481static void
543readstdin(void) { 482readstdin(void)
483{
544 char buf[sizeof text], *p, *maxstr = NULL; 484 char buf[sizeof text], *p, *maxstr = NULL;
545 size_t i, max = 0, size = 0; 485 size_t i, max = 0, size = 0;
546 486
547 /* read each line from stdin and add it to the item list */ 487 /* read each line from stdin and add it to the item list */
548 for(i = 0; fgets(buf, sizeof buf, stdin); i++) { 488 for (i = 0; fgets(buf, sizeof buf, stdin); i++) {
549 if(i+1 >= size / sizeof *items) 489 if (i + 1 >= size / sizeof *items)
550 if(!(items = realloc(items, (size += BUFSIZ)))) 490 if (!(items = realloc(items, (size += BUFSIZ))))
551 die("cannot realloc %u bytes:", size); 491 die("cannot realloc %u bytes:", size);
552 if((p = strchr(buf, '\n'))) 492 if ((p = strchr(buf, '\n')))
553 *p = '\0'; 493 *p = '\0';
554 if(!(items[i].text = strdup(buf))) 494 if (!(items[i].text = strdup(buf)))
555 die("cannot strdup %u bytes:", strlen(buf)+1); 495 die("cannot strdup %u bytes:", strlen(buf) + 1);
556 items[i].out = false; 496 items[i].out = false;
557 if(strlen(items[i].text) > max) 497 if (strlen(items[i].text) > max)
558 max = strlen(maxstr = items[i].text); 498 max = strlen(maxstr = items[i].text);
559 } 499 }
560 if(items) 500 if (items)
561 items[i].text = NULL; 501 items[i].text = NULL;
562 inputw = maxstr ? TEXTW(maxstr) : 0; 502 inputw = maxstr ? TEXTW(maxstr) : 0;
563 lines = MIN(lines, i); 503 lines = MIN(lines, i);
564} 504}
565 505
566void 506static void
567run(void) { 507run(void)
508{
568 XEvent ev; 509 XEvent ev;
569 510
570 while(!XNextEvent(dpy, &ev)) { 511 while (!XNextEvent(dpy, &ev)) {
571 if(XFilterEvent(&ev, win)) 512 if (XFilterEvent(&ev, win))
572 continue; 513 continue;
573 switch(ev.type) { 514 switch(ev.type) {
574 case Expose: 515 case Expose:
575 if(ev.xexpose.count == 0) 516 if (ev.xexpose.count == 0)
576 drw_map(drw, win, 0, 0, mw, mh); 517 drw_map(drw, win, 0, 0, mw, mh);
577 break; 518 break;
578 case KeyPress: 519 case KeyPress:
579 keypress(&ev.xkey); 520 keypress(&ev.xkey);
580 break; 521 break;
581 case SelectionNotify: 522 case SelectionNotify:
582 if(ev.xselection.property == utf8) 523 if (ev.xselection.property == utf8)
583 paste(); 524 paste();
584 break; 525 break;
585 case VisibilityNotify: 526 case VisibilityNotify:
586 if(ev.xvisibility.state != VisibilityUnobscured) 527 if (ev.xvisibility.state != VisibilityUnobscured)
587 XRaiseWindow(dpy, win); 528 XRaiseWindow(dpy, win);
588 break; 529 break;
589 } 530 }
590 } 531 }
591} 532}
592 533
593void 534static void
594setup(void) { 535setup(void)
536{
595 int x, y; 537 int x, y;
596 XSetWindowAttributes swa; 538 XSetWindowAttributes swa;
597 XIM xim; 539 XIM xim;
@@ -619,36 +561,35 @@ setup(void) {
619 lines = MAX(lines, 0); 561 lines = MAX(lines, 0);
620 mh = (lines + 1) * bh; 562 mh = (lines + 1) * bh;
621#ifdef XINERAMA 563#ifdef XINERAMA
622 if((info = XineramaQueryScreens(dpy, &n))) { 564 if ((info = XineramaQueryScreens(dpy, &n))) {
623 XGetInputFocus(dpy, &w, &di); 565 XGetInputFocus(dpy, &w, &di);
624 if(mon != -1 && mon < n) 566 if (mon != -1 && mon < n)
625 i = mon; 567 i = mon;
626 if(!i && w != root && w != PointerRoot && w != None) { 568 if (!i && w != root && w != PointerRoot && w != None) {
627 /* find top-level window containing current input focus */ 569 /* find top-level window containing current input focus */
628 do { 570 do {
629 if(XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) 571 if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws)
630 XFree(dws); 572 XFree(dws);
631 } while(w != root && w != pw); 573 } while (w != root && w != pw);
632 /* find xinerama screen with which the window intersects most */ 574 /* find xinerama screen with which the window intersects most */
633 if(XGetWindowAttributes(dpy, pw, &wa)) 575 if (XGetWindowAttributes(dpy, pw, &wa))
634 for(j = 0; j < n; j++) 576 for (j = 0; j < n; j++)
635 if((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { 577 if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) {
636 area = a; 578 area = a;
637 i = j; 579 i = j;
638 } 580 }
639 } 581 }
640 /* no focused window is on screen, so use pointer location instead */ 582 /* no focused window is on screen, so use pointer location instead */
641 if(mon == -1 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) 583 if (mon == -1 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du))
642 for(i = 0; i < n; i++) 584 for (i = 0; i < n; i++)
643 if(INTERSECT(x, y, 1, 1, info[i])) 585 if (INTERSECT(x, y, 1, 1, info[i]))
644 break; 586 break;
645 587
646 x = info[i].x_org; 588 x = info[i].x_org;
647 y = info[i].y_org + (topbar ? 0 : info[i].height - mh); 589 y = info[i].y_org + (topbar ? 0 : info[i].height - mh);
648 mw = info[i].width; 590 mw = info[i].width;
649 XFree(info); 591 XFree(info);
650 } 592 } else
651 else
652#endif 593#endif
653 { 594 {
654 x = 0; 595 x = 0;
@@ -678,9 +619,77 @@ setup(void) {
678 drawmenu(); 619 drawmenu();
679} 620}
680 621
681void 622static void
682usage(void) { 623usage(void)
624{
683 fputs("usage: dmenu [-b] [-f] [-i] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" 625 fputs("usage: dmenu [-b] [-f] [-i] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
684 " [-nb color] [-nf color] [-sb color] [-sf color] [-v]\n", stderr); 626 " [-nb color] [-nf color] [-sb color] [-sf color] [-v]\n", stderr);
685 exit(1); 627 exit(1);
686} 628}
629
630int
631main(int argc, char *argv[])
632{
633 bool fast = false;
634 int i;
635
636 for (i = 1; i < argc; i++)
637 /* these options take no arguments */
638 if (!strcmp(argv[i], "-v")) { /* prints version information */
639 puts("dmenu-"VERSION);
640 exit(0);
641 } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */
642 topbar = false;
643 else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
644 fast = true;
645 else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
646 fstrncmp = strncasecmp;
647 fstrstr = cistrstr;
648 } else if (i + 1 == argc)
649 usage();
650 /* these options take one argument */
651 else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */
652 lines = atoi(argv[++i]);
653 else if (!strcmp(argv[i], "-m"))
654 mon = atoi(argv[++i]);
655 else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */
656 prompt = argv[++i];
657 else if (!strcmp(argv[i], "-fn")) /* font or font set */
658 fonts[0] = argv[++i];
659 else if (!strcmp(argv[i], "-nb")) /* normal background color */
660 normbgcolor = argv[++i];
661 else if (!strcmp(argv[i], "-nf")) /* normal foreground color */
662 normfgcolor = argv[++i];
663 else if (!strcmp(argv[i], "-sb")) /* selected background color */
664 selbgcolor = argv[++i];
665 else if (!strcmp(argv[i], "-sf")) /* selected foreground color */
666 selfgcolor = argv[++i];
667 else
668 usage();
669
670 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
671 fputs("warning: no locale support\n", stderr);
672 if (!(dpy = XOpenDisplay(NULL)))
673 die("cannot open display\n");
674 screen = DefaultScreen(dpy);
675 root = RootWindow(dpy, screen);
676 sw = DisplayWidth(dpy, screen);
677 sh = DisplayHeight(dpy, screen);
678 drw = drw_create(dpy, screen, root, sw, sh);
679 drw_load_fonts(drw, fonts, LENGTH(fonts));
680 if (!drw->fontcount)
681 die("no fonts could be loaded.\n");
682 drw_setscheme(drw, &scheme[SchemeNorm]);
683
684 if (fast) {
685 grabkeyboard();
686 readstdin();
687 } else {
688 readstdin();
689 grabkeyboard();
690 }
691 setup();
692 run();
693
694 return 1; /* unreachable */
695}