Skip to content

Instantly share code, notes, and snippets.

@k-takata
Last active October 29, 2018 03:32
Show Gist options
  • Save k-takata/d743783c86fc80509b0b0b1d56582c5b to your computer and use it in GitHub Desktop.
Save k-takata/d743783c86fc80509b0b0b1d56582c5b to your computer and use it in GitHub Desktop.
Import the ConPTY patch for Vim at vim/vim#3474 by @ntak
From ae8f04efd02e0521c08b2abd9b32c3525a863121 Mon Sep 17 00:00:00 2001
From: "K.Takata" <[email protected]>
Date: Mon, 29 Oct 2018 12:08:17 +0900
Subject: [PATCH] Import PR#3474 by @ntak
---
src/channel.c | 39 +++-
src/evalfunc.c | 4 +
src/option.c | 7 +-
src/os_mswin.c | 2 +
src/os_win32.c | 159 ++++++++++++--
src/proto/os_win32.pro | 1 +
src/proto/terminal.pro | 2 +
src/structs.h | 2 +
src/terminal.c | 381 +++++++++++++++++++++++++++++++++-
src/testdir/test_autocmd.vim | 8 +-
src/testdir/test_terminal.vim | 24 ++-
11 files changed, 590 insertions(+), 39 deletions(-)
diff --git a/src/channel.c b/src/channel.c
index 0ca6e5495..5e03a939e 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -1677,10 +1677,6 @@ channel_get_all(channel_T *channel, ch_part_T part)
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);
-
/* Concatenate everything into one buffer. */
for (node = head->rq_next; node != NULL; node = node->rq_next)
len += node->rq_buflen;
@@ -1703,11 +1699,27 @@ channel_get_all(channel_T *channel, ch_part_T part)
} while (p != NULL);
/* turn all NUL into NL */
- while (len > 0)
+ p = res;
+ while (p < res + len)
{
- --len;
- if (res[len] == NUL)
- res[len] = NL;
+ /* Crush the escape sequence OSC 0/1/2 */
+ if (*p == 0x1b)
+ {
+ if ((res + len) - p > 3
+ && 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 */
+ p[-1] = 0x07;
+ }
+ }
+ else if (*p == NUL)
+ *p = NL;
+ ++p;
}
return res;
@@ -4252,7 +4264,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 */
@@ -5325,6 +5337,15 @@ job_cleanup(job_T *job)
* not use "job" after this! */
job_free(job);
}
+ else if (job->jv_channel->ch_anonymous_pipe && !job->jv_channel->ch_killing)
+ {
+ /* Explicitly delete anonymous pipe handle. */
+ ++safe_to_invoke_callback;
+ channel_free_contents(job->jv_channel);
+ job->jv_channel->ch_job = NULL;
+ job->jv_channel = NULL;
+ --safe_to_invoke_callback;
+ }
}
/*
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 82ea05af3..216c9d494 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -6642,6 +6642,10 @@ f_has(typval_T *argvars, typval_T *rettv)
#if defined(FEAT_TERMINAL) && defined(WIN3264)
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
}
diff --git a/src/option.c b/src/option.c
index f238abe84..029a63c81 100644
--- a/src/option.c
+++ b/src/option.c
@@ -3738,7 +3738,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)
{
diff --git a/src/os_mswin.c b/src/os_mswin.c
index 9767a5b07..046c18c1b 100644
--- a/src/os_mswin.c
+++ b/src/os_mswin.c
@@ -228,6 +228,8 @@ mch_exit(int r)
free_all_mem();
#endif
+ vtp_flag_exit();
+
exit(r);
}
diff --git a/src/os_win32.c b/src/os_win32.c
index 6d6faa9ab..5cf17515c 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.
@@ -2188,6 +2191,9 @@ mch_init(void)
#ifdef FEAT_CLIPBOARD
win_clip_init();
#endif
+
+ vtp_flag_init();
+
}
@@ -2688,6 +2694,7 @@ mch_init(void)
win_clip_init();
#endif
+ vtp_flag_init();
vtp_init();
}
@@ -5719,7 +5726,11 @@ mch_signal_job(job_T *job, char_u *how)
{
/* deadly signal */
if (job->jv_job_object != NULL)
+ {
+ if (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;
}
@@ -7689,31 +7700,141 @@ 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)
+
+#ifdef FEAT_GUI_W32
+static HWINSTA origsta;
+static HWINSTA newsta;
+static HDESK origdesk;
+static HDESK newdesk;
+static PROCESS_INFORMATION pista;
+#endif
+
+ static void
+vtp_flag_init(void)
+{
+ DWORD ver = get_build_number();
+ DWORD mode;
+ HANDLE out;
+
+#ifdef FEAT_GUI_W32
+ char name[256];
+ char startup[256];
+ char_u *cmd;
+ char cmdline[256];
+ SECURITY_ATTRIBUTES sa;
+ STARTUPINFO si;
+
+ if (ver >= CONPTY_FIRST_SUPPORT_BUILD)
+ {
+ /* A console opened on another window station, get its standard
+ * output. */
+
+ vim_memset(&sa, 0, sizeof(sa));
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = TRUE;
+
+ origdesk = GetThreadDesktop(GetCurrentThreadId());
+ origsta = GetProcessWindowStation();
+
+ newsta = CreateWindowStation(NULL, 0, WINSTA_ALL_ACCESS, &sa);
+
+ if (SetProcessWindowStation(newsta))
+ {
+ newdesk = CreateDesktop("Default", NULL, NULL, 0, GENERIC_ALL,
+ &sa);
+ SetThreadDesktop(newdesk);
+ name[0] = NUL;
+ GetUserObjectInformation(newsta, UOI_NAME, name, sizeof(name),
+ NULL);
+ sprintf(startup, "%s\\Default", name);
+
+ vim_memset(&si, 0, sizeof(si));
+ si.cb = sizeof(STARTUPINFO);
+ si.lpDesktop = startup;
+ si.dwFlags = STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_SHOWNOACTIVATE;
+
+ vim_memset(&pista, 0, sizeof(pista));
+
+ cmd = mch_getenv("COMSPEC");
+ if (cmd == NULL || *cmd == NUL)
+ cmd = (char_u *)default_shell();
+ sprintf(cmdline, "%s /k", cmd);
+
+ CreateProcess((char *)cmd, cmdline, NULL, NULL, TRUE,
+ CREATE_NEW_CONSOLE, NULL, NULL, &si, &pista);
+ Sleep(100);
+ AttachConsole(pista.dwProcessId);
+
+ freopen("CONOUT$", "w", stdout);
+ SetStdHandle(STD_OUTPUT_HANDLE, CreateFile("CONOUT$",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &sa, OPEN_EXISTING, 0, NULL));
+ }
+ }
+#endif
+ 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;
+
+#ifdef FEAT_GUI_W32
+ if (ver >= CONPTY_FIRST_SUPPORT_BUILD)
+ {
+ SetThreadDesktop(origdesk);
+ SetProcessWindowStation(origsta);
+ }
+#endif
+}
+
+ void
+vtp_flag_exit(void)
+{
+#ifdef FEAT_GUI_W32
+ DWORD ver;
+
+ ver = get_build_number();
+ if (ver >= CONPTY_FIRST_SUPPORT_BUILD)
+ {
+ SetProcessWindowStation(newsta);
+ SetThreadDesktop(newdesk);
+ TerminateProcess(pista.hProcess, 0);
+ TerminateThread(pista.hThread, 0);
+ FreeConsole();
+ SetThreadDesktop(origdesk);
+ SetProcessWindowStation(origsta);
+ CloseWindowStation(newsta);
+ CloseDesktop(newdesk);
+ }
+#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)
@@ -7896,12 +8017,6 @@ control_console_color_rgb(void)
reset_console_color_rgb();
}
- int
-has_vtp_working(void)
-{
- return vtp_working;
-}
-
int
use_vtp(void)
{
@@ -7915,3 +8030,9 @@ is_term_win32(void)
}
#endif
+
+ int
+has_vtp_working(void)
+{
+ return vtp_working;
+}
diff --git a/src/proto/os_win32.pro b/src/proto/os_win32.pro
index 7f45c5cf1..2354036e1 100644
--- a/src/proto/os_win32.pro
+++ b/src/proto/os_win32.pro
@@ -73,4 +73,5 @@ void control_console_color_rgb(void);
int has_vtp_working(void);
int use_vtp(void);
int is_term_win32(void);
+void vtp_flag_exit(void);
/* vim: set ft=c : */
diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro
index 1031876c4..bebdcc991 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 a0c06b5f2..2848b8782 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1679,6 +1679,8 @@ struct channel_S {
* 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_killing;
+ int ch_anonymous_pipe;
int ch_refcount; /* reference count */
int ch_copyID;
diff --git a/src/terminal.c b/src/terminal.c
index 6927d6902..c42a40af0 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
@@ -846,6 +873,7 @@ free_terminal(buf_T *buf)
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);
@@ -2629,6 +2657,14 @@ handle_settermprop(
* 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)
{
@@ -5385,6 +5421,330 @@ term_getjob(term_T *term)
* 2. MS-Windows implementation.
*/
+typedef HRESULT (WINAPI *PfnCreatePseudoConsole)(COORD, HANDLE, HANDLE, DWORD, HPCON*);
+static PfnCreatePseudoConsole pCreatePseudoConsole;
+typedef HRESULT (WINAPI *PfnResizePseudoConsole)(HPCON, COORD);
+static PfnResizePseudoConsole pResizePseudoConsole;
+typedef HRESULT (WINAPI *PfnClosePseudoConsole)(HPCON);
+static PfnClosePseudoConsole pClosePseudoConsole;
+typedef BOOL (*PfnInitializeProcThreadAttributeList)(LPPROC_THREAD_ATTRIBUTE_LIST, DWORD, DWORD, PSIZE_T);
+static PfnInitializeProcThreadAttributeList pInitializeProcThreadAttributeList;
+typedef BOOL (*PfnUpdateProcThreadAttribute)(LPPROC_THREAD_ATTRIBUTE_LIST, DWORD, DWORD_PTR, PVOID, SIZE_T, PVOID, PSIZE_T);
+static PfnUpdateProcThreadAttribute pUpdateProcThreadAttribute;
+typedef void (*PfnDeleteProcThreadAttributeList)(LPPROC_THREAD_ATTRIBUTE_LIST);
+static PfnDeleteProcThreadAttributeList pDeleteProcThreadAttributeList;
+
+ static int
+dyn_conpty_init(void)
+{
+ static BOOL handled = FALSE;
+ static int result;
+ HMODULE hKerneldll;
+
+ if (handled)
+ return result;
+
+ if (!has_vtp_working())
+ {
+ handled = TRUE;
+ result = FAIL;
+ return FAIL;
+ }
+
+ hKerneldll = GetModuleHandle("kernel32.dll");
+ if (hKerneldll != NULL)
+ {
+ pCreatePseudoConsole =
+ (PfnCreatePseudoConsole)GetProcAddress(
+ hKerneldll, "CreatePseudoConsole");
+ pResizePseudoConsole =
+ (PfnResizePseudoConsole)GetProcAddress(
+ hKerneldll, "ResizePseudoConsole");
+ pClosePseudoConsole =
+ (PfnClosePseudoConsole)GetProcAddress(
+ hKerneldll, "ClosePseudoConsole");
+ pInitializeProcThreadAttributeList =
+ (PfnInitializeProcThreadAttributeList)GetProcAddress(
+ hKerneldll, "InitializeProcThreadAttributeList");
+ pUpdateProcThreadAttribute =
+ (PfnUpdateProcThreadAttribute)GetProcAddress(
+ hKerneldll, "UpdateProcThreadAttribute");
+ pDeleteProcThreadAttributeList =
+ (PfnDeleteProcThreadAttributeList)GetProcAddress(
+ hKerneldll, "DeleteProcThreadAttributeList");
+ if (pCreatePseudoConsole != NULL
+ && pResizePseudoConsole != NULL
+ && pClosePseudoConsole != NULL
+ && pInitializeProcThreadAttributeList != NULL
+ && pUpdateProcThreadAttribute != NULL
+ && pDeleteProcThreadAttributeList != NULL)
+ {
+ handled = TRUE;
+ result = OK;
+ return OK;
+ }
+ }
+ return FAIL;
+}
+
+ 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);
+
+ create_vterm(term, term->tl_rows, term->tl_cols);
+
+#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_channel = channel;
+ job->jv_proc_info = proc_info;
+ job->jv_job_object = jo;
+ job->jv_status = JOB_STARTED;
+ ++job->jv_refcount;
+ term->tl_job = job;
+
+ /* Redirecting stdout and stderr doesn't work at the job level. Instead
+ * open the file here and handle it in. opt->jo_io was changed in
+ * setup_job_options(), use the original flags here. */
+ if (orig_opt->jo_io[PART_OUT] == JIO_FILE)
+ {
+ char_u *fname = opt->jo_io_name[PART_OUT];
+
+ ch_log(channel, "Opening output file %s", fname);
+ term->tl_out_fd = mch_fopen((char *)fname, WRITEBIN);
+ if (term->tl_out_fd == NULL)
+ EMSG2(_(e_notopen), fname);
+ }
+
+ return OK;
+
+failed:
+ ga_clear(&ga_cmd);
+ ga_clear(&ga_env);
+ vim_free(cmd_wchar);
+ vim_free(cmd_wchar_copy);
+ vim_free(cwd_wchar);
+ if (channel != NULL)
+ channel_clear(channel);
+ if (job != NULL)
+ {
+ job->jv_channel = NULL;
+ job_cleanup(job);
+ }
+ term->tl_job = NULL;
+ if (jo != NULL)
+ CloseHandle(jo);
+
+ if (term->tl_siex.lpAttributeList != NULL)
+ {
+ pDeleteProcThreadAttributeList(term->tl_siex.lpAttributeList);
+ vim_free(term->tl_siex.lpAttributeList);
+ }
+ if (o_theirs != NULL)
+ CloseHandle(o_theirs);
+ if (o_ours != NULL)
+ CloseHandle(o_ours);
+ if (i_ours != NULL)
+ CloseHandle(i_ours);
+ if (i_theirs != NULL)
+ CloseHandle(i_theirs);
+ if (term->tl_conpty != NULL)
+ pClosePseudoConsole(term->tl_conpty);
+ return FAIL;
+}
+
+ static void
+conpty_term_report_winsize(term_T *term, int rows, int cols)
+{
+ COORD consize;
+
+ consize.X = cols;
+ consize.Y = rows;
+ pResizePseudoConsole(term->tl_conpty, consize);
+}
+
+ void
+term_free_conpty(term_T *term)
+{
+ if (term->tl_siex.lpAttributeList != NULL)
+ {
+ pDeleteProcThreadAttributeList(term->tl_siex.lpAttributeList);
+ vim_free(term->tl_siex.lpAttributeList);
+ }
+ term->tl_siex.lpAttributeList = NULL;
+ if (term->tl_conpty != NULL)
+ pClosePseudoConsole(term->tl_conpty);
+ term->tl_conpty = NULL;
+}
+
+ int
+use_conpty(void)
+{
+ return has_conpty;
+}
+
# ifndef PROTO
#define WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN 1ul
@@ -5502,8 +5862,14 @@ term_and_job_init(
garray_T ga_cmd, ga_env;
char_u *cmd = NULL;
- if (dyn_winpty_init(TRUE) == FAIL)
- return FAIL;
+ has_conpty = dyn_conpty_init() != FAIL ? TRUE : FALSE;
+ if (has_conpty)
+ return conpty_term_and_job_init(term, argvar, argv, opt, orig_opt);
+
+ has_winpty = dyn_winpty_init(FALSE) != FAIL ? TRUE : FALSE;
+ if (!has_winpty && !has_conpty)
+ return dyn_winpty_init(TRUE);
+
ga_init2(&ga_cmd, (int)sizeof(char*), 20);
ga_init2(&ga_env, (int)sizeof(char*), 20);
@@ -5523,6 +5889,8 @@ term_and_job_init(
goto failed;
}
+ term->tl_arg0_cmd = vim_strsave(cmd);
+
cmd_wchar = enc_to_utf16(cmd, NULL);
ga_clear(&ga_cmd);
if (cmd_wchar == NULL)
@@ -5761,6 +6129,7 @@ failed:
static void
term_free_vterm(term_T *term)
{
+ term_free_conpty(term);
if (term->tl_winpty != NULL)
winpty_free(term->tl_winpty);
term->tl_winpty = NULL;
@@ -5778,14 +6147,16 @@ term_free_vterm(term_T *term)
static void
term_report_winsize(term_T *term, int rows, int cols)
{
- if (term->tl_winpty)
+ if (use_conpty())
+ conpty_term_report_winsize(term, rows, cols);
+ else if (term->tl_winpty)
winpty_set_size(term->tl_winpty, cols, rows, NULL);
}
int
terminal_enabled(void)
{
- return dyn_winpty_init(FALSE) == OK;
+ return dyn_winpty_init(FALSE) == OK || dyn_conpty_init() == OK;
}
# else
@@ -5809,6 +6180,8 @@ term_and_job_init(
jobopt_T *opt,
jobopt_T *orig_opt UNUSED)
{
+ term->tl_arg0_cmd = NULL;
+
create_vterm(term, term->tl_rows, term->tl_cols);
#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index 3d650e473..dbf1949da 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -1345,7 +1345,13 @@ func Test_Changed_FirstTime()
let buf = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile'], {'term_rows': 3})
call assert_equal('running', term_getstatus(buf))
" Wait for the ruler (in the status line) to be shown.
- call WaitForAssert({-> assert_match('\<All$', term_getline(buf, 3))})
+ " In ConPTY, there is additional character which is drawn up to the width of
+ " the screen.
+ if has('conpty')
+ call WaitForAssert({-> assert_match('\<All.*$', term_getline(buf, 3))})
+ else
+ call WaitForAssert({-> assert_match('\<All$', term_getline(buf, 3))})
+ endif
" It's only adding autocmd, so that no event occurs.
call term_sendkeys(buf, ":au! TextChanged <buffer> call writefile(['No'], 'Xchanged.txt')\<cr>")
call term_sendkeys(buf, "\<C-\\>\<C-N>:qa!\<cr>")
diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim
index 235cd3160..2b29aeaae 100644
--- a/src/testdir/test_terminal.vim
+++ b/src/testdir/test_terminal.vim
@@ -39,8 +39,11 @@ func Test_terminal_basic()
call assert_match('^/dev/', job_info(g:job).tty_out)
call assert_match('^/dev/', term_gettty(''))
else
- call assert_match('^\\\\.\\pipe\\', job_info(g:job).tty_out)
- call assert_match('^\\\\.\\pipe\\', term_gettty(''))
+ " ConPTY works on anonymous pipe.
+ if !has('conpty')
+ call assert_match('^\\\\.\\pipe\\', job_info(g:job).tty_out)
+ call assert_match('^\\\\.\\pipe\\', term_gettty(''))
+ endif
endif
call assert_equal('t', mode())
call assert_equal('yes', b:done)
@@ -129,7 +132,12 @@ endfunc
func Get_cat_123_cmd()
if has('win32')
- return 'cmd /c "cls && color 2 && echo 123"'
+ if !has('conpty')
+ return 'cmd /c "cls && color 2 && echo 123"'
+ else
+ " When clearing twice, extra sequence is not output.
+ return 'cmd /c "cls && cls && color 2 && echo 123"'
+ endif
else
call writefile(["\<Esc>[32m123"], 'Xtext')
return "cat Xtext"
@@ -557,6 +565,9 @@ func Test_terminal_noblock()
" The shell or something else has a problem dealing with more than 1000
" characters at the same time.
let len = 1000
+ " NPFS is used in Windows, nonblocking mode does not work properly.
+ elseif has('win32')
+ let len = 1
else
let len = 5000
endif
@@ -689,8 +700,11 @@ func Test_terminal_redir_file()
let cmd = Get_cat_123_cmd()
let buf = term_start(cmd, {'out_io': 'file', 'out_name': 'Xfile'})
call term_wait(buf)
- call WaitForAssert({-> assert_notequal(0, len(readfile("Xfile")))})
- call assert_match('123', readfile('Xfile')[0])
+ " ConPTY may precede escape sequence. There are things that are not so.
+ if !has('conpty')
+ call WaitForAssert({-> assert_notequal(0, len(readfile("Xfile")))})
+ call assert_match('123', readfile('Xfile')[0])
+ endif
let g:job = term_getjob(buf)
call WaitForAssert({-> assert_equal("dead", job_status(g:job))})
call delete('Xfile')
--
2.17.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment