diff options
Diffstat (limited to 'src/nvim/terminal.c')
-rw-r--r-- | src/nvim/terminal.c | 206 |
1 files changed, 160 insertions, 46 deletions
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 1527738165..b5a3cffe2f 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -43,26 +43,31 @@ #include <vterm.h> #include <vterm_keycodes.h> -#include "nvim/api/private/defs.h" +#include "klib/kvec.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/change.h" #include "nvim/channel.h" +#include "nvim/channel_defs.h" #include "nvim/cursor.h" #include "nvim/drawline.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" +#include "nvim/event/defs.h" +#include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/event/time.h" #include "nvim/ex_docmd.h" -#include "nvim/func_attr.h" #include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/keycodes.h" #include "nvim/macros_defs.h" @@ -74,19 +79,23 @@ #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/msgpack_rpc/channel_defs.h" -#include "nvim/normal.h" +#include "nvim/normal_defs.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/pos_defs.h" #include "nvim/state.h" +#include "nvim/state_defs.h" +#include "nvim/strings.h" #include "nvim/terminal.h" #include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/vim_defs.h" +#include "nvim/window.h" -typedef struct terminal_state { +typedef struct { VimState state; Terminal *term; int save_rd; // saved value of RedrawingDisabled @@ -154,6 +163,9 @@ struct terminal { bool color_set[16]; + // When there is a pending TermRequest autocommand, block and store input. + StringBuilder *pending_send; + size_t refcount; // reference count }; @@ -169,6 +181,82 @@ static VTermScreenCallbacks vterm_screen_callbacks = { static Set(ptr_t) invalidated_terminals = SET_INIT; +static void emit_termrequest(void **argv) +{ + Terminal *term = argv[0]; + char *payload = argv[1]; + size_t payload_length = (size_t)argv[2]; + StringBuilder *pending_send = argv[3]; + + buf_T *buf = handle_get_buffer(term->buf_handle); + String termrequest = { .data = payload, .size = payload_length }; + Object data = STRING_OBJ(termrequest); + set_vim_var_string(VV_TERMREQUEST, payload, (ptrdiff_t)payload_length); + apply_autocmds_group(EVENT_TERMREQUEST, NULL, NULL, false, AUGROUP_ALL, buf, NULL, &data); + xfree(payload); + + StringBuilder *term_pending_send = term->pending_send; + term->pending_send = NULL; + if (kv_size(*pending_send)) { + terminal_send(term, pending_send->items, pending_send->size); + kv_destroy(*pending_send); + } + if (term_pending_send != pending_send) { + term->pending_send = term_pending_send; + } + xfree(pending_send); +} + +static void schedule_termrequest(Terminal *term, char *payload, size_t payload_length) +{ + term->pending_send = xmalloc(sizeof(StringBuilder)); + kv_init(*term->pending_send); + multiqueue_put(main_loop.events, emit_termrequest, term, payload, (void *)payload_length, + term->pending_send); +} + +static int on_osc(int command, VTermStringFragment frag, void *user) +{ + if (frag.str == NULL) { + return 0; + } + if (!has_event(EVENT_TERMREQUEST)) { + return 1; + } + + StringBuilder request = KV_INITIAL_VALUE; + kv_printf(request, "\x1b]%d;", command); + kv_concat_len(request, frag.str, frag.len); + schedule_termrequest(user, request.items, request.size); + return 1; +} + +static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user) +{ + if (command == NULL || frag.str == NULL) { + return 0; + } + if (!has_event(EVENT_TERMREQUEST)) { + return 1; + } + + StringBuilder request = KV_INITIAL_VALUE; + kv_printf(request, "\x1bP%*s", (int)commandlen, command); + kv_concat_len(request, frag.str, frag.len); + schedule_termrequest(user, request.items, request.size); + return 1; +} + +static VTermStateFallbacks vterm_fallbacks = { + .control = NULL, + .csi = NULL, + .osc = on_osc, + .dcs = on_dcs, + .apc = NULL, + .pm = NULL, + .sos = NULL, +}; + void terminal_init(void) { time_watcher_init(&main_loop, &refresh_timer, NULL); @@ -189,7 +277,7 @@ void terminal_teardown(void) static void term_output_callback(const char *s, size_t len, void *user_data) { - terminal_send((Terminal *)user_data, (char *)s, len); + terminal_send((Terminal *)user_data, s, len); } // public API {{{ @@ -205,36 +293,37 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) FUNC_ATTR_NONNULL_ALL { // Create a new terminal instance and configure it - Terminal *rv = *termpp = xcalloc(1, sizeof(Terminal)); - rv->opts = opts; - rv->cursor.visible = true; + Terminal *term = *termpp = xcalloc(1, sizeof(Terminal)); + term->opts = opts; + term->cursor.visible = true; // Associate the terminal instance with the new buffer - rv->buf_handle = buf->handle; - buf->terminal = rv; + term->buf_handle = buf->handle; + buf->terminal = term; // Create VTerm - rv->vt = vterm_new(opts.height, opts.width); - vterm_set_utf8(rv->vt, 1); + term->vt = vterm_new(opts.height, opts.width); + vterm_set_utf8(term->vt, 1); // Setup state - VTermState *state = vterm_obtain_state(rv->vt); + VTermState *state = vterm_obtain_state(term->vt); // Set up screen - rv->vts = vterm_obtain_screen(rv->vt); - vterm_screen_enable_altscreen(rv->vts, true); - vterm_screen_enable_reflow(rv->vts, true); + term->vts = vterm_obtain_screen(term->vt); + vterm_screen_enable_altscreen(term->vts, true); + vterm_screen_enable_reflow(term->vts, true); // delete empty lines at the end of the buffer - vterm_screen_set_callbacks(rv->vts, &vterm_screen_callbacks, rv); - vterm_screen_set_damage_merge(rv->vts, VTERM_DAMAGE_SCROLL); - vterm_screen_reset(rv->vts, 1); - vterm_output_set_callback(rv->vt, term_output_callback, rv); + vterm_screen_set_callbacks(term->vts, &vterm_screen_callbacks, term); + vterm_screen_set_unrecognised_fallbacks(term->vts, &vterm_fallbacks, term); + vterm_screen_set_damage_merge(term->vts, VTERM_DAMAGE_SCROLL); + vterm_screen_reset(term->vts, 1); + vterm_output_set_callback(term->vt, term_output_callback, term); // force a initial refresh of the screen to ensure the buffer will always // have as many lines as screen rows when refresh_scrollback is called - rv->invalid_start = 0; - rv->invalid_end = opts.height; + term->invalid_start = 0; + term->invalid_end = opts.height; aco_save_T aco; aucmd_prepbuf(&aco, buf); - refresh_screen(rv, buf); - set_option_value("buftype", STATIC_CSTR_AS_OPTVAL("terminal"), OPT_LOCAL); + refresh_screen(term, buf); + set_option_value(kOptBuftype, STATIC_CSTR_AS_OPTVAL("terminal"), OPT_LOCAL); // Default settings for terminal buffers buf->b_p_ma = false; // 'nomodifiable' @@ -242,8 +331,8 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) buf->b_p_scbk = // 'scrollback' (initialize local from global) (p_scbk < 0) ? 10000 : MAX(1, p_scbk); buf->b_p_tw = 0; // 'textwidth' - set_option_value("wrap", BOOLEAN_OPTVAL(false), OPT_LOCAL); - set_option_value("list", BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value(kOptWrap, BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value(kOptList, BOOLEAN_OPTVAL(false), OPT_LOCAL); if (buf->b_ffname != NULL) { buf_set_term_title(buf, buf->b_ffname, strlen(buf->b_ffname)); } @@ -251,7 +340,7 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) // Reset cursor in current window. curwin->w_cursor = (pos_T){ .lnum = 1, .col = 0, .coladd = 0 }; // Initialize to check if the scrollback buffer has been allocated in a TermOpen autocmd. - rv->sb_buffer = NULL; + term->sb_buffer = NULL; // Apply TermOpen autocmds _before_ configuring the scrollback buffer. apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, buf); @@ -261,14 +350,14 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) return; // Terminal has already been destroyed. } - if (rv->sb_buffer == NULL) { + if (term->sb_buffer == NULL) { // Local 'scrollback' _after_ autocmds. if (buf->b_p_scbk < 1) { buf->b_p_scbk = SB_MAX; } // Configure the scrollback buffer. - rv->sb_size = (size_t)buf->b_p_scbk; - rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size); + term->sb_size = (size_t)buf->b_p_scbk; + term->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * term->sb_size); } // Configure the color palette. Try to get the color from: @@ -277,14 +366,12 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) // - g:terminal_color_{NUM} // - the VTerm instance for (int i = 0; i < 16; i++) { - RgbValue color_val = -1; char var[64]; snprintf(var, sizeof(var), "terminal_color_%d", i); char *name = get_config_string(var); if (name) { int dummy; - color_val = name_to_color(name, &dummy); - xfree(name); + RgbValue color_val = name_to_color(name, &dummy); if (color_val != -1) { VTermColor color; @@ -293,7 +380,7 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) (uint8_t)((color_val >> 8) & 0xFF), (uint8_t)((color_val >> 0) & 0xFF)); vterm_state_set_palette_color(state, i, &color); - rv->color_set[i] = true; + term->color_set[i] = true; } } } @@ -388,7 +475,8 @@ void terminal_check_size(Terminal *term) int curwidth, curheight; vterm_get_size(term->vt, &curheight, &curwidth); - uint16_t width = 0, height = 0; + uint16_t width = 0; + uint16_t height = 0; // Check if there is a window that displays the terminal and find the maximum width and height. // Skip the autocommand window which isn't actually displayed. @@ -556,6 +644,8 @@ static int terminal_check(VimState *state) curbuf->b_locked--; } + may_trigger_win_scrolled_resized(); + if (need_maketitle) { // Update title in terminal-mode. #7248 maketitle(); } @@ -681,11 +771,15 @@ void terminal_destroy(Terminal **termpp) } } -void terminal_send(Terminal *term, char *data, size_t size) +static void terminal_send(Terminal *term, const char *data, size_t size) { if (term->closed) { return; } + if (term->pending_send) { + kv_concat_len(*term->pending_send, data, size); + return; + } term->opts.write_cb(data, size, term->opts.data); } @@ -763,7 +857,7 @@ void terminal_paste(int count, char **y_array, size_t y_size) vterm_keyboard_end_paste(curbuf->terminal->vt); } -void terminal_send_key(Terminal *term, int c) +static void terminal_send_key(Terminal *term, int c) { VTermModifier mod = VTERM_MOD_NONE; @@ -781,13 +875,27 @@ void terminal_send_key(Terminal *term, int c) } } -void terminal_receive(Terminal *term, char *data, size_t len) +void terminal_receive(Terminal *term, const char *data, size_t len) { if (!data) { return; } - vterm_input_write(term->vt, data, len); + if (term->opts.force_crlf) { + StringBuilder crlf_data = KV_INITIAL_VALUE; + + for (size_t i = 0; i < len; i++) { + if (data[i] == '\n' && (i == 0 || (i > 0 && data[i - 1] != '\r'))) { + kv_push(crlf_data, '\r'); + } + kv_push(crlf_data, data[i]); + } + + vterm_input_write(term->vt, crlf_data.items, kv_size(crlf_data)); + kv_destroy(crlf_data); + } else { + vterm_input_write(term->vt, data, len); + } vterm_screen_flush_damage(term->vts); } @@ -840,8 +948,8 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *te bool fg_indexed = VTERM_COLOR_IS_INDEXED(&cell.fg); bool bg_indexed = VTERM_COLOR_IS_INDEXED(&cell.bg); - int vt_fg_idx = ((!fg_default && fg_indexed) ? cell.fg.indexed.idx + 1 : 0); - int vt_bg_idx = ((!bg_default && bg_indexed) ? cell.bg.indexed.idx + 1 : 0); + int16_t vt_fg_idx = ((!fg_default && fg_indexed) ? cell.fg.indexed.idx + 1 : 0); + int16_t vt_bg_idx = ((!bg_default && bg_indexed) ? cell.bg.indexed.idx + 1 : 0); bool fg_set = vt_fg_idx && vt_fg_idx <= 16 && term->color_set[vt_fg_idx - 1]; bool bg_set = vt_bg_idx && vt_bg_idx <= 16 && term->color_set[vt_bg_idx - 1]; @@ -866,6 +974,7 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *te .rgb_bg_color = vt_bg, .rgb_sp_color = -1, .hl_blend = -1, + .url = -1, }); } @@ -926,6 +1035,7 @@ static void buf_set_term_title(buf_T *buf, const char *title, size_t len) STRING_OBJ(((String){ .data = (char *)title, .size = len })), false, false, + NULL, &err); api_clear_error(&err); status_redraw_buf(buf); @@ -1414,15 +1524,19 @@ static void mouse_action(Terminal *term, int button, int row, int col, bool pres // terminal should lose focus static bool send_mouse_event(Terminal *term, int c) { - int row = mouse_row, col = mouse_col, grid = mouse_grid; + int row = mouse_row; + int col = mouse_col; + int grid = mouse_grid; win_T *mouse_win = mouse_find_win(&grid, &row, &col); if (mouse_win == NULL) { goto end; } int offset; - if (term->forward_mouse && mouse_win->w_buffer->terminal == term - && col >= (offset = win_col_off(mouse_win))) { + if (term->forward_mouse && mouse_win->w_buffer->terminal == term && row >= 0 + && (grid > 1 || row + mouse_win->w_winbar_height < mouse_win->w_height) + && col >= (offset = win_col_off(mouse_win)) + && (grid > 1 || col < mouse_win->w_width)) { // event in the terminal window and mouse events was enabled by the // program. translate and forward the event int button; @@ -1802,10 +1916,10 @@ static char *get_config_string(char *key) { Error err = ERROR_INIT; // Only called from terminal_open where curbuf->terminal is the context. - Object obj = dict_get_value(curbuf->b_vars, cstr_as_string(key), &err); + Object obj = dict_get_value(curbuf->b_vars, cstr_as_string(key), NULL, &err); api_clear_error(&err); if (obj.type == kObjectTypeNil) { - obj = dict_get_value(&globvardict, cstr_as_string(key), &err); + obj = dict_get_value(&globvardict, cstr_as_string(key), NULL, &err); api_clear_error(&err); } if (obj.type == kObjectTypeString) { |