blob: b653cc24face6e0b4fa0d01d63a3e72a960b647d [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 *
13 * For a terminal one VTerm is constructed. This uses libvterm. A copy of
14 * that library is in the libvterm directory.
15 *
16 * The VTerm invokes callbacks when its screen contents changes. The line
17 * range is stored in tl_dirty_row_start and tl_dirty_row_end. Once in a
18 * while, if the window is visible, the screen contents is drawn.
19 *
20 * If the terminal window has keyboard focus, typed keys are converted to the
21 * terminal encoding and writting to the job over a channel.
22 *
23 * If the job produces output, it is written to the VTerm.
24 * This will result in screen updates.
25 *
26 * TODO:
27 * - +terminal in features[] in version.c
28 * - free b_term when closing terminal.
29 * - remove term from first_term list when closing terminal.
30 * - set buffer options to be scratch, hidden, nomodifiable, etc.
31 * - set buffer name to command, add (1) to avoid duplicates.
32 * - command line completion (command name)
33 * - support fixed size when 'termsize' is "rowsXcols".
34 * - support minimal size when 'termsize' is "rows*cols".
35 * - support minimal size when 'termsize' is empty.
36 * - implement ":buf {term-buf-name}"
37 * - implement term_getsize()
38 * - implement term_setsize()
39 * - implement term_sendkeys() send keystrokes to a terminal
40 * - implement term_wait() wait for screen to be updated
41 * - implement term_scrape() inspect terminal screen
42 * - implement term_open() open terminal window
43 * - implement term_getjob()
44 */
45
46#include "vim.h"
47
48#ifdef FEAT_TERMINAL
49
50#include "libvterm/include/vterm.h"
51
52/* typedef term_T in structs.h */
53struct terminal_S {
54 term_T *tl_next;
55
56 VTerm *tl_vterm;
57 job_T *tl_job;
58
59 /* Range of screen rows to update. Zero based. */
60 int tl_dirty_row_start; /* -1 if nothing dirty */
61 int tl_dirty_row_end; /* row below last one to update */
62
63 pos_T tl_cursor;
64};
65
66#define MAX_ROW 999999 /* used for tl_dirty_row_end to update all rows */
67
68/*
69 * List of all active terminals.
70 */
71static term_T *first_term = NULL;
72
73static int handle_damage(VTermRect rect, void *user);
74static int handle_moverect(VTermRect dest, VTermRect src, void *user);
75static int handle_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user);
76static int handle_resize(int rows, int cols, void *user);
77
78static VTermScreenCallbacks screen_callbacks = {
79 handle_damage, /* damage */
80 handle_moverect, /* moverect */
81 handle_movecursor, /* movecursor */
82 NULL, /* settermprop */
83 NULL, /* bell */
84 handle_resize, /* resize */
85 NULL, /* sb_pushline */
86 NULL /* sb_popline */
87};
88
89/*
90 * ":terminal": open a terminal window and execute a job in it.
91 */
92 void
93ex_terminal(exarg_T *eap)
94{
95 int rows;
96 int cols;
97 exarg_T split_ea;
98 win_T *old_curwin = curwin;
99 typval_T argvars[2];
100 term_T *term;
101 VTerm *vterm;
102 VTermScreen *screen;
103
104 if (check_restricted() || check_secure())
105 return;
106
107 term = (term_T *)alloc_clear(sizeof(term_T));
108 if (term == NULL)
109 return;
110 term->tl_dirty_row_end = MAX_ROW;
111
112 /* Open a new window or tab. */
113 vim_memset(&split_ea, 0, sizeof(split_ea));
114 split_ea.cmdidx = CMD_new;
115 split_ea.cmd = (char_u *)"new";
116 split_ea.arg = (char_u *)"";
117 ex_splitview(&split_ea);
118 if (curwin == old_curwin)
119 {
120 /* split failed */
121 vim_free(term);
122 return;
123 }
124
125 curbuf->b_term = term;
126 term->tl_next = first_term;
127 first_term = term;
128
129 /* TODO: set buffer type, hidden, etc. */
130
131 if (*curwin->w_p_tms != NUL)
132 {
133 char_u *p = vim_strchr(curwin->w_p_tms, 'x') + 1;
134
135 rows = atoi((char *)curwin->w_p_tms);
136 cols = atoi((char *)p);
137 /* TODO: resize window if possible. */
138 }
139 else
140 {
141 rows = curwin->w_height;
142 cols = curwin->w_width;
143 }
144
145 vterm = vterm_new(rows, cols);
146 term->tl_vterm = vterm;
147 screen = vterm_obtain_screen(vterm);
148 vterm_screen_set_callbacks(screen, &screen_callbacks, term);
149
150 argvars[0].v_type = VAR_STRING;
151 argvars[0].vval.v_string = eap->arg;
152 argvars[1].v_type = VAR_UNKNOWN;
153 term->tl_job = job_start(argvars);
154
155 /* TODO: setup channels to/from job */
156 /* Setup pty, see mch_call_shell(). */
157}
158
159 static int
160handle_damage(VTermRect rect, void *user)
161{
162 term_T *term = (term_T *)user;
163
164 term->tl_dirty_row_start = MIN(term->tl_dirty_row_start, rect.start_row);
165 term->tl_dirty_row_end = MAX(term->tl_dirty_row_end, rect.end_row);
166 return 1;
167}
168
169 static int
170handle_moverect(VTermRect dest, VTermRect src, void *user)
171{
172 /* TODO */
173 return 1;
174}
175
176 static int
177handle_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
178{
179 /* TODO: handle moving the cursor. */
180 return 1;
181}
182
183 static int
184handle_resize(int rows, int cols, void *user)
185{
186 /* TODO: handle terminal resize. */
187 return 1;
188}
189
190/* TODO: Use win_del_lines() to make scroll up efficient. */
191
192/* TODO: function to update the window.
193 * Get the screen contents from vterm with vterm_screen_get_cell().
194 * put in current_ScreenLine and call screen_line().
195 */
196
197/* TODO: function to wait for input and send it to the job.
198 * Return when a CTRL-W command is typed that moves to another window.
199 * Convert special keys to vterm keys:
200 * - Write keys to vterm: vterm_keyboard_key()
201 * - read the output (xterm escape sequences): vterm_output_read()
202 * - Write output to channel.
203 */
204
205/* TODO: function to read job output from the channel.
206 * write to vterm: vterm_input_write()
207 * This will invoke screen callbacks.
208 * call vterm_screen_flush_damage()
209 */
210
211#endif /* FEAT_TERMINAL */