public inbox for cygwin-developers@cygwin.com
 help / color / mirror / Atom feed
* [PATCH v8 0/1] Pseudo console support in PTY (v8)
@ 2019-08-21 15:43 Takashi Yano
  2019-08-21 15:43 ` [PATCH v8 1/1] Cygwin: pty: add pseudo console support Takashi Yano
  2019-08-23 22:30 ` [PATCH v8 0/1] Pseudo console support in PTY (v8) Thomas Wolff
  0 siblings, 2 replies; 14+ messages in thread
From: Takashi Yano @ 2019-08-21 15:43 UTC (permalink / raw)
  To: cygwin-developers; +Cc: Takashi Yano

Changes from v7:
Make the programs work which call WIN32 APIs such as WriteConsole()
directly despite being linked with cygwin1.dll. This is realized
by hooking some WIN32 APIs and switching I/O to the pseudo console
if console access is detected.

Takashi Yano (1):
  Cygwin: pty: add pseudo console support.

 winsup/cygwin/dtable.cc               |   51 +
 winsup/cygwin/fhandler.h              |   43 +-
 winsup/cygwin/fhandler_console.cc     |   32 +
 winsup/cygwin/fhandler_tty.cc         | 1483 ++++++++++++++++++++++++-
 winsup/cygwin/fork.cc                 |   24 +
 winsup/cygwin/select.cc               |   22 +-
 winsup/cygwin/spawn.cc                |   55 +
 winsup/cygwin/strace.cc               |   24 +
 winsup/cygwin/tty.cc                  |    8 +
 winsup/cygwin/tty.h                   |   24 +-
 winsup/utils/cygwin-console-helper.cc |   14 +-
 11 files changed, 1731 insertions(+), 49 deletions(-)

-- 
2.21.0

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v8 1/1] Cygwin: pty: add pseudo console support.
  2019-08-21 15:43 [PATCH v8 0/1] Pseudo console support in PTY (v8) Takashi Yano
@ 2019-08-21 15:43 ` Takashi Yano
  2019-08-27  8:24   ` Corinna Vinschen
  2019-08-23 22:30 ` [PATCH v8 0/1] Pseudo console support in PTY (v8) Thomas Wolff
  1 sibling, 1 reply; 14+ messages in thread
From: Takashi Yano @ 2019-08-21 15:43 UTC (permalink / raw)
  To: cygwin-developers; +Cc: Takashi Yano

- Support pseudo console in PTY. Pseudo console is a new feature
  in Windows 10 1809, which provides console APIs on virtual
  terminal. With this patch, native console applications can work
  in PTY such as mintty, ssh, gnu screen or tmux.
---
 winsup/cygwin/dtable.cc               |   51 +
 winsup/cygwin/fhandler.h              |   43 +-
 winsup/cygwin/fhandler_console.cc     |   32 +
 winsup/cygwin/fhandler_tty.cc         | 1483 ++++++++++++++++++++++++-
 winsup/cygwin/fork.cc                 |   24 +
 winsup/cygwin/select.cc               |   22 +-
 winsup/cygwin/spawn.cc                |   55 +
 winsup/cygwin/strace.cc               |   24 +
 winsup/cygwin/tty.cc                  |    8 +
 winsup/cygwin/tty.h                   |   24 +-
 winsup/utils/cygwin-console-helper.cc |   14 +-
 11 files changed, 1731 insertions(+), 49 deletions(-)

diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
index 636221a77..ba5d16206 100644
--- a/winsup/cygwin/dtable.cc
+++ b/winsup/cygwin/dtable.cc
@@ -147,6 +147,36 @@ dtable::get_debugger_info ()
 void
 dtable::stdio_init ()
 {
+  bool need_fixup_handle = false;
+  fhandler_pty_slave *ptys = NULL;
+  bool is_pty[3] = {false, false, false};
+  for (int fd = 0; fd < 3; fd ++)
+    {
+      fhandler_base *fh = cygheap->fdtab[fd];
+      if (fh && fh->get_major () == DEV_PTYS_MAJOR)
+	{
+	  ptys = (fhandler_pty_slave *) fh;
+	  if (ptys->getPseudoConsole ())
+	    {
+	      is_pty[fd] = true;
+	      bool attached = !!fhandler_console::get_console_process_id
+		(ptys->getHelperProcessId (), true);
+	      if (!attached)
+		{
+		  /* Not attached to pseudo console in fork() or spawn()
+		     by some reason. This happens if the executable is
+		     a windows GUI binary, such as mintty. */
+		  FreeConsole ();
+		  AttachConsole (ptys->getHelperProcessId ());
+		  need_fixup_handle = true;
+		}
+	      ptys->reset_switch_to_pcon ();
+	    }
+	}
+    }
+  if (need_fixup_handle)
+    goto fixup_handle;
+
   if (myself->cygstarted || ISSTATE (myself, PID_CYGPARENT))
     {
       tty_min *t = cygwin_shared->tty.get_cttyp ();
@@ -155,6 +185,27 @@ dtable::stdio_init ()
       return;
     }
 
+fixup_handle:
+  if (need_fixup_handle)
+    {
+      HANDLE h;
+      h = CreateFile ("CONIN$", GENERIC_READ, FILE_SHARE_READ,
+		       NULL, OPEN_EXISTING, 0, 0);
+      if (is_pty[0])
+	{
+	  SetStdHandle (STD_INPUT_HANDLE, h);
+	  ptys->set_handle (h);
+	}
+      h = CreateFile ("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE,
+		       NULL, OPEN_EXISTING, 0, 0);
+      if (is_pty[1])
+	SetStdHandle (STD_OUTPUT_HANDLE, h);
+      if (is_pty[2])
+	SetStdHandle (STD_ERROR_HANDLE, h);
+      if (is_pty[1] || is_pty[2])
+	ptys->set_output_handle (h);
+    }
+
   HANDLE in = GetStdHandle (STD_INPUT_HANDLE);
   HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
   HANDLE err = GetStdHandle (STD_ERROR_HANDLE);
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 794948dba..8eb8531e3 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -2019,6 +2019,7 @@ private:
   static bool need_invisible ();
   static void free_console ();
   static const char *get_nonascii_key (INPUT_RECORD& input_rec, char *);
+  static DWORD get_console_process_id (DWORD pid, bool match);
 
   fhandler_console (void *) {}
 
@@ -2051,8 +2052,8 @@ class fhandler_pty_common: public fhandler_termios
  public:
   fhandler_pty_common ()
     : fhandler_termios (),
-      output_mutex (NULL),
-    input_mutex (NULL), input_available_event (NULL)
+    output_mutex (NULL), input_mutex (NULL),
+    input_available_event (NULL)
   {
     pc.file_attributes (FILE_ATTRIBUTE_NORMAL);
   }
@@ -2089,14 +2090,29 @@ class fhandler_pty_common: public fhandler_termios
     return fh;
   }
 
+  bool attach_pcon_in_fork (void)
+  {
+    return get_ttyp ()->attach_pcon_in_fork;
+  }
+  DWORD getHelperProcessId (void)
+  {
+    return get_ttyp ()->HelperProcessId;
+  }
+  HPCON getPseudoConsole (void)
+  {
+    return get_ttyp ()->hPseudoConsole;
+  }
+
  protected:
-  BOOL process_opost_output (HANDLE h, const void *ptr, ssize_t& len, bool is_echo);
+  BOOL process_opost_output (HANDLE h,
+			     const void *ptr, ssize_t& len, bool is_echo);
 };
 
 class fhandler_pty_slave: public fhandler_pty_common
 {
   HANDLE inuse;			// used to indicate that a tty is in use
-  HANDLE output_handle_cyg;
+  HANDLE output_handle_cyg, io_handle_cyg;
+  bool pcon_attached;
 
   /* Helper functions for fchmod and fchown. */
   bool fch_open_handles (bool chown);
@@ -2109,6 +2125,8 @@ class fhandler_pty_slave: public fhandler_pty_common
 
   void set_output_handle_cyg (HANDLE h) { output_handle_cyg = h; }
   HANDLE& get_output_handle_cyg () { return output_handle_cyg; }
+  void set_handle_cyg (HANDLE h) { io_handle_cyg = h; }
+  HANDLE& get_handle_cyg () { return io_handle_cyg; }
 
   int open (int flags, mode_t mode = 0);
   void open_setup (int flags);
@@ -2149,6 +2167,15 @@ class fhandler_pty_slave: public fhandler_pty_common
     copyto (fh);
     return fh;
   }
+  void set_switch_to_pcon (void);
+  void reset_switch_to_pcon (void);
+  void push_to_pcon_screenbuffer (const char *ptr, size_t len);
+  bool has_master_opened (void);
+  void mask_switch_to_pcon (bool mask)
+  {
+    get_ttyp ()->mask_switch_to_pcon = mask;
+  }
+  void fixup_after_attach (bool native_maybe);
 };
 
 #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit))
@@ -2157,17 +2184,17 @@ class fhandler_pty_master: public fhandler_pty_common
   int pktmode;			// non-zero if pty in a packet mode.
   HANDLE master_ctl;		// Control socket for handle duplication
   cygthread *master_thread;	// Master control thread
-  HANDLE from_master, to_master;
+  HANDLE from_master, to_master, from_slave, to_slave;
   HANDLE echo_r, echo_w;
   DWORD dwProcessId;		// Owner of master handles
-  HANDLE io_handle_cyg, to_master_cyg;
+  HANDLE to_master_cyg, from_master_cyg;
   cygthread *master_fwd_thread;	// Master forwarding thread
 
 public:
   HANDLE get_echo_handle () const { return echo_r; }
-  HANDLE& get_handle_cyg () { return io_handle_cyg; }
   /* Constructor */
   fhandler_pty_master (int);
+  ~fhandler_pty_master ();
 
   DWORD pty_master_thread ();
   DWORD pty_master_fwd_thread ();
@@ -2212,6 +2239,8 @@ public:
     copyto (fh);
     return fh;
   }
+
+  bool setup_pseudoconsole (void);
 };
 
 class fhandler_dev_null: public fhandler_base
diff --git a/winsup/cygwin/fhandler_console.cc b/winsup/cygwin/fhandler_console.cc
index 67638055e..997c50d23 100644
--- a/winsup/cygwin/fhandler_console.cc
+++ b/winsup/cygwin/fhandler_console.cc
@@ -1056,6 +1056,19 @@ fhandler_console::close ()
 
   CloseHandle (get_handle ());
   CloseHandle (get_output_handle ());
+
+  /* If already attached to pseudo console, don't call free_console () */
+  cygheap_fdenum cfd (false);
+  while (cfd.next () >= 0)
+    if (cfd->get_major () == DEV_PTYM_MAJOR ||
+	cfd->get_major () == DEV_PTYS_MAJOR)
+      {
+	fhandler_pty_common *t =
+	  (fhandler_pty_common *) (fhandler_base *) cfd;
+	if (get_console_process_id (t->getHelperProcessId (), true))
+	  return 0;
+      }
+
   if (!have_execed)
     free_console ();
   return 0;
@@ -3119,6 +3132,25 @@ fhandler_console::need_invisible ()
   return b;
 }
 
+DWORD
+fhandler_console::get_console_process_id (DWORD pid, bool match)
+{
+  DWORD tmp;
+  int num = GetConsoleProcessList (&tmp, 1);
+  DWORD *list = (DWORD *)
+	      HeapAlloc (GetProcessHeap (), 0, num * sizeof (DWORD));
+  num = GetConsoleProcessList (list, num);
+  tmp = 0;
+  for (int i=0; i<num; i++)
+    if ((match && list[i] == pid) || (!match && list[i] != pid))
+      {
+	tmp = list[i];
+	//break;
+      }
+  HeapFree (GetProcessHeap (), 0, list);
+  return tmp;
+}
+
 DWORD
 fhandler_console::__acquire_input_mutex (const char *fn, int ln, DWORD ms)
 {
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index 312c2d083..1257a576d 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -25,6 +25,26 @@ details. */
 #include "child_info.h"
 #include <asm/socket.h>
 #include "cygwait.h"
+#include "tls_pbuf.h"
+
+#define ALWAYS_USE_PCON false
+
+/* Not yet defined in Mingw-w64 */
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif /* ENABLE_VIRTUAL_TERMINAL_PROCESSING */
+#ifndef DISABLE_NEWLINE_AUTO_RETURN
+#define DISABLE_NEWLINE_AUTO_RETURN 0x0008
+#endif /* DISABLE_NEWLINE_AUTO_RETURN */
+#ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
+#define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE 0x00020016
+#endif /* PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE */
+#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
+#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
+#endif /* ENABLE_VIRTUAL_TERMINAL_INPUT */
+
+extern "C" int sscanf (const char *, const char *, ...);
+extern "C" int ttyname_r(int, char*, size_t);
 
 #define close_maybe(h) \
   do { \
@@ -39,11 +59,314 @@ struct pipe_request {
 
 struct pipe_reply {
   HANDLE from_master;
+  HANDLE from_master_cyg;
   HANDLE to_master;
   HANDLE to_master_cyg;
   DWORD error;
 };
 
+/* Hook WIN32 API */
+static
+void *hook_api (const char *mname, const char *name, const void *fn)
+{
+  HMODULE hm = GetModuleHandle (mname);
+  PIMAGE_NT_HEADERS pExeNTHdr = PIMAGE_NT_HEADERS (PBYTE (hm)
+				   + PIMAGE_DOS_HEADER (hm)->e_lfanew);
+  DWORD importRVA = pExeNTHdr->OptionalHeader.DataDirectory
+    [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+  PIMAGE_IMPORT_DESCRIPTOR pdfirst =
+    (PIMAGE_IMPORT_DESCRIPTOR) ((char *) hm + importRVA);
+  for (PIMAGE_IMPORT_DESCRIPTOR pd = pdfirst; pd->FirstThunk; pd++)
+    {
+      //const char *mn = (char *)((char *) hm + pd->Name);
+      //system_printf("Module: %s", mn);
+      if (pd->OriginalFirstThunk == 0)
+	continue;
+      PIMAGE_THUNK_DATA pt =
+	(PIMAGE_THUNK_DATA) ((char *) hm + pd->FirstThunk);
+      PIMAGE_THUNK_DATA pn =
+	(PIMAGE_THUNK_DATA) ((char *) hm + pd->OriginalFirstThunk);
+      for (PIMAGE_THUNK_DATA pi = pt; pn->u1.Ordinal; pi++, pn++)
+	{
+	  if (IMAGE_SNAP_BY_ORDINAL (pn->u1.Ordinal))
+	    continue;
+	  PIMAGE_IMPORT_BY_NAME pimp =
+	    (PIMAGE_IMPORT_BY_NAME) ((char *) hm + pn->u1.AddressOfData);
+	  //system_printf("Func: %s", (char *)pimp->Name);
+	  if (strcmp (name, (char *) pimp->Name) != 0)
+	    continue;
+#ifdef __x86_64__
+#define THUNK_FUNC_TYPE ULONGLONG
+#else
+#define THUNK_FUNC_TYPE DWORD
+#endif
+	  DWORD ofl = PAGE_READWRITE;
+	  if (!VirtualProtect (pi, sizeof (THUNK_FUNC_TYPE), ofl, &ofl))
+	    return NULL;
+	  void *origfn = (void *) pi->u1.Function;
+	  pi->u1.Function = (THUNK_FUNC_TYPE) fn;
+	  VirtualProtect (pi, sizeof (THUNK_FUNC_TYPE), ofl, &ofl);
+	  return origfn;
+	}
+    }
+  return NULL;
+}
+
+static void
+set_switch_to_pcon (void)
+{
+  cygheap_fdenum cfd (false);
+  while (cfd.next () >= 0)
+    if (cfd->get_major () == DEV_PTYS_MAJOR)
+      {
+	fhandler_base *fh = cfd;
+	fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
+	ptys->set_switch_to_pcon ();
+	return;
+      }
+}
+
+static bool isHybrid;
+
+static BOOL (WINAPI *WriteFile_Orig)
+     (HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED);
+static BOOL WINAPI
+WriteFile_Hooked
+     (HANDLE h, LPCVOID p, DWORD l, LPDWORD n, LPOVERLAPPED o)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return WriteFile_Orig (h, p, l, n, o);
+}
+static BOOL (WINAPI *WriteConsoleA_Orig)
+     (HANDLE, LPCVOID, DWORD, LPDWORD, LPVOID);
+static BOOL WINAPI
+WriteConsoleA_Hooked
+     (HANDLE h, LPCVOID p, DWORD l, LPDWORD n, LPVOID o)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return WriteConsoleA_Orig (h, p, l, n, o);
+}
+static BOOL (WINAPI *WriteConsoleW_Orig)
+     (HANDLE, LPCVOID, DWORD, LPDWORD, LPVOID);
+static BOOL WINAPI
+WriteConsoleW_Hooked
+     (HANDLE h, LPCVOID p, DWORD l, LPDWORD n, LPVOID o)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return WriteConsoleW_Orig (h, p, l, n, o);
+}
+static BOOL (WINAPI *ReadFile_Orig)
+     (HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED);
+static BOOL WINAPI
+ReadFile_Hooked
+     (HANDLE h, LPVOID p, DWORD l, LPDWORD n, LPOVERLAPPED o)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return ReadFile_Orig (h, p, l, n, o);
+}
+static BOOL (WINAPI *ReadConsoleA_Orig)
+     (HANDLE, LPVOID, DWORD, LPDWORD, LPVOID);
+static BOOL WINAPI
+ReadConsoleA_Hooked
+     (HANDLE h, LPVOID p, DWORD l, LPDWORD n, LPVOID o)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return ReadConsoleA_Orig (h, p, l, n, o);
+}
+static BOOL (WINAPI *ReadConsoleW_Orig)
+     (HANDLE, LPVOID, DWORD, LPDWORD, LPVOID);
+static BOOL WINAPI
+ReadConsoleW_Hooked
+     (HANDLE h, LPVOID p, DWORD l, LPDWORD n, LPVOID o)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return ReadConsoleW_Orig (h, p, l, n, o);
+}
+static BOOL (WINAPI *WriteConsoleOutputA_Orig)
+     (HANDLE, CONST CHAR_INFO, COORD, COORD, PSMALL_RECT);
+static BOOL WINAPI
+WriteConsoleOutputA_Hooked
+     (HANDLE h, CONST CHAR_INFO p, COORD s, COORD c, PSMALL_RECT r)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return WriteConsoleOutputA_Orig (h, p, s, c, r);
+}
+static BOOL (WINAPI *WriteConsoleOutputW_Orig)
+     (HANDLE, CONST CHAR_INFO, COORD, COORD, PSMALL_RECT);
+static BOOL WINAPI
+WriteConsoleOutputW_Hooked
+     (HANDLE h, CONST CHAR_INFO p, COORD s, COORD c, PSMALL_RECT r)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return WriteConsoleOutputW_Orig (h, p, s, c, r);
+}
+static BOOL (WINAPI *WriteConsoleOutputCharacterA_Orig)
+     (HANDLE, LPCSTR, DWORD, COORD, LPDWORD);
+static BOOL WINAPI
+WriteConsoleOutputCharacterA_Hooked
+     (HANDLE h, LPCSTR p, DWORD l, COORD c, LPDWORD n)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return WriteConsoleOutputCharacterA_Orig (h, p, l, c, n);
+}
+static BOOL (WINAPI *WriteConsoleOutputCharacterW_Orig)
+     (HANDLE, LPCWSTR, DWORD, COORD, LPDWORD);
+static BOOL WINAPI
+WriteConsoleOutputCharacterW_Hooked
+     (HANDLE h, LPCWSTR p, DWORD l, COORD c, LPDWORD n)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return WriteConsoleOutputCharacterW_Orig (h, p, l, c, n);
+}
+static BOOL (WINAPI *WriteConsoleOutputAttribute_Orig)
+     (HANDLE, CONST WORD *, DWORD, COORD, LPDWORD);
+static BOOL WINAPI
+WriteConsoleOutputAttribute_Hooked
+     (HANDLE h, CONST WORD *a, DWORD l, COORD c, LPDWORD n)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return WriteConsoleOutputAttribute_Orig (h, a, l, c, n);
+}
+static BOOL (WINAPI *WriteConsoleInputA_Orig)
+     (HANDLE, CONST INPUT_RECORD, DWORD, LPDWORD);
+static BOOL WINAPI
+WriteConsoleInputA_Hooked
+     (HANDLE h, CONST INPUT_RECORD r, DWORD l, LPDWORD n)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return WriteConsoleInputA_Orig (h, r, l, n);
+}
+static BOOL (WINAPI *WriteConsoleInputW_Orig)
+     (HANDLE, CONST INPUT_RECORD, DWORD, LPDWORD);
+static BOOL WINAPI
+WriteConsoleInputW_Hooked
+     (HANDLE h, CONST INPUT_RECORD r, DWORD l, LPDWORD n)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return WriteConsoleInputW_Orig (h, r, l, n);
+}
+static BOOL (WINAPI *ReadConsoleInputA_Orig)
+     (HANDLE, PINPUT_RECORD, DWORD, LPDWORD);
+static BOOL WINAPI
+ReadConsoleInputA_Hooked
+     (HANDLE h, PINPUT_RECORD r, DWORD l, LPDWORD n)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return ReadConsoleInputA_Orig (h, r, l, n);
+}
+static BOOL (WINAPI *ReadConsoleInputW_Orig)
+     (HANDLE, PINPUT_RECORD, DWORD, LPDWORD);
+static BOOL WINAPI
+ReadConsoleInputW_Hooked
+     (HANDLE h, PINPUT_RECORD r, DWORD l, LPDWORD n)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return ReadConsoleInputW_Orig (h, r, l, n);
+}
+static BOOL (WINAPI *PeekConsoleInputA_Orig)
+     (HANDLE, PINPUT_RECORD, DWORD, LPDWORD);
+static BOOL WINAPI
+PeekConsoleInputA_Hooked
+     (HANDLE h, PINPUT_RECORD r, DWORD l, LPDWORD n)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return PeekConsoleInputA_Orig (h, r, l, n);
+}
+static BOOL (WINAPI *PeekConsoleInputW_Orig)
+     (HANDLE, PINPUT_RECORD, DWORD, LPDWORD);
+static BOOL WINAPI
+PeekConsoleInputW_Hooked
+     (HANDLE h, PINPUT_RECORD r, DWORD l, LPDWORD n)
+{
+  DWORD dummy;
+  if (!isHybrid && GetConsoleMode (h, &dummy))
+    {
+      isHybrid = true;
+      set_switch_to_pcon ();
+    }
+  return PeekConsoleInputW_Orig (h, r, l, n);
+}
+
 bool
 bytes_available (DWORD& n, HANDLE h)
 {
@@ -67,7 +390,7 @@ bytes_available (DWORD& n, HANDLE h)
 bool
 fhandler_pty_common::bytes_available (DWORD &n)
 {
-  return ::bytes_available (n, get_handle ());
+  return ::bytes_available (n, get_handle_cyg ());
 }
 
 #ifdef DEBUGGING
@@ -235,7 +558,7 @@ fhandler_pty_master::process_slave_output (char *buf, size_t len, int pktmode_on
 	  /* Check echo pipe first. */
 	  if (::bytes_available (echo_cnt, echo_r) && echo_cnt > 0)
 	    break;
-	  if (!::bytes_available (n, get_handle_cyg ()))
+	  if (!bytes_available (n))
 	    goto err;
 	  if (n)
 	    break;
@@ -296,7 +619,7 @@ fhandler_pty_master::process_slave_output (char *buf, size_t len, int pktmode_on
 	      goto err;
 	    }
 	}
-      else if (!ReadFile (get_handle_cyg (), outbuf, rlen, &n, NULL))
+      else if (!ReadFile (get_handle (), outbuf, rlen, &n, NULL))
 	{
 	  termios_printf ("ReadFile failed, %E");
 	  goto err;
@@ -331,7 +654,8 @@ out:
 /* pty slave stuff */
 
 fhandler_pty_slave::fhandler_pty_slave (int unit)
-  : fhandler_pty_common (), inuse (NULL), output_handle_cyg (NULL)
+  : fhandler_pty_common (), inuse (NULL), output_handle_cyg (NULL),
+  io_handle_cyg (NULL), pcon_attached (false)
 {
   if (unit >= 0)
     dev ().parse (DEV_PTYS_MAJOR, unit);
@@ -340,11 +664,14 @@ fhandler_pty_slave::fhandler_pty_slave (int unit)
 int
 fhandler_pty_slave::open (int flags, mode_t)
 {
-  HANDLE pty_owner, from_master_local, to_master_local, to_master_cyg_local;
+  HANDLE pty_owner;
+  HANDLE from_master_local, from_master_cyg_local;
+  HANDLE to_master_local, to_master_cyg_local;
   HANDLE *handles[] =
   {
     &from_master_local, &input_available_event, &input_mutex, &inuse,
     &output_mutex, &to_master_local, &pty_owner, &to_master_cyg_local,
+    &from_master_cyg_local,
     NULL
   };
 
@@ -396,7 +723,7 @@ fhandler_pty_slave::open (int flags, mode_t)
     release_output_mutex ();
   }
 
-  if (!get_ttyp ()->from_master () ||
+  if (!get_ttyp ()->from_master () || !get_ttyp ()->from_master_cyg () ||
       !get_ttyp ()->to_master () || !get_ttyp ()->to_master_cyg ())
     {
       errmsg = "pty handles have been closed";
@@ -441,6 +768,15 @@ fhandler_pty_slave::open (int flags, mode_t)
 	  __seterrno ();
 	  goto err_no_msg;
 	}
+      if (!DuplicateHandle (pty_owner, get_ttyp ()->from_master_cyg (),
+			    GetCurrentProcess (), &from_master_cyg_local, 0, TRUE,
+			    DUPLICATE_SAME_ACCESS))
+	{
+	  termios_printf ("can't duplicate input from %u/%p, %E",
+			  get_ttyp ()->master_pid, get_ttyp ()->from_master_cyg ());
+	  __seterrno ();
+	  goto err_no_msg;
+	}
       if (!DuplicateHandle (pty_owner, get_ttyp ()->to_master (),
 			  GetCurrentProcess (), &to_master_local, 0, TRUE,
 			  DUPLICATE_SAME_ACCESS))
@@ -474,9 +810,11 @@ fhandler_pty_slave::open (int flags, mode_t)
 	  goto err;
 	}
       from_master_local = repl.from_master;
+      from_master_cyg_local = repl.from_master_cyg;
       to_master_local = repl.to_master;
       to_master_cyg_local = repl.to_master_cyg;
-      if (!from_master_local || !to_master_local || !to_master_cyg_local)
+      if (!from_master_local || !from_master_cyg_local ||
+	  !to_master_local || !to_master_cyg_local)
 	{
 	  SetLastError (repl.error);
 	  errmsg = "error duplicating pipes, %E";
@@ -484,17 +822,21 @@ fhandler_pty_slave::open (int flags, mode_t)
 	}
     }
   VerifyHandle (from_master_local);
+  VerifyHandle (from_master_cyg_local);
   VerifyHandle (to_master_local);
   VerifyHandle (to_master_cyg_local);
 
   termios_printf ("duplicated from_master %p->%p from pty_owner",
 		  get_ttyp ()->from_master (), from_master_local);
+  termios_printf ("duplicated from_master_cyg %p->%p from pty_owner",
+		  get_ttyp ()->from_master_cyg (), from_master_cyg_local);
   termios_printf ("duplicated to_master %p->%p from pty_owner",
 		  get_ttyp ()->to_master (), to_master_local);
   termios_printf ("duplicated to_master_cyg %p->%p from pty_owner",
 		  get_ttyp ()->to_master_cyg (), to_master_cyg_local);
 
   set_handle (from_master_local);
+  set_handle_cyg (from_master_cyg_local);
   set_output_handle (to_master_local);
   set_output_handle_cyg (to_master_cyg_local);
 
@@ -540,6 +882,26 @@ fhandler_pty_slave::cleanup ()
 int
 fhandler_pty_slave::close ()
 {
+#if 0
+  if (getPseudoConsole ())
+    {
+      INPUT_RECORD inp[128];
+      DWORD n;
+      PeekFunc =
+	PeekConsoleInputA_Orig ? PeekConsoleInputA_Orig : PeekConsoleInput;
+      PeekFunc (get_handle (), inp, 128, &n);
+      bool pipe_empty = true;
+      while (n-- > 0)
+	if (inp[n].EventType == KEY_EVENT && inp[n].Event.KeyEvent.bKeyDown)
+	  pipe_empty = false;
+      if (pipe_empty)
+	{
+	  /* Flush input buffer */
+	  size_t len = UINT_MAX;
+	  read (NULL, len);
+	}
+    }
+#endif
   termios_printf ("closing last open %s handle", ttyname ());
   if (inuse && !CloseHandle (inuse))
     termios_printf ("CloseHandle (inuse), %E");
@@ -548,11 +910,20 @@ fhandler_pty_slave::close ()
   if (!ForceCloseHandle (get_output_handle_cyg ()))
     termios_printf ("CloseHandle (get_output_handle_cyg ()<%p>), %E",
 	get_output_handle_cyg ());
+  if (!ForceCloseHandle (get_handle_cyg ()))
+    termios_printf ("CloseHandle (get_handle_cyg ()<%p>), %E",
+	get_handle_cyg ());
   if ((unsigned) myself->ctty == FHDEV (DEV_PTYS_MAJOR, get_minor ()))
     fhandler_console::free_console ();	/* assumes that we are the last pty closer */
+#if 1
+  if (pcon_attached)
+    init_console_handler (false);
+#endif
   fhandler_pty_common::close ();
   if (!ForceCloseHandle (output_mutex))
     termios_printf ("CloseHandle (output_mutex<%p>), %E", output_mutex);
+  if (pcon_attached)
+    get_ttyp ()->num_pcon_attached_slaves --;
   return 0;
 }
 
@@ -596,6 +967,201 @@ fhandler_pty_slave::init (HANDLE h, DWORD a, mode_t)
   return ret;
 }
 
+void
+fhandler_pty_slave::set_switch_to_pcon (void)
+{
+  acquire_output_mutex (INFINITE);
+  if (!get_ttyp ()->switch_to_pcon)
+    {
+      Sleep (20);
+      if (get_ttyp ()->pcon_pid == 0 ||
+	  kill (get_ttyp ()->pcon_pid, 0) != 0)
+	get_ttyp ()->pcon_pid = myself->pid;
+      get_ttyp ()->switch_to_pcon = true;
+    }
+  release_output_mutex ();
+}
+
+void
+fhandler_pty_slave::reset_switch_to_pcon (void)
+{
+  if (ALWAYS_USE_PCON)
+    return;
+  if (isHybrid)
+    {
+      set_switch_to_pcon ();
+      return;
+    }
+  acquire_output_mutex (INFINITE);
+  if (get_ttyp ()->pcon_pid &&
+      get_ttyp ()->pcon_pid != myself->pid &&
+      kill (get_ttyp ()->pcon_pid, 0) == 0)
+    {
+    /* There is a process which is grabbing pseudo console. */
+      release_output_mutex ();
+      return;
+    }
+  if (get_ttyp ()->switch_to_pcon &&
+      get_ttyp ()->pcon_pid != myself->pid)
+    {
+      DWORD mode;
+      GetConsoleMode (get_handle (), &mode);
+      SetConsoleMode (get_handle (), mode & ~ENABLE_ECHO_INPUT);
+      Sleep (20); /* Wait for pty_master_fwd_thread() */
+    }
+  get_ttyp ()->pcon_pid = 0;
+  get_ttyp ()->switch_to_pcon = false;
+  release_output_mutex ();
+}
+
+void
+fhandler_pty_slave::push_to_pcon_screenbuffer (const char *ptr, size_t len)
+{
+  DWORD pidRestore = 0;
+  if (!fhandler_console::get_console_process_id (getHelperProcessId (), true))
+    if (pcon_attached)
+      {
+	system_printf ("pty%d: pcon_attach mismatch?????? (%p)", get_minor (), this);
+	pcon_attached = false;
+      }
+  /* If not attached pseudo console yet, try to attach temporally. */
+  if (!pcon_attached)
+    {
+      if (has_master_opened ())
+	return;
+
+      pidRestore =
+	fhandler_console::get_console_process_id (GetCurrentProcessId (),
+						  false);
+      /* If pidRestore is not set, give up to push. */
+      if (!pidRestore)
+	return;
+
+      FreeConsole ();
+      if (!AttachConsole (getHelperProcessId ()))
+	{
+	  system_printf ("pty%d: AttachConsole(%d) failed. (%p) %08lx", get_minor (), getHelperProcessId (), this, GetLastError ());
+	  goto detach;
+	}
+    }
+  char *buf;
+  size_t nlen;
+  DWORD origCP;
+  origCP = GetConsoleOutputCP ();
+  SetConsoleOutputCP (get_ttyp ()->TermCodePage);
+  /* Just copy */
+  buf = (char *) HeapAlloc (GetProcessHeap (), 0, len);
+  memcpy (buf, (char *)ptr, len);
+  nlen = len;
+  char *p0, *p1;
+  p0 = p1 = buf;
+  /* Remove alternate screen buffer drawing */
+  while (p0 && p1)
+    {
+      if (!get_ttyp ()->screen_alternated)
+	{
+	  /* Check switching to alternate screen buffer */
+	  p0 = (char *) memmem (p1, nlen - (p1-buf), "\033[?1049h", 8);
+	  if (p0)
+	    {
+	      //p0 += 8;
+	      get_ttyp ()->screen_alternated = true;
+	    }
+	}
+      if (get_ttyp ()->screen_alternated)
+	{
+	  /* Check switching to main screen buffer */
+	  p1 = (char *) memmem (p0, nlen - (p0-buf), "\033[?1049l", 8);
+	  if (p1)
+	    {
+	      p1 += 8;
+	      get_ttyp ()->screen_alternated = false;
+	      memmove (p0, p1, buf+nlen - p1);
+	      nlen -= p1 - p0;
+	    }
+	  else
+	    nlen = p0 - buf;
+	}
+    }
+  if (!nlen) /* Nothing to be synchronized */
+    goto cleanup;
+  if (get_ttyp ()->switch_to_pcon)
+    goto cleanup;
+  /* Remove ESC sequence which returns results to console
+     input buffer. Without this, cursor position report
+     is put into the input buffer as a garbage. */
+  /* Remove ESC sequence to report cursor position. */
+  while ((p0 = (char *) memmem (buf, nlen, "\033[6n", 4)))
+    {
+      memmove (p0, p0+4, nlen - (p0+4 - buf));
+      nlen -= 4;
+    }
+  /* Remove ESC sequence to report terminal identity. */
+  while ((p0 = (char *) memmem (buf, nlen, "\033[0c", 4)))
+    {
+      memmove (p0, p0+4, nlen - (p0+4 - buf));
+      nlen -= 4;
+    }
+  DWORD dwMode, flags;
+  flags = ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+  GetConsoleMode (get_output_handle (), &dwMode);
+  if (!(get_ttyp ()->ti.c_oflag & OPOST) ||
+      !(get_ttyp ()->ti.c_oflag & ONLCR))
+    flags |= DISABLE_NEWLINE_AUTO_RETURN;
+  SetConsoleMode (get_output_handle (), dwMode | flags);
+  char *p;
+  p = buf;
+  DWORD wLen, written;
+  written = 0;
+  int retry_count;
+  retry_count = 0;
+  BOOL (WINAPI *WriteFunc)
+    (HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED);
+  WriteFunc = WriteFile_Orig ? WriteFile_Orig : WriteFile;
+  while (written <  nlen)
+    {
+      if (!WriteFunc (get_output_handle (), p, nlen - written, &wLen, NULL))
+	{
+	  termios_printf ("WriteFile failed, %E");
+	  this->open (0, 0); /* Re-open handles */
+	  /* Fix pseudo console window size */
+	  struct winsize win;
+	  this->ioctl (TIOCGWINSZ, &win);
+	  this->ioctl (TIOCSWINSZ, &win);
+	  if (++retry_count > 3)
+	    break;
+	}
+      written += wLen;
+      p += wLen;
+    }
+  /* Detach from pseudo console and resume. */
+  SetConsoleMode (get_output_handle (), dwMode);
+cleanup:
+  SetConsoleOutputCP (origCP);
+  HeapFree (GetProcessHeap (), 0, buf);
+detach:
+  if (!pcon_attached)
+    {
+      FreeConsole ();
+      if (!AttachConsole (pidRestore))
+	{
+	  system_printf ("pty%d: AttachConsole(%d) failed. (%p) %08lx", get_minor (), pidRestore, this, GetLastError ());
+	  pcon_attached = false;
+	}
+    }
+}
+
+bool
+fhandler_pty_slave::has_master_opened (void)
+{
+  cygheap_fdenum cfd (false);
+  while (cfd.next () >= 0)
+    if (cfd->get_major () == DEV_PTYM_MAJOR &&
+	cfd->get_minor () == get_minor ())
+      return true;
+  return false;
+}
+
 ssize_t __stdcall
 fhandler_pty_slave::write (const void *ptr, size_t len)
 {
@@ -609,7 +1175,51 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 
   push_process_state process_state (PID_TTYOU);
 
-  if (!process_opost_output (get_output_handle_cyg (), ptr, towrite, false))
+  reset_switch_to_pcon ();
+
+  char *buf;
+  ssize_t nlen;
+  UINT targetCodePage = (get_ttyp ()->switch_to_pcon) ?
+    GetConsoleOutputCP () : get_ttyp ()->TermCodePage;
+  if (targetCodePage != get_ttyp ()->TermCodePage)
+    {
+      size_t wlen =
+	MultiByteToWideChar (get_ttyp ()->TermCodePage, 0,
+			     (char *)ptr, len, NULL, 0);
+      wchar_t *wbuf = (wchar_t *)
+	HeapAlloc (GetProcessHeap (), 0, wlen * sizeof (wchar_t));
+      wlen =
+	MultiByteToWideChar (get_ttyp ()->TermCodePage, 0,
+			     (char *)ptr, len, wbuf, wlen);
+      nlen = WideCharToMultiByte (targetCodePage, 0,
+				  wbuf, wlen, NULL, 0, NULL, NULL);
+      buf = (char *) HeapAlloc (GetProcessHeap (), 0, nlen);
+      nlen = WideCharToMultiByte (targetCodePage, 0,
+				  wbuf, wlen, buf, nlen, NULL, NULL);
+      HeapFree (GetProcessHeap (), 0, wbuf);
+    }
+  else
+    {
+      /* Just copy */
+      buf = (char *) HeapAlloc (GetProcessHeap (), 0, len);
+      memcpy (buf, (char *)ptr, len);
+      nlen = len;
+    }
+
+  DWORD dwMode, flags;
+  flags = ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+  if (!(get_ttyp ()->ti.c_oflag & OPOST) ||
+      !(get_ttyp ()->ti.c_oflag & ONLCR))
+    flags |= DISABLE_NEWLINE_AUTO_RETURN;
+  if (get_ttyp ()->switch_to_pcon)
+    {
+      GetConsoleMode (get_output_handle (), &dwMode);
+      SetConsoleMode (get_output_handle (), dwMode | flags);
+    }
+  HANDLE to =
+    (get_ttyp ()->switch_to_pcon /*&& get_ttyp ()->pcon_pid == myself->pid*/) ?
+    get_output_handle () : get_output_handle_cyg ();
+  if (!process_opost_output (to, buf, nlen, false))
     {
       DWORD err = GetLastError ();
       termios_printf ("WriteFile failed, %E");
@@ -623,12 +1233,26 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 	}
       towrite = -1;
     }
+  HeapFree (GetProcessHeap (), 0, buf);
+  flags = ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+  if (get_ttyp ()->switch_to_pcon)
+    SetConsoleMode (get_output_handle (), dwMode | flags);
+
+  /* Push slave output to pseudo console screen buffer */
+  if (getPseudoConsole ())
+    {
+      acquire_output_mutex (INFINITE);
+      push_to_pcon_screenbuffer ((char *)ptr, len);
+      release_output_mutex ();
+    }
+
   return towrite;
 }
 
 void __reg3
 fhandler_pty_slave::read (void *ptr, size_t& len)
 {
+  char *ptr0 = (char *)ptr;
   ssize_t totalread = 0;
   int vmin = 0;
   int vtime = 0;	/* Initialized to prevent -Wuninitialized warning */
@@ -644,10 +1268,17 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
       return;
     }
 
-  termios_printf ("read(%p, %lu) handle %p", ptr, len, get_handle ());
+  termios_printf ("read(%p, %lu) handle %p", ptr, len, get_handle_cyg ());
 
   push_process_state process_state (PID_TTYIN);
 
+  if (ptr) /* Indicating not tcflush(). */
+    {
+      reset_switch_to_pcon ();
+      if (get_ttyp ()->pcon_pid != myself->pid)
+	mask_switch_to_pcon (true);
+    }
+
   if (is_nonblocking () || !ptr) /* Indicating tcflush(). */
     time_to_wait = 0;
   else if ((get_ttyp ()->ti.c_lflag & ICANON))
@@ -746,6 +1377,53 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
 	    }
 	  goto out;
 	}
+      if (get_ttyp ()->switch_to_pcon &&
+	  !get_ttyp ()->mask_switch_to_pcon)
+	{
+	  DWORD dwMode;
+	  GetConsoleMode (get_handle (), &dwMode);
+	  DWORD flags = ENABLE_VIRTUAL_TERMINAL_INPUT;
+	  if (get_ttyp ()->ti.c_lflag & ECHO)
+	    flags |= ENABLE_ECHO_INPUT;
+	  if (get_ttyp ()->ti.c_lflag & ICANON)
+	    flags |= ENABLE_LINE_INPUT;
+	  if (flags & ENABLE_ECHO_INPUT && !(flags & ENABLE_LINE_INPUT))
+	    flags &= ~ENABLE_ECHO_INPUT;
+	  if ((get_ttyp ()->ti.c_lflag & ISIG) &&
+	      !(get_ttyp ()->ti.c_iflag & IGNBRK))
+	    flags |= ENABLE_PROCESSED_INPUT;
+	  if (dwMode != flags)
+	    SetConsoleMode (get_handle (), flags);
+	  /* Read get_handle() instad of get_handle_cyg() */
+	  BOOL (WINAPI *ReadFunc)
+	    (HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED);
+	  ReadFunc = ReadFile_Orig ? ReadFile_Orig : ReadFile;
+	  DWORD rlen;
+	  if (!ReadFunc (get_handle (), ptr, len, &rlen, NULL))
+	    {
+	      termios_printf ("read failed, %E");
+	      ReleaseMutex (input_mutex);
+	      set_errno (EIO);
+	      totalread = -1;
+	      goto out;
+	    }
+	  INPUT_RECORD inp[128];
+	  DWORD n;
+	  BOOL (WINAPI *PeekFunc)
+	    (HANDLE, PINPUT_RECORD, DWORD, LPDWORD);
+	  PeekFunc =
+	    PeekConsoleInputA_Orig ? PeekConsoleInputA_Orig : PeekConsoleInput;
+	  PeekFunc (get_handle (), inp, 128, &n);
+	  bool pipe_empty = true;
+	  while (n-- > 0)
+	    if (inp[n].EventType == KEY_EVENT && inp[n].Event.KeyEvent.bKeyDown)
+	      pipe_empty = false;
+	  if (pipe_empty)
+	    ResetEvent (input_available_event);
+	  ReleaseMutex (input_mutex);
+	  len = rlen;
+	  return;
+	}
       if (!bytes_available (bytes_in_pipe))
 	{
 	  ReleaseMutex (input_mutex);
@@ -757,6 +1435,7 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
       if (ptr && !bytes_in_pipe && !vmin && !time_to_wait)
 	{
 	  ReleaseMutex (input_mutex);
+	  mask_switch_to_pcon (false);
 	  len = (size_t) bytes_in_pipe;
 	  return;
 	}
@@ -777,7 +1456,7 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
       if (readlen)
 	{
 	  termios_printf ("reading %lu bytes (vtime %d)", readlen, vtime);
-	  if (!ReadFile (get_handle (), buf, readlen, &n, NULL))
+	  if (!ReadFile (get_handle_cyg (), buf, readlen, &n, NULL))
 	    {
 	      termios_printf ("read failed, %E");
 	      ReleaseMutex (input_mutex);
@@ -861,6 +1540,16 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
 out:
   termios_printf ("%d = read(%p, %lu)", totalread, ptr, len);
   len = (size_t) totalread;
+#if 1 /* Experimenta code */
+  /* Push slave read as echo to pseudo console screen buffer. */
+  if (getPseudoConsole () && ptr0 && (get_ttyp ()->ti.c_lflag & ECHO))
+    {
+      acquire_output_mutex (INFINITE);
+      push_to_pcon_screenbuffer (ptr0, len);
+      release_output_mutex ();
+    }
+#endif
+  mask_switch_to_pcon (false);
 }
 
 int
@@ -890,6 +1579,7 @@ fhandler_pty_master::dup (fhandler_base *child, int)
 int
 fhandler_pty_slave::tcgetattr (struct termios *t)
 {
+  reset_switch_to_pcon ();
   *t = get_ttyp ()->ti;
   return 0;
 }
@@ -897,6 +1587,22 @@ fhandler_pty_slave::tcgetattr (struct termios *t)
 int
 fhandler_pty_slave::tcsetattr (int, const struct termios *t)
 {
+  reset_switch_to_pcon ();
+#if 0
+  if (get_ttyp ()->switch_to_pcon)
+    {
+      DWORD flags = ENABLE_VIRTUAL_TERMINAL_INPUT;
+      if (t->c_lflag & ECHO)
+	flags |= ENABLE_ECHO_INPUT;
+      if (t->c_lflag & ICANON)
+	flags |= ENABLE_LINE_INPUT;
+      if (flags & ENABLE_ECHO_INPUT && !(flags & ENABLE_LINE_INPUT))
+	flags &= ~ENABLE_ECHO_INPUT;
+      if ((t->c_lflag & ISIG) && !(t->c_iflag & IGNBRK))
+	flags |= ENABLE_PROCESSED_INPUT;
+      SetConsoleMode (get_handle (), flags);
+    }
+#endif
   acquire_output_mutex (INFINITE);
   get_ttyp ()->ti = *t;
   release_output_mutex ();
@@ -908,7 +1614,9 @@ fhandler_pty_slave::tcflush (int queue)
 {
   int ret = 0;
 
-  termios_printf ("tcflush(%d) handle %p", queue, get_handle ());
+  termios_printf ("tcflush(%d) handle %p", queue, get_handle_cyg ());
+
+  reset_switch_to_pcon ();
 
   if (queue == TCIFLUSH || queue == TCIOFLUSH)
     {
@@ -929,6 +1637,7 @@ int
 fhandler_pty_slave::ioctl (unsigned int cmd, void *arg)
 {
   termios_printf ("ioctl (%x)", cmd);
+  reset_switch_to_pcon ();
   int res = fhandler_termios::ioctl (cmd, arg);
   if (res <= 0)
     return res;
@@ -995,6 +1704,60 @@ fhandler_pty_slave::ioctl (unsigned int cmd, void *arg)
       get_ttyp ()->winsize = get_ttyp ()->arg.winsize;
       break;
     case TIOCSWINSZ:
+      if (getPseudoConsole ())
+	{
+	  /* If not attached pseudo console yet, try to attach
+	     temporally. */
+	  DWORD pidRestore = 0;
+	  if (!pcon_attached)
+	    {
+	      if (has_master_opened () && get_ttyp ()->attach_pcon_in_fork)
+		goto resize_cyg;
+
+	      pidRestore = fhandler_console::get_console_process_id
+		(GetCurrentProcessId (), false);
+
+	      /* This happens at mintty startup if fhandler_console::
+		 need_invisible() is called in stdio_init() in dtable.cc */
+	      if (!pidRestore) /* Give up to resize pseudo console */
+		goto resize_cyg;
+
+	      FreeConsole ();
+	      if (!AttachConsole (getHelperProcessId ()))
+		{
+		  system_printf ("pty%d: AttachConsole(%d) failed. (%p) %08lx", get_minor(), getHelperProcessId (), this, GetLastError ());
+		  goto cleanup;
+		}
+	    }
+	  COORD size;
+	  size.X = ((struct winsize *) arg)->ws_col;
+	  size.Y = ((struct winsize *) arg)->ws_row;
+	  CONSOLE_SCREEN_BUFFER_INFO csbi;
+	  if (GetConsoleScreenBufferInfo (get_output_handle (), &csbi))
+	    if (size.X == csbi.srWindow.Right - csbi.srWindow.Left + 1 &&
+		size.Y == csbi.srWindow.Bottom - csbi.srWindow.Top + 1)
+	      goto cleanup;
+	  if (!SetConsoleScreenBufferSize (get_output_handle (), size))
+	    goto cleanup;
+	  SMALL_RECT rect;
+	  rect.Left = 0;
+	  rect.Top = 0;
+	  rect.Right = size.X-1;
+	  rect.Bottom = size.Y-1;
+	  SetConsoleWindowInfo (get_output_handle (), TRUE, &rect);
+cleanup:
+	  /* Detach from pseudo console and resume. */
+	  if (pidRestore)
+	    {
+	      FreeConsole ();
+	      if (!AttachConsole (pidRestore))
+		{
+		  system_printf ("pty%d: AttachConsole(%d) failed. (%p) %08lx", get_minor (), pidRestore, this, GetLastError ());
+		  pcon_attached = false;
+		}
+	    }
+	}
+resize_cyg:
       if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
 	  || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
 	{
@@ -1228,8 +1991,9 @@ errout:
 fhandler_pty_master::fhandler_pty_master (int unit)
   : fhandler_pty_common (), pktmode (0), master_ctl (NULL),
     master_thread (NULL), from_master (NULL), to_master (NULL),
-    echo_r (NULL), echo_w (NULL), dwProcessId (0),
-    io_handle_cyg (NULL), to_master_cyg (NULL), master_fwd_thread (NULL)
+    from_slave (NULL), to_slave (NULL), echo_r (NULL), echo_w (NULL),
+    dwProcessId (0), to_master_cyg (NULL), from_master_cyg (NULL),
+    master_fwd_thread (NULL)
 {
   if (unit >= 0)
     dev ().parse (DEV_PTYM_MAJOR, unit);
@@ -1241,6 +2005,15 @@ fhandler_pty_master::fhandler_pty_master (int unit)
   set_name ("/dev/ptmx");
 }
 
+fhandler_pty_master::~fhandler_pty_master ()
+{
+  /* Without this wait, helper process for pseudo console
+     sometimes remains running after the pty session is
+     closed. The reason is not clear. */
+  if (to_master && from_master)
+    Sleep (20);
+}
+
 int
 fhandler_pty_master::open (int flags, mode_t)
 {
@@ -1285,7 +2058,8 @@ fhandler_pty_master::cleanup ()
 {
   report_tty_counts (this, "closing master", "");
   if (archetype)
-    from_master = to_master = to_master_cyg = NULL;
+    from_master = from_master_cyg =
+      to_master = to_master_cyg = from_slave = to_slave = NULL;
   fhandler_base::cleanup ();
 }
 
@@ -1294,9 +2068,11 @@ fhandler_pty_master::close ()
 {
   OBJECT_BASIC_INFORMATION obi;
   NTSTATUS status;
+  pid_t master_pid_tmp = get_ttyp ()->master_pid;
 
-  termios_printf ("closing from_master(%p)/to_master(%p)/to_master_cyg(%p) since we own them(%u)",
-		  from_master, to_master, to_master_cyg, dwProcessId);
+  termios_printf ("closing from_master(%p)/from_master_cyg(%p)/to_master(%p)/to_master_cyg(%p) since we own them(%u)",
+		  from_master, from_master_cyg,
+		  to_master, to_master_cyg, dwProcessId);
   if (cygwin_finished_initializing)
     {
       if (master_ctl && get_ttyp ()->master_pid == myself->pid)
@@ -1334,9 +2110,30 @@ fhandler_pty_master::close ()
     termios_printf ("CloseHandle (output_mutex<%p>), %E", output_mutex);
   if (!NT_SUCCESS (status))
     debug_printf ("NtQueryObject: %y", status);
-  else if (obi.HandleCount == 1)
+  else if (obi.HandleCount == (getPseudoConsole () ? 2 : 1))
+			      /* Helper process has inherited one. */
     {
       termios_printf("Closing last master of pty%d", get_minor ());
+      /* Close Pseudo Console */
+      if (getPseudoConsole ())
+	{
+	  /* Terminate helper process */
+	  SetEvent (get_ttyp ()->hHelperGoodbye);
+	  WaitForSingleObject (get_ttyp ()->hHelperProcess, INFINITE);
+	  /* FIXME: Pseudo console can be accessed via its handle
+	     only in the process which created it. What else can we do? */
+	  if (master_pid_tmp == myself->pid)
+	    {
+	      /* Release pseudo console */
+	      HMODULE hModule = GetModuleHandle ("kernel32.dll");
+	      FARPROC func = GetProcAddress (hModule, "ClosePseudoConsole");
+	      VOID (WINAPI *ClosePseudoConsole) (HPCON) = NULL;
+	      ClosePseudoConsole = (VOID (WINAPI *) (HPCON)) func;
+	      ClosePseudoConsole (getPseudoConsole ());
+	    }
+	  get_ttyp ()->hPseudoConsole = NULL;
+	  get_ttyp ()->switch_to_pcon = false;
+	}
       if (get_ttyp ()->getsid () > 0)
 	kill (get_ttyp ()->getsid (), SIGHUP);
       SetEvent (input_available_event);
@@ -1344,17 +2141,24 @@ fhandler_pty_master::close ()
 
   if (!ForceCloseHandle (from_master))
     termios_printf ("error closing from_master %p, %E", from_master);
+  if (from_master_cyg != from_master) /* Avoid double close. */
+    if (!ForceCloseHandle (from_master_cyg))
+      termios_printf ("error closing from_master_cyg %p, %E", from_master_cyg);
   if (!ForceCloseHandle (to_master))
     termios_printf ("error closing to_master %p, %E", to_master);
   from_master = to_master = NULL;
-  if (!ForceCloseHandle (get_handle_cyg ()))
-    termios_printf ("error closing io_handle_cyg %p, %E", get_handle_cyg ());
+  if (!ForceCloseHandle (from_slave))
+    termios_printf ("error closing from_slave %p, %E", from_slave);
+  from_slave = NULL;
   if (!ForceCloseHandle (to_master_cyg))
     termios_printf ("error closing to_master_cyg %p, %E", to_master_cyg);
-  get_handle_cyg () = to_master_cyg = NULL;
+  to_master_cyg = from_master_cyg = NULL;
   ForceCloseHandle (echo_r);
   ForceCloseHandle (echo_w);
   echo_r = echo_w = NULL;
+  if (to_slave)
+    ForceCloseHandle (to_slave);
+  to_slave = NULL;
 
   if (have_execed || get_ttyp ()->master_pid != myself->pid)
     termios_printf ("not clearing: %d, master_pid %d", have_execed, get_ttyp ()->master_pid);
@@ -1376,6 +2180,45 @@ fhandler_pty_master::write (const void *ptr, size_t len)
     return (ssize_t) bg;
 
   push_process_state process_state (PID_TTYOU);
+
+  /* Write terminal input to to_slave pipe instead of output_handle
+     if current application is native console application. */
+  if (get_ttyp ()->switch_to_pcon &&
+      !get_ttyp ()->mask_switch_to_pcon)
+    {
+      char *buf;
+      size_t nlen;
+      if (get_ttyp ()->TermCodePage != CP_UTF8)
+	{
+	  size_t wlen =
+	    MultiByteToWideChar (get_ttyp ()->TermCodePage, 0,
+				 (char *)ptr, len, NULL, 0);
+	  wchar_t *wbuf = (wchar_t *)
+	    HeapAlloc (GetProcessHeap (), 0, wlen * sizeof (wchar_t));
+	  wlen =
+	    MultiByteToWideChar (get_ttyp ()->TermCodePage, 0,
+				 (char *)ptr, len, wbuf, wlen);
+	  nlen = WideCharToMultiByte (CP_UTF8, 0,
+				      wbuf, wlen, NULL, 0, NULL, NULL);
+	  buf = (char *) HeapAlloc (GetProcessHeap (), 0, nlen);
+	  nlen = WideCharToMultiByte (CP_UTF8, 0,
+				      wbuf, wlen, buf, nlen, NULL, NULL);
+	  HeapFree (GetProcessHeap (), 0, wbuf);
+	}
+      else
+	{
+	  /* Just copy */
+	  buf = (char *) HeapAlloc (GetProcessHeap (), 0, len);
+	  memcpy (buf, (char *)ptr, len);
+	  nlen = len;
+	}
+      DWORD wLen;
+      WriteFile (to_slave, buf, nlen, &wLen, NULL);
+      SetEvent (input_available_event);
+      HeapFree (GetProcessHeap (), 0, buf);
+      return len;
+    }
+
   line_edit_status status = line_edit (p++, len, ti, &ret);
   if (status > line_edit_signalled && status != line_edit_pipe_full)
     ret = -1;
@@ -1443,6 +2286,19 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg)
       *(struct winsize *) arg = get_ttyp ()->winsize;
       break;
     case TIOCSWINSZ:
+      /* FIXME: Pseudo console can be accessed via its handle
+	 only in the process which created it. What else can we do? */
+      if (getPseudoConsole () && get_ttyp ()->master_pid == myself->pid)
+	{
+	  HMODULE hModule = GetModuleHandle ("kernel32.dll");
+	  FARPROC func = GetProcAddress (hModule, "ResizePseudoConsole");
+	  HRESULT (WINAPI *ResizePseudoConsole) (HPCON, COORD) = NULL;
+	  ResizePseudoConsole = (HRESULT (WINAPI *) (HPCON, COORD)) func;
+	  COORD size;
+	  size.X = ((struct winsize *) arg)->ws_col;
+	  size.Y = ((struct winsize *) arg)->ws_row;
+	  ResizePseudoConsole (getPseudoConsole (), size);
+	}
       if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
 	  || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
 	{
@@ -1458,7 +2314,7 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg)
     case FIONREAD:
       {
 	DWORD n;
-	if (!::bytes_available (n, get_handle_cyg ()))
+	if (!bytes_available (n))
 	  {
 	    set_errno (EINVAL);
 	    return -1;
@@ -1494,9 +2350,175 @@ fhandler_pty_common::set_close_on_exec (bool val)
   close_on_exec (val);
 }
 
+void
+fhandler_pty_slave::fixup_after_attach (bool native_maybe)
+{
+  if (getPseudoConsole ())
+    {
+      if (fhandler_console::get_console_process_id (getHelperProcessId (),
+						    true))
+	{
+	  if (!pcon_attached)
+	    {
+	      setlocale (LC_CTYPE, "");
+	      /* Set console code page form locale */
+	      wchar_t lc[32];
+	      wchar_t *p;
+	      mbstowcs (lc, setlocale (LC_CTYPE, NULL), 32);
+	      p = wcschr (lc, L'.');
+	      if (p)
+		*p = L'\0';
+	      p = wcschr (lc, L'_');
+	      if (p)
+		*p = L'-';
+	      LCID lcid = LocaleNameToLCID (lc, 0);
+	      UINT CodePage;
+	      if (lcid == 0 || lcid == (LCID) -1)
+		CodePage = 65001;
+	      else if (!GetLocaleInfo (lcid,
+			  LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
+			  (char *) &CodePage, sizeof (CodePage)))
+		CodePage = 65001;
+	      SetConsoleCP (CodePage);
+	      SetConsoleOutputCP (CodePage);
+	      /* Set terminal code page from locale */
+	      /* FIXME: How the code page corresponding to charset
+		 can be retrieved? */
+	      char *p1;
+	      p1 = strchr (setlocale (LC_CTYPE, NULL), '.');
+	      if (p1)
+		{
+		  if (strcasecmp (p1+1, "UTF-8") == 0)
+		    get_ttyp ()->TermCodePage = 65001;
+		  else if (strcasecmp (p1+1, "ASCII") == 0)
+		    get_ttyp ()->TermCodePage = 20127;
+		  else if (strcasecmp (p1+1, "EUC-JP") == 0)
+		    get_ttyp ()->TermCodePage = 20932;
+		  else if (strcasecmp (p1+1, "EUCJP") == 0)
+		    get_ttyp ()->TermCodePage = 20932;
+		  else if (strcasecmp (p1+1, "SJIS") == 0)
+		    get_ttyp ()->TermCodePage = 932;
+		  else if (strcasecmp (p1+1, "GB2312") == 0)
+		    get_ttyp ()->TermCodePage = 936;
+		  else if (strcasecmp (p1+1, "GBK") == 0)
+		    get_ttyp ()->TermCodePage = 936;
+		  else if (strcasecmp (p1+1, "EUC-CN") == 0)
+		    get_ttyp ()->TermCodePage = 936;
+		  else if (strcasecmp (p1+1, "EUCCN") == 0)
+		    get_ttyp ()->TermCodePage = 936;
+		  else if (strcasecmp (p1+1, "EUC-KR") == 0)
+		    get_ttyp ()->TermCodePage = 949 /*51949*/;
+		  else if (strcasecmp (p1+1, "EUCKR") == 0)
+		    get_ttyp ()->TermCodePage = 949 /*51949*/;
+		  else if (strcasecmp (p1+1, "BIG5") == 0)
+		    get_ttyp ()->TermCodePage = 950;
+		  else if (strcasecmp (p1+1, "ISO-8859-1") == 0)
+		    {
+		      if (lcid == 0x041c)
+			get_ttyp ()->TermCodePage = 1250;
+		      else if (lcid == 0x0443)
+			get_ttyp ()->TermCodePage = 1254;
+		      else
+			get_ttyp ()->TermCodePage = 1252;
+		    }
+		  else if (strcasecmp (p1+1, "ISO-8859-2") == 0)
+		    {
+		      if (lcid == 0x042e)
+			get_ttyp ()->TermCodePage = 1252;
+		      else
+			get_ttyp ()->TermCodePage = 1250;
+		    }
+		  else if (strcasecmp (p1+1, "KOI8-U") == 0)
+		    get_ttyp ()->TermCodePage = 1251;
+		  else if (strcasecmp (p1+1, "KOI8U") == 0)
+		    get_ttyp ()->TermCodePage = 1251;
+		  else if (strcasecmp (p1+1, "KOI8-R") == 0)
+		    get_ttyp ()->TermCodePage = 20866;
+		  else if (strcasecmp (p1+1, "KOI8R") == 0)
+		    get_ttyp ()->TermCodePage = 20866;
+		  else if (strcasecmp (p1+1, "ISO-8859-5") == 0)
+		    get_ttyp ()->TermCodePage = 1251;
+		  else if (strcasecmp (p1+1, "ISO-8859-14") == 0)
+		    get_ttyp ()->TermCodePage = 1252;
+		  else if (strcasecmp (p1+1, "ISO-8859-15") == 0)
+		    {
+		      if (lcid == 0x0425)
+			get_ttyp ()->TermCodePage = 1257;
+		      else
+			get_ttyp ()->TermCodePage = 1252;
+		    }
+		  else if (strcasecmp (p1+1, "ISO-8859-7") == 0)
+		    get_ttyp ()->TermCodePage = 1253;
+		  else if (strcasecmp (p1+1, "ISO-8859-9") == 0)
+		    get_ttyp ()->TermCodePage = 1254;
+		  else if (strcasecmp (p1+1, "ISO-8859-8") == 0)
+		    get_ttyp ()->TermCodePage = 1255;
+		  else if (strcasecmp (p1+1, "ISO-8859-6") == 0)
+		    get_ttyp ()->TermCodePage = 1256;
+		  else if (strcasecmp (p1+1, "ISO-8859-13") == 0)
+		    get_ttyp ()->TermCodePage = 1257;
+		  else if (strcasecmp (p1+1, "ISO-8859-3") == 0)
+		    get_ttyp ()->TermCodePage = 28593;
+		  else if (strcasecmp (p1+1, "ISO-8859-4") == 0)
+		    get_ttyp ()->TermCodePage = 28594;
+		  else if (strcasecmp (p1+1, "ISO-8859-10") == 0)
+		    get_ttyp ()->TermCodePage = 28600;
+		  else if (strcasecmp (p1+1, "ISO-8859-11") == 0)
+		    get_ttyp ()->TermCodePage = 28601;
+		  else if (strcasecmp (p1+1, "ISO-8859-16") == 0)
+		    get_ttyp ()->TermCodePage = 28606;
+		  else if (strcasecmp (p1+1, "TIS620") == 0)
+		    get_ttyp ()->TermCodePage = 874;
+		  else if (strcasecmp (p1+1, "TIS-620") == 0)
+		    get_ttyp ()->TermCodePage = 874;
+		  else if (strncasecmp (p1+1, "CP", 2) == 0)
+		    get_ttyp ()->TermCodePage = strtoul (p1+3, NULL, 0);
+		}
+	      if (get_ttyp ()->TermCodePage == 0)
+		get_ttyp ()->TermCodePage = 65001; /* Fallback */
+#if 1 /* Experimental code */
+	      /* Clear screen to synchronize pseudo console screen buffer
+		 with real terminal. */
+	      /* FIXME: Clearing sequence may not be "^[[H^[[J"
+		 depending on the terminal type. However, it is
+		 "^[[H^[[J" for pseudo console. */
+	      DWORD n;
+	      if (get_ttyp ()->num_pcon_attached_slaves == 0
+		  && !ALWAYS_USE_PCON)
+		/* Assume this is the first process using this pty slave. */
+		WriteFile (get_output_handle_cyg (),
+			   "\033[H\033[J", 6, &n, NULL);
+#endif
+	      pcon_attached = true;
+	      get_ttyp ()->num_pcon_attached_slaves ++;
+	    }
+	}
+      else
+	pcon_attached = false;
+    }
+  if (pcon_attached && native_maybe)
+    {
+      FlushConsoleInputBuffer (get_handle ());
+      DWORD mode;
+      GetConsoleMode (get_handle (), &mode);
+      SetConsoleMode (get_handle (), mode | ENABLE_ECHO_INPUT);
+      acquire_output_mutex (INFINITE);
+      get_ttyp ()->switch_to_pcon = true;
+      if (get_ttyp ()->pcon_pid == 0 ||
+	  kill (get_ttyp ()->pcon_pid, 0) != 0)
+	get_ttyp ()->pcon_pid = myself->pid;
+      release_output_mutex ();
+    }
+#if 1
+  if (pcon_attached)
+    init_console_handler (true);
+#endif
+}
+
 void
 fhandler_pty_slave::fixup_after_fork (HANDLE parent)
 {
+  fixup_after_attach (false);
   // fork_fixup (parent, inuse, "inuse");
   // fhandler_pty_common::fixup_after_fork (parent);
   report_tty_counts (this, "inherited", "");
@@ -1507,6 +2529,155 @@ fhandler_pty_slave::fixup_after_exec ()
 {
   if (!close_on_exec ())
     fixup_after_fork (NULL);
+  acquire_output_mutex (INFINITE);
+  if (get_ttyp ()->pcon_pid == myself->pid)
+    {
+      get_ttyp ()->pcon_pid = ALWAYS_USE_PCON ? myself->pid : 0;
+      get_ttyp ()->switch_to_pcon = ALWAYS_USE_PCON;
+    }
+  release_output_mutex ();
+
+  /* Hook Console API */
+  if (getPseudoConsole ())
+    {
+      void *api;
+      if (!WriteFile_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "WriteFile",
+			  (void *) WriteFile_Hooked);
+	  WriteFile_Orig = (BOOL (WINAPI *)
+		    (HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED)) api;
+	  //if (!api) system_printf("WriteFile: %p", api);
+	}
+      if (!WriteConsoleA_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "WriteConsoleA",
+			  (void *) WriteConsoleA_Hooked);
+	  WriteConsoleA_Orig = (BOOL (WINAPI *)
+		    (HANDLE, LPCVOID, DWORD, LPDWORD, LPVOID)) api;
+	  //if (!api) system_printf("WriteConsoleA: %p", api);
+	}
+      if (!WriteConsoleW_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "WriteConsoleW",
+			  (void *) WriteConsoleW_Hooked);
+	  WriteConsoleW_Orig = (BOOL (WINAPI *)
+		    (HANDLE, LPCVOID, DWORD, LPDWORD, LPVOID)) api;
+	  //if (!api) system_printf("WriteConsoleW: %p", api);
+	}
+      if (!ReadFile_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "ReadFile",
+			  (void *) ReadFile_Hooked);
+	  ReadFile_Orig = (BOOL (WINAPI *)
+		    (HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED)) api;
+	  //if (!api) system_printf("ReadFile: %p", api);
+	}
+      if (!ReadConsoleA_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "ReadConsoleA",
+			  (void *) ReadConsoleA_Hooked);
+	  ReadConsoleA_Orig = (BOOL (WINAPI *)
+		    (HANDLE, LPVOID, DWORD, LPDWORD, LPVOID)) api;
+	  //if (!api) system_printf("ReadConsoleA: %p", api);
+	}
+      if (!ReadConsoleW_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "ReadConsoleW",
+			  (void *) ReadConsoleW_Hooked);
+	  ReadConsoleW_Orig = (BOOL (WINAPI *)
+		    (HANDLE, LPVOID, DWORD, LPDWORD, LPVOID)) api;
+	  //if (!api) system_printf("ReadConsoleW: %p", api);
+	}
+      if (!WriteConsoleOutputA_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "WriteConsoleOutputA",
+			  (void *) WriteConsoleOutputA_Hooked);
+	  WriteConsoleOutputA_Orig = (BOOL (WINAPI *)
+		    (HANDLE, CONST CHAR_INFO, COORD, COORD, PSMALL_RECT)) api;
+	  //if (!api) system_printf("WriteConsoleOutputA: %p", api);
+	}
+      if (!WriteConsoleOutputW_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "WriteConsoleOutputW",
+			  (void *) WriteConsoleOutputW_Hooked);
+	  WriteConsoleOutputW_Orig = (BOOL (WINAPI *)
+		    (HANDLE, CONST CHAR_INFO, COORD, COORD, PSMALL_RECT)) api;
+	  //if (!api) system_printf("WriteConsoleOutputW: %p", api);
+	}
+      if (!WriteConsoleOutputCharacterA_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "WriteConsoleOutputCharacterA",
+			  (void *) WriteConsoleOutputCharacterA_Hooked);
+	  WriteConsoleOutputCharacterA_Orig = (BOOL (WINAPI *)
+		    (HANDLE, LPCSTR, DWORD, COORD, LPDWORD)) api;
+	  //if (!api) system_printf("WriteConsoleOutputCharacterA: %p", api);
+	}
+      if (!WriteConsoleOutputCharacterW_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "WriteConsoleOutputCharacterW",
+			  (void *) WriteConsoleOutputCharacterW_Hooked);
+	  WriteConsoleOutputCharacterW_Orig = (BOOL (WINAPI *)
+		    (HANDLE, LPCWSTR, DWORD, COORD, LPDWORD)) api;
+	  //if (!api) system_printf("WriteConsoleOutputCharacterW: %p", api);
+	}
+      if (!WriteConsoleOutputAttribute_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "WriteConsoleOutputAttribute",
+			  (void *) WriteConsoleOutputAttribute_Hooked);
+	  WriteConsoleOutputAttribute_Orig = (BOOL (WINAPI *)
+		    (HANDLE h, CONST WORD *a, DWORD l, COORD c, LPDWORD n)) api;
+	  //if (!api) system_printf("WriteConsoleOutputAttribute: %p", api);
+	}
+      if (!WriteConsoleInputA_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "WriteConsoleInputA",
+			  (void *) WriteConsoleInputA_Hooked);
+	  WriteConsoleInputA_Orig = (BOOL (WINAPI *)
+		    (HANDLE, CONST INPUT_RECORD, DWORD, LPDWORD)) api;
+	  //if (!api) system_printf("WriteConsoleInputA: %p", api);
+	}
+      if (!WriteConsoleInputW_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "WriteConsoleInputW",
+			  (void *) WriteConsoleInputW_Hooked);
+	  WriteConsoleInputW_Orig = (BOOL (WINAPI *)
+		    (HANDLE, CONST INPUT_RECORD, DWORD, LPDWORD)) api;
+	  //if (!api) system_printf("WriteConsoleInputW: %p", api);
+	}
+      if (!ReadConsoleInputA_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "ReadConsoleInputA",
+			  (void *) ReadConsoleInputA_Hooked);
+	  ReadConsoleInputA_Orig = (BOOL (WINAPI *)
+		    (HANDLE, PINPUT_RECORD, DWORD, LPDWORD)) api;
+	  //if (!api) system_printf("ReadConsoleInputA: %p", api);
+	}
+      if (!ReadConsoleInputW_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "ReadConsoleInputW",
+			  (void *) ReadConsoleInputW_Hooked);
+	  ReadConsoleInputW_Orig = (BOOL (WINAPI *)
+		    (HANDLE, PINPUT_RECORD, DWORD, LPDWORD)) api;
+	  //if (!api) system_printf("ReadConsoleInputW: %p", api);
+	}
+      if (!PeekConsoleInputA_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "PeekConsoleInputA",
+			  (void *) PeekConsoleInputA_Hooked);
+	  PeekConsoleInputA_Orig = (BOOL (WINAPI *)
+		    (HANDLE, PINPUT_RECORD, DWORD, LPDWORD)) api;
+	  //if (!api) system_printf("PeekConsoleInputA: %p", api);
+	}
+      if (!PeekConsoleInputW_Orig)
+	{
+	  api = hook_api ("kernel32.dll", "PeekConsoleInputW",
+			  (void *) PeekConsoleInputW_Hooked);
+	  PeekConsoleInputW_Orig = (BOOL (WINAPI *)
+		    (HANDLE, PINPUT_RECORD, DWORD, LPDWORD)) api;
+	  //if (!api) system_printf("PeekConsoleInputW: %p", api);
+	}
+    }
 }
 
 /* This thread function handles the master control pipe.  It waits for a
@@ -1544,7 +2715,7 @@ fhandler_pty_master::pty_master_thread ()
   while (!exit && (ConnectNamedPipe (master_ctl, NULL)
 		   || GetLastError () == ERROR_PIPE_CONNECTED))
     {
-      pipe_reply repl = { NULL, NULL, 0 };
+      pipe_reply repl = { NULL, NULL, NULL, 0 };
       bool deimp = false;
       NTSTATUS allow = STATUS_ACCESS_DENIED;
       ACCESS_MASK access = EVENT_MODIFY_STATE;
@@ -1614,6 +2785,13 @@ fhandler_pty_master::pty_master_thread ()
 	      termios_printf ("DuplicateHandle (from_master), %E");
 	      goto reply;
 	    }
+	  if (!DuplicateHandle (GetCurrentProcess (), from_master_cyg,
+			       client, &repl.from_master_cyg,
+			       0, TRUE, DUPLICATE_SAME_ACCESS))
+	    {
+	      termios_printf ("DuplicateHandle (from_master_cyg), %E");
+	      goto reply;
+	    }
 	  if (!DuplicateHandle (GetCurrentProcess (), to_master,
 				client, &repl.to_master,
 				0, TRUE, DUPLICATE_SAME_ACCESS))
@@ -1657,25 +2835,110 @@ DWORD
 fhandler_pty_master::pty_master_fwd_thread ()
 {
   DWORD rlen;
-  char outbuf[OUT_BUFFER_SIZE];
+  char outbuf[65536];
 
-  termios_printf("Started.");
+  termios_printf ("Started.");
   for (;;)
     {
-      if (!ReadFile (get_handle (), outbuf, sizeof outbuf, &rlen, NULL))
+      if (!ReadFile (from_slave, outbuf, sizeof outbuf, &rlen, NULL))
 	{
 	  termios_printf ("ReadFile for forwarding failed, %E");
 	  break;
 	}
       ssize_t wlen = rlen;
+      char *ptr = outbuf;
+      if (getPseudoConsole ())
+	{
+	  /* Avoid duplicating slave output which is already sent to
+	     to_master_cyg */
+	  if (!get_ttyp ()->switch_to_pcon)
+	    continue;
+
+	  /* Avoid setting window title to "cygwin-console-helper.exe" */
+	  int state = 0;
+	  int start_at = 0;
+	  for (DWORD i=0; i<rlen; i++)
+	    if (outbuf[i] == '\033')
+	      {
+		start_at = i;
+		state = 1;
+		continue;
+	      }
+	    else if ((state == 1 && outbuf[i] == ']') ||
+		     (state == 2 && outbuf[i] == '0') ||
+		     (state == 3 && outbuf[i] == ';') ||
+		     (state == 4 && outbuf[i] == '\0'))
+	      {
+		state ++;
+		continue;
+	      }
+	    else if (state == 5 && outbuf[i] == '\a')
+	      {
+		memmove (&outbuf[start_at], &outbuf[i+1], rlen-i-1);
+		state = 0;
+		rlen = wlen = start_at + rlen - i - 1;
+		continue;
+	      }
+	    else if (state != 4 || outbuf[i] == '\a')
+	      {
+		state = 0;
+		continue;
+	      }
+
+	  char *buf;
+	  size_t nlen;
+	  if (get_ttyp ()->TermCodePage != CP_UTF8)
+	    {
+	      size_t wlen2 =
+		MultiByteToWideChar (CP_UTF8, 0,
+				     (char *)ptr, wlen, NULL, 0);
+	      wchar_t *wbuf = (wchar_t *)
+		HeapAlloc (GetProcessHeap (), 0, wlen2 * sizeof (wchar_t));
+	      wlen2 =
+		MultiByteToWideChar (CP_UTF8, 0,
+				     (char *)ptr, wlen, wbuf, wlen2);
+	      nlen = WideCharToMultiByte (get_ttyp ()->TermCodePage, 0,
+					  wbuf, wlen2, NULL, 0, NULL, NULL);
+	      buf = (char *) HeapAlloc (GetProcessHeap (), 0, nlen);
+	      nlen = WideCharToMultiByte (get_ttyp ()->TermCodePage, 0,
+					  wbuf, wlen2, buf, nlen, NULL, NULL);
+	      HeapFree (GetProcessHeap (), 0, wbuf);
+	    }
+	  else
+	    {
+	      /* Just copy */
+	      buf = (char *) HeapAlloc (GetProcessHeap (), 0, wlen);
+	      memcpy (buf, (char *)ptr, wlen);
+	      nlen = wlen;
+	    }
+	  ptr = buf;
+	  wlen = rlen = nlen;
+
+	  /* OPOST processing was already done in pseudo console,
+	     so just write it to to_master_cyg. */
+	  DWORD written;
+	  while (rlen>0)
+	    {
+	      if (!WriteFile (to_master_cyg, ptr, wlen, &written, NULL))
+		{
+		  termios_printf ("WriteFile for forwarding failed, %E");
+		  break;
+		}
+	      ptr += written;
+	      wlen = (rlen -= written);
+	    }
+	  HeapFree (GetProcessHeap (), 0, buf);
+	  continue;
+	}
       while (rlen>0)
 	{
-	  if (!process_opost_output (to_master_cyg, outbuf, wlen, false))
+	  if (!process_opost_output (to_master_cyg, ptr, wlen, false))
 	    {
 	      termios_printf ("WriteFile for forwarding failed, %E");
 	      break;
 	    }
-	  rlen -= wlen;
+	  ptr += wlen;
+	  wlen = (rlen -= wlen);
 	}
     }
   return 0;
@@ -1687,6 +2950,135 @@ pty_master_fwd_thread (VOID *arg)
   return ((fhandler_pty_master *) arg)->pty_master_fwd_thread ();
 }
 
+/* If master process is running as service, attaching to
+   pseudo console should be done in fork. If attaching
+   is done in spawn for inetd or sshd, it fails because
+   the helper process is running as privileged user while
+   slave process is not. This function is used to determine
+   if the process is running as a srvice or not. */
+static bool
+is_running_as_service (void)
+{
+  DWORD dwSize = 0;
+  PTOKEN_GROUPS pGroupInfo;
+  tmp_pathbuf tp;
+  pGroupInfo = (PTOKEN_GROUPS) tp.w_get ();
+  NtQueryInformationToken (hProcToken, TokenGroups, pGroupInfo,
+					2 * NT_MAX_PATH, &dwSize);
+  for (DWORD i=0; i<pGroupInfo->GroupCount; i++)
+    if (RtlEqualSid (well_known_service_sid, pGroupInfo->Groups[i].Sid))
+      return true;
+  for (DWORD i=0; i<pGroupInfo->GroupCount; i++)
+    if (RtlEqualSid (well_known_interactive_sid, pGroupInfo->Groups[i].Sid))
+      return false;
+  return true;
+}
+
+bool
+fhandler_pty_master::setup_pseudoconsole ()
+{
+  /* Pseudo console supprot is realized using a tricky technic.
+     PTY need the pseudo console handles, however, they cannot
+     be retrieved by normal procedure. Therefore, run a helper
+     process in a pseudo console and get them from the helper.
+     Slave process will attach to the pseudo console in the
+     helper process using AttachConsole(). */
+  HMODULE hModule = GetModuleHandle ("kernel32.dll");
+  FARPROC func = GetProcAddress (hModule, "CreatePseudoConsole");
+  HRESULT (WINAPI *CreatePseudoConsole)
+    (COORD, HANDLE, HANDLE, DWORD, HPCON *) = NULL;
+  if (!func)
+    return false;
+  CreatePseudoConsole =
+    (HRESULT (WINAPI *) (COORD, HANDLE, HANDLE, DWORD, HPCON *)) func;
+  COORD size = {80, 25};
+  CreatePipe (&from_master, &to_slave, &sec_none, 0);
+  HRESULT res = CreatePseudoConsole (size, from_master, to_master,
+				     0, &get_ttyp ()->hPseudoConsole);
+  if (res != S_OK)
+    {
+      system_printf ("CreatePseudoConsole() failed. %08x\n",
+		     GetLastError());
+      CloseHandle (from_master);
+      CloseHandle (to_slave);
+      from_master = from_master_cyg;
+      to_slave = NULL;
+      get_ttyp ()->hPseudoConsole = NULL;
+      return false;
+    }
+
+  /* If master process is running as service, attaching to
+     pseudo console should be done in fork. If attaching
+     is done in spawn for inetd or sshd, it fails because
+     the helper process is running as privileged user while
+     slave process is not. */
+  if (is_running_as_service ())
+    get_ttyp ()->attach_pcon_in_fork = true;
+
+  SIZE_T bytesRequired;
+  InitializeProcThreadAttributeList (NULL, 1, 0, &bytesRequired);
+  STARTUPINFOEXW si_helper;
+  ZeroMemory (&si_helper, sizeof (si_helper));
+  si_helper.StartupInfo.cb = sizeof (STARTUPINFOEXW);
+  si_helper.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)
+    HeapAlloc (GetProcessHeap (), 0, bytesRequired);
+  InitializeProcThreadAttributeList (si_helper.lpAttributeList,
+				     1, 0, &bytesRequired);
+  UpdateProcThreadAttribute (si_helper.lpAttributeList,
+			     0,
+			     PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
+			     get_ttyp ()->hPseudoConsole,
+			     sizeof (get_ttyp ()->hPseudoConsole),
+			     NULL, NULL);
+  HANDLE hello = CreateEvent (&sec_none, true, false, NULL);
+  HANDLE goodbye = CreateEvent (&sec_none, true, false, NULL);
+  /* Create a pipe for receiving pseudo console handles */
+  HANDLE hr, hw;
+  CreatePipe (&hr, &hw, &sec_none, 0);
+  /* Create helper process */
+  WCHAR cmd[256];
+  path_conv helper ("/bin/cygwin-console-helper.exe");
+  size_t len = helper.get_wide_win32_path_len ();
+  helper.get_wide_win32_path (cmd);
+  __small_swprintf (cmd + len, L" %p %p %p", hello, goodbye, hw);
+  si_helper.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
+  si_helper.StartupInfo.hStdInput = NULL;
+  si_helper.StartupInfo.hStdOutput = NULL;
+  si_helper.StartupInfo.hStdError = NULL;
+  PROCESS_INFORMATION pi_helper;
+  CreateProcessW (NULL, cmd, &sec_none, &sec_none,
+		  TRUE, EXTENDED_STARTUPINFO_PRESENT,
+		  NULL, NULL, &si_helper.StartupInfo, &pi_helper);
+  WaitForSingleObject (hello, INFINITE);
+  /* Retrieve pseudo console handles */
+  DWORD rLen;
+  char buf[64];
+  ReadFile (hr, buf, sizeof (buf), &rLen, NULL);
+  buf[rLen] = '\0';
+  HANDLE hpConIn, hpConOut;
+  sscanf (buf, "StdHandles=%p,%p", &hpConIn, &hpConOut);
+  DuplicateHandle (pi_helper.hProcess, hpConIn,
+		   GetCurrentProcess (), &hpConIn, 0,
+		   TRUE, DUPLICATE_SAME_ACCESS);
+  DuplicateHandle (pi_helper.hProcess, hpConOut,
+		   GetCurrentProcess (), &hpConOut, 0,
+		   TRUE, DUPLICATE_SAME_ACCESS);
+  CloseHandle (hr);
+  CloseHandle (hw);
+  /* Clean up */
+  DeleteProcThreadAttributeList (si_helper.lpAttributeList);
+  HeapFree (GetProcessHeap (), 0, si_helper.lpAttributeList);
+  /* Setting information of stuffs regarding pseudo console */
+  get_ttyp ()->hHelperGoodbye = goodbye;
+  get_ttyp ()->hHelperProcess = pi_helper.hProcess;
+  get_ttyp ()->HelperProcessId = pi_helper.dwProcessId;
+  CloseHandle (from_master);
+  CloseHandle (to_master);
+  from_master = hpConIn;
+  to_master = hpConOut;
+  return true;
+}
+
 bool
 fhandler_pty_master::setup ()
 {
@@ -1695,10 +3087,15 @@ fhandler_pty_master::setup ()
   SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE };
 
   /* Find an unallocated pty to use. */
-  int unit = cygwin_shared->tty.allocate (from_master, get_output_handle ());
+  int unit = cygwin_shared->tty.allocate (from_master_cyg, get_output_handle ());
   if (unit < 0)
     return false;
 
+  /* from_master should be used for pseudo console.
+     Just copy from_master_cyg here for the case that
+     pseudo console is not available. */
+  from_master = from_master_cyg;
+
   ProtectHandle1 (get_output_handle (), to_pty);
 
   tty& t = *cygwin_shared->tty[unit];
@@ -1715,7 +3112,7 @@ fhandler_pty_master::setup ()
 
   char pipename[sizeof("ptyNNNN-to-master-cyg")];
   __small_sprintf (pipename, "pty%d-to-master", unit);
-  res = fhandler_pipe::create (&sec_none, &get_handle (), &to_master,
+  res = fhandler_pipe::create (&sec_none, &from_slave, &to_master,
 			       fhandler_pty_common::pipesize, pipename, 0);
   if (res)
     {
@@ -1724,7 +3121,7 @@ fhandler_pty_master::setup ()
     }
 
   __small_sprintf (pipename, "pty%d-to-master-cyg", unit);
-  res = fhandler_pipe::create (&sec_none, &get_handle_cyg (), &to_master_cyg,
+  res = fhandler_pipe::create (&sec_none, &get_handle (), &to_master_cyg,
 			       fhandler_pty_common::pipesize, pipename, 0);
   if (res)
     {
@@ -1732,7 +3129,7 @@ fhandler_pty_master::setup ()
       goto err;
     }
 
-  ProtectHandle1 (get_handle (), from_pty);
+  ProtectHandle1 (from_slave, from_pty);
 
   __small_sprintf (pipename, "pty%d-echoloop", unit);
   res = fhandler_pipe::create (&sec_none, &echo_r, &echo_w,
@@ -1797,7 +3194,10 @@ fhandler_pty_master::setup ()
       goto err;
     }
 
+  setup_pseudoconsole ();
+
   t.set_from_master (from_master);
+  t.set_from_master_cyg (from_master_cyg);
   t.set_to_master (to_master);
   t.set_to_master_cyg (to_master_cyg);
   t.winsize.ws_col = 80;
@@ -1807,19 +3207,20 @@ fhandler_pty_master::setup ()
   dev ().parse (DEV_PTYM_MAJOR, unit);
 
   termios_printf ("this %p, pty%d opened - from_pty <%p,%p>, to_pty %p",
-	this, unit, get_handle (), get_handle_cyg (),
+	this, unit, from_slave, get_handle (),
 	get_output_handle ());
   return true;
 
 err:
   __seterrno ();
+  close_maybe (from_slave);
   close_maybe (get_handle ());
-  close_maybe (get_handle_cyg ());
   close_maybe (get_output_handle ());
   close_maybe (input_available_event);
   close_maybe (output_mutex);
   close_maybe (input_mutex);
   close_maybe (from_master);
+  close_maybe (from_master_cyg);
   close_maybe (to_master);
   close_maybe (to_master_cyg);
   close_maybe (echo_r);
@@ -1840,14 +3241,20 @@ fhandler_pty_master::fixup_after_fork (HANDLE parent)
       if (myself->pid == t.master_pid)
 	{
 	  t.set_from_master (arch->from_master);
+	  t.set_from_master_cyg (arch->from_master_cyg);
 	  t.set_to_master (arch->to_master);
 	  t.set_to_master_cyg (arch->to_master_cyg);
 	}
       arch->dwProcessId = wpid;
     }
   from_master = arch->from_master;
+  from_master_cyg = arch->from_master_cyg;
   to_master = arch->to_master;
   to_master_cyg = arch->to_master_cyg;
+#if 0 /* Not sure if this is necessary. */
+  from_slave = arch->from_slave;
+  to_slave = arch->to_slave;
+#endif
   report_tty_counts (this, "inherited master", "");
 }
 
@@ -1857,7 +3264,8 @@ fhandler_pty_master::fixup_after_exec ()
   if (!close_on_exec ())
     fixup_after_fork (spawn_info->parent);
   else
-    from_master = to_master = to_master_cyg = NULL;
+    from_master = from_master_cyg = to_master = to_master_cyg =
+      from_slave = to_slave = NULL;
 }
 
 BOOL
@@ -1865,6 +3273,9 @@ fhandler_pty_common::process_opost_output (HANDLE h, const void *ptr, ssize_t& l
 {
   ssize_t towrite = len;
   BOOL res = TRUE;
+  BOOL (WINAPI *WriteFunc)
+    (HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED);
+  WriteFunc = WriteFile_Orig ? WriteFile_Orig : WriteFile;
   while (towrite)
     {
       if (!is_echo)
@@ -1887,7 +3298,7 @@ fhandler_pty_common::process_opost_output (HANDLE h, const void *ptr, ssize_t& l
       if (!(get_ttyp ()->ti.c_oflag & OPOST))	// raw output mode
 	{
 	  DWORD n = MIN (OUT_BUFFER_SIZE, towrite);
-	  res = WriteFile (h, ptr, n, &n, NULL);
+	  res = WriteFunc (h, ptr, n, &n, NULL);
 	  if (!res)
 	    break;
 	  ptr = (char *) ptr + n;
@@ -1939,7 +3350,7 @@ fhandler_pty_common::process_opost_output (HANDLE h, const void *ptr, ssize_t& l
 		}
 	    }
 	  release_output_mutex ();
-	  res = WriteFile (h, outbuf, n, &n, NULL);
+	  res = WriteFunc (h, outbuf, n, &n, NULL);
 	  if (!res)
 	    break;
 	  ptr = (char *) ptr + rc;
diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc
index 7080144b9..0c089dbe9 100644
--- a/winsup/cygwin/fork.cc
+++ b/winsup/cygwin/fork.cc
@@ -134,6 +134,30 @@ child_info::prefork (bool detached)
 int __stdcall
 frok::child (volatile char * volatile here)
 {
+  cygheap_fdenum cfd (false);
+  while (cfd.next () >= 0)
+    if (cfd->get_major () == DEV_PTYM_MAJOR)
+      {
+	fhandler_base *fh = cfd;
+	fhandler_pty_master *ptym = (fhandler_pty_master *) fh;
+	if (ptym->getPseudoConsole () &&
+	    !fhandler_console::get_console_process_id (
+				ptym->getHelperProcessId (), true))
+
+	  {
+	    debug_printf ("found a PTY master %d: helper_PID=%d",
+			  ptym->get_minor (), ptym->getHelperProcessId ());
+	    if (ptym->attach_pcon_in_fork ())
+	      {
+		FreeConsole ();
+		if (!AttachConsole (ptym->getHelperProcessId ()))
+		  /* Error */;
+		else
+		  break;
+	      }
+	  }
+      }
+
   HANDLE& hParent = ch.parent;
 
   sync_with_parent ("after longjmp", true);
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index ac73e0a88..d29f3d2f4 100644
--- a/winsup/cygwin/select.cc
+++ b/winsup/cygwin/select.cc
@@ -667,6 +667,9 @@ peek_pipe (select_record *s, bool from_select)
 	    fhm->flush_to_slave ();
 	  }
 	  break;
+	case DEV_PTYS_MAJOR:
+	  ((fhandler_pty_slave *) fh)->reset_switch_to_pcon ();
+	  break;
 	default:
 	  if (fh->get_readahead_valid ())
 	    {
@@ -1178,17 +1181,32 @@ verify_tty_slave (select_record *me, fd_set *readfds, fd_set *writefds,
   return set_bits (me, readfds, writefds, exceptfds);
 }
 
+static int
+pty_slave_startup (select_record *s, select_stuff *)
+{
+  fhandler_base *fh = (fhandler_base *) s->fh;
+  ((fhandler_pty_slave *) fh)->mask_switch_to_pcon (true);
+  return 1;
+}
+
+static void
+pty_slave_cleanup (select_record *s, select_stuff *)
+{
+  fhandler_base *fh = (fhandler_base *) s->fh;
+  ((fhandler_pty_slave *) fh)->mask_switch_to_pcon (false);
+}
+
 select_record *
 fhandler_pty_slave::select_read (select_stuff *ss)
 {
   select_record *s = ss->start.next;
   s->h = input_available_event;
-  s->startup = no_startup;
+  s->startup = pty_slave_startup;
   s->peek = peek_pipe;
   s->verify = verify_tty_slave;
   s->read_selected = true;
   s->read_ready = false;
-  s->cleanup = NULL;
+  s->cleanup = pty_slave_cleanup;
   return s;
 }
 
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index d95772802..b4045fb12 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -288,6 +288,54 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
       return -1;
     }
 
+  DWORD pidRestore =
+    fhandler_console::get_console_process_id (GetCurrentProcessId (), false);
+  bool attach_to_pcon = false;
+  fhandler_pty_slave *ptys = NULL;
+  for (int fd = 0; fd < 3; fd ++)
+    {
+      fhandler_base *fh = ::cygheap->fdtab[fd];
+      if (fh && fh->get_major () == DEV_PTYS_MAJOR)
+	{
+	  ptys = (fhandler_pty_slave *) fh;
+	  if (ptys->getPseudoConsole () &&
+	      !fhandler_console::get_console_process_id (
+				  ptys->getHelperProcessId (), true))
+	    {
+	      DWORD dwHelperProcessId = ptys->getHelperProcessId ();
+	      debug_printf ("found a PTY slave %d: helper_PID=%d",
+			    fh->get_minor (), dwHelperProcessId);
+	      FreeConsole ();
+	      if (!AttachConsole (dwHelperProcessId))
+		{
+		  /* Fallback */
+		  DWORD target[3] = {
+		    STD_INPUT_HANDLE,
+		    STD_OUTPUT_HANDLE,
+		    STD_ERROR_HANDLE
+		  };
+		  if (fd == 0)
+		    {
+		      ptys->set_handle (ptys->get_handle_cyg ());
+		      SetStdHandle (target[fd], ptys->get_handle ());
+		    }
+		  else
+		    {
+		      ptys->set_output_handle (ptys->get_output_handle_cyg ());
+		      SetStdHandle (target[fd], ptys->get_output_handle ());
+		    }
+		}
+	      else
+		{
+		  attach_to_pcon = true;
+		  break;
+		}
+	    }
+	}
+    }
+  if (ptys)
+    ptys->fixup_after_attach (true);
+
   av newargv;
   linebuf cmd;
   PWCHAR envblock = NULL;
@@ -869,6 +917,13 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
   this->cleanup ();
   if (envblock)
     free (envblock);
+
+  if (attach_to_pcon && pidRestore)
+    {
+      FreeConsole ();
+      AttachConsole (pidRestore);
+    }
+
   return (int) res;
 }
 
diff --git a/winsup/cygwin/strace.cc b/winsup/cygwin/strace.cc
index 35f8a59ae..b1eb5f3e4 100644
--- a/winsup/cygwin/strace.cc
+++ b/winsup/cygwin/strace.cc
@@ -279,6 +279,30 @@ strace::vprntf (unsigned category, const char *func, const char *fmt, va_list ap
 	      CloseHandle (h);
 	    }
 	}
+#if 1 /* Experimental code */
+      /* PTY with pseudo console cannot display data written to
+	 STD_ERROR_HANDLE (output_handle) if the process is cygwin
+	 process. output_handle works only in native console apps.
+	 Therefore the data should be written to output_handle_cyg
+	 as well. */
+      fhandler_base *fh = ::cygheap->fdtab[2];
+      if (fh && fh->get_major () == DEV_PTYS_MAJOR)
+	{
+	  fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
+	  if (ptys->getPseudoConsole ())
+	    {
+	      HANDLE h_cyg = ptys->get_output_handle_cyg ();
+	      if (buf[len-1] == '\n' && len < NT_MAX_PATH - 1)
+		{
+		  buf[len-1] = '\r';
+		  buf[len] = '\n';
+		  len ++;
+		}
+	      WriteFile (h_cyg, buf, len, &done, 0);
+	      FlushFileBuffers (h_cyg);
+	    }
+	}
+#endif
     }
 
 #ifndef NOSTRACE
diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc
index ad46cb312..006f07b1a 100644
--- a/winsup/cygwin/tty.cc
+++ b/winsup/cygwin/tty.cc
@@ -234,7 +234,15 @@ tty::init ()
   was_opened = false;
   master_pid = 0;
   is_console = false;
+  attach_pcon_in_fork = false;
+  hPseudoConsole = NULL;
   column = 0;
+  switch_to_pcon = false;
+  screen_alternated = false;
+  mask_switch_to_pcon = false;
+  pcon_pid = 0;
+  num_pcon_attached_slaves = 0;
+  TermCodePage = 65001;
 }
 
 HANDLE
diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h
index 9aee43b9c..d59b2027d 100644
--- a/winsup/cygwin/tty.h
+++ b/winsup/cygwin/tty.h
@@ -28,6 +28,8 @@ details. */
 #define MIN_CTRL_C_SLOP 50
 #endif
 
+typedef void *HPCON;
+
 #include <devices.h>
 class tty_min
 {
@@ -88,14 +90,28 @@ public:
 
 private:
   HANDLE _from_master;
+  HANDLE _from_master_cyg;
   HANDLE _to_master;
   HANDLE _to_master_cyg;
+  HPCON hPseudoConsole;
+  HANDLE hHelperProcess;
+  DWORD HelperProcessId;
+  HANDLE hHelperGoodbye;
+  bool attach_pcon_in_fork;
+  bool switch_to_pcon;
+  bool screen_alternated;
+  bool mask_switch_to_pcon;
+  pid_t pcon_pid;
+  int num_pcon_attached_slaves;
+  UINT TermCodePage;
 
 public:
-  HANDLE from_master() const { return _from_master; }
-  HANDLE to_master() const { return _to_master; }
-  HANDLE to_master_cyg() const { return _to_master_cyg; }
+  HANDLE from_master () const { return _from_master; }
+  HANDLE from_master_cyg () const { return _from_master_cyg; }
+  HANDLE to_master () const { return _to_master; }
+  HANDLE to_master_cyg () const { return _to_master_cyg; }
   void set_from_master (HANDLE h) { _from_master = h; }
+  void set_from_master_cyg (HANDLE h) { _from_master_cyg = h; }
   void set_to_master (HANDLE h) { _to_master = h; }
   void set_to_master_cyg (HANDLE h) { _to_master_cyg = h; }
 
@@ -117,7 +133,9 @@ public:
   void set_master_ctl_closed () {master_pid = -1;}
   static void __stdcall create_master (int);
   static void __stdcall init_session ();
+  friend class fhandler_pty_common;
   friend class fhandler_pty_master;
+  friend class fhandler_pty_slave;
 };
 
 class tty_list
diff --git a/winsup/utils/cygwin-console-helper.cc b/winsup/utils/cygwin-console-helper.cc
index 0e04f4d18..ad451ecf5 100644
--- a/winsup/utils/cygwin-console-helper.cc
+++ b/winsup/utils/cygwin-console-helper.cc
@@ -1,12 +1,24 @@
 #include <windows.h>
+#include <stdio.h>
 int
 main (int argc, char **argv)
 {
   char *end;
-  if (argc != 3)
+  if (argc < 3)
     exit (1);
   HANDLE h = (HANDLE) strtoull (argv[1], &end, 0);
   SetEvent (h);
+  if (argc == 4) /* Pseudo console helper mode for PTY */
+    {
+      HANDLE hPipe = (HANDLE) strtoull (argv[3], &end, 0);
+      char buf[64];
+      sprintf (buf, "StdHandles=%p,%p\n",
+	       GetStdHandle (STD_INPUT_HANDLE),
+	       GetStdHandle (STD_OUTPUT_HANDLE));
+      DWORD dwLen;
+      WriteFile (hPipe, buf, strlen (buf), &dwLen, NULL);
+      CloseHandle (hPipe);
+    }
   h = (HANDLE) strtoull (argv[2], &end, 0);
   WaitForSingleObject (h, INFINITE);
   exit (0);
-- 
2.21.0

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v8 0/1] Pseudo console support in PTY (v8)
  2019-08-21 15:43 [PATCH v8 0/1] Pseudo console support in PTY (v8) Takashi Yano
  2019-08-21 15:43 ` [PATCH v8 1/1] Cygwin: pty: add pseudo console support Takashi Yano
@ 2019-08-23 22:30 ` Thomas Wolff
  2019-08-24  6:18   ` Takashi Yano
  1 sibling, 1 reply; 14+ messages in thread
From: Thomas Wolff @ 2019-08-23 22:30 UTC (permalink / raw)
  To: cygwin-developers

Am 21.08.2019 um 17:42 schrieb Takashi Yano:
> Changes from v7:
> Make the programs work which call WIN32 APIs such as WriteConsole()
> directly despite being linked with cygwin1.dll. This is realized
> by hooking some WIN32 APIs and switching I/O to the pseudo console
> if console access is detected.
>
> ...
I've retested my cases, and also checked WriteConsole and WriteFile 
cases. These work fine now, amazing!

Character set conversion for non-cygwin programs in a non-UTF-8 terminal 
still does not work for me. But that is a minor issue, at least it's not 
worse than before.

Switching to the alternate screen buffer and back via escape sequence is 
now completely disabled while running a non-cygwin program like cmd.exe. 
This is effectively fine, I just wonder how much effort you put into 
handling that (it seemed to work well already in v7 when switching was 
still possible); you can still force-switch from the mintty extended 
context menu and see it forced back on further interaction...

After all, I'm looking forward to the integration of your patch in 
cygwin, so I can close long-standing mintty issue #56 :)
Many thanks
Thomas

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v8 0/1] Pseudo console support in PTY (v8)
  2019-08-23 22:30 ` [PATCH v8 0/1] Pseudo console support in PTY (v8) Thomas Wolff
@ 2019-08-24  6:18   ` Takashi Yano
  2019-08-24 22:22     ` Thomas Wolff
  0 siblings, 1 reply; 14+ messages in thread
From: Takashi Yano @ 2019-08-24  6:18 UTC (permalink / raw)
  To: cygwin-developers

Hi Thomas,

Thank you very much for testing.

On Sat, 24 Aug 2019 00:30:02 +0200
Thomas Wolff wrote:
> Character set conversion for non-cygwin programs in a non-UTF-8 terminal 
> still does not work for me. But that is a minor issue, at least it's not 
> worse than before.

How do you start mintty for non-UTF-8?

I tested using your STC in mintty start by 'mintty -o Charset=CP1252'.
It seems to work as expected.

-- 
Takashi Yano <takashi.yano@nifty.ne.jp>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v8 0/1] Pseudo console support in PTY (v8)
  2019-08-24  6:18   ` Takashi Yano
@ 2019-08-24 22:22     ` Thomas Wolff
  2019-08-27 18:06       ` Takashi Yano
  0 siblings, 1 reply; 14+ messages in thread
From: Thomas Wolff @ 2019-08-24 22:22 UTC (permalink / raw)
  To: cygwin-developers

Am 24.08.2019 um 08:18 schrieb Takashi Yano:
> Hi Thomas,
>
> Thank you very much for testing.
>
> On Sat, 24 Aug 2019 00:30:02 +0200
> Thomas Wolff wrote:
>> Character set conversion for non-cygwin programs in a non-UTF-8 terminal
>> still does not work for me. But that is a minor issue, at least it's not
>> worse than before.
> How do you start mintty for non-UTF-8?
>
> I tested using your STC in mintty start by 'mintty -o Charset=CP1252'.
> It seems to work as expected.
Option Charset needs an additional option Locale to have an effect.
The easier way is
LC_ALL=de_DE mintty
or
LC_ALL=ja_JP mintty

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v8 1/1] Cygwin: pty: add pseudo console support.
  2019-08-21 15:43 ` [PATCH v8 1/1] Cygwin: pty: add pseudo console support Takashi Yano
@ 2019-08-27  8:24   ` Corinna Vinschen
  2019-08-27  8:39     ` Thomas Wolff
  0 siblings, 1 reply; 14+ messages in thread
From: Corinna Vinschen @ 2019-08-27  8:24 UTC (permalink / raw)
  To: Takashi Yano
  Cc: cygwin-developers, Thomas Wolff, Ken Brown, Yaakov Selkowitz, Jon TURNEY

[-- Attachment #1: Type: text/plain, Size: 638 bytes --]

On Aug 22 00:42, Takashi Yano wrote:
> - Support pseudo console in PTY. Pseudo console is a new feature
>   in Windows 10 1809, which provides console APIs on virtual
>   terminal. With this patch, native console applications can work
>   in PTY such as mintty, ssh, gnu screen or tmux.

Great, the stderr problem is definitely fixed.  I've extremely limited
time until late October, so I'll push the release into late October or
November.  I think, given the time frame, we could easily push this
change into 3.1.

Does anybody have problems with that?


Thanks a lot,
Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v8 1/1] Cygwin: pty: add pseudo console support.
  2019-08-27  8:24   ` Corinna Vinschen
@ 2019-08-27  8:39     ` Thomas Wolff
  2019-08-27  8:47       ` Corinna Vinschen
  0 siblings, 1 reply; 14+ messages in thread
From: Thomas Wolff @ 2019-08-27  8:39 UTC (permalink / raw)
  To: Takashi Yano, cygwin-developers, Ken Brown, Yaakov Selkowitz, Jon TURNEY

Am 27.08.2019 um 10:24 schrieb Corinna Vinschen:
> On Aug 22 00:42, Takashi Yano wrote:
>> - Support pseudo console in PTY. Pseudo console is a new feature
>>    in Windows 10 1809, which provides console APIs on virtual
>>    terminal. With this patch, native console applications can work
>>    in PTY such as mintty, ssh, gnu screen or tmux.
> Great, the stderr problem is definitely fixed.  I've extremely limited
> time until late October, so I'll push the release into late October or
> November.  I think, given the time frame, we could easily push this
> change into 3.1.
>
> Does anybody have problems with that?
I don't, but as it's a big issue, it may be reasonable to have it in a 
test release well before final release.
Thomas

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v8 1/1] Cygwin: pty: add pseudo console support.
  2019-08-27  8:39     ` Thomas Wolff
@ 2019-08-27  8:47       ` Corinna Vinschen
  2019-08-27 15:52         ` Takashi Yano
  0 siblings, 1 reply; 14+ messages in thread
From: Corinna Vinschen @ 2019-08-27  8:47 UTC (permalink / raw)
  To: Thomas Wolff
  Cc: Takashi Yano, cygwin-developers, Ken Brown, Yaakov Selkowitz, Jon TURNEY

[-- Attachment #1: Type: text/plain, Size: 962 bytes --]

On Aug 27 10:38, Thomas Wolff wrote:
> Am 27.08.2019 um 10:24 schrieb Corinna Vinschen:
> > On Aug 22 00:42, Takashi Yano wrote:
> > > - Support pseudo console in PTY. Pseudo console is a new feature
> > >    in Windows 10 1809, which provides console APIs on virtual
> > >    terminal. With this patch, native console applications can work
> > >    in PTY such as mintty, ssh, gnu screen or tmux.
> > Great, the stderr problem is definitely fixed.  I've extremely limited
> > time until late October, so I'll push the release into late October or
> > November.  I think, given the time frame, we could easily push this
> > change into 3.1.
> > 
> > Does anybody have problems with that?
> I don't, but as it's a big issue, it may be reasonable to have it in a test
> release well before final release.
> Thomas

Yeah, I'll certainly create a test release as soon as I pushed the patch.


Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v8 1/1] Cygwin: pty: add pseudo console support.
  2019-08-27  8:47       ` Corinna Vinschen
@ 2019-08-27 15:52         ` Takashi Yano
  2019-08-27 15:58           ` Corinna Vinschen
  0 siblings, 1 reply; 14+ messages in thread
From: Takashi Yano @ 2019-08-27 15:52 UTC (permalink / raw)
  To: cygwin-developers

Hi Corinna,

On Tue, 27 Aug 2019 10:47:31 +0200
Corinna Vinschen wrote:
> On Aug 27 10:38, Thomas Wolff wrote:
> > Am 27.08.2019 um 10:24 schrieb Corinna Vinschen:
> > > On Aug 22 00:42, Takashi Yano wrote:
> > > > - Support pseudo console in PTY. Pseudo console is a new feature
> > > >    in Windows 10 1809, which provides console APIs on virtual
> > > >    terminal. With this patch, native console applications can work
> > > >    in PTY such as mintty, ssh, gnu screen or tmux.
> > > Great, the stderr problem is definitely fixed.  I've extremely limited
> > > time until late October, so I'll push the release into late October or
> > > November.  I think, given the time frame, we could easily push this
> > > change into 3.1.
> > > 
> > > Does anybody have problems with that?
> > I don't, but as it's a big issue, it may be reasonable to have it in a test
> > release well before final release.
> > Thomas
> 
> Yeah, I'll certainly create a test release as soon as I pushed the patch.

I will post v9 patch as soo as possible. Could you please wait it?

v9 patch will be:
- Fix again code conversion to make it consistent with that of mintty.
- Fix management of the state variable which keep attach status
  to the pseudo console.
- Fix small bug and refacter codes regarding API hook.

-- 
Takashi Yano <takashi.yano@nifty.ne.jp>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v8 1/1] Cygwin: pty: add pseudo console support.
  2019-08-27 15:52         ` Takashi Yano
@ 2019-08-27 15:58           ` Corinna Vinschen
  0 siblings, 0 replies; 14+ messages in thread
From: Corinna Vinschen @ 2019-08-27 15:58 UTC (permalink / raw)
  To: cygwin-developers

[-- Attachment #1: Type: text/plain, Size: 1528 bytes --]

On Aug 28 00:53, Takashi Yano wrote:
> Hi Corinna,
> 
> On Tue, 27 Aug 2019 10:47:31 +0200
> Corinna Vinschen wrote:
> > On Aug 27 10:38, Thomas Wolff wrote:
> > > Am 27.08.2019 um 10:24 schrieb Corinna Vinschen:
> > > > On Aug 22 00:42, Takashi Yano wrote:
> > > > > - Support pseudo console in PTY. Pseudo console is a new feature
> > > > >    in Windows 10 1809, which provides console APIs on virtual
> > > > >    terminal. With this patch, native console applications can work
> > > > >    in PTY such as mintty, ssh, gnu screen or tmux.
> > > > Great, the stderr problem is definitely fixed.  I've extremely limited
> > > > time until late October, so I'll push the release into late October or
> > > > November.  I think, given the time frame, we could easily push this
> > > > change into 3.1.
> > > > 
> > > > Does anybody have problems with that?
> > > I don't, but as it's a big issue, it may be reasonable to have it in a test
> > > release well before final release.
> > > Thomas
> > 
> > Yeah, I'll certainly create a test release as soon as I pushed the patch.
> 
> I will post v9 patch as soo as possible. Could you please wait it?

Of course, no worries!

> v9 patch will be:
> - Fix again code conversion to make it consistent with that of mintty.
> - Fix management of the state variable which keep attach status
>   to the pseudo console.
> - Fix small bug and refacter codes regarding API hook.

Looking forward to it,
Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v8 0/1] Pseudo console support in PTY (v8)
  2019-08-24 22:22     ` Thomas Wolff
@ 2019-08-27 18:06       ` Takashi Yano
  2019-08-29  6:49         ` Thomas Wolff
  0 siblings, 1 reply; 14+ messages in thread
From: Takashi Yano @ 2019-08-27 18:06 UTC (permalink / raw)
  To: cygwin-developers

Hi Thomas,

On Sun, 25 Aug 2019 00:22:29 +0200
Thomas Wolff wrote:
> > I tested using your STC in mintty start by 'mintty -o Charset=CP1252'.
> > It seems to work as expected.
> Option Charset needs an additional option Locale to have an effect.
> The easier way is
> LC_ALL=de_DE mintty
> or
> LC_ALL=ja_JP mintty

I have posted v9 patch. Hopefully, this works now.
Could you please test?

-- 
Takashi Yano <takashi.yano@nifty.ne.jp>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v8 0/1] Pseudo console support in PTY (v8)
  2019-08-27 18:06       ` Takashi Yano
@ 2019-08-29  6:49         ` Thomas Wolff
  2019-08-29  8:05           ` Corinna Vinschen
  0 siblings, 1 reply; 14+ messages in thread
From: Thomas Wolff @ 2019-08-29  6:49 UTC (permalink / raw)
  To: cygwin-developers

Hi Takashi,

Am 27.08.2019 um 20:06 schrieb Takashi Yano:
> Hi Thomas,
>
> On Sun, 25 Aug 2019 00:22:29 +0200
> Thomas Wolff wrote:
>>> I tested using your STC in mintty start by 'mintty -o Charset=CP1252'.
>>> It seems to work as expected.
>> Option Charset needs an additional option Locale to have an effect.
>> The easier way is
>> LC_ALL=de_DE mintty
>> or
>> LC_ALL=ja_JP mintty
> I have posted v9 patch. Hopefully, this works now.
> Could you please test?
Quickly restesting the character conversion in non-UTF-8 terminal, this 
works fine, thank you.
Not retested the whole test suite yet...
But I'd say the patch is fine now for a test release.
Thomas

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v8 0/1] Pseudo console support in PTY (v8)
  2019-08-29  6:49         ` Thomas Wolff
@ 2019-08-29  8:05           ` Corinna Vinschen
  2019-08-29 11:58             ` Corinna Vinschen
  0 siblings, 1 reply; 14+ messages in thread
From: Corinna Vinschen @ 2019-08-29  8:05 UTC (permalink / raw)
  To: cygwin-developers

[-- Attachment #1: Type: text/plain, Size: 985 bytes --]

On Aug 29 08:48, Thomas Wolff wrote:
> Hi Takashi,
> 
> Am 27.08.2019 um 20:06 schrieb Takashi Yano:
> > Hi Thomas,
> > 
> > On Sun, 25 Aug 2019 00:22:29 +0200
> > Thomas Wolff wrote:
> > > > I tested using your STC in mintty start by 'mintty -o Charset=CP1252'.
> > > > It seems to work as expected.
> > > Option Charset needs an additional option Locale to have an effect.
> > > The easier way is
> > > LC_ALL=de_DE mintty
> > > or
> > > LC_ALL=ja_JP mintty
> > I have posted v9 patch. Hopefully, this works now.
> > Could you please test?
> Quickly restesting the character conversion in non-UTF-8 terminal, this
> works fine, thank you.
> Not retested the whole test suite yet...
> But I'd say the patch is fine now for a test release.
> Thomas

Thanks for testing, I'll push the patch and create a test release
as soon as I find time.

Takashi, any idea for a one sentence release text?


Thanks,
Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v8 0/1] Pseudo console support in PTY (v8)
  2019-08-29  8:05           ` Corinna Vinschen
@ 2019-08-29 11:58             ` Corinna Vinschen
  0 siblings, 0 replies; 14+ messages in thread
From: Corinna Vinschen @ 2019-08-29 11:58 UTC (permalink / raw)
  To: cygwin-developers

[-- Attachment #1: Type: text/plain, Size: 1142 bytes --]

On Aug 29 10:05, Corinna Vinschen wrote:
> On Aug 29 08:48, Thomas Wolff wrote:
> > Hi Takashi,
> > 
> > Am 27.08.2019 um 20:06 schrieb Takashi Yano:
> > > Hi Thomas,
> > > 
> > > On Sun, 25 Aug 2019 00:22:29 +0200
> > > Thomas Wolff wrote:
> > > > > I tested using your STC in mintty start by 'mintty -o Charset=CP1252'.
> > > > > It seems to work as expected.
> > > > Option Charset needs an additional option Locale to have an effect.
> > > > The easier way is
> > > > LC_ALL=de_DE mintty
> > > > or
> > > > LC_ALL=ja_JP mintty
> > > I have posted v9 patch. Hopefully, this works now.
> > > Could you please test?
> > Quickly restesting the character conversion in non-UTF-8 terminal, this
> > works fine, thank you.
> > Not retested the whole test suite yet...
> > But I'd say the patch is fine now for a test release.
> > Thomas
> 
> Thanks for testing, I'll push the patch and create a test release
> as soon as I find time.
> 
> Takashi, any idea for a one sentence release text?

Never mind, your commit message is just fine for the job.


Thanks,
Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2019-08-29 11:58 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-21 15:43 [PATCH v8 0/1] Pseudo console support in PTY (v8) Takashi Yano
2019-08-21 15:43 ` [PATCH v8 1/1] Cygwin: pty: add pseudo console support Takashi Yano
2019-08-27  8:24   ` Corinna Vinschen
2019-08-27  8:39     ` Thomas Wolff
2019-08-27  8:47       ` Corinna Vinschen
2019-08-27 15:52         ` Takashi Yano
2019-08-27 15:58           ` Corinna Vinschen
2019-08-23 22:30 ` [PATCH v8 0/1] Pseudo console support in PTY (v8) Thomas Wolff
2019-08-24  6:18   ` Takashi Yano
2019-08-24 22:22     ` Thomas Wolff
2019-08-27 18:06       ` Takashi Yano
2019-08-29  6:49         ` Thomas Wolff
2019-08-29  8:05           ` Corinna Vinschen
2019-08-29 11:58             ` Corinna Vinschen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).