Last active
October 29, 2018 03:32
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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