aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/tui
diff options
context:
space:
mode:
authorbfredl <bjorn.linse@gmail.com>2022-05-02 21:10:01 +0200
committerbfredl <bjorn.linse@gmail.com>2022-12-31 13:25:26 +0100
commit43e8ec92de9e0850e7d202cb7ff9051bc408447e (patch)
treefcaef65604e05fb9cc34cf7543c7d92af9c38dcf /src/nvim/tui
parent24488169564c39a506c235bf6a33b8e23a8cb528 (diff)
downloadrneovim-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.c66
-rw-r--r--src/nvim/tui/tui.c198
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)