diff options
| -rw-r--r-- | dmenu.c | 417 |
1 files changed, 213 insertions, 204 deletions
| @@ -35,16 +35,16 @@ struct Item { | |||
| 35 | bool out; | 35 | bool out; |
| 36 | }; | 36 | }; |
| 37 | 37 | ||
| 38 | static void appenditem(Item *item, Item **list, Item **last); | 38 | static void appenditem(Item *, Item **, Item **); |
| 39 | static void calcoffsets(void); | 39 | static void calcoffsets(void); |
| 40 | static char *cistrstr(const char *s, const char *sub); | 40 | static char *cistrstr(const char *, const char *); |
| 41 | static void cleanup(void); | 41 | static void cleanup(void); |
| 42 | static void drawmenu(void); | 42 | static void drawmenu(void); |
| 43 | static void grabkeyboard(void); | 43 | static void grabkeyboard(void); |
| 44 | static void insert(const char *str, ssize_t n); | 44 | static void insert(const char *, ssize_t); |
| 45 | static void keypress(XKeyEvent *ev); | 45 | static void keypress(XKeyEvent *); |
| 46 | static void match(void); | 46 | static void match(void); |
| 47 | static size_t nextrune(int inc); | 47 | static size_t nextrune(int); |
| 48 | static void paste(void); | 48 | static void paste(void); |
| 49 | static void readstdin(void); | 49 | static void readstdin(void); |
| 50 | static void run(void); | 50 | static void run(void); |
| @@ -53,100 +53,31 @@ static void usage(void); | |||
| 53 | 53 | ||
| 54 | static char text[BUFSIZ] = ""; | 54 | static char text[BUFSIZ] = ""; |
| 55 | static int bh, mw, mh; | 55 | static int bh, mw, mh; |
| 56 | static int sw, sh; /* X display screen geometry width, height */ | ||
| 56 | static int inputw, promptw; | 57 | static int inputw, promptw; |
| 57 | static size_t cursor = 0; | 58 | static size_t cursor; |
| 58 | static Atom clip, utf8; | ||
| 59 | static Item *items = NULL; | 59 | static Item *items = NULL; |
| 60 | static Item *matches, *matchend; | 60 | static Item *matches, *matchend; |
| 61 | static Item *prev, *curr, *next, *sel; | 61 | static Item *prev, *curr, *next, *sel; |
| 62 | static Window win; | 62 | static int mon = -1, screen; |
| 63 | |||
| 64 | static Atom clip, utf8; | ||
| 65 | static Display *dpy; | ||
| 66 | static Window root, win; | ||
| 63 | static XIC xic; | 67 | static XIC xic; |
| 64 | static int mon = -1; | ||
| 65 | 68 | ||
| 66 | static ClrScheme scheme[SchemeLast]; | 69 | static ClrScheme scheme[SchemeLast]; |
| 67 | static Display *dpy; | ||
| 68 | static int screen; | ||
| 69 | static Window root; | ||
| 70 | static Drw *drw; | 70 | static Drw *drw; |
| 71 | static int sw, sh; /* X display screen geometry width, height */ | ||
| 72 | 71 | ||
| 73 | #include "config.h" | 72 | #include "config.h" |
| 74 | 73 | ||
| 75 | static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; | 74 | static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; |
| 76 | static char *(*fstrstr)(const char *, const char *) = strstr; | 75 | static char *(*fstrstr)(const char *, const char *) = strstr; |
| 77 | 76 | ||
| 78 | int | 77 | static void |
| 79 | main(int argc, char *argv[]) { | 78 | appenditem(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 | |||
| 147 | void | ||
| 148 | appenditem(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 | ||
| 159 | void | 90 | static void |
| 160 | calcoffsets(void) { | 91 | calcoffsets(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 | ||
| 176 | void | 108 | static void |
| 177 | cleanup(void) { | 109 | cleanup(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 | ||
| 190 | char * | 123 | static char * |
| 191 | cistrstr(const char *s, const char *sub) { | 124 | cistrstr(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 | ||
| 200 | void | 134 | static void |
| 201 | drawmenu(void) { | 135 | drawmenu(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 | ||
| 269 | void | 203 | static void |
| 270 | grabkeyboard(void) { | 204 | grabkeyboard(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 | ||
| 283 | void | 218 | static void |
| 284 | insert(const char *str, ssize_t n) { | 219 | insert(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 | ||
| 295 | void | 231 | static void |
| 296 | keypress(XKeyEvent *ev) { | 232 | keypress(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 | ||
| 464 | void | 401 | static void |
| 465 | match(void) { | 402 | match(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 | ||
| 518 | size_t | 454 | static size_t |
| 519 | nextrune(int inc) { | 455 | nextrune(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 | ||
| 527 | void | 465 | static void |
| 528 | paste(void) { | 466 | paste(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 | ||
| 542 | void | 481 | static void |
| 543 | readstdin(void) { | 482 | readstdin(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 | ||
| 566 | void | 506 | static void |
| 567 | run(void) { | 507 | run(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 | ||
| 593 | void | 534 | static void |
| 594 | setup(void) { | 535 | setup(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 | ||
| 681 | void | 622 | static void |
| 682 | usage(void) { | 623 | usage(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 | |||
| 630 | int | ||
| 631 | main(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 | } | ||
