blob: 20c927248a3c60655535272ef371343f05eac5e5 [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 Moolenaar3c3a80d2017-08-03 17:06:45 +020040 * - add argument to term_wait() for waiting time.
Bram Moolenaard85f2712017-07-28 21:51:57 +020041 * - For the scrollback buffer store lines in the buffer, only attributes in
42 * tl_scrollback.
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020043 * - When the job ends:
Bram Moolenaar21554412017-07-24 21:44:43 +020044 * - Need an option or argument to drop the window+buffer right away, to be
Bram Moolenaar423802d2017-07-30 16:52:24 +020045 * used for a shell or Vim. 'termfinish'; "close", "open" (open window when
46 * job finishes).
47 * - add option values to the command:
48 * :term <24x80> <close> vim notes.txt
Bram Moolenaar12d93ee2017-07-30 19:02:02 +020049 * - support different cursor shapes, colors and attributes
50 * - make term_getcursor() return type (none/block/bar/underline) and
51 * attributes (color, blink, etc.)
Bram Moolenaard85f2712017-07-28 21:51:57 +020052 * - To set BS correctly, check get_stty(); Pass the fd of the pty.
Bram Moolenaar69198192017-08-05 14:10:48 +020053 * For the GUI fill termios with default values, perhaps like pangoterm:
54 * http://bazaar.launchpad.net/~leonerd/pangoterm/trunk/view/head:/main.c#L134
55 * Also get the NL behavior from there.
Bram Moolenaar423802d2017-07-30 16:52:24 +020056 * - do not store terminal window in viminfo. Or prefix term:// ?
Bram Moolenaar21554412017-07-24 21:44:43 +020057 * - add a character in :ls output
Bram Moolenaar43c007f2017-07-30 17:45:37 +020058 * - add 't' to mode()
Bram Moolenaard8dc1792017-08-03 11:55:21 +020059 * - set 'filetype' to "terminal"?
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020060 * - use win_del_lines() to make scroll-up efficient.
Bram Moolenaar7c9aec42017-08-03 13:51:25 +020061 * - Make StatusLineTerm adjust UserN highlighting like StatusLineNC does, see
62 * use of hightlight_stlnc[].
Bram Moolenaar94053a52017-08-01 21:44:33 +020063 * - implement term_setsize()
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020064 * - add test for giving error for invalid 'termsize' value.
Bram Moolenaare4f25e42017-07-07 11:54:15 +020065 * - support minimal size when 'termsize' is "rows*cols".
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020066 * - support minimal size when 'termsize' is empty?
Bram Moolenaar5a1feb82017-07-22 18:04:08 +020067 * - implement "term" for job_start(): more job options when starting a
Bram Moolenaar78712a72017-08-05 14:50:12 +020068 * terminal. Allow:
69 * "in_io", "in_top", "in_bot", "in_name", "in_buf"
70 "out_io", "out_name", "out_buf", "out_modifiable", "out_msg"
71 "err_io", "err_name", "err_buf", "err_modifiable", "err_msg"
72 * Check that something is connected to the terminal.
73 * Test: "cat" reading from a file or buffer
74 * "ls" writing stdout to a file or buffer
75 * shell writing stderr to a file or buffer
Bram Moolenaar7c9aec42017-08-03 13:51:25 +020076 * - support ":term NONE" to open a terminal with a pty but not running a job
77 * in it. The pty can be passed to gdb to run the executable in.
Bram Moolenaar423802d2017-07-30 16:52:24 +020078 * - if the job in the terminal does not support the mouse, we can use the
79 * mouse in the Terminal window for copy/paste.
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020080 * - when 'encoding' is not utf-8, or the job is using another encoding, setup
81 * conversions.
Bram Moolenaarc9456ce2017-07-30 21:46:04 +020082 * - update ":help function-list" for terminal functions.
Bram Moolenaardbe948d2017-07-23 22:50:51 +020083 * - In the GUI use a terminal emulator for :!cmd.
Bram Moolenaar12d853f2017-08-01 18:04:04 +020084 * - Copy text in the vterm to the Vim buffer once in a while, so that
85 * completion works.
Bram Moolenaare4f25e42017-07-07 11:54:15 +020086 */
87
88#include "vim.h"
89
Bram Moolenaarc6df10e2017-07-29 20:15:08 +020090#if defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaare4f25e42017-07-07 11:54:15 +020091
Bram Moolenaard5310982017-08-05 15:16:32 +020092#ifndef MIN
93# define MIN(x,y) ((x) < (y) ? (x) : (y))
94#endif
95#ifndef MAX
96# define MAX(x,y) ((x) > (y) ? (x) : (y))
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020097#endif
Bram Moolenaare4f25e42017-07-07 11:54:15 +020098
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020099#include "libvterm/include/vterm.h"
100
Bram Moolenaard85f2712017-07-28 21:51:57 +0200101typedef struct sb_line_S {
102 int sb_cols; /* can differ per line */
103 VTermScreenCell *sb_cells; /* allocated */
104} sb_line_T;
105
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200106/* typedef term_T in structs.h */
107struct terminal_S {
108 term_T *tl_next;
109
Bram Moolenaar423802d2017-07-30 16:52:24 +0200110 VTerm *tl_vterm;
111 job_T *tl_job;
112 buf_T *tl_buffer;
113
Bram Moolenaar7c9aec42017-08-03 13:51:25 +0200114 /* used when tl_job is NULL and only a pty was created */
115 int tl_tty_fd;
116 char_u *tl_tty_name;
117
Bram Moolenaar423802d2017-07-30 16:52:24 +0200118 int tl_terminal_mode;
119 int tl_channel_closed;
120
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200121#ifdef WIN3264
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200122 void *tl_winpty_config;
123 void *tl_winpty;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200124#endif
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200125
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200126 /* last known vterm size */
127 int tl_rows;
128 int tl_cols;
129 /* vterm size does not follow window size */
130 int tl_rows_fixed;
131 int tl_cols_fixed;
132
Bram Moolenaar21554412017-07-24 21:44:43 +0200133 char_u *tl_title; /* NULL or allocated */
134 char_u *tl_status_text; /* NULL or allocated */
135
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200136 /* Range of screen rows to update. Zero based. */
137 int tl_dirty_row_start; /* -1 if nothing dirty */
138 int tl_dirty_row_end; /* row below last one to update */
139
Bram Moolenaard85f2712017-07-28 21:51:57 +0200140 garray_T tl_scrollback;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200141 int tl_scrollback_scrolled;
Bram Moolenaard85f2712017-07-28 21:51:57 +0200142
Bram Moolenaar22aad2f2017-07-30 18:19:46 +0200143 VTermPos tl_cursor_pos;
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200144 int tl_cursor_visible;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200145};
146
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200147/*
148 * List of all active terminals.
149 */
150static term_T *first_term = NULL;
151
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200152
153#define MAX_ROW 999999 /* used for tl_dirty_row_end to update all rows */
154#define KEY_BUF_LEN 200
155
156/*
157 * Functions with separate implementation for MS-Windows and Unix-like systems.
158 */
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200159static 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 +0200160static void term_report_winsize(term_T *term, int rows, int cols);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200161static void term_free_vterm(term_T *term);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200162
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200163/**************************************
164 * 1. Generic code for all systems.
165 */
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200166
167/*
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200168 * Determine the terminal size from 'termsize' and the current window.
169 * Assumes term->tl_rows and term->tl_cols are zero.
170 */
171 static void
172set_term_and_win_size(term_T *term)
173{
174 if (*curwin->w_p_tms != NUL)
175 {
176 char_u *p = vim_strchr(curwin->w_p_tms, 'x') + 1;
177
178 term->tl_rows = atoi((char *)curwin->w_p_tms);
179 term->tl_cols = atoi((char *)p);
180 }
181 if (term->tl_rows == 0)
182 term->tl_rows = curwin->w_height;
183 else
184 {
185 win_setheight_win(term->tl_rows, curwin);
186 term->tl_rows_fixed = TRUE;
187 }
188 if (term->tl_cols == 0)
189 term->tl_cols = curwin->w_width;
190 else
191 {
192 win_setwidth_win(term->tl_cols, curwin);
193 term->tl_cols_fixed = TRUE;
194 }
195}
196
197/*
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200198 * Initialize job options for a terminal job.
199 * Caller may overrule some of them.
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200200 */
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200201 static void
202init_job_options(jobopt_T *opt)
203{
204 clear_job_options(opt);
205
206 opt->jo_mode = MODE_RAW;
207 opt->jo_out_mode = MODE_RAW;
208 opt->jo_err_mode = MODE_RAW;
209 opt->jo_set = JO_MODE | JO_OUT_MODE | JO_ERR_MODE;
210
211 opt->jo_io[PART_OUT] = JIO_BUFFER;
212 opt->jo_io[PART_ERR] = JIO_BUFFER;
213 opt->jo_set |= JO_OUT_IO + JO_ERR_IO;
214
215 opt->jo_modifiable[PART_OUT] = 0;
216 opt->jo_modifiable[PART_ERR] = 0;
217 opt->jo_set |= JO_OUT_MODIFIABLE + JO_ERR_MODIFIABLE;
218
219 opt->jo_set |= JO_OUT_BUF + JO_ERR_BUF;
220}
221
222/*
223 * Set job options mandatory for a terminal job.
224 */
225 static void
226setup_job_options(jobopt_T *opt, int rows, int cols)
227{
228 opt->jo_io_buf[PART_OUT] = curbuf->b_fnum;
229 opt->jo_io_buf[PART_ERR] = curbuf->b_fnum;
230 opt->jo_pty = TRUE;
231 opt->jo_term_rows = rows;
232 opt->jo_term_cols = cols;
233}
234
235 static void
236term_start(char_u *cmd, jobopt_T *opt)
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200237{
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200238 exarg_T split_ea;
239 win_T *old_curwin = curwin;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200240 term_T *term;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200241
242 if (check_restricted() || check_secure())
243 return;
244
245 term = (term_T *)alloc_clear(sizeof(term_T));
246 if (term == NULL)
247 return;
248 term->tl_dirty_row_end = MAX_ROW;
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200249 term->tl_cursor_visible = TRUE;
Bram Moolenaard85f2712017-07-28 21:51:57 +0200250 ga_init2(&term->tl_scrollback, sizeof(sb_line_T), 300);
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200251
252 /* Open a new window or tab. */
253 vim_memset(&split_ea, 0, sizeof(split_ea));
254 split_ea.cmdidx = CMD_new;
255 split_ea.cmd = (char_u *)"new";
256 split_ea.arg = (char_u *)"";
Bram Moolenaarcfcc0222017-08-05 17:13:48 +0200257 if (opt->jo_term_rows > 0 && !(cmdmod.split & WSP_VERT))
258 {
259 split_ea.line2 = opt->jo_term_rows;
260 split_ea.addr_count = 1;
261 }
262 if (opt->jo_term_cols > 0 && (cmdmod.split & WSP_VERT))
263 {
264 split_ea.line2 = opt->jo_term_cols;
265 split_ea.addr_count = 1;
266 }
267
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200268 ex_splitview(&split_ea);
269 if (curwin == old_curwin)
270 {
271 /* split failed */
272 vim_free(term);
273 return;
274 }
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200275 term->tl_buffer = curbuf;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200276 curbuf->b_term = term;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200277
Bram Moolenaarcfcc0222017-08-05 17:13:48 +0200278 /* only one size was taken care of with :new, do the other one */
279 if (opt->jo_term_rows > 0 && (cmdmod.split & WSP_VERT))
280 win_setheight(opt->jo_term_rows);
281 if (opt->jo_term_cols > 0 && !(cmdmod.split & WSP_VERT))
282 win_setwidth(opt->jo_term_cols);
283
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200284 /* Link the new terminal in the list of active terminals. */
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200285 term->tl_next = first_term;
286 first_term = term;
287
Bram Moolenaar293424c2017-07-26 23:11:01 +0200288 if (cmd == NULL || *cmd == NUL)
289 cmd = p_sh;
290
Bram Moolenaar78712a72017-08-05 14:50:12 +0200291 if (opt->jo_term_name != NULL)
292 curbuf->b_ffname = vim_strsave(opt->jo_term_name);
293 else
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200294 {
295 int i;
296 size_t len = STRLEN(cmd) + 10;
Bram Moolenaara1b5b092017-07-26 21:29:34 +0200297 char_u *p = alloc((int)len);
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200298
Bram Moolenaar20e6cd02017-08-01 20:25:22 +0200299 for (i = 0; p != NULL; ++i)
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200300 {
Bram Moolenaar20e6cd02017-08-01 20:25:22 +0200301 /* Prepend a ! to the command name to avoid the buffer name equals
302 * the executable, otherwise ":w!" would overwrite it. */
303 if (i == 0)
304 vim_snprintf((char *)p, len, "!%s", cmd);
305 else
306 vim_snprintf((char *)p, len, "!%s (%d)", cmd, i);
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200307 if (buflist_findname(p) == NULL)
308 {
309 curbuf->b_ffname = p;
310 break;
311 }
312 }
313 }
314 curbuf->b_fname = curbuf->b_ffname;
315
Bram Moolenaareb44a682017-08-03 22:44:55 +0200316 set_string_option_direct((char_u *)"buftype", -1,
317 (char_u *)"terminal", OPT_FREE|OPT_LOCAL, 0);
318
Bram Moolenaar20e6cd02017-08-01 20:25:22 +0200319 /* Mark the buffer as not modifiable. It can only be made modifiable after
320 * the job finished. */
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200321 curbuf->b_p_ma = FALSE;
Bram Moolenaareb44a682017-08-03 22:44:55 +0200322
323 /* Set 'bufhidden' to "hide": allow closing the window. */
324 set_string_option_direct((char_u *)"bufhidden", -1,
325 (char_u *)"hide", OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200326
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200327 set_term_and_win_size(term);
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200328 setup_job_options(opt, term->tl_rows, term->tl_cols);
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200329
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200330 /* System dependent: setup the vterm and start the job in it. */
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200331 if (term_and_job_init(term, term->tl_rows, term->tl_cols, cmd, opt) == OK)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200332 {
333 /* store the size we ended up with */
334 vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols);
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200335 }
336 else
337 {
Bram Moolenaard85f2712017-07-28 21:51:57 +0200338 free_terminal(curbuf);
Bram Moolenaar61a66052017-07-22 18:39:00 +0200339
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200340 /* Wiping out the buffer will also close the window and call
341 * free_terminal(). */
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200342 do_buffer(DOBUF_WIPE, DOBUF_CURRENT, FORWARD, 0, TRUE);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200343 }
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200344}
345
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200346/*
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200347 * ":terminal": open a terminal window and execute a job in it.
348 */
349 void
350ex_terminal(exarg_T *eap)
351{
352 jobopt_T opt;
353
354 init_job_options(&opt);
Bram Moolenaarcfcc0222017-08-05 17:13:48 +0200355
356 if (eap->addr_count == 2)
357 {
358 opt.jo_term_rows = eap->line1;
359 opt.jo_term_cols = eap->line2;
360 }
361 else if (eap->addr_count == 1)
362 {
363 if (cmdmod.split & WSP_VERT)
364 opt.jo_term_cols = eap->line2;
365 else
366 opt.jo_term_rows = eap->line2;
367 }
368 /* TODO: get more options from before the command */
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +0200369
370 term_start(eap->arg, &opt);
371}
372
373/*
Bram Moolenaar63ecdda2017-07-28 22:29:35 +0200374 * Free the scrollback buffer for "term".
375 */
376 static void
377free_scrollback(term_T *term)
378{
379 int i;
380
381 for (i = 0; i < term->tl_scrollback.ga_len; ++i)
382 vim_free(((sb_line_T *)term->tl_scrollback.ga_data + i)->sb_cells);
383 ga_clear(&term->tl_scrollback);
384}
385
386/*
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200387 * Free a terminal and everything it refers to.
388 * Kills the job if there is one.
389 * Called when wiping out a buffer.
390 */
391 void
Bram Moolenaard85f2712017-07-28 21:51:57 +0200392free_terminal(buf_T *buf)
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200393{
Bram Moolenaard85f2712017-07-28 21:51:57 +0200394 term_T *term = buf->b_term;
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200395 term_T *tp;
396
397 if (term == NULL)
398 return;
399 if (first_term == term)
400 first_term = term->tl_next;
401 else
402 for (tp = first_term; tp->tl_next != NULL; tp = tp->tl_next)
403 if (tp->tl_next == term)
404 {
405 tp->tl_next = term->tl_next;
406 break;
407 }
408
409 if (term->tl_job != NULL)
410 {
Bram Moolenaar61a66052017-07-22 18:39:00 +0200411 if (term->tl_job->jv_status != JOB_ENDED
412 && term->tl_job->jv_status != JOB_FAILED)
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200413 job_stop(term->tl_job, NULL, "kill");
414 job_unref(term->tl_job);
415 }
416
Bram Moolenaar63ecdda2017-07-28 22:29:35 +0200417 free_scrollback(term);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200418
419 term_free_vterm(term);
Bram Moolenaar21554412017-07-24 21:44:43 +0200420 vim_free(term->tl_title);
421 vim_free(term->tl_status_text);
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200422 vim_free(term);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200423 buf->b_term = NULL;
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200424}
425
426/*
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200427 * Write job output "msg[len]" to the vterm.
428 */
429 static void
430term_write_job_output(term_T *term, char_u *msg, size_t len)
431{
432 VTerm *vterm = term->tl_vterm;
433 char_u *p;
434 size_t done;
435 size_t len_now;
436
437 for (done = 0; done < len; done += len_now)
438 {
439 for (p = msg + done; p < msg + len; )
440 {
441 if (*p == NL)
442 break;
Bram Moolenaara1b5b092017-07-26 21:29:34 +0200443 p += utf_ptr2len_len(p, (int)(len - (p - msg)));
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200444 }
445 len_now = p - msg - done;
446 vterm_input_write(vterm, (char *)msg + done, len_now);
447 if (p < msg + len && *p == NL)
448 {
449 /* Convert NL to CR-NL, that appears to work best. */
450 vterm_input_write(vterm, "\r\n", 2);
451 ++len_now;
452 }
453 }
454
455 /* this invokes the damage callbacks */
456 vterm_screen_flush_damage(vterm_obtain_screen(vterm));
457}
458
Bram Moolenaar1c844932017-07-24 23:36:41 +0200459 static void
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200460update_cursor(term_T *term, int redraw)
Bram Moolenaar1c844932017-07-24 23:36:41 +0200461{
Bram Moolenaar392d1bf2017-07-31 21:18:58 +0200462 if (term->tl_terminal_mode)
463 return;
Bram Moolenaar1c844932017-07-24 23:36:41 +0200464 setcursor();
Bram Moolenaar4cc93dc2017-07-26 21:49:37 +0200465 if (redraw && term->tl_buffer == curbuf)
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200466 {
Bram Moolenaar4cc93dc2017-07-26 21:49:37 +0200467 if (term->tl_cursor_visible)
468 cursor_on();
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200469 out_flush();
Bram Moolenaar1c844932017-07-24 23:36:41 +0200470#ifdef FEAT_GUI
Bram Moolenaar12d93ee2017-07-30 19:02:02 +0200471 if (gui.in_use)
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200472 gui_update_cursor(FALSE, FALSE);
Bram Moolenaar1c844932017-07-24 23:36:41 +0200473#endif
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200474 }
Bram Moolenaar1c844932017-07-24 23:36:41 +0200475}
476
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200477/*
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200478 * Invoked when "msg" output from a job was received. Write it to the terminal
479 * of "buffer".
480 */
481 void
482write_to_term(buf_T *buffer, char_u *msg, channel_T *channel)
483{
484 size_t len = STRLEN(msg);
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200485 term_T *term = buffer->b_term;
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200486
Bram Moolenaard85f2712017-07-28 21:51:57 +0200487 if (term->tl_vterm == NULL)
488 {
Bram Moolenaar2f3a90a2017-08-03 14:49:29 +0200489 ch_log(channel, "NOT writing %d bytes to terminal", (int)len);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200490 return;
491 }
Bram Moolenaar2f3a90a2017-08-03 14:49:29 +0200492 ch_log(channel, "writing %d bytes to terminal", (int)len);
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200493 term_write_job_output(term, msg, len);
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200494
Bram Moolenaar392d1bf2017-07-31 21:18:58 +0200495 if (!term->tl_terminal_mode)
496 {
497 /* TODO: only update once in a while. */
498 update_screen(0);
499 update_cursor(term, TRUE);
500 }
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200501}
502
503/*
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200504 * Send a mouse position and click to the vterm
505 */
506 static int
507term_send_mouse(VTerm *vterm, int button, int pressed)
508{
509 VTermModifier mod = VTERM_MOD_NONE;
510
511 vterm_mouse_move(vterm, mouse_row - W_WINROW(curwin),
512 mouse_col - W_WINCOL(curwin), mod);
513 vterm_mouse_button(vterm, button, pressed, mod);
514 return TRUE;
515}
516
517/*
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200518 * Convert typed key "c" into bytes to send to the job.
519 * Return the number of bytes in "buf".
520 */
521 static int
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200522term_convert_key(term_T *term, int c, char *buf)
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200523{
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200524 VTerm *vterm = term->tl_vterm;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200525 VTermKey key = VTERM_KEY_NONE;
526 VTermModifier mod = VTERM_MOD_NONE;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200527 int mouse = FALSE;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200528
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200529 switch (c)
530 {
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200531 case CAR: key = VTERM_KEY_ENTER; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200532 case ESC: key = VTERM_KEY_ESCAPE; break;
Bram Moolenaare906ae82017-07-21 21:10:01 +0200533 /* VTERM_KEY_BACKSPACE becomes 0x7f DEL */
534 case K_BS: c = BS; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200535 case K_DEL: key = VTERM_KEY_DEL; break;
536 case K_DOWN: key = VTERM_KEY_DOWN; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200537 case K_S_DOWN: mod = VTERM_MOD_SHIFT;
538 key = VTERM_KEY_DOWN; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200539 case K_END: key = VTERM_KEY_END; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200540 case K_S_END: mod = VTERM_MOD_SHIFT;
541 key = VTERM_KEY_END; break;
542 case K_C_END: mod = VTERM_MOD_CTRL;
543 key = VTERM_KEY_END; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200544 case K_F10: key = VTERM_KEY_FUNCTION(10); break;
545 case K_F11: key = VTERM_KEY_FUNCTION(11); break;
546 case K_F12: key = VTERM_KEY_FUNCTION(12); break;
547 case K_F1: key = VTERM_KEY_FUNCTION(1); break;
548 case K_F2: key = VTERM_KEY_FUNCTION(2); break;
549 case K_F3: key = VTERM_KEY_FUNCTION(3); break;
550 case K_F4: key = VTERM_KEY_FUNCTION(4); break;
551 case K_F5: key = VTERM_KEY_FUNCTION(5); break;
552 case K_F6: key = VTERM_KEY_FUNCTION(6); break;
553 case K_F7: key = VTERM_KEY_FUNCTION(7); break;
554 case K_F8: key = VTERM_KEY_FUNCTION(8); break;
555 case K_F9: key = VTERM_KEY_FUNCTION(9); break;
556 case K_HOME: key = VTERM_KEY_HOME; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200557 case K_S_HOME: mod = VTERM_MOD_SHIFT;
558 key = VTERM_KEY_HOME; break;
559 case K_C_HOME: mod = VTERM_MOD_CTRL;
560 key = VTERM_KEY_HOME; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200561 case K_INS: key = VTERM_KEY_INS; break;
562 case K_K0: key = VTERM_KEY_KP_0; break;
563 case K_K1: key = VTERM_KEY_KP_1; break;
564 case K_K2: key = VTERM_KEY_KP_2; break;
565 case K_K3: key = VTERM_KEY_KP_3; break;
566 case K_K4: key = VTERM_KEY_KP_4; break;
567 case K_K5: key = VTERM_KEY_KP_5; break;
568 case K_K6: key = VTERM_KEY_KP_6; break;
569 case K_K7: key = VTERM_KEY_KP_7; break;
570 case K_K8: key = VTERM_KEY_KP_8; break;
571 case K_K9: key = VTERM_KEY_KP_9; break;
572 case K_KDEL: key = VTERM_KEY_DEL; break; /* TODO */
573 case K_KDIVIDE: key = VTERM_KEY_KP_DIVIDE; break;
574 case K_KEND: key = VTERM_KEY_KP_1; break; /* TODO */
575 case K_KENTER: key = VTERM_KEY_KP_ENTER; break;
576 case K_KHOME: key = VTERM_KEY_KP_7; break; /* TODO */
577 case K_KINS: key = VTERM_KEY_KP_0; break; /* TODO */
578 case K_KMINUS: key = VTERM_KEY_KP_MINUS; break;
579 case K_KMULTIPLY: key = VTERM_KEY_KP_MULT; break;
580 case K_KPAGEDOWN: key = VTERM_KEY_KP_3; break; /* TODO */
581 case K_KPAGEUP: key = VTERM_KEY_KP_9; break; /* TODO */
582 case K_KPLUS: key = VTERM_KEY_KP_PLUS; break;
583 case K_KPOINT: key = VTERM_KEY_KP_PERIOD; break;
584 case K_LEFT: key = VTERM_KEY_LEFT; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200585 case K_S_LEFT: mod = VTERM_MOD_SHIFT;
586 key = VTERM_KEY_LEFT; break;
587 case K_C_LEFT: mod = VTERM_MOD_CTRL;
588 key = VTERM_KEY_LEFT; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200589 case K_PAGEDOWN: key = VTERM_KEY_PAGEDOWN; break;
590 case K_PAGEUP: key = VTERM_KEY_PAGEUP; break;
591 case K_RIGHT: key = VTERM_KEY_RIGHT; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200592 case K_S_RIGHT: mod = VTERM_MOD_SHIFT;
593 key = VTERM_KEY_RIGHT; break;
594 case K_C_RIGHT: mod = VTERM_MOD_CTRL;
595 key = VTERM_KEY_RIGHT; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200596 case K_UP: key = VTERM_KEY_UP; break;
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200597 case K_S_UP: mod = VTERM_MOD_SHIFT;
598 key = VTERM_KEY_UP; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200599 case TAB: key = VTERM_KEY_TAB; break;
Bram Moolenaare825d8b2017-07-19 23:20:19 +0200600
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200601 case K_MOUSEUP: mouse = term_send_mouse(vterm, 5, 1); break;
602 case K_MOUSEDOWN: mouse = term_send_mouse(vterm, 4, 1); break;
603 case K_MOUSELEFT: /* TODO */ return 0;
604 case K_MOUSERIGHT: /* TODO */ return 0;
Bram Moolenaare825d8b2017-07-19 23:20:19 +0200605
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200606 case K_LEFTMOUSE:
607 case K_LEFTMOUSE_NM: mouse = term_send_mouse(vterm, 1, 1); break;
608 case K_LEFTDRAG: mouse = term_send_mouse(vterm, 1, 1); break;
609 case K_LEFTRELEASE:
610 case K_LEFTRELEASE_NM: mouse = term_send_mouse(vterm, 1, 0); break;
611 case K_MIDDLEMOUSE: mouse = term_send_mouse(vterm, 2, 1); break;
612 case K_MIDDLEDRAG: mouse = term_send_mouse(vterm, 2, 1); break;
613 case K_MIDDLERELEASE: mouse = term_send_mouse(vterm, 2, 0); break;
614 case K_RIGHTMOUSE: mouse = term_send_mouse(vterm, 3, 1); break;
615 case K_RIGHTDRAG: mouse = term_send_mouse(vterm, 3, 1); break;
616 case K_RIGHTRELEASE: mouse = term_send_mouse(vterm, 3, 0); break;
617 case K_X1MOUSE: /* TODO */ return 0;
618 case K_X1DRAG: /* TODO */ return 0;
619 case K_X1RELEASE: /* TODO */ return 0;
620 case K_X2MOUSE: /* TODO */ return 0;
621 case K_X2DRAG: /* TODO */ return 0;
622 case K_X2RELEASE: /* TODO */ return 0;
Bram Moolenaare825d8b2017-07-19 23:20:19 +0200623
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200624 case K_IGNORE: return 0;
625 case K_NOP: return 0;
626 case K_UNDO: return 0;
627 case K_HELP: return 0;
628 case K_XF1: key = VTERM_KEY_FUNCTION(1); break;
629 case K_XF2: key = VTERM_KEY_FUNCTION(2); break;
630 case K_XF3: key = VTERM_KEY_FUNCTION(3); break;
631 case K_XF4: key = VTERM_KEY_FUNCTION(4); break;
632 case K_SELECT: return 0;
633#ifdef FEAT_GUI
634 case K_VER_SCROLLBAR: return 0;
635 case K_HOR_SCROLLBAR: return 0;
636#endif
637#ifdef FEAT_GUI_TABLINE
638 case K_TABLINE: return 0;
639 case K_TABMENU: return 0;
640#endif
641#ifdef FEAT_NETBEANS_INTG
642 case K_F21: key = VTERM_KEY_FUNCTION(21); break;
643#endif
644#ifdef FEAT_DND
645 case K_DROP: return 0;
646#endif
647#ifdef FEAT_AUTOCMD
648 case K_CURSORHOLD: return 0;
649#endif
650 case K_PS: vterm_keyboard_start_paste(vterm); return 0;
651 case K_PE: vterm_keyboard_end_paste(vterm); return 0;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200652 }
653
654 /*
655 * Convert special keys to vterm keys:
656 * - Write keys to vterm: vterm_keyboard_key()
657 * - Write output to channel.
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200658 * TODO: use mod_mask
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200659 */
660 if (key != VTERM_KEY_NONE)
661 /* Special key, let vterm convert it. */
662 vterm_keyboard_key(vterm, key, mod);
Bram Moolenaar6e1ef282017-07-29 22:23:40 +0200663 else if (!mouse)
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200664 /* Normal character, let vterm convert it. */
665 vterm_keyboard_unichar(vterm, c, mod);
666
667 /* Read back the converted escape sequence. */
Bram Moolenaara1b5b092017-07-26 21:29:34 +0200668 return (int)vterm_output_read(vterm, buf, KEY_BUF_LEN);
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200669}
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200670
Bram Moolenaar938783d2017-07-16 20:13:26 +0200671/*
Bram Moolenaarb000e322017-07-30 19:38:21 +0200672 * Return TRUE if the job for "term" is still running.
Bram Moolenaard85f2712017-07-28 21:51:57 +0200673 */
Bram Moolenaar94053a52017-08-01 21:44:33 +0200674 int
Bram Moolenaard85f2712017-07-28 21:51:57 +0200675term_job_running(term_T *term)
676{
Bram Moolenaar1e8340b2017-07-29 15:53:39 +0200677 /* Also consider the job finished when the channel is closed, to avoid a
678 * race condition when updating the title. */
Bram Moolenaarb4a67212017-08-03 19:22:36 +0200679 return term != NULL
680 && term->tl_job != NULL
Bram Moolenaar1e8340b2017-07-29 15:53:39 +0200681 && term->tl_job->jv_status == JOB_STARTED
682 && channel_is_open(term->tl_job->jv_channel);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200683}
684
685/*
Bram Moolenaar423802d2017-07-30 16:52:24 +0200686 * Add the last line of the scrollback buffer to the buffer in the window.
687 */
688 static void
689add_scrollback_line_to_buffer(term_T *term)
690{
691 linenr_T lnum = term->tl_scrollback.ga_len - 1;
692 sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data + lnum;
693 garray_T ga;
694 int c;
695 int col;
696 int i;
697
698 ga_init2(&ga, 1, 100);
699 for (col = 0; col < line->sb_cols; col += line->sb_cells[col].width)
700 {
701 if (ga_grow(&ga, MB_MAXBYTES) == FAIL)
702 goto failed;
703 for (i = 0; (c = line->sb_cells[col].chars[i]) > 0 || i == 0; ++i)
704 ga.ga_len += mb_char2bytes(c == NUL ? ' ' : c,
705 (char_u *)ga.ga_data + ga.ga_len);
706 }
707 if (ga_grow(&ga, 1) == FAIL)
708 goto failed;
709 *((char_u *)ga.ga_data + ga.ga_len) = NUL;
710 ml_append_buf(term->tl_buffer, lnum, ga.ga_data, ga.ga_len + 1, FALSE);
711
712 if (lnum == 0)
713 {
714 /* Delete the empty line that was in the empty buffer. */
715 curbuf = term->tl_buffer;
716 ml_delete(2, FALSE);
717 curbuf = curwin->w_buffer;
718 }
719
720failed:
721 ga_clear(&ga);
722}
723
724/*
725 * Add the current lines of the terminal to scrollback and to the buffer.
726 * Called after the job has ended and when switching to Terminal mode.
727 */
728 static void
729move_terminal_to_buffer(term_T *term)
730{
731 win_T *wp;
732 int len;
733 int lines_skipped = 0;
734 VTermPos pos;
735 VTermScreenCell cell;
736 VTermScreenCell *p;
Bram Moolenaar8e5eece2017-08-04 20:29:53 +0200737 VTermScreen *screen;
Bram Moolenaar423802d2017-07-30 16:52:24 +0200738
Bram Moolenaar8e5eece2017-08-04 20:29:53 +0200739 if (term->tl_vterm == NULL)
740 return;
741 screen = vterm_obtain_screen(term->tl_vterm);
Bram Moolenaar423802d2017-07-30 16:52:24 +0200742 for (pos.row = 0; pos.row < term->tl_rows; ++pos.row)
743 {
744 len = 0;
745 for (pos.col = 0; pos.col < term->tl_cols; ++pos.col)
746 if (vterm_screen_get_cell(screen, pos, &cell) != 0
747 && cell.chars[0] != NUL)
748 len = pos.col + 1;
749
750 if (len == 0)
751 ++lines_skipped;
752 else
753 {
754 while (lines_skipped > 0)
755 {
756 /* Line was skipped, add an empty line. */
757 --lines_skipped;
758 if (ga_grow(&term->tl_scrollback, 1) == OK)
759 {
760 sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data
761 + term->tl_scrollback.ga_len;
762
763 line->sb_cols = 0;
764 line->sb_cells = NULL;
765 ++term->tl_scrollback.ga_len;
766
767 add_scrollback_line_to_buffer(term);
768 }
769 }
770
771 p = (VTermScreenCell *)alloc((int)sizeof(VTermScreenCell) * len);
772 if (p != NULL && ga_grow(&term->tl_scrollback, 1) == OK)
773 {
774 sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data
775 + term->tl_scrollback.ga_len;
776
777 for (pos.col = 0; pos.col < len; ++pos.col)
778 {
779 if (vterm_screen_get_cell(screen, pos, &cell) == 0)
780 vim_memset(p + pos.col, 0, sizeof(cell));
781 else
782 p[pos.col] = cell;
783 }
784 line->sb_cols = len;
785 line->sb_cells = p;
786 ++term->tl_scrollback.ga_len;
787
788 add_scrollback_line_to_buffer(term);
789 }
790 else
791 vim_free(p);
792 }
793 }
794
795 FOR_ALL_WINDOWS(wp)
796 {
797 if (wp->w_buffer == term->tl_buffer)
798 {
799 wp->w_cursor.lnum = term->tl_buffer->b_ml.ml_line_count;
800 wp->w_cursor.col = 0;
801 wp->w_valid = 0;
802 redraw_win_later(wp, NOT_VALID);
803 }
804 }
805}
806
807 static void
808set_terminal_mode(term_T *term, int on)
809{
810 term->tl_terminal_mode = on;
811 vim_free(term->tl_status_text);
812 term->tl_status_text = NULL;
813 if (term->tl_buffer == curbuf)
814 maketitle();
815}
816
817/*
818 * Called after the job if finished and Terminal mode is not active:
819 * Move the vterm contents into the scrollback buffer and free the vterm.
820 */
821 static void
822cleanup_vterm(term_T *term)
823{
824 move_terminal_to_buffer(term);
825 term_free_vterm(term);
826 set_terminal_mode(term, FALSE);
827}
828
829/*
830 * Switch from sending keys to the job to Terminal-Normal mode.
831 * Suspends updating the terminal window.
832 */
833 static void
834term_enter_terminal_mode()
835{
836 term_T *term = curbuf->b_term;
837
838 /* Append the current terminal contents to the buffer. */
839 move_terminal_to_buffer(term);
840
841 set_terminal_mode(term, TRUE);
842}
843
844/*
845 * Returns TRUE if the current window contains a terminal and we are in
846 * Terminal-Normal mode.
847 */
848 int
849term_in_terminal_mode()
850{
851 term_T *term = curbuf->b_term;
852
853 return term != NULL && term->tl_terminal_mode;
854}
855
856/*
857 * Switch from Terminal-Normal mode to sending keys to the job.
858 * Restores updating the terminal window.
859 */
860 void
861term_leave_terminal_mode()
862{
863 term_T *term = curbuf->b_term;
864 sb_line_T *line;
865 garray_T *gap;
866
867 /* Remove the terminal contents from the scrollback and the buffer. */
868 gap = &term->tl_scrollback;
869 while (curbuf->b_ml.ml_line_count > term->tl_scrollback_scrolled)
870 {
871 ml_delete(curbuf->b_ml.ml_line_count, FALSE);
872 line = (sb_line_T *)gap->ga_data + gap->ga_len - 1;
873 vim_free(line->sb_cells);
874 --gap->ga_len;
875 if (gap->ga_len == 0)
876 break;
877 }
878 check_cursor();
879
880 set_terminal_mode(term, FALSE);
881
882 if (term->tl_channel_closed)
883 cleanup_vterm(term);
884 redraw_buf_and_status_later(curbuf, NOT_VALID);
885}
886
887/*
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +0200888 * Get a key from the user without mapping.
889 * TODO: use terminal mode mappings.
890 */
891 static int
892term_vgetc()
893{
894 int c;
895
896 ++no_mapping;
897 ++allow_keys;
898 got_int = FALSE;
899 c = vgetc();
Bram Moolenaar43c007f2017-07-30 17:45:37 +0200900 got_int = FALSE;
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +0200901 --no_mapping;
902 --allow_keys;
903 return c;
904}
905
906/*
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200907 * Send keys to terminal.
Bram Moolenaar69198192017-08-05 14:10:48 +0200908 * Return FAIL when the key needs to be handled in Normal mode.
909 * Return OK when the key was dropped or sent to the terminal.
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200910 */
Bram Moolenaar98fd66d2017-08-05 19:34:47 +0200911 int
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200912send_keys_to_term(term_T *term, int c, int typed)
913{
914 char msg[KEY_BUF_LEN];
915 size_t len;
916 static int mouse_was_outside = FALSE;
917 int dragging_outside = FALSE;
918
919 /* Catch keys that need to be handled as in Normal mode. */
920 switch (c)
921 {
922 case NUL:
923 case K_ZERO:
924 if (typed)
925 stuffcharReadbuff(c);
926 return FAIL;
927
928 case K_IGNORE:
929 return FAIL;
930
931 case K_LEFTDRAG:
932 case K_MIDDLEDRAG:
933 case K_RIGHTDRAG:
934 case K_X1DRAG:
935 case K_X2DRAG:
936 dragging_outside = mouse_was_outside;
937 /* FALLTHROUGH */
938 case K_LEFTMOUSE:
939 case K_LEFTMOUSE_NM:
940 case K_LEFTRELEASE:
941 case K_LEFTRELEASE_NM:
942 case K_MIDDLEMOUSE:
943 case K_MIDDLERELEASE:
944 case K_RIGHTMOUSE:
945 case K_RIGHTRELEASE:
946 case K_X1MOUSE:
947 case K_X1RELEASE:
948 case K_X2MOUSE:
949 case K_X2RELEASE:
Bram Moolenaar98fd66d2017-08-05 19:34:47 +0200950
951 case K_MOUSEUP:
952 case K_MOUSEDOWN:
953 case K_MOUSELEFT:
954 case K_MOUSERIGHT:
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200955 if (mouse_row < W_WINROW(curwin)
956 || mouse_row >= (W_WINROW(curwin) + curwin->w_height)
957 || mouse_col < W_WINCOL(curwin)
958 || mouse_col >= W_ENDCOL(curwin)
959 || dragging_outside)
960 {
Bram Moolenaar98fd66d2017-08-05 19:34:47 +0200961 /* click or scroll outside the current window */
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200962 if (typed)
963 {
964 stuffcharReadbuff(c);
965 mouse_was_outside = TRUE;
966 }
967 return FAIL;
968 }
969 }
970 if (typed)
971 mouse_was_outside = FALSE;
972
973 /* Convert the typed key to a sequence of bytes for the job. */
974 len = term_convert_key(term, c, msg);
975 if (len > 0)
976 /* TODO: if FAIL is returned, stop? */
977 channel_send(term->tl_job->jv_channel, PART_IN,
978 (char_u *)msg, (int)len, NULL);
979
980 return OK;
981}
982
Bram Moolenaar0e23e9c2017-07-30 18:47:19 +0200983 static void
984position_cursor(win_T *wp, VTermPos *pos)
985{
986 wp->w_wrow = MIN(pos->row, MAX(0, wp->w_height - 1));
987 wp->w_wcol = MIN(pos->col, MAX(0, wp->w_width - 1));
988 wp->w_valid |= (VALID_WCOL|VALID_WROW);
989}
990
Bram Moolenaarc6df10e2017-07-29 20:15:08 +0200991/*
Bram Moolenaarc9456ce2017-07-30 21:46:04 +0200992 * Handle CTRL-W "": send register contents to the job.
993 */
994 static void
995term_paste_register(int prev_c UNUSED)
996{
997 int c;
998 list_T *l;
999 listitem_T *item;
1000 long reglen = 0;
1001 int type;
1002
1003#ifdef FEAT_CMDL_INFO
1004 if (add_to_showcmd(prev_c))
1005 if (add_to_showcmd('"'))
1006 out_flush();
1007#endif
1008 c = term_vgetc();
1009#ifdef FEAT_CMDL_INFO
1010 clear_showcmd();
1011#endif
1012
1013 /* CTRL-W "= prompt for expression to evaluate. */
1014 if (c == '=' && get_expr_register() != '=')
1015 return;
1016
1017 l = (list_T *)get_reg_contents(c, GREG_LIST);
1018 if (l != NULL)
1019 {
1020 type = get_reg_type(c, &reglen);
1021 for (item = l->lv_first; item != NULL; item = item->li_next)
1022 {
1023 char_u *s = get_tv_string(&item->li_tv);
1024
1025 channel_send(curbuf->b_term->tl_job->jv_channel, PART_IN,
1026 s, STRLEN(s), NULL);
1027 if (item->li_next != NULL || type == MLINE)
1028 channel_send(curbuf->b_term->tl_job->jv_channel, PART_IN,
1029 (char_u *)"\r", 1, NULL);
1030 }
1031 list_free(l);
1032 }
1033}
1034
1035/*
Bram Moolenaar423802d2017-07-30 16:52:24 +02001036 * Returns TRUE if the current window contains a terminal and we are sending
1037 * keys to the job.
1038 */
1039 int
1040term_use_loop()
1041{
1042 term_T *term = curbuf->b_term;
1043
1044 return term != NULL
1045 && !term->tl_terminal_mode
1046 && term->tl_vterm != NULL
1047 && term_job_running(term);
1048}
1049
1050/*
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001051 * Wait for input and send it to the job.
1052 * Return when the start of a CTRL-W command is typed or anything else that
1053 * should be handled as a Normal mode command.
Bram Moolenaard85f2712017-07-28 21:51:57 +02001054 * Returns OK if a typed character is to be handled in Normal mode, FAIL if
1055 * the terminal was closed.
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001056 */
Bram Moolenaard85f2712017-07-28 21:51:57 +02001057 int
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001058terminal_loop(void)
1059{
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001060 int c;
Bram Moolenaardbe948d2017-07-23 22:50:51 +02001061 int termkey = 0;
1062
1063 if (*curwin->w_p_tk != NUL)
1064 termkey = string_to_key(curwin->w_p_tk, TRUE);
Bram Moolenaar0e23e9c2017-07-30 18:47:19 +02001065 position_cursor(curwin, &curbuf->b_term->tl_cursor_pos);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001066
1067 for (;;)
1068 {
1069 /* TODO: skip screen update when handling a sequence of keys. */
Bram Moolenaar43c007f2017-07-30 17:45:37 +02001070 /* Repeat redrawing in case a message is received while redrawing. */
1071 while (curwin->w_redr_type != 0)
1072 update_screen(0);
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001073 update_cursor(curbuf->b_term, FALSE);
Bram Moolenaar423802d2017-07-30 16:52:24 +02001074
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001075 c = term_vgetc();
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02001076 if (!term_use_loop())
Bram Moolenaard85f2712017-07-28 21:51:57 +02001077 /* job finished while waiting for a character */
1078 break;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001079
Bram Moolenaarfae42832017-08-01 22:24:26 +02001080#ifdef UNIX
1081 may_send_sigint(c, curbuf->b_term->tl_job->jv_pid, 0);
1082#endif
1083#ifdef WIN3264
1084 if (c == Ctrl_C)
1085 /* We don't know if the job can handle CTRL-C itself or not, this
1086 * may kill the shell instead of killing the command running in the
1087 * shell. */
Bram Moolenaard8dc1792017-08-03 11:55:21 +02001088 mch_stop_job(curbuf->b_term->tl_job, (char_u *)"quit");
Bram Moolenaarfae42832017-08-01 22:24:26 +02001089#endif
1090
Bram Moolenaar69198192017-08-05 14:10:48 +02001091 if (c == (termkey == 0 ? Ctrl_W : termkey) || c == Ctrl_BSL)
Bram Moolenaardbe948d2017-07-23 22:50:51 +02001092 {
Bram Moolenaarc9456ce2017-07-30 21:46:04 +02001093 int prev_c = c;
1094
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001095#ifdef FEAT_CMDL_INFO
1096 if (add_to_showcmd(c))
1097 out_flush();
1098#endif
1099 c = term_vgetc();
1100#ifdef FEAT_CMDL_INFO
1101 clear_showcmd();
1102#endif
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02001103 if (!term_use_loop())
Bram Moolenaard85f2712017-07-28 21:51:57 +02001104 /* job finished while waiting for a character */
1105 break;
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001106
Bram Moolenaar69198192017-08-05 14:10:48 +02001107 if (prev_c == Ctrl_BSL)
1108 {
1109 if (c == Ctrl_N)
1110 /* CTRL-\ CTRL-N : execute one Normal mode command. */
1111 return OK;
1112 /* Send both keys to the terminal. */
1113 send_keys_to_term(curbuf->b_term, prev_c, TRUE);
1114 }
1115 else if (termkey == 0 && c == '.')
Bram Moolenaar423802d2017-07-30 16:52:24 +02001116 {
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001117 /* "CTRL-W .": send CTRL-W to the job */
1118 c = Ctrl_W;
Bram Moolenaar423802d2017-07-30 16:52:24 +02001119 }
Bram Moolenaarc9456ce2017-07-30 21:46:04 +02001120 else if (c == 'N')
Bram Moolenaar423802d2017-07-30 16:52:24 +02001121 {
1122 term_enter_terminal_mode();
1123 return FAIL;
1124 }
Bram Moolenaarc9456ce2017-07-30 21:46:04 +02001125 else if (c == '"')
1126 {
1127 term_paste_register(prev_c);
1128 continue;
1129 }
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001130 else if (termkey == 0 || c != termkey)
1131 {
1132 stuffcharReadbuff(Ctrl_W);
1133 stuffcharReadbuff(c);
Bram Moolenaard85f2712017-07-28 21:51:57 +02001134 return OK;
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +02001135 }
Bram Moolenaardbe948d2017-07-23 22:50:51 +02001136 }
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001137 if (send_keys_to_term(curbuf->b_term, c, TRUE) != OK)
1138 return OK;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001139 }
Bram Moolenaard85f2712017-07-28 21:51:57 +02001140 return FAIL;
Bram Moolenaar1f2903c2017-07-23 19:51:01 +02001141}
1142
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001143/*
1144 * Called when a job has finished.
Bram Moolenaar423802d2017-07-30 16:52:24 +02001145 * This updates the title and status, but does not close the vter, because
1146 * there might still be pending output in the channel.
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001147 */
1148 void
1149term_job_ended(job_T *job)
1150{
1151 term_T *term;
1152 int did_one = FALSE;
1153
1154 for (term = first_term; term != NULL; term = term->tl_next)
1155 if (term->tl_job == job)
1156 {
1157 vim_free(term->tl_title);
1158 term->tl_title = NULL;
1159 vim_free(term->tl_status_text);
1160 term->tl_status_text = NULL;
1161 redraw_buf_and_status_later(term->tl_buffer, VALID);
1162 did_one = TRUE;
1163 }
1164 if (did_one)
1165 redraw_statuslines();
1166 if (curbuf->b_term != NULL)
1167 {
1168 if (curbuf->b_term->tl_job == job)
1169 maketitle();
1170 update_cursor(curbuf->b_term, TRUE);
1171 }
1172}
1173
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001174 static void
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001175may_toggle_cursor(term_T *term)
1176{
1177 if (curbuf == term->tl_buffer)
1178 {
1179 if (term->tl_cursor_visible)
1180 cursor_on();
1181 else
1182 cursor_off();
1183 }
1184}
1185
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001186 static int
1187handle_damage(VTermRect rect, void *user)
1188{
1189 term_T *term = (term_T *)user;
1190
1191 term->tl_dirty_row_start = MIN(term->tl_dirty_row_start, rect.start_row);
1192 term->tl_dirty_row_end = MAX(term->tl_dirty_row_end, rect.end_row);
1193 redraw_buf_later(term->tl_buffer, NOT_VALID);
1194 return 1;
1195}
1196
1197 static int
1198handle_moverect(VTermRect dest UNUSED, VTermRect src UNUSED, void *user)
1199{
1200 term_T *term = (term_T *)user;
1201
1202 /* TODO */
1203 redraw_buf_later(term->tl_buffer, NOT_VALID);
1204 return 1;
1205}
1206
1207 static int
1208handle_movecursor(
1209 VTermPos pos,
1210 VTermPos oldpos UNUSED,
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001211 int visible,
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001212 void *user)
1213{
1214 term_T *term = (term_T *)user;
1215 win_T *wp;
Bram Moolenaar22aad2f2017-07-30 18:19:46 +02001216
1217 term->tl_cursor_pos = pos;
1218 term->tl_cursor_visible = visible;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001219
1220 FOR_ALL_WINDOWS(wp)
1221 {
1222 if (wp->w_buffer == term->tl_buffer)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001223 position_cursor(wp, &pos);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001224 }
Bram Moolenaar392d1bf2017-07-31 21:18:58 +02001225 if (term->tl_buffer == curbuf && !term->tl_terminal_mode)
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001226 {
1227 may_toggle_cursor(term);
Bram Moolenaar12d93ee2017-07-30 19:02:02 +02001228 update_cursor(term, term->tl_cursor_visible);
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001229 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001230
1231 return 1;
1232}
1233
Bram Moolenaar21554412017-07-24 21:44:43 +02001234 static int
1235handle_settermprop(
1236 VTermProp prop,
1237 VTermValue *value,
1238 void *user)
1239{
1240 term_T *term = (term_T *)user;
1241
1242 switch (prop)
1243 {
1244 case VTERM_PROP_TITLE:
1245 vim_free(term->tl_title);
1246 term->tl_title = vim_strsave((char_u *)value->string);
1247 vim_free(term->tl_status_text);
1248 term->tl_status_text = NULL;
1249 if (term == curbuf->b_term)
1250 maketitle();
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001251 break;
1252
1253 case VTERM_PROP_CURSORVISIBLE:
1254 term->tl_cursor_visible = value->boolean;
1255 may_toggle_cursor(term);
1256 out_flush();
1257 break;
1258
Bram Moolenaar21554412017-07-24 21:44:43 +02001259 default:
1260 break;
1261 }
Bram Moolenaarfc716d72017-07-25 23:08:47 +02001262 /* Always return 1, otherwise vterm doesn't store the value internally. */
1263 return 1;
Bram Moolenaar21554412017-07-24 21:44:43 +02001264}
1265
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001266/*
1267 * The job running in the terminal resized the terminal.
1268 */
1269 static int
1270handle_resize(int rows, int cols, void *user)
1271{
1272 term_T *term = (term_T *)user;
1273 win_T *wp;
1274
1275 term->tl_rows = rows;
1276 term->tl_cols = cols;
1277 FOR_ALL_WINDOWS(wp)
1278 {
1279 if (wp->w_buffer == term->tl_buffer)
1280 {
1281 win_setheight_win(rows, wp);
1282 win_setwidth_win(cols, wp);
1283 }
1284 }
1285
1286 redraw_buf_later(term->tl_buffer, NOT_VALID);
1287 return 1;
1288}
1289
Bram Moolenaard85f2712017-07-28 21:51:57 +02001290/*
1291 * Handle a line that is pushed off the top of the screen.
1292 */
1293 static int
1294handle_pushline(int cols, const VTermScreenCell *cells, void *user)
1295{
1296 term_T *term = (term_T *)user;
1297
1298 /* TODO: Limit the number of lines that are stored. */
1299 /* TODO: put the text in the buffer. */
1300 if (ga_grow(&term->tl_scrollback, 1) == OK)
1301 {
Bram Moolenaar696d00f2017-07-29 14:52:43 +02001302 VTermScreenCell *p = NULL;
1303 int len = 0;
1304 int i;
1305 sb_line_T *line;
Bram Moolenaard85f2712017-07-28 21:51:57 +02001306
1307 /* do not store empty cells at the end */
1308 for (i = 0; i < cols; ++i)
1309 if (cells[i].chars[0] != 0)
1310 len = i + 1;
1311
Bram Moolenaar696d00f2017-07-29 14:52:43 +02001312 if (len > 0)
1313 p = (VTermScreenCell *)alloc((int)sizeof(VTermScreenCell) * len);
Bram Moolenaard85f2712017-07-28 21:51:57 +02001314 if (p != NULL)
Bram Moolenaard85f2712017-07-28 21:51:57 +02001315 mch_memmove(p, cells, sizeof(VTermScreenCell) * len);
Bram Moolenaar696d00f2017-07-29 14:52:43 +02001316
1317 line = (sb_line_T *)term->tl_scrollback.ga_data
1318 + term->tl_scrollback.ga_len;
1319 line->sb_cols = len;
1320 line->sb_cells = p;
1321 ++term->tl_scrollback.ga_len;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001322 ++term->tl_scrollback_scrolled;
Bram Moolenaar423802d2017-07-30 16:52:24 +02001323
1324 add_scrollback_line_to_buffer(term);
Bram Moolenaard85f2712017-07-28 21:51:57 +02001325 }
1326 return 0; /* ignored */
1327}
1328
Bram Moolenaar21554412017-07-24 21:44:43 +02001329static VTermScreenCallbacks screen_callbacks = {
1330 handle_damage, /* damage */
1331 handle_moverect, /* moverect */
1332 handle_movecursor, /* movecursor */
1333 handle_settermprop, /* settermprop */
1334 NULL, /* bell */
1335 handle_resize, /* resize */
Bram Moolenaard85f2712017-07-28 21:51:57 +02001336 handle_pushline, /* sb_pushline */
Bram Moolenaar21554412017-07-24 21:44:43 +02001337 NULL /* sb_popline */
1338};
1339
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001340/*
Bram Moolenaard85f2712017-07-28 21:51:57 +02001341 * Called when a channel has been closed.
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001342 * If this was a channel for a terminal window then finish it up.
Bram Moolenaard85f2712017-07-28 21:51:57 +02001343 */
1344 void
1345term_channel_closed(channel_T *ch)
1346{
1347 term_T *term;
1348 int did_one = FALSE;
1349
1350 for (term = first_term; term != NULL; term = term->tl_next)
1351 if (term->tl_job == ch->ch_job)
1352 {
Bram Moolenaar423802d2017-07-30 16:52:24 +02001353 term->tl_channel_closed = TRUE;
1354
Bram Moolenaard85f2712017-07-28 21:51:57 +02001355 vim_free(term->tl_title);
1356 term->tl_title = NULL;
1357 vim_free(term->tl_status_text);
1358 term->tl_status_text = NULL;
1359
Bram Moolenaar423802d2017-07-30 16:52:24 +02001360 /* Unless in Terminal-Normal mode: clear the vterm. */
1361 if (!term->tl_terminal_mode)
1362 cleanup_vterm(term);
Bram Moolenaard85f2712017-07-28 21:51:57 +02001363
1364 redraw_buf_and_status_later(term->tl_buffer, NOT_VALID);
1365 did_one = TRUE;
1366 }
1367 if (did_one)
1368 {
1369 redraw_statuslines();
1370
1371 /* Need to break out of vgetc(). */
1372 ins_char_typebuf(K_IGNORE);
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02001373 typebuf_was_filled = TRUE;
Bram Moolenaard85f2712017-07-28 21:51:57 +02001374
Bram Moolenaar12d93ee2017-07-30 19:02:02 +02001375 term = curbuf->b_term;
1376 if (term != NULL)
Bram Moolenaard85f2712017-07-28 21:51:57 +02001377 {
Bram Moolenaar12d93ee2017-07-30 19:02:02 +02001378 if (term->tl_job == ch->ch_job)
Bram Moolenaard85f2712017-07-28 21:51:57 +02001379 maketitle();
Bram Moolenaar12d93ee2017-07-30 19:02:02 +02001380 update_cursor(term, term->tl_cursor_visible);
Bram Moolenaard85f2712017-07-28 21:51:57 +02001381 }
1382 }
1383}
1384
1385/*
Bram Moolenaareeac6772017-07-23 15:48:37 +02001386 * Reverse engineer the RGB value into a cterm color index.
1387 * First color is 1. Return 0 if no match found.
1388 */
1389 static int
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001390color2index(VTermColor *color, int fg, int *boldp)
Bram Moolenaareeac6772017-07-23 15:48:37 +02001391{
1392 int red = color->red;
1393 int blue = color->blue;
1394 int green = color->green;
1395
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02001396 /* The argument for lookup_color() is for the color_names[] table. */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001397 if (red == 0)
1398 {
1399 if (green == 0)
1400 {
1401 if (blue == 0)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001402 return lookup_color(0, fg, boldp) + 1; /* black */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001403 if (blue == 224)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001404 return lookup_color(1, fg, boldp) + 1; /* dark blue */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001405 }
1406 else if (green == 224)
1407 {
1408 if (blue == 0)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001409 return lookup_color(2, fg, boldp) + 1; /* dark green */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001410 if (blue == 224)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001411 return lookup_color(3, fg, boldp) + 1; /* dark cyan */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001412 }
1413 }
1414 else if (red == 224)
1415 {
1416 if (green == 0)
1417 {
1418 if (blue == 0)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001419 return lookup_color(4, fg, boldp) + 1; /* dark red */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001420 if (blue == 224)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001421 return lookup_color(5, fg, boldp) + 1; /* dark magenta */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001422 }
1423 else if (green == 224)
1424 {
1425 if (blue == 0)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001426 return lookup_color(6, fg, boldp) + 1; /* dark yellow / brown */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001427 if (blue == 224)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001428 return lookup_color(8, fg, boldp) + 1; /* white / light grey */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001429 }
1430 }
1431 else if (red == 128)
1432 {
1433 if (green == 128 && blue == 128)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001434 return lookup_color(12, fg, boldp) + 1; /* high intensity black / dark grey */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001435 }
1436 else if (red == 255)
1437 {
1438 if (green == 64)
1439 {
1440 if (blue == 64)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001441 return lookup_color(20, fg, boldp) + 1; /* light red */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001442 if (blue == 255)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001443 return lookup_color(22, fg, boldp) + 1; /* light magenta */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001444 }
1445 else if (green == 255)
1446 {
1447 if (blue == 64)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001448 return lookup_color(24, fg, boldp) + 1; /* yellow */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001449 if (blue == 255)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001450 return lookup_color(26, fg, boldp) + 1; /* white */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001451 }
1452 }
1453 else if (red == 64)
1454 {
1455 if (green == 64)
1456 {
1457 if (blue == 255)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001458 return lookup_color(14, fg, boldp) + 1; /* light blue */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001459 }
1460 else if (green == 255)
1461 {
1462 if (blue == 64)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001463 return lookup_color(16, fg, boldp) + 1; /* light green */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001464 if (blue == 255)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001465 return lookup_color(18, fg, boldp) + 1; /* light cyan */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001466 }
1467 }
1468 if (t_colors >= 256)
1469 {
1470 if (red == blue && red == green)
1471 {
1472 /* 24-color greyscale */
1473 static int cutoff[23] = {
1474 0x05, 0x10, 0x1B, 0x26, 0x31, 0x3C, 0x47, 0x52,
1475 0x5D, 0x68, 0x73, 0x7F, 0x8A, 0x95, 0xA0, 0xAB,
1476 0xB6, 0xC1, 0xCC, 0xD7, 0xE2, 0xED, 0xF9};
1477 int i;
1478
1479 for (i = 0; i < 23; ++i)
1480 if (red < cutoff[i])
1481 return i + 233;
1482 return 256;
1483 }
1484
1485 /* 216-color cube */
1486 return 17 + ((red + 25) / 0x33) * 36
1487 + ((green + 25) / 0x33) * 6
1488 + (blue + 25) / 0x33;
1489 }
1490 return 0;
1491}
1492
1493/*
1494 * Convert the attributes of a vterm cell into an attribute index.
1495 */
1496 static int
1497cell2attr(VTermScreenCell *cell)
1498{
1499 int attr = 0;
1500
1501 if (cell->attrs.bold)
1502 attr |= HL_BOLD;
1503 if (cell->attrs.underline)
1504 attr |= HL_UNDERLINE;
1505 if (cell->attrs.italic)
1506 attr |= HL_ITALIC;
1507 if (cell->attrs.strike)
1508 attr |= HL_STANDOUT;
1509 if (cell->attrs.reverse)
1510 attr |= HL_INVERSE;
Bram Moolenaareeac6772017-07-23 15:48:37 +02001511
1512#ifdef FEAT_GUI
1513 if (gui.in_use)
1514 {
Bram Moolenaar26af85d2017-07-23 16:45:10 +02001515 guicolor_T fg, bg;
1516
1517 fg = gui_mch_get_rgb_color(cell->fg.red, cell->fg.green, cell->fg.blue);
1518 bg = gui_mch_get_rgb_color(cell->bg.red, cell->bg.green, cell->bg.blue);
1519 return get_gui_attr_idx(attr, fg, bg);
Bram Moolenaareeac6772017-07-23 15:48:37 +02001520 }
1521 else
1522#endif
1523#ifdef FEAT_TERMGUICOLORS
1524 if (p_tgc)
1525 {
Bram Moolenaar065f41c2017-07-23 18:07:56 +02001526 guicolor_T fg, bg;
1527
1528 fg = gui_get_rgb_color_cmn(cell->fg.red, cell->fg.green, cell->fg.blue);
1529 bg = gui_get_rgb_color_cmn(cell->bg.red, cell->bg.green, cell->bg.blue);
1530
1531 return get_tgc_attr_idx(attr, fg, bg);
Bram Moolenaareeac6772017-07-23 15:48:37 +02001532 }
Bram Moolenaar065f41c2017-07-23 18:07:56 +02001533 else
Bram Moolenaareeac6772017-07-23 15:48:37 +02001534#endif
1535 {
Bram Moolenaar12d853f2017-08-01 18:04:04 +02001536 int bold = MAYBE;
1537 int fg = color2index(&cell->fg, TRUE, &bold);
1538 int bg = color2index(&cell->bg, FALSE, &bold);
1539
1540 /* with 8 colors set the bold attribute to get a bright foreground */
1541 if (bold == TRUE)
1542 attr |= HL_BOLD;
1543 return get_cterm_attr_idx(attr, fg, bg);
Bram Moolenaareeac6772017-07-23 15:48:37 +02001544 }
1545 return 0;
1546}
1547
1548/*
Bram Moolenaard85f2712017-07-28 21:51:57 +02001549 * Called to update the window that contains a terminal.
1550 * Returns FAIL when there is no terminal running in this window.
Bram Moolenaare4f25e42017-07-07 11:54:15 +02001551 */
Bram Moolenaard85f2712017-07-28 21:51:57 +02001552 int
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001553term_update_window(win_T *wp)
Bram Moolenaar938783d2017-07-16 20:13:26 +02001554{
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001555 term_T *term = wp->w_buffer->b_term;
Bram Moolenaard85f2712017-07-28 21:51:57 +02001556 VTerm *vterm;
1557 VTermScreen *screen;
1558 VTermState *state;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001559 VTermPos pos;
Bram Moolenaar938783d2017-07-16 20:13:26 +02001560
Bram Moolenaar423802d2017-07-30 16:52:24 +02001561 if (term == NULL || term->tl_vterm == NULL || term->tl_terminal_mode)
Bram Moolenaard85f2712017-07-28 21:51:57 +02001562 return FAIL;
Bram Moolenaar423802d2017-07-30 16:52:24 +02001563
Bram Moolenaard85f2712017-07-28 21:51:57 +02001564 vterm = term->tl_vterm;
1565 screen = vterm_obtain_screen(vterm);
1566 state = vterm_obtain_state(vterm);
1567
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001568 /*
1569 * If the window was resized a redraw will be triggered and we get here.
1570 * Adjust the size of the vterm unless 'termsize' specifies a fixed size.
1571 */
1572 if ((!term->tl_rows_fixed && term->tl_rows != wp->w_height)
1573 || (!term->tl_cols_fixed && term->tl_cols != wp->w_width))
Bram Moolenaarb13501f2017-07-22 22:32:56 +02001574 {
Bram Moolenaar96ad8c92017-07-28 14:17:34 +02001575 int rows = term->tl_rows_fixed ? term->tl_rows : wp->w_height;
1576 int cols = term->tl_cols_fixed ? term->tl_cols : wp->w_width;
1577 win_T *twp;
1578
1579 FOR_ALL_WINDOWS(twp)
1580 {
1581 /* When more than one window shows the same terminal, use the
1582 * smallest size. */
1583 if (twp->w_buffer == term->tl_buffer)
1584 {
1585 if (!term->tl_rows_fixed && rows > twp->w_height)
1586 rows = twp->w_height;
1587 if (!term->tl_cols_fixed && cols > twp->w_width)
1588 cols = twp->w_width;
1589 }
1590 }
Bram Moolenaarb13501f2017-07-22 22:32:56 +02001591
1592 vterm_set_size(vterm, rows, cols);
Bram Moolenaar2f3a90a2017-08-03 14:49:29 +02001593 ch_log(term->tl_job->jv_channel, "Resizing terminal to %d lines",
Bram Moolenaarb13501f2017-07-22 22:32:56 +02001594 rows);
Bram Moolenaar43da3e32017-07-23 17:27:54 +02001595 term_report_winsize(term, rows, cols);
Bram Moolenaarb13501f2017-07-22 22:32:56 +02001596 }
Bram Moolenaar58556cd2017-07-20 23:04:46 +02001597
1598 /* The cursor may have been moved when resizing. */
1599 vterm_state_get_cursorpos(state, &pos);
1600 position_cursor(wp, &pos);
1601
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001602 /* TODO: Only redraw what changed. */
1603 for (pos.row = 0; pos.row < wp->w_height; ++pos.row)
Bram Moolenaar938783d2017-07-16 20:13:26 +02001604 {
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001605 int off = screen_get_current_line_off();
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001606 int max_col = MIN(wp->w_width, term->tl_cols);
Bram Moolenaar938783d2017-07-16 20:13:26 +02001607
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001608 if (pos.row < term->tl_rows)
1609 {
1610 for (pos.col = 0; pos.col < max_col; )
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001611 {
1612 VTermScreenCell cell;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001613 int c;
Bram Moolenaar938783d2017-07-16 20:13:26 +02001614
Bram Moolenaareeac6772017-07-23 15:48:37 +02001615 if (vterm_screen_get_cell(screen, pos, &cell) == 0)
1616 vim_memset(&cell, 0, sizeof(cell));
1617
1618 /* TODO: composing chars */
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001619 c = cell.chars[0];
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001620 if (c == NUL)
1621 {
1622 ScreenLines[off] = ' ';
Bram Moolenaar8a773062017-07-24 22:29:21 +02001623#if defined(FEAT_MBYTE)
1624 if (enc_utf8)
1625 ScreenLinesUC[off] = NUL;
1626#endif
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001627 }
1628 else
1629 {
1630#if defined(FEAT_MBYTE)
1631 if (enc_utf8 && c >= 0x80)
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02001632 {
1633 ScreenLines[off] = ' ';
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001634 ScreenLinesUC[off] = c;
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02001635 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001636 else
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02001637 {
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001638 ScreenLines[off] = c;
Bram Moolenaar8a773062017-07-24 22:29:21 +02001639 if (enc_utf8)
1640 ScreenLinesUC[off] = NUL;
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02001641 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001642#else
1643 ScreenLines[off] = c;
1644#endif
1645 }
Bram Moolenaareeac6772017-07-23 15:48:37 +02001646 ScreenAttrs[off] = cell2attr(&cell);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001647
1648 ++pos.col;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001649 ++off;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001650 if (cell.width == 2)
1651 {
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02001652 ScreenLines[off] = NUL;
Bram Moolenaar8a773062017-07-24 22:29:21 +02001653#if defined(FEAT_MBYTE)
1654 if (enc_utf8)
1655 ScreenLinesUC[off] = NUL;
1656#endif
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001657 ++pos.col;
1658 ++off;
1659 }
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001660 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001661 }
Bram Moolenaare825d8b2017-07-19 23:20:19 +02001662 else
1663 pos.col = 0;
Bram Moolenaar938783d2017-07-16 20:13:26 +02001664
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001665 screen_line(wp->w_winrow + pos.row, wp->w_wincol,
1666 pos.col, wp->w_width, FALSE);
Bram Moolenaar938783d2017-07-16 20:13:26 +02001667 }
Bram Moolenaard85f2712017-07-28 21:51:57 +02001668
1669 return OK;
Bram Moolenaar938783d2017-07-16 20:13:26 +02001670}
Bram Moolenaare4f25e42017-07-07 11:54:15 +02001671
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001672/*
Bram Moolenaar63ecdda2017-07-28 22:29:35 +02001673 * Return TRUE if "wp" is a terminal window where the job has finished.
1674 */
1675 int
1676term_is_finished(buf_T *buf)
1677{
1678 return buf->b_term != NULL && buf->b_term->tl_vterm == NULL;
1679}
1680
1681/*
Bram Moolenaar423802d2017-07-30 16:52:24 +02001682 * Return TRUE if "wp" is a terminal window where the job has finished or we
1683 * are in Terminal-Normal mode.
1684 */
1685 int
1686term_show_buffer(buf_T *buf)
1687{
1688 term_T *term = buf->b_term;
1689
1690 return term != NULL && (term->tl_vterm == NULL || term->tl_terminal_mode);
1691}
1692
1693/*
Bram Moolenaar63ecdda2017-07-28 22:29:35 +02001694 * The current buffer is going to be changed. If there is terminal
1695 * highlighting remove it now.
1696 */
1697 void
1698term_change_in_curbuf(void)
1699{
1700 term_T *term = curbuf->b_term;
1701
1702 if (term_is_finished(curbuf) && term->tl_scrollback.ga_len > 0)
1703 {
1704 free_scrollback(term);
1705 redraw_buf_later(term->tl_buffer, NOT_VALID);
Bram Moolenaar20e6cd02017-08-01 20:25:22 +02001706
1707 /* The buffer is now like a normal buffer, it cannot be easily
1708 * abandoned when changed. */
1709 set_string_option_direct((char_u *)"buftype", -1,
1710 (char_u *)"", OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaar63ecdda2017-07-28 22:29:35 +02001711 }
1712}
1713
1714/*
1715 * Get the screen attribute for a position in the buffer.
1716 */
1717 int
1718term_get_attr(buf_T *buf, linenr_T lnum, int col)
1719{
1720 term_T *term = buf->b_term;
1721 sb_line_T *line;
1722
Bram Moolenaar70229f92017-07-29 16:01:53 +02001723 if (lnum > term->tl_scrollback.ga_len)
Bram Moolenaar63ecdda2017-07-28 22:29:35 +02001724 return 0;
1725 line = (sb_line_T *)term->tl_scrollback.ga_data + lnum - 1;
1726 if (col >= line->sb_cols)
1727 return 0;
1728 return cell2attr(line->sb_cells + col);
1729}
1730
1731/*
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001732 * Create a new vterm and initialize it.
1733 */
1734 static void
1735create_vterm(term_T *term, int rows, int cols)
1736{
1737 VTerm *vterm;
1738 VTermScreen *screen;
1739
1740 vterm = vterm_new(rows, cols);
1741 term->tl_vterm = vterm;
1742 screen = vterm_obtain_screen(vterm);
1743 vterm_screen_set_callbacks(screen, &screen_callbacks, term);
1744 /* TODO: depends on 'encoding'. */
1745 vterm_set_utf8(vterm, 1);
Bram Moolenaareeac6772017-07-23 15:48:37 +02001746
1747 /* Vterm uses a default black background. Set it to white when
1748 * 'background' is "light". */
1749 if (*p_bg == 'l')
1750 {
1751 VTermColor fg, bg;
1752
1753 fg.red = fg.green = fg.blue = 0;
1754 bg.red = bg.green = bg.blue = 255;
1755 vterm_state_set_default_colors(vterm_obtain_state(vterm), &fg, &bg);
1756 }
1757
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001758 /* Required to initialize most things. */
1759 vterm_screen_reset(screen, 1 /* hard */);
1760}
1761
Bram Moolenaar21554412017-07-24 21:44:43 +02001762/*
1763 * Return the text to show for the buffer name and status.
1764 */
1765 char_u *
1766term_get_status_text(term_T *term)
1767{
1768 if (term->tl_status_text == NULL)
1769 {
1770 char_u *txt;
1771 size_t len;
1772
Bram Moolenaar423802d2017-07-30 16:52:24 +02001773 if (term->tl_terminal_mode)
1774 {
1775 if (term_job_running(term))
1776 txt = (char_u *)_("Terminal");
1777 else
1778 txt = (char_u *)_("Terminal-finished");
1779 }
1780 else if (term->tl_title != NULL)
Bram Moolenaar21554412017-07-24 21:44:43 +02001781 txt = term->tl_title;
1782 else if (term_job_running(term))
1783 txt = (char_u *)_("running");
1784 else
1785 txt = (char_u *)_("finished");
1786 len = 9 + STRLEN(term->tl_buffer->b_fname) + STRLEN(txt);
Bram Moolenaara1b5b092017-07-26 21:29:34 +02001787 term->tl_status_text = alloc((int)len);
Bram Moolenaar21554412017-07-24 21:44:43 +02001788 if (term->tl_status_text != NULL)
1789 vim_snprintf((char *)term->tl_status_text, len, "%s [%s]",
1790 term->tl_buffer->b_fname, txt);
1791 }
1792 return term->tl_status_text;
1793}
1794
Bram Moolenaarf86eea92017-07-28 13:51:30 +02001795/*
1796 * Mark references in jobs of terminals.
1797 */
1798 int
1799set_ref_in_term(int copyID)
1800{
1801 int abort = FALSE;
1802 term_T *term;
1803 typval_T tv;
1804
1805 for (term = first_term; term != NULL; term = term->tl_next)
1806 if (term->tl_job != NULL)
1807 {
1808 tv.v_type = VAR_JOB;
1809 tv.vval.v_job = term->tl_job;
1810 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
1811 }
1812 return abort;
1813}
1814
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001815/*
Bram Moolenaar97870002017-07-30 18:28:38 +02001816 * Get the buffer from the first argument in "argvars".
1817 * Returns NULL when the buffer is not for a terminal window.
1818 */
1819 static buf_T *
1820term_get_buf(typval_T *argvars)
1821{
1822 buf_T *buf;
1823
1824 (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */
1825 ++emsg_off;
1826 buf = get_buf_tv(&argvars[0], FALSE);
1827 --emsg_off;
1828 if (buf == NULL || buf->b_term == NULL)
1829 return NULL;
1830 return buf;
1831}
1832
1833/*
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001834 * "term_getattr(attr, name)" function
1835 */
1836 void
1837f_term_getattr(typval_T *argvars, typval_T *rettv)
1838{
1839 int attr;
1840 size_t i;
1841 char_u *name;
1842
1843 static struct {
1844 char *name;
1845 int attr;
1846 } attrs[] = {
1847 {"bold", HL_BOLD},
1848 {"italic", HL_ITALIC},
1849 {"underline", HL_UNDERLINE},
1850 {"strike", HL_STANDOUT},
1851 {"reverse", HL_INVERSE},
1852 };
1853
1854 attr = get_tv_number(&argvars[0]);
1855 name = get_tv_string_chk(&argvars[1]);
1856 if (name == NULL)
1857 return;
1858
1859 for (i = 0; i < sizeof(attrs)/sizeof(attrs[0]); ++i)
1860 if (STRCMP(name, attrs[i].name) == 0)
1861 {
1862 rettv->vval.v_number = (attr & attrs[i].attr) != 0 ? 1 : 0;
1863 break;
1864 }
1865}
1866
1867/*
Bram Moolenaar97870002017-07-30 18:28:38 +02001868 * "term_getcursor(buf)" function
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001869 */
Bram Moolenaar97870002017-07-30 18:28:38 +02001870 void
1871f_term_getcursor(typval_T *argvars, typval_T *rettv)
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001872{
Bram Moolenaar97870002017-07-30 18:28:38 +02001873 buf_T *buf = term_get_buf(argvars);
1874 list_T *l;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001875
Bram Moolenaar97870002017-07-30 18:28:38 +02001876 if (rettv_list_alloc(rettv) == FAIL)
1877 return;
1878 if (buf == NULL)
1879 return;
1880
1881 l = rettv->vval.v_list;
Bram Moolenaarc2ce52c2017-08-01 18:35:38 +02001882 list_append_number(l, buf->b_term->tl_cursor_pos.row + 1);
1883 list_append_number(l, buf->b_term->tl_cursor_pos.col + 1);
Bram Moolenaar97870002017-07-30 18:28:38 +02001884 list_append_number(l, buf->b_term->tl_cursor_visible);
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001885}
1886
1887/*
1888 * "term_getjob(buf)" function
1889 */
1890 void
1891f_term_getjob(typval_T *argvars, typval_T *rettv)
1892{
1893 buf_T *buf = term_get_buf(argvars);
1894
1895 rettv->v_type = VAR_JOB;
1896 rettv->vval.v_job = NULL;
1897 if (buf == NULL)
1898 return;
1899
1900 rettv->vval.v_job = buf->b_term->tl_job;
1901 if (rettv->vval.v_job != NULL)
1902 ++rettv->vval.v_job->jv_refcount;
1903}
1904
Bram Moolenaarc2ce52c2017-08-01 18:35:38 +02001905 static int
1906get_row_number(typval_T *tv, term_T *term)
1907{
1908 if (tv->v_type == VAR_STRING
1909 && tv->vval.v_string != NULL
1910 && STRCMP(tv->vval.v_string, ".") == 0)
1911 return term->tl_cursor_pos.row;
1912 return (int)get_tv_number(tv) - 1;
1913}
1914
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001915/*
1916 * "term_getline(buf, row)" function
1917 */
1918 void
1919f_term_getline(typval_T *argvars, typval_T *rettv)
1920{
1921 buf_T *buf = term_get_buf(argvars);
1922 term_T *term;
1923 int row;
1924
1925 rettv->v_type = VAR_STRING;
1926 if (buf == NULL)
1927 return;
1928 term = buf->b_term;
Bram Moolenaarc2ce52c2017-08-01 18:35:38 +02001929 row = get_row_number(&argvars[1], term);
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001930
1931 if (term->tl_vterm == NULL)
1932 {
1933 linenr_T lnum = row + term->tl_scrollback_scrolled + 1;
1934
1935 /* vterm is finished, get the text from the buffer */
1936 if (lnum > 0 && lnum <= buf->b_ml.ml_line_count)
1937 rettv->vval.v_string = vim_strsave(ml_get_buf(buf, lnum, FALSE));
1938 }
1939 else
1940 {
1941 VTermScreen *screen = vterm_obtain_screen(term->tl_vterm);
1942 VTermRect rect;
1943 int len;
1944 char_u *p;
1945
Bram Moolenaar5c838a32017-08-02 22:10:34 +02001946 if (row < 0 || row >= term->tl_rows)
1947 return;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02001948 len = term->tl_cols * MB_MAXBYTES + 1;
1949 p = alloc(len);
1950 if (p == NULL)
1951 return;
1952 rettv->vval.v_string = p;
1953
1954 rect.start_col = 0;
1955 rect.end_col = term->tl_cols;
1956 rect.start_row = row;
1957 rect.end_row = row + 1;
1958 p[vterm_screen_get_text(screen, (char *)p, len, rect)] = NUL;
1959 }
1960}
1961
1962/*
1963 * "term_getsize(buf)" function
1964 */
1965 void
1966f_term_getsize(typval_T *argvars, typval_T *rettv)
1967{
1968 buf_T *buf = term_get_buf(argvars);
1969 list_T *l;
1970
1971 if (rettv_list_alloc(rettv) == FAIL)
1972 return;
1973 if (buf == NULL)
1974 return;
1975
1976 l = rettv->vval.v_list;
1977 list_append_number(l, buf->b_term->tl_rows);
1978 list_append_number(l, buf->b_term->tl_cols);
1979}
1980
1981/*
Bram Moolenaarb000e322017-07-30 19:38:21 +02001982 * "term_getstatus(buf)" function
1983 */
1984 void
1985f_term_getstatus(typval_T *argvars, typval_T *rettv)
1986{
1987 buf_T *buf = term_get_buf(argvars);
1988 term_T *term;
1989 char_u val[100];
1990
1991 rettv->v_type = VAR_STRING;
1992 if (buf == NULL)
1993 return;
1994 term = buf->b_term;
1995
1996 if (term_job_running(term))
1997 STRCPY(val, "running");
1998 else
1999 STRCPY(val, "finished");
2000 if (term->tl_terminal_mode)
2001 STRCAT(val, ",terminal");
2002 rettv->vval.v_string = vim_strsave(val);
2003}
2004
2005/*
2006 * "term_gettitle(buf)" function
2007 */
2008 void
2009f_term_gettitle(typval_T *argvars, typval_T *rettv)
2010{
2011 buf_T *buf = term_get_buf(argvars);
2012
2013 rettv->v_type = VAR_STRING;
2014 if (buf == NULL)
2015 return;
2016
2017 if (buf->b_term->tl_title != NULL)
2018 rettv->vval.v_string = vim_strsave(buf->b_term->tl_title);
2019}
2020
2021/*
Bram Moolenaar7c9aec42017-08-03 13:51:25 +02002022 * "term_gettty(buf)" function
2023 */
2024 void
2025f_term_gettty(typval_T *argvars, typval_T *rettv)
2026{
2027 buf_T *buf = term_get_buf(argvars);
2028 char_u *p;
2029
2030 rettv->v_type = VAR_STRING;
2031 if (buf == NULL)
2032 return;
2033 if (buf->b_term->tl_job != NULL)
2034 p = buf->b_term->tl_job->jv_tty_name;
2035 else
2036 p = buf->b_term->tl_tty_name;
2037 if (p != NULL)
2038 rettv->vval.v_string = vim_strsave(p);
2039}
2040
2041/*
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002042 * "term_list()" function
2043 */
2044 void
2045f_term_list(typval_T *argvars UNUSED, typval_T *rettv)
2046{
2047 term_T *tp;
2048 list_T *l;
2049
2050 if (rettv_list_alloc(rettv) == FAIL || first_term == NULL)
2051 return;
2052
2053 l = rettv->vval.v_list;
2054 for (tp = first_term; tp != NULL; tp = tp->tl_next)
2055 if (tp != NULL && tp->tl_buffer != NULL)
2056 if (list_append_number(l,
2057 (varnumber_T)tp->tl_buffer->b_fnum) == FAIL)
2058 return;
2059}
2060
2061/*
2062 * "term_scrape(buf, row)" function
2063 */
2064 void
2065f_term_scrape(typval_T *argvars, typval_T *rettv)
2066{
2067 buf_T *buf = term_get_buf(argvars);
2068 VTermScreen *screen = NULL;
2069 VTermPos pos;
2070 list_T *l;
2071 term_T *term;
2072
2073 if (rettv_list_alloc(rettv) == FAIL)
2074 return;
2075 if (buf == NULL)
2076 return;
2077 term = buf->b_term;
2078 if (term->tl_vterm != NULL)
2079 screen = vterm_obtain_screen(term->tl_vterm);
2080
2081 l = rettv->vval.v_list;
Bram Moolenaarc2ce52c2017-08-01 18:35:38 +02002082 pos.row = get_row_number(&argvars[1], term);
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002083 for (pos.col = 0; pos.col < term->tl_cols; )
2084 {
2085 dict_T *dcell;
2086 VTermScreenCell cell;
2087 char_u rgb[8];
2088 char_u mbs[MB_MAXBYTES * VTERM_MAX_CHARS_PER_CELL + 1];
2089 int off = 0;
2090 int i;
2091
2092 if (screen == NULL)
2093 {
2094 linenr_T lnum = pos.row + term->tl_scrollback_scrolled;
2095 sb_line_T *line;
2096
2097 /* vterm has finished, get the cell from scrollback */
2098 if (lnum < 0 || lnum >= term->tl_scrollback.ga_len)
2099 break;
2100 line = (sb_line_T *)term->tl_scrollback.ga_data + lnum;
2101 if (pos.col >= line->sb_cols)
2102 break;
2103 cell = line->sb_cells[pos.col];
2104 }
2105 else if (vterm_screen_get_cell(screen, pos, &cell) == 0)
2106 break;
2107 dcell = dict_alloc();
2108 list_append_dict(l, dcell);
2109
2110 for (i = 0; i < VTERM_MAX_CHARS_PER_CELL; ++i)
2111 {
2112 if (cell.chars[i] == 0)
2113 break;
2114 off += (*utf_char2bytes)((int)cell.chars[i], mbs + off);
2115 }
2116 mbs[off] = NUL;
2117 dict_add_nr_str(dcell, "chars", 0, mbs);
2118
2119 vim_snprintf((char *)rgb, 8, "#%02x%02x%02x",
2120 cell.fg.red, cell.fg.green, cell.fg.blue);
2121 dict_add_nr_str(dcell, "fg", 0, rgb);
2122 vim_snprintf((char *)rgb, 8, "#%02x%02x%02x",
2123 cell.bg.red, cell.bg.green, cell.bg.blue);
2124 dict_add_nr_str(dcell, "bg", 0, rgb);
2125
2126 dict_add_nr_str(dcell, "attr", cell2attr(&cell), NULL);
2127 dict_add_nr_str(dcell, "width", cell.width, NULL);
2128
2129 ++pos.col;
2130 if (cell.width == 2)
2131 ++pos.col;
2132 }
2133}
2134
2135/*
2136 * "term_sendkeys(buf, keys)" function
2137 */
2138 void
2139f_term_sendkeys(typval_T *argvars, typval_T *rettv)
2140{
2141 buf_T *buf = term_get_buf(argvars);
2142 char_u *msg;
2143 term_T *term;
2144
2145 rettv->v_type = VAR_UNKNOWN;
2146 if (buf == NULL)
2147 return;
2148
2149 msg = get_tv_string_chk(&argvars[1]);
2150 if (msg == NULL)
2151 return;
2152 term = buf->b_term;
2153 if (term->tl_vterm == NULL)
2154 return;
2155
2156 while (*msg != NUL)
2157 {
2158 send_keys_to_term(term, PTR2CHAR(msg), FALSE);
2159 msg += MB_PTR2LEN(msg);
2160 }
2161
Bram Moolenaar392d1bf2017-07-31 21:18:58 +02002162 if (!term->tl_terminal_mode)
2163 {
2164 /* TODO: only update once in a while. */
2165 update_screen(0);
2166 if (buf == curbuf)
2167 update_cursor(term, TRUE);
2168 }
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002169}
2170
2171/*
2172 * "term_start(command, options)" function
2173 */
2174 void
2175f_term_start(typval_T *argvars, typval_T *rettv)
2176{
2177 char_u *cmd = get_tv_string_chk(&argvars[0]);
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002178 jobopt_T opt;
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002179
2180 if (cmd == NULL)
2181 return;
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002182 init_job_options(&opt);
2183 /* TODO: allow more job options */
2184 if (argvars[1].v_type != VAR_UNKNOWN
2185 && get_job_options(&argvars[1], &opt,
2186 JO_TIMEOUT_ALL + JO_STOPONEXIT
Bram Moolenaar78712a72017-08-05 14:50:12 +02002187 + JO_EXIT_CB + JO_CLOSE_CALLBACK
2188 + JO2_TERM_NAME) == FAIL)
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002189 return;
2190
2191 term_start(cmd, &opt);
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002192
2193 if (curbuf->b_term != NULL)
2194 rettv->vval.v_number = curbuf->b_fnum;
2195}
2196
2197/*
2198 * "term_wait" function
2199 */
2200 void
2201f_term_wait(typval_T *argvars, typval_T *rettv UNUSED)
2202{
2203 buf_T *buf = term_get_buf(argvars);
2204
2205 if (buf == NULL)
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002206 {
2207 ch_log(NULL, "term_wait(): invalid argument");
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002208 return;
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002209 }
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002210 if (buf->b_term->tl_job == NULL)
2211 {
2212 ch_log(NULL, "term_wait(): no job to wait for");
2213 return;
2214 }
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002215
2216 /* Get the job status, this will detect a job that finished. */
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002217 if (STRCMP(job_status(buf->b_term->tl_job), "dead") == 0)
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002218 {
2219 /* The job is dead, keep reading channel I/O until the channel is
2220 * closed. */
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002221 ch_log(NULL, "term_wait(): waiting for channel to close");
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002222 while (buf->b_term != NULL && !buf->b_term->tl_channel_closed)
2223 {
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002224 mch_check_messages();
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002225 parse_queued_messages();
2226 ui_delay(10L, FALSE);
2227 }
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002228 mch_check_messages();
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002229 parse_queued_messages();
2230 }
2231 else
2232 {
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002233 mch_check_messages();
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002234 parse_queued_messages();
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002235
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002236 /* Wait for 10 msec for any channel I/O. */
2237 /* TODO: use delay from optional argument */
2238 ui_delay(10L, TRUE);
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002239 mch_check_messages();
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002240
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002241 /* Flushing messages on channels is hopefully sufficient.
2242 * TODO: is there a better way? */
2243 parse_queued_messages();
2244 }
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002245}
2246
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002247# ifdef WIN3264
2248
Bram Moolenaarc6df10e2017-07-29 20:15:08 +02002249/**************************************
2250 * 2. MS-Windows implementation.
2251 */
2252
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002253#define WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN 1ul
2254#define WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN 2ull
2255
Bram Moolenaar8a773062017-07-24 22:29:21 +02002256void* (*winpty_config_new)(UINT64, void*);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002257void* (*winpty_open)(void*, void*);
Bram Moolenaar8a773062017-07-24 22:29:21 +02002258void* (*winpty_spawn_config_new)(UINT64, void*, LPCWSTR, void*, void*, void*);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002259BOOL (*winpty_spawn)(void*, void*, HANDLE*, HANDLE*, DWORD*, void*);
2260void (*winpty_config_set_initial_size)(void*, int, int);
2261LPCWSTR (*winpty_conin_name)(void*);
2262LPCWSTR (*winpty_conout_name)(void*);
2263LPCWSTR (*winpty_conerr_name)(void*);
2264void (*winpty_free)(void*);
2265void (*winpty_config_free)(void*);
2266void (*winpty_spawn_config_free)(void*);
2267void (*winpty_error_free)(void*);
2268LPCWSTR (*winpty_error_msg)(void*);
Bram Moolenaar43da3e32017-07-23 17:27:54 +02002269BOOL (*winpty_set_size)(void*, int, int, void*);
Bram Moolenaar5be8dd02017-08-03 20:52:19 +02002270HANDLE (*winpty_agent_process)(void*);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002271
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002272#define WINPTY_DLL "winpty.dll"
2273
2274static HINSTANCE hWinPtyDLL = NULL;
2275
2276 int
2277dyn_winpty_init(void)
2278{
2279 int i;
2280 static struct
2281 {
2282 char *name;
2283 FARPROC *ptr;
2284 } winpty_entry[] =
2285 {
2286 {"winpty_conerr_name", (FARPROC*)&winpty_conerr_name},
2287 {"winpty_config_free", (FARPROC*)&winpty_config_free},
2288 {"winpty_config_new", (FARPROC*)&winpty_config_new},
2289 {"winpty_config_set_initial_size", (FARPROC*)&winpty_config_set_initial_size},
2290 {"winpty_conin_name", (FARPROC*)&winpty_conin_name},
2291 {"winpty_conout_name", (FARPROC*)&winpty_conout_name},
2292 {"winpty_error_free", (FARPROC*)&winpty_error_free},
2293 {"winpty_free", (FARPROC*)&winpty_free},
2294 {"winpty_open", (FARPROC*)&winpty_open},
2295 {"winpty_spawn", (FARPROC*)&winpty_spawn},
2296 {"winpty_spawn_config_free", (FARPROC*)&winpty_spawn_config_free},
2297 {"winpty_spawn_config_new", (FARPROC*)&winpty_spawn_config_new},
2298 {"winpty_error_msg", (FARPROC*)&winpty_error_msg},
Bram Moolenaar43da3e32017-07-23 17:27:54 +02002299 {"winpty_set_size", (FARPROC*)&winpty_set_size},
Bram Moolenaar5be8dd02017-08-03 20:52:19 +02002300 {"winpty_agent_process", (FARPROC*)&winpty_agent_process},
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002301 {NULL, NULL}
2302 };
2303
2304 /* No need to initialize twice. */
2305 if (hWinPtyDLL)
2306 return 1;
2307 /* Load winpty.dll */
2308 hWinPtyDLL = vimLoadLib(WINPTY_DLL);
2309 if (!hWinPtyDLL)
2310 {
2311 EMSG2(_(e_loadlib), WINPTY_DLL);
2312 return 0;
2313 }
2314 for (i = 0; winpty_entry[i].name != NULL
2315 && winpty_entry[i].ptr != NULL; ++i)
2316 {
2317 if ((*winpty_entry[i].ptr = (FARPROC)GetProcAddress(hWinPtyDLL,
2318 winpty_entry[i].name)) == NULL)
2319 {
2320 EMSG2(_(e_loadfunc), winpty_entry[i].name);
2321 return 0;
2322 }
2323 }
2324
2325 return 1;
2326}
2327
2328/*
2329 * Create a new terminal of "rows" by "cols" cells.
2330 * Store a reference in "term".
2331 * Return OK or FAIL.
2332 */
2333 static int
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002334term_and_job_init(term_T *term, int rows, int cols, char_u *cmd, jobopt_T *opt)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002335{
Bram Moolenaarab6eec32017-07-27 21:46:43 +02002336 WCHAR *p;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002337 channel_T *channel = NULL;
2338 job_T *job = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002339 DWORD error;
2340 HANDLE jo = NULL, child_process_handle, child_thread_handle;
2341 void *winpty_err;
Bram Moolenaarab6eec32017-07-27 21:46:43 +02002342 void *spawn_config = NULL;
Bram Moolenaar5be8dd02017-08-03 20:52:19 +02002343 char buf[MAX_PATH];
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002344
2345 if (!dyn_winpty_init())
2346 return FAIL;
2347
Bram Moolenaarab6eec32017-07-27 21:46:43 +02002348 p = enc_to_utf16(cmd, NULL);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002349 if (p == NULL)
2350 return FAIL;
2351
2352 job = job_alloc();
2353 if (job == NULL)
2354 goto failed;
2355
2356 channel = add_channel();
2357 if (channel == NULL)
2358 goto failed;
2359
2360 term->tl_winpty_config = winpty_config_new(0, &winpty_err);
2361 if (term->tl_winpty_config == NULL)
2362 goto failed;
2363
2364 winpty_config_set_initial_size(term->tl_winpty_config, cols, rows);
2365 term->tl_winpty = winpty_open(term->tl_winpty_config, &winpty_err);
2366 if (term->tl_winpty == NULL)
2367 goto failed;
2368
Bram Moolenaar7c9aec42017-08-03 13:51:25 +02002369 /* TODO: if the command is "NONE" only create a pty. */
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002370 spawn_config = winpty_spawn_config_new(
2371 WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN |
2372 WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN,
2373 NULL,
2374 p,
2375 NULL,
2376 NULL,
2377 &winpty_err);
2378 if (spawn_config == NULL)
2379 goto failed;
2380
2381 channel = add_channel();
2382 if (channel == NULL)
2383 goto failed;
2384
2385 job = job_alloc();
2386 if (job == NULL)
2387 goto failed;
2388
2389 if (!winpty_spawn(term->tl_winpty, spawn_config, &child_process_handle,
2390 &child_thread_handle, &error, &winpty_err))
2391 goto failed;
2392
2393 channel_set_pipes(channel,
2394 (sock_T) CreateFileW(
2395 winpty_conin_name(term->tl_winpty),
2396 GENERIC_WRITE, 0, NULL,
2397 OPEN_EXISTING, 0, NULL),
2398 (sock_T) CreateFileW(
2399 winpty_conout_name(term->tl_winpty),
2400 GENERIC_READ, 0, NULL,
2401 OPEN_EXISTING, 0, NULL),
2402 (sock_T) CreateFileW(
2403 winpty_conerr_name(term->tl_winpty),
2404 GENERIC_READ, 0, NULL,
2405 OPEN_EXISTING, 0, NULL));
2406
2407 jo = CreateJobObject(NULL, NULL);
2408 if (jo == NULL)
2409 goto failed;
2410
2411 if (!AssignProcessToJobObject(jo, child_process_handle))
Bram Moolenaarab6eec32017-07-27 21:46:43 +02002412 {
2413 /* Failed, switch the way to terminate process with TerminateProcess. */
2414 CloseHandle(jo);
2415 jo = NULL;
2416 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002417
2418 winpty_spawn_config_free(spawn_config);
Bram Moolenaarab6eec32017-07-27 21:46:43 +02002419 vim_free(p);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002420
2421 create_vterm(term, rows, cols);
2422
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002423 channel_set_job(channel, job, opt);
Bram Moolenaar102dc7f2017-08-03 20:59:29 +02002424 job_set_options(job, opt);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002425
2426 job->jv_channel = channel;
2427 job->jv_proc_info.hProcess = child_process_handle;
2428 job->jv_proc_info.dwProcessId = GetProcessId(child_process_handle);
2429 job->jv_job_object = jo;
2430 job->jv_status = JOB_STARTED;
Bram Moolenaar5be8dd02017-08-03 20:52:19 +02002431 sprintf(buf, "winpty://%lu",
2432 GetProcessId(winpty_agent_process(term->tl_winpty)));
2433 job->jv_tty_name = vim_strsave((char_u*)buf);
Bram Moolenaar0e83f022017-07-27 22:07:35 +02002434 ++job->jv_refcount;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002435 term->tl_job = job;
2436
2437 return OK;
2438
2439failed:
Bram Moolenaarab6eec32017-07-27 21:46:43 +02002440 if (spawn_config != NULL)
2441 winpty_spawn_config_free(spawn_config);
2442 vim_free(p);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002443 if (channel != NULL)
2444 channel_clear(channel);
2445 if (job != NULL)
Bram Moolenaarcdeae992017-07-23 17:22:35 +02002446 {
2447 job->jv_channel = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002448 job_cleanup(job);
Bram Moolenaarcdeae992017-07-23 17:22:35 +02002449 }
2450 term->tl_job = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002451 if (jo != NULL)
2452 CloseHandle(jo);
2453 if (term->tl_winpty != NULL)
2454 winpty_free(term->tl_winpty);
Bram Moolenaarcdeae992017-07-23 17:22:35 +02002455 term->tl_winpty = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002456 if (term->tl_winpty_config != NULL)
2457 winpty_config_free(term->tl_winpty_config);
Bram Moolenaarcdeae992017-07-23 17:22:35 +02002458 term->tl_winpty_config = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002459 if (winpty_err != NULL)
2460 {
2461 char_u *msg = utf16_to_enc(
2462 (short_u *)winpty_error_msg(winpty_err), NULL);
2463
2464 EMSG(msg);
2465 winpty_error_free(winpty_err);
2466 }
2467 return FAIL;
2468}
2469
2470/*
2471 * Free the terminal emulator part of "term".
2472 */
2473 static void
Bram Moolenaard85f2712017-07-28 21:51:57 +02002474term_free_vterm(term_T *term)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002475{
Bram Moolenaarcdeae992017-07-23 17:22:35 +02002476 if (term->tl_winpty != NULL)
2477 winpty_free(term->tl_winpty);
Bram Moolenaard85f2712017-07-28 21:51:57 +02002478 term->tl_winpty = NULL;
Bram Moolenaarcdeae992017-07-23 17:22:35 +02002479 if (term->tl_winpty_config != NULL)
2480 winpty_config_free(term->tl_winpty_config);
Bram Moolenaard85f2712017-07-28 21:51:57 +02002481 term->tl_winpty_config = NULL;
Bram Moolenaarcdeae992017-07-23 17:22:35 +02002482 if (term->tl_vterm != NULL)
2483 vterm_free(term->tl_vterm);
Bram Moolenaardcbfa332017-07-28 23:16:13 +02002484 term->tl_vterm = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002485}
2486
Bram Moolenaar43da3e32017-07-23 17:27:54 +02002487/*
2488 * Request size to terminal.
2489 */
2490 static void
2491term_report_winsize(term_T *term, int rows, int cols)
2492{
2493 winpty_set_size(term->tl_winpty, cols, rows, NULL);
2494}
2495
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002496# else
2497
2498/**************************************
2499 * 3. Unix-like implementation.
2500 */
2501
2502/*
2503 * Create a new terminal of "rows" by "cols" cells.
2504 * Start job for "cmd".
2505 * Store the pointers in "term".
2506 * Return OK or FAIL.
2507 */
2508 static int
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002509term_and_job_init(term_T *term, int rows, int cols, char_u *cmd, jobopt_T *opt)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002510{
2511 typval_T argvars[2];
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002512
2513 create_vterm(term, rows, cols);
2514
Bram Moolenaar7c9aec42017-08-03 13:51:25 +02002515 /* TODO: if the command is "NONE" only create a pty. */
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002516 argvars[0].v_type = VAR_STRING;
2517 argvars[0].vval.v_string = cmd;
Bram Moolenaar3c3a80d2017-08-03 17:06:45 +02002518
2519 term->tl_job = job_start(argvars, opt);
Bram Moolenaar0e83f022017-07-27 22:07:35 +02002520 if (term->tl_job != NULL)
2521 ++term->tl_job->jv_refcount;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002522
Bram Moolenaar61a66052017-07-22 18:39:00 +02002523 return term->tl_job != NULL
2524 && term->tl_job->jv_channel != NULL
2525 && term->tl_job->jv_status != JOB_FAILED ? OK : FAIL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002526}
2527
2528/*
2529 * Free the terminal emulator part of "term".
2530 */
2531 static void
Bram Moolenaard85f2712017-07-28 21:51:57 +02002532term_free_vterm(term_T *term)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002533{
Bram Moolenaarcdeae992017-07-23 17:22:35 +02002534 if (term->tl_vterm != NULL)
2535 vterm_free(term->tl_vterm);
Bram Moolenaard85f2712017-07-28 21:51:57 +02002536 term->tl_vterm = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002537}
Bram Moolenaar43da3e32017-07-23 17:27:54 +02002538
2539/*
2540 * Request size to terminal.
2541 */
2542 static void
2543term_report_winsize(term_T *term, int rows, int cols)
2544{
2545 /* Use an ioctl() to report the new window size to the job. */
2546 if (term->tl_job != NULL && term->tl_job->jv_channel != NULL)
2547 {
2548 int fd = -1;
2549 int part;
2550
2551 for (part = PART_OUT; part < PART_COUNT; ++part)
2552 {
2553 fd = term->tl_job->jv_channel->ch_part[part].ch_fd;
2554 if (isatty(fd))
2555 break;
2556 }
2557 if (part < PART_COUNT && mch_report_winsize(fd, rows, cols) == OK)
2558 mch_stop_job(term->tl_job, (char_u *)"winch");
2559 }
2560}
2561
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02002562# endif
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02002563
Bram Moolenaare4f25e42017-07-07 11:54:15 +02002564#endif /* FEAT_TERMINAL */