+ /* move existing text out of the way, insert new text, and update cursor */
+ memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0));
+ if (n > 0)
+ memcpy(&text[cursor], str, n);
+ cursor += n;
+ match();
+}
+
+static size_t
+nextrune(int inc)
+{
+ ssize_t n;
+
+ /* return location of next utf8 rune in the given direction (+1 or -1) */
+ for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc)
+ ;
+ return n;
+}
+
+static void
+keypress(XKeyEvent *ev)
+{
+ char buf[32];
+ int len;
+ KeySym ksym = NoSymbol;
+ Status status;
+
+ len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
+ if (status == XBufferOverflow)
+ return;
+ if (ev->state & ControlMask)
+ switch(ksym) {
+ case XK_a: ksym = XK_Home; break;
+ case XK_b: ksym = XK_Left; break;
+ case XK_c: ksym = XK_Escape; break;
+ case XK_d: ksym = XK_Delete; break;
+ case XK_e: ksym = XK_End; break;
+ case XK_f: ksym = XK_Right; break;
+ case XK_g: ksym = XK_Escape; break;
+ case XK_h: ksym = XK_BackSpace; break;
+ case XK_i: ksym = XK_Tab; break;
+ case XK_j: /* fallthrough */
+ case XK_J: /* fallthrough */
+ case XK_m: /* fallthrough */
+ case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break;
+ case XK_n: ksym = XK_Down; break;
+ case XK_p: ksym = XK_Up; break;
+
+ case XK_k: /* delete right */
+ text[cursor] = '\0';
+ match();