blob: 9e1a5e23e8662c858bce917301cebb4bd1723a07 [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
22 * that 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 *
35 * TODO:
Bram Moolenaard85f2712017-07-28 21:51:57 +020036 * - For the scrollback buffer store lines in the buffer, only attributes in
37 * tl_scrollback.
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020038 * - When the job ends:
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020039 * - Display the scrollback buffer (but with attributes).
40 * Make the buffer not modifiable, drop attributes when making changes.
Bram Moolenaar21554412017-07-24 21:44:43 +020041 * - Need an option or argument to drop the window+buffer right away, to be
42 * used for a shell or Vim.
Bram Moolenaard85f2712017-07-28 21:51:57 +020043 * - To set BS correctly, check get_stty(); Pass the fd of the pty.
44 * - Patch for functions: Yasuhiro Matsumoto, #1871
45 * - do not store terminal buffer in viminfo. Or prefix term:// ?
Bram Moolenaar21554412017-07-24 21:44:43 +020046 * - add a character in :ls output
Bram Moolenaar938783d2017-07-16 20:13:26 +020047 * - when closing window and job has not ended, make terminal hidden?
Bram Moolenaard85f2712017-07-28 21:51:57 +020048 * - when closing window and job has ended, make buffer hidden?
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +020049 * - don't allow exiting Vim when a terminal is still running a job
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020050 * - use win_del_lines() to make scroll-up efficient.
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020051 * - add test for giving error for invalid 'termsize' value.
Bram Moolenaare4f25e42017-07-07 11:54:15 +020052 * - support minimal size when 'termsize' is "rows*cols".
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020053 * - support minimal size when 'termsize' is empty?
Bram Moolenaar5a1feb82017-07-22 18:04:08 +020054 * - implement "term" for job_start(): more job options when starting a
55 * terminal.
Bram Moolenaar96ca27a2017-07-17 23:20:24 +020056 * - implement term_list() list of buffers with a terminal
57 * - implement term_getsize(buf)
58 * - implement term_setsize(buf)
59 * - implement term_sendkeys(buf, keys) send keystrokes to a terminal
60 * - implement term_wait(buf) wait for screen to be updated
61 * - implement term_scrape(buf, row) inspect terminal screen
62 * - implement term_open(command, options) open terminal window
63 * - implement term_getjob(buf)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020064 * - when 'encoding' is not utf-8, or the job is using another encoding, setup
65 * conversions.
Bram Moolenaardbe948d2017-07-23 22:50:51 +020066 * - In the GUI use a terminal emulator for :!cmd.
Bram Moolenaare4f25e42017-07-07 11:54:15 +020067 */
68
69#include "vim.h"
70
71#ifdef FEAT_TERMINAL
72
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020073#ifdef WIN3264
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020074# define MIN(x,y) (x < y ? x : y)
75# define MAX(x,y) (x > y ? x : y)
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020076#endif
Bram Moolenaare4f25e42017-07-07 11:54:15 +020077
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020078#include "libvterm/include/vterm.h"
79
Bram Moolenaard85f2712017-07-28 21:51:57 +020080typedef struct sb_line_S {
81 int sb_cols; /* can differ per line */
82 VTermScreenCell *sb_cells; /* allocated */
83} sb_line_T;
84
Bram Moolenaare4f25e42017-07-07 11:54:15 +020085/* typedef term_T in structs.h */
86struct terminal_S {
87 term_T *tl_next;
88
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020089#ifdef WIN3264
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020090 void *tl_winpty_config;
91 void *tl_winpty;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +020092#endif
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020093 VTerm *tl_vterm;
Bram Moolenaare4f25e42017-07-07 11:54:15 +020094 job_T *tl_job;
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +020095 buf_T *tl_buffer;
Bram Moolenaare4f25e42017-07-07 11:54:15 +020096
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +020097 /* last known vterm size */
98 int tl_rows;
99 int tl_cols;
100 /* vterm size does not follow window size */
101 int tl_rows_fixed;
102 int tl_cols_fixed;
103
Bram Moolenaar21554412017-07-24 21:44:43 +0200104 char_u *tl_title; /* NULL or allocated */
105 char_u *tl_status_text; /* NULL or allocated */
106
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200107 /* Range of screen rows to update. Zero based. */
108 int tl_dirty_row_start; /* -1 if nothing dirty */
109 int tl_dirty_row_end; /* row below last one to update */
110
Bram Moolenaard85f2712017-07-28 21:51:57 +0200111 garray_T tl_scrollback;
112
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200113 pos_T tl_cursor;
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200114 int tl_cursor_visible;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200115};
116
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200117/*
118 * List of all active terminals.
119 */
120static term_T *first_term = NULL;
121
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200122
123#define MAX_ROW 999999 /* used for tl_dirty_row_end to update all rows */
124#define KEY_BUF_LEN 200
125
126/*
127 * Functions with separate implementation for MS-Windows and Unix-like systems.
128 */
129static int term_and_job_init(term_T *term, int rows, int cols, char_u *cmd);
Bram Moolenaar43da3e32017-07-23 17:27:54 +0200130static void term_report_winsize(term_T *term, int rows, int cols);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200131static void term_free_vterm(term_T *term);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200132
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200133/**************************************
134 * 1. Generic code for all systems.
135 */
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200136
137/*
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200138 * Determine the terminal size from 'termsize' and the current window.
139 * Assumes term->tl_rows and term->tl_cols are zero.
140 */
141 static void
142set_term_and_win_size(term_T *term)
143{
144 if (*curwin->w_p_tms != NUL)
145 {
146 char_u *p = vim_strchr(curwin->w_p_tms, 'x') + 1;
147
148 term->tl_rows = atoi((char *)curwin->w_p_tms);
149 term->tl_cols = atoi((char *)p);
150 }
151 if (term->tl_rows == 0)
152 term->tl_rows = curwin->w_height;
153 else
154 {
155 win_setheight_win(term->tl_rows, curwin);
156 term->tl_rows_fixed = TRUE;
157 }
158 if (term->tl_cols == 0)
159 term->tl_cols = curwin->w_width;
160 else
161 {
162 win_setwidth_win(term->tl_cols, curwin);
163 term->tl_cols_fixed = TRUE;
164 }
165}
166
167/*
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200168 * ":terminal": open a terminal window and execute a job in it.
169 */
170 void
171ex_terminal(exarg_T *eap)
172{
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200173 exarg_T split_ea;
174 win_T *old_curwin = curwin;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200175 term_T *term;
Bram Moolenaare173fd02017-07-22 19:03:32 +0200176 char_u *cmd = eap->arg;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200177
178 if (check_restricted() || check_secure())
179 return;
180
181 term = (term_T *)alloc_clear(sizeof(term_T));
182 if (term == NULL)
183 return;
184 term->tl_dirty_row_end = MAX_ROW;
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200185 term->tl_cursor_visible = TRUE;
Bram Moolenaard85f2712017-07-28 21:51:57 +0200186 ga_init2(&term->tl_scrollback, sizeof(sb_line_T), 300);
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200187
188 /* Open a new window or tab. */
189 vim_memset(&split_ea, 0, sizeof(split_ea));
190 split_ea.cmdidx = CMD_new;
191 split_ea.cmd = (char_u *)"new";
192 split_ea.arg = (char_u *)"";
193 ex_splitview(&split_ea);
194 if (curwin == old_curwin)
195 {
196 /* split failed */
197 vim_free(term);
198 return;
199 }
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200200 term->tl_buffer = curbuf;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200201 curbuf->b_term = term;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200202
203 /* Link the new terminal in the list of active terminals. */
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200204 term->tl_next = first_term;
205 first_term = term;
206
Bram Moolenaar293424c2017-07-26 23:11:01 +0200207 if (cmd == NULL || *cmd == NUL)
208 cmd = p_sh;
209
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200210 if (buflist_findname(cmd) == NULL)
211 curbuf->b_ffname = vim_strsave(cmd);
212 else
213 {
214 int i;
215 size_t len = STRLEN(cmd) + 10;
Bram Moolenaara1b5b092017-07-26 21:29:34 +0200216 char_u *p = alloc((int)len);
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200217
218 for (i = 1; p != NULL; ++i)
219 {
220 vim_snprintf((char *)p, len, "%s (%d)", cmd, i);
221 if (buflist_findname(p) == NULL)
222 {
223 curbuf->b_ffname = p;
224 break;
225 }
226 }
227 }
228 curbuf->b_fname = curbuf->b_ffname;
229
230 /* Mark the buffer as changed, so that it's not easy to abandon the job. */
231 curbuf->b_changed = TRUE;
232 curbuf->b_p_ma = FALSE;
233 set_string_option_direct((char_u *)"buftype", -1,
234 (char_u *)"terminal", OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200235
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200236 set_term_and_win_size(term);
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200237
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200238 /* System dependent: setup the vterm and start the job in it. */
Bram Moolenaare173fd02017-07-22 19:03:32 +0200239 if (term_and_job_init(term, term->tl_rows, term->tl_cols, cmd) == OK)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200240 {
241 /* store the size we ended up with */
242 vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols);
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200243 }
244 else
245 {
Bram Moolenaard85f2712017-07-28 21:51:57 +0200246 free_terminal(curbuf);
Bram Moolenaar61a66052017-07-22 18:39:00 +0200247
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200248 /* Wiping out the buffer will also close the window and call
249 * free_terminal(). */
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200250 do_buffer(DOBUF_WIPE, DOBUF_CURRENT, FORWARD, 0, TRUE);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200251 }
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200252
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200253 /* TODO: Setup pty, see mch_call_shell(). */
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200254}
255
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200256/*
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200257 * Free a terminal and everything it refers to.
258 * Kills the job if there is one.
259 * Called when wiping out a buffer.
260 */
261 void
Bram Moolenaard85f2712017-07-28 21:51:57 +0200262free_terminal(buf_T *buf)
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200263{
Bram Moolenaard85f2712017-07-28 21:51:57 +0200264 term_T *term = buf->b_term;
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200265 term_T *tp;
Bram Moolenaard85f2712017-07-28 21:51:57 +0200266 int i;
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200267
268 if (term == NULL)
269 return;
270 if (first_term == term)
271 first_term = term->tl_next;
272 else
273 for (tp = first_term; tp->tl_next != NULL; tp = tp->tl_next)
274 if (tp->tl_next == term)
275 {
276 tp->tl_next = term->tl_next;
277 break;
278 }
279
280 if (term->tl_job != NULL)
281 {
Bram Moolenaar61a66052017-07-22 18:39:00 +0200282 if (term->tl_job->jv_status != JOB_ENDED
283 && term->tl_job->jv_status != JOB_FAILED)
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200284 job_stop(term->tl_job, NULL, "kill");
285 job_unref(term->tl_job);
286 }
287
Bram Moolenaard85f2712017-07-28 21:51:57 +0200288 for (i = 0; i < term->tl_scrollback.ga_len; ++i)
289 vim_free(((sb_line_T *)term->tl_scrollback.ga_data + i) ->sb_cells);
290 ga_clear(&term->tl_scrollback);
291
292 term_free_vterm(term);
Bram Moolenaar21554412017-07-24 21:44:43 +0200293 vim_free(term->tl_title);
294 vim_free(term->tl_status_text);
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200295 vim_free(term);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200296 buf->b_term = NULL;
Bram Moolenaar96ca27a2017-07-17 23:20:24 +0200297}
298
299/*
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200300 * Write job output "msg[len]" to the vterm.
301 */
302 static void
303term_write_job_output(term_T *term, char_u *msg, size_t len)
304{
305 VTerm *vterm = term->tl_vterm;
306 char_u *p;
307 size_t done;
308 size_t len_now;
309
310 for (done = 0; done < len; done += len_now)
311 {
312 for (p = msg + done; p < msg + len; )
313 {
314 if (*p == NL)
315 break;
Bram Moolenaara1b5b092017-07-26 21:29:34 +0200316 p += utf_ptr2len_len(p, (int)(len - (p - msg)));
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200317 }
318 len_now = p - msg - done;
319 vterm_input_write(vterm, (char *)msg + done, len_now);
320 if (p < msg + len && *p == NL)
321 {
322 /* Convert NL to CR-NL, that appears to work best. */
323 vterm_input_write(vterm, "\r\n", 2);
324 ++len_now;
325 }
326 }
327
328 /* this invokes the damage callbacks */
329 vterm_screen_flush_damage(vterm_obtain_screen(vterm));
330}
331
Bram Moolenaar1c844932017-07-24 23:36:41 +0200332 static void
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200333update_cursor(term_T *term, int redraw)
Bram Moolenaar1c844932017-07-24 23:36:41 +0200334{
Bram Moolenaar1c844932017-07-24 23:36:41 +0200335 setcursor();
Bram Moolenaar4cc93dc2017-07-26 21:49:37 +0200336 if (redraw && term->tl_buffer == curbuf)
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200337 {
Bram Moolenaar4cc93dc2017-07-26 21:49:37 +0200338 if (term->tl_cursor_visible)
339 cursor_on();
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200340 out_flush();
Bram Moolenaar1c844932017-07-24 23:36:41 +0200341#ifdef FEAT_GUI
Bram Moolenaar4cc93dc2017-07-26 21:49:37 +0200342 if (gui.in_use && term->tl_cursor_visible)
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200343 gui_update_cursor(FALSE, FALSE);
Bram Moolenaar1c844932017-07-24 23:36:41 +0200344#endif
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200345 }
Bram Moolenaar1c844932017-07-24 23:36:41 +0200346}
347
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200348/*
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200349 * Invoked when "msg" output from a job was received. Write it to the terminal
350 * of "buffer".
351 */
352 void
353write_to_term(buf_T *buffer, char_u *msg, channel_T *channel)
354{
355 size_t len = STRLEN(msg);
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200356 term_T *term = buffer->b_term;
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200357
Bram Moolenaard85f2712017-07-28 21:51:57 +0200358 if (term->tl_vterm == NULL)
359 {
360 ch_logn(channel, "NOT writing %d bytes to terminal", (int)len);
361 return;
362 }
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200363 ch_logn(channel, "writing %d bytes to terminal", (int)len);
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200364 term_write_job_output(term, msg, len);
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200365
366 /* TODO: only update once in a while. */
367 update_screen(0);
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200368 update_cursor(term, TRUE);
Bram Moolenaarcb8bbe92017-07-16 13:48:22 +0200369}
370
371/*
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200372 * Convert typed key "c" into bytes to send to the job.
373 * Return the number of bytes in "buf".
374 */
375 static int
376term_convert_key(int c, char *buf)
377{
378 VTerm *vterm = curbuf->b_term->tl_vterm;
379 VTermKey key = VTERM_KEY_NONE;
380 VTermModifier mod = VTERM_MOD_NONE;
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200381
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200382 switch (c)
383 {
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200384 case CAR: key = VTERM_KEY_ENTER; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200385 case ESC: key = VTERM_KEY_ESCAPE; break;
Bram Moolenaare906ae82017-07-21 21:10:01 +0200386 /* VTERM_KEY_BACKSPACE becomes 0x7f DEL */
387 case K_BS: c = BS; break;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200388 case K_DEL: key = VTERM_KEY_DEL; break;
389 case K_DOWN: key = VTERM_KEY_DOWN; break;
390 case K_END: key = VTERM_KEY_END; break;
391 case K_F10: key = VTERM_KEY_FUNCTION(10); break;
392 case K_F11: key = VTERM_KEY_FUNCTION(11); break;
393 case K_F12: key = VTERM_KEY_FUNCTION(12); break;
394 case K_F1: key = VTERM_KEY_FUNCTION(1); break;
395 case K_F2: key = VTERM_KEY_FUNCTION(2); break;
396 case K_F3: key = VTERM_KEY_FUNCTION(3); break;
397 case K_F4: key = VTERM_KEY_FUNCTION(4); break;
398 case K_F5: key = VTERM_KEY_FUNCTION(5); break;
399 case K_F6: key = VTERM_KEY_FUNCTION(6); break;
400 case K_F7: key = VTERM_KEY_FUNCTION(7); break;
401 case K_F8: key = VTERM_KEY_FUNCTION(8); break;
402 case K_F9: key = VTERM_KEY_FUNCTION(9); break;
403 case K_HOME: key = VTERM_KEY_HOME; break;
404 case K_INS: key = VTERM_KEY_INS; break;
405 case K_K0: key = VTERM_KEY_KP_0; break;
406 case K_K1: key = VTERM_KEY_KP_1; break;
407 case K_K2: key = VTERM_KEY_KP_2; break;
408 case K_K3: key = VTERM_KEY_KP_3; break;
409 case K_K4: key = VTERM_KEY_KP_4; break;
410 case K_K5: key = VTERM_KEY_KP_5; break;
411 case K_K6: key = VTERM_KEY_KP_6; break;
412 case K_K7: key = VTERM_KEY_KP_7; break;
413 case K_K8: key = VTERM_KEY_KP_8; break;
414 case K_K9: key = VTERM_KEY_KP_9; break;
415 case K_KDEL: key = VTERM_KEY_DEL; break; /* TODO */
416 case K_KDIVIDE: key = VTERM_KEY_KP_DIVIDE; break;
417 case K_KEND: key = VTERM_KEY_KP_1; break; /* TODO */
418 case K_KENTER: key = VTERM_KEY_KP_ENTER; break;
419 case K_KHOME: key = VTERM_KEY_KP_7; break; /* TODO */
420 case K_KINS: key = VTERM_KEY_KP_0; break; /* TODO */
421 case K_KMINUS: key = VTERM_KEY_KP_MINUS; break;
422 case K_KMULTIPLY: key = VTERM_KEY_KP_MULT; break;
423 case K_KPAGEDOWN: key = VTERM_KEY_KP_3; break; /* TODO */
424 case K_KPAGEUP: key = VTERM_KEY_KP_9; break; /* TODO */
425 case K_KPLUS: key = VTERM_KEY_KP_PLUS; break;
426 case K_KPOINT: key = VTERM_KEY_KP_PERIOD; break;
427 case K_LEFT: key = VTERM_KEY_LEFT; break;
428 case K_PAGEDOWN: key = VTERM_KEY_PAGEDOWN; break;
429 case K_PAGEUP: key = VTERM_KEY_PAGEUP; break;
430 case K_RIGHT: key = VTERM_KEY_RIGHT; break;
431 case K_UP: key = VTERM_KEY_UP; break;
432 case TAB: key = VTERM_KEY_TAB; break;
Bram Moolenaare825d8b2017-07-19 23:20:19 +0200433
434 case K_MOUSEUP: /* TODO */ break;
435 case K_MOUSEDOWN: /* TODO */ break;
436 case K_MOUSELEFT: /* TODO */ break;
437 case K_MOUSERIGHT: /* TODO */ break;
438
439 case K_LEFTMOUSE: /* TODO */ break;
440 case K_LEFTMOUSE_NM: /* TODO */ break;
441 case K_LEFTDRAG: /* TODO */ break;
442 case K_LEFTRELEASE: /* TODO */ break;
443 case K_LEFTRELEASE_NM: /* TODO */ break;
444 case K_MIDDLEMOUSE: /* TODO */ break;
445 case K_MIDDLEDRAG: /* TODO */ break;
446 case K_MIDDLERELEASE: /* TODO */ break;
447 case K_RIGHTMOUSE: /* TODO */ break;
448 case K_RIGHTDRAG: /* TODO */ break;
449 case K_RIGHTRELEASE: /* TODO */ break;
450 case K_X1MOUSE: /* TODO */ break;
451 case K_X1DRAG: /* TODO */ break;
452 case K_X1RELEASE: /* TODO */ break;
453 case K_X2MOUSE: /* TODO */ break;
454 case K_X2DRAG: /* TODO */ break;
455 case K_X2RELEASE: /* TODO */ break;
456
457 /* TODO: handle all special keys and modifiers that terminal_loop()
458 * does not handle. */
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200459 }
460
461 /*
462 * Convert special keys to vterm keys:
463 * - Write keys to vterm: vterm_keyboard_key()
464 * - Write output to channel.
465 */
466 if (key != VTERM_KEY_NONE)
467 /* Special key, let vterm convert it. */
468 vterm_keyboard_key(vterm, key, mod);
469 else
470 /* Normal character, let vterm convert it. */
471 vterm_keyboard_unichar(vterm, c, mod);
472
473 /* Read back the converted escape sequence. */
Bram Moolenaara1b5b092017-07-26 21:29:34 +0200474 return (int)vterm_output_read(vterm, buf, KEY_BUF_LEN);
Bram Moolenaar8c0095c2017-07-18 22:53:21 +0200475}
Bram Moolenaare4f25e42017-07-07 11:54:15 +0200476
Bram Moolenaar938783d2017-07-16 20:13:26 +0200477/*
Bram Moolenaard85f2712017-07-28 21:51:57 +0200478 * Return TRUE if the job for "buf" is still running.
479 */
480 static int
481term_job_running(term_T *term)
482{
483 return term->tl_job != NULL && term->tl_job->jv_status == JOB_STARTED;
484}
485
486/*
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +0200487 * Get a key from the user without mapping.
488 * TODO: use terminal mode mappings.
489 */
490 static int
491term_vgetc()
492{
493 int c;
494
495 ++no_mapping;
496 ++allow_keys;
497 got_int = FALSE;
498 c = vgetc();
499 --no_mapping;
500 --allow_keys;
501 return c;
502}
503
504/*
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200505 * Wait for input and send it to the job.
506 * Return when the start of a CTRL-W command is typed or anything else that
507 * should be handled as a Normal mode command.
Bram Moolenaard85f2712017-07-28 21:51:57 +0200508 * Returns OK if a typed character is to be handled in Normal mode, FAIL if
509 * the terminal was closed.
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200510 */
Bram Moolenaard85f2712017-07-28 21:51:57 +0200511 int
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200512terminal_loop(void)
513{
514 char buf[KEY_BUF_LEN];
515 int c;
516 size_t len;
517 static int mouse_was_outside = FALSE;
518 int dragging_outside = FALSE;
Bram Moolenaardbe948d2017-07-23 22:50:51 +0200519 int termkey = 0;
520
Bram Moolenaard85f2712017-07-28 21:51:57 +0200521 if (curbuf->b_term->tl_vterm == NULL || !term_job_running(curbuf->b_term))
522 /* job finished */
523 return OK;
524
Bram Moolenaardbe948d2017-07-23 22:50:51 +0200525 if (*curwin->w_p_tk != NUL)
526 termkey = string_to_key(curwin->w_p_tk, TRUE);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200527
528 for (;;)
529 {
530 /* TODO: skip screen update when handling a sequence of keys. */
531 update_screen(0);
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200532 update_cursor(curbuf->b_term, FALSE);
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +0200533 c = term_vgetc();
Bram Moolenaard85f2712017-07-28 21:51:57 +0200534 if (curbuf->b_term->tl_vterm == NULL
535 || !term_job_running(curbuf->b_term))
536 /* job finished while waiting for a character */
537 break;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200538
Bram Moolenaardbe948d2017-07-23 22:50:51 +0200539 if (c == (termkey == 0 ? Ctrl_W : termkey))
540 {
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +0200541#ifdef FEAT_CMDL_INFO
542 if (add_to_showcmd(c))
543 out_flush();
544#endif
545 c = term_vgetc();
546#ifdef FEAT_CMDL_INFO
547 clear_showcmd();
548#endif
Bram Moolenaard85f2712017-07-28 21:51:57 +0200549 if (curbuf->b_term->tl_vterm == NULL
550 || !term_job_running(curbuf->b_term))
551 /* job finished while waiting for a character */
552 break;
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +0200553
554 if (termkey == 0 && c == '.')
555 /* "CTRL-W .": send CTRL-W to the job */
556 c = Ctrl_W;
557 else if (termkey == 0 || c != termkey)
558 {
559 stuffcharReadbuff(Ctrl_W);
560 stuffcharReadbuff(c);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200561 return OK;
Bram Moolenaar1f28b4c2017-07-28 13:48:34 +0200562 }
Bram Moolenaardbe948d2017-07-23 22:50:51 +0200563 }
564
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200565 /* Catch keys that need to be handled as in Normal mode. */
566 switch (c)
567 {
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200568 case NUL:
569 case K_ZERO:
570 stuffcharReadbuff(c);
Bram Moolenaard85f2712017-07-28 21:51:57 +0200571 return OK;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200572
573 case K_IGNORE: continue;
574
575 case K_LEFTDRAG:
576 case K_MIDDLEDRAG:
577 case K_RIGHTDRAG:
578 case K_X1DRAG:
579 case K_X2DRAG:
580 dragging_outside = mouse_was_outside;
581 /* FALLTHROUGH */
582 case K_LEFTMOUSE:
583 case K_LEFTMOUSE_NM:
584 case K_LEFTRELEASE:
585 case K_LEFTRELEASE_NM:
586 case K_MIDDLEMOUSE:
587 case K_MIDDLERELEASE:
588 case K_RIGHTMOUSE:
589 case K_RIGHTRELEASE:
590 case K_X1MOUSE:
591 case K_X1RELEASE:
592 case K_X2MOUSE:
593 case K_X2RELEASE:
594 if (mouse_row < W_WINROW(curwin)
595 || mouse_row >= (W_WINROW(curwin) + curwin->w_height)
596 || mouse_col < W_WINCOL(curwin)
597 || mouse_col >= W_ENDCOL(curwin)
598 || dragging_outside)
599 {
600 /* click outside the current window */
601 stuffcharReadbuff(c);
602 mouse_was_outside = TRUE;
Bram Moolenaard85f2712017-07-28 21:51:57 +0200603 return OK;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200604 }
605 }
606 mouse_was_outside = FALSE;
607
608 /* Convert the typed key to a sequence of bytes for the job. */
609 len = term_convert_key(c, buf);
610 if (len > 0)
611 /* TODO: if FAIL is returned, stop? */
612 channel_send(curbuf->b_term->tl_job->jv_channel, PART_IN,
Bram Moolenaard85f2712017-07-28 21:51:57 +0200613 (char_u *)buf, (int)len, NULL);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200614 }
Bram Moolenaard85f2712017-07-28 21:51:57 +0200615 return FAIL;
Bram Moolenaar1f2903c2017-07-23 19:51:01 +0200616}
617
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200618 static void
619position_cursor(win_T *wp, VTermPos *pos)
620{
621 wp->w_wrow = MIN(pos->row, MAX(0, wp->w_height - 1));
622 wp->w_wcol = MIN(pos->col, MAX(0, wp->w_width - 1));
623}
624
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200625 static void
626may_toggle_cursor(term_T *term)
627{
628 if (curbuf == term->tl_buffer)
629 {
630 if (term->tl_cursor_visible)
631 cursor_on();
632 else
633 cursor_off();
634 }
635}
636
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200637 static int
638handle_damage(VTermRect rect, void *user)
639{
640 term_T *term = (term_T *)user;
641
642 term->tl_dirty_row_start = MIN(term->tl_dirty_row_start, rect.start_row);
643 term->tl_dirty_row_end = MAX(term->tl_dirty_row_end, rect.end_row);
644 redraw_buf_later(term->tl_buffer, NOT_VALID);
645 return 1;
646}
647
648 static int
649handle_moverect(VTermRect dest UNUSED, VTermRect src UNUSED, void *user)
650{
651 term_T *term = (term_T *)user;
652
653 /* TODO */
654 redraw_buf_later(term->tl_buffer, NOT_VALID);
655 return 1;
656}
657
658 static int
659handle_movecursor(
660 VTermPos pos,
661 VTermPos oldpos UNUSED,
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200662 int visible,
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200663 void *user)
664{
665 term_T *term = (term_T *)user;
666 win_T *wp;
667 int is_current = FALSE;
668
669 FOR_ALL_WINDOWS(wp)
670 {
671 if (wp->w_buffer == term->tl_buffer)
672 {
673 position_cursor(wp, &pos);
674 if (wp == curwin)
675 is_current = TRUE;
676 }
677 }
678
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200679 term->tl_cursor_visible = visible;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200680 if (is_current)
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200681 {
682 may_toggle_cursor(term);
683 update_cursor(term, TRUE);
684 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200685
686 return 1;
687}
688
Bram Moolenaar21554412017-07-24 21:44:43 +0200689 static int
690handle_settermprop(
691 VTermProp prop,
692 VTermValue *value,
693 void *user)
694{
695 term_T *term = (term_T *)user;
696
697 switch (prop)
698 {
699 case VTERM_PROP_TITLE:
700 vim_free(term->tl_title);
701 term->tl_title = vim_strsave((char_u *)value->string);
702 vim_free(term->tl_status_text);
703 term->tl_status_text = NULL;
704 if (term == curbuf->b_term)
705 maketitle();
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200706 break;
707
708 case VTERM_PROP_CURSORVISIBLE:
709 term->tl_cursor_visible = value->boolean;
710 may_toggle_cursor(term);
711 out_flush();
712 break;
713
Bram Moolenaar21554412017-07-24 21:44:43 +0200714 default:
715 break;
716 }
Bram Moolenaarfc716d72017-07-25 23:08:47 +0200717 /* Always return 1, otherwise vterm doesn't store the value internally. */
718 return 1;
Bram Moolenaar21554412017-07-24 21:44:43 +0200719}
720
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200721/*
722 * The job running in the terminal resized the terminal.
723 */
724 static int
725handle_resize(int rows, int cols, void *user)
726{
727 term_T *term = (term_T *)user;
728 win_T *wp;
729
730 term->tl_rows = rows;
731 term->tl_cols = cols;
732 FOR_ALL_WINDOWS(wp)
733 {
734 if (wp->w_buffer == term->tl_buffer)
735 {
736 win_setheight_win(rows, wp);
737 win_setwidth_win(cols, wp);
738 }
739 }
740
741 redraw_buf_later(term->tl_buffer, NOT_VALID);
742 return 1;
743}
744
Bram Moolenaard85f2712017-07-28 21:51:57 +0200745/*
746 * Handle a line that is pushed off the top of the screen.
747 */
748 static int
749handle_pushline(int cols, const VTermScreenCell *cells, void *user)
750{
751 term_T *term = (term_T *)user;
752
753 /* TODO: Limit the number of lines that are stored. */
754 /* TODO: put the text in the buffer. */
755 if (ga_grow(&term->tl_scrollback, 1) == OK)
756 {
757 VTermScreenCell *p;
758 int len;
759 int i;
760
761 /* do not store empty cells at the end */
762 for (i = 0; i < cols; ++i)
763 if (cells[i].chars[0] != 0)
764 len = i + 1;
765
766 p = (VTermScreenCell *)alloc((int)sizeof(VTermScreenCell) * len);
767 if (p != NULL)
768 {
769 sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data
770 + term->tl_scrollback.ga_len;
771
772 mch_memmove(p, cells, sizeof(VTermScreenCell) * len);
773 line->sb_cols = len;
774 line->sb_cells = p;
775 ++term->tl_scrollback.ga_len;
776 }
777 }
778 return 0; /* ignored */
779}
780
781/*
782 * Fill the buffer with the scrollback lines and current lines of the terminal.
783 * Called after the job has ended.
784 */
785 static void
786move_scrollback_to_buffer(term_T *term)
787{
788 linenr_T lnum;
789 garray_T ga;
790 int c;
791 int col;
792 int i;
793 win_T *wp;
794 int len;
795 int lines_skipped = 0;
796 VTermPos pos;
797 VTermScreenCell cell;
798 VTermScreenCell *p;
799 VTermScreen *screen = vterm_obtain_screen(term->tl_vterm);
800
801 /* Append the the visible lines to the scrollback. */
802 for (pos.row = 0; pos.row < term->tl_rows; ++pos.row)
803 {
804 len = 0;
805 for (pos.col = 0; pos.col < term->tl_cols; ++pos.col)
806 if (vterm_screen_get_cell(screen, pos, &cell) != 0
807 && cell.chars[0] != NUL)
808 len = pos.col + 1;
809
810 if (len > 0)
811 {
812 while (lines_skipped > 0)
813 {
814 /* Line was skipped, add an empty line. */
815 --lines_skipped;
816 if (ga_grow(&term->tl_scrollback, 1) == OK)
817 {
818 sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data
819 + term->tl_scrollback.ga_len;
820
821 line->sb_cols = 0;
822 line->sb_cells = NULL;
823 ++term->tl_scrollback.ga_len;
824 }
825 }
826
827 p = (VTermScreenCell *)alloc((int)sizeof(VTermScreenCell) * len);
828 if (p != NULL && ga_grow(&term->tl_scrollback, 1) == OK)
829 {
830 sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data
831 + term->tl_scrollback.ga_len;
832
833 for (pos.col = 0; pos.col < len; ++pos.col)
834 {
835 if (vterm_screen_get_cell(screen, pos, &cell) == 0)
836 vim_memset(p + pos.col, 0, sizeof(cell));
837 else
838 p[pos.col] = cell;
839 }
840 line->sb_cols = len;
841 line->sb_cells = p;
842 ++term->tl_scrollback.ga_len;
843 }
844 else
845 vim_free(p);
846 }
847 }
848
849 /* Add the text to the buffer. */
850 ga_init2(&ga, 1, 100);
851 for (lnum = 0; lnum < term->tl_scrollback.ga_len; ++lnum)
852 {
853 sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data + lnum;
854
855 ga.ga_len = 0;
856 for (col = 0; col < line->sb_cols; ++col)
857 for (i = 0; (c = line->sb_cells[col].chars[i]) != 0 || i == 0; ++i)
858 {
859 if (ga_grow(&ga, MB_MAXBYTES) == FAIL)
860 goto failed;
861 ga.ga_len += mb_char2bytes(c == NUL ? ' ' : c,
862 (char_u *)ga.ga_data + ga.ga_len);
863 }
864 *((char_u *)ga.ga_data + ga.ga_len) = NUL;
865 ml_append_buf(term->tl_buffer, lnum, ga.ga_data, ga.ga_len + 1, FALSE);
866 }
867
868 /* Delete the empty line that was in the empty buffer. */
869 curbuf = term->tl_buffer;
870 ml_delete(lnum + 1, FALSE);
871 curbuf = curwin->w_buffer;
872
873failed:
874 ga_clear(&ga);
875
876 FOR_ALL_WINDOWS(wp)
877 {
878 if (wp->w_buffer == term->tl_buffer)
879 {
880 wp->w_cursor.lnum = term->tl_buffer->b_ml.ml_line_count;
881 wp->w_cursor.col = 0;
882 wp->w_valid = 0;
883 }
884 }
885}
886
Bram Moolenaar21554412017-07-24 21:44:43 +0200887static VTermScreenCallbacks screen_callbacks = {
888 handle_damage, /* damage */
889 handle_moverect, /* moverect */
890 handle_movecursor, /* movecursor */
891 handle_settermprop, /* settermprop */
892 NULL, /* bell */
893 handle_resize, /* resize */
Bram Moolenaard85f2712017-07-28 21:51:57 +0200894 handle_pushline, /* sb_pushline */
Bram Moolenaar21554412017-07-24 21:44:43 +0200895 NULL /* sb_popline */
896};
897
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +0200898/*
Bram Moolenaard85f2712017-07-28 21:51:57 +0200899 * Called when a channel has been closed.
900 */
901 void
902term_channel_closed(channel_T *ch)
903{
904 term_T *term;
905 int did_one = FALSE;
906
907 for (term = first_term; term != NULL; term = term->tl_next)
908 if (term->tl_job == ch->ch_job)
909 {
910 vim_free(term->tl_title);
911 term->tl_title = NULL;
912 vim_free(term->tl_status_text);
913 term->tl_status_text = NULL;
914
915 /* move the lines into the buffer and free the vterm */
916 move_scrollback_to_buffer(term);
917 term_free_vterm(term);
918
919 redraw_buf_and_status_later(term->tl_buffer, NOT_VALID);
920 did_one = TRUE;
921 }
922 if (did_one)
923 {
924 redraw_statuslines();
925
926 /* Need to break out of vgetc(). */
927 ins_char_typebuf(K_IGNORE);
928
929 if (curbuf->b_term != NULL)
930 {
931 if (curbuf->b_term->tl_job == ch->ch_job)
932 maketitle();
933 update_cursor(curbuf->b_term, TRUE);
934 }
935 }
936}
937
938/*
Bram Moolenaareeac6772017-07-23 15:48:37 +0200939 * Reverse engineer the RGB value into a cterm color index.
940 * First color is 1. Return 0 if no match found.
941 */
942 static int
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +0200943color2index(VTermColor *color, int foreground)
Bram Moolenaareeac6772017-07-23 15:48:37 +0200944{
945 int red = color->red;
946 int blue = color->blue;
947 int green = color->green;
948
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +0200949 /* The argument for lookup_color() is for the color_names[] table. */
Bram Moolenaareeac6772017-07-23 15:48:37 +0200950 if (red == 0)
951 {
952 if (green == 0)
953 {
954 if (blue == 0)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +0200955 return lookup_color(0, foreground) + 1; /* black */
Bram Moolenaareeac6772017-07-23 15:48:37 +0200956 if (blue == 224)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +0200957 return lookup_color(1, foreground) + 1; /* dark blue */
Bram Moolenaareeac6772017-07-23 15:48:37 +0200958 }
959 else if (green == 224)
960 {
961 if (blue == 0)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +0200962 return lookup_color(2, foreground) + 1; /* dark green */
Bram Moolenaareeac6772017-07-23 15:48:37 +0200963 if (blue == 224)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +0200964 return lookup_color(3, foreground) + 1; /* dark cyan */
Bram Moolenaareeac6772017-07-23 15:48:37 +0200965 }
966 }
967 else if (red == 224)
968 {
969 if (green == 0)
970 {
971 if (blue == 0)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +0200972 return lookup_color(4, foreground) + 1; /* dark red */
Bram Moolenaareeac6772017-07-23 15:48:37 +0200973 if (blue == 224)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +0200974 return lookup_color(5, foreground) + 1; /* dark magenta */
Bram Moolenaareeac6772017-07-23 15:48:37 +0200975 }
976 else if (green == 224)
977 {
978 if (blue == 0)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +0200979 return lookup_color(6, foreground) + 1; /* dark yellow / brown */
Bram Moolenaareeac6772017-07-23 15:48:37 +0200980 if (blue == 224)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +0200981 return lookup_color(8, foreground) + 1; /* white / light grey */
Bram Moolenaareeac6772017-07-23 15:48:37 +0200982 }
983 }
984 else if (red == 128)
985 {
986 if (green == 128 && blue == 128)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +0200987 return lookup_color(12, foreground) + 1; /* high intensity black / dark grey */
Bram Moolenaareeac6772017-07-23 15:48:37 +0200988 }
989 else if (red == 255)
990 {
991 if (green == 64)
992 {
993 if (blue == 64)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +0200994 return lookup_color(20, foreground) + 1; /* light red */
Bram Moolenaareeac6772017-07-23 15:48:37 +0200995 if (blue == 255)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +0200996 return lookup_color(22, foreground) + 1; /* light magenta */
Bram Moolenaareeac6772017-07-23 15:48:37 +0200997 }
998 else if (green == 255)
999 {
1000 if (blue == 64)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02001001 return lookup_color(24, foreground) + 1; /* yellow */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001002 if (blue == 255)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02001003 return lookup_color(26, foreground) + 1; /* white */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001004 }
1005 }
1006 else if (red == 64)
1007 {
1008 if (green == 64)
1009 {
1010 if (blue == 255)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02001011 return lookup_color(14, foreground) + 1; /* light blue */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001012 }
1013 else if (green == 255)
1014 {
1015 if (blue == 64)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02001016 return lookup_color(16, foreground) + 1; /* light green */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001017 if (blue == 255)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02001018 return lookup_color(18, foreground) + 1; /* light cyan */
Bram Moolenaareeac6772017-07-23 15:48:37 +02001019 }
1020 }
1021 if (t_colors >= 256)
1022 {
1023 if (red == blue && red == green)
1024 {
1025 /* 24-color greyscale */
1026 static int cutoff[23] = {
1027 0x05, 0x10, 0x1B, 0x26, 0x31, 0x3C, 0x47, 0x52,
1028 0x5D, 0x68, 0x73, 0x7F, 0x8A, 0x95, 0xA0, 0xAB,
1029 0xB6, 0xC1, 0xCC, 0xD7, 0xE2, 0xED, 0xF9};
1030 int i;
1031
1032 for (i = 0; i < 23; ++i)
1033 if (red < cutoff[i])
1034 return i + 233;
1035 return 256;
1036 }
1037
1038 /* 216-color cube */
1039 return 17 + ((red + 25) / 0x33) * 36
1040 + ((green + 25) / 0x33) * 6
1041 + (blue + 25) / 0x33;
1042 }
1043 return 0;
1044}
1045
1046/*
1047 * Convert the attributes of a vterm cell into an attribute index.
1048 */
1049 static int
1050cell2attr(VTermScreenCell *cell)
1051{
1052 int attr = 0;
1053
1054 if (cell->attrs.bold)
1055 attr |= HL_BOLD;
1056 if (cell->attrs.underline)
1057 attr |= HL_UNDERLINE;
1058 if (cell->attrs.italic)
1059 attr |= HL_ITALIC;
1060 if (cell->attrs.strike)
1061 attr |= HL_STANDOUT;
1062 if (cell->attrs.reverse)
1063 attr |= HL_INVERSE;
1064 if (cell->attrs.strike)
1065 attr |= HL_UNDERLINE;
1066
1067#ifdef FEAT_GUI
1068 if (gui.in_use)
1069 {
Bram Moolenaar26af85d2017-07-23 16:45:10 +02001070 guicolor_T fg, bg;
1071
1072 fg = gui_mch_get_rgb_color(cell->fg.red, cell->fg.green, cell->fg.blue);
1073 bg = gui_mch_get_rgb_color(cell->bg.red, cell->bg.green, cell->bg.blue);
1074 return get_gui_attr_idx(attr, fg, bg);
Bram Moolenaareeac6772017-07-23 15:48:37 +02001075 }
1076 else
1077#endif
1078#ifdef FEAT_TERMGUICOLORS
1079 if (p_tgc)
1080 {
Bram Moolenaar065f41c2017-07-23 18:07:56 +02001081 guicolor_T fg, bg;
1082
1083 fg = gui_get_rgb_color_cmn(cell->fg.red, cell->fg.green, cell->fg.blue);
1084 bg = gui_get_rgb_color_cmn(cell->bg.red, cell->bg.green, cell->bg.blue);
1085
1086 return get_tgc_attr_idx(attr, fg, bg);
Bram Moolenaareeac6772017-07-23 15:48:37 +02001087 }
Bram Moolenaar065f41c2017-07-23 18:07:56 +02001088 else
Bram Moolenaareeac6772017-07-23 15:48:37 +02001089#endif
1090 {
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02001091 return get_cterm_attr_idx(attr, color2index(&cell->fg, TRUE),
1092 color2index(&cell->bg, FALSE));
Bram Moolenaareeac6772017-07-23 15:48:37 +02001093 }
1094 return 0;
1095}
1096
1097/*
Bram Moolenaard85f2712017-07-28 21:51:57 +02001098 * Called to update the window that contains a terminal.
1099 * Returns FAIL when there is no terminal running in this window.
Bram Moolenaare4f25e42017-07-07 11:54:15 +02001100 */
Bram Moolenaard85f2712017-07-28 21:51:57 +02001101 int
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001102term_update_window(win_T *wp)
Bram Moolenaar938783d2017-07-16 20:13:26 +02001103{
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001104 term_T *term = wp->w_buffer->b_term;
Bram Moolenaard85f2712017-07-28 21:51:57 +02001105 VTerm *vterm;
1106 VTermScreen *screen;
1107 VTermState *state;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001108 VTermPos pos;
Bram Moolenaar938783d2017-07-16 20:13:26 +02001109
Bram Moolenaard85f2712017-07-28 21:51:57 +02001110 if (term == NULL || term->tl_vterm == NULL)
1111 return FAIL;
1112 vterm = term->tl_vterm;
1113 screen = vterm_obtain_screen(vterm);
1114 state = vterm_obtain_state(vterm);
1115
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001116 /*
1117 * If the window was resized a redraw will be triggered and we get here.
1118 * Adjust the size of the vterm unless 'termsize' specifies a fixed size.
1119 */
1120 if ((!term->tl_rows_fixed && term->tl_rows != wp->w_height)
1121 || (!term->tl_cols_fixed && term->tl_cols != wp->w_width))
Bram Moolenaarb13501f2017-07-22 22:32:56 +02001122 {
Bram Moolenaar96ad8c92017-07-28 14:17:34 +02001123 int rows = term->tl_rows_fixed ? term->tl_rows : wp->w_height;
1124 int cols = term->tl_cols_fixed ? term->tl_cols : wp->w_width;
1125 win_T *twp;
1126
1127 FOR_ALL_WINDOWS(twp)
1128 {
1129 /* When more than one window shows the same terminal, use the
1130 * smallest size. */
1131 if (twp->w_buffer == term->tl_buffer)
1132 {
1133 if (!term->tl_rows_fixed && rows > twp->w_height)
1134 rows = twp->w_height;
1135 if (!term->tl_cols_fixed && cols > twp->w_width)
1136 cols = twp->w_width;
1137 }
1138 }
Bram Moolenaarb13501f2017-07-22 22:32:56 +02001139
1140 vterm_set_size(vterm, rows, cols);
1141 ch_logn(term->tl_job->jv_channel, "Resizing terminal to %d lines",
1142 rows);
Bram Moolenaar43da3e32017-07-23 17:27:54 +02001143 term_report_winsize(term, rows, cols);
Bram Moolenaarb13501f2017-07-22 22:32:56 +02001144 }
Bram Moolenaar58556cd2017-07-20 23:04:46 +02001145
1146 /* The cursor may have been moved when resizing. */
1147 vterm_state_get_cursorpos(state, &pos);
1148 position_cursor(wp, &pos);
1149
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001150 /* TODO: Only redraw what changed. */
1151 for (pos.row = 0; pos.row < wp->w_height; ++pos.row)
Bram Moolenaar938783d2017-07-16 20:13:26 +02001152 {
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001153 int off = screen_get_current_line_off();
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001154 int max_col = MIN(wp->w_width, term->tl_cols);
Bram Moolenaar938783d2017-07-16 20:13:26 +02001155
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001156 if (pos.row < term->tl_rows)
1157 {
1158 for (pos.col = 0; pos.col < max_col; )
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001159 {
1160 VTermScreenCell cell;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001161 int c;
Bram Moolenaar938783d2017-07-16 20:13:26 +02001162
Bram Moolenaareeac6772017-07-23 15:48:37 +02001163 if (vterm_screen_get_cell(screen, pos, &cell) == 0)
1164 vim_memset(&cell, 0, sizeof(cell));
1165
1166 /* TODO: composing chars */
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001167 c = cell.chars[0];
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001168 if (c == NUL)
1169 {
1170 ScreenLines[off] = ' ';
Bram Moolenaar8a773062017-07-24 22:29:21 +02001171#if defined(FEAT_MBYTE)
1172 if (enc_utf8)
1173 ScreenLinesUC[off] = NUL;
1174#endif
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001175 }
1176 else
1177 {
1178#if defined(FEAT_MBYTE)
1179 if (enc_utf8 && c >= 0x80)
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02001180 {
1181 ScreenLines[off] = ' ';
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001182 ScreenLinesUC[off] = c;
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02001183 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001184 else
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02001185 {
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001186 ScreenLines[off] = c;
Bram Moolenaar8a773062017-07-24 22:29:21 +02001187 if (enc_utf8)
1188 ScreenLinesUC[off] = NUL;
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02001189 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001190#else
1191 ScreenLines[off] = c;
1192#endif
1193 }
Bram Moolenaareeac6772017-07-23 15:48:37 +02001194 ScreenAttrs[off] = cell2attr(&cell);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001195
1196 ++pos.col;
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001197 ++off;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001198 if (cell.width == 2)
1199 {
Bram Moolenaar9f1f49b2017-07-22 18:14:17 +02001200 ScreenLines[off] = NUL;
Bram Moolenaar8a773062017-07-24 22:29:21 +02001201#if defined(FEAT_MBYTE)
1202 if (enc_utf8)
1203 ScreenLinesUC[off] = NUL;
1204#endif
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001205 ++pos.col;
1206 ++off;
1207 }
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001208 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001209 }
Bram Moolenaare825d8b2017-07-19 23:20:19 +02001210 else
1211 pos.col = 0;
Bram Moolenaar938783d2017-07-16 20:13:26 +02001212
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001213 screen_line(wp->w_winrow + pos.row, wp->w_wincol,
1214 pos.col, wp->w_width, FALSE);
Bram Moolenaar938783d2017-07-16 20:13:26 +02001215 }
Bram Moolenaard85f2712017-07-28 21:51:57 +02001216
1217 return OK;
Bram Moolenaar938783d2017-07-16 20:13:26 +02001218}
Bram Moolenaare4f25e42017-07-07 11:54:15 +02001219
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001220/*
1221 * Set job options common for Unix and MS-Windows.
1222 */
1223 static void
1224setup_job_options(jobopt_T *opt, int rows, int cols)
1225{
1226 clear_job_options(opt);
1227 opt->jo_mode = MODE_RAW;
1228 opt->jo_out_mode = MODE_RAW;
1229 opt->jo_err_mode = MODE_RAW;
1230 opt->jo_set = JO_MODE | JO_OUT_MODE | JO_ERR_MODE;
Bram Moolenaar1f2903c2017-07-23 19:51:01 +02001231
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001232 opt->jo_io[PART_OUT] = JIO_BUFFER;
1233 opt->jo_io[PART_ERR] = JIO_BUFFER;
Bram Moolenaar1f2903c2017-07-23 19:51:01 +02001234 opt->jo_set |= JO_OUT_IO + JO_ERR_IO;
1235
1236 opt->jo_modifiable[PART_OUT] = 0;
1237 opt->jo_modifiable[PART_ERR] = 0;
1238 opt->jo_set |= JO_OUT_MODIFIABLE + JO_ERR_MODIFIABLE;
1239
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001240 opt->jo_io_buf[PART_OUT] = curbuf->b_fnum;
1241 opt->jo_io_buf[PART_ERR] = curbuf->b_fnum;
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02001242 opt->jo_pty = TRUE;
Bram Moolenaar1f2903c2017-07-23 19:51:01 +02001243 opt->jo_set |= JO_OUT_BUF + JO_ERR_BUF;
1244
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001245 opt->jo_term_rows = rows;
1246 opt->jo_term_cols = cols;
1247}
1248
1249/*
1250 * Create a new vterm and initialize it.
1251 */
1252 static void
1253create_vterm(term_T *term, int rows, int cols)
1254{
1255 VTerm *vterm;
1256 VTermScreen *screen;
1257
1258 vterm = vterm_new(rows, cols);
1259 term->tl_vterm = vterm;
1260 screen = vterm_obtain_screen(vterm);
1261 vterm_screen_set_callbacks(screen, &screen_callbacks, term);
1262 /* TODO: depends on 'encoding'. */
1263 vterm_set_utf8(vterm, 1);
Bram Moolenaareeac6772017-07-23 15:48:37 +02001264
1265 /* Vterm uses a default black background. Set it to white when
1266 * 'background' is "light". */
1267 if (*p_bg == 'l')
1268 {
1269 VTermColor fg, bg;
1270
1271 fg.red = fg.green = fg.blue = 0;
1272 bg.red = bg.green = bg.blue = 255;
1273 vterm_state_set_default_colors(vterm_obtain_state(vterm), &fg, &bg);
1274 }
1275
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001276 /* Required to initialize most things. */
1277 vterm_screen_reset(screen, 1 /* hard */);
1278}
1279
Bram Moolenaar21554412017-07-24 21:44:43 +02001280/*
1281 * Return the text to show for the buffer name and status.
1282 */
1283 char_u *
1284term_get_status_text(term_T *term)
1285{
1286 if (term->tl_status_text == NULL)
1287 {
1288 char_u *txt;
1289 size_t len;
1290
1291 if (term->tl_title != NULL)
1292 txt = term->tl_title;
1293 else if (term_job_running(term))
1294 txt = (char_u *)_("running");
1295 else
1296 txt = (char_u *)_("finished");
1297 len = 9 + STRLEN(term->tl_buffer->b_fname) + STRLEN(txt);
Bram Moolenaara1b5b092017-07-26 21:29:34 +02001298 term->tl_status_text = alloc((int)len);
Bram Moolenaar21554412017-07-24 21:44:43 +02001299 if (term->tl_status_text != NULL)
1300 vim_snprintf((char *)term->tl_status_text, len, "%s [%s]",
1301 term->tl_buffer->b_fname, txt);
1302 }
1303 return term->tl_status_text;
1304}
1305
Bram Moolenaarf86eea92017-07-28 13:51:30 +02001306/*
1307 * Mark references in jobs of terminals.
1308 */
1309 int
1310set_ref_in_term(int copyID)
1311{
1312 int abort = FALSE;
1313 term_T *term;
1314 typval_T tv;
1315
1316 for (term = first_term; term != NULL; term = term->tl_next)
1317 if (term->tl_job != NULL)
1318 {
1319 tv.v_type = VAR_JOB;
1320 tv.vval.v_job = term->tl_job;
1321 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
1322 }
1323 return abort;
1324}
1325
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001326# ifdef WIN3264
1327
1328#define WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN 1ul
1329#define WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN 2ull
1330
Bram Moolenaar8a773062017-07-24 22:29:21 +02001331void* (*winpty_config_new)(UINT64, void*);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001332void* (*winpty_open)(void*, void*);
Bram Moolenaar8a773062017-07-24 22:29:21 +02001333void* (*winpty_spawn_config_new)(UINT64, void*, LPCWSTR, void*, void*, void*);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001334BOOL (*winpty_spawn)(void*, void*, HANDLE*, HANDLE*, DWORD*, void*);
1335void (*winpty_config_set_initial_size)(void*, int, int);
1336LPCWSTR (*winpty_conin_name)(void*);
1337LPCWSTR (*winpty_conout_name)(void*);
1338LPCWSTR (*winpty_conerr_name)(void*);
1339void (*winpty_free)(void*);
1340void (*winpty_config_free)(void*);
1341void (*winpty_spawn_config_free)(void*);
1342void (*winpty_error_free)(void*);
1343LPCWSTR (*winpty_error_msg)(void*);
Bram Moolenaar43da3e32017-07-23 17:27:54 +02001344BOOL (*winpty_set_size)(void*, int, int, void*);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001345
1346/**************************************
1347 * 2. MS-Windows implementation.
1348 */
1349
1350#define WINPTY_DLL "winpty.dll"
1351
1352static HINSTANCE hWinPtyDLL = NULL;
1353
1354 int
1355dyn_winpty_init(void)
1356{
1357 int i;
1358 static struct
1359 {
1360 char *name;
1361 FARPROC *ptr;
1362 } winpty_entry[] =
1363 {
1364 {"winpty_conerr_name", (FARPROC*)&winpty_conerr_name},
1365 {"winpty_config_free", (FARPROC*)&winpty_config_free},
1366 {"winpty_config_new", (FARPROC*)&winpty_config_new},
1367 {"winpty_config_set_initial_size", (FARPROC*)&winpty_config_set_initial_size},
1368 {"winpty_conin_name", (FARPROC*)&winpty_conin_name},
1369 {"winpty_conout_name", (FARPROC*)&winpty_conout_name},
1370 {"winpty_error_free", (FARPROC*)&winpty_error_free},
1371 {"winpty_free", (FARPROC*)&winpty_free},
1372 {"winpty_open", (FARPROC*)&winpty_open},
1373 {"winpty_spawn", (FARPROC*)&winpty_spawn},
1374 {"winpty_spawn_config_free", (FARPROC*)&winpty_spawn_config_free},
1375 {"winpty_spawn_config_new", (FARPROC*)&winpty_spawn_config_new},
1376 {"winpty_error_msg", (FARPROC*)&winpty_error_msg},
Bram Moolenaar43da3e32017-07-23 17:27:54 +02001377 {"winpty_set_size", (FARPROC*)&winpty_set_size},
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001378 {NULL, NULL}
1379 };
1380
1381 /* No need to initialize twice. */
1382 if (hWinPtyDLL)
1383 return 1;
1384 /* Load winpty.dll */
1385 hWinPtyDLL = vimLoadLib(WINPTY_DLL);
1386 if (!hWinPtyDLL)
1387 {
1388 EMSG2(_(e_loadlib), WINPTY_DLL);
1389 return 0;
1390 }
1391 for (i = 0; winpty_entry[i].name != NULL
1392 && winpty_entry[i].ptr != NULL; ++i)
1393 {
1394 if ((*winpty_entry[i].ptr = (FARPROC)GetProcAddress(hWinPtyDLL,
1395 winpty_entry[i].name)) == NULL)
1396 {
1397 EMSG2(_(e_loadfunc), winpty_entry[i].name);
1398 return 0;
1399 }
1400 }
1401
1402 return 1;
1403}
1404
1405/*
1406 * Create a new terminal of "rows" by "cols" cells.
1407 * Store a reference in "term".
1408 * Return OK or FAIL.
1409 */
1410 static int
1411term_and_job_init(term_T *term, int rows, int cols, char_u *cmd)
1412{
Bram Moolenaarab6eec32017-07-27 21:46:43 +02001413 WCHAR *p;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001414 channel_T *channel = NULL;
1415 job_T *job = NULL;
1416 jobopt_T opt;
1417 DWORD error;
1418 HANDLE jo = NULL, child_process_handle, child_thread_handle;
1419 void *winpty_err;
Bram Moolenaarab6eec32017-07-27 21:46:43 +02001420 void *spawn_config = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001421
1422 if (!dyn_winpty_init())
1423 return FAIL;
1424
Bram Moolenaarab6eec32017-07-27 21:46:43 +02001425 p = enc_to_utf16(cmd, NULL);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001426 if (p == NULL)
1427 return FAIL;
1428
1429 job = job_alloc();
1430 if (job == NULL)
1431 goto failed;
1432
1433 channel = add_channel();
1434 if (channel == NULL)
1435 goto failed;
1436
1437 term->tl_winpty_config = winpty_config_new(0, &winpty_err);
1438 if (term->tl_winpty_config == NULL)
1439 goto failed;
1440
1441 winpty_config_set_initial_size(term->tl_winpty_config, cols, rows);
1442 term->tl_winpty = winpty_open(term->tl_winpty_config, &winpty_err);
1443 if (term->tl_winpty == NULL)
1444 goto failed;
1445
1446 spawn_config = winpty_spawn_config_new(
1447 WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN |
1448 WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN,
1449 NULL,
1450 p,
1451 NULL,
1452 NULL,
1453 &winpty_err);
1454 if (spawn_config == NULL)
1455 goto failed;
1456
1457 channel = add_channel();
1458 if (channel == NULL)
1459 goto failed;
1460
1461 job = job_alloc();
1462 if (job == NULL)
1463 goto failed;
1464
1465 if (!winpty_spawn(term->tl_winpty, spawn_config, &child_process_handle,
1466 &child_thread_handle, &error, &winpty_err))
1467 goto failed;
1468
1469 channel_set_pipes(channel,
1470 (sock_T) CreateFileW(
1471 winpty_conin_name(term->tl_winpty),
1472 GENERIC_WRITE, 0, NULL,
1473 OPEN_EXISTING, 0, NULL),
1474 (sock_T) CreateFileW(
1475 winpty_conout_name(term->tl_winpty),
1476 GENERIC_READ, 0, NULL,
1477 OPEN_EXISTING, 0, NULL),
1478 (sock_T) CreateFileW(
1479 winpty_conerr_name(term->tl_winpty),
1480 GENERIC_READ, 0, NULL,
1481 OPEN_EXISTING, 0, NULL));
1482
1483 jo = CreateJobObject(NULL, NULL);
1484 if (jo == NULL)
1485 goto failed;
1486
1487 if (!AssignProcessToJobObject(jo, child_process_handle))
Bram Moolenaarab6eec32017-07-27 21:46:43 +02001488 {
1489 /* Failed, switch the way to terminate process with TerminateProcess. */
1490 CloseHandle(jo);
1491 jo = NULL;
1492 }
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001493
1494 winpty_spawn_config_free(spawn_config);
Bram Moolenaarab6eec32017-07-27 21:46:43 +02001495 vim_free(p);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001496
1497 create_vterm(term, rows, cols);
1498
1499 setup_job_options(&opt, rows, cols);
1500 channel_set_job(channel, job, &opt);
1501
1502 job->jv_channel = channel;
1503 job->jv_proc_info.hProcess = child_process_handle;
1504 job->jv_proc_info.dwProcessId = GetProcessId(child_process_handle);
1505 job->jv_job_object = jo;
1506 job->jv_status = JOB_STARTED;
Bram Moolenaar0e83f022017-07-27 22:07:35 +02001507 ++job->jv_refcount;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001508 term->tl_job = job;
1509
1510 return OK;
1511
1512failed:
Bram Moolenaarab6eec32017-07-27 21:46:43 +02001513 if (spawn_config != NULL)
1514 winpty_spawn_config_free(spawn_config);
1515 vim_free(p);
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001516 if (channel != NULL)
1517 channel_clear(channel);
1518 if (job != NULL)
Bram Moolenaarcdeae992017-07-23 17:22:35 +02001519 {
1520 job->jv_channel = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001521 job_cleanup(job);
Bram Moolenaarcdeae992017-07-23 17:22:35 +02001522 }
1523 term->tl_job = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001524 if (jo != NULL)
1525 CloseHandle(jo);
1526 if (term->tl_winpty != NULL)
1527 winpty_free(term->tl_winpty);
Bram Moolenaarcdeae992017-07-23 17:22:35 +02001528 term->tl_winpty = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001529 if (term->tl_winpty_config != NULL)
1530 winpty_config_free(term->tl_winpty_config);
Bram Moolenaarcdeae992017-07-23 17:22:35 +02001531 term->tl_winpty_config = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001532 if (winpty_err != NULL)
1533 {
1534 char_u *msg = utf16_to_enc(
1535 (short_u *)winpty_error_msg(winpty_err), NULL);
1536
1537 EMSG(msg);
1538 winpty_error_free(winpty_err);
1539 }
1540 return FAIL;
1541}
1542
1543/*
1544 * Free the terminal emulator part of "term".
1545 */
1546 static void
Bram Moolenaard85f2712017-07-28 21:51:57 +02001547term_free_vterm(term_T *term)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001548{
Bram Moolenaarcdeae992017-07-23 17:22:35 +02001549 if (term->tl_winpty != NULL)
1550 winpty_free(term->tl_winpty);
Bram Moolenaard85f2712017-07-28 21:51:57 +02001551 term->tl_winpty = NULL;
Bram Moolenaarcdeae992017-07-23 17:22:35 +02001552 if (term->tl_winpty_config != NULL)
1553 winpty_config_free(term->tl_winpty_config);
Bram Moolenaard85f2712017-07-28 21:51:57 +02001554 term->tl_winpty_config = NULL;
Bram Moolenaarcdeae992017-07-23 17:22:35 +02001555 if (term->tl_vterm != NULL)
1556 vterm_free(term->tl_vterm);
Bram Moolenaard85f2712017-07-28 21:51:57 +02001557 term->tl_vterm = NULL
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001558}
1559
Bram Moolenaar43da3e32017-07-23 17:27:54 +02001560/*
1561 * Request size to terminal.
1562 */
1563 static void
1564term_report_winsize(term_T *term, int rows, int cols)
1565{
1566 winpty_set_size(term->tl_winpty, cols, rows, NULL);
1567}
1568
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001569# else
1570
1571/**************************************
1572 * 3. Unix-like implementation.
1573 */
1574
1575/*
1576 * Create a new terminal of "rows" by "cols" cells.
1577 * Start job for "cmd".
1578 * Store the pointers in "term".
1579 * Return OK or FAIL.
1580 */
1581 static int
1582term_and_job_init(term_T *term, int rows, int cols, char_u *cmd)
1583{
1584 typval_T argvars[2];
1585 jobopt_T opt;
1586
1587 create_vterm(term, rows, cols);
1588
1589 argvars[0].v_type = VAR_STRING;
1590 argvars[0].vval.v_string = cmd;
1591 setup_job_options(&opt, rows, cols);
1592 term->tl_job = job_start(argvars, &opt);
Bram Moolenaar0e83f022017-07-27 22:07:35 +02001593 if (term->tl_job != NULL)
1594 ++term->tl_job->jv_refcount;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001595
Bram Moolenaar61a66052017-07-22 18:39:00 +02001596 return term->tl_job != NULL
1597 && term->tl_job->jv_channel != NULL
1598 && term->tl_job->jv_status != JOB_FAILED ? OK : FAIL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001599}
1600
1601/*
1602 * Free the terminal emulator part of "term".
1603 */
1604 static void
Bram Moolenaard85f2712017-07-28 21:51:57 +02001605term_free_vterm(term_T *term)
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001606{
Bram Moolenaarcdeae992017-07-23 17:22:35 +02001607 if (term->tl_vterm != NULL)
1608 vterm_free(term->tl_vterm);
Bram Moolenaard85f2712017-07-28 21:51:57 +02001609 term->tl_vterm = NULL;
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001610}
Bram Moolenaar43da3e32017-07-23 17:27:54 +02001611
1612/*
1613 * Request size to terminal.
1614 */
1615 static void
1616term_report_winsize(term_T *term, int rows, int cols)
1617{
1618 /* Use an ioctl() to report the new window size to the job. */
1619 if (term->tl_job != NULL && term->tl_job->jv_channel != NULL)
1620 {
1621 int fd = -1;
1622 int part;
1623
1624 for (part = PART_OUT; part < PART_COUNT; ++part)
1625 {
1626 fd = term->tl_job->jv_channel->ch_part[part].ch_fd;
1627 if (isatty(fd))
1628 break;
1629 }
1630 if (part < PART_COUNT && mch_report_winsize(fd, rows, cols) == OK)
1631 mch_stop_job(term->tl_job, (char_u *)"winch");
1632 }
1633}
1634
Bram Moolenaar8f84c3a2017-07-22 16:14:44 +02001635# endif
Bram Moolenaar8c0095c2017-07-18 22:53:21 +02001636
Bram Moolenaare4f25e42017-07-07 11:54:15 +02001637#endif /* FEAT_TERMINAL */