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); |