patch 8.1.0870: Vim doesn't use the new ConPTY support in Windows 10

Problem:    Vim doesn't use the new ConPTY support in Windows 10.
Solution:   Use ConPTY support, if available. (Nobuhiro Takasaki, closes #3794)
diff --git a/src/channel.c b/src/channel.c
index 20cf462..484d013 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -1720,11 +1720,7 @@
     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 @@
     }
     *p = NUL;
 
-    /* Free all buffers */
+    // Free all buffers
     do
     {
 	p = channel_get(channel, part, NULL);
@@ -1747,16 +1743,37 @@
 
     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 = 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 @@
 		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 @@
 	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 374e701..fa7ed9b 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -6738,6 +6738,10 @@
 	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 0562610..6cc3be2 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -1432,7 +1432,8 @@
 	|| 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 6d2bab1..77d1024 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 @@
 			    {(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 @@
 #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 @@
 	    {
 		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 @@
 #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 @@
 		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 @@
 	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 @@
 	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 @@
 #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 @@
 #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 @@
 #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 90c0508..2985781 100644
--- a/src/option.h
+++ b/src/option.h
@@ -1112,6 +1112,7 @@
 #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 6a127a4..10ca418 100644
--- a/src/os_win32.c
+++ b/src/os_win32.c
@@ -186,8 +186,10 @@
 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 @@
 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 @@
     return ver;
 }
 
-
+#ifndef FEAT_GUI_W32
 /*
  * Version of ReadConsoleInput() that works with IME.
  * Works around problems on Windows 8.
@@ -1508,9 +1511,8 @@
 	/* 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 @@
 #ifdef FEAT_CLIPBOARD
     win_clip_init();
 #endif
+
+    vtp_flag_init();
 }
 
 
@@ -2675,6 +2679,7 @@
     win_clip_init();
 #endif
 
+    vtp_flag_init();
     vtp_init();
 }
 
@@ -5683,7 +5688,11 @@
     {
 	/* 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 @@
     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 @@
 }
 
     int
-has_vtp_working(void)
-{
-    return vtp_working;
-}
-
-    int
 use_vtp(void)
 {
     return USE_VTP;
@@ -7847,3 +7872,9 @@
 }
 
 #endif
+
+    int
+has_vtp_working(void)
+{
+    return vtp_working;
+}
diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro
index e6914c0..0fa62b7 100644
--- a/src/proto/terminal.pro
+++ b/src/proto/terminal.pro
@@ -57,4 +57,6 @@
 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 e6cc829..5d0541b 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -282,6 +282,8 @@
 # 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 @@
     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 @@
 #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 @@
 # 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 7605914..41cd5c9 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -65,6 +65,23 @@
     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 @@
     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 @@
 /* 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 @@
 	    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 @@
     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 @@
 	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 @@
     {
 	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 @@
 		    + 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 @@
  * 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_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)
+	    semsg(_(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);
+    }
+    term->tl_siex.lpAttributeList = NULL;
+    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);
+    term->tl_conpty = NULL;
+    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
@@ -5516,16 +5888,11 @@
     return OK;
 }
 
-/*
- * Create a new terminal of "rows" by "cols" cells.
- * Store a reference in "term".
- * Return OK or FAIL.
- */
     static int
-term_and_job_init(
+winpty_term_and_job_init(
 	term_T	    *term,
 	typval_T    *argvar,
-	char	    **argv UNUSED,
+	char	    **argv,
 	jobopt_T    *opt,
 	jobopt_T    *orig_opt)
 {
@@ -5543,8 +5910,6 @@
     garray_T	    ga_cmd, ga_env;
     char_u	    *cmd = NULL;
 
-    if (dyn_winpty_init(TRUE) == FAIL)
-	return FAIL;
     ga_init2(&ga_cmd, (int)sizeof(char*), 20);
     ga_init2(&ga_env, (int)sizeof(char*), 20);
 
@@ -5564,6 +5929,8 @@
 	goto failed;
     }
 
+    term->tl_arg0_cmd = vim_strsave(cmd);
+
     cmd_wchar = enc_to_utf16(cmd, NULL);
     ga_clear(&ga_cmd);
     if (cmd_wchar == NULL)
@@ -5676,9 +6043,9 @@
     job->jv_job_object = jo;
     job->jv_status = JOB_STARTED;
     job->jv_tty_in = utf16_to_enc(
-	    (short_u*)winpty_conin_name(term->tl_winpty), NULL);
+	    (short_u *)winpty_conin_name(term->tl_winpty), NULL);
     job->jv_tty_out = utf16_to_enc(
-	    (short_u*)winpty_conout_name(term->tl_winpty), NULL);
+	    (short_u *)winpty_conout_name(term->tl_winpty), NULL);
     ++job->jv_refcount;
     term->tl_job = job;
 
@@ -5722,7 +6089,7 @@
     term->tl_winpty_config = NULL;
     if (winpty_err != NULL)
     {
-	char_u *msg = utf16_to_enc(
+	char *msg = (char *)utf16_to_enc(
 				(short_u *)winpty_error_msg(winpty_err), NULL);
 
 	emsg(msg);
@@ -5731,6 +6098,76 @@
     return FAIL;
 }
 
+/*
+ * Create a new terminal of "rows" by "cols" cells.
+ * Store a reference in "term".
+ * Return OK or FAIL.
+ */
+    static int
+term_and_job_init(
+	term_T	    *term,
+	typval_T    *argvar,
+	char	    **argv UNUSED,
+	jobopt_T    *opt,
+	jobopt_T    *orig_opt)
+{
+    int		    use_winpty = FALSE;
+    int		    use_conpty = FALSE;
+
+    has_winpty = dyn_winpty_init(FALSE) != FAIL ? TRUE : FALSE;
+    has_conpty = dyn_conpty_init(FALSE) != FAIL ? TRUE : FALSE;
+
+    if (!has_winpty && !has_conpty)
+	// If neither is available give the errors for winpty, since when
+	// conpty is not available it can't be installed either.
+	return dyn_winpty_init(TRUE);
+
+    if (opt->jo_term_mode == 'w')
+	set_string_option_direct((char_u *)"tmod", -1, (char_u *)"winpty",
+							OPT_FREE|OPT_LOCAL, 0);
+    if (opt->jo_term_mode == 'c')
+	set_string_option_direct((char_u *)"tmod", -1, (char_u *)"conpty",
+							OPT_FREE|OPT_LOCAL, 0);
+
+    if (curwin->w_p_tmod == NULL || *curwin->w_p_tmod == NUL)
+    {
+	if (has_conpty)
+	    use_conpty = TRUE;
+	else if (has_winpty)
+	    use_winpty = TRUE;
+	// else: error
+    }
+    else if (STRICMP(curwin->w_p_tmod, "winpty") == 0)
+    {
+	if (has_winpty)
+	    use_winpty = TRUE;
+    }
+    else if (STRICMP(curwin->w_p_tmod, "conpty") == 0)
+    {
+	if (has_conpty)
+	    use_conpty = TRUE;
+	else
+	    return dyn_conpty_init(TRUE);
+    }
+
+    if (use_conpty)
+    {
+	set_string_option_direct((char_u *)"tmod", -1, (char_u *)"conpty",
+							OPT_FREE|OPT_LOCAL, 0);
+	return conpty_term_and_job_init(term, argvar, argv, opt, orig_opt);
+    }
+
+    if (use_winpty)
+    {
+	set_string_option_direct((char_u *)"tmod", -1, (char_u *)"winpty",
+							OPT_FREE|OPT_LOCAL, 0);
+	return winpty_term_and_job_init(term, argvar, argv, opt, orig_opt);
+    }
+
+    // error
+    return dyn_winpty_init(TRUE);
+}
+
     static int
 create_pty_only(term_T *term, jobopt_T *options)
 {
@@ -5804,6 +6241,7 @@
     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;
@@ -5821,6 +6259,8 @@
     static void
 term_report_winsize(term_T *term, int rows, int cols)
 {
+    if (term->tl_conpty)
+	conpty_term_report_winsize(term, rows, cols);
     if (term->tl_winpty)
 	winpty_set_size(term->tl_winpty, cols, rows, NULL);
 }
@@ -5828,7 +6268,7 @@
     int
 terminal_enabled(void)
 {
-    return dyn_winpty_init(FALSE) == OK;
+    return dyn_winpty_init(FALSE) == OK || dyn_conpty_init(FALSE) == OK;
 }
 
 # else
@@ -5852,6 +6292,8 @@
 	jobopt_T    *opt,
 	jobopt_T    *orig_opt UNUSED)
 {
+    term->tl_arg0_cmd = NULL;
+
     if (create_vterm(term, term->tl_rows, term->tl_cols) == FAIL)
 	return FAIL;
 
diff --git a/src/testdir/gen_opt_test.vim b/src/testdir/gen_opt_test.vim
index c0e89cf..8c75d37 100644
--- a/src/testdir/gen_opt_test.vim
+++ b/src/testdir/gen_opt_test.vim
@@ -131,6 +131,7 @@
       \ 'term': [[], []],
       \ 'termguicolors': [[], []],
       \ 'termencoding': [has('gui_gtk') ? [] : ['', 'utf-8'], ['xxx']],
+      \ 'termmode': [['', 'winpty', 'conpty'], ['xxx']],
       \ 'termwinsize': [['', '24x80', '0x80', '32x0', '0x0'], ['xxx', '80', '8ax9', '24x80b']],
       \ 'toolbar': [['', 'icons', 'text'], ['xxx']],
       \ 'toolbariconsize': [['', 'tiny', 'huge'], ['xxx']],
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index 9e8a02c..71df515 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -1397,7 +1397,13 @@
   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_mksession.vim b/src/testdir/test_mksession.vim
index 3038422..456cd1e 100644
--- a/src/testdir/test_mksession.vim
+++ b/src/testdir/test_mksession.vim
@@ -295,7 +295,7 @@
       call assert_report('unexpected shell line: ' . line)
     endif
   endfor
-  call assert_match('terminal ++curwin ++cols=\d\+ ++rows=\d\+\s*$', term_cmd)
+  call assert_match('terminal ++curwin ++cols=\d\+ ++rows=\d\+\s*.*$', term_cmd)
 
   call Stop_shell_in_terminal(bufnr('%'))
   call delete('Xtest_mks.out')
@@ -375,7 +375,7 @@
       let term_cmd = line
     endif
   endfor
-  call assert_match('terminal ++curwin ++cols=\d\+ ++rows=\d\+ other', term_cmd)
+  call assert_match('terminal ++curwin ++cols=\d\+ ++rows=\d\+.*other', term_cmd)
 
   call Stop_shell_in_terminal(bufnr('%'))
   call delete('Xtest_mks.out')
diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim
index 698da04..8eb43a0 100644
--- a/src/testdir/test_terminal.vim
+++ b/src/testdir/test_terminal.vim
@@ -39,8 +39,11 @@
     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 @@
 
 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"
@@ -143,8 +151,8 @@
 
   call WaitForAssert({-> assert_equal("dead", job_status(g:job))})
   call WaitForAssert({-> assert_equal(0, g:buf)})
-  unlet g:buf
   unlet g:job
+  unlet g:buf
   call delete('Xtext')
 endfunc
 
@@ -563,6 +571,9 @@
     " 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
@@ -693,8 +704,11 @@
   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')
@@ -1661,6 +1675,10 @@
 endfunc
 
 func Test_terminal_does_not_truncate_last_newlines()
+  " This test does not pass through ConPTY.
+  if has('conpty')
+    return
+  endif
   let contents = [
   \   [ 'One', '', 'X' ],
   \   [ 'Two', '', '' ],
diff --git a/src/version.c b/src/version.c
index 6da6bea..3e0dc7e 100644
--- a/src/version.c
+++ b/src/version.c
@@ -784,6 +784,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    870,
+/**/
     869,
 /**/
     868,