blob: 893cd0f1b84439e57a3388d109f5e7f8a664cdb4 [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.
37 *
Bram Moolenaare4f25e42017-07-07 11:54:15 +020038 * TODO:
Bram Moolenaard8dc1792017-08-03 11:55:21 +020039 * - MS-Windows: no redraw for 'updatetime' #1915
Bram Moolenaar423802d2017-07-30 16:52:24 +020040 * - in bash mouse clicks are inserting characters.
41 * - mouse scroll: when over other window, scroll that window.
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +020042 * - add argument to term_wait() for waiting time.
Bram Moolenaard85f2712017-07-28 21:51:57 +020043 * - For the scrollback buffer store lines in the buffer, only attributes in
44 * tl_scrollback.
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020045 * - When the job ends:
Bram Moolenaar21554412017-07-24 21:44:43 +020046 * - Need an option or argument to drop the window+buffer right away, to be
Bram Moolenaar423802d2017-07-30 16:52:24 +020047 * used for a shell or Vim. 'termfinish'; "close", "open" (open window when
48 * job finishes).
49 * - add option values to the command:
50 * :term <24x80> <close> vim notes.txt
Bram Moolenaar12d93ee2017-07-30 19:02:02 +020051 * - support different cursor shapes, colors and attributes
52 * - make term_getcursor() return type (none/block/bar/underline) and
53 * attributes (color, blink, etc.)
Bram Moolenaard85f2712017-07-28 21:51:57 +020054 * - To set BS correctly, check get_stty(); Pass the fd of the pty.
Bram Moolenaar423802d2017-07-30 16:52:24 +020055 * - do not store terminal window in viminfo. Or prefix term:// ?
Bram Moolenaar21554412017-07-24 21:44:43 +020056 * - add a character in :ls output
Bram Moolenaar43c007f2017-07-30 17:45:37 +020057 * - add 't' to mode()
Bram Moolenaard8dc1792017-08-03 11:55:21 +020058 * - set 'filetype' to "terminal"?
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020059 * - use win_del_lines() to make scroll-up efficient.
Bram Moolenaar7c9aec42017-08-03 13:51:25 +020060 * - Make StatusLineTerm adjust UserN highlighting like StatusLineNC does, see
61 * use of hightlight_stlnc[].
Bram Moolenaar94053a52017-08-01 21:44:33 +020062 * - implement term_setsize()
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020063 * - add test for giving error for invalid 'termsize' value.
Bram Moolenaare4f25e42017-07-07 11:54:15 +020064 * - support minimal size when 'termsize' is "rows*cols".
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020065 * - support minimal size when 'termsize' is empty?
Bram Moolenaar5a1feb82017-07-22 18:04:08 +020066 * - implement "term" for job_start(): more job options when starting a
67 * terminal.
Bram Moolenaar7c9aec42017-08-03 13:51:25 +020068 * - support ":term NONE" to open a terminal with a pty but not running a job
69 * in it. The pty can be passed to gdb to run the executable in.
Bram Moolenaar423802d2017-07-30 16:52:24 +020070 * - if the job in the terminal does not support the mouse, we can use the
71 * mouse in the Terminal window for copy/paste.
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020072 * - when 'encoding' is not utf-8, or the job is using another encoding, setup
73 * conversions.
Bram Moolenaarc9456ce2017-07-30 21:46:04 +020074 * - update ":help function-list" for terminal functions.
Bram Moolenaardbe948d2017-07-23 22:50:51 +020075 * - In the GUI use a terminal emulator for :!cmd.
Bram Moolenaar12d853f2017-08-01 18:04:04 +020076 * - Copy text in the vterm to the Vim buffer once in a while, so that
77 * completion works.
Bram Moolenaare4f25e42017-07-07 11:54:15 +020078 */
79
80#include "vim.h"
81
Bram Moolenaarc6df10e2017-07-29 20:15:08 +020082#if defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaare4f25e42017-07-07 11:54:15 +020083
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020084#ifdef WIN3264
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020085# define MIN(x,y) (x < y ? x : y)
86# define MAX(x,y) (x > y ? x : y)
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020087#endif
Bram Moolenaare4f25e42017-07-07 11:54:15 +020088
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020089#include "libvterm/include/vterm.h"
90
Bram Moolenaard85f2712017-07-28 21:51:57 +020091typedef struct sb_line_S {
92 int sb_cols; /* can differ per line */
93 VTermScreenCell *sb_cells; /* allocated */
94} sb_line_T;
95
Bram Moolenaare4f25e42017-07-07 11:54:15 +020096/* typedef term_T in structs.h */
97struct terminal_S {
98 term_T *tl_next;
99
Bram Moolenaar423802d2017-07-30 16:52:24 +0200100 VTerm *tl_vterm;
101 job_T *tl_job;
102 buf_T *tl_buffer;
103
Bram Moolenaar7c9aec42017-08-03 13:51:25 +0200104 /* used when tl_job is NULL and only a pty was created */
105 int tl_tty_fd;
106 char_u *tl_tty_name;
107
Bram Moolenaar423802d2017-07-30 16:52:24 +0200108 int tl_terminal_mode;
109 int tl_channel_closed;
110
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200111#ifdef WIN3264
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200112 void *tl_winpty_config;
113 void *tl_winpty;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200114#endif
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200115
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200116 /* last known vterm size */
117 int tl_rows;
118 int tl_cols;
119 /* vterm size does not follow window size */
120 int tl_rows_fixed;
121 int tl_cols_fixed;
122
Bram Moolenaar21554412017-07-24 21:44:43 +0200123 char_u *tl_title; /* NULL or allocated */
124 char_u *tl_status_text; /* NULL or allocated */
125
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200126 /* Range of screen rows to update. Zero based. */
127 int tl_dirty_row_start; /* -1 if nothing dirty */
128 int tl_dirty_row_end; /* row below last one to update */
129
Bram Moolenaard85f2712017-07-28 21:51:57 +0200130 garray_T tl_scrollback;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200131 int tl_scrollback_scrolled;
Bram Moolenaard85f2712017-07-28 21:51:57 +0200132
Bram Moolenaar22aad2f2017-07-30 18:19:46 +0200133 VTermPos tl_cursor_pos;
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200134 int tl_cursor_visible;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200135};
136
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200137/*
138 * List of all active terminals.
139 */
140static term_T *first_term = NULL;
141
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200142
143#define MAX_ROW 999999 /* used for tl_dirty_row_end to update all rows */
144#define KEY_BUF_LEN 200
145
146/*
147 * Functions with separate implementation for MS-Windows and Unix-like systems.
148 */
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200149static int term_and_job_init(term_T *term, int rows, int cols, char_u *cmd, jobopt_T *opt);
Bram Moolenaar43da3e32017-07-23 17:27:54 +0200150static void term_report_winsize(term_T *term, int rows, int cols);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200151static void term_free_vterm(term_T *term);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200152
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200153/**************************************
154 * 1. Generic code for all systems.
155 */
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200156
157/*
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200158 * Determine the terminal size from 'termsize' and the current window.
159 * Assumes term->tl_rows and term->tl_cols are zero.
160 */
161 static void
162set_term_and_win_size(term_T *term)
163{
164 if (*curwin->w_p_tms != NUL)
165 {
166 char_u *p = vim_strchr(curwin->w_p_tms, 'x') + 1;
167
168 term->tl_rows = atoi((char *)curwin->w_p_tms);
169 term->tl_cols = atoi((char *)p);
170 }
171 if (term->tl_rows == 0)
172 term->tl_rows = curwin->w_height;
173 else
174 {
175 win_setheight_win(term->tl_rows, curwin);
176 term->tl_rows_fixed = TRUE;
177 }
178 if (term->tl_cols == 0)
179 term->tl_cols = curwin->w_width;
180 else
181 {
182 win_setwidth_win(term->tl_cols, curwin);
183 term->tl_cols_fixed = TRUE;
184 }
185}
186
187/*
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200188 * Initialize job options for a terminal job.
189 * Caller may overrule some of them.
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200190 */
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200191 static void
192init_job_options(jobopt_T *opt)
193{
194 clear_job_options(opt);
195
196 opt->jo_mode = MODE_RAW;
197 opt->jo_out_mode = MODE_RAW;
198 opt->jo_err_mode = MODE_RAW;
199 opt->jo_set = JO_MODE | JO_OUT_MODE | JO_ERR_MODE;
200
201 opt->jo_io[PART_OUT] = JIO_BUFFER;
202 opt->jo_io[PART_ERR] = JIO_BUFFER;
203 opt->jo_set |= JO_OUT_IO + JO_ERR_IO;
204
205 opt->jo_modifiable[PART_OUT] = 0;
206 opt->jo_modifiable[PART_ERR] = 0;
207 opt->jo_set |= JO_OUT_MODIFIABLE + JO_ERR_MODIFIABLE;
208
209 opt->jo_set |= JO_OUT_BUF + JO_ERR_BUF;
210}
211
212/*
213 * Set job options mandatory for a terminal job.
214 */
215 static void
216setup_job_options(jobopt_T *opt, int rows, int cols)
217{
218 opt->jo_io_buf[PART_OUT] = curbuf->b_fnum;
219 opt->jo_io_buf[PART_ERR] = curbuf->b_fnum;
220 opt->jo_pty = TRUE;
221 opt->jo_term_rows = rows;
222 opt->jo_term_cols = cols;
223}
224
225 static void
226term_start(char_u *cmd, jobopt_T *opt)
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200227{
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200228 exarg_T split_ea;
229 win_T *old_curwin = curwin;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200230 term_T *term;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200231
232 if (check_restricted() || check_secure())
233 return;
234
235 term = (term_T *)alloc_clear(sizeof(term_T));
236 if (term == NULL)
237 return;
238 term->tl_dirty_row_end = MAX_ROW;
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200239 term->tl_cursor_visible = TRUE;
Bram Moolenaard85f2712017-07-28 21:51:57 +0200240 ga_init2(&term->tl_scrollback, sizeof(sb_line_T), 300);
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200241
242 /* Open a new window or tab. */
243 vim_memset(&split_ea, 0, sizeof(split_ea));
244 split_ea.cmdidx = CMD_new;
245 split_ea.cmd = (char_u *)"new";
246 split_ea.arg = (char_u *)"";
247 ex_splitview(&split_ea);
248 if (curwin == old_curwin)
249 {
250 /* split failed */
251 vim_free(term);
252 return;
253 }
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200254 term->tl_buffer = curbuf;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200255 curbuf->b_term = term;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200256
257 /* Link the new terminal in the list of active terminals. */
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200258 term->tl_next = first_term;
259 first_term = term;
260
Bram Moolenaar293424c2017-07-26 23:11:01 +0200261 if (cmd == NULL || *cmd == NUL)
262 cmd = p_sh;
263
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200264 {
265 int i;
266 size_t len = STRLEN(cmd) + 10;
Bram Moolenaara1b5b092017-07-26 21:29:34 +0200267 char_u *p = alloc((int)len);
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200268
Bram Moolenaar20e6cd02017-08-01 20:25:22 +0200269 for (i = 0; p != NULL; ++i)
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200270 {
Bram Moolenaar20e6cd02017-08-01 20:25:22 +0200271 /* Prepend a ! to the command name to avoid the buffer name equals
272 * the executable, otherwise ":w!" would overwrite it. */
273 if (i == 0)
274 vim_snprintf((char *)p, len, "!%s", cmd);
275 else
276 vim_snprintf((char *)p, len, "!%s (%d)", cmd, i);
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200277 if (buflist_findname(p) == NULL)
278 {
279 curbuf->b_ffname = p;
280 break;
281 }
282 }
283 }
284 curbuf->b_fname = curbuf->b_ffname;
285
Bram Moolenaareb44a682017-08-03 22:44:55 +0200286 set_string_option_direct((char_u *)"buftype", -1,
287 (char_u *)"terminal", OPT_FREE|OPT_LOCAL, 0);
288
Bram Moolenaar20e6cd02017-08-01 20:25:22 +0200289 /* Mark the buffer as not modifiable. It can only be made modifiable after
290 * the job finished. */
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200291 curbuf->b_p_ma = FALSE;
Bram Moolenaareb44a682017-08-03 22:44:55 +0200292
293 /* Set 'bufhidden' to "hide": allow closing the window. */
294 set_string_option_direct((char_u *)"bufhidden", -1,
295 (char_u *)"hide", OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200296
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200297 set_term_and_win_size(term);
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200298 setup_job_options(opt, term->tl_rows, term->tl_cols);
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200299
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200300 /* System dependent: setup the vterm and start the job in it. */
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200301 if (term_and_job_init(term, term->tl_rows, term->tl_cols, cmd, opt) == OK)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200302 {
303 /* store the size we ended up with */
304 vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols);
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200305 }
306 else
307 {
Bram Moolenaard85f2712017-07-28 21:51:57 +0200308 free_terminal(curbuf);
Bram Moolenaar61a66052017-07-22 18:39:00 +0200309
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200310 /* Wiping out the buffer will also close the window and call
311 * free_terminal(). */
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200312 do_buffer(DOBUF_WIPE, DOBUF_CURRENT, FORWARD, 0, TRUE);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200313 }
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200314}
315
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200316/*
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200317 * ":terminal": open a terminal window and execute a job in it.
318 */
319 void
320ex_terminal(exarg_T *eap)
321{
322 jobopt_T opt;
323
324 init_job_options(&opt);
325 /* TODO: get options from before the command */
326
327 term_start(eap->arg, &opt);
328}
329
330/*
Bram Moolenaar63ecdda2017-07-28 22:29:35 +0200331 * Free the scrollback buffer for "term".
332 */
333 static void
334free_scrollback(term_T *term)
335{
336 int i;
337
338 for (i = 0; i < term->tl_scrollback.ga_len; ++i)
339 vim_free(((sb_line_T *)term->tl_scrollback.ga_data + i)->sb_cells);
340 ga_clear(&term->tl_scrollback);
341}
342
343/*
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200344 * Free a terminal and everything it refers to.
345 * Kills the job if there is one.
346 * Called when wiping out a buffer.
347 */
348 void
Bram Moolenaard85f2712017-07-28 21:51:57 +0200349free_terminal(buf_T *buf)
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200350{
Bram Moolenaard85f2712017-07-28 21:51:57 +0200351 term_T *term = buf->b_term;
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200352 term_T *tp;
353
354 if (term == NULL)
355 return;
356 if (first_term == term)
357 first_term = term->tl_next;
358 else
359 for (tp = first_term; tp->tl_next != NULL; tp = tp->tl_next)
360 if (tp->tl_next == term)
361 {
362 tp->tl_next = term->tl_next;
363 break;
364 }
365
366 if (term->tl_job != NULL)
367 {
Bram Moolenaar61a66052017-07-22 18:39:00 +0200368 if (term->tl_job->jv_status != JOB_ENDED
369 && term->tl_job->jv_status != JOB_FAILED)
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200370 job_stop(term->tl_job, NULL, "kill");
371 job_unref(term->tl_job);
372 }
373
Bram Moolenaar63ecdda2017-07-28 22:29:35 +0200374 free_scrollback(term);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200375
376 term_free_vterm(term);
Bram Moolenaar21554412017-07-24 21:44:43 +0200377 vim_free(term->tl_title);
378 vim_free(term->tl_status_text);
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200379 vim_free(term);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200380 buf->b_term = NULL;
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200381}
382
383/*
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200384 * Write job output "msg[len]" to the vterm.
385 */
386 static void
387term_write_job_output(term_T *term, char_u *msg, size_t len)
388{
389 VTerm *vterm = term->tl_vterm;
390 char_u *p;
391 size_t done;
392 size_t len_now;
393
394 for (done = 0; done < len; done += len_now)
395 {
396 for (p = msg + done; p < msg + len; )
397 {
398 if (*p == NL)
399 break;
Bram Moolenaara1b5b092017-07-26 21:29:34 +0200400 p += utf_ptr2len_len(p, (int)(len - (p - msg)));
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200401 }
402 len_now = p - msg - done;
403 vterm_input_write(vterm, (char *)msg + done, len_now);
404 if (p < msg + len && *p == NL)
405 {
406 /* Convert NL to CR-NL, that appears to work best. */
407 vterm_input_write(vterm, "\r\n", 2);
408 ++len_now;
409 }
410 }
411
412 /* this invokes the damage callbacks */
413 vterm_screen_flush_damage(vterm_obtain_screen(vterm));
414}
415
Bram Moolenaar1c844932017-07-24 23:36:41 +0200416 static void
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200417update_cursor(term_T *term, int redraw)
Bram Moolenaar1c844932017-07-24 23:36:41 +0200418{
Bram Moolenaar392d1bf2017-07-31 21:18:58 +0200419 if (term->tl_terminal_mode)
420 return;
Bram Moolenaar1c844932017-07-24 23:36:41 +0200421 setcursor();
Bram Moolenaar4cc93dc2017-07-26 21:49:37 +0200422 if (redraw && term->tl_buffer == curbuf)
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200423 {
Bram Moolenaar4cc93dc2017-07-26 21:49:37 +0200424 if (term->tl_cursor_visible)
425 cursor_on();
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200426 out_flush();
Bram Moolenaar1c844932017-07-24 23:36:41 +0200427#ifdef FEAT_GUI
Bram Moolenaar12d93ee2017-07-30 19:02:02 +0200428 if (gui.in_use)
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200429 gui_update_cursor(FALSE, FALSE);
Bram Moolenaar1c844932017-07-24 23:36:41 +0200430#endif
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200431 }
Bram Moolenaar1c844932017-07-24 23:36:41 +0200432}
433
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200434/*
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200435 * Invoked when "msg" output from a job was received. Write it to the terminal
436 * of "buffer".
437 */
438 void
439write_to_term(buf_T *buffer, char_u *msg, channel_T *channel)
440{
441 size_t len = STRLEN(msg);
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200442 term_T *term = buffer->b_term;
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200443
Bram Moolenaard85f2712017-07-28 21:51:57 +0200444 if (term->tl_vterm == NULL)
445 {
Bram Moolenaar2f3a90a2017-08-03 14:49:29 +0200446 ch_log(channel, "NOT writing %d bytes to terminal", (int)len);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200447 return;
448 }
Bram Moolenaar2f3a90a2017-08-03 14:49:29 +0200449 ch_log(channel, "writing %d bytes to terminal", (int)len);
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200450 term_write_job_output(term, msg, len);
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200451
Bram Moolenaar392d1bf2017-07-31 21:18:58 +0200452 if (!term->tl_terminal_mode)
453 {
454 /* TODO: only update once in a while. */
455 update_screen(0);
456 update_cursor(term, TRUE);
457 }
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200458}
459
460/*
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200461 * Send a mouse position and click to the vterm
462 */
463 static int
464term_send_mouse(VTerm *vterm, int button, int pressed)
465{
466 VTermModifier mod = VTERM_MOD_NONE;
467
468 vterm_mouse_move(vterm, mouse_row - W_WINROW(curwin),
469 mouse_col - W_WINCOL(curwin), mod);
470 vterm_mouse_button(vterm, button, pressed, mod);
471 return TRUE;
472}
473
474/*
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200475 * Convert typed key "c" into bytes to send to the job.
476 * Return the number of bytes in "buf".
477 */
478 static int
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200479term_convert_key(term_T *term, int c, char *buf)
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200480{
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200481 VTerm *vterm = term->tl_vterm;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200482 VTermKey key = VTERM_KEY_NONE;
483 VTermModifier mod = VTERM_MOD_NONE;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200484 int mouse = FALSE;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200485
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200486 switch (c)
487 {
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200488 case CAR: key = VTERM_KEY_ENTER; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200489 case ESC: key = VTERM_KEY_ESCAPE; break;
Bram Moolenaare906ae82017-07-21 21:10:01 +0200490 /* VTERM_KEY_BACKSPACE becomes 0x7f DEL */
491 case K_BS: c = BS; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200492 case K_DEL: key = VTERM_KEY_DEL; break;
493 case K_DOWN: key = VTERM_KEY_DOWN; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200494 case K_S_DOWN: mod = VTERM_MOD_SHIFT;
495 key = VTERM_KEY_DOWN; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200496 case K_END: key = VTERM_KEY_END; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200497 case K_S_END: mod = VTERM_MOD_SHIFT;
498 key = VTERM_KEY_END; break;
499 case K_C_END: mod = VTERM_MOD_CTRL;
500 key = VTERM_KEY_END; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200501 case K_F10: key = VTERM_KEY_FUNCTION(10); break;
502 case K_F11: key = VTERM_KEY_FUNCTION(11); break;
503 case K_F12: key = VTERM_KEY_FUNCTION(12); break;
504 case K_F1: key = VTERM_KEY_FUNCTION(1); break;
505 case K_F2: key = VTERM_KEY_FUNCTION(2); break;
506 case K_F3: key = VTERM_KEY_FUNCTION(3); break;
507 case K_F4: key = VTERM_KEY_FUNCTION(4); break;
508 case K_F5: key = VTERM_KEY_FUNCTION(5); break;
509 case K_F6: key = VTERM_KEY_FUNCTION(6); break;
510 case K_F7: key = VTERM_KEY_FUNCTION(7); break;
511 case K_F8: key = VTERM_KEY_FUNCTION(8); break;
512 case K_F9: key = VTERM_KEY_FUNCTION(9); break;
513 case K_HOME: key = VTERM_KEY_HOME; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200514 case K_S_HOME: mod = VTERM_MOD_SHIFT;
515 key = VTERM_KEY_HOME; break;
516 case K_C_HOME: mod = VTERM_MOD_CTRL;
517 key = VTERM_KEY_HOME; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200518 case K_INS: key = VTERM_KEY_INS; break;
519 case K_K0: key = VTERM_KEY_KP_0; break;
520 case K_K1: key = VTERM_KEY_KP_1; break;
521 case K_K2: key = VTERM_KEY_KP_2; break;
522 case K_K3: key = VTERM_KEY_KP_3; break;
523 case K_K4: key = VTERM_KEY_KP_4; break;
524 case K_K5: key = VTERM_KEY_KP_5; break;
525 case K_K6: key = VTERM_KEY_KP_6; break;
526 case K_K7: key = VTERM_KEY_KP_7; break;
527 case K_K8: key = VTERM_KEY_KP_8; break;
528 case K_K9: key = VTERM_KEY_KP_9; break;
529 case K_KDEL: key = VTERM_KEY_DEL; break; /* TODO */
530 case K_KDIVIDE: key = VTERM_KEY_KP_DIVIDE; break;
531 case K_KEND: key = VTERM_KEY_KP_1; break; /* TODO */
532 case K_KENTER: key = VTERM_KEY_KP_ENTER; break;
533 case K_KHOME: key = VTERM_KEY_KP_7; break; /* TODO */
534 case K_KINS: key = VTERM_KEY_KP_0; break; /* TODO */
535 case K_KMINUS: key = VTERM_KEY_KP_MINUS; break;
536 case K_KMULTIPLY: key = VTERM_KEY_KP_MULT; break;
537 case K_KPAGEDOWN: key = VTERM_KEY_KP_3; break; /* TODO */
538 case K_KPAGEUP: key = VTERM_KEY_KP_9; break; /* TODO */
539 case K_KPLUS: key = VTERM_KEY_KP_PLUS; break;
540 case K_KPOINT: key = VTERM_KEY_KP_PERIOD; break;
541 case K_LEFT: key = VTERM_KEY_LEFT; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200542 case K_S_LEFT: mod = VTERM_MOD_SHIFT;
543 key = VTERM_KEY_LEFT; break;
544 case K_C_LEFT: mod = VTERM_MOD_CTRL;
545 key = VTERM_KEY_LEFT; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200546 case K_PAGEDOWN: key = VTERM_KEY_PAGEDOWN; break;
547 case K_PAGEUP: key = VTERM_KEY_PAGEUP; break;
548 case K_RIGHT: key = VTERM_KEY_RIGHT; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200549 case K_S_RIGHT: mod = VTERM_MOD_SHIFT;
550 key = VTERM_KEY_RIGHT; break;
551 case K_C_RIGHT: mod = VTERM_MOD_CTRL;
552 key = VTERM_KEY_RIGHT; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200553 case K_UP: key = VTERM_KEY_UP; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200554 case K_S_UP: mod = VTERM_MOD_SHIFT;
555 key = VTERM_KEY_UP; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200556 case TAB: key = VTERM_KEY_TAB; break;
Bram Moolenaare825d8b2017-07-19 23:20:19 +0200557
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200558 case K_MOUSEUP: mouse = term_send_mouse(vterm, 5, 1); break;
559 case K_MOUSEDOWN: mouse = term_send_mouse(vterm, 4, 1); break;
560 case K_MOUSELEFT: /* TODO */ return 0;
561 case K_MOUSERIGHT: /* TODO */ return 0;
Bram Moolenaare825d8b2017-07-19 23:20:19 +0200562
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200563 case K_LEFTMOUSE:
564 case K_LEFTMOUSE_NM: mouse = term_send_mouse(vterm, 1, 1); break;
565 case K_LEFTDRAG: mouse = term_send_mouse(vterm, 1, 1); break;
566 case K_LEFTRELEASE:
567 case K_LEFTRELEASE_NM: mouse = term_send_mouse(vterm, 1, 0); break;
568 case K_MIDDLEMOUSE: mouse = term_send_mouse(vterm, 2, 1); break;
569 case K_MIDDLEDRAG: mouse = term_send_mouse(vterm, 2, 1); break;
570 case K_MIDDLERELEASE: mouse = term_send_mouse(vterm, 2, 0); break;
571 case K_RIGHTMOUSE: mouse = term_send_mouse(vterm, 3, 1); break;
572 case K_RIGHTDRAG: mouse = term_send_mouse(vterm, 3, 1); break;
573 case K_RIGHTRELEASE: mouse = term_send_mouse(vterm, 3, 0); break;
574 case K_X1MOUSE: /* TODO */ return 0;
575 case K_X1DRAG: /* TODO */ return 0;
576 case K_X1RELEASE: /* TODO */ return 0;
577 case K_X2MOUSE: /* TODO */ return 0;
578 case K_X2DRAG: /* TODO */ return 0;
579 case K_X2RELEASE: /* TODO */ return 0;
Bram Moolenaare825d8b2017-07-19 23:20:19 +0200580
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200581 case K_IGNORE: return 0;
582 case K_NOP: return 0;
583 case K_UNDO: return 0;
584 case K_HELP: return 0;
585 case K_XF1: key = VTERM_KEY_FUNCTION(1); break;
586 case K_XF2: key = VTERM_KEY_FUNCTION(2); break;
587 case K_XF3: key = VTERM_KEY_FUNCTION(3); break;
588 case K_XF4: key = VTERM_KEY_FUNCTION(4); break;
589 case K_SELECT: return 0;
590#ifdef FEAT_GUI
591 case K_VER_SCROLLBAR: return 0;
592 case K_HOR_SCROLLBAR: return 0;
593#endif
594#ifdef FEAT_GUI_TABLINE
595 case K_TABLINE: return 0;
596 case K_TABMENU: return 0;
597#endif
598#ifdef FEAT_NETBEANS_INTG
599 case K_F21: key = VTERM_KEY_FUNCTION(21); break;
600#endif
601#ifdef FEAT_DND
602 case K_DROP: return 0;
603#endif
604#ifdef FEAT_AUTOCMD
605 case K_CURSORHOLD: return 0;
606#endif
607 case K_PS: vterm_keyboard_start_paste(vterm); return 0;
608 case K_PE: vterm_keyboard_end_paste(vterm); return 0;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200609 }
610
611 /*
612 * Convert special keys to vterm keys:
613 * - Write keys to vterm: vterm_keyboard_key()
614 * - Write output to channel.
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200615 * TODO: use mod_mask
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200616 */
617 if (key != VTERM_KEY_NONE)
618 /* Special key, let vterm convert it. */
619 vterm_keyboard_key(vterm, key, mod);
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200620 else if (!mouse)
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200621 /* Normal character, let vterm convert it. */
622 vterm_keyboard_unichar(vterm, c, mod);
623
624 /* Read back the converted escape sequence. */
Bram Moolenaara1b5b092017-07-26 21:29:34 +0200625 return (int)vterm_output_read(vterm, buf, KEY_BUF_LEN);
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200626}
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200627
Bram Moolenaar938783d2017-07-16 20:13:26 +0200628/*
Bram Moolenaarb000e322017-07-30 19:38:21 +0200629 * Return TRUE if the job for "term" is still running.
Bram Moolenaard85f2712017-07-28 21:51:57 +0200630 */
Bram Moolenaar94053a52017-08-01 21:44:33 +0200631 int
Bram Moolenaard85f2712017-07-28 21:51:57 +0200632term_job_running(term_T *term)
633{
Bram Moolenaar1e8340b2017-07-29 15:53:39 +0200634 /* Also consider the job finished when the channel is closed, to avoid a
635 * race condition when updating the title. */
Bram Moolenaarb4a67212017-08-03 19:22:36 +0200636 return term != NULL
637 && term->tl_job != NULL
Bram Moolenaar1e8340b2017-07-29 15:53:39 +0200638 && term->tl_job->jv_status == JOB_STARTED
639 && channel_is_open(term->tl_job->jv_channel);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200640}
641
642/*
Bram Moolenaar423802d2017-07-30 16:52:24 +0200643 * Add the last line of the scrollback buffer to the buffer in the window.
644 */
645 static void
646add_scrollback_line_to_buffer(term_T *term)
647{
648 linenr_T lnum = term->tl_scrollback.ga_len - 1;
649 sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data + lnum;
650 garray_T ga;
651 int c;
652 int col;
653 int i;
654
655 ga_init2(&ga, 1, 100);
656 for (col = 0; col < line->sb_cols; col += line->sb_cells[col].width)
657 {
658 if (ga_grow(&ga, MB_MAXBYTES) == FAIL)
659 goto failed;
660 for (i = 0; (c = line->sb_cells[col].chars[i]) > 0 || i == 0; ++i)
661 ga.ga_len += mb_char2bytes(c == NUL ? ' ' : c,
662 (char_u *)ga.ga_data + ga.ga_len);
663 }
664 if (ga_grow(&ga, 1) == FAIL)
665 goto failed;
666 *((char_u *)ga.ga_data + ga.ga_len) = NUL;
667 ml_append_buf(term->tl_buffer, lnum, ga.ga_data, ga.ga_len + 1, FALSE);
668
669 if (lnum == 0)
670 {
671 /* Delete the empty line that was in the empty buffer. */
672 curbuf = term->tl_buffer;
673 ml_delete(2, FALSE);
674 curbuf = curwin->w_buffer;
675 }
676
677failed:
678 ga_clear(&ga);
679}
680
681/*
682 * Add the current lines of the terminal to scrollback and to the buffer.
683 * Called after the job has ended and when switching to Terminal mode.
684 */
685 static void
686move_terminal_to_buffer(term_T *term)
687{
688 win_T *wp;
689 int len;
690 int lines_skipped = 0;
691 VTermPos pos;
692 VTermScreenCell cell;
693 VTermScreenCell *p;
Bram Moolenaar8e5eece2017-08-04 20:29:53 +0200694 VTermScreen *screen;
Bram Moolenaar423802d2017-07-30 16:52:24 +0200695
Bram Moolenaar8e5eece2017-08-04 20:29:53 +0200696 if (term->tl_vterm == NULL)
697 return;
698 screen = vterm_obtain_screen(term->tl_vterm);
Bram Moolenaar423802d2017-07-30 16:52:24 +0200699 for (pos.row = 0; pos.row < term->tl_rows; ++pos.row)
700 {
701 len = 0;
702 for (pos.col = 0; pos.col < term->tl_cols; ++pos.col)
703 if (vterm_screen_get_cell(screen, pos, &cell) != 0
704 && cell.chars[0] != NUL)
705 len = pos.col + 1;
706
707 if (len == 0)
708 ++lines_skipped;
709 else
710 {
711 while (lines_skipped > 0)
712 {
713 /* Line was skipped, add an empty line. */
714 --lines_skipped;
715 if (ga_grow(&term->tl_scrollback, 1) == OK)
716 {
717 sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data
718 + term->tl_scrollback.ga_len;
719
720 line->sb_cols = 0;
721 line->sb_cells = NULL;
722 ++term->tl_scrollback.ga_len;
723
724 add_scrollback_line_to_buffer(term);
725 }
726 }
727
728 p = (VTermScreenCell *)alloc((int)sizeof(VTermScreenCell) * len);
729 if (p != NULL && ga_grow(&term->tl_scrollback, 1) == OK)
730 {
731 sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data
732 + term->tl_scrollback.ga_len;
733
734 for (pos.col = 0; pos.col < len; ++pos.col)
735 {
736 if (vterm_screen_get_cell(screen, pos, &cell) == 0)
737 vim_memset(p + pos.col, 0, sizeof(cell));
738 else
739 p[pos.col] = cell;
740 }
741 line->sb_cols = len;
742 line->sb_cells = p;
743 ++term->tl_scrollback.ga_len;
744
745 add_scrollback_line_to_buffer(term);
746 }
747 else
748 vim_free(p);
749 }
750 }
751
752 FOR_ALL_WINDOWS(wp)
753 {
754 if (wp->w_buffer == term->tl_buffer)
755 {
756 wp->w_cursor.lnum = term->tl_buffer->b_ml.ml_line_count;
757 wp->w_cursor.col = 0;
758 wp->w_valid = 0;
759 redraw_win_later(wp, NOT_VALID);
760 }
761 }
762}
763
764 static void
765set_terminal_mode(term_T *term, int on)
766{
767 term->tl_terminal_mode = on;
768 vim_free(term->tl_status_text);
769 term->tl_status_text = NULL;
770 if (term->tl_buffer == curbuf)
771 maketitle();
772}
773
774/*
775 * Called after the job if finished and Terminal mode is not active:
776 * Move the vterm contents into the scrollback buffer and free the vterm.
777 */
778 static void
779cleanup_vterm(term_T *term)
780{
781 move_terminal_to_buffer(term);
782 term_free_vterm(term);
783 set_terminal_mode(term, FALSE);
784}
785
786/*
787 * Switch from sending keys to the job to Terminal-Normal mode.
788 * Suspends updating the terminal window.
789 */
790 static void
791term_enter_terminal_mode()
792{
793 term_T *term = curbuf->b_term;
794
795 /* Append the current terminal contents to the buffer. */
796 move_terminal_to_buffer(term);
797
798 set_terminal_mode(term, TRUE);
799}
800
801/*
802 * Returns TRUE if the current window contains a terminal and we are in
803 * Terminal-Normal mode.
804 */
805 int
806term_in_terminal_mode()
807{
808 term_T *term = curbuf->b_term;
809
810 return term != NULL && term->tl_terminal_mode;
811}
812
813/*
814 * Switch from Terminal-Normal mode to sending keys to the job.
815 * Restores updating the terminal window.
816 */
817 void
818term_leave_terminal_mode()
819{
820 term_T *term = curbuf->b_term;
821 sb_line_T *line;
822 garray_T *gap;
823
824 /* Remove the terminal contents from the scrollback and the buffer. */
825 gap = &term->tl_scrollback;
826 while (curbuf->b_ml.ml_line_count > term->tl_scrollback_scrolled)
827 {
828 ml_delete(curbuf->b_ml.ml_line_count, FALSE);
829 line = (sb_line_T *)gap->ga_data + gap->ga_len - 1;
830 vim_free(line->sb_cells);
831 --gap->ga_len;
832 if (gap->ga_len == 0)
833 break;
834 }
835 check_cursor();
836
837 set_terminal_mode(term, FALSE);
838
839 if (term->tl_channel_closed)
840 cleanup_vterm(term);
841 redraw_buf_and_status_later(curbuf, NOT_VALID);
842}
843
844/*
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +0200845 * Get a key from the user without mapping.
846 * TODO: use terminal mode mappings.
847 */
848 static int
849term_vgetc()
850{
851 int c;
852
853 ++no_mapping;
854 ++allow_keys;
855 got_int = FALSE;
856 c = vgetc();
Bram Moolenaar43c007f2017-07-30 17:45:37 +0200857 got_int = FALSE;
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +0200858 --no_mapping;
859 --allow_keys;
860 return c;
861}
862
863/*
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200864 * Send keys to terminal.
865 */
866 static int
867send_keys_to_term(term_T *term, int c, int typed)
868{
869 char msg[KEY_BUF_LEN];
870 size_t len;
871 static int mouse_was_outside = FALSE;
872 int dragging_outside = FALSE;
873
874 /* Catch keys that need to be handled as in Normal mode. */
875 switch (c)
876 {
877 case NUL:
878 case K_ZERO:
879 if (typed)
880 stuffcharReadbuff(c);
881 return FAIL;
882
883 case K_IGNORE:
884 return FAIL;
885
886 case K_LEFTDRAG:
887 case K_MIDDLEDRAG:
888 case K_RIGHTDRAG:
889 case K_X1DRAG:
890 case K_X2DRAG:
891 dragging_outside = mouse_was_outside;
892 /* FALLTHROUGH */
893 case K_LEFTMOUSE:
894 case K_LEFTMOUSE_NM:
895 case K_LEFTRELEASE:
896 case K_LEFTRELEASE_NM:
897 case K_MIDDLEMOUSE:
898 case K_MIDDLERELEASE:
899 case K_RIGHTMOUSE:
900 case K_RIGHTRELEASE:
901 case K_X1MOUSE:
902 case K_X1RELEASE:
903 case K_X2MOUSE:
904 case K_X2RELEASE:
905 if (mouse_row < W_WINROW(curwin)
906 || mouse_row >= (W_WINROW(curwin) + curwin->w_height)
907 || mouse_col < W_WINCOL(curwin)
908 || mouse_col >= W_ENDCOL(curwin)
909 || dragging_outside)
910 {
911 /* click outside the current window */
912 if (typed)
913 {
914 stuffcharReadbuff(c);
915 mouse_was_outside = TRUE;
916 }
917 return FAIL;
918 }
919 }
920 if (typed)
921 mouse_was_outside = FALSE;
922
923 /* Convert the typed key to a sequence of bytes for the job. */
924 len = term_convert_key(term, c, msg);
925 if (len > 0)
926 /* TODO: if FAIL is returned, stop? */
927 channel_send(term->tl_job->jv_channel, PART_IN,
928 (char_u *)msg, (int)len, NULL);
929
930 return OK;
931}
932
Bram Moolenaar0e23e9c2017-07-30 18:47:19 +0200933 static void
934position_cursor(win_T *wp, VTermPos *pos)
935{
936 wp->w_wrow = MIN(pos->row, MAX(0, wp->w_height - 1));
937 wp->w_wcol = MIN(pos->col, MAX(0, wp->w_width - 1));
938 wp->w_valid |= (VALID_WCOL|VALID_WROW);
939}
940
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200941/*
Bram Moolenaarc9456ce2017-07-30 21:46:04 +0200942 * Handle CTRL-W "": send register contents to the job.
943 */
944 static void
945term_paste_register(int prev_c UNUSED)
946{
947 int c;
948 list_T *l;
949 listitem_T *item;
950 long reglen = 0;
951 int type;
952
953#ifdef FEAT_CMDL_INFO
954 if (add_to_showcmd(prev_c))
955 if (add_to_showcmd('"'))
956 out_flush();
957#endif
958 c = term_vgetc();
959#ifdef FEAT_CMDL_INFO
960 clear_showcmd();
961#endif
962
963 /* CTRL-W "= prompt for expression to evaluate. */
964 if (c == '=' && get_expr_register() != '=')
965 return;
966
967 l = (list_T *)get_reg_contents(c, GREG_LIST);
968 if (l != NULL)
969 {
970 type = get_reg_type(c, &reglen);
971 for (item = l->lv_first; item != NULL; item = item->li_next)
972 {
973 char_u *s = get_tv_string(&item->li_tv);
974
975 channel_send(curbuf->b_term->tl_job->jv_channel, PART_IN,
976 s, STRLEN(s), NULL);
977 if (item->li_next != NULL || type == MLINE)
978 channel_send(curbuf->b_term->tl_job->jv_channel, PART_IN,
979 (char_u *)"\r", 1, NULL);
980 }
981 list_free(l);
982 }
983}
984
985/*
Bram Moolenaar423802d2017-07-30 16:52:24 +0200986 * Returns TRUE if the current window contains a terminal and we are sending
987 * keys to the job.
988 */
989 int
990term_use_loop()
991{
992 term_T *term = curbuf->b_term;
993
994 return term != NULL
995 && !term->tl_terminal_mode
996 && term->tl_vterm != NULL
997 && term_job_running(term);
998}
999
1000/*
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001001 * Wait for input and send it to the job.
1002 * Return when the start of a CTRL-W command is typed or anything else that
1003 * should be handled as a Normal mode command.
Bram Moolenaard85f2712017-07-28 21:51:57 +02001004 * Returns OK if a typed character is to be handled in Normal mode, FAIL if
1005 * the terminal was closed.
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001006 */
Bram Moolenaard85f2712017-07-28 21:51:57 +02001007 int
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001008terminal_loop(void)
1009{
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001010 int c;
Bram Moolenaardbe948d2017-07-23 22:50:51 +02001011 int termkey = 0;
1012
1013 if (*curwin->w_p_tk != NUL)
1014 termkey = string_to_key(curwin->w_p_tk, TRUE);
Bram Moolenaar0e23e9c2017-07-30 18:47:19 +02001015 position_cursor(curwin, &curbuf->b_term->tl_cursor_pos);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001016
1017 for (;;)
1018 {
1019 /* TODO: skip screen update when handling a sequence of keys. */
Bram Moolenaar43c007f2017-07-30 17:45:37 +02001020 /* Repeat redrawing in case a message is received while redrawing. */
1021 while (curwin->w_redr_type != 0)
1022 update_screen(0);
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001023 update_cursor(curbuf->b_term, FALSE);
Bram Moolenaar423802d2017-07-30 16:52:24 +02001024
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001025 c = term_vgetc();
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02001026 if (!term_use_loop())
Bram Moolenaard85f2712017-07-28 21:51:57 +02001027 /* job finished while waiting for a character */
1028 break;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001029
Bram Moolenaarfae42832017-08-01 22:24:26 +02001030#ifdef UNIX
1031 may_send_sigint(c, curbuf->b_term->tl_job->jv_pid, 0);
1032#endif
1033#ifdef WIN3264
1034 if (c == Ctrl_C)
1035 /* We don't know if the job can handle CTRL-C itself or not, this
1036 * may kill the shell instead of killing the command running in the
1037 * shell. */
Bram Moolenaard8dc1792017-08-03 11:55:21 +02001038 mch_stop_job(curbuf->b_term->tl_job, (char_u *)"quit");
Bram Moolenaarfae42832017-08-01 22:24:26 +02001039#endif
1040
Bram Moolenaardbe948d2017-07-23 22:50:51 +02001041 if (c == (termkey == 0 ? Ctrl_W : termkey))
1042 {
Bram Moolenaarc9456ce2017-07-30 21:46:04 +02001043 int prev_c = c;
1044
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001045#ifdef FEAT_CMDL_INFO
1046 if (add_to_showcmd(c))
1047 out_flush();
1048#endif
1049 c = term_vgetc();
1050#ifdef FEAT_CMDL_INFO
1051 clear_showcmd();
1052#endif
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02001053 if (!term_use_loop())
Bram Moolenaard85f2712017-07-28 21:51:57 +02001054 /* job finished while waiting for a character */
1055 break;
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001056
1057 if (termkey == 0 && c == '.')
Bram Moolenaar423802d2017-07-30 16:52:24 +02001058 {
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001059 /* "CTRL-W .": send CTRL-W to the job */
1060 c = Ctrl_W;
Bram Moolenaar423802d2017-07-30 16:52:24 +02001061 }
Bram Moolenaarc9456ce2017-07-30 21:46:04 +02001062 else if (c == 'N')
Bram Moolenaar423802d2017-07-30 16:52:24 +02001063 {
1064 term_enter_terminal_mode();
1065 return FAIL;
1066 }
Bram Moolenaarc9456ce2017-07-30 21:46:04 +02001067 else if (c == '"')
1068 {
1069 term_paste_register(prev_c);
1070 continue;
1071 }
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001072 else if (termkey == 0 || c != termkey)
1073 {
1074 stuffcharReadbuff(Ctrl_W);
1075 stuffcharReadbuff(c);
Bram Moolenaard85f2712017-07-28 21:51:57 +02001076 return OK;
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001077 }
Bram Moolenaardbe948d2017-07-23 22:50:51 +02001078 }
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001079 if (send_keys_to_term(curbuf->b_term, c, TRUE) != OK)
1080 return OK;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001081 }
Bram Moolenaard85f2712017-07-28 21:51:57 +02001082 return FAIL;
Bram Moolenaar1f2903c2017-07-23 19:51:01 +02001083}
1084
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001085/*
1086 * Called when a job has finished.
Bram Moolenaar423802d2017-07-30 16:52:24 +02001087 * This updates the title and status, but does not close the vter, because
1088 * there might still be pending output in the channel.
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001089 */
1090 void
1091term_job_ended(job_T *job)
1092{
1093 term_T *term;
1094 int did_one = FALSE;
1095
1096 for (term = first_term; term != NULL; term = term->tl_next)
1097 if (term->tl_job == job)
1098 {
1099 vim_free(term->tl_title);
1100 term->tl_title = NULL;
1101 vim_free(term->tl_status_text);
1102 term->tl_status_text = NULL;
1103 redraw_buf_and_status_later(term->tl_buffer, VALID);
1104 did_one = TRUE;
1105 }
1106 if (did_one)
1107 redraw_statuslines();
1108 if (curbuf->b_term != NULL)
1109 {
1110 if (curbuf->b_term->tl_job == job)
1111 maketitle();
1112 update_cursor(curbuf->b_term, TRUE);
1113 }
1114}
1115
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001116 static void
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001117may_toggle_cursor(term_T *term)
1118{
1119 if (curbuf == term->tl_buffer)
1120 {
1121 if (term->tl_cursor_visible)
1122 cursor_on();
1123 else
1124 cursor_off();
1125 }
1126}
1127
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001128 static int
1129handle_damage(VTermRect rect, void *user)
1130{
1131 term_T *term = (term_T *)user;
1132
1133 term->tl_dirty_row_start = MIN(term->tl_dirty_row_start, rect.start_row);
1134 term->tl_dirty_row_end = MAX(term->tl_dirty_row_end, rect.end_row);
1135 redraw_buf_later(term->tl_buffer, NOT_VALID);
1136 return 1;
1137}
1138
1139 static int
1140handle_moverect(VTermRect dest UNUSED, VTermRect src UNUSED, void *user)
1141{
1142 term_T *term = (term_T *)user;
1143
1144 /* TODO */
1145 redraw_buf_later(term->tl_buffer, NOT_VALID);
1146 return 1;
1147}
1148
1149 static int
1150handle_movecursor(
1151 VTermPos pos,
1152 VTermPos oldpos UNUSED,
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001153 int visible,
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001154 void *user)
1155{
1156 term_T *term = (term_T *)user;
1157 win_T *wp;
Bram Moolenaar22aad2f2017-07-30 18:19:46 +02001158
1159 term->tl_cursor_pos = pos;
1160 term->tl_cursor_visible = visible;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001161
1162 FOR_ALL_WINDOWS(wp)
1163 {
1164 if (wp->w_buffer == term->tl_buffer)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001165 position_cursor(wp, &pos);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001166 }
Bram Moolenaar392d1bf2017-07-31 21:18:58 +02001167 if (term->tl_buffer == curbuf && !term->tl_terminal_mode)
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001168 {
1169 may_toggle_cursor(term);
Bram Moolenaar12d93ee2017-07-30 19:02:02 +02001170 update_cursor(term, term->tl_cursor_visible);
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001171 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001172
1173 return 1;
1174}
1175
Bram Moolenaar21554412017-07-24 21:44:43 +02001176 static int
1177handle_settermprop(
1178 VTermProp prop,
1179 VTermValue *value,
1180 void *user)
1181{
1182 term_T *term = (term_T *)user;
1183
1184 switch (prop)
1185 {
1186 case VTERM_PROP_TITLE:
1187 vim_free(term->tl_title);
1188 term->tl_title = vim_strsave((char_u *)value->string);
1189 vim_free(term->tl_status_text);
1190 term->tl_status_text = NULL;
1191 if (term == curbuf->b_term)
1192 maketitle();
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001193 break;
1194
1195 case VTERM_PROP_CURSORVISIBLE:
1196 term->tl_cursor_visible = value->boolean;
1197 may_toggle_cursor(term);
1198 out_flush();
1199 break;
1200
Bram Moolenaar21554412017-07-24 21:44:43 +02001201 default:
1202 break;
1203 }
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001204 /* Always return 1, otherwise vterm doesn't store the value internally. */
1205 return 1;
Bram Moolenaar21554412017-07-24 21:44:43 +02001206}
1207
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001208/*
1209 * The job running in the terminal resized the terminal.
1210 */
1211 static int
1212handle_resize(int rows, int cols, void *user)
1213{
1214 term_T *term = (term_T *)user;
1215 win_T *wp;
1216
1217 term->tl_rows = rows;
1218 term->tl_cols = cols;
1219 FOR_ALL_WINDOWS(wp)
1220 {
1221 if (wp->w_buffer == term->tl_buffer)
1222 {
1223 win_setheight_win(rows, wp);
1224 win_setwidth_win(cols, wp);
1225 }
1226 }
1227
1228 redraw_buf_later(term->tl_buffer, NOT_VALID);
1229 return 1;
1230}
1231
Bram Moolenaard85f2712017-07-28 21:51:57 +02001232/*
1233 * Handle a line that is pushed off the top of the screen.
1234 */
1235 static int
1236handle_pushline(int cols, const VTermScreenCell *cells, void *user)
1237{
1238 term_T *term = (term_T *)user;
1239
1240 /* TODO: Limit the number of lines that are stored. */
1241 /* TODO: put the text in the buffer. */
1242 if (ga_grow(&term->tl_scrollback, 1) == OK)
1243 {
Bram Moolenaar696d00f2017-07-29 14:52:43 +02001244 VTermScreenCell *p = NULL;
1245 int len = 0;
1246 int i;
1247 sb_line_T *line;
Bram Moolenaard85f2712017-07-28 21:51:57 +02001248
1249 /* do not store empty cells at the end */
1250 for (i = 0; i < cols; ++i)
1251 if (cells[i].chars[0] != 0)
1252 len = i + 1;
1253
Bram Moolenaar696d00f2017-07-29 14:52:43 +02001254 if (len > 0)
1255 p = (VTermScreenCell *)alloc((int)sizeof(VTermScreenCell) * len);
Bram Moolenaard85f2712017-07-28 21:51:57 +02001256 if (p != NULL)
Bram Moolenaard85f2712017-07-28 21:51:57 +02001257 mch_memmove(p, cells, sizeof(VTermScreenCell) * len);
Bram Moolenaar696d00f2017-07-29 14:52:43 +02001258
1259 line = (sb_line_T *)term->tl_scrollback.ga_data
1260 + term->tl_scrollback.ga_len;
1261 line->sb_cols = len;
1262 line->sb_cells = p;
1263 ++term->tl_scrollback.ga_len;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001264 ++term->tl_scrollback_scrolled;
Bram Moolenaar423802d2017-07-30 16:52:24 +02001265
1266 add_scrollback_line_to_buffer(term);
Bram Moolenaard85f2712017-07-28 21:51:57 +02001267 }
1268 return 0; /* ignored */
1269}
1270
Bram Moolenaar21554412017-07-24 21:44:43 +02001271static VTermScreenCallbacks screen_callbacks = {
1272 handle_damage, /* damage */
1273 handle_moverect, /* moverect */
1274 handle_movecursor, /* movecursor */
1275 handle_settermprop, /* settermprop */
1276 NULL, /* bell */
1277 handle_resize, /* resize */
Bram Moolenaard85f2712017-07-28 21:51:57 +02001278 handle_pushline, /* sb_pushline */
Bram Moolenaar21554412017-07-24 21:44:43 +02001279 NULL /* sb_popline */
1280};
1281
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001282/*
Bram Moolenaard85f2712017-07-28 21:51:57 +02001283 * Called when a channel has been closed.
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001284 * If this was a channel for a terminal window then finish it up.
Bram Moolenaard85f2712017-07-28 21:51:57 +02001285 */
1286 void
1287term_channel_closed(channel_T *ch)
1288{
1289 term_T *term;
1290 int did_one = FALSE;
1291
1292 for (term = first_term; term != NULL; term = term->tl_next)
1293 if (term->tl_job == ch->ch_job)
1294 {
Bram Moolenaar423802d2017-07-30 16:52:24 +02001295 term->tl_channel_closed = TRUE;
1296
Bram Moolenaard85f2712017-07-28 21:51:57 +02001297 vim_free(term->tl_title);
1298 term->tl_title = NULL;
1299 vim_free(term->tl_status_text);
1300 term->tl_status_text = NULL;
1301
Bram Moolenaar423802d2017-07-30 16:52:24 +02001302 /* Unless in Terminal-Normal mode: clear the vterm. */
1303 if (!term->tl_terminal_mode)
1304 cleanup_vterm(term);
Bram Moolenaard85f2712017-07-28 21:51:57 +02001305
1306 redraw_buf_and_status_later(term->tl_buffer, NOT_VALID);
1307 did_one = TRUE;
1308 }
1309 if (did_one)
1310 {
1311 redraw_statuslines();
1312
1313 /* Need to break out of vgetc(). */
1314 ins_char_typebuf(K_IGNORE);
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001315 typebuf_was_filled = TRUE;
Bram Moolenaard85f2712017-07-28 21:51:57 +02001316
Bram Moolenaar12d93ee2017-07-30 19:02:02 +02001317 term = curbuf->b_term;
1318 if (term != NULL)
Bram Moolenaard85f2712017-07-28 21:51:57 +02001319 {
Bram Moolenaar12d93ee2017-07-30 19:02:02 +02001320 if (term->tl_job == ch->ch_job)
Bram Moolenaard85f2712017-07-28 21:51:57 +02001321 maketitle();
Bram Moolenaar12d93ee2017-07-30 19:02:02 +02001322 update_cursor(term, term->tl_cursor_visible);
Bram Moolenaard85f2712017-07-28 21:51:57 +02001323 }
1324 }
1325}
1326
1327/*
Bram Moolenaareeac6772017-07-23 15:48:37 +02001328 * Reverse engineer the RGB value into a cterm color index.
1329 * First color is 1. Return 0 if no match found.
1330 */
1331 static int
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001332color2index(VTermColor *color, int fg, int *boldp)
Bram Moolenaareeac6772017-07-23 15:48:37 +02001333{
1334 int red = color->red;
1335 int blue = color->blue;
1336 int green = color->green;
1337
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02001338 /* The argument for lookup_color() is for the color_names[] table. */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001339 if (red == 0)
1340 {
1341 if (green == 0)
1342 {
1343 if (blue == 0)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001344 return lookup_color(0, fg, boldp) + 1; /* black */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001345 if (blue == 224)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001346 return lookup_color(1, fg, boldp) + 1; /* dark blue */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001347 }
1348 else if (green == 224)
1349 {
1350 if (blue == 0)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001351 return lookup_color(2, fg, boldp) + 1; /* dark green */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001352 if (blue == 224)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001353 return lookup_color(3, fg, boldp) + 1; /* dark cyan */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001354 }
1355 }
1356 else if (red == 224)
1357 {
1358 if (green == 0)
1359 {
1360 if (blue == 0)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001361 return lookup_color(4, fg, boldp) + 1; /* dark red */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001362 if (blue == 224)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001363 return lookup_color(5, fg, boldp) + 1; /* dark magenta */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001364 }
1365 else if (green == 224)
1366 {
1367 if (blue == 0)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001368 return lookup_color(6, fg, boldp) + 1; /* dark yellow / brown */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001369 if (blue == 224)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001370 return lookup_color(8, fg, boldp) + 1; /* white / light grey */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001371 }
1372 }
1373 else if (red == 128)
1374 {
1375 if (green == 128 && blue == 128)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001376 return lookup_color(12, fg, boldp) + 1; /* high intensity black / dark grey */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001377 }
1378 else if (red == 255)
1379 {
1380 if (green == 64)
1381 {
1382 if (blue == 64)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001383 return lookup_color(20, fg, boldp) + 1; /* light red */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001384 if (blue == 255)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001385 return lookup_color(22, fg, boldp) + 1; /* light magenta */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001386 }
1387 else if (green == 255)
1388 {
1389 if (blue == 64)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001390 return lookup_color(24, fg, boldp) + 1; /* yellow */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001391 if (blue == 255)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001392 return lookup_color(26, fg, boldp) + 1; /* white */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001393 }
1394 }
1395 else if (red == 64)
1396 {
1397 if (green == 64)
1398 {
1399 if (blue == 255)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001400 return lookup_color(14, fg, boldp) + 1; /* light blue */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001401 }
1402 else if (green == 255)
1403 {
1404 if (blue == 64)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001405 return lookup_color(16, fg, boldp) + 1; /* light green */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001406 if (blue == 255)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001407 return lookup_color(18, fg, boldp) + 1; /* light cyan */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001408 }
1409 }
1410 if (t_colors >= 256)
1411 {
1412 if (red == blue && red == green)
1413 {
1414 /* 24-color greyscale */
1415 static int cutoff[23] = {
1416 0x05, 0x10, 0x1B, 0x26, 0x31, 0x3C, 0x47, 0x52,
1417 0x5D, 0x68, 0x73, 0x7F, 0x8A, 0x95, 0xA0, 0xAB,
1418 0xB6, 0xC1, 0xCC, 0xD7, 0xE2, 0xED, 0xF9};
1419 int i;
1420
1421 for (i = 0; i < 23; ++i)
1422 if (red < cutoff[i])
1423 return i + 233;
1424 return 256;
1425 }
1426
1427 /* 216-color cube */
1428 return 17 + ((red + 25) / 0x33) * 36
1429 + ((green + 25) / 0x33) * 6
1430 + (blue + 25) / 0x33;
1431 }
1432 return 0;
1433}
1434
1435/*
1436 * Convert the attributes of a vterm cell into an attribute index.
1437 */
1438 static int
1439cell2attr(VTermScreenCell *cell)
1440{
1441 int attr = 0;
1442
1443 if (cell->attrs.bold)
1444 attr |= HL_BOLD;
1445 if (cell->attrs.underline)
1446 attr |= HL_UNDERLINE;
1447 if (cell->attrs.italic)
1448 attr |= HL_ITALIC;
1449 if (cell->attrs.strike)
1450 attr |= HL_STANDOUT;
1451 if (cell->attrs.reverse)
1452 attr |= HL_INVERSE;
Bram Moolenaareeac6772017-07-23 15:48:37 +02001453
1454#ifdef FEAT_GUI
1455 if (gui.in_use)
1456 {
Bram Moolenaar26af85d2017-07-23 16:45:10 +02001457 guicolor_T fg, bg;
1458
1459 fg = gui_mch_get_rgb_color(cell->fg.red, cell->fg.green, cell->fg.blue);
1460 bg = gui_mch_get_rgb_color(cell->bg.red, cell->bg.green, cell->bg.blue);
1461 return get_gui_attr_idx(attr, fg, bg);
Bram Moolenaareeac6772017-07-23 15:48:37 +02001462 }
1463 else
1464#endif
1465#ifdef FEAT_TERMGUICOLORS
1466 if (p_tgc)
1467 {
Bram Moolenaar065f41c2017-07-23 18:07:56 +02001468 guicolor_T fg, bg;
1469
1470 fg = gui_get_rgb_color_cmn(cell->fg.red, cell->fg.green, cell->fg.blue);
1471 bg = gui_get_rgb_color_cmn(cell->bg.red, cell->bg.green, cell->bg.blue);
1472
1473 return get_tgc_attr_idx(attr, fg, bg);
Bram Moolenaareeac6772017-07-23 15:48:37 +02001474 }
Bram Moolenaar065f41c2017-07-23 18:07:56 +02001475 else
Bram Moolenaareeac6772017-07-23 15:48:37 +02001476#endif
1477 {
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001478 int bold = MAYBE;
1479 int fg = color2index(&cell->fg, TRUE, &bold);
1480 int bg = color2index(&cell->bg, FALSE, &bold);
1481
1482 /* with 8 colors set the bold attribute to get a bright foreground */
1483 if (bold == TRUE)
1484 attr |= HL_BOLD;
1485 return get_cterm_attr_idx(attr, fg, bg);
Bram Moolenaareeac6772017-07-23 15:48:37 +02001486 }
1487 return 0;
1488}
1489
1490/*
Bram Moolenaard85f2712017-07-28 21:51:57 +02001491 * Called to update the window that contains a terminal.
1492 * Returns FAIL when there is no terminal running in this window.
Bram Moolenaare4f25e42017-07-07 11:54:15 +02001493 */
Bram Moolenaard85f2712017-07-28 21:51:57 +02001494 int
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001495term_update_window(win_T *wp)
Bram Moolenaar938783d2017-07-16 20:13:26 +02001496{
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001497 term_T *term = wp->w_buffer->b_term;
Bram Moolenaard85f2712017-07-28 21:51:57 +02001498 VTerm *vterm;
1499 VTermScreen *screen;
1500 VTermState *state;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001501 VTermPos pos;
Bram Moolenaar938783d2017-07-16 20:13:26 +02001502
Bram Moolenaar423802d2017-07-30 16:52:24 +02001503 if (term == NULL || term->tl_vterm == NULL || term->tl_terminal_mode)
Bram Moolenaard85f2712017-07-28 21:51:57 +02001504 return FAIL;
Bram Moolenaar423802d2017-07-30 16:52:24 +02001505
Bram Moolenaard85f2712017-07-28 21:51:57 +02001506 vterm = term->tl_vterm;
1507 screen = vterm_obtain_screen(vterm);
1508 state = vterm_obtain_state(vterm);
1509
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001510 /*
1511 * If the window was resized a redraw will be triggered and we get here.
1512 * Adjust the size of the vterm unless 'termsize' specifies a fixed size.
1513 */
1514 if ((!term->tl_rows_fixed && term->tl_rows != wp->w_height)
1515 || (!term->tl_cols_fixed && term->tl_cols != wp->w_width))
Bram Moolenaarb13501f2017-07-22 22:32:56 +02001516 {
Bram Moolenaar96ad8c92017-07-28 14:17:34 +02001517 int rows = term->tl_rows_fixed ? term->tl_rows : wp->w_height;
1518 int cols = term->tl_cols_fixed ? term->tl_cols : wp->w_width;
1519 win_T *twp;
1520
1521 FOR_ALL_WINDOWS(twp)
1522 {
1523 /* When more than one window shows the same terminal, use the
1524 * smallest size. */
1525 if (twp->w_buffer == term->tl_buffer)
1526 {
1527 if (!term->tl_rows_fixed && rows > twp->w_height)
1528 rows = twp->w_height;
1529 if (!term->tl_cols_fixed && cols > twp->w_width)
1530 cols = twp->w_width;
1531 }
1532 }
Bram Moolenaarb13501f2017-07-22 22:32:56 +02001533
1534 vterm_set_size(vterm, rows, cols);
Bram Moolenaar2f3a90a2017-08-03 14:49:29 +02001535 ch_log(term->tl_job->jv_channel, "Resizing terminal to %d lines",
Bram Moolenaarb13501f2017-07-22 22:32:56 +02001536 rows);
Bram Moolenaar43da3e32017-07-23 17:27:54 +02001537 term_report_winsize(term, rows, cols);
Bram Moolenaarb13501f2017-07-22 22:32:56 +02001538 }
Bram Moolenaar58556cd2017-07-20 23:04:46 +02001539
1540 /* The cursor may have been moved when resizing. */
1541 vterm_state_get_cursorpos(state, &pos);
1542 position_cursor(wp, &pos);
1543
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001544 /* TODO: Only redraw what changed. */
1545 for (pos.row = 0; pos.row < wp->w_height; ++pos.row)
Bram Moolenaar938783d2017-07-16 20:13:26 +02001546 {
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001547 int off = screen_get_current_line_off();
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001548 int max_col = MIN(wp->w_width, term->tl_cols);
Bram Moolenaar938783d2017-07-16 20:13:26 +02001549
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001550 if (pos.row < term->tl_rows)
1551 {
1552 for (pos.col = 0; pos.col < max_col; )
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001553 {
1554 VTermScreenCell cell;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001555 int c;
Bram Moolenaar938783d2017-07-16 20:13:26 +02001556
Bram Moolenaareeac6772017-07-23 15:48:37 +02001557 if (vterm_screen_get_cell(screen, pos, &cell) == 0)
1558 vim_memset(&cell, 0, sizeof(cell));
1559
1560 /* TODO: composing chars */
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001561 c = cell.chars[0];
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001562 if (c == NUL)
1563 {
1564 ScreenLines[off] = ' ';
Bram Moolenaar8a773062017-07-24 22:29:21 +02001565#if defined(FEAT_MBYTE)
1566 if (enc_utf8)
1567 ScreenLinesUC[off] = NUL;
1568#endif
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001569 }
1570 else
1571 {
1572#if defined(FEAT_MBYTE)
1573 if (enc_utf8 && c >= 0x80)
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02001574 {
1575 ScreenLines[off] = ' ';
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001576 ScreenLinesUC[off] = c;
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02001577 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001578 else
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02001579 {
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001580 ScreenLines[off] = c;
Bram Moolenaar8a773062017-07-24 22:29:21 +02001581 if (enc_utf8)
1582 ScreenLinesUC[off] = NUL;
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02001583 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001584#else
1585 ScreenLines[off] = c;
1586#endif
1587 }
Bram Moolenaareeac6772017-07-23 15:48:37 +02001588 ScreenAttrs[off] = cell2attr(&cell);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001589
1590 ++pos.col;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001591 ++off;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001592 if (cell.width == 2)
1593 {
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02001594 ScreenLines[off] = NUL;
Bram Moolenaar8a773062017-07-24 22:29:21 +02001595#if defined(FEAT_MBYTE)
1596 if (enc_utf8)
1597 ScreenLinesUC[off] = NUL;
1598#endif
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001599 ++pos.col;
1600 ++off;
1601 }
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001602 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001603 }
Bram Moolenaare825d8b2017-07-19 23:20:19 +02001604 else
1605 pos.col = 0;
Bram Moolenaar938783d2017-07-16 20:13:26 +02001606
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001607 screen_line(wp->w_winrow + pos.row, wp->w_wincol,
1608 pos.col, wp->w_width, FALSE);
Bram Moolenaar938783d2017-07-16 20:13:26 +02001609 }
Bram Moolenaard85f2712017-07-28 21:51:57 +02001610
1611 return OK;
Bram Moolenaar938783d2017-07-16 20:13:26 +02001612}
Bram Moolenaare4f25e42017-07-07 11:54:15 +02001613
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001614/*
Bram Moolenaar63ecdda2017-07-28 22:29:35 +02001615 * Return TRUE if "wp" is a terminal window where the job has finished.
1616 */
1617 int
1618term_is_finished(buf_T *buf)
1619{
1620 return buf->b_term != NULL && buf->b_term->tl_vterm == NULL;
1621}
1622
1623/*
Bram Moolenaar423802d2017-07-30 16:52:24 +02001624 * Return TRUE if "wp" is a terminal window where the job has finished or we
1625 * are in Terminal-Normal mode.
1626 */
1627 int
1628term_show_buffer(buf_T *buf)
1629{
1630 term_T *term = buf->b_term;
1631
1632 return term != NULL && (term->tl_vterm == NULL || term->tl_terminal_mode);
1633}
1634
1635/*
Bram Moolenaar63ecdda2017-07-28 22:29:35 +02001636 * The current buffer is going to be changed. If there is terminal
1637 * highlighting remove it now.
1638 */
1639 void
1640term_change_in_curbuf(void)
1641{
1642 term_T *term = curbuf->b_term;
1643
1644 if (term_is_finished(curbuf) && term->tl_scrollback.ga_len > 0)
1645 {
1646 free_scrollback(term);
1647 redraw_buf_later(term->tl_buffer, NOT_VALID);
Bram Moolenaar20e6cd02017-08-01 20:25:22 +02001648
1649 /* The buffer is now like a normal buffer, it cannot be easily
1650 * abandoned when changed. */
1651 set_string_option_direct((char_u *)"buftype", -1,
1652 (char_u *)"", OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaar63ecdda2017-07-28 22:29:35 +02001653 }
1654}
1655
1656/*
1657 * Get the screen attribute for a position in the buffer.
1658 */
1659 int
1660term_get_attr(buf_T *buf, linenr_T lnum, int col)
1661{
1662 term_T *term = buf->b_term;
1663 sb_line_T *line;
1664
Bram Moolenaar70229f92017-07-29 16:01:53 +02001665 if (lnum > term->tl_scrollback.ga_len)
Bram Moolenaar63ecdda2017-07-28 22:29:35 +02001666 return 0;
1667 line = (sb_line_T *)term->tl_scrollback.ga_data + lnum - 1;
1668 if (col >= line->sb_cols)
1669 return 0;
1670 return cell2attr(line->sb_cells + col);
1671}
1672
1673/*
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001674 * Create a new vterm and initialize it.
1675 */
1676 static void
1677create_vterm(term_T *term, int rows, int cols)
1678{
1679 VTerm *vterm;
1680 VTermScreen *screen;
1681
1682 vterm = vterm_new(rows, cols);
1683 term->tl_vterm = vterm;
1684 screen = vterm_obtain_screen(vterm);
1685 vterm_screen_set_callbacks(screen, &screen_callbacks, term);
1686 /* TODO: depends on 'encoding'. */
1687 vterm_set_utf8(vterm, 1);
Bram Moolenaareeac6772017-07-23 15:48:37 +02001688
1689 /* Vterm uses a default black background. Set it to white when
1690 * 'background' is "light". */
1691 if (*p_bg == 'l')
1692 {
1693 VTermColor fg, bg;
1694
1695 fg.red = fg.green = fg.blue = 0;
1696 bg.red = bg.green = bg.blue = 255;
1697 vterm_state_set_default_colors(vterm_obtain_state(vterm), &fg, &bg);
1698 }
1699
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001700 /* Required to initialize most things. */
1701 vterm_screen_reset(screen, 1 /* hard */);
1702}
1703
Bram Moolenaar21554412017-07-24 21:44:43 +02001704/*
1705 * Return the text to show for the buffer name and status.
1706 */
1707 char_u *
1708term_get_status_text(term_T *term)
1709{
1710 if (term->tl_status_text == NULL)
1711 {
1712 char_u *txt;
1713 size_t len;
1714
Bram Moolenaar423802d2017-07-30 16:52:24 +02001715 if (term->tl_terminal_mode)
1716 {
1717 if (term_job_running(term))
1718 txt = (char_u *)_("Terminal");
1719 else
1720 txt = (char_u *)_("Terminal-finished");
1721 }
1722 else if (term->tl_title != NULL)
Bram Moolenaar21554412017-07-24 21:44:43 +02001723 txt = term->tl_title;
1724 else if (term_job_running(term))
1725 txt = (char_u *)_("running");
1726 else
1727 txt = (char_u *)_("finished");
1728 len = 9 + STRLEN(term->tl_buffer->b_fname) + STRLEN(txt);
Bram Moolenaara1b5b092017-07-26 21:29:34 +02001729 term->tl_status_text = alloc((int)len);
Bram Moolenaar21554412017-07-24 21:44:43 +02001730 if (term->tl_status_text != NULL)
1731 vim_snprintf((char *)term->tl_status_text, len, "%s [%s]",
1732 term->tl_buffer->b_fname, txt);
1733 }
1734 return term->tl_status_text;
1735}
1736
Bram Moolenaarf86eea92017-07-28 13:51:30 +02001737/*
1738 * Mark references in jobs of terminals.
1739 */
1740 int
1741set_ref_in_term(int copyID)
1742{
1743 int abort = FALSE;
1744 term_T *term;
1745 typval_T tv;
1746
1747 for (term = first_term; term != NULL; term = term->tl_next)
1748 if (term->tl_job != NULL)
1749 {
1750 tv.v_type = VAR_JOB;
1751 tv.vval.v_job = term->tl_job;
1752 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
1753 }
1754 return abort;
1755}
1756
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001757/*
Bram Moolenaar97870002017-07-30 18:28:38 +02001758 * Get the buffer from the first argument in "argvars".
1759 * Returns NULL when the buffer is not for a terminal window.
1760 */
1761 static buf_T *
1762term_get_buf(typval_T *argvars)
1763{
1764 buf_T *buf;
1765
1766 (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */
1767 ++emsg_off;
1768 buf = get_buf_tv(&argvars[0], FALSE);
1769 --emsg_off;
1770 if (buf == NULL || buf->b_term == NULL)
1771 return NULL;
1772 return buf;
1773}
1774
1775/*
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001776 * "term_getattr(attr, name)" function
1777 */
1778 void
1779f_term_getattr(typval_T *argvars, typval_T *rettv)
1780{
1781 int attr;
1782 size_t i;
1783 char_u *name;
1784
1785 static struct {
1786 char *name;
1787 int attr;
1788 } attrs[] = {
1789 {"bold", HL_BOLD},
1790 {"italic", HL_ITALIC},
1791 {"underline", HL_UNDERLINE},
1792 {"strike", HL_STANDOUT},
1793 {"reverse", HL_INVERSE},
1794 };
1795
1796 attr = get_tv_number(&argvars[0]);
1797 name = get_tv_string_chk(&argvars[1]);
1798 if (name == NULL)
1799 return;
1800
1801 for (i = 0; i < sizeof(attrs)/sizeof(attrs[0]); ++i)
1802 if (STRCMP(name, attrs[i].name) == 0)
1803 {
1804 rettv->vval.v_number = (attr & attrs[i].attr) != 0 ? 1 : 0;
1805 break;
1806 }
1807}
1808
1809/*
Bram Moolenaar97870002017-07-30 18:28:38 +02001810 * "term_getcursor(buf)" function
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001811 */
Bram Moolenaar97870002017-07-30 18:28:38 +02001812 void
1813f_term_getcursor(typval_T *argvars, typval_T *rettv)
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001814{
Bram Moolenaar97870002017-07-30 18:28:38 +02001815 buf_T *buf = term_get_buf(argvars);
1816 list_T *l;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001817
Bram Moolenaar97870002017-07-30 18:28:38 +02001818 if (rettv_list_alloc(rettv) == FAIL)
1819 return;
1820 if (buf == NULL)
1821 return;
1822
1823 l = rettv->vval.v_list;
Bram Moolenaarc2ce52c2017-08-01 18:35:38 +02001824 list_append_number(l, buf->b_term->tl_cursor_pos.row + 1);
1825 list_append_number(l, buf->b_term->tl_cursor_pos.col + 1);
Bram Moolenaar97870002017-07-30 18:28:38 +02001826 list_append_number(l, buf->b_term->tl_cursor_visible);
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001827}
1828
1829/*
1830 * "term_getjob(buf)" function
1831 */
1832 void
1833f_term_getjob(typval_T *argvars, typval_T *rettv)
1834{
1835 buf_T *buf = term_get_buf(argvars);
1836
1837 rettv->v_type = VAR_JOB;
1838 rettv->vval.v_job = NULL;
1839 if (buf == NULL)
1840 return;
1841
1842 rettv->vval.v_job = buf->b_term->tl_job;
1843 if (rettv->vval.v_job != NULL)
1844 ++rettv->vval.v_job->jv_refcount;
1845}
1846
Bram Moolenaarc2ce52c2017-08-01 18:35:38 +02001847 static int
1848get_row_number(typval_T *tv, term_T *term)
1849{
1850 if (tv->v_type == VAR_STRING
1851 && tv->vval.v_string != NULL
1852 && STRCMP(tv->vval.v_string, ".") == 0)
1853 return term->tl_cursor_pos.row;
1854 return (int)get_tv_number(tv) - 1;
1855}
1856
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001857/*
1858 * "term_getline(buf, row)" function
1859 */
1860 void
1861f_term_getline(typval_T *argvars, typval_T *rettv)
1862{
1863 buf_T *buf = term_get_buf(argvars);
1864 term_T *term;
1865 int row;
1866
1867 rettv->v_type = VAR_STRING;
1868 if (buf == NULL)
1869 return;
1870 term = buf->b_term;
Bram Moolenaarc2ce52c2017-08-01 18:35:38 +02001871 row = get_row_number(&argvars[1], term);
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001872
1873 if (term->tl_vterm == NULL)
1874 {
1875 linenr_T lnum = row + term->tl_scrollback_scrolled + 1;
1876
1877 /* vterm is finished, get the text from the buffer */
1878 if (lnum > 0 && lnum <= buf->b_ml.ml_line_count)
1879 rettv->vval.v_string = vim_strsave(ml_get_buf(buf, lnum, FALSE));
1880 }
1881 else
1882 {
1883 VTermScreen *screen = vterm_obtain_screen(term->tl_vterm);
1884 VTermRect rect;
1885 int len;
1886 char_u *p;
1887
Bram Moolenaar5c838a32017-08-02 22:10:34 +02001888 if (row < 0 || row >= term->tl_rows)
1889 return;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001890 len = term->tl_cols * MB_MAXBYTES + 1;
1891 p = alloc(len);
1892 if (p == NULL)
1893 return;
1894 rettv->vval.v_string = p;
1895
1896 rect.start_col = 0;
1897 rect.end_col = term->tl_cols;
1898 rect.start_row = row;
1899 rect.end_row = row + 1;
1900 p[vterm_screen_get_text(screen, (char *)p, len, rect)] = NUL;
1901 }
1902}
1903
1904/*
1905 * "term_getsize(buf)" function
1906 */
1907 void
1908f_term_getsize(typval_T *argvars, typval_T *rettv)
1909{
1910 buf_T *buf = term_get_buf(argvars);
1911 list_T *l;
1912
1913 if (rettv_list_alloc(rettv) == FAIL)
1914 return;
1915 if (buf == NULL)
1916 return;
1917
1918 l = rettv->vval.v_list;
1919 list_append_number(l, buf->b_term->tl_rows);
1920 list_append_number(l, buf->b_term->tl_cols);
1921}
1922
1923/*
Bram Moolenaarb000e322017-07-30 19:38:21 +02001924 * "term_getstatus(buf)" function
1925 */
1926 void
1927f_term_getstatus(typval_T *argvars, typval_T *rettv)
1928{
1929 buf_T *buf = term_get_buf(argvars);
1930 term_T *term;
1931 char_u val[100];
1932
1933 rettv->v_type = VAR_STRING;
1934 if (buf == NULL)
1935 return;
1936 term = buf->b_term;
1937
1938 if (term_job_running(term))
1939 STRCPY(val, "running");
1940 else
1941 STRCPY(val, "finished");
1942 if (term->tl_terminal_mode)
1943 STRCAT(val, ",terminal");
1944 rettv->vval.v_string = vim_strsave(val);
1945}
1946
1947/*
1948 * "term_gettitle(buf)" function
1949 */
1950 void
1951f_term_gettitle(typval_T *argvars, typval_T *rettv)
1952{
1953 buf_T *buf = term_get_buf(argvars);
1954
1955 rettv->v_type = VAR_STRING;
1956 if (buf == NULL)
1957 return;
1958
1959 if (buf->b_term->tl_title != NULL)
1960 rettv->vval.v_string = vim_strsave(buf->b_term->tl_title);
1961}
1962
1963/*
Bram Moolenaar7c9aec42017-08-03 13:51:25 +02001964 * "term_gettty(buf)" function
1965 */
1966 void
1967f_term_gettty(typval_T *argvars, typval_T *rettv)
1968{
1969 buf_T *buf = term_get_buf(argvars);
1970 char_u *p;
1971
1972 rettv->v_type = VAR_STRING;
1973 if (buf == NULL)
1974 return;
1975 if (buf->b_term->tl_job != NULL)
1976 p = buf->b_term->tl_job->jv_tty_name;
1977 else
1978 p = buf->b_term->tl_tty_name;
1979 if (p != NULL)
1980 rettv->vval.v_string = vim_strsave(p);
1981}
1982
1983/*
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001984 * "term_list()" function
1985 */
1986 void
1987f_term_list(typval_T *argvars UNUSED, typval_T *rettv)
1988{
1989 term_T *tp;
1990 list_T *l;
1991
1992 if (rettv_list_alloc(rettv) == FAIL || first_term == NULL)
1993 return;
1994
1995 l = rettv->vval.v_list;
1996 for (tp = first_term; tp != NULL; tp = tp->tl_next)
1997 if (tp != NULL && tp->tl_buffer != NULL)
1998 if (list_append_number(l,
1999 (varnumber_T)tp->tl_buffer->b_fnum) == FAIL)
2000 return;
2001}
2002
2003/*
2004 * "term_scrape(buf, row)" function
2005 */
2006 void
2007f_term_scrape(typval_T *argvars, typval_T *rettv)
2008{
2009 buf_T *buf = term_get_buf(argvars);
2010 VTermScreen *screen = NULL;
2011 VTermPos pos;
2012 list_T *l;
2013 term_T *term;
2014
2015 if (rettv_list_alloc(rettv) == FAIL)
2016 return;
2017 if (buf == NULL)
2018 return;
2019 term = buf->b_term;
2020 if (term->tl_vterm != NULL)
2021 screen = vterm_obtain_screen(term->tl_vterm);
2022
2023 l = rettv->vval.v_list;
Bram Moolenaarc2ce52c2017-08-01 18:35:38 +02002024 pos.row = get_row_number(&argvars[1], term);
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002025 for (pos.col = 0; pos.col < term->tl_cols; )
2026 {
2027 dict_T *dcell;
2028 VTermScreenCell cell;
2029 char_u rgb[8];
2030 char_u mbs[MB_MAXBYTES * VTERM_MAX_CHARS_PER_CELL + 1];
2031 int off = 0;
2032 int i;
2033
2034 if (screen == NULL)
2035 {
2036 linenr_T lnum = pos.row + term->tl_scrollback_scrolled;
2037 sb_line_T *line;
2038
2039 /* vterm has finished, get the cell from scrollback */
2040 if (lnum < 0 || lnum >= term->tl_scrollback.ga_len)
2041 break;
2042 line = (sb_line_T *)term->tl_scrollback.ga_data + lnum;
2043 if (pos.col >= line->sb_cols)
2044 break;
2045 cell = line->sb_cells[pos.col];
2046 }
2047 else if (vterm_screen_get_cell(screen, pos, &cell) == 0)
2048 break;
2049 dcell = dict_alloc();
2050 list_append_dict(l, dcell);
2051
2052 for (i = 0; i < VTERM_MAX_CHARS_PER_CELL; ++i)
2053 {
2054 if (cell.chars[i] == 0)
2055 break;
2056 off += (*utf_char2bytes)((int)cell.chars[i], mbs + off);
2057 }
2058 mbs[off] = NUL;
2059 dict_add_nr_str(dcell, "chars", 0, mbs);
2060
2061 vim_snprintf((char *)rgb, 8, "#%02x%02x%02x",
2062 cell.fg.red, cell.fg.green, cell.fg.blue);
2063 dict_add_nr_str(dcell, "fg", 0, rgb);
2064 vim_snprintf((char *)rgb, 8, "#%02x%02x%02x",
2065 cell.bg.red, cell.bg.green, cell.bg.blue);
2066 dict_add_nr_str(dcell, "bg", 0, rgb);
2067
2068 dict_add_nr_str(dcell, "attr", cell2attr(&cell), NULL);
2069 dict_add_nr_str(dcell, "width", cell.width, NULL);
2070
2071 ++pos.col;
2072 if (cell.width == 2)
2073 ++pos.col;
2074 }
2075}
2076
2077/*
2078 * "term_sendkeys(buf, keys)" function
2079 */
2080 void
2081f_term_sendkeys(typval_T *argvars, typval_T *rettv)
2082{
2083 buf_T *buf = term_get_buf(argvars);
2084 char_u *msg;
2085 term_T *term;
2086
2087 rettv->v_type = VAR_UNKNOWN;
2088 if (buf == NULL)
2089 return;
2090
2091 msg = get_tv_string_chk(&argvars[1]);
2092 if (msg == NULL)
2093 return;
2094 term = buf->b_term;
2095 if (term->tl_vterm == NULL)
2096 return;
2097
2098 while (*msg != NUL)
2099 {
2100 send_keys_to_term(term, PTR2CHAR(msg), FALSE);
2101 msg += MB_PTR2LEN(msg);
2102 }
2103
Bram Moolenaar392d1bf2017-07-31 21:18:58 +02002104 if (!term->tl_terminal_mode)
2105 {
2106 /* TODO: only update once in a while. */
2107 update_screen(0);
2108 if (buf == curbuf)
2109 update_cursor(term, TRUE);
2110 }
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002111}
2112
2113/*
2114 * "term_start(command, options)" function
2115 */
2116 void
2117f_term_start(typval_T *argvars, typval_T *rettv)
2118{
2119 char_u *cmd = get_tv_string_chk(&argvars[0]);
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002120 jobopt_T opt;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002121
2122 if (cmd == NULL)
2123 return;
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002124 init_job_options(&opt);
2125 /* TODO: allow more job options */
2126 if (argvars[1].v_type != VAR_UNKNOWN
2127 && get_job_options(&argvars[1], &opt,
2128 JO_TIMEOUT_ALL + JO_STOPONEXIT
2129 + JO_EXIT_CB + JO_CLOSE_CALLBACK) == FAIL)
2130 return;
2131
2132 term_start(cmd, &opt);
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002133
2134 if (curbuf->b_term != NULL)
2135 rettv->vval.v_number = curbuf->b_fnum;
2136}
2137
2138/*
2139 * "term_wait" function
2140 */
2141 void
2142f_term_wait(typval_T *argvars, typval_T *rettv UNUSED)
2143{
2144 buf_T *buf = term_get_buf(argvars);
2145
2146 if (buf == NULL)
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002147 {
2148 ch_log(NULL, "term_wait(): invalid argument");
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002149 return;
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002150 }
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002151 if (buf->b_term->tl_job == NULL)
2152 {
2153 ch_log(NULL, "term_wait(): no job to wait for");
2154 return;
2155 }
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002156
2157 /* Get the job status, this will detect a job that finished. */
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002158 if (STRCMP(job_status(buf->b_term->tl_job), "dead") == 0)
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002159 {
2160 /* The job is dead, keep reading channel I/O until the channel is
2161 * closed. */
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002162 ch_log(NULL, "term_wait(): waiting for channel to close");
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002163 while (buf->b_term != NULL && !buf->b_term->tl_channel_closed)
2164 {
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002165 mch_check_messages();
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002166 parse_queued_messages();
2167 ui_delay(10L, FALSE);
2168 }
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002169 mch_check_messages();
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002170 parse_queued_messages();
2171 }
2172 else
2173 {
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002174 mch_check_messages();
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002175 parse_queued_messages();
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002176
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002177 /* Wait for 10 msec for any channel I/O. */
2178 /* TODO: use delay from optional argument */
2179 ui_delay(10L, TRUE);
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002180 mch_check_messages();
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002181
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002182 /* Flushing messages on channels is hopefully sufficient.
2183 * TODO: is there a better way? */
2184 parse_queued_messages();
2185 }
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002186}
2187
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002188# ifdef WIN3264
2189
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002190/**************************************
2191 * 2. MS-Windows implementation.
2192 */
2193
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002194#define WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN 1ul
2195#define WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN 2ull
2196
Bram Moolenaar8a773062017-07-24 22:29:21 +02002197void* (*winpty_config_new)(UINT64, void*);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002198void* (*winpty_open)(void*, void*);
Bram Moolenaar8a773062017-07-24 22:29:21 +02002199void* (*winpty_spawn_config_new)(UINT64, void*, LPCWSTR, void*, void*, void*);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002200BOOL (*winpty_spawn)(void*, void*, HANDLE*, HANDLE*, DWORD*, void*);
2201void (*winpty_config_set_initial_size)(void*, int, int);
2202LPCWSTR (*winpty_conin_name)(void*);
2203LPCWSTR (*winpty_conout_name)(void*);
2204LPCWSTR (*winpty_conerr_name)(void*);
2205void (*winpty_free)(void*);
2206void (*winpty_config_free)(void*);
2207void (*winpty_spawn_config_free)(void*);
2208void (*winpty_error_free)(void*);
2209LPCWSTR (*winpty_error_msg)(void*);
Bram Moolenaar43da3e32017-07-23 17:27:54 +02002210BOOL (*winpty_set_size)(void*, int, int, void*);
Bram Moolenaar5be8dd02017-08-03 20:52:19 +02002211HANDLE (*winpty_agent_process)(void*);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002212
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002213#define WINPTY_DLL "winpty.dll"
2214
2215static HINSTANCE hWinPtyDLL = NULL;
2216
2217 int
2218dyn_winpty_init(void)
2219{
2220 int i;
2221 static struct
2222 {
2223 char *name;
2224 FARPROC *ptr;
2225 } winpty_entry[] =
2226 {
2227 {"winpty_conerr_name", (FARPROC*)&winpty_conerr_name},
2228 {"winpty_config_free", (FARPROC*)&winpty_config_free},
2229 {"winpty_config_new", (FARPROC*)&winpty_config_new},
2230 {"winpty_config_set_initial_size", (FARPROC*)&winpty_config_set_initial_size},
2231 {"winpty_conin_name", (FARPROC*)&winpty_conin_name},
2232 {"winpty_conout_name", (FARPROC*)&winpty_conout_name},
2233 {"winpty_error_free", (FARPROC*)&winpty_error_free},
2234 {"winpty_free", (FARPROC*)&winpty_free},
2235 {"winpty_open", (FARPROC*)&winpty_open},
2236 {"winpty_spawn", (FARPROC*)&winpty_spawn},
2237 {"winpty_spawn_config_free", (FARPROC*)&winpty_spawn_config_free},
2238 {"winpty_spawn_config_new", (FARPROC*)&winpty_spawn_config_new},
2239 {"winpty_error_msg", (FARPROC*)&winpty_error_msg},
Bram Moolenaar43da3e32017-07-23 17:27:54 +02002240 {"winpty_set_size", (FARPROC*)&winpty_set_size},
Bram Moolenaar5be8dd02017-08-03 20:52:19 +02002241 {"winpty_agent_process", (FARPROC*)&winpty_agent_process},
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002242 {NULL, NULL}
2243 };
2244
2245 /* No need to initialize twice. */
2246 if (hWinPtyDLL)
2247 return 1;
2248 /* Load winpty.dll */
2249 hWinPtyDLL = vimLoadLib(WINPTY_DLL);
2250 if (!hWinPtyDLL)
2251 {
2252 EMSG2(_(e_loadlib), WINPTY_DLL);
2253 return 0;
2254 }
2255 for (i = 0; winpty_entry[i].name != NULL
2256 && winpty_entry[i].ptr != NULL; ++i)
2257 {
2258 if ((*winpty_entry[i].ptr = (FARPROC)GetProcAddress(hWinPtyDLL,
2259 winpty_entry[i].name)) == NULL)
2260 {
2261 EMSG2(_(e_loadfunc), winpty_entry[i].name);
2262 return 0;
2263 }
2264 }
2265
2266 return 1;
2267}
2268
2269/*
2270 * Create a new terminal of "rows" by "cols" cells.
2271 * Store a reference in "term".
2272 * Return OK or FAIL.
2273 */
2274 static int
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002275term_and_job_init(term_T *term, int rows, int cols, char_u *cmd, jobopt_T *opt)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002276{
Bram Moolenaarab6eec32017-07-27 21:46:43 +02002277 WCHAR *p;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002278 channel_T *channel = NULL;
2279 job_T *job = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002280 DWORD error;
2281 HANDLE jo = NULL, child_process_handle, child_thread_handle;
2282 void *winpty_err;
Bram Moolenaarab6eec32017-07-27 21:46:43 +02002283 void *spawn_config = NULL;
Bram Moolenaar5be8dd02017-08-03 20:52:19 +02002284 char buf[MAX_PATH];
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002285
2286 if (!dyn_winpty_init())
2287 return FAIL;
2288
Bram Moolenaarab6eec32017-07-27 21:46:43 +02002289 p = enc_to_utf16(cmd, NULL);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002290 if (p == NULL)
2291 return FAIL;
2292
2293 job = job_alloc();
2294 if (job == NULL)
2295 goto failed;
2296
2297 channel = add_channel();
2298 if (channel == NULL)
2299 goto failed;
2300
2301 term->tl_winpty_config = winpty_config_new(0, &winpty_err);
2302 if (term->tl_winpty_config == NULL)
2303 goto failed;
2304
2305 winpty_config_set_initial_size(term->tl_winpty_config, cols, rows);
2306 term->tl_winpty = winpty_open(term->tl_winpty_config, &winpty_err);
2307 if (term->tl_winpty == NULL)
2308 goto failed;
2309
Bram Moolenaar7c9aec42017-08-03 13:51:25 +02002310 /* TODO: if the command is "NONE" only create a pty. */
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002311 spawn_config = winpty_spawn_config_new(
2312 WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN |
2313 WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN,
2314 NULL,
2315 p,
2316 NULL,
2317 NULL,
2318 &winpty_err);
2319 if (spawn_config == NULL)
2320 goto failed;
2321
2322 channel = add_channel();
2323 if (channel == NULL)
2324 goto failed;
2325
2326 job = job_alloc();
2327 if (job == NULL)
2328 goto failed;
2329
2330 if (!winpty_spawn(term->tl_winpty, spawn_config, &child_process_handle,
2331 &child_thread_handle, &error, &winpty_err))
2332 goto failed;
2333
2334 channel_set_pipes(channel,
2335 (sock_T) CreateFileW(
2336 winpty_conin_name(term->tl_winpty),
2337 GENERIC_WRITE, 0, NULL,
2338 OPEN_EXISTING, 0, NULL),
2339 (sock_T) CreateFileW(
2340 winpty_conout_name(term->tl_winpty),
2341 GENERIC_READ, 0, NULL,
2342 OPEN_EXISTING, 0, NULL),
2343 (sock_T) CreateFileW(
2344 winpty_conerr_name(term->tl_winpty),
2345 GENERIC_READ, 0, NULL,
2346 OPEN_EXISTING, 0, NULL));
2347
2348 jo = CreateJobObject(NULL, NULL);
2349 if (jo == NULL)
2350 goto failed;
2351
2352 if (!AssignProcessToJobObject(jo, child_process_handle))
Bram Moolenaarab6eec32017-07-27 21:46:43 +02002353 {
2354 /* Failed, switch the way to terminate process with TerminateProcess. */
2355 CloseHandle(jo);
2356 jo = NULL;
2357 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002358
2359 winpty_spawn_config_free(spawn_config);
Bram Moolenaarab6eec32017-07-27 21:46:43 +02002360 vim_free(p);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002361
2362 create_vterm(term, rows, cols);
2363
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002364 channel_set_job(channel, job, opt);
Bram Moolenaar102dc7f2017-08-03 20:59:29 +02002365 job_set_options(job, opt);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002366
2367 job->jv_channel = channel;
2368 job->jv_proc_info.hProcess = child_process_handle;
2369 job->jv_proc_info.dwProcessId = GetProcessId(child_process_handle);
2370 job->jv_job_object = jo;
2371 job->jv_status = JOB_STARTED;
Bram Moolenaar5be8dd02017-08-03 20:52:19 +02002372 sprintf(buf, "winpty://%lu",
2373 GetProcessId(winpty_agent_process(term->tl_winpty)));
2374 job->jv_tty_name = vim_strsave((char_u*)buf);
Bram Moolenaar0e83f022017-07-27 22:07:35 +02002375 ++job->jv_refcount;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002376 term->tl_job = job;
2377
2378 return OK;
2379
2380failed:
Bram Moolenaarab6eec32017-07-27 21:46:43 +02002381 if (spawn_config != NULL)
2382 winpty_spawn_config_free(spawn_config);
2383 vim_free(p);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002384 if (channel != NULL)
2385 channel_clear(channel);
2386 if (job != NULL)
Bram Moolenaarcdeae992017-07-23 17:22:35 +02002387 {
2388 job->jv_channel = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002389 job_cleanup(job);
Bram Moolenaarcdeae992017-07-23 17:22:35 +02002390 }
2391 term->tl_job = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002392 if (jo != NULL)
2393 CloseHandle(jo);
2394 if (term->tl_winpty != NULL)
2395 winpty_free(term->tl_winpty);
Bram Moolenaarcdeae992017-07-23 17:22:35 +02002396 term->tl_winpty = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002397 if (term->tl_winpty_config != NULL)
2398 winpty_config_free(term->tl_winpty_config);
Bram Moolenaarcdeae992017-07-23 17:22:35 +02002399 term->tl_winpty_config = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002400 if (winpty_err != NULL)
2401 {
2402 char_u *msg = utf16_to_enc(
2403 (short_u *)winpty_error_msg(winpty_err), NULL);
2404
2405 EMSG(msg);
2406 winpty_error_free(winpty_err);
2407 }
2408 return FAIL;
2409}
2410
2411/*
2412 * Free the terminal emulator part of "term".
2413 */
2414 static void
Bram Moolenaard85f2712017-07-28 21:51:57 +02002415term_free_vterm(term_T *term)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002416{
Bram Moolenaarcdeae992017-07-23 17:22:35 +02002417 if (term->tl_winpty != NULL)
2418 winpty_free(term->tl_winpty);
Bram Moolenaard85f2712017-07-28 21:51:57 +02002419 term->tl_winpty = NULL;
Bram Moolenaarcdeae992017-07-23 17:22:35 +02002420 if (term->tl_winpty_config != NULL)
2421 winpty_config_free(term->tl_winpty_config);
Bram Moolenaard85f2712017-07-28 21:51:57 +02002422 term->tl_winpty_config = NULL;
Bram Moolenaarcdeae992017-07-23 17:22:35 +02002423 if (term->tl_vterm != NULL)
2424 vterm_free(term->tl_vterm);
Bram Moolenaardcbfa332017-07-28 23:16:13 +02002425 term->tl_vterm = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002426}
2427
Bram Moolenaar43da3e32017-07-23 17:27:54 +02002428/*
2429 * Request size to terminal.
2430 */
2431 static void
2432term_report_winsize(term_T *term, int rows, int cols)
2433{
2434 winpty_set_size(term->tl_winpty, cols, rows, NULL);
2435}
2436
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002437# else
2438
2439/**************************************
2440 * 3. Unix-like implementation.
2441 */
2442
2443/*
2444 * Create a new terminal of "rows" by "cols" cells.
2445 * Start job for "cmd".
2446 * Store the pointers in "term".
2447 * Return OK or FAIL.
2448 */
2449 static int
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002450term_and_job_init(term_T *term, int rows, int cols, char_u *cmd, jobopt_T *opt)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002451{
2452 typval_T argvars[2];
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002453
2454 create_vterm(term, rows, cols);
2455
Bram Moolenaar7c9aec42017-08-03 13:51:25 +02002456 /* TODO: if the command is "NONE" only create a pty. */
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002457 argvars[0].v_type = VAR_STRING;
2458 argvars[0].vval.v_string = cmd;
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002459
2460 term->tl_job = job_start(argvars, opt);
Bram Moolenaar0e83f022017-07-27 22:07:35 +02002461 if (term->tl_job != NULL)
2462 ++term->tl_job->jv_refcount;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002463
Bram Moolenaar61a66052017-07-22 18:39:00 +02002464 return term->tl_job != NULL
2465 && term->tl_job->jv_channel != NULL
2466 && term->tl_job->jv_status != JOB_FAILED ? OK : FAIL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002467}
2468
2469/*
2470 * Free the terminal emulator part of "term".
2471 */
2472 static void
Bram Moolenaard85f2712017-07-28 21:51:57 +02002473term_free_vterm(term_T *term)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002474{
Bram Moolenaarcdeae992017-07-23 17:22:35 +02002475 if (term->tl_vterm != NULL)
2476 vterm_free(term->tl_vterm);
Bram Moolenaard85f2712017-07-28 21:51:57 +02002477 term->tl_vterm = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002478}
Bram Moolenaar43da3e32017-07-23 17:27:54 +02002479
2480/*
2481 * Request size to terminal.
2482 */
2483 static void
2484term_report_winsize(term_T *term, int rows, int cols)
2485{
2486 /* Use an ioctl() to report the new window size to the job. */
2487 if (term->tl_job != NULL && term->tl_job->jv_channel != NULL)
2488 {
2489 int fd = -1;
2490 int part;
2491
2492 for (part = PART_OUT; part < PART_COUNT; ++part)
2493 {
2494 fd = term->tl_job->jv_channel->ch_part[part].ch_fd;
2495 if (isatty(fd))
2496 break;
2497 }
2498 if (part < PART_COUNT && mch_report_winsize(fd, rows, cols) == OK)
2499 mch_stop_job(term->tl_job, (char_u *)"winch");
2500 }
2501}
2502
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002503# endif
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02002504
Bram Moolenaare4f25e42017-07-07 11:54:15 +02002505#endif /* FEAT_TERMINAL */