aboutsummaryrefslogtreecommitdiff
path: root/src/vterm/state.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2025-02-05 23:09:29 +0000
committerJosh Rahm <joshuarahm@gmail.com>2025-02-05 23:09:29 +0000
commitd5f194ce780c95821a855aca3c19426576d28ae0 (patch)
treed45f461b19f9118ad2bb1f440a7a08973ad18832 /src/vterm/state.c
parentc5d770d311841ea5230426cc4c868e8db27300a8 (diff)
parent44740e561fc93afe3ebecfd3618bda2d2abeafb0 (diff)
downloadrneovim-rahm.tar.gz
rneovim-rahm.tar.bz2
rneovim-rahm.zip
Merge remote-tracking branch 'upstream/master' into mix_20240309HEADrahm
Diffstat (limited to 'src/vterm/state.c')
-rw-r--r--src/vterm/state.c2347
1 files changed, 0 insertions, 2347 deletions
diff --git a/src/vterm/state.c b/src/vterm/state.c
deleted file mode 100644
index e09b39436a..0000000000
--- a/src/vterm/state.c
+++ /dev/null
@@ -1,2347 +0,0 @@
-#include "vterm_internal.h"
-
-#include <stdio.h>
-#include <string.h>
-
-#define strneq(a,b,n) (strncmp(a,b,n)==0)
-
-#if defined(DEBUG) && DEBUG > 1
-# define DEBUG_GLYPH_COMBINE
-#endif
-
-/* Some convenient wrappers to make callback functions easier */
-
-static void putglyph(VTermState *state, const uint32_t chars[], int width, VTermPos pos)
-{
- VTermGlyphInfo info = {
- .chars = chars,
- .width = width,
- .protected_cell = state->protected_cell,
- .dwl = state->lineinfo[pos.row].doublewidth,
- .dhl = state->lineinfo[pos.row].doubleheight,
- };
-
- if(state->callbacks && state->callbacks->putglyph)
- if((*state->callbacks->putglyph)(&info, pos, state->cbdata))
- return;
-
- DEBUG_LOG("libvterm: Unhandled putglyph U+%04x at (%d,%d)\n", chars[0], pos.col, pos.row);
-}
-
-static void updatecursor(VTermState *state, VTermPos *oldpos, int cancel_phantom)
-{
- if(state->pos.col == oldpos->col && state->pos.row == oldpos->row)
- return;
-
- if(cancel_phantom)
- state->at_phantom = 0;
-
- if(state->callbacks && state->callbacks->movecursor)
- if((*state->callbacks->movecursor)(state->pos, *oldpos, state->mode.cursor_visible, state->cbdata))
- return;
-}
-
-static void erase(VTermState *state, VTermRect rect, int selective)
-{
- if(rect.end_col == state->cols) {
- /* If we're erasing the final cells of any lines, cancel the continuation
- * marker on the subsequent line
- */
- for(int row = rect.start_row + 1; row < rect.end_row + 1 && row < state->rows; row++)
- state->lineinfo[row].continuation = 0;
- }
-
- if(state->callbacks && state->callbacks->erase)
- if((*state->callbacks->erase)(rect, selective, state->cbdata))
- return;
-}
-
-static VTermState *vterm_state_new(VTerm *vt)
-{
- VTermState *state = vterm_allocator_malloc(vt, sizeof(VTermState));
-
- state->vt = vt;
-
- state->rows = vt->rows;
- state->cols = vt->cols;
-
- state->mouse_col = 0;
- state->mouse_row = 0;
- state->mouse_buttons = 0;
-
- state->mouse_protocol = MOUSE_X10;
-
- state->callbacks = NULL;
- state->cbdata = NULL;
-
- state->selection.callbacks = NULL;
- state->selection.user = NULL;
- state->selection.buffer = NULL;
-
- vterm_state_newpen(state);
-
- state->bold_is_highbright = 0;
-
- state->combine_chars_size = 16;
- state->combine_chars = vterm_allocator_malloc(state->vt, state->combine_chars_size * sizeof(state->combine_chars[0]));
-
- state->tabstops = vterm_allocator_malloc(state->vt, (state->cols + 7) / 8);
-
- state->lineinfos[BUFIDX_PRIMARY] = vterm_allocator_malloc(state->vt, state->rows * sizeof(VTermLineInfo));
- /* TODO: Make an 'enable' function */
- state->lineinfos[BUFIDX_ALTSCREEN] = vterm_allocator_malloc(state->vt, state->rows * sizeof(VTermLineInfo));
- state->lineinfo = state->lineinfos[BUFIDX_PRIMARY];
-
- state->encoding_utf8.enc = vterm_lookup_encoding(ENC_UTF8, 'u');
- if(*state->encoding_utf8.enc->init)
- (*state->encoding_utf8.enc->init)(state->encoding_utf8.enc, state->encoding_utf8.data);
-
- return state;
-}
-
-INTERNAL void vterm_state_free(VTermState *state)
-{
- vterm_allocator_free(state->vt, state->tabstops);
- vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_PRIMARY]);
- if(state->lineinfos[BUFIDX_ALTSCREEN])
- vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_ALTSCREEN]);
- vterm_allocator_free(state->vt, state->combine_chars);
- vterm_allocator_free(state->vt, state);
-}
-
-static void scroll(VTermState *state, VTermRect rect, int downward, int rightward)
-{
- if(!downward && !rightward)
- return;
-
- int rows = rect.end_row - rect.start_row;
- if(downward > rows)
- downward = rows;
- else if(downward < -rows)
- downward = -rows;
-
- int cols = rect.end_col - rect.start_col;
- if(rightward > cols)
- rightward = cols;
- else if(rightward < -cols)
- rightward = -cols;
-
- // Update lineinfo if full line
- if(rect.start_col == 0 && rect.end_col == state->cols && rightward == 0) {
- int height = rect.end_row - rect.start_row - abs(downward);
-
- if(downward > 0) {
- memmove(state->lineinfo + rect.start_row,
- state->lineinfo + rect.start_row + downward,
- height * sizeof(state->lineinfo[0]));
- for(int row = rect.end_row - downward; row < rect.end_row; row++)
- state->lineinfo[row] = (VTermLineInfo){ 0 };
- }
- else {
- memmove(state->lineinfo + rect.start_row - downward,
- state->lineinfo + rect.start_row,
- height * sizeof(state->lineinfo[0]));
- for(int row = rect.start_row; row < rect.start_row - downward; row++)
- state->lineinfo[row] = (VTermLineInfo){ 0 };
- }
- }
-
- if(state->callbacks && state->callbacks->scrollrect)
- if((*state->callbacks->scrollrect)(rect, downward, rightward, state->cbdata))
- return;
-
- if(state->callbacks)
- vterm_scroll_rect(rect, downward, rightward,
- state->callbacks->moverect, state->callbacks->erase, state->cbdata);
-}
-
-static void linefeed(VTermState *state)
-{
- if(state->pos.row == SCROLLREGION_BOTTOM(state) - 1) {
- VTermRect rect = {
- .start_row = state->scrollregion_top,
- .end_row = SCROLLREGION_BOTTOM(state),
- .start_col = SCROLLREGION_LEFT(state),
- .end_col = SCROLLREGION_RIGHT(state),
- };
-
- scroll(state, rect, 1, 0);
- }
- else if(state->pos.row < state->rows-1)
- state->pos.row++;
-}
-
-static void grow_combine_buffer(VTermState *state)
-{
- size_t new_size = state->combine_chars_size * 2;
- uint32_t *new_chars = vterm_allocator_malloc(state->vt, new_size * sizeof(new_chars[0]));
-
- memcpy(new_chars, state->combine_chars, state->combine_chars_size * sizeof(new_chars[0]));
-
- vterm_allocator_free(state->vt, state->combine_chars);
-
- state->combine_chars = new_chars;
- state->combine_chars_size = new_size;
-}
-
-static void set_col_tabstop(VTermState *state, int col)
-{
- unsigned char mask = 1 << (col & 7);
- state->tabstops[col >> 3] |= mask;
-}
-
-static void clear_col_tabstop(VTermState *state, int col)
-{
- unsigned char mask = 1 << (col & 7);
- state->tabstops[col >> 3] &= ~mask;
-}
-
-static int is_col_tabstop(VTermState *state, int col)
-{
- unsigned char mask = 1 << (col & 7);
- return state->tabstops[col >> 3] & mask;
-}
-
-static int is_cursor_in_scrollregion(const VTermState *state)
-{
- if(state->pos.row < state->scrollregion_top ||
- state->pos.row >= SCROLLREGION_BOTTOM(state))
- return 0;
- if(state->pos.col < SCROLLREGION_LEFT(state) ||
- state->pos.col >= SCROLLREGION_RIGHT(state))
- return 0;
-
- return 1;
-}
-
-static void tab(VTermState *state, int count, int direction)
-{
- while(count > 0) {
- if(direction > 0) {
- if(state->pos.col >= THISROWWIDTH(state)-1)
- return;
-
- state->pos.col++;
- }
- else if(direction < 0) {
- if(state->pos.col < 1)
- return;
-
- state->pos.col--;
- }
-
- if(is_col_tabstop(state, state->pos.col))
- count--;
- }
-}
-
-#define NO_FORCE 0
-#define FORCE 1
-
-#define DWL_OFF 0
-#define DWL_ON 1
-
-#define DHL_OFF 0
-#define DHL_TOP 1
-#define DHL_BOTTOM 2
-
-static void set_lineinfo(VTermState *state, int row, int force, int dwl, int dhl)
-{
- VTermLineInfo info = state->lineinfo[row];
-
- if(dwl == DWL_OFF)
- info.doublewidth = DWL_OFF;
- else if(dwl == DWL_ON)
- info.doublewidth = DWL_ON;
- // else -1 to ignore
-
- if(dhl == DHL_OFF)
- info.doubleheight = DHL_OFF;
- else if(dhl == DHL_TOP)
- info.doubleheight = DHL_TOP;
- else if(dhl == DHL_BOTTOM)
- info.doubleheight = DHL_BOTTOM;
-
- if((state->callbacks &&
- state->callbacks->setlineinfo &&
- (*state->callbacks->setlineinfo)(row, &info, state->lineinfo + row, state->cbdata))
- || force)
- state->lineinfo[row] = info;
-}
-
-static int on_text(const char bytes[], size_t len, void *user)
-{
- VTermState *state = user;
-
- VTermPos oldpos = state->pos;
-
- uint32_t *codepoints = (uint32_t *)(state->vt->tmpbuffer);
- size_t maxpoints = (state->vt->tmpbuffer_len) / sizeof(uint32_t);
-
- int npoints = 0;
- size_t eaten = 0;
-
- VTermEncodingInstance *encoding =
- state->gsingle_set ? &state->encoding[state->gsingle_set] :
- !(bytes[eaten] & 0x80) ? &state->encoding[state->gl_set] :
- state->vt->mode.utf8 ? &state->encoding_utf8 :
- &state->encoding[state->gr_set];
-
- (*encoding->enc->decode)(encoding->enc, encoding->data,
- codepoints, &npoints, state->gsingle_set ? 1 : maxpoints,
- bytes, &eaten, len);
-
- /* There's a chance an encoding (e.g. UTF-8) hasn't found enough bytes yet
- * for even a single codepoint
- */
- if(!npoints)
- return eaten;
-
- if(state->gsingle_set && npoints)
- state->gsingle_set = 0;
-
- int i = 0;
-
- /* This is a combining char. that needs to be merged with the previous
- * glyph output */
- if(vterm_unicode_is_combining(codepoints[i])) {
- /* See if the cursor has moved since */
- if(state->pos.row == state->combine_pos.row && state->pos.col == state->combine_pos.col + state->combine_width) {
-#ifdef DEBUG_GLYPH_COMBINE
- int printpos;
- printf("DEBUG: COMBINING SPLIT GLYPH of chars {");
- for(printpos = 0; state->combine_chars[printpos]; printpos++)
- printf("U+%04x ", state->combine_chars[printpos]);
- printf("} + {");
-#endif
-
- /* Find where we need to append these combining chars */
- int saved_i = 0;
- while(state->combine_chars[saved_i])
- saved_i++;
-
- /* Add extra ones */
- while(i < npoints && vterm_unicode_is_combining(codepoints[i])) {
- if(saved_i >= state->combine_chars_size)
- grow_combine_buffer(state);
- state->combine_chars[saved_i++] = codepoints[i++];
- }
- if(saved_i >= state->combine_chars_size)
- grow_combine_buffer(state);
- state->combine_chars[saved_i] = 0;
-
-#ifdef DEBUG_GLYPH_COMBINE
- for(; state->combine_chars[printpos]; printpos++)
- printf("U+%04x ", state->combine_chars[printpos]);
- printf("}\n");
-#endif
-
- /* Now render it */
- putglyph(state, state->combine_chars, state->combine_width, state->combine_pos);
- }
- else {
- DEBUG_LOG("libvterm: TODO: Skip over split char+combining\n");
- }
- }
-
- for(; i < npoints; i++) {
- // Try to find combining characters following this
- int glyph_starts = i;
- int glyph_ends;
- for(glyph_ends = i + 1;
- (glyph_ends < npoints) && (glyph_ends < glyph_starts + VTERM_MAX_CHARS_PER_CELL);
- glyph_ends++)
- if(!vterm_unicode_is_combining(codepoints[glyph_ends]))
- break;
-
- int width = 0;
-
- uint32_t chars[VTERM_MAX_CHARS_PER_CELL + 1];
-
- for( ; i < glyph_ends; i++) {
- chars[i - glyph_starts] = codepoints[i];
- int this_width = vterm_unicode_width(codepoints[i]);
-#ifdef DEBUG
- if(this_width < 0) {
- fprintf(stderr, "Text with negative-width codepoint U+%04x\n", codepoints[i]);
- abort();
- }
-#endif
- width += this_width;
- }
-
- while(i < npoints && vterm_unicode_is_combining(codepoints[i]))
- i++;
-
- chars[glyph_ends - glyph_starts] = 0;
- i--;
-
-#ifdef DEBUG_GLYPH_COMBINE
- int printpos;
- printf("DEBUG: COMBINED GLYPH of %d chars {", glyph_ends - glyph_starts);
- for(printpos = 0; printpos < glyph_ends - glyph_starts; printpos++)
- printf("U+%04x ", chars[printpos]);
- printf("}, onscreen width %d\n", width);
-#endif
-
- if(state->at_phantom || state->pos.col + width > THISROWWIDTH(state)) {
- linefeed(state);
- state->pos.col = 0;
- state->at_phantom = 0;
- state->lineinfo[state->pos.row].continuation = 1;
- }
-
- if(state->mode.insert) {
- /* TODO: This will be a little inefficient for large bodies of text, as
- * it'll have to 'ICH' effectively before every glyph. We should scan
- * ahead and ICH as many times as required
- */
- VTermRect rect = {
- .start_row = state->pos.row,
- .end_row = state->pos.row + 1,
- .start_col = state->pos.col,
- .end_col = THISROWWIDTH(state),
- };
- scroll(state, rect, 0, -1);
- }
-
- putglyph(state, chars, width, state->pos);
-
- if(i == npoints - 1) {
- /* End of the buffer. Save the chars in case we have to combine with
- * more on the next call */
- int save_i;
- for(save_i = 0; chars[save_i]; save_i++) {
- if(save_i >= state->combine_chars_size)
- grow_combine_buffer(state);
- state->combine_chars[save_i] = chars[save_i];
- }
- if(save_i >= state->combine_chars_size)
- grow_combine_buffer(state);
- state->combine_chars[save_i] = 0;
- state->combine_width = width;
- state->combine_pos = state->pos;
- }
-
- if(state->pos.col + width >= THISROWWIDTH(state)) {
- if(state->mode.autowrap)
- state->at_phantom = 1;
- }
- else {
- state->pos.col += width;
- }
- }
-
- updatecursor(state, &oldpos, 0);
-
-#ifdef DEBUG
- if(state->pos.row < 0 || state->pos.row >= state->rows ||
- state->pos.col < 0 || state->pos.col >= state->cols) {
- fprintf(stderr, "Position out of bounds after text: (%d,%d)\n",
- state->pos.row, state->pos.col);
- abort();
- }
-#endif
-
- return eaten;
-}
-
-static int on_control(unsigned char control, void *user)
-{
- VTermState *state = user;
-
- VTermPos oldpos = state->pos;
-
- switch(control) {
- case 0x07: // BEL - ECMA-48 8.3.3
- if(state->callbacks && state->callbacks->bell)
- (*state->callbacks->bell)(state->cbdata);
- break;
-
- case 0x08: // BS - ECMA-48 8.3.5
- if(state->pos.col > 0)
- state->pos.col--;
- break;
-
- case 0x09: // HT - ECMA-48 8.3.60
- tab(state, 1, +1);
- break;
-
- case 0x0a: // LF - ECMA-48 8.3.74
- case 0x0b: // VT
- case 0x0c: // FF
- linefeed(state);
- if(state->mode.newline)
- state->pos.col = 0;
- break;
-
- case 0x0d: // CR - ECMA-48 8.3.15
- state->pos.col = 0;
- break;
-
- case 0x0e: // LS1 - ECMA-48 8.3.76
- state->gl_set = 1;
- break;
-
- case 0x0f: // LS0 - ECMA-48 8.3.75
- state->gl_set = 0;
- break;
-
- case 0x84: // IND - DEPRECATED but implemented for completeness
- linefeed(state);
- break;
-
- case 0x85: // NEL - ECMA-48 8.3.86
- linefeed(state);
- state->pos.col = 0;
- break;
-
- case 0x88: // HTS - ECMA-48 8.3.62
- set_col_tabstop(state, state->pos.col);
- break;
-
- case 0x8d: // RI - ECMA-48 8.3.104
- if(state->pos.row == state->scrollregion_top) {
- VTermRect rect = {
- .start_row = state->scrollregion_top,
- .end_row = SCROLLREGION_BOTTOM(state),
- .start_col = SCROLLREGION_LEFT(state),
- .end_col = SCROLLREGION_RIGHT(state),
- };
-
- scroll(state, rect, -1, 0);
- }
- else if(state->pos.row > 0)
- state->pos.row--;
- break;
-
- case 0x8e: // SS2 - ECMA-48 8.3.141
- state->gsingle_set = 2;
- break;
-
- case 0x8f: // SS3 - ECMA-48 8.3.142
- state->gsingle_set = 3;
- break;
-
- default:
- if(state->fallbacks && state->fallbacks->control)
- if((*state->fallbacks->control)(control, state->fbdata))
- return 1;
-
- return 0;
- }
-
- updatecursor(state, &oldpos, 1);
-
-#ifdef DEBUG
- if(state->pos.row < 0 || state->pos.row >= state->rows ||
- state->pos.col < 0 || state->pos.col >= state->cols) {
- fprintf(stderr, "Position out of bounds after Ctrl %02x: (%d,%d)\n",
- control, state->pos.row, state->pos.col);
- abort();
- }
-#endif
-
- return 1;
-}
-
-static int settermprop_bool(VTermState *state, VTermProp prop, int v)
-{
- VTermValue val = { .boolean = v };
- return vterm_state_set_termprop(state, prop, &val);
-}
-
-static int settermprop_int(VTermState *state, VTermProp prop, int v)
-{
- VTermValue val = { .number = v };
- return vterm_state_set_termprop(state, prop, &val);
-}
-
-static int settermprop_string(VTermState *state, VTermProp prop, VTermStringFragment frag)
-{
- VTermValue val = { .string = frag };
- return vterm_state_set_termprop(state, prop, &val);
-}
-
-static void savecursor(VTermState *state, int save)
-{
- if(save) {
- state->saved.pos = state->pos;
- state->saved.mode.cursor_visible = state->mode.cursor_visible;
- state->saved.mode.cursor_blink = state->mode.cursor_blink;
- state->saved.mode.cursor_shape = state->mode.cursor_shape;
-
- vterm_state_savepen(state, 1);
- }
- else {
- VTermPos oldpos = state->pos;
-
- state->pos = state->saved.pos;
-
- settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, state->saved.mode.cursor_visible);
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, state->saved.mode.cursor_blink);
- settermprop_int (state, VTERM_PROP_CURSORSHAPE, state->saved.mode.cursor_shape);
-
- vterm_state_savepen(state, 0);
-
- updatecursor(state, &oldpos, 1);
- }
-}
-
-static int on_escape(const char *bytes, size_t len, void *user)
-{
- VTermState *state = user;
-
- /* Easier to decode this from the first byte, even though the final
- * byte terminates it
- */
- switch(bytes[0]) {
- case ' ':
- if(len != 2)
- return 0;
-
- switch(bytes[1]) {
- case 'F': // S7C1T
- state->vt->mode.ctrl8bit = 0;
- break;
-
- case 'G': // S8C1T
- state->vt->mode.ctrl8bit = 1;
- break;
-
- default:
- return 0;
- }
- return 2;
-
- case '#':
- if(len != 2)
- return 0;
-
- switch(bytes[1]) {
- case '3': // DECDHL top
- if(state->mode.leftrightmargin)
- break;
- set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_TOP);
- break;
-
- case '4': // DECDHL bottom
- if(state->mode.leftrightmargin)
- break;
- set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_BOTTOM);
- break;
-
- case '5': // DECSWL
- if(state->mode.leftrightmargin)
- break;
- set_lineinfo(state, state->pos.row, NO_FORCE, DWL_OFF, DHL_OFF);
- break;
-
- case '6': // DECDWL
- if(state->mode.leftrightmargin)
- break;
- set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_OFF);
- break;
-
- case '8': // DECALN
- {
- VTermPos pos;
- uint32_t E[] = { 'E', 0 };
- for(pos.row = 0; pos.row < state->rows; pos.row++)
- for(pos.col = 0; pos.col < ROWWIDTH(state, pos.row); pos.col++)
- putglyph(state, E, 1, pos);
- break;
- }
-
- default:
- return 0;
- }
- return 2;
-
- case '(': case ')': case '*': case '+': // SCS
- if(len != 2)
- return 0;
-
- {
- int setnum = bytes[0] - 0x28;
- VTermEncoding *newenc = vterm_lookup_encoding(ENC_SINGLE_94, bytes[1]);
-
- if(newenc) {
- state->encoding[setnum].enc = newenc;
-
- if(newenc->init)
- (*newenc->init)(newenc, state->encoding[setnum].data);
- }
- }
-
- return 2;
-
- case '7': // DECSC
- savecursor(state, 1);
- return 1;
-
- case '8': // DECRC
- savecursor(state, 0);
- return 1;
-
- case '<': // Ignored by VT100. Used in VT52 mode to switch up to VT100
- return 1;
-
- case '=': // DECKPAM
- state->mode.keypad = 1;
- return 1;
-
- case '>': // DECKPNM
- state->mode.keypad = 0;
- return 1;
-
- case 'c': // RIS - ECMA-48 8.3.105
- {
- VTermPos oldpos = state->pos;
- vterm_state_reset(state, 1);
- if(state->callbacks && state->callbacks->movecursor)
- (*state->callbacks->movecursor)(state->pos, oldpos, state->mode.cursor_visible, state->cbdata);
- return 1;
- }
-
- case 'n': // LS2 - ECMA-48 8.3.78
- state->gl_set = 2;
- return 1;
-
- case 'o': // LS3 - ECMA-48 8.3.80
- state->gl_set = 3;
- return 1;
-
- case '~': // LS1R - ECMA-48 8.3.77
- state->gr_set = 1;
- return 1;
-
- case '}': // LS2R - ECMA-48 8.3.79
- state->gr_set = 2;
- return 1;
-
- case '|': // LS3R - ECMA-48 8.3.81
- state->gr_set = 3;
- return 1;
-
- default:
- return 0;
- }
-}
-
-static void set_mode(VTermState *state, int num, int val)
-{
- switch(num) {
- case 4: // IRM - ECMA-48 7.2.10
- state->mode.insert = val;
- break;
-
- case 20: // LNM - ANSI X3.4-1977
- state->mode.newline = val;
- break;
-
- default:
- DEBUG_LOG("libvterm: Unknown mode %d\n", num);
- return;
- }
-}
-
-static void set_dec_mode(VTermState *state, int num, int val)
-{
- switch(num) {
- case 1:
- state->mode.cursor = val;
- break;
-
- case 5: // DECSCNM - screen mode
- settermprop_bool(state, VTERM_PROP_REVERSE, val);
- break;
-
- case 6: // DECOM - origin mode
- {
- VTermPos oldpos = state->pos;
- state->mode.origin = val;
- state->pos.row = state->mode.origin ? state->scrollregion_top : 0;
- state->pos.col = state->mode.origin ? SCROLLREGION_LEFT(state) : 0;
- updatecursor(state, &oldpos, 1);
- }
- break;
-
- case 7:
- state->mode.autowrap = val;
- break;
-
- case 12:
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, val);
- break;
-
- case 25:
- settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, val);
- break;
-
- case 69: // DECVSSM - vertical split screen mode
- // DECLRMM - left/right margin mode
- state->mode.leftrightmargin = val;
- if(val) {
- // Setting DECVSSM must clear doublewidth/doubleheight state of every line
- for(int row = 0; row < state->rows; row++)
- set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
- }
-
- break;
-
- case 1000:
- case 1002:
- case 1003:
- settermprop_int(state, VTERM_PROP_MOUSE,
- !val ? VTERM_PROP_MOUSE_NONE :
- (num == 1000) ? VTERM_PROP_MOUSE_CLICK :
- (num == 1002) ? VTERM_PROP_MOUSE_DRAG :
- VTERM_PROP_MOUSE_MOVE);
- break;
-
- case 1004:
- settermprop_bool(state, VTERM_PROP_FOCUSREPORT, val);
- state->mode.report_focus = val;
- break;
-
- case 1005:
- state->mouse_protocol = val ? MOUSE_UTF8 : MOUSE_X10;
- break;
-
- case 1006:
- state->mouse_protocol = val ? MOUSE_SGR : MOUSE_X10;
- break;
-
- case 1015:
- state->mouse_protocol = val ? MOUSE_RXVT : MOUSE_X10;
- break;
-
- case 1047:
- settermprop_bool(state, VTERM_PROP_ALTSCREEN, val);
- break;
-
- case 1048:
- savecursor(state, val);
- break;
-
- case 1049:
- settermprop_bool(state, VTERM_PROP_ALTSCREEN, val);
- savecursor(state, val);
- break;
-
- case 2004:
- state->mode.bracketpaste = val;
- break;
-
- default:
- DEBUG_LOG("libvterm: Unknown DEC mode %d\n", num);
- return;
- }
-}
-
-static void request_dec_mode(VTermState *state, int num)
-{
- int reply;
-
- switch(num) {
- case 1:
- reply = state->mode.cursor;
- break;
-
- case 5:
- reply = state->mode.screen;
- break;
-
- case 6:
- reply = state->mode.origin;
- break;
-
- case 7:
- reply = state->mode.autowrap;
- break;
-
- case 12:
- reply = state->mode.cursor_blink;
- break;
-
- case 25:
- reply = state->mode.cursor_visible;
- break;
-
- case 69:
- reply = state->mode.leftrightmargin;
- break;
-
- case 1000:
- reply = state->mouse_flags == MOUSE_WANT_CLICK;
- break;
-
- case 1002:
- reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_DRAG);
- break;
-
- case 1003:
- reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_MOVE);
- break;
-
- case 1004:
- reply = state->mode.report_focus;
- break;
-
- case 1005:
- reply = state->mouse_protocol == MOUSE_UTF8;
- break;
-
- case 1006:
- reply = state->mouse_protocol == MOUSE_SGR;
- break;
-
- case 1015:
- reply = state->mouse_protocol == MOUSE_RXVT;
- break;
-
- case 1047:
- reply = state->mode.alt_screen;
- break;
-
- case 2004:
- reply = state->mode.bracketpaste;
- break;
-
- default:
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, 0);
- return;
- }
-
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, reply ? 1 : 2);
-}
-
-static void request_version_string(VTermState *state)
-{
- vterm_push_output_sprintf_str(state->vt, C1_DCS, true, ">|libvterm(%d.%d)",
- VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR);
-}
-
-static int on_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user)
-{
- VTermState *state = user;
- int leader_byte = 0;
- int intermed_byte = 0;
- int cancel_phantom = 1;
-
- if(leader && leader[0]) {
- if(leader[1]) // longer than 1 char
- return 0;
-
- switch(leader[0]) {
- case '?':
- case '>':
- leader_byte = leader[0];
- break;
- default:
- return 0;
- }
- }
-
- if(intermed && intermed[0]) {
- if(intermed[1]) // longer than 1 char
- return 0;
-
- switch(intermed[0]) {
- case ' ':
- case '!':
- case '"':
- case '$':
- case '\'':
- intermed_byte = intermed[0];
- break;
- default:
- return 0;
- }
- }
-
- VTermPos oldpos = state->pos;
-
- // Some temporaries for later code
- int count, val;
- int row, col;
- VTermRect rect;
- int selective;
-
-#define LBOUND(v,min) if((v) < (min)) (v) = (min)
-#define UBOUND(v,max) if((v) > (max)) (v) = (max)
-
-#define LEADER(l,b) ((l << 8) | b)
-#define INTERMED(i,b) ((i << 16) | b)
-
- switch(intermed_byte << 16 | leader_byte << 8 | command) {
- case 0x40: // ICH - ECMA-48 8.3.64
- count = CSI_ARG_COUNT(args[0]);
-
- if(!is_cursor_in_scrollregion(state))
- break;
-
- rect.start_row = state->pos.row;
- rect.end_row = state->pos.row + 1;
- rect.start_col = state->pos.col;
- if(state->mode.leftrightmargin)
- rect.end_col = SCROLLREGION_RIGHT(state);
- else
- rect.end_col = THISROWWIDTH(state);
-
- scroll(state, rect, 0, -count);
-
- break;
-
- case 0x41: // CUU - ECMA-48 8.3.22
- count = CSI_ARG_COUNT(args[0]);
- state->pos.row -= count;
- state->at_phantom = 0;
- break;
-
- case 0x42: // CUD - ECMA-48 8.3.19
- count = CSI_ARG_COUNT(args[0]);
- state->pos.row += count;
- state->at_phantom = 0;
- break;
-
- case 0x43: // CUF - ECMA-48 8.3.20
- count = CSI_ARG_COUNT(args[0]);
- state->pos.col += count;
- state->at_phantom = 0;
- break;
-
- case 0x44: // CUB - ECMA-48 8.3.18
- count = CSI_ARG_COUNT(args[0]);
- state->pos.col -= count;
- state->at_phantom = 0;
- break;
-
- case 0x45: // CNL - ECMA-48 8.3.12
- count = CSI_ARG_COUNT(args[0]);
- state->pos.col = 0;
- state->pos.row += count;
- state->at_phantom = 0;
- break;
-
- case 0x46: // CPL - ECMA-48 8.3.13
- count = CSI_ARG_COUNT(args[0]);
- state->pos.col = 0;
- state->pos.row -= count;
- state->at_phantom = 0;
- break;
-
- case 0x47: // CHA - ECMA-48 8.3.9
- val = CSI_ARG_OR(args[0], 1);
- state->pos.col = val-1;
- state->at_phantom = 0;
- break;
-
- case 0x48: // CUP - ECMA-48 8.3.21
- row = CSI_ARG_OR(args[0], 1);
- col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
- // zero-based
- state->pos.row = row-1;
- state->pos.col = col-1;
- if(state->mode.origin) {
- state->pos.row += state->scrollregion_top;
- state->pos.col += SCROLLREGION_LEFT(state);
- }
- state->at_phantom = 0;
- break;
-
- case 0x49: // CHT - ECMA-48 8.3.10
- count = CSI_ARG_COUNT(args[0]);
- tab(state, count, +1);
- break;
-
- case 0x4a: // ED - ECMA-48 8.3.39
- case LEADER('?', 0x4a): // DECSED - Selective Erase in Display
- selective = (leader_byte == '?');
- switch(CSI_ARG(args[0])) {
- case CSI_ARG_MISSING:
- case 0:
- rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1;
- rect.start_col = state->pos.col; rect.end_col = state->cols;
- if(rect.end_col > rect.start_col)
- erase(state, rect, selective);
-
- rect.start_row = state->pos.row + 1; rect.end_row = state->rows;
- rect.start_col = 0;
- for(int row_ = rect.start_row; row_ < rect.end_row; row_++)
- set_lineinfo(state, row_, FORCE, DWL_OFF, DHL_OFF);
- if(rect.end_row > rect.start_row)
- erase(state, rect, selective);
- break;
-
- case 1:
- rect.start_row = 0; rect.end_row = state->pos.row;
- rect.start_col = 0; rect.end_col = state->cols;
- for(int row_ = rect.start_row; row_ < rect.end_row; row_++)
- set_lineinfo(state, row_, FORCE, DWL_OFF, DHL_OFF);
- if(rect.end_col > rect.start_col)
- erase(state, rect, selective);
-
- rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1;
- rect.end_col = state->pos.col + 1;
- if(rect.end_row > rect.start_row)
- erase(state, rect, selective);
- break;
-
- case 2:
- rect.start_row = 0; rect.end_row = state->rows;
- rect.start_col = 0; rect.end_col = state->cols;
- for(int row_ = rect.start_row; row_ < rect.end_row; row_++)
- set_lineinfo(state, row_, FORCE, DWL_OFF, DHL_OFF);
- erase(state, rect, selective);
- break;
-
- case 3:
- if(state->callbacks && state->callbacks->sb_clear)
- if((*state->callbacks->sb_clear)(state->cbdata))
- return 1;
- break;
- }
- break;
-
- case 0x4b: // EL - ECMA-48 8.3.41
- case LEADER('?', 0x4b): // DECSEL - Selective Erase in Line
- selective = (leader_byte == '?');
- rect.start_row = state->pos.row;
- rect.end_row = state->pos.row + 1;
-
- switch(CSI_ARG(args[0])) {
- case CSI_ARG_MISSING:
- case 0:
- rect.start_col = state->pos.col; rect.end_col = THISROWWIDTH(state); break;
- case 1:
- rect.start_col = 0; rect.end_col = state->pos.col + 1; break;
- case 2:
- rect.start_col = 0; rect.end_col = THISROWWIDTH(state); break;
- default:
- return 0;
- }
-
- if(rect.end_col > rect.start_col)
- erase(state, rect, selective);
-
- break;
-
- case 0x4c: // IL - ECMA-48 8.3.67
- count = CSI_ARG_COUNT(args[0]);
-
- if(!is_cursor_in_scrollregion(state))
- break;
-
- rect.start_row = state->pos.row;
- rect.end_row = SCROLLREGION_BOTTOM(state);
- rect.start_col = SCROLLREGION_LEFT(state);
- rect.end_col = SCROLLREGION_RIGHT(state);
-
- scroll(state, rect, -count, 0);
-
- break;
-
- case 0x4d: // DL - ECMA-48 8.3.32
- count = CSI_ARG_COUNT(args[0]);
-
- if(!is_cursor_in_scrollregion(state))
- break;
-
- rect.start_row = state->pos.row;
- rect.end_row = SCROLLREGION_BOTTOM(state);
- rect.start_col = SCROLLREGION_LEFT(state);
- rect.end_col = SCROLLREGION_RIGHT(state);
-
- scroll(state, rect, count, 0);
-
- break;
-
- case 0x50: // DCH - ECMA-48 8.3.26
- count = CSI_ARG_COUNT(args[0]);
-
- if(!is_cursor_in_scrollregion(state))
- break;
-
- rect.start_row = state->pos.row;
- rect.end_row = state->pos.row + 1;
- rect.start_col = state->pos.col;
- if(state->mode.leftrightmargin)
- rect.end_col = SCROLLREGION_RIGHT(state);
- else
- rect.end_col = THISROWWIDTH(state);
-
- scroll(state, rect, 0, count);
-
- break;
-
- case 0x53: // SU - ECMA-48 8.3.147
- count = CSI_ARG_COUNT(args[0]);
-
- rect.start_row = state->scrollregion_top;
- rect.end_row = SCROLLREGION_BOTTOM(state);
- rect.start_col = SCROLLREGION_LEFT(state);
- rect.end_col = SCROLLREGION_RIGHT(state);
-
- scroll(state, rect, count, 0);
-
- break;
-
- case 0x54: // SD - ECMA-48 8.3.113
- count = CSI_ARG_COUNT(args[0]);
-
- rect.start_row = state->scrollregion_top;
- rect.end_row = SCROLLREGION_BOTTOM(state);
- rect.start_col = SCROLLREGION_LEFT(state);
- rect.end_col = SCROLLREGION_RIGHT(state);
-
- scroll(state, rect, -count, 0);
-
- break;
-
- case 0x58: // ECH - ECMA-48 8.3.38
- count = CSI_ARG_COUNT(args[0]);
-
- rect.start_row = state->pos.row;
- rect.end_row = state->pos.row + 1;
- rect.start_col = state->pos.col;
- rect.end_col = state->pos.col + count;
- UBOUND(rect.end_col, THISROWWIDTH(state));
-
- erase(state, rect, 0);
- break;
-
- case 0x5a: // CBT - ECMA-48 8.3.7
- count = CSI_ARG_COUNT(args[0]);
- tab(state, count, -1);
- break;
-
- case 0x60: // HPA - ECMA-48 8.3.57
- col = CSI_ARG_OR(args[0], 1);
- state->pos.col = col-1;
- state->at_phantom = 0;
- break;
-
- case 0x61: // HPR - ECMA-48 8.3.59
- count = CSI_ARG_COUNT(args[0]);
- state->pos.col += count;
- state->at_phantom = 0;
- break;
-
- case 0x62: { // REP - ECMA-48 8.3.103
- const int row_width = THISROWWIDTH(state);
- count = CSI_ARG_COUNT(args[0]);
- col = state->pos.col + count;
- UBOUND(col, row_width);
- while (state->pos.col < col) {
- putglyph(state, state->combine_chars, state->combine_width, state->pos);
- state->pos.col += state->combine_width;
- }
- if (state->pos.col + state->combine_width >= row_width) {
- if (state->mode.autowrap) {
- state->at_phantom = 1;
- cancel_phantom = 0;
- }
- }
- break;
- }
-
- case 0x63: // DA - ECMA-48 8.3.24
- val = CSI_ARG_OR(args[0], 0);
- if(val == 0)
- // DEC VT100 response
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?1;2c");
- break;
-
- case LEADER('>', 0x63): // DEC secondary Device Attributes
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, ">%d;%d;%dc", 0, 100, 0);
- break;
-
- case 0x64: // VPA - ECMA-48 8.3.158
- row = CSI_ARG_OR(args[0], 1);
- state->pos.row = row-1;
- if(state->mode.origin)
- state->pos.row += state->scrollregion_top;
- state->at_phantom = 0;
- break;
-
- case 0x65: // VPR - ECMA-48 8.3.160
- count = CSI_ARG_COUNT(args[0]);
- state->pos.row += count;
- state->at_phantom = 0;
- break;
-
- case 0x66: // HVP - ECMA-48 8.3.63
- row = CSI_ARG_OR(args[0], 1);
- col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
- // zero-based
- state->pos.row = row-1;
- state->pos.col = col-1;
- if(state->mode.origin) {
- state->pos.row += state->scrollregion_top;
- state->pos.col += SCROLLREGION_LEFT(state);
- }
- state->at_phantom = 0;
- break;
-
- case 0x67: // TBC - ECMA-48 8.3.154
- val = CSI_ARG_OR(args[0], 0);
-
- switch(val) {
- case 0:
- clear_col_tabstop(state, state->pos.col);
- break;
- case 3:
- case 5:
- for(col = 0; col < state->cols; col++)
- clear_col_tabstop(state, col);
- break;
- case 1:
- case 2:
- case 4:
- break;
- /* TODO: 1, 2 and 4 aren't meaningful yet without line tab stops */
- default:
- return 0;
- }
- break;
-
- case 0x68: // SM - ECMA-48 8.3.125
- if(!CSI_ARG_IS_MISSING(args[0]))
- set_mode(state, CSI_ARG(args[0]), 1);
- break;
-
- case LEADER('?', 0x68): // DEC private mode set
- for(int i = 0; i < argcount; i++) {
- if(!CSI_ARG_IS_MISSING(args[i]))
- set_dec_mode(state, CSI_ARG(args[i]), 1);
- }
- break;
-
- case 0x6a: // HPB - ECMA-48 8.3.58
- count = CSI_ARG_COUNT(args[0]);
- state->pos.col -= count;
- state->at_phantom = 0;
- break;
-
- case 0x6b: // VPB - ECMA-48 8.3.159
- count = CSI_ARG_COUNT(args[0]);
- state->pos.row -= count;
- state->at_phantom = 0;
- break;
-
- case 0x6c: // RM - ECMA-48 8.3.106
- if(!CSI_ARG_IS_MISSING(args[0]))
- set_mode(state, CSI_ARG(args[0]), 0);
- break;
-
- case LEADER('?', 0x6c): // DEC private mode reset
- for(int i = 0; i < argcount; i++) {
- if(!CSI_ARG_IS_MISSING(args[i]))
- set_dec_mode(state, CSI_ARG(args[i]), 0);
- }
- break;
-
- case 0x6d: // SGR - ECMA-48 8.3.117
- vterm_state_setpen(state, args, argcount);
- break;
-
- case LEADER('?', 0x6d): // DECSGR
- /* No actual DEC terminal recognised these, but some printers did. These
- * are alternative ways to request subscript/superscript/off
- */
- for(int argi = 0; argi < argcount; argi++) {
- long arg;
- switch(arg = CSI_ARG(args[argi])) {
- case 4: // Superscript on
- arg = 73;
- vterm_state_setpen(state, &arg, 1);
- break;
- case 5: // Subscript on
- arg = 74;
- vterm_state_setpen(state, &arg, 1);
- break;
- case 24: // Super+subscript off
- arg = 75;
- vterm_state_setpen(state, &arg, 1);
- break;
- }
- }
- break;
-
- case 0x6e: // DSR - ECMA-48 8.3.35
- case LEADER('?', 0x6e): // DECDSR
- val = CSI_ARG_OR(args[0], 0);
-
- {
- char *qmark = (leader_byte == '?') ? "?" : "";
-
- switch(val) {
- case 0: case 1: case 2: case 3: case 4:
- // ignore - these are replies
- break;
- case 5:
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s0n", qmark);
- break;
- case 6: // CPR - cursor position report
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s%d;%dR", qmark, state->pos.row + 1, state->pos.col + 1);
- break;
- }
- }
- break;
-
-
- case INTERMED('!', 0x70): // DECSTR - DEC soft terminal reset
- vterm_state_reset(state, 0);
- break;
-
- case LEADER('?', INTERMED('$', 0x70)):
- request_dec_mode(state, CSI_ARG(args[0]));
- break;
-
- case LEADER('>', 0x71): // XTVERSION - xterm query version string
- request_version_string(state);
- break;
-
- case INTERMED(' ', 0x71): // DECSCUSR - DEC set cursor shape
- val = CSI_ARG_OR(args[0], 1);
-
- switch(val) {
- case 0: case 1:
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
- settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
- break;
- case 2:
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
- settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
- break;
- case 3:
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
- settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE);
- break;
- case 4:
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
- settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE);
- break;
- case 5:
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
- settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT);
- break;
- case 6:
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
- settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT);
- break;
- }
-
- break;
-
- case INTERMED('"', 0x71): // DECSCA - DEC select character protection attribute
- val = CSI_ARG_OR(args[0], 0);
-
- switch(val) {
- case 0: case 2:
- state->protected_cell = 0;
- break;
- case 1:
- state->protected_cell = 1;
- break;
- }
-
- break;
-
- case 0x72: // DECSTBM - DEC custom
- state->scrollregion_top = CSI_ARG_OR(args[0], 1) - 1;
- state->scrollregion_bottom = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]);
- LBOUND(state->scrollregion_top, 0);
- UBOUND(state->scrollregion_top, state->rows);
- LBOUND(state->scrollregion_bottom, -1);
- if(state->scrollregion_top == 0 && state->scrollregion_bottom == state->rows)
- state->scrollregion_bottom = -1;
- else
- UBOUND(state->scrollregion_bottom, state->rows);
-
- if(SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) {
- // Invalid
- state->scrollregion_top = 0;
- state->scrollregion_bottom = -1;
- }
-
- // Setting the scrolling region restores the cursor to the home position
- state->pos.row = 0;
- state->pos.col = 0;
- if(state->mode.origin) {
- state->pos.row += state->scrollregion_top;
- state->pos.col += SCROLLREGION_LEFT(state);
- }
-
- break;
-
- case 0x73: // DECSLRM - DEC custom
- // Always allow setting these margins, just they won't take effect without DECVSSM
- state->scrollregion_left = CSI_ARG_OR(args[0], 1) - 1;
- state->scrollregion_right = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]);
- LBOUND(state->scrollregion_left, 0);
- UBOUND(state->scrollregion_left, state->cols);
- LBOUND(state->scrollregion_right, -1);
- if(state->scrollregion_left == 0 && state->scrollregion_right == state->cols)
- state->scrollregion_right = -1;
- else
- UBOUND(state->scrollregion_right, state->cols);
-
- if(state->scrollregion_right > -1 &&
- state->scrollregion_right <= state->scrollregion_left) {
- // Invalid
- state->scrollregion_left = 0;
- state->scrollregion_right = -1;
- }
-
- // Setting the scrolling region restores the cursor to the home position
- state->pos.row = 0;
- state->pos.col = 0;
- if(state->mode.origin) {
- state->pos.row += state->scrollregion_top;
- state->pos.col += SCROLLREGION_LEFT(state);
- }
-
- break;
-
- case INTERMED('\'', 0x7D): // DECIC
- count = CSI_ARG_COUNT(args[0]);
-
- if(!is_cursor_in_scrollregion(state))
- break;
-
- rect.start_row = state->scrollregion_top;
- rect.end_row = SCROLLREGION_BOTTOM(state);
- rect.start_col = state->pos.col;
- rect.end_col = SCROLLREGION_RIGHT(state);
-
- scroll(state, rect, 0, -count);
-
- break;
-
- case INTERMED('\'', 0x7E): // DECDC
- count = CSI_ARG_COUNT(args[0]);
-
- if(!is_cursor_in_scrollregion(state))
- break;
-
- rect.start_row = state->scrollregion_top;
- rect.end_row = SCROLLREGION_BOTTOM(state);
- rect.start_col = state->pos.col;
- rect.end_col = SCROLLREGION_RIGHT(state);
-
- scroll(state, rect, 0, count);
-
- break;
-
- default:
- if(state->fallbacks && state->fallbacks->csi)
- if((*state->fallbacks->csi)(leader, args, argcount, intermed, command, state->fbdata))
- return 1;
-
- return 0;
- }
-
- if(state->mode.origin) {
- LBOUND(state->pos.row, state->scrollregion_top);
- UBOUND(state->pos.row, SCROLLREGION_BOTTOM(state)-1);
- LBOUND(state->pos.col, SCROLLREGION_LEFT(state));
- UBOUND(state->pos.col, SCROLLREGION_RIGHT(state)-1);
- }
- else {
- LBOUND(state->pos.row, 0);
- UBOUND(state->pos.row, state->rows-1);
- LBOUND(state->pos.col, 0);
- UBOUND(state->pos.col, THISROWWIDTH(state)-1);
- }
-
- updatecursor(state, &oldpos, cancel_phantom);
-
-#ifdef DEBUG
- if(state->pos.row < 0 || state->pos.row >= state->rows ||
- state->pos.col < 0 || state->pos.col >= state->cols) {
- fprintf(stderr, "Position out of bounds after CSI %c: (%d,%d)\n",
- command, state->pos.row, state->pos.col);
- abort();
- }
-
- if(SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) {
- fprintf(stderr, "Scroll region height out of bounds after CSI %c: %d <= %d\n",
- command, SCROLLREGION_BOTTOM(state), state->scrollregion_top);
- abort();
- }
-
- if(SCROLLREGION_RIGHT(state) <= SCROLLREGION_LEFT(state)) {
- fprintf(stderr, "Scroll region width out of bounds after CSI %c: %d <= %d\n",
- command, SCROLLREGION_RIGHT(state), SCROLLREGION_LEFT(state));
- abort();
- }
-#endif
-
- return 1;
-}
-
-static char base64_one(uint8_t b)
-{
- if(b < 26)
- return 'A' + b;
- else if(b < 52)
- return 'a' + b - 26;
- else if(b < 62)
- return '0' + b - 52;
- else if(b == 62)
- return '+';
- else if(b == 63)
- return '/';
- return 0;
-}
-
-static uint8_t unbase64one(char c)
-{
- if(c >= 'A' && c <= 'Z')
- return c - 'A';
- else if(c >= 'a' && c <= 'z')
- return c - 'a' + 26;
- else if(c >= '0' && c <= '9')
- return c - '0' + 52;
- else if(c == '+')
- return 62;
- else if(c == '/')
- return 63;
-
- return 0xFF;
-}
-
-static void osc_selection(VTermState *state, VTermStringFragment frag)
-{
- if(frag.initial) {
- state->tmp.selection.mask = 0;
- state->tmp.selection.state = SELECTION_INITIAL;
- }
-
- while(!state->tmp.selection.state && frag.len) {
- /* Parse selection parameter */
- switch(frag.str[0]) {
- case 'c':
- state->tmp.selection.mask |= VTERM_SELECTION_CLIPBOARD;
- break;
- case 'p':
- state->tmp.selection.mask |= VTERM_SELECTION_PRIMARY;
- break;
- case 'q':
- state->tmp.selection.mask |= VTERM_SELECTION_SECONDARY;
- break;
- case 's':
- state->tmp.selection.mask |= VTERM_SELECTION_SELECT;
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- state->tmp.selection.mask |= (VTERM_SELECTION_CUT0 << (frag.str[0] - '0'));
- break;
-
- case ';':
- state->tmp.selection.state = SELECTION_SELECTED;
- if(!state->tmp.selection.mask)
- state->tmp.selection.mask = VTERM_SELECTION_SELECT|VTERM_SELECTION_CUT0;
- break;
- }
-
- frag.str++;
- frag.len--;
- }
-
- if(!frag.len) {
- /* Clear selection if we're already finished but didn't do anything */
- if(frag.final && state->selection.callbacks->set) {
- (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){
- .str = NULL,
- .len = 0,
- .initial = state->tmp.selection.state != SELECTION_SET,
- .final = true,
- }, state->selection.user);
- }
- return;
- }
-
- if(state->tmp.selection.state == SELECTION_SELECTED) {
- if(frag.str[0] == '?') {
- state->tmp.selection.state = SELECTION_QUERY;
- }
- else {
- state->tmp.selection.state = SELECTION_SET_INITIAL;
- state->tmp.selection.recvpartial = 0;
- }
- }
-
- if(state->tmp.selection.state == SELECTION_QUERY) {
- if(state->selection.callbacks->query)
- (*state->selection.callbacks->query)(state->tmp.selection.mask, state->selection.user);
- return;
- }
-
- if(state->tmp.selection.state == SELECTION_INVALID)
- return;
-
- if(state->selection.callbacks->set) {
- size_t bufcur = 0;
- char *buffer = state->selection.buffer;
-
- uint32_t x = 0; /* Current decoding value */
- int n = 0; /* Number of sextets consumed */
-
- if(state->tmp.selection.recvpartial) {
- n = state->tmp.selection.recvpartial >> 24;
- x = state->tmp.selection.recvpartial & 0x03FFFF; /* could be up to 18 bits of state in here */
-
- state->tmp.selection.recvpartial = 0;
- }
-
- while((state->selection.buflen - bufcur) >= 3 && frag.len) {
- if(frag.str[0] == '=') {
- if(n == 2) {
- buffer[0] = (x >> 4) & 0xFF;
- buffer += 1, bufcur += 1;
- }
- if(n == 3) {
- buffer[0] = (x >> 10) & 0xFF;
- buffer[1] = (x >> 2) & 0xFF;
- buffer += 2, bufcur += 2;
- }
-
- while(frag.len && frag.str[0] == '=')
- frag.str++, frag.len--;
-
- n = 0;
- }
- else {
- uint8_t b = unbase64one(frag.str[0]);
- if(b == 0xFF) {
- DEBUG_LOG("base64decode bad input %02X\n", (uint8_t)frag.str[0]);
-
- state->tmp.selection.state = SELECTION_INVALID;
- if(state->selection.callbacks->set) {
- (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){
- .str = NULL,
- .len = 0,
- .initial = true,
- .final = true,
- }, state->selection.user);
- }
- break;
- }
-
- x = (x << 6) | b;
- n++;
- frag.str++, frag.len--;
-
- if(n == 4) {
- buffer[0] = (x >> 16) & 0xFF;
- buffer[1] = (x >> 8) & 0xFF;
- buffer[2] = (x >> 0) & 0xFF;
-
- buffer += 3, bufcur += 3;
- x = 0;
- n = 0;
- }
- }
-
- if(!frag.len || (state->selection.buflen - bufcur) < 3) {
- if(bufcur) {
- (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){
- .str = state->selection.buffer,
- .len = bufcur,
- .initial = state->tmp.selection.state == SELECTION_SET_INITIAL,
- .final = frag.final && !frag.len,
- }, state->selection.user);
- state->tmp.selection.state = SELECTION_SET;
- }
-
- buffer = state->selection.buffer;
- bufcur = 0;
- }
- }
-
- if(n)
- state->tmp.selection.recvpartial = (n << 24) | x;
- }
-}
-
-static int on_osc(int command, VTermStringFragment frag, void *user)
-{
- VTermState *state = user;
-
- switch(command) {
- case 0:
- settermprop_string(state, VTERM_PROP_ICONNAME, frag);
- settermprop_string(state, VTERM_PROP_TITLE, frag);
- return 1;
-
- case 1:
- settermprop_string(state, VTERM_PROP_ICONNAME, frag);
- return 1;
-
- case 2:
- settermprop_string(state, VTERM_PROP_TITLE, frag);
- return 1;
-
- case 52:
- if(state->selection.callbacks)
- osc_selection(state, frag);
-
- return 1;
-
- default:
- if(state->fallbacks && state->fallbacks->osc)
- if((*state->fallbacks->osc)(command, frag, state->fbdata))
- return 1;
- }
-
- return 0;
-}
-
-static void request_status_string(VTermState *state, VTermStringFragment frag)
-{
- VTerm *vt = state->vt;
-
- char *tmp = state->tmp.decrqss;
-
- if(frag.initial)
- tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0;
-
- int i = 0;
- while(i < sizeof(state->tmp.decrqss)-1 && tmp[i])
- i++;
- while(i < sizeof(state->tmp.decrqss)-1 && frag.len--)
- tmp[i++] = (frag.str++)[0];
- tmp[i] = 0;
-
- if(!frag.final)
- return;
-
- switch(tmp[0] | tmp[1]<<8 | tmp[2]<<16) {
- case 'm': {
- // Query SGR
- long args[20];
- int argc = vterm_state_getpen(state, args, sizeof(args)/sizeof(args[0]));
- size_t cur = 0;
-
- cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
- vt->mode.ctrl8bit ? "\x90" "1$r" : ESC_S "P" "1$r"); // DCS 1$r ...
- if(cur >= vt->tmpbuffer_len)
- return;
-
- for(int argi = 0; argi < argc; argi++) {
- cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
- argi == argc - 1 ? "%ld" :
- CSI_ARG_HAS_MORE(args[argi]) ? "%ld:" :
- "%ld;",
- CSI_ARG(args[argi]));
- if(cur >= vt->tmpbuffer_len)
- return;
- }
-
- cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
- vt->mode.ctrl8bit ? "m" "\x9C" : "m" ESC_S "\\"); // ... m ST
- if(cur >= vt->tmpbuffer_len)
- return;
-
- vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
- return;
- }
-
- case 'r':
- // Query DECSTBM
- vterm_push_output_sprintf_str(vt, C1_DCS, true,
- "1$r%d;%dr", state->scrollregion_top+1, SCROLLREGION_BOTTOM(state));
- return;
-
- case 's':
- // Query DECSLRM
- vterm_push_output_sprintf_str(vt, C1_DCS, true,
- "1$r%d;%ds", SCROLLREGION_LEFT(state)+1, SCROLLREGION_RIGHT(state));
- return;
-
- case ' '|('q'<<8): {
- // Query DECSCUSR
- int reply;
- switch(state->mode.cursor_shape) {
- case VTERM_PROP_CURSORSHAPE_BLOCK: reply = 2; break;
- case VTERM_PROP_CURSORSHAPE_UNDERLINE: reply = 4; break;
- case VTERM_PROP_CURSORSHAPE_BAR_LEFT: reply = 6; break;
- }
- if(state->mode.cursor_blink)
- reply--;
- vterm_push_output_sprintf_str(vt, C1_DCS, true,
- "1$r%d q", reply);
- return;
- }
-
- case '\"'|('q'<<8):
- // Query DECSCA
- vterm_push_output_sprintf_str(vt, C1_DCS, true,
- "1$r%d\"q", state->protected_cell ? 1 : 2);
- return;
- }
-
- vterm_push_output_sprintf_str(state->vt, C1_DCS, true, "0$r");
-}
-
-static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
-{
- VTermState *state = user;
-
- if(commandlen == 2 && strneq(command, "$q", 2)) {
- request_status_string(state, frag);
- return 1;
- }
- else if(state->fallbacks && state->fallbacks->dcs)
- if((*state->fallbacks->dcs)(command, commandlen, frag, state->fbdata))
- return 1;
-
- DEBUG_LOG("libvterm: Unhandled DCS %.*s\n", (int)commandlen, command);
- return 0;
-}
-
-static int on_apc(VTermStringFragment frag, void *user)
-{
- VTermState *state = user;
-
- if(state->fallbacks && state->fallbacks->apc)
- if((*state->fallbacks->apc)(frag, state->fbdata))
- return 1;
-
- /* No DEBUG_LOG because all APCs are unhandled */
- return 0;
-}
-
-static int on_pm(VTermStringFragment frag, void *user)
-{
- VTermState *state = user;
-
- if(state->fallbacks && state->fallbacks->pm)
- if((*state->fallbacks->pm)(frag, state->fbdata))
- return 1;
-
- /* No DEBUG_LOG because all PMs are unhandled */
- return 0;
-}
-
-static int on_sos(VTermStringFragment frag, void *user)
-{
- VTermState *state = user;
-
- if(state->fallbacks && state->fallbacks->sos)
- if((*state->fallbacks->sos)(frag, state->fbdata))
- return 1;
-
- /* No DEBUG_LOG because all SOSs are unhandled */
- return 0;
-}
-
-static int on_resize(int rows, int cols, void *user)
-{
- VTermState *state = user;
- VTermPos oldpos = state->pos;
-
- if(cols != state->cols) {
- unsigned char *newtabstops = vterm_allocator_malloc(state->vt, (cols + 7) / 8);
-
- /* TODO: This can all be done much more efficiently bytewise */
- int col;
- for(col = 0; col < state->cols && col < cols; col++) {
- unsigned char mask = 1 << (col & 7);
- if(state->tabstops[col >> 3] & mask)
- newtabstops[col >> 3] |= mask;
- else
- newtabstops[col >> 3] &= ~mask;
- }
-
- for( ; col < cols; col++) {
- unsigned char mask = 1 << (col & 7);
- if(col % 8 == 0)
- newtabstops[col >> 3] |= mask;
- else
- newtabstops[col >> 3] &= ~mask;
- }
-
- vterm_allocator_free(state->vt, state->tabstops);
- state->tabstops = newtabstops;
- }
-
- state->rows = rows;
- state->cols = cols;
-
- if(state->scrollregion_bottom > -1)
- UBOUND(state->scrollregion_bottom, state->rows);
- if(state->scrollregion_right > -1)
- UBOUND(state->scrollregion_right, state->cols);
-
- VTermStateFields fields = {
- .pos = state->pos,
- .lineinfos = { [0] = state->lineinfos[0], [1] = state->lineinfos[1] },
- };
-
- if(state->callbacks && state->callbacks->resize) {
- (*state->callbacks->resize)(rows, cols, &fields, state->cbdata);
- state->pos = fields.pos;
-
- state->lineinfos[0] = fields.lineinfos[0];
- state->lineinfos[1] = fields.lineinfos[1];
- }
- else {
- if(rows != state->rows) {
- for(int bufidx = BUFIDX_PRIMARY; bufidx <= BUFIDX_ALTSCREEN; bufidx++) {
- VTermLineInfo *oldlineinfo = state->lineinfos[bufidx];
- if(!oldlineinfo)
- continue;
-
- VTermLineInfo *newlineinfo = vterm_allocator_malloc(state->vt, rows * sizeof(VTermLineInfo));
-
- int row;
- for(row = 0; row < state->rows && row < rows; row++) {
- newlineinfo[row] = oldlineinfo[row];
- }
-
- for( ; row < rows; row++) {
- newlineinfo[row] = (VTermLineInfo){
- .doublewidth = 0,
- };
- }
-
- vterm_allocator_free(state->vt, state->lineinfos[bufidx]);
- state->lineinfos[bufidx] = newlineinfo;
- }
- }
- }
-
- state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY];
-
- if(state->at_phantom && state->pos.col < cols-1) {
- state->at_phantom = 0;
- state->pos.col++;
- }
-
- if(state->pos.row < 0)
- state->pos.row = 0;
- if(state->pos.row >= rows)
- state->pos.row = rows - 1;
- if(state->pos.col < 0)
- state->pos.col = 0;
- if(state->pos.col >= cols)
- state->pos.col = cols - 1;
-
- updatecursor(state, &oldpos, 1);
-
- return 1;
-}
-
-static const VTermParserCallbacks parser_callbacks = {
- .text = on_text,
- .control = on_control,
- .escape = on_escape,
- .csi = on_csi,
- .osc = on_osc,
- .dcs = on_dcs,
- .apc = on_apc,
- .pm = on_pm,
- .sos = on_sos,
- .resize = on_resize,
-};
-
-VTermState *vterm_obtain_state(VTerm *vt)
-{
- if(vt->state)
- return vt->state;
-
- VTermState *state = vterm_state_new(vt);
- vt->state = state;
-
- vterm_parser_set_callbacks(vt, &parser_callbacks, state);
-
- return state;
-}
-
-void vterm_state_reset(VTermState *state, int hard)
-{
- state->scrollregion_top = 0;
- state->scrollregion_bottom = -1;
- state->scrollregion_left = 0;
- state->scrollregion_right = -1;
-
- state->mode.keypad = 0;
- state->mode.cursor = 0;
- state->mode.autowrap = 1;
- state->mode.insert = 0;
- state->mode.newline = 0;
- state->mode.alt_screen = 0;
- state->mode.origin = 0;
- state->mode.leftrightmargin = 0;
- state->mode.bracketpaste = 0;
- state->mode.report_focus = 0;
-
- state->mouse_flags = 0;
-
- state->vt->mode.ctrl8bit = 0;
-
- for(int col = 0; col < state->cols; col++)
- if(col % 8 == 0)
- set_col_tabstop(state, col);
- else
- clear_col_tabstop(state, col);
-
- for(int row = 0; row < state->rows; row++)
- set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
-
- if(state->callbacks && state->callbacks->initpen)
- (*state->callbacks->initpen)(state->cbdata);
-
- vterm_state_resetpen(state);
-
- VTermEncoding *default_enc = state->vt->mode.utf8 ?
- vterm_lookup_encoding(ENC_UTF8, 'u') :
- vterm_lookup_encoding(ENC_SINGLE_94, 'B');
-
- for(int i = 0; i < 4; i++) {
- state->encoding[i].enc = default_enc;
- if(default_enc->init)
- (*default_enc->init)(default_enc, state->encoding[i].data);
- }
-
- state->gl_set = 0;
- state->gr_set = 1;
- state->gsingle_set = 0;
-
- state->protected_cell = 0;
-
- // Initialise the props
- settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, 1);
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
- settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
-
- if(hard) {
- state->pos.row = 0;
- state->pos.col = 0;
- state->at_phantom = 0;
-
- VTermRect rect = { 0, state->rows, 0, state->cols };
- erase(state, rect, 0);
- }
-}
-
-void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos)
-{
- *cursorpos = state->pos;
-}
-
-void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user)
-{
- if(callbacks) {
- state->callbacks = callbacks;
- state->cbdata = user;
-
- if(state->callbacks && state->callbacks->initpen)
- (*state->callbacks->initpen)(state->cbdata);
- }
- else {
- state->callbacks = NULL;
- state->cbdata = NULL;
- }
-}
-
-void *vterm_state_get_cbdata(VTermState *state)
-{
- return state->cbdata;
-}
-
-void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user)
-{
- if(fallbacks) {
- state->fallbacks = fallbacks;
- state->fbdata = user;
- }
- else {
- state->fallbacks = NULL;
- state->fbdata = NULL;
- }
-}
-
-void *vterm_state_get_unrecognised_fbdata(VTermState *state)
-{
- return state->fbdata;
-}
-
-int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val)
-{
- /* Only store the new value of the property if usercode said it was happy.
- * This is especially important for altscreen switching */
- if(state->callbacks && state->callbacks->settermprop)
- if(!(*state->callbacks->settermprop)(prop, val, state->cbdata))
- return 0;
-
- switch(prop) {
- case VTERM_PROP_TITLE:
- case VTERM_PROP_ICONNAME:
- // we don't store these, just transparently pass through
- return 1;
- case VTERM_PROP_CURSORVISIBLE:
- state->mode.cursor_visible = val->boolean;
- return 1;
- case VTERM_PROP_CURSORBLINK:
- state->mode.cursor_blink = val->boolean;
- return 1;
- case VTERM_PROP_CURSORSHAPE:
- state->mode.cursor_shape = val->number;
- return 1;
- case VTERM_PROP_REVERSE:
- state->mode.screen = val->boolean;
- return 1;
- case VTERM_PROP_ALTSCREEN:
- state->mode.alt_screen = val->boolean;
- state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY];
- if(state->mode.alt_screen) {
- VTermRect rect = {
- .start_row = 0,
- .start_col = 0,
- .end_row = state->rows,
- .end_col = state->cols,
- };
- erase(state, rect, 0);
- }
- return 1;
- case VTERM_PROP_MOUSE:
- state->mouse_flags = 0;
- if(val->number)
- state->mouse_flags |= MOUSE_WANT_CLICK;
- if(val->number == VTERM_PROP_MOUSE_DRAG)
- state->mouse_flags |= MOUSE_WANT_DRAG;
- if(val->number == VTERM_PROP_MOUSE_MOVE)
- state->mouse_flags |= MOUSE_WANT_MOVE;
- return 1;
- case VTERM_PROP_FOCUSREPORT:
- state->mode.report_focus = val->boolean;
- return 1;
-
- case VTERM_N_PROPS:
- return 0;
- }
-
- return 0;
-}
-
-void vterm_state_focus_in(VTermState *state)
-{
- if(state->mode.report_focus)
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "I");
-}
-
-void vterm_state_focus_out(VTermState *state)
-{
- if(state->mode.report_focus)
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "O");
-}
-
-const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row)
-{
- return state->lineinfo + row;
-}
-
-void vterm_state_set_selection_callbacks(VTermState *state, const VTermSelectionCallbacks *callbacks, void *user,
- char *buffer, size_t buflen)
-{
- if(buflen && !buffer)
- buffer = vterm_allocator_malloc(state->vt, buflen);
-
- state->selection.callbacks = callbacks;
- state->selection.user = user;
- state->selection.buffer = buffer;
- state->selection.buflen = buflen;
-}
-
-void vterm_state_send_selection(VTermState *state, VTermSelectionMask mask, VTermStringFragment frag)
-{
- VTerm *vt = state->vt;
-
- if(frag.initial) {
- /* TODO: support sending more than one mask bit */
- static const char selection_chars[] = "cpqs";
- int idx;
- for(idx = 0; idx < 4; idx++)
- if(mask & (1 << idx))
- break;
-
- vterm_push_output_sprintf_str(vt, C1_OSC, false, "52;%c;", selection_chars[idx]);
-
- state->tmp.selection.sendpartial = 0;
- }
-
- if(frag.len) {
- size_t bufcur = 0;
- char *buffer = state->selection.buffer;
-
- uint32_t x = 0;
- int n = 0;
-
- if(state->tmp.selection.sendpartial) {
- n = state->tmp.selection.sendpartial >> 24;
- x = state->tmp.selection.sendpartial & 0xFFFFFF;
-
- state->tmp.selection.sendpartial = 0;
- }
-
- while((state->selection.buflen - bufcur) >= 4 && frag.len) {
- x = (x << 8) | frag.str[0];
- n++;
- frag.str++, frag.len--;
-
- if(n == 3) {
- buffer[0] = base64_one((x >> 18) & 0x3F);
- buffer[1] = base64_one((x >> 12) & 0x3F);
- buffer[2] = base64_one((x >> 6) & 0x3F);
- buffer[3] = base64_one((x >> 0) & 0x3F);
-
- buffer += 4, bufcur += 4;
- x = 0;
- n = 0;
- }
-
- if(!frag.len || (state->selection.buflen - bufcur) < 4) {
- if(bufcur)
- vterm_push_output_bytes(vt, state->selection.buffer, bufcur);
-
- buffer = state->selection.buffer;
- bufcur = 0;
- }
- }
-
- if(n)
- state->tmp.selection.sendpartial = (n << 24) | x;
- }
-
- if(frag.final) {
- if(state->tmp.selection.sendpartial) {
- int n = state->tmp.selection.sendpartial >> 24;
- uint32_t x = state->tmp.selection.sendpartial & 0xFFFFFF;
- char *buffer = state->selection.buffer;
-
- /* n is either 1 or 2 now */
- x <<= (n == 1) ? 16 : 8;
-
- buffer[0] = base64_one((x >> 18) & 0x3F);
- buffer[1] = base64_one((x >> 12) & 0x3F);
- buffer[2] = (n == 1) ? '=' : base64_one((x >> 6) & 0x3F);
- buffer[3] = '=';
-
- vterm_push_output_sprintf_str(vt, 0, true, "%.*s", 4, buffer);
- }
- else
- vterm_push_output_sprintf_str(vt, 0, true, "");
- }
-}