public inbox for cygwin-developers@cygwin.com
 help / color / mirror / Atom feed
* Pseudo console support in PTY
@ 2019-03-30 13:08 Takashi Yano
  2019-03-30 19:47 ` Corinna Vinschen
                   ` (2 more replies)
  0 siblings, 3 replies; 53+ messages in thread
From: Takashi Yano @ 2019-03-30 13:08 UTC (permalink / raw)
  To: cygwin-developers

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

Hello cygwin developers,

I have worked on implementing pseudo console support into cygwin
PTY by request from Corinna. Pseudo console is a new feature
after Windows 10 1809, which provides console APIs on virtual
terminal.
https://blogs.msdn.microsoft.com/commandline/2018/08/02/windows-command-line-introducing-the-windows-pseudo-console-conpty/
https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session

After much effort, it began to work partially. So, I would
like to announce to this mailing list. Attached are the
patchs against cygwin git HEAD.

Patch 0001 and 0002 are not really needed by the pseudo console
support, but are the modification I have done at the same time.

Patch 0001: This just renames and unifies the function names.
  Both get_io_handle() and get_handle() were identical and
  returned io_handle. So, they were unified.

Patch 0002: This revises console code for better color handling
  and fixing select() behaviour. This provides 24 bit color
  support after Windows 10 build 14931. For legacy console,
  fake 256 color support is implemented, which use the nearest
  color from 16 system colors.

Patch 0003: Support pseudo console in PTY. With this patch,
  native console applications can work in PTY such as mintty,
  ssh, gnu screen or tmux.

Anyone who are interested in this work, please test. Any
discussions and suggestions are also welcome.

You can download the binaries from:

D=http://tyan0.dip.jp/cygwin
${D}/x86_64/test/cygwin1-20190330.dll.xz
${D}/x86_64/test/cygwin-console-helper.exe.xz
${D}/x86/test/cygwin1-20190330.dll.xz
${D}/x86/test/cygwin-console-helper.exe.xz

This cygwin1.dll requires new cygwin-console-helper.exe.
Please unxz and put them into /bin directory.

Known problems:
* Sometimes the screen layout would be broken.
* mintty fails to start if it is started in console cygwin
  session.

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

[-- Attachment #2: 0001-Cygwin-gs-et_io_handle-ranamed-to-gs-et_handle.patch --]
[-- Type: application/octet-stream, Size: 28260 bytes --]

From 45ca734e1e783ea1202ca8a5673b3fda7acc3a54 Mon Sep 17 00:00:00 2001
From: Takashi Yano <takashi.yano@nifty.ne.jp>
Date: Sat, 30 Mar 2019 16:12:02 +0900
Subject: [PATCH 1/3] Cygwin: [gs]et_io_handle(): ranamed to [gs]et_handle().

- Unify get_io_handle() and get_handle() to get_handle().
  Both of them returned same value; io_handle.
- Rename set_io_handle() to set_handle().
---
 winsup/cygwin/dtable.cc               | 16 +++++++--------
 winsup/cygwin/fhandler.cc             | 12 ++++++------
 winsup/cygwin/fhandler.h              |  9 +++++----
 winsup/cygwin/fhandler_console.cc     | 26 ++++++++++++-------------
 winsup/cygwin/fhandler_disk_file.cc   | 16 +++++++--------
 winsup/cygwin/fhandler_fifo.cc        | 12 ++++++------
 winsup/cygwin/fhandler_pipe.cc        |  2 +-
 winsup/cygwin/fhandler_process_fd.cc  |  6 +++---
 winsup/cygwin/fhandler_registry.cc    |  6 +++---
 winsup/cygwin/fhandler_socket_inet.cc | 10 +++++-----
 winsup/cygwin/fhandler_socket_unix.cc | 16 +++++++--------
 winsup/cygwin/fhandler_tty.cc         | 28 +++++++++++++--------------
 winsup/cygwin/mmap.cc                 |  6 +++---
 winsup/cygwin/select.cc               |  8 ++++----
 14 files changed, 87 insertions(+), 86 deletions(-)

diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
index 86e0c716d..636221a77 100644
--- a/winsup/cygwin/dtable.cc
+++ b/winsup/cygwin/dtable.cc
@@ -647,7 +647,7 @@ build_fh_pc (path_conv& pc)
   else if ((fh->archetype = cygheap->fdtab.find_archetype (fh->dev ())))
     {
       debug_printf ("found an archetype for %s(%d/%d) io_handle %p", fh->get_name (), fh->dev ().get_major (), fh->dev ().get_minor (),
-		    fh->archetype->get_io_handle ());
+		    fh->archetype->get_handle ());
       if (!fh->get_name ())
 	fh->set_name (fh->archetype->dev ().name ());
     }
@@ -688,7 +688,7 @@ dtable::dup_worker (fhandler_base *oldfh, int flags)
   else
     {
       if (!oldfh->archetype)
-	newfh->set_io_handle (NULL);
+	newfh->set_handle (NULL);
 
       newfh->pc.reset_conv_handle ();
       if (oldfh->dup (newfh, flags))
@@ -708,7 +708,7 @@ dtable::dup_worker (fhandler_base *oldfh, int flags)
 	  /* The O_CLOEXEC flag enforces close-on-exec behaviour. */
 	  newfh->set_close_on_exec (!!(flags & O_CLOEXEC));
 	  debug_printf ("duped '%s' old %p, new %p", oldfh->get_name (),
-			oldfh->get_io_handle (), newfh->get_io_handle ());
+			oldfh->get_handle (), newfh->get_handle ());
 	}
     }
   return newfh;
@@ -765,7 +765,7 @@ dtable::dup3 (int oldfd, int newfd, int flags)
     }
 
   debug_printf ("newfh->io_handle %p, oldfh->io_handle %p, new win32_name %p, old win32_name %p",
-		newfh->get_io_handle (), fds[oldfd]->get_io_handle (), newfh->get_win32_name (), fds[oldfd]->get_win32_name ());
+		newfh->get_handle (), fds[oldfd]->get_handle (), newfh->get_win32_name (), fds[oldfd]->get_win32_name ());
 
   if (!not_open (newfd))
     close (newfd);
@@ -891,12 +891,12 @@ dtable::fixup_after_exec ()
 	/* Close the handle if it's close-on-exec or if an error was detected
 	   (typically with opening a console in a gui app) by fixup_after_exec.
 	 */
-	if (fh->close_on_exec () || (!fh->nohandle () && !fh->get_io_handle ()))
+	if (fh->close_on_exec () || (!fh->nohandle () && !fh->get_handle ()))
 	  fixup_close (i, fh);
 	else if (fh->get_popen_pid ())
 	  close (i);
 	else if (i == 0)
-	  SetStdHandle (std_consts[i], fh->get_io_handle ());
+	  SetStdHandle (std_consts[i], fh->get_handle ());
 	else if (i <= 2)
 	  SetStdHandle (std_consts[i], fh->get_output_handle ());
       }
@@ -913,7 +913,7 @@ dtable::fixup_after_fork (HANDLE parent)
 	  {
 	    debug_printf ("fd %d (%s)", i, fh->get_name ());
 	    fh->fixup_after_fork (parent);
-	    if (!fh->nohandle () && !fh->get_io_handle ())
+	    if (!fh->nohandle () && !fh->get_handle ())
 	      {
 		/* This should actually never happen but it's here to make sure
 		   we don't crash due to access of an unopened file handle.  */
@@ -922,7 +922,7 @@ dtable::fixup_after_fork (HANDLE parent)
 	      }
 	  }
 	if (i == 0)
-	  SetStdHandle (std_consts[i], fh->get_io_handle ());
+	  SetStdHandle (std_consts[i], fh->get_handle ());
 	else if (i <= 2)
 	  SetStdHandle (std_consts[i], fh->get_output_handle ());
       }
diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc
index 659435e0a..b0c9c50c3 100644
--- a/winsup/cygwin/fhandler.cc
+++ b/winsup/cygwin/fhandler.cc
@@ -461,7 +461,7 @@ fhandler_base::open_with_arch (int flags, mode_t mode)
     }
   else if (archetype)
     {
-      if (!archetype->get_io_handle ())
+      if (!archetype->get_handle ())
 	{
 	  copyto (archetype);
 	  archetype_usecount (1);
@@ -522,7 +522,7 @@ fhandler_base::open_null (int flags)
       __seterrno_from_nt_status (status);
       goto done;
     }
-  set_io_handle (fh);
+  set_handle (fh);
   set_flags (flags, pc.binmode ());
   res = 1;
   set_open_status ();
@@ -775,7 +775,7 @@ fhandler_base::open (int flags, mode_t mode)
 	}
     }
 
-  set_io_handle (fh);
+  set_handle (fh);
   set_flags (flags, pc.binmode ());
 
   res = 1;
@@ -1298,7 +1298,7 @@ fhandler_base_overlapped::close ()
      /* Cancelling seems to be necessary for cases where a reader is
 	 still executing when a signal handler performs a close.  */
       if (!writer)
-	CancelIo (get_io_handle ());
+	CancelIo (get_handle ());
       destroy_overlapped ();
       res = fhandler_base::close ();
     }
@@ -1382,7 +1382,7 @@ fhandler_base::fstatvfs (struct statvfs *sfs)
 int
 fhandler_base::init (HANDLE f, DWORD a, mode_t bin)
 {
-  set_io_handle (f);
+  set_handle (f);
   access = a;
   a &= GENERIC_READ | GENERIC_WRITE;
   int flags = 0;
@@ -1417,7 +1417,7 @@ fhandler_base::dup (fhandler_base *child, int)
 	}
 
       VerifyHandle (nh);
-      child->set_io_handle (nh);
+      child->set_handle (nh);
     }
   return 0;
 }
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 21fec9e38..b336eb63a 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -227,7 +227,7 @@ class fhandler_base
   virtual ~fhandler_base ();
 
   /* Non-virtual simple accessor functions. */
-  void set_io_handle (HANDLE x) { io_handle = x; }
+  void set_handle (HANDLE x) { io_handle = x; }
 
   dev_t& get_device () { return dev (); }
   _major_t get_major () { return dev ().get_major (); }
@@ -430,9 +430,9 @@ public:
   /* Virtual accessor functions to hide the fact
      that some fd's have two handles. */
   virtual HANDLE& get_handle () { return io_handle; }
-  virtual HANDLE& get_io_handle () { return io_handle; }
-  virtual HANDLE& get_io_handle_cyg () { return io_handle; }
+  virtual HANDLE& get_handle_cyg () { return io_handle; }
   virtual HANDLE& get_output_handle () { return io_handle; }
+  virtual HANDLE& get_output_handle_cyg () { return io_handle; }
   virtual HANDLE get_stat_handle () { return pc.handle () ?: io_handle; }
   virtual HANDLE get_echo_handle () const { return NULL; }
   virtual bool hit_eof () {return false;}
@@ -1726,6 +1726,7 @@ class fhandler_termios: public fhandler_base
     need_fork_fixup (true);
   }
   HANDLE& get_output_handle () { return output_handle; }
+  HANDLE& get_output_handle_cyg () { return output_handle; }
   line_edit_status line_edit (const char *rptr, size_t nread, termios&,
 			      ssize_t *bytes_read = NULL);
   void set_output_handle (HANDLE h) { output_handle = h; }
@@ -2109,7 +2110,7 @@ class fhandler_pty_master: public fhandler_pty_common
 
 public:
   HANDLE get_echo_handle () const { return echo_r; }
-  HANDLE& get_io_handle_cyg () { return io_handle_cyg; }
+  HANDLE& get_handle_cyg () { return io_handle_cyg; }
   /* Constructor */
   fhandler_pty_master (int);
 
diff --git a/winsup/cygwin/fhandler_console.cc b/winsup/cygwin/fhandler_console.cc
index 6a0d640a8..281c2005c 100644
--- a/winsup/cygwin/fhandler_console.cc
+++ b/winsup/cygwin/fhandler_console.cc
@@ -155,7 +155,7 @@ fhandler_console::set_unit ()
     pc.file_attributes (FILE_ATTRIBUTE_NORMAL);
   else
     {
-      set_io_handle (NULL);
+      set_handle (NULL);
       set_output_handle (NULL);
       created = false;
     }
@@ -298,7 +298,7 @@ fhandler_console::read (void *pv, size_t& buflen)
 {
   push_process_state process_state (PID_TTYIN);
 
-  HANDLE h = get_io_handle ();
+  HANDLE h = get_handle ();
 
 #define buf ((char *) pv)
 
@@ -818,7 +818,7 @@ fhandler_console::open (int flags, mode_t)
 
   tcinit (false);
 
-  set_io_handle (NULL);
+  set_handle (NULL);
   set_output_handle (NULL);
 
   /* Open the input handle as handle_ */
@@ -831,7 +831,7 @@ fhandler_console::open (int flags, mode_t)
       __seterrno ();
       return 0;
     }
-  set_io_handle (h);
+  set_handle (h);
 
   h = CreateFileW (L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
 		  FILE_SHARE_READ | FILE_SHARE_WRITE, &sec_none,
@@ -856,11 +856,11 @@ fhandler_console::open (int flags, mode_t)
   set_open_status ();
 
   DWORD cflags;
-  if (GetConsoleMode (get_io_handle (), &cflags))
-    SetConsoleMode (get_io_handle (),
+  if (GetConsoleMode (get_handle (), &cflags))
+    SetConsoleMode (get_handle (),
 		    ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT | cflags);
 
-  debug_printf ("opened conin$ %p, conout$ %p", get_io_handle (),
+  debug_printf ("opened conin$ %p, conout$ %p", get_handle (),
 		get_output_handle ());
 
   return 1;
@@ -878,7 +878,7 @@ fhandler_console::open_setup (int flags)
 int
 fhandler_console::close ()
 {
-  CloseHandle (get_io_handle ());
+  CloseHandle (get_handle ());
   CloseHandle (get_output_handle ());
   if (!have_execed)
     free_console ();
@@ -948,7 +948,7 @@ fhandler_console::ioctl (unsigned int cmd, void *arg)
 	  DWORD n;
 	  int ret = 0;
 	  INPUT_RECORD inp[INREC_SIZE];
-	  if (!PeekConsoleInputW (get_io_handle (), inp, INREC_SIZE, &n))
+	  if (!PeekConsoleInputW (get_handle (), inp, INREC_SIZE, &n))
 	    {
 	      set_errno (EINVAL);
 	      return -1;
@@ -972,7 +972,7 @@ fhandler_console::tcflush (int queue)
   if (queue == TCIFLUSH
       || queue == TCIOFLUSH)
     {
-      if (!FlushConsoleInputBuffer (get_io_handle ()))
+      if (!FlushConsoleInputBuffer (get_handle ()))
 	{
 	  __seterrno ();
 	  res = -1;
@@ -1004,7 +1004,7 @@ fhandler_console::input_tcsetattr (int, struct termios const *t)
 
   DWORD oflags;
 
-  if (!GetConsoleMode (get_io_handle (), &oflags))
+  if (!GetConsoleMode (get_handle (), &oflags))
     oflags = 0;
   DWORD flags = 0;
 
@@ -1050,7 +1050,7 @@ fhandler_console::input_tcsetattr (int, struct termios const *t)
     res = 0;
   else
     {
-      res = SetConsoleMode (get_io_handle (), flags) ? 0 : -1;
+      res = SetConsoleMode (get_handle (), flags) ? 0 : -1;
       if (res < 0)
 	__seterrno ();
       syscall_printf ("%d = tcsetattr(,%p) enable flags %y, c_lflag %y iflag %y",
@@ -1080,7 +1080,7 @@ fhandler_console::tcgetattr (struct termios *t)
 
   DWORD flags;
 
-  if (!GetConsoleMode (get_io_handle (), &flags))
+  if (!GetConsoleMode (get_handle (), &flags))
     {
       __seterrno ();
       res = -1;
diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc
index 193192762..84d86456b 100644
--- a/winsup/cygwin/fhandler_disk_file.cc
+++ b/winsup/cygwin/fhandler_disk_file.cc
@@ -212,7 +212,7 @@ fhandler_base::fstat_by_nfs_ea (struct stat *buf)
   cyg_ldap cldap;
   bool ldap_open = false;
 
-  if (get_io_handle ())
+  if (get_handle ())
     {
       /* NFS stumbles over its own caching.  If you write to the file,
 	 a subsequent fstat does not return the actual size of the file,
@@ -220,8 +220,8 @@ fhandler_base::fstat_by_nfs_ea (struct stat *buf)
 	 access through another handle invalidates the caching within the
 	 NFS client. */
       if (get_access () & GENERIC_WRITE)
-	FlushFileBuffers (get_io_handle ());
-      pc.get_finfo (get_io_handle ());
+	FlushFileBuffers (get_handle ());
+      pc.get_finfo (get_handle ());
     }
   buf->st_dev = nfs_attr->fsid;
   buf->st_ino = nfs_attr->fileid;
@@ -291,7 +291,7 @@ fhandler_base::fstat_by_handle (struct stat *buf)
 
   /* If the file has been opened for other purposes than stat, we can't rely
      on the information stored in pc.fai.  So we overwrite them here. */
-  if (get_io_handle ())
+  if (get_handle ())
     {
       status = pc.get_finfo (h);
       if (!NT_SUCCESS (status))
@@ -386,7 +386,7 @@ fhandler_base::fstat_fs (struct stat *buf)
       nohandle (false);
       close_fs ();
       nohandle (no_handle);
-      set_io_handle (NULL);
+      set_handle (NULL);
     }
   if (res)
     res = fstat_by_name (buf);
@@ -1465,7 +1465,7 @@ fhandler_base::open_fs (int flags, mode_t mode)
       /* The file info in pc is wrong at this point for newly created files.
 	 Refresh it before fetching any file info. */
       if (new_file)
-	pc.get_finfo (get_io_handle ());
+	pc.get_finfo (get_handle ());
 
       if (pc.isgood_inode (pc.get_ino ()))
 	ino = pc.get_ino ();
@@ -2615,7 +2615,7 @@ fhandler_disk_file::fs_ioc_setflags (uint64_t flags)
       if (fh != get_handle ())
 	NtClose (fh);
       NtClose (get_handle ());
-      set_io_handle (NULL);
+      set_handle (NULL);
 
       pc.get_wide_win32_path (path);
       cret = (flags & FS_ENCRYPT_FL)
@@ -2630,7 +2630,7 @@ fhandler_disk_file::fs_ioc_setflags (uint64_t flags)
 	  __seterrno_from_nt_status (status);
 	  return -1;
 	}
-      set_io_handle (fh);
+      set_handle (fh);
       if (!cret)
 	{
 	  __seterrno ();
diff --git a/winsup/cygwin/fhandler_fifo.cc b/winsup/cygwin/fhandler_fifo.cc
index 7847cca82..68b9e7717 100644
--- a/winsup/cygwin/fhandler_fifo.cc
+++ b/winsup/cygwin/fhandler_fifo.cc
@@ -266,7 +266,7 @@ fhandler_fifo::open_pipe ()
   sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
   status = NtOpenFile (&ph, access, &attr, &io, sharing, 0);
   if (NT_SUCCESS (status))
-    set_io_handle (ph);
+    set_handle (ph);
   return status;
 }
 
@@ -293,7 +293,7 @@ fhandler_fifo::add_client ()
   HANDLE ph = create_pipe_instance (first);
   if (!ph)
     goto errout;
-  fh->set_io_handle (ph);
+  fh->set_handle (ph);
   fh->set_flags (get_flags ());
   if (fc.connect () < 0)
     {
@@ -486,7 +486,7 @@ fhandler_fifo::open (int flags, mode_t)
 	  res = error_errno_set;
 	  goto out;
 	}
-      set_io_handle (ph);
+      set_handle (ph);
       set_pipe_non_blocking (ph, true);
       if (!(fh = build_fh_dev (dev ())))
 	{
@@ -494,7 +494,7 @@ fhandler_fifo::open (int flags, mode_t)
 	  res = error_errno_set;
 	  goto out;
 	}
-      fh->set_io_handle (ph);
+      fh->set_handle (ph);
       fh->set_flags (flags);
       if (!(connect_evt = create_event ()))
 	{
@@ -604,8 +604,8 @@ out:
 	  CloseHandle (write_ready);
 	  write_ready = NULL;
 	}
-      if (get_io_handle ())
-	CloseHandle (get_io_handle ());
+      if (get_handle ())
+	CloseHandle (get_handle ());
       if (listen_client_thr)
 	CloseHandle (listen_client_thr);
     }
diff --git a/winsup/cygwin/fhandler_pipe.cc b/winsup/cygwin/fhandler_pipe.cc
index 31e73ceb0..edbaded68 100644
--- a/winsup/cygwin/fhandler_pipe.cc
+++ b/winsup/cygwin/fhandler_pipe.cc
@@ -97,7 +97,7 @@ fhandler_pipe::open (int flags, mode_t mode)
 	      || (rwflags == O_WRONLY && !(cfd->get_access () & GENERIC_WRITE)))
 	    continue;
 	  cfd->copyto (this);
-	  set_io_handle (NULL);
+	  set_handle (NULL);
 	  pc.reset_conv_handle ();
 	  if (!cfd->dup (this, flags))
 	    return 1;
diff --git a/winsup/cygwin/fhandler_process_fd.cc b/winsup/cygwin/fhandler_process_fd.cc
index 3bf8b74d8..71ba8b645 100644
--- a/winsup/cygwin/fhandler_process_fd.cc
+++ b/winsup/cygwin/fhandler_process_fd.cc
@@ -116,7 +116,7 @@ fhandler_process_fd::fd_reopen (int flags, mode_t mode)
   fh = fetch_fh (hdl, 0);
   if (!fh)
     return NULL;
-  fh->set_io_handle (hdl);
+  fh->set_handle (hdl);
   int ret = fh->open_with_arch (flags, mode);
   CloseHandle (hdl);
   if (!ret)
@@ -139,7 +139,7 @@ fhandler_process_fd::fstat (struct stat *statbuf)
   fh = fetch_fh (hdl, 0);
   if (!fh)
     return -1;
-  fh->set_io_handle (hdl);
+  fh->set_handle (hdl);
   int ret = fh->fstat (statbuf);
   CloseHandle (hdl);
   delete fh;
@@ -155,7 +155,7 @@ fhandler_process_fd::link (const char *newpath)
   fh = fetch_fh (hdl, FFH_LINKAT);
   if (!fh)
     return -1;
-  fh->set_io_handle (hdl);
+  fh->set_handle (hdl);
   int ret = fh->link (newpath);
   CloseHandle (hdl);
   delete fh;
diff --git a/winsup/cygwin/fhandler_registry.cc b/winsup/cygwin/fhandler_registry.cc
index 6c6702afd..ecaed085c 100644
--- a/winsup/cygwin/fhandler_registry.cc
+++ b/winsup/cygwin/fhandler_registry.cc
@@ -826,7 +826,7 @@ fhandler_registry::open (int flags, mode_t mode)
 	      }
 	    else
 	      {
-		set_io_handle (fetch_hkey (i));
+		set_handle (fetch_hkey (i));
 		/* Marking as nohandle allows to call dup on pseudo registry
 		   handles. */
 		if (get_handle () >= HKEY_CLASSES_ROOT)
@@ -881,7 +881,7 @@ fhandler_registry::open (int flags, mode_t mode)
       else
 	flags |= O_DIROPEN;
 
-      set_io_handle (handle);
+      set_handle (handle);
       set_close_on_exec (!!(flags & O_CLOEXEC));
       value_name = cwcsdup (dec_file);
 
@@ -1118,7 +1118,7 @@ fhandler_registry::dup (fhandler_base *child, int flags)
      allows fhandler_base::dup to succeed as usual for nohandle fhandlers.
      Here we just have to fix up by copying the pseudo handle value. */
   if ((HKEY) get_handle () >= HKEY_CLASSES_ROOT)
-    fhs->set_io_handle (get_handle ());
+    fhs->set_handle (get_handle ());
   if (value_name)
     fhs->value_name = cwcsdup (value_name);
   return ret;
diff --git a/winsup/cygwin/fhandler_socket_inet.cc b/winsup/cygwin/fhandler_socket_inet.cc
index dbfbcf588..46af1c4be 100644
--- a/winsup/cygwin/fhandler_socket_inet.cc
+++ b/winsup/cygwin/fhandler_socket_inet.cc
@@ -518,14 +518,14 @@ fhandler_socket_wsock::fixup_after_fork (HANDLE parent)
   if (new_sock == INVALID_SOCKET)
     {
       set_winsock_errno ();
-      set_io_handle ((HANDLE) INVALID_SOCKET);
+      set_handle ((HANDLE) INVALID_SOCKET);
     }
   else
     {
       /* Even though the original socket was not inheritable, the duplicated
 	 socket is potentially inheritable again. */
       SetHandleInformation ((HANDLE) new_sock, HANDLE_FLAG_INHERIT, 0);
-      set_io_handle ((HANDLE) new_sock);
+      set_handle ((HANDLE) new_sock);
       debug_printf ("WSASocket succeeded (%p)", new_sock);
     }
 }
@@ -571,13 +571,13 @@ fhandler_socket_wsock::dup (fhandler_base *child, int flags)
 
   cygheap->user.deimpersonate ();
   fhs->init_fixup_before ();
-  fhs->set_io_handle (get_io_handle ());
+  fhs->set_handle (get_handle ());
   int ret = fhs->fixup_before_fork_exec (GetCurrentProcessId ());
   cygheap->user.reimpersonate ();
   if (!ret)
     {
       fhs->fixup_after_fork (GetCurrentProcess ());
-      if (fhs->get_io_handle() != (HANDLE) INVALID_SOCKET)
+      if (fhs->get_handle() != (HANDLE) INVALID_SOCKET)
 	return 0;
     }
   cygheap->fdtab.dec_need_fixup_before ();
@@ -645,7 +645,7 @@ fhandler_socket_wsock::set_socket_handle (SOCKET sock, int af, int type,
             }
         }
     }
-  set_io_handle ((HANDLE) sock);
+  set_handle ((HANDLE) sock);
   set_addr_family (af);
   set_socket_type (type);
   if (!init_events ())
diff --git a/winsup/cygwin/fhandler_socket_unix.cc b/winsup/cygwin/fhandler_socket_unix.cc
index e71d2a722..eea7e76b3 100644
--- a/winsup/cygwin/fhandler_socket_unix.cc
+++ b/winsup/cygwin/fhandler_socket_unix.cc
@@ -938,7 +938,7 @@ fhandler_socket_unix::open_pipe (PUNICODE_STRING pipe_name, bool xchg_sock_info)
   status = NtOpenFile (&ph, access, &attr, &io, sharing, 0);
   if (NT_SUCCESS (status))
     {
-      set_io_handle (ph);
+      set_handle (ph);
       if (xchg_sock_info)
 	send_sock_info (false);
     }
@@ -1365,7 +1365,7 @@ fhandler_socket_unix::socket (int af, int type, int protocol, int flags)
   if (flags & SOCK_CLOEXEC)
     set_close_on_exec (true);
   init_cred ();
-  set_io_handle (NULL);
+  set_handle (NULL);
   set_unique_id ();
   set_ino (get_unique_id ());
   return 0;
@@ -1412,7 +1412,7 @@ fhandler_socket_unix::socketpair (int af, int type, int protocol, int flags,
   pipe = create_pipe (true);
   if (!pipe)
     goto create_pipe_failed;
-  set_io_handle (pipe);
+  set_handle (pipe);
   sun_path (&sun);
   fh->peer_sun_path (&sun);
   connect_state (listener);
@@ -1483,12 +1483,12 @@ fhandler_socket_unix::bind (const struct sockaddr *name, int namelen)
 	  binding_state (unbound);
 	  return -1;
 	}
-      set_io_handle (pipe);
+      set_handle (pipe);
     }
   backing_file_handle = unnamed ? autobind (&sun) : create_file (&sun);
   if (!backing_file_handle)
     {
-      set_io_handle (NULL);
+      set_handle (NULL);
       if (pipe)
 	NtClose (pipe);
       binding_state (unbound);
@@ -1538,7 +1538,7 @@ fhandler_socket_unix::listen (int backlog)
       connect_state (unconnected);
       return -1;
     }
-  set_io_handle (pipe);
+  set_handle (pipe);
   state_lock ();
   set_cred ();
   state_unlock ();
@@ -1575,7 +1575,7 @@ fhandler_socket_unix::accept4 (struct sockaddr *peer, int *len, int flags)
       else
 	{
 	  /* Set new io handle. */
-	  set_io_handle (new_inst);
+	  set_handle (new_inst);
 	  io_unlock ();
 	  /* Prepare new file descriptor. */
 	  cygheap_fdnew fd;
@@ -1600,7 +1600,7 @@ fhandler_socket_unix::accept4 (struct sockaddr *peer, int *len, int flags)
 		  sock->pc.set_nt_native_path (pc.get_nt_native_path ());
 		  sock->connect_state (connected);
 		  sock->binding_state (binding_state ());
-		  sock->set_io_handle (accepted);
+		  sock->set_handle (accepted);
 
 		  sock->sun_path (sun_path ());
 		  sock->sock_cred (sock_cred ());
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index 7fe46ebef..312c2d083 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -235,7 +235,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_io_handle_cyg ()))
+	  if (!::bytes_available (n, get_handle_cyg ()))
 	    goto err;
 	  if (n)
 	    break;
@@ -296,7 +296,7 @@ fhandler_pty_master::process_slave_output (char *buf, size_t len, int pktmode_on
 	      goto err;
 	    }
 	}
-      else if (!ReadFile (get_io_handle_cyg (), outbuf, rlen, &n, NULL))
+      else if (!ReadFile (get_handle_cyg (), outbuf, rlen, &n, NULL))
 	{
 	  termios_printf ("ReadFile failed, %E");
 	  goto err;
@@ -494,7 +494,7 @@ fhandler_pty_slave::open (int flags, mode_t)
   termios_printf ("duplicated to_master_cyg %p->%p from pty_owner",
 		  get_ttyp ()->to_master_cyg (), to_master_cyg_local);
 
-  set_io_handle (from_master_local);
+  set_handle (from_master_local);
   set_output_handle (to_master_local);
   set_output_handle_cyg (to_master_cyg_local);
 
@@ -1347,11 +1347,11 @@ fhandler_pty_master::close ()
   if (!ForceCloseHandle (to_master))
     termios_printf ("error closing to_master %p, %E", to_master);
   from_master = to_master = NULL;
-  if (!ForceCloseHandle (get_io_handle_cyg ()))
-    termios_printf ("error closing io_handle_cyg %p, %E", get_io_handle_cyg ());
+  if (!ForceCloseHandle (get_handle_cyg ()))
+    termios_printf ("error closing io_handle_cyg %p, %E", get_handle_cyg ());
   if (!ForceCloseHandle (to_master_cyg))
     termios_printf ("error closing to_master_cyg %p, %E", to_master_cyg);
-  get_io_handle_cyg () = to_master_cyg = NULL;
+  get_handle_cyg () = to_master_cyg = NULL;
   ForceCloseHandle (echo_r);
   ForceCloseHandle (echo_w);
   echo_r = echo_w = NULL;
@@ -1458,7 +1458,7 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg)
     case FIONREAD:
       {
 	DWORD n;
-	if (!::bytes_available (n, get_io_handle_cyg ()))
+	if (!::bytes_available (n, get_handle_cyg ()))
 	  {
 	    set_errno (EINVAL);
 	    return -1;
@@ -1662,7 +1662,7 @@ fhandler_pty_master::pty_master_fwd_thread ()
   termios_printf("Started.");
   for (;;)
     {
-      if (!ReadFile (get_io_handle (), outbuf, sizeof outbuf, &rlen, NULL))
+      if (!ReadFile (get_handle (), outbuf, sizeof outbuf, &rlen, NULL))
 	{
 	  termios_printf ("ReadFile for forwarding failed, %E");
 	  break;
@@ -1715,7 +1715,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_io_handle (), &to_master,
+  res = fhandler_pipe::create (&sec_none, &get_handle (), &to_master,
 			       fhandler_pty_common::pipesize, pipename, 0);
   if (res)
     {
@@ -1724,7 +1724,7 @@ fhandler_pty_master::setup ()
     }
 
   __small_sprintf (pipename, "pty%d-to-master-cyg", unit);
-  res = fhandler_pipe::create (&sec_none, &get_io_handle_cyg (), &to_master_cyg,
+  res = fhandler_pipe::create (&sec_none, &get_handle_cyg (), &to_master_cyg,
 			       fhandler_pty_common::pipesize, pipename, 0);
   if (res)
     {
@@ -1732,7 +1732,7 @@ fhandler_pty_master::setup ()
       goto err;
     }
 
-  ProtectHandle1 (get_io_handle (), from_pty);
+  ProtectHandle1 (get_handle (), from_pty);
 
   __small_sprintf (pipename, "pty%d-echoloop", unit);
   res = fhandler_pipe::create (&sec_none, &echo_r, &echo_w,
@@ -1807,14 +1807,14 @@ 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_io_handle (), get_io_handle_cyg (),
+	this, unit, get_handle (), get_handle_cyg (),
 	get_output_handle ());
   return true;
 
 err:
   __seterrno ();
-  close_maybe (get_io_handle ());
-  close_maybe (get_io_handle_cyg ());
+  close_maybe (get_handle ());
+  close_maybe (get_handle_cyg ());
   close_maybe (get_output_handle ());
   close_maybe (input_available_event);
   close_maybe (output_mutex);
diff --git a/winsup/cygwin/mmap.cc b/winsup/cygwin/mmap.cc
index 1d81d534f..9eb1643a0 100644
--- a/winsup/cygwin/mmap.cc
+++ b/winsup/cygwin/mmap.cc
@@ -516,7 +516,7 @@ mmap_record::alloc_fh ()
 {
   if (anonymous ())
     {
-      fh_anonymous.set_io_handle (INVALID_HANDLE_VALUE);
+      fh_anonymous.set_handle (INVALID_HANDLE_VALUE);
       fh_anonymous.set_access (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE);
       return &fh_anonymous;
     }
@@ -901,7 +901,7 @@ mmap64 (void *addr, size_t len, int prot, int flags, int fd, off_t off)
 
   size_t pagesize = wincap.allocation_granularity ();
 
-  fh_anonymous.set_io_handle (INVALID_HANDLE_VALUE);
+  fh_anonymous.set_handle (INVALID_HANDLE_VALUE);
   fh_anonymous.set_access (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE);
 
   /* EINVAL error conditions. */
@@ -1033,7 +1033,7 @@ mmap64 (void *addr, size_t len, int prot, int flags, int fd, off_t off)
 	  fh_disk_file = new (ccalloc (HEAP_FHANDLER, 1, sizeof *fh_disk_file))
 			     fhandler_disk_file;
 	  fh_disk_file->set_name (fh->pc);
-	  fh_disk_file->set_io_handle (h);
+	  fh_disk_file->set_handle (h);
 	  fh_disk_file->set_access (fh->get_access () | GENERIC_EXECUTE);
 	  fh = fh_disk_file;
 	}
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index 991494aa8..9b18e8f80 100644
--- a/winsup/cygwin/select.cc
+++ b/winsup/cygwin/select.cc
@@ -74,7 +74,7 @@ details. */
 })
 
 #define set_handle_or_return_if_not_open(h, s) \
-  h = (s)->fh->get_io_handle_cyg (); \
+  h = (s)->fh->get_handle_cyg (); \
   if (cygheap->fdtab.not_open ((s)->fd)) \
     { \
       (s)->thread_errno =  EBADF; \
@@ -1459,7 +1459,7 @@ fhandler_base::select_read (select_stuff *ss)
       s->startup = no_startup;
       s->verify = verify_ok;
     }
-  s->h = get_io_handle_cyg ();
+  s->h = get_handle_cyg ();
   s->read_selected = true;
   s->read_ready = true;
   return s;
@@ -1474,7 +1474,7 @@ fhandler_base::select_write (select_stuff *ss)
       s->startup = no_startup;
       s->verify = verify_ok;
     }
-  s->h = get_handle ();
+  s->h = get_output_handle_cyg ();
   s->write_selected = true;
   s->write_ready = true;
   return s;
@@ -1747,7 +1747,7 @@ fhandler_socket_unix::select_read (select_stuff *ss)
       s->startup = no_startup;
       s->verify = verify_ok;
     }
-  s->h = get_io_handle_cyg ();
+  s->h = get_handle_cyg ();
   s->read_selected = true;
   s->read_ready = true;
   return s;
-- 
2.17.0


[-- Attachment #3: 0002-Cygwin-console-rework-implementation.patch --]
[-- Type: application/octet-stream, Size: 50630 bytes --]

From 88cd4bbca1750e9a33545e52c04be624376b838d Mon Sep 17 00:00:00 2001
From: Takashi Yano <takashi.yano@nifty.ne.jp>
Date: Sat, 30 Mar 2019 16:17:20 +0900
Subject: [PATCH 2/3] Cygwin: console: rework implementation.

- Revise console code to utilize xterm emulation of Windows 10.
  This enables 24 bit color support.
- Add fake 256 color support for legacy console. Now, the nearest
  color from 16 system colors is used.
- Fix behaviour of select() of console in canonical mode. Previously,
  select() returned by only one key even in canonical mode.
- Make the console I/O functions thread-safe.
---
 winsup/cygwin/fhandler.h          |   36 +-
 winsup/cygwin/fhandler_console.cc | 1087 +++++++++++++++++++----------
 winsup/cygwin/select.cc           |   90 +--
 3 files changed, 779 insertions(+), 434 deletions(-)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index b336eb63a..0477888d7 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1698,6 +1698,12 @@ class fhandler_serial: public fhandler_base
   }
 };
 
+#define acquire_input_mutex(ms) \
+  __acquire_input_mutex (__PRETTY_FUNCTION__, __LINE__, ms)
+
+#define release_input_mutex() \
+  __release_input_mutex (__PRETTY_FUNCTION__, __LINE__)
+
 #define acquire_output_mutex(ms) \
   __acquire_output_mutex (__PRETTY_FUNCTION__, __LINE__, ms)
 
@@ -1778,6 +1784,8 @@ enum ansi_intensity
 #define eattitle 7
 #define gotparen 8
 #define gotrparen 9
+#define eatpalette 10
+#define endpalette 11
 #define MAXARGS 10
 
 enum cltype
@@ -1791,6 +1799,9 @@ enum cltype
 
 class dev_console
 {
+  bool cap24bit_color; /* 24bit-color capability */
+  pid_t owner;
+
   WORD default_color, underline_color, dim_color;
 
   /* Used to determine if an input keystroke should be modified with META. */
@@ -1880,10 +1891,20 @@ public:
     tty_min tty_min_state;
     dev_console con;
   };
+  bool input_ready;
+  enum input_states
+  {
+    input_error = -1,
+    input_processing = 0,
+    input_ok = 1,
+    input_signalled = 2,
+    input_winch = 3
+  };
 private:
   static const unsigned MAX_WRITE_CHARS;
   static console_state *shared_console_info;
   static bool invisible_console;
+  HANDLE input_mutex, output_mutex;
 
   /* Used when we encounter a truncated multi-byte sequence.  The
      lead bytes are stored here and revisited in the next write call. */
@@ -1953,8 +1974,11 @@ private:
   bool focus_aware () {return shared_console_info->con.use_focus;}
   bool get_cons_readahead_valid ()
   {
-    return shared_console_info->con.cons_rapoi != NULL &&
+    acquire_input_mutex (INFINITE);
+    bool ret = shared_console_info->con.cons_rapoi != NULL &&
       *shared_console_info->con.cons_rapoi;
+    release_input_mutex ();
+    return ret;
   }
 
   select_record *select_read (select_stuff *);
@@ -1965,7 +1989,7 @@ private:
   void fixup_after_fork (HANDLE) {fixup_after_fork_exec (false);}
   void set_close_on_exec (bool val);
   void set_input_state ();
-  void send_winch_maybe ();
+  bool send_winch_maybe ();
   void setup ();
   bool set_unit ();
   static bool need_invisible ();
@@ -1988,6 +2012,14 @@ private:
     copyto (fh);
     return fh;
   }
+
+  input_states process_input_message ();
+  void setup_io_mutex (void);
+  DWORD __acquire_input_mutex (const char *fn, int ln, DWORD ms);
+  void __release_input_mutex (const char *fn, int ln);
+  DWORD __acquire_output_mutex (const char *fn, int ln, DWORD ms);
+  void __release_output_mutex (const char *fn, int ln);
+
   friend tty_min * tty_list::get_cttyp ();
 };
 
diff --git a/winsup/cygwin/fhandler_console.cc b/winsup/cygwin/fhandler_console.cc
index 281c2005c..f7b50d0a8 100644
--- a/winsup/cygwin/fhandler_console.cc
+++ b/winsup/cygwin/fhandler_console.cc
@@ -15,6 +15,7 @@ details. */
 #include <sys/param.h>
 #include <sys/cygwin.h>
 #include <cygwin/kd.h>
+#include <unistd.h>
 #include "cygerrno.h"
 #include "security.h"
 #include "path.h"
@@ -32,6 +33,16 @@ details. */
 #include "child_info.h"
 #include "cygwait.h"
 
+#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 ENABLE_VIRTUAL_TERMINAL_INPUT
+#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
+#endif /* ENABLE_VIRTUAL_TERMINAL_INPUT */
+
 /* Don't make this bigger than NT_MAX_PATH as long as the temporary buffer
    is allocated using tmp_pathbuf!!! */
 #define CONVERT_LIMIT NT_MAX_PATH
@@ -148,7 +159,11 @@ fhandler_console::set_unit ()
       if (created)
 	shared_console_info->tty_min_state.setntty (DEV_CONS_MAJOR, console_unit (me));
       devset = (fh_devices) shared_console_info->tty_min_state.getntty ();
+      if (created)
+	con.owner = getpid ();
     }
+  if (!created && shared_console_info && kill (con.owner, 0) == -1)
+    con.owner = getpid ();
 
   dev ().parse (devset);
   if (devset != FH_ERROR)
@@ -167,33 +182,44 @@ void
 fhandler_console::setup ()
 {
   if (set_unit ())
-      {
-	con.scroll_region.Bottom = -1;
-	con.dwLastCursorPosition.X = -1;
-	con.dwLastCursorPosition.Y = -1;
-	con.dwLastMousePosition.X = -1;
-	con.dwLastMousePosition.Y = -1;
-	con.dwLastButtonState = 0;	/* none pressed */
-	con.last_button_code = 3;	/* released */
-	con.underline_color = FOREGROUND_GREEN | FOREGROUND_BLUE;
-	con.dim_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
-	con.meta_mask = LEFT_ALT_PRESSED;
-	/* Set the mask that determines if an input keystroke is modified by
-	   META.  We set this based on the keyboard layout language loaded
-	   for the current thread.  The left <ALT> key always generates
-	   META, but the right <ALT> key only generates META if we are using
-	   an English keyboard because many "international" keyboards
-	   replace common shell symbols ('[', '{', etc.) with accented
-	   language-specific characters (umlaut, accent grave, etc.).  On
-	   these keyboards right <ALT> (called AltGr) is used to produce the
-	   shell symbols and should not be interpreted as META. */
-	if (PRIMARYLANGID (LOWORD (GetKeyboardLayout (0))) == LANG_ENGLISH)
-	  con.meta_mask |= RIGHT_ALT_PRESSED;
-	con.set_default_attr ();
-	con.backspace_keycode = CERASE;
-	con.cons_rapoi = NULL;
-	shared_console_info->tty_min_state.is_console = true;
-      }
+    {
+      con.scroll_region.Bottom = -1;
+      con.dwLastCursorPosition.X = -1;
+      con.dwLastCursorPosition.Y = -1;
+      con.dwLastMousePosition.X = -1;
+      con.dwLastMousePosition.Y = -1;
+      con.dwLastButtonState = 0;	/* none pressed */
+      con.last_button_code = 3;	/* released */
+      con.underline_color = FOREGROUND_GREEN | FOREGROUND_BLUE;
+      con.dim_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+      con.meta_mask = LEFT_ALT_PRESSED;
+      /* Set the mask that determines if an input keystroke is modified by
+	 META.  We set this based on the keyboard layout language loaded
+	 for the current thread.  The left <ALT> key always generates
+	 META, but the right <ALT> key only generates META if we are using
+	 an English keyboard because many "international" keyboards
+	 replace common shell symbols ('[', '{', etc.) with accented
+	 language-specific characters (umlaut, accent grave, etc.).  On
+	 these keyboards right <ALT> (called AltGr) is used to produce the
+	 shell symbols and should not be interpreted as META. */
+      if (PRIMARYLANGID (LOWORD (GetKeyboardLayout (0))) == LANG_ENGLISH)
+	con.meta_mask |= RIGHT_ALT_PRESSED;
+      con.set_default_attr ();
+      con.backspace_keycode = CERASE;
+      con.cons_rapoi = NULL;
+      shared_console_info->tty_min_state.is_console = true;
+      /* Check if 24bit color is available */
+      DWORD dwVersion = GetVersion ();
+      dwVersion = (LOBYTE (LOWORD (dwVersion)) << 24)
+	| (HIBYTE (LOWORD (dwVersion)) << 16) | HIWORD (dwVersion);
+      if (dwVersion >= ((10 << 24) | (0 << 16) | 14931))
+	{
+	  con.cap24bit_color = true;
+	  /* If system has 24bit color capability,
+	     use xterm compatible mode. */
+	  setenv ("TERM", "xterm-256color", 1);
+	}
+    }
 }
 
 /* Return the tty structure associated with a given tty number.  If the
@@ -211,6 +237,45 @@ tty_list::get_cttyp ()
     return NULL;
 }
 
+void
+fhandler_console::setup_io_mutex (void)
+{
+  char buf[MAX_PATH];
+  DWORD res;
+
+  res = WAIT_FAILED;
+  if (!input_mutex || WAIT_FAILED == (res = acquire_input_mutex (0)))
+    {
+      shared_name (buf, "cygcons.input.mutex", get_minor ());
+      input_mutex = OpenMutex (MAXIMUM_ALLOWED, TRUE, buf);
+      if (!input_mutex)
+	input_mutex = CreateMutex (&sec_none, FALSE, buf);
+      if (!input_mutex)
+	{
+	  __seterrno ();
+	  return;
+	}
+    }
+  if (res == WAIT_OBJECT_0)
+    release_input_mutex ();
+
+  res = WAIT_FAILED;
+  if (!output_mutex || WAIT_FAILED == (res = acquire_output_mutex (0)))
+    {
+      shared_name (buf, "cygcons.output.mutex", get_minor ());
+      output_mutex = OpenMutex (MAXIMUM_ALLOWED, TRUE, buf);
+      if (!output_mutex)
+	output_mutex = CreateMutex (&sec_none, FALSE, buf);
+      if (!output_mutex)
+	{
+	  __seterrno ();
+	  return;
+	}
+    }
+  if (res == WAIT_OBJECT_0)
+    release_output_mutex ();
+}
+
 inline DWORD
 dev_console::con_to_str (char *d, int dlen, WCHAR w)
 {
@@ -251,7 +316,7 @@ fhandler_console::set_cursor_maybe ()
     }
 }
 
-void
+bool
 fhandler_console::send_winch_maybe ()
 {
   SHORT y = con.dwWinSize.Y;
@@ -263,7 +328,9 @@ fhandler_console::send_winch_maybe ()
       con.scroll_region.Top = 0;
       con.scroll_region.Bottom = -1;
       get_ttyp ()->kill_pgrp (SIGWINCH);
+      return true;
     }
+  return false;
 }
 
 /* Check whether a mouse event is to be reported as an escape sequence */
@@ -296,36 +363,17 @@ fhandler_console::mouse_aware (MOUSE_EVENT_RECORD& mouse_event)
 void __reg3
 fhandler_console::read (void *pv, size_t& buflen)
 {
+  termios_printf ("read(%p,%d)", pv, buflen);
+
   push_process_state process_state (PID_TTYIN);
 
-  HANDLE h = get_handle ();
+  int copied_chars = 0;
 
-#define buf ((char *) pv)
+  DWORD timeout = is_nonblocking () ? 0 : INFINITE;
 
-  int ch;
   set_input_state ();
 
-  /* Check console read-ahead buffer filled from terminal requests */
-  if (con.cons_rapoi && *con.cons_rapoi)
-    {
-      *buf = *con.cons_rapoi++;
-      buflen = 1;
-      return;
-    }
-
-  int copied_chars = get_readahead_into_buffer (buf, buflen);
-
-  if (copied_chars)
-    {
-      buflen = copied_chars;
-      return;
-    }
-
-  DWORD timeout = is_nonblocking () ? 0 : INFINITE;
-  char tmp[60];
-
-  termios ti = get_ttyp ()->ti;
-  for (;;)
+  while (!input_ready && !get_cons_readahead_valid ())
     {
       int bgres;
       if ((bgres = bg_check (SIGTTIN)) <= bg_eof)
@@ -335,7 +383,7 @@ fhandler_console::read (void *pv, size_t& buflen)
 	}
 
       set_cursor_maybe ();	/* to make cursor appear on the screen immediately */
-      switch (cygwait (h, timeout))
+      switch (cygwait (get_handle (), timeout))
 	{
 	case WAIT_OBJECT_0:
 	  break;
@@ -353,366 +401,414 @@ fhandler_console::read (void *pv, size_t& buflen)
 	  goto err;
 	}
 
-      DWORD nread;
-      INPUT_RECORD input_rec;
-      const char *toadd = NULL;
+#define buf ((char *) pv)
 
-      if (!ReadConsoleInputW (h, &input_rec, 1, &nread))
+      int ret;
+      acquire_input_mutex (INFINITE);
+      ret = process_input_message ();
+      release_input_mutex ();
+      switch (ret)
 	{
-	  syscall_printf ("ReadConsoleInput failed, %E");
-	  goto err;		/* seems to be failure */
+	case input_error:
+	  goto err;
+	case input_processing:
+	  continue;
+	case input_ok: /* input ready */
+	  break;
+	case input_signalled: /* signalled */
+	  goto sig_exit;
+	case input_winch:
+	  continue;
+	default:
+	  /* Should not come here */
+	  goto err;
 	}
+    }
 
-      const WCHAR &unicode_char = input_rec.Event.KeyEvent.uChar.UnicodeChar;
-      const DWORD &ctrl_key_state = input_rec.Event.KeyEvent.dwControlKeyState;
+  /* Check console read-ahead buffer filled from terminal requests */
+  acquire_input_mutex (INFINITE);
+  while (con.cons_rapoi && *con.cons_rapoi && buflen)
+    {
+      buf[copied_chars++] = *con.cons_rapoi++;
+      buflen --;
+    }
 
-      /* check the event that occurred */
-      switch (input_rec.EventType)
-	{
-	case KEY_EVENT:
+  copied_chars +=
+    get_readahead_into_buffer (buf + copied_chars, buflen);
 
-	  con.nModifiers = 0;
+  if (!ralen)
+    input_ready = false;
+  release_input_mutex ();
+
+#undef buf
+
+  buflen = copied_chars;
+  return;
+
+err:
+  __seterrno ();
+  buflen = (size_t) -1;
+  return;
+
+sig_exit:
+  set_sig_errno (EINTR);
+  buflen = (size_t) -1;
+}
+
+fhandler_console::input_states
+fhandler_console::process_input_message (void)
+{
+  char tmp[60];
+
+  if (!shared_console_info)
+    return input_error;
+
+  termios *ti = &(get_ttyp ()->ti);
+
+  DWORD nread;
+  INPUT_RECORD input_rec;
+  const char *toadd = NULL;
+
+  if (!ReadConsoleInputW (get_handle (), &input_rec, 1, &nread))
+    {
+      termios_printf ("ReadConsoleInput failed, %E");
+      return input_error;
+    }
+
+  const WCHAR &unicode_char = input_rec.Event.KeyEvent.uChar.UnicodeChar;
+  const DWORD &ctrl_key_state = input_rec.Event.KeyEvent.dwControlKeyState;
+
+  /* check the event that occurred */
+  switch (input_rec.EventType)
+    {
+    case KEY_EVENT:
+
+      con.nModifiers = 0;
 
 #ifdef DEBUGGING
-	  /* allow manual switching to/from raw mode via ctrl-alt-scrolllock */
-	  if (input_rec.Event.KeyEvent.bKeyDown
-	      && input_rec.Event.KeyEvent.wVirtualKeyCode == VK_SCROLL
-	      && (ctrl_key_state & (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED))
-		  == (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED))
-	    {
-	      set_raw_win32_keyboard_mode (!con.raw_win32_keyboard_mode);
-	      continue;
-	    }
+      /* allow manual switching to/from raw mode via ctrl-alt-scrolllock */
+      if (input_rec.Event.KeyEvent.bKeyDown
+	  && input_rec.Event.KeyEvent.wVirtualKeyCode == VK_SCROLL
+	  && (ctrl_key_state & (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED))
+	  == (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED))
+	{
+	  set_raw_win32_keyboard_mode (!con.raw_win32_keyboard_mode);
+	  return input_processing;
+	}
 #endif
 
-	  if (con.raw_win32_keyboard_mode)
+      if (con.raw_win32_keyboard_mode)
+	{
+	  __small_sprintf (tmp, "\033{%u;%u;%u;%u;%u;%luK",
+			   input_rec.Event.KeyEvent.bKeyDown,
+			   input_rec.Event.KeyEvent.wRepeatCount,
+			   input_rec.Event.KeyEvent.wVirtualKeyCode,
+			   input_rec.Event.KeyEvent.wVirtualScanCode,
+			   input_rec.Event.KeyEvent.uChar.UnicodeChar,
+			   input_rec.Event.KeyEvent.dwControlKeyState);
+	  toadd = tmp;
+	  nread = strlen (toadd);
+	  break;
+	}
+
+      /* Ignore key up events, except for Alt+Numpad events. */
+      if (!input_rec.Event.KeyEvent.bKeyDown &&
+	  !is_alt_numpad_event (&input_rec))
+	return input_processing;
+      /* Ignore Alt+Numpad keys.  They are eventually handled below after
+	 releasing the Alt key. */
+      if (input_rec.Event.KeyEvent.bKeyDown
+	  && is_alt_numpad_key (&input_rec))
+	return input_processing;
+
+      if (ctrl_key_state & SHIFT_PRESSED)
+	con.nModifiers |= 1;
+      if (ctrl_key_state & RIGHT_ALT_PRESSED)
+	con.nModifiers |= 2;
+      if (ctrl_key_state & CTRL_PRESSED)
+	con.nModifiers |= 4;
+      if (ctrl_key_state & LEFT_ALT_PRESSED)
+	con.nModifiers |= 8;
+
+      /* Allow Backspace to emit ^? and escape sequences. */
+      if (input_rec.Event.KeyEvent.wVirtualKeyCode == VK_BACK)
+	{
+	  char c = con.backspace_keycode;
+	  nread = 0;
+	  if (ctrl_key_state & ALT_PRESSED)
 	    {
-	      __small_sprintf (tmp, "\033{%u;%u;%u;%u;%u;%luK",
-				    input_rec.Event.KeyEvent.bKeyDown,
-				    input_rec.Event.KeyEvent.wRepeatCount,
-				    input_rec.Event.KeyEvent.wVirtualKeyCode,
-				    input_rec.Event.KeyEvent.wVirtualScanCode,
-				    input_rec.Event.KeyEvent.uChar.UnicodeChar,
-				    input_rec.Event.KeyEvent.dwControlKeyState);
-	      toadd = tmp;
-	      nread = strlen (toadd);
-	      break;
+	      if (con.metabit)
+		c |= 0x80;
+	      else
+		tmp[nread++] = '\e';
 	    }
-
-	  /* Ignore key up events, except for Alt+Numpad events. */
-	  if (!input_rec.Event.KeyEvent.bKeyDown &&
-	      !is_alt_numpad_event (&input_rec))
-	    continue;
-	  /* Ignore Alt+Numpad keys.  They are eventually handled below after
-	     releasing the Alt key. */
-	  if (input_rec.Event.KeyEvent.bKeyDown
-	      && is_alt_numpad_key (&input_rec))
-	    continue;
-
-	  if (ctrl_key_state & SHIFT_PRESSED)
-	    con.nModifiers |= 1;
-	  if (ctrl_key_state & RIGHT_ALT_PRESSED)
-	    con.nModifiers |= 2;
-	  if (ctrl_key_state & CTRL_PRESSED)
-	    con.nModifiers |= 4;
-	  if (ctrl_key_state & LEFT_ALT_PRESSED)
-	    con.nModifiers |= 8;
-
-	  /* Allow Backspace to emit ^? and escape sequences. */
-	  if (input_rec.Event.KeyEvent.wVirtualKeyCode == VK_BACK)
+	  tmp[nread++] = c;
+	  tmp[nread] = 0;
+	  toadd = tmp;
+	}
+      /* Allow Ctrl-Space to emit ^@ */
+      else if (input_rec.Event.KeyEvent.wVirtualKeyCode
+	       == (con.cap24bit_color ? '2' : VK_SPACE)
+	       && (ctrl_key_state & CTRL_PRESSED)
+	       && !(ctrl_key_state & ALT_PRESSED))
+	toadd = "";
+      else if (unicode_char == 0
+	       /* arrow/function keys */
+	       || (input_rec.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
+	{
+	  toadd = get_nonascii_key (input_rec, tmp);
+	  if (!toadd)
 	    {
-	      char c = con.backspace_keycode;
-	      nread = 0;
-	      if (ctrl_key_state & ALT_PRESSED)
-		{
-		  if (con.metabit)
-		    c |= 0x80;
-		  else
-		    tmp[nread++] = '\e';
-		}
-	      tmp[nread++] = c;
-	      tmp[nread] = 0;
-	      toadd = tmp;
+	      con.nModifiers = 0;
+	      return input_processing;
 	    }
-	  /* Allow Ctrl-Space to emit ^@ */
-	  else if (input_rec.Event.KeyEvent.wVirtualKeyCode == VK_SPACE
-		   && (ctrl_key_state & CTRL_PRESSED)
-		   && !(ctrl_key_state & ALT_PRESSED))
-	    toadd = "";
-	  else if (unicode_char == 0
-	      /* arrow/function keys */
-	      || (input_rec.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
+	  nread = strlen (toadd);
+	}
+      else
+	{
+	  nread = con.con_to_str (tmp + 1, 59, unicode_char);
+	  /* Determine if the keystroke is modified by META.  The tricky
+	     part is to distinguish whether the right Alt key should be
+	     recognized as Alt, or as AltGr. */
+	  bool meta =
+	    /* Alt but not AltGr (= left ctrl + right alt)? */
+	    (ctrl_key_state & ALT_PRESSED) != 0
+	    && ((ctrl_key_state & CTRL_PRESSED) == 0
+		/* but also allow Alt-AltGr: */
+		|| (ctrl_key_state & ALT_PRESSED) == ALT_PRESSED
+		|| (unicode_char <= 0x1f || unicode_char == 0x7f));
+	  if (!meta)
 	    {
-	      toadd = get_nonascii_key (input_rec, tmp);
-	      if (!toadd)
-		{
-		  con.nModifiers = 0;
-		  continue;
-		}
-	      nread = strlen (toadd);
+	      /* Determine if the character is in the current multibyte
+		 charset.  The test is easy.  If the multibyte sequence
+		 is > 1 and the first byte is ASCII CAN, the character
+		 has been translated into the ASCII CAN + UTF-8 replacement
+		 sequence.  If so, just ignore the keypress.
+		 FIXME: Is there a better solution? */
+	      if (nread > 1 && tmp[1] == 0x18)
+		beep ();
+	      else
+		toadd = tmp + 1;
+	    }
+	  else if (con.metabit)
+	    {
+	      tmp[1] |= 0x80;
+	      toadd = tmp + 1;
 	    }
 	  else
 	    {
-	      nread = con.con_to_str (tmp + 1, 59, unicode_char);
-	      /* Determine if the keystroke is modified by META.  The tricky
-		 part is to distinguish whether the right Alt key should be
-		 recognized as Alt, or as AltGr. */
-	      bool meta =
-		     /* Alt but not AltGr (= left ctrl + right alt)? */
-		     (ctrl_key_state & ALT_PRESSED) != 0
-		     && ((ctrl_key_state & CTRL_PRESSED) == 0
-			    /* but also allow Alt-AltGr: */
-			 || (ctrl_key_state & ALT_PRESSED) == ALT_PRESSED
-			 || (unicode_char <= 0x1f || unicode_char == 0x7f));
-	      if (!meta)
+	      tmp[0] = '\033';
+	      tmp[1] = cyg_tolower (tmp[1]);
+	      toadd = tmp;
+	      nread++;
+	      con.nModifiers &= ~4;
+	    }
+	}
+      break;
+
+    case MOUSE_EVENT:
+      send_winch_maybe ();
+	{
+	  MOUSE_EVENT_RECORD& mouse_event = input_rec.Event.MouseEvent;
+	  /* As a unique guard for mouse report generation,
+	     call mouse_aware() which is common with select(), so the result
+	     of select() and the actual read() will be consistent on the
+	     issue of whether input (i.e. a mouse escape sequence) will
+	     be available or not */
+	  if (mouse_aware (mouse_event))
+	    {
+	      /* Note: Reported mouse position was already retrieved by
+		 mouse_aware() and adjusted by window scroll buffer offset */
+
+	      /* Treat the double-click event like a regular button press */
+	      if (mouse_event.dwEventFlags == DOUBLE_CLICK)
 		{
-		  /* Determine if the character is in the current multibyte
-		     charset.  The test is easy.  If the multibyte sequence
-		     is > 1 and the first byte is ASCII CAN, the character
-		     has been translated into the ASCII CAN + UTF-8 replacement
-		     sequence.  If so, just ignore the keypress.
-		     FIXME: Is there a better solution? */
-		  if (nread > 1 && tmp[1] == 0x18)
-		    beep ();
-		  else
-		    toadd = tmp + 1;
+		  syscall_printf ("mouse: double-click -> click");
+		  mouse_event.dwEventFlags = 0;
 		}
-	      else if (con.metabit)
+
+	      /* This code assumes Windows never reports multiple button
+		 events at the same time. */
+	      int b = 0;
+	      char sz[32];
+	      char mode6_term = 'M';
+
+	      if (mouse_event.dwEventFlags == MOUSE_WHEELED)
 		{
-		  tmp[1] |= 0x80;
-		  toadd = tmp + 1;
+		  if (mouse_event.dwButtonState & 0xFF800000)
+		    {
+		      b = 0x41;
+		      strcpy (sz, "wheel down");
+		    }
+		  else
+		    {
+		      b = 0x40;
+		      strcpy (sz, "wheel up");
+		    }
 		}
 	      else
 		{
-		  tmp[0] = '\033';
-		  tmp[1] = cyg_tolower (tmp[1]);
-		  toadd = tmp;
-		  nread++;
-		  con.nModifiers &= ~4;
+		  /* Ignore unimportant mouse buttons */
+		  mouse_event.dwButtonState &= 0x7;
+
+		  if (mouse_event.dwEventFlags == MOUSE_MOVED)
+		    {
+		      b = con.last_button_code;
+		    }
+		  else if (mouse_event.dwButtonState < con.dwLastButtonState && !con.ext_mouse_mode6)
+		    {
+		      b = 3;
+		      strcpy (sz, "btn up");
+		    }
+		  else if ((mouse_event.dwButtonState & 1) != (con.dwLastButtonState & 1))
+		    {
+		      b = 0;
+		      strcpy (sz, "btn1 down");
+		    }
+		  else if ((mouse_event.dwButtonState & 2) != (con.dwLastButtonState & 2))
+		    {
+		      b = 2;
+		      strcpy (sz, "btn2 down");
+		    }
+		  else if ((mouse_event.dwButtonState & 4) != (con.dwLastButtonState & 4))
+		    {
+		      b = 1;
+		      strcpy (sz, "btn3 down");
+		    }
+
+		  if (con.ext_mouse_mode6 /* distinguish release */
+		      && mouse_event.dwButtonState < con.dwLastButtonState)
+		    mode6_term = 'm';
+
+		  con.last_button_code = b;
+
+		  if (mouse_event.dwEventFlags == MOUSE_MOVED)
+		    {
+		      b += 32;
+		      strcpy (sz, "move");
+		    }
+		  else
+		    {
+		      /* Remember the modified button state */
+		      con.dwLastButtonState = mouse_event.dwButtonState;
+		    }
 		}
-	    }
-	  break;
-
-	case MOUSE_EVENT:
-	  send_winch_maybe ();
-	  {
-	    MOUSE_EVENT_RECORD& mouse_event = input_rec.Event.MouseEvent;
-	    /* As a unique guard for mouse report generation,
-	       call mouse_aware() which is common with select(), so the result
-	       of select() and the actual read() will be consistent on the
-	       issue of whether input (i.e. a mouse escape sequence) will
-	       be available or not */
-	    if (mouse_aware (mouse_event))
-	      {
-		/* Note: Reported mouse position was already retrieved by
-		   mouse_aware() and adjusted by window scroll buffer offset */
-
-		/* Treat the double-click event like a regular button press */
-		if (mouse_event.dwEventFlags == DOUBLE_CLICK)
-		  {
-		    syscall_printf ("mouse: double-click -> click");
-		    mouse_event.dwEventFlags = 0;
-		  }
-
-		/* This code assumes Windows never reports multiple button
-		   events at the same time. */
-		int b = 0;
-		char sz[32];
-		char mode6_term = 'M';
-
-		if (mouse_event.dwEventFlags == MOUSE_WHEELED)
-		  {
-		    if (mouse_event.dwButtonState & 0xFF800000)
-		      {
-			b = 0x41;
-			strcpy (sz, "wheel down");
-		      }
-		    else
-		      {
-			b = 0x40;
-			strcpy (sz, "wheel up");
-		      }
-		  }
-		else
-		  {
-		    /* Ignore unimportant mouse buttons */
-		    mouse_event.dwButtonState &= 0x7;
-
-		    if (mouse_event.dwEventFlags == MOUSE_MOVED)
-		      {
-			b = con.last_button_code;
-		      }
-		    else if (mouse_event.dwButtonState < con.dwLastButtonState && !con.ext_mouse_mode6)
-		      {
-			b = 3;
-			strcpy (sz, "btn up");
-		      }
-		    else if ((mouse_event.dwButtonState & 1) != (con.dwLastButtonState & 1))
-		      {
-			b = 0;
-			strcpy (sz, "btn1 down");
-		      }
-		    else if ((mouse_event.dwButtonState & 2) != (con.dwLastButtonState & 2))
-		      {
-			b = 2;
-			strcpy (sz, "btn2 down");
-		      }
-		    else if ((mouse_event.dwButtonState & 4) != (con.dwLastButtonState & 4))
-		      {
-			b = 1;
-			strcpy (sz, "btn3 down");
-		      }
 
-		    if (con.ext_mouse_mode6 /* distinguish release */
-			&& mouse_event.dwButtonState < con.dwLastButtonState)
-			mode6_term = 'm';
+	      /* Remember mouse position */
+	      con.dwLastMousePosition.X = con.dwMousePosition.X;
+	      con.dwLastMousePosition.Y = con.dwMousePosition.Y;
 
-		    con.last_button_code = b;
+	      /* Remember the modifiers */
+	      con.nModifiers = 0;
+	      if (mouse_event.dwControlKeyState & SHIFT_PRESSED)
+		con.nModifiers |= 0x4;
+	      if (mouse_event.dwControlKeyState & ALT_PRESSED)
+		con.nModifiers |= 0x8;
+	      if (mouse_event.dwControlKeyState & CTRL_PRESSED)
+		con.nModifiers |= 0x10;
 
-		    if (mouse_event.dwEventFlags == MOUSE_MOVED)
-		      {
-			b += 32;
-			strcpy (sz, "move");
-		      }
-		    else
-		      {
-			/* Remember the modified button state */
-			con.dwLastButtonState = mouse_event.dwButtonState;
-		      }
-		  }
-
-		/* Remember mouse position */
-		con.dwLastMousePosition.X = con.dwMousePosition.X;
-		con.dwLastMousePosition.Y = con.dwMousePosition.Y;
-
-		/* Remember the modifiers */
-		con.nModifiers = 0;
-		if (mouse_event.dwControlKeyState & SHIFT_PRESSED)
-		    con.nModifiers |= 0x4;
-		if (mouse_event.dwControlKeyState & ALT_PRESSED)
-		    con.nModifiers |= 0x8;
-		if (mouse_event.dwControlKeyState & CTRL_PRESSED)
-		    con.nModifiers |= 0x10;
-
-		/* Indicate the modifiers */
-		b |= con.nModifiers;
-
-		/* We can now create the code. */
-		if (con.ext_mouse_mode6)
-		  {
-		    __small_sprintf (tmp, "\033[<%d;%d;%d%c", b,
-				     con.dwMousePosition.X + 1,
-				     con.dwMousePosition.Y + 1,
-				     mode6_term);
-		    nread = strlen (tmp);
-		  }
-		else if (con.ext_mouse_mode15)
-		  {
-		    __small_sprintf (tmp, "\033[%d;%d;%dM", b + 32,
-				     con.dwMousePosition.X + 1,
-				     con.dwMousePosition.Y + 1);
-		    nread = strlen (tmp);
-		  }
-		else if (con.ext_mouse_mode5)
-		  {
-		    unsigned int xcode = con.dwMousePosition.X + ' ' + 1;
-		    unsigned int ycode = con.dwMousePosition.Y + ' ' + 1;
-
-		    __small_sprintf (tmp, "\033[M%c", b + ' ');
-		    nread = 4;
-		    /* the neat nested encoding function of mintty
-		       does not compile in g++, so let's unfold it: */
-		    if (xcode < 0x80)
-		      tmp [nread++] = xcode;
-		    else if (xcode < 0x800)
-		      {
-			tmp [nread++] = 0xC0 + (xcode >> 6);
-			tmp [nread++] = 0x80 + (xcode & 0x3F);
-		      }
-		    else
-		      tmp [nread++] = 0;
-		    if (ycode < 0x80)
-		      tmp [nread++] = ycode;
-		    else if (ycode < 0x800)
-		      {
-			tmp [nread++] = 0xC0 + (ycode >> 6);
-			tmp [nread++] = 0x80 + (ycode & 0x3F);
-		      }
-		    else
-		      tmp [nread++] = 0;
-		  }
-		else
-		  {
-		    unsigned int xcode = con.dwMousePosition.X + ' ' + 1;
-		    unsigned int ycode = con.dwMousePosition.Y + ' ' + 1;
-		    if (xcode >= 256)
-		      xcode = 0;
-		    if (ycode >= 256)
-		      ycode = 0;
-		    __small_sprintf (tmp, "\033[M%c%c%c", b + ' ',
-				     xcode, ycode);
-		    nread = 6;	/* tmp may contain NUL bytes */
-		  }
-		syscall_printf ("mouse: %s at (%d,%d)", sz,
-				con.dwMousePosition.X,
-				con.dwMousePosition.Y);
-
-		toadd = tmp;
-	      }
-	  }
-	  break;
+	      /* Indicate the modifiers */
+	      b |= con.nModifiers;
 
-	case FOCUS_EVENT:
-	  if (con.use_focus)
-	    {
-	      if (input_rec.Event.FocusEvent.bSetFocus)
-		__small_sprintf (tmp, "\033[I");
+	      /* We can now create the code. */
+	      if (con.ext_mouse_mode6)
+		{
+		  __small_sprintf (tmp, "\033[<%d;%d;%d%c", b,
+				   con.dwMousePosition.X + 1,
+				   con.dwMousePosition.Y + 1,
+				   mode6_term);
+		  nread = strlen (tmp);
+		}
+	      else if (con.ext_mouse_mode15)
+		{
+		  __small_sprintf (tmp, "\033[%d;%d;%dM", b + 32,
+				   con.dwMousePosition.X + 1,
+				   con.dwMousePosition.Y + 1);
+		  nread = strlen (tmp);
+		}
+	      else if (con.ext_mouse_mode5)
+		{
+		  unsigned int xcode = con.dwMousePosition.X + ' ' + 1;
+		  unsigned int ycode = con.dwMousePosition.Y + ' ' + 1;
+
+		  __small_sprintf (tmp, "\033[M%c", b + ' ');
+		  nread = 4;
+		  /* the neat nested encoding function of mintty
+		     does not compile in g++, so let's unfold it: */
+		  if (xcode < 0x80)
+		    tmp [nread++] = xcode;
+		  else if (xcode < 0x800)
+		    {
+		      tmp [nread++] = 0xC0 + (xcode >> 6);
+		      tmp [nread++] = 0x80 + (xcode & 0x3F);
+		    }
+		  else
+		    tmp [nread++] = 0;
+		  if (ycode < 0x80)
+		    tmp [nread++] = ycode;
+		  else if (ycode < 0x800)
+		    {
+		      tmp [nread++] = 0xC0 + (ycode >> 6);
+		      tmp [nread++] = 0x80 + (ycode & 0x3F);
+		    }
+		  else
+		    tmp [nread++] = 0;
+		}
 	      else
-		__small_sprintf (tmp, "\033[O");
+		{
+		  unsigned int xcode = con.dwMousePosition.X + ' ' + 1;
+		  unsigned int ycode = con.dwMousePosition.Y + ' ' + 1;
+		  if (xcode >= 256)
+		    xcode = 0;
+		  if (ycode >= 256)
+		    ycode = 0;
+		  __small_sprintf (tmp, "\033[M%c%c%c", b + ' ',
+				   xcode, ycode);
+		  nread = 6;	/* tmp may contain NUL bytes */
+		}
+	      syscall_printf ("mouse: %s at (%d,%d)", sz,
+			      con.dwMousePosition.X,
+			      con.dwMousePosition.Y);
 
 	      toadd = tmp;
-	      nread = 3;
 	    }
-	  break;
-
-	case WINDOW_BUFFER_SIZE_EVENT:
-	  send_winch_maybe ();
-	  /* fall through */
-	default:
-	  continue;
 	}
+      break;
 
-      if (toadd)
+    case FOCUS_EVENT:
+      if (con.use_focus)
 	{
-	  line_edit_status res = line_edit (toadd, nread, ti);
-	  if (res == line_edit_signalled)
-	    goto sig_exit;
-	  else if (res == line_edit_input_done)
-	    break;
-	}
-    }
+	  if (input_rec.Event.FocusEvent.bSetFocus)
+	    __small_sprintf (tmp, "\033[I");
+	  else
+	    __small_sprintf (tmp, "\033[O");
 
-  while (buflen)
-    if ((ch = get_readahead ()) < 0)
+	  toadd = tmp;
+	  nread = 3;
+	}
       break;
-    else
-      {
-	buf[copied_chars++] = (unsigned char)(ch & 0xff);
-	buflen--;
-      }
-#undef buf
 
-  buflen = copied_chars;
-  return;
-
-err:
-  __seterrno ();
-  buflen = (size_t) -1;
-  return;
+    case WINDOW_BUFFER_SIZE_EVENT:
+      if (send_winch_maybe ())
+	return input_winch;
+      /* fall through */
+    default:
+      return input_processing;
+    }
 
-sig_exit:
-  set_sig_errno (EINTR);
-  buflen = (size_t) -1;
+  if (toadd)
+    {
+      ssize_t ret;
+      line_edit_status res = line_edit (toadd, nread, *ti, &ret);
+      if (res == line_edit_signalled)
+	return input_signalled;
+      else if (res == line_edit_input_done)
+	{
+	  input_ready = true;
+	  return input_ok;
+	}
+    }
+  return input_processing;
 }
 
 void
@@ -844,6 +940,8 @@ fhandler_console::open (int flags, mode_t)
     }
   set_output_handle (h);
 
+  setup_io_mutex ();
+
   if (con.fillin (get_output_handle ()))
     {
       con.current_win32_attr = con.b.wAttributes;
@@ -855,10 +953,23 @@ fhandler_console::open (int flags, mode_t)
   get_ttyp ()->rstcons (false);
   set_open_status ();
 
+  if (getpid () == con.owner && con.cap24bit_color)
+    {
+      DWORD dwMode;
+      /* Enable xterm compatible mode in output */
+      GetConsoleMode (get_output_handle (), &dwMode);
+      dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+      SetConsoleMode (get_output_handle (), dwMode);
+      /* Enable xterm compatible mode in input */
+      GetConsoleMode (get_handle (), &dwMode);
+      dwMode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
+      SetConsoleMode (get_handle (), dwMode);
+    }
+
   DWORD cflags;
   if (GetConsoleMode (get_handle (), &cflags))
-    SetConsoleMode (get_handle (),
-		    ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT | cflags);
+    SetConsoleMode (get_handle (), ENABLE_WINDOW_INPUT
+		    | (con.cap24bit_color ? 0 : ENABLE_MOUSE_INPUT) | cflags);
 
   debug_printf ("opened conin$ %p, conout$ %p", get_handle (),
 		get_output_handle ());
@@ -878,6 +989,26 @@ fhandler_console::open_setup (int flags)
 int
 fhandler_console::close ()
 {
+  debug_printf ("closing: %p, %p", get_handle (), get_output_handle ());
+
+  CloseHandle (input_mutex);
+  input_mutex = NULL;
+  CloseHandle (output_mutex);
+  output_mutex = NULL;
+
+  if (shared_console_info && getpid () == con.owner && con.cap24bit_color)
+    {
+      DWORD dwMode;
+      /* Disable xterm compatible mode in input */
+      GetConsoleMode (get_handle (), &dwMode);
+      dwMode &= ~ENABLE_VIRTUAL_TERMINAL_INPUT;
+      SetConsoleMode (get_handle (), dwMode);
+      /* Disable xterm compatible mode in output */
+      GetConsoleMode (get_output_handle (), &dwMode);
+      dwMode &= ~ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+      SetConsoleMode (get_output_handle (), dwMode);
+    }
+
   CloseHandle (get_handle ());
   CloseHandle (get_output_handle ());
   if (!have_execed)
@@ -891,6 +1022,7 @@ fhandler_console::ioctl (unsigned int cmd, void *arg)
   int res = fhandler_termios::ioctl (cmd, arg);
   if (res <= 0)
     return res;
+  acquire_output_mutex (INFINITE);
   switch (cmd)
     {
       case TIOCGWINSZ:
@@ -906,20 +1038,25 @@ fhandler_console::ioctl (unsigned int cmd, void *arg)
 	    syscall_printf ("WINSZ: (row=%d,col=%d)",
 			   ((struct winsize *) arg)->ws_row,
 			   ((struct winsize *) arg)->ws_col);
+	    release_output_mutex ();
 	    return 0;
 	  }
 	else
 	  {
 	    syscall_printf ("WINSZ failed");
 	    __seterrno ();
+	    release_output_mutex ();
 	    return -1;
 	  }
+	release_output_mutex ();
 	return 0;
       case TIOCSWINSZ:
 	bg_check (SIGTTOU);
+	release_output_mutex ();
 	return 0;
       case KDGKBMETA:
 	*(int *) arg = (con.metabit) ? K_METABIT : K_ESCPREFIX;
+	release_output_mutex ();
 	return 0;
       case KDSKBMETA:
 	if ((intptr_t) arg == K_METABIT)
@@ -929,16 +1066,20 @@ fhandler_console::ioctl (unsigned int cmd, void *arg)
 	else
 	  {
 	    set_errno (EINVAL);
+	    release_output_mutex ();
 	    return -1;
 	  }
+	release_output_mutex ();
 	return 0;
       case TIOCLINUX:
 	if (*(unsigned char *) arg == 6)
 	  {
 	    *(unsigned char *) arg = (unsigned char) con.nModifiers;
+	    release_output_mutex ();
 	    return 0;
 	  }
 	set_errno (EINVAL);
+	release_output_mutex ();
 	return -1;
       case FIONREAD:
 	{
@@ -951,17 +1092,20 @@ fhandler_console::ioctl (unsigned int cmd, void *arg)
 	  if (!PeekConsoleInputW (get_handle (), inp, INREC_SIZE, &n))
 	    {
 	      set_errno (EINVAL);
+	      release_output_mutex ();
 	      return -1;
 	    }
 	  while (n-- > 0)
 	    if (inp[n].EventType == KEY_EVENT && inp[n].Event.KeyEvent.bKeyDown)
 	      ++ret;
 	  *(int *) arg = ret;
+	  release_output_mutex ();
 	  return 0;
 	}
 	break;
     }
 
+  release_output_mutex ();
   return fhandler_base::ioctl (cmd, arg);
 }
 
@@ -987,6 +1131,13 @@ fhandler_console::output_tcsetattr (int, struct termios const *t)
   /* All the output bits we can ignore */
 
   DWORD flags = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
+  /* If system has 24bit color capability, use xterm compatible mode. */
+  if (con.cap24bit_color)
+    {
+      flags |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+      if (!(t->c_oflag & OPOST) || !(t->c_oflag & ONLCR))
+	flags |= DISABLE_NEWLINE_AUTO_RETURN;
+    }
 
   int res = SetConsoleMode (get_output_handle (), flags) ? 0 : -1;
   if (res)
@@ -1043,7 +1194,10 @@ fhandler_console::input_tcsetattr (int, struct termios const *t)
       flags |= ENABLE_PROCESSED_INPUT;
     }
 
-  flags |= ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT;
+  flags |= ENABLE_WINDOW_INPUT | (con.cap24bit_color ? 0 : ENABLE_MOUSE_INPUT);
+  /* if system has 24bit color capability, use xterm compatible mode. */
+  if (con.cap24bit_color)
+    flags |= ENABLE_VIRTUAL_TERMINAL_INPUT;
 
   int res;
   if (flags == oflags)
@@ -1110,7 +1264,8 @@ fhandler_console::tcgetattr (struct termios *t)
 }
 
 fhandler_console::fhandler_console (fh_devices unit) :
-  fhandler_termios ()
+  fhandler_termios (), input_ready (false),
+  input_mutex (NULL), output_mutex (NULL)
 {
   if (unit > 0)
     dev ().parse (unit);
@@ -1602,11 +1757,32 @@ static const char base_chars[256] =
 /*F0 F1 F2 F3 F4 F5 F6 F7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
 /*F8 F9 FA FB FC FD FE FF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR };
 
+static const char table256[256] =
+{
+   0, 4, 2, 6, 1, 5, 3, 7, 8,12,10,14, 9,13,11,15,
+   0, 1, 1, 1, 9, 9, 2, 3, 3, 3, 3, 9, 2, 3, 3, 3,
+   3,11, 2, 3, 3, 3,11,11,10, 3, 3,11,11,11,10,10,
+  11,11,11,11, 4, 5, 5, 5, 5, 9, 6, 8, 8, 8, 8, 9,
+   6, 8, 8, 8, 8, 7, 6, 8, 8, 8, 7, 7, 6, 8, 8, 7,
+   7,11,10,10, 7, 7,11,11, 4, 5, 5, 5, 5,13, 6, 8,
+   8, 8, 8, 7, 6, 8, 8, 8, 7, 7, 6, 8, 8, 7, 7, 7,
+   6, 8, 7, 7, 7, 7,14, 7, 7, 7, 7, 7, 4, 5, 5, 5,
+  13,13, 6, 8, 8, 8, 7, 7, 6, 8, 8, 7, 7, 7, 6, 8,
+   7, 7, 7, 7,14, 7, 7, 7, 7, 7,14, 7, 7, 7, 7,15,
+  12, 5, 5,13,13,13, 6, 8, 8, 7, 7,13, 6, 8, 7, 7,
+   7, 7,14, 7, 7, 7, 7, 7,14, 7, 7, 7, 7,15,14,14,
+   7, 7,15,15,12,12,13,13,13,13,12,12, 7, 7,13,13,
+  14, 7, 7, 7, 7, 7,14, 7, 7, 7, 7,15,14,14, 7, 7,
+  15,15,14,14, 7,15,15,15, 0, 0, 0, 0, 0, 0, 8, 8,
+   8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7,15,15
+};
+
 void
 fhandler_console::char_command (char c)
 {
   int x, y, n;
   char buf[40];
+  int r, g, b;
 
   switch (c)
     {
@@ -1678,6 +1854,40 @@ fhandler_console::char_command (char c)
 	     case 37:		/* WHITE foreg */
 	       con.fg = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
 	       break;
+	     case 38:
+	       if (con.nargs < 1)
+		 /* Sequence error (abort) */
+		 break;
+	       switch (con.args[1])
+		 {
+		 case 2:
+		   if (con.nargs != 4)
+		     /* Sequence error (abort) */
+		     break;
+		   r = con.args[2];
+		   g = con.args[3];
+		   b = con.args[4];
+		   r = r < (95 + 1) / 2 ? 0 : r > 255 ? 5 : (r - 55 + 20) / 40;
+		   g = g < (95 + 1) / 2 ? 0 : g > 255 ? 5 : (g - 55 + 20) / 40;
+		   b = b < (95 + 1) / 2 ? 0 : b > 255 ? 5 : (b - 55 + 20) / 40;
+		   con.fg = table256[16 + r*36 + g*6 + b];
+		   break;
+		 case 5:
+		   if (con.nargs != 2)
+		     /* Sequence error (abort) */
+		     break;
+		   {
+		     int idx = con.args[2];
+		     if (idx < 0)
+		       idx = 0;
+		     if (idx > 255)
+		       idx = 255;
+		     con.fg = table256[idx];
+		   }
+		   break;
+		 }
+	       i += con.nargs;
+	       break;
 	     case 39:
 	       con.fg = con.default_color & FOREGROUND_ATTR_MASK;
 	       break;
@@ -1705,6 +1915,40 @@ fhandler_console::char_command (char c)
 	     case 47:    /* WHITE background */
 	       con.bg = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
 	       break;
+	     case 48:
+	       if (con.nargs < 1)
+		 /* Sequence error (abort) */
+		 break;
+	       switch (con.args[1])
+		 {
+		 case 2:
+		   if (con.nargs != 4)
+		     /* Sequence error (abort) */
+		     break;
+		   r = con.args[2];
+		   g = con.args[3];
+		   b = con.args[4];
+		   r = r < (95 + 1) / 2 ? 0 : r > 255 ? 5 : (r - 55 + 20) / 40;
+		   g = g < (95 + 1) / 2 ? 0 : g > 255 ? 5 : (g - 55 + 20) / 40;
+		   b = b < (95 + 1) / 2 ? 0 : b > 255 ? 5 : (b - 55 + 20) / 40;
+		   con.bg = table256[16 + r*36 + g*6 + b] << 4;
+		   break;
+		 case 5:
+		   if (con.nargs != 2)
+		     /* Sequence error (abort) */
+		     break;
+		   {
+		     int idx = con.args[2];
+		     if (idx < 0)
+		       idx = 0;
+		     if (idx > 255)
+		       idx = 255;
+		     con.bg = table256[idx] << 4;
+		   }
+		   break;
+		 }
+	       i += con.nargs;
+	       break;
 	     case 49:
 	       con.bg = con.default_color & BACKGROUND_ATTR_MASK;
 	       break;
@@ -1938,9 +2182,12 @@ fhandler_console::char_command (char c)
 	 fhandler_console object associated with standard input.
 	 So puts_readahead does not work.
 	 Use a common console read-ahead buffer instead. */
+      acquire_input_mutex (INFINITE);
       con.cons_rapoi = NULL;
       strcpy (con.cons_rabuf, buf);
       con.cons_rapoi = con.cons_rabuf;
+      release_input_mutex ();
+      PostMessageW (GetConsoleWindow (), WM_SETFOCUS, 0, 0);
       break;
     case 'n':
       switch (con.args[0])
@@ -1950,9 +2197,12 @@ fhandler_console::char_command (char c)
 	  y -= con.b.srWindow.Top;
 	  /* x -= con.b.srWindow.Left;		// not available yet */
 	  __small_sprintf (buf, "\033[%d;%dR", y + 1, x + 1);
+	  acquire_input_mutex (INFINITE);
 	  con.cons_rapoi = NULL;
 	  strcpy (con.cons_rabuf, buf);
 	  con.cons_rapoi = con.cons_rabuf;
+	  release_input_mutex ();
+	  PostMessageW (GetConsoleWindow (), WM_SETFOCUS, 0, 0);
 	  break;
 	default:
 	  goto bad_escape;
@@ -2143,10 +2393,12 @@ fhandler_console::write_normal (const unsigned char *src,
   /* Loop over src buffer as long as we have just simple characters.  Stop
      as soon as we reach the conversion limit, or if we encounter a control
      character or a truncated or invalid mutibyte sequence. */
+  /* If system has 24bit color capability, just write all control
+     sequences to console since xterm compatible mode is enabled. */
   memset (&ps, 0, sizeof ps);
   while (found < end
 	 && found - src < CONVERT_LIMIT
-	 && base_chars[*found] == NOR)
+	 && (con.cap24bit_color || base_chars[*found] == NOR) )
     {
       switch (ret = f_mbtowc (_REENT, NULL, (const char *) found,
 			       end - found, &ps))
@@ -2295,6 +2547,7 @@ fhandler_console::write (const void *vsrc, size_t len)
 
   debug_printf ("%p, %ld", vsrc, len);
 
+  acquire_output_mutex (INFINITE);
   while (src < end)
     {
       paranoid_printf ("char %0c state is %d", *src, con.state);
@@ -2303,7 +2556,10 @@ fhandler_console::write (const void *vsrc, size_t len)
 	case normal:
 	  src = write_normal (src, end);
 	  if (!src) /* write_normal failed */
-	    return -1;
+	    {
+	      release_output_mutex ();
+	      return -1;
+	    }
 	  break;
 	case gotesc:
 	  if (*src == '[')		/* CSI Control Sequence Introducer */
@@ -2394,6 +2650,8 @@ fhandler_console::write (const void *vsrc, size_t len)
 	    con.rarg = con.rarg * 10 + (*src - '0');
 	  else if (*src == ';' && (con.rarg == 2 || con.rarg == 0))
 	    con.state = gettitle;
+	  else if (*src == ';' && (con.rarg == 4 || con.rarg == 104))
+	    con.state = eatpalette;
 	  else
 	    con.state = eattitle;
 	  src++;
@@ -2416,6 +2674,21 @@ fhandler_console::write (const void *vsrc, size_t len)
 	    src++;
 	    break;
 	  }
+	case eatpalette:
+	  if (*src == '\033')
+	    con.state = endpalette;
+	  else if (*src == '\a')
+	    con.state = normal;
+	  src++;
+	  break;
+	case endpalette:
+	  if (*src == '\\')
+	    con.state = normal;
+	  else
+	    /* Sequence error (abort) */
+	    con.state = normal;
+	  src++;
+	  break;
 	case gotsquare:
 	  if (*src == ';')
 	    {
@@ -2455,6 +2728,7 @@ fhandler_console::write (const void *vsrc, size_t len)
 	  break;
 	}
     }
+  release_output_mutex ();
 
   syscall_printf ("%ld = fhandler_console::write(...)", len);
 
@@ -2582,6 +2856,7 @@ void
 fhandler_console::fixup_after_fork_exec (bool execing)
 {
   set_unit ();
+  setup_io_mutex ();
 }
 
 // #define WINSTA_ACCESS (WINSTA_READATTRIBUTES | STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | WINSTA_CREATEDESKTOP | WINSTA_EXITWINDOWS)
@@ -2764,3 +3039,57 @@ fhandler_console::need_invisible ()
   debug_printf ("invisible_console %d", invisible_console);
   return b;
 }
+
+DWORD
+fhandler_console::__acquire_input_mutex (const char *fn, int ln, DWORD ms)
+{
+#ifdef DEBUGGING
+  strace.prntf (_STRACE_TERMIOS, fn, "(%d): trying to get input_mutex", ln);
+#endif
+  DWORD res = WaitForSingleObject (input_mutex, ms);
+  if (res != WAIT_OBJECT_0)
+    strace.prntf (_STRACE_TERMIOS, fn,
+		  "(%d): Failed to acquire input_mutex %08x",
+		  ln, GetLastError ());
+#ifdef DEBUGGING
+  else
+    strace.prntf (_STRACE_TERMIOS, fn, "(%d): got input_mutex", ln);
+#endif
+  return res;
+}
+
+void
+fhandler_console::__release_input_mutex (const char *fn, int ln)
+{
+  ReleaseMutex (input_mutex);
+#ifdef DEBUGGING
+  strace.prntf (_STRACE_TERMIOS, fn, "(%d): release input_mutex", ln);
+#endif
+}
+
+DWORD
+fhandler_console::__acquire_output_mutex (const char *fn, int ln, DWORD ms)
+{
+#ifdef DEBUGGING
+  strace.prntf (_STRACE_TERMIOS, fn, "(%d): trying to get output_mutex", ln);
+#endif
+  DWORD res = WaitForSingleObject (output_mutex, ms);
+  if (res != WAIT_OBJECT_0)
+    strace.prntf (_STRACE_TERMIOS, fn,
+		  "(%d): Failed to acquire output_mutex %08x",
+		  ln, GetLastError ());
+#ifdef DEBUGGING
+  else
+    strace.prntf (_STRACE_TERMIOS, fn, "(%d): got output_mutex", ln);
+#endif
+  return res;
+}
+
+void
+fhandler_console::__release_output_mutex (const char *fn, int ln)
+{
+  ReleaseMutex (output_mutex);
+#ifdef DEBUGGING
+  strace.prntf (_STRACE_TERMIOS, fn, "(%d): release output_mutex", ln);
+#endif
+}
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index 9b18e8f80..85242ec06 100644
--- a/winsup/cygwin/select.cc
+++ b/winsup/cygwin/select.cc
@@ -202,7 +202,9 @@ select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
 	     right value >= 0, matching the number of bits set in the
 	     fds records.  if ret is 0, continue to loop. */
 	  ret = sel.poll (readfds, writefds, exceptfds);
-	  if (!ret)
+	  if (ret < 0)
+	    wait_state = select_stuff::select_signalled;
+	  else if (!ret)
 	    wait_state = select_stuff::select_set_zero;
 	}
       /* Always clean up everything here.  If we're looping then build it
@@ -479,6 +481,7 @@ was_timeout:
 	 events like mouse movements.  The verify function will detect these
 	 situations.  If it returns false, then this wakeup was a false alarm
 	 and we should go back to waiting. */
+      int ret = 0;
       while ((s = s->next))
 	if (s->saw_error ())
 	  {
@@ -488,8 +491,13 @@ was_timeout:
 	  }
 	else if ((((wait_ret >= m && s->windows_handle)
 	           || s->h == w4[wait_ret]))
-		 && s->verify (s, readfds, writefds, exceptfds))
+		 && (ret = s->verify (s, readfds, writefds, exceptfds)) > 0)
 	  res = select_ok;
+	else if (ret < 0)
+	  {
+	    res = select_signalled;
+	    goto out;
+	  }
 
       select_printf ("res after verify %d", res);
       break;
@@ -539,8 +547,12 @@ select_stuff::poll (fd_set *readfds, fd_set *writefds, fd_set *exceptfds)
   int n = 0;
   select_record *s = &start;
   while ((s = s->next))
-    n += (!s->peek || s->peek (s, true)) ?
-	 set_bits (s, readfds, writefds, exceptfds) : 0;
+    {
+      int ret = s->peek ? s->peek (s, true) : 1;
+      if (ret < 0)
+	return -1;
+      n += (ret > 0) ?  set_bits (s, readfds, writefds, exceptfds) : 0;
+    }
   return n;
 }
 
@@ -1010,16 +1022,10 @@ peek_console (select_record *me, bool)
     return me->write_ready;
 
   if (fh->get_cons_readahead_valid ())
-    {
-      select_printf ("cons_readahead");
-      return me->read_ready = true;
-    }
+    return me->read_ready = true;
 
-  if (fh->get_readahead_valid ())
-    {
-      select_printf ("readahead");
-      return me->read_ready = true;
-    }
+  if (fh->input_ready)
+    return me->read_ready = true;
 
   if (me->read_ready)
     {
@@ -1030,46 +1036,25 @@ peek_console (select_record *me, bool)
   INPUT_RECORD irec;
   DWORD events_read;
   HANDLE h;
-  char tmpbuf[17];
   set_handle_or_return_if_not_open (h, me);
 
-  for (;;)
-    if (fh->bg_check (SIGTTIN, true) <= bg_eof)
-      return me->read_ready = true;
-    else if (!PeekConsoleInputW (h, &irec, 1, &events_read) || !events_read)
-      break;
-    else
-      {
-	fh->send_winch_maybe ();
-	if (irec.EventType == KEY_EVENT)
-	  {
-	    if (irec.Event.KeyEvent.bKeyDown)
-	      {
-		/* Ignore Alt+Numpad keys. They are eventually handled in the
-		   key-up case below. */
-		if (is_alt_numpad_key (&irec))
-		   ;
-		/* Handle normal input. */
-		else if (irec.Event.KeyEvent.uChar.UnicodeChar
-			 || fhandler_console::get_nonascii_key (irec, tmpbuf))
-		  return me->read_ready = true;
-	      }
-	    /* Ignore key up events, except for Alt+Numpad events. */
-	    else if (is_alt_numpad_event (&irec))
-	      return me->read_ready = true;
-	  }
-	else
-	  {
-	    if (irec.EventType == MOUSE_EVENT
-		&& fh->mouse_aware (irec.Event.MouseEvent))
-		return me->read_ready = true;
-	    if (irec.EventType == FOCUS_EVENT && fh->focus_aware ())
-		return me->read_ready = true;
-	  }
-
-	/* Read and discard the event */
-	ReadConsoleInputW (h, &irec, 1, &events_read);
-      }
+  while (!fh->input_ready && !fh->get_cons_readahead_valid ())
+    {
+      if (fh->bg_check (SIGTTIN, true) <= bg_eof)
+	return me->read_ready = true;
+      else if (!PeekConsoleInputW (h, &irec, 1, &events_read) || !events_read)
+	break;
+      fh->acquire_input_mutex (INFINITE);
+      if (fhandler_console::input_winch == fh->process_input_message ())
+	{
+	  set_sig_errno (EINTR);
+	  fh->release_input_mutex ();
+	  return -1;
+	}
+      fh->release_input_mutex ();
+    }
+  if (fh->input_ready || fh->get_cons_readahead_valid ())
+    return me->read_ready = true;
 
   return me->write_ready;
 }
@@ -1081,7 +1066,6 @@ verify_console (select_record *me, fd_set *rfds, fd_set *wfds,
   return peek_console (me, true);
 }
 
-
 select_record *
 fhandler_console::select_read (select_stuff *ss)
 {
@@ -1096,7 +1080,7 @@ fhandler_console::select_read (select_stuff *ss)
   s->peek = peek_console;
   s->h = get_handle ();
   s->read_selected = true;
-  s->read_ready = get_readahead_valid ();
+  s->read_ready = input_ready || get_cons_readahead_valid ();
   return s;
 }
 
-- 
2.17.0


[-- Attachment #4: 0003-Cygwin-pty-support-pseudo-console.patch --]
[-- Type: application/octet-stream, Size: 38103 bytes --]

From c5af197c8e4c19eb24d80ea8b2ca87241d3440a9 Mon Sep 17 00:00:00 2001
From: Takashi Yano <takashi.yano@nifty.ne.jp>
Date: Sat, 30 Mar 2019 16:24:38 +0900
Subject: [PATCH 3/3] Cygwin: pty: support pseudo console.

---
 winsup/cygwin/dtable.cc               |  28 ++
 winsup/cygwin/fhandler.h              |  30 +-
 winsup/cygwin/fhandler_console.cc     |  31 ++
 winsup/cygwin/fhandler_tty.cc         | 399 ++++++++++++++++++++++++--
 winsup/cygwin/fork.cc                 |  20 ++
 winsup/cygwin/select.cc               |   3 +
 winsup/cygwin/spawn.cc                |  32 +++
 winsup/cygwin/tty.cc                  |   3 +
 winsup/cygwin/tty.h                   |  19 +-
 winsup/utils/cygwin-console-helper.cc |  14 +-
 10 files changed, 540 insertions(+), 39 deletions(-)

diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
index 636221a77..cc0a52cb1 100644
--- a/winsup/cygwin/dtable.cc
+++ b/winsup/cygwin/dtable.cc
@@ -68,6 +68,24 @@ set_std_handle (int fd)
     SetStdHandle (std_consts[fd], fh ? fh->get_output_handle () : NULL);
 }
 
+#if 0
+static bool
+is_running_as_service (void)
+{
+  DWORD dwSize = 0;
+  PTOKEN_GROUPS pGroupInfo;
+  tmp_pathbuf tp;
+  bool ret = false;
+  pGroupInfo = (PTOKEN_GROUPS) tp.w_get ();
+  NtQueryInformationToken (hProcToken, TokenGroups, pGroupInfo,
+					2 * NT_MAX_PATH, &dwSize);
+  for (DWORD i=0; i<pGroupInfo->GroupCount && !ret; i++)
+    if (well_known_service_sid == pGroupInfo->Groups[i].Sid)
+      ret = true;
+  return ret;
+}
+#endif
+
 int
 dtable::extend (size_t howmuch, size_t min)
 {
@@ -147,6 +165,11 @@ dtable::get_debugger_info ()
 void
 dtable::stdio_init ()
 {
+#if 1
+  if (wcsstr (myself->progname, L"mintty"))
+    return;
+#endif
+
   if (myself->cygstarted || ISSTATE (myself, PID_CYGPARENT))
     {
       tty_min *t = cygwin_shared->tty.get_cttyp ();
@@ -159,6 +182,11 @@ dtable::stdio_init ()
   HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
   HANDLE err = GetStdHandle (STD_ERROR_HANDLE);
 
+#if 0
+  if (!is_running_as_service () && !in && !out && !err)
+    return;
+#endif
+
   init_std_file_from_handle (0, in);
 
   /* STD_ERROR_HANDLE has been observed to be the same as
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 0477888d7..b5a6bbcc7 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1995,6 +1995,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 *) {}
 
@@ -2066,6 +2067,19 @@ class fhandler_pty_common: public fhandler_termios
     return fh;
   }
 
+  bool attach_pcon_in_spawn (void)
+  {
+    return get_ttyp ()->attach_pcon_in_spawn;
+  }
+  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);
 };
@@ -2073,7 +2087,8 @@ class fhandler_pty_common: public fhandler_termios
 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);
@@ -2086,6 +2101,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);
@@ -2126,6 +2143,12 @@ class fhandler_pty_slave: public fhandler_pty_common
     copyto (fh);
     return fh;
   }
+  void reset_switch_to_pcon (void)
+  {
+    if (get_ttyp ()->switch_to_pcon == 2)
+      Sleep (20);
+    get_ttyp ()->switch_to_pcon = 0;
+  }
 };
 
 #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit))
@@ -2134,15 +2157,14 @@ 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);
 
diff --git a/winsup/cygwin/fhandler_console.cc b/winsup/cygwin/fhandler_console.cc
index f7b50d0a8..bcd71a4d1 100644
--- a/winsup/cygwin/fhandler_console.cc
+++ b/winsup/cygwin/fhandler_console.cc
@@ -1011,6 +1011,18 @@ fhandler_console::close ()
 
   CloseHandle (get_handle ());
   CloseHandle (get_output_handle ());
+
+  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;
@@ -3040,6 +3052,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..8272b14a1 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -25,6 +25,14 @@ details. */
 #include "child_info.h"
 #include <asm/socket.h>
 #include "cygwait.h"
+#include "tls_pbuf.h"
+
+#ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
+#define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE 0x00020016
+#endif
+
+extern "C" int sscanf (const char *, const char *, ...);
+extern "C" int ttyname_r(int, char*, size_t);
 
 #define close_maybe(h) \
   do { \
@@ -39,6 +47,7 @@ struct pipe_request {
 
 struct pipe_reply {
   HANDLE from_master;
+  HANDLE from_master_cyg;
   HANDLE to_master;
   HANDLE to_master_cyg;
   DWORD error;
@@ -67,7 +76,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 +244,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 +305,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 +340,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),
+  pcon_attached (false)
 {
   if (unit >= 0)
     dev ().parse (DEV_PTYS_MAJOR, unit);
@@ -340,11 +350,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 +409,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 +454,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 +496,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,22 +508,28 @@ 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);
 
   fhandler_console::need_invisible ();
   set_open_status ();
+  if (pcon_attached && get_ttyp ()->switch_to_pcon == 0)
+    get_ttyp ()->switch_to_pcon = 1;
   return 1;
 
 err:
@@ -548,6 +578,9 @@ 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 */
   fhandler_pty_common::close ();
@@ -609,6 +642,20 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 
   push_process_state process_state (PID_TTYOU);
 
+  reset_switch_to_pcon ();
+
+  if (pcon_attached)
+    { /* Push slave output to pseudo console screen buffer */
+      DWORD wLen, written = 0;
+      char *p = (char *) ptr;
+      while (written <  len)
+	{
+	  WriteFile (get_output_handle (), p, len - written, &wLen, NULL);
+	  written += wLen;
+	  p += wLen;
+	}
+    }
+
   if (!process_opost_output (get_output_handle_cyg (), ptr, towrite, false))
     {
       DWORD err = GetLastError ();
@@ -644,10 +691,12 @@ 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);
 
+  reset_switch_to_pcon ();
+
   if (is_nonblocking () || !ptr) /* Indicating tcflush(). */
     time_to_wait = 0;
   else if ((get_ttyp ()->ti.c_lflag & ICANON))
@@ -777,7 +826,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);
@@ -890,6 +939,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 +947,7 @@ fhandler_pty_slave::tcgetattr (struct termios *t)
 int
 fhandler_pty_slave::tcsetattr (int, const struct termios *t)
 {
+  reset_switch_to_pcon ();
   acquire_output_mutex (INFINITE);
   get_ttyp ()->ti = *t;
   release_output_mutex ();
@@ -908,8 +959,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)
     {
       size_t len = UINT_MAX;
@@ -929,6 +981,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 +1048,55 @@ fhandler_pty_slave::ioctl (unsigned int cmd, void *arg)
       get_ttyp ()->winsize = get_ttyp ()->arg.winsize;
       break;
     case TIOCSWINSZ:
+      HPCON hPseudoConsole = get_ttyp ()->hPseudoConsole;
+      COORD size;
+      size.X = ((struct winsize *) arg)->ws_col;
+      size.Y = ((struct winsize *) arg)->ws_row;
+#if 0
+      CONSOLE_SCREEN_BUFFER_INFO csbi;
+      if (!GetConsoleScreenBufferInfo (get_output_handle (), &csbi))
+	goto cyg_setsize;
+      if (size.X == csbi.srWindow.Right - csbi.srWindow.Left + 1 &&
+	  size.Y == csbi.srWindow.Bottom - csbi.srWindow.Top + 1)
+	goto cyg_setsize;
+#endif
+      if (hPseudoConsole)
+	{
+	  if (myself->pid == get_ttyp ()->master_pid)
+	    {
+	      HMODULE hModule = GetModuleHandle ("kernel32.dll");
+	      FARPROC func = GetProcAddress (hModule, "ResizePseudoConsole");
+	      HRESULT (WINAPI *ResizePseudoConsole) (HPCON, COORD) = NULL;
+	      ResizePseudoConsole = (HRESULT (WINAPI *) (HPCON, COORD)) func;
+	      if (!ResizePseudoConsole (hPseudoConsole, size))
+		goto cyg_setsize;
+	    }
+	  else
+	    {
+	      DWORD pidRestore =
+		fhandler_console::get_console_process_id (GetCurrentProcessId (), false);
+	      if (!pcon_attached)
+		{
+		  FreeConsole ();
+		  AttachConsole (getHelperProcessId ());
+		}
+	      SMALL_RECT rect;
+	      if (!SetConsoleScreenBufferSize (get_output_handle (), size))
+		goto detach_pcon;
+	      rect.Left = 0;
+	      rect.Top = 0;
+	      rect.Right = size.X-1;
+	      rect.Bottom = size.Y-1;
+	      SetConsoleWindowInfo (get_output_handle (), TRUE, &rect);
+detach_pcon:
+	      if (!pcon_attached && pidRestore)
+		{
+		  FreeConsole ();
+		  AttachConsole (pidRestore);
+		}
+	    }
+	}
+cyg_setsize:
       if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
 	  || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
 	{
@@ -1228,8 +1330,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);
@@ -1285,7 +1388,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 ();
 }
 
@@ -1295,8 +1399,9 @@ fhandler_pty_master::close ()
   OBJECT_BASIC_INFORMATION obi;
   NTSTATUS status;
 
-  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)
@@ -1320,6 +1425,20 @@ fhandler_pty_master::close ()
 	    }
 	  release_output_mutex ();
 	  master_fwd_thread->terminate_thread ();
+
+	  /* Pseudo Console Support */
+	  if (get_ttyp ()->hPseudoConsole)
+	    {
+	      SetEvent (get_ttyp ()->hHelperGoodbye);
+	      WaitForSingleObject (get_ttyp ()->hHelperProcess, INFINITE);
+	      HMODULE hModule = GetModuleHandle ("kernel32.dll");
+	      FARPROC func = GetProcAddress (hModule, "ClosePseudoConsole");
+	      VOID (WINAPI *ClosePseudoConsole) (HPCON) = NULL;
+	      ClosePseudoConsole = (VOID (WINAPI *) (HPCON)) func;
+	      ClosePseudoConsole (get_ttyp ()->hPseudoConsole);
+	      get_ttyp ()->hPseudoConsole = NULL;
+	      get_ttyp ()->switch_to_pcon = 0;
+	    }
 	}
     }
 
@@ -1344,17 +1463,22 @@ fhandler_pty_master::close ()
 
   if (!ForceCloseHandle (from_master))
     termios_printf ("error closing from_master %p, %E", from_master);
+  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;
+  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 +1500,14 @@ fhandler_pty_master::write (const void *ptr, size_t len)
     return (ssize_t) bg;
 
   push_process_state process_state (PID_TTYOU);
+
+  if (get_ttyp ()->switch_to_pcon == 2)
+    {
+      DWORD wLen;
+      WriteFile (to_slave, ptr, len, &wLen, NULL);
+      return wLen;
+    }
+
   line_edit_status status = line_edit (p++, len, ti, &ret);
   if (status > line_edit_signalled && status != line_edit_pipe_full)
     ret = -1;
@@ -1443,6 +1575,17 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg)
       *(struct winsize *) arg = get_ttyp ()->winsize;
       break;
     case TIOCSWINSZ:
+      if (get_ttyp ()->hPseudoConsole)
+	{
+	  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 (get_ttyp ()->hPseudoConsole, size);
+	}
       if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
 	  || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
 	{
@@ -1458,7 +1601,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;
@@ -1497,14 +1640,22 @@ fhandler_pty_common::set_close_on_exec (bool val)
 void
 fhandler_pty_slave::fixup_after_fork (HANDLE parent)
 {
+  if (fhandler_console::get_console_process_id (get_ttyp ()->HelperProcessId, true))
+    pcon_attached = true;
   // fork_fixup (parent, inuse, "inuse");
   // fhandler_pty_common::fixup_after_fork (parent);
+  if (pcon_attached && get_ttyp ()->switch_to_pcon == 0)
+    get_ttyp ()->switch_to_pcon = 1;
   report_tty_counts (this, "inherited", "");
 }
 
 void
 fhandler_pty_slave::fixup_after_exec ()
 {
+  if (fhandler_console::get_console_process_id (get_ttyp ()->HelperProcessId, true))
+    pcon_attached = true;
+  if (pcon_attached && get_ttyp ()->switch_to_pcon == 0)
+    get_ttyp ()->switch_to_pcon = 1;
   if (!close_on_exec ())
     fixup_after_fork (NULL);
 }
@@ -1544,7 +1695,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 +1765,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))
@@ -1659,23 +1817,80 @@ fhandler_pty_master::pty_master_fwd_thread ()
   DWORD rlen;
   char outbuf[OUT_BUFFER_SIZE];
 
-  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 (get_ttyp ()->hPseudoConsole)
+	{
+	  if (get_ttyp ()->switch_to_pcon == 1)
+	    get_ttyp ()->switch_to_pcon = 2;
+#if 1
+	  /* 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;
+	      }
+#endif
+
+	  /* Avoid duplicating slave output which is already sent to
+	   * to_master_cyg */
+	  if (get_ttyp ()->switch_to_pcon != 2)
+	    continue;
+
+	  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);
+	    }
+	  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 +1902,22 @@ pty_master_fwd_thread (VOID *arg)
   return ((fhandler_pty_master *) arg)->pty_master_fwd_thread ();
 }
 
+static bool
+is_running_as_service (void)
+{
+  DWORD dwSize = 0;
+  PTOKEN_GROUPS pGroupInfo;
+  tmp_pathbuf tp;
+  bool ret = false;
+  pGroupInfo = (PTOKEN_GROUPS) tp.w_get ();
+  NtQueryInformationToken (hProcToken, TokenGroups, pGroupInfo,
+					2 * NT_MAX_PATH, &dwSize);
+  for (DWORD i=0; i<pGroupInfo->GroupCount && !ret; i++)
+    if (well_known_service_sid == pGroupInfo->Groups[i].Sid)
+      ret = true;
+  return ret;
+}
+
 bool
 fhandler_pty_master::setup ()
 {
@@ -1695,10 +1926,14 @@ 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. */
+  /* FIXME: Currently just copy from_master_cyg */
+  from_master = from_master_cyg;
+
   ProtectHandle1 (get_output_handle (), to_pty);
 
   tty& t = *cygwin_shared->tty[unit];
@@ -1715,7 +1950,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 +1959,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 +1967,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 +2032,101 @@ fhandler_pty_master::setup ()
       goto err;
     }
 
+  { /* Pseudo Console Support */
+    HMODULE hModule = GetModuleHandle ("kernel32.dll");
+    FARPROC func = GetProcAddress (hModule, "CreatePseudoConsole");
+    HRESULT (WINAPI *CreatePseudoConsole)
+      (COORD, HANDLE, HANDLE, DWORD, HPCON *) = NULL;
+    if (func)
+      {
+	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, &t.hPseudoConsole);
+	if (res != S_OK)
+	  {
+	    system_printf ("CreatePseudoConsole() failed. %08x\n",
+			  GetLastError());
+	    t.hPseudoConsole = NULL;
+	  }
+	else
+	  {
+#if 0
+	    char tty[32];
+	    for (int fd=0; fd<3; fd++)
+	      {
+		ttyname_r (fd, tty, sizeof(tty));
+		if (strncmp (tty, "/dev/cons", 9) == 0)
+		  t.attach_pcon_in_spawn = true;
+	      }
+#else
+	    if (!is_running_as_service ())
+	      t.attach_pcon_in_spawn = true;
+#endif
+	    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,
+				       t.hPseudoConsole,
+				       sizeof (t.hPseudoConsole),
+				       NULL, NULL);
+	    HANDLE hello = CreateEvent (&sec_none, true, false, NULL);
+	    HANDLE goodbye = CreateEvent (&sec_none, true, false, NULL);
+	    HANDLE hr, hw;
+	    CreatePipe (&hr, &hw, &sec_none, 0);
+	    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);
+	    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);
+	    DeleteProcThreadAttributeList (si_helper.lpAttributeList);
+	    HeapFree (GetProcessHeap (), 0, si_helper.lpAttributeList);
+	    t.hHelperGoodbye = goodbye;
+	    t.hHelperProcess = pi_helper.hProcess;
+	    t.HelperProcessId = pi_helper.dwProcessId;
+	    CloseHandle (from_master);
+	    CloseHandle (to_master);
+	    from_master = hpConIn;
+	    to_master = hpConOut;
+	  }
+      }
+  }
+
   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 +2136,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 +2170,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
+  from_slave = arch->from_slave;
+  to_slave = arch->to_slave;
+#endif
   report_tty_counts (this, "inherited master", "");
 }
 
@@ -1857,7 +2193,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
diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc
index 7e1c08990..cb4da7330 100644
--- a/winsup/cygwin/fork.cc
+++ b/winsup/cygwin/fork.cc
@@ -134,6 +134,26 @@ 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;
+	if (((fhandler_pty_master *) fh)->getPseudoConsole ())
+	  {
+	    DWORD dwHelperProcessId =
+	      ((fhandler_pty_master *) fh)->getHelperProcessId ();
+	    debug_printf ("found a PTY master %d: helper_PID=%d",
+			  cfd->get_minor (), dwHelperProcessId);
+	    if (!((fhandler_pty_master *) fh)->attach_pcon_in_spawn ())
+	      {
+		FreeConsole ();
+		AttachConsole (dwHelperProcessId);
+		break;
+	      }
+	  }
+      }
+
   HANDLE& hParent = ch.parent;
 
   sync_with_parent ("after longjmp", true);
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index 85242ec06..04249eb94 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 ())
 	    {
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index 4e549f7d4..cb4bdca06 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -288,6 +288,31 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
       return -1;
     }
 
+  DWORD pidRestore =
+    fhandler_console::get_console_process_id (GetCurrentProcessId (), false);
+  cygheap_fdenum cfd (false);
+  bool attach_to_pcon = false;
+  int fd;
+  while ((fd = cfd.next ()) >= 0 && fd < 3)
+    if (cfd->get_major () == DEV_PTYS_MAJOR)
+      {
+	fhandler_base *fh = cfd;
+	if (((fhandler_pty_slave *) fh)->getPseudoConsole ())
+	  {
+	    DWORD dwHelperProcessId =
+	      ((fhandler_pty_slave *) fh)->getHelperProcessId ();
+	    debug_printf ("found a PTY slave %d: helper_PID=%d",
+			  cfd->get_minor (), dwHelperProcessId);
+	    if (((fhandler_pty_slave *) fh)->attach_pcon_in_spawn ())
+	      {
+		FreeConsole ();
+		AttachConsole (dwHelperProcessId);
+		attach_to_pcon = true;
+		break;
+	      }
+	  }
+      }
+
   av newargv;
   linebuf cmd;
   PWCHAR envblock = NULL;
@@ -868,6 +893,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/tty.cc b/winsup/cygwin/tty.cc
index ad46cb312..92d646ff5 100644
--- a/winsup/cygwin/tty.cc
+++ b/winsup/cygwin/tty.cc
@@ -234,7 +234,10 @@ tty::init ()
   was_opened = false;
   master_pid = 0;
   is_console = false;
+  attach_pcon_in_spawn = false;
+  hPseudoConsole = NULL;
   column = 0;
+  switch_to_pcon = 0;
 }
 
 HANDLE
diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h
index 9aee43b9c..7c6ba15d7 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,23 @@ 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_spawn;
+  int switch_to_pcon;
 
 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 +128,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 8f62ed7e6..bef143f96 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) strtoul (argv[1], &end, 0);
   SetEvent (h);
+  if (argc == 4) /* Pseudo console helper mode for PTY */
+    {
+      HANDLE hPipe = (HANDLE) strtoul (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) strtoul (argv[2], &end, 0);
   WaitForSingleObject (h, INFINITE);
   exit (0);
-- 
2.17.0


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

* Re: Pseudo console support in PTY
  2019-03-30 13:08 Pseudo console support in PTY Takashi Yano
@ 2019-03-30 19:47 ` Corinna Vinschen
  2019-03-30 19:59   ` Corinna Vinschen
  2019-03-30 23:07 ` Thomas Wolff
  2019-04-02 11:02 ` Corinna Vinschen
  2 siblings, 1 reply; 53+ messages in thread
From: Corinna Vinschen @ 2019-03-30 19:47 UTC (permalink / raw)
  To: Takashi Yano; +Cc: cygwin-developers

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

Hi Takashi,

On Mar 30 22:08, Takashi Yano wrote:
> Hello cygwin developers,
> 
> I have worked on implementing pseudo console support into cygwin
> PTY by request from Corinna. Pseudo console is a new feature
> after Windows 10 1809, which provides console APIs on virtual
> terminal.
> https://blogs.msdn.microsoft.com/commandline/2018/08/02/windows-command-line-introducing-the-windows-pseudo-console-conpty/
> https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session
> 
> After much effort, it began to work partially. So, I would
> like to announce to this mailing list. Attached are the
> patchs against cygwin git HEAD.

\o/

Thanks a lot for doing this!

> Patch 0001 and 0002 are not really needed by the pseudo console
> support, but are the modification I have done at the same time.
> 
> Patch 0001: This just renames and unifies the function names.
>   Both get_io_handle() and get_handle() were identical and
>   returned io_handle. So, they were unified.

This one is ok to be pushed to master as is.

> Patch 0002: This revises console code for better color handling
>   and fixing select() behaviour. This provides 24 bit color
>   support after Windows 10 build 14931. For legacy console,
>   fake 256 color support is implemented, which use the nearest
>   color from 16 system colors.

This patch is certainly a nice addition, but I'd prefer this to be split
into a patchset of at least 3 separate patches:

- Fix select

- Make console thread-safe

- Color code changes

These are three very different changes which should all stand on their
own.  This also improves looking for bugs.

> Patch 0003: Support pseudo console in PTY. With this patch,
>   native console applications can work in PTY such as mintty,
>   ssh, gnu screen or tmux.

This patch is missing a bit of commenting.  There are a couple of #if
0/1 blocks.  It would be nice to know what these are good for (and
ultimately the #if 0/1's should go away).  It would alos be helpful
if you see a chance to split this into a patchset with a few
self-contained, co-ordinated patches.

Apart from patch 1, which I'll push in a minute, please send any
patch submissions to cygwin-patches.

Thanks a lot for working on this.  If all goes well we could even push
this into 3.1, together with Ken's FIFO patches.

This is all pretty exciting.

> Anyone who are interested in this work, please test. Any
> discussions and suggestions are also welcome.
> 
> You can download the binaries from:

Already running your patches with self-built cygwin DLL and
cygwin-console-helper ;)

It seems the mintty startup is a bit slower than before, but
personally I could live with that.

> D=http://tyan0.dip.jp/cygwin
> ${D}/x86_64/test/cygwin1-20190330.dll.xz
> ${D}/x86_64/test/cygwin-console-helper.exe.xz
> ${D}/x86/test/cygwin1-20190330.dll.xz
> ${D}/x86/test/cygwin-console-helper.exe.xz
> 
> This cygwin1.dll requires new cygwin-console-helper.exe.
> Please unxz and put them into /bin directory.
> 
> Known problems:
> * Sometimes the screen layout would be broken.
> * mintty fails to start if it is started in console cygwin
>   session.

Good to know, thanks!


Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: Pseudo console support in PTY
  2019-03-30 19:47 ` Corinna Vinschen
@ 2019-03-30 19:59   ` Corinna Vinschen
  0 siblings, 0 replies; 53+ messages in thread
From: Corinna Vinschen @ 2019-03-30 19:59 UTC (permalink / raw)
  To: Takashi Yano; +Cc: cygwin-developers

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

On Mar 30 20:46, Corinna Vinschen wrote:
> On Mar 30 22:08, Takashi Yano wrote:
> > Known problems:
> > * Sometimes the screen layout would be broken.
> > * mintty fails to start if it is started in console cygwin
> >   session.

Perhaps a FreeConsole is missing at some point?


Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: Pseudo console support in PTY
  2019-03-30 13:08 Pseudo console support in PTY Takashi Yano
  2019-03-30 19:47 ` Corinna Vinschen
@ 2019-03-30 23:07 ` Thomas Wolff
  2019-03-31 14:38   ` Corinna Vinschen
  2019-03-31 15:00   ` Takashi Yano
  2019-04-02 11:02 ` Corinna Vinschen
  2 siblings, 2 replies; 53+ messages in thread
From: Thomas Wolff @ 2019-03-30 23:07 UTC (permalink / raw)
  To: cygwin-developers

Am 30.03.2019 um 14:08 schrieb Takashi Yano:
> Hello cygwin developers,
>
> I have worked on implementing pseudo console support into cygwin
> PTY by request from Corinna. Pseudo console is a new feature
> after Windows 10 1809, which provides console APIs on virtual
> terminal.
> https://blogs.msdn.microsoft.com/commandline/2018/08/02/windows-command-line-introducing-the-windows-pseudo-console-conpty/
> https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session
>
> After much effort, it began to work partially. So, I would
> like to announce to this mailing list. Attached are the
> patchs against cygwin git HEAD.
>
> ...
>
> Patch 0003: Support pseudo console in PTY. With this patch,
>    native console applications can work in PTY such as mintty,
>    ssh, gnu screen or tmux.
This will be great to have in cygwin.
> Anyone who are interested in this work, please test. Any
> discussions and suggestions are also welcome.
I had previously (before ConPTY was announced) suggested a patch to 
inject winpty as a wrapper, for more or less the same purpose, to 
integrate non-cygwin applications seamlessly in pty-based terminals 
(https://cygwin.com/ml/cygwin-developers/2018-04/msg00002.html).
While the patch did not proceed because winpty was reported to fail in 
certain settings, I could demonstrate it was sufficient to inject the 
pty interaction gateway only when actually running a console application 
(non-cygwin program), and handle that all in spawn.cc. So I am wondering 
whether your patch is maybe doing "too much", and why e.g. 
fhandler_console should be modified, as there is no need for such 
adaptive handling when running a console application in a console, even 
through cygwin.
> Known problems:
> * Sometimes the screen layout would be broken.
If you describe the issues, I'll be glad to try some analysis.
> * mintty fails to start if it is started in console cygwin session.

If you limit the usage to running non-cygwin console apps, there is no need for any slowdown in a terminal when running cygwin apps.

Corinna wrote:
> This is all pretty exciting.
Yes, and I'd suggest Takashi for a gold star already, once this works.

Thomas

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

* Re: Pseudo console support in PTY
  2019-03-30 23:07 ` Thomas Wolff
@ 2019-03-31 14:38   ` Corinna Vinschen
  2019-03-31 15:00   ` Takashi Yano
  1 sibling, 0 replies; 53+ messages in thread
From: Corinna Vinschen @ 2019-03-31 14:38 UTC (permalink / raw)
  To: cygwin-developers

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

On Mar 31 00:07, Thomas Wolff wrote:
> Am 30.03.2019 um 14:08 schrieb Takashi Yano:
> > Hello cygwin developers,
> > 
> > I have worked on implementing pseudo console support into cygwin
> > PTY by request from Corinna. Pseudo console is a new feature
> > after Windows 10 1809, which provides console APIs on virtual
> > terminal.
> > https://blogs.msdn.microsoft.com/commandline/2018/08/02/windows-command-line-introducing-the-windows-pseudo-console-conpty/
> > https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session
> > 
> > After much effort, it began to work partially. So, I would
> > like to announce to this mailing list. Attached are the
> > patchs against cygwin git HEAD.
> > [...]
> Corinna wrote:
> > This is all pretty exciting.
> Yes, and I'd suggest Takashi for a gold star already, once this works.

Definitely.  Just as Ken is bound to get one for revamping the FIFO code.


Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: Pseudo console support in PTY
  2019-03-30 23:07 ` Thomas Wolff
  2019-03-31 14:38   ` Corinna Vinschen
@ 2019-03-31 15:00   ` Takashi Yano
  1 sibling, 0 replies; 53+ messages in thread
From: Takashi Yano @ 2019-03-31 15:00 UTC (permalink / raw)
  To: cygwin-developers

Hi Thomas,

On Sun, 31 Mar 2019 00:07:08 +0100 Thomas Wolff wrote:
> (non-cygwin program), and handle that all in spawn.cc. So I am wondering 
> whether your patch is maybe doing "too much", and why e.g. 
> fhandler_console should be modified, as there is no need for such 
> adaptive handling when running a console application in a console, even 
> through cygwin.

As for the console code, I did "too much" if limited to pseudo
console support. It is just another job. It is essentially not
necessary for pseudo console stuff. Please reffer to:
https://sourceware.org/ml/cygwin-patches/2019-q1/msg00090.html
for more detail.

> > Known problems:
> > * Sometimes the screen layout would be broken.
> If you describe the issues, I'll be glad to try some analysis.

Thank you.

Actually, the cause is almost known. Pseudo console sometimes re-
draws the screen according to screen buffer. However, the screen
buffer does not have the screen contents drawn before PTY was
started. This breaks the screen layout.

One solution may be to clear screen when PTY is started.
It's annoying a bit...

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

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

* Re: Pseudo console support in PTY
  2019-03-30 13:08 Pseudo console support in PTY Takashi Yano
  2019-03-30 19:47 ` Corinna Vinschen
  2019-03-30 23:07 ` Thomas Wolff
@ 2019-04-02 11:02 ` Corinna Vinschen
  2019-04-02 17:16   ` Thomas Wolff
  2019-04-03 16:36   ` [PATCH v2 0/1] Pseudo console support in PTY (v2) Takashi Yano
  2 siblings, 2 replies; 53+ messages in thread
From: Corinna Vinschen @ 2019-04-02 11:02 UTC (permalink / raw)
  To: Takashi Yano; +Cc: cygwin-developers

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

Hi Takashi,

On Mar 30 22:08, Takashi Yano wrote:
> Patch 0003: Support pseudo console in PTY. With this patch,
>   native console applications can work in PTY such as mintty,
>   ssh, gnu screen or tmux.

I found another weird effect which I can't explain off the top
of my head:  system_printf() debug output does not show up
in a mintty anymore with this patch.  Any idea why?


Thanks,
Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: Pseudo console support in PTY
  2019-04-02 11:02 ` Corinna Vinschen
@ 2019-04-02 17:16   ` Thomas Wolff
  2019-04-02 17:51     ` Corinna Vinschen
  2019-04-03 16:36   ` [PATCH v2 0/1] Pseudo console support in PTY (v2) Takashi Yano
  1 sibling, 1 reply; 53+ messages in thread
From: Thomas Wolff @ 2019-04-02 17:16 UTC (permalink / raw)
  To: cygwin-developers

Am 02.04.2019 um 13:02 schrieb Corinna Vinschen:
> Hi Takashi,
>
> On Mar 30 22:08, Takashi Yano wrote:
>> Patch 0003: Support pseudo console in PTY. With this patch,
>>    native console applications can work in PTY such as mintty,
>>    ssh, gnu screen or tmux.
I've now tried your patched binaries on two systems. On one they don't 
run, on the other I saw no noticeable effect, particularly no impact on 
the pty/console interoperability...
Thomas

> I found another weird effect which I can't explain off the top
> of my head:  system_printf() debug output does not show up
> in a mintty anymore with this patch.  Any idea why?
>
>
> Thanks,
> Corinna

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

* Re: Pseudo console support in PTY
  2019-04-02 17:16   ` Thomas Wolff
@ 2019-04-02 17:51     ` Corinna Vinschen
  2019-04-03  7:18       ` Thomas Wolff
  0 siblings, 1 reply; 53+ messages in thread
From: Corinna Vinschen @ 2019-04-02 17:51 UTC (permalink / raw)
  To: Thomas Wolff; +Cc: cygwin-developers

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

On Apr  2 19:16, Thomas Wolff wrote:
> Am 02.04.2019 um 13:02 schrieb Corinna Vinschen:
> > Hi Takashi,
> > 
> > On Mar 30 22:08, Takashi Yano wrote:
> > > Patch 0003: Support pseudo console in PTY. With this patch,
> > >    native console applications can work in PTY such as mintty,
> > >    ssh, gnu screen or tmux.
> I've now tried your patched binaries on two systems. On one they don't run,
> on the other I saw no noticeable effect, particularly no impact on the
> pty/console interoperability...

Try starting cmd from your shell in mintty and do some native stuff.
You'll notice two differences:

- cmd history works
- dir /p will actually page


Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: Pseudo console support in PTY
  2019-04-02 17:51     ` Corinna Vinschen
@ 2019-04-03  7:18       ` Thomas Wolff
  2019-04-03  7:28         ` Corinna Vinschen
  0 siblings, 1 reply; 53+ messages in thread
From: Thomas Wolff @ 2019-04-03  7:18 UTC (permalink / raw)
  To: cygwin-developers

Am 02.04.2019 um 19:50 schrieb Corinna Vinschen:
> On Apr  2 19:16, Thomas Wolff wrote:
>> Am 02.04.2019 um 13:02 schrieb Corinna Vinschen:
>>> Hi Takashi,
>>>
>>> On Mar 30 22:08, Takashi Yano wrote:
>>>> Patch 0003: Support pseudo console in PTY. With this patch,
>>>>     native console applications can work in PTY such as mintty,
>>>>     ssh, gnu screen or tmux.
>> I've now tried your patched binaries on two systems. On one they don't run,
>> on the other I saw no noticeable effect, particularly no impact on the
>> pty/console interoperability...
> Try starting cmd from your shell in mintty and do some native stuff.
I've now self-compiled cygwin dll on a third system and can run a cygwin 
console.
But the build process did not generate the cygwin-console-helper, maybe 
that's the reason why mintty does not start?

> You'll notice two differences:
>
> - cmd history works
In the cygwin console, it does not, echos the cursor-up escape sequence 
instead. See my previous comment that I think ConPTY should only be 
applied if running at a pty. (And for other reasons only when starting a 
non-cygwin app.)
> - dir /p will actually page
Does it also translate the character set?
Test case, when running in a German UI Windows:
xcopy
echoes either of
Unzul�ssige Parameteranzahl
Unzulässige Parameteranzahl

Thomas

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

* Re: Pseudo console support in PTY
  2019-04-03  7:18       ` Thomas Wolff
@ 2019-04-03  7:28         ` Corinna Vinschen
  2019-04-03  7:55           ` Thomas Wolff
  0 siblings, 1 reply; 53+ messages in thread
From: Corinna Vinschen @ 2019-04-03  7:28 UTC (permalink / raw)
  To: Thomas Wolff; +Cc: cygwin-developers, Takashi Yano

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

On Apr  3 09:18, Thomas Wolff wrote:
> Am 02.04.2019 um 19:50 schrieb Corinna Vinschen:
> > On Apr  2 19:16, Thomas Wolff wrote:
> > > Am 02.04.2019 um 13:02 schrieb Corinna Vinschen:
> > > > Hi Takashi,
> > > > 
> > > > On Mar 30 22:08, Takashi Yano wrote:
> > > > > Patch 0003: Support pseudo console in PTY. With this patch,
> > > > >     native console applications can work in PTY such as mintty,
> > > > >     ssh, gnu screen or tmux.
> > > I've now tried your patched binaries on two systems. On one they don't run,
> > > on the other I saw no noticeable effect, particularly no impact on the
> > > pty/console interoperability...
> > Try starting cmd from your shell in mintty and do some native stuff.
> I've now self-compiled cygwin dll on a third system and can run a cygwin
> console.
> But the build process did not generate the cygwin-console-helper, maybe
> that's the reason why mintty does not start?
> 
> > You'll notice two differences:
> > 
> > - cmd history works
> In the cygwin console, it does not, echos the cursor-up escape sequence

Oh, right, ...

> instead. See my previous comment that I think ConPTY should only be applied
> if running at a pty. (And for other reasons only when starting a non-cygwin
> app.)

but you got that wrong.  The conpty stuff *is* only applied for ptys.

The above appears to be a fallout of the console changes to support
the Windows console changes to emulate an xterm-256color,
https://sourceware.org/git/?p=newlib-cygwin.git;a=commitdiff;h=bd627864ab41

Takashi, can you take a look?

> > - dir /p will actually page
> Does it also translate the character set?
> Test case, when running in a German UI Windows:
> xcopy
> echoes either of
> Unzul�ssige Parameteranzahl
> Unzulässige Parameteranzahl

I'm only running English systems, so I don't know.  Is that in a
console or in mintty?


Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: Pseudo console support in PTY
  2019-04-03  7:28         ` Corinna Vinschen
@ 2019-04-03  7:55           ` Thomas Wolff
  2019-04-03  8:02             ` Corinna Vinschen
  2019-04-04  4:15             ` Takashi Yano
  0 siblings, 2 replies; 53+ messages in thread
From: Thomas Wolff @ 2019-04-03  7:55 UTC (permalink / raw)
  To: cygwin-developers, Takashi Yano


Am 03.04.2019 um 09:27 schrieb Corinna Vinschen:
> On Apr  3 09:18, Thomas Wolff wrote:
>> Am 02.04.2019 um 19:50 schrieb Corinna Vinschen:
>>> On Apr  2 19:16, Thomas Wolff wrote:
>>>> Am 02.04.2019 um 13:02 schrieb Corinna Vinschen:
>>>>> Hi Takashi,
>>>>>
>>>>> On Mar 30 22:08, Takashi Yano wrote:
>>>>>> Patch 0003: Support pseudo console in PTY. With this patch,
>>>>>>      native console applications can work in PTY such as mintty,
>>>>>>      ssh, gnu screen or tmux.
>>>> I've now tried your patched binaries on two systems. On one they don't run,
>>>> on the other I saw no noticeable effect, particularly no impact on the
>>>> pty/console interoperability...
>>> Try starting cmd from your shell in mintty and do some native stuff.
>> I've now self-compiled cygwin dll on a third system and can run a cygwin
>> console.
>> But the build process did not generate the cygwin-console-helper, maybe
>> that's the reason why mintty does not start?
Can you give a hint that enables me to test the patch?

>>> You'll notice two differences:
>>>
>>> - cmd history works
>> In the cygwin console, it does not, echos the cursor-up escape sequence
> Oh, right, ...
>
>> instead. See my previous comment that I think ConPTY should only be applied
>> if running at a pty. (And for other reasons only when starting a non-cygwin
>> app.)
> but you got that wrong.  The conpty stuff *is* only applied for ptys.
So cygwin running as cygwin console still sets up a pty? Anyway, what I 
mean it's (assumedly) not needed in a cygwin console, which I detected 
in my previous winpty injection patch by !strncmp (ttyname (0), 
"/dev/pty", 8).

> The above appears to be a fallout of the console changes to support
> the Windows console changes to emulate an xterm-256color,
> https://sourceware.org/git/?p=newlib-cygwin.git;a=commitdiff;h=bd627864ab41
>
> Takashi, can you take a look?
>
>>> - dir /p will actually page
>> Does it also translate the character set?
>> Test case, when running in a German UI Windows:
>> xcopy
>> echoes either of
>> Unzul�ssige Parameteranzahl
>> Unzulässige Parameteranzahl
> I'm only running English systems, so I don't know.  Is that in a console or in mintty?
That's in mintty. (In a cygwin console, output is fine already as the 
Windows output API somehow seems to go directly to it.) You might have 
the same test effect with a small program like:
#include <wchar.h>
#include <windows.h>
void main() {
   wchar_t * s = u"bäh 3€\n";
   DWORD written;
   HANDLE con = GetStdHandle(STD_OUTPUT_HANDLE);
   WriteConsoleW(con, s, wcslen(s), &written, 0);
   SetConsoleTextAttribute(con, FOREGROUND_RED);
   WriteConsoleW(con, s, wcslen(s), &written, 0);
}

Further test cases:
- raw keyboard input
- signals, ^C in cmd...

Thomas

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

* Re: Pseudo console support in PTY
  2019-04-03  7:55           ` Thomas Wolff
@ 2019-04-03  8:02             ` Corinna Vinschen
  2019-04-03 11:33               ` Thomas Wolff
  2019-04-04  4:15             ` Takashi Yano
  1 sibling, 1 reply; 53+ messages in thread
From: Corinna Vinschen @ 2019-04-03  8:02 UTC (permalink / raw)
  To: Thomas Wolff; +Cc: cygwin-developers, Takashi Yano

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

On Apr  3 09:55, Thomas Wolff wrote:
> 
> Am 03.04.2019 um 09:27 schrieb Corinna Vinschen:
> > On Apr  3 09:18, Thomas Wolff wrote:
> > > Am 02.04.2019 um 19:50 schrieb Corinna Vinschen:
> > > > On Apr  2 19:16, Thomas Wolff wrote:
> > > > > Am 02.04.2019 um 13:02 schrieb Corinna Vinschen:
> > > > > > Hi Takashi,
> > > > > > 
> > > > > > On Mar 30 22:08, Takashi Yano wrote:
> > > > > > > Patch 0003: Support pseudo console in PTY. With this patch,
> > > > > > >      native console applications can work in PTY such as mintty,
> > > > > > >      ssh, gnu screen or tmux.
> > > > > I've now tried your patched binaries on two systems. On one they don't run,
> > > > > on the other I saw no noticeable effect, particularly no impact on the
> > > > > pty/console interoperability...
> > > > Try starting cmd from your shell in mintty and do some native stuff.
> > > I've now self-compiled cygwin dll on a third system and can run a cygwin
> > > console.
> > > But the build process did not generate the cygwin-console-helper, maybe
> > > that's the reason why mintty does not start?
> Can you give a hint that enables me to test the patch?
> 
> > > > You'll notice two differences:
> > > > 
> > > > - cmd history works
> > > In the cygwin console, it does not, echos the cursor-up escape sequence
> > Oh, right, ...
> > 
> > > instead. See my previous comment that I think ConPTY should only be applied
> > > if running at a pty. (And for other reasons only when starting a non-cygwin
> > > app.)
> > but you got that wrong.  The conpty stuff *is* only applied for ptys.
> So cygwin running as cygwin console still sets up a pty?

Still?  It never did.  Call tty in a console and you get /dev/consX.
The ConPTY stuff only runs if the application requests a pty.


Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: Pseudo console support in PTY
  2019-04-03  8:02             ` Corinna Vinschen
@ 2019-04-03 11:33               ` Thomas Wolff
  2019-04-03 12:17                 ` Corinna Vinschen
  0 siblings, 1 reply; 53+ messages in thread
From: Thomas Wolff @ 2019-04-03 11:33 UTC (permalink / raw)
  To: cygwin-developers, Takashi Yano

On 03.04.2019 10:02, Corinna Vinschen wrote:
> On Apr  3 09:55, Thomas Wolff wrote:
>> Am 03.04.2019 um 09:27 schrieb Corinna Vinschen:
>>> On Apr  3 09:18, Thomas Wolff wrote:
>>>> Am 02.04.2019 um 19:50 schrieb Corinna Vinschen:
>>>>> On Apr  2 19:16, Thomas Wolff wrote:
>>>>>> Am 02.04.2019 um 13:02 schrieb Corinna Vinschen:
>>>>>>> Hi Takashi,
>>>>>>>
>>>>>>> On Mar 30 22:08, Takashi Yano wrote:
>>>>>>>> Patch 0003: Support pseudo console in PTY. With this patch,
>>>>>>>>       native console applications can work in PTY such as mintty,
>>>>>>>>       ssh, gnu screen or tmux.
>>>>>> I've now tried your patched binaries on two systems. On one they don't run,
>>>>>> on the other I saw no noticeable effect, particularly no impact on the
>>>>>> pty/console interoperability...
>>>>> Try starting cmd from your shell in mintty and do some native stuff.
>>>> I've now self-compiled cygwin dll on a third system and can run a cygwin
>>>> console.
>>>> But the build process did not generate the cygwin-console-helper, maybe
>>>> that's the reason why mintty does not start?
>> Can you give a hint that enables me to test the patch?
>>
>>>>> You'll notice two differences:
>>>>>
>>>>> - cmd history works
>>>> In the cygwin console, it does not, echos the cursor-up escape sequence
>>> Oh, right, ...
>>>
>>>> instead. See my previous comment that I think ConPTY should only be applied
>>>> if running at a pty. (And for other reasons only when starting a non-cygwin
>>>> app.)
>>> but you got that wrong.  The conpty stuff *is* only applied for ptys.
>> So cygwin running as cygwin console still sets up a pty?
> Still?  It never did.  Call tty in a console and you get /dev/consX.
Misunderstanding, didn't mean "still", sorry.
> The ConPTY stuff only runs if the application requests a pty.
OK, but I think it should additionally be restricted to running 
non-cygwin apps.

By the way, when I previously reported the patch to have no effect, that 
was on a Windows 7 system, so of course it couldn't, sorry :/
Still failing to test the patch in mintty on Windows 10...
Thomas

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

* Re: Pseudo console support in PTY
  2019-04-03 11:33               ` Thomas Wolff
@ 2019-04-03 12:17                 ` Corinna Vinschen
  2019-04-04  4:17                   ` Takashi Yano
  0 siblings, 1 reply; 53+ messages in thread
From: Corinna Vinschen @ 2019-04-03 12:17 UTC (permalink / raw)
  To: Thomas Wolff; +Cc: cygwin-developers, Takashi Yano

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

On Apr  3 13:32, Thomas Wolff wrote:
> On 03.04.2019 10:02, Corinna Vinschen wrote:
> > On Apr  3 09:55, Thomas Wolff wrote:
> > > Am 03.04.2019 um 09:27 schrieb Corinna Vinschen:
> > > > On Apr  3 09:18, Thomas Wolff wrote:
> > > > > Am 02.04.2019 um 19:50 schrieb Corinna Vinschen:
> > > > > > On Apr  2 19:16, Thomas Wolff wrote:
> > > > > > > Am 02.04.2019 um 13:02 schrieb Corinna Vinschen:
> > > > > > > > Hi Takashi,
> > > > > > > > 
> > > > > > > > On Mar 30 22:08, Takashi Yano wrote:
> > > > > > > > > Patch 0003: Support pseudo console in PTY. With this patch,
> > > > > > > > >       native console applications can work in PTY such as mintty,
> > > > > > > > >       ssh, gnu screen or tmux.
> > > > > > > I've now tried your patched binaries on two systems. On one they don't run,
> > > > > > > on the other I saw no noticeable effect, particularly no impact on the
> > > > > > > pty/console interoperability...
> > > > > > Try starting cmd from your shell in mintty and do some native stuff.
> > > > > I've now self-compiled cygwin dll on a third system and can run a cygwin
> > > > > console.
> > > > > But the build process did not generate the cygwin-console-helper, maybe
> > > > > that's the reason why mintty does not start?
> > > Can you give a hint that enables me to test the patch?
> > > 
> > > > > > You'll notice two differences:
> > > > > > 
> > > > > > - cmd history works
> > > > > In the cygwin console, it does not, echos the cursor-up escape sequence
> > > > Oh, right, ...
> > > > 
> > > > > instead. See my previous comment that I think ConPTY should only be applied
> > > > > if running at a pty. (And for other reasons only when starting a non-cygwin
> > > > > app.)
> > > > but you got that wrong.  The conpty stuff *is* only applied for ptys.
> > > So cygwin running as cygwin console still sets up a pty?
> > Still?  It never did.  Call tty in a console and you get /dev/consX.
> Misunderstanding, didn't mean "still", sorry.
> > The ConPTY stuff only runs if the application requests a pty.
> OK, but I think it should additionally be restricted to running non-cygwin
> apps.
> 
> By the way, when I previously reported the patch to have no effect, that was
> on a Windows 7 system, so of course it couldn't, sorry :/
> Still failing to test the patch in mintty on Windows 10...

WJFFM, given the current restrictions.

W10 1703 or later?


Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* [PATCH v2 0/1] Pseudo console support in PTY (v2)
  2019-04-02 11:02 ` Corinna Vinschen
  2019-04-02 17:16   ` Thomas Wolff
@ 2019-04-03 16:36   ` Takashi Yano
  2019-04-03 16:37     ` [PATCH v2 1/1] Cygwin: pty: add pseudo console support Takashi Yano
                       ` (2 more replies)
  1 sibling, 3 replies; 53+ messages in thread
From: Takashi Yano @ 2019-04-03 16:36 UTC (permalink / raw)
  To: cygwin-developers; +Cc: Takashi Yano

On Sat, 30 Mar 2019 22:08:04 +0900 Takashi Yano wrote:
> Known problems:
> * mintty fails to start if it is started in console cygwin
>   session.

This has been resolved in the new version.

On Tue, 2 Apr 2019 13:02:48 +0200 Corinna Vinschen wrote:
> I found another weird effect which I can't explain off the top
> of my head:  system_printf() debug output does not show up
> in a mintty anymore with this patch.  Any idea why?

I am not sure, but this may be fixed in the new version.
Could you please try?


D=http://tyan0.dip.jp/cygwin
${D}/x86_64/test/cygwin1-20190404.dll.xz
${D}/x86_64/test/cygwin-console-helper.exe.xz
${D}/x86/test/cygwin1-20190404.dll.xz
${D}/x86/test/cygwin-console-helper.exe.xz


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

 winsup/cygwin/dtable.cc               |   7 +
 winsup/cygwin/fhandler.h              |  42 +-
 winsup/cygwin/fhandler_console.cc     |  32 ++
 winsup/cygwin/fhandler_tty.cc         | 583 ++++++++++++++++++++++++--
 winsup/cygwin/fork.cc                 |  20 +
 winsup/cygwin/select.cc               |   3 +
 winsup/cygwin/spawn.cc                |  32 ++
 winsup/cygwin/tty.cc                  |   4 +
 winsup/cygwin/tty.h                   |  20 +-
 winsup/utils/cygwin-console-helper.cc |  14 +-
 10 files changed, 716 insertions(+), 41 deletions(-)

-- 
2.17.0

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

* [PATCH v2 1/1] Cygwin: pty: add pseudo console support.
  2019-04-03 16:36   ` [PATCH v2 0/1] Pseudo console support in PTY (v2) Takashi Yano
@ 2019-04-03 16:37     ` Takashi Yano
  2019-04-03 16:50     ` [PATCH v2 0/1] Pseudo console support in PTY (v2) Corinna Vinschen
  2019-04-03 17:11     ` Corinna Vinschen
  2 siblings, 0 replies; 53+ messages in thread
From: Takashi Yano @ 2019-04-03 16:37 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 privides 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               |   7 +
 winsup/cygwin/fhandler.h              |  42 +-
 winsup/cygwin/fhandler_console.cc     |  32 ++
 winsup/cygwin/fhandler_tty.cc         | 583 ++++++++++++++++++++++++--
 winsup/cygwin/fork.cc                 |  20 +
 winsup/cygwin/select.cc               |   3 +
 winsup/cygwin/spawn.cc                |  32 ++
 winsup/cygwin/tty.cc                  |   4 +
 winsup/cygwin/tty.h                   |  20 +-
 winsup/utils/cygwin-console-helper.cc |  14 +-
 10 files changed, 716 insertions(+), 41 deletions(-)

diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
index 636221a77..3f5e86075 100644
--- a/winsup/cygwin/dtable.cc
+++ b/winsup/cygwin/dtable.cc
@@ -147,6 +147,13 @@ dtable::get_debugger_info ()
 void
 dtable::stdio_init ()
 {
+#if 1 /* Experimental code */
+  /* This is workaround for the issue:
+     https://www.cygwin.com/ml/cygwin-developers/2019-04/msg00000.html
+     when pseudo console is enabled. */
+  fhandler_console::need_invisible ();
+#endif
+
   if (myself->cygstarted || ISSTATE (myself, PID_CYGPARENT))
     {
       tty_min *t = cygwin_shared->tty.get_cttyp ();
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index bc66377cd..75f07b275 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1994,6 +1994,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 *) {}
 
@@ -2026,14 +2027,17 @@ 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), pcon_attached (false),
+    change_codepage_to_utf8 (true)
   {
     pc.file_attributes (FILE_ATTRIBUTE_NORMAL);
   }
   static const unsigned pipesize = 128 * 1024;
   HANDLE output_mutex, input_mutex;
   HANDLE input_available_event;
+  bool pcon_attached;
+  bool change_codepage_to_utf8;
 
   bool use_archetype () const {return true;}
   DWORD __acquire_output_mutex (const char *fn, int ln, DWORD ms);
@@ -2064,14 +2068,29 @@ class fhandler_pty_common: public fhandler_termios
     return fh;
   }
 
+  bool attach_pcon_in_spawn (void)
+  {
+    return get_ttyp ()->attach_pcon_in_spawn;
+  }
+  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);
+  void push_to_pcon_screenbuffer (HANDLE h, const char *ptr, size_t len);
 };
 
 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;
 
   /* Helper functions for fchmod and fchown. */
   bool fch_open_handles (bool chown);
@@ -2084,6 +2103,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);
@@ -2124,6 +2145,12 @@ class fhandler_pty_slave: public fhandler_pty_common
     copyto (fh);
     return fh;
   }
+  void reset_switch_to_pcon (void)
+  {
+    if (get_ttyp ()->switch_to_pcon == 2)
+      Sleep (20);
+    get_ttyp ()->switch_to_pcon = 0;
+  }
 };
 
 #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit))
@@ -2132,15 +2159,14 @@ 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);
 
@@ -2187,6 +2213,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 6d42ed888..d2e3184a6 100644
--- a/winsup/cygwin/fhandler_console.cc
+++ b/winsup/cygwin/fhandler_console.cc
@@ -1019,6 +1019,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;
@@ -3065,6 +3078,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..0c7fd1d6c 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -25,6 +25,18 @@ details. */
 #include "child_info.h"
 #include <asm/socket.h>
 #include "cygwait.h"
+#include "tls_pbuf.h"
+
+/* Not yet defined in Mingw-w64 */
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif /* ENABLE_VIRTUAL_TERMINAL_PROCESSING */
+#ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
+#define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE 0x00020016
+#endif
+
+extern "C" int sscanf (const char *, const char *, ...);
+extern "C" int ttyname_r(int, char*, size_t);
 
 #define close_maybe(h) \
   do { \
@@ -39,6 +51,7 @@ struct pipe_request {
 
 struct pipe_reply {
   HANDLE from_master;
+  HANDLE from_master_cyg;
   HANDLE to_master;
   HANDLE to_master_cyg;
   DWORD error;
@@ -67,7 +80,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
@@ -144,6 +157,11 @@ fhandler_pty_common::__release_output_mutex (const char *fn, int ln)
 void
 fhandler_pty_master::doecho (const void *str, DWORD len)
 {
+#if 0 /* Experimental code */
+  /* Push echo output to pseudo console screen buffer */
+  if (getPseudoConsole ())
+    push_to_pcon_screenbuffer (to_master, (char *)str, len);
+#endif
   ssize_t towrite = len;
   if (!process_opost_output (echo_w, str, towrite, true))
     termios_printf ("Write to echo pipe failed, %E");
@@ -235,7 +253,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 +314,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;
@@ -340,11 +358,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 +417,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 +462,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 +504,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,22 +516,28 @@ 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);
 
   fhandler_console::need_invisible ();
   set_open_status ();
+  if (pcon_attached && get_ttyp ()->switch_to_pcon == 0)
+    get_ttyp ()->switch_to_pcon = 1;
   return 1;
 
 err:
@@ -548,6 +586,9 @@ 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 */
   fhandler_pty_common::close ();
@@ -596,6 +637,95 @@ fhandler_pty_slave::init (HANDLE h, DWORD a, mode_t)
   return ret;
 }
 
+void
+fhandler_pty_common::push_to_pcon_screenbuffer (HANDLE h,
+						const char *ptr, size_t len)
+{
+  /* Switch to alternate screen buffer */
+  if (memmem (ptr, len, "\033[?1049h", 8))
+    get_ttyp ()->screen_alternated = true;
+  /* Switch to main screen buffer */
+  if (memmem (ptr, len, "\033[?1049l", 8))
+    get_ttyp ()->screen_alternated = false;
+  /* ALnternate screen buffer is not needed to synchronize */
+  if (get_ttyp ()->screen_alternated)
+    return;
+  DWORD pidRestore = 0;
+  /* If not attached pseudo console yet, try to attach temporally. */
+  if (!pcon_attached)
+    {
+      pidRestore =
+	fhandler_console::get_console_process_id (GetCurrentProcessId (),
+						  false);
+      /* If pidRestore is not set, give up to push. */
+      if (!pidRestore)
+	return;
+      FreeConsole ();
+      AttachConsole (getHelperProcessId ());
+    }
+  char *buf;
+  size_t nlen;
+  if (!change_codepage_to_utf8 && strstr (setlocale (LC_CTYPE, NULL), "UTF-8"))
+    {
+      /* Code page conversion from UTF-8 to OEM code page */
+      /* FIXME: The terminal code may not be UTF-8. */
+      size_t wlen =
+	MultiByteToWideChar (CP_UTF8, 0, (char *)ptr, len, NULL, 0);
+      wchar_t *wbuf = (wchar_t *)
+	HeapAlloc (GetProcessHeap (), 0, wlen * sizeof (wchar_t));
+      wlen =
+	MultiByteToWideChar (CP_UTF8, 0, (char *)ptr, len, wbuf, wlen);
+      nlen = WideCharToMultiByte (CP_OEMCP, 0,
+				  wbuf, wlen, NULL, 0, NULL, NULL);
+      buf = (char *) HeapAlloc (GetProcessHeap (), 0, nlen);
+      nlen = WideCharToMultiByte (CP_OEMCP, 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;
+    }
+  /* Remove ESC sequence which returns results to console
+     input buffer. Without this, cursor position report
+     is put into the input buffer as a garbage. */
+  char *p0;
+  /* 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;
+  GetConsoleMode (h, &dwMode);
+  SetConsoleMode (h, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
+  char *p = buf;
+  DWORD wLen, written = 0;
+  while (written <  nlen)
+    {
+      WriteFile (h, p, nlen - written, &wLen, NULL);
+      written += wLen;
+      p += wLen;
+    }
+  /* Detach from pseudo console and resume. */
+  SetConsoleMode (h, dwMode);
+  HeapFree (GetProcessHeap (), 0, buf);
+  if (!pcon_attached)
+    {
+      FreeConsole ();
+      AttachConsole (pidRestore);
+    }
+}
+
 ssize_t __stdcall
 fhandler_pty_slave::write (const void *ptr, size_t len)
 {
@@ -609,6 +739,12 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 
   push_process_state process_state (PID_TTYOU);
 
+  reset_switch_to_pcon ();
+
+  /* Push slave output to pseudo console screen buffer */
+  if (getPseudoConsole ())
+    push_to_pcon_screenbuffer (get_output_handle (), (char *)ptr, len);
+
   if (!process_opost_output (get_output_handle_cyg (), ptr, towrite, false))
     {
       DWORD err = GetLastError ();
@@ -644,10 +780,12 @@ 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);
 
+  reset_switch_to_pcon ();
+
   if (is_nonblocking () || !ptr) /* Indicating tcflush(). */
     time_to_wait = 0;
   else if ((get_ttyp ()->ti.c_lflag & ICANON))
@@ -777,7 +915,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);
@@ -890,6 +1028,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 +1036,7 @@ fhandler_pty_slave::tcgetattr (struct termios *t)
 int
 fhandler_pty_slave::tcsetattr (int, const struct termios *t)
 {
+  reset_switch_to_pcon ();
   acquire_output_mutex (INFINITE);
   get_ttyp ()->ti = *t;
   release_output_mutex ();
@@ -908,8 +1048,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)
     {
       size_t len = UINT_MAX;
@@ -929,6 +1070,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 +1137,52 @@ 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)
+	    {
+	      pidRestore = fhandler_console::get_console_process_id
+		(GetCurrentProcessId (), false);
+	      FreeConsole ();
+	      AttachConsole (getHelperProcessId ());
+	    }
+	  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 detach_pcon;
+	  if (!SetConsoleScreenBufferSize (get_output_handle (), size))
+	    goto detach_pcon;
+	  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);
+detach_pcon:
+	  /* Detach from pseudo console and resume. */
+	  if (pidRestore)
+	    {
+	      FreeConsole ();
+	      AttachConsole (pidRestore);
+	    }
+	  else if (!pcon_attached)
+	    { /* If failed to resume, leave being attached. */
+	      if (change_codepage_to_utf8)
+		{
+		  /* FIXME: The terminal code may not be UTF-8. */
+		  SetConsoleCP (65001);
+		  SetConsoleOutputCP (65001);
+		}
+	      pcon_attached = true;
+	    }
+	}
       if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
 	  || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
 	{
@@ -1228,8 +1416,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);
@@ -1285,7 +1474,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 ();
 }
 
@@ -1295,8 +1485,9 @@ fhandler_pty_master::close ()
   OBJECT_BASIC_INFORMATION obi;
   NTSTATUS status;
 
-  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)
@@ -1320,6 +1511,22 @@ fhandler_pty_master::close ()
 	    }
 	  release_output_mutex ();
 	  master_fwd_thread->terminate_thread ();
+
+	  /* Pseudo Console Support */
+	  if (getPseudoConsole ())
+	    {
+	      /* Terminate helper process */
+	      SetEvent (get_ttyp ()->hHelperGoodbye);
+	      WaitForSingleObject (get_ttyp ()->hHelperProcess, INFINITE);
+	      /* 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 = 0;
+	    }
 	}
     }
 
@@ -1344,17 +1551,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 +1590,16 @@ 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 == 2)
+    {
+      DWORD wLen;
+      WriteFile (to_slave, ptr, len, &wLen, NULL);
+      return wLen;
+    }
+
   line_edit_status status = line_edit (p++, len, ti, &ret);
   if (status > line_edit_signalled && status != line_edit_pipe_full)
     ret = -1;
@@ -1443,6 +1667,17 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg)
       *(struct winsize *) arg = get_ttyp ()->winsize;
       break;
     case TIOCSWINSZ:
+      if (getPseudoConsole ())
+	{
+	  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 +1693,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;
@@ -1497,14 +1732,82 @@ fhandler_pty_common::set_close_on_exec (bool val)
 void
 fhandler_pty_slave::fixup_after_fork (HANDLE parent)
 {
+  if (getPseudoConsole ())
+    {
+      if (fhandler_console::get_console_process_id (getHelperProcessId (),
+						    true))
+	if (!pcon_attached)
+	  {
+	    if (change_codepage_to_utf8)
+	      {
+		/* FIXME: The terminal code may not be UTF-8. */
+		SetConsoleCP (65001);
+		SetConsoleOutputCP (65001);
+	      }
+#if 0 /* 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. */
+	    if (!get_ttyp ()->screen_alternated)
+	      {
+#if 0
+		DWORD n;
+		WriteFile (get_output_handle_cyg (),
+			   "\033[H\033[J", 6, &n, NULL);
+#else
+		this->write ("\033[H\033[J", 6);
+#endif
+	      }
+#endif
+	    pcon_attached = true;
+	  }
+    }
   // fork_fixup (parent, inuse, "inuse");
   // fhandler_pty_common::fixup_after_fork (parent);
+  if (pcon_attached && get_ttyp ()->switch_to_pcon == 0)
+    get_ttyp ()->switch_to_pcon = 1;
   report_tty_counts (this, "inherited", "");
 }
 
 void
 fhandler_pty_slave::fixup_after_exec ()
 {
+  if (getPseudoConsole ())
+    {
+      if (fhandler_console::get_console_process_id (getHelperProcessId (),
+						    true))
+	if (!pcon_attached)
+	  {
+	    if (change_codepage_to_utf8)
+	      {
+		/* FIXME: The terminal code may not be UTF-8. */
+		SetConsoleCP (65001);
+		SetConsoleOutputCP (65001);
+	      }
+#if 0 /* 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. */
+	    if (!get_ttyp ()->screen_alternated)
+	      {
+#if 0
+		DWORD n;
+		WriteFile (get_output_handle_cyg (),
+			   "\033[H\033[J", 6, &n, NULL);
+#else
+		this->write ("\033[H\033[J", 6);
+#endif
+	      }
+#endif
+	    pcon_attached = true;
+	  }
+    }
+  if (pcon_attached && get_ttyp ()->switch_to_pcon == 0)
+    get_ttyp ()->switch_to_pcon = 1;
   if (!close_on_exec ())
     fixup_after_fork (NULL);
 }
@@ -1544,7 +1847,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 +1917,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))
@@ -1659,23 +1969,80 @@ fhandler_pty_master::pty_master_fwd_thread ()
   DWORD rlen;
   char outbuf[OUT_BUFFER_SIZE];
 
-  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 ())
+	{
+	  if (get_ttyp ()->switch_to_pcon == 1)
+	    get_ttyp ()->switch_to_pcon = 2;
+	  /* 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;
+	      }
+
+	  /* Avoid duplicating slave output which is already sent to
+	     to_master_cyg */
+	  if (get_ttyp ()->switch_to_pcon != 2)
+	    continue;
+
+	  /* 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);
+	    }
+	  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 +2054,146 @@ 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 0
+  /* If master is running on real console,
+     attach to pseudo console in spawn. */
+  char tty[32];
+  for (int fd=0; fd<3; fd++)
+    {
+      ttyname_r (fd, tty, sizeof(tty));
+      if (strncmp (tty, "/dev/cons", 9) == 0)
+	get_ttyp ()->attach_pcon_in_spawn = true;
+    }
+#endif
+#if 1
+  /* 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_spawn = true;
+#endif
+  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 +2202,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 +2227,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 +2236,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 +2244,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 +2309,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 +2322,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 +2356,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 +2379,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
diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc
index 7e1c08990..cb4da7330 100644
--- a/winsup/cygwin/fork.cc
+++ b/winsup/cygwin/fork.cc
@@ -134,6 +134,26 @@ 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;
+	if (((fhandler_pty_master *) fh)->getPseudoConsole ())
+	  {
+	    DWORD dwHelperProcessId =
+	      ((fhandler_pty_master *) fh)->getHelperProcessId ();
+	    debug_printf ("found a PTY master %d: helper_PID=%d",
+			  cfd->get_minor (), dwHelperProcessId);
+	    if (!((fhandler_pty_master *) fh)->attach_pcon_in_spawn ())
+	      {
+		FreeConsole ();
+		AttachConsole (dwHelperProcessId);
+		break;
+	      }
+	  }
+      }
+
   HANDLE& hParent = ch.parent;
 
   sync_with_parent ("after longjmp", true);
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index 85242ec06..04249eb94 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 ())
 	    {
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index 4e549f7d4..cb4bdca06 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -288,6 +288,31 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
       return -1;
     }
 
+  DWORD pidRestore =
+    fhandler_console::get_console_process_id (GetCurrentProcessId (), false);
+  cygheap_fdenum cfd (false);
+  bool attach_to_pcon = false;
+  int fd;
+  while ((fd = cfd.next ()) >= 0 && fd < 3)
+    if (cfd->get_major () == DEV_PTYS_MAJOR)
+      {
+	fhandler_base *fh = cfd;
+	if (((fhandler_pty_slave *) fh)->getPseudoConsole ())
+	  {
+	    DWORD dwHelperProcessId =
+	      ((fhandler_pty_slave *) fh)->getHelperProcessId ();
+	    debug_printf ("found a PTY slave %d: helper_PID=%d",
+			  cfd->get_minor (), dwHelperProcessId);
+	    if (((fhandler_pty_slave *) fh)->attach_pcon_in_spawn ())
+	      {
+		FreeConsole ();
+		AttachConsole (dwHelperProcessId);
+		attach_to_pcon = true;
+		break;
+	      }
+	  }
+      }
+
   av newargv;
   linebuf cmd;
   PWCHAR envblock = NULL;
@@ -868,6 +893,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/tty.cc b/winsup/cygwin/tty.cc
index ad46cb312..06bae302e 100644
--- a/winsup/cygwin/tty.cc
+++ b/winsup/cygwin/tty.cc
@@ -234,7 +234,11 @@ tty::init ()
   was_opened = false;
   master_pid = 0;
   is_console = false;
+  attach_pcon_in_spawn = false;
+  hPseudoConsole = NULL;
   column = 0;
+  switch_to_pcon = 0;
+  screen_alternated = false;
 }
 
 HANDLE
diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h
index 9aee43b9c..3268b668d 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,24 @@ 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_spawn;
+  int switch_to_pcon;
+  bool screen_alternated;
 
 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 +129,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 8f62ed7e6..bef143f96 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) strtoul (argv[1], &end, 0);
   SetEvent (h);
+  if (argc == 4) /* Pseudo console helper mode for PTY */
+    {
+      HANDLE hPipe = (HANDLE) strtoul (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) strtoul (argv[2], &end, 0);
   WaitForSingleObject (h, INFINITE);
   exit (0);
-- 
2.17.0

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

* Re: [PATCH v2 0/1] Pseudo console support in PTY (v2)
  2019-04-03 16:36   ` [PATCH v2 0/1] Pseudo console support in PTY (v2) Takashi Yano
  2019-04-03 16:37     ` [PATCH v2 1/1] Cygwin: pty: add pseudo console support Takashi Yano
@ 2019-04-03 16:50     ` Corinna Vinschen
  2019-04-04  5:27       ` Takashi Yano
  2019-04-03 17:11     ` Corinna Vinschen
  2 siblings, 1 reply; 53+ messages in thread
From: Corinna Vinschen @ 2019-04-03 16:50 UTC (permalink / raw)
  To: Takashi Yano; +Cc: cygwin-developers

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

On Apr  4 01:36, Takashi Yano wrote:
> On Sat, 30 Mar 2019 22:08:04 +0900 Takashi Yano wrote:
> > Known problems:
> > * mintty fails to start if it is started in console cygwin
> >   session.
> 
> This has been resolved in the new version.
> 
> On Tue, 2 Apr 2019 13:02:48 +0200 Corinna Vinschen wrote:
> > I found another weird effect which I can't explain off the top
> > of my head:  system_printf() debug output does not show up
> > in a mintty anymore with this patch.  Any idea why?
> 
> I am not sure, but this may be fixed in the new version.
> Could you please try?

I will, but can you please outline the changes between v1 and v2 of your
patch?  I'd like to understand the code and what the challenges are to
get this working.


Thanks,
Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: [PATCH v2 0/1] Pseudo console support in PTY (v2)
  2019-04-03 16:36   ` [PATCH v2 0/1] Pseudo console support in PTY (v2) Takashi Yano
  2019-04-03 16:37     ` [PATCH v2 1/1] Cygwin: pty: add pseudo console support Takashi Yano
  2019-04-03 16:50     ` [PATCH v2 0/1] Pseudo console support in PTY (v2) Corinna Vinschen
@ 2019-04-03 17:11     ` Corinna Vinschen
  2019-04-04  8:59       ` Takashi Yano
  2 siblings, 1 reply; 53+ messages in thread
From: Corinna Vinschen @ 2019-04-03 17:11 UTC (permalink / raw)
  To: Takashi Yano; +Cc: cygwin-developers

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

On Apr  4 01:36, Takashi Yano wrote:
> On Sat, 30 Mar 2019 22:08:04 +0900 Takashi Yano wrote:
> > Known problems:
> > * mintty fails to start if it is started in console cygwin
> >   session.
> 
> This has been resolved in the new version.

Confirmed.

> On Tue, 2 Apr 2019 13:02:48 +0200 Corinna Vinschen wrote:
> > I found another weird effect which I can't explain off the top
> > of my head:  system_printf() debug output does not show up
> > in a mintty anymore with this patch.  Any idea why?
> 
> I am not sure, but this may be fixed in the new version.
> Could you please try?

Yes, it seems like that's fixed.

Mintty seems to start faster than with v1, too.

I encountered two other issues though, while running cygport to build
the latest gawk testbuild:

  $ cygport gawk.cygport build
  >>> Compiling gawk-4.2.62-1.x86_64

- This ">>> Compiling ..." line is printed by cygport itself.  It's bold
  black without the ConPTY patch, but it's bold white with your ConPTY
  patch.  Any idea why?

- There's a good chance that pressing Ctrl-C at this point will not only
  exit cygport, but also the shell *and* mintty.  Is it possible that
  another call to init_console_handler is required when the process
  runs in the ConPTY?


Thanks,
Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: Pseudo console support in PTY
  2019-04-03  7:55           ` Thomas Wolff
  2019-04-03  8:02             ` Corinna Vinschen
@ 2019-04-04  4:15             ` Takashi Yano
  1 sibling, 0 replies; 53+ messages in thread
From: Takashi Yano @ 2019-04-04  4:15 UTC (permalink / raw)
  To: cygwin-developers

Hi Thomas,

On Wed, 3 Apr 2019 09:55:29 +0200 Thomas Wolff wrote:
> Am 03.04.2019 um 09:27 schrieb Corinna Vinschen:
> > On Apr  3 09:18, Thomas Wolff wrote:
> >> I've now self-compiled cygwin dll on a third system and can run a cygwin
> >> console.
> >> But the build process did not generate the cygwin-console-helper, maybe
> >> that's the reason why mintty does not start?
> Can you give a hint that enables me to test the patch?

cygwin-console-helper.exe will be built in directory:
${arch}-pc-cygwin/winsup/utils

Did you look into this directory?

Without patched cygwin-console-helper.exe, mintty will not
start.

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

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

* Re: Pseudo console support in PTY
  2019-04-03 12:17                 ` Corinna Vinschen
@ 2019-04-04  4:17                   ` Takashi Yano
  2019-04-04  8:06                     ` Corinna Vinschen
  0 siblings, 1 reply; 53+ messages in thread
From: Takashi Yano @ 2019-04-04  4:17 UTC (permalink / raw)
  To: cygwin-developers

On Wed, 3 Apr 2019 14:16:52 +0200 Corinna Vinschen wrote:
> WJFFM, given the current restrictions.
> W10 1703 or later?

Pseudo console requires W10 1809.

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

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

* Re: [PATCH v2 0/1] Pseudo console support in PTY (v2)
  2019-04-03 16:50     ` [PATCH v2 0/1] Pseudo console support in PTY (v2) Corinna Vinschen
@ 2019-04-04  5:27       ` Takashi Yano
  2019-04-04  8:17         ` Thomas Wolff
  0 siblings, 1 reply; 53+ messages in thread
From: Takashi Yano @ 2019-04-04  5:27 UTC (permalink / raw)
  To: cygwin-developers

Hi Corinna,

On Wed, 3 Apr 2019 18:50:29 +0200 Corinna Vinschen wrote:
> I will, but can you please outline the changes between v1 and v2 of your
> patch?  I'd like to understand the code and what the challenges are to
> get this working.

Major changes are as follows.

(1) Pseudo console setup code in pty master is moved into the function
    setup_pseudoconsole().
(2) The code for pushing slave output into pseudo console screen buffer,
    which is for synchronization between real terminal and pseudo console
    screen buffer, is moved into the function push_to_pcon_screenbuffer().
(3) In push_to_pcon_screenbuffer(), add code which attach to pseudo
    console temporally if slave is not attached yet.
(4) Call fhandler_console::need_invisible() in stdio_init() in dtable.cc
    instead of just returning from function when progname is "mintty".
(5) Pushing slave output into pseudo console screen buffer is disabled
    when alternate screen buffer is used.
(6) Change code page to 65001 (UTF-8) at startup of pty slave.
(7) Remove "\033[6n" and "\033[0c", which generate report result in
    console input buffer, in the data to be pushed into pseudo console
    screen buffer.
(8) Add some comments.

(1) and (2) are not essential changes.

(3) fixes the issue:
On Thu,  4 Apr 2019 01:36:20 +0900 Takashi Yano wrote:
> On Sat, 30 Mar 2019 22:08:04 +0900 Takashi Yano wrote:
> > Known problems:
> > * mintty fails to start if it is started in console cygwin
> >   session.
> This has been resolved in the new version.

(3) also makes it unnecessary the code returning from function
stdio_init() in dtable.cc when progname is "mintty".

(4) is for:
On Tue, 2 Apr 2019 13:02:48 +0200 Corinna Vinschen wrote:
> I found another weird effect which I can't explain off the top
> of my head:  system_printf() debug output does not show up
> in a mintty anymore with this patch.  Any idea why?
howver, I am not sure why this solves the issue.

(5) makes vim screen drawing speed up a little.
(6) avoids garbled characters.
(7) avoids the garbage in the input buffer for native console apps
after vim is used.

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

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

* Re: Pseudo console support in PTY
  2019-04-04  4:17                   ` Takashi Yano
@ 2019-04-04  8:06                     ` Corinna Vinschen
  0 siblings, 0 replies; 53+ messages in thread
From: Corinna Vinschen @ 2019-04-04  8:06 UTC (permalink / raw)
  To: cygwin-developers

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

On Apr  4 13:17, Takashi Yano wrote:
> On Wed, 3 Apr 2019 14:16:52 +0200 Corinna Vinschen wrote:
> > WJFFM, given the current restrictions.
> > W10 1703 or later?
> 
> Pseudo console requires W10 1809.

Oh, right!  1703 is the 256 color console stuff.


Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: [PATCH v2 0/1] Pseudo console support in PTY (v2)
  2019-04-04  5:27       ` Takashi Yano
@ 2019-04-04  8:17         ` Thomas Wolff
  2019-04-04  9:34           ` Takashi Yano
  0 siblings, 1 reply; 53+ messages in thread
From: Thomas Wolff @ 2019-04-04  8:17 UTC (permalink / raw)
  To: cygwin-developers

Hi
> Without patched cygwin-console-helper.exe, mintty will not start.
I was meanwhile able to build the patch (v1) on a Windows 7 system and 
try it on a Windows 10 system.

Takashi Yano wrote:
> Hi Corinna,
>
> On Wed, 3 Apr 2019 18:50:29 +0200 Corinna Vinschen wrote:
>> I will, but can you please outline the changes between v1 and v2 of your
>> patch?  I'd like to understand the code and what the challenges are to
>> get this working.
> Major changes are as follows.
>
> (1) Pseudo console setup code in pty master is moved into the function
>      setup_pseudoconsole().
In my test, character attributes (colour) were sometimes only reproduced 
when running the test program for the first time. Maybe v2 fixes that.
> (2) The code for pushing slave output into pseudo console screen buffer,
>      which is for synchronization between real terminal and pseudo console
>      screen buffer, is moved into the function push_to_pcon_screenbuffer().
> (3) In push_to_pcon_screenbuffer(), add code which attach to pseudo
>      console temporally if slave is not attached yet.
> (4) Call fhandler_console::need_invisible() in stdio_init() in dtable.cc
>      instead of just returning from function when progname is "mintty".
What's the specific handling for mintty? Puzzled, I think all pty-based 
terminals need the same handling.
> (5) Pushing slave output into pseudo console screen buffer is disabled
>      when alternate screen buffer is used.
Confused. What does console handling have to do with the alternate 
screen? It's purely a client-side concept with respect to the pty. If 
the terminal is in alternate screen mode, console output is directed to 
that screen buffer and should be handled the same way, right?
> (6) Change code page to 65001 (UTF-8) at startup of pty slave.
What about other codepages, depending on the locale?
> (7) Remove "\033[6n" and "\033[0c", which generate report result in
>      console input buffer, in the data to be pushed into pseudo console
>      screen buffer.
Why's that? If a Windows console app really sends those, it will do so 
on purpose (e.g. because it detects the %TERM% variable to conclude it's 
running in a terminal), so it will also expect the respective response.

I wasn't able to test raw keyboard input and terminal resize so far 
(looking for a suitable test app, some command-line Windows editor 
perhaps...)

There is one other interoperabililty problem I'm concerned about but it 
may not be related and perhaps not fixable via ConPTY:  If I start a 
Windows GUI program, e.g. notepad, and enter ^Z in the terminal, notepad 
is terminated rather than suspended. Maybe Cygwin should just ignore 
suspend signals towards Windows programs.

Kind regards
Thomas

> (8) Add some comments.
>
> (1) and (2) are not essential changes.
>
> (3) fixes the issue:
> On Thu,  4 Apr 2019 01:36:20 +0900 Takashi Yano wrote:
>> On Sat, 30 Mar 2019 22:08:04 +0900 Takashi Yano wrote:
>>> Known problems:
>>> * mintty fails to start if it is started in console cygwin
>>>    session.
>> This has been resolved in the new version.
> (3) also makes it unnecessary the code returning from function
> stdio_init() in dtable.cc when progname is "mintty".
>
> (4) is for:
> On Tue, 2 Apr 2019 13:02:48 +0200 Corinna Vinschen wrote:
>> I found another weird effect which I can't explain off the top
>> of my head:  system_printf() debug output does not show up
>> in a mintty anymore with this patch.  Any idea why?
> howver, I am not sure why this solves the issue.
>
> (5) makes vim screen drawing speed up a little.
> (6) avoids garbled characters.
> (7) avoids the garbage in the input buffer for native console apps
> after vim is used.
>

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

* Re: [PATCH v2 0/1] Pseudo console support in PTY (v2)
  2019-04-03 17:11     ` Corinna Vinschen
@ 2019-04-04  8:59       ` Takashi Yano
  2019-04-04 10:46         ` Corinna Vinschen
  0 siblings, 1 reply; 53+ messages in thread
From: Takashi Yano @ 2019-04-04  8:59 UTC (permalink / raw)
  To: cygwin-developers

Hi Corinna,

On Wed, 3 Apr 2019 19:11:05 +0200 Corinna Vinschen wrote:
> Confirmed.
...
> Yes, it seems like that's fixed.

Thanks for testing.

>   $ cygport gawk.cygport build
>   >>> Compiling gawk-4.2.62-1.x86_64
> 
> - This ">>> Compiling ..." line is printed by cygport itself.  It's bold
>   black without the ConPTY patch, but it's bold white with your ConPTY
>   patch.  Any idea why?

I cannot reproduce this. ">>> Compiling ..." line is printed as bright
white (not bold) in my environment in both cygwin 3.0.5 and the version
with pseudo console support. "bold black"? What color is the background
of your mintty?

> - There's a good chance that pressing Ctrl-C at this point will not only
>   exit cygport, but also the shell *and* mintty.  Is it possible that
>   another call to init_console_handler is required when the process
>   runs in the ConPTY?

I can reproduce this. However, this is very few case in my environment,
so it is difficult to find what is happening. Please wait a while.

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

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

* Re: [PATCH v2 0/1] Pseudo console support in PTY (v2)
  2019-04-04  8:17         ` Thomas Wolff
@ 2019-04-04  9:34           ` Takashi Yano
  0 siblings, 0 replies; 53+ messages in thread
From: Takashi Yano @ 2019-04-04  9:34 UTC (permalink / raw)
  To: cygwin-developers

Hi Thomas,

On Thu, 4 Apr 2019 10:17:29 +0200 Thomas Wolff wrote:
> > (4) Call fhandler_console::need_invisible() in stdio_init() in dtable.cc
> >      instead of just returning from function when progname is "mintty".
> What's the specific handling for mintty? Puzzled, I think all pty-based 
> terminals need the same handling.

I agree.
In v1 mintty needs such tweak, however, it is not necessary any more in v2.

> > (5) Pushing slave output into pseudo console screen buffer is disabled
> >      when alternate screen buffer is used.
> Confused. What does console handling have to do with the alternate 
> screen? It's purely a client-side concept with respect to the pty. If 
> the terminal is in alternate screen mode, console output is directed to 
> that screen buffer and should be handled the same way, right?
> > (6) Change code page to 65001 (UTF-8) at startup of pty slave.
> What about other codepages, depending on the locale?
> > (7) Remove "\033[6n" and "\033[0c", which generate report result in
> >      console input buffer, in the data to be pushed into pseudo console
> >      screen buffer.
> Why's that? If a Windows console app really sends those, it will do so 
> on purpose (e.g. because it detects the %TERM% variable to conclude it's 
> running in a terminal), so it will also expect the respective response.

PTY code is very complex. PTY slave has 4 handles; two for cygwin apps,
others for native console apps. io_handle_cyg and output_handle_cyg are
for cygwin apps. io_handle and output_handle are for native console apps.
Pseudo console is attached to io_handle and output_handle. Data written
into output_handle goes through pseudo console and stored in screen
buffer and displayed. However, as for cygwin apps, data written into
output_handle_cyg does not go through pseudo console, so it is not
stored in pseudo console screen buffer. Pseudo console sometimes redraws
screen based on its own screen buffer. The data from cygwin apps is not
in pseudo console screen buffer, so it disappears. To avoid this, data
from cygwin apps is pushed into pseudo console screen buffer by pushing
it to output_handle. At the same time, to avoid displaying the data twice,
forwarding is stopped if cygwin app is active. (5) means the contents
in alternate screen buffer are not necessary to synchronize with real
terminal becuse alternate screen buffer is usually used only temporally.
As for (7), when "\033[6n" is written by cygwin apps to output_handle_cyg,
the result is returned from io_handle_cyg. If "\033[6n" is pushed into
output_handle, the results also appears in io_handle. Since this is not
read by cygwin apps, it remains in input buffer for native apps. Thus
"\033[6n" from cygwin apps should not be pushed into output_handle.
Of course "\033[6n" from native apps is not removed and written into
output_handle and result is returend from io_handle.

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

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

* Re: [PATCH v2 0/1] Pseudo console support in PTY (v2)
  2019-04-04  8:59       ` Takashi Yano
@ 2019-04-04 10:46         ` Corinna Vinschen
  2019-04-06 11:13           ` [PATCH v3 0/1] Pseudo console support in PTY (v3) Takashi Yano
  0 siblings, 1 reply; 53+ messages in thread
From: Corinna Vinschen @ 2019-04-04 10:46 UTC (permalink / raw)
  To: Takashi Yano; +Cc: cygwin-developers

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

On Apr  4 17:59, Takashi Yano wrote:
> Hi Corinna,
> 
> On Wed, 3 Apr 2019 19:11:05 +0200 Corinna Vinschen wrote:
> > Confirmed.
> ...
> > Yes, it seems like that's fixed.
> 
> Thanks for testing.
> 
> >   $ cygport gawk.cygport build
> >   >>> Compiling gawk-4.2.62-1.x86_64
> > 
> > - This ">>> Compiling ..." line is printed by cygport itself.  It's bold
> >   black without the ConPTY patch, but it's bold white with your ConPTY
> >   patch.  Any idea why?
> 
> I cannot reproduce this. ">>> Compiling ..." line is printed as bright
> white (not bold) in my environment in both cygwin 3.0.5 and the version
> with pseudo console support. "bold black"? What color is the background
> of your mintty?

I'm using black characters on a medium grey background.  That may have
an influence I guess.

> > - There's a good chance that pressing Ctrl-C at this point will not only
> >   exit cygport, but also the shell *and* mintty.  Is it possible that
> >   another call to init_console_handler is required when the process
> >   runs in the ConPTY?
> 
> I can reproduce this. However, this is very few case in my environment,
> so it is difficult to find what is happening. Please wait a while.

Sure, no worries.  Take your time, this is all pretty new.


Thanks a lot,
Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* [PATCH v3 0/1] Pseudo console support in PTY (v3)
  2019-04-04 10:46         ` Corinna Vinschen
@ 2019-04-06 11:13           ` Takashi Yano
  2019-04-06 11:14             ` [PATCH v3 1/1] Cygwin: pty: add pseudo console support Takashi Yano
                               ` (2 more replies)
  0 siblings, 3 replies; 53+ messages in thread
From: Takashi Yano @ 2019-04-06 11:13 UTC (permalink / raw)
  To: cygwin-developers; +Cc: Takashi Yano

The major changes from v2 is as follows.

(1) I dig into this problem,
    https://www.cygwin.com/ml/cygwin-developers/2019-04/msg00000.html
    and adoptted another approach. Slave process is left unattached
    by some reason if the executable is a windows GUI binary, such
    as mintty. So the code is added in stdio_init() in dtable.cc,
    which makes slave attach to pseudo console and fix up handles,
    if the slave is on pty but not attached. The previous approach,
    which uses fhandler_console::need_invisible(), has a side effect
    that the mintty looks running on cons*, although it actually has
    no TTY.

(2) The ctrl-C issue reported in
    https://cygwin.com/ml/cygwin-developers/2019-04/msg00012.html
    has been fixed. This seems to be caused by race condition of a
    variable, switch_to_pcon. This variable holds the state for
    switching I/O between pseudo console for native console apps
    and conventional pty for cygwin apps. The handling of this
    variable is refined. I suppose the issue is fixed in v3.

(3) Code page handling is refined a bit. Now, code page 65001 (UTF8)
    and native code page in your region should work without garbled
    characters. This works properly only on terminal which supports
    UTF-8.

(4) Synchronization between real terminal and pseudo console screen
    buffer is improved. For this, screen is cleared on startup of pty
    slave app. Also echo for key input is pushed into pseudo console
    screen buffer.

By (4),
On Sat, 30 Mar 2019 22:08:04 +0900 Takashi Yano wrote:
> Known problems:
> * Sometimes the screen layout would be broken.
is almost resolved.

D=http://tyan0.dip.jp/cygwin
${D}/x86_64/test/cygwin1-20190406.dll.xz
${D}/x86_64/test/cygwin-console-helper.exe.xz
${D}/x86/test/cygwin1-20190406.dll.xz
${D}/x86/test/cygwin-console-helper.exe.xz


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

 winsup/cygwin/dtable.cc               |  57 +++
 winsup/cygwin/fhandler.h              |  46 +-
 winsup/cygwin/fhandler_console.cc     |  32 ++
 winsup/cygwin/fhandler_tty.cc         | 610 ++++++++++++++++++++++++--
 winsup/cygwin/fork.cc                 |  20 +
 winsup/cygwin/select.cc               |  22 +-
 winsup/cygwin/spawn.cc                |  32 ++
 winsup/cygwin/tty.cc                  |   5 +
 winsup/cygwin/tty.h                   |  21 +-
 winsup/utils/cygwin-console-helper.cc |  14 +-
 10 files changed, 816 insertions(+), 43 deletions(-)

-- 
2.17.0

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

* [PATCH v3 1/1] Cygwin: pty: add pseudo console support.
  2019-04-06 11:13           ` [PATCH v3 0/1] Pseudo console support in PTY (v3) Takashi Yano
@ 2019-04-06 11:14             ` Takashi Yano
  2019-04-06 17:43             ` [PATCH v3 0/1] Pseudo console support in PTY (v3) Corinna Vinschen
  2019-04-06 21:33             ` [PATCH v3 0/1] Pseudo console support in PTY (v3) Thomas Wolff
  2 siblings, 0 replies; 53+ messages in thread
From: Takashi Yano @ 2019-04-06 11:14 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 privides 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               |  57 +++
 winsup/cygwin/fhandler.h              |  46 +-
 winsup/cygwin/fhandler_console.cc     |  32 ++
 winsup/cygwin/fhandler_tty.cc         | 610 ++++++++++++++++++++++++--
 winsup/cygwin/fork.cc                 |  20 +
 winsup/cygwin/select.cc               |  22 +-
 winsup/cygwin/spawn.cc                |  32 ++
 winsup/cygwin/tty.cc                  |   5 +
 winsup/cygwin/tty.h                   |  21 +-
 winsup/utils/cygwin-console-helper.cc |  14 +-
 10 files changed, 816 insertions(+), 43 deletions(-)

diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
index 636221a77..0aaddab95 100644
--- a/winsup/cygwin/dtable.cc
+++ b/winsup/cygwin/dtable.cc
@@ -147,6 +147,42 @@ dtable::get_debugger_info ()
 void
 dtable::stdio_init ()
 {
+#if 0 /* Experimental code */
+  /* This is workaround for the issue:
+     https://www.cygwin.com/ml/cygwin-developers/2019-04/msg00000.html
+     when pseudo console is enabled. */
+  fhandler_console::need_invisible ();
+#endif
+
+  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;
+		}
+	    }
+	}
+    }
+  if (need_fixup_handle)
+    goto fixup_handle;
+
   if (myself->cygstarted || ISSTATE (myself, PID_CYGPARENT))
     {
       tty_min *t = cygwin_shared->tty.get_cttyp ();
@@ -155,6 +191,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 bc66377cd..73992d208 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1994,6 +1994,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 *) {}
 
@@ -2026,14 +2027,15 @@ 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), pcon_attached (false)
   {
     pc.file_attributes (FILE_ATTRIBUTE_NORMAL);
   }
   static const unsigned pipesize = 128 * 1024;
   HANDLE output_mutex, input_mutex;
   HANDLE input_available_event;
+  bool pcon_attached;
 
   bool use_archetype () const {return true;}
   DWORD __acquire_output_mutex (const char *fn, int ln, DWORD ms);
@@ -2064,14 +2066,28 @@ class fhandler_pty_common: public fhandler_termios
     return fh;
   }
 
+  bool attach_pcon_in_spawn (void)
+  {
+    return get_ttyp ()->attach_pcon_in_spawn;
+  }
+  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;
 
   /* Helper functions for fchmod and fchown. */
   bool fch_open_handles (bool chown);
@@ -2084,6 +2100,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);
@@ -2124,6 +2142,19 @@ class fhandler_pty_slave: public fhandler_pty_common
     copyto (fh);
     return fh;
   }
+  void reset_switch_to_pcon (void)
+  {
+    if (get_ttyp ()->switch_to_pcon == 2)
+      Sleep (20);
+    WaitForSingleObject (input_mutex, INFINITE);
+    get_ttyp ()->switch_to_pcon = 0;
+    ReleaseMutex (input_mutex);
+  }
+  void push_to_pcon_screenbuffer (const char *ptr, size_t len);
+  void mask_switch_to_pcon (bool mask)
+  {
+    get_ttyp ()->mask_switch_to_pcon = mask;
+  }
 };
 
 #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit))
@@ -2132,15 +2163,14 @@ 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);
 
@@ -2187,6 +2217,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 e3656a33a..335467b0b 100644
--- a/winsup/cygwin/fhandler_console.cc
+++ b/winsup/cygwin/fhandler_console.cc
@@ -1028,6 +1028,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;
@@ -3082,6 +3095,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..5ce634305 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -25,6 +25,21 @@ details. */
 #include "child_info.h"
 #include <asm/socket.h>
 #include "cygwait.h"
+#include "tls_pbuf.h"
+
+/* 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 */
+
+extern "C" int sscanf (const char *, const char *, ...);
+extern "C" int ttyname_r(int, char*, size_t);
 
 #define close_maybe(h) \
   do { \
@@ -39,6 +54,7 @@ struct pipe_request {
 
 struct pipe_reply {
   HANDLE from_master;
+  HANDLE from_master_cyg;
   HANDLE to_master;
   HANDLE to_master_cyg;
   DWORD error;
@@ -67,7 +83,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 +251,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 +312,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;
@@ -340,11 +356,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 +415,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 +460,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 +502,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,22 +514,30 @@ 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);
 
   fhandler_console::need_invisible ();
   set_open_status ();
+  WaitForSingleObject (input_mutex, INFINITE);
+  if (pcon_attached && get_ttyp ()->switch_to_pcon == 0)
+    get_ttyp ()->switch_to_pcon = 1;
+  ReleaseMutex (input_mutex);
   return 1;
 
 err:
@@ -548,6 +586,9 @@ 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 */
   fhandler_pty_common::close ();
@@ -596,6 +637,115 @@ fhandler_pty_slave::init (HANDLE h, DWORD a, mode_t)
   return ret;
 }
 
+void
+fhandler_pty_slave::push_to_pcon_screenbuffer (const char *ptr, size_t len)
+{
+  DWORD pidRestore = 0;
+  /* If not attached pseudo console yet, try to attach temporally. */
+  if (!pcon_attached)
+    {
+      pidRestore =
+	fhandler_console::get_console_process_id (GetCurrentProcessId (),
+						  false);
+      /* If pidRestore is not set, give up to push. */
+      if (!pidRestore)
+	return;
+      FreeConsole ();
+      AttachConsole (getHelperProcessId ());
+    }
+  char *buf;
+  size_t nlen;
+  if (GetConsoleOutputCP () != 65001 &&
+      strstr (setlocale (LC_CTYPE, NULL), "UTF-8"))
+    {
+      /* Code page conversion from UTF-8 to OEM code page */
+      /* FIXME: The terminal code may not be UTF-8. */
+      size_t wlen =
+	MultiByteToWideChar (CP_UTF8, 0, (char *)ptr, len, NULL, 0);
+      wchar_t *wbuf = (wchar_t *)
+	HeapAlloc (GetProcessHeap (), 0, wlen * sizeof (wchar_t));
+      wlen =
+	MultiByteToWideChar (CP_UTF8, 0, (char *)ptr, len, wbuf, wlen);
+      nlen = WideCharToMultiByte (CP_OEMCP, 0,
+				  wbuf, wlen, NULL, 0, NULL, NULL);
+      buf = (char *) HeapAlloc (GetProcessHeap (), 0, nlen);
+      nlen = WideCharToMultiByte (CP_OEMCP, 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;
+    }
+  /* Remove alternate screen buffer drawing */
+  char *p0 = buf, *p1 = buf;
+  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)
+	    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)
+	    {
+	      get_ttyp ()->screen_alternated = false;
+	      memmove (p0, p1+8, buf+nlen - (p1+8));
+	      nlen -= p1+8 - p0;
+	    }
+	  else
+	    nlen = p0 - buf;
+	}
+    }
+  if (!nlen) /* Nothing to be synchronized */
+    return;
+  /* 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 = 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 = buf;
+  DWORD wLen, written = 0;
+  while (written <  nlen)
+    {
+      WriteFile (get_output_handle (), p, nlen - written, &wLen, NULL);
+      written += wLen;
+      p += wLen;
+    }
+  /* Detach from pseudo console and resume. */
+  SetConsoleMode (get_output_handle (), dwMode);
+  HeapFree (GetProcessHeap (), 0, buf);
+  if (!pcon_attached)
+    {
+      FreeConsole ();
+      AttachConsole (pidRestore);
+    }
+}
+
 ssize_t __stdcall
 fhandler_pty_slave::write (const void *ptr, size_t len)
 {
@@ -609,6 +759,16 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 
   push_process_state process_state (PID_TTYOU);
 
+  reset_switch_to_pcon ();
+
+  /* Push slave output to pseudo console screen buffer */
+  if (getPseudoConsole ())
+    {
+      acquire_output_mutex (INFINITE);
+      push_to_pcon_screenbuffer ((char *)ptr, len);
+      release_output_mutex ();
+    }
+
   if (!process_opost_output (get_output_handle_cyg (), ptr, towrite, false))
     {
       DWORD err = GetLastError ();
@@ -629,6 +789,7 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 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 +805,13 @@ 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);
 
+  mask_switch_to_pcon (true);
+  reset_switch_to_pcon ();
+
   if (is_nonblocking () || !ptr) /* Indicating tcflush(). */
     time_to_wait = 0;
   else if ((get_ttyp ()->ti.c_lflag & ICANON))
@@ -757,6 +921,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 +942,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 +1026,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 +1065,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 +1073,7 @@ fhandler_pty_slave::tcgetattr (struct termios *t)
 int
 fhandler_pty_slave::tcsetattr (int, const struct termios *t)
 {
+  reset_switch_to_pcon ();
   acquire_output_mutex (INFINITE);
   get_ttyp ()->ti = *t;
   release_output_mutex ();
@@ -908,7 +1085,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 +1108,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 +1175,49 @@ 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)
+	    {
+	      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 ();
+	      AttachConsole (getHelperProcessId ());
+	    }
+	  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 ();
+	      AttachConsole (pidRestore);
+	    }
+	}
+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 +1451,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);
@@ -1285,7 +1509,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 ();
 }
 
@@ -1295,8 +1520,9 @@ fhandler_pty_master::close ()
   OBJECT_BASIC_INFORMATION obi;
   NTSTATUS status;
 
-  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)
@@ -1320,6 +1546,22 @@ fhandler_pty_master::close ()
 	    }
 	  release_output_mutex ();
 	  master_fwd_thread->terminate_thread ();
+
+	  /* Pseudo Console Support */
+	  if (getPseudoConsole ())
+	    {
+	      /* Terminate helper process */
+	      SetEvent (get_ttyp ()->hHelperGoodbye);
+	      WaitForSingleObject (get_ttyp ()->hHelperProcess, INFINITE);
+	      /* 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 = 0;
+	    }
 	}
     }
 
@@ -1344,17 +1586,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 +1625,17 @@ 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 == 2 &&
+      !get_ttyp ()->mask_switch_to_pcon)
+    {
+      DWORD wLen;
+      WriteFile (to_slave, ptr, len, &wLen, NULL);
+      return wLen;
+    }
+
   line_edit_status status = line_edit (p++, len, ti, &ret);
   if (status > line_edit_signalled && status != line_edit_pipe_full)
     ret = -1;
@@ -1443,6 +1703,17 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg)
       *(struct winsize *) arg = get_ttyp ()->winsize;
       break;
     case TIOCSWINSZ:
+      if (getPseudoConsole ())
+	{
+	  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 +1729,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;
@@ -1497,14 +1768,71 @@ fhandler_pty_common::set_close_on_exec (bool val)
 void
 fhandler_pty_slave::fixup_after_fork (HANDLE parent)
 {
+  if (getPseudoConsole ())
+    {
+      if (fhandler_console::get_console_process_id (getHelperProcessId (),
+						    true))
+	if (!pcon_attached)
+	  {
+	    if (strstr (setlocale (LC_CTYPE, NULL), "UTF-8"))
+	      {
+		SetConsoleCP (65001);
+		SetConsoleOutputCP (65001);
+	      }
+#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 ()->screen_alternated)
+	      WriteFile (get_output_handle_cyg (), "\033[H\033[J", 6, &n, NULL);
+#endif
+	    pcon_attached = true;
+	  }
+    }
   // fork_fixup (parent, inuse, "inuse");
   // fhandler_pty_common::fixup_after_fork (parent);
+  WaitForSingleObject (input_mutex, INFINITE);
+  if (pcon_attached && get_ttyp ()->switch_to_pcon == 0)
+    get_ttyp ()->switch_to_pcon = 1;
+  ReleaseMutex (input_mutex);
   report_tty_counts (this, "inherited", "");
 }
 
 void
 fhandler_pty_slave::fixup_after_exec ()
 {
+  if (getPseudoConsole ())
+    {
+      if (fhandler_console::get_console_process_id (getHelperProcessId (),
+						    true))
+	if (!pcon_attached)
+	  {
+	    if (strstr (setlocale (LC_CTYPE, NULL), "UTF-8"))
+	      {
+		/* FIXME: The terminal code may not be UTF-8. */
+		SetConsoleCP (65001);
+		SetConsoleOutputCP (65001);
+	      }
+#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 ()->screen_alternated)
+	      WriteFile (get_output_handle_cyg (), "\033[H\033[J", 6, &n, NULL);
+#endif
+	    pcon_attached = true;
+	  }
+    }
+  WaitForSingleObject (input_mutex, INFINITE);
+  if (pcon_attached && get_ttyp ()->switch_to_pcon == 0)
+    get_ttyp ()->switch_to_pcon = 1;
+  ReleaseMutex (input_mutex);
   if (!close_on_exec ())
     fixup_after_fork (NULL);
 }
@@ -1544,7 +1872,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 +1942,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))
@@ -1659,23 +1994,82 @@ fhandler_pty_master::pty_master_fwd_thread ()
   DWORD rlen;
   char outbuf[OUT_BUFFER_SIZE];
 
-  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 ())
+	{
+	  WaitForSingleObject (input_mutex, INFINITE);
+	  if (get_ttyp ()->switch_to_pcon == 1)
+	    get_ttyp ()->switch_to_pcon = 2;
+	  ReleaseMutex (input_mutex);
+	  /* Avoid duplicating slave output which is already sent to
+	     to_master_cyg */
+	  if (get_ttyp ()->switch_to_pcon != 2)
+	    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;
+	      }
+
+	  /* 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);
+	    }
+	  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 +2081,146 @@ 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 0
+  /* If master is running on real console,
+     attach to pseudo console in spawn. */
+  char tty[32];
+  for (int fd=0; fd<3; fd++)
+    {
+      ttyname_r (fd, tty, sizeof(tty));
+      if (strncmp (tty, "/dev/cons", 9) == 0)
+	get_ttyp ()->attach_pcon_in_spawn = true;
+    }
+#endif
+#if 1
+  /* 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_spawn = true;
+#endif
+  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 +2229,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 +2254,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 +2263,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 +2271,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 +2336,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 +2349,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 +2383,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 +2406,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
diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc
index 7e1c08990..cb4da7330 100644
--- a/winsup/cygwin/fork.cc
+++ b/winsup/cygwin/fork.cc
@@ -134,6 +134,26 @@ 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;
+	if (((fhandler_pty_master *) fh)->getPseudoConsole ())
+	  {
+	    DWORD dwHelperProcessId =
+	      ((fhandler_pty_master *) fh)->getHelperProcessId ();
+	    debug_printf ("found a PTY master %d: helper_PID=%d",
+			  cfd->get_minor (), dwHelperProcessId);
+	    if (!((fhandler_pty_master *) fh)->attach_pcon_in_spawn ())
+	      {
+		FreeConsole ();
+		AttachConsole (dwHelperProcessId);
+		break;
+	      }
+	  }
+      }
+
   HANDLE& hParent = ch.parent;
 
   sync_with_parent ("after longjmp", true);
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index 85242ec06..495754026 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 ())
 	    {
@@ -1176,17 +1179,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 579b3c9c3..3d30d6cc6 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -288,6 +288,31 @@ 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;
+  for (int fd = 0; fd < 3; fd ++)
+    {
+      fhandler_base *fh = ::cygheap->fdtab[fd];
+      if (fh && fh->get_major () == DEV_PTYS_MAJOR)
+	{
+	  fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
+	  if (ptys->getPseudoConsole ())
+	    {
+	      DWORD dwHelperProcessId = ptys->getHelperProcessId ();
+	      debug_printf ("found a PTY slave %d: helper_PID=%d",
+			    fh->get_minor (), dwHelperProcessId);
+	      if (ptys->attach_pcon_in_spawn ())
+		{
+		  FreeConsole ();
+		  AttachConsole (dwHelperProcessId);
+		  attach_to_pcon = true;
+		  break;
+		}
+	    }
+	}
+    }
+
   av newargv;
   linebuf cmd;
   PWCHAR envblock = NULL;
@@ -867,6 +892,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/tty.cc b/winsup/cygwin/tty.cc
index ad46cb312..a0028d3b3 100644
--- a/winsup/cygwin/tty.cc
+++ b/winsup/cygwin/tty.cc
@@ -234,7 +234,12 @@ tty::init ()
   was_opened = false;
   master_pid = 0;
   is_console = false;
+  attach_pcon_in_spawn = false;
+  hPseudoConsole = NULL;
   column = 0;
+  switch_to_pcon = 0;
+  screen_alternated = false;
+  mask_switch_to_pcon = false;
 }
 
 HANDLE
diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h
index 9aee43b9c..000fca9c2 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,25 @@ 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_spawn;
+  int switch_to_pcon;
+  bool screen_alternated;
+  bool mask_switch_to_pcon;
 
 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 +130,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 8f62ed7e6..bef143f96 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) strtoul (argv[1], &end, 0);
   SetEvent (h);
+  if (argc == 4) /* Pseudo console helper mode for PTY */
+    {
+      HANDLE hPipe = (HANDLE) strtoul (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) strtoul (argv[2], &end, 0);
   WaitForSingleObject (h, INFINITE);
   exit (0);
-- 
2.17.0

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

* Re: [PATCH v3 0/1] Pseudo console support in PTY (v3)
  2019-04-06 11:13           ` [PATCH v3 0/1] Pseudo console support in PTY (v3) Takashi Yano
  2019-04-06 11:14             ` [PATCH v3 1/1] Cygwin: pty: add pseudo console support Takashi Yano
@ 2019-04-06 17:43             ` Corinna Vinschen
  2019-04-12 10:22               ` [PATCH v4 0/1] Pseudo console support in PTY (v4) Takashi Yano
  2019-04-06 21:33             ` [PATCH v3 0/1] Pseudo console support in PTY (v3) Thomas Wolff
  2 siblings, 1 reply; 53+ messages in thread
From: Corinna Vinschen @ 2019-04-06 17:43 UTC (permalink / raw)
  To: Takashi Yano; +Cc: cygwin-developers

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

Hi Takashi,

On Apr  6 20:13, Takashi Yano wrote:
> The major changes from v2 is as follows.
> 
> (1) I dig into this problem,
>     https://www.cygwin.com/ml/cygwin-developers/2019-04/msg00000.html
>     and adoptted another approach. Slave process is left unattached
>     by some reason if the executable is a windows GUI binary, such
>     as mintty. So the code is added in stdio_init() in dtable.cc,
>     which makes slave attach to pseudo console and fix up handles,
>     if the slave is on pty but not attached. The previous approach,
>     which uses fhandler_console::need_invisible(), has a side effect
>     that the mintty looks running on cons*, although it actually has
>     no TTY.
> 
> (2) The ctrl-C issue reported in
>     https://cygwin.com/ml/cygwin-developers/2019-04/msg00012.html
>     has been fixed. This seems to be caused by race condition of a
>     variable, switch_to_pcon. This variable holds the state for
>     switching I/O between pseudo console for native console apps
>     and conventional pty for cygwin apps. The handling of this
>     variable is refined. I suppose the issue is fixed in v3.

This looks better, but there's still some problem:

$ cygport gawk.cygport build
>>> Compiling gawk-4.2.62-1.x86_64
[pressing Ctrl-C multiple times]
     14 [main] chmod (7016) child_copy: cygheap read copy failed, 0x180376408..0
x1803887A8, done 0, windows pid 7016, Win32 error 299
    766 [main] chmod (7016) C:\cygwin64\bin\chmod.exe: *** fatal error - ccalloc would have returned NULL
     14 [main] chmod (7016) child_copy: cygheap read copy failed, 0x180376408..0x1803887A8, done 0, windows pid 7016, Win32 error 299
    766 [main] chmod (7016) C:\cygwin64\bin\chmod.exe: *** fatal error - ccalloc
 would have returned NULL

I can reproduce this pretty easily, 1 out of 5 or so.  Since I wasn't
sure this is really your patch, I checked again without your patch but
I can't get it to fail like this.

> (3) Code page handling is refined a bit. Now, code page 65001 (UTF8)
>     and native code page in your region should work without garbled
>     characters. This works properly only on terminal which supports
>     UTF-8.
> 
> (4) Synchronization between real terminal and pseudo console screen
>     buffer is improved. For this, screen is cleared on startup of pty
>     slave app. Also echo for key input is pushed into pseudo console
>     screen buffer.

Apart from the above problem, this looks good from what I see in my
testing.

Tell me as soon as you're confident enough with your code to push it to
master.  Even if there are still bugs, it allows to test this with a
slightly bigger audience via the developer snapshots.  But that's your
choice, no pressure from my side.


Thanks,
Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: [PATCH v3 0/1] Pseudo console support in PTY (v3)
  2019-04-06 11:13           ` [PATCH v3 0/1] Pseudo console support in PTY (v3) Takashi Yano
  2019-04-06 11:14             ` [PATCH v3 1/1] Cygwin: pty: add pseudo console support Takashi Yano
  2019-04-06 17:43             ` [PATCH v3 0/1] Pseudo console support in PTY (v3) Corinna Vinschen
@ 2019-04-06 21:33             ` Thomas Wolff
  2019-04-07  5:05               ` Takashi Yano
  2 siblings, 1 reply; 53+ messages in thread
From: Thomas Wolff @ 2019-04-06 21:33 UTC (permalink / raw)
  To: Takashi Yano, cygwin-developers

Hi Takashi,

> The major changes from v2 is as follows.
My testing showed the following observations.
> ...
>
> (3) Code page handling is refined a bit. Now, code page 65001 (UTF8)
>       and native code page in your region should work without garbled
>      characters. This works properly only on terminal which supports
>      UTF-8.
Character output conversion works with my test program (using 
WriteConsoleW). Using java, output works too (with proper java encoding 
option), but non-ASCII input characters are replaced by and echoed as space.

If I try a non-Unicode terminal session, e.g. running mintty -o Locale=C 
-o Charset=CP1252 -, and enter a € key, mintty hangs. While you said it 
doesn't work (yet), it should better not hang at least. I'd like to 
repeat at this point that the whole mechanism should only be applied if 
a non-cygwin program is run. (If it were, entering € in bash would not 
block the terminal.)

> (4) Synchronization between real terminal and pseudo console screen
>      buffer is improved. For this, screen is cleared on startup of pty
>      slave app. Also echo for key input is pushed into pseudo console
>      screen buffer.
Do you actively synchronize anything? I thought this is not needed 
anymore (unlike winpty) because the ConPTY performs the adaptation.

Terminal reports "\033[6n" and "\033[0c" are apparently emulated and 
sent in reverse order;
response to primary device attribute request is wrong (not what mintty 
would report).
There is no response to secondary device attribute request ("\033[>0c") 
and others,
e.g. '\033[18t' or '\033]10;?\033\' and many others; why can't these 
requests be passed to the terminal and handled transparently? (You 
explained something around handles before but I don't get the point.)

Output to alternate screen seems to be forced to the primary screen, mostly;
if I try to switch screen in various ways (menu, echo "\033[?1047h" > 
/dev/pty1, before or while cmd.exe runs),
behaviour appears to be inconsistent and not as expected (expected 
behaviour is that any output goes to the active screen). Again, I don't 
see a need that you intercept this at all.

Thomas

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

* Re: [PATCH v3 0/1] Pseudo console support in PTY (v3)
  2019-04-06 21:33             ` [PATCH v3 0/1] Pseudo console support in PTY (v3) Thomas Wolff
@ 2019-04-07  5:05               ` Takashi Yano
  2019-04-07 12:02                 ` Takashi Yano
  2019-04-07 21:21                 ` Thomas Wolff
  0 siblings, 2 replies; 53+ messages in thread
From: Takashi Yano @ 2019-04-07  5:05 UTC (permalink / raw)
  To: cygwin-developers

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

Hi Thomas,

Thank you very much for testing.

On Sat, 6 Apr 2019 23:33:04 +0200 Thomas Wolff wrote:
> Character output conversion works with my test program (using 
> WriteConsoleW). Using java, output works too (with proper java encoding 
> option), but non-ASCII input characters are replaced by and echoed as space.

Indeed... I will fix it.

> If I try a non-Unicode terminal session, e.g. running mintty -o Locale=C 
> -o Charset=CP1252 -, and enter a € key, mintty hangs. While you said it 
> doesn't work (yet), it should better not hang at least. I'd like to 
> repeat at this point that the whole mechanism should only be applied if 
> a non-cygwin program is run. (If it were, entering € in bash would not 
> block the terminal.)

OK. I will check why this happens.

> Do you actively synchronize anything? I thought this is not needed 
> anymore (unlike winpty) because the ConPTY performs the adaptation.

I don't think so.

> Terminal reports "\033[6n" and "\033[0c" are apparently emulated and 
> sent in reverse order;
> response to primary device attribute request is wrong (not what mintty 
> would report).
> There is no response to secondary device attribute request ("\033[>0c") 
> and others,
> e.g. '\033[18t' or '\033]10;?\033\' and many others; why can't these 
> requests be passed to the terminal and handled transparently? (You 
> explained something around handles before but I don't get the point.)

This is not caused by my intercept, but pseudo console itself.
Unfortunately, pseudo console is not transparent at all. Pseudo
console interprets VT100 sequences in output and processes them.
Then, it fowards another VT100 sequences to the pipe associated
with the pseudo console.

> Output to alternate screen seems to be forced to the primary screen, mostly;
> if I try to switch screen in various ways (menu, echo "\033[?1047h" > 
> /dev/pty1, before or while cmd.exe runs),
> behaviour appears to be inconsistent and not as expected (expected 
> behaviour is that any output goes to the active screen).

Hmmm, this may be caused by "synchronization". I will also check
this.

> Again, I don't see a need that you intercept this at all.

The patch attached disables all my intercept. It is against v3.
Please check the output from PTY using script command with this
patch. You can use your test case for the test. You will find many
VT100 ESC secuenses you don't write to console. These are written
by pseudo console itself.

Also, you can confirm screen layout is broken if you run cygwin
apps and native console apps alternately. "Synchronization" is
necessary to avoid this, I think.

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

[-- Attachment #2: for-Thomas.patch --]
[-- Type: application/octet-stream, Size: 1650 bytes --]

diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index 5ce634305..72b2b0141 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -640,6 +640,7 @@ fhandler_pty_slave::init (HANDLE h, DWORD a, mode_t)
 void
 fhandler_pty_slave::push_to_pcon_screenbuffer (const char *ptr, size_t len)
 {
+  return;
   DWORD pidRestore = 0;
   /* If not attached pseudo console yet, try to attach temporally. */
   if (!pcon_attached)
@@ -1779,7 +1780,7 @@ fhandler_pty_slave::fixup_after_fork (HANDLE parent)
 		SetConsoleCP (65001);
 		SetConsoleOutputCP (65001);
 	      }
-#if 1 /* Experimental code */
+#if 0 /* Experimental code */
 	    /* Clear screen to synchronize pseudo console screen buffer
 	       with real terminal. */
 	    /* FIXME: Clearing sequence may not be "^[[H^[[J"
@@ -1816,7 +1817,7 @@ fhandler_pty_slave::fixup_after_exec ()
 		SetConsoleCP (65001);
 		SetConsoleOutputCP (65001);
 	      }
-#if 1 /* Experimental code */
+#if 0 /* Experimental code */
 	    /* Clear screen to synchronize pseudo console screen buffer
 	       with real terminal. */
 	    /* FIXME: Clearing sequence may not be "^[[H^[[J"
@@ -2010,6 +2011,7 @@ fhandler_pty_master::pty_master_fwd_thread ()
 	  if (get_ttyp ()->switch_to_pcon == 1)
 	    get_ttyp ()->switch_to_pcon = 2;
 	  ReleaseMutex (input_mutex);
+#if 0
 	  /* Avoid duplicating slave output which is already sent to
 	     to_master_cyg */
 	  if (get_ttyp ()->switch_to_pcon != 2)
@@ -2060,6 +2062,7 @@ fhandler_pty_master::pty_master_fwd_thread ()
 	      wlen = (rlen -= written);
 	    }
 	  continue;
+#endif
 	}
       while (rlen>0)
 	{

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

* Re: [PATCH v3 0/1] Pseudo console support in PTY (v3)
  2019-04-07  5:05               ` Takashi Yano
@ 2019-04-07 12:02                 ` Takashi Yano
  2019-04-07 21:21                 ` Thomas Wolff
  1 sibling, 0 replies; 53+ messages in thread
From: Takashi Yano @ 2019-04-07 12:02 UTC (permalink / raw)
  To: cygwin-developers

On Sun, 7 Apr 2019 14:05:35 +0900 Takashi Yano wrote:
> On Sat, 6 Apr 2019 23:33:04 +0200 Thomas Wolff wrote:
> > Character output conversion works with my test program (using 
> > WriteConsoleW). Using java, output works too (with proper java encoding 
> > option), but non-ASCII input characters are replaced by and echoed as space.
> Indeed... I will fix it.

Once, I thought I could reproduce, however, I cannot reproduce this
now. Could you please let me know how you tested.

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

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

* Re: [PATCH v3 0/1] Pseudo console support in PTY (v3)
  2019-04-07  5:05               ` Takashi Yano
  2019-04-07 12:02                 ` Takashi Yano
@ 2019-04-07 21:21                 ` Thomas Wolff
  1 sibling, 0 replies; 53+ messages in thread
From: Thomas Wolff @ 2019-04-07 21:21 UTC (permalink / raw)
  To: cygwin-developers, Takashi Yano

Am 07.04.2019 um 07:05 schrieb Takashi Yano:
> Hi Thomas,
>
> Thank you very much for testing.
You're welcome; I'm very interested in this solution, not only, but also 
because the interoperability problems cause a series of problem reports 
to mintty.

> On Sat, 6 Apr 2019 23:33:04 +0200 Thomas Wolff wrote:
> ...
>> Terminal reports "\033[6n" and "\033[0c" are apparently emulated and
>> sent in reverse order;
>> response to primary device attribute request is wrong (not what mintty
>> would report).
>> There is no response to secondary device attribute request ("\033[>0c")
>> and others,
>> e.g. '\033[18t' or '\033]10;?\033\' and many others; why can't these
>> requests be passed to the terminal and handled transparently? (You
>> explained something around handles before but I don't get the point.)
> This is not caused by my intercept, but pseudo console itself.
> Unfortunately, pseudo console is not transparent at all. Pseudo
> console interprets VT100 sequences in output and processes them.
> Then, it fowards another VT100 sequences to the pipe associated
> with the pseudo console.
OK, understood. So ConPTY is not only a pty adapter but also a kind of 
half-terminal emulator, like tmux or screen.

>
>> Output to alternate screen seems to be forced to the primary screen, mostly;
>> if I try to switch screen in various ways (menu, echo "\033[?1047h" >
>> /dev/pty1, before or while cmd.exe runs),
>> behaviour appears to be inconsistent and not as expected (expected
>> behaviour is that any output goes to the active screen).
> Hmmm, this may be caused by "synchronization". I will also check this.
>
>> Again, I don't see a need that you intercept this at all.
> The patch attached disables all my intercept. It is against v3.
> Please check the output from PTY using script command with this
> patch. You can use your test case for the test. You will find many
> VT100 ESC secuenses you don't write to console. These are written
> by pseudo console itself.
OK, I may find time to analyse that. Meanwhile, my test case for the 
above unfortunately still fails with the "disabling" patch:
Start mintty, run `tty`, let's say it says /dev/pty0.
In mintty, run cmd.
In another shell (mintty or not), run echo "\033[?1047h" > /dev/pty0.
In mintty, in cmd, type dir... nothing happens. "dir" will be echoed and 
its output shown in unpatched cygwin.

> Also, you can confirm screen layout is broken if you run cygwin
> apps and native console apps alternately. "Synchronization" is
> necessary to avoid this, I think.
I have not observed any "broken" screen layout, but I notice a cleared 
screen when starting cmd (does not clear in unpatched cygwin). Other 
non-cygwin programs, like java, do not clear the screen. Do you have an 
explanation?

> On Sat, 6 Apr 2019 23:33:04 +0200 Thomas Wolff wrote:
>> Character output conversion works with my test program (using
>> WriteConsoleW). Using java, output works too (with proper java encoding
>> option), but non-ASCII input characters are replaced by and echoed as space.
> Indeed... I will fix it.
> Once, I thought I could reproduce, however, I cannot reproduce this
> now. Could you please let me know how you tested.
I'm attaching my Java test program below.
I compile it with javac -encoding UTF-8 and run it with java 
-Dfile.encoding=UTF-8. This makes non-ASCII output work in a UTF-8 
terminal. Non-ASCII input however is blanked.

Kind regards,
Thomas


import java.io.*;

public class JavaPW
{
   public static void main(String args[]) throws IOException
   {
     System.out.println("Writing Unicode: bäh 3€");

     BufferedReader rd = new BufferedReader(new 
InputStreamReader(System.in, "UTF-8"));
     System.out.println("Reading input from System.in");
     System.out.println("Please enter your input: ");
     String input = rd.readLine();
     System.out.println("User Input from System.in: " + input);

     Console console = System.console();
     System.out.println("Reading input from console");
     System.out.println("Please enter your input: ");
     input = console.readLine();
     System.out.println("User Input from console: " + input);

     System.out.println("Reading password from Console in Java: ");
     //password will not be echoed to console and stored in char array
     char[] password = console.readPassword();
     System.out.println("Password entered by user: " + new 
String(password));
   }
}

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

* [PATCH v4 0/1] Pseudo console support in PTY (v4)
  2019-04-06 17:43             ` [PATCH v3 0/1] Pseudo console support in PTY (v3) Corinna Vinschen
@ 2019-04-12 10:22               ` Takashi Yano
  2019-04-12 10:23                 ` [PATCH v4 1/1] Cygwin: pty: add pseudo console support Takashi Yano
                                   ` (2 more replies)
  0 siblings, 3 replies; 53+ messages in thread
From: Takashi Yano @ 2019-04-12 10:22 UTC (permalink / raw)
  To: cygwin-developers; +Cc: Takashi Yano

Hi Thomas and Corinna,

I revised the patch to v4.

Major chages from v3:
(1) Refine the algorithm for switching I/O between cygwin apps and
    native console apps. This resolves the following issues.
  - Output from native console app cannot be displayed if the app is
    executed in the background. This is because the I/O is switched
    to cygwin shell.
  - When a native console app is foreground, it lose input if you
    execute:
    echo something > /dev/pty*
    from another terminal.
  - The native console app, which just read input without any output,
    fails.
(2) Add error handlings when attaching to pseudo console fails.
(3) The timing of terminating cygwin-console-helper is changed.
    Previously, it was when the pty master opened first was closed.
    Now, it is when the last pty master is closed.
(4) Clear-screen for "synchronization" is limited only to the first
    attach of pty slave.
(5) Procedures after attaching to pseudo console are moved into
    the function: fixup_after_attach().
(6) Avoid that the code page is set to 65001 even when the locale
    is not *.UTF-8. This fixes the issue that shell freezes when
    non ascii chars are typed.
(7) As a test, call init_console_handler() at attaching to pseudo
    console according to Corinna's suggestion.

On Sat, 6 Apr 2019 23:33:04 +0200 Thomas Wolff wrote:
> If I try a non-Unicode terminal session, e.g. running mintty -o Locale=C 
> -o Charset=CP1252 -, and enter a € key, mintty hangs. While you said it 
> doesn't work (yet), it should better not hang at least.

This is resolved by (6).


On Sat, 6 Apr 2019 23:33:04 +0200 Thomas Wolff wrote:
> Output to alternate screen seems to be forced to the primary screen, mostly;
> if I try to switch screen in various ways (menu, echo "\033[?1047h" > 
> /dev/pty1, before or while cmd.exe runs),
> behaviour appears to be inconsistent and not as expected (expected 
> behaviour is that any output goes to the active screen). Again, I don't 
> see a need that you intercept this at all.

This is resolved by (1).


On Sat, 6 Apr 2019 23:33:04 +0200 Thomas Wolff wrote:
> Character output conversion works with my test program (using 
> WriteConsoleW). Using java, output works too (with proper java encoding 
> option), but non-ASCII input characters are replaced by and echoed as space.

Thanks for the test case. This can be reproduced, but the behaviour
is exactly the same as that in the command prompt, in the both cases
(a) and (b) below. So I think it is not the problem of implementation
of pty.

(a)
chcp 65001
java -Dfile.encoding=UTF-8 YourProg

(b)
chcp 1252
java -Dfile.encoding=CP1252 YourProg


Regarding (7),

On Sat, 6 Apr 2019 19:43:35 +0200 Corinna Vinschen wrote:
> This looks better, but there's still some problem:
> $ cygport gawk.cygport build
> >>> Compiling gawk-4.2.62-1.x86_64
> [pressing Ctrl-C multiple times]
>      14 [main] chmod (7016) child_copy: cygheap read copy failed, 0x180376408..0
> x1803887A8, done 0, windows pid 7016, Win32 error 299
>     766 [main] chmod (7016) C:\cygwin64\bin\chmod.exe: *** fatal error - ccalloc would have returned NULL
>      14 [main] chmod (7016) child_copy: cygheap read copy failed, 0x180376408..0x1803887A8, done 0, windows pid 7016, Win32 error 299
>     766 [main] chmod (7016) C:\cygwin64\bin\chmod.exe: *** fatal error - ccalloc
>  would have returned NULL

I cannot reproduce this, but I added init_console_handler() call after
attaching to pseudo console for a test according to your suggestion.
Could you please test?


D=http://tyan0.dip.jp/cygwin
${D}/x86_64/test/cygwin1-20190412.dll.xz
${D}/x86_64/test/cygwin-console-helper.exe.xz
${D}/x86/test/cygwin1-20190412.dll.xz
${D}/x86/test/cygwin-console-helper.exe.xz


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

 winsup/cygwin/dtable.cc               |  57 +++
 winsup/cygwin/fhandler.h              |  42 +-
 winsup/cygwin/fhandler_console.cc     |  32 ++
 winsup/cygwin/fhandler_tty.cc         | 671 ++++++++++++++++++++++++--
 winsup/cygwin/fork.cc                 |  24 +
 winsup/cygwin/select.cc               |  22 +-
 winsup/cygwin/spawn.cc                |  55 +++
 winsup/cygwin/tty.cc                  |   7 +
 winsup/cygwin/tty.h                   |  23 +-
 winsup/utils/cygwin-console-helper.cc |  14 +-
 10 files changed, 903 insertions(+), 44 deletions(-)

-- 
2.17.0

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

* [PATCH v4 1/1] Cygwin: pty: add pseudo console support.
  2019-04-12 10:22               ` [PATCH v4 0/1] Pseudo console support in PTY (v4) Takashi Yano
@ 2019-04-12 10:23                 ` Takashi Yano
  2019-04-12 12:29                 ` [PATCH v4 0/1] Pseudo console support in PTY (v4) Corinna Vinschen
  2019-04-14 15:23                 ` [PATCH v5 0/1] Pseudo console support in PTY (v5) Takashi Yano
  2 siblings, 0 replies; 53+ messages in thread
From: Takashi Yano @ 2019-04-12 10:23 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               |  57 +++
 winsup/cygwin/fhandler.h              |  42 +-
 winsup/cygwin/fhandler_console.cc     |  32 ++
 winsup/cygwin/fhandler_tty.cc         | 671 ++++++++++++++++++++++++--
 winsup/cygwin/fork.cc                 |  24 +
 winsup/cygwin/select.cc               |  22 +-
 winsup/cygwin/spawn.cc                |  55 +++
 winsup/cygwin/tty.cc                  |   7 +
 winsup/cygwin/tty.h                   |  23 +-
 winsup/utils/cygwin-console-helper.cc |  14 +-
 10 files changed, 903 insertions(+), 44 deletions(-)

diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
index 636221a77..0aaddab95 100644
--- a/winsup/cygwin/dtable.cc
+++ b/winsup/cygwin/dtable.cc
@@ -147,6 +147,42 @@ dtable::get_debugger_info ()
 void
 dtable::stdio_init ()
 {
+#if 0 /* Experimental code */
+  /* This is workaround for the issue:
+     https://www.cygwin.com/ml/cygwin-developers/2019-04/msg00000.html
+     when pseudo console is enabled. */
+  fhandler_console::need_invisible ();
+#endif
+
+  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;
+		}
+	    }
+	}
+    }
+  if (need_fixup_handle)
+    goto fixup_handle;
+
   if (myself->cygstarted || ISSTATE (myself, PID_CYGPARENT))
     {
       tty_min *t = cygwin_shared->tty.get_cttyp ();
@@ -155,6 +191,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 bc66377cd..c31ae7230 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1994,6 +1994,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 *) {}
 
@@ -2026,14 +2027,15 @@ 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), pcon_attached (false)
   {
     pc.file_attributes (FILE_ATTRIBUTE_NORMAL);
   }
   static const unsigned pipesize = 128 * 1024;
   HANDLE output_mutex, input_mutex;
   HANDLE input_available_event;
+  bool pcon_attached;
 
   bool use_archetype () const {return true;}
   DWORD __acquire_output_mutex (const char *fn, int ln, DWORD ms);
@@ -2064,14 +2066,28 @@ 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;
 
   /* Helper functions for fchmod and fchown. */
   bool fch_open_handles (bool chown);
@@ -2084,6 +2100,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);
@@ -2124,6 +2142,14 @@ class fhandler_pty_slave: public fhandler_pty_common
     copyto (fh);
     return fh;
   }
+  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))
@@ -2132,17 +2158,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 ();
@@ -2187,6 +2213,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 e3656a33a..335467b0b 100644
--- a/winsup/cygwin/fhandler_console.cc
+++ b/winsup/cygwin/fhandler_console.cc
@@ -1028,6 +1028,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;
@@ -3082,6 +3095,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..9e07ff311 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -25,6 +25,21 @@ details. */
 #include "child_info.h"
 #include <asm/socket.h>
 #include "cygwait.h"
+#include "tls_pbuf.h"
+
+/* 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 */
+
+extern "C" int sscanf (const char *, const char *, ...);
+extern "C" int ttyname_r(int, char*, size_t);
 
 #define close_maybe(h) \
   do { \
@@ -39,6 +54,7 @@ struct pipe_request {
 
 struct pipe_reply {
   HANDLE from_master;
+  HANDLE from_master_cyg;
   HANDLE to_master;
   HANDLE to_master_cyg;
   DWORD error;
@@ -67,7 +83,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 +251,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 +312,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;
@@ -340,11 +356,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 +415,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 +460,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 +502,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 +514,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);
 
@@ -548,11 +582,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 +639,165 @@ fhandler_pty_slave::init (HANDLE h, DWORD a, mode_t)
   return ret;
 }
 
+void
+fhandler_pty_slave::reset_switch_to_pcon (void)
+{
+  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. */
+    return;
+  if (get_ttyp ()->switch_to_pcon &&
+      get_ttyp ()->pcon_pid != myself->pid)
+    Sleep (20);
+  get_ttyp ()->pcon_pid = 0;
+  get_ttyp ()->switch_to_pcon = false;
+}
+
+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 ("pcon_attach mismatch??????");
+	pcon_attached = false;
+      }
+  /* If not attached pseudo console yet, try to attach temporally. */
+  if (!pcon_attached)
+    {
+      if (has_master_opened () && get_ttyp ()->attach_pcon_in_fork)
+	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 ()))
+	goto detach;
+    }
+  char *buf;
+  size_t nlen;
+  if (GetConsoleOutputCP () != 65001 &&
+      strstr (setlocale (LC_CTYPE, NULL), "UTF-8"))
+    {
+      /* Code page conversion from UTF-8 to OEM code page */
+      /* FIXME: The terminal code may not be UTF-8. */
+      size_t wlen =
+	MultiByteToWideChar (CP_UTF8, 0, (char *)ptr, len, NULL, 0);
+      wchar_t *wbuf = (wchar_t *)
+	HeapAlloc (GetProcessHeap (), 0, wlen * sizeof (wchar_t));
+      wlen =
+	MultiByteToWideChar (CP_UTF8, 0, (char *)ptr, len, wbuf, wlen);
+      nlen = WideCharToMultiByte (CP_OEMCP, 0,
+				  wbuf, wlen, NULL, 0, NULL, NULL);
+      buf = (char *) HeapAlloc (GetProcessHeap (), 0, nlen);
+      nlen = WideCharToMultiByte (CP_OEMCP, 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;
+    }
+  /* Remove alternate screen buffer drawing */
+  char *p0, *p1;
+  p0 = p1 = buf;
+  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;
+  while (written <  nlen)
+    {
+      WriteFile (get_output_handle (), p, nlen - written, &wLen, NULL);
+      written += wLen;
+      p += wLen;
+    }
+  /* Detach from pseudo console and resume. */
+  SetConsoleMode (get_output_handle (), dwMode);
+cleanup:
+  HeapFree (GetProcessHeap (), 0, buf);
+detach:
+  if (!pcon_attached)
+    {
+      FreeConsole ();
+      if (!AttachConsole (pidRestore))
+	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,6 +811,16 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 
   push_process_state process_state (PID_TTYOU);
 
+  reset_switch_to_pcon ();
+
+  /* Push slave output to pseudo console screen buffer */
+  if (getPseudoConsole ())
+    {
+      acquire_output_mutex (INFINITE);
+      push_to_pcon_screenbuffer ((char *)ptr, len);
+      release_output_mutex ();
+    }
+
   if (!process_opost_output (get_output_handle_cyg (), ptr, towrite, false))
     {
       DWORD err = GetLastError ();
@@ -629,6 +841,7 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 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 +857,13 @@ 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);
 
+  mask_switch_to_pcon (true);
+  reset_switch_to_pcon ();
+
   if (is_nonblocking () || !ptr) /* Indicating tcflush(). */
     time_to_wait = 0;
   else if ((get_ttyp ()->ti.c_lflag & ICANON))
@@ -757,6 +973,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 +994,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 +1078,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 +1117,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 +1125,7 @@ fhandler_pty_slave::tcgetattr (struct termios *t)
 int
 fhandler_pty_slave::tcsetattr (int, const struct termios *t)
 {
+  reset_switch_to_pcon ();
   acquire_output_mutex (INFINITE);
   get_ttyp ()->ti = *t;
   release_output_mutex ();
@@ -908,7 +1137,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 +1160,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 +1227,54 @@ 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 ()))
+		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))
+		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 +1508,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 +1522,11 @@ fhandler_pty_master::fhandler_pty_master (int unit)
   set_name ("/dev/ptmx");
 }
 
+fhandler_pty_master::~fhandler_pty_master ()
+{
+  close ();
+}
+
 int
 fhandler_pty_master::open (int flags, mode_t)
 {
@@ -1285,7 +1571,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 +1581,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 +1623,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 +1654,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 +1693,17 @@ 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)
+    {
+      DWORD wLen;
+      WriteFile (to_slave, ptr, len, &wLen, NULL);
+      return wLen;
+    }
+
   line_edit_status status = line_edit (p++, len, ti, &ret);
   if (status > line_edit_signalled && status != line_edit_pipe_full)
     ret = -1;
@@ -1443,6 +1771,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 +1799,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 +1835,59 @@ 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, "");
+#if 1
+	      if (strstr (setlocale (LC_CTYPE, NULL), "UTF-8"))
+		{
+		  /* FIXME: The terminal code may not be UTF-8. */
+		  SetConsoleCP (65001);
+		  SetConsoleOutputCP (65001);
+		}
+#endif
+#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)
+		/* 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)
+    get_ttyp ()->switch_to_pcon = true;
+  if (pcon_attached && native_maybe &&
+      (get_ttyp ()->pcon_pid == 0 || kill (get_ttyp ()->pcon_pid, 0) != 0))
+    get_ttyp ()->pcon_pid = myself->pid;
+#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 +1898,11 @@ fhandler_pty_slave::fixup_after_exec ()
 {
   if (!close_on_exec ())
     fixup_after_fork (NULL);
+  if (get_ttyp ()->pcon_pid == myself->pid)
+    {
+      get_ttyp ()->switch_to_pcon = false;
+      get_ttyp ()->pcon_pid = 0;
+    }
 }
 
 /* This thread function handles the master control pipe.  It waits for a
@@ -1544,7 +1940,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 +2010,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))
@@ -1659,23 +2062,78 @@ fhandler_pty_master::pty_master_fwd_thread ()
   DWORD rlen;
   char outbuf[OUT_BUFFER_SIZE];
 
-  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;
+	      }
+
+	  /* 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);
+	    }
+	  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 +2145,141 @@ 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;
+#if 0
+  PSECURITY_ATTRIBUTES sa = sec_user((PSECURITY_ATTRIBUTES) alloca (1024),
+				     cygheap->user.sid (),
+				     well_known_world_sid,
+				     GENERIC_READ | GENERIC_WRITE);
+#endif
+  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 +2288,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 +2313,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 +2322,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 +2330,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 +2395,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 +2408,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 +2442,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 +2465,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
diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc
index 7e1c08990..4d4b000f0 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 85242ec06..495754026 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 ())
 	    {
@@ -1176,17 +1179,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 579b3c9c3..8f9bcb68e 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;
@@ -867,6 +915,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/tty.cc b/winsup/cygwin/tty.cc
index ad46cb312..64bbf2542 100644
--- a/winsup/cygwin/tty.cc
+++ b/winsup/cygwin/tty.cc
@@ -234,7 +234,14 @@ 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;
 }
 
 HANDLE
diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h
index 9aee43b9c..fd8bee569 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,27 @@ 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;
 
 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 +132,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 8f62ed7e6..bef143f96 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) strtoul (argv[1], &end, 0);
   SetEvent (h);
+  if (argc == 4) /* Pseudo console helper mode for PTY */
+    {
+      HANDLE hPipe = (HANDLE) strtoul (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) strtoul (argv[2], &end, 0);
   WaitForSingleObject (h, INFINITE);
   exit (0);
-- 
2.17.0

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

* Re: [PATCH v4 0/1] Pseudo console support in PTY (v4)
  2019-04-12 10:22               ` [PATCH v4 0/1] Pseudo console support in PTY (v4) Takashi Yano
  2019-04-12 10:23                 ` [PATCH v4 1/1] Cygwin: pty: add pseudo console support Takashi Yano
@ 2019-04-12 12:29                 ` Corinna Vinschen
  2019-04-15  8:18                   ` Corinna Vinschen
  2019-04-14 15:23                 ` [PATCH v5 0/1] Pseudo console support in PTY (v5) Takashi Yano
  2 siblings, 1 reply; 53+ messages in thread
From: Corinna Vinschen @ 2019-04-12 12:29 UTC (permalink / raw)
  To: cygwin-developers

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

On Apr 12 19:20, Takashi Yano wrote:
> Hi Thomas and Corinna,
> 
> I revised the patch to v4.
> [...]
> On Sat, 6 Apr 2019 19:43:35 +0200 Corinna Vinschen wrote:
> > This looks better, but there's still some problem:
> > $ cygport gawk.cygport build
> > >>> Compiling gawk-4.2.62-1.x86_64
> > [pressing Ctrl-C multiple times]
> >      14 [main] chmod (7016) child_copy: cygheap read copy failed, 0x180376408..0
> > x1803887A8, done 0, windows pid 7016, Win32 error 299
> >     766 [main] chmod (7016) C:\cygwin64\bin\chmod.exe: *** fatal error - ccalloc would have returned NULL
> >      14 [main] chmod (7016) child_copy: cygheap read copy failed, 0x180376408..0x1803887A8, done 0, windows pid 7016, Win32 error 299
> >     766 [main] chmod (7016) C:\cygwin64\bin\chmod.exe: *** fatal error - ccalloc
> >  would have returned NULL
> 
> I cannot reproduce this, but I added init_console_handler() call after
> attaching to pseudo console for a test according to your suggestion.
> Could you please test?

Thanks!  Will do over the weekend.


Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* [PATCH v5 1/1] Cygwin: pty: add pseudo console support.
  2019-04-14 15:23                 ` [PATCH v5 0/1] Pseudo console support in PTY (v5) Takashi Yano
@ 2019-04-14 15:23                   ` Takashi Yano
  2019-04-15  8:38                     ` Corinna Vinschen
  2019-04-14 16:06                   ` [PATCH v5 0/1] Pseudo console support in PTY (v5) Takashi Yano
  1 sibling, 1 reply; 53+ messages in thread
From: Takashi Yano @ 2019-04-14 15:23 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               |  57 +++
 winsup/cygwin/fhandler.h              |  42 +-
 winsup/cygwin/fhandler_console.cc     |  32 ++
 winsup/cygwin/fhandler_tty.cc         | 684 ++++++++++++++++++++++++--
 winsup/cygwin/fork.cc                 |  24 +
 winsup/cygwin/select.cc               |  22 +-
 winsup/cygwin/spawn.cc                |  55 +++
 winsup/cygwin/strace.cc               |  24 +
 winsup/cygwin/tty.cc                  |   7 +
 winsup/cygwin/tty.h                   |  23 +-
 winsup/utils/cygwin-console-helper.cc |  14 +-
 11 files changed, 940 insertions(+), 44 deletions(-)

diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
index 636221a77..0aaddab95 100644
--- a/winsup/cygwin/dtable.cc
+++ b/winsup/cygwin/dtable.cc
@@ -147,6 +147,42 @@ dtable::get_debugger_info ()
 void
 dtable::stdio_init ()
 {
+#if 0 /* Experimental code */
+  /* This is workaround for the issue:
+     https://www.cygwin.com/ml/cygwin-developers/2019-04/msg00000.html
+     when pseudo console is enabled. */
+  fhandler_console::need_invisible ();
+#endif
+
+  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;
+		}
+	    }
+	}
+    }
+  if (need_fixup_handle)
+    goto fixup_handle;
+
   if (myself->cygstarted || ISSTATE (myself, PID_CYGPARENT))
     {
       tty_min *t = cygwin_shared->tty.get_cttyp ();
@@ -155,6 +191,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 bc66377cd..c31ae7230 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1994,6 +1994,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 *) {}
 
@@ -2026,14 +2027,15 @@ 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), pcon_attached (false)
   {
     pc.file_attributes (FILE_ATTRIBUTE_NORMAL);
   }
   static const unsigned pipesize = 128 * 1024;
   HANDLE output_mutex, input_mutex;
   HANDLE input_available_event;
+  bool pcon_attached;
 
   bool use_archetype () const {return true;}
   DWORD __acquire_output_mutex (const char *fn, int ln, DWORD ms);
@@ -2064,14 +2066,28 @@ 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;
 
   /* Helper functions for fchmod and fchown. */
   bool fch_open_handles (bool chown);
@@ -2084,6 +2100,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);
@@ -2124,6 +2142,14 @@ class fhandler_pty_slave: public fhandler_pty_common
     copyto (fh);
     return fh;
   }
+  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))
@@ -2132,17 +2158,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 ();
@@ -2187,6 +2213,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 e3656a33a..335467b0b 100644
--- a/winsup/cygwin/fhandler_console.cc
+++ b/winsup/cygwin/fhandler_console.cc
@@ -1028,6 +1028,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;
@@ -3082,6 +3095,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..14165bdfc 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -25,6 +25,21 @@ details. */
 #include "child_info.h"
 #include <asm/socket.h>
 #include "cygwait.h"
+#include "tls_pbuf.h"
+
+/* 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 */
+
+extern "C" int sscanf (const char *, const char *, ...);
+extern "C" int ttyname_r(int, char*, size_t);
 
 #define close_maybe(h) \
   do { \
@@ -39,6 +54,7 @@ struct pipe_request {
 
 struct pipe_reply {
   HANDLE from_master;
+  HANDLE from_master_cyg;
   HANDLE to_master;
   HANDLE to_master_cyg;
   DWORD error;
@@ -67,7 +83,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 +251,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 +312,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;
@@ -340,11 +356,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 +415,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 +460,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 +502,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 +514,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);
 
@@ -548,11 +582,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 +639,177 @@ fhandler_pty_slave::init (HANDLE h, DWORD a, mode_t)
   return ret;
 }
 
+void
+fhandler_pty_slave::reset_switch_to_pcon (void)
+{
+  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. */
+    return;
+  if (get_ttyp ()->switch_to_pcon &&
+      get_ttyp ()->pcon_pid != myself->pid)
+    Sleep (20);
+  get_ttyp ()->pcon_pid = 0;
+  get_ttyp ()->switch_to_pcon = false;
+}
+
+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 ("pcon_attach mismatch??????");
+	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 ()))
+	goto detach;
+    }
+  char *buf;
+  size_t nlen;
+  if (GetConsoleOutputCP () != 65001 &&
+      strstr (setlocale (LC_CTYPE, NULL), "UTF-8"))
+    {
+      /* Code page conversion from UTF-8 to OEM code page */
+      /* FIXME: The terminal code may not be UTF-8. */
+      size_t wlen =
+	MultiByteToWideChar (CP_UTF8, 0, (char *)ptr, len, NULL, 0);
+      wchar_t *wbuf = (wchar_t *)
+	HeapAlloc (GetProcessHeap (), 0, wlen * sizeof (wchar_t));
+      wlen =
+	MultiByteToWideChar (CP_UTF8, 0, (char *)ptr, len, wbuf, wlen);
+      nlen = WideCharToMultiByte (CP_OEMCP, 0,
+				  wbuf, wlen, NULL, 0, NULL, NULL);
+      buf = (char *) HeapAlloc (GetProcessHeap (), 0, nlen);
+      nlen = WideCharToMultiByte (CP_OEMCP, 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;
+    }
+  /* Remove alternate screen buffer drawing */
+  char *p0, *p1;
+  p0 = p1 = buf;
+  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;
+  while (written <  nlen)
+    {
+      if (!WriteFile (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:
+  HeapFree (GetProcessHeap (), 0, buf);
+detach:
+  if (!pcon_attached)
+    {
+      FreeConsole ();
+      if (!AttachConsole (pidRestore))
+	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,6 +823,16 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 
   push_process_state process_state (PID_TTYOU);
 
+  reset_switch_to_pcon ();
+
+  /* Push slave output to pseudo console screen buffer */
+  if (getPseudoConsole ())
+    {
+      acquire_output_mutex (INFINITE);
+      push_to_pcon_screenbuffer ((char *)ptr, len);
+      release_output_mutex ();
+    }
+
   if (!process_opost_output (get_output_handle_cyg (), ptr, towrite, false))
     {
       DWORD err = GetLastError ();
@@ -629,6 +853,7 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 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 +869,13 @@ 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);
 
+  mask_switch_to_pcon (true);
+  reset_switch_to_pcon ();
+
   if (is_nonblocking () || !ptr) /* Indicating tcflush(). */
     time_to_wait = 0;
   else if ((get_ttyp ()->ti.c_lflag & ICANON))
@@ -757,6 +985,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 +1006,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 +1090,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 +1129,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 +1137,7 @@ fhandler_pty_slave::tcgetattr (struct termios *t)
 int
 fhandler_pty_slave::tcsetattr (int, const struct termios *t)
 {
+  reset_switch_to_pcon ();
   acquire_output_mutex (INFINITE);
   get_ttyp ()->ti = *t;
   release_output_mutex ();
@@ -908,7 +1149,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 +1172,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 +1239,54 @@ 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 ())
+		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 ()))
+		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))
+		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 +1520,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 +1534,12 @@ fhandler_pty_master::fhandler_pty_master (int unit)
   set_name ("/dev/ptmx");
 }
 
+fhandler_pty_master::~fhandler_pty_master ()
+{
+  if (to_master && from_master)
+    close ();
+}
+
 int
 fhandler_pty_master::open (int flags, mode_t)
 {
@@ -1285,7 +1584,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 +1594,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 +1636,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 +1667,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 +1706,17 @@ 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)
+    {
+      DWORD wLen;
+      WriteFile (to_slave, ptr, len, &wLen, NULL);
+      return wLen;
+    }
+
   line_edit_status status = line_edit (p++, len, ti, &ret);
   if (status > line_edit_signalled && status != line_edit_pipe_full)
     ret = -1;
@@ -1443,6 +1784,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 +1812,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 +1848,59 @@ 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, "");
+#if 1
+	      if (strstr (setlocale (LC_CTYPE, NULL), "UTF-8"))
+		{
+		  /* FIXME: The terminal code may not be UTF-8. */
+		  SetConsoleCP (65001);
+		  SetConsoleOutputCP (65001);
+		}
+#endif
+#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)
+		/* 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)
+    get_ttyp ()->switch_to_pcon = true;
+  if (pcon_attached && native_maybe &&
+      (get_ttyp ()->pcon_pid == 0 || kill (get_ttyp ()->pcon_pid, 0) != 0))
+    get_ttyp ()->pcon_pid = myself->pid;
+#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 +1911,11 @@ fhandler_pty_slave::fixup_after_exec ()
 {
   if (!close_on_exec ())
     fixup_after_fork (NULL);
+  if (get_ttyp ()->pcon_pid == myself->pid)
+    {
+      get_ttyp ()->switch_to_pcon = false;
+      get_ttyp ()->pcon_pid = 0;
+    }
 }
 
 /* This thread function handles the master control pipe.  It waits for a
@@ -1544,7 +1953,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 +2023,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))
@@ -1659,23 +2075,78 @@ fhandler_pty_master::pty_master_fwd_thread ()
   DWORD rlen;
   char outbuf[OUT_BUFFER_SIZE];
 
-  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;
+	      }
+
+	  /* 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);
+	    }
+	  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 +2158,141 @@ 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;
+#if 0
+  PSECURITY_ATTRIBUTES sa = sec_user((PSECURITY_ATTRIBUTES) alloca (1024),
+				     cygheap->user.sid (),
+				     well_known_world_sid,
+				     GENERIC_READ | GENERIC_WRITE);
+#endif
+  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 +2301,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 +2326,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 +2335,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 +2343,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 +2408,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 +2421,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 +2455,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 +2478,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
diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc
index 7e1c08990..4d4b000f0 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 85242ec06..495754026 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 ())
 	    {
@@ -1176,17 +1179,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 579b3c9c3..8f9bcb68e 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;
@@ -867,6 +915,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..64bbf2542 100644
--- a/winsup/cygwin/tty.cc
+++ b/winsup/cygwin/tty.cc
@@ -234,7 +234,14 @@ 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;
 }
 
 HANDLE
diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h
index 9aee43b9c..fd8bee569 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,27 @@ 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;
 
 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 +132,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 8f62ed7e6..bef143f96 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) strtoul (argv[1], &end, 0);
   SetEvent (h);
+  if (argc == 4) /* Pseudo console helper mode for PTY */
+    {
+      HANDLE hPipe = (HANDLE) strtoul (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) strtoul (argv[2], &end, 0);
   WaitForSingleObject (h, INFINITE);
   exit (0);
-- 
2.17.0

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

* [PATCH v5 0/1] Pseudo console support in PTY (v5)
  2019-04-12 10:22               ` [PATCH v4 0/1] Pseudo console support in PTY (v4) Takashi Yano
  2019-04-12 10:23                 ` [PATCH v4 1/1] Cygwin: pty: add pseudo console support Takashi Yano
  2019-04-12 12:29                 ` [PATCH v4 0/1] Pseudo console support in PTY (v4) Corinna Vinschen
@ 2019-04-14 15:23                 ` Takashi Yano
  2019-04-14 15:23                   ` [PATCH v5 1/1] Cygwin: pty: add pseudo console support Takashi Yano
  2019-04-14 16:06                   ` [PATCH v5 0/1] Pseudo console support in PTY (v5) Takashi Yano
  2 siblings, 2 replies; 53+ messages in thread
From: Takashi Yano @ 2019-04-14 15:23 UTC (permalink / raw)
  To: cygwin-developers; +Cc: Takashi Yano

Major chages from v4:
(1) Try to reopen pty slave on write error in
    push_to_pcon_screenbuffer().
(2) Avoid double close for pty master.
(3) Make system_printf() work again.

"mintty --help" in mintty window works again by (1).
In v3 and v4, system_printf() does not work. (3) fixes this issue.

Regarding (3), system_printf() write message to STD_ERROR_HANDLE,
which is associated with output_handle. However, the message written
to output_handle is shown only when the native console app is active.
Therefore, in v5, the messages is written to output_handle_cyg as well.

D=http://tyan0.dip.jp/cygwin
${D}/x86_64/test/cygwin1-20190414.dll.xz
${D}/x86_64/test/cygwin-console-helper.exe.xz
${D}/x86/test/cygwin1-20190414.dll.xz
${D}/x86/test/cygwin-console-helper.exe.xz


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

 winsup/cygwin/dtable.cc               |  57 +++
 winsup/cygwin/fhandler.h              |  42 +-
 winsup/cygwin/fhandler_console.cc     |  32 ++
 winsup/cygwin/fhandler_tty.cc         | 684 ++++++++++++++++++++++++--
 winsup/cygwin/fork.cc                 |  24 +
 winsup/cygwin/select.cc               |  22 +-
 winsup/cygwin/spawn.cc                |  55 +++
 winsup/cygwin/strace.cc               |  24 +
 winsup/cygwin/tty.cc                  |   7 +
 winsup/cygwin/tty.h                   |  23 +-
 winsup/utils/cygwin-console-helper.cc |  14 +-
 11 files changed, 940 insertions(+), 44 deletions(-)

-- 
2.17.0

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

* Re: [PATCH v5 0/1] Pseudo console support in PTY (v5)
  2019-04-14 15:23                 ` [PATCH v5 0/1] Pseudo console support in PTY (v5) Takashi Yano
  2019-04-14 15:23                   ` [PATCH v5 1/1] Cygwin: pty: add pseudo console support Takashi Yano
@ 2019-04-14 16:06                   ` Takashi Yano
  2019-04-16  1:49                     ` Takashi Yano
  1 sibling, 1 reply; 53+ messages in thread
From: Takashi Yano @ 2019-04-14 16:06 UTC (permalink / raw)
  To: cygwin-developers

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

v5 seems to need additional patch attached.

D=http://tyan0.dip.jp/cygwin
${D}/x86_64/test/cygwin1-20190415.dll.xz
${D}/x86_64/test/cygwin-console-helper.exe.xz
${D}/x86/test/cygwin1-20190415.dll.xz
${D}/x86/test/cygwin-console-helper.exe.xz

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

[-- Attachment #2: v5-additional.patch --]
[-- Type: application/octet-stream, Size: 836 bytes --]

diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index 14165bdfc..ce82c5793 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -667,7 +667,7 @@ fhandler_pty_slave::push_to_pcon_screenbuffer (const char *ptr, size_t len)
   /* If not attached pseudo console yet, try to attach temporally. */
   if (!pcon_attached)
     {
-      if (has_master_opened ())
+      if (has_master_opened () && get_ttyp ()->attach_pcon_in_fork)
 	return;
 
       pidRestore =
@@ -1246,7 +1246,7 @@ fhandler_pty_slave::ioctl (unsigned int cmd, void *arg)
 	  DWORD pidRestore = 0;
 	  if (!pcon_attached)
 	    {
-	      if (has_master_opened ())
+	      if (has_master_opened () && get_ttyp ()->attach_pcon_in_fork)
 		goto resize_cyg;
 
 	      pidRestore = fhandler_console::get_console_process_id

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

* Re: [PATCH v4 0/1] Pseudo console support in PTY (v4)
  2019-04-12 12:29                 ` [PATCH v4 0/1] Pseudo console support in PTY (v4) Corinna Vinschen
@ 2019-04-15  8:18                   ` Corinna Vinschen
  2019-04-15 23:17                     ` Takashi Yano
  0 siblings, 1 reply; 53+ messages in thread
From: Corinna Vinschen @ 2019-04-15  8:18 UTC (permalink / raw)
  To: cygwin-developers

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

On Apr 12 14:28, Corinna Vinschen wrote:
> On Apr 12 19:20, Takashi Yano wrote:
> > Hi Thomas and Corinna,
> > 
> > I revised the patch to v4.
> > [...]
> > On Sat, 6 Apr 2019 19:43:35 +0200 Corinna Vinschen wrote:
> > > This looks better, but there's still some problem:
> > > $ cygport gawk.cygport build
> > > >>> Compiling gawk-4.2.62-1.x86_64
> > > [pressing Ctrl-C multiple times]
> > >      14 [main] chmod (7016) child_copy: cygheap read copy failed, 0x180376408..0
> > > x1803887A8, done 0, windows pid 7016, Win32 error 299
> > >     766 [main] chmod (7016) C:\cygwin64\bin\chmod.exe: *** fatal error - ccalloc would have returned NULL
> > >      14 [main] chmod (7016) child_copy: cygheap read copy failed, 0x180376408..0x1803887A8, done 0, windows pid 7016, Win32 error 299
> > >     766 [main] chmod (7016) C:\cygwin64\bin\chmod.exe: *** fatal error - ccalloc
> > >  would have returned NULL
> > 
> > I cannot reproduce this, but I added init_console_handler() call after
> > attaching to pseudo console for a test according to your suggestion.
> > Could you please test?
> 
> Thanks!  Will do over the weekend.

I couldn't reproduce this anymore with v4.


Thanks,
Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: [PATCH v5 1/1] Cygwin: pty: add pseudo console support.
  2019-04-14 15:23                   ` [PATCH v5 1/1] Cygwin: pty: add pseudo console support Takashi Yano
@ 2019-04-15  8:38                     ` Corinna Vinschen
  2019-04-16  0:41                       ` Takashi Yano
  0 siblings, 1 reply; 53+ messages in thread
From: Corinna Vinschen @ 2019-04-15  8:38 UTC (permalink / raw)
  To: cygwin-developers

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

On Apr 15 00:23, 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.
> ---
>  winsup/cygwin/dtable.cc               |  57 +++
>  winsup/cygwin/fhandler.h              |  42 +-
>  winsup/cygwin/fhandler_console.cc     |  32 ++
>  winsup/cygwin/fhandler_tty.cc         | 684 ++++++++++++++++++++++++--
>  winsup/cygwin/fork.cc                 |  24 +
>  winsup/cygwin/select.cc               |  22 +-
>  winsup/cygwin/spawn.cc                |  55 +++
>  winsup/cygwin/strace.cc               |  24 +
>  winsup/cygwin/tty.cc                  |   7 +
>  winsup/cygwin/tty.h                   |  23 +-
>  winsup/utils/cygwin-console-helper.cc |  14 +-
>  11 files changed, 940 insertions(+), 44 deletions(-)
> [...]
> 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. */

This is supposed to be combined with the preceeding code to a single
expression in the end, right?  I don't quite understand why writing
to STD_ERROR_HANDLE doesn't work.  Is STD_ERROR_HANDLE not connected
to the ConPty?

> +      fhandler_base *fh = ::cygheap->fdtab[2];
> +      if (fh && fh->get_major () == DEV_PTYS_MAJOR)

For a quick test if the process is connected to a pty, you can
use `if (istty_slave_dev (myself->ctty))'


Thanks,
Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: [PATCH v4 0/1] Pseudo console support in PTY (v4)
  2019-04-15  8:18                   ` Corinna Vinschen
@ 2019-04-15 23:17                     ` Takashi Yano
  0 siblings, 0 replies; 53+ messages in thread
From: Takashi Yano @ 2019-04-15 23:17 UTC (permalink / raw)
  To: cygwin-developers

On Mon, 15 Apr 2019 10:18:17 +0200 Corinna Vinschen wrote:
> On Apr 12 14:28, Corinna Vinschen wrote:
> > On Apr 12 19:20, Takashi Yano wrote:
> > > Hi Thomas and Corinna,
> > > 
> > > I revised the patch to v4.
> > > [...]
> > > On Sat, 6 Apr 2019 19:43:35 +0200 Corinna Vinschen wrote:
> > > > This looks better, but there's still some problem:
> > > > $ cygport gawk.cygport build
> > > > >>> Compiling gawk-4.2.62-1.x86_64
> > > > [pressing Ctrl-C multiple times]
> > > >      14 [main] chmod (7016) child_copy: cygheap read copy failed, 0x180376408..0
> > > > x1803887A8, done 0, windows pid 7016, Win32 error 299
> > > >     766 [main] chmod (7016) C:\cygwin64\bin\chmod.exe: *** fatal error - ccalloc would have returned NULL
> > > >      14 [main] chmod (7016) child_copy: cygheap read copy failed, 0x180376408..0x1803887A8, done 0, windows pid 7016, Win32 error 299
> > > >     766 [main] chmod (7016) C:\cygwin64\bin\chmod.exe: *** fatal error - ccalloc
> > > >  would have returned NULL
> > > 
> > > I cannot reproduce this, but I added init_console_handler() call after
> > > attaching to pseudo console for a test according to your suggestion.
> > > Could you please test?
> > 
> > Thanks!  Will do over the weekend.
> 
> I couldn't reproduce this anymore with v4.

Thank you for testing.

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

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

* Re: [PATCH v5 1/1] Cygwin: pty: add pseudo console support.
  2019-04-15  8:38                     ` Corinna Vinschen
@ 2019-04-16  0:41                       ` Takashi Yano
  2019-04-16  9:16                         ` Corinna Vinschen
  0 siblings, 1 reply; 53+ messages in thread
From: Takashi Yano @ 2019-04-16  0:41 UTC (permalink / raw)
  To: cygwin-developers

On Mon, 15 Apr 2019 10:38:32 +0200 Corinna Vinschen wrote:
> This is supposed to be combined with the preceeding code to a single
> expression in the end, right?

Do you mean this code and the preceding code should be executed
exclusively? If so, the answer is no. If the stderr is pty slave,
the message should be written to both output_handle and
output_handle_cyg. The reason will be described later.

> I don't quite understand why writing
> to STD_ERROR_HANDLE doesn't work.  Is STD_ERROR_HANDLE not connected
> to the ConPty?

You are right. STD_ERROR_HANDLE (output_handle) is connected to
the ConPTY. The data written to output_handle is fowarded to
to_master_cyg in the master, then displayed. to_master_cyg is
duplicated to output_handle_cyg in the slave. Therefore, both
writing to output_handle and writing to output_handle_cyg seem
to work, at first glance.

However, in the fact, the code below stops forwarding if the
process is a cygwin process. 
+	  /* Avoid duplicating slave output which is already sent to
+	     to_master_cyg */
+	  if (!get_ttyp ()->switch_to_pcon)
+	    continue;

The role of the code above is as follows. ConPTY sometimes redraws
screen based on its own screen buffer. The screen buffer has only
the data written to output_handle. Basically, cygwin apps use
output_handle_cyg and native apps use output_handle. Terefore,
outputs from cygwin apps are disappeared at that time. To avoid
this, the data write to output_handle_cyg is pushed into the ConPTY
screen buffer by WriteFile() to output_handle. To prevent the data
from being displayed twice, forwarding is stopped by the code above
if the process is a cygwin app.

In strace.cc, writing to output_handle is necessary for pushing
the data into ConPTY screen buffer, and writing to output_handle_cyg
is necessary for displaying it.

> > +      fhandler_base *fh = ::cygheap->fdtab[2];
> > +      if (fh && fh->get_major () == DEV_PTYS_MAJOR)
> 
> For a quick test if the process is connected to a pty, you can
> use `if (istty_slave_dev (myself->ctty))'

Thanks for advice. My code is checking if the stderr is pty or
not, and write the message to output_handle_cyg if the stderr
is pty. If the stderr is redirected to another device, writing
is not done. Does istty_slave_dev() checking work same for
stderr if it is redirected? How can I get output_handle_cyg
after checking using istty_slave_dev()?

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

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

* Re: [PATCH v5 0/1] Pseudo console support in PTY (v5)
  2019-04-14 16:06                   ` [PATCH v5 0/1] Pseudo console support in PTY (v5) Takashi Yano
@ 2019-04-16  1:49                     ` Takashi Yano
  2019-04-16  1:51                       ` Takashi Yano
  0 siblings, 1 reply; 53+ messages in thread
From: Takashi Yano @ 2019-04-16  1:49 UTC (permalink / raw)
  To: cygwin-developers

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

On Mon, 15 Apr 2019 01:05:41 +0900 Takashi Yano wrote:
> v5 seems to need additional patch attached.

Please apply the patch attached instead of previous one.

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

[-- Attachment #2: v5-additional.patch --]
[-- Type: application/octet-stream, Size: 864 bytes --]

diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index 14165bdfc..df01cb0cf 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -1246,7 +1246,7 @@ fhandler_pty_slave::ioctl (unsigned int cmd, void *arg)
 	  DWORD pidRestore = 0;
 	  if (!pcon_attached)
 	    {
-	      if (has_master_opened ())
+	      if (has_master_opened () && get_ttyp ()->attach_pcon_in_fork)
 		goto resize_cyg;
 
 	      pidRestore = fhandler_console::get_console_process_id
@@ -1536,8 +1536,11 @@ fhandler_pty_master::fhandler_pty_master (int unit)
 
 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)
-    close ();
+    Sleep (20);
 }
 
 int

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

* Re: [PATCH v5 0/1] Pseudo console support in PTY (v5)
  2019-04-16  1:49                     ` Takashi Yano
@ 2019-04-16  1:51                       ` Takashi Yano
  0 siblings, 0 replies; 53+ messages in thread
From: Takashi Yano @ 2019-04-16  1:51 UTC (permalink / raw)
  To: cygwin-developers

On Tue, 16 Apr 2019 10:49:36 +0900 Takashi Yano wrote:
> On Mon, 15 Apr 2019 01:05:41 +0900 Takashi Yano wrote:
> > v5 seems to need additional patch attached.
> Please apply the patch attached instead of previous one.

Binaries of v5 with this patch are available below.

D=http://tyan0.dip.jp/cygwin
${D}/x86_64/test/cygwin1-20190416.dll.xz
${D}/x86_64/test/cygwin-console-helper.exe.xz
${D}/x86/test/cygwin1-20190416.dll.xz
${D}/x86/test/cygwin-console-helper.exe.xz

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

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

* Re: [PATCH v5 1/1] Cygwin: pty: add pseudo console support.
  2019-04-16  0:41                       ` Takashi Yano
@ 2019-04-16  9:16                         ` Corinna Vinschen
  2019-06-24 10:53                           ` Corinna Vinschen
  0 siblings, 1 reply; 53+ messages in thread
From: Corinna Vinschen @ 2019-04-16  9:16 UTC (permalink / raw)
  To: cygwin-developers

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

On Apr 16 09:41, Takashi Yano wrote:
> On Mon, 15 Apr 2019 10:38:32 +0200 Corinna Vinschen wrote:
> > This is supposed to be combined with the preceeding code to a single
> > expression in the end, right?
> 
> Do you mean this code and the preceding code should be executed
> exclusively? If so, the answer is no. If the stderr is pty slave,
> the message should be written to both output_handle and
> output_handle_cyg. The reason will be described later.
> 
> > I don't quite understand why writing
> > to STD_ERROR_HANDLE doesn't work.  Is STD_ERROR_HANDLE not connected
> > to the ConPty?
> 
> You are right. STD_ERROR_HANDLE (output_handle) is connected to
> the ConPTY. The data written to output_handle is fowarded to
> to_master_cyg in the master, then displayed. to_master_cyg is
> duplicated to output_handle_cyg in the slave. Therefore, both
> writing to output_handle and writing to output_handle_cyg seem
> to work, at first glance.
> 
> However, in the fact, the code below stops forwarding if the
> process is a cygwin process. 
> +	  /* Avoid duplicating slave output which is already sent to
> +	     to_master_cyg */
> +	  if (!get_ttyp ()->switch_to_pcon)
> +	    continue;
> 
> The role of the code above is as follows. ConPTY sometimes redraws
> screen based on its own screen buffer. The screen buffer has only
> the data written to output_handle. Basically, cygwin apps use
> output_handle_cyg and native apps use output_handle. Terefore,
> outputs from cygwin apps are disappeared at that time. To avoid
> this, the data write to output_handle_cyg is pushed into the ConPTY
> screen buffer by WriteFile() to output_handle. To prevent the data
> from being displayed twice, forwarding is stopped by the code above
> if the process is a cygwin app.
> 
> In strace.cc, writing to output_handle is necessary for pushing
> the data into ConPTY screen buffer, and writing to output_handle_cyg
> is necessary for displaying it.

Huh, crazy.  Thanks for explaining the context.

> 
> > > +      fhandler_base *fh = ::cygheap->fdtab[2];
> > > +      if (fh && fh->get_major () == DEV_PTYS_MAJOR)
> > 
> > For a quick test if the process is connected to a pty, you can
> > use `if (istty_slave_dev (myself->ctty))'
> 
> Thanks for advice. My code is checking if the stderr is pty or
> not, and write the message to output_handle_cyg if the stderr
> is pty. If the stderr is redirected to another device, writing
> is not done. Does istty_slave_dev() checking work same for
> stderr if it is redirected? How can I get output_handle_cyg
> after checking using istty_slave_dev()?

Well... I didn't really think about it.  YOu would have to
fetch ::cygheap->fdtab[2] anyway so checking myself->ctty
doesn't really help.


Thanks,
Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: [PATCH v5 1/1] Cygwin: pty: add pseudo console support.
  2019-04-16  9:16                         ` Corinna Vinschen
@ 2019-06-24 10:53                           ` Corinna Vinschen
  2019-07-25 14:31                             ` Corinna Vinschen
  2019-08-12 12:07                             ` Takashi Yano
  0 siblings, 2 replies; 53+ messages in thread
From: Corinna Vinschen @ 2019-06-24 10:53 UTC (permalink / raw)
  To: cygwin-developers

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

Hi Takashi,

On Apr 16 11:16, Corinna Vinschen wrote:
> On Apr 16 09:41, Takashi Yano wrote:
> > On Mon, 15 Apr 2019 10:38:32 +0200 Corinna Vinschen wrote:
> > > This is supposed to be combined with the preceeding code to a single
> > > expression in the end, right?
> > 
> > Do you mean this code and the preceding code should be executed
> > exclusively? If so, the answer is no. If the stderr is pty slave,
> > the message should be written to both output_handle and
> > output_handle_cyg. The reason will be described later.
> > 
> > > I don't quite understand why writing
> > > to STD_ERROR_HANDLE doesn't work.  Is STD_ERROR_HANDLE not connected
> > > to the ConPty?
> > 
> > You are right. STD_ERROR_HANDLE (output_handle) is connected to
> > the ConPTY. The data written to output_handle is fowarded to
> > to_master_cyg in the master, then displayed. to_master_cyg is
> > duplicated to output_handle_cyg in the slave. Therefore, both
> > writing to output_handle and writing to output_handle_cyg seem
> > to work, at first glance.
> > 
> > However, in the fact, the code below stops forwarding if the
> > process is a cygwin process. 
> > +	  /* Avoid duplicating slave output which is already sent to
> > +	     to_master_cyg */
> > +	  if (!get_ttyp ()->switch_to_pcon)
> > +	    continue;
> > 
> > The role of the code above is as follows. ConPTY sometimes redraws
> > screen based on its own screen buffer. The screen buffer has only
> > the data written to output_handle. Basically, cygwin apps use
> > output_handle_cyg and native apps use output_handle. Terefore,
> > outputs from cygwin apps are disappeared at that time. To avoid
> > this, the data write to output_handle_cyg is pushed into the ConPTY
> > screen buffer by WriteFile() to output_handle. To prevent the data
> > from being displayed twice, forwarding is stopped by the code above
> > if the process is a cygwin app.
> > 
> > In strace.cc, writing to output_handle is necessary for pushing
> > the data into ConPTY screen buffer, and writing to output_handle_cyg
> > is necessary for displaying it.
> 
> Huh, crazy.  Thanks for explaining the context.
> 
> > 
> > > > +      fhandler_base *fh = ::cygheap->fdtab[2];
> > > > +      if (fh && fh->get_major () == DEV_PTYS_MAJOR)
> > > 
> > > For a quick test if the process is connected to a pty, you can
> > > use `if (istty_slave_dev (myself->ctty))'
> > 
> > Thanks for advice. My code is checking if the stderr is pty or
> > not, and write the message to output_handle_cyg if the stderr
> > is pty. If the stderr is redirected to another device, writing
> > is not done. Does istty_slave_dev() checking work same for
> > stderr if it is redirected? How can I get output_handle_cyg
> > after checking using istty_slave_dev()?
> 
> Well... I didn't really think about it.  YOu would have to
> fetch ::cygheap->fdtab[2] anyway so checking myself->ctty
> doesn't really help.

Any news on this?  Do you consider the latest state from April
stable enough for master?


Thanks,
Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: [PATCH v5 1/1] Cygwin: pty: add pseudo console support.
  2019-06-24 10:53                           ` Corinna Vinschen
@ 2019-07-25 14:31                             ` Corinna Vinschen
  2019-08-08 19:24                               ` Corinna Vinschen
  2019-08-12 12:07                             ` Takashi Yano
  1 sibling, 1 reply; 53+ messages in thread
From: Corinna Vinschen @ 2019-07-25 14:31 UTC (permalink / raw)
  To: cygwin-developers

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

Hi Takashi,

I hope you're still with us?


Corinna

On Jun 24 12:53, Corinna Vinschen wrote:
> On Apr 16 11:16, Corinna Vinschen wrote:
> > On Apr 16 09:41, Takashi Yano wrote:
> > > On Mon, 15 Apr 2019 10:38:32 +0200 Corinna Vinschen wrote:
> > > > This is supposed to be combined with the preceeding code to a single
> > > > expression in the end, right?
> > > 
> > > Do you mean this code and the preceding code should be executed
> > > exclusively? If so, the answer is no. If the stderr is pty slave,
> > > the message should be written to both output_handle and
> > > output_handle_cyg. The reason will be described later.
> > > 
> > > > I don't quite understand why writing
> > > > to STD_ERROR_HANDLE doesn't work.  Is STD_ERROR_HANDLE not connected
> > > > to the ConPty?
> > > 
> > > You are right. STD_ERROR_HANDLE (output_handle) is connected to
> > > the ConPTY. The data written to output_handle is fowarded to
> > > to_master_cyg in the master, then displayed. to_master_cyg is
> > > duplicated to output_handle_cyg in the slave. Therefore, both
> > > writing to output_handle and writing to output_handle_cyg seem
> > > to work, at first glance.
> > > 
> > > However, in the fact, the code below stops forwarding if the
> > > process is a cygwin process. 
> > > +	  /* Avoid duplicating slave output which is already sent to
> > > +	     to_master_cyg */
> > > +	  if (!get_ttyp ()->switch_to_pcon)
> > > +	    continue;
> > > 
> > > The role of the code above is as follows. ConPTY sometimes redraws
> > > screen based on its own screen buffer. The screen buffer has only
> > > the data written to output_handle. Basically, cygwin apps use
> > > output_handle_cyg and native apps use output_handle. Terefore,
> > > outputs from cygwin apps are disappeared at that time. To avoid
> > > this, the data write to output_handle_cyg is pushed into the ConPTY
> > > screen buffer by WriteFile() to output_handle. To prevent the data
> > > from being displayed twice, forwarding is stopped by the code above
> > > if the process is a cygwin app.
> > > 
> > > In strace.cc, writing to output_handle is necessary for pushing
> > > the data into ConPTY screen buffer, and writing to output_handle_cyg
> > > is necessary for displaying it.
> > 
> > Huh, crazy.  Thanks for explaining the context.
> > 
> > > 
> > > > > +      fhandler_base *fh = ::cygheap->fdtab[2];
> > > > > +      if (fh && fh->get_major () == DEV_PTYS_MAJOR)
> > > > 
> > > > For a quick test if the process is connected to a pty, you can
> > > > use `if (istty_slave_dev (myself->ctty))'
> > > 
> > > Thanks for advice. My code is checking if the stderr is pty or
> > > not, and write the message to output_handle_cyg if the stderr
> > > is pty. If the stderr is redirected to another device, writing
> > > is not done. Does istty_slave_dev() checking work same for
> > > stderr if it is redirected? How can I get output_handle_cyg
> > > after checking using istty_slave_dev()?
> > 
> > Well... I didn't really think about it.  YOu would have to
> > fetch ::cygheap->fdtab[2] anyway so checking myself->ctty
> > doesn't really help.
> 
> Any news on this?  Do you consider the latest state from April
> stable enough for master?
> 
> 
> Thanks,
> Corinna
> 
> -- 
> Corinna Vinschen
> Cygwin Maintainer



-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: [PATCH v5 1/1] Cygwin: pty: add pseudo console support.
  2019-07-25 14:31                             ` Corinna Vinschen
@ 2019-08-08 19:24                               ` Corinna Vinschen
  0 siblings, 0 replies; 53+ messages in thread
From: Corinna Vinschen @ 2019-08-08 19:24 UTC (permalink / raw)
  To: Takashi Yano; +Cc: cygwin-developers

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

Hi Takashi,

desperately trying again, this time directly to you.  I hope you're
doing well.


Corinna


On Jul 25 16:31, Corinna Vinschen wrote:
> I hope you're still with us?
> 
> 
> Corinna
> 
> On Jun 24 12:53, Corinna Vinschen wrote:
> > On Apr 16 11:16, Corinna Vinschen wrote:
> > > On Apr 16 09:41, Takashi Yano wrote:
> > > > On Mon, 15 Apr 2019 10:38:32 +0200 Corinna Vinschen wrote:
> > > > > This is supposed to be combined with the preceeding code to a single
> > > > > expression in the end, right?
> > > > 
> > > > Do you mean this code and the preceding code should be executed
> > > > exclusively? If so, the answer is no. If the stderr is pty slave,
> > > > the message should be written to both output_handle and
> > > > output_handle_cyg. The reason will be described later.
> > > > 
> > > > > I don't quite understand why writing
> > > > > to STD_ERROR_HANDLE doesn't work.  Is STD_ERROR_HANDLE not connected
> > > > > to the ConPty?
> > > > 
> > > > You are right. STD_ERROR_HANDLE (output_handle) is connected to
> > > > the ConPTY. The data written to output_handle is fowarded to
> > > > to_master_cyg in the master, then displayed. to_master_cyg is
> > > > duplicated to output_handle_cyg in the slave. Therefore, both
> > > > writing to output_handle and writing to output_handle_cyg seem
> > > > to work, at first glance.
> > > > 
> > > > However, in the fact, the code below stops forwarding if the
> > > > process is a cygwin process. 
> > > > +	  /* Avoid duplicating slave output which is already sent to
> > > > +	     to_master_cyg */
> > > > +	  if (!get_ttyp ()->switch_to_pcon)
> > > > +	    continue;
> > > > 
> > > > The role of the code above is as follows. ConPTY sometimes redraws
> > > > screen based on its own screen buffer. The screen buffer has only
> > > > the data written to output_handle. Basically, cygwin apps use
> > > > output_handle_cyg and native apps use output_handle. Terefore,
> > > > outputs from cygwin apps are disappeared at that time. To avoid
> > > > this, the data write to output_handle_cyg is pushed into the ConPTY
> > > > screen buffer by WriteFile() to output_handle. To prevent the data
> > > > from being displayed twice, forwarding is stopped by the code above
> > > > if the process is a cygwin app.
> > > > 
> > > > In strace.cc, writing to output_handle is necessary for pushing
> > > > the data into ConPTY screen buffer, and writing to output_handle_cyg
> > > > is necessary for displaying it.
> > > 
> > > Huh, crazy.  Thanks for explaining the context.
> > > 
> > > > 
> > > > > > +      fhandler_base *fh = ::cygheap->fdtab[2];
> > > > > > +      if (fh && fh->get_major () == DEV_PTYS_MAJOR)
> > > > > 
> > > > > For a quick test if the process is connected to a pty, you can
> > > > > use `if (istty_slave_dev (myself->ctty))'
> > > > 
> > > > Thanks for advice. My code is checking if the stderr is pty or
> > > > not, and write the message to output_handle_cyg if the stderr
> > > > is pty. If the stderr is redirected to another device, writing
> > > > is not done. Does istty_slave_dev() checking work same for
> > > > stderr if it is redirected? How can I get output_handle_cyg
> > > > after checking using istty_slave_dev()?
> > > 
> > > Well... I didn't really think about it.  YOu would have to
> > > fetch ::cygheap->fdtab[2] anyway so checking myself->ctty
> > > doesn't really help.
> > 
> > Any news on this?  Do you consider the latest state from April
> > stable enough for master?
> > 
> > 
> > Thanks,
> > Corinna
> > 
> > -- 
> > Corinna Vinschen
> > Cygwin Maintainer
> 
> 
> 
> -- 
> Corinna Vinschen
> Cygwin Maintainer



-- 
Corinna Vinschen
Cygwin Maintainer

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

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

* Re: [PATCH v5 1/1] Cygwin: pty: add pseudo console support.
  2019-06-24 10:53                           ` Corinna Vinschen
  2019-07-25 14:31                             ` Corinna Vinschen
@ 2019-08-12 12:07                             ` Takashi Yano
  2019-08-12 12:50                               ` Corinna Vinschen
  1 sibling, 1 reply; 53+ messages in thread
From: Takashi Yano @ 2019-08-12 12:07 UTC (permalink / raw)
  To: cygwin-developers

Hi Corinna,

On Mon, 24 Jun 2019 12:53:37 +0200
Corinna Vinschen wrote:
> Any news on this?  Do you consider the latest state from April
> stable enough for master?

First, I apologize for a very lazy response. To tell the truth,
there has not been much progress.

Anyway, I will post v6 soon. It is almost stable.

In my test, the biggest problem is the failure to attach console
after setuid() in sshd if the user belongs to "Users" group only.
This causes mis-synchronization in the screen buffer.

To reproduce this problem, login to cygwin via ssh and execute
ssh again. Then some debug messages are shown as follows.

Last login: Mon Aug 12 20:15:54 2019 from ::1
CYGWIN_NT-10.0-WOW Express5800-S70 3.1.0(0.340/5/3) 2019-08-12 09:42 i686 Cygwin
[yano@Express5800-S70 ~]$ ssh localhost
      1 [main] ssh 1927 fhandler_pty_slave::push_to_pcon_screenbuffer: pty1: AttachConsole(21124) failed. (0x612E3C50) 00000005
     52 [main] ssh 1927 fhandler_pty_slave::push_to_pcon_screenbuffer: pty1: AttachConsole(21124) failed. (0x612E3C50) 00000005
yano@localhost's password:

That is, if the following commands are executed sequentially:

ssh localhost
ssh localhost (again)
ls
exit
cmd

the result of ls disappears from the screen.

This problem does not occur if the user belongs to "Administrators"
group.

It is reasonable to fail to attach console to
cygwin-console-helper.exe because it is running as system
service account, however, attaching to other processes executed
by myself also fails in the ssh session.

I have been stuck with this issue in the last several weeks.
Any advice will be appreciated. 

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

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

* Re: [PATCH v5 1/1] Cygwin: pty: add pseudo console support.
  2019-08-12 12:07                             ` Takashi Yano
@ 2019-08-12 12:50                               ` Corinna Vinschen
  2019-08-12 14:36                                 ` Takashi Yano
  0 siblings, 1 reply; 53+ messages in thread
From: Corinna Vinschen @ 2019-08-12 12:50 UTC (permalink / raw)
  To: Takashi Yano; +Cc: cygwin-developers

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

Hi Takashi!


I'm glad to read from you again :)

On Aug 12 21:07, Takashi Yano wrote:
> Hi Corinna,
> 
> On Mon, 24 Jun 2019 12:53:37 +0200
> Corinna Vinschen wrote:
> > Any news on this?  Do you consider the latest state from April
> > stable enough for master?
> 
> First, I apologize for a very lazy response. To tell the truth,
> there has not been much progress.

No worries.

> Anyway, I will post v6 soon. It is almost stable.

An important question is if we should put this into Cygwin 3.1 or if
it's better to keep the 3.1 release the "FIFO revamp" release and make
3.2 the WinPTY release.  That's probably the better approach...

> In my test, the biggest problem is the failure to attach console
> after setuid() in sshd if the user belongs to "Users" group only.
> This causes mis-synchronization in the screen buffer.
> 
> To reproduce this problem, login to cygwin via ssh and execute
> ssh again. Then some debug messages are shown as follows.
> 
> Last login: Mon Aug 12 20:15:54 2019 from ::1
> CYGWIN_NT-10.0-WOW Express5800-S70 3.1.0(0.340/5/3) 2019-08-12 09:42 i686 Cygwin
> [yano@Express5800-S70 ~]$ ssh localhost
>       1 [main] ssh 1927 fhandler_pty_slave::push_to_pcon_screenbuffer: pty1: AttachConsole(21124) failed. (0x612E3C50) 00000005
>      52 [main] ssh 1927 fhandler_pty_slave::push_to_pcon_screenbuffer: pty1: AttachConsole(21124) failed. (0x612E3C50) 00000005
> yano@localhost's password:
> 
> That is, if the following commands are executed sequentially:
> 
> ssh localhost
> ssh localhost (again)
> ls
> exit
> cmd
> 
> the result of ls disappears from the screen.
> 
> This problem does not occur if the user belongs to "Administrators"
> group.
> 
> It is reasonable to fail to attach console to
> cygwin-console-helper.exe because it is running as system
> service account, however, attaching to other processes executed
> by myself also fails in the ssh session.
> 
> I have been stuck with this issue in the last several weeks.
> Any advice will be appreciated. 

It's likely a result of the console object's DACL no?  I guess it's
equivalent to the default DACL of the creating process.  If so, it's
kind of like

  SYSTEM:rwx
  Administrators:rwx. 

It may be worth a try to use the get_object_sd, et_object_sd,
create_object_sd_from_attribute functions along the lines of what
fhandler_pty_slave::fchmod and fhandler_pty_slave::fchown do to add a
user to the console DACL.

This may fail on Windows Vista because of the console being represented
by a pseudo handle only, but it may work just fine starting with Windows
7.  Assuming the security stuff makes sense without the WinPTY code at
all...


Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

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

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

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

Hi Corinna,

On Mon, 12 Aug 2019 14:50:05 +0200
Corinna Vinschen wrote:
> An important question is if we should put this into Cygwin 3.1 or if
> it's better to keep the 3.1 release the "FIFO revamp" release and make
> 3.2 the WinPTY release.  That's probably the better approach...

I think it is better to release ConPTY support after tested as snapshot.

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

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

end of thread, other threads:[~2019-08-12 14:36 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-30 13:08 Pseudo console support in PTY Takashi Yano
2019-03-30 19:47 ` Corinna Vinschen
2019-03-30 19:59   ` Corinna Vinschen
2019-03-30 23:07 ` Thomas Wolff
2019-03-31 14:38   ` Corinna Vinschen
2019-03-31 15:00   ` Takashi Yano
2019-04-02 11:02 ` Corinna Vinschen
2019-04-02 17:16   ` Thomas Wolff
2019-04-02 17:51     ` Corinna Vinschen
2019-04-03  7:18       ` Thomas Wolff
2019-04-03  7:28         ` Corinna Vinschen
2019-04-03  7:55           ` Thomas Wolff
2019-04-03  8:02             ` Corinna Vinschen
2019-04-03 11:33               ` Thomas Wolff
2019-04-03 12:17                 ` Corinna Vinschen
2019-04-04  4:17                   ` Takashi Yano
2019-04-04  8:06                     ` Corinna Vinschen
2019-04-04  4:15             ` Takashi Yano
2019-04-03 16:36   ` [PATCH v2 0/1] Pseudo console support in PTY (v2) Takashi Yano
2019-04-03 16:37     ` [PATCH v2 1/1] Cygwin: pty: add pseudo console support Takashi Yano
2019-04-03 16:50     ` [PATCH v2 0/1] Pseudo console support in PTY (v2) Corinna Vinschen
2019-04-04  5:27       ` Takashi Yano
2019-04-04  8:17         ` Thomas Wolff
2019-04-04  9:34           ` Takashi Yano
2019-04-03 17:11     ` Corinna Vinschen
2019-04-04  8:59       ` Takashi Yano
2019-04-04 10:46         ` Corinna Vinschen
2019-04-06 11:13           ` [PATCH v3 0/1] Pseudo console support in PTY (v3) Takashi Yano
2019-04-06 11:14             ` [PATCH v3 1/1] Cygwin: pty: add pseudo console support Takashi Yano
2019-04-06 17:43             ` [PATCH v3 0/1] Pseudo console support in PTY (v3) Corinna Vinschen
2019-04-12 10:22               ` [PATCH v4 0/1] Pseudo console support in PTY (v4) Takashi Yano
2019-04-12 10:23                 ` [PATCH v4 1/1] Cygwin: pty: add pseudo console support Takashi Yano
2019-04-12 12:29                 ` [PATCH v4 0/1] Pseudo console support in PTY (v4) Corinna Vinschen
2019-04-15  8:18                   ` Corinna Vinschen
2019-04-15 23:17                     ` Takashi Yano
2019-04-14 15:23                 ` [PATCH v5 0/1] Pseudo console support in PTY (v5) Takashi Yano
2019-04-14 15:23                   ` [PATCH v5 1/1] Cygwin: pty: add pseudo console support Takashi Yano
2019-04-15  8:38                     ` Corinna Vinschen
2019-04-16  0:41                       ` Takashi Yano
2019-04-16  9:16                         ` Corinna Vinschen
2019-06-24 10:53                           ` Corinna Vinschen
2019-07-25 14:31                             ` Corinna Vinschen
2019-08-08 19:24                               ` Corinna Vinschen
2019-08-12 12:07                             ` Takashi Yano
2019-08-12 12:50                               ` Corinna Vinschen
2019-08-12 14:36                                 ` Takashi Yano
2019-04-14 16:06                   ` [PATCH v5 0/1] Pseudo console support in PTY (v5) Takashi Yano
2019-04-16  1:49                     ` Takashi Yano
2019-04-16  1:51                       ` Takashi Yano
2019-04-06 21:33             ` [PATCH v3 0/1] Pseudo console support in PTY (v3) Thomas Wolff
2019-04-07  5:05               ` Takashi Yano
2019-04-07 12:02                 ` Takashi Yano
2019-04-07 21:21                 ` Thomas Wolff

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).