diff options
| -rw-r--r-- | dmenu.c | 72 |
1 files changed, 35 insertions, 37 deletions
| @@ -15,7 +15,6 @@ | |||
| 15 | #define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh)) | 15 | #define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh)) |
| 16 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) | 16 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) |
| 17 | #define MAX(a,b) ((a) > (b) ? (a) : (b)) | 17 | #define MAX(a,b) ((a) > (b) ? (a) : (b)) |
| 18 | #define UTF8_CODEPOINT(c) (((c) & 0xc0) != 0x80) | ||
| 19 | 18 | ||
| 20 | typedef struct Item Item; | 19 | typedef struct Item Item; |
| 21 | struct Item { | 20 | struct Item { |
| @@ -32,7 +31,8 @@ static void grabkeyboard(void); | |||
| 32 | static void insert(const char *s, ssize_t n); | 31 | static void insert(const char *s, ssize_t n); |
| 33 | static void keypress(XKeyEvent *ev); | 32 | static void keypress(XKeyEvent *ev); |
| 34 | static void match(void); | 33 | static void match(void); |
| 35 | static void paste(Atom atom); | 34 | static size_t nextrune(int incr); |
| 35 | static void paste(void); | ||
| 36 | static void readstdin(void); | 36 | static void readstdin(void); |
| 37 | static void run(void); | 37 | static void run(void); |
| 38 | static void setup(void); | 38 | static void setup(void); |
| @@ -52,7 +52,7 @@ static unsigned int lines = 0; | |||
| 52 | static unsigned int promptw; | 52 | static unsigned int promptw; |
| 53 | static unsigned long normcol[ColLast]; | 53 | static unsigned long normcol[ColLast]; |
| 54 | static unsigned long selcol[ColLast]; | 54 | static unsigned long selcol[ColLast]; |
| 55 | static Atom clip, utf8; | 55 | static Atom utf8; |
| 56 | static Bool topbar = True; | 56 | static Bool topbar = True; |
| 57 | static DC *dc; | 57 | static DC *dc; |
| 58 | static Item *items = NULL; | 58 | static Item *items = NULL; |
| @@ -159,7 +159,9 @@ grabkeyboard(void) { | |||
| 159 | 159 | ||
| 160 | void | 160 | void |
| 161 | insert(const char *s, ssize_t n) { | 161 | insert(const char *s, ssize_t n) { |
| 162 | memmove(text + cursor + n, text + cursor, sizeof text - cursor - n); | 162 | if(strlen(text) + n > sizeof text - 1) |
| 163 | return; | ||
| 164 | memmove(text + cursor + n, text + cursor, sizeof text - cursor - MAX(n, 0)); | ||
| 163 | if(n > 0) | 165 | if(n > 0) |
| 164 | memcpy(text + cursor, s, n); | 166 | memcpy(text + cursor, s, n); |
| 165 | cursor += n; | 167 | cursor += n; |
| @@ -169,7 +171,6 @@ insert(const char *s, ssize_t n) { | |||
| 169 | void | 171 | void |
| 170 | keypress(XKeyEvent *ev) { | 172 | keypress(XKeyEvent *ev) { |
| 171 | char buf[32]; | 173 | char buf[32]; |
| 172 | int n; | ||
| 173 | size_t len; | 174 | size_t len; |
| 174 | KeySym ksym; | 175 | KeySym ksym; |
| 175 | 176 | ||
| @@ -217,38 +218,31 @@ keypress(XKeyEvent *ev) { | |||
| 217 | ksym = XK_Up; | 218 | ksym = XK_Up; |
| 218 | break; | 219 | break; |
| 219 | case XK_u: /* delete left */ | 220 | case XK_u: /* delete left */ |
| 220 | insert(NULL, -cursor); | 221 | insert(NULL, 0 - cursor); |
| 221 | break; | 222 | break; |
| 222 | case XK_w: /* delete word */ | 223 | case XK_w: /* delete word */ |
| 223 | if(cursor == 0) | 224 | while(cursor > 0 && text[nextrune(-1)] == ' ') |
| 224 | return; | 225 | insert(NULL, nextrune(-1) - cursor); |
| 225 | n = 0; | 226 | while(cursor > 0 && text[nextrune(-1)] != ' ') |
| 226 | while(cursor - n++ > 0 && text[cursor - n] == ' '); | 227 | insert(NULL, nextrune(-1) - cursor); |
| 227 | while(cursor - n++ > 0 && text[cursor - n] != ' '); | ||
| 228 | insert(NULL, 1-n); | ||
| 229 | break; | 228 | break; |
| 230 | case XK_y: /* paste selection */ | 229 | case XK_y: /* paste selection */ |
| 231 | XConvertSelection(dc->dpy, XA_PRIMARY, utf8, clip, win, CurrentTime); | 230 | XConvertSelection(dc->dpy, XA_PRIMARY, utf8, utf8, win, CurrentTime); |
| 232 | return; | 231 | return; |
| 233 | } | 232 | } |
| 234 | } | 233 | } |
| 235 | switch(ksym) { | 234 | switch(ksym) { |
| 236 | default: | 235 | default: |
| 237 | if(isprint(*buf)) | 236 | if(isprint(*buf)) |
| 238 | insert(buf, MIN(strlen(buf), sizeof text - cursor)); | 237 | insert(buf, strlen(buf)); |
| 239 | break; | ||
| 240 | case XK_BackSpace: | ||
| 241 | if(cursor == 0) | ||
| 242 | return; | ||
| 243 | for(n = 1; cursor - n > 0 && !UTF8_CODEPOINT(text[cursor - n]); n++); | ||
| 244 | insert(NULL, -n); | ||
| 245 | break; | 238 | break; |
| 246 | case XK_Delete: | 239 | case XK_Delete: |
| 247 | if(cursor == len) | 240 | if(cursor == len) |
| 248 | return; | 241 | return; |
| 249 | for(n = 1; cursor + n < len && !UTF8_CODEPOINT(text[cursor + n]); n++); | 242 | cursor = nextrune(+1); |
| 250 | cursor += n; | 243 | case XK_BackSpace: |
| 251 | insert(NULL, -n); | 244 | if(cursor > 0) |
| 245 | insert(NULL, nextrune(-1) - cursor); | ||
| 252 | break; | 246 | break; |
| 253 | case XK_End: | 247 | case XK_End: |
| 254 | if(cursor < len) { | 248 | if(cursor < len) { |
| @@ -274,15 +268,13 @@ keypress(XKeyEvent *ev) { | |||
| 274 | break; | 268 | break; |
| 275 | case XK_Left: | 269 | case XK_Left: |
| 276 | if(cursor > 0 && (!sel || !sel->left || lines > 0)) { | 270 | if(cursor > 0 && (!sel || !sel->left || lines > 0)) { |
| 277 | while(cursor-- > 0 && !UTF8_CODEPOINT(text[cursor])); | 271 | cursor = nextrune(-1); |
| 278 | break; | 272 | break; |
| 279 | } | 273 | } |
| 280 | else if(lines > 0) | 274 | else if(lines > 0) |
| 281 | return; | 275 | return; |
| 282 | case XK_Up: | 276 | case XK_Up: |
| 283 | if(!sel || !sel->left) | 277 | if(sel && sel->left && (sel = sel->left)->right == curr) { |
| 284 | return; | ||
| 285 | if((sel = sel->left)->right == curr) { | ||
| 286 | curr = prev; | 278 | curr = prev; |
| 287 | calcoffsets(); | 279 | calcoffsets(); |
| 288 | } | 280 | } |
| @@ -306,15 +298,13 @@ keypress(XKeyEvent *ev) { | |||
| 306 | exit(EXIT_SUCCESS); | 298 | exit(EXIT_SUCCESS); |
| 307 | case XK_Right: | 299 | case XK_Right: |
| 308 | if(cursor < len) { | 300 | if(cursor < len) { |
| 309 | while(cursor++ < len && !UTF8_CODEPOINT(text[cursor])); | 301 | cursor = nextrune(+1); |
| 310 | break; | 302 | break; |
| 311 | } | 303 | } |
| 312 | else if(lines > 0) | 304 | else if(lines > 0) |
| 313 | return; | 305 | return; |
| 314 | case XK_Down: | 306 | case XK_Down: |
| 315 | if(!sel || !sel->right) | 307 | if(sel && sel->right && (sel = sel->right) == next) { |
| 316 | return; | ||
| 317 | if((sel = sel->right) == next) { | ||
| 318 | curr = next; | 308 | curr = next; |
| 319 | calcoffsets(); | 309 | calcoffsets(); |
| 320 | } | 310 | } |
| @@ -370,14 +360,23 @@ match(void) { | |||
| 370 | calcoffsets(); | 360 | calcoffsets(); |
| 371 | } | 361 | } |
| 372 | 362 | ||
| 363 | size_t | ||
| 364 | nextrune(int incr) { | ||
| 365 | size_t n, len; | ||
| 366 | |||
| 367 | len = strlen(text); | ||
| 368 | for(n = cursor + incr; n >= 0 && n < len && (text[n] & 0xc0) == 0x80; n += incr); | ||
| 369 | return n; | ||
| 370 | } | ||
| 371 | |||
| 373 | void | 372 | void |
| 374 | paste(Atom atom) { | 373 | paste(void) { |
| 375 | char *p, *q; | 374 | char *p, *q; |
| 376 | int di; | 375 | int di; |
| 377 | unsigned long dl; | 376 | unsigned long dl; |
| 378 | Atom da; | 377 | Atom da; |
| 379 | 378 | ||
| 380 | XGetWindowProperty(dc->dpy, win, atom, 0, sizeof text - cursor, False, | 379 | XGetWindowProperty(dc->dpy, win, utf8, 0, (sizeof text / 4) + 1, False, |
| 381 | utf8, &da, &di, &dl, &dl, (unsigned char **)&p); | 380 | utf8, &da, &di, &dl, &dl, (unsigned char **)&p); |
| 382 | insert(p, (q = strchr(p, '\n')) ? q-p : strlen(p)); | 381 | insert(p, (q = strchr(p, '\n')) ? q-p : strlen(p)); |
| 383 | XFree(p); | 382 | XFree(p); |
| @@ -396,8 +395,8 @@ readstdin(void) { | |||
| 396 | eprintf("cannot malloc %u bytes\n", sizeof *item); | 395 | eprintf("cannot malloc %u bytes\n", sizeof *item); |
| 397 | if(!(item->text = strdup(buf))) | 396 | if(!(item->text = strdup(buf))) |
| 398 | eprintf("cannot strdup %u bytes\n", strlen(buf)+1); | 397 | eprintf("cannot strdup %u bytes\n", strlen(buf)+1); |
| 399 | inputw = MAX(inputw, textw(dc, item->text)); | ||
| 400 | item->next = item->left = item->right = NULL; | 398 | item->next = item->left = item->right = NULL; |
| 399 | inputw = MAX(inputw, textw(dc, item->text)); | ||
| 401 | } | 400 | } |
| 402 | } | 401 | } |
| 403 | 402 | ||
| @@ -415,8 +414,8 @@ run(void) { | |||
| 415 | keypress(&ev.xkey); | 414 | keypress(&ev.xkey); |
| 416 | break; | 415 | break; |
| 417 | case SelectionNotify: | 416 | case SelectionNotify: |
| 418 | if(ev.xselection.property != None) | 417 | if(ev.xselection.property == utf8) |
| 419 | paste(ev.xselection.property); | 418 | paste(); |
| 420 | break; | 419 | break; |
| 421 | case VisibilityNotify: | 420 | case VisibilityNotify: |
| 422 | if(ev.xvisibility.state != VisibilityUnobscured) | 421 | if(ev.xvisibility.state != VisibilityUnobscured) |
| @@ -437,7 +436,6 @@ setup(void) { | |||
| 437 | screen = DefaultScreen(dc->dpy); | 436 | screen = DefaultScreen(dc->dpy); |
| 438 | root = RootWindow(dc->dpy, screen); | 437 | root = RootWindow(dc->dpy, screen); |
| 439 | utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False); | 438 | utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False); |
| 440 | clip = XInternAtom(dc->dpy, "_DMENU_STRING", False); | ||
| 441 | 439 | ||
| 442 | normcol[ColBG] = getcolor(dc, normbgcolor); | 440 | normcol[ColBG] = getcolor(dc, normbgcolor); |
| 443 | normcol[ColFG] = getcolor(dc, normfgcolor); | 441 | normcol[ColFG] = getcolor(dc, normfgcolor); |
