Mercurial > vim
comparison src/terminal.c @ 11719:13ecb3e64399 v8.0.0742
patch 8.0.0742: terminal feature does not work on MS-Windows
commit https://github.com/vim/vim/commit/8f84c3a8666cea04484ec93fa05386bf33f93f5a
Author: Bram Moolenaar <Bram@vim.org>
Date: Sat Jul 22 16:14:44 2017 +0200
patch 8.0.0742: terminal feature does not work on MS-Windows
Problem: Terminal feature does not work on MS-Windows.
Solution: Use libvterm and libwinpty on MS-Windows. (Yasuhiro Matsumoto)
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Sat, 22 Jul 2017 16:15:04 +0200 |
parents | d7430b56c9ed |
children | 1922710ee8fa |
comparison
equal
deleted
inserted
replaced
11718:74f4fb97b581 | 11719:13ecb3e64399 |
---|---|
13 * There are three parts: | 13 * There are three parts: |
14 * 1. Generic code for all systems. | 14 * 1. Generic code for all systems. |
15 * 2. The MS-Windows implementation. | 15 * 2. The MS-Windows implementation. |
16 * Uses a hidden console for the terminal emulator. | 16 * Uses a hidden console for the terminal emulator. |
17 * 3. The Unix-like implementation. | 17 * 3. The Unix-like implementation. |
18 * Uses libvterm for the terminal emulator. | 18 * Uses libvterm for the terminal emulator directly. |
19 * | |
20 * For each terminal one VTerm is constructed. This uses libvterm. A copy of | |
21 * that library is in the libvterm directory. | |
19 * | 22 * |
20 * When a terminal window is opened, a job is started that will be connected to | 23 * When a terminal window is opened, a job is started that will be connected to |
21 * the terminal emulator. | 24 * the terminal emulator. |
22 * | 25 * |
23 * If the terminal window has keyboard focus, typed keys are converted to the | 26 * If the terminal window has keyboard focus, typed keys are converted to the |
27 * terminal emulator invokes callbacks when its screen content changes. The | 30 * terminal emulator invokes callbacks when its screen content changes. The |
28 * line range is stored in tl_dirty_row_start and tl_dirty_row_end. Once in a | 31 * line range is stored in tl_dirty_row_start and tl_dirty_row_end. Once in a |
29 * while, if the terminal window is visible, the screen contents is drawn. | 32 * while, if the terminal window is visible, the screen contents is drawn. |
30 * | 33 * |
31 * TODO: | 34 * TODO: |
35 * - When 'termsize' is set and dragging the separator the terminal gets messed | |
36 * up. | |
37 * - Use a pty for I/O with the job. | |
32 * - set buffer options to be scratch, hidden, nomodifiable, etc. | 38 * - set buffer options to be scratch, hidden, nomodifiable, etc. |
33 * - set buffer name to command, add (1) to avoid duplicates. | 39 * - set buffer name to command, add (1) to avoid duplicates. |
34 * - If [command] is not given the 'shell' option is used. | 40 * - If [command] is not given the 'shell' option is used. |
35 * - Add a scrollback buffer (contains lines to scroll off the top). | 41 * - Add a scrollback buffer (contains lines to scroll off the top). |
36 * Can use the buf_T lines, store attributes somewhere else? | 42 * Can use the buf_T lines, store attributes somewhere else? |
39 * - Put the terminal contents in the scrollback buffer. | 45 * - Put the terminal contents in the scrollback buffer. |
40 * - Free the terminal emulator. | 46 * - Free the terminal emulator. |
41 * - Display the scrollback buffer (but with attributes). | 47 * - Display the scrollback buffer (but with attributes). |
42 * Make the buffer not modifiable, drop attributes when making changes. | 48 * Make the buffer not modifiable, drop attributes when making changes. |
43 * - when closing window and job has not ended, make terminal hidden? | 49 * - when closing window and job has not ended, make terminal hidden? |
44 * - Use a pty for I/O with the job. | |
45 * - Windows implementation: | |
46 * (WiP): https://github.com/mattn/vim/tree/terminal | |
47 * src/os_win32.c mch_open_terminal() | |
48 * Using winpty ? | |
49 * - use win_del_lines() to make scroll-up efficient. | 50 * - use win_del_lines() to make scroll-up efficient. |
50 * - command line completion for :terminal | 51 * - command line completion for :terminal |
51 * - support fixed size when 'termsize' is "rowsXcols". | 52 * - add test for giving error for invalid 'termsize' value. |
52 * - support minimal size when 'termsize' is "rows*cols". | 53 * - support minimal size when 'termsize' is "rows*cols". |
53 * - support minimal size when 'termsize' is empty. | 54 * - support minimal size when 'termsize' is empty? |
54 * - implement ":buf {term-buf-name}" | 55 * - implement ":buf {term-buf-name}" |
55 * - implement term_list() list of buffers with a terminal | 56 * - implement term_list() list of buffers with a terminal |
56 * - implement term_getsize(buf) | 57 * - implement term_getsize(buf) |
57 * - implement term_setsize(buf) | 58 * - implement term_setsize(buf) |
58 * - implement term_sendkeys(buf, keys) send keystrokes to a terminal | 59 * - implement term_sendkeys(buf, keys) send keystrokes to a terminal |
59 * - implement term_wait(buf) wait for screen to be updated | 60 * - implement term_wait(buf) wait for screen to be updated |
60 * - implement term_scrape(buf, row) inspect terminal screen | 61 * - implement term_scrape(buf, row) inspect terminal screen |
61 * - implement term_open(command, options) open terminal window | 62 * - implement term_open(command, options) open terminal window |
62 * - implement term_getjob(buf) | 63 * - implement term_getjob(buf) |
63 * - implement 'termkey' | 64 * - implement 'termkey' |
65 * - when 'encoding' is not utf-8, or the job is using another encoding, setup | |
66 * conversions. | |
64 */ | 67 */ |
65 | 68 |
66 #include "vim.h" | 69 #include "vim.h" |
67 | 70 |
68 #ifdef FEAT_TERMINAL | 71 #ifdef FEAT_TERMINAL |
69 | 72 |
70 #ifdef WIN3264 | 73 #ifdef WIN3264 |
71 /* MS-Windows: use a native console. */ | 74 # define MIN(x,y) (x < y ? x : y) |
72 #else | 75 # define MAX(x,y) (x > y ? x : y) |
73 /* Unix-like: use libvterm. */ | |
74 # include "libvterm/include/vterm.h" | |
75 #endif | 76 #endif |
77 | |
78 #include "libvterm/include/vterm.h" | |
76 | 79 |
77 /* typedef term_T in structs.h */ | 80 /* typedef term_T in structs.h */ |
78 struct terminal_S { | 81 struct terminal_S { |
79 term_T *tl_next; | 82 term_T *tl_next; |
80 | 83 |
81 #ifdef WIN3264 | 84 #ifdef WIN3264 |
82 /* console handle? */ | 85 void *tl_winpty_config; |
83 #else | 86 void *tl_winpty; |
87 #endif | |
84 VTerm *tl_vterm; | 88 VTerm *tl_vterm; |
85 #endif | |
86 job_T *tl_job; | 89 job_T *tl_job; |
87 buf_T *tl_buffer; | 90 buf_T *tl_buffer; |
91 | |
92 /* last known vterm size */ | |
93 int tl_rows; | |
94 int tl_cols; | |
95 /* vterm size does not follow window size */ | |
96 int tl_rows_fixed; | |
97 int tl_cols_fixed; | |
88 | 98 |
89 /* Range of screen rows to update. Zero based. */ | 99 /* Range of screen rows to update. Zero based. */ |
90 int tl_dirty_row_start; /* -1 if nothing dirty */ | 100 int tl_dirty_row_start; /* -1 if nothing dirty */ |
91 int tl_dirty_row_end; /* row below last one to update */ | 101 int tl_dirty_row_end; /* row below last one to update */ |
92 | 102 |
93 pos_T tl_cursor; | 103 pos_T tl_cursor; |
94 }; | 104 }; |
95 | 105 |
106 /* | |
107 * List of all active terminals. | |
108 */ | |
109 static term_T *first_term = NULL; | |
110 | |
111 | |
96 #define MAX_ROW 999999 /* used for tl_dirty_row_end to update all rows */ | 112 #define MAX_ROW 999999 /* used for tl_dirty_row_end to update all rows */ |
97 #define KEY_BUF_LEN 200 | 113 #define KEY_BUF_LEN 200 |
98 | 114 |
99 /* Functions implemented for MS-Windows and Unix-like systems. */ | 115 /* |
100 static int term_init(term_T *term, int rows, int cols); | 116 * Functions with separate implementation for MS-Windows and Unix-like systems. |
117 */ | |
118 static int term_and_job_init(term_T *term, int rows, int cols, char_u *cmd); | |
101 static void term_free(term_T *term); | 119 static void term_free(term_T *term); |
102 static void term_write_job_output(term_T *term, char_u *msg, size_t len); | |
103 static int term_convert_key(int c, char *buf); | |
104 static void term_update_lines(win_T *wp); | |
105 | |
106 /* | |
107 * List of all active terminals. | |
108 */ | |
109 static term_T *first_term = NULL; | |
110 | 120 |
111 /************************************** | 121 /************************************** |
112 * 1. Generic code for all systems. | 122 * 1. Generic code for all systems. |
113 */ | 123 */ |
114 | 124 |
115 /* | 125 /* |
126 * Determine the terminal size from 'termsize' and the current window. | |
127 * Assumes term->tl_rows and term->tl_cols are zero. | |
128 */ | |
129 static void | |
130 set_term_and_win_size(term_T *term) | |
131 { | |
132 if (*curwin->w_p_tms != NUL) | |
133 { | |
134 char_u *p = vim_strchr(curwin->w_p_tms, 'x') + 1; | |
135 | |
136 term->tl_rows = atoi((char *)curwin->w_p_tms); | |
137 term->tl_cols = atoi((char *)p); | |
138 } | |
139 if (term->tl_rows == 0) | |
140 term->tl_rows = curwin->w_height; | |
141 else | |
142 { | |
143 win_setheight_win(term->tl_rows, curwin); | |
144 term->tl_rows_fixed = TRUE; | |
145 } | |
146 if (term->tl_cols == 0) | |
147 term->tl_cols = curwin->w_width; | |
148 else | |
149 { | |
150 win_setwidth_win(term->tl_cols, curwin); | |
151 term->tl_cols_fixed = TRUE; | |
152 } | |
153 } | |
154 | |
155 /* | |
116 * ":terminal": open a terminal window and execute a job in it. | 156 * ":terminal": open a terminal window and execute a job in it. |
117 */ | 157 */ |
118 void | 158 void |
119 ex_terminal(exarg_T *eap) | 159 ex_terminal(exarg_T *eap) |
120 { | 160 { |
121 int rows; | |
122 int cols; | |
123 exarg_T split_ea; | 161 exarg_T split_ea; |
124 win_T *old_curwin = curwin; | 162 win_T *old_curwin = curwin; |
125 typval_T argvars[2]; | |
126 term_T *term; | 163 term_T *term; |
127 jobopt_T opt; | |
128 | 164 |
129 if (check_restricted() || check_secure()) | 165 if (check_restricted() || check_secure()) |
130 return; | 166 return; |
131 | 167 |
132 term = (term_T *)alloc_clear(sizeof(term_T)); | 168 term = (term_T *)alloc_clear(sizeof(term_T)); |
145 /* split failed */ | 181 /* split failed */ |
146 vim_free(term); | 182 vim_free(term); |
147 return; | 183 return; |
148 } | 184 } |
149 term->tl_buffer = curbuf; | 185 term->tl_buffer = curbuf; |
150 | |
151 curbuf->b_term = term; | 186 curbuf->b_term = term; |
187 | |
188 /* Link the new terminal in the list of active terminals. */ | |
152 term->tl_next = first_term; | 189 term->tl_next = first_term; |
153 first_term = term; | 190 first_term = term; |
154 | 191 |
155 /* TODO: set buffer type, hidden, etc. */ | 192 /* TODO: set buffer type, hidden, etc. */ |
156 | 193 |
157 if (*curwin->w_p_tms != NUL) | 194 set_term_and_win_size(term); |
158 { | 195 |
159 char_u *p = vim_strchr(curwin->w_p_tms, 'x') + 1; | 196 /* System dependent: setup the vterm and start the job in it. */ |
160 | 197 if (term_and_job_init(term, term->tl_rows, term->tl_cols, eap->arg) == OK) |
161 rows = atoi((char *)curwin->w_p_tms); | 198 { |
162 cols = atoi((char *)p); | 199 /* store the size we ended up with */ |
163 /* TODO: resize window if possible. */ | 200 vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols); |
164 } | 201 } |
165 else | 202 else |
166 { | 203 { |
167 rows = curwin->w_height; | 204 /* Wiping out the buffer will also close the window and call |
168 cols = curwin->w_width; | 205 * free_terminal(). */ |
169 } | |
170 | |
171 if (term_init(term, rows, cols) == OK) | |
172 { | |
173 argvars[0].v_type = VAR_STRING; | |
174 argvars[0].vval.v_string = eap->arg; | |
175 | |
176 clear_job_options(&opt); | |
177 opt.jo_mode = MODE_RAW; | |
178 opt.jo_out_mode = MODE_RAW; | |
179 opt.jo_err_mode = MODE_RAW; | |
180 opt.jo_set = JO_MODE | JO_OUT_MODE | JO_ERR_MODE; | |
181 opt.jo_io[PART_OUT] = JIO_BUFFER; | |
182 opt.jo_io[PART_ERR] = JIO_BUFFER; | |
183 opt.jo_set |= JO_OUT_IO + (JO_OUT_IO << (PART_ERR - PART_OUT)); | |
184 opt.jo_io_buf[PART_OUT] = curbuf->b_fnum; | |
185 opt.jo_io_buf[PART_ERR] = curbuf->b_fnum; | |
186 opt.jo_set |= JO_OUT_BUF + (JO_OUT_BUF << (PART_ERR - PART_OUT)); | |
187 opt.jo_term_rows = rows; | |
188 opt.jo_term_cols = cols; | |
189 | |
190 term->tl_job = job_start(argvars, &opt); | |
191 } | |
192 | |
193 if (term->tl_job == NULL) | |
194 /* Wiping out the buffer will also close the window. */ | |
195 do_buffer(DOBUF_WIPE, DOBUF_CURRENT, FORWARD, 0, TRUE); | 206 do_buffer(DOBUF_WIPE, DOBUF_CURRENT, FORWARD, 0, TRUE); |
207 } | |
196 | 208 |
197 /* TODO: Setup pty, see mch_call_shell(). */ | 209 /* TODO: Setup pty, see mch_call_shell(). */ |
198 } | 210 } |
199 | 211 |
200 /* | 212 /* |
229 term_free(term); | 241 term_free(term); |
230 vim_free(term); | 242 vim_free(term); |
231 } | 243 } |
232 | 244 |
233 /* | 245 /* |
234 * Invoked when "msg" output from a job was received. Write it to the terminal | 246 * Write job output "msg[len]" to the vterm. |
235 * of "buffer". | |
236 */ | |
237 void | |
238 write_to_term(buf_T *buffer, char_u *msg, channel_T *channel) | |
239 { | |
240 size_t len = STRLEN(msg); | |
241 term_T *term = buffer->b_term; | |
242 | |
243 ch_logn(channel, "writing %d bytes to terminal", (int)len); | |
244 term_write_job_output(term, msg, len); | |
245 | |
246 /* TODO: only update once in a while. */ | |
247 update_screen(0); | |
248 setcursor(); | |
249 out_flush(); | |
250 } | |
251 | |
252 /* | |
253 * Wait for input and send it to the job. | |
254 * Return when the start of a CTRL-W command is typed or anything else that | |
255 * should be handled as a Normal mode command. | |
256 */ | |
257 void | |
258 terminal_loop(void) | |
259 { | |
260 char buf[KEY_BUF_LEN]; | |
261 int c; | |
262 size_t len; | |
263 static int mouse_was_outside = FALSE; | |
264 int dragging_outside = FALSE; | |
265 | |
266 for (;;) | |
267 { | |
268 /* TODO: skip screen update when handling a sequence of keys. */ | |
269 update_screen(0); | |
270 setcursor(); | |
271 out_flush(); | |
272 ++no_mapping; | |
273 ++allow_keys; | |
274 c = vgetc(); | |
275 --no_mapping; | |
276 --allow_keys; | |
277 | |
278 /* Catch keys that need to be handled as in Normal mode. */ | |
279 switch (c) | |
280 { | |
281 case Ctrl_W: | |
282 case NUL: | |
283 case K_ZERO: | |
284 stuffcharReadbuff(c); | |
285 return; | |
286 | |
287 case K_IGNORE: continue; | |
288 | |
289 case K_LEFTDRAG: | |
290 case K_MIDDLEDRAG: | |
291 case K_RIGHTDRAG: | |
292 case K_X1DRAG: | |
293 case K_X2DRAG: | |
294 dragging_outside = mouse_was_outside; | |
295 /* FALLTHROUGH */ | |
296 case K_LEFTMOUSE: | |
297 case K_LEFTMOUSE_NM: | |
298 case K_LEFTRELEASE: | |
299 case K_LEFTRELEASE_NM: | |
300 case K_MIDDLEMOUSE: | |
301 case K_MIDDLERELEASE: | |
302 case K_RIGHTMOUSE: | |
303 case K_RIGHTRELEASE: | |
304 case K_X1MOUSE: | |
305 case K_X1RELEASE: | |
306 case K_X2MOUSE: | |
307 case K_X2RELEASE: | |
308 if (mouse_row < W_WINROW(curwin) | |
309 || mouse_row >= (W_WINROW(curwin) + curwin->w_height) | |
310 || mouse_col < W_WINCOL(curwin) | |
311 || mouse_col >= W_ENDCOL(curwin) | |
312 || dragging_outside) | |
313 { | |
314 /* click outside the current window */ | |
315 stuffcharReadbuff(c); | |
316 mouse_was_outside = TRUE; | |
317 return; | |
318 } | |
319 } | |
320 mouse_was_outside = FALSE; | |
321 | |
322 /* Convert the typed key to a sequence of bytes for the job. */ | |
323 len = term_convert_key(c, buf); | |
324 if (len > 0) | |
325 /* TODO: if FAIL is returned, stop? */ | |
326 channel_send(curbuf->b_term->tl_job->jv_channel, PART_IN, | |
327 (char_u *)buf, len, NULL); | |
328 } | |
329 } | |
330 | |
331 /* | |
332 * Called to update the window that contains the terminal. | |
333 */ | |
334 void | |
335 term_update_window(win_T *wp) | |
336 { | |
337 term_update_lines(wp); | |
338 } | |
339 | |
340 static void | |
341 position_cursor(win_T *wp, VTermPos *pos) | |
342 { | |
343 wp->w_wrow = MIN(pos->row, MAX(0, wp->w_height - 1)); | |
344 wp->w_wcol = MIN(pos->col, MAX(0, wp->w_width - 1)); | |
345 } | |
346 | |
347 #ifdef WIN3264 | |
348 | |
349 /************************************** | |
350 * 2. MS-Windows implementation. | |
351 */ | |
352 | |
353 /* | |
354 * Create a new terminal of "rows" by "cols" cells. | |
355 * Store a reference in "term". | |
356 * Return OK or FAIL. | |
357 */ | |
358 static int | |
359 term_init(term_T *term, int rows, int cols) | |
360 { | |
361 /* TODO: Create a hidden console */ | |
362 return FAIL; | |
363 } | |
364 | |
365 /* | |
366 * Free the terminal emulator part of "term". | |
367 */ | |
368 static void | |
369 term_free(term_T *term) | |
370 { | |
371 /* TODO */ | |
372 } | |
373 | |
374 /* | |
375 * Write job output "msg[len]" to the terminal. | |
376 */ | |
377 static void | |
378 term_write_job_output(term_T *term, char_u *msg, size_t len) | |
379 { | |
380 /* TODO */ | |
381 } | |
382 | |
383 /* | |
384 * Convert typed key "c" into bytes to send to the job. | |
385 * Return the number of bytes in "buf". | |
386 */ | |
387 static int | |
388 term_convert_key(int c, char *buf) | |
389 { | |
390 /* TODO */ | |
391 return 0; | |
392 } | |
393 | |
394 /* | |
395 * Called to update the window that contains the terminal. | |
396 */ | |
397 static void | |
398 term_update_lines(win_T *wp) | |
399 { | |
400 /* TODO */ | |
401 } | |
402 | |
403 #else | |
404 | |
405 /************************************** | |
406 * 3. Unix-like implementation. | |
407 * | |
408 * For a terminal one VTerm is constructed. This uses libvterm. A copy of | |
409 * that library is in the libvterm directory. | |
410 */ | |
411 | |
412 static int handle_damage(VTermRect rect, void *user); | |
413 static int handle_moverect(VTermRect dest, VTermRect src, void *user); | |
414 static int handle_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user); | |
415 static int handle_resize(int rows, int cols, void *user); | |
416 | |
417 static VTermScreenCallbacks screen_callbacks = { | |
418 handle_damage, /* damage */ | |
419 handle_moverect, /* moverect */ | |
420 handle_movecursor, /* movecursor */ | |
421 NULL, /* settermprop */ | |
422 NULL, /* bell */ | |
423 handle_resize, /* resize */ | |
424 NULL, /* sb_pushline */ | |
425 NULL /* sb_popline */ | |
426 }; | |
427 | |
428 /* | |
429 * Create a new terminal of "rows" by "cols" cells. | |
430 * Store a reference in "term". | |
431 * Return OK or FAIL. | |
432 */ | |
433 static int | |
434 term_init(term_T *term, int rows, int cols) | |
435 { | |
436 VTerm *vterm = vterm_new(rows, cols); | |
437 VTermScreen *screen; | |
438 | |
439 term->tl_vterm = vterm; | |
440 screen = vterm_obtain_screen(vterm); | |
441 vterm_screen_set_callbacks(screen, &screen_callbacks, term); | |
442 /* TODO: depends on 'encoding'. */ | |
443 vterm_set_utf8(vterm, 1); | |
444 /* Required to initialize most things. */ | |
445 vterm_screen_reset(screen, 1 /* hard */); | |
446 | |
447 return OK; | |
448 } | |
449 | |
450 /* | |
451 * Free the terminal emulator part of "term". | |
452 */ | |
453 static void | |
454 term_free(term_T *term) | |
455 { | |
456 vterm_free(term->tl_vterm); | |
457 } | |
458 | |
459 /* | |
460 * Write job output "msg[len]" to the terminal. | |
461 */ | 247 */ |
462 static void | 248 static void |
463 term_write_job_output(term_T *term, char_u *msg, size_t len) | 249 term_write_job_output(term_T *term, char_u *msg, size_t len) |
464 { | 250 { |
465 VTerm *vterm = term->tl_vterm; | 251 VTerm *vterm = term->tl_vterm; |
471 { | 257 { |
472 for (p = msg + done; p < msg + len; ) | 258 for (p = msg + done; p < msg + len; ) |
473 { | 259 { |
474 if (*p == NL) | 260 if (*p == NL) |
475 break; | 261 break; |
476 p += mb_ptr2len_len(p, len - (p - msg)); | 262 p += utf_ptr2len_len(p, len - (p - msg)); |
477 } | 263 } |
478 len_now = p - msg - done; | 264 len_now = p - msg - done; |
479 vterm_input_write(vterm, (char *)msg + done, len_now); | 265 vterm_input_write(vterm, (char *)msg + done, len_now); |
480 if (p < msg + len && *p == NL) | 266 if (p < msg + len && *p == NL) |
481 { | 267 { |
482 /* Convert NL to CR-NL, that appears to work best. */ | 268 /* Convert NL to CR-NL, that appears to work best. */ |
483 vterm_input_write(vterm, "\r\n", 2); | 269 vterm_input_write(vterm, "\r\n", 2); |
484 ++len_now; | 270 ++len_now; |
485 } | 271 } |
486 } | 272 } |
273 | |
274 /* this invokes the damage callbacks */ | |
487 vterm_screen_flush_damage(vterm_obtain_screen(vterm)); | 275 vterm_screen_flush_damage(vterm_obtain_screen(vterm)); |
488 } | 276 } |
489 | 277 |
490 static int | 278 /* |
491 handle_damage(VTermRect rect, void *user) | 279 * Invoked when "msg" output from a job was received. Write it to the terminal |
492 { | 280 * of "buffer". |
493 term_T *term = (term_T *)user; | 281 */ |
494 | 282 void |
495 term->tl_dirty_row_start = MIN(term->tl_dirty_row_start, rect.start_row); | 283 write_to_term(buf_T *buffer, char_u *msg, channel_T *channel) |
496 term->tl_dirty_row_end = MAX(term->tl_dirty_row_end, rect.end_row); | 284 { |
497 redraw_buf_later(term->tl_buffer, NOT_VALID); | 285 size_t len = STRLEN(msg); |
498 return 1; | 286 term_T *term = buffer->b_term; |
499 } | 287 |
500 | 288 ch_logn(channel, "writing %d bytes to terminal", (int)len); |
501 static int | 289 term_write_job_output(term, msg, len); |
502 handle_moverect(VTermRect dest UNUSED, VTermRect src UNUSED, void *user) | 290 |
503 { | 291 /* TODO: only update once in a while. */ |
504 term_T *term = (term_T *)user; | 292 update_screen(0); |
505 | 293 setcursor(); |
506 /* TODO */ | 294 out_flush(); |
507 redraw_buf_later(term->tl_buffer, NOT_VALID); | |
508 return 1; | |
509 } | |
510 | |
511 static int | |
512 handle_movecursor( | |
513 VTermPos pos, | |
514 VTermPos oldpos UNUSED, | |
515 int visible UNUSED, | |
516 void *user) | |
517 { | |
518 term_T *term = (term_T *)user; | |
519 win_T *wp; | |
520 int is_current = FALSE; | |
521 | |
522 FOR_ALL_WINDOWS(wp) | |
523 { | |
524 if (wp->w_buffer == term->tl_buffer) | |
525 { | |
526 position_cursor(wp, &pos); | |
527 if (wp == curwin) | |
528 is_current = TRUE; | |
529 } | |
530 } | |
531 | |
532 if (is_current) | |
533 { | |
534 setcursor(); | |
535 out_flush(); | |
536 } | |
537 | |
538 return 1; | |
539 } | |
540 | |
541 static int | |
542 handle_resize(int rows, int cols, void *user) | |
543 { | |
544 term_T *term = (term_T *)user; | |
545 win_T *wp; | |
546 | |
547 FOR_ALL_WINDOWS(wp) | |
548 { | |
549 if (wp->w_buffer == term->tl_buffer) | |
550 { | |
551 win_setheight_win(rows, wp); | |
552 win_setwidth_win(cols, wp); | |
553 } | |
554 } | |
555 | |
556 redraw_buf_later(term->tl_buffer, NOT_VALID); | |
557 return 1; | |
558 } | 295 } |
559 | 296 |
560 /* | 297 /* |
561 * Convert typed key "c" into bytes to send to the job. | 298 * Convert typed key "c" into bytes to send to the job. |
562 * Return the number of bytes in "buf". | 299 * Return the number of bytes in "buf". |
662 /* Read back the converted escape sequence. */ | 399 /* Read back the converted escape sequence. */ |
663 return vterm_output_read(vterm, buf, KEY_BUF_LEN); | 400 return vterm_output_read(vterm, buf, KEY_BUF_LEN); |
664 } | 401 } |
665 | 402 |
666 /* | 403 /* |
404 * Wait for input and send it to the job. | |
405 * Return when the start of a CTRL-W command is typed or anything else that | |
406 * should be handled as a Normal mode command. | |
407 */ | |
408 void | |
409 terminal_loop(void) | |
410 { | |
411 char buf[KEY_BUF_LEN]; | |
412 int c; | |
413 size_t len; | |
414 static int mouse_was_outside = FALSE; | |
415 int dragging_outside = FALSE; | |
416 | |
417 for (;;) | |
418 { | |
419 /* TODO: skip screen update when handling a sequence of keys. */ | |
420 update_screen(0); | |
421 setcursor(); | |
422 out_flush(); | |
423 ++no_mapping; | |
424 ++allow_keys; | |
425 got_int = FALSE; | |
426 c = vgetc(); | |
427 --no_mapping; | |
428 --allow_keys; | |
429 | |
430 /* Catch keys that need to be handled as in Normal mode. */ | |
431 switch (c) | |
432 { | |
433 case Ctrl_W: | |
434 case NUL: | |
435 case K_ZERO: | |
436 stuffcharReadbuff(c); | |
437 return; | |
438 | |
439 case K_IGNORE: continue; | |
440 | |
441 case K_LEFTDRAG: | |
442 case K_MIDDLEDRAG: | |
443 case K_RIGHTDRAG: | |
444 case K_X1DRAG: | |
445 case K_X2DRAG: | |
446 dragging_outside = mouse_was_outside; | |
447 /* FALLTHROUGH */ | |
448 case K_LEFTMOUSE: | |
449 case K_LEFTMOUSE_NM: | |
450 case K_LEFTRELEASE: | |
451 case K_LEFTRELEASE_NM: | |
452 case K_MIDDLEMOUSE: | |
453 case K_MIDDLERELEASE: | |
454 case K_RIGHTMOUSE: | |
455 case K_RIGHTRELEASE: | |
456 case K_X1MOUSE: | |
457 case K_X1RELEASE: | |
458 case K_X2MOUSE: | |
459 case K_X2RELEASE: | |
460 if (mouse_row < W_WINROW(curwin) | |
461 || mouse_row >= (W_WINROW(curwin) + curwin->w_height) | |
462 || mouse_col < W_WINCOL(curwin) | |
463 || mouse_col >= W_ENDCOL(curwin) | |
464 || dragging_outside) | |
465 { | |
466 /* click outside the current window */ | |
467 stuffcharReadbuff(c); | |
468 mouse_was_outside = TRUE; | |
469 return; | |
470 } | |
471 } | |
472 mouse_was_outside = FALSE; | |
473 | |
474 /* Convert the typed key to a sequence of bytes for the job. */ | |
475 len = term_convert_key(c, buf); | |
476 if (len > 0) | |
477 /* TODO: if FAIL is returned, stop? */ | |
478 channel_send(curbuf->b_term->tl_job->jv_channel, PART_IN, | |
479 (char_u *)buf, len, NULL); | |
480 } | |
481 } | |
482 | |
483 static void | |
484 position_cursor(win_T *wp, VTermPos *pos) | |
485 { | |
486 wp->w_wrow = MIN(pos->row, MAX(0, wp->w_height - 1)); | |
487 wp->w_wcol = MIN(pos->col, MAX(0, wp->w_width - 1)); | |
488 } | |
489 | |
490 static int handle_damage(VTermRect rect, void *user); | |
491 static int handle_moverect(VTermRect dest, VTermRect src, void *user); | |
492 static int handle_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user); | |
493 static int handle_resize(int rows, int cols, void *user); | |
494 | |
495 static VTermScreenCallbacks screen_callbacks = { | |
496 handle_damage, /* damage */ | |
497 handle_moverect, /* moverect */ | |
498 handle_movecursor, /* movecursor */ | |
499 NULL, /* settermprop */ | |
500 NULL, /* bell */ | |
501 handle_resize, /* resize */ | |
502 NULL, /* sb_pushline */ | |
503 NULL /* sb_popline */ | |
504 }; | |
505 | |
506 static int | |
507 handle_damage(VTermRect rect, void *user) | |
508 { | |
509 term_T *term = (term_T *)user; | |
510 | |
511 term->tl_dirty_row_start = MIN(term->tl_dirty_row_start, rect.start_row); | |
512 term->tl_dirty_row_end = MAX(term->tl_dirty_row_end, rect.end_row); | |
513 redraw_buf_later(term->tl_buffer, NOT_VALID); | |
514 return 1; | |
515 } | |
516 | |
517 static int | |
518 handle_moverect(VTermRect dest UNUSED, VTermRect src UNUSED, void *user) | |
519 { | |
520 term_T *term = (term_T *)user; | |
521 | |
522 /* TODO */ | |
523 redraw_buf_later(term->tl_buffer, NOT_VALID); | |
524 return 1; | |
525 } | |
526 | |
527 static int | |
528 handle_movecursor( | |
529 VTermPos pos, | |
530 VTermPos oldpos UNUSED, | |
531 int visible UNUSED, | |
532 void *user) | |
533 { | |
534 term_T *term = (term_T *)user; | |
535 win_T *wp; | |
536 int is_current = FALSE; | |
537 | |
538 FOR_ALL_WINDOWS(wp) | |
539 { | |
540 if (wp->w_buffer == term->tl_buffer) | |
541 { | |
542 position_cursor(wp, &pos); | |
543 if (wp == curwin) | |
544 is_current = TRUE; | |
545 } | |
546 } | |
547 | |
548 if (is_current) | |
549 { | |
550 setcursor(); | |
551 out_flush(); | |
552 } | |
553 | |
554 return 1; | |
555 } | |
556 | |
557 /* | |
558 * The job running in the terminal resized the terminal. | |
559 */ | |
560 static int | |
561 handle_resize(int rows, int cols, void *user) | |
562 { | |
563 term_T *term = (term_T *)user; | |
564 win_T *wp; | |
565 | |
566 term->tl_rows = rows; | |
567 term->tl_cols = cols; | |
568 FOR_ALL_WINDOWS(wp) | |
569 { | |
570 if (wp->w_buffer == term->tl_buffer) | |
571 { | |
572 win_setheight_win(rows, wp); | |
573 win_setwidth_win(cols, wp); | |
574 } | |
575 } | |
576 | |
577 redraw_buf_later(term->tl_buffer, NOT_VALID); | |
578 return 1; | |
579 } | |
580 | |
581 /* | |
667 * Called to update the window that contains the terminal. | 582 * Called to update the window that contains the terminal. |
668 */ | 583 */ |
669 static void | 584 void |
670 term_update_lines(win_T *wp) | 585 term_update_window(win_T *wp) |
671 { | 586 { |
672 int vterm_rows; | 587 term_T *term = wp->w_buffer->b_term; |
673 int vterm_cols; | 588 VTerm *vterm = term->tl_vterm; |
674 VTerm *vterm = wp->w_buffer->b_term->tl_vterm; | |
675 VTermScreen *screen = vterm_obtain_screen(vterm); | 589 VTermScreen *screen = vterm_obtain_screen(vterm); |
676 VTermState *state = vterm_obtain_state(vterm); | 590 VTermState *state = vterm_obtain_state(vterm); |
677 VTermPos pos; | 591 VTermPos pos; |
678 | 592 |
679 vterm_get_size(vterm, &vterm_rows, &vterm_cols); | 593 /* |
680 | 594 * If the window was resized a redraw will be triggered and we get here. |
681 if (*wp->w_p_tms == NUL | 595 * Adjust the size of the vterm unless 'termsize' specifies a fixed size. |
682 && (vterm_rows != wp->w_height || vterm_cols != wp->w_width)) | 596 */ |
683 { | 597 if ((!term->tl_rows_fixed && term->tl_rows != wp->w_height) |
684 vterm_set_size(vterm, wp->w_height, wp->w_width); | 598 || (!term->tl_cols_fixed && term->tl_cols != wp->w_width)) |
685 /* Get the size again, in case setting the didn't work. */ | 599 vterm_set_size(vterm, |
686 vterm_get_size(vterm, &vterm_rows, &vterm_cols); | 600 term->tl_rows_fixed ? term->tl_rows : wp->w_height, |
687 } | 601 term->tl_cols_fixed ? term->tl_cols : wp->w_width); |
688 | 602 |
689 /* The cursor may have been moved when resizing. */ | 603 /* The cursor may have been moved when resizing. */ |
690 vterm_state_get_cursorpos(state, &pos); | 604 vterm_state_get_cursorpos(state, &pos); |
691 position_cursor(wp, &pos); | 605 position_cursor(wp, &pos); |
692 | 606 |
693 /* TODO: Only redraw what changed. */ | 607 /* TODO: Only redraw what changed. */ |
694 for (pos.row = 0; pos.row < wp->w_height; ++pos.row) | 608 for (pos.row = 0; pos.row < wp->w_height; ++pos.row) |
695 { | 609 { |
696 int off = screen_get_current_line_off(); | 610 int off = screen_get_current_line_off(); |
697 | 611 int max_col = MIN(wp->w_width, term->tl_cols); |
698 if (pos.row < vterm_rows) | 612 |
699 for (pos.col = 0; pos.col < wp->w_width && pos.col < vterm_cols; | 613 if (pos.row < term->tl_rows) |
700 ++pos.col) | 614 { |
615 for (pos.col = 0; pos.col < max_col; ) | |
701 { | 616 { |
702 VTermScreenCell cell; | 617 VTermScreenCell cell; |
703 int c; | 618 int c; |
704 | 619 |
705 vterm_screen_get_cell(screen, pos, &cell); | 620 vterm_screen_get_cell(screen, pos, &cell); |
621 c = cell.chars[0]; | |
622 if (c == NUL) | |
623 { | |
624 ScreenLines[off] = ' '; | |
625 ScreenLinesUC[off] = NUL; | |
626 } | |
627 else | |
628 { | |
629 #if defined(FEAT_MBYTE) | |
630 if (enc_utf8 && c >= 0x80) | |
631 ScreenLinesUC[off] = c; | |
632 else | |
633 ScreenLines[off] = c; | |
634 #else | |
635 ScreenLines[off] = c; | |
636 #endif | |
637 } | |
706 /* TODO: use cell.attrs and colors */ | 638 /* TODO: use cell.attrs and colors */ |
707 /* TODO: use cell.width */ | |
708 /* TODO: multi-byte chars */ | |
709 c = cell.chars[0]; | |
710 ScreenLines[off] = c == NUL ? ' ' : c; | |
711 ScreenAttrs[off] = 0; | 639 ScreenAttrs[off] = 0; |
640 | |
641 ++pos.col; | |
712 ++off; | 642 ++off; |
643 if (cell.width == 2) | |
644 { | |
645 ScreenLines[off] = ' '; | |
646 ScreenLinesUC[off] = NUL; | |
647 ++pos.col; | |
648 ++off; | |
649 } | |
713 } | 650 } |
651 } | |
714 else | 652 else |
715 pos.col = 0; | 653 pos.col = 0; |
716 | 654 |
717 screen_line(wp->w_winrow + pos.row, wp->w_wincol, pos.col, wp->w_width, | 655 screen_line(wp->w_winrow + pos.row, wp->w_wincol, |
718 FALSE); | 656 pos.col, wp->w_width, FALSE); |
719 } | 657 } |
720 } | 658 } |
721 | 659 |
722 #endif | 660 /* |
661 * Set job options common for Unix and MS-Windows. | |
662 */ | |
663 static void | |
664 setup_job_options(jobopt_T *opt, int rows, int cols) | |
665 { | |
666 clear_job_options(opt); | |
667 opt->jo_mode = MODE_RAW; | |
668 opt->jo_out_mode = MODE_RAW; | |
669 opt->jo_err_mode = MODE_RAW; | |
670 opt->jo_set = JO_MODE | JO_OUT_MODE | JO_ERR_MODE; | |
671 opt->jo_io[PART_OUT] = JIO_BUFFER; | |
672 opt->jo_io[PART_ERR] = JIO_BUFFER; | |
673 opt->jo_set |= JO_OUT_IO + (JO_OUT_IO << (PART_ERR - PART_OUT)); | |
674 opt->jo_io_buf[PART_OUT] = curbuf->b_fnum; | |
675 opt->jo_io_buf[PART_ERR] = curbuf->b_fnum; | |
676 opt->jo_set |= JO_OUT_BUF + (JO_OUT_BUF << (PART_ERR - PART_OUT)); | |
677 opt->jo_term_rows = rows; | |
678 opt->jo_term_cols = cols; | |
679 } | |
680 | |
681 /* | |
682 * Create a new vterm and initialize it. | |
683 */ | |
684 static void | |
685 create_vterm(term_T *term, int rows, int cols) | |
686 { | |
687 VTerm *vterm; | |
688 VTermScreen *screen; | |
689 | |
690 vterm = vterm_new(rows, cols); | |
691 term->tl_vterm = vterm; | |
692 screen = vterm_obtain_screen(vterm); | |
693 vterm_screen_set_callbacks(screen, &screen_callbacks, term); | |
694 /* TODO: depends on 'encoding'. */ | |
695 vterm_set_utf8(vterm, 1); | |
696 /* Required to initialize most things. */ | |
697 vterm_screen_reset(screen, 1 /* hard */); | |
698 } | |
699 | |
700 # ifdef WIN3264 | |
701 | |
702 #define WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN 1ul | |
703 #define WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN 2ull | |
704 | |
705 void* (*winpty_config_new)(int, void*); | |
706 void* (*winpty_open)(void*, void*); | |
707 void* (*winpty_spawn_config_new)(int, void*, LPCWSTR, void*, void*, void*); | |
708 BOOL (*winpty_spawn)(void*, void*, HANDLE*, HANDLE*, DWORD*, void*); | |
709 void (*winpty_config_set_initial_size)(void*, int, int); | |
710 LPCWSTR (*winpty_conin_name)(void*); | |
711 LPCWSTR (*winpty_conout_name)(void*); | |
712 LPCWSTR (*winpty_conerr_name)(void*); | |
713 void (*winpty_free)(void*); | |
714 void (*winpty_config_free)(void*); | |
715 void (*winpty_spawn_config_free)(void*); | |
716 void (*winpty_error_free)(void*); | |
717 LPCWSTR (*winpty_error_msg)(void*); | |
718 | |
719 /************************************** | |
720 * 2. MS-Windows implementation. | |
721 */ | |
722 | |
723 #define WINPTY_DLL "winpty.dll" | |
724 | |
725 static HINSTANCE hWinPtyDLL = NULL; | |
726 | |
727 int | |
728 dyn_winpty_init(void) | |
729 { | |
730 int i; | |
731 static struct | |
732 { | |
733 char *name; | |
734 FARPROC *ptr; | |
735 } winpty_entry[] = | |
736 { | |
737 {"winpty_conerr_name", (FARPROC*)&winpty_conerr_name}, | |
738 {"winpty_config_free", (FARPROC*)&winpty_config_free}, | |
739 {"winpty_config_new", (FARPROC*)&winpty_config_new}, | |
740 {"winpty_config_set_initial_size", (FARPROC*)&winpty_config_set_initial_size}, | |
741 {"winpty_conin_name", (FARPROC*)&winpty_conin_name}, | |
742 {"winpty_conout_name", (FARPROC*)&winpty_conout_name}, | |
743 {"winpty_error_free", (FARPROC*)&winpty_error_free}, | |
744 {"winpty_free", (FARPROC*)&winpty_free}, | |
745 {"winpty_open", (FARPROC*)&winpty_open}, | |
746 {"winpty_spawn", (FARPROC*)&winpty_spawn}, | |
747 {"winpty_spawn_config_free", (FARPROC*)&winpty_spawn_config_free}, | |
748 {"winpty_spawn_config_new", (FARPROC*)&winpty_spawn_config_new}, | |
749 {"winpty_error_msg", (FARPROC*)&winpty_error_msg}, | |
750 {NULL, NULL} | |
751 }; | |
752 | |
753 /* No need to initialize twice. */ | |
754 if (hWinPtyDLL) | |
755 return 1; | |
756 /* Load winpty.dll */ | |
757 hWinPtyDLL = vimLoadLib(WINPTY_DLL); | |
758 if (!hWinPtyDLL) | |
759 { | |
760 EMSG2(_(e_loadlib), WINPTY_DLL); | |
761 return 0; | |
762 } | |
763 for (i = 0; winpty_entry[i].name != NULL | |
764 && winpty_entry[i].ptr != NULL; ++i) | |
765 { | |
766 if ((*winpty_entry[i].ptr = (FARPROC)GetProcAddress(hWinPtyDLL, | |
767 winpty_entry[i].name)) == NULL) | |
768 { | |
769 EMSG2(_(e_loadfunc), winpty_entry[i].name); | |
770 return 0; | |
771 } | |
772 } | |
773 | |
774 return 1; | |
775 } | |
776 | |
777 /* | |
778 * Create a new terminal of "rows" by "cols" cells. | |
779 * Store a reference in "term". | |
780 * Return OK or FAIL. | |
781 */ | |
782 static int | |
783 term_and_job_init(term_T *term, int rows, int cols, char_u *cmd) | |
784 { | |
785 WCHAR *p = enc_to_utf16(cmd, NULL); | |
786 channel_T *channel = NULL; | |
787 job_T *job = NULL; | |
788 jobopt_T opt; | |
789 DWORD error; | |
790 HANDLE jo = NULL, child_process_handle, child_thread_handle; | |
791 void *winpty_err; | |
792 void *spawn_config; | |
793 | |
794 if (!dyn_winpty_init()) | |
795 return FAIL; | |
796 | |
797 if (p == NULL) | |
798 return FAIL; | |
799 | |
800 job = job_alloc(); | |
801 if (job == NULL) | |
802 goto failed; | |
803 | |
804 channel = add_channel(); | |
805 if (channel == NULL) | |
806 goto failed; | |
807 | |
808 term->tl_winpty_config = winpty_config_new(0, &winpty_err); | |
809 if (term->tl_winpty_config == NULL) | |
810 goto failed; | |
811 | |
812 winpty_config_set_initial_size(term->tl_winpty_config, cols, rows); | |
813 term->tl_winpty = winpty_open(term->tl_winpty_config, &winpty_err); | |
814 if (term->tl_winpty == NULL) | |
815 goto failed; | |
816 | |
817 spawn_config = winpty_spawn_config_new( | |
818 WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN | | |
819 WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN, | |
820 NULL, | |
821 p, | |
822 NULL, | |
823 NULL, | |
824 &winpty_err); | |
825 if (spawn_config == NULL) | |
826 goto failed; | |
827 | |
828 channel = add_channel(); | |
829 if (channel == NULL) | |
830 goto failed; | |
831 | |
832 job = job_alloc(); | |
833 if (job == NULL) | |
834 goto failed; | |
835 | |
836 if (!winpty_spawn(term->tl_winpty, spawn_config, &child_process_handle, | |
837 &child_thread_handle, &error, &winpty_err)) | |
838 goto failed; | |
839 | |
840 channel_set_pipes(channel, | |
841 (sock_T) CreateFileW( | |
842 winpty_conin_name(term->tl_winpty), | |
843 GENERIC_WRITE, 0, NULL, | |
844 OPEN_EXISTING, 0, NULL), | |
845 (sock_T) CreateFileW( | |
846 winpty_conout_name(term->tl_winpty), | |
847 GENERIC_READ, 0, NULL, | |
848 OPEN_EXISTING, 0, NULL), | |
849 (sock_T) CreateFileW( | |
850 winpty_conerr_name(term->tl_winpty), | |
851 GENERIC_READ, 0, NULL, | |
852 OPEN_EXISTING, 0, NULL)); | |
853 | |
854 jo = CreateJobObject(NULL, NULL); | |
855 if (jo == NULL) | |
856 goto failed; | |
857 | |
858 if (!AssignProcessToJobObject(jo, child_process_handle)) | |
859 goto failed; | |
860 | |
861 winpty_spawn_config_free(spawn_config); | |
862 | |
863 create_vterm(term, rows, cols); | |
864 | |
865 setup_job_options(&opt, rows, cols); | |
866 channel_set_job(channel, job, &opt); | |
867 | |
868 job->jv_channel = channel; | |
869 job->jv_proc_info.hProcess = child_process_handle; | |
870 job->jv_proc_info.dwProcessId = GetProcessId(child_process_handle); | |
871 job->jv_job_object = jo; | |
872 job->jv_status = JOB_STARTED; | |
873 term->tl_job = job; | |
874 | |
875 return OK; | |
876 | |
877 failed: | |
878 if (channel != NULL) | |
879 channel_clear(channel); | |
880 if (job != NULL) | |
881 job_cleanup(job); | |
882 if (jo != NULL) | |
883 CloseHandle(jo); | |
884 if (term->tl_winpty != NULL) | |
885 winpty_free(term->tl_winpty); | |
886 if (term->tl_winpty_config != NULL) | |
887 winpty_config_free(term->tl_winpty_config); | |
888 if (winpty_err != NULL) | |
889 { | |
890 char_u *msg = utf16_to_enc( | |
891 (short_u *)winpty_error_msg(winpty_err), NULL); | |
892 | |
893 EMSG(msg); | |
894 winpty_error_free(winpty_err); | |
895 } | |
896 return FAIL; | |
897 } | |
898 | |
899 /* | |
900 * Free the terminal emulator part of "term". | |
901 */ | |
902 static void | |
903 term_free(term_T *term) | |
904 { | |
905 winpty_free(term->tl_winpty); | |
906 winpty_config_free(term->tl_winpty_config); | |
907 vterm_free(term->tl_vterm); | |
908 } | |
909 | |
910 # else | |
911 | |
912 /************************************** | |
913 * 3. Unix-like implementation. | |
914 */ | |
915 | |
916 /* | |
917 * Create a new terminal of "rows" by "cols" cells. | |
918 * Start job for "cmd". | |
919 * Store the pointers in "term". | |
920 * Return OK or FAIL. | |
921 */ | |
922 static int | |
923 term_and_job_init(term_T *term, int rows, int cols, char_u *cmd) | |
924 { | |
925 typval_T argvars[2]; | |
926 jobopt_T opt; | |
927 | |
928 create_vterm(term, rows, cols); | |
929 | |
930 argvars[0].v_type = VAR_STRING; | |
931 argvars[0].vval.v_string = cmd; | |
932 setup_job_options(&opt, rows, cols); | |
933 term->tl_job = job_start(argvars, &opt); | |
934 | |
935 return term->tl_job != NULL ? OK : FAIL; | |
936 } | |
937 | |
938 /* | |
939 * Free the terminal emulator part of "term". | |
940 */ | |
941 static void | |
942 term_free(term_T *term) | |
943 { | |
944 vterm_free(term->tl_vterm); | |
945 } | |
946 # endif | |
723 | 947 |
724 #endif /* FEAT_TERMINAL */ | 948 #endif /* FEAT_TERMINAL */ |