vtgl

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

Commit: 4af608e8bdcc929907c6bd60abf3bdb434bf50b5
Parent: 5329fd7e405d7030cd24466b5d2368b12414ea19
Author: Randy Palamar
Date:   Wed,  6 Nov 2024 21:30:26 -0700

move mouse input to the platform layer

Diffstat:
Mdebug.c | 22++++++++--------------
Mdebug.h | 6++----
Mplatform_linux_x11.c | 52+++++++++++++++++++++++++++++++++++++++++++++++++++-
Mutil.c | 2+-
Mvtgl.c | 85+++++++++++++++++++++++++++++++++++--------------------------------------------
Mvtgl.h | 19+++++++++++++++++++
6 files changed, 119 insertions(+), 67 deletions(-)

diff --git a/debug.c b/debug.c @@ -186,7 +186,8 @@ refresh_collation(DebugState *ds) } static void -draw_debug_bar_chart(Term *t, DebugState *ds, RenderCtx *rc, v2 bar_chart_top_left, f32 bar_chart_magnitude) +draw_debug_bar_chart(Term *t, DebugState *ds, TerminalInput *input, RenderCtx *rc, + v2 bar_chart_top_left, f32 bar_chart_magnitude) { BEGIN_TIMED_BLOCK(); @@ -199,11 +200,6 @@ draw_debug_bar_chart(Term *t, DebugState *ds, RenderCtx *rc, v2 bar_chart_top_le f32 target_time = 1 / target_fps; f32 secs_scale = bar_chart_magnitude * target_fps; - /* TODO: store the mouse somewhere */ - f64 xpos, ypos; - glfwGetCursorPos(t->gl.window, &xpos, &ypos); - v2 mouse = {.x = xpos, .y = t->gl.window_size.h - ypos}; - /* TODO */ v4 fg = normalize_colour((Colour){.rgba = 0x1e9e33ff}); v4 colours[] = { @@ -238,7 +234,7 @@ draw_debug_bar_chart(Term *t, DebugState *ds, RenderCtx *rc, v2 bar_chart_top_le Rect r = {.pos = pos, .size = {.h = bar_thick, .w = time}}; if (r.size.w > 1.0f) { push_rect(rc, r, colours[dr->colour_index % ARRAY_COUNT(colours)]); - if (point_in_rect(mouse, r)) { + if (point_in_rect(input->mouse, r)) { hot_region = dr; hot_region_secs = cycs * cycs_to_secs; } @@ -280,23 +276,21 @@ draw_debug_bar_chart(Term *t, DebugState *ds, RenderCtx *rc, v2 bar_chart_top_le stream_push_s8(&txt, c_str_to_s8(txt_meta->file_name)); stream_push_byte(&txt, ':'); stream_push_u64(&txt, txt_meta->line_number); - push_s8(rc, mouse, fg, g_ui_debug_font_id, stream_to_s8(&txt)); + push_s8(rc, input->mouse, fg, g_ui_debug_font_id, stream_to_s8(&txt)); } - /* TODO: this should be stored so that we can properly mask */ - b32 pressed = glfwGetMouseButton(t->gl.window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS; - if (pressed && pressed != ds->mouse_pressed_last_frame) { + ButtonState *mouse_left = input->mouse_buttons + MOUSE_LEFT; + if (mouse_left->ended_down && mouse_left->transitions > 0) { if (hot_region) ds->selected_metadata = hot_region->meta; else ds->selected_metadata = 0; refresh_collation(ds); } - ds->mouse_pressed_last_frame = pressed; END_TIMED_BLOCK(); } static void -draw_debug_overlay(TerminalMemory *term_memory, RenderCtx *rc) +draw_debug_overlay(TerminalMemory *term_memory, TerminalInput *input, RenderCtx *rc) { Term *t = term_memory->memory; DebugState *ds = term_memory->debug_memory; @@ -308,7 +302,7 @@ draw_debug_overlay(TerminalMemory *term_memory, RenderCtx *rc) v2 bar_chart_top_left = v2_from_iv2(t->gl.window_size); bar_chart_top_left.x *= 0.5; - draw_debug_bar_chart(t, ds, rc, bar_chart_top_left, 0.25 * t->gl.window_size.w); + draw_debug_bar_chart(t, ds, input, rc, bar_chart_top_left, 0.25 * t->gl.window_size.w); Arena memory = *ds->temp_memory.arena; diff --git a/debug.h b/debug.h @@ -61,7 +61,6 @@ typedef struct { u32 frame_draw_count; DebugMetadata *selected_metadata; - b32 mouse_pressed_last_frame; u32 record_count; u32 record_working_index; @@ -157,10 +156,9 @@ static DebugTable g_debug_table; RECORD_DEBUG_META_COMMON(__counter_##name, #name) #define END_NAMED_BLOCK(name) RECORD_DEBUG_EVENT(__counter_##name, DE_END) - -typedef struct TerminalMemory TerminalMemory; +typedef struct TerminalInput TerminalInput; typedef struct Term Term; typedef struct RenderCtx RenderCtx; static void dump_lines_to_file(Term *t); -static void draw_debug_overlay(TerminalMemory *memory, RenderCtx *rc); +static void draw_debug_overlay(TerminalMemory *memory, TerminalInput *input, RenderCtx *rc); #endif diff --git a/platform_linux_x11.c b/platform_linux_x11.c @@ -3,7 +3,6 @@ #include <GLFW/glfw3.h> #include "util.h" - #include "vtgl.h" #ifndef VERSION @@ -79,6 +78,14 @@ do_debug(TerminalMemory *t, Stream *err) #endif /* _DEBUG */ static void +button_action(ButtonState *button, b32 pressed) +{ + if (pressed != button->ended_down) + button->transitions++; + button->ended_down = pressed; +} + +static void glfw_error_callback(int code, const char *desc) { u8 buf[256]; @@ -100,6 +107,31 @@ fb_callback(GLFWwindow *win, i32 w, i32 h) ctx->input->window_size = (iv2){.w = w, .h = h}; } +static void +mouse_button_callback(GLFWwindow *win, i32 button, i32 action, i32 modifiers) +{ + tmp_user_ctx *ctx = glfwGetWindowUserPointer(win); + TerminalInput *input = ctx->input; + + switch (button) { + case GLFW_MOUSE_BUTTON_LEFT: + button_action(input->mouse_buttons + MOUSE_LEFT, action == GLFW_PRESS); + break; + case GLFW_MOUSE_BUTTON_RIGHT: + button_action(input->mouse_buttons + MOUSE_RIGHT, action == GLFW_PRESS); + break; + case GLFW_MOUSE_BUTTON_MIDDLE: + button_action(input->mouse_buttons + MOUSE_MIDDLE, action == GLFW_PRESS); + break; + case GLFW_MOUSE_BUTTON_4: + button_action(input->mouse_buttons + MOUSE_EXTENDED_0, action == GLFW_PRESS); + break; + case GLFW_MOUSE_BUTTON_5: + button_action(input->mouse_buttons + MOUSE_EXTENDED_1, action == GLFW_PRESS); + break; + } +} + static GLFWwindow * init_window(tmp_user_ctx *ctx, iv2 window_size) { @@ -125,6 +157,7 @@ init_window(tmp_user_ctx *ctx, iv2 window_size) glfwSwapInterval(1); glfwSetFramebufferSizeCallback(window, fb_callback); + glfwSetMouseButtonCallback(window, mouse_button_callback); return window; } @@ -238,6 +271,21 @@ check_shaders(GLCtx *gl, Arena a, Stream *err) } static void +update_input(GLFWwindow *win, TerminalInput *input) +{ + /* NOTE: mouse */ + input->mouse_scroll = (v2){0}; + + f64 mouse_x, mouse_y; + glfwGetCursorPos(win, &mouse_x, &mouse_y); + input->mouse.x = mouse_x; + input->mouse.y = input->window_size.h - mouse_y; + + for (u32 i = 0; i < ARRAY_COUNT(input->mouse_buttons); i++) + input->mouse_buttons[i].transitions = 0; +} + +static void usage(char *argv0, Stream *err) { stream_push_s8(err, s8("usage: ")); @@ -362,6 +410,8 @@ main(i32 argc, char *argv[], char *envp[]) f64 last_time = os_get_time(); while (!glfwWindowShouldClose(window)) { + update_input(window, &input); + do_debug(&term_memory, &error_stream); /* TODO: cpu time excluding waiting for the vblank */ diff --git a/util.c b/util.c @@ -55,7 +55,7 @@ static Range normalize_range(Range r) { Range result; - if (r.start.y < r.end.y) { + if (!is_valid_range(r) || r.start.y < r.end.y) { result = r; } else if (r.end.y < r.start.y) { result = (Range){.start = r.end, .end = r.start}; diff --git a/vtgl.c b/vtgl.c @@ -64,11 +64,11 @@ get_occupied_size(Term *t) } static v2 -get_terminal_bot_left(Term *t) +get_terminal_top_left(Term *t) { v2 os = get_occupied_size(t); v2 delta = {.x = t->gl.window_size.w - os.w, .y = t->gl.window_size.h - os.h}; - v2 result = {.x = delta.x / 2, .y = delta.y / 2}; + v2 result = {.x = delta.x / 2, .y = t->gl.window_size.h - delta.y / 2}; return result; } @@ -443,10 +443,10 @@ mouse_to_cell_space(Term *t, v2 mouse) { iv2 result = {0}; v2 cell_size = get_cell_size(&t->fa); - v2 bot_left = get_terminal_bot_left(t); + v2 top_left = get_terminal_top_left(t); - result.x = (i32)((mouse.x - bot_left.x) / cell_size.w); - result.y = (i32)((mouse.y - bot_left.y) / cell_size.h); + result.x = (i32)((mouse.x - top_left.x) / cell_size.w); + result.y = (i32)((top_left.y - mouse.y) / cell_size.h); CLAMP(result.x, 0, t->size.w - 1); CLAMP(result.y, 0, t->size.h - 1); @@ -493,25 +493,43 @@ stream_push_selection(Stream *s, TermView *tv, Range sel, u32 term_width) } static void -update_selection(Term *t) +update_selection(Term *t, v2 mouse, ButtonState *mouse_left) { Selection *sel = &t->selection; - b32 held = glfwGetMouseButton(t->gl.window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS; + + if (mouse_left->transitions && mouse_left->ended_down) { + if (is_valid_range(sel->range)) + t->gl.flags |= UPDATE_RENDER_BUFFER; + + sel->range.end = INVALID_RANGE_END; + sel->last_mouse = mouse; + sel->click_param = DOUBLE_CLICK_TIME; + + if (sel->state != SS_WORDS) + sel->state++; + + iv2 cell = mouse_to_cell_space(t, sel->last_mouse); + if (t->views[t->view_idx].fb.rows[cell.y][cell.x].bg & ATTR_WDUMMY) { + ASSERT(t->views[t->view_idx].fb.rows[cell.y][cell.x - 1].bg & ATTR_WIDE); + cell.x--; + } + + if (sel->state == SS_WORDS) sel->anchor = get_word_around_cell(t, cell); + else sel->anchor = (Range){.start = cell, .end = cell}; + + return; + } sel->click_param -= dt_for_frame; if (sel->click_param < 0) { sel->click_param = 0; - if (!held) + if (!mouse_left->ended_down) sel->state = SS_NONE; } - if (!held) + if (!mouse_left->ended_down) return; - f64 xpos, ypos; - glfwGetCursorPos(t->gl.window, &xpos, &ypos); - v2 mouse = {.x = xpos, .y = ypos}; - if (sel->state != SS_WORDS && (mouse.x == sel->last_mouse.x && mouse.y == sel->last_mouse.y)) return; sel->last_mouse = mouse; @@ -711,38 +729,10 @@ key_callback(GLFWwindow *win, i32 key, i32 sc, i32 act, i32 mods) } static void -mouse_button_callback(GLFWwindow *win, i32 btn, i32 act, i32 mod) +handle_keybindings(Term *t, TerminalInput *input) { - tmp_user_ctx *ctx = glfwGetWindowUserPointer(win); - Term *t = ctx->memory->memory; /* TODO: map other mouse buttons */ - if (btn != GLFW_MOUSE_BUTTON_LEFT) - return; - - if (act == GLFW_RELEASE) - return; - - f64 xpos, ypos; - glfwGetCursorPos(win, &xpos, &ypos); - t->selection.range.end = (iv2){.x = -1, .y = -1}; - t->selection.last_mouse = (v2){.x = xpos, .y = ypos}; - t->selection.click_param = DOUBLE_CLICK_TIME; - - if (t->selection.state != SS_WORDS) - t->selection.state++; - - iv2 cell = mouse_to_cell_space(t, t->selection.last_mouse); - if (t->views[t->view_idx].fb.rows[cell.y][cell.x].bg & ATTR_WDUMMY) { - ASSERT(t->views[t->view_idx].fb.rows[cell.y][cell.x - 1].bg & ATTR_WIDE); - cell.x--; - } - - if (t->selection.state == SS_WORDS) { - t->selection.anchor = get_word_around_cell(t, cell); - } else { - t->selection.anchor = (Range){.start = cell, .end = cell}; - t->selection.range.end = INVALID_RANGE_END; - } + update_selection(t, input->mouse, input->mouse_buttons + MOUSE_LEFT); } static void @@ -824,7 +814,6 @@ reset_terminal(TerminalMemory *memory) Term *t = memory->memory; glfwSetCharCallback(t->gl.window, char_callback); glfwSetKeyCallback(t->gl.window, key_callback); - glfwSetMouseButtonCallback(t->gl.window, mouse_button_callback); //glfwSetWindowRefreshCallback(t->gl.window, refresh_callback); glfwSetScrollCallback(t->gl.window, scroll_callback); } @@ -977,13 +966,15 @@ DEBUG_EXPORT VTGL_FRAME_STEP_FN(vtgl_frame_step) END_NAMED_BLOCK(input_from_child); + BEGIN_NAMED_BLOCK(mouse_and_keyboard_input); + handle_keybindings(t, input, &memory->platform_api); + END_NAMED_BLOCK(mouse_and_keyboard_input); + if (t->gl.flags & (NEEDS_REFILL|NEEDS_FULL_REFILL)) { blit_lines(t, t->arena_for_frame, 0); t->gl.flags |= UPDATE_RENDER_BUFFER; } - update_selection(t); - set_projection_matrix(&t->gl); BEGIN_NAMED_BLOCK(update_render); @@ -1043,7 +1034,7 @@ DEBUG_EXPORT VTGL_FRAME_STEP_FN(vtgl_frame_step) * processing/effects shader */ BEGIN_NAMED_BLOCK(debug_overlay); glUseProgram(t->gl.programs[SHADER_RECTS]); - draw_debug_overlay(memory, &rc); + draw_debug_overlay(memory, input, &rc); flush_render_push_buffer(&rc); END_NAMED_BLOCK(debug_overlay); diff --git a/vtgl.h b/vtgl.h @@ -17,11 +17,30 @@ typedef struct { platform_write_fn *write; } PlatformAPI; +enum mouse_buttons { + MOUSE_LEFT, + MOUSE_RIGHT, + MOUSE_MIDDLE, + MOUSE_EXTENDED_0, + MOUSE_EXTENDED_1, + MOUSE_BUTTON_COUNT, +}; + typedef struct { + /* TODO: is this even supported or does GLFW only call you once per poll? */ + i32 transitions; + b32 ended_down; +} ButtonState; + +typedef struct TerminalInput { b32 data_available; b32 executable_reloaded; iv2 window_size; + v2 mouse; + v2 mouse_scroll; + ButtonState mouse_buttons[MOUSE_BUTTON_COUNT]; + f32 dt; } TerminalInput;