vtgl

terminal emulator implemented in OpenGL
git clone anongit@rnpnr.xyz:vtgl.git
Log | Files | Refs | Feed | LICENSE

Commit: e6e87c06a0103378165dc3fd65c7a9e16d9412ad
Parent: 3b575234f1161b46ba4df154e7fa5a40592531ca
Author: Randy Palamar
Date:   Sun, 17 Nov 2024 17:55:30 -0700

support mouse motion tracking and SGR reporting scheme

Diffstat:
Mterminal.c | 16++++++++++++----
Mutil.h | 7+++++--
Mvtgl.c | 125+++++++++++++++++++++++++++++++++++++++++++------------------------------------
3 files changed, 85 insertions(+), 63 deletions(-)

diff --git a/terminal.c b/terminal.c @@ -570,10 +570,6 @@ set_mode(Term *t, CSI *csi, b32 set, ModeState src, ModeState *dest) dest->win &= ~WM_REVERSE; if (set) dest->win |= (src.win & WM_REVERSE); break; - case PRIV(1000): /* xterm: report mouse button presses */ - dest->win &= ~WM_MOUSE_MASK; - if (set) dest->win |= (src.win & WM_MOUSE_BTN); - break; case PRIV(6): /* DECOM: Cursor Origin Mode */ if (set) t->cursor.state |= CURSOR_ORIGIN; else t->cursor.state &= ~CURSOR_ORIGIN; @@ -583,6 +579,18 @@ set_mode(Term *t, CSI *csi, b32 set, ModeState src, ModeState *dest) dest->term &= ~TM_AUTO_WRAP; if (set) dest->term |= (src.term & TM_AUTO_WRAP); break; + case PRIV(1000): /* xterm: report mouse button presses */ + dest->win &= ~WM_MOUSE_MASK; + if (set) dest->win |= (src.win & WM_MOUSE_BTN); + break; + case PRIV(1002): /* xterm: cell motion tracking */ + dest->win &= ~WM_MOUSE_MASK; + if (set) dest->win |= (src.win & WM_MOUSE_TRK); + break; + case PRIV(1006): /* xterm: SGR mouse mode */ + dest->win &= ~WM_MOUSE_SGR; + if (set) dest->win |= (src.win & WM_MOUSE_SGR); + break; case PRIV(3): /* DECCOLM: 132/80 Column Mode */ case PRIV(4): /* DECSCLM: Fast/Slow Scroll */ case PRIV(8): /* DECARM: Auto-Repeat Keys */ diff --git a/util.h b/util.h @@ -260,10 +260,11 @@ enum window_mode { WM_MOUSE_X10 = 1 << 5, WM_MOUSE_BTN = 1 << 6, WM_MOUSE_TRK = 1 << 7, + WM_MOUSE_SGR = 1 << 8, - WM_MOUSE_MASK = WM_MOUSE_X10|WM_MOUSE_BTN|WM_MOUSE_TRK, + WM_MOUSE_MASK = WM_MOUSE_X10|WM_MOUSE_BTN|WM_MOUSE_TRK|WM_MOUSE_SGR, - WM_ALL_MASK = WM_MOUSE_TRK|(WM_MOUSE_TRK - 1), + WM_ALL_MASK = WM_MOUSE_SGR|(WM_MOUSE_SGR - 1), }; #define MODE_STATE_ALL_MASK (ModeState){.term = TM_ALL_MASK, .win = WM_ALL_MASK} @@ -483,6 +484,8 @@ typedef struct { u32 click_count; f32 multi_click_t; + + iv2 last_cell_report; } InteractionState; enum selection_states { diff --git a/vtgl.c b/vtgl.c @@ -199,13 +199,6 @@ pressed_last_frame(ButtonState *button) return result; } -static b32 -released_last_frame(ButtonState *button) -{ - b32 result = !button->ended_down && button->transitions; - return result; -} - static void set_projection_matrix(GLCtx *gl) { @@ -767,21 +760,42 @@ KEYBIND_FN(zoom) } static void -report_mouse(Term *t, TerminalInput *input, b32 pressed) +report_mouse(Term *t, TerminalInput *input, b32 released, b32 beginning) { - i32 value; + if ((t->mode.win & WM_MOUSE_X10) && released) + return; + + iv2 pos = mouse_to_cell_space(t, input->mouse); + if ((pos.x > (255 - 32)) || (pos.y > (255 - 32))) + return; + /* TODO: pass the button into this function once they are given in order */ /* TODO: extended mouse buttons (up to button 11) should also be encoded */ - if (!pressed) value = 3; - else if (pressed_last_frame(input->keys + MOUSE_LEFT)) value = 0; - else if (pressed_last_frame(input->keys + MOUSE_MIDDLE)) value = 1; - else if (pressed_last_frame(input->keys + MOUSE_RIGHT)) value = 2; - else if (input->mouse_scroll.y > 0) value = 64 + (4 - 4); - else if (input->mouse_scroll.y < 0) value = 64 + (5 - 4); - else return; - - if ((t->mode.win & WM_MOUSE_X10) && !pressed) - return; + i32 button = 0; + if (input->keys[MOUSE_LEFT].ended_down) button = 1; + else if (input->keys[MOUSE_MIDDLE].ended_down) button = 2; + else if (input->keys[MOUSE_RIGHT].ended_down) button = 3; + else if (input->mouse_scroll.y > 0) button = 4; + else if (input->mouse_scroll.y < 0) button = 5; + + i32 value; + if (t->mode.win & WM_MOUSE_TRK && !beginning && !released) { + if (equal_iv2(t->interaction.last_cell_report, pos)) + return; + value = 32; + } else { + value = 0; + } + t->interaction.last_cell_report = pos; + + if ((t->mode.win & WM_MOUSE_SGR) && !button) + value += 0; + else if (!button) + value += 3; + else if (button >= 4) + value += 64 + button - 4; + else + value += button - 1; if (!(t->mode.win & WM_MOUSE_X10)) { value += ((input->modifiers & MOD_SHIFT) ? 4 : 0) @@ -789,22 +803,33 @@ report_mouse(Term *t, TerminalInput *input, b32 pressed) + ((input->modifiers & MOD_CONTROL) ? 16 : 0); } - iv2 pos = mouse_to_cell_space(t, input->mouse); - if ((pos.x < (255 - 32)) && (pos.y < (255 - 32))) { - Stream buf = arena_stream(t->arena_for_frame); + Stream buf = arena_stream(t->arena_for_frame); + if (t->mode.win & WM_MOUSE_SGR) { + stream_push_s8(&buf, s8("\x1B[<")); + stream_push_i64(&buf, value); + stream_push_byte(&buf, ';'); + stream_push_i64(&buf, pos.x + 1); + stream_push_byte(&buf, ';'); + stream_push_i64(&buf, pos.y + 1); + stream_push_byte(&buf, released? 'm' : 'M'); + } else if ((pos.x < (255 - 32)) && (pos.y < (255 - 32))) { stream_push_s8(&buf, s8("\x1B[M")); stream_push_byte(&buf, 32 + value); stream_push_byte(&buf, 32 + pos.x + 1); stream_push_byte(&buf, 32 + pos.y + 1); - t->platform->write(t->child, stream_to_s8(&buf), 0); + } else { + INVALID_CODE_PATH; + return; } + + t->platform->write(t->child, stream_to_s8(&buf), 0); } static void begin_terminal_interaction(Term *t, TerminalInput *input, u32 click_count) { if (t->mode.win & WM_MOUSE_MASK) { - report_mouse(t, input, 1); + report_mouse(t, input, 0, 1); } else { if (pressed_last_frame(input->keys + MOUSE_LEFT)) begin_selection(t, click_count, input->mouse); @@ -814,12 +839,14 @@ begin_terminal_interaction(Term *t, TerminalInput *input, u32 click_count) static b32 terminal_interaction(Term *t, PlatformAPI *platform, TerminalInput *input, u32 click_count) { - b32 should_end_interaction = 0; + + b32 should_end_interaction = !input->keys[MOUSE_LEFT].ended_down && + !input->keys[MOUSE_MIDDLE].ended_down && + !input->keys[MOUSE_RIGHT].ended_down; if (t->mode.win & WM_MOUSE_MASK) { - should_end_interaction = released_last_frame(input->keys + MOUSE_LEFT) || - released_last_frame(input->keys + MOUSE_MIDDLE) || - released_last_frame(input->keys + MOUSE_RIGHT); + if (t->mode.win & WM_MOUSE_TRK) + report_mouse(t, input, should_end_interaction, 0); } else { update_selection(t, input); if (pressed_last_frame(input->keys + MOUSE_MIDDLE)) @@ -848,18 +875,6 @@ terminal_interaction(Term *t, PlatformAPI *platform, TerminalInput *input, u32 c return should_end_interaction; } -static void -end_terminal_interaction(Term *t, TerminalInput *input) -{ - if (t->mode.win & WM_MOUSE_MASK) { - b32 end_from_release = released_last_frame(input->keys + MOUSE_LEFT) || - released_last_frame(input->keys + MOUSE_MIDDLE) || - released_last_frame(input->keys + MOUSE_RIGHT); - if (end_from_release) - report_mouse(t, input, 0); - } -} - DEBUG_EXPORT VTGL_HANDLE_KEYS_FN(vtgl_handle_keys) { Term *t = memory->memory; @@ -1010,11 +1025,7 @@ begin_interaction(InteractionState *is, TerminalInput *input) static void end_interaction(InteractionState *is, TerminalInput *input) { - switch (is->active.type) { - case IS_TERM: end_terminal_interaction(is->active.var.value, input); break; - default: break; - } - is->active = (Interaction){0}; + is->active = (Interaction){.type = IS_NONE}; } static void @@ -1034,19 +1045,19 @@ handle_interactions(Term *t, TerminalInput *input, PlatformAPI *platform) end_interaction(is, input); begin_interaction(is, input); } + } - switch (is->active.type) { - case IS_NONE: break; - case IS_NOP: break; - case IS_SET: end_interaction(is, input); break; - case IS_AUTO: /* TODO */ break; - case IS_DRAG: /* TODO */ break; - case IS_DEBUG: /* TODO */ break; - case IS_TERM: { - if (terminal_interaction(t, platform, input, is->click_count)) - end_interaction(is, input); - } break; - } + switch (is->active.type) { + case IS_NONE: break; + case IS_NOP: break; + case IS_SET: end_interaction(is, input); break; + case IS_AUTO: /* TODO */ break; + case IS_DRAG: /* TODO */ break; + case IS_DEBUG: /* TODO */ break; + case IS_TERM: { + if (terminal_interaction(t, platform, input, is->click_count)) + end_interaction(is, input); + } break; } }