blob: 9660c64ba752d4b7e71fe4d41f6da640afd70354 [file] [log] [blame]
Bram Moolenaare4f25e42017-07-07 11:54:15 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * Terminal window support, see ":help :terminal".
12 *
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020013 * There are three parts:
14 * 1. Generic code for all systems.
Bram Moolenaarb13501f2017-07-22 22:32:56 +020015 * Uses libvterm for the terminal emulator.
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020016 * 2. The MS-Windows implementation.
Bram Moolenaarb13501f2017-07-22 22:32:56 +020017 * Uses winpty.
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020018 * 3. The Unix-like implementation.
Bram Moolenaarb13501f2017-07-22 22:32:56 +020019 * Uses pseudo-tty's (pty's).
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020020 *
21 * For each terminal one VTerm is constructed. This uses libvterm. A copy of
Bram Moolenaar63ecdda2017-07-28 22:29:35 +020022 * this library is in the libvterm directory.
Bram Moolenaare4f25e42017-07-07 11:54:15 +020023 *
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020024 * When a terminal window is opened, a job is started that will be connected to
25 * the terminal emulator.
Bram Moolenaare4f25e42017-07-07 11:54:15 +020026 *
27 * If the terminal window has keyboard focus, typed keys are converted to the
Bram Moolenaar8a773062017-07-24 22:29:21 +020028 * terminal encoding and writing to the job over a channel.
Bram Moolenaare4f25e42017-07-07 11:54:15 +020029 *
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020030 * If the job produces output, it is written to the terminal emulator. The
31 * terminal emulator invokes callbacks when its screen content changes. The
32 * line range is stored in tl_dirty_row_start and tl_dirty_row_end. Once in a
33 * while, if the terminal window is visible, the screen contents is drawn.
Bram Moolenaare4f25e42017-07-07 11:54:15 +020034 *
Bram Moolenaar63ecdda2017-07-28 22:29:35 +020035 * When the job ends the text is put in a buffer. Redrawing then happens from
36 * that buffer, attributes come from the scrollback buffer tl_scrollback.
Bram Moolenaar679653e2017-08-13 14:13:19 +020037 * When the buffer is changed it is turned into a normal buffer, the attributes
38 * in tl_scrollback are no longer used.
Bram Moolenaar63ecdda2017-07-28 22:29:35 +020039 *
Bram Moolenaare4f25e42017-07-07 11:54:15 +020040 * TODO:
Bram Moolenaar13ebb032017-08-26 22:02:51 +020041 * - ":term NONE" does not work in MS-Windows.
Bram Moolenaar94053a52017-08-01 21:44:33 +020042 * - implement term_setsize()
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020043 * - add test for giving error for invalid 'termsize' value.
Bram Moolenaare4f25e42017-07-07 11:54:15 +020044 * - support minimal size when 'termsize' is "rows*cols".
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020045 * - support minimal size when 'termsize' is empty?
Bram Moolenaar285f2432017-08-23 23:10:21 +020046 * - GUI: when using tabs, focus in terminal, click on tab does not work.
47 * - GUI: when 'confirm' is set and trying to exit Vim, dialog offers to save
48 * changes to "!shell".
49 * (justrajdeep, 2017 Aug 22)
Bram Moolenaar4f44b882017-08-13 20:06:18 +020050 * - For the GUI fill termios with default values, perhaps like pangoterm:
51 * http://bazaar.launchpad.net/~leonerd/pangoterm/trunk/view/head:/main.c#L134
Bram Moolenaar423802d2017-07-30 16:52:24 +020052 * - if the job in the terminal does not support the mouse, we can use the
53 * mouse in the Terminal window for copy/paste.
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020054 * - when 'encoding' is not utf-8, or the job is using another encoding, setup
55 * conversions.
Bram Moolenaardbe948d2017-07-23 22:50:51 +020056 * - In the GUI use a terminal emulator for :!cmd.
Bram Moolenaar12d853f2017-08-01 18:04:04 +020057 * - Copy text in the vterm to the Vim buffer once in a while, so that
58 * completion works.
Bram Moolenaare4f25e42017-07-07 11:54:15 +020059 */
60
61#include "vim.h"
62
Bram Moolenaarc6df10e2017-07-29 20:15:08 +020063#if defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaare4f25e42017-07-07 11:54:15 +020064
Bram Moolenaard5310982017-08-05 15:16:32 +020065#ifndef MIN
66# define MIN(x,y) ((x) < (y) ? (x) : (y))
67#endif
68#ifndef MAX
69# define MAX(x,y) ((x) > (y) ? (x) : (y))
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020070#endif
Bram Moolenaare4f25e42017-07-07 11:54:15 +020071
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020072#include "libvterm/include/vterm.h"
73
Bram Moolenaar33a43be2017-08-06 21:36:22 +020074/* This is VTermScreenCell without the characters, thus much smaller. */
75typedef struct {
76 VTermScreenCellAttrs attrs;
77 char width;
78 VTermColor fg, bg;
79} cellattr_T;
80
Bram Moolenaard85f2712017-07-28 21:51:57 +020081typedef struct sb_line_S {
Bram Moolenaar33a43be2017-08-06 21:36:22 +020082 int sb_cols; /* can differ per line */
83 cellattr_T *sb_cells; /* allocated */
Bram Moolenaard85f2712017-07-28 21:51:57 +020084} sb_line_T;
85
Bram Moolenaare4f25e42017-07-07 11:54:15 +020086/* typedef term_T in structs.h */
87struct terminal_S {
88 term_T *tl_next;
89
Bram Moolenaar423802d2017-07-30 16:52:24 +020090 VTerm *tl_vterm;
91 job_T *tl_job;
92 buf_T *tl_buffer;
93
Bram Moolenaar7c9aec42017-08-03 13:51:25 +020094 /* used when tl_job is NULL and only a pty was created */
95 int tl_tty_fd;
96 char_u *tl_tty_name;
97
Bram Moolenaar6d819742017-08-06 14:57:49 +020098 int tl_normal_mode; /* TRUE: Terminal-Normal mode */
Bram Moolenaar423802d2017-07-30 16:52:24 +020099 int tl_channel_closed;
Bram Moolenaardd693ce2017-08-10 23:15:19 +0200100 int tl_finish; /* 'c' for ++close, 'o' for ++open */
Bram Moolenaar37c45832017-08-12 16:01:04 +0200101 char_u *tl_opencmd;
Bram Moolenaardada6d22017-09-02 17:18:35 +0200102 char_u *tl_eof_chars;
Bram Moolenaar423802d2017-07-30 16:52:24 +0200103
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200104#ifdef WIN3264
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200105 void *tl_winpty_config;
106 void *tl_winpty;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200107#endif
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200108
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200109 /* last known vterm size */
110 int tl_rows;
111 int tl_cols;
112 /* vterm size does not follow window size */
113 int tl_rows_fixed;
114 int tl_cols_fixed;
115
Bram Moolenaar21554412017-07-24 21:44:43 +0200116 char_u *tl_title; /* NULL or allocated */
117 char_u *tl_status_text; /* NULL or allocated */
118
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200119 /* Range of screen rows to update. Zero based. */
120 int tl_dirty_row_start; /* -1 if nothing dirty */
121 int tl_dirty_row_end; /* row below last one to update */
122
Bram Moolenaard85f2712017-07-28 21:51:57 +0200123 garray_T tl_scrollback;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200124 int tl_scrollback_scrolled;
Bram Moolenaard85f2712017-07-28 21:51:57 +0200125
Bram Moolenaar22aad2f2017-07-30 18:19:46 +0200126 VTermPos tl_cursor_pos;
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200127 int tl_cursor_visible;
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +0200128 int tl_cursor_blink;
129 int tl_cursor_shape; /* 1: block, 2: underline, 3: bar */
130 char_u *tl_cursor_color; /* NULL or allocated */
Bram Moolenaare41e3b42017-08-11 16:24:50 +0200131
132 int tl_using_altscreen;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200133};
134
Bram Moolenaaraaa8a352017-08-05 20:17:00 +0200135#define TMODE_ONCE 1 /* CTRL-\ CTRL-N used */
136#define TMODE_LOOP 2 /* CTRL-W N used */
137
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200138/*
139 * List of all active terminals.
140 */
141static term_T *first_term = NULL;
142
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +0200143/* Terminal active in terminal_loop(). */
144static term_T *in_terminal_loop = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200145
146#define MAX_ROW 999999 /* used for tl_dirty_row_end to update all rows */
147#define KEY_BUF_LEN 200
148
149/*
150 * Functions with separate implementation for MS-Windows and Unix-like systems.
151 */
Bram Moolenaar13ebb032017-08-26 22:02:51 +0200152static int term_and_job_init(term_T *term, typval_T *argvar, jobopt_T *opt);
153static int create_pty_only(term_T *term, jobopt_T *opt);
Bram Moolenaar43da3e32017-07-23 17:27:54 +0200154static void term_report_winsize(term_T *term, int rows, int cols);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200155static void term_free_vterm(term_T *term);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200156
Bram Moolenaar4f44b882017-08-13 20:06:18 +0200157/* The characters that we know (or assume) that the terminal expects for the
158 * backspace and enter keys. */
159static int term_backspace_char = BS;
160static int term_enter_char = CAR;
161static int term_nl_does_cr = FALSE;
162
163
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200164/**************************************
165 * 1. Generic code for all systems.
166 */
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200167
168/*
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200169 * Determine the terminal size from 'termsize' and the current window.
170 * Assumes term->tl_rows and term->tl_cols are zero.
171 */
172 static void
173set_term_and_win_size(term_T *term)
174{
175 if (*curwin->w_p_tms != NUL)
176 {
177 char_u *p = vim_strchr(curwin->w_p_tms, 'x') + 1;
178
179 term->tl_rows = atoi((char *)curwin->w_p_tms);
180 term->tl_cols = atoi((char *)p);
181 }
182 if (term->tl_rows == 0)
183 term->tl_rows = curwin->w_height;
184 else
185 {
186 win_setheight_win(term->tl_rows, curwin);
187 term->tl_rows_fixed = TRUE;
188 }
189 if (term->tl_cols == 0)
190 term->tl_cols = curwin->w_width;
191 else
192 {
193 win_setwidth_win(term->tl_cols, curwin);
194 term->tl_cols_fixed = TRUE;
195 }
196}
197
198/*
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200199 * Initialize job options for a terminal job.
200 * Caller may overrule some of them.
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200201 */
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200202 static void
203init_job_options(jobopt_T *opt)
204{
205 clear_job_options(opt);
206
207 opt->jo_mode = MODE_RAW;
208 opt->jo_out_mode = MODE_RAW;
209 opt->jo_err_mode = MODE_RAW;
210 opt->jo_set = JO_MODE | JO_OUT_MODE | JO_ERR_MODE;
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200211}
212
213/*
214 * Set job options mandatory for a terminal job.
215 */
216 static void
217setup_job_options(jobopt_T *opt, int rows, int cols)
218{
Bram Moolenaare88fc7a2017-09-03 20:59:40 +0200219 if (!(opt->jo_set & JO_OUT_IO))
220 {
221 /* Connect stdout to the terminal. */
222 opt->jo_io[PART_OUT] = JIO_BUFFER;
223 opt->jo_io_buf[PART_OUT] = curbuf->b_fnum;
224 opt->jo_modifiable[PART_OUT] = 0;
225 opt->jo_set |= JO_OUT_IO + JO_OUT_BUF + JO_OUT_MODIFIABLE;
226 }
227
228 if (!(opt->jo_set & JO_ERR_IO))
229 {
230 /* Connect stderr to the terminal. */
231 opt->jo_io[PART_ERR] = JIO_BUFFER;
232 opt->jo_io_buf[PART_ERR] = curbuf->b_fnum;
233 opt->jo_modifiable[PART_ERR] = 0;
234 opt->jo_set |= JO_ERR_IO + JO_ERR_BUF + JO_ERR_MODIFIABLE;
235 }
236
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200237 opt->jo_pty = TRUE;
Bram Moolenaar08d384f2017-08-11 21:51:23 +0200238 if ((opt->jo_set2 & JO2_TERM_ROWS) == 0)
239 opt->jo_term_rows = rows;
240 if ((opt->jo_set2 & JO2_TERM_COLS) == 0)
241 opt->jo_term_cols = cols;
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200242}
243
244 static void
Bram Moolenaardcaa6132017-08-13 17:13:09 +0200245term_start(typval_T *argvar, jobopt_T *opt, int forceit)
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200246{
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200247 exarg_T split_ea;
248 win_T *old_curwin = curwin;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200249 term_T *term;
Bram Moolenaar8cad9302017-08-12 14:32:32 +0200250 buf_T *old_curbuf = NULL;
Bram Moolenaar13ebb032017-08-26 22:02:51 +0200251 int res;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200252
253 if (check_restricted() || check_secure())
254 return;
255
Bram Moolenaare88fc7a2017-09-03 20:59:40 +0200256 if ((opt->jo_set & (JO_IN_IO + JO_OUT_IO + JO_ERR_IO))
257 == (JO_IN_IO + JO_OUT_IO + JO_ERR_IO)
258 || (!(opt->jo_set & JO_OUT_IO) && (opt->jo_set & JO_OUT_BUF))
259 || (!(opt->jo_set & JO_ERR_IO) && (opt->jo_set & JO_ERR_BUF)))
260 {
261 EMSG(_(e_invarg));
262 return;
263 }
264
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200265 term = (term_T *)alloc_clear(sizeof(term_T));
266 if (term == NULL)
267 return;
268 term->tl_dirty_row_end = MAX_ROW;
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200269 term->tl_cursor_visible = TRUE;
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +0200270 term->tl_cursor_shape = VTERM_PROP_CURSORSHAPE_BLOCK;
Bram Moolenaardd693ce2017-08-10 23:15:19 +0200271 term->tl_finish = opt->jo_term_finish;
Bram Moolenaard85f2712017-07-28 21:51:57 +0200272 ga_init2(&term->tl_scrollback, sizeof(sb_line_T), 300);
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200273
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200274 vim_memset(&split_ea, 0, sizeof(split_ea));
Bram Moolenaarda43b612017-08-11 22:27:50 +0200275 if (opt->jo_curwin)
Bram Moolenaarcfcc0222017-08-05 17:13:48 +0200276 {
Bram Moolenaarda43b612017-08-11 22:27:50 +0200277 /* Create a new buffer in the current window. */
278 if (!can_abandon(curbuf, forceit))
279 {
Bram Moolenaarf5be7cd2017-08-17 16:55:13 +0200280 no_write_message();
Bram Moolenaar8cad9302017-08-12 14:32:32 +0200281 vim_free(term);
Bram Moolenaarda43b612017-08-11 22:27:50 +0200282 return;
283 }
284 if (do_ecmd(0, NULL, NULL, &split_ea, ECMD_ONE,
285 ECMD_HIDE + (forceit ? ECMD_FORCEIT : 0), curwin) == FAIL)
Bram Moolenaar8cad9302017-08-12 14:32:32 +0200286 {
287 vim_free(term);
Bram Moolenaarda43b612017-08-11 22:27:50 +0200288 return;
Bram Moolenaar8cad9302017-08-12 14:32:32 +0200289 }
290 }
291 else if (opt->jo_hidden)
292 {
293 buf_T *buf;
294
295 /* Create a new buffer without a window. Make it the current buffer for
296 * a moment to be able to do the initialisations. */
297 buf = buflist_new((char_u *)"", NULL, (linenr_T)0,
298 BLN_NEW | BLN_LISTED);
299 if (buf == NULL || ml_open(buf) == FAIL)
300 {
301 vim_free(term);
302 return;
303 }
304 old_curbuf = curbuf;
305 --curbuf->b_nwindows;
306 curbuf = buf;
307 curwin->w_buffer = buf;
308 ++curbuf->b_nwindows;
Bram Moolenaarcfcc0222017-08-05 17:13:48 +0200309 }
Bram Moolenaarda43b612017-08-11 22:27:50 +0200310 else
Bram Moolenaarcfcc0222017-08-05 17:13:48 +0200311 {
Bram Moolenaarda43b612017-08-11 22:27:50 +0200312 /* Open a new window or tab. */
313 split_ea.cmdidx = CMD_new;
314 split_ea.cmd = (char_u *)"new";
315 split_ea.arg = (char_u *)"";
316 if (opt->jo_term_rows > 0 && !(cmdmod.split & WSP_VERT))
317 {
318 split_ea.line2 = opt->jo_term_rows;
319 split_ea.addr_count = 1;
320 }
321 if (opt->jo_term_cols > 0 && (cmdmod.split & WSP_VERT))
322 {
323 split_ea.line2 = opt->jo_term_cols;
324 split_ea.addr_count = 1;
325 }
Bram Moolenaarcfcc0222017-08-05 17:13:48 +0200326
Bram Moolenaarda43b612017-08-11 22:27:50 +0200327 ex_splitview(&split_ea);
328 if (curwin == old_curwin)
329 {
330 /* split failed */
331 vim_free(term);
332 return;
333 }
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200334 }
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200335 term->tl_buffer = curbuf;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200336 curbuf->b_term = term;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200337
Bram Moolenaar8cad9302017-08-12 14:32:32 +0200338 if (!opt->jo_hidden)
339 {
340 /* only one size was taken care of with :new, do the other one */
341 if (opt->jo_term_rows > 0 && (cmdmod.split & WSP_VERT))
342 win_setheight(opt->jo_term_rows);
343 if (opt->jo_term_cols > 0 && !(cmdmod.split & WSP_VERT))
344 win_setwidth(opt->jo_term_cols);
345 }
Bram Moolenaarcfcc0222017-08-05 17:13:48 +0200346
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200347 /* Link the new terminal in the list of active terminals. */
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200348 term->tl_next = first_term;
349 first_term = term;
350
Bram Moolenaar78712a72017-08-05 14:50:12 +0200351 if (opt->jo_term_name != NULL)
352 curbuf->b_ffname = vim_strsave(opt->jo_term_name);
353 else
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200354 {
355 int i;
Bram Moolenaardcaa6132017-08-13 17:13:09 +0200356 size_t len;
357 char_u *cmd, *p;
358
359 if (argvar->v_type == VAR_STRING)
Bram Moolenaar13ebb032017-08-26 22:02:51 +0200360 {
Bram Moolenaardcaa6132017-08-13 17:13:09 +0200361 cmd = argvar->vval.v_string;
Bram Moolenaar13ebb032017-08-26 22:02:51 +0200362 if (cmd == NULL)
363 cmd = (char_u *)"";
364 else if (STRCMP(cmd, "NONE") == 0)
365 cmd = (char_u *)"pty";
366 }
Bram Moolenaardcaa6132017-08-13 17:13:09 +0200367 else if (argvar->v_type != VAR_LIST
368 || argvar->vval.v_list == NULL
369 || argvar->vval.v_list->lv_len < 1)
370 cmd = (char_u*)"";
371 else
372 cmd = get_tv_string_chk(&argvar->vval.v_list->lv_first->li_tv);
373
374 len = STRLEN(cmd) + 10;
375 p = alloc((int)len);
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200376
Bram Moolenaar20e6cd02017-08-01 20:25:22 +0200377 for (i = 0; p != NULL; ++i)
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200378 {
Bram Moolenaar20e6cd02017-08-01 20:25:22 +0200379 /* Prepend a ! to the command name to avoid the buffer name equals
380 * the executable, otherwise ":w!" would overwrite it. */
381 if (i == 0)
382 vim_snprintf((char *)p, len, "!%s", cmd);
383 else
384 vim_snprintf((char *)p, len, "!%s (%d)", cmd, i);
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200385 if (buflist_findname(p) == NULL)
386 {
387 curbuf->b_ffname = p;
388 break;
389 }
390 }
391 }
392 curbuf->b_fname = curbuf->b_ffname;
393
Bram Moolenaar37c45832017-08-12 16:01:04 +0200394 if (opt->jo_term_opencmd != NULL)
395 term->tl_opencmd = vim_strsave(opt->jo_term_opencmd);
396
Bram Moolenaar3346cc42017-09-02 14:54:21 +0200397 if (opt->jo_eof_chars != NULL)
398 term->tl_eof_chars = vim_strsave(opt->jo_eof_chars);
Bram Moolenaar3346cc42017-09-02 14:54:21 +0200399
Bram Moolenaareb44a682017-08-03 22:44:55 +0200400 set_string_option_direct((char_u *)"buftype", -1,
401 (char_u *)"terminal", OPT_FREE|OPT_LOCAL, 0);
402
Bram Moolenaar20e6cd02017-08-01 20:25:22 +0200403 /* Mark the buffer as not modifiable. It can only be made modifiable after
404 * the job finished. */
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200405 curbuf->b_p_ma = FALSE;
Bram Moolenaareb44a682017-08-03 22:44:55 +0200406
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200407 set_term_and_win_size(term);
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200408 setup_job_options(opt, term->tl_rows, term->tl_cols);
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200409
Bram Moolenaar13ebb032017-08-26 22:02:51 +0200410 /* System dependent: setup the vterm and maybe start the job in it. */
411 if (argvar->v_type == VAR_STRING
412 && argvar->vval.v_string != NULL
413 && STRCMP(argvar->vval.v_string, "NONE") == 0)
414 res = create_pty_only(term, opt);
415 else
416 res = term_and_job_init(term, argvar, opt);
417
418 if (res == OK)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200419 {
Bram Moolenaar292d5692017-08-08 21:52:22 +0200420 /* Get and remember the size we ended up with. Update the pty. */
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200421 vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols);
Bram Moolenaar292d5692017-08-08 21:52:22 +0200422 term_report_winsize(term, term->tl_rows, term->tl_cols);
Bram Moolenaar8cad9302017-08-12 14:32:32 +0200423
Bram Moolenaar97bd5e62017-08-18 20:50:30 +0200424 /* Make sure we don't get stuck on sending keys to the job, it leads to
425 * a deadlock if the job is waiting for Vim to read. */
426 channel_set_nonblock(term->tl_job->jv_channel, PART_IN);
427
Bram Moolenaar8cad9302017-08-12 14:32:32 +0200428 if (old_curbuf != NULL)
429 {
430 --curbuf->b_nwindows;
431 curbuf = old_curbuf;
432 curwin->w_buffer = curbuf;
433 ++curbuf->b_nwindows;
434 }
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200435 }
436 else
437 {
Bram Moolenaar8cad9302017-08-12 14:32:32 +0200438 buf_T *buf = curbuf;
439
Bram Moolenaard85f2712017-07-28 21:51:57 +0200440 free_terminal(curbuf);
Bram Moolenaar8cad9302017-08-12 14:32:32 +0200441 if (old_curbuf != NULL)
442 {
443 --curbuf->b_nwindows;
444 curbuf = old_curbuf;
445 curwin->w_buffer = curbuf;
446 ++curbuf->b_nwindows;
447 }
Bram Moolenaar61a66052017-07-22 18:39:00 +0200448
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200449 /* Wiping out the buffer will also close the window and call
450 * free_terminal(). */
Bram Moolenaar8cad9302017-08-12 14:32:32 +0200451 do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200452 }
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200453}
454
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200455/*
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200456 * ":terminal": open a terminal window and execute a job in it.
457 */
458 void
459ex_terminal(exarg_T *eap)
460{
Bram Moolenaardcaa6132017-08-13 17:13:09 +0200461 typval_T argvar;
Bram Moolenaardd693ce2017-08-10 23:15:19 +0200462 jobopt_T opt;
463 char_u *cmd;
Bram Moolenaar4fa10192017-08-14 22:56:27 +0200464 char_u *tofree = NULL;
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200465
466 init_job_options(&opt);
Bram Moolenaarcfcc0222017-08-05 17:13:48 +0200467
Bram Moolenaardd693ce2017-08-10 23:15:19 +0200468 cmd = eap->arg;
469 while (*cmd && *cmd == '+' && *(cmd + 1) == '+')
470 {
Bram Moolenaarb2412082017-08-20 18:09:14 +0200471 char_u *p, *ep;
Bram Moolenaardd693ce2017-08-10 23:15:19 +0200472
473 cmd += 2;
474 p = skiptowhite(cmd);
Bram Moolenaarb2412082017-08-20 18:09:14 +0200475 ep = vim_strchr(cmd, '=');
476 if (ep != NULL && ep < p)
477 p = ep;
478
Bram Moolenaardd693ce2017-08-10 23:15:19 +0200479 if ((int)(p - cmd) == 5 && STRNICMP(cmd, "close", 5) == 0)
480 opt.jo_term_finish = 'c';
481 else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "open", 4) == 0)
482 opt.jo_term_finish = 'o';
Bram Moolenaarda43b612017-08-11 22:27:50 +0200483 else if ((int)(p - cmd) == 6 && STRNICMP(cmd, "curwin", 6) == 0)
484 opt.jo_curwin = 1;
Bram Moolenaar8cad9302017-08-12 14:32:32 +0200485 else if ((int)(p - cmd) == 6 && STRNICMP(cmd, "hidden", 6) == 0)
486 opt.jo_hidden = 1;
Bram Moolenaarb2412082017-08-20 18:09:14 +0200487 else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "rows", 4) == 0
488 && ep != NULL && isdigit(ep[1]))
489 {
490 opt.jo_set2 |= JO2_TERM_ROWS;
491 opt.jo_term_rows = atoi((char *)ep + 1);
492 p = skiptowhite(cmd);
493 }
494 else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "cols", 4) == 0
495 && ep != NULL && isdigit(ep[1]))
496 {
497 opt.jo_set2 |= JO2_TERM_COLS;
498 opt.jo_term_cols = atoi((char *)ep + 1);
499 p = skiptowhite(cmd);
500 }
Bram Moolenaaref68e4f2017-09-02 16:28:36 +0200501 else if ((int)(p - cmd) == 3 && STRNICMP(cmd, "eof", 3) == 0
502 && ep != NULL)
503 {
Bram Moolenaaref68e4f2017-09-02 16:28:36 +0200504 char_u *buf = NULL;
505 char_u *keys;
506
507 p = skiptowhite(cmd);
508 *p = NUL;
509 keys = replace_termcodes(ep + 1, &buf, TRUE, TRUE, TRUE);
510 opt.jo_set2 |= JO2_EOF_CHARS;
511 opt.jo_eof_chars = vim_strsave(keys);
512 vim_free(buf);
513 *p = ' ';
Bram Moolenaaref68e4f2017-09-02 16:28:36 +0200514 }
Bram Moolenaardd693ce2017-08-10 23:15:19 +0200515 else
516 {
517 if (*p)
518 *p = NUL;
519 EMSG2(_("E181: Invalid attribute: %s"), cmd);
520 return;
521 }
522 cmd = skipwhite(p);
523 }
Bram Moolenaar2438ae32017-08-13 17:38:11 +0200524 if (cmd == NULL || *cmd == NUL)
Bram Moolenaar4fa10192017-08-14 22:56:27 +0200525 /* Make a copy, an autocommand may set 'shell'. */
526 tofree = cmd = vim_strsave(p_sh);
Bram Moolenaardd693ce2017-08-10 23:15:19 +0200527
Bram Moolenaarb2412082017-08-20 18:09:14 +0200528 if (eap->addr_count > 0)
Bram Moolenaarcfcc0222017-08-05 17:13:48 +0200529 {
Bram Moolenaarb2412082017-08-20 18:09:14 +0200530 /* Write lines from current buffer to the job. */
531 opt.jo_set |= JO_IN_IO | JO_IN_BUF | JO_IN_TOP | JO_IN_BOT;
532 opt.jo_io[PART_IN] = JIO_BUFFER;
533 opt.jo_io_buf[PART_IN] = curbuf->b_fnum;
534 opt.jo_in_top = eap->line1;
535 opt.jo_in_bot = eap->line2;
Bram Moolenaarcfcc0222017-08-05 17:13:48 +0200536 }
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200537
Bram Moolenaardcaa6132017-08-13 17:13:09 +0200538 argvar.v_type = VAR_STRING;
539 argvar.vval.v_string = cmd;
540 term_start(&argvar, &opt, eap->forceit);
Bram Moolenaar4fa10192017-08-14 22:56:27 +0200541 vim_free(tofree);
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200542}
543
544/*
Bram Moolenaar63ecdda2017-07-28 22:29:35 +0200545 * Free the scrollback buffer for "term".
546 */
547 static void
548free_scrollback(term_T *term)
549{
550 int i;
551
552 for (i = 0; i < term->tl_scrollback.ga_len; ++i)
553 vim_free(((sb_line_T *)term->tl_scrollback.ga_data + i)->sb_cells);
554 ga_clear(&term->tl_scrollback);
555}
556
557/*
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200558 * Free a terminal and everything it refers to.
559 * Kills the job if there is one.
560 * Called when wiping out a buffer.
561 */
562 void
Bram Moolenaard85f2712017-07-28 21:51:57 +0200563free_terminal(buf_T *buf)
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200564{
Bram Moolenaard85f2712017-07-28 21:51:57 +0200565 term_T *term = buf->b_term;
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200566 term_T *tp;
567
568 if (term == NULL)
569 return;
570 if (first_term == term)
571 first_term = term->tl_next;
572 else
573 for (tp = first_term; tp->tl_next != NULL; tp = tp->tl_next)
574 if (tp->tl_next == term)
575 {
576 tp->tl_next = term->tl_next;
577 break;
578 }
579
580 if (term->tl_job != NULL)
581 {
Bram Moolenaar61a66052017-07-22 18:39:00 +0200582 if (term->tl_job->jv_status != JOB_ENDED
Bram Moolenaar13ebb032017-08-26 22:02:51 +0200583 && term->tl_job->jv_status != JOB_FINISHED
584 && term->tl_job->jv_status != JOB_FAILED)
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200585 job_stop(term->tl_job, NULL, "kill");
586 job_unref(term->tl_job);
587 }
588
Bram Moolenaar63ecdda2017-07-28 22:29:35 +0200589 free_scrollback(term);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200590
591 term_free_vterm(term);
Bram Moolenaar21554412017-07-24 21:44:43 +0200592 vim_free(term->tl_title);
593 vim_free(term->tl_status_text);
Bram Moolenaar37c45832017-08-12 16:01:04 +0200594 vim_free(term->tl_opencmd);
Bram Moolenaar3346cc42017-09-02 14:54:21 +0200595 vim_free(term->tl_eof_chars);
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +0200596 vim_free(term->tl_cursor_color);
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200597 vim_free(term);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200598 buf->b_term = NULL;
Bram Moolenaar679653e2017-08-13 14:13:19 +0200599 if (in_terminal_loop == term)
600 in_terminal_loop = NULL;
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200601}
602
603/*
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200604 * Write job output "msg[len]" to the vterm.
605 */
606 static void
607term_write_job_output(term_T *term, char_u *msg, size_t len)
608{
609 VTerm *vterm = term->tl_vterm;
610 char_u *p;
611 size_t done;
612 size_t len_now;
613
Bram Moolenaar4f44b882017-08-13 20:06:18 +0200614 if (term_nl_does_cr)
615 vterm_input_write(vterm, (char *)msg, len);
616 else
617 /* need to convert NL to CR-NL */
618 for (done = 0; done < len; done += len_now)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200619 {
Bram Moolenaar4f44b882017-08-13 20:06:18 +0200620 for (p = msg + done; p < msg + len; )
621 {
622 if (*p == NL)
623 break;
624 p += utf_ptr2len_len(p, (int)(len - (p - msg)));
625 }
626 len_now = p - msg - done;
627 vterm_input_write(vterm, (char *)msg + done, len_now);
628 if (p < msg + len && *p == NL)
629 {
630 vterm_input_write(vterm, "\r\n", 2);
631 ++len_now;
632 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200633 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200634
635 /* this invokes the damage callbacks */
636 vterm_screen_flush_damage(vterm_obtain_screen(vterm));
637}
638
Bram Moolenaar1c844932017-07-24 23:36:41 +0200639 static void
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200640update_cursor(term_T *term, int redraw)
Bram Moolenaar1c844932017-07-24 23:36:41 +0200641{
Bram Moolenaar6d819742017-08-06 14:57:49 +0200642 if (term->tl_normal_mode)
Bram Moolenaar392d1bf2017-07-31 21:18:58 +0200643 return;
Bram Moolenaar1c844932017-07-24 23:36:41 +0200644 setcursor();
Bram Moolenaar5cc1f2c2017-08-13 15:16:53 +0200645 if (redraw)
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200646 {
Bram Moolenaar5cc1f2c2017-08-13 15:16:53 +0200647 if (term->tl_buffer == curbuf && term->tl_cursor_visible)
Bram Moolenaar4cc93dc2017-07-26 21:49:37 +0200648 cursor_on();
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200649 out_flush();
Bram Moolenaar1c844932017-07-24 23:36:41 +0200650#ifdef FEAT_GUI
Bram Moolenaar12d93ee2017-07-30 19:02:02 +0200651 if (gui.in_use)
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200652 gui_update_cursor(FALSE, FALSE);
Bram Moolenaar1c844932017-07-24 23:36:41 +0200653#endif
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200654 }
Bram Moolenaar1c844932017-07-24 23:36:41 +0200655}
656
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200657/*
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200658 * Invoked when "msg" output from a job was received. Write it to the terminal
659 * of "buffer".
660 */
661 void
662write_to_term(buf_T *buffer, char_u *msg, channel_T *channel)
663{
664 size_t len = STRLEN(msg);
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200665 term_T *term = buffer->b_term;
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200666
Bram Moolenaard85f2712017-07-28 21:51:57 +0200667 if (term->tl_vterm == NULL)
668 {
Bram Moolenaar2f3a90a2017-08-03 14:49:29 +0200669 ch_log(channel, "NOT writing %d bytes to terminal", (int)len);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200670 return;
671 }
Bram Moolenaar2f3a90a2017-08-03 14:49:29 +0200672 ch_log(channel, "writing %d bytes to terminal", (int)len);
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200673 term_write_job_output(term, msg, len);
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200674
Bram Moolenaar5cc1f2c2017-08-13 15:16:53 +0200675 /* In Terminal-Normal mode we are displaying the buffer, not the terminal
676 * contents, thus no screen update is needed. */
Bram Moolenaar6d819742017-08-06 14:57:49 +0200677 if (!term->tl_normal_mode)
Bram Moolenaar392d1bf2017-07-31 21:18:58 +0200678 {
679 /* TODO: only update once in a while. */
Bram Moolenaar5cc1f2c2017-08-13 15:16:53 +0200680 ch_log(term->tl_job->jv_channel, "updating screen");
681 if (buffer == curbuf)
682 {
683 update_screen(0);
684 update_cursor(term, TRUE);
685 }
686 else
Bram Moolenaar02e177d2017-08-26 23:43:28 +0200687 redraw_after_callback(TRUE);
Bram Moolenaar392d1bf2017-07-31 21:18:58 +0200688 }
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200689}
690
691/*
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200692 * Send a mouse position and click to the vterm
693 */
694 static int
695term_send_mouse(VTerm *vterm, int button, int pressed)
696{
697 VTermModifier mod = VTERM_MOD_NONE;
698
699 vterm_mouse_move(vterm, mouse_row - W_WINROW(curwin),
700 mouse_col - W_WINCOL(curwin), mod);
701 vterm_mouse_button(vterm, button, pressed, mod);
702 return TRUE;
703}
704
705/*
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200706 * Convert typed key "c" into bytes to send to the job.
707 * Return the number of bytes in "buf".
708 */
709 static int
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200710term_convert_key(term_T *term, int c, char *buf)
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200711{
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200712 VTerm *vterm = term->tl_vterm;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200713 VTermKey key = VTERM_KEY_NONE;
714 VTermModifier mod = VTERM_MOD_NONE;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200715 int mouse = FALSE;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200716
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200717 switch (c)
718 {
Bram Moolenaar4f44b882017-08-13 20:06:18 +0200719 case CAR: c = term_enter_char; break;
720 /* don't use VTERM_KEY_BACKSPACE, it always
721 * becomes 0x7f DEL */
722 case K_BS: c = term_backspace_char; break;
723
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200724 case ESC: key = VTERM_KEY_ESCAPE; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200725 case K_DEL: key = VTERM_KEY_DEL; break;
726 case K_DOWN: key = VTERM_KEY_DOWN; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200727 case K_S_DOWN: mod = VTERM_MOD_SHIFT;
728 key = VTERM_KEY_DOWN; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200729 case K_END: key = VTERM_KEY_END; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200730 case K_S_END: mod = VTERM_MOD_SHIFT;
731 key = VTERM_KEY_END; break;
732 case K_C_END: mod = VTERM_MOD_CTRL;
733 key = VTERM_KEY_END; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200734 case K_F10: key = VTERM_KEY_FUNCTION(10); break;
735 case K_F11: key = VTERM_KEY_FUNCTION(11); break;
736 case K_F12: key = VTERM_KEY_FUNCTION(12); break;
737 case K_F1: key = VTERM_KEY_FUNCTION(1); break;
738 case K_F2: key = VTERM_KEY_FUNCTION(2); break;
739 case K_F3: key = VTERM_KEY_FUNCTION(3); break;
740 case K_F4: key = VTERM_KEY_FUNCTION(4); break;
741 case K_F5: key = VTERM_KEY_FUNCTION(5); break;
742 case K_F6: key = VTERM_KEY_FUNCTION(6); break;
743 case K_F7: key = VTERM_KEY_FUNCTION(7); break;
744 case K_F8: key = VTERM_KEY_FUNCTION(8); break;
745 case K_F9: key = VTERM_KEY_FUNCTION(9); break;
746 case K_HOME: key = VTERM_KEY_HOME; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200747 case K_S_HOME: mod = VTERM_MOD_SHIFT;
748 key = VTERM_KEY_HOME; break;
749 case K_C_HOME: mod = VTERM_MOD_CTRL;
750 key = VTERM_KEY_HOME; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200751 case K_INS: key = VTERM_KEY_INS; break;
752 case K_K0: key = VTERM_KEY_KP_0; break;
753 case K_K1: key = VTERM_KEY_KP_1; break;
754 case K_K2: key = VTERM_KEY_KP_2; break;
755 case K_K3: key = VTERM_KEY_KP_3; break;
756 case K_K4: key = VTERM_KEY_KP_4; break;
757 case K_K5: key = VTERM_KEY_KP_5; break;
758 case K_K6: key = VTERM_KEY_KP_6; break;
759 case K_K7: key = VTERM_KEY_KP_7; break;
760 case K_K8: key = VTERM_KEY_KP_8; break;
761 case K_K9: key = VTERM_KEY_KP_9; break;
762 case K_KDEL: key = VTERM_KEY_DEL; break; /* TODO */
763 case K_KDIVIDE: key = VTERM_KEY_KP_DIVIDE; break;
764 case K_KEND: key = VTERM_KEY_KP_1; break; /* TODO */
765 case K_KENTER: key = VTERM_KEY_KP_ENTER; break;
766 case K_KHOME: key = VTERM_KEY_KP_7; break; /* TODO */
767 case K_KINS: key = VTERM_KEY_KP_0; break; /* TODO */
768 case K_KMINUS: key = VTERM_KEY_KP_MINUS; break;
769 case K_KMULTIPLY: key = VTERM_KEY_KP_MULT; break;
770 case K_KPAGEDOWN: key = VTERM_KEY_KP_3; break; /* TODO */
771 case K_KPAGEUP: key = VTERM_KEY_KP_9; break; /* TODO */
772 case K_KPLUS: key = VTERM_KEY_KP_PLUS; break;
773 case K_KPOINT: key = VTERM_KEY_KP_PERIOD; break;
774 case K_LEFT: key = VTERM_KEY_LEFT; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200775 case K_S_LEFT: mod = VTERM_MOD_SHIFT;
776 key = VTERM_KEY_LEFT; break;
777 case K_C_LEFT: mod = VTERM_MOD_CTRL;
778 key = VTERM_KEY_LEFT; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200779 case K_PAGEDOWN: key = VTERM_KEY_PAGEDOWN; break;
780 case K_PAGEUP: key = VTERM_KEY_PAGEUP; break;
781 case K_RIGHT: key = VTERM_KEY_RIGHT; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200782 case K_S_RIGHT: mod = VTERM_MOD_SHIFT;
783 key = VTERM_KEY_RIGHT; break;
784 case K_C_RIGHT: mod = VTERM_MOD_CTRL;
785 key = VTERM_KEY_RIGHT; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200786 case K_UP: key = VTERM_KEY_UP; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200787 case K_S_UP: mod = VTERM_MOD_SHIFT;
788 key = VTERM_KEY_UP; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200789 case TAB: key = VTERM_KEY_TAB; break;
Bram Moolenaare825d8b2017-07-19 23:20:19 +0200790
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200791 case K_MOUSEUP: mouse = term_send_mouse(vterm, 5, 1); break;
792 case K_MOUSEDOWN: mouse = term_send_mouse(vterm, 4, 1); break;
793 case K_MOUSELEFT: /* TODO */ return 0;
794 case K_MOUSERIGHT: /* TODO */ return 0;
Bram Moolenaare825d8b2017-07-19 23:20:19 +0200795
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200796 case K_LEFTMOUSE:
797 case K_LEFTMOUSE_NM: mouse = term_send_mouse(vterm, 1, 1); break;
798 case K_LEFTDRAG: mouse = term_send_mouse(vterm, 1, 1); break;
799 case K_LEFTRELEASE:
800 case K_LEFTRELEASE_NM: mouse = term_send_mouse(vterm, 1, 0); break;
801 case K_MIDDLEMOUSE: mouse = term_send_mouse(vterm, 2, 1); break;
802 case K_MIDDLEDRAG: mouse = term_send_mouse(vterm, 2, 1); break;
803 case K_MIDDLERELEASE: mouse = term_send_mouse(vterm, 2, 0); break;
804 case K_RIGHTMOUSE: mouse = term_send_mouse(vterm, 3, 1); break;
805 case K_RIGHTDRAG: mouse = term_send_mouse(vterm, 3, 1); break;
806 case K_RIGHTRELEASE: mouse = term_send_mouse(vterm, 3, 0); break;
807 case K_X1MOUSE: /* TODO */ return 0;
808 case K_X1DRAG: /* TODO */ return 0;
809 case K_X1RELEASE: /* TODO */ return 0;
810 case K_X2MOUSE: /* TODO */ return 0;
811 case K_X2DRAG: /* TODO */ return 0;
812 case K_X2RELEASE: /* TODO */ return 0;
Bram Moolenaare825d8b2017-07-19 23:20:19 +0200813
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200814 case K_IGNORE: return 0;
815 case K_NOP: return 0;
816 case K_UNDO: return 0;
817 case K_HELP: return 0;
818 case K_XF1: key = VTERM_KEY_FUNCTION(1); break;
819 case K_XF2: key = VTERM_KEY_FUNCTION(2); break;
820 case K_XF3: key = VTERM_KEY_FUNCTION(3); break;
821 case K_XF4: key = VTERM_KEY_FUNCTION(4); break;
822 case K_SELECT: return 0;
823#ifdef FEAT_GUI
824 case K_VER_SCROLLBAR: return 0;
825 case K_HOR_SCROLLBAR: return 0;
826#endif
827#ifdef FEAT_GUI_TABLINE
828 case K_TABLINE: return 0;
829 case K_TABMENU: return 0;
830#endif
831#ifdef FEAT_NETBEANS_INTG
832 case K_F21: key = VTERM_KEY_FUNCTION(21); break;
833#endif
834#ifdef FEAT_DND
835 case K_DROP: return 0;
836#endif
837#ifdef FEAT_AUTOCMD
838 case K_CURSORHOLD: return 0;
839#endif
840 case K_PS: vterm_keyboard_start_paste(vterm); return 0;
841 case K_PE: vterm_keyboard_end_paste(vterm); return 0;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200842 }
843
844 /*
845 * Convert special keys to vterm keys:
846 * - Write keys to vterm: vterm_keyboard_key()
847 * - Write output to channel.
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200848 * TODO: use mod_mask
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200849 */
850 if (key != VTERM_KEY_NONE)
851 /* Special key, let vterm convert it. */
852 vterm_keyboard_key(vterm, key, mod);
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200853 else if (!mouse)
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200854 /* Normal character, let vterm convert it. */
855 vterm_keyboard_unichar(vterm, c, mod);
856
857 /* Read back the converted escape sequence. */
Bram Moolenaara1b5b092017-07-26 21:29:34 +0200858 return (int)vterm_output_read(vterm, buf, KEY_BUF_LEN);
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200859}
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200860
Bram Moolenaar938783d2017-07-16 20:13:26 +0200861/*
Bram Moolenaarb000e322017-07-30 19:38:21 +0200862 * Return TRUE if the job for "term" is still running.
Bram Moolenaard85f2712017-07-28 21:51:57 +0200863 */
Bram Moolenaar94053a52017-08-01 21:44:33 +0200864 int
Bram Moolenaard85f2712017-07-28 21:51:57 +0200865term_job_running(term_T *term)
866{
Bram Moolenaar1e8340b2017-07-29 15:53:39 +0200867 /* Also consider the job finished when the channel is closed, to avoid a
868 * race condition when updating the title. */
Bram Moolenaarb4a67212017-08-03 19:22:36 +0200869 return term != NULL
870 && term->tl_job != NULL
Bram Moolenaar13ebb032017-08-26 22:02:51 +0200871 && channel_is_open(term->tl_job->jv_channel)
872 && (term->tl_job->jv_status == JOB_STARTED
873 || term->tl_job->jv_channel->ch_keep_open);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200874}
875
876/*
Bram Moolenaar423802d2017-07-30 16:52:24 +0200877 * Add the last line of the scrollback buffer to the buffer in the window.
878 */
879 static void
Bram Moolenaar33a43be2017-08-06 21:36:22 +0200880add_scrollback_line_to_buffer(term_T *term, char_u *text, int len)
Bram Moolenaar423802d2017-07-30 16:52:24 +0200881{
Bram Moolenaarf8d57a52017-08-07 20:38:42 +0200882 buf_T *buf = term->tl_buffer;
883 int empty = (buf->b_ml.ml_flags & ML_EMPTY);
884 linenr_T lnum = buf->b_ml.ml_line_count;
Bram Moolenaar423802d2017-07-30 16:52:24 +0200885
Bram Moolenaar58302322017-08-22 20:33:53 +0200886#ifdef WIN3264
Bram Moolenaar740c4332017-08-21 22:01:27 +0200887 if (!enc_utf8 && enc_codepage > 0)
888 {
889 WCHAR *ret = NULL;
890 int length = 0;
891
892 MultiByteToWideChar_alloc(CP_UTF8, 0, (char*)text, len + 1,
893 &ret, &length);
894 if (ret != NULL)
895 {
896 WideCharToMultiByte_alloc(enc_codepage, 0,
897 ret, length, (char **)&text, &len, 0, 0);
898 vim_free(ret);
899 ml_append_buf(term->tl_buffer, lnum, text, len, FALSE);
900 vim_free(text);
901 }
902 }
903 else
904#endif
905 ml_append_buf(term->tl_buffer, lnum, text, len + 1, FALSE);
Bram Moolenaarf8d57a52017-08-07 20:38:42 +0200906 if (empty)
Bram Moolenaar423802d2017-07-30 16:52:24 +0200907 {
908 /* Delete the empty line that was in the empty buffer. */
Bram Moolenaarf8d57a52017-08-07 20:38:42 +0200909 curbuf = buf;
910 ml_delete(1, FALSE);
Bram Moolenaar423802d2017-07-30 16:52:24 +0200911 curbuf = curwin->w_buffer;
912 }
Bram Moolenaar423802d2017-07-30 16:52:24 +0200913}
914
915/*
916 * Add the current lines of the terminal to scrollback and to the buffer.
Bram Moolenaar33a43be2017-08-06 21:36:22 +0200917 * Called after the job has ended and when switching to Terminal-Normal mode.
Bram Moolenaar423802d2017-07-30 16:52:24 +0200918 */
919 static void
920move_terminal_to_buffer(term_T *term)
921{
922 win_T *wp;
923 int len;
924 int lines_skipped = 0;
925 VTermPos pos;
926 VTermScreenCell cell;
Bram Moolenaar33a43be2017-08-06 21:36:22 +0200927 cellattr_T *p;
Bram Moolenaar8e5eece2017-08-04 20:29:53 +0200928 VTermScreen *screen;
Bram Moolenaar423802d2017-07-30 16:52:24 +0200929
Bram Moolenaar8e5eece2017-08-04 20:29:53 +0200930 if (term->tl_vterm == NULL)
931 return;
932 screen = vterm_obtain_screen(term->tl_vterm);
Bram Moolenaar423802d2017-07-30 16:52:24 +0200933 for (pos.row = 0; pos.row < term->tl_rows; ++pos.row)
934 {
935 len = 0;
936 for (pos.col = 0; pos.col < term->tl_cols; ++pos.col)
937 if (vterm_screen_get_cell(screen, pos, &cell) != 0
938 && cell.chars[0] != NUL)
939 len = pos.col + 1;
940
941 if (len == 0)
942 ++lines_skipped;
943 else
944 {
945 while (lines_skipped > 0)
946 {
947 /* Line was skipped, add an empty line. */
948 --lines_skipped;
949 if (ga_grow(&term->tl_scrollback, 1) == OK)
950 {
951 sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data
952 + term->tl_scrollback.ga_len;
953
954 line->sb_cols = 0;
955 line->sb_cells = NULL;
956 ++term->tl_scrollback.ga_len;
957
Bram Moolenaar33a43be2017-08-06 21:36:22 +0200958 add_scrollback_line_to_buffer(term, (char_u *)"", 0);
Bram Moolenaar423802d2017-07-30 16:52:24 +0200959 }
960 }
961
Bram Moolenaar33a43be2017-08-06 21:36:22 +0200962 p = (cellattr_T *)alloc((int)sizeof(cellattr_T) * len);
Bram Moolenaar423802d2017-07-30 16:52:24 +0200963 if (p != NULL && ga_grow(&term->tl_scrollback, 1) == OK)
964 {
Bram Moolenaar33a43be2017-08-06 21:36:22 +0200965 garray_T ga;
966 int width;
967 sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data
Bram Moolenaar423802d2017-07-30 16:52:24 +0200968 + term->tl_scrollback.ga_len;
969
Bram Moolenaar33a43be2017-08-06 21:36:22 +0200970 ga_init2(&ga, 1, 100);
971 for (pos.col = 0; pos.col < len; pos.col += width)
Bram Moolenaar423802d2017-07-30 16:52:24 +0200972 {
973 if (vterm_screen_get_cell(screen, pos, &cell) == 0)
Bram Moolenaar33a43be2017-08-06 21:36:22 +0200974 {
975 width = 1;
976 vim_memset(p + pos.col, 0, sizeof(cellattr_T));
977 if (ga_grow(&ga, 1) == OK)
Bram Moolenaar6c4d12c2017-08-23 23:36:25 +0200978 ga.ga_len += utf_char2bytes(' ',
Bram Moolenaar33a43be2017-08-06 21:36:22 +0200979 (char_u *)ga.ga_data + ga.ga_len);
980 }
Bram Moolenaar423802d2017-07-30 16:52:24 +0200981 else
Bram Moolenaar33a43be2017-08-06 21:36:22 +0200982 {
983 width = cell.width;
984
985 p[pos.col].width = cell.width;
986 p[pos.col].attrs = cell.attrs;
987 p[pos.col].fg = cell.fg;
988 p[pos.col].bg = cell.bg;
989
990 if (ga_grow(&ga, MB_MAXBYTES) == OK)
991 {
992 int i;
993 int c;
994
995 for (i = 0; (c = cell.chars[i]) > 0 || i == 0; ++i)
Bram Moolenaar740c4332017-08-21 22:01:27 +0200996 ga.ga_len += utf_char2bytes(c == NUL ? ' ' : c,
Bram Moolenaar33a43be2017-08-06 21:36:22 +0200997 (char_u *)ga.ga_data + ga.ga_len);
998 }
999 }
Bram Moolenaar423802d2017-07-30 16:52:24 +02001000 }
1001 line->sb_cols = len;
1002 line->sb_cells = p;
1003 ++term->tl_scrollback.ga_len;
1004
Bram Moolenaar33a43be2017-08-06 21:36:22 +02001005 if (ga_grow(&ga, 1) == FAIL)
1006 add_scrollback_line_to_buffer(term, (char_u *)"", 0);
1007 else
1008 {
1009 *((char_u *)ga.ga_data + ga.ga_len) = NUL;
1010 add_scrollback_line_to_buffer(term, ga.ga_data, ga.ga_len);
1011 }
1012 ga_clear(&ga);
Bram Moolenaar423802d2017-07-30 16:52:24 +02001013 }
1014 else
1015 vim_free(p);
1016 }
1017 }
1018
1019 FOR_ALL_WINDOWS(wp)
1020 {
1021 if (wp->w_buffer == term->tl_buffer)
1022 {
1023 wp->w_cursor.lnum = term->tl_buffer->b_ml.ml_line_count;
1024 wp->w_cursor.col = 0;
1025 wp->w_valid = 0;
Bram Moolenaare0f314a2017-08-13 16:01:31 +02001026 if (wp->w_cursor.lnum >= wp->w_height)
1027 {
1028 linenr_T min_topline = wp->w_cursor.lnum - wp->w_height + 1;
1029
1030 if (wp->w_topline < min_topline)
1031 wp->w_topline = min_topline;
1032 }
Bram Moolenaar423802d2017-07-30 16:52:24 +02001033 redraw_win_later(wp, NOT_VALID);
1034 }
1035 }
1036}
1037
1038 static void
Bram Moolenaar6d819742017-08-06 14:57:49 +02001039set_terminal_mode(term_T *term, int normal_mode)
Bram Moolenaar423802d2017-07-30 16:52:24 +02001040{
Bram Moolenaar6d819742017-08-06 14:57:49 +02001041 term->tl_normal_mode = normal_mode;
Bram Moolenaar423802d2017-07-30 16:52:24 +02001042 vim_free(term->tl_status_text);
1043 term->tl_status_text = NULL;
1044 if (term->tl_buffer == curbuf)
1045 maketitle();
1046}
1047
1048/*
1049 * Called after the job if finished and Terminal mode is not active:
1050 * Move the vterm contents into the scrollback buffer and free the vterm.
1051 */
1052 static void
1053cleanup_vterm(term_T *term)
1054{
Bram Moolenaar8cad9302017-08-12 14:32:32 +02001055 if (term->tl_finish != 'c')
Bram Moolenaardd693ce2017-08-10 23:15:19 +02001056 move_terminal_to_buffer(term);
Bram Moolenaar423802d2017-07-30 16:52:24 +02001057 term_free_vterm(term);
Bram Moolenaar6d819742017-08-06 14:57:49 +02001058 set_terminal_mode(term, FALSE);
Bram Moolenaar423802d2017-07-30 16:52:24 +02001059}
1060
1061/*
Bram Moolenaaraaa8a352017-08-05 20:17:00 +02001062 * Switch from Terminal-Job mode to Terminal-Normal mode.
Bram Moolenaar423802d2017-07-30 16:52:24 +02001063 * Suspends updating the terminal window.
1064 */
1065 static void
Bram Moolenaar6d819742017-08-06 14:57:49 +02001066term_enter_normal_mode(void)
Bram Moolenaar423802d2017-07-30 16:52:24 +02001067{
1068 term_T *term = curbuf->b_term;
1069
1070 /* Append the current terminal contents to the buffer. */
1071 move_terminal_to_buffer(term);
1072
Bram Moolenaar6d819742017-08-06 14:57:49 +02001073 set_terminal_mode(term, TRUE);
Bram Moolenaaraaa8a352017-08-05 20:17:00 +02001074
Bram Moolenaar6d819742017-08-06 14:57:49 +02001075 /* Move the window cursor to the position of the cursor in the
1076 * terminal. */
1077 curwin->w_cursor.lnum = term->tl_scrollback_scrolled
1078 + term->tl_cursor_pos.row + 1;
1079 check_cursor();
1080 coladvance(term->tl_cursor_pos.col);
Bram Moolenaaraaa8a352017-08-05 20:17:00 +02001081
Bram Moolenaar6d819742017-08-06 14:57:49 +02001082 /* Display the same lines as in the terminal. */
1083 curwin->w_topline = term->tl_scrollback_scrolled + 1;
Bram Moolenaar423802d2017-07-30 16:52:24 +02001084}
1085
1086/*
1087 * Returns TRUE if the current window contains a terminal and we are in
1088 * Terminal-Normal mode.
1089 */
1090 int
Bram Moolenaar6d819742017-08-06 14:57:49 +02001091term_in_normal_mode(void)
Bram Moolenaar423802d2017-07-30 16:52:24 +02001092{
1093 term_T *term = curbuf->b_term;
1094
Bram Moolenaar6d819742017-08-06 14:57:49 +02001095 return term != NULL && term->tl_normal_mode;
Bram Moolenaar423802d2017-07-30 16:52:24 +02001096}
1097
1098/*
Bram Moolenaaraaa8a352017-08-05 20:17:00 +02001099 * Switch from Terminal-Normal mode to Terminal-Job mode.
Bram Moolenaar423802d2017-07-30 16:52:24 +02001100 * Restores updating the terminal window.
1101 */
1102 void
Bram Moolenaar6d819742017-08-06 14:57:49 +02001103term_enter_job_mode()
Bram Moolenaar423802d2017-07-30 16:52:24 +02001104{
1105 term_T *term = curbuf->b_term;
1106 sb_line_T *line;
1107 garray_T *gap;
1108
1109 /* Remove the terminal contents from the scrollback and the buffer. */
1110 gap = &term->tl_scrollback;
Bram Moolenaar77ac9b52017-08-19 21:23:05 +02001111 while (curbuf->b_ml.ml_line_count > term->tl_scrollback_scrolled
1112 && gap->ga_len > 0)
Bram Moolenaar423802d2017-07-30 16:52:24 +02001113 {
1114 ml_delete(curbuf->b_ml.ml_line_count, FALSE);
1115 line = (sb_line_T *)gap->ga_data + gap->ga_len - 1;
1116 vim_free(line->sb_cells);
1117 --gap->ga_len;
Bram Moolenaar423802d2017-07-30 16:52:24 +02001118 }
1119 check_cursor();
1120
Bram Moolenaar6d819742017-08-06 14:57:49 +02001121 set_terminal_mode(term, FALSE);
Bram Moolenaar423802d2017-07-30 16:52:24 +02001122
1123 if (term->tl_channel_closed)
1124 cleanup_vterm(term);
1125 redraw_buf_and_status_later(curbuf, NOT_VALID);
1126}
1127
1128/*
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001129 * Get a key from the user without mapping.
Bram Moolenaar679653e2017-08-13 14:13:19 +02001130 * Note: while waiting a terminal may be closed and freed if the channel is
1131 * closed and ++close was used.
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001132 * TODO: use terminal mode mappings.
1133 */
1134 static int
1135term_vgetc()
1136{
1137 int c;
1138
1139 ++no_mapping;
1140 ++allow_keys;
1141 got_int = FALSE;
Bram Moolenaar9698ad72017-08-12 14:52:15 +02001142#ifdef WIN3264
1143 ctrl_break_was_pressed = FALSE;
1144#endif
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001145 c = vgetc();
Bram Moolenaar43c007f2017-07-30 17:45:37 +02001146 got_int = FALSE;
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001147 --no_mapping;
1148 --allow_keys;
1149 return c;
1150}
1151
1152/*
Bram Moolenaarb2412082017-08-20 18:09:14 +02001153 * Get the part that is connected to the tty. Normally this is PART_IN, but
1154 * when writing buffer lines to the job it can be another. This makes it
1155 * possible to do "1,5term vim -".
1156 */
1157 static ch_part_T
1158get_tty_part(term_T *term)
1159{
1160#ifdef UNIX
1161 ch_part_T parts[3] = {PART_IN, PART_OUT, PART_ERR};
1162 int i;
1163
1164 for (i = 0; i < 3; ++i)
1165 {
1166 int fd = term->tl_job->jv_channel->ch_part[parts[i]].ch_fd;
1167
1168 if (isatty(fd))
1169 return parts[i];
1170 }
1171#endif
1172 return PART_IN;
1173}
1174
1175/*
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001176 * Send keys to terminal.
Bram Moolenaar69198192017-08-05 14:10:48 +02001177 * Return FAIL when the key needs to be handled in Normal mode.
1178 * Return OK when the key was dropped or sent to the terminal.
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001179 */
Bram Moolenaar98fd66d2017-08-05 19:34:47 +02001180 int
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001181send_keys_to_term(term_T *term, int c, int typed)
1182{
1183 char msg[KEY_BUF_LEN];
1184 size_t len;
1185 static int mouse_was_outside = FALSE;
1186 int dragging_outside = FALSE;
1187
1188 /* Catch keys that need to be handled as in Normal mode. */
1189 switch (c)
1190 {
1191 case NUL:
1192 case K_ZERO:
1193 if (typed)
1194 stuffcharReadbuff(c);
1195 return FAIL;
1196
1197 case K_IGNORE:
1198 return FAIL;
1199
1200 case K_LEFTDRAG:
1201 case K_MIDDLEDRAG:
1202 case K_RIGHTDRAG:
1203 case K_X1DRAG:
1204 case K_X2DRAG:
1205 dragging_outside = mouse_was_outside;
1206 /* FALLTHROUGH */
1207 case K_LEFTMOUSE:
1208 case K_LEFTMOUSE_NM:
1209 case K_LEFTRELEASE:
1210 case K_LEFTRELEASE_NM:
1211 case K_MIDDLEMOUSE:
1212 case K_MIDDLERELEASE:
1213 case K_RIGHTMOUSE:
1214 case K_RIGHTRELEASE:
1215 case K_X1MOUSE:
1216 case K_X1RELEASE:
1217 case K_X2MOUSE:
1218 case K_X2RELEASE:
Bram Moolenaar98fd66d2017-08-05 19:34:47 +02001219
1220 case K_MOUSEUP:
1221 case K_MOUSEDOWN:
1222 case K_MOUSELEFT:
1223 case K_MOUSERIGHT:
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001224 if (mouse_row < W_WINROW(curwin)
1225 || mouse_row >= (W_WINROW(curwin) + curwin->w_height)
1226 || mouse_col < W_WINCOL(curwin)
1227 || mouse_col >= W_ENDCOL(curwin)
1228 || dragging_outside)
1229 {
Bram Moolenaar98fd66d2017-08-05 19:34:47 +02001230 /* click or scroll outside the current window */
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001231 if (typed)
1232 {
1233 stuffcharReadbuff(c);
1234 mouse_was_outside = TRUE;
1235 }
1236 return FAIL;
1237 }
1238 }
1239 if (typed)
1240 mouse_was_outside = FALSE;
1241
1242 /* Convert the typed key to a sequence of bytes for the job. */
1243 len = term_convert_key(term, c, msg);
1244 if (len > 0)
1245 /* TODO: if FAIL is returned, stop? */
Bram Moolenaarb2412082017-08-20 18:09:14 +02001246 channel_send(term->tl_job->jv_channel, get_tty_part(term),
1247 (char_u *)msg, (int)len, NULL);
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001248
1249 return OK;
1250}
1251
Bram Moolenaar0e23e9c2017-07-30 18:47:19 +02001252 static void
1253position_cursor(win_T *wp, VTermPos *pos)
1254{
1255 wp->w_wrow = MIN(pos->row, MAX(0, wp->w_height - 1));
1256 wp->w_wcol = MIN(pos->col, MAX(0, wp->w_width - 1));
1257 wp->w_valid |= (VALID_WCOL|VALID_WROW);
1258}
1259
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001260/*
Bram Moolenaarc9456ce2017-07-30 21:46:04 +02001261 * Handle CTRL-W "": send register contents to the job.
1262 */
1263 static void
1264term_paste_register(int prev_c UNUSED)
1265{
1266 int c;
1267 list_T *l;
1268 listitem_T *item;
1269 long reglen = 0;
1270 int type;
1271
1272#ifdef FEAT_CMDL_INFO
1273 if (add_to_showcmd(prev_c))
1274 if (add_to_showcmd('"'))
1275 out_flush();
1276#endif
1277 c = term_vgetc();
1278#ifdef FEAT_CMDL_INFO
1279 clear_showcmd();
1280#endif
Bram Moolenaar679653e2017-08-13 14:13:19 +02001281 if (!term_use_loop())
1282 /* job finished while waiting for a character */
1283 return;
Bram Moolenaarc9456ce2017-07-30 21:46:04 +02001284
1285 /* CTRL-W "= prompt for expression to evaluate. */
1286 if (c == '=' && get_expr_register() != '=')
1287 return;
Bram Moolenaar679653e2017-08-13 14:13:19 +02001288 if (!term_use_loop())
1289 /* job finished while waiting for a character */
1290 return;
Bram Moolenaarc9456ce2017-07-30 21:46:04 +02001291
1292 l = (list_T *)get_reg_contents(c, GREG_LIST);
1293 if (l != NULL)
1294 {
1295 type = get_reg_type(c, &reglen);
1296 for (item = l->lv_first; item != NULL; item = item->li_next)
1297 {
1298 char_u *s = get_tv_string(&item->li_tv);
Bram Moolenaar285f2432017-08-23 23:10:21 +02001299#ifdef WIN3264
1300 char_u *tmp = s;
Bram Moolenaarc9456ce2017-07-30 21:46:04 +02001301
Bram Moolenaar285f2432017-08-23 23:10:21 +02001302 if (!enc_utf8 && enc_codepage > 0)
1303 {
1304 WCHAR *ret = NULL;
1305 int length = 0;
1306
Bram Moolenaar4ad3b2b2017-08-30 15:57:33 +02001307 MultiByteToWideChar_alloc(enc_codepage, 0, (char *)s,
1308 (int)STRLEN(s), &ret, &length);
Bram Moolenaar285f2432017-08-23 23:10:21 +02001309 if (ret != NULL)
1310 {
1311 WideCharToMultiByte_alloc(CP_UTF8, 0,
1312 ret, length, (char **)&s, &length, 0, 0);
1313 vim_free(ret);
1314 }
1315 }
1316#endif
Bram Moolenaarc9456ce2017-07-30 21:46:04 +02001317 channel_send(curbuf->b_term->tl_job->jv_channel, PART_IN,
Bram Moolenaar4ad3b2b2017-08-30 15:57:33 +02001318 s, (int)STRLEN(s), NULL);
Bram Moolenaar285f2432017-08-23 23:10:21 +02001319#ifdef WIN3264
1320 if (tmp != s)
1321 vim_free(s);
1322#endif
1323
Bram Moolenaarc9456ce2017-07-30 21:46:04 +02001324 if (item->li_next != NULL || type == MLINE)
1325 channel_send(curbuf->b_term->tl_job->jv_channel, PART_IN,
1326 (char_u *)"\r", 1, NULL);
1327 }
1328 list_free(l);
1329 }
1330}
1331
Bram Moolenaar3d9bdfe2017-08-12 22:55:58 +02001332#if defined(FEAT_GUI) || defined(PROTO)
1333/*
1334 * Return TRUE when the cursor of the terminal should be displayed.
1335 */
1336 int
1337use_terminal_cursor()
1338{
1339 return in_terminal_loop != NULL;
1340}
1341
1342 cursorentry_T *
1343term_get_cursor_shape(guicolor_T *fg, guicolor_T *bg)
1344{
1345 term_T *term = in_terminal_loop;
1346 static cursorentry_T entry;
1347
1348 vim_memset(&entry, 0, sizeof(entry));
1349 entry.shape = entry.mshape =
1350 term->tl_cursor_shape == VTERM_PROP_CURSORSHAPE_UNDERLINE ? SHAPE_HOR :
1351 term->tl_cursor_shape == VTERM_PROP_CURSORSHAPE_BAR_LEFT ? SHAPE_VER :
1352 SHAPE_BLOCK;
1353 entry.percentage = 20;
1354 if (term->tl_cursor_blink)
1355 {
1356 entry.blinkwait = 700;
1357 entry.blinkon = 400;
Bram Moolenaar58302322017-08-22 20:33:53 +02001358 entry.blinkoff = 250;
Bram Moolenaar3d9bdfe2017-08-12 22:55:58 +02001359 }
1360 *fg = gui.back_pixel;
1361 if (term->tl_cursor_color == NULL)
1362 *bg = gui.norm_pixel;
1363 else
1364 *bg = color_name2handle(term->tl_cursor_color);
1365 entry.name = "n";
1366 entry.used_for = SHAPE_CURSOR;
1367
1368 return &entry;
1369}
1370#endif
1371
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001372static int did_change_cursor = FALSE;
1373
1374 static void
1375may_set_cursor_props(term_T *term)
1376{
Bram Moolenaar3d9bdfe2017-08-12 22:55:58 +02001377#ifdef FEAT_GUI
1378 /* For the GUI the cursor properties are obtained with
1379 * term_get_cursor_shape(). */
1380 if (gui.in_use)
1381 return;
1382#endif
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001383 if (in_terminal_loop == term)
1384 {
Bram Moolenaar893029a2017-08-12 21:15:34 +02001385 did_change_cursor = TRUE;
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001386 if (term->tl_cursor_color != NULL)
1387 term_cursor_color(term->tl_cursor_color);
1388 else
1389 term_cursor_color((char_u *)"");
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001390 term_cursor_shape(term->tl_cursor_shape, term->tl_cursor_blink);
1391 }
1392}
1393
1394 static void
1395may_restore_cursor_props(void)
1396{
Bram Moolenaar3d9bdfe2017-08-12 22:55:58 +02001397#ifdef FEAT_GUI
1398 if (gui.in_use)
1399 return;
1400#endif
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001401 if (did_change_cursor)
1402 {
1403 did_change_cursor = FALSE;
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001404 term_cursor_color((char_u *)"");
Bram Moolenaar3eee06e2017-08-19 19:40:50 +02001405 /* this will restore the initial cursor style, if possible */
1406 ui_cursor_shape_forced(TRUE);
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001407 }
1408}
1409
Bram Moolenaarc9456ce2017-07-30 21:46:04 +02001410/*
Bram Moolenaar423802d2017-07-30 16:52:24 +02001411 * Returns TRUE if the current window contains a terminal and we are sending
1412 * keys to the job.
1413 */
1414 int
Bram Moolenaar6d819742017-08-06 14:57:49 +02001415term_use_loop(void)
Bram Moolenaar423802d2017-07-30 16:52:24 +02001416{
1417 term_T *term = curbuf->b_term;
1418
1419 return term != NULL
Bram Moolenaar6d819742017-08-06 14:57:49 +02001420 && !term->tl_normal_mode
Bram Moolenaar423802d2017-07-30 16:52:24 +02001421 && term->tl_vterm != NULL
1422 && term_job_running(term);
1423}
1424
1425/*
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001426 * Wait for input and send it to the job.
1427 * Return when the start of a CTRL-W command is typed or anything else that
1428 * should be handled as a Normal mode command.
Bram Moolenaard85f2712017-07-28 21:51:57 +02001429 * Returns OK if a typed character is to be handled in Normal mode, FAIL if
1430 * the terminal was closed.
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001431 */
Bram Moolenaard85f2712017-07-28 21:51:57 +02001432 int
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001433terminal_loop(void)
1434{
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001435 int c;
Bram Moolenaardbe948d2017-07-23 22:50:51 +02001436 int termkey = 0;
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001437 int ret;
1438
Bram Moolenaar679653e2017-08-13 14:13:19 +02001439 /* Remember the terminal we are sending keys to. However, the terminal
1440 * might be closed while waiting for a character, e.g. typing "exit" in a
1441 * shell and ++close was used. Therefore use curbuf->b_term instead of a
1442 * stored reference. */
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001443 in_terminal_loop = curbuf->b_term;
Bram Moolenaardbe948d2017-07-23 22:50:51 +02001444
1445 if (*curwin->w_p_tk != NUL)
1446 termkey = string_to_key(curwin->w_p_tk, TRUE);
Bram Moolenaar0e23e9c2017-07-30 18:47:19 +02001447 position_cursor(curwin, &curbuf->b_term->tl_cursor_pos);
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001448 may_set_cursor_props(curbuf->b_term);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001449
Bram Moolenaar4f44b882017-08-13 20:06:18 +02001450#ifdef UNIX
1451 {
Bram Moolenaarb2412082017-08-20 18:09:14 +02001452 int part = get_tty_part(curbuf->b_term);
1453 int fd = curbuf->b_term->tl_job->jv_channel->ch_part[part].ch_fd;
Bram Moolenaar4f44b882017-08-13 20:06:18 +02001454
1455 if (isatty(fd))
1456 {
1457 ttyinfo_T info;
1458
1459 /* Get the current backspace and enter characters of the pty. */
1460 if (get_tty_info(fd, &info) == OK)
1461 {
1462 term_backspace_char = info.backspace;
1463 term_enter_char = info.enter;
1464 term_nl_does_cr = info.nl_does_cr;
1465 }
1466 }
1467 }
1468#endif
1469
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001470 for (;;)
1471 {
1472 /* TODO: skip screen update when handling a sequence of keys. */
Bram Moolenaar43c007f2017-07-30 17:45:37 +02001473 /* Repeat redrawing in case a message is received while redrawing. */
1474 while (curwin->w_redr_type != 0)
1475 update_screen(0);
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001476 update_cursor(curbuf->b_term, FALSE);
Bram Moolenaar423802d2017-07-30 16:52:24 +02001477
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001478 c = term_vgetc();
Bram Moolenaar6d819742017-08-06 14:57:49 +02001479 if (!term_use_loop())
Bram Moolenaard85f2712017-07-28 21:51:57 +02001480 /* job finished while waiting for a character */
1481 break;
Bram Moolenaar3d9bdfe2017-08-12 22:55:58 +02001482 if (c == K_IGNORE)
1483 continue;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001484
Bram Moolenaarfae42832017-08-01 22:24:26 +02001485#ifdef WIN3264
Bram Moolenaar589b1102017-08-12 16:39:05 +02001486 /* On Windows winpty handles CTRL-C, don't send a CTRL_C_EVENT.
Bram Moolenaar9698ad72017-08-12 14:52:15 +02001487 * Use CTRL-BREAK to kill the job. */
Bram Moolenaar9698ad72017-08-12 14:52:15 +02001488 if (ctrl_break_was_pressed)
1489 mch_signal_job(curbuf->b_term->tl_job, (char_u *)"kill");
Bram Moolenaarfae42832017-08-01 22:24:26 +02001490#endif
1491
Bram Moolenaar69198192017-08-05 14:10:48 +02001492 if (c == (termkey == 0 ? Ctrl_W : termkey) || c == Ctrl_BSL)
Bram Moolenaardbe948d2017-07-23 22:50:51 +02001493 {
Bram Moolenaarc9456ce2017-07-30 21:46:04 +02001494 int prev_c = c;
1495
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001496#ifdef FEAT_CMDL_INFO
1497 if (add_to_showcmd(c))
1498 out_flush();
1499#endif
1500 c = term_vgetc();
1501#ifdef FEAT_CMDL_INFO
1502 clear_showcmd();
1503#endif
Bram Moolenaar6d819742017-08-06 14:57:49 +02001504 if (!term_use_loop())
Bram Moolenaard85f2712017-07-28 21:51:57 +02001505 /* job finished while waiting for a character */
1506 break;
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001507
Bram Moolenaar69198192017-08-05 14:10:48 +02001508 if (prev_c == Ctrl_BSL)
1509 {
1510 if (c == Ctrl_N)
Bram Moolenaaraaa8a352017-08-05 20:17:00 +02001511 {
Bram Moolenaar6d819742017-08-06 14:57:49 +02001512 /* CTRL-\ CTRL-N : go to Terminal-Normal mode. */
1513 term_enter_normal_mode();
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001514 ret = FAIL;
1515 goto theend;
Bram Moolenaaraaa8a352017-08-05 20:17:00 +02001516 }
Bram Moolenaar69198192017-08-05 14:10:48 +02001517 /* Send both keys to the terminal. */
1518 send_keys_to_term(curbuf->b_term, prev_c, TRUE);
1519 }
Bram Moolenaar8e539c52017-08-18 22:57:06 +02001520 else if (c == Ctrl_C)
1521 {
1522 /* "CTRL-W CTRL-C" or 'termkey' CTRL-C: end the job */
1523 mch_signal_job(curbuf->b_term->tl_job, (char_u *)"kill");
1524 }
Bram Moolenaar69198192017-08-05 14:10:48 +02001525 else if (termkey == 0 && c == '.')
Bram Moolenaar423802d2017-07-30 16:52:24 +02001526 {
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001527 /* "CTRL-W .": send CTRL-W to the job */
1528 c = Ctrl_W;
Bram Moolenaar423802d2017-07-30 16:52:24 +02001529 }
Bram Moolenaarc9456ce2017-07-30 21:46:04 +02001530 else if (c == 'N')
Bram Moolenaar423802d2017-07-30 16:52:24 +02001531 {
Bram Moolenaar6d819742017-08-06 14:57:49 +02001532 /* CTRL-W N : go to Terminal-Normal mode. */
1533 term_enter_normal_mode();
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001534 ret = FAIL;
1535 goto theend;
Bram Moolenaar423802d2017-07-30 16:52:24 +02001536 }
Bram Moolenaarc9456ce2017-07-30 21:46:04 +02001537 else if (c == '"')
1538 {
1539 term_paste_register(prev_c);
1540 continue;
1541 }
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001542 else if (termkey == 0 || c != termkey)
1543 {
1544 stuffcharReadbuff(Ctrl_W);
1545 stuffcharReadbuff(c);
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001546 ret = OK;
1547 goto theend;
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001548 }
Bram Moolenaardbe948d2017-07-23 22:50:51 +02001549 }
Bram Moolenaar58302322017-08-22 20:33:53 +02001550# ifdef WIN3264
Bram Moolenaar740c4332017-08-21 22:01:27 +02001551 if (!enc_utf8 && has_mbyte && c >= 0x80)
1552 {
1553 WCHAR wc;
1554 char_u mb[3];
1555
1556 mb[0] = (unsigned)c >> 8;
1557 mb[1] = c;
1558 if (MultiByteToWideChar(GetACP(), 0, (char*)mb, 2, &wc, 1) > 0)
1559 c = wc;
1560 }
1561# endif
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001562 if (send_keys_to_term(curbuf->b_term, c, TRUE) != OK)
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001563 {
1564 ret = OK;
1565 goto theend;
1566 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001567 }
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001568 ret = FAIL;
1569
1570theend:
1571 in_terminal_loop = NULL;
1572 may_restore_cursor_props();
1573 return ret;
Bram Moolenaar1f2903c2017-07-23 19:51:01 +02001574}
1575
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001576/*
1577 * Called when a job has finished.
Bram Moolenaar8cad9302017-08-12 14:32:32 +02001578 * This updates the title and status, but does not close the vterm, because
Bram Moolenaar423802d2017-07-30 16:52:24 +02001579 * there might still be pending output in the channel.
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001580 */
1581 void
1582term_job_ended(job_T *job)
1583{
1584 term_T *term;
1585 int did_one = FALSE;
1586
1587 for (term = first_term; term != NULL; term = term->tl_next)
1588 if (term->tl_job == job)
1589 {
1590 vim_free(term->tl_title);
1591 term->tl_title = NULL;
1592 vim_free(term->tl_status_text);
1593 term->tl_status_text = NULL;
1594 redraw_buf_and_status_later(term->tl_buffer, VALID);
1595 did_one = TRUE;
1596 }
1597 if (did_one)
1598 redraw_statuslines();
1599 if (curbuf->b_term != NULL)
1600 {
1601 if (curbuf->b_term->tl_job == job)
1602 maketitle();
1603 update_cursor(curbuf->b_term, TRUE);
1604 }
1605}
1606
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001607 static void
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001608may_toggle_cursor(term_T *term)
1609{
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001610 if (in_terminal_loop == term)
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001611 {
1612 if (term->tl_cursor_visible)
1613 cursor_on();
1614 else
1615 cursor_off();
1616 }
1617}
1618
Bram Moolenaarcfce7172017-08-17 20:31:48 +02001619/*
1620 * Reverse engineer the RGB value into a cterm color index.
1621 * First color is 1. Return 0 if no match found.
1622 */
1623 static int
1624color2index(VTermColor *color, int fg, int *boldp)
1625{
1626 int red = color->red;
1627 int blue = color->blue;
1628 int green = color->green;
1629
1630 /* The argument for lookup_color() is for the color_names[] table. */
1631 if (red == 0)
1632 {
1633 if (green == 0)
1634 {
1635 if (blue == 0)
1636 return lookup_color(0, fg, boldp) + 1; /* black */
1637 if (blue == 224)
1638 return lookup_color(1, fg, boldp) + 1; /* dark blue */
1639 }
1640 else if (green == 224)
1641 {
1642 if (blue == 0)
1643 return lookup_color(2, fg, boldp) + 1; /* dark green */
1644 if (blue == 224)
1645 return lookup_color(3, fg, boldp) + 1; /* dark cyan */
1646 }
1647 }
1648 else if (red == 224)
1649 {
1650 if (green == 0)
1651 {
1652 if (blue == 0)
1653 return lookup_color(4, fg, boldp) + 1; /* dark red */
1654 if (blue == 224)
1655 return lookup_color(5, fg, boldp) + 1; /* dark magenta */
1656 }
1657 else if (green == 224)
1658 {
1659 if (blue == 0)
1660 return lookup_color(6, fg, boldp) + 1; /* dark yellow / brown */
1661 if (blue == 224)
1662 return lookup_color(8, fg, boldp) + 1; /* white / light grey */
1663 }
1664 }
1665 else if (red == 128)
1666 {
1667 if (green == 128 && blue == 128)
1668 return lookup_color(12, fg, boldp) + 1; /* high intensity black / dark grey */
1669 }
1670 else if (red == 255)
1671 {
1672 if (green == 64)
1673 {
1674 if (blue == 64)
1675 return lookup_color(20, fg, boldp) + 1; /* light red */
1676 if (blue == 255)
1677 return lookup_color(22, fg, boldp) + 1; /* light magenta */
1678 }
1679 else if (green == 255)
1680 {
1681 if (blue == 64)
1682 return lookup_color(24, fg, boldp) + 1; /* yellow */
1683 if (blue == 255)
1684 return lookup_color(26, fg, boldp) + 1; /* white */
1685 }
1686 }
1687 else if (red == 64)
1688 {
1689 if (green == 64)
1690 {
1691 if (blue == 255)
1692 return lookup_color(14, fg, boldp) + 1; /* light blue */
1693 }
1694 else if (green == 255)
1695 {
1696 if (blue == 64)
1697 return lookup_color(16, fg, boldp) + 1; /* light green */
1698 if (blue == 255)
1699 return lookup_color(18, fg, boldp) + 1; /* light cyan */
1700 }
1701 }
1702 if (t_colors >= 256)
1703 {
1704 if (red == blue && red == green)
1705 {
1706 /* 24-color greyscale */
1707 static int cutoff[23] = {
1708 0x05, 0x10, 0x1B, 0x26, 0x31, 0x3C, 0x47, 0x52,
1709 0x5D, 0x68, 0x73, 0x7F, 0x8A, 0x95, 0xA0, 0xAB,
1710 0xB6, 0xC1, 0xCC, 0xD7, 0xE2, 0xED, 0xF9};
1711 int i;
1712
1713 for (i = 0; i < 23; ++i)
1714 if (red < cutoff[i])
1715 return i + 233;
1716 return 256;
1717 }
1718
1719 /* 216-color cube */
1720 return 17 + ((red + 25) / 0x33) * 36
Bram Moolenaar740c4332017-08-21 22:01:27 +02001721 + ((green + 25) / 0x33) * 6
Bram Moolenaarcfce7172017-08-17 20:31:48 +02001722 + (blue + 25) / 0x33;
1723 }
1724 return 0;
1725}
1726
1727/*
1728 * Convert the attributes of a vterm cell into an attribute index.
1729 */
1730 static int
1731cell2attr(VTermScreenCellAttrs cellattrs, VTermColor cellfg, VTermColor cellbg)
1732{
1733 int attr = 0;
1734
1735 if (cellattrs.bold)
1736 attr |= HL_BOLD;
1737 if (cellattrs.underline)
1738 attr |= HL_UNDERLINE;
1739 if (cellattrs.italic)
1740 attr |= HL_ITALIC;
1741 if (cellattrs.strike)
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +02001742 attr |= HL_STRIKETHROUGH;
Bram Moolenaarcfce7172017-08-17 20:31:48 +02001743 if (cellattrs.reverse)
1744 attr |= HL_INVERSE;
1745
1746#ifdef FEAT_GUI
1747 if (gui.in_use)
1748 {
1749 guicolor_T fg, bg;
1750
1751 fg = gui_mch_get_rgb_color(cellfg.red, cellfg.green, cellfg.blue);
1752 bg = gui_mch_get_rgb_color(cellbg.red, cellbg.green, cellbg.blue);
1753 return get_gui_attr_idx(attr, fg, bg);
1754 }
1755 else
1756#endif
1757#ifdef FEAT_TERMGUICOLORS
1758 if (p_tgc)
1759 {
1760 guicolor_T fg, bg;
1761
1762 fg = gui_get_rgb_color_cmn(cellfg.red, cellfg.green, cellfg.blue);
1763 bg = gui_get_rgb_color_cmn(cellbg.red, cellbg.green, cellbg.blue);
1764
1765 return get_tgc_attr_idx(attr, fg, bg);
1766 }
1767 else
1768#endif
1769 {
1770 int bold = MAYBE;
1771 int fg = color2index(&cellfg, TRUE, &bold);
1772 int bg = color2index(&cellbg, FALSE, &bold);
1773
1774 /* with 8 colors set the bold attribute to get a bright foreground */
1775 if (bold == TRUE)
1776 attr |= HL_BOLD;
1777 return get_cterm_attr_idx(attr, fg, bg);
1778 }
1779 return 0;
1780}
1781
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001782 static int
1783handle_damage(VTermRect rect, void *user)
1784{
1785 term_T *term = (term_T *)user;
1786
1787 term->tl_dirty_row_start = MIN(term->tl_dirty_row_start, rect.start_row);
1788 term->tl_dirty_row_end = MAX(term->tl_dirty_row_end, rect.end_row);
1789 redraw_buf_later(term->tl_buffer, NOT_VALID);
1790 return 1;
1791}
1792
1793 static int
Bram Moolenaar6bb18a82017-08-13 22:14:17 +02001794handle_moverect(VTermRect dest, VTermRect src, void *user)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001795{
1796 term_T *term = (term_T *)user;
1797
Bram Moolenaarcfce7172017-08-17 20:31:48 +02001798 /* Scrolling up is done much more efficiently by deleting lines instead of
1799 * redrawing the text. */
Bram Moolenaar6bb18a82017-08-13 22:14:17 +02001800 if (dest.start_col == src.start_col
1801 && dest.end_col == src.end_col
1802 && dest.start_row < src.start_row)
Bram Moolenaarcfce7172017-08-17 20:31:48 +02001803 {
1804 win_T *wp;
1805 VTermColor fg, bg;
1806 VTermScreenCellAttrs attr;
1807 int clear_attr;
1808
1809 /* Set the color to clear lines with. */
1810 vterm_state_get_default_colors(vterm_obtain_state(term->tl_vterm),
1811 &fg, &bg);
1812 vim_memset(&attr, 0, sizeof(attr));
1813 clear_attr = cell2attr(attr, fg, bg);
1814
Bram Moolenaar6bb18a82017-08-13 22:14:17 +02001815 FOR_ALL_WINDOWS(wp)
1816 {
1817 if (wp->w_buffer == term->tl_buffer)
Bram Moolenaar6bb18a82017-08-13 22:14:17 +02001818 win_del_lines(wp, dest.start_row,
Bram Moolenaarcfce7172017-08-17 20:31:48 +02001819 src.start_row - dest.start_row, FALSE, FALSE,
1820 clear_attr);
Bram Moolenaar6bb18a82017-08-13 22:14:17 +02001821 }
Bram Moolenaarcfce7172017-08-17 20:31:48 +02001822 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001823 redraw_buf_later(term->tl_buffer, NOT_VALID);
1824 return 1;
1825}
1826
1827 static int
1828handle_movecursor(
1829 VTermPos pos,
1830 VTermPos oldpos UNUSED,
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001831 int visible,
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001832 void *user)
1833{
1834 term_T *term = (term_T *)user;
1835 win_T *wp;
Bram Moolenaar22aad2f2017-07-30 18:19:46 +02001836
1837 term->tl_cursor_pos = pos;
1838 term->tl_cursor_visible = visible;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001839
1840 FOR_ALL_WINDOWS(wp)
1841 {
1842 if (wp->w_buffer == term->tl_buffer)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001843 position_cursor(wp, &pos);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001844 }
Bram Moolenaar6d819742017-08-06 14:57:49 +02001845 if (term->tl_buffer == curbuf && !term->tl_normal_mode)
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001846 {
1847 may_toggle_cursor(term);
Bram Moolenaar12d93ee2017-07-30 19:02:02 +02001848 update_cursor(term, term->tl_cursor_visible);
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001849 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001850
1851 return 1;
1852}
1853
Bram Moolenaar21554412017-07-24 21:44:43 +02001854 static int
1855handle_settermprop(
1856 VTermProp prop,
1857 VTermValue *value,
1858 void *user)
1859{
1860 term_T *term = (term_T *)user;
1861
1862 switch (prop)
1863 {
1864 case VTERM_PROP_TITLE:
1865 vim_free(term->tl_title);
Bram Moolenaar274a52f2017-08-13 16:09:31 +02001866 /* a blank title isn't useful, make it empty, so that "running" is
1867 * displayed */
1868 if (*skipwhite((char_u *)value->string) == NUL)
1869 term->tl_title = NULL;
Bram Moolenaar33d66bd2017-08-23 23:51:58 +02001870#ifdef WIN3264
1871 else if (!enc_utf8 && enc_codepage > 0)
1872 {
1873 WCHAR *ret = NULL;
1874 int length = 0;
1875
1876 MultiByteToWideChar_alloc(CP_UTF8, 0,
Bram Moolenaar4ad3b2b2017-08-30 15:57:33 +02001877 (char*)value->string, (int)STRLEN(value->string),
Bram Moolenaar33d66bd2017-08-23 23:51:58 +02001878 &ret, &length);
1879 if (ret != NULL)
1880 {
1881 WideCharToMultiByte_alloc(enc_codepage, 0,
1882 ret, length, (char**)&term->tl_title,
1883 &length, 0, 0);
1884 vim_free(ret);
1885 }
1886 }
1887#endif
Bram Moolenaar274a52f2017-08-13 16:09:31 +02001888 else
1889 term->tl_title = vim_strsave((char_u *)value->string);
Bram Moolenaar21554412017-07-24 21:44:43 +02001890 vim_free(term->tl_status_text);
1891 term->tl_status_text = NULL;
1892 if (term == curbuf->b_term)
1893 maketitle();
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001894 break;
1895
1896 case VTERM_PROP_CURSORVISIBLE:
1897 term->tl_cursor_visible = value->boolean;
1898 may_toggle_cursor(term);
1899 out_flush();
1900 break;
1901
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001902 case VTERM_PROP_CURSORBLINK:
1903 term->tl_cursor_blink = value->boolean;
1904 may_set_cursor_props(term);
1905 break;
1906
1907 case VTERM_PROP_CURSORSHAPE:
1908 term->tl_cursor_shape = value->number;
1909 may_set_cursor_props(term);
1910 break;
1911
1912 case VTERM_PROP_CURSORCOLOR:
1913 vim_free(term->tl_cursor_color);
Bram Moolenaar3d9bdfe2017-08-12 22:55:58 +02001914 if (*value->string == NUL)
1915 term->tl_cursor_color = NULL;
1916 else
1917 term->tl_cursor_color = vim_strsave((char_u *)value->string);
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02001918 may_set_cursor_props(term);
1919 break;
1920
Bram Moolenaare41e3b42017-08-11 16:24:50 +02001921 case VTERM_PROP_ALTSCREEN:
1922 /* TODO: do anything else? */
1923 term->tl_using_altscreen = value->boolean;
1924 break;
1925
Bram Moolenaar21554412017-07-24 21:44:43 +02001926 default:
1927 break;
1928 }
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001929 /* Always return 1, otherwise vterm doesn't store the value internally. */
1930 return 1;
Bram Moolenaar21554412017-07-24 21:44:43 +02001931}
1932
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001933/*
1934 * The job running in the terminal resized the terminal.
1935 */
1936 static int
1937handle_resize(int rows, int cols, void *user)
1938{
1939 term_T *term = (term_T *)user;
1940 win_T *wp;
1941
1942 term->tl_rows = rows;
1943 term->tl_cols = cols;
1944 FOR_ALL_WINDOWS(wp)
1945 {
1946 if (wp->w_buffer == term->tl_buffer)
1947 {
1948 win_setheight_win(rows, wp);
1949 win_setwidth_win(cols, wp);
1950 }
1951 }
1952
1953 redraw_buf_later(term->tl_buffer, NOT_VALID);
1954 return 1;
1955}
1956
Bram Moolenaard85f2712017-07-28 21:51:57 +02001957/*
1958 * Handle a line that is pushed off the top of the screen.
1959 */
1960 static int
1961handle_pushline(int cols, const VTermScreenCell *cells, void *user)
1962{
1963 term_T *term = (term_T *)user;
1964
1965 /* TODO: Limit the number of lines that are stored. */
Bram Moolenaard85f2712017-07-28 21:51:57 +02001966 if (ga_grow(&term->tl_scrollback, 1) == OK)
1967 {
Bram Moolenaar33a43be2017-08-06 21:36:22 +02001968 cellattr_T *p = NULL;
Bram Moolenaar696d00f2017-07-29 14:52:43 +02001969 int len = 0;
1970 int i;
Bram Moolenaar33a43be2017-08-06 21:36:22 +02001971 int c;
1972 int col;
Bram Moolenaar696d00f2017-07-29 14:52:43 +02001973 sb_line_T *line;
Bram Moolenaar33a43be2017-08-06 21:36:22 +02001974 garray_T ga;
Bram Moolenaard85f2712017-07-28 21:51:57 +02001975
1976 /* do not store empty cells at the end */
1977 for (i = 0; i < cols; ++i)
1978 if (cells[i].chars[0] != 0)
1979 len = i + 1;
1980
Bram Moolenaar7fadbf82017-08-07 22:08:05 +02001981 ga_init2(&ga, 1, 100);
Bram Moolenaar696d00f2017-07-29 14:52:43 +02001982 if (len > 0)
Bram Moolenaar33a43be2017-08-06 21:36:22 +02001983 p = (cellattr_T *)alloc((int)sizeof(cellattr_T) * len);
Bram Moolenaard85f2712017-07-28 21:51:57 +02001984 if (p != NULL)
Bram Moolenaar33a43be2017-08-06 21:36:22 +02001985 {
Bram Moolenaar33a43be2017-08-06 21:36:22 +02001986 for (col = 0; col < len; col += cells[col].width)
1987 {
1988 if (ga_grow(&ga, MB_MAXBYTES) == FAIL)
1989 {
1990 ga.ga_len = 0;
1991 break;
1992 }
1993 for (i = 0; (c = cells[col].chars[i]) > 0 || i == 0; ++i)
Bram Moolenaar6c4d12c2017-08-23 23:36:25 +02001994 ga.ga_len += utf_char2bytes(c == NUL ? ' ' : c,
Bram Moolenaar33a43be2017-08-06 21:36:22 +02001995 (char_u *)ga.ga_data + ga.ga_len);
1996 p[col].width = cells[col].width;
1997 p[col].attrs = cells[col].attrs;
1998 p[col].fg = cells[col].fg;
1999 p[col].bg = cells[col].bg;
2000 }
2001 }
2002 if (ga_grow(&ga, 1) == FAIL)
2003 add_scrollback_line_to_buffer(term, (char_u *)"", 0);
2004 else
2005 {
2006 *((char_u *)ga.ga_data + ga.ga_len) = NUL;
2007 add_scrollback_line_to_buffer(term, ga.ga_data, ga.ga_len);
2008 }
2009 ga_clear(&ga);
Bram Moolenaar696d00f2017-07-29 14:52:43 +02002010
2011 line = (sb_line_T *)term->tl_scrollback.ga_data
2012 + term->tl_scrollback.ga_len;
2013 line->sb_cols = len;
2014 line->sb_cells = p;
2015 ++term->tl_scrollback.ga_len;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002016 ++term->tl_scrollback_scrolled;
Bram Moolenaard85f2712017-07-28 21:51:57 +02002017 }
2018 return 0; /* ignored */
2019}
2020
Bram Moolenaar21554412017-07-24 21:44:43 +02002021static VTermScreenCallbacks screen_callbacks = {
2022 handle_damage, /* damage */
2023 handle_moverect, /* moverect */
2024 handle_movecursor, /* movecursor */
2025 handle_settermprop, /* settermprop */
2026 NULL, /* bell */
2027 handle_resize, /* resize */
Bram Moolenaard85f2712017-07-28 21:51:57 +02002028 handle_pushline, /* sb_pushline */
Bram Moolenaar21554412017-07-24 21:44:43 +02002029 NULL /* sb_popline */
2030};
2031
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002032/*
Bram Moolenaard85f2712017-07-28 21:51:57 +02002033 * Called when a channel has been closed.
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002034 * If this was a channel for a terminal window then finish it up.
Bram Moolenaard85f2712017-07-28 21:51:57 +02002035 */
2036 void
2037term_channel_closed(channel_T *ch)
2038{
2039 term_T *term;
2040 int did_one = FALSE;
2041
2042 for (term = first_term; term != NULL; term = term->tl_next)
2043 if (term->tl_job == ch->ch_job)
2044 {
Bram Moolenaar423802d2017-07-30 16:52:24 +02002045 term->tl_channel_closed = TRUE;
Bram Moolenaardd693ce2017-08-10 23:15:19 +02002046 did_one = TRUE;
Bram Moolenaar423802d2017-07-30 16:52:24 +02002047
Bram Moolenaard85f2712017-07-28 21:51:57 +02002048 vim_free(term->tl_title);
2049 term->tl_title = NULL;
2050 vim_free(term->tl_status_text);
2051 term->tl_status_text = NULL;
2052
Bram Moolenaar423802d2017-07-30 16:52:24 +02002053 /* Unless in Terminal-Normal mode: clear the vterm. */
Bram Moolenaar6d819742017-08-06 14:57:49 +02002054 if (!term->tl_normal_mode)
Bram Moolenaardd693ce2017-08-10 23:15:19 +02002055 {
2056 int fnum = term->tl_buffer->b_fnum;
2057
Bram Moolenaar423802d2017-07-30 16:52:24 +02002058 cleanup_vterm(term);
Bram Moolenaard85f2712017-07-28 21:51:57 +02002059
Bram Moolenaardd693ce2017-08-10 23:15:19 +02002060 if (term->tl_finish == 'c')
2061 {
2062 /* ++close or term_finish == "close" */
Bram Moolenaar8cad9302017-08-12 14:32:32 +02002063 ch_log(NULL, "terminal job finished, closing window");
Bram Moolenaardd693ce2017-08-10 23:15:19 +02002064 curbuf = term->tl_buffer;
2065 do_bufdel(DOBUF_WIPE, (char_u *)"", 1, fnum, fnum, FALSE);
2066 break;
2067 }
2068 if (term->tl_finish == 'o' && term->tl_buffer->b_nwindows == 0)
2069 {
2070 char buf[50];
2071
2072 /* TODO: use term_opencmd */
Bram Moolenaar8cad9302017-08-12 14:32:32 +02002073 ch_log(NULL, "terminal job finished, opening window");
Bram Moolenaar37c45832017-08-12 16:01:04 +02002074 vim_snprintf(buf, sizeof(buf),
2075 term->tl_opencmd == NULL
Bram Moolenaar589b1102017-08-12 16:39:05 +02002076 ? "botright sbuf %d"
2077 : (char *)term->tl_opencmd, fnum);
Bram Moolenaardd693ce2017-08-10 23:15:19 +02002078 do_cmdline_cmd((char_u *)buf);
2079 }
Bram Moolenaar8cad9302017-08-12 14:32:32 +02002080 else
2081 ch_log(NULL, "terminal job finished");
Bram Moolenaardd693ce2017-08-10 23:15:19 +02002082 }
2083
Bram Moolenaard85f2712017-07-28 21:51:57 +02002084 redraw_buf_and_status_later(term->tl_buffer, NOT_VALID);
Bram Moolenaard85f2712017-07-28 21:51:57 +02002085 }
2086 if (did_one)
2087 {
2088 redraw_statuslines();
2089
2090 /* Need to break out of vgetc(). */
2091 ins_char_typebuf(K_IGNORE);
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002092 typebuf_was_filled = TRUE;
Bram Moolenaard85f2712017-07-28 21:51:57 +02002093
Bram Moolenaar12d93ee2017-07-30 19:02:02 +02002094 term = curbuf->b_term;
2095 if (term != NULL)
Bram Moolenaard85f2712017-07-28 21:51:57 +02002096 {
Bram Moolenaar12d93ee2017-07-30 19:02:02 +02002097 if (term->tl_job == ch->ch_job)
Bram Moolenaard85f2712017-07-28 21:51:57 +02002098 maketitle();
Bram Moolenaar12d93ee2017-07-30 19:02:02 +02002099 update_cursor(term, term->tl_cursor_visible);
Bram Moolenaard85f2712017-07-28 21:51:57 +02002100 }
2101 }
2102}
2103
2104/*
Bram Moolenaar6d819742017-08-06 14:57:49 +02002105 * Called to update a window that contains an active terminal.
2106 * Returns FAIL when there is no terminal running in this window or in
2107 * Terminal-Normal mode.
Bram Moolenaare4f25e42017-07-07 11:54:15 +02002108 */
Bram Moolenaard85f2712017-07-28 21:51:57 +02002109 int
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002110term_update_window(win_T *wp)
Bram Moolenaar938783d2017-07-16 20:13:26 +02002111{
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002112 term_T *term = wp->w_buffer->b_term;
Bram Moolenaard85f2712017-07-28 21:51:57 +02002113 VTerm *vterm;
2114 VTermScreen *screen;
2115 VTermState *state;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02002116 VTermPos pos;
Bram Moolenaar938783d2017-07-16 20:13:26 +02002117
Bram Moolenaar6d819742017-08-06 14:57:49 +02002118 if (term == NULL || term->tl_vterm == NULL || term->tl_normal_mode)
Bram Moolenaard85f2712017-07-28 21:51:57 +02002119 return FAIL;
Bram Moolenaar423802d2017-07-30 16:52:24 +02002120
Bram Moolenaard85f2712017-07-28 21:51:57 +02002121 vterm = term->tl_vterm;
2122 screen = vterm_obtain_screen(vterm);
2123 state = vterm_obtain_state(vterm);
2124
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002125 /*
2126 * If the window was resized a redraw will be triggered and we get here.
2127 * Adjust the size of the vterm unless 'termsize' specifies a fixed size.
2128 */
2129 if ((!term->tl_rows_fixed && term->tl_rows != wp->w_height)
2130 || (!term->tl_cols_fixed && term->tl_cols != wp->w_width))
Bram Moolenaarb13501f2017-07-22 22:32:56 +02002131 {
Bram Moolenaar96ad8c92017-07-28 14:17:34 +02002132 int rows = term->tl_rows_fixed ? term->tl_rows : wp->w_height;
2133 int cols = term->tl_cols_fixed ? term->tl_cols : wp->w_width;
2134 win_T *twp;
2135
2136 FOR_ALL_WINDOWS(twp)
2137 {
2138 /* When more than one window shows the same terminal, use the
2139 * smallest size. */
2140 if (twp->w_buffer == term->tl_buffer)
2141 {
2142 if (!term->tl_rows_fixed && rows > twp->w_height)
2143 rows = twp->w_height;
2144 if (!term->tl_cols_fixed && cols > twp->w_width)
2145 cols = twp->w_width;
2146 }
2147 }
Bram Moolenaarb13501f2017-07-22 22:32:56 +02002148
2149 vterm_set_size(vterm, rows, cols);
Bram Moolenaar2f3a90a2017-08-03 14:49:29 +02002150 ch_log(term->tl_job->jv_channel, "Resizing terminal to %d lines",
Bram Moolenaarb13501f2017-07-22 22:32:56 +02002151 rows);
Bram Moolenaar43da3e32017-07-23 17:27:54 +02002152 term_report_winsize(term, rows, cols);
Bram Moolenaarb13501f2017-07-22 22:32:56 +02002153 }
Bram Moolenaar58556cd2017-07-20 23:04:46 +02002154
2155 /* The cursor may have been moved when resizing. */
2156 vterm_state_get_cursorpos(state, &pos);
2157 position_cursor(wp, &pos);
2158
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02002159 /* TODO: Only redraw what changed. */
2160 for (pos.row = 0; pos.row < wp->w_height; ++pos.row)
Bram Moolenaar938783d2017-07-16 20:13:26 +02002161 {
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02002162 int off = screen_get_current_line_off();
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002163 int max_col = MIN(wp->w_width, term->tl_cols);
Bram Moolenaar938783d2017-07-16 20:13:26 +02002164
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002165 if (pos.row < term->tl_rows)
2166 {
2167 for (pos.col = 0; pos.col < max_col; )
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02002168 {
2169 VTermScreenCell cell;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002170 int c;
Bram Moolenaar938783d2017-07-16 20:13:26 +02002171
Bram Moolenaareeac6772017-07-23 15:48:37 +02002172 if (vterm_screen_get_cell(screen, pos, &cell) == 0)
2173 vim_memset(&cell, 0, sizeof(cell));
2174
2175 /* TODO: composing chars */
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02002176 c = cell.chars[0];
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002177 if (c == NUL)
2178 {
2179 ScreenLines[off] = ' ';
Bram Moolenaar8a773062017-07-24 22:29:21 +02002180 if (enc_utf8)
2181 ScreenLinesUC[off] = NUL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002182 }
2183 else
2184 {
Bram Moolenaar740c4332017-08-21 22:01:27 +02002185 if (enc_utf8)
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02002186 {
Bram Moolenaar740c4332017-08-21 22:01:27 +02002187 if (c >= 0x80)
2188 {
2189 ScreenLines[off] = ' ';
2190 ScreenLinesUC[off] = c;
2191 }
2192 else
2193 {
2194 ScreenLines[off] = c;
Bram Moolenaar8a773062017-07-24 22:29:21 +02002195 ScreenLinesUC[off] = NUL;
Bram Moolenaar740c4332017-08-21 22:01:27 +02002196 }
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02002197 }
Bram Moolenaarec0e07a2017-08-22 22:21:37 +02002198#ifdef WIN3264
Bram Moolenaar740c4332017-08-21 22:01:27 +02002199 else if (has_mbyte && c >= 0x80)
2200 {
2201 char_u mb[MB_MAXBYTES+1];
2202 WCHAR wc = c;
2203
2204 if (WideCharToMultiByte(GetACP(), 0, &wc, 1,
2205 (char*)mb, 2, 0, 0) > 1)
2206 {
2207 ScreenLines[off] = mb[0];
Bram Moolenaard2c45a12017-08-22 22:29:00 +02002208 ScreenLines[off + 1] = mb[1];
Bram Moolenaar740c4332017-08-21 22:01:27 +02002209 cell.width = mb_ptr2cells(mb);
2210 }
2211 else
2212 ScreenLines[off] = c;
2213 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002214#endif
Bram Moolenaarec0e07a2017-08-22 22:21:37 +02002215 else
Bram Moolenaar740c4332017-08-21 22:01:27 +02002216 ScreenLines[off] = c;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002217 }
Bram Moolenaar33a43be2017-08-06 21:36:22 +02002218 ScreenAttrs[off] = cell2attr(cell.attrs, cell.fg, cell.bg);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002219
2220 ++pos.col;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02002221 ++off;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002222 if (cell.width == 2)
2223 {
Bram Moolenaar8a773062017-07-24 22:29:21 +02002224 if (enc_utf8)
2225 ScreenLinesUC[off] = NUL;
Bram Moolenaard2c45a12017-08-22 22:29:00 +02002226
2227 /* don't set the second byte to NUL for a DBCS encoding, it
2228 * has been set above */
2229 if (enc_utf8 || !has_mbyte)
Bram Moolenaar740c4332017-08-21 22:01:27 +02002230 ScreenLines[off] = NUL;
Bram Moolenaard2c45a12017-08-22 22:29:00 +02002231
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002232 ++pos.col;
2233 ++off;
2234 }
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02002235 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002236 }
Bram Moolenaare825d8b2017-07-19 23:20:19 +02002237 else
2238 pos.col = 0;
Bram Moolenaar938783d2017-07-16 20:13:26 +02002239
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002240 screen_line(wp->w_winrow + pos.row, wp->w_wincol,
2241 pos.col, wp->w_width, FALSE);
Bram Moolenaar938783d2017-07-16 20:13:26 +02002242 }
Bram Moolenaard85f2712017-07-28 21:51:57 +02002243
2244 return OK;
Bram Moolenaar938783d2017-07-16 20:13:26 +02002245}
Bram Moolenaare4f25e42017-07-07 11:54:15 +02002246
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002247/*
Bram Moolenaar63ecdda2017-07-28 22:29:35 +02002248 * Return TRUE if "wp" is a terminal window where the job has finished.
2249 */
2250 int
2251term_is_finished(buf_T *buf)
2252{
2253 return buf->b_term != NULL && buf->b_term->tl_vterm == NULL;
2254}
2255
2256/*
Bram Moolenaar423802d2017-07-30 16:52:24 +02002257 * Return TRUE if "wp" is a terminal window where the job has finished or we
Bram Moolenaar6d819742017-08-06 14:57:49 +02002258 * are in Terminal-Normal mode, thus we show the buffer contents.
Bram Moolenaar423802d2017-07-30 16:52:24 +02002259 */
2260 int
2261term_show_buffer(buf_T *buf)
2262{
2263 term_T *term = buf->b_term;
2264
Bram Moolenaar6d819742017-08-06 14:57:49 +02002265 return term != NULL && (term->tl_vterm == NULL || term->tl_normal_mode);
Bram Moolenaar423802d2017-07-30 16:52:24 +02002266}
2267
2268/*
Bram Moolenaar63ecdda2017-07-28 22:29:35 +02002269 * The current buffer is going to be changed. If there is terminal
2270 * highlighting remove it now.
2271 */
2272 void
2273term_change_in_curbuf(void)
2274{
2275 term_T *term = curbuf->b_term;
2276
2277 if (term_is_finished(curbuf) && term->tl_scrollback.ga_len > 0)
2278 {
2279 free_scrollback(term);
2280 redraw_buf_later(term->tl_buffer, NOT_VALID);
Bram Moolenaar20e6cd02017-08-01 20:25:22 +02002281
2282 /* The buffer is now like a normal buffer, it cannot be easily
2283 * abandoned when changed. */
2284 set_string_option_direct((char_u *)"buftype", -1,
2285 (char_u *)"", OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaar63ecdda2017-07-28 22:29:35 +02002286 }
2287}
2288
2289/*
2290 * Get the screen attribute for a position in the buffer.
2291 */
2292 int
2293term_get_attr(buf_T *buf, linenr_T lnum, int col)
2294{
Bram Moolenaar33a43be2017-08-06 21:36:22 +02002295 term_T *term = buf->b_term;
2296 sb_line_T *line;
2297 cellattr_T *cellattr;
Bram Moolenaar63ecdda2017-07-28 22:29:35 +02002298
Bram Moolenaar70229f92017-07-29 16:01:53 +02002299 if (lnum > term->tl_scrollback.ga_len)
Bram Moolenaar63ecdda2017-07-28 22:29:35 +02002300 return 0;
2301 line = (sb_line_T *)term->tl_scrollback.ga_data + lnum - 1;
2302 if (col >= line->sb_cols)
2303 return 0;
Bram Moolenaar33a43be2017-08-06 21:36:22 +02002304 cellattr = line->sb_cells + col;
2305 return cell2attr(cellattr->attrs, cellattr->fg, cellattr->bg);
Bram Moolenaar63ecdda2017-07-28 22:29:35 +02002306}
2307
2308/*
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002309 * Create a new vterm and initialize it.
2310 */
2311 static void
2312create_vterm(term_T *term, int rows, int cols)
2313{
2314 VTerm *vterm;
2315 VTermScreen *screen;
Bram Moolenaar0cbba822017-08-21 21:39:28 +02002316 VTermValue value;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002317
2318 vterm = vterm_new(rows, cols);
2319 term->tl_vterm = vterm;
2320 screen = vterm_obtain_screen(vterm);
2321 vterm_screen_set_callbacks(screen, &screen_callbacks, term);
2322 /* TODO: depends on 'encoding'. */
2323 vterm_set_utf8(vterm, 1);
Bram Moolenaareeac6772017-07-23 15:48:37 +02002324
2325 /* Vterm uses a default black background. Set it to white when
2326 * 'background' is "light". */
2327 if (*p_bg == 'l')
2328 {
2329 VTermColor fg, bg;
2330
2331 fg.red = fg.green = fg.blue = 0;
2332 bg.red = bg.green = bg.blue = 255;
2333 vterm_state_set_default_colors(vterm_obtain_state(vterm), &fg, &bg);
2334 }
2335
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002336 /* Required to initialize most things. */
2337 vterm_screen_reset(screen, 1 /* hard */);
Bram Moolenaare41e3b42017-08-11 16:24:50 +02002338
2339 /* Allow using alternate screen. */
2340 vterm_screen_enable_altscreen(screen, 1);
Bram Moolenaar0cbba822017-08-21 21:39:28 +02002341
Bram Moolenaar58302322017-08-22 20:33:53 +02002342 /* For unix do not use a blinking cursor. In an xterm this causes the
2343 * cursor to blink if it's blinking in the xterm.
Bram Moolenaarbe0b7292017-08-24 21:48:26 +02002344 * For Windows we respect the system wide setting. */
Bram Moolenaar58302322017-08-22 20:33:53 +02002345#ifdef WIN3264
Bram Moolenaarbe0b7292017-08-24 21:48:26 +02002346 if (GetCaretBlinkTime() == INFINITE)
2347 value.boolean = 0;
2348 else
2349 value.boolean = 1;
Bram Moolenaar58302322017-08-22 20:33:53 +02002350#else
Bram Moolenaar0cbba822017-08-21 21:39:28 +02002351 value.boolean = 0;
Bram Moolenaar58302322017-08-22 20:33:53 +02002352#endif
Bram Moolenaar0cbba822017-08-21 21:39:28 +02002353 vterm_state_set_termprop(vterm_obtain_state(vterm),
2354 VTERM_PROP_CURSORBLINK, &value);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002355}
2356
Bram Moolenaar21554412017-07-24 21:44:43 +02002357/*
2358 * Return the text to show for the buffer name and status.
2359 */
2360 char_u *
2361term_get_status_text(term_T *term)
2362{
2363 if (term->tl_status_text == NULL)
2364 {
2365 char_u *txt;
2366 size_t len;
2367
Bram Moolenaar6d819742017-08-06 14:57:49 +02002368 if (term->tl_normal_mode)
Bram Moolenaar423802d2017-07-30 16:52:24 +02002369 {
2370 if (term_job_running(term))
2371 txt = (char_u *)_("Terminal");
2372 else
2373 txt = (char_u *)_("Terminal-finished");
2374 }
2375 else if (term->tl_title != NULL)
Bram Moolenaar21554412017-07-24 21:44:43 +02002376 txt = term->tl_title;
2377 else if (term_job_running(term))
2378 txt = (char_u *)_("running");
2379 else
2380 txt = (char_u *)_("finished");
2381 len = 9 + STRLEN(term->tl_buffer->b_fname) + STRLEN(txt);
Bram Moolenaara1b5b092017-07-26 21:29:34 +02002382 term->tl_status_text = alloc((int)len);
Bram Moolenaar21554412017-07-24 21:44:43 +02002383 if (term->tl_status_text != NULL)
2384 vim_snprintf((char *)term->tl_status_text, len, "%s [%s]",
2385 term->tl_buffer->b_fname, txt);
2386 }
2387 return term->tl_status_text;
2388}
2389
Bram Moolenaarf86eea92017-07-28 13:51:30 +02002390/*
2391 * Mark references in jobs of terminals.
2392 */
2393 int
2394set_ref_in_term(int copyID)
2395{
2396 int abort = FALSE;
2397 term_T *term;
2398 typval_T tv;
2399
2400 for (term = first_term; term != NULL; term = term->tl_next)
2401 if (term->tl_job != NULL)
2402 {
2403 tv.v_type = VAR_JOB;
2404 tv.vval.v_job = term->tl_job;
2405 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
2406 }
2407 return abort;
2408}
2409
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002410/*
Bram Moolenaar97870002017-07-30 18:28:38 +02002411 * Get the buffer from the first argument in "argvars".
2412 * Returns NULL when the buffer is not for a terminal window.
2413 */
2414 static buf_T *
2415term_get_buf(typval_T *argvars)
2416{
2417 buf_T *buf;
2418
2419 (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */
2420 ++emsg_off;
2421 buf = get_buf_tv(&argvars[0], FALSE);
2422 --emsg_off;
2423 if (buf == NULL || buf->b_term == NULL)
2424 return NULL;
2425 return buf;
2426}
2427
2428/*
Bram Moolenaare41e3b42017-08-11 16:24:50 +02002429 * "term_getaltscreen(buf)" function
2430 */
2431 void
2432f_term_getaltscreen(typval_T *argvars, typval_T *rettv)
2433{
2434 buf_T *buf = term_get_buf(argvars);
2435
2436 if (buf == NULL)
2437 return;
2438 rettv->vval.v_number = buf->b_term->tl_using_altscreen;
2439}
2440
2441/*
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002442 * "term_getattr(attr, name)" function
2443 */
2444 void
2445f_term_getattr(typval_T *argvars, typval_T *rettv)
2446{
2447 int attr;
2448 size_t i;
2449 char_u *name;
2450
2451 static struct {
2452 char *name;
2453 int attr;
2454 } attrs[] = {
2455 {"bold", HL_BOLD},
2456 {"italic", HL_ITALIC},
2457 {"underline", HL_UNDERLINE},
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +02002458 {"strike", HL_STRIKETHROUGH},
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002459 {"reverse", HL_INVERSE},
2460 };
2461
2462 attr = get_tv_number(&argvars[0]);
2463 name = get_tv_string_chk(&argvars[1]);
2464 if (name == NULL)
2465 return;
2466
2467 for (i = 0; i < sizeof(attrs)/sizeof(attrs[0]); ++i)
2468 if (STRCMP(name, attrs[i].name) == 0)
2469 {
2470 rettv->vval.v_number = (attr & attrs[i].attr) != 0 ? 1 : 0;
2471 break;
2472 }
2473}
2474
2475/*
Bram Moolenaar97870002017-07-30 18:28:38 +02002476 * "term_getcursor(buf)" function
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002477 */
Bram Moolenaar97870002017-07-30 18:28:38 +02002478 void
2479f_term_getcursor(typval_T *argvars, typval_T *rettv)
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002480{
Bram Moolenaar97870002017-07-30 18:28:38 +02002481 buf_T *buf = term_get_buf(argvars);
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02002482 term_T *term;
Bram Moolenaar97870002017-07-30 18:28:38 +02002483 list_T *l;
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02002484 dict_T *d;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002485
Bram Moolenaar97870002017-07-30 18:28:38 +02002486 if (rettv_list_alloc(rettv) == FAIL)
2487 return;
2488 if (buf == NULL)
2489 return;
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02002490 term = buf->b_term;
Bram Moolenaar97870002017-07-30 18:28:38 +02002491
2492 l = rettv->vval.v_list;
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02002493 list_append_number(l, term->tl_cursor_pos.row + 1);
2494 list_append_number(l, term->tl_cursor_pos.col + 1);
2495
2496 d = dict_alloc();
2497 if (d != NULL)
2498 {
2499 dict_add_nr_str(d, "visible", term->tl_cursor_visible, NULL);
Bram Moolenaar4db25542017-08-28 22:43:05 +02002500 dict_add_nr_str(d, "blink", blink_state_is_inverted()
2501 ? !term->tl_cursor_blink : term->tl_cursor_blink, NULL);
Bram Moolenaar3cd43cc2017-08-12 19:51:41 +02002502 dict_add_nr_str(d, "shape", term->tl_cursor_shape, NULL);
2503 dict_add_nr_str(d, "color", 0L, term->tl_cursor_color == NULL
2504 ? (char_u *)"" : term->tl_cursor_color);
2505 list_append_dict(l, d);
2506 }
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002507}
2508
2509/*
2510 * "term_getjob(buf)" function
2511 */
2512 void
2513f_term_getjob(typval_T *argvars, typval_T *rettv)
2514{
2515 buf_T *buf = term_get_buf(argvars);
2516
2517 rettv->v_type = VAR_JOB;
2518 rettv->vval.v_job = NULL;
2519 if (buf == NULL)
2520 return;
2521
2522 rettv->vval.v_job = buf->b_term->tl_job;
2523 if (rettv->vval.v_job != NULL)
2524 ++rettv->vval.v_job->jv_refcount;
2525}
2526
Bram Moolenaarc2ce52c2017-08-01 18:35:38 +02002527 static int
2528get_row_number(typval_T *tv, term_T *term)
2529{
2530 if (tv->v_type == VAR_STRING
2531 && tv->vval.v_string != NULL
2532 && STRCMP(tv->vval.v_string, ".") == 0)
2533 return term->tl_cursor_pos.row;
2534 return (int)get_tv_number(tv) - 1;
2535}
2536
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002537/*
2538 * "term_getline(buf, row)" function
2539 */
2540 void
2541f_term_getline(typval_T *argvars, typval_T *rettv)
2542{
2543 buf_T *buf = term_get_buf(argvars);
2544 term_T *term;
2545 int row;
2546
2547 rettv->v_type = VAR_STRING;
2548 if (buf == NULL)
2549 return;
2550 term = buf->b_term;
Bram Moolenaarc2ce52c2017-08-01 18:35:38 +02002551 row = get_row_number(&argvars[1], term);
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002552
2553 if (term->tl_vterm == NULL)
2554 {
2555 linenr_T lnum = row + term->tl_scrollback_scrolled + 1;
2556
2557 /* vterm is finished, get the text from the buffer */
2558 if (lnum > 0 && lnum <= buf->b_ml.ml_line_count)
2559 rettv->vval.v_string = vim_strsave(ml_get_buf(buf, lnum, FALSE));
2560 }
2561 else
2562 {
2563 VTermScreen *screen = vterm_obtain_screen(term->tl_vterm);
2564 VTermRect rect;
2565 int len;
2566 char_u *p;
2567
Bram Moolenaar5c838a32017-08-02 22:10:34 +02002568 if (row < 0 || row >= term->tl_rows)
2569 return;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002570 len = term->tl_cols * MB_MAXBYTES + 1;
2571 p = alloc(len);
2572 if (p == NULL)
2573 return;
2574 rettv->vval.v_string = p;
2575
2576 rect.start_col = 0;
2577 rect.end_col = term->tl_cols;
2578 rect.start_row = row;
2579 rect.end_row = row + 1;
2580 p[vterm_screen_get_text(screen, (char *)p, len, rect)] = NUL;
2581 }
2582}
2583
2584/*
Bram Moolenaar82b9ca02017-08-08 23:06:46 +02002585 * "term_getscrolled(buf)" function
2586 */
2587 void
2588f_term_getscrolled(typval_T *argvars, typval_T *rettv)
2589{
2590 buf_T *buf = term_get_buf(argvars);
2591
2592 if (buf == NULL)
2593 return;
2594 rettv->vval.v_number = buf->b_term->tl_scrollback_scrolled;
2595}
2596
2597/*
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002598 * "term_getsize(buf)" function
2599 */
2600 void
2601f_term_getsize(typval_T *argvars, typval_T *rettv)
2602{
2603 buf_T *buf = term_get_buf(argvars);
2604 list_T *l;
2605
2606 if (rettv_list_alloc(rettv) == FAIL)
2607 return;
2608 if (buf == NULL)
2609 return;
2610
2611 l = rettv->vval.v_list;
2612 list_append_number(l, buf->b_term->tl_rows);
2613 list_append_number(l, buf->b_term->tl_cols);
2614}
2615
2616/*
Bram Moolenaarb000e322017-07-30 19:38:21 +02002617 * "term_getstatus(buf)" function
2618 */
2619 void
2620f_term_getstatus(typval_T *argvars, typval_T *rettv)
2621{
2622 buf_T *buf = term_get_buf(argvars);
2623 term_T *term;
2624 char_u val[100];
2625
2626 rettv->v_type = VAR_STRING;
2627 if (buf == NULL)
2628 return;
2629 term = buf->b_term;
2630
2631 if (term_job_running(term))
2632 STRCPY(val, "running");
2633 else
2634 STRCPY(val, "finished");
Bram Moolenaar6d819742017-08-06 14:57:49 +02002635 if (term->tl_normal_mode)
2636 STRCAT(val, ",normal");
Bram Moolenaarb000e322017-07-30 19:38:21 +02002637 rettv->vval.v_string = vim_strsave(val);
2638}
2639
2640/*
2641 * "term_gettitle(buf)" function
2642 */
2643 void
2644f_term_gettitle(typval_T *argvars, typval_T *rettv)
2645{
2646 buf_T *buf = term_get_buf(argvars);
2647
2648 rettv->v_type = VAR_STRING;
2649 if (buf == NULL)
2650 return;
2651
2652 if (buf->b_term->tl_title != NULL)
2653 rettv->vval.v_string = vim_strsave(buf->b_term->tl_title);
2654}
2655
2656/*
Bram Moolenaar7c9aec42017-08-03 13:51:25 +02002657 * "term_gettty(buf)" function
2658 */
2659 void
2660f_term_gettty(typval_T *argvars, typval_T *rettv)
2661{
2662 buf_T *buf = term_get_buf(argvars);
2663 char_u *p;
2664
2665 rettv->v_type = VAR_STRING;
2666 if (buf == NULL)
2667 return;
2668 if (buf->b_term->tl_job != NULL)
2669 p = buf->b_term->tl_job->jv_tty_name;
2670 else
2671 p = buf->b_term->tl_tty_name;
2672 if (p != NULL)
2673 rettv->vval.v_string = vim_strsave(p);
2674}
2675
2676/*
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002677 * "term_list()" function
2678 */
2679 void
2680f_term_list(typval_T *argvars UNUSED, typval_T *rettv)
2681{
2682 term_T *tp;
2683 list_T *l;
2684
2685 if (rettv_list_alloc(rettv) == FAIL || first_term == NULL)
2686 return;
2687
2688 l = rettv->vval.v_list;
2689 for (tp = first_term; tp != NULL; tp = tp->tl_next)
2690 if (tp != NULL && tp->tl_buffer != NULL)
2691 if (list_append_number(l,
2692 (varnumber_T)tp->tl_buffer->b_fnum) == FAIL)
2693 return;
2694}
2695
2696/*
2697 * "term_scrape(buf, row)" function
2698 */
2699 void
2700f_term_scrape(typval_T *argvars, typval_T *rettv)
2701{
2702 buf_T *buf = term_get_buf(argvars);
2703 VTermScreen *screen = NULL;
2704 VTermPos pos;
2705 list_T *l;
2706 term_T *term;
Bram Moolenaar33a43be2017-08-06 21:36:22 +02002707 char_u *p;
2708 sb_line_T *line;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002709
2710 if (rettv_list_alloc(rettv) == FAIL)
2711 return;
2712 if (buf == NULL)
2713 return;
2714 term = buf->b_term;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002715
2716 l = rettv->vval.v_list;
Bram Moolenaarc2ce52c2017-08-01 18:35:38 +02002717 pos.row = get_row_number(&argvars[1], term);
Bram Moolenaar33a43be2017-08-06 21:36:22 +02002718
2719 if (term->tl_vterm != NULL)
Bram Moolenaare20b3eb2017-08-07 21:26:29 +02002720 {
Bram Moolenaar33a43be2017-08-06 21:36:22 +02002721 screen = vterm_obtain_screen(term->tl_vterm);
Bram Moolenaare20b3eb2017-08-07 21:26:29 +02002722 p = NULL;
2723 line = NULL;
2724 }
Bram Moolenaar33a43be2017-08-06 21:36:22 +02002725 else
2726 {
2727 linenr_T lnum = pos.row + term->tl_scrollback_scrolled;
2728
2729 if (lnum < 0 || lnum >= term->tl_scrollback.ga_len)
2730 return;
2731 p = ml_get_buf(buf, lnum + 1, FALSE);
2732 line = (sb_line_T *)term->tl_scrollback.ga_data + lnum;
2733 }
2734
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002735 for (pos.col = 0; pos.col < term->tl_cols; )
2736 {
2737 dict_T *dcell;
Bram Moolenaar33a43be2017-08-06 21:36:22 +02002738 int width;
2739 VTermScreenCellAttrs attrs;
2740 VTermColor fg, bg;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002741 char_u rgb[8];
2742 char_u mbs[MB_MAXBYTES * VTERM_MAX_CHARS_PER_CELL + 1];
2743 int off = 0;
2744 int i;
2745
2746 if (screen == NULL)
2747 {
Bram Moolenaar33a43be2017-08-06 21:36:22 +02002748 cellattr_T *cellattr;
2749 int len;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002750
2751 /* vterm has finished, get the cell from scrollback */
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002752 if (pos.col >= line->sb_cols)
2753 break;
Bram Moolenaar33a43be2017-08-06 21:36:22 +02002754 cellattr = line->sb_cells + pos.col;
2755 width = cellattr->width;
2756 attrs = cellattr->attrs;
2757 fg = cellattr->fg;
2758 bg = cellattr->bg;
2759 len = MB_PTR2LEN(p);
2760 mch_memmove(mbs, p, len);
2761 mbs[len] = NUL;
2762 p += len;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002763 }
Bram Moolenaar33a43be2017-08-06 21:36:22 +02002764 else
2765 {
2766 VTermScreenCell cell;
2767 if (vterm_screen_get_cell(screen, pos, &cell) == 0)
2768 break;
2769 for (i = 0; i < VTERM_MAX_CHARS_PER_CELL; ++i)
2770 {
2771 if (cell.chars[i] == 0)
2772 break;
2773 off += (*utf_char2bytes)((int)cell.chars[i], mbs + off);
2774 }
2775 mbs[off] = NUL;
2776 width = cell.width;
2777 attrs = cell.attrs;
2778 fg = cell.fg;
2779 bg = cell.bg;
2780 }
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002781 dcell = dict_alloc();
2782 list_append_dict(l, dcell);
2783
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002784 dict_add_nr_str(dcell, "chars", 0, mbs);
2785
2786 vim_snprintf((char *)rgb, 8, "#%02x%02x%02x",
Bram Moolenaar33a43be2017-08-06 21:36:22 +02002787 fg.red, fg.green, fg.blue);
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002788 dict_add_nr_str(dcell, "fg", 0, rgb);
2789 vim_snprintf((char *)rgb, 8, "#%02x%02x%02x",
Bram Moolenaar33a43be2017-08-06 21:36:22 +02002790 bg.red, bg.green, bg.blue);
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002791 dict_add_nr_str(dcell, "bg", 0, rgb);
2792
Bram Moolenaar33a43be2017-08-06 21:36:22 +02002793 dict_add_nr_str(dcell, "attr",
2794 cell2attr(attrs, fg, bg), NULL);
2795 dict_add_nr_str(dcell, "width", width, NULL);
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002796
2797 ++pos.col;
Bram Moolenaar33a43be2017-08-06 21:36:22 +02002798 if (width == 2)
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002799 ++pos.col;
2800 }
2801}
2802
2803/*
2804 * "term_sendkeys(buf, keys)" function
2805 */
2806 void
2807f_term_sendkeys(typval_T *argvars, typval_T *rettv)
2808{
2809 buf_T *buf = term_get_buf(argvars);
2810 char_u *msg;
2811 term_T *term;
2812
2813 rettv->v_type = VAR_UNKNOWN;
2814 if (buf == NULL)
2815 return;
2816
2817 msg = get_tv_string_chk(&argvars[1]);
2818 if (msg == NULL)
2819 return;
2820 term = buf->b_term;
2821 if (term->tl_vterm == NULL)
2822 return;
2823
2824 while (*msg != NUL)
2825 {
2826 send_keys_to_term(term, PTR2CHAR(msg), FALSE);
2827 msg += MB_PTR2LEN(msg);
2828 }
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002829}
2830
2831/*
2832 * "term_start(command, options)" function
2833 */
2834 void
2835f_term_start(typval_T *argvars, typval_T *rettv)
2836{
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002837 jobopt_T opt;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002838
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002839 init_job_options(&opt);
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002840 if (argvars[1].v_type != VAR_UNKNOWN
2841 && get_job_options(&argvars[1], &opt,
2842 JO_TIMEOUT_ALL + JO_STOPONEXIT
Bram Moolenaare88fc7a2017-09-03 20:59:40 +02002843 + JO_EXIT_CB + JO_CLOSE_CALLBACK + JO_OUT_IO,
Bram Moolenaar37c45832017-08-12 16:01:04 +02002844 JO2_TERM_NAME + JO2_TERM_FINISH + JO2_HIDDEN + JO2_TERM_OPENCMD
Bram Moolenaarda43b612017-08-11 22:27:50 +02002845 + JO2_TERM_COLS + JO2_TERM_ROWS + JO2_VERTICAL + JO2_CURWIN
Bram Moolenaar3346cc42017-09-02 14:54:21 +02002846 + JO2_CWD + JO2_ENV + JO2_EOF_CHARS) == FAIL)
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002847 return;
2848
Bram Moolenaar08d384f2017-08-11 21:51:23 +02002849 if (opt.jo_vertical)
2850 cmdmod.split = WSP_VERT;
Bram Moolenaardcaa6132017-08-13 17:13:09 +02002851 term_start(&argvars[0], &opt, FALSE);
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002852
2853 if (curbuf->b_term != NULL)
2854 rettv->vval.v_number = curbuf->b_fnum;
2855}
2856
2857/*
2858 * "term_wait" function
2859 */
2860 void
2861f_term_wait(typval_T *argvars, typval_T *rettv UNUSED)
2862{
2863 buf_T *buf = term_get_buf(argvars);
2864
2865 if (buf == NULL)
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002866 {
2867 ch_log(NULL, "term_wait(): invalid argument");
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002868 return;
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002869 }
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002870 if (buf->b_term->tl_job == NULL)
2871 {
2872 ch_log(NULL, "term_wait(): no job to wait for");
2873 return;
2874 }
Bram Moolenaar13ebb032017-08-26 22:02:51 +02002875 if (buf->b_term->tl_job->jv_channel == NULL)
2876 /* channel is closed, nothing to do */
2877 return;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002878
2879 /* Get the job status, this will detect a job that finished. */
Bram Moolenaar13ebb032017-08-26 22:02:51 +02002880 if ((buf->b_term->tl_job->jv_channel == NULL
2881 || !buf->b_term->tl_job->jv_channel->ch_keep_open)
2882 && STRCMP(job_status(buf->b_term->tl_job), "dead") == 0)
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002883 {
2884 /* The job is dead, keep reading channel I/O until the channel is
2885 * closed. */
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002886 ch_log(NULL, "term_wait(): waiting for channel to close");
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002887 while (buf->b_term != NULL && !buf->b_term->tl_channel_closed)
2888 {
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002889 mch_check_messages();
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002890 parse_queued_messages();
2891 ui_delay(10L, FALSE);
2892 }
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002893 mch_check_messages();
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002894 parse_queued_messages();
2895 }
2896 else
2897 {
Bram Moolenaarf3402b12017-08-06 19:07:08 +02002898 long wait = 10L;
2899
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002900 mch_check_messages();
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002901 parse_queued_messages();
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002902
Bram Moolenaarf3402b12017-08-06 19:07:08 +02002903 /* Wait for some time for any channel I/O. */
2904 if (argvars[1].v_type != VAR_UNKNOWN)
2905 wait = get_tv_number(&argvars[1]);
2906 ui_delay(wait, TRUE);
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002907 mch_check_messages();
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002908
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002909 /* Flushing messages on channels is hopefully sufficient.
2910 * TODO: is there a better way? */
2911 parse_queued_messages();
2912 }
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002913}
2914
Bram Moolenaardada6d22017-09-02 17:18:35 +02002915/*
2916 * Called when a channel has sent all the lines to a terminal.
2917 * Send a CTRL-D to mark the end of the text.
2918 */
2919 void
2920term_send_eof(channel_T *ch)
2921{
2922 term_T *term;
2923
2924 for (term = first_term; term != NULL; term = term->tl_next)
2925 if (term->tl_job == ch->ch_job)
2926 {
2927 if (term->tl_eof_chars != NULL)
2928 {
2929 channel_send(ch, PART_IN, term->tl_eof_chars,
2930 (int)STRLEN(term->tl_eof_chars), NULL);
2931 channel_send(ch, PART_IN, (char_u *)"\r", 1, NULL);
2932 }
2933# ifdef WIN3264
2934 else
2935 /* Default: CTRL-D */
2936 channel_send(ch, PART_IN, (char_u *)"\004\r", 2, NULL);
2937# endif
2938 }
2939}
2940
Bram Moolenaara83e3962017-08-17 14:39:07 +02002941# if defined(WIN3264) || defined(PROTO)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002942
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002943/**************************************
2944 * 2. MS-Windows implementation.
2945 */
2946
Bram Moolenaara83e3962017-08-17 14:39:07 +02002947# ifndef PROTO
2948
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002949#define WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN 1ul
2950#define WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN 2ull
2951
Bram Moolenaar8a773062017-07-24 22:29:21 +02002952void* (*winpty_config_new)(UINT64, void*);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002953void* (*winpty_open)(void*, void*);
Bram Moolenaar8a773062017-07-24 22:29:21 +02002954void* (*winpty_spawn_config_new)(UINT64, void*, LPCWSTR, void*, void*, void*);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002955BOOL (*winpty_spawn)(void*, void*, HANDLE*, HANDLE*, DWORD*, void*);
2956void (*winpty_config_set_initial_size)(void*, int, int);
2957LPCWSTR (*winpty_conin_name)(void*);
2958LPCWSTR (*winpty_conout_name)(void*);
2959LPCWSTR (*winpty_conerr_name)(void*);
2960void (*winpty_free)(void*);
2961void (*winpty_config_free)(void*);
2962void (*winpty_spawn_config_free)(void*);
2963void (*winpty_error_free)(void*);
2964LPCWSTR (*winpty_error_msg)(void*);
Bram Moolenaar43da3e32017-07-23 17:27:54 +02002965BOOL (*winpty_set_size)(void*, int, int, void*);
Bram Moolenaar5be8dd02017-08-03 20:52:19 +02002966HANDLE (*winpty_agent_process)(void*);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002967
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002968#define WINPTY_DLL "winpty.dll"
2969
2970static HINSTANCE hWinPtyDLL = NULL;
Bram Moolenaara83e3962017-08-17 14:39:07 +02002971# endif
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002972
Bram Moolenaara83e3962017-08-17 14:39:07 +02002973 static int
2974dyn_winpty_init(int verbose)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002975{
2976 int i;
2977 static struct
2978 {
2979 char *name;
2980 FARPROC *ptr;
2981 } winpty_entry[] =
2982 {
2983 {"winpty_conerr_name", (FARPROC*)&winpty_conerr_name},
2984 {"winpty_config_free", (FARPROC*)&winpty_config_free},
2985 {"winpty_config_new", (FARPROC*)&winpty_config_new},
2986 {"winpty_config_set_initial_size", (FARPROC*)&winpty_config_set_initial_size},
2987 {"winpty_conin_name", (FARPROC*)&winpty_conin_name},
2988 {"winpty_conout_name", (FARPROC*)&winpty_conout_name},
2989 {"winpty_error_free", (FARPROC*)&winpty_error_free},
2990 {"winpty_free", (FARPROC*)&winpty_free},
2991 {"winpty_open", (FARPROC*)&winpty_open},
2992 {"winpty_spawn", (FARPROC*)&winpty_spawn},
2993 {"winpty_spawn_config_free", (FARPROC*)&winpty_spawn_config_free},
2994 {"winpty_spawn_config_new", (FARPROC*)&winpty_spawn_config_new},
2995 {"winpty_error_msg", (FARPROC*)&winpty_error_msg},
Bram Moolenaar43da3e32017-07-23 17:27:54 +02002996 {"winpty_set_size", (FARPROC*)&winpty_set_size},
Bram Moolenaar5be8dd02017-08-03 20:52:19 +02002997 {"winpty_agent_process", (FARPROC*)&winpty_agent_process},
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002998 {NULL, NULL}
2999 };
3000
3001 /* No need to initialize twice. */
3002 if (hWinPtyDLL)
Bram Moolenaara83e3962017-08-17 14:39:07 +02003003 return OK;
Bram Moolenaar9e13aa72017-08-16 23:14:08 +02003004 /* Load winpty.dll, prefer using the 'winptydll' option, fall back to just
3005 * winpty.dll. */
3006 if (*p_winptydll != NUL)
3007 hWinPtyDLL = vimLoadLib((char *)p_winptydll);
3008 if (!hWinPtyDLL)
3009 hWinPtyDLL = vimLoadLib(WINPTY_DLL);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003010 if (!hWinPtyDLL)
3011 {
Bram Moolenaara83e3962017-08-17 14:39:07 +02003012 if (verbose)
3013 EMSG2(_(e_loadlib), *p_winptydll != NUL ? p_winptydll
3014 : (char_u *)WINPTY_DLL);
3015 return FAIL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003016 }
3017 for (i = 0; winpty_entry[i].name != NULL
3018 && winpty_entry[i].ptr != NULL; ++i)
3019 {
3020 if ((*winpty_entry[i].ptr = (FARPROC)GetProcAddress(hWinPtyDLL,
3021 winpty_entry[i].name)) == NULL)
3022 {
Bram Moolenaara83e3962017-08-17 14:39:07 +02003023 if (verbose)
3024 EMSG2(_(e_loadfunc), winpty_entry[i].name);
3025 return FAIL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003026 }
3027 }
3028
Bram Moolenaara83e3962017-08-17 14:39:07 +02003029 return OK;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003030}
3031
3032/*
3033 * Create a new terminal of "rows" by "cols" cells.
3034 * Store a reference in "term".
3035 * Return OK or FAIL.
3036 */
3037 static int
Bram Moolenaarb2412082017-08-20 18:09:14 +02003038term_and_job_init(
3039 term_T *term,
Bram Moolenaarb2412082017-08-20 18:09:14 +02003040 typval_T *argvar,
3041 jobopt_T *opt)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003042{
Bram Moolenaar5983d502017-08-20 19:22:56 +02003043 WCHAR *cmd_wchar = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003044 channel_T *channel = NULL;
3045 job_T *job = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003046 DWORD error;
Bram Moolenaar5983d502017-08-20 19:22:56 +02003047 HANDLE jo = NULL;
3048 HANDLE child_process_handle;
3049 HANDLE child_thread_handle;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003050 void *winpty_err;
Bram Moolenaarab6eec32017-07-27 21:46:43 +02003051 void *spawn_config = NULL;
Bram Moolenaar5be8dd02017-08-03 20:52:19 +02003052 char buf[MAX_PATH];
Bram Moolenaardcaa6132017-08-13 17:13:09 +02003053 garray_T ga;
3054 char_u *cmd;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003055
Bram Moolenaara83e3962017-08-17 14:39:07 +02003056 if (dyn_winpty_init(TRUE) == FAIL)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003057 return FAIL;
3058
Bram Moolenaardcaa6132017-08-13 17:13:09 +02003059 if (argvar->v_type == VAR_STRING)
3060 cmd = argvar->vval.v_string;
3061 else
3062 {
3063 ga_init2(&ga, (int)sizeof(char*), 20);
3064 if (win32_build_cmd(argvar->vval.v_list, &ga) == FAIL)
3065 goto failed;
3066 cmd = ga.ga_data;
3067 }
3068
Bram Moolenaar5983d502017-08-20 19:22:56 +02003069 cmd_wchar = enc_to_utf16(cmd, NULL);
3070 if (cmd_wchar == NULL)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003071 return FAIL;
3072
3073 job = job_alloc();
3074 if (job == NULL)
3075 goto failed;
3076
3077 channel = add_channel();
3078 if (channel == NULL)
3079 goto failed;
3080
3081 term->tl_winpty_config = winpty_config_new(0, &winpty_err);
3082 if (term->tl_winpty_config == NULL)
3083 goto failed;
3084
Bram Moolenaar13ebb032017-08-26 22:02:51 +02003085 winpty_config_set_initial_size(term->tl_winpty_config,
3086 term->tl_cols, term->tl_rows);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003087 term->tl_winpty = winpty_open(term->tl_winpty_config, &winpty_err);
3088 if (term->tl_winpty == NULL)
3089 goto failed;
3090
Bram Moolenaar7c9aec42017-08-03 13:51:25 +02003091 /* TODO: if the command is "NONE" only create a pty. */
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003092 spawn_config = winpty_spawn_config_new(
3093 WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN |
3094 WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN,
3095 NULL,
Bram Moolenaar5983d502017-08-20 19:22:56 +02003096 cmd_wchar,
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003097 NULL,
3098 NULL,
3099 &winpty_err);
3100 if (spawn_config == NULL)
3101 goto failed;
3102
3103 channel = add_channel();
3104 if (channel == NULL)
3105 goto failed;
3106
3107 job = job_alloc();
3108 if (job == NULL)
3109 goto failed;
3110
Bram Moolenaar5983d502017-08-20 19:22:56 +02003111 if (opt->jo_set & JO_IN_BUF)
3112 job->jv_in_buf = buflist_findnr(opt->jo_io_buf[PART_IN]);
3113
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003114 if (!winpty_spawn(term->tl_winpty, spawn_config, &child_process_handle,
3115 &child_thread_handle, &error, &winpty_err))
3116 goto failed;
3117
3118 channel_set_pipes(channel,
Bram Moolenaar5983d502017-08-20 19:22:56 +02003119 (sock_T)CreateFileW(
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003120 winpty_conin_name(term->tl_winpty),
3121 GENERIC_WRITE, 0, NULL,
3122 OPEN_EXISTING, 0, NULL),
Bram Moolenaar5983d502017-08-20 19:22:56 +02003123 (sock_T)CreateFileW(
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003124 winpty_conout_name(term->tl_winpty),
3125 GENERIC_READ, 0, NULL,
3126 OPEN_EXISTING, 0, NULL),
Bram Moolenaar5983d502017-08-20 19:22:56 +02003127 (sock_T)CreateFileW(
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003128 winpty_conerr_name(term->tl_winpty),
3129 GENERIC_READ, 0, NULL,
3130 OPEN_EXISTING, 0, NULL));
3131
Bram Moolenaaref68e4f2017-09-02 16:28:36 +02003132 /* Write lines with CR instead of NL. */
3133 channel->ch_write_text_mode = TRUE;
3134
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003135 jo = CreateJobObject(NULL, NULL);
3136 if (jo == NULL)
3137 goto failed;
3138
3139 if (!AssignProcessToJobObject(jo, child_process_handle))
Bram Moolenaarab6eec32017-07-27 21:46:43 +02003140 {
3141 /* Failed, switch the way to terminate process with TerminateProcess. */
3142 CloseHandle(jo);
3143 jo = NULL;
3144 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003145
3146 winpty_spawn_config_free(spawn_config);
Bram Moolenaar5983d502017-08-20 19:22:56 +02003147 vim_free(cmd_wchar);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003148
Bram Moolenaar13ebb032017-08-26 22:02:51 +02003149 create_vterm(term, term->tl_rows, term->tl_cols);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003150
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02003151 channel_set_job(channel, job, opt);
Bram Moolenaar102dc7f2017-08-03 20:59:29 +02003152 job_set_options(job, opt);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003153
3154 job->jv_channel = channel;
3155 job->jv_proc_info.hProcess = child_process_handle;
3156 job->jv_proc_info.dwProcessId = GetProcessId(child_process_handle);
3157 job->jv_job_object = jo;
3158 job->jv_status = JOB_STARTED;
Bram Moolenaar5be8dd02017-08-03 20:52:19 +02003159 sprintf(buf, "winpty://%lu",
3160 GetProcessId(winpty_agent_process(term->tl_winpty)));
3161 job->jv_tty_name = vim_strsave((char_u*)buf);
Bram Moolenaar0e83f022017-07-27 22:07:35 +02003162 ++job->jv_refcount;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003163 term->tl_job = job;
3164
3165 return OK;
3166
3167failed:
Bram Moolenaardcaa6132017-08-13 17:13:09 +02003168 if (argvar->v_type == VAR_LIST)
3169 vim_free(ga.ga_data);
Bram Moolenaar5983d502017-08-20 19:22:56 +02003170 if (cmd_wchar != NULL)
3171 vim_free(cmd_wchar);
Bram Moolenaarab6eec32017-07-27 21:46:43 +02003172 if (spawn_config != NULL)
3173 winpty_spawn_config_free(spawn_config);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003174 if (channel != NULL)
3175 channel_clear(channel);
3176 if (job != NULL)
Bram Moolenaarcdeae992017-07-23 17:22:35 +02003177 {
3178 job->jv_channel = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003179 job_cleanup(job);
Bram Moolenaarcdeae992017-07-23 17:22:35 +02003180 }
3181 term->tl_job = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003182 if (jo != NULL)
3183 CloseHandle(jo);
3184 if (term->tl_winpty != NULL)
3185 winpty_free(term->tl_winpty);
Bram Moolenaarcdeae992017-07-23 17:22:35 +02003186 term->tl_winpty = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003187 if (term->tl_winpty_config != NULL)
3188 winpty_config_free(term->tl_winpty_config);
Bram Moolenaarcdeae992017-07-23 17:22:35 +02003189 term->tl_winpty_config = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003190 if (winpty_err != NULL)
3191 {
3192 char_u *msg = utf16_to_enc(
3193 (short_u *)winpty_error_msg(winpty_err), NULL);
3194
3195 EMSG(msg);
3196 winpty_error_free(winpty_err);
3197 }
3198 return FAIL;
3199}
3200
Bram Moolenaar13ebb032017-08-26 22:02:51 +02003201 static int
3202create_pty_only(term_T *term, jobopt_T *opt)
3203{
3204 /* TODO: implement this */
3205 return FAIL;
3206}
3207
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003208/*
3209 * Free the terminal emulator part of "term".
3210 */
3211 static void
Bram Moolenaard85f2712017-07-28 21:51:57 +02003212term_free_vterm(term_T *term)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003213{
Bram Moolenaarcdeae992017-07-23 17:22:35 +02003214 if (term->tl_winpty != NULL)
3215 winpty_free(term->tl_winpty);
Bram Moolenaard85f2712017-07-28 21:51:57 +02003216 term->tl_winpty = NULL;
Bram Moolenaarcdeae992017-07-23 17:22:35 +02003217 if (term->tl_winpty_config != NULL)
3218 winpty_config_free(term->tl_winpty_config);
Bram Moolenaard85f2712017-07-28 21:51:57 +02003219 term->tl_winpty_config = NULL;
Bram Moolenaarcdeae992017-07-23 17:22:35 +02003220 if (term->tl_vterm != NULL)
3221 vterm_free(term->tl_vterm);
Bram Moolenaardcbfa332017-07-28 23:16:13 +02003222 term->tl_vterm = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003223}
3224
Bram Moolenaar43da3e32017-07-23 17:27:54 +02003225/*
3226 * Request size to terminal.
3227 */
3228 static void
3229term_report_winsize(term_T *term, int rows, int cols)
3230{
3231 winpty_set_size(term->tl_winpty, cols, rows, NULL);
3232}
3233
Bram Moolenaara83e3962017-08-17 14:39:07 +02003234 int
3235terminal_enabled(void)
3236{
3237 return dyn_winpty_init(FALSE) == OK;
3238}
3239
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003240# else
3241
3242/**************************************
3243 * 3. Unix-like implementation.
3244 */
3245
3246/*
3247 * Create a new terminal of "rows" by "cols" cells.
3248 * Start job for "cmd".
3249 * Store the pointers in "term".
3250 * Return OK or FAIL.
3251 */
3252 static int
Bram Moolenaarb2412082017-08-20 18:09:14 +02003253term_and_job_init(
3254 term_T *term,
Bram Moolenaarb2412082017-08-20 18:09:14 +02003255 typval_T *argvar,
3256 jobopt_T *opt)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003257{
Bram Moolenaar13ebb032017-08-26 22:02:51 +02003258 create_vterm(term, term->tl_rows, term->tl_cols);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003259
Bram Moolenaardcaa6132017-08-13 17:13:09 +02003260 term->tl_job = job_start(argvar, opt);
Bram Moolenaar0e83f022017-07-27 22:07:35 +02003261 if (term->tl_job != NULL)
3262 ++term->tl_job->jv_refcount;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003263
Bram Moolenaar61a66052017-07-22 18:39:00 +02003264 return term->tl_job != NULL
3265 && term->tl_job->jv_channel != NULL
3266 && term->tl_job->jv_status != JOB_FAILED ? OK : FAIL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003267}
3268
Bram Moolenaar13ebb032017-08-26 22:02:51 +02003269 static int
3270create_pty_only(term_T *term, jobopt_T *opt)
3271{
3272 int ret;
3273
3274 create_vterm(term, term->tl_rows, term->tl_cols);
3275
3276 term->tl_job = job_alloc();
3277 if (term->tl_job == NULL)
3278 return FAIL;
3279 ++term->tl_job->jv_refcount;
3280
3281 /* behave like the job is already finished */
3282 term->tl_job->jv_status = JOB_FINISHED;
3283
3284 ret = mch_create_pty_channel(term->tl_job, opt);
3285
3286 return ret;
3287}
3288
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003289/*
3290 * Free the terminal emulator part of "term".
3291 */
3292 static void
Bram Moolenaard85f2712017-07-28 21:51:57 +02003293term_free_vterm(term_T *term)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003294{
Bram Moolenaarcdeae992017-07-23 17:22:35 +02003295 if (term->tl_vterm != NULL)
3296 vterm_free(term->tl_vterm);
Bram Moolenaard85f2712017-07-28 21:51:57 +02003297 term->tl_vterm = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003298}
Bram Moolenaar43da3e32017-07-23 17:27:54 +02003299
3300/*
3301 * Request size to terminal.
3302 */
3303 static void
3304term_report_winsize(term_T *term, int rows, int cols)
3305{
3306 /* Use an ioctl() to report the new window size to the job. */
3307 if (term->tl_job != NULL && term->tl_job->jv_channel != NULL)
3308 {
3309 int fd = -1;
3310 int part;
3311
3312 for (part = PART_OUT; part < PART_COUNT; ++part)
3313 {
3314 fd = term->tl_job->jv_channel->ch_part[part].ch_fd;
3315 if (isatty(fd))
3316 break;
3317 }
3318 if (part < PART_COUNT && mch_report_winsize(fd, rows, cols) == OK)
Bram Moolenaar2d33e902017-08-11 16:31:54 +02003319 mch_signal_job(term->tl_job, (char_u *)"winch");
Bram Moolenaar43da3e32017-07-23 17:27:54 +02003320 }
3321}
3322
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02003323# endif
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02003324
Bram Moolenaare4f25e42017-07-07 11:54:15 +02003325#endif /* FEAT_TERMINAL */