diff options
| author | bfredl <bjorn.linse@gmail.com> | 2022-05-02 21:10:01 +0200 |
|---|---|---|
| committer | bfredl <bjorn.linse@gmail.com> | 2022-12-31 13:25:26 +0100 |
| commit | 43e8ec92de9e0850e7d202cb7ff9051bc408447e (patch) | |
| tree | fcaef65604e05fb9cc34cf7543c7d92af9c38dcf /src/nvim/tui | |
| parent | 24488169564c39a506c235bf6a33b8e23a8cb528 (diff) | |
| download | rneovim-43e8ec92de9e0850e7d202cb7ff9051bc408447e.tar.gz rneovim-43e8ec92de9e0850e7d202cb7ff9051bc408447e.tar.bz2 rneovim-43e8ec92de9e0850e7d202cb7ff9051bc408447e.zip | |
fix(tui): more work in the TUI
Diffstat (limited to 'src/nvim/tui')
| -rw-r--r-- | src/nvim/tui/input.c | 66 | ||||
| -rw-r--r-- | src/nvim/tui/tui.c | 198 |
2 files changed, 89 insertions, 175 deletions
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index ca1f7c25d7..c6066597f0 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -220,17 +220,11 @@ static void tinput_wait_enqueue(void **argv) const size_t len = rbuffer_size(input->key_buffer); String keys = { .data = xmallocz(len), .size = len }; rbuffer_read(input->key_buffer, keys.data, len); - if (ui_client_channel_id) { - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(keys)); // 'data' - ADD(args, BOOLEAN_OBJ(true)); // 'crlf' - ADD(args, INTEGER_OBJ(input->paste)); // 'phase' - rpc_send_event(ui_client_channel_id, "nvim_paste", args); - } else { - // TODO - // multiqueue_put(main_loop.events, tinput_paste_event, 3, - // keys.data, keys.size, (intptr_t)input->paste); - } + Array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(keys)); // 'data' + ADD(args, BOOLEAN_OBJ(true)); // 'crlf' + ADD(args, INTEGER_OBJ(input->paste)); // 'phase' + rpc_send_event(ui_client_channel_id, "nvim_paste", args); if (input->paste == 1) { // Paste phase: "continue" input->paste = 2; @@ -239,39 +233,22 @@ static void tinput_wait_enqueue(void **argv) } else { // enqueue input for the main thread or Nvim server RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) { const String keys = { .data = buf, .size = len }; - size_t consumed; - if (ui_client_channel_id) { - Array args = ARRAY_DICT_INIT; - Error err = ERROR_INIT; - ADD(args, STRING_OBJ(copy_string(keys, NULL))); - // TODO(bfredl): could be non-blocking now with paste? - ArenaMem res_mem = NULL; - Object result = rpc_send_call(ui_client_channel_id, "nvim_input", args, &res_mem, &err); - consumed = result.type == kObjectTypeInteger ? (size_t)result.data.integer : 0; - arena_mem_free(res_mem); - } else { - // TODO - // consumed = input_enqueue(keys); - abort(); - } - if (consumed) { - rbuffer_consumed(input->key_buffer, consumed); - } + Array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(copy_string(keys, NULL))); + // NOTE: This is non-blocking and won't check partially processed input, + // but should be fine as all big sends are handled with nvim_paste, not nvim_input + rpc_send_event(ui_client_channel_id, "nvim_input", args); + rbuffer_consumed(input->key_buffer, len); rbuffer_reset(input->key_buffer); - if (consumed < len) { - break; - } } } } - static void tinput_flush(TermInput *input, bool wait_until_empty) { size_t drain_boundary = wait_until_empty ? 0 : 0xff; - // TODO: fuuuuuuuuuuuuuuu do { - tinput_wait_enqueue((void**)&input); + tinput_wait_enqueue((void **)&input); } while (rbuffer_size(input->key_buffer) > drain_boundary); } @@ -550,7 +527,8 @@ static bool handle_focus_event(TermInput *input) || !rbuffer_cmp(input->read_stream.buffer, "\x1b[O", 3))) { bool focus_gained = *rbuffer_get(input->read_stream.buffer, 2) == 'I'; // Advance past the sequence - + rbuffer_consumed(input->read_stream.buffer, 3); + Array args = ARRAY_DICT_INIT; ADD(args, BOOLEAN_OBJ(focus_gained)); rpc_send_event(ui_client_channel_id, "nvim_ui_set_focus", args); @@ -602,11 +580,13 @@ static HandleState handle_bracketed_paste(TermInput *input) static void set_bg(char *bgvalue) { - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(cstr_to_string("term_background"))); - ADD(args, STRING_OBJ(cstr_as_string(xstrdup(bgvalue)))); - - rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args); + if (ui_client_attached) { + Array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstr_to_string("term_background"))); + ADD(args, STRING_OBJ(cstr_as_string(xstrdup(bgvalue)))); + + rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args); + } } // During startup, tui.c requests the background color (see `ext.get_bg`). @@ -688,8 +668,10 @@ static HandleState handle_background_color(TermInput *input) double g = (double)rgb[1] / (double)rgb_max[1]; double b = (double)rgb[2] / (double)rgb_max[2]; double luminance = (0.299 * r) + (0.587 * g) + (0.114 * b); // CCIR 601 - char *bgvalue = luminance < 0.5 ? "dark" : "light"; + bool is_dark = luminance < 0.5; + char *bgvalue = is_dark ? "dark" : "light"; DLOG("bg response: %s", bgvalue); + ui_client_bg_respose = is_dark ? kTrue : kFalse; set_bg(bgvalue); input->waiting_for_bg_response = 0; } else if (!done && !bad) { diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 4634c77a1f..89ca77a09a 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -18,6 +18,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" #include "nvim/ascii.h" +#include "nvim/cursor_shape.h" #include "nvim/event/defs.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" @@ -37,18 +38,15 @@ #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/os/signal.h" -#include "nvim/ui.h" -#include "nvim/vim.h" #ifdef MSWIN # include "nvim/os/os_win_console.h" #endif -#include "nvim/cursor_shape.h" -#include "nvim/macros.h" #include "nvim/tui/input.h" #include "nvim/tui/terminfo.h" #include "nvim/tui/tui.h" #include "nvim/ugrid.h" -#include "nvim/msgpack_rpc/channel.h" +#include "nvim/ui.h" +#include "nvim/vim.h" // Space reserved in two output buffers to make the cursor normal or invisible // when flushing. No existing terminal will require 32 bytes to do that. @@ -108,8 +106,8 @@ struct TUIData { uv_pipe_t pipe; } output_handle; bool out_isatty; - SignalWatcher winch_handle, cont_handle; - bool cont_received; + SignalWatcher winch_handle; + uv_timer_t startup_delay_timer; UGrid grid; kvec_t(Rect) invalid_regions; int row, col; @@ -164,7 +162,6 @@ struct TUIData { static int got_winch = 0; static bool cursor_style_enabled = false; -char *termname_local; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/tui.c.generated.h" #endif @@ -199,8 +196,30 @@ UI *tui_start(void) CLEAR_FIELD(ui->ui_ext); ui->ui_ext[kUILinegrid] = true; ui->ui_ext[kUITermColors] = true; - - tui_main(ui); + + TUIData *data = xcalloc(1, sizeof(TUIData)); + ui->data = data; + data->ui = ui; + data->is_starting = true; + data->screenshot = NULL; + data->stopped = false; + data->loop = &main_loop; + kv_init(data->invalid_regions); + signal_watcher_init(data->loop, &data->winch_handle, ui); + + // TODO(bfredl): zero hl is empty, send this explicitly? + kv_push(data->attrs, HLATTRS_INIT); + + data->input.tk_ti_hook_fn = tui_tk_ti_getstr; + tinput_init(&data->input, &main_loop); + ugrid_init(&data->grid); + tui_terminal_start(ui); + + uv_timer_init(&data->loop->uv, &data->startup_delay_timer); + data->startup_delay_timer.data = ui; + uv_timer_start(&data->startup_delay_timer, after_startup_cb, + 100, 0); + ui_attach_impl(ui, 0); return ui; @@ -291,18 +310,18 @@ static void terminfo_start(UI *ui) #endif // Set up unibilium/terminfo. - termname_local = NULL; + ui_client_termname = NULL; if (term) { - os_env_var_lock(); data->ut = unibi_from_term(term); - os_env_var_unlock(); if (data->ut) { - termname_local = xstrdup(term); - data->term = xstrdup(term); + ui_client_termname = xstrdup(term); + if (!data->term) { + data->term = xstrdup(term); + } } } if (!data->ut) { - data->ut = terminfo_from_builtin(term, &termname_local); + data->ut = terminfo_from_builtin(term, &ui_client_termname); } // None of the following work over SSH; see :help TERM . @@ -437,13 +456,18 @@ static void tui_terminal_start(UI *ui) { TUIData *data = ui->data; data->print_attr_id = -1; - ugrid_init(&data->grid); terminfo_start(ui); tui_guess_size(ui); signal_watcher_start(&data->winch_handle, sigwinch_cb, SIGWINCH); tinput_start(&data->input); } +static void after_startup_cb(uv_timer_t *handle) +{ + UI *ui = handle->data; + tui_terminal_after_startup(ui); +} + static void tui_terminal_after_startup(UI *ui) FUNC_ATTR_NONNULL_ALL { @@ -455,26 +479,30 @@ static void tui_terminal_after_startup(UI *ui) flush_buf(ui); } +/// stop the terminal but allow it to restart later (like after suspend) static void tui_terminal_stop(UI *ui) { TUIData *data = ui->data; if (uv_is_closing(STRUCT_CAST(uv_handle_t, &data->output_handle))) { // Race between SIGCONT (tui.c) and SIGHUP (os/signal.c)? #8075 ELOG("TUI already stopped (race?)"); - data->stopped = true; + data->stopped = true; return; } tinput_stop(&data->input); signal_watcher_stop(&data->winch_handle); terminfo_stop(ui); - ugrid_free(&data->grid); } static void tui_stop(UI *ui) { - tui_terminal_stop(ui); TUIData *data = ui->data; + tui_terminal_stop(ui); + tinput_destroy(&data->input); data->stopped = true; + signal_watcher_close(&data->winch_handle, NULL); + uv_close((uv_handle_t *)&data->startup_delay_timer, NULL); + ui_detach_impl(ui, 0); } /// Returns true if UI `ui` is stopped. @@ -484,57 +512,11 @@ static bool tui_is_stopped(UI *ui) return data->stopped; } -// Main function for TUI -static void tui_main(UI *ui) -{ - TUIData *data = xcalloc(1, sizeof(TUIData)); - ui->data = data; - data->ui = ui; - data->is_starting = true; - data->screenshot = NULL; - data->stopped = false; - data->loop = &main_loop; - kv_init(data->invalid_regions); - signal_watcher_init(data->loop, &data->winch_handle, ui); - signal_watcher_init(data->loop, &data->cont_handle, data); -#ifdef UNIX - signal_watcher_start(&data->cont_handle, sigcont_cb, SIGCONT); -#endif - - // TODO(bfredl): zero hl is empty, send this explicitly? - kv_push(data->attrs, HLATTRS_INIT); - - data->input.tk_ti_hook_fn = tui_tk_ti_getstr; - tinput_init(&data->input, &main_loop); - tui_terminal_start(ui); - // TODO: borked! - // loop_schedule(&main_loop, event_create(show_termcap_event, 1, data->ut)); - -} - -void tui_execute(void) - FUNC_ATTR_NORETURN +#ifdef EXITFREE +void tui_free_all_mem(UI *ui) { - UI *ui = ui_get_by_index(1); - LOOP_PROCESS_EVENTS(&main_loop, main_loop.events, -1); - tui_io_driven_loop(ui); - tui_exit_safe(ui); - getout(0); -} - -// Doesn't return until the TUI is closed (by call of tui_stop()) -static void tui_io_driven_loop(UI *ui){ - // "Passive" (I/O-driven) loop: TUI process's "main loop". - while (!tui_is_stopped(ui)) { - loop_poll_events(&main_loop, -1); - } -} - -// TODO: call me when EXITFREE -#if 0 -static void tui_data_destroy(void **argv) { - UI *ui = argv[0]; TUIData *data = ui->data; + ugrid_free(&data->grid); kv_destroy(data->invalid_regions); kv_destroy(data->attrs); xfree(data->space_buf); @@ -544,24 +526,6 @@ static void tui_data_destroy(void **argv) { } #endif -void tui_exit_safe(UI *ui) { - TUIData *data = ui->data; - if (!tui_is_stopped(ui)) { - tui_stop(ui); - } - tinput_destroy(&data->input); - signal_watcher_stop(&data->cont_handle); - signal_watcher_close(&data->cont_handle, NULL); - signal_watcher_close(&data->winch_handle, NULL); -} - -#ifdef UNIX -static void sigcont_cb(SignalWatcher *watcher, int signum, void *data) -{ - ((TUIData *)data)->cont_received = true; -} -#endif - static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data) { got_winch++; @@ -571,7 +535,6 @@ static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data) } tui_guess_size(ui); - ui_schedule_refresh(); } static bool attrs_differ(UI *ui, int id1, int id2, bool rgb) @@ -1408,7 +1371,6 @@ static void tui_flush(UI *ui) flush_buf(ui); } -#if 0 /// Dumps termcap info to the messages area, if 'verbose' >= 3. static void show_verbose_terminfo(TUIData *data) { @@ -1431,35 +1393,15 @@ static void show_verbose_terminfo(TUIData *data) ADD(end_fold, STRING_OBJ(cstr_to_string("Title"))); ADD(chunks, ARRAY_OBJ(end_fold)); - if (ui_client_channel_id) { - Array args = ARRAY_DICT_INIT; - ADD(args, ARRAY_OBJ(chunks)); - ADD(args, BOOLEAN_OBJ(true)); // history - Dictionary opts = ARRAY_DICT_INIT; - PUT(opts, "verbose", BOOLEAN_OBJ(true)); - ADD(args, DICTIONARY_OBJ(opts)); - rpc_send_event(ui_client_channel_id, "nvim_echo", args); - } else { - loop_schedule_deferred(&main_loop, event_create(verbose_terminfo_event, 2, - chunks.items, chunks.size)); - } + Array args = ARRAY_DICT_INIT; + ADD(args, ARRAY_OBJ(chunks)); + ADD(args, BOOLEAN_OBJ(true)); // history + Dictionary opts = ARRAY_DICT_INIT; + PUT(opts, "verbose", BOOLEAN_OBJ(true)); + ADD(args, DICTIONARY_OBJ(opts)); + rpc_send_event(ui_client_channel_id, "nvim_echo", args); } -static void verbose_terminfo_event(void **argv) -{ - Array chunks = { .items = argv[0], .size = (size_t)argv[1] }; - Dict(echo_opts) opts = { .verbose = BOOLEAN_OBJ(true) }; - Error err = ERROR_INIT; - nvim_echo(chunks, true, &opts, &err); - api_free_array(chunks); - if (ERROR_SET(&err)) { - fprintf(stderr, "TUI bought the farm: %s\n", err.msg); - exit(1); - } - api_clear_error(&err); -} -#endif - #ifdef UNIX static void suspend_event(void **argv) { @@ -1467,15 +1409,9 @@ static void suspend_event(void **argv) TUIData *data = ui->data; bool enable_mouse = data->mouse_enabled; tui_terminal_stop(ui); - data->cont_received = false; stream_set_blocking(input_global_fd(), true); // normalize stream (#2598) - signal_stop(); - kill(0, SIGTSTP); // make TUI process run in background - signal_start(); - while (!data->cont_received) { - // poll the event loop until SIGCONT is received - loop_poll_events(data->loop, -1); - } + + kill(0, SIGTSTP); tui_terminal_start(ui); tui_terminal_after_startup(ui); @@ -1488,16 +1424,13 @@ static void suspend_event(void **argv) static void tui_suspend(UI *ui) { - TUIData *data = ui->data; +// on a non-UNIX system, this is a no-op #ifdef UNIX // kill(0, SIGTSTP) won't stop the UI thread, so we must poll for SIGCONT // before continuing. This is done in another callback to avoid // loop_poll_events recursion - multiqueue_put_event(data->loop->fast_events, + multiqueue_put_event(resize_events, event_create(suspend_event, 1, ui)); -#else - // Resume the main thread as suspending isn't implemented. - CONTINUE(data->bridge); #endif } @@ -1612,10 +1545,6 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, I // printed immediately without an intervening newline. final_column_wrap(ui); } - - // TODO: wat - //xfree((void *) chunk); - //xfree((void *) attrs); } static void invalidate(UI *ui, int top, int bot, int left, int right) @@ -1682,6 +1611,9 @@ static void tui_guess_size(UI *ui) ui->width = width; ui->height = height; + + // TODO(bfredl): only if different from last value + ui_schedule_refresh(); } static void unibi_goto(UI *ui, int row, int col) |