/* * Convert a string into a buffer of UTF-8 characters. Terminated by size == 0. * Caller frees. */ struct utf8_data * utf8_fromcstr(const char *src) { struct utf8_data *dst; size_t n; enum utf8_state more; dst = NULL; n = 0; while (*src != '\0') { dst = xreallocarray(dst, n + 1, sizeof *dst); if ((more = utf8_open(&dst[n], *src)) == UTF8_MORE) { while (*++src != '\0' && more == UTF8_MORE) more = utf8_append(&dst[n], *src); if (more == UTF8_DONE) { n++; continue; } src -= dst[n].have; } utf8_set(&dst[n], *src); n++; src++; } dst = xreallocarray(dst, n + 1, sizeof *dst); dst[n].size = 0; return (dst); }
/* Calculate string length. */ size_t printflike2 screen_write_strlen(int utf8flag, const char *fmt, ...) { va_list ap; char *msg; struct utf8_data utf8data; u_char *ptr; size_t left, size = 0; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); va_end(ap); ptr = msg; while (*ptr != '\0') { if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) { ptr++; left = strlen(ptr); if (left < utf8data.size - 1) break; while (utf8_append(&utf8data, *ptr)) ptr++; ptr++; size += utf8data.width; } else { size++; ptr++; } } free(msg); return (size); }
/* * Encode len characters from src into dst, which is guaranteed to have four * bytes available for each character from src (for \abc or UTF-8) plus space * for \0. */ int utf8_strvis(char *dst, const char *src, size_t len, int flag) { struct utf8_data ud; const char *start, *end; enum utf8_state more; size_t i; start = dst; end = src + len; while (src < end) { if ((more = utf8_open(&ud, *src)) == UTF8_MORE) { while (++src < end && more == UTF8_MORE) more = utf8_append(&ud, *src); if (more == UTF8_DONE) { /* UTF-8 character finished. */ for (i = 0; i < ud.size; i++) *dst++ = ud.data[i]; continue; } /* Not a complete, valid UTF-8 character. */ src -= ud.have; } if (src < end - 1) dst = vis(dst, src[0], flag, src[1]); else if (src < end) dst = vis(dst, src[0], flag, '\0'); src++; } *dst = '\0'; return (dst - start); }
/* Append to UTF-8 character. */ int input_utf8_add(struct input_ctx *ictx) { log_debug("%s", __func__); utf8_append(&ictx->utf8data, ictx->ch); return (0); }
void screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct grid_cell *gc, const char *fmt, va_list ap) { char *msg; struct utf8_data ud; u_char *ptr; size_t left, size = 0; enum utf8_state more; xvasprintf(&msg, fmt, ap); ptr = msg; while (*ptr != '\0') { if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); if (left < (size_t)ud.size - 1) break; while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) ptr++; ptr++; if (more == UTF8_DONE) { if (maxlen > 0 && size + ud.width > (size_t) maxlen) { while (size < (size_t) maxlen) { screen_write_putc(ctx, gc, ' '); size++; } break; } size += ud.width; utf8_copy(&gc->data, &ud); screen_write_cell(ctx, gc); } } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) break; if (*ptr == '\001') gc->attr ^= GRID_ATTR_CHARSET; else if (*ptr > 0x1f && *ptr < 0x7f) { size++; screen_write_putc(ctx, gc, *ptr); } ptr++; } } free(msg); }
void screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, const struct grid_cell *gcp, const char *fmt, va_list ap) { struct grid_cell gc; struct utf8_data *ud = &gc.data; char *msg; u_char *ptr; size_t left, size = 0; enum utf8_state more; memcpy(&gc, gcp, sizeof gc); xvasprintf(&msg, fmt, ap); ptr = msg; while (*ptr != '\0') { if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); if (left < (size_t)ud->size - 1) break; while ((more = utf8_append(ud, *ptr)) == UTF8_MORE) ptr++; ptr++; if (more != UTF8_DONE) continue; if (maxlen > 0 && size + ud->width > (size_t)maxlen) { while (size < (size_t)maxlen) { screen_write_putc(ctx, &gc, ' '); size++; } break; } size += ud->width; screen_write_cell(ctx, &gc); } else { if (maxlen > 0 && size + 1 > (size_t)maxlen) break; if (*ptr == '\001') gc.attr ^= GRID_ATTR_CHARSET; else if (*ptr > 0x1f && *ptr < 0x7f) { size++; screen_write_putc(ctx, &gc, *ptr); } ptr++; } } free(msg); }
/* Close UTF-8 string. */ int input_utf8_close(struct input_ctx *ictx) { log_debug("%s", __func__); utf8_append(&ictx->utf8data, ictx->ch); grid_cell_set(&ictx->cell, &ictx->utf8data); screen_write_cell(&ictx->ctx, &ictx->cell); return (0); }
void kitten_putc(Boxed stack, Boxed definitions) { trace("kitten_putc()\n"); assert(stack); assert(is_quotation(stack)); assert(is_integer(top(stack))); Boxed a = pop(stack); Integer character = integer_unbox(a); assert(character >= 0 && character <= UINT32_MAX); char buffer[5] = { 0 }; utf8_append(character, (uint8_t*)buffer); printf("%s", buffer); }
void screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, va_list ap) { char *msg; struct utf8_data utf8data; u_char *ptr; size_t left, size = 0; xvasprintf(&msg, fmt, ap); ptr = msg; while (*ptr != '\0') { if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) { ptr++; left = strlen(ptr); if (left < utf8data.size - 1) break; while (utf8_append(&utf8data, *ptr)) ptr++; ptr++; if (maxlen > 0 && size + utf8data.width > (size_t) maxlen) { while (size < (size_t) maxlen) { screen_write_putc(ctx, gc, ' '); size++; } break; } size += utf8data.width; gc->flags |= GRID_FLAG_UTF8; screen_write_cell(ctx, gc, &utf8data); gc->flags &= ~GRID_FLAG_UTF8; } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) break; if (*ptr == '\001') gc->attr ^= GRID_ATTR_CHARSET; else { size++; screen_write_putc(ctx, gc, *ptr); } ptr++; } } free(msg); }
/* Close UTF-8 string. */ int input_utf8_close(struct input_ctx *ictx) { log_debug("%s", __func__); utf8_append(&ictx->utf8data, ictx->ch); ictx->cell.flags |= GRID_FLAG_UTF8; screen_write_cell(&ictx->ctx, &ictx->cell, &ictx->utf8data); ictx->cell.flags &= ~GRID_FLAG_UTF8; return (0); }
/* Look up part of the next key. */ static int tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key, size_t *size, int expired) { struct tty_key *tk, *tk1; struct utf8_data ud; enum utf8_state more; u_int i; wchar_t wc; log_debug("next key is %zu (%.*s) (expired=%d)", len, (int)len, buf, expired); /* Is this a known key? */ tk = tty_keys_find(tty, buf, len, size); if (tk != NULL && tk->key != KEYC_UNKNOWN) { tk1 = tk; do log_debug("keys in list: %#llx", tk1->key); while ((tk1 = tk1->next) != NULL); if (tk->next != NULL && !expired) return (1); *key = tk->key; return (0); } /* Is this valid UTF-8? */ more = utf8_open(&ud, (u_char)*buf); if (more == UTF8_MORE) { *size = ud.size; if (len < ud.size) { if (!expired) return (1); return (-1); } for (i = 1; i < ud.size; i++) more = utf8_append(&ud, (u_char)buf[i]); if (more != UTF8_DONE) return (-1); if (utf8_combine(&ud, &wc) != UTF8_DONE) return (-1); *key = wc; log_debug("UTF-8 key %.*s %#llx", (int)ud.size, buf, *key); return (0); } return (-1); }
/* * Open UTF-8 sequence. * * 11000010-11011111 C2-DF start of 2-byte sequence * 11100000-11101111 E0-EF start of 3-byte sequence * 11110000-11110100 F0-F4 start of 4-byte sequence */ enum utf8_state utf8_open(struct utf8_data *ud, u_char ch) { memset(ud, 0, sizeof *ud); if (ch >= 0xc2 && ch <= 0xdf) ud->size = 2; else if (ch >= 0xe0 && ch <= 0xef) ud->size = 3; else if (ch >= 0xf0 && ch <= 0xf4) ud->size = 4; else return (UTF8_ERROR); utf8_append(ud, ch); return (UTF8_MORE); }
/* * Open UTF-8 sequence. * * 11000010-11011111 C2-DF start of 2-byte sequence * 11100000-11101111 E0-EF start of 3-byte sequence * 11110000-11110100 F0-F4 start of 4-byte sequence * * Returns 1 if more UTF-8 to come, 0 if not UTF-8. */ int utf8_open(struct utf8_data *utf8data, u_char ch) { memset(utf8data, 0, sizeof *utf8data); if (ch >= 0xc2 && ch <= 0xdf) utf8data->size = 2; else if (ch >= 0xe0 && ch <= 0xef) utf8data->size = 3; else if (ch >= 0xf0 && ch <= 0xf4) utf8data->size = 4; else return (0); utf8_append(utf8data, ch); return (1); }
/* Calculate string length. */ size_t screen_write_strlen(const char *fmt, ...) { va_list ap; char *msg; struct utf8_data ud; u_char *ptr; size_t left, size = 0; enum utf8_state more; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); va_end(ap); ptr = msg; while (*ptr != '\0') { if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); if (left < (size_t)ud.size - 1) break; while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) ptr++; ptr++; if (more == UTF8_DONE) size += ud.width; } else { if (*ptr > 0x1f && *ptr < 0x7f) size++; ptr++; } } free(msg); return (size); }
/* * Sanitize a string, changing any UTF-8 characters to '_'. Caller should free * the returned string. Anything not valid printable ASCII or UTF-8 is * stripped. */ char * utf8_sanitize(const char *src) { char *dst; size_t n; enum utf8_state more; struct utf8_data ud; u_int i; dst = NULL; n = 0; while (*src != '\0') { dst = xreallocarray(dst, n + 1, sizeof *dst); if ((more = utf8_open(&ud, *src)) == UTF8_MORE) { while (*++src != '\0' && more == UTF8_MORE) more = utf8_append(&ud, *src); if (more == UTF8_DONE) { dst = xreallocarray(dst, n + ud.width, sizeof *dst); for (i = 0; i < ud.width; i++) dst[n++] = '_'; continue; } src -= ud.have; } if (*src > 0x1f && *src < 0x7f) dst[n++] = *src; else dst[n++] = '_'; src++; } dst = xreallocarray(dst, n + 1, sizeof *dst); dst[n] = '\0'; return (dst); }
/* Get width of UTF-8 string. */ u_int utf8_cstrwidth(const char *s) { struct utf8_data tmp; u_int width; enum utf8_state more; width = 0; while (*s != '\0') { if ((more = utf8_open(&tmp, *s)) == UTF8_MORE) { while (*++s != '\0' && more == UTF8_MORE) more = utf8_append(&tmp, *s); if (more == UTF8_DONE) { width += tmp.width; continue; } s -= tmp.have; } if (*s > 0x1f && *s != 0x7f) width++; s++; } return (width); }
/* Lookup a string and convert to a key value. */ key_code key_string_lookup_string(const char *string) { static const char *other = "!#()+,-.0123456789:;<=>?'\r\t"; key_code key; u_short u; int size; key_code modifiers; struct utf8_data ud; u_int i; enum utf8_state more; wchar_t wc; /* Is this no key? */ if (strcasecmp(string, "None") == 0) return (KEYC_NONE); /* Is this a hexadecimal value? */ if (string[0] == '0' && string[1] == 'x') { if (sscanf(string + 2, "%hx%n", &u, &size) != 1 || size > 4) return (KEYC_UNKNOWN); return (u); } /* Check for modifiers. */ modifiers = 0; if (string[0] == '^' && string[1] != '\0') { modifiers |= KEYC_CTRL; string++; } modifiers |= key_string_get_modifiers(&string); if (string[0] == '\0') return (KEYC_UNKNOWN); /* Is this a standard ASCII key? */ if (string[1] == '\0' && (u_char)string[0] <= 127) { key = (u_char)string[0]; if (key < 32 || key == 127) return (KEYC_UNKNOWN); } else { /* Try as a UTF-8 key. */ if ((more = utf8_open(&ud, (u_char)*string)) == UTF8_MORE) { if (strlen(string) != ud.size) return (KEYC_UNKNOWN); for (i = 1; i < ud.size; i++) more = utf8_append(&ud, (u_char)string[i]); if (more != UTF8_DONE) return (KEYC_UNKNOWN); if (utf8_combine(&ud, &wc) != UTF8_DONE) return (KEYC_UNKNOWN); return (wc | modifiers); } /* Otherwise look the key up in the table. */ key = key_string_search_table(string); if (key == KEYC_UNKNOWN) return (KEYC_UNKNOWN); } /* Convert the standard control keys. */ if (key < KEYC_BASE && (modifiers & KEYC_CTRL) && !strchr(other, key)) { if (key >= 97 && key <= 122) key -= 96; else if (key >= 64 && key <= 95) key -= 64; else if (key == 32) key = 0; else if (key == 63) key = KEYC_BSPACE; else return (KEYC_UNKNOWN); modifiers &= ~KEYC_CTRL; } return (key | modifiers); }
/* * Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial * (probably a mouse sequence but need more data). */ int tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) { struct mouse_event *m = &tty->mouse; struct utf8_data utf8data; u_int i, value; /* * Standard mouse sequences are \033[M followed by three characters * indicating buttons, X and Y, all based at 32 with 1,1 top-left. * * UTF-8 mouse sequences are similar but the three are expressed as * UTF-8 characters. */ *size = 0; /* First three bytes are always \033[M. */ if (buf[0] != '\033') return (-1); if (len == 1) return (1); if (buf[1] != '[') return (-1); if (len == 2) return (1); if (buf[2] != 'M') return (-1); if (len == 3) return (1); /* Read the three inputs. */ *size = 3; for (i = 0; i < 3; i++) { if (len < *size) return (1); if (tty->mode & MODE_MOUSE_UTF8) { if (utf8_open(&utf8data, buf[*size])) { if (utf8data.size != 2) return (-1); (*size)++; if (len < *size) return (1); utf8_append(&utf8data, buf[*size]); value = utf8_combine(&utf8data); } else value = (unsigned char)buf[*size]; (*size)++; } else { value = (unsigned char)buf[*size]; (*size)++; } if (i == 0) m->b = value; else if (i == 1) m->x = value; else m->y = value; } log_debug("mouse input: %.*s", (int) *size, buf); /* Check and return the mouse input. */ if (m->b < 32 || m->x < 33 || m->y < 33) return (-1); m->b -= 32; m->x -= 33; m->y -= 33; log_debug("mouse position: x=%u y=%u b=%u", m->x, m->y, m->b); return (0); }
/* * Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial * (probably a mouse sequence but need more data). */ int tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) { struct mouse_event *m = &tty->mouse; struct utf8_data ud; u_int i, value, x, y, b, sgr_b; u_char sgr_type, c; enum utf8_state more; /* * Standard mouse sequences are \033[M followed by three characters * indicating button, X and Y, all based at 32 with 1,1 top-left. * * UTF-8 mouse sequences are similar but the three are expressed as * UTF-8 characters. * * SGR extended mouse sequences are \033[< followed by three numbers in * decimal and separated by semicolons indicating button, X and Y. A * trailing 'M' is click or scroll and trailing 'm' release. All are * based at 0 with 1,1 top-left. */ *size = 0; x = y = b = sgr_b = 0; sgr_type = ' '; /* First two bytes are always \033[. */ if (buf[0] != '\033') return (-1); if (len == 1) return (1); if (buf[1] != '[') return (-1); if (len == 2) return (1); /* * Third byte is M in old standard and UTF-8 extension, < in SGR * extension. */ if (buf[2] == 'M') { /* Read the three inputs. */ *size = 3; for (i = 0; i < 3; i++) { if (len <= *size) return (1); if (tty->mode & MODE_MOUSE_UTF8) { if (utf8_open(&ud, buf[*size]) == UTF8_MORE) { if (ud.size != 2) return (-1); (*size)++; if (len <= *size) return (1); more = utf8_append(&ud, buf[*size]); if (more != UTF8_DONE) return (-1); value = utf8_combine(&ud); } else value = (u_char)buf[*size]; (*size)++; } else { value = (u_char)buf[*size]; (*size)++; } if (i == 0) b = value; else if (i == 1) x = value; else y = value; } log_debug("mouse input: %.*s", (int)*size, buf); /* Check and return the mouse input. */ if (b < 32) return (-1); b -= 32; if (x >= 33) x -= 33; else x = 256 - x; if (y >= 33) y -= 33; else y = 256 - y; } else if (buf[2] == '<') { /* Read the three inputs. */ *size = 3; while (1) { if (len <= *size) return (1); c = (u_char)buf[(*size)++]; if (c == ';') break; if (c < '0' || c > '9') return (-1); sgr_b = 10 * sgr_b + (c - '0'); } while (1) { if (len <= *size) return (1); c = (u_char)buf[(*size)++]; if (c == ';') break; if (c < '0' || c > '9') return (-1); x = 10 * x + (c - '0'); } while (1) { if (len <= *size) return (1); c = (u_char)buf[(*size)++]; if (c == 'M' || c == 'm') break; if (c < '0' || c > '9') return (-1); y = 10 * y + (c - '0'); } log_debug("mouse input (SGR): %.*s", (int)*size, buf); /* Check and return the mouse input. */ if (x < 1 || y < 1) return (-1); x--; y--; b = sgr_b; /* Type is M for press, m for release. */ sgr_type = c; if (sgr_type == 'm') b |= 3; /* * Some terminals (like PuTTY 0.63) mistakenly send * button-release events for scroll-wheel button-press event. * Discard it before it reaches any program running inside * tmux. */ if (sgr_type == 'm' && (sgr_b & 64)) return (-2); } else return (-1); /* Fill mouse event. */ m->lx = m->x; m->x = x; m->ly = m->y; m->y = y; m->lb = m->b; m->b = b; m->sgr_type = sgr_type; m->sgr_b = sgr_b; return (0); }
/* * Process at least one key in the buffer and invoke tty->key_callback. Return * 0 if there are no further keys, or 1 if there could be more in the buffer. */ key_code tty_keys_next(struct tty *tty) { struct tty_key *tk; struct timeval tv; const char *buf; size_t len, size; cc_t bspace; int delay, expired = 0; key_code key; struct utf8_data ud; enum utf8_state more; u_int i; /* Get key buffer. */ buf = EVBUFFER_DATA(tty->event->input); len = EVBUFFER_LENGTH(tty->event->input); if (len == 0) return (0); log_debug("keys are %zu (%.*s)", len, (int) len, buf); /* Is this a mouse key press? */ switch (tty_keys_mouse(tty, buf, len, &size)) { case 0: /* yes */ key = KEYC_MOUSE; goto complete_key; case -1: /* no, or not valid */ break; case -2: /* yes, but we don't care. */ key = KEYC_MOUSE; goto discard_key; case 1: /* partial */ goto partial_key; } /* Look for matching key string and return if found. */ tk = tty_keys_find(tty, buf, len, &size); if (tk != NULL) { if (tk->next != NULL) goto partial_key; key = tk->key; goto complete_key; } /* Try to parse a key with an xterm-style modifier. */ switch (xterm_keys_find(buf, len, &size, &key)) { case 0: /* found */ goto complete_key; case -1: /* not found */ break; case 1: goto partial_key; } first_key: /* Is this a meta key? */ if (len >= 2 && buf[0] == '\033') { if (buf[1] != '\033') { key = buf[1] | KEYC_ESCAPE; size = 2; goto complete_key; } tk = tty_keys_find(tty, buf + 1, len - 1, &size); if (tk != NULL && (!expired || tk->next == NULL)) { size++; /* include escape */ if (tk->next != NULL) goto partial_key; key = tk->key; if (key != KEYC_NONE) key |= KEYC_ESCAPE; goto complete_key; } } /* Is this valid UTF-8? */ if ((more = utf8_open(&ud, (u_char)*buf) == UTF8_MORE)) { size = ud.size; if (len < size) { if (expired) goto discard_key; goto partial_key; } for (i = 1; i < size; i++) more = utf8_append(&ud, (u_char)buf[i]); if (more != UTF8_DONE) goto discard_key; key = utf8_combine(&ud); log_debug("UTF-8 key %.*s %#llx", (int)size, buf, key); goto complete_key; } /* No key found, take first. */ key = (u_char)*buf; size = 1; /* * Check for backspace key using termios VERASE - the terminfo * kbs entry is extremely unreliable, so cannot be safely * used. termios should have a better idea. */ bspace = tty->tio.c_cc[VERASE]; if (bspace != _POSIX_VDISABLE && key == bspace) key = KEYC_BSPACE; goto complete_key; partial_key: log_debug("partial key %.*s", (int) len, buf); /* If timer is going, check for expiration. */ if (tty->flags & TTY_TIMER) { if (evtimer_initialized(&tty->key_timer) && !evtimer_pending(&tty->key_timer, NULL)) { expired = 1; goto first_key; } return (0); } /* Get the time period. */ delay = options_get_number(global_options, "escape-time"); tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; /* Start the timer. */ if (event_initialized(&tty->key_timer)) evtimer_del(&tty->key_timer); evtimer_set(&tty->key_timer, tty_keys_callback, tty); evtimer_add(&tty->key_timer, &tv); tty->flags |= TTY_TIMER; return (0); complete_key: log_debug("complete key %.*s %#llx", (int)size, buf, key); /* Remove data from buffer. */ evbuffer_drain(tty->event->input, size); /* Remove key timer. */ if (event_initialized(&tty->key_timer)) evtimer_del(&tty->key_timer); tty->flags &= ~TTY_TIMER; /* Check for focus events. */ if (key == KEYC_FOCUS_OUT) { tty->client->flags &= ~CLIENT_FOCUSED; return (1); } else if (key == KEYC_FOCUS_IN) { tty->client->flags |= CLIENT_FOCUSED; return (1); } /* Fire the key. */ if (key != KEYC_NONE) server_client_handle_key(tty->client, key); return (1); discard_key: log_debug("discard key %.*s %#llx", (int)size, buf, key); /* Remove data from buffer. */ evbuffer_drain(tty->event->input, size); return (1); }
/* * Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial * (probably a mouse sequence but need more data). */ int tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) { struct mouse_event *m = &tty->mouse; struct utf8_data utf8data; u_int i, value, x, y, b; /* * Standard mouse sequences are \033[M followed by three characters * indicating buttons, X and Y, all based at 32 with 1,1 top-left. * * UTF-8 mouse sequences are similar but the three are expressed as * UTF-8 characters. */ *size = 0; x = y = b = 0; /* First three bytes are always \033[M. */ if (buf[0] != '\033') return (-1); if (len == 1) return (1); if (buf[1] != '[') return (-1); if (len == 2) return (1); if (buf[2] != 'M') return (-1); if (len == 3) return (1); /* Read the three inputs. */ *size = 3; for (i = 0; i < 3; i++) { if (len < *size) return (1); if (tty->mode & MODE_MOUSE_UTF8) { if (utf8_open(&utf8data, buf[*size])) { if (utf8data.size != 2) return (-1); (*size)++; if (len < *size) return (1); utf8_append(&utf8data, buf[*size]); value = utf8_combine(&utf8data); } else value = (unsigned char)buf[*size]; (*size)++; } else { value = (unsigned char)buf[*size]; (*size)++; } if (i == 0) b = value; else if (i == 1) x = value; else y = value; } log_debug("mouse input: %.*s", (int) *size, buf); /* Check and return the mouse input. */ if (b < 32 || x < 33 || y < 33) return (-1); b -= 32; x -= 33; y -= 33; log_debug("mouse position: x=%u y=%u b=%u", x, y, b); /* Fill in mouse structure. */ if (~m->event & MOUSE_EVENT_WHEEL) { m->lx = m->x; m->ly = m->y; } m->xb = b; if (b & 64) { /* wheel button */ b &= 3; if (b == 0) m->wheel = MOUSE_WHEEL_UP; else if (b == 1) m->wheel = MOUSE_WHEEL_DOWN; m->event = MOUSE_EVENT_WHEEL; } else if ((b & 3) == 3) { if (~m->event & MOUSE_EVENT_DRAG && x == m->x && y == m->y) { m->event = MOUSE_EVENT_CLICK; } else m->event = MOUSE_EVENT_DRAG; m->event |= MOUSE_EVENT_UP; } else { if (b & 32) /* drag motion */ m->event = MOUSE_EVENT_DRAG; else { if (m->event & MOUSE_EVENT_UP && x == m->x && y == m->y) m->clicks = (m->clicks + 1) % 3; else m->clicks = 0; m->sx = x; m->sy = y; m->event = MOUSE_EVENT_DOWN; } m->button = (b & 3); } m->x = x; m->y = y; return (0); }
/* Write string, similar to nputs, but with embedded formatting (#[]). */ void printflike5 screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, ...) { struct grid_cell lgc; struct utf8_data utf8data; va_list ap; char *msg; u_char *ptr, *last; size_t left, size = 0; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); va_end(ap); memcpy(&lgc, gc, sizeof lgc); ptr = msg; while (*ptr != '\0') { if (ptr[0] == '#' && ptr[1] == '[') { ptr += 2; last = ptr + strcspn(ptr, "]"); if (*last == '\0') { /* No ]. Not much point in doing anything. */ break; } *last = '\0'; screen_write_parsestyle(gc, &lgc, ptr); ptr = last + 1; continue; } if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) { ptr++; left = strlen(ptr); if (left < utf8data.size - 1) break; while (utf8_append(&utf8data, *ptr)) ptr++; ptr++; if (maxlen > 0 && size + utf8data.width > (size_t) maxlen) { while (size < (size_t) maxlen) { screen_write_putc(ctx, gc, ' '); size++; } break; } size += utf8data.width; lgc.flags |= GRID_FLAG_UTF8; screen_write_cell(ctx, &lgc, &utf8data); lgc.flags &= ~GRID_FLAG_UTF8; } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) break; size++; screen_write_putc(ctx, &lgc, *ptr); ptr++; } } free(msg); }
/* Write string, similar to nputs, but with embedded formatting (#[]). */ void screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, const struct grid_cell *gcp, const char *fmt, ...) { struct grid_cell gc; struct utf8_data *ud = &gc.data; va_list ap; char *msg; u_char *ptr, *last; size_t left, size = 0; enum utf8_state more; memcpy(&gc, gcp, sizeof gc); va_start(ap, fmt); xvasprintf(&msg, fmt, ap); va_end(ap); ptr = msg; while (*ptr != '\0') { if (ptr[0] == '#' && ptr[1] == '[') { ptr += 2; last = ptr + strcspn(ptr, "]"); if (*last == '\0') { /* No ]. Not much point in doing anything. */ break; } *last = '\0'; style_parse(gcp, &gc, ptr); ptr = last + 1; continue; } if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); if (left < (size_t)ud->size - 1) break; while ((more = utf8_append(ud, *ptr)) == UTF8_MORE) ptr++; ptr++; if (more != UTF8_DONE) continue; if (maxlen > 0 && size + ud->width > (size_t)maxlen) { while (size < (size_t)maxlen) { screen_write_putc(ctx, &gc, ' '); size++; } break; } size += ud->width; screen_write_cell(ctx, &gc); } else { if (maxlen > 0 && size + 1 > (size_t)maxlen) break; if (*ptr > 0x1f && *ptr < 0x7f) { size++; screen_write_putc(ctx, &gc, *ptr); } ptr++; } } free(msg); }
/* * Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial * (probably a mouse sequence but need more data). */ int tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) { struct mouse_event *m = &tty->mouse; struct utf8_data utf8data; u_int i, value, x, y, b, sgr, sgr_b, sgr_rel; unsigned char c; /* * Standard mouse sequences are \033[M followed by three characters * indicating button, X and Y, all based at 32 with 1,1 top-left. * * UTF-8 mouse sequences are similar but the three are expressed as * UTF-8 characters. * * SGR extended mouse sequences are \033[< followed by three numbers in * decimal and separated by semicolons indicating button, X and Y. A * trailing 'M' is click or scroll and trailing 'm' release. All are * based at 0 with 1,1 top-left. */ *size = 0; x = y = b = sgr = sgr_b = sgr_rel = 0; /* First two bytes are always \033[. */ if (buf[0] != '\033') return (-1); if (len == 1) return (1); if (buf[1] != '[') return (-1); if (len == 2) return (1); /* * Third byte is M in old standard and UTF-8 extension, < in SGR * extension. */ if (buf[2] == 'M') { /* Read the three inputs. */ *size = 3; for (i = 0; i < 3; i++) { if (len <= *size) return (1); if (tty->mode & MODE_MOUSE_UTF8) { if (utf8_open(&utf8data, buf[*size])) { if (utf8data.size != 2) return (-1); (*size)++; if (len <= *size) return (1); utf8_append(&utf8data, buf[*size]); value = utf8_combine(&utf8data); } else value = (u_char) buf[*size]; (*size)++; } else { value = (u_char) buf[*size]; (*size)++; } if (i == 0) b = value; else if (i == 1) x = value; else y = value; } log_debug("mouse input: %.*s", (int) *size, buf); /* Check and return the mouse input. */ if (b < 32 || x < 33 || y < 33) return (-1); b -= 32; x -= 33; y -= 33; } else if (buf[2] == '<') { /* Read the three inputs. */ *size = 3; while (1) { if (len <= *size) return (1); c = (u_char)buf[(*size)++]; if (c == ';') break; if (c < '0' || c > '9') return (-1); sgr_b = 10 * sgr_b + (c - '0'); } while (1) { if (len <= *size) return (1); c = (u_char)buf[(*size)++]; if (c == ';') break; if (c < '0' || c > '9') return (-1); x = 10 * x + (c - '0'); } while (1) { if (len <= *size) return (1); c = (u_char) buf[(*size)++]; if (c == 'M' || c == 'm') break; if (c < '0' || c > '9') return (-1); y = 10 * y + (c - '0'); } log_debug("mouse input (sgr): %.*s", (int) *size, buf); /* Check and return the mouse input. */ if (x < 1 || y < 1) return (-1); x--; y--; sgr = 1; sgr_rel = (c == 'm'); /* Figure out what b would be in old format. */ b = sgr_b; if (sgr_rel) b |= 3; } else return (-1); /* Fill in mouse structure. */ if (~m->event & MOUSE_EVENT_WHEEL) { m->lx = m->x; m->ly = m->y; } m->xb = b; m->sgr = sgr; m->sgr_xb = sgr_b; m->sgr_rel = sgr_rel; if (b & 64) { /* wheel button */ b &= 3; if (b == 0) m->wheel = MOUSE_WHEEL_UP; else if (b == 1) m->wheel = MOUSE_WHEEL_DOWN; m->event = MOUSE_EVENT_WHEEL; } else if ((b & 3) == 3) { if (~m->event & MOUSE_EVENT_DRAG && x == m->x && y == m->y) { m->event = MOUSE_EVENT_CLICK; } else m->event = MOUSE_EVENT_DRAG; m->event |= MOUSE_EVENT_UP; } else { if (b & 32) /* drag motion */ m->event = MOUSE_EVENT_DRAG; else { if (m->event & MOUSE_EVENT_UP && x == m->x && y == m->y) m->clicks = (m->clicks + 1) % 3; else m->clicks = 0; m->sx = x; m->sy = y; m->event = MOUSE_EVENT_DOWN; } m->button = (b & 3); } m->x = x; m->y = y; return (0); }