0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00

patch 8.0.0730: terminal feature only supports Unix-like systems

Problem:    Terminal feature only supports Unix-like systems.
Solution:   Prepare for adding an MS-Windows implementaiton.
This commit is contained in:
Bram Moolenaar 2017-07-18 22:53:21 +02:00
parent 8008b6318d
commit 8c0095c59a
2 changed files with 337 additions and 181 deletions

View File

@ -10,18 +10,23 @@
/* /*
* Terminal window support, see ":help :terminal". * Terminal window support, see ":help :terminal".
* *
* For a terminal one VTerm is constructed. This uses libvterm. A copy of * There are three parts:
* that library is in the libvterm directory. * 1. Generic code for all systems.
* 2. The MS-Windows implementation.
* Uses a hidden console for the terminal emulator.
* 3. The Unix-like implementation.
* Uses libvterm for the terminal emulator.
* *
* The VTerm invokes callbacks when its screen contents changes. The line * When a terminal window is opened, a job is started that will be connected to
* range is stored in tl_dirty_row_start and tl_dirty_row_end. Once in a * the terminal emulator.
* while, if the terminal window is visible, the screen contents is drawn.
* *
* If the terminal window has keyboard focus, typed keys are converted to the * If the terminal window has keyboard focus, typed keys are converted to the
* terminal encoding and writting to the job over a channel. * terminal encoding and writting to the job over a channel.
* *
* If the job produces output, it is written to the VTerm. * If the job produces output, it is written to the terminal emulator. The
* This will result in screen updates. * terminal emulator invokes callbacks when its screen content changes. The
* line range is stored in tl_dirty_row_start and tl_dirty_row_end. Once in a
* while, if the terminal window is visible, the screen contents is drawn.
* *
* TODO: * TODO:
* - pressing Enter sends two CR and/or NL characters to "bash -i"? * - pressing Enter sends two CR and/or NL characters to "bash -i"?
@ -29,14 +34,21 @@
* - set buffer options to be scratch, hidden, nomodifiable, etc. * - set buffer options to be scratch, hidden, nomodifiable, etc.
* - set buffer name to command, add (1) to avoid duplicates. * - set buffer name to command, add (1) to avoid duplicates.
* - If [command] is not given the 'shell' option is used. * - If [command] is not given the 'shell' option is used.
* - if the job ends, write "-- JOB ENDED --" in the terminal * - Add a scrollback buffer (contains lines to scroll off the top).
* - when closing window and job ended, delete the terminal * Can use the buf_T lines, store attributes somewhere else?
* - When the job ends:
* - Write "-- JOB ENDED --" in the terminal.
* - Put the terminal contents in the scrollback buffer.
* - Free the terminal emulator.
* - Display the scrollback buffer (but with attributes).
* Make the buffer not modifiable, drop attributes when making changes.
* - when closing window and job has not ended, make terminal hidden? * - when closing window and job has not ended, make terminal hidden?
* - Use a pty for I/O with the job. * - Use a pty for I/O with the job.
* - Windows implementation: * - Windows implementation:
* (WiP): https://github.com/mattn/vim/tree/terminal * (WiP): https://github.com/mattn/vim/tree/terminal
* src/os_win32.c mch_open_terminal() * src/os_win32.c mch_open_terminal()
Using winpty ? * Using winpty ?
* - use win_del_lines() to make scroll-up efficient.
* - command line completion for :terminal * - command line completion for :terminal
* - support fixed size when 'termsize' is "rowsXcols". * - support fixed size when 'termsize' is "rowsXcols".
* - support minimal size when 'termsize' is "rows*cols". * - support minimal size when 'termsize' is "rows*cols".
@ -57,13 +69,22 @@
#ifdef FEAT_TERMINAL #ifdef FEAT_TERMINAL
#include "libvterm/include/vterm.h" #ifdef WIN3264
/* MS-Windows: use a native console. */
#else
/* Unix-like: use libvterm. */
# include "libvterm/include/vterm.h"
#endif
/* typedef term_T in structs.h */ /* typedef term_T in structs.h */
struct terminal_S { struct terminal_S {
term_T *tl_next; term_T *tl_next;
#ifdef WIN3264
/* console handle? */
#else
VTerm *tl_vterm; VTerm *tl_vterm;
#endif
job_T *tl_job; job_T *tl_job;
buf_T *tl_buffer; buf_T *tl_buffer;
@ -75,27 +96,23 @@ struct terminal_S {
}; };
#define MAX_ROW 999999 /* used for tl_dirty_row_end to update all rows */ #define MAX_ROW 999999 /* used for tl_dirty_row_end to update all rows */
#define KEY_BUF_LEN 200
/* Functions implemented for MS-Windows and Unix-like systems. */
static int term_init(term_T *term, int rows, int cols);
static void term_free(term_T *term);
static void term_write_job_output(term_T *term, char_u *msg, size_t len);
static int term_convert_key(int c, char *buf);
static void term_update_lines(win_T *wp);
/* /*
* List of all active terminals. * List of all active terminals.
*/ */
static term_T *first_term = NULL; static term_T *first_term = NULL;
static int handle_damage(VTermRect rect, void *user); /**************************************
static int handle_moverect(VTermRect dest, VTermRect src, void *user); * 1. Generic code for all systems.
static int handle_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user); */
static int handle_resize(int rows, int cols, void *user);
static VTermScreenCallbacks screen_callbacks = {
handle_damage, /* damage */
handle_moverect, /* moverect */
handle_movecursor, /* movecursor */
NULL, /* settermprop */
NULL, /* bell */
handle_resize, /* resize */
NULL, /* sb_pushline */
NULL /* sb_popline */
};
/* /*
* ":terminal": open a terminal window and execute a job in it. * ":terminal": open a terminal window and execute a job in it.
@ -109,8 +126,6 @@ ex_terminal(exarg_T *eap)
win_T *old_curwin = curwin; win_T *old_curwin = curwin;
typval_T argvars[2]; typval_T argvars[2];
term_T *term; term_T *term;
VTerm *vterm;
VTermScreen *screen;
jobopt_T opt; jobopt_T opt;
if (check_restricted() || check_secure()) if (check_restricted() || check_secure())
@ -155,18 +170,8 @@ ex_terminal(exarg_T *eap)
cols = curwin->w_width; cols = curwin->w_width;
} }
vterm = vterm_new(rows, cols); if (term_init(term, rows, cols) == OK)
term->tl_vterm = vterm; {
screen = vterm_obtain_screen(vterm);
vterm_screen_set_callbacks(screen, &screen_callbacks, term);
/* TODO: depends on 'encoding'. */
vterm_set_utf8(vterm, 1);
/* Required to initialize most things. */
vterm_screen_reset(screen, 1 /* hard */);
/* By default NL means CR-NL. */
vterm_input_write(vterm, "\x1b[20h", 5);
argvars[0].v_type = VAR_STRING; argvars[0].v_type = VAR_STRING;
argvars[0].vval.v_string = eap->arg; argvars[0].vval.v_string = eap->arg;
@ -183,12 +188,13 @@ ex_terminal(exarg_T *eap)
opt.jo_set |= JO_OUT_BUF + (JO_OUT_BUF << (PART_ERR - PART_OUT)); opt.jo_set |= JO_OUT_BUF + (JO_OUT_BUF << (PART_ERR - PART_OUT));
term->tl_job = job_start(argvars, &opt); term->tl_job = job_start(argvars, &opt);
}
if (term->tl_job == NULL) if (term->tl_job == NULL)
/* Wiping out the buffer will also close the window. */ /* Wiping out the buffer will also close the window. */
do_buffer(DOBUF_WIPE, DOBUF_CURRENT, FORWARD, 0, TRUE); do_buffer(DOBUF_WIPE, DOBUF_CURRENT, FORWARD, 0, TRUE);
/* Setup pty, see mch_call_shell(). */ /* TODO: Setup pty, see mch_call_shell(). */
} }
/* /*
@ -220,7 +226,7 @@ free_terminal(term_T *term)
job_unref(term->tl_job); job_unref(term->tl_job);
} }
vterm_free(term->tl_vterm); term_free(term);
vim_free(term); vim_free(term);
} }
@ -232,11 +238,10 @@ free_terminal(term_T *term)
write_to_term(buf_T *buffer, char_u *msg, channel_T *channel) write_to_term(buf_T *buffer, char_u *msg, channel_T *channel)
{ {
size_t len = STRLEN(msg); size_t len = STRLEN(msg);
VTerm *vterm = buffer->b_term->tl_vterm; term_T *term = buffer->b_term;
ch_logn(channel, "writing %d bytes to terminal", (int)len); ch_logn(channel, "writing %d bytes to terminal", (int)len);
vterm_input_write(vterm, (char *)msg, len); term_write_job_output(term, msg, len);
vterm_screen_flush_damage(vterm_obtain_screen(vterm));
/* TODO: only update once in a while. */ /* TODO: only update once in a while. */
update_screen(0); update_screen(0);
@ -244,45 +249,174 @@ write_to_term(buf_T *buffer, char_u *msg, channel_T *channel)
out_flush(); out_flush();
} }
/*
* Wait for input and send it to the job.
* Return when a CTRL-W command is typed that moves to another window.
*/
void
terminal_loop(void)
{
char buf[KEY_BUF_LEN];
int c;
size_t len;
for (;;)
{
/* TODO: skip screen update when handling a sequence of keys. */
update_screen(0);
setcursor();
out_flush();
c = vgetc();
if (c == Ctrl_W)
{
stuffcharReadbuff(Ctrl_W);
return;
}
/* Convert the typed key to a sequence of bytes for the job. */
len = term_convert_key(c, buf);
if (len > 0)
/* TODO: if FAIL is returned, stop? */
channel_send(curbuf->b_term->tl_job->jv_channel, PART_IN,
(char_u *)buf, len, NULL);
}
}
/* /*
* Called to update the window that contains the terminal. * Called to update the window that contains the terminal.
*/ */
void void
term_update_window(win_T *wp) term_update_window(win_T *wp)
{ {
int vterm_rows; term_update_lines(wp);
int vterm_cols; }
VTerm *vterm = wp->w_buffer->b_term->tl_vterm;
VTermScreen *screen = vterm_obtain_screen(vterm);
VTermPos pos;
vterm_get_size(vterm, &vterm_rows, &vterm_cols); #ifdef WIN3264
/* TODO: Only redraw what changed. */ /**************************************
for (pos.row = 0; pos.row < wp->w_height; ++pos.row) * 2. MS-Windows implementation.
{ */
int off = screen_get_current_line_off();
if (pos.row < vterm_rows) /*
for (pos.col = 0; pos.col < wp->w_width && pos.col < vterm_cols; * Create a new terminal of "rows" by "cols" cells.
++pos.col) * Store a reference in "term".
{ * Return OK or FAIL.
VTermScreenCell cell; */
int c; static int
term_init(term_T *term, int rows, int cols)
{
/* TODO: Create a hidden console */
return FAIL;
}
vterm_screen_get_cell(screen, pos, &cell); /*
/* TODO: use cell.attrs and colors */ * Free the terminal emulator part of "term".
/* TODO: use cell.width */ */
/* TODO: multi-byte chars */ static void
c = cell.chars[0]; term_free(term_T *term)
ScreenLines[off] = c == NUL ? ' ' : c; {
ScreenAttrs[off] = 0; /* TODO */
++off; }
}
screen_line(wp->w_winrow + pos.row, wp->w_wincol, pos.col, wp->w_width, /*
FALSE); * Write job output "msg[len]" to the terminal.
} */
static void
term_write_job_output(term_T *term, char_u *msg, size_t len)
{
/* TODO */
}
/*
* Convert typed key "c" into bytes to send to the job.
* Return the number of bytes in "buf".
*/
static int
term_convert_key(int c, char *buf)
{
/* TODO */
return 0;
}
/*
* Called to update the window that contains the terminal.
*/
static void
term_update_lines(win_T *wp)
{
/* TODO */
}
#else
/**************************************
* 3. Unix-like implementation.
*
* For a terminal one VTerm is constructed. This uses libvterm. A copy of
* that library is in the libvterm directory.
*/
static int handle_damage(VTermRect rect, void *user);
static int handle_moverect(VTermRect dest, VTermRect src, void *user);
static int handle_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user);
static int handle_resize(int rows, int cols, void *user);
static VTermScreenCallbacks screen_callbacks = {
handle_damage, /* damage */
handle_moverect, /* moverect */
handle_movecursor, /* movecursor */
NULL, /* settermprop */
NULL, /* bell */
handle_resize, /* resize */
NULL, /* sb_pushline */
NULL /* sb_popline */
};
/*
* Create a new terminal of "rows" by "cols" cells.
* Store a reference in "term".
* Return OK or FAIL.
*/
static int
term_init(term_T *term, int rows, int cols)
{
VTerm *vterm = vterm_new(rows, cols);
VTermScreen *screen;
term->tl_vterm = vterm;
screen = vterm_obtain_screen(vterm);
vterm_screen_set_callbacks(screen, &screen_callbacks, term);
/* TODO: depends on 'encoding'. */
vterm_set_utf8(vterm, 1);
/* Required to initialize most things. */
vterm_screen_reset(screen, 1 /* hard */);
/* By default NL means CR-NL. */
vterm_input_write(vterm, "\x1b[20h", 5);
return OK;
}
/*
* Free the terminal emulator part of "term".
*/
static void
term_free(term_T *term)
{
vterm_free(term->tl_vterm);
}
/*
* Write job output "msg[len]" to the terminal.
*/
static void
term_write_job_output(term_T *term, char_u *msg, size_t len)
{
VTerm *vterm = term->tl_vterm;
vterm_input_write(vterm, (char *)msg, len);
vterm_screen_flush_damage(vterm_obtain_screen(vterm));
} }
static int static int
@ -344,37 +478,19 @@ handle_resize(int rows, int cols, void *user)
return 1; return 1;
} }
/* TODO: Use win_del_lines() to make scroll up efficient. */
/* /*
* Wait for input and send it to the job. * Convert typed key "c" into bytes to send to the job.
* Return when a CTRL-W command is typed that moves to another window. * Return the number of bytes in "buf".
*/ */
void static int
terminal_loop(void) term_convert_key(int c, char *buf)
{ {
VTerm *vterm = curbuf->b_term->tl_vterm; VTerm *vterm = curbuf->b_term->tl_vterm;
char buf[200];
for (;;)
{
int c;
VTermKey key = VTERM_KEY_NONE; VTermKey key = VTERM_KEY_NONE;
VTermModifier mod = VTERM_MOD_NONE; VTermModifier mod = VTERM_MOD_NONE;
size_t len;
update_screen(0);
setcursor();
out_flush();
c = vgetc();
switch (c) switch (c)
{ {
case Ctrl_W:
stuffcharReadbuff(Ctrl_W);
return;
/* TODO: which of these two should be used? */ /* TODO: which of these two should be used? */
#if 0 #if 0
case CAR: key = VTERM_KEY_ENTER; break; case CAR: key = VTERM_KEY_ENTER; break;
@ -443,12 +559,50 @@ terminal_loop(void)
vterm_keyboard_unichar(vterm, c, mod); vterm_keyboard_unichar(vterm, c, mod);
/* Read back the converted escape sequence. */ /* Read back the converted escape sequence. */
len = vterm_output_read(vterm, buf, sizeof(buf)); return vterm_output_read(vterm, buf, KEY_BUF_LEN);
}
/* TODO: if FAIL is returned, stop? */ /*
channel_send(curbuf->b_term->tl_job->jv_channel, PART_IN, * Called to update the window that contains the terminal.
(char_u *)buf, len, NULL); */
static void
term_update_lines(win_T *wp)
{
int vterm_rows;
int vterm_cols;
VTerm *vterm = wp->w_buffer->b_term->tl_vterm;
VTermScreen *screen = vterm_obtain_screen(vterm);
VTermPos pos;
vterm_get_size(vterm, &vterm_rows, &vterm_cols);
/* TODO: Only redraw what changed. */
for (pos.row = 0; pos.row < wp->w_height; ++pos.row)
{
int off = screen_get_current_line_off();
if (pos.row < vterm_rows)
for (pos.col = 0; pos.col < wp->w_width && pos.col < vterm_cols;
++pos.col)
{
VTermScreenCell cell;
int c;
vterm_screen_get_cell(screen, pos, &cell);
/* TODO: use cell.attrs and colors */
/* TODO: use cell.width */
/* TODO: multi-byte chars */
c = cell.chars[0];
ScreenLines[off] = c == NUL ? ' ' : c;
ScreenAttrs[off] = 0;
++off;
}
screen_line(wp->w_winrow + pos.row, wp->w_wincol, pos.col, wp->w_width,
FALSE);
} }
} }
#endif
#endif /* FEAT_TERMINAL */ #endif /* FEAT_TERMINAL */

View File

@ -769,6 +769,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
730,
/**/ /**/
729, 729,
/**/ /**/