static void output_mouse(VTermState *state, int code, int pressed, int modifiers, int col, int row) { modifiers <<= 2; switch(state->mouse_protocol) { case MOUSE_X10: if(col + 0x21 > 0xff) col = 0xff - 0x21; if(row + 0x21 > 0xff) row = 0xff - 0x21; if(!pressed) code = 3; vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%c%c%c", (code | modifiers) + 0x20, col + 0x21, row + 0x21); break; case MOUSE_UTF8: { char utf8[18]; size_t len = 0; if(!pressed) code = 3; len += fill_utf8((code | modifiers) + 0x20, utf8 + len); len += fill_utf8(col + 0x21, utf8 + len); len += fill_utf8(row + 0x21, utf8 + len); utf8[len] = 0; vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%s", utf8); } break; case MOUSE_SGR: vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "<%d;%d;%d%c", code | modifiers, col + 1, row + 1, pressed ? 'M' : 'm'); break; case MOUSE_RXVT: if(!pressed) code = 3; vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%d;%d;%dM", code | modifiers, col + 1, row + 1); break; } }
void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod) { int needs_CSIu; /* The shift modifier is never important for Unicode characters * apart from Space */ if(c != ' ') mod &= ~VTERM_MOD_SHIFT; if(mod == 0) { /* Normal text - ignore just shift */ char str[6]; int seqlen = fill_utf8(c, str); vterm_push_output_bytes(vt, str, seqlen); return; } switch(c) { /* Special Ctrl- letters that can't be represented elsewise */ case 'i': case 'j': case 'm': case '[': needs_CSIu = 1; break; /* Ctrl-\ ] ^ _ don't need CSUu */ case '\\': case ']': case '^': case '_': needs_CSIu = 0; break; /* Shift-space needs CSIu */ case ' ': needs_CSIu = !!(mod & VTERM_MOD_SHIFT); break; /* All other characters needs CSIu except for letters a-z */ default: needs_CSIu = (c < 'a' || c > 'z'); } /* ALT we can just prefix with ESC; anything else requires CSI u */ if(needs_CSIu && (mod & ~VTERM_MOD_ALT)) { vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", c, mod+1); return; } if(mod & VTERM_MOD_CTRL) c &= 0x1f; vterm_push_output_sprintf(vt, "%s%c", mod & VTERM_MOD_ALT ? ESC_S : "", c); }
void termkey_canonicalise(TermKey *tk, TermKeyKey *key) { int flags = tk->canonflags; if(flags & TERMKEY_CANON_SPACESYMBOL) { if(key->type == TERMKEY_TYPE_UNICODE && key->code.codepoint == 0x20) { key->type = TERMKEY_TYPE_KEYSYM; key->code.sym = TERMKEY_SYM_SPACE; } } else { if(key->type == TERMKEY_TYPE_KEYSYM && key->code.sym == TERMKEY_SYM_SPACE) { key->type = TERMKEY_TYPE_UNICODE; key->code.codepoint = 0x20; fill_utf8(key); } } if(flags & TERMKEY_CANON_DELBS) { if(key->type == TERMKEY_TYPE_KEYSYM && key->code.sym == TERMKEY_SYM_DEL) { key->code.sym = TERMKEY_SYM_BACKSPACE; } } }
static void emit_codepoint(TermKey *tk, long codepoint, TermKeyKey *key) { if(codepoint == 0) { // ASCII NUL = Ctrl-Space key->type = TERMKEY_TYPE_KEYSYM; key->code.sym = TERMKEY_SYM_SPACE; key->modifiers = TERMKEY_KEYMOD_CTRL; } else if(codepoint < 0x20) { // C0 range key->code.codepoint = 0; key->modifiers = 0; if(!(tk->flags & TERMKEY_FLAG_NOINTERPRET) && tk->c0[codepoint].sym != TERMKEY_SYM_UNKNOWN) { key->code.sym = tk->c0[codepoint].sym; key->modifiers |= tk->c0[codepoint].modifier_set; } if(!key->code.sym) { key->type = TERMKEY_TYPE_UNICODE; /* Generically modified Unicode ought not report the SHIFT state, or else * we get into complicationg trying to report Shift-; vs : and so on... * In order to be able to represent Ctrl-Shift-A as CTRL modified * unicode A, we need to call Ctrl-A simply 'a', lowercase */ if(codepoint+0x40 >= 'A' && codepoint+0x40 <= 'Z') // it's a letter - use lowecase instead key->code.codepoint = codepoint + 0x60; else key->code.codepoint = codepoint + 0x40; key->modifiers = TERMKEY_KEYMOD_CTRL; } else { key->type = TERMKEY_TYPE_KEYSYM; } } else if(codepoint == 0x7f && !(tk->flags & TERMKEY_FLAG_NOINTERPRET)) { // ASCII DEL key->type = TERMKEY_TYPE_KEYSYM; key->code.sym = TERMKEY_SYM_DEL; key->modifiers = 0; } else if(codepoint >= 0x20 && codepoint < 0x80) { // ASCII lowbyte range key->type = TERMKEY_TYPE_UNICODE; key->code.codepoint = codepoint; key->modifiers = 0; } else if(codepoint >= 0x80 && codepoint < 0xa0) { // UTF-8 never starts with a C1 byte. So we can be sure of these key->type = TERMKEY_TYPE_UNICODE; key->code.codepoint = codepoint - 0x40; key->modifiers = TERMKEY_KEYMOD_CTRL|TERMKEY_KEYMOD_ALT; } else { // UTF-8 codepoint key->type = TERMKEY_TYPE_UNICODE; key->code.codepoint = codepoint; key->modifiers = 0; } termkey_canonicalise(tk, key); if(key->type == TERMKEY_TYPE_UNICODE) fill_utf8(key); }
const char *termkey_strpkey(TermKey *tk, const char *str, TermKeyKey *key, TermKeyFormat format) { struct modnames *mods = &modnames[!!(format & TERMKEY_FORMAT_LONGMOD) + !!(format & TERMKEY_FORMAT_ALTISMETA) * 2 + !!(format & TERMKEY_FORMAT_LOWERMOD) * 4]; key->modifiers = 0; if((format & TERMKEY_FORMAT_CARETCTRL) && str[0] == '^' && str[1]) { str = termkey_strpkey(tk, str+1, key, format & ~TERMKEY_FORMAT_CARETCTRL); if(!str || key->type != TERMKEY_TYPE_UNICODE || key->code.codepoint < '@' || key->code.codepoint > '_' || key->modifiers != 0) return NULL; if(key->code.codepoint >= 'A' && key->code.codepoint <= 'Z') key->code.codepoint += 0x20; key->modifiers = TERMKEY_KEYMOD_CTRL; fill_utf8(key); return (char *)str; } const char *sep_at; while((sep_at = strchr(str, (format & TERMKEY_FORMAT_SPACEMOD) ? ' ' : '-'))) { size_t n = sep_at - str; if(n == strlen(mods->alt) && strncmp(mods->alt, str, n) == 0) key->modifiers |= TERMKEY_KEYMOD_ALT; else if(n == strlen(mods->ctrl) && strncmp(mods->ctrl, str, n) == 0) key->modifiers |= TERMKEY_KEYMOD_CTRL; else if(n == strlen(mods->shift) && strncmp(mods->shift, str, n) == 0) key->modifiers |= TERMKEY_KEYMOD_SHIFT; else break; str = sep_at + 1; } size_t nbytes; ssize_t snbytes; const char *endstr; if((endstr = termkey_lookup_keyname_format(tk, str, &key->code.sym, format))) { key->type = TERMKEY_TYPE_KEYSYM; str = endstr; } else if(sscanf(str, "F%d%zn", &key->code.number, &snbytes) == 1) { key->type = TERMKEY_TYPE_FUNCTION; str += snbytes; } // Unicode must be last else if(parse_utf8((unsigned const char *)str, strlen(str), &key->code.codepoint, &nbytes) == TERMKEY_RES_KEY) { key->type = TERMKEY_TYPE_UNICODE; fill_utf8(key); str += nbytes; } // TODO: Consider mouse events? else return NULL; termkey_canonicalise(tk, key); return (char *)str; }
size_t termkey_strfkey(TermKey *tk, char *buffer, size_t len, TermKeyKey *key, TermKeyFormat format) { size_t pos = 0; size_t l = 0; struct modnames *mods = &modnames[!!(format & TERMKEY_FORMAT_LONGMOD) + !!(format & TERMKEY_FORMAT_ALTISMETA) * 2 + !!(format & TERMKEY_FORMAT_LOWERMOD) * 4]; int wrapbracket = (format & TERMKEY_FORMAT_WRAPBRACKET) && (key->type != TERMKEY_TYPE_UNICODE || key->modifiers != 0); char sep = (format & TERMKEY_FORMAT_SPACEMOD) ? ' ' : '-'; if(format & TERMKEY_FORMAT_CARETCTRL && key->type == TERMKEY_TYPE_UNICODE && key->modifiers == TERMKEY_KEYMOD_CTRL) { long codepoint = key->code.codepoint; // Handle some of the special casesfirst if(codepoint >= 'a' && codepoint <= 'z') { l = snprintf(buffer + pos, len - pos, wrapbracket ? "<^%c>" : "^%c", (char)codepoint - 0x20); if(l <= 0) return pos; pos += l; return pos; } else if((codepoint >= '@' && codepoint < 'A') || (codepoint > 'Z' && codepoint <= '_')) { l = snprintf(buffer + pos, len - pos, wrapbracket ? "<^%c>" : "^%c", (char)codepoint); if(l <= 0) return pos; pos += l; return pos; } } if(wrapbracket) { l = snprintf(buffer + pos, len - pos, "<"); if(l <= 0) return pos; pos += l; } if(key->modifiers & TERMKEY_KEYMOD_ALT) { l = snprintf(buffer + pos, len - pos, "%s%c", mods->alt, sep); if(l <= 0) return pos; pos += l; } if(key->modifiers & TERMKEY_KEYMOD_CTRL) { l = snprintf(buffer + pos, len - pos, "%s%c", mods->ctrl, sep); if(l <= 0) return pos; pos += l; } if(key->modifiers & TERMKEY_KEYMOD_SHIFT) { l = snprintf(buffer + pos, len - pos, "%s%c", mods->shift, sep); if(l <= 0) return pos; pos += l; } switch(key->type) { case TERMKEY_TYPE_UNICODE: if(!key->utf8[0]) // In case of user-supplied key structures fill_utf8(key); l = snprintf(buffer + pos, len - pos, "%s", key->utf8); break; case TERMKEY_TYPE_KEYSYM: { const char *name = termkey_get_keyname(tk, key->code.sym); if(format & TERMKEY_FORMAT_LOWERSPACE) l = snprint_cameltospaces(buffer + pos, len - pos, name); else l = snprintf(buffer + pos, len - pos, "%s", name); } break; case TERMKEY_TYPE_FUNCTION: l = snprintf(buffer + pos, len - pos, "%c%d", (format & TERMKEY_FORMAT_LOWERSPACE ? 'f' : 'F'), key->code.number); break; case TERMKEY_TYPE_MOUSE: { TermKeyMouseEvent ev; int button; int line, col; termkey_interpret_mouse(tk, key, &ev, &button, &line, &col); static const char *evnames[] = { "Unknown", "Press", "Drag", "Release" }; l = snprintf(buffer + pos, len - pos, "Mouse%s(%d)", evnames[ev], button); if(format & TERMKEY_FORMAT_MOUSE_POS) { if(l <= 0) return pos; pos += l; l = snprintf(buffer + pos, len - pos, " @ (%u,%u)", col, line); } } break; case TERMKEY_TYPE_POSITION: l = snprintf(buffer + pos, len - pos, "Position"); break; case TERMKEY_TYPE_MODEREPORT: { int initial, mode, value; termkey_interpret_modereport(tk, key, &initial, &mode, &value); if(initial) l = snprintf(buffer + pos, len - pos, "Mode(%c%d=%d)", initial, mode, value); else l = snprintf(buffer + pos, len - pos, "Mode(%d=%d)", mode, value); } case TERMKEY_TYPE_UNKNOWN_CSI: l = snprintf(buffer + pos, len - pos, "CSI %c", key->code.number & 0xff); break; } if(l <= 0) return pos; pos += l; if(wrapbracket) { l = snprintf(buffer + pos, len - pos, ">"); if(l <= 0) return pos; pos += l; } return pos; }
static void dump_cell(const VTermScreenCell *cell, const VTermScreenCell *prevcell) { switch(format) { case FORMAT_PLAIN: break; case FORMAT_SGR: { // If all 7 attributes change, that means 7 SGRs max // Each colour could consume up to 3 int sgr[7 + 2*3]; int sgri = 0; if(!prevcell->attrs.bold && cell->attrs.bold) sgr[sgri++] = 1; if(prevcell->attrs.bold && !cell->attrs.bold) sgr[sgri++] = 22; if(!prevcell->attrs.underline && cell->attrs.underline) sgr[sgri++] = 4; if(prevcell->attrs.underline && !cell->attrs.underline) sgr[sgri++] = 24; if(!prevcell->attrs.italic && cell->attrs.italic) sgr[sgri++] = 3; if(prevcell->attrs.italic && !cell->attrs.italic) sgr[sgri++] = 23; if(!prevcell->attrs.blink && cell->attrs.blink) sgr[sgri++] = 5; if(prevcell->attrs.blink && !cell->attrs.blink) sgr[sgri++] = 25; if(!prevcell->attrs.reverse && cell->attrs.reverse) sgr[sgri++] = 7; if(prevcell->attrs.reverse && !cell->attrs.reverse) sgr[sgri++] = 27; if(!prevcell->attrs.strike && cell->attrs.strike) sgr[sgri++] = 9; if(prevcell->attrs.strike && !cell->attrs.strike) sgr[sgri++] = 29; if(!prevcell->attrs.font && cell->attrs.font) sgr[sgri++] = 10 + cell->attrs.font; if(prevcell->attrs.font && !cell->attrs.font) sgr[sgri++] = 10; if(prevcell->fg.red != cell->fg.red || prevcell->fg.green != cell->fg.green || prevcell->fg.blue != cell->fg.blue) { int index = col2index(cell->fg); if(index == -1) sgr[sgri++] = 39; else if(index < 8) sgr[sgri++] = 30 + index; else if(index < 16) sgr[sgri++] = 90 + (index - 8); else { sgr[sgri++] = 38; sgr[sgri++] = 5 | (1<<31); sgr[sgri++] = index | (1<<31); } } if(prevcell->bg.red != cell->bg.red || prevcell->bg.green != cell->bg.green || prevcell->bg.blue != cell->bg.blue) { int index = col2index(cell->bg); if(index == -1) sgr[sgri++] = 49; else if(index < 8) sgr[sgri++] = 40 + index; else if(index < 16) sgr[sgri++] = 100 + (index - 8); else { sgr[sgri++] = 48; sgr[sgri++] = 5 | (1<<31); sgr[sgri++] = index | (1<<31); } } if(!sgri) break; printf("\e["); for(int i = 0; i < sgri; i++) printf(!i ? "%d" : sgr[i] & (1<<31) ? ":%d" : ";%d", sgr[i] & ~(1<<31)); printf("m"); } break; } for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell->chars[i]; i++) { char bytes[6]; bytes[fill_utf8(cell->chars[i], bytes)] = 0; printf("%s", bytes); } }