summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-02-03 14:53:10 +0100
committerBram Moolenaar <Bram@vim.org>2019-02-03 14:53:10 +0100
commitaa5df7e3127dff6b7336df0903f5c569a40eb174 (patch)
tree491988c562057debdd52dc8198958e4099fd9452
parent01a6c21691631ee55744a1799a9725e5d6521cf4 (diff)
patch 8.1.0870: Vim doesn't use the new ConPTY support in Windows 10v8.1.0870
Problem: Vim doesn't use the new ConPTY support in Windows 10. Solution: Use ConPTY support, if available. (Nobuhiro Takasaki, closes #3794)
-rw-r--r--runtime/doc/eval.txt9
-rw-r--r--runtime/doc/options.txt17
-rw-r--r--runtime/doc/terminal.txt10
-rw-r--r--src/channel.c73
-rw-r--r--src/evalfunc.c4
-rw-r--r--src/globals.h3
-rw-r--r--src/option.c36
-rw-r--r--src/option.h1
-rw-r--r--src/os_win32.c75
-rw-r--r--src/proto/terminal.pro2
-rw-r--r--src/structs.h18
-rw-r--r--src/terminal.c474
-rw-r--r--src/testdir/gen_opt_test.vim1
-rw-r--r--src/testdir/test_autocmd.vim8
-rw-r--r--src/testdir/test_mksession.vim4
-rw-r--r--src/testdir/test_terminal.vim30
-rw-r--r--src/version.c2
17 files changed, 694 insertions, 73 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index d888d9c491..cce17b9cf5 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1,4 +1,4 @@
-*eval.txt* For Vim version 8.1. Last change: 2019 Jan 29
+*eval.txt* For Vim version 8.1. Last change: 2019 Feb 03
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -696,7 +696,7 @@ similar to -1. >
:let otherblob = myblob[:] " make a copy of the Blob
If the first index is beyond the last byte of the Blob or the second index is
-before the first index, the result is an empty list. There is no error
+before the first index, the result is an empty Blob. There is no error
message.
If the second index is equal to or greater than the length of the list the
@@ -9469,6 +9469,10 @@ term_start({cmd}, {options}) *term_start()*
"ansi_colors" A list of 16 color names or hex codes
defining the ANSI palette used in GUI
color modes. See |g:terminal_ansi_colors|.
+ "term_mode" (MS-Windows only): Specify which pty to
+ use:
+ "winpty": Use winpty
+ "conpty": Use ConPTY (if available)
{only available when compiled with the |+terminal| feature}
@@ -10186,6 +10190,7 @@ cmdline_hist Compiled with |cmdline-history| support.
cmdline_info Compiled with 'showcmd' and 'ruler' support.
comments Compiled with |'comments'| support.
compatible Compiled to be very Vi compatible.
+conpty Platform where |ConPTY| can be used.
cryptv Compiled with encryption support |encryption|.
cscope Compiled with |cscope| support.
cursorbind Compiled with |cursorbind| (always true)
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 04c38b2041..e73a9dae80 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -8054,6 +8054,23 @@ A jump table for the options with a short description can be found at |Q_op|.
Note that the "cterm" attributes are still used, not the "gui" ones.
NOTE: This option is reset when 'compatible' is set.
+ *'termmode'* *'tmod'*
+'termmode' 'tmod' string (default "")
+ local to window
+ {not in Vi, MS-Windows only}
+ Whether the window uses winpty or |ConPTY| as the virtual console.
+ When set before opening the terminal, it influences what pty is used.
+ When opening the terminal it will be set to the actually used pty.
+
+ Possible values are:
+ "" use ConPTY if possible, winpty otherwise
+ "winpty" use winpty, fail if not supported
+ "conpty" use |ConPTY|, fail if not supported
+
+ |ConPTY| support depends on the platform (Windows 10 October 2018
+ edition). winpty support needs to be installed. If neither is
+ supported then you cannot open a terminal window.
+
*'termwinscroll'* *'twsl'*
'termwinscroll' 'twsl' number (default 10000)
local to buffer
diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt
index 0db69df7d3..3b78b550e2 100644
--- a/runtime/doc/terminal.txt
+++ b/runtime/doc/terminal.txt
@@ -228,7 +228,8 @@ Syntax ~
for Python "++eof=exit()". Special
codes can be used like with `:map`,
e.g. "<C-Z>" for CTRL-Z.
-
+ ++winpty Use winpty as the virtual console.
+ ++conpty Use |ConPTY| as the virtual console.
If you want to use more options use the |term_start()|
function.
If you want to split the window vertically, use: >
@@ -410,6 +411,13 @@ Just put the files somewhere in your PATH. You can set the 'winptydll' option
to point to the right file, if needed. If you have both the 32-bit and 64-bit
version, rename to winpty32.dll and winpty64.dll to match the way Vim was
build.
+ *ConPTY*
+On more recent versions of MS-Windows 10 (beginning with the "October 2018
+Update"), winpty is no longer required. On those versions, |:terminal| will use
+Windows' built-in support for hosting terminal applications, "ConPTY". When
+ConPTY is in use, there may be rendering artifacts regarding ambiguous-width
+characters. If you encounter any such issues, set 'termmode' to winpty (which
+you then must have instlled).
Environment variables are used to pass information to the running job:
VIM_SERVERNAME v:servername
diff --git a/src/channel.c b/src/channel.c
index 20cf46238c..484d0139b1 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -1720,11 +1720,7 @@ channel_get_all(channel_T *channel, ch_part_T part, int *outlen)
char_u *res;
char_u *p;
- /* If there is only one buffer just get that one. */
- if (head->rq_next == NULL || head->rq_next->rq_next == NULL)
- return channel_get(channel, part, outlen);
-
- /* Concatenate everything into one buffer. */
+ // Concatenate everything into one buffer.
for (node = head->rq_next; node != NULL; node = node->rq_next)
len += node->rq_buflen;
res = lalloc(len + 1, TRUE);
@@ -1738,7 +1734,7 @@ channel_get_all(channel_T *channel, ch_part_T part, int *outlen)
}
*p = NUL;
- /* Free all buffers */
+ // Free all buffers
do
{
p = channel_get(channel, part, NULL);
@@ -1747,16 +1743,37 @@ channel_get_all(channel_T *channel, ch_part_T part, int *outlen)
if (outlen != NULL)
{
+ // Returning the length, keep NUL characters.
*outlen += len;
return res;
}
- /* turn all NUL into NL */
- while (len > 0)
+ // Turn all NUL into NL, so that the result can be used as a string.
+ p = res;
+ while (p < res + len)
{
- --len;
- if (res[len] == NUL)
- res[len] = NL;
+ if (*p == NUL)
+ *p = NL;
+#ifdef WIN32
+ else if (*p == 0x1b)
+ {
+ // crush the escape sequence OSC 0/1/2: ESC ]0;
+ if (p + 3 < res + len
+ && p[1] == ']'
+ && (p[2] == '0' || p[2] == '1' || p[2] == '2')
+ && p[3] == ';')
+ {
+ // '\a' becomes a NL
+ while (p < res + (len - 1) && *p != '\a')
+ ++p;
+ // BEL is zero width characters, suppress display mistake
+ // ConPTY (after 10.0.18317) requires advance checking
+ if (p[-1] == NUL)
+ p[-1] = 0x07;
+ }
+ }
+#endif
+ ++p;
}
return res;
@@ -4330,7 +4347,7 @@ channel_parse_messages(void)
channel = first_channel;
continue;
}
- if (channel->ch_to_be_freed)
+ if (channel->ch_to_be_freed || channel->ch_killing)
{
channel_free(channel);
/* channel has been freed, start over */
@@ -4930,6 +4947,28 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2)
opt->jo_set2 |= JO2_TERM_KILL;
opt->jo_term_kill = tv_get_string_chk(item);
}
+ else if (STRCMP(hi->hi_key, "term_mode") == 0)
+ {
+ char_u *p;
+
+ if (!(supported2 & JO2_TERM_MODE))
+ break;
+ opt->jo_set2 |= JO2_TERM_MODE;
+ p = tv_get_string_chk(item);
+ if (p == NULL)
+ {
+ semsg(_(e_invargval), "term_mode");
+ return FAIL;
+ }
+ // Allow empty string, "winpty", "conpty".
+ if (!(*p == NUL || STRCMP(p, "winpty") == 0
+ || STRCMP(p, "conpty") == 0))
+ {
+ semsg(_(e_invargval), "term_mode");
+ return FAIL;
+ }
+ opt->jo_term_mode = p[0];
+ }
# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
else if (STRCMP(hi->hi_key, "ansi_colors") == 0)
{
@@ -5440,6 +5479,16 @@ job_cleanup(job_T *job)
channel_need_redraw = TRUE;
}
+ if (job->jv_channel != NULL
+ && job->jv_channel->ch_anonymous_pipe && !job->jv_channel->ch_killing)
+ {
+ ++safe_to_invoke_callback;
+ channel_free_contents(job->jv_channel);
+ job->jv_channel->ch_job = NULL;
+ job->jv_channel = NULL;
+ --safe_to_invoke_callback;
+ }
+
// Do not free the job in case the close callback of the associated channel
// isn't invoked yet and may get information by job_info().
if (job->jv_refcount == 0 && !job_channel_still_useful(job))
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 374e70132f..fa7ed9bab1 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -6738,6 +6738,10 @@ f_has(typval_T *argvars, typval_T *rettv)
else if (STRICMP(name, "terminal") == 0)
n = terminal_enabled();
#endif
+#if defined(FEAT_TERMINAL) && defined(WIN3264)
+ else if (STRICMP(name, "conpty") == 0)
+ n = use_conpty();
+#endif
}
rettv->vval.v_number = n;
diff --git a/src/globals.h b/src/globals.h
index 0562610fd0..6cc3be2158 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -1432,7 +1432,8 @@ EXTERN char e_fsync[] INIT(= N_("E667: Fsync failed"));
|| defined(DYNAMIC_ICONV) \
|| defined(DYNAMIC_GETTEXT) \
|| defined(DYNAMIC_MZSCHEME) \
- || defined(DYNAMIC_LUA)
+ || defined(DYNAMIC_LUA) \
+ || defined(FEAT_TERMINAL)
EXTERN char e_loadlib[] INIT(= N_("E370: Could not load library %s"));
EXTERN char e_loadfunc[] INIT(= N_("E448: Could not load library function %s"));
#endif
diff --git a/src/option.c b/src/option.c
index 6d2bab1e02..77d1024ecd 100644
--- a/src/option.c
+++ b/src/option.c
@@ -253,6 +253,7 @@
# define PV_TWK OPT_WIN(WV_TWK)
# define PV_TWS OPT_WIN(WV_TWS)
# define PV_TWSL OPT_BUF(BV_TWSL)
+# define PV_TMOD OPT_WIN(WV_TMOD)
#endif
#ifdef FEAT_SIGNS
# define PV_SCL OPT_WIN(WV_SCL)
@@ -2700,6 +2701,15 @@ static struct vimoption options[] =
{(char_u *)FALSE, (char_u *)FALSE}
#endif
SCTX_INIT},
+ {"termmode", "tmod", P_STRING|P_ALLOCED|P_VI_DEF,
+#ifdef FEAT_TERMINAL
+ (char_u *)VAR_WIN, PV_TMOD,
+ {(char_u *)"", (char_u *)NULL}
+#else
+ (char_u *)NULL, PV_NONE,
+ {(char_u *)NULL, (char_u *)0L}
+#endif
+ SCTX_INIT},
{"termwinkey", "twk", P_STRING|P_ALLOCED|P_RWIN|P_VI_DEF,
#ifdef FEAT_TERMINAL
(char_u *)VAR_WIN, PV_TWK,
@@ -3208,6 +3218,9 @@ static char *(p_cot_values[]) = {"menu", "menuone", "longest", "preview", "noins
#ifdef FEAT_SIGNS
static char *(p_scl_values[]) = {"yes", "no", "auto", NULL};
#endif
+#ifdef FEAT_TERMINAL
+static char *(p_tmod_values[]) = {"winpty", "conpty", "", NULL};
+#endif
static void set_options_default(int opt_flags);
static void set_string_default_esc(char *name, char_u *val, int escape);
@@ -3661,7 +3674,12 @@ set_init_1(int clean_arg)
{
char buf[50];
- sprintf(buf, "cp%ld", (long)GetConsoleCP());
+ /* Win32 console: In ConPTY, GetConsoleCP() returns zero.
+ * Use an alternative value. */
+ if (GetConsoleCP() == 0)
+ sprintf(buf, "cp%ld", (long)GetACP());
+ else
+ sprintf(buf, "cp%ld", (long)GetConsoleCP());
p_tenc = vim_strsave((char_u *)buf);
if (p_tenc != NULL)
{
@@ -7468,14 +7486,14 @@ did_set_string_option(
#endif
#ifdef FEAT_TERMINAL
- /* 'termwinkey' */
+ // 'termwinkey'
else if (varp == &curwin->w_p_twk)
{
if (*curwin->w_p_twk != NUL
&& string_to_key(curwin->w_p_twk, TRUE) == 0)
errmsg = e_invarg;
}
- /* 'termwinsize' */
+ // 'termwinsize'
else if (varp == &curwin->w_p_tws)
{
if (*curwin->w_p_tws != NUL)
@@ -7487,6 +7505,12 @@ did_set_string_option(
errmsg = e_invarg;
}
}
+ // 'termmode'
+ else if (varp == &curwin->w_p_tmod)
+ {
+ if (check_opt_strings(*varp, p_tmod_values, FALSE) != OK)
+ errmsg = e_invarg;
+ }
#endif
#ifdef FEAT_VARTABS
@@ -8838,7 +8862,7 @@ set_bool_option(
if (!has_vtp_working())
{
p_tgc = 0;
- return (char_u*)N_("E954: 24-bit colors are not supported on this environment");
+ return N_("E954: 24-bit colors are not supported on this environment");
}
if (is_term_win32())
swap_tcap();
@@ -10928,6 +10952,7 @@ get_varp(struct vimoption *p)
case PV_TWK: return (char_u *)&(curwin->w_p_twk);
case PV_TWS: return (char_u *)&(curwin->w_p_tws);
case PV_TWSL: return (char_u *)&(curbuf->b_p_twsl);
+ case PV_TMOD: return (char_u *)&(curwin->w_p_tmod);
#endif
case PV_AI: return (char_u *)&(curbuf->b_p_ai);
@@ -11128,6 +11153,7 @@ copy_winopt(winopt_T *from, winopt_T *to)
#ifdef FEAT_TERMINAL
to->wo_twk = vim_strsave(from->wo_twk);
to->wo_tws = vim_strsave(from->wo_tws);
+ to->wo_tmod = vim_strsave(from->wo_tmod);
#endif
#ifdef FEAT_FOLDING
to->wo_fdc = from->wo_fdc;
@@ -11198,6 +11224,7 @@ check_winopt(winopt_T *wop UNUSED)
#ifdef FEAT_TERMINAL
check_string_option(&wop->wo_twk);
check_string_option(&wop->wo_tws);
+ check_string_option(&wop->wo_tmod);
#endif
#ifdef FEAT_LINEBREAK
check_string_option(&wop->wo_briopt);
@@ -11241,6 +11268,7 @@ clear_winopt(winopt_T *wop UNUSED)
#ifdef FEAT_TERMINAL
clear_string_option(&wop->wo_twk);
clear_string_option(&wop->wo_tws);
+ clear_string_option(&wop->wo_tmod);
#endif
}
diff --git a/src/option.h b/src/option.h
index 90c05080bd..2985781e65 100644
--- a/src/option.h
+++ b/src/option.h
@@ -1112,6 +1112,7 @@ enum
#ifdef FEAT_TERMINAL
, WV_TWK
, WV_TWS
+ , WV_TMOD
#endif
, WV_CRBIND
#ifdef FEAT_LINEBREAK
diff --git a/src/os_win32.c b/src/os_win32.c
index 6a127a44a3..10ca41881e 100644
--- a/src/os_win32.c
+++ b/src/os_win32.c
@@ -186,8 +186,10 @@ static int win32_getattrs(char_u *name);
static int win32_setattrs(char_u *name, int attrs);
static int win32_set_archive(char_u *name);
-#ifndef FEAT_GUI_W32
static int vtp_working = 0;
+static void vtp_flag_init();
+
+#ifndef FEAT_GUI_W32
static void vtp_init();
static void vtp_exit();
static int vtp_printf(char *format, ...);
@@ -247,6 +249,7 @@ static PfnGetConsoleScreenBufferInfoEx pGetConsoleScreenBufferInfoEx;
typedef BOOL (WINAPI *PfnSetConsoleScreenBufferInfoEx)(HANDLE, PDYN_CONSOLE_SCREEN_BUFFER_INFOEX);
static PfnSetConsoleScreenBufferInfoEx pSetConsoleScreenBufferInfoEx;
static BOOL has_csbiex = FALSE;
+#endif
/*
* Get version number including build number
@@ -276,7 +279,7 @@ get_build_number(void)
return ver;
}
-
+#ifndef FEAT_GUI_W32
/*
* Version of ReadConsoleInput() that works with IME.
* Works around problems on Windows 8.
@@ -1508,9 +1511,8 @@ WaitForChar(long msec, int ignore_input)
/* Wait forever. */
dwEndTime = INFINITE;
- /* We need to loop until the end of the time period, because
- * we might get multiple unusable mouse events in that time.
- */
+ // We need to loop until the end of the time period, because
+ // we might get multiple unusable mouse events in that time.
for (;;)
{
// Only process messages when waiting.
@@ -2175,6 +2177,8 @@ mch_init(void)
#ifdef FEAT_CLIPBOARD
win_clip_init();
#endif
+
+ vtp_flag_init();
}
@@ -2675,6 +2679,7 @@ mch_init(void)
win_clip_init();
#endif
+ vtp_flag_init();
vtp_init();
}
@@ -5683,7 +5688,11 @@ mch_signal_job(job_T *job, char_u *how)
{
/* deadly signal */
if (job->jv_job_object != NULL)
+ {
+ if (job->jv_channel != NULL && job->jv_channel->ch_anonymous_pipe)
+ job->jv_channel->ch_killing = TRUE;
return TerminateJobObject(job->jv_job_object, 0) ? OK : FAIL;
+ }
return terminate_all(job->jv_proc_info.hProcess, 0) ? OK : FAIL;
}
@@ -7621,31 +7630,53 @@ mch_setenv(char *var, char *value, int x)
return 0;
}
-#ifndef FEAT_GUI_W32
-
/*
* Support for 256 colors and 24-bit colors was added in Windows 10
* version 1703 (Creators update).
*/
-# define VTP_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 15063)
+#define VTP_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 15063)
+
+/*
+ * Support for pseudo-console (ConPTY) was added in windows 10
+ * version 1809 (October 2018 update).
+ */
+#define CONPTY_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 17763)
+
+ static void
+vtp_flag_init(void)
+{
+ DWORD ver = get_build_number();
+#ifndef FEAT_GUI_W32
+ DWORD mode;
+ HANDLE out;
+
+ out = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ vtp_working = (ver >= VTP_FIRST_SUPPORT_BUILD) ? 1 : 0;
+ GetConsoleMode(out, &mode);
+ mode |= (ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
+ if (SetConsoleMode(out, mode) == 0)
+ vtp_working = 0;
+#endif
+
+#ifdef FEAT_GUI_W32
+ if (ver >= CONPTY_FIRST_SUPPORT_BUILD)
+ vtp_working = 1;
+#endif
+
+}
+
+#ifndef FEAT_GUI_W32
static void
vtp_init(void)
{
- DWORD ver, mode;
HMODULE hKerneldll;
DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi;
# ifdef FEAT_TERMGUICOLORS
COLORREF fg, bg;
# endif
- ver = get_build_number();
- vtp_working = (ver >= VTP_FIRST_SUPPORT_BUILD) ? 1 : 0;
- GetConsoleMode(g_hConOut, &mode);
- mode |= (ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
- if (SetConsoleMode(g_hConOut, mode) == 0)
- vtp_working = 0;
-
/* Use functions supported from Vista */
hKerneldll = GetModuleHandle("kernel32.dll");
if (hKerneldll != NULL)
@@ -7829,12 +7860,6 @@ control_console_color_rgb(void)
}
int
-has_vtp_working(void)
-{
- return vtp_working;
-}
-
- int
use_vtp(void)
{
return USE_VTP;
@@ -7847,3 +7872,9 @@ is_term_win32(void)
}
#endif
+
+ int
+has_vtp_working(void)
+{
+ return vtp_working;
+}
diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro
index e6914c0ba0..0fa62b7109 100644
--- a/src/proto/terminal.pro
+++ b/src/proto/terminal.pro
@@ -57,4 +57,6 @@ void f_term_wait(typval_T *argvars, typval_T *rettv);
void term_send_eof(channel_T *ch);
job_T *term_getjob(term_T *term);
int terminal_enabled(void);
+void term_free_conpty(term_T *term);
+int use_conpty(void);
/* vim: set ft=c : */
diff --git a/src/structs.h b/src/structs.h
index e6cc8291f1..5d0541bc83 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -282,6 +282,8 @@ typedef struct
# define w_p_twk w_onebuf_opt.wo_twk /* 'termwinkey' */
char_u *wo_tws;
# define w_p_tws w_onebuf_opt.wo_tws /* 'termwinsize' */
+ char_u *wo_tmod;
+# define w_p_tmod w_onebuf_opt.wo_tmod /* 'termmode' */
#endif
#ifdef FEAT_EVAL
@@ -1728,13 +1730,15 @@ struct channel_S {
int ch_keep_open; /* do not close on read error */
int ch_nonblock;
- job_T *ch_job; /* Job that uses this channel; this does not
- * count as a reference to avoid a circular
- * reference, the job refers to the channel. */
- int ch_job_killed; /* TRUE when there was a job and it was killed
- * or we know it died. */
+ job_T *ch_job; // Job that uses this channel; this does not
+ // count as a reference to avoid a circular
+ // reference, the job refers to the channel.
+ int ch_job_killed; // TRUE when there was a job and it was killed
+ // or we know it died.
+ int ch_anonymous_pipe; // ConPTY
+ int ch_killing; // TerminateJobObject() was called
- int ch_refcount; /* reference count */
+ int ch_refcount; // reference count
int ch_copyID;
};
@@ -1787,6 +1791,7 @@ struct channel_S {
#define JO2_NORESTORE 0x2000 /* "norestore" */
#define JO2_TERM_KILL 0x4000 /* "term_kill" */
#define JO2_ANSI_COLORS 0x8000 /* "ansi_colors" */
+#define JO2_TERM_MODE 0x10000 /* "term_mode" */
#define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
#define JO_CB_ALL \
@@ -1859,6 +1864,7 @@ typedef struct
# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
long_u jo_ansi_colors[16];
# endif
+ int jo_term_mode; // first character of "term_mode"
#endif
} jobopt_T;
diff --git a/src/terminal.c b/src/terminal.c
index 7605914438..41cd5c952b 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -65,6 +65,23 @@ typedef struct sb_line_S {
cellattr_T sb_fill_attr; /* for short line */
} sb_line_T;
+#ifdef WIN3264
+# ifndef HPCON
+# define HPCON VOID*
+# endif
+# ifndef EXTENDED_STARTUPINFO_PRESENT
+# define EXTENDED_STARTUPINFO_PRESENT 0x00080000
+# endif
+# ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
+# define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE 0x00020016
+# endif
+typedef struct _DYN_STARTUPINFOEXW
+{
+ STARTUPINFOW StartupInfo;
+ LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
+} DYN_STARTUPINFOEXW, *PDYN_STARTUPINFOEXW;
+#endif
+
/* typedef term_T in structs.h */
struct terminal_S {
term_T *tl_next;
@@ -92,10 +109,15 @@ struct terminal_S {
char_u *tl_opencmd;
char_u *tl_eof_chars;
+ char_u *tl_arg0_cmd; // To format the status bar
+
#ifdef WIN3264
void *tl_winpty_config;
void *tl_winpty;
+ HPCON tl_conpty;
+ DYN_STARTUPINFOEXW tl_siex; // Structure that always needs to be hold
+
FILE *tl_out_fd;
#endif
#if defined(FEAT_SESSION)
@@ -147,6 +169,11 @@ static term_T *first_term = NULL;
/* Terminal active in terminal_loop(). */
static term_T *in_terminal_loop = NULL;
+#ifdef WIN3264
+static BOOL has_winpty = FALSE;
+static BOOL has_conpty = FALSE;
+#endif
+
#define MAX_ROW 999999 /* used for tl_dirty_row_end to update all rows */
#define KEY_BUF_LEN 200
@@ -715,6 +742,16 @@ ex_terminal(exarg_T *eap)
vim_free(buf);
*p = ' ';
}
+ else if ((int)(p - cmd) == 6 && STRNICMP(cmd, "winpty", 6) == 0)
+ {
+ opt.jo_set2 |= JO2_TERM_MODE;
+ opt.jo_term_mode = 'w';
+ }
+ else if ((int)(p - cmd) == 6 && STRNICMP(cmd, "conpty", 6) == 0)
+ {
+ opt.jo_set2 |= JO2_TERM_MODE;
+ opt.jo_term_mode = 'c';
+ }
else
{
if (*p)
@@ -771,6 +808,11 @@ term_write_session(FILE *fd, win_T *wp)
if (fprintf(fd, "terminal ++curwin ++cols=%d ++rows=%d ",
term->tl_cols, term->tl_rows) < 0)
return FAIL;
+#ifdef WIN3264
+ if (*wp->w_p_tmod != NUL)
+ if (fprintf(fd, "++%s ", wp->w_p_tmod) < 0)
+ return FAIL;
+#endif
if (term->tl_command != NULL && fputs((char *)term->tl_command, fd) < 0)
return FAIL;
@@ -871,6 +913,7 @@ free_unused_terminals()
vim_free(term->tl_status_text);
vim_free(term->tl_opencmd);
vim_free(term->tl_eof_chars);
+ vim_free(term->tl_arg0_cmd);
#ifdef WIN3264
if (term->tl_out_fd != NULL)
fclose(term->tl_out_fd);
@@ -2639,10 +2682,18 @@ handle_settermprop(
{
case VTERM_PROP_TITLE:
vim_free(term->tl_title);
- /* a blank title isn't useful, make it empty, so that "running" is
- * displayed */
+ // a blank title isn't useful, make it empty, so that "running" is
+ // displayed
if (*skipwhite((char_u *)value->string) == NUL)
term->tl_title = NULL;
+ // Same as blank
+ else if (term->tl_arg0_cmd != NULL
+ && STRNCMP(term->tl_arg0_cmd, (char_u *)value->string,
+ (int)STRLEN(term->tl_arg0_cmd)) == 0)
+ term->tl_title = NULL;
+ // Empty corrupted data of winpty
+ else if (STRNCMP(" - ", (char_u *)value->string, 4) == 0)
+ term->tl_title = NULL;
#ifdef WIN3264
else if (!enc_utf8 && enc_codepage > 0)
{
@@ -5318,7 +5369,7 @@ f_term_start(typval_T *argvars, typval_T *rettv)
+ JO2_TERM_COLS + JO2_TERM_ROWS + JO2_VERTICAL + JO2_CURWIN
+ JO2_CWD + JO2_ENV + JO2_EOF_CHARS
+ JO2_NORESTORE + JO2_TERM_KILL
- + JO2_ANSI_COLORS) == FAIL)
+ + JO2_ANSI_COLORS + JO2_TERM_MODE) == FAIL)
return;
buf = term_start(&argvars[0], NULL, &opt, 0);
@@ -5426,6 +5477,327 @@ term_getjob(term_T *term)
* 2. MS-Windows implementation.
*/
+HRESULT (WINAPI *pCreatePseudoConsole)(COORD, HANDLE, HANDLE, DWORD, HPCON*);
+HRESULT (WINAPI *pResizePseudoConsole)(HPCON, COORD);
+HRESULT (WINAPI *pClosePseudoConsole)(HPCON);
+BOOL (*pInitializeProcThreadAttributeList)(LPPROC_THREAD_ATTRIBUTE_LIST, DWORD, DWORD, PSIZE_T);
+BOOL (*pUpdateProcThreadAttribute)(LPPROC_THREAD_ATTRIBUTE_LIST, DWORD, DWORD_PTR, PVOID, SIZE_T, PVOID, PSIZE_T);
+void (*pDeleteProcThreadAttributeList)(LPPROC_THREAD_ATTRIBUTE_LIST);
+
+ static int
+dyn_conpty_init(int verbose)
+{
+ static BOOL handled = FALSE;
+ static int result;
+ HMODULE hKerneldll;
+ int i;
+ static struct
+ {
+ char *name;
+ FARPROC *ptr;
+ } conpty_entry[] =
+ {
+ {"CreatePseudoConsole", (FARPROC*)&pCreatePseudoConsole},
+ {"ResizePseudoConsole", (FARPROC*)&pResizePseudoConsole},
+ {"ClosePseudoConsole", (FARPROC*)&pClosePseudoConsole},
+ {"InitializeProcThreadAttributeList",
+ (FARPROC*)&pInitializeProcThreadAttributeList},
+ {"UpdateProcThreadAttribute",
+ (FARPROC*)&pUpdateProcThreadAttribute},
+ {"DeleteProcThreadAttributeList",
+ (FARPROC*)&pDeleteProcThreadAttributeList},
+ {NULL, NULL}
+ };
+
+ if (handled)
+ return result;
+
+ if (!has_vtp_working())
+ {
+ handled = TRUE;
+ result = FAIL;
+ return FAIL;
+ }
+
+ hKerneldll = vimLoadLib("kernel32.dll");
+ for (i = 0; conpty_entry[i].name != NULL
+ && conpty_entry[i].ptr != NULL; ++i)
+ {
+ if ((*conpty_entry[i].ptr = (FARPROC)GetProcAddress(hKerneldll,
+ conpty_entry[i].name)) == NULL)
+ {
+ if (verbose)
+ semsg(_(e_loadfunc), conpty_entry[i].name);
+ return FAIL;
+ }
+ }
+
+ handled = TRUE;
+ result = OK;
+ return OK;
+}
+
+ static int
+conpty_term_and_job_init(
+ term_T *term,
+ typval_T *argvar,
+ char **argv,
+ jobopt_T *opt,
+ jobopt_T *orig_opt)
+{
+ WCHAR *cmd_wchar = NULL;
+ WCHAR *cmd_wchar_copy = NULL;
+ WCHAR *cwd_wchar = NULL;
+ WCHAR *env_wchar = NULL;
+ channel_T *channel = NULL;
+ job_T *job = NULL;
+ HANDLE jo = NULL;
+ garray_T ga_cmd, ga_env;
+ char_u *cmd = NULL;
+ HRESULT hr;
+ COORD consize;
+ SIZE_T breq;
+ PROCESS_INFORMATION proc_info;
+ HANDLE i_theirs = NULL;
+ HANDLE o_theirs = NULL;
+ HANDLE i_ours = NULL;
+ HANDLE o_ours = NULL;
+
+ ga_init2(&ga_cmd, (int)sizeof(char*), 20);
+ ga_init2(&ga_env, (int)sizeof(char*), 20);
+
+ if (argvar->v_type == VAR_STRING)
+ {
+ cmd = argvar->vval.v_string;
+ }
+ else if (argvar->v_type == VAR_LIST)
+ {
+ if (win32_build_cmd(argvar->vval.v_list, &ga_cmd) == FAIL)
+ goto failed;
+ cmd = ga_cmd.ga_data;
+ }
+ if (cmd == NULL || *cmd == NUL)
+ {
+ emsg(_(e_invarg));
+ goto failed;
+ }
+
+ term->tl_arg0_cmd = vim_strsave(cmd);
+
+ cmd_wchar = enc_to_utf16(cmd, NULL);
+
+ if (cmd_wchar != NULL)
+ {
+ /* Request by CreateProcessW */
+ breq = wcslen(cmd_wchar) + 1 + 1; /* Addition of NUL by API */
+ cmd_wchar_copy = (PWSTR)alloc((int)(breq * sizeof(WCHAR)));
+ wcsncpy(cmd_wchar_copy, cmd_wchar, breq - 1);
+ }
+
+ ga_clear(&ga_cmd);
+ if (cmd_wchar == NULL)
+ goto failed;
+ if (opt->jo_cwd != NULL)
+ cwd_wchar = enc_to_utf16(opt->jo_cwd, NULL);
+
+ win32_build_env(opt->jo_env, &ga_env, TRUE);
+ env_wchar = ga_env.ga_data;
+
+ if (!CreatePipe(&i_theirs, &i_ours, NULL, 0))
+ goto failed;
+ if (!CreatePipe(&o_ours, &o_theirs, NULL, 0))
+ goto failed;
+
+ consize.X = term->tl_cols;
+ consize.Y = term->tl_rows;
+ hr = pCreatePseudoConsole(consize, i_theirs, o_theirs, 0,
+ &term->tl_conpty);
+ if (FAILED(hr))
+ goto failed;
+
+ term->tl_siex.StartupInfo.cb = sizeof(term->tl_siex);
+
+ /* Set up pipe inheritance safely: Vista or later. */
+ pInitializeProcThreadAttributeList(NULL, 1, 0, &breq);
+ term->tl_siex.lpAttributeList =
+ (PPROC_THREAD_ATTRIBUTE_LIST)alloc((int)breq);
+ if (!term->tl_siex.lpAttributeList)
+ goto failed;
+ if (!pInitializeProcThreadAttributeList(term->tl_siex.lpAttributeList, 1,
+ 0, &breq))
+ goto failed;
+ if (!pUpdateProcThreadAttribute(
+ term->tl_siex.lpAttributeList, 0,
+ PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, term->tl_conpty,
+ sizeof(HPCON), NULL, NULL))
+ goto failed;
+
+ channel = add_channel();
+ if (channel == NULL)
+ goto failed;
+
+ job = job_alloc();
+ if (job == NULL)
+ goto failed;
+ if (argvar->v_type == VAR_STRING)
+ {
+ int argc;
+
+ build_argv_from_string(cmd, &job->jv_argv, &argc);
+ }
+ else
+ {
+ int argc;
+
+ build_argv_from_list(argvar->vval.v_list, &job->jv_argv, &argc);
+ }
+
+ if (opt->jo_set & JO_IN_BUF)
+ job->jv_in_buf = buflist_findnr(opt->jo_io_buf[PART_IN]);
+
+ if (!CreateProcessW(NULL, cmd_wchar_copy, NULL, NULL, FALSE,
+ EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT
+ | CREATE_SUSPENDED | CREATE_NEW_PROCESS_GROUP
+ | CREATE_DEFAULT_ERROR_MODE,
+ env_wchar, cwd_wchar,
+ &term->tl_siex.StartupInfo, &proc_info))
+ goto failed;
+
+ CloseHandle(i_theirs);
+ CloseHandle(o_theirs);
+
+ channel_set_pipes(channel,
+ (sock_T)i_ours,
+ (sock_T)o_ours,
+ (sock_T)o_ours);
+
+ /* Write lines with CR instead of NL. */
+ channel->ch_write_text_mode = TRUE;
+
+ /* Use to explicitly delete anonymous pipe handle. */
+ channel->ch_anonymous_pipe = TRUE;
+
+ jo = CreateJobObject(NULL, NULL);
+ if (jo == NULL)
+ goto failed;
+
+ if (!AssignProcessToJobObject(jo, proc_info.hProcess))
+ {
+ /* Failed, switch the way to terminate process with TerminateProcess. */
+ CloseHandle(jo);
+ jo = NULL;
+ }
+
+ ResumeThread(proc_info.hThread);
+ CloseHandle(proc_info.hThread);
+
+ vim_free(cmd_wchar);
+ vim_free(cmd_wchar_copy);
+ vim_free(cwd_wchar);
+ vim_free(env_wchar);
+
+ if (create_vterm(term, term->tl_rows, term->tl_cols) == FAIL)
+ goto failed;
+
+#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
+ if (opt->jo_set2 & JO2_ANSI_COLORS)
+ set_vterm_palette(term->tl_vterm, opt->jo_ansi_colors);
+ else
+ init_vterm_ansi_colors(term->tl_vterm);
+#endif
+
+ channel_set_job(channel, job, opt);
+ job_set_options(job, opt);
+
+ job->jv_