diff options
Diffstat (limited to 'src/terminal.c')
-rw-r--r-- | src/terminal.c | 3670 |
1 files changed, 0 insertions, 3670 deletions
diff --git a/src/terminal.c b/src/terminal.c deleted file mode 100644 index 487989d97a..0000000000 --- a/src/terminal.c +++ /dev/null @@ -1,3670 +0,0 @@ -/* vi:set ts=8 sts=4 sw=4 noet: - * - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - * See README.txt for an overview of the Vim source code. - */ - -/* - * Terminal window support, see ":help :terminal". - * - * There are three parts: - * 1. Generic code for all systems. - * Uses libvterm for the terminal emulator. - * 2. The MS-Windows implementation. - * Uses winpty. - * 3. The Unix-like implementation. - * Uses pseudo-tty's (pty's). - * - * For each terminal one VTerm is constructed. This uses libvterm. A copy of - * this library is in the libvterm directory. - * - * When a terminal window is opened, a job is started that will be connected to - * the terminal emulator. - * - * If the terminal window has keyboard focus, typed keys are converted to the - * terminal encoding and writing to the job over a channel. - * - * If the job produces output, it is written to the terminal emulator. The - * terminal emulator invokes callbacks when its screen content changes. The - * line range is stored in tl_dirty_row_start and tl_dirty_row_end. Once in a - * while, if the terminal window is visible, the screen contents is drawn. - * - * When the job ends the text is put in a buffer. Redrawing then happens from - * that buffer, attributes come from the scrollback buffer tl_scrollback. - * When the buffer is changed it is turned into a normal buffer, the attributes - * in tl_scrollback are no longer used. - * - * TODO: - * - Shift-Tab does not work. - * - click in Window toolbar of other window: save/restore Insert and Visual - * - Redirecting output does not work on MS-Windows, Test_terminal_redir_file() - * is disabled. - * - implement term_setsize() - * - add test for giving error for invalid 'termsize' value. - * - support minimal size when 'termsize' is "rows*cols". - * - support minimal size when 'termsize' is empty? - * - GUI: when using tabs, focus in terminal, click on tab does not work. - * - GUI: when 'confirm' is set and trying to exit Vim, dialog offers to save - * changes to "!shell". - * (justrajdeep, 2017 Aug 22) - * - For the GUI fill termios with default values, perhaps like pangoterm: - * http://bazaar.launchpad.net/~leonerd/pangoterm/trunk/view/head:/main.c#L134 - * - if the job in the terminal does not support the mouse, we can use the - * mouse in the Terminal window for copy/paste. - * - when 'encoding' is not utf-8, or the job is using another encoding, setup - * conversions. - * - In the GUI use a terminal emulator for :!cmd. Make the height the same as - * the window and position it higher up when it gets filled, so it looks like - * the text scrolls up. - * - Copy text in the vterm to the Vim buffer once in a while, so that - * completion works. - * - add an optional limit for the scrollback size. When reaching it remove - * 10% at the start. - */ - -#include "vim.h" - -#if defined(FEAT_TERMINAL) || defined(PROTO) - -#ifndef MIN -# define MIN(x,y) ((x) < (y) ? (x) : (y)) -#endif -#ifndef MAX -# define MAX(x,y) ((x) > (y) ? (x) : (y)) -#endif - -#include "libvterm/include/vterm.h" - -/* This is VTermScreenCell without the characters, thus much smaller. */ -typedef struct { - VTermScreenCellAttrs attrs; - char width; - VTermColor fg, bg; -} cellattr_T; - -typedef struct sb_line_S { - int sb_cols; /* can differ per line */ - cellattr_T *sb_cells; /* allocated */ - cellattr_T sb_fill_attr; /* for short line */ -} sb_line_T; - -/* typedef term_T in structs.h */ -struct terminal_S { - term_T *tl_next; - - VTerm *tl_vterm; - job_T *tl_job; - buf_T *tl_buffer; - - /* Set when setting the size of a vterm, reset after redrawing. */ - int tl_vterm_size_changed; - - /* used when tl_job is NULL and only a pty was created */ - int tl_tty_fd; - char_u *tl_tty_in; - char_u *tl_tty_out; - - int tl_normal_mode; /* TRUE: Terminal-Normal mode */ - int tl_channel_closed; - int tl_finish; /* 'c' for ++close, 'o' for ++open */ - char_u *tl_opencmd; - char_u *tl_eof_chars; - -#ifdef WIN3264 - void *tl_winpty_config; - void *tl_winpty; -#endif - - /* last known vterm size */ - int tl_rows; - int tl_cols; - /* vterm size does not follow window size */ - int tl_rows_fixed; - int tl_cols_fixed; - - char_u *tl_title; /* NULL or allocated */ - char_u *tl_status_text; /* NULL or allocated */ - - /* Range of screen rows to update. Zero based. */ - int tl_dirty_row_start; /* -1 if nothing dirty */ - int tl_dirty_row_end; /* row below last one to update */ - - garray_T tl_scrollback; - int tl_scrollback_scrolled; - cellattr_T tl_default_color; - - VTermPos tl_cursor_pos; - int tl_cursor_visible; - int tl_cursor_blink; - int tl_cursor_shape; /* 1: block, 2: underline, 3: bar */ - char_u *tl_cursor_color; /* NULL or allocated */ - - int tl_using_altscreen; -}; - -#define TMODE_ONCE 1 /* CTRL-\ CTRL-N used */ -#define TMODE_LOOP 2 /* CTRL-W N used */ - -/* - * List of all active terminals. - */ -static term_T *first_term = NULL; - -/* Terminal active in terminal_loop(). */ -static term_T *in_terminal_loop = NULL; - -#define MAX_ROW 999999 /* used for tl_dirty_row_end to update all rows */ -#define KEY_BUF_LEN 200 - -/* - * Functions with separate implementation for MS-Windows and Unix-like systems. - */ -static int term_and_job_init(term_T *term, typval_T *argvar, jobopt_T *opt); -static int create_pty_only(term_T *term, jobopt_T *opt); -static void term_report_winsize(term_T *term, int rows, int cols); -static void term_free_vterm(term_T *term); - -/* The characters that we know (or assume) that the terminal expects for the - * backspace and enter keys. */ -static int term_backspace_char = BS; -static int term_enter_char = CAR; -static int term_nl_does_cr = FALSE; - - -/************************************** - * 1. Generic code for all systems. - */ - -/* - * Determine the terminal size from 'termsize' and the current window. - * Assumes term->tl_rows and term->tl_cols are zero. - */ - static void -set_term_and_win_size(term_T *term) -{ - if (*curwin->w_p_tms != NUL) - { - char_u *p = vim_strchr(curwin->w_p_tms, 'x') + 1; - - term->tl_rows = atoi((char *)curwin->w_p_tms); - term->tl_cols = atoi((char *)p); - } - if (term->tl_rows == 0) - term->tl_rows = curwin->w_height; - else - { - win_setheight_win(term->tl_rows, curwin); - term->tl_rows_fixed = TRUE; - } - if (term->tl_cols == 0) - term->tl_cols = curwin->w_width; - else - { - win_setwidth_win(term->tl_cols, curwin); - term->tl_cols_fixed = TRUE; - } -} - -/* - * Initialize job options for a terminal job. - * Caller may overrule some of them. - */ - static void -init_job_options(jobopt_T *opt) -{ - clear_job_options(opt); - - opt->jo_mode = MODE_RAW; - opt->jo_out_mode = MODE_RAW; - opt->jo_err_mode = MODE_RAW; - opt->jo_set = JO_MODE | JO_OUT_MODE | JO_ERR_MODE; -} - -/* - * Set job options mandatory for a terminal job. - */ - static void -setup_job_options(jobopt_T *opt, int rows, int cols) -{ - if (!(opt->jo_set & JO_OUT_IO)) - { - /* Connect stdout to the terminal. */ - opt->jo_io[PART_OUT] = JIO_BUFFER; - opt->jo_io_buf[PART_OUT] = curbuf->b_fnum; - opt->jo_modifiable[PART_OUT] = 0; - opt->jo_set |= JO_OUT_IO + JO_OUT_BUF + JO_OUT_MODIFIABLE; - } - - if (!(opt->jo_set & JO_ERR_IO)) - { - /* Connect stderr to the terminal. */ - opt->jo_io[PART_ERR] = JIO_BUFFER; - opt->jo_io_buf[PART_ERR] = curbuf->b_fnum; - opt->jo_modifiable[PART_ERR] = 0; - opt->jo_set |= JO_ERR_IO + JO_ERR_BUF + JO_ERR_MODIFIABLE; - } - - opt->jo_pty = TRUE; - if ((opt->jo_set2 & JO2_TERM_ROWS) == 0) - opt->jo_term_rows = rows; - if ((opt->jo_set2 & JO2_TERM_COLS) == 0) - opt->jo_term_cols = cols; -} - -/* - * Start a terminal window and return its buffer. - * Returns NULL when failed. - */ - static buf_T * -term_start(typval_T *argvar, jobopt_T *opt, int forceit) -{ - exarg_T split_ea; - win_T *old_curwin = curwin; - term_T *term; - buf_T *old_curbuf = NULL; - int res; - buf_T *newbuf; - - if (check_restricted() || check_secure()) - return NULL; - - if ((opt->jo_set & (JO_IN_IO + JO_OUT_IO + JO_ERR_IO)) - == (JO_IN_IO + JO_OUT_IO + JO_ERR_IO) - || (!(opt->jo_set & JO_OUT_IO) && (opt->jo_set & JO_OUT_BUF)) - || (!(opt->jo_set & JO_ERR_IO) && (opt->jo_set & JO_ERR_BUF))) - { - EMSG(_(e_invarg)); - return NULL; - } - - term = (term_T *)alloc_clear(sizeof(term_T)); - if (term == NULL) - return NULL; - term->tl_dirty_row_end = MAX_ROW; - term->tl_cursor_visible = TRUE; - term->tl_cursor_shape = VTERM_PROP_CURSORSHAPE_BLOCK; - term->tl_finish = opt->jo_term_finish; - ga_init2(&term->tl_scrollback, sizeof(sb_line_T), 300); - - vim_memset(&split_ea, 0, sizeof(split_ea)); - if (opt->jo_curwin) - { - /* Create a new buffer in the current window. */ - if (!can_abandon(curbuf, forceit)) - { - no_write_message(); - vim_free(term); - return NULL; - } - if (do_ecmd(0, NULL, NULL, &split_ea, ECMD_ONE, - ECMD_HIDE + (forceit ? ECMD_FORCEIT : 0), curwin) == FAIL) - { - vim_free(term); - return NULL; - } - } - else if (opt->jo_hidden) - { - buf_T *buf; - - /* Create a new buffer without a window. Make it the current buffer for - * a moment to be able to do the initialisations. */ - buf = buflist_new((char_u *)"", NULL, (linenr_T)0, - BLN_NEW | BLN_LISTED); - if (buf == NULL || ml_open(buf) == FAIL) - { - vim_free(term); - return NULL; - } - old_curbuf = curbuf; - --curbuf->b_nwindows; - curbuf = buf; - curwin->w_buffer = buf; - ++curbuf->b_nwindows; - } - else - { - /* Open a new window or tab. */ - split_ea.cmdidx = CMD_new; - split_ea.cmd = (char_u *)"new"; - split_ea.arg = (char_u *)""; - if (opt->jo_term_rows > 0 && !(cmdmod.split & WSP_VERT)) - { - split_ea.line2 = opt->jo_term_rows; - split_ea.addr_count = 1; - } - if (opt->jo_term_cols > 0 && (cmdmod.split & WSP_VERT)) - { - split_ea.line2 = opt->jo_term_cols; - split_ea.addr_count = 1; - } - - ex_splitview(&split_ea); - if (curwin == old_curwin) - { - /* split failed */ - vim_free(term); - return NULL; - } - } - term->tl_buffer = curbuf; - curbuf->b_term = term; - - if (!opt->jo_hidden) - { - /* only one size was taken care of with :new, do the other one */ - if (opt->jo_term_rows > 0 && (cmdmod.split & WSP_VERT)) - win_setheight(opt->jo_term_rows); - if (opt->jo_term_cols > 0 && !(cmdmod.split & WSP_VERT)) - win_setwidth(opt->jo_term_cols); - } - - /* Link the new terminal in the list of active terminals. */ - term->tl_next = first_term; - first_term = term; - - if (opt->jo_term_name != NULL) - curbuf->b_ffname = vim_strsave(opt->jo_term_name); - else - { - int i; - size_t len; - char_u *cmd, *p; - - if (argvar->v_type == VAR_STRING) - { - cmd = argvar->vval.v_string; - if (cmd == NULL) - cmd = (char_u *)""; - else if (STRCMP(cmd, "NONE") == 0) - cmd = (char_u *)"pty"; - } - else if (argvar->v_type != VAR_LIST - || argvar->vval.v_list == NULL - || argvar->vval.v_list->lv_len < 1 - || (cmd = get_tv_string_chk( - &argvar->vval.v_list->lv_first->li_tv)) == NULL) - cmd = (char_u*)""; - - len = STRLEN(cmd) + 10; - p = alloc((int)len); - - for (i = 0; p != NULL; ++i) - { - /* Prepend a ! to the command name to avoid the buffer name equals - * the executable, otherwise ":w!" would overwrite it. */ - if (i == 0) - vim_snprintf((char *)p, len, "!%s", cmd); - else - vim_snprintf((char *)p, len, "!%s (%d)", cmd, i); - if (buflist_findname(p) == NULL) - { - vim_free(curbuf->b_ffname); - curbuf->b_ffname = p; - break; - } - } - } - curbuf->b_fname = curbuf->b_ffname; - - if (opt->jo_term_opencmd != NULL) - term->tl_opencmd = vim_strsave(opt->jo_term_opencmd); - - if (opt->jo_eof_chars != NULL) - term->tl_eof_chars = vim_strsave(opt->jo_eof_chars); - - set_string_option_direct((char_u *)"buftype", -1, - (char_u *)"terminal", OPT_FREE|OPT_LOCAL, 0); - - /* Mark the buffer as not modifiable. It can only be made modifiable after - * the job finished. */ - curbuf->b_p_ma = FALSE; - - set_term_and_win_size(term); - setup_job_options(opt, term->tl_rows, term->tl_cols); - - /* System dependent: setup the vterm and maybe start the job in it. */ - if (argvar->v_type == VAR_STRING - && argvar->vval.v_string != NULL - && STRCMP(argvar->vval.v_string, "NONE") == 0) - res = create_pty_only(term, opt); - else - res = term_and_job_init(term, argvar, opt); - - newbuf = curbuf; - if (res == OK) - { - /* Get and remember the size we ended up with. Update the pty. */ - vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols); - term_report_winsize(term, term->tl_rows, term->tl_cols); - - /* Make sure we don't get stuck on sending keys to the job, it leads to - * a deadlock if the job is waiting for Vim to read. */ - channel_set_nonblock(term->tl_job->jv_channel, PART_IN); - - if (old_curbuf != NULL) - { - --curbuf->b_nwindows; - curbuf = old_curbuf; - curwin->w_buffer = curbuf; - ++curbuf->b_nwindows; - } - } - else - { - buf_T *buf = curbuf; - - free_terminal(curbuf); - if (old_curbuf != NULL) - { - --curbuf->b_nwindows; - curbuf = old_curbuf; - curwin->w_buffer = curbuf; - ++curbuf->b_nwindows; - } - - /* Wiping out the buffer will also close the window and call - * free_terminal(). */ - do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE); - return NULL; - } - return newbuf; -} - -/* - * ":terminal": open a terminal window and execute a job in it. - */ - void -ex_terminal(exarg_T *eap) -{ - typval_T argvar[2]; - jobopt_T opt; - char_u *cmd; - char_u *tofree = NULL; - - init_job_options(&opt); - - cmd = eap->arg; - while (*cmd && *cmd == '+' && *(cmd + 1) == '+') - { - char_u *p, *ep; - - cmd += 2; - p = skiptowhite(cmd); - ep = vim_strchr(cmd, '='); - if (ep != NULL && ep < p) - p = ep; - - if ((int)(p - cmd) == 5 && STRNICMP(cmd, "close", 5) == 0) - opt.jo_term_finish = 'c'; - else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "open", 4) == 0) - opt.jo_term_finish = 'o'; - else if ((int)(p - cmd) == 6 && STRNICMP(cmd, "curwin", 6) == 0) - opt.jo_curwin = 1; - else if ((int)(p - cmd) == 6 && STRNICMP(cmd, "hidden", 6) == 0) - opt.jo_hidden = 1; - else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "rows", 4) == 0 - && ep != NULL && isdigit(ep[1])) - { - opt.jo_set2 |= JO2_TERM_ROWS; - opt.jo_term_rows = atoi((char *)ep + 1); - p = skiptowhite(cmd); - } - else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "cols", 4) == 0 - && ep != NULL && isdigit(ep[1])) - { - opt.jo_set2 |= JO2_TERM_COLS; - opt.jo_term_cols = atoi((char *)ep + 1); - p = skiptowhite(cmd); - } - else if ((int)(p - cmd) == 3 && STRNICMP(cmd, "eof", 3) == 0 - && ep != NULL) - { - char_u *buf = NULL; - char_u *keys; - - p = skiptowhite(cmd); - *p = NUL; - keys = replace_termcodes(ep + 1, &buf, TRUE, TRUE, TRUE); - opt.jo_set2 |= JO2_EOF_CHARS; - opt.jo_eof_chars = vim_strsave(keys); - vim_free(buf); - *p = ' '; - } - else - { - if (*p) - *p = NUL; - EMSG2(_("E181: Invalid attribute: %s"), cmd); - return; - } - cmd = skipwhite(p); - } - if (*cmd == NUL) - /* Make a copy of 'shell', an autocommand may change the option. */ - tofree = cmd = vim_strsave(p_sh); - - if (eap->addr_count > 0) - { - /* Write lines from current buffer to the job. */ - opt.jo_set |= JO_IN_IO | JO_IN_BUF | JO_IN_TOP | JO_IN_BOT; - opt.jo_io[PART_IN] = JIO_BUFFER; - opt.jo_io_buf[PART_IN] = curbuf->b_fnum; - opt.jo_in_top = eap->line1; - opt.jo_in_bot = eap->line2; - } - - argvar[0].v_type = VAR_STRING; - argvar[0].vval.v_string = cmd; - argvar[1].v_type = VAR_UNKNOWN; - term_start(argvar, &opt, eap->forceit); - vim_free(tofree); - vim_free(opt.jo_eof_chars); -} - -/* - * Free the scrollback buffer for "term". - */ - static void -free_scrollback(term_T *term) -{ - int i; - - for (i = 0; i < term->tl_scrollback.ga_len; ++i) - vim_free(((sb_line_T *)term->tl_scrollback.ga_data + i)->sb_cells); - ga_clear(&term->tl_scrollback); -} - -/* - * Free a terminal and everything it refers to. - * Kills the job if there is one. - * Called when wiping out a buffer. - */ - void -free_terminal(buf_T *buf) -{ - term_T *term = buf->b_term; - term_T *tp; - - if (term == NULL) - return; - if (first_term == term) - first_term = term->tl_next; - else - for (tp = first_term; tp->tl_next != NULL; tp = tp->tl_next) - if (tp->tl_next == term) - { - tp->tl_next = term->tl_next; - break; - } - - if (term->tl_job != NULL) - { - if (term->tl_job->jv_status != JOB_ENDED - && term->tl_job->jv_status != JOB_FINISHED - && term->tl_job->jv_status != JOB_FAILED) - job_stop(term->tl_job, NULL, "kill"); - job_unref(term->tl_job); - } - - free_scrollback(term); - - term_free_vterm(term); - vim_free(term->tl_title); - vim_free(term->tl_status_text); - vim_free(term->tl_opencmd); - vim_free(term->tl_eof_chars); - vim_free(term->tl_cursor_color); - vim_free(term); - buf->b_term = NULL; - if (in_terminal_loop == term) - in_terminal_loop = NULL; -} - -/* - * Write job output "msg[len]" to the vterm. - */ - static void -term_write_job_output(term_T *term, char_u *msg, size_t len) -{ - VTerm *vterm = term->tl_vterm; - char_u *p; - size_t done; - size_t len_now; - - if (term_nl_does_cr) - vterm_input_write(vterm, (char *)msg, len); - else - /* need to convert NL to CR-NL */ - for (done = 0; done < len; done += len_now) - { - for (p = msg + done; p < msg + len; ) - { - if (*p == NL) - break; - p += utf_ptr2len_len(p, (int)(len - (p - msg))); - } - len_now = p - msg - done; - vterm_input_write(vterm, (char *)msg + done, len_now); - if (p < msg + len && *p == NL) - { - vterm_input_write(vterm, "\r\n", 2); - ++len_now; - } - } - - /* this invokes the damage callbacks */ - vterm_screen_flush_damage(vterm_obtain_screen(vterm)); -} - - static void -update_cursor(term_T *term, int redraw) -{ - if (term->tl_normal_mode) - return; - setcursor(); - if (redraw) - { - if (term->tl_buffer == curbuf && term->tl_cursor_visible) - cursor_on(); - out_flush(); -#ifdef FEAT_GUI - if (gui.in_use) - gui_update_cursor(FALSE, FALSE); -#endif - } -} - -/* - * Invoked when "msg" output from a job was received. Write it to the terminal - * of "buffer". - */ - void -write_to_term(buf_T *buffer, char_u *msg, channel_T *channel) -{ - size_t len = STRLEN(msg); - term_T *term = buffer->b_term; - - if (term->tl_vterm == NULL) - { - ch_log(channel, "NOT writing %d bytes to terminal", (int)len); - return; - } - ch_log(channel, "writing %d bytes to terminal", (int)len); - term_write_job_output(term, msg, len); - - /* In Terminal-Normal mode we are displaying the buffer, not the terminal - * contents, thus no screen update is needed. */ - if (!term->tl_normal_mode) - { - /* TODO: only update once in a while. */ - ch_log(term->tl_job->jv_channel, "updating screen"); - if (buffer == curbuf) - { - update_screen(0); - update_cursor(term, TRUE); - } - else - redraw_after_callback(TRUE); - } -} - -/* - * Send a mouse position and click to the vterm - */ - static int -term_send_mouse(VTerm *vterm, int button, int pressed) -{ - VTermModifier mod = VTERM_MOD_NONE; - - vterm_mouse_move(vterm, mouse_row - W_WINROW(curwin), - mouse_col - W_WINCOL(curwin), mod); - vterm_mouse_button(vterm, button, pressed, mod); - return TRUE; -} - -/* - * Convert typed key "c" into bytes to send to the job. - * Return the number of bytes in "buf". - */ - static int -term_convert_key(term_T *term, int c, char *buf) -{ - VTerm *vterm = term->tl_vterm; - VTermKey key = VTERM_KEY_NONE; - VTermModifier mod = VTERM_MOD_NONE; - int mouse = FALSE; - - switch (c) - { - case CAR: c = term_enter_char; break; - /* don't use VTERM_KEY_BACKSPACE, it always - * becomes 0x7f DEL */ - case K_BS: c = term_backspace_char; break; - - case ESC: key = VTERM_KEY_ESCAPE; break; - case K_DEL: key = VTERM_KEY_DEL; break; - case K_DOWN: key = VTERM_KEY_DOWN; break; - case K_S_DOWN: mod = VTERM_MOD_SHIFT; - key = VTERM_KEY_DOWN; break; - case K_END: key = VTERM_KEY_END; break; - case K_S_END: mod = VTERM_MOD_SHIFT; - key = VTERM_KEY_END; break; - case K_C_END: mod = VTERM_MOD_CTRL; - key = VTERM_KEY_END; break; - case K_F10: key = VTERM_KEY_FUNCTION(10); break; - case K_F11: key = VTERM_KEY_FUNCTION(11); break; - case K_F12: key = VTERM_KEY_FUNCTION(12); break; - case K_F1: key = VTERM_KEY_FUNCTION(1); break; - case K_F2: key = VTERM_KEY_FUNCTION(2); break; - case K_F3: key = VTERM_KEY_FUNCTION(3); break; - case K_F4: key = VTERM_KEY_FUNCTION(4); break; - case K_F5: key = VTERM_KEY_FUNCTION(5); break; - case K_F6: key = VTERM_KEY_FUNCTION(6); break; - case K_F7: key = VTERM_KEY_FUNCTION(7); break; - case K_F8: key = VTERM_KEY_FUNCTION(8); break; - case K_F9: key = VTERM_KEY_FUNCTION(9); break; - case K_HOME: key = VTERM_KEY_HOME; break; - case K_S_HOME: mod = VTERM_MOD_SHIFT; - key = VTERM_KEY_HOME; break; - case K_C_HOME: mod = VTERM_MOD_CTRL; - key = VTERM_KEY_HOME; break; - case K_INS: key = VTERM_KEY_INS; break; - case K_K0: key = VTERM_KEY_KP_0; break; - case K_K1: key = VTERM_KEY_KP_1; break; - case K_K2: key = VTERM_KEY_KP_2; break; - case K_K3: key = VTERM_KEY_KP_3; break; - case K_K4: key = VTERM_KEY_KP_4; break; - case K_K5: key = VTERM_KEY_KP_5; break; - case K_K6: key = VTERM_KEY_KP_6; break; - case K_K7: key = VTERM_KEY_KP_7; break; - case K_K8: key = VTERM_KEY_KP_8; break; - case K_K9: key = VTERM_KEY_KP_9; break; - case K_KDEL: key = VTERM_KEY_DEL; break; /* TODO */ - case K_KDIVIDE: key = VTERM_KEY_KP_DIVIDE; break; - case K_KEND: key = VTERM_KEY_KP_1; break; /* TODO */ - case K_KENTER: key = VTERM_KEY_KP_ENTER; break; - case K_KHOME: key = VTERM_KEY_KP_7; break; /* TODO */ - case K_KINS: key = VTERM_KEY_KP_0; break; /* TODO */ - case K_KMINUS: key = VTERM_KEY_KP_MINUS; break; - case K_KMULTIPLY: key = VTERM_KEY_KP_MULT; break; - case K_KPAGEDOWN: key = VTERM_KEY_KP_3; break; /* TODO */ - case K_KPAGEUP: key = VTERM_KEY_KP_9; break; /* TODO */ - case K_KPLUS: key = VTERM_KEY_KP_PLUS; break; - case K_KPOINT: key = VTERM_KEY_KP_PERIOD; break; - case K_LEFT: key = VTERM_KEY_LEFT; break; - case K_S_LEFT: mod = VTERM_MOD_SHIFT; - key = VTERM_KEY_LEFT; break; - case K_C_LEFT: mod = VTERM_MOD_CTRL; - key = VTERM_KEY_LEFT; break; - case K_PAGEDOWN: key = VTERM_KEY_PAGEDOWN; break; - case K_PAGEUP: key = VTERM_KEY_PAGEUP; break; - case K_RIGHT: key = VTERM_KEY_RIGHT; break; - case K_S_RIGHT: mod = VTERM_MOD_SHIFT; - key = VTERM_KEY_RIGHT; break; - case K_C_RIGHT: mod = VTERM_MOD_CTRL; - key = VTERM_KEY_RIGHT; break; - case K_UP: key = VTERM_KEY_UP; break; - case K_S_UP: mod = VTERM_MOD_SHIFT; - key = VTERM_KEY_UP; break; - case TAB: key = VTERM_KEY_TAB; break; - - case K_MOUSEUP: mouse = term_send_mouse(vterm, 5, 1); break; - case K_MOUSEDOWN: mouse = term_send_mouse(vterm, 4, 1); break; - case K_MOUSELEFT: /* TODO */ return 0; - case K_MOUSERIGHT: /* TODO */ return 0; - - case K_LEFTMOUSE: - case K_LEFTMOUSE_NM: mouse = term_send_mouse(vterm, 1, 1); break; - case K_LEFTDRAG: mouse = term_send_mouse(vterm, 1, 1); break; - case K_LEFTRELEASE: - case K_LEFTRELEASE_NM: mouse = term_send_mouse(vterm, 1, 0); break; - case K_MIDDLEMOUSE: mouse = term_send_mouse(vterm, 2, 1); break; - case K_MIDDLEDRAG: mouse = term_send_mouse(vterm, 2, 1); break; - case K_MIDDLERELEASE: mouse = term_send_mouse(vterm, 2, 0); break; - case K_RIGHTMOUSE: mouse = term_send_mouse(vterm, 3, 1); break; - case K_RIGHTDRAG: mouse = term_send_mouse(vterm, 3, 1); break; - case K_RIGHTRELEASE: mouse = term_send_mouse(vterm, 3, 0); break; - case K_X1MOUSE: /* TODO */ return 0; - case K_X1DRAG: /* TODO */ return 0; - case K_X1RELEASE: /* TODO */ return 0; - case K_X2MOUSE: /* TODO */ return 0; - case K_X2DRAG: /* TODO */ return 0; - case K_X2RELEASE: /* TODO */ return 0; - - case K_IGNORE: return 0; - case K_NOP: return 0; - case K_UNDO: return 0; - case K_HELP: return 0; - case K_XF1: key = VTERM_KEY_FUNCTION(1); break; - case K_XF2: key = VTERM_KEY_FUNCTION(2); break; - case K_XF3: key = VTERM_KEY_FUNCTION(3); break; - case K_XF4: key = VTERM_KEY_FUNCTION(4); break; - case K_SELECT: return 0; -#ifdef FEAT_GUI - case K_VER_SCROLLBAR: return 0; - case K_HOR_SCROLLBAR: return 0; -#endif -#ifdef FEAT_GUI_TABLINE - case K_TABLINE: return 0; - case K_TABMENU: return 0; -#endif -#ifdef FEAT_NETBEANS_INTG - case K_F21: key = VTERM_KEY_FUNCTION(21); break; -#endif -#ifdef FEAT_DND - case K_DROP: return 0; -#endif -#ifdef FEAT_AUTOCMD - case K_CURSORHOLD: return 0; -#endif - case K_PS: vterm_keyboard_start_paste(vterm); return 0; - case K_PE: vterm_keyboard_end_paste(vterm); return 0; - } - - /* - * Convert special keys to vterm keys: - * - Write keys to vterm: vterm_keyboard_key() - * - Write output to channel. - * TODO: use mod_mask - */ - if (key != VTERM_KEY_NONE) - /* Special key, let vterm convert it. */ - vterm_keyboard_key(vterm, key, mod); - else if (!mouse) - /* Normal character, let vterm convert it. */ - vterm_keyboard_unichar(vterm, c, mod); - - /* Read back the converted escape sequence. */ - return (int)vterm_output_read(vterm, buf, KEY_BUF_LEN); -} - -/* - * Return TRUE if the job for "term" is still running. - */ - int -term_job_running(term_T *term) -{ - /* Also consider the job finished when the channel is closed, to avoid a - * race condition when updating the title. */ - return term != NULL - && term->tl_job != NULL - && channel_is_open(term->tl_job->jv_channel) - && (term->tl_job->jv_status == JOB_STARTED - || term->tl_job->jv_channel->ch_keep_open); -} - -/* - * Return TRUE if "term" has an active channel and used ":term NONE". - */ - int -term_none_open(term_T *term) -{ - /* Also consider the job finished when the channel is closed, to avoid a - * race condition when updating the title. */ - return term != NULL - && term->tl_job != NULL - && channel_is_open(term->tl_job->jv_channel) - && term->tl_job->jv_channel->ch_keep_open; -} - -/* - * Add the last line of the scrollback buffer to the buffer in the window. - */ - static void -add_scrollback_line_to_buffer(term_T *term, char_u *text, int len) -{ - buf_T *buf = term->tl_buffer; - int empty = (buf->b_ml.ml_flags & ML_EMPTY); - linenr_T lnum = buf->b_ml.ml_line_count; - -#ifdef WIN3264 - if (!enc_utf8 && enc_codepage > 0) - { - WCHAR *ret = NULL; - int length = 0; - - MultiByteToWideChar_alloc(CP_UTF8, 0, (char*)text, len + 1, - &ret, &length); - if (ret != NULL) - { - WideCharToMultiByte_alloc(enc_codepage, 0, - ret, length, (char **)&text, &len, 0, 0); - vim_free(ret); - ml_append_buf(term->tl_buffer, lnum, text, len, FALSE); - vim_free(text); - } - } - else -#endif - ml_append_buf(term->tl_buffer, lnum, text, len + 1, FALSE); - if (empty) - { - /* Delete the empty line that was in the empty buffer. */ - curbuf = buf; - ml_delete(1, FALSE); - curbuf = curwin->w_buffer; - } -} - - static void -cell2cellattr(const VTermScreenCell *cell, cellattr_T *attr) -{ - attr->width = cell->width; - attr->attrs = cell->attrs; - attr->fg = cell->fg; - attr->bg = cell->bg; -} - - static int -equal_celattr(cellattr_T *a, cellattr_T *b) -{ - /* Comparing the colors should be sufficient. */ - return a->fg.red == b->fg.red - && a->fg.green == b->fg.green - && a->fg.blue == b->fg.blue - && a->bg.red == b->bg.red - && a->bg.green == b->bg.green - && a->bg.blue == b->bg.blue; -} - - -/* - * Add the current lines of the terminal to scrollback and to the buffer. - * Called after the job has ended and when switching to Terminal-Normal mode. - */ - static void -move_terminal_to_buffer(term_T *term) -{ - win_T *wp; - int len; - int lines_skipped = 0; - VTermPos pos; - VTermScreenCell cell; - cellattr_T fill_attr, new_fill_attr; - cellattr_T *p; - VTermScreen *screen; - - if (term->tl_vterm == NULL) - return; - screen = vterm_obtain_screen(term->tl_vterm); - fill_attr = new_fill_attr = term->tl_default_color; - - for (pos.row = 0; pos.row < term->tl_rows; ++pos.row) - { - len = 0; - for (pos.col = 0; pos.col < term->tl_cols; ++pos.col) - if (vterm_screen_get_cell(screen, pos, &cell) != 0 - && cell.chars[0] != NUL) - { - len = pos.col + 1; - new_fill_attr = term->tl_default_color; - } - else - /* Assume the last attr is the filler attr. */ - cell2cellattr(&cell, &new_fill_attr); - - if (len == 0 && equal_celattr(&new_fill_attr, &fill_attr)) - ++lines_skipped; - else - { - while (lines_skipped > 0) - { - /* Line was skipped, add an empty line. */ - --lines_skipped; - if (ga_grow(&term->tl_scrollback, 1) == OK) - { - sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data - + term->tl_scrollback.ga_len; - - line->sb_cols = 0; - line->sb_cells = NULL; - line->sb_fill_attr = fill_attr; - ++term->tl_scrollback.ga_len; - - add_scrollback_line_to_buffer(term, (char_u *)"", 0); - } - } - - if (len == 0) - p = NULL; - else - p = (cellattr_T *)alloc((int)sizeof(cellattr_T) * len); - if ((p != NULL || len == 0) - && ga_grow(&term->tl_scrollback, 1) == OK) - { - garray_T ga; - int width; - sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data - + term->tl_scrollback.ga_len; - - ga_init2(&ga, 1, 100); - for (pos.col = 0; pos.col < len; pos.col += width) - { - if (vterm_screen_get_cell(screen, pos, &cell) == 0) - { - width = 1; - vim_memset(p + pos.col, 0, sizeof(cellattr_T)); - if (ga_grow(&ga, 1) == OK) - ga.ga_len += utf_char2bytes(' ', - (char_u *)ga.ga_data + ga.ga_len); - } - else - { - width = cell.width; - - cell2cellattr(&cell, &p[pos.col]); - - if (ga_grow(&ga, MB_MAXBYTES) == OK) - { - int i; - int c; - - for (i = 0; (c = cell.chars[i]) > 0 || i == 0; ++i) - ga.ga_len += utf_char2bytes(c == NUL ? ' ' : c, - (char_u *)ga.ga_data + ga.ga_len); - } - } - } - line->sb_cols = len; - line->sb_cells = p; - line->sb_fill_attr = new_fill_attr; - fill_attr = new_fill_attr; - ++term->tl_scrollback.ga_len; - - if (ga_grow(&ga, 1) == FAIL) - add_scrollback_line_to_buffer(term, (char_u *)"", 0); - else - { - *((char_u *)ga.ga_data + ga.ga_len) = NUL; - add_scrollback_line_to_buffer(term, ga.ga_data, ga.ga_len); - } - ga_clear(&ga); - } - else - vim_free(p); - } - } - - /* Obtain the current background color. */ - vterm_state_get_default_colors(vterm_obtain_state(term->tl_vterm), - &term->tl_default_color.fg, &term->tl_default_color.bg); - - FOR_ALL_WINDOWS(wp) - { - if (wp->w_buffer == term->tl_buffer) - { - wp->w_cursor.lnum = term->tl_buffer->b_ml.ml_line_count; - wp->w_cursor.col = 0; - wp->w_valid = 0; - if (wp->w_cursor.lnum >= wp->w_height) - { - linenr_T min_topline = wp->w_cursor.lnum - wp->w_height + 1; - - if (wp->w_topline < min_topline) - wp->w_topline = min_topline; - } - redraw_win_later(wp, NOT_VALID); - } - } -} - - static void -set_terminal_mode(term_T *term, int normal_mode) -{ - term->tl_normal_mode = normal_mode; - vim_free(term->tl_status_text); - term->tl_status_text = NULL; - if (term->tl_buffer == curbuf) - maketitle(); -} - -/* - * Called after the job if finished and Terminal mode is not active: - * Move the vterm contents into the scrollback buffer and free the vterm. - */ - static void -cleanup_vterm(term_T *term) -{ - if (term->tl_finish != 'c') - move_terminal_to_buffer(term); - term_free_vterm(term); - set_terminal_mode(term, FALSE); -} - -/* - * Switch from Terminal-Job mode to Terminal-Normal mode. - * Suspends updating the terminal window. - */ - static void -term_enter_normal_mode(void) -{ - term_T *term = curbuf->b_term; - - /* Append the current terminal contents to the buffer. */ - move_terminal_to_buffer(term); - - set_terminal_mode(term, TRUE); - - /* Move the window cursor to the position of the cursor in the - * terminal. */ - curwin->w_cursor.lnum = term->tl_scrollback_scrolled - + term->tl_cursor_pos.row + 1; - check_cursor(); - coladvance(term->tl_cursor_pos.col); - - |