public inbox for cygwin-cvs@sourceware.org
help / color / mirror / Atom feed
* [newlib-cygwin] Cygwin: pty: Inherit typeahead data between two input pipes.
@ 2021-01-28 10:25 Corinna Vinschen
  0 siblings, 0 replies; 2+ messages in thread
From: Corinna Vinschen @ 2021-01-28 10:25 UTC (permalink / raw)
  To: cygwin-cvs

https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=10d083c745dd89e824adc960d555975afc26b785

commit 10d083c745dd89e824adc960d555975afc26b785
Author: Takashi Yano via Cygwin-patches <cygwin-patches@cygwin.com>
Date:   Thu Jan 28 12:26:11 2021 +0900

    Cygwin: pty: Inherit typeahead data between two input pipes.
    
    - PTY has a problem that the key input, which is typed during windows
      native app is running, disappears when it returns to shell. This is
      beacuse pty has two input pipes, one is for cygwin apps and the other
      one is for native windows apps. The key input during windows native
      program is running is sent to the second input pipe while cygwin
      shell reads input from the first input pipe. This issue had been
      fixed once by commit 29431fcb, however, the new implementation of
      pseudo console support by commit bb428520 could not inherit this
      feature. This patch realize transfering input data between these
      two pipes bidirectionally by utilizing cygwin-console-helper process.
      The helper process is launched prior to starting the non-cygwin app,
      however, exits immediately unlike previous implementation.

Diff:
---
 winsup/cygwin/fhandler.h      |  14 +-
 winsup/cygwin/fhandler_tty.cc | 545 +++++++++++++++++++++++++++++++++---------
 winsup/cygwin/spawn.cc        |  82 ++++---
 winsup/cygwin/tty.cc          |   2 -
 winsup/cygwin/tty.h           |   8 +-
 5 files changed, 504 insertions(+), 147 deletions(-)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index af1ef3a45..378e9c13b 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -2291,6 +2291,13 @@ class fhandler_pty_slave: public fhandler_pty_common
   void fch_close_handles ();
 
  public:
+  /* Transfer direction for transfer_input() */
+  enum xfer_dir
+  {
+    to_nat,
+    to_cyg
+  };
+
   /* Constructor */
   fhandler_pty_slave (int);
 
@@ -2340,7 +2347,7 @@ class fhandler_pty_slave: public fhandler_pty_common
     copyto (fh);
     return fh;
   }
-  bool setup_pseudoconsole (STARTUPINFOEXW *si, bool nopcon);
+  bool setup_pseudoconsole (bool nopcon);
   static void close_pseudoconsole (tty *ttyp);
   bool term_has_pcon_cap (const WCHAR *env);
   void set_switch_to_pcon (void);
@@ -2349,6 +2356,9 @@ class fhandler_pty_slave: public fhandler_pty_common
   void setup_locale (void);
   tty *get_ttyp () { return (tty *) tc (); } /* Override as public */
   void create_invisible_console (void);
+  static void transfer_input (xfer_dir dir, HANDLE from, tty *ttyp,
+			      _minor_t unit, HANDLE input_available_event);
+  HANDLE get_input_available_event (void) { return input_available_event; }
 };
 
 #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit))
@@ -2361,6 +2371,8 @@ public:
     HANDLE from_master_cyg;
     HANDLE to_master;
     HANDLE to_master_cyg;
+    HANDLE to_slave;
+    HANDLE to_slave_cyg;
     HANDLE master_ctl;
     HANDLE input_available_event;
   };
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index 7f0752614..f0b2cd60a 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -56,6 +56,8 @@ struct pipe_reply {
   HANDLE from_master_cyg;
   HANDLE to_master;
   HANDLE to_master_cyg;
+  HANDLE to_slave;
+  HANDLE to_slave_cyg;
   DWORD error;
 };
 
@@ -848,8 +850,14 @@ fhandler_pty_slave::reset_switch_to_pcon (void)
     return;
   if (isHybrid)
     return;
+  if (get_ttyp ()->switch_to_pcon_in && !get_ttyp ()->h_pseudo_console)
+    fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg,
+					get_handle (),
+					get_ttyp (), get_minor (),
+					input_available_event);
   get_ttyp ()->pcon_pid = 0;
   get_ttyp ()->switch_to_pcon_in = false;
+  get_ttyp ()->h_pseudo_console = NULL;
 }
 
 ssize_t __stdcall
@@ -891,6 +899,22 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 void
 fhandler_pty_slave::mask_switch_to_pcon_in (bool mask)
 {
+  if (get_ttyp ()->switch_to_pcon_in
+      && (get_ttyp ()->pcon_pid == myself->pid
+	  || !get_ttyp ()->h_pseudo_console)
+      && get_ttyp ()->mask_switch_to_pcon_in != mask)
+    {
+      if (mask)
+	fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg,
+					    get_handle (),
+					    get_ttyp (), get_minor (),
+					    input_available_event);
+      else
+	fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_nat,
+					    get_handle_cyg (),
+					    get_ttyp (), get_minor (),
+					    input_available_event);
+    }
   get_ttyp ()->mask_switch_to_pcon_in = mask;
 }
 
@@ -1591,7 +1615,7 @@ fhandler_pty_common::resize_pseudo_console (struct winsize *ws)
   size.X = ws->ws_col;
   size.Y = ws->ws_row;
   pinfo p (get_ttyp ()->pcon_pid);
-  if (p && !get_ttyp ()->do_not_resize_pcon)
+  if (p)
     {
       HPCON_INTERNAL hpcon_local;
       HANDLE pcon_owner =
@@ -1763,6 +1787,17 @@ fhandler_pty_master::write (const void *ptr, size_t len)
 	      get_ttyp ()->pcon_start = false;
 	    }
 	  ReleaseMutex (input_mutex);
+	  if (get_ttyp ()->switch_to_pcon_in)
+	    {
+	      fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_nat,
+						  from_master_cyg,
+						  get_ttyp (), get_minor (),
+						  input_available_event);
+	      /* This accept_input() call is needed in order to transfer input
+		 which is not accepted yet to non-cygwin pipe. */
+	      if (get_readahead_valid ())
+		accept_input ();
+	    }
 	  return len;
 	}
 
@@ -1983,7 +2018,7 @@ fhandler_pty_master::pty_master_thread (const master_thread_param_t *p)
   while (!exit && (ConnectNamedPipe (p->master_ctl, NULL)
 		   || GetLastError () == ERROR_PIPE_CONNECTED))
     {
-      pipe_reply repl = { NULL, NULL, NULL, NULL, 0 };
+      pipe_reply repl = { NULL, NULL, NULL, NULL, NULL, NULL, 0 };
       bool deimp = false;
       NTSTATUS allow = STATUS_ACCESS_DENIED;
       ACCESS_MASK access = EVENT_MODIFY_STATE;
@@ -2074,6 +2109,20 @@ fhandler_pty_master::pty_master_thread (const master_thread_param_t *p)
 	      termios_printf ("DuplicateHandle (to_master_cyg), %E");
 	      goto reply;
 	    }
+	  if (!DuplicateHandle (GetCurrentProcess (), p->to_slave,
+				client, &repl.to_slave,
+				0, TRUE, DUPLICATE_SAME_ACCESS))
+	    {
+	      termios_printf ("DuplicateHandle (to_slave), %E");
+	      goto reply;
+	    }
+	  if (!DuplicateHandle (GetCurrentProcess (), p->to_slave_cyg,
+				client, &repl.to_slave_cyg,
+				0, TRUE, DUPLICATE_SAME_ACCESS))
+	    {
+	      termios_printf ("DuplicateHandle (to_slave_cyg), %E");
+	      goto reply;
+	    }
 	}
 reply:
       repl.error = GetLastError ();
@@ -2130,22 +2179,44 @@ fhandler_pty_master::pty_master_fwd_thread (const master_fwd_thread_param_t *p)
       char *ptr = outbuf;
       if (p->ttyp->h_pseudo_console)
 	{
-	  if (!p->ttyp->has_set_title)
-	    {
-	      /* Remove Set title sequence */
-	      char *p0, *p1;
-	      p0 = outbuf;
-	      while ((p0 = (char *) memmem (p0, rlen, "\033]0;", 4))
-		     && (p1 = (char *) memchr (p0, '\007', rlen-(p0-outbuf))))
-		{
-		  memmove (p0, p1 + 1, rlen - (p1 + 1 - outbuf));
-		  rlen -= p1 + 1 - p0;
-		  wlen = rlen;
-		}
-	    }
-	  /* Remove CSI > Pm m */
+	  /* 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 ++;
+		continue;
+	      }
+	    else if (state == 4 && outbuf[i] == '\a')
+	      {
+		const char *helper_str = "\\bin\\cygwin-console-helper.exe";
+		if (memmem (&outbuf[start_at], i + 1 - start_at,
+			    helper_str, strlen (helper_str)))
+		  {
+		    memmove (&outbuf[start_at], &outbuf[i+1], rlen-i-1);
+		    rlen = wlen = start_at + rlen - i - 1;
+		  }
+		state = 0;
+		continue;
+	      }
+	    else if (outbuf[i] == '\a')
+	      {
+		state = 0;
+		continue;
+	      }
+
+	  /* Remove CSI > Pm m */
+	  state = 0;
+	  start_at = 0;
 	  for (DWORD i = 0; i < rlen; i++)
 	    if (outbuf[i] == '\033')
 	      {
@@ -2419,6 +2490,8 @@ fhandler_pty_master::setup ()
   t.set_from_master_cyg (from_master_cyg);
   t.set_to_master (to_master);
   t.set_to_master_cyg (to_master_cyg);
+  t.set_to_slave (to_slave);
+  t.set_to_slave_cyg (get_output_handle ());
   t.winsize.ws_col = 80;
   t.winsize.ws_row = 25;
   t.master_pid = myself->pid;
@@ -2579,10 +2652,15 @@ fhandler_pty_common::process_opost_output (HANDLE h, const void *ptr,
   return res;
 }
 
+  /* 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(). */
 bool
-fhandler_pty_slave::setup_pseudoconsole (STARTUPINFOEXW *si, bool nopcon)
+fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
 {
-
   /* Setting switch_to_pcon_in is necessary even if
      pseudo console will not be activated. */
   fhandler_base *fh = ::cygheap->fdtab[0];
@@ -2613,60 +2691,151 @@ fhandler_pty_slave::setup_pseudoconsole (STARTUPINFOEXW *si, bool nopcon)
       return false;
     }
 
-  COORD size = {
-    (SHORT) get_ttyp ()->winsize.ws_col,
-    (SHORT) get_ttyp ()->winsize.ws_row
-  };
-  const DWORD inherit_cursor = 1;
-  SetLastError (ERROR_SUCCESS);
-  HRESULT res = CreatePseudoConsole (size, get_handle (), get_output_handle (),
-				     inherit_cursor,
-				     &get_ttyp ()->h_pseudo_console);
-  if (res != S_OK || GetLastError () == ERROR_PROC_NOT_FOUND)
-    {
-      if (res != S_OK)
-	system_printf ("CreatePseudoConsole() failed. %08x %08x\n",
-		       GetLastError (), res);
-      goto fallback;
-    }
-
-  SIZE_T bytesRequired;
-  InitializeProcThreadAttributeList (NULL, 1, 0, &bytesRequired);
-  ZeroMemory (si, sizeof (*si));
-  si->StartupInfo.cb = sizeof (STARTUPINFOEXW);
-  si->lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)
-    HeapAlloc (GetProcessHeap (), 0, bytesRequired);
-  if (si->lpAttributeList == NULL)
-    goto cleanup_pseudo_console;
-  if (!InitializeProcThreadAttributeList (si->lpAttributeList,
-					  1, 0, &bytesRequired))
-    goto cleanup_heap;
-  if (!UpdateProcThreadAttribute (si->lpAttributeList,
-				  0,
-				  PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
-				  get_ttyp ()->h_pseudo_console,
-				  sizeof (get_ttyp ()->h_pseudo_console),
-				  NULL, NULL))
-    goto cleanup_heap;
-  si->StartupInfo.dwFlags = STARTF_USESTDHANDLES;
-  si->StartupInfo.hStdInput = NULL;
-  si->StartupInfo.hStdOutput = NULL;
-  si->StartupInfo.hStdError = NULL;
+  STARTUPINFOEXW si;
+  PROCESS_INFORMATION pi;
+  HANDLE hello, goodbye;
+  HANDLE hr, hw;
+  HANDLE hpConIn, hpConOut;
 
-  {
-    fhandler_base *fh0 = ::cygheap->fdtab[0];
-    if (fh0 && fh0->get_device () != get_device ())
-      si->StartupInfo.hStdInput = fh0->get_handle ();
-    fhandler_base *fh1 = ::cygheap->fdtab[1];
-    if (fh1 && fh1->get_device () != get_device ())
-      {
-	get_ttyp ()->do_not_resize_pcon = true;
-	si->StartupInfo.hStdOutput = fh1->get_output_handle ();
-      }
-    fhandler_base *fh2 = ::cygheap->fdtab[2];
-    if (fh2 && fh2->get_device () != get_device ())
-      si->StartupInfo.hStdError = fh2->get_output_handle ();
-  }
+  do
+    {
+      COORD size = {
+	(SHORT) get_ttyp ()->winsize.ws_col,
+	(SHORT) get_ttyp ()->winsize.ws_row
+      };
+      const DWORD inherit_cursor = 1;
+      SetLastError (ERROR_SUCCESS);
+      HRESULT res = CreatePseudoConsole (size, get_handle (),
+					 get_output_handle (),
+					 inherit_cursor,
+					 &get_ttyp ()->h_pseudo_console);
+      if (res != S_OK || GetLastError () == ERROR_PROC_NOT_FOUND)
+	{
+	  if (res != S_OK)
+	    system_printf ("CreatePseudoConsole() failed. %08x %08x\n",
+			   GetLastError (), res);
+	  goto fallback;
+	}
+
+      SIZE_T bytesRequired;
+      InitializeProcThreadAttributeList (NULL, 2, 0, &bytesRequired);
+      ZeroMemory (&si, sizeof (si));
+      si.StartupInfo.cb = sizeof (STARTUPINFOEXW);
+      si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)
+	HeapAlloc (GetProcessHeap (), 0, bytesRequired);
+      if (si.lpAttributeList == NULL)
+	goto cleanup_pseudo_console;
+      if (!InitializeProcThreadAttributeList (si.lpAttributeList,
+					      2, 0, &bytesRequired))
+	goto cleanup_heap;
+      if (!UpdateProcThreadAttribute (si.lpAttributeList,
+				      0,
+				      PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
+				      get_ttyp ()->h_pseudo_console,
+				      sizeof (get_ttyp ()->h_pseudo_console),
+				      NULL, NULL))
+
+	goto cleanup_heap;
+
+      hello = CreateEvent (&sec_none, true, false, NULL);
+      goodbye = CreateEvent (&sec_none, true, false, NULL);
+      CreatePipe (&hr, &hw, &sec_none, 0);
+
+      HANDLE handles_to_inherit[] = {hello, goodbye, hw};
+      if (!UpdateProcThreadAttribute (si.lpAttributeList,
+				      0,
+				      PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+				      handles_to_inherit,
+				      sizeof (handles_to_inherit),
+				      NULL, NULL))
+	goto cleanup_event_and_pipes;
+
+      /* Execute helper process */
+      WCHAR cmd[MAX_PATH];
+      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.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
+      si.StartupInfo.hStdInput = NULL;
+      si.StartupInfo.hStdOutput = NULL;
+      si.StartupInfo.hStdError = NULL;
+
+      get_ttyp ()->pcon_start = true;
+      if (!CreateProcessW (NULL, cmd, &sec_none, &sec_none,
+			   TRUE, EXTENDED_STARTUPINFO_PRESENT,
+			   NULL, NULL, &si.StartupInfo, &pi))
+	goto cleanup_event_and_pipes;
+
+      for (;;)
+	{
+	  DWORD wait_result = WaitForSingleObject (hello, 500);
+	  if (wait_result == WAIT_OBJECT_0)
+	    break;
+	  if (wait_result != WAIT_TIMEOUT)
+	    goto cleanup_helper_process;
+	  DWORD exit_code;
+	  if (!GetExitCodeProcess (pi.hProcess, &exit_code))
+	    goto cleanup_helper_process;
+	  if (exit_code == STILL_ACTIVE)
+	    continue;
+	  if (exit_code != 0 ||
+	      WaitForSingleObject (hello, 500) != WAIT_OBJECT_0)
+	    goto cleanup_helper_process;
+	  break;
+	}
+      CloseHandle (hello);
+      CloseHandle (pi.hThread);
+
+      /* Duplicate pseudo console handles */
+      DWORD rlen;
+      char buf[64];
+      if (!ReadFile (hr, buf, sizeof (buf), &rlen, NULL))
+	goto cleanup_helper_process;
+      buf[rlen] = '\0';
+      sscanf (buf, "StdHandles=%p,%p", &hpConIn, &hpConOut);
+      if (!DuplicateHandle (pi.hProcess, hpConIn,
+			    GetCurrentProcess (), &hpConIn, 0,
+			    TRUE, DUPLICATE_SAME_ACCESS))
+	goto cleanup_helper_process;
+      if (!DuplicateHandle (pi.hProcess, hpConOut,
+			    GetCurrentProcess (), &hpConOut, 0,
+			    TRUE, DUPLICATE_SAME_ACCESS))
+	goto cleanup_pcon_in;
+
+      CloseHandle (hr);
+      CloseHandle (hw);
+      DeleteProcThreadAttributeList (si.lpAttributeList);
+      HeapFree (GetProcessHeap (), 0, si.lpAttributeList);
+
+      /* Attach to pseudo console */
+      FreeConsole ();
+      AttachConsole (pi.dwProcessId);
+
+      /* Terminate helper process */
+      SetEvent (goodbye);
+      WaitForSingleObject (pi.hProcess, INFINITE);
+      CloseHandle (goodbye);
+      CloseHandle (pi.hProcess);
+
+      /* Set handle */
+      HANDLE orig_input_handle = get_handle ();
+      HANDLE orig_output_handle = get_output_handle ();
+      cygheap_fdenum cfd (false);
+      while (cfd.next () >= 0)
+	if (cfd->get_device () == get_device ())
+	  {
+	    fhandler_base *fh = cfd;
+	    fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
+	    if (ptys->get_handle () == orig_input_handle)
+	      ptys->set_handle (hpConIn);
+	    if (ptys->get_output_handle () == orig_output_handle)
+	      ptys->set_output_handle (hpConOut);
+	  }
+      CloseHandle (orig_input_handle);
+      CloseHandle (orig_output_handle);
+    }
+  while (false);
 
   if (get_ttyp ()->pcon_pid == 0 || !pinfo (get_ttyp ()->pcon_pid))
     get_ttyp ()->pcon_pid = myself->pid;
@@ -2676,11 +2845,23 @@ fhandler_pty_slave::setup_pseudoconsole (STARTUPINFOEXW *si, bool nopcon)
       HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_ttyp ()->h_pseudo_console;
       get_ttyp ()->h_pcon_write_pipe = hp->hWritePipe;
     }
-  get_ttyp ()->pcon_start = true;
   return true;
 
+cleanup_pcon_in:
+  CloseHandle (hpConIn);
+cleanup_helper_process:
+  SetEvent (goodbye);
+  WaitForSingleObject (pi.hProcess, INFINITE);
+  CloseHandle (pi.hProcess);
+  goto skip_close_hello;
+cleanup_event_and_pipes:
+  CloseHandle (hello);
+skip_close_hello:
+  CloseHandle (goodbye);
+  CloseHandle (hr);
+  CloseHandle (hw);
 cleanup_heap:
-  HeapFree (GetProcessHeap (), 0, si->lpAttributeList);
+  HeapFree (GetProcessHeap (), 0, si.lpAttributeList);
 cleanup_pseudo_console:
   if (get_ttyp ()->h_pseudo_console)
     {
@@ -2690,6 +2871,7 @@ cleanup_pseudo_console:
       CloseHandle (tmp);
     }
 fallback:
+  get_ttyp ()->pcon_start = false;
   get_ttyp ()->h_pseudo_console = NULL;
   return false;
 }
@@ -2702,6 +2884,8 @@ fhandler_pty_slave::close_pseudoconsole (tty *ttyp)
   if (ttyp->h_pseudo_console)
     {
       ttyp->wait_pcon_fwd ();
+      FreeConsole ();
+      AttachConsole (ATTACH_PARENT_PROCESS);
       HPCON_INTERNAL *hp = (HPCON_INTERNAL *) ttyp->h_pseudo_console;
       HANDLE tmp = hp->hConHostProcess;
       ClosePseudoConsole (ttyp->h_pseudo_console);
@@ -2710,7 +2894,6 @@ fhandler_pty_slave::close_pseudoconsole (tty *ttyp)
       ttyp->switch_to_pcon_in = false;
       ttyp->pcon_pid = 0;
       ttyp->pcon_start = false;
-      ttyp->do_not_resize_pcon = false;
     }
 }
 
@@ -2793,7 +2976,6 @@ fhandler_pty_slave::term_has_pcon_cap (const WCHAR *env)
   char buf[1024];
   char *p;
   int len;
-  int x1, y1, x2, y2;
   int wait_cnt = 0;
 
   /* Check if terminal has ANSI escape sequence. */
@@ -2805,6 +2987,7 @@ fhandler_pty_slave::term_has_pcon_cap (const WCHAR *env)
   /* Set h_pseudo_console and pcon_start so that the response
      will sent to io_handle rather than io_handle_cyg. */
   get_ttyp ()->h_pseudo_console = (HPCON *) -1; /* dummy */
+  get_ttyp ()->pcon_pid = myself->pid;
   /* pcon_start will be cleared in master write() when CSI6n is responded. */
   get_ttyp ()->pcon_start = true;
   WriteFile (get_output_handle_cyg (), "\033[6n", 4, &n, NULL);
@@ -2820,8 +3003,9 @@ fhandler_pty_slave::term_has_pcon_cap (const WCHAR *env)
 	  len -= n;
 	  *p = '\0';
 	  char *p1 = strrchr (buf, '\033');
+	  int x, y;
 	  char c;
-	  if (p1 == NULL || sscanf (p1, "\033[%d;%d%c", &y1, &x1, &c) != 3
+	  if (p1 == NULL || sscanf (p1, "\033[%d;%d%c", &y, &x, &c) != 3
 	      || c != 'R')
 	    continue;
 	  wait_cnt = 0;
@@ -2833,48 +3017,14 @@ fhandler_pty_slave::term_has_pcon_cap (const WCHAR *env)
 	Sleep (1);
     }
   while (len);
+  get_ttyp ()->h_pseudo_console = NULL;
+  get_ttyp ()->pcon_pid = 0;
   if (len == 0)
     goto not_has_csi6n;
 
   get_ttyp ()->has_csi6n = true;
   get_ttyp ()->pcon_cap_checked = true;
 
-  /* Check if terminal has set-title capability */
-  WaitForSingleObject (input_mutex, INFINITE);
-  /* Set pcon_start again because it should be cleared
-     in master write(). */
-  get_ttyp ()->pcon_start = true;
-  WriteFile (get_output_handle_cyg (), "\033]0;\033\\\033[6n", 10, &n, NULL);
-  ReleaseMutex (input_mutex);
-  p = buf;
-  len = sizeof (buf) - 1;
-  do
-    {
-      ReadFile (get_handle (), p, len, &n, NULL);
-      p += n;
-      len -= n;
-      *p = '\0';
-      char *p2 = strrchr (buf, '\033');
-      char c;
-      if (p2 == NULL || sscanf (p2, "\033[%d;%d%c", &y2, &x2, &c) != 3
-	  || c != 'R')
-	continue;
-      break;
-    }
-  while (len);
-  get_ttyp ()->h_pseudo_console = NULL;
-
-  if (len == 0)
-    return true;
-
-  if (x2 == x1 && y2 == y1)
-    /* If "\033]0;\033\\" does not move cursor position,
-       set-title is supposed to be supported. */
-    get_ttyp ()->has_set_title = true;
-  else
-    /* Try to erase garbage string caused by "\033]0;\033\\" */
-    for (int i=0; i<x2-x1; i++)
-      WriteFile (get_output_handle_cyg (), "\b \b", 3, &n, NULL);
   return true;
 
 not_has_csi6n:
@@ -2914,6 +3064,8 @@ fhandler_pty_master::get_master_thread_param (master_thread_param_t *p)
   p->from_master_cyg = from_master_cyg;
   p->to_master = to_master;
   p->to_master_cyg = to_master_cyg;
+  p->to_slave = to_slave;
+  p->to_slave_cyg = get_output_handle ();
   p->master_ctl = master_ctl;
   p->input_available_event = input_available_event;
   SetEvent (thread_param_copied_event);
@@ -2928,3 +3080,172 @@ fhandler_pty_master::get_master_fwd_thread_param (master_fwd_thread_param_t *p)
   p->ttyp = get_ttyp ();
   SetEvent (thread_param_copied_event);
 }
+
+#define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
+#define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
+void
+fhandler_pty_slave::transfer_input (xfer_dir dir, HANDLE from, tty *ttyp,
+				    _minor_t unit, HANDLE input_available_event)
+{
+  HANDLE to;
+  if (dir == to_nat)
+    to = ttyp->to_slave ();
+  else
+    to = ttyp->to_slave_cyg ();
+
+  pinfo p (ttyp->master_pid);
+  HANDLE pty_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE, p->dwProcessId);
+  if (pty_owner)
+    {
+      DuplicateHandle (pty_owner, to, GetCurrentProcess (), &to,
+		       0, TRUE, DUPLICATE_SAME_ACCESS);
+      CloseHandle (pty_owner);
+    }
+  else
+    {
+      char pipe[MAX_PATH];
+      __small_sprintf (pipe,
+		       "\\\\.\\pipe\\cygwin-%S-pty%d-master-ctl",
+		       &cygheap->installation_key, unit);
+      pipe_request req = { GetCurrentProcessId () };
+      pipe_reply repl;
+      DWORD len;
+      if (!CallNamedPipe (pipe, &req, sizeof req,
+			  &repl, sizeof repl, &len, 500))
+	return; /* What can we do? */
+      if (dir == to_nat)
+	to = repl.to_slave;
+      else
+	to = repl.to_slave_cyg;
+    }
+
+  UINT cp_from = 0, cp_to = 0;
+
+  if (dir == to_nat)
+    {
+      cp_from = ttyp->term_code_page;
+      if (ttyp->h_pseudo_console)
+	cp_to = CP_UTF8;
+      else
+	cp_to = GetConsoleCP ();
+    }
+  else
+    {
+      cp_from = GetConsoleCP ();
+      cp_to = ttyp->term_code_page;
+    }
+
+  tmp_pathbuf tp;
+  char *buf = tp.c_get ();
+
+  bool transfered = false;
+
+  if (dir == to_cyg && ttyp->h_pseudo_console)
+    { /* from handle is console handle */
+      INPUT_RECORD r[INREC_SIZE];
+      DWORD n;
+      while (PeekConsoleInputA (from, r, INREC_SIZE, &n) && n)
+	{
+	  ReadConsoleInputA (from, r, n, &n);
+	  int len = 0;
+	  char *ptr = buf;
+	  for (DWORD i = 0; i < n; i++)
+	    if (r[i].EventType == KEY_EVENT && r[i].Event.KeyEvent.bKeyDown)
+	      {
+		DWORD ctrl_key_state = r[i].Event.KeyEvent.dwControlKeyState;
+		if (r[i].Event.KeyEvent.uChar.AsciiChar)
+		  {
+		    if ((ctrl_key_state & ALT_PRESSED)
+			&& r[i].Event.KeyEvent.uChar.AsciiChar <= 0x7f)
+		      buf[len++] = '\033'; /* Meta */
+		    buf[len++] = r[i].Event.KeyEvent.uChar.AsciiChar;
+		  }
+		/* Allow Ctrl-Space to emit ^@ */
+		else if (r[i].Event.KeyEvent.wVirtualKeyCode == '2'
+			 && (ctrl_key_state & CTRL_PRESSED)
+			 && !(ctrl_key_state & ALT_PRESSED))
+		  buf[len++] = '\0';
+		else if (r[i].Event.KeyEvent.wVirtualKeyCode == VK_F3)
+		  {
+		    /* If the cursor position report for CSI6n matches
+		       with e.g. "ESC[1;2R", pseudo console translates
+		       it to Shift-F3. This is a workaround for that. */
+		    int ctrl = 1;
+		    if (ctrl_key_state & SHIFT_PRESSED) ctrl += 1;
+		    if (ctrl_key_state & ALT_PRESSED) ctrl += 2;
+		    if (ctrl_key_state & CTRL_PRESSED) ctrl += 4;
+		    __small_sprintf (buf + len, "\033[1;%1dR", ctrl);
+		    len += 6;
+		  }
+		else
+		  { /* arrow/function keys */
+		    /* FIXME: The current code generates cygwin terminal
+		       sequence rather than xterm sequence. */
+		    char tmp[16];
+		    const char *add =
+		      fhandler_console::get_nonascii_key (r[i], tmp);
+		    if (add)
+		      {
+			strcpy (buf + len, add);
+			len += strlen (add);
+		      }
+		  }
+	      }
+	  if (cp_to != cp_from)
+	    {
+	      static mbstate_t mbp;
+	      char *mbbuf = tp.c_get ();
+	      size_t nlen = NT_MAX_PATH;
+	      convert_mb_str (cp_to, mbbuf, &nlen, cp_from, buf, len, &mbp);
+	      ptr = mbbuf;
+	      len = nlen;
+	    }
+	  /* Call WriteFile() line by line */
+	  char *p0 = ptr;
+	  char *p1 = ptr;
+	  while ((p1 = (char *) memchr (p0, '\r', len - (p0 - ptr))))
+	    {
+	      *p1 = '\n';
+	      n = p1 - p0 + 1;
+	      if (n && WriteFile (to, p0, n, &n, NULL) && n)
+		transfered = true;
+	      p0 = p1 + 1;
+	    }
+	  n = len - (p0 - ptr);
+	  if (n && WriteFile (to, p0, n, &n, NULL) && n)
+	    transfered = true;
+	}
+    }
+  else
+    {
+      DWORD bytes_in_pipe;
+      while (::bytes_available (bytes_in_pipe, from) && bytes_in_pipe)
+	{
+	  DWORD n = MIN (bytes_in_pipe, NT_MAX_PATH);
+	  ReadFile (from, buf, n, &n, NULL);
+	  char *ptr = buf;
+	  if (dir == to_nat && ttyp->h_pseudo_console)
+	    {
+	      char *p = buf;
+	      while ((p = (char *) memchr (p, '\n', n - (p - buf))))
+		*p = '\r';
+	    }
+	  if (cp_to != cp_from)
+	    {
+	      static mbstate_t mbp;
+	      char *mbbuf = tp.c_get ();
+	      size_t nlen = NT_MAX_PATH;
+	      convert_mb_str (cp_to, mbbuf, &nlen, cp_from, buf, n, &mbp);
+	      ptr = mbbuf;
+	      n = nlen;
+	    }
+	  if (n && WriteFile (to, ptr, n, &n, NULL) && n)
+	    transfered = true;;
+	}
+    }
+
+  if (dir == to_nat)
+    ResetEvent (input_available_event);
+  else if (transfered)
+    SetEvent (input_available_event);
+}
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index d03492ee6..b80a20f13 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -653,6 +653,36 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 	      }
 	}
 
+      bool enable_pcon = false;
+      HANDLE ptys_from_master = NULL;
+      HANDLE ptys_input_available_event = NULL;
+      tty *ptys_ttyp = NULL;
+      _minor_t ptys_unit = 0;
+      if (!iscygwin () && ptys_primary && is_console_app (runpath))
+	{
+	  bool nopcon = mode != _P_OVERLAY && mode != _P_WAIT;
+	  if (disable_pcon || !ptys_primary->term_has_pcon_cap (envblock))
+	    nopcon = true;
+	  if (ptys_primary->setup_pseudoconsole (nopcon))
+	    enable_pcon = true;
+	  ptys_ttyp = ptys_primary->get_ttyp ();
+	  ptys_unit = ptys_primary->get_minor ();
+	  ptys_from_master = ptys_primary->get_handle ();
+	  DuplicateHandle (GetCurrentProcess (), ptys_from_master,
+			   GetCurrentProcess (), &ptys_from_master,
+			   0, 0, DUPLICATE_SAME_ACCESS);
+	  ptys_input_available_event =
+	    ptys_primary->get_input_available_event ();
+	  DuplicateHandle (GetCurrentProcess (), ptys_input_available_event,
+			   GetCurrentProcess (), &ptys_input_available_event,
+			   0, 0, DUPLICATE_SAME_ACCESS);
+	  if (!enable_pcon)
+	    fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_nat,
+						ptys_primary->get_handle_cyg (),
+						ptys_ttyp, ptys_unit,
+						ptys_input_available_event);
+	}
+
       /* Set up needed handles for stdio */
       si.dwFlags = STARTF_USESTDHANDLES;
       si.hStdInput = handle ((in__stdin < 0 ? 0 : in__stdin), false);
@@ -664,25 +694,6 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
       if (!iscygwin ())
 	init_console_handler (myself->ctty > 0);
 
-      bool enable_pcon = false;
-      tty *ptys_ttyp = NULL;
-      STARTUPINFOEXW si_pcon;
-      ZeroMemory (&si_pcon, sizeof (si_pcon));
-      STARTUPINFOW *si_tmp = &si;
-      if (!iscygwin () && ptys_primary && is_console_app (runpath))
-	{
-	  bool nopcon = mode != _P_OVERLAY && mode != _P_WAIT;
-	  if (disable_pcon || !ptys_primary->term_has_pcon_cap (envblock))
-	    nopcon = true;
-	  if (ptys_primary->setup_pseudoconsole (&si_pcon, nopcon))
-	    {
-	      c_flags |= EXTENDED_STARTUPINFO_PRESENT;
-	      si_tmp = &si_pcon.StartupInfo;
-	      enable_pcon = true;
-	      ptys_ttyp = ptys_primary->get_ttyp ();
-	    }
-	}
-
     loop:
       /* When ruid != euid we create the new process under the current original
 	 account and impersonate in child, this way maintaining the different
@@ -711,7 +722,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 			       c_flags,
 			       envblock,	/* environment */
 			       NULL,
-			       si_tmp,
+			       &si,
 			       &pi);
 	}
       else
@@ -765,7 +776,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 			       c_flags,
 			       envblock,	/* environment */
 			       NULL,
-			       si_tmp,
+			       &si,
 			       &pi);
 	  if (hwst)
 	    {
@@ -778,11 +789,6 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 	      CloseDesktop (hdsk);
 	    }
 	}
-      if (enable_pcon)
-	{
-	  DeleteProcThreadAttributeList (si_pcon.lpAttributeList);
-	  HeapFree (GetProcessHeap (), 0, si_pcon.lpAttributeList);
-	}
 
       if (mode != _P_OVERLAY)
 	SetHandleInformation (my_wr_proc_pipe, HANDLE_FLAG_INHERIT,
@@ -954,14 +960,21 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 	    }
 	  if (sem)
 	    __posix_spawn_sem_release (sem, 0);
-	  if (enable_pcon)
+	  if (enable_pcon || ptys_ttyp || cons_native)
+	    WaitForSingleObject (pi.hProcess, INFINITE);
+	  if (ptys_ttyp)
 	    {
-	      WaitForSingleObject (pi.hProcess, INFINITE);
-	      fhandler_pty_slave::close_pseudoconsole (ptys_ttyp);
+	      fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg,
+						  ptys_from_master,
+						  ptys_ttyp, ptys_unit,
+						  ptys_input_available_event);
+	      CloseHandle (ptys_from_master);
+	      CloseHandle (ptys_input_available_event);
 	    }
+	  if (enable_pcon)
+	    fhandler_pty_slave::close_pseudoconsole (ptys_ttyp);
 	  else if (cons_native)
 	    {
-	      WaitForSingleObject (pi.hProcess, INFINITE);
 	      fhandler_console::request_xterm_mode_output (true,
 							   &cons_handle_set);
 	      fhandler_console::request_xterm_mode_input (true,
@@ -975,6 +988,15 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 	  system_call.arm ();
 	  if (waitpid (cygpid, &res, 0) != cygpid)
 	    res = -1;
+	  if (ptys_ttyp)
+	    {
+	      fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg,
+						  ptys_from_master,
+						  ptys_ttyp, ptys_unit,
+						  ptys_input_available_event);
+	      CloseHandle (ptys_from_master);
+	      CloseHandle (ptys_input_available_event);
+	    }
 	  if (enable_pcon)
 	    fhandler_pty_slave::close_pseudoconsole (ptys_ttyp);
 	  else if (cons_native)
diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc
index c6e13f111..436f5c6c3 100644
--- a/winsup/cygwin/tty.cc
+++ b/winsup/cygwin/tty.cc
@@ -244,8 +244,6 @@ tty::init ()
   pcon_start = false;
   pcon_cap_checked = false;
   has_csi6n = false;
-  has_set_title = false;
-  do_not_resize_pcon = false;
   need_invisible_console = false;
   invisible_console_pid = 0;
 }
diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h
index a975aba45..eb604588c 100644
--- a/winsup/cygwin/tty.h
+++ b/winsup/cygwin/tty.h
@@ -93,6 +93,8 @@ private:
   HANDLE _from_master_cyg;
   HANDLE _to_master;
   HANDLE _to_master_cyg;
+  HANDLE _to_slave;
+  HANDLE _to_slave_cyg;
   HPCON h_pseudo_console;
   bool pcon_start;
   bool switch_to_pcon_in;
@@ -103,8 +105,6 @@ private:
   HANDLE h_pcon_write_pipe;
   bool pcon_cap_checked;
   bool has_csi6n;
-  bool has_set_title;
-  bool do_not_resize_pcon;
   bool need_invisible_console;
   pid_t invisible_console_pid;
 
@@ -113,10 +113,14 @@ public:
   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; }
+  HANDLE to_slave () const { return _to_slave; }
+  HANDLE to_slave_cyg () const { return _to_slave_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; }
+  void set_to_slave (HANDLE h) { _to_slave = h; }
+  void set_to_slave_cyg (HANDLE h) { _to_slave_cyg = h; }
 
   int read_retval;
   bool was_opened;	/* True if opened at least once. */


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

* [newlib-cygwin] Cygwin: pty: Inherit typeahead data between two input pipes.
@ 2020-02-10 10:04 Corinna Vinschen
  0 siblings, 0 replies; 2+ messages in thread
From: Corinna Vinschen @ 2020-02-10 10:04 UTC (permalink / raw)
  To: cygwin-cvs

https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=29431fcb5b14d4c5ac3b3161a076eb1a208349d9

commit 29431fcb5b14d4c5ac3b3161a076eb1a208349d9
Author: Takashi Yano <takashi.yano@nifty.ne.jp>
Date:   Sun Feb 9 23:46:59 2020 +0900

    Cygwin: pty: Inherit typeahead data between two input pipes.
    
    - PTY has a problem that the key input, which is typed during
      windows native app is running, disappear when it returns to shell.
      (Problem 3 in https://cygwin.com/ml/cygwin/2020-02/msg00007.html)
      This is beacuse pty has two input pipes, one is for cygwin apps
      and the other one is for native windows apps. The key input during
      windows native program is running is sent to the second input pipe
      while cygwin shell reads input from the first input pipe.
      This patch realize transfering input data between these two pipes.

Diff:
---
 winsup/cygwin/fhandler.h      |  12 +-
 winsup/cygwin/fhandler_tty.cc | 400 ++++++++++++++++++++++++++++++++----------
 winsup/cygwin/select.cc       |   2 +
 winsup/cygwin/tty.cc          |   3 +
 winsup/cygwin/tty.h           |   3 +
 5 files changed, 329 insertions(+), 91 deletions(-)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index a22f3a1..993d735 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -326,7 +326,7 @@ class fhandler_base
   virtual size_t &raixput () { return ra.raixput; };
   virtual size_t &rabuflen () { return ra.rabuflen; };
 
-  bool get_readahead_valid () { return raixget () < ralen (); }
+  virtual bool get_readahead_valid () { return raixget () < ralen (); }
   int puts_readahead (const char *s, size_t len = (size_t) -1);
   int put_readahead (char value);
 
@@ -335,7 +335,7 @@ class fhandler_base
 
   void set_readahead_valid (int val, int ch = -1);
 
-  int get_readahead_into_buffer (char *buf, size_t buflen);
+  virtual int get_readahead_into_buffer (char *buf, size_t buflen);
 
   bool has_acls () const { return pc.has_acls (); }
 
@@ -1768,7 +1768,7 @@ class fhandler_termios: public fhandler_base
   int ioctl (int, void *);
   tty_min *_tc;
   tty *get_ttyp () {return (tty *) tc ();}
-  int eat_readahead (int n);
+  virtual int eat_readahead (int n);
 
  public:
   tty_min*& tc () {return _tc;}
@@ -2168,6 +2168,9 @@ class fhandler_pty_slave: public fhandler_pty_common
   ssize_t __stdcall write (const void *ptr, size_t len);
   void __reg3 read (void *ptr, size_t& len);
   int init (HANDLE, DWORD, mode_t);
+  int eat_readahead (int n);
+  int get_readahead_into_buffer (char *buf, size_t buflen);
+  bool get_readahead_valid (void);
 
   int tcsetattr (int a, const struct termios *t);
   int tcgetattr (struct termios *t);
@@ -2217,6 +2220,8 @@ class fhandler_pty_slave: public fhandler_pty_common
   void set_freeconsole_on_close (bool val);
   void trigger_redraw_screen (void);
   void wait_pcon_fwd (void);
+  void pull_pcon_input (void);
+  void update_pcon_input_state (bool need_lock);
 };
 
 #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit))
@@ -2281,6 +2286,7 @@ public:
   }
 
   bool setup_pseudoconsole (void);
+  void transfer_input_to_pcon (void);
 };
 
 class fhandler_dev_null: public fhandler_base
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index 1c23c93..f2fd680 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -440,6 +440,10 @@ fhandler_pty_master::flush_to_slave ()
 {
   if (get_readahead_valid () && !(get_ttyp ()->ti.c_lflag & ICANON))
     accept_input ();
+  WaitForSingleObject (input_mutex, INFINITE);
+  if (!get_ttyp ()->pcon_in_empty && !(get_ttyp ()->ti.c_lflag & ICANON))
+    SetEvent (input_available_event);
+  ReleaseMutex (input_mutex);
 }
 
 DWORD
@@ -519,7 +523,7 @@ fhandler_pty_master::accept_input ()
       termios_printf ("sending EOF to slave");
       get_ttyp ()->read_retval = 0;
     }
-  else
+  else if (!to_be_read_from_pcon ())
     {
       char *p = rabuf ();
       DWORD rc;
@@ -986,7 +990,6 @@ fhandler_pty_slave::close ()
     termios_printf ("CloseHandle (output_mutex<%p>), %E", output_mutex);
   if (pcon_attached_to == get_minor ())
     get_ttyp ()->num_pcon_attached_slaves --;
-  get_ttyp ()->mask_switch_to_pcon_in = false;
   return 0;
 }
 
@@ -1076,6 +1079,157 @@ fhandler_pty_slave::restore_reattach_pcon (void)
   pid_restore = 0;
 }
 
+/* This function requests transfering the input data from the input
+   pipe for cygwin apps to the other input pipe for native apps. */
+void
+fhandler_pty_slave::pull_pcon_input (void)
+{
+  get_ttyp ()->req_transfer_input_to_pcon = true;
+  while (get_ttyp ()->req_transfer_input_to_pcon)
+    Sleep (1);
+}
+
+void
+fhandler_pty_slave::update_pcon_input_state (bool need_lock)
+{
+  if (need_lock)
+    WaitForSingleObject (input_mutex, INFINITE);
+  /* Flush input buffer if it is requested by master.
+     This happens if ^C is pressed in pseudo console side. */
+  if (get_ttyp ()->req_flush_pcon_input)
+    {
+      FlushConsoleInputBuffer (get_handle ());
+      get_ttyp ()->req_flush_pcon_input = false;
+    }
+  /* Peek console input buffer and update state. */
+  INPUT_RECORD inp[INREC_SIZE];
+  DWORD n;
+  BOOL (WINAPI *PeekFunc)
+    (HANDLE, PINPUT_RECORD, DWORD, LPDWORD);
+  PeekFunc =
+    PeekConsoleInputA_Orig ? : PeekConsoleInput;
+  PeekFunc (get_handle (), inp, INREC_SIZE, &n);
+  bool saw_accept = false;
+  bool pipe_empty = true;
+  while (n-- > 0)
+    if (inp[n].EventType == KEY_EVENT && inp[n].Event.KeyEvent.bKeyDown &&
+	inp[n].Event.KeyEvent.uChar.AsciiChar)
+      {
+	pipe_empty = false;
+	char c = inp[n].Event.KeyEvent.uChar.AsciiChar;
+	const char sigs[] = {
+	  (char) get_ttyp ()->ti.c_cc[VINTR],
+	  (char) get_ttyp ()->ti.c_cc[VQUIT],
+	  (char) get_ttyp ()->ti.c_cc[VSUSP],
+	};
+	const char eols[] = {
+	  (char) get_ttyp ()->ti.c_cc[VEOF],
+	  (char) get_ttyp ()->ti.c_cc[VEOL],
+	  (char) get_ttyp ()->ti.c_cc[VEOL2],
+	  '\n',
+	  '\r'
+	};
+	if (is_line_input () && memchr (eols, c, sizeof (eols)))
+	  saw_accept = true;
+	if ((get_ttyp ()->ti.c_lflag & ISIG) &&
+	    memchr (sigs, c, sizeof (sigs)))
+	  saw_accept = true;
+      }
+  get_ttyp ()->pcon_in_empty = pipe_empty && !(ralen () > raixget ());
+  if (!get_readahead_valid () &&
+      (pipe_empty || (is_line_input () && !saw_accept)) &&
+      get_ttyp ()->read_retval == 1 && bytes_available (n) && n == 0)
+    ResetEvent (input_available_event);
+  if (need_lock)
+    ReleaseMutex (input_mutex);
+}
+
+int
+fhandler_pty_slave::eat_readahead (int n)
+{
+  int oralen = ralen () - raixget ();
+  if (n < 0)
+    n = ralen () - raixget ();
+  if (n > 0 && ralen () > raixget ())
+    {
+      const char eols[] = {
+	(char) get_ttyp ()->ti.c_cc[VEOF],
+	(char) get_ttyp ()->ti.c_cc[VEOL],
+	(char) get_ttyp ()->ti.c_cc[VEOL2],
+	'\n'
+      };
+      while (n > 0 && ralen () > raixget ())
+	{
+	  if (memchr (eols, rabuf ()[ralen ()-1], sizeof (eols)))
+	    break;
+	  -- n;
+	  -- ralen ();
+	}
+
+      /* If IUTF8 is set, the terminal is in UTF-8 mode.  If so, we erase
+	 a complete UTF-8 multibyte sequence on VERASE/VWERASE.  Otherwise,
+	 if we only erase a single byte, invalid unicode chars are left in
+	 the input. */
+      if (get_ttyp ()->ti.c_iflag & IUTF8)
+	while (ralen () > 0 &&
+	       ((unsigned char) rabuf ()[ralen ()] & 0xc0) == 0x80)
+	  --ralen ();
+
+      if (raixget () >= ralen ())
+	raixget () = raixput () = ralen () = 0;
+      else if (raixput () > ralen ())
+	raixput () = ralen ();
+    }
+
+  return oralen;
+}
+
+int
+fhandler_pty_slave::get_readahead_into_buffer (char *buf, size_t buflen)
+{
+  int ch;
+  int copied_chars = 0;
+
+  while (buflen)
+    if ((ch = get_readahead ()) < 0)
+      break;
+    else
+      {
+	const char eols[] = {
+	  (char) get_ttyp ()->ti.c_cc[VEOF],
+	  (char) get_ttyp ()->ti.c_cc[VEOL],
+	  (char) get_ttyp ()->ti.c_cc[VEOL2],
+	  '\n'
+	};
+	buf[copied_chars++] = (unsigned char)(ch & 0xff);
+	buflen--;
+	if (is_line_input () && memchr (eols, ch & 0xff, sizeof (eols)))
+	  break;
+      }
+
+  return copied_chars;
+}
+
+bool
+fhandler_pty_slave::get_readahead_valid (void)
+{
+  if (is_line_input ())
+    {
+      const char eols[] = {
+	(char) get_ttyp ()->ti.c_cc[VEOF],
+	(char) get_ttyp ()->ti.c_cc[VEOL],
+	(char) get_ttyp ()->ti.c_cc[VEOL2],
+	'\n'
+      };
+      for (size_t i=raixget (); i<ralen (); i++)
+	if (memchr (eols, rabuf ()[i], sizeof (eols)))
+	  return true;
+      return false;
+    }
+  else
+    return ralen () > raixget ();
+}
+
 void
 fhandler_pty_slave::set_switch_to_pcon (int fd_set)
 {
@@ -1083,13 +1237,7 @@ fhandler_pty_slave::set_switch_to_pcon (int fd_set)
     fd = fd_set;
   if (fd == 0 && !get_ttyp ()->switch_to_pcon_in)
     {
-      pid_restore = 0;
-      if (pcon_attached_to != get_minor ())
-	if (!try_reattach_pcon ())
-	  goto skip_console_setting;
-      FlushConsoleInputBuffer (get_handle ());
-skip_console_setting:
-      restore_reattach_pcon ();
+      pull_pcon_input ();
       if (get_ttyp ()->pcon_pid == 0 ||
 	  !pinfo (get_ttyp ()->pcon_pid))
 	get_ttyp ()->pcon_pid = myself->pid;
@@ -1121,16 +1269,7 @@ fhandler_pty_slave::reset_switch_to_pcon (void)
     /* There is a process which is grabbing pseudo console. */
     return;
   if (isHybrid)
-    {
-      DWORD bytes_in_pipe;
-      WaitForSingleObject (input_mutex, INFINITE);
-      if (bytes_available (bytes_in_pipe) && !bytes_in_pipe)
-	ResetEvent (input_available_event);
-      FlushConsoleInputBuffer (get_handle ());
-      ReleaseMutex (input_mutex);
-      init_console_handler (true);
-      return;
-    }
+    return;
   if (do_not_reset_switch_to_pcon)
     return;
   if (get_ttyp ()->switch_to_pcon_out)
@@ -1333,6 +1472,14 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
     if (!try_reattach_pcon ())
       fallback = true;
 
+  if (get_ttyp ()->switch_to_pcon_out && !fallback &&
+      (memmem (buf, nlen, "\033[6n", 4) || memmem (buf, nlen, "\033[0c", 4)))
+    {
+      get_ttyp ()->pcon_in_empty = false;
+      if (!is_line_input ())
+	SetEvent (input_available_event);
+    }
+
   DWORD dwMode, flags;
   flags = ENABLE_VIRTUAL_TERMINAL_PROCESSING;
   if (!(get_ttyp ()->ti.c_oflag & OPOST) ||
@@ -1382,18 +1529,14 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 void
 fhandler_pty_slave::mask_switch_to_pcon_in (bool mask)
 {
-  if (!mask && get_ttyp ()->pcon_pid &&
-      get_ttyp ()->pcon_pid != myself->pid &&
-      !!pinfo (get_ttyp ()->pcon_pid))
-    return;
   get_ttyp ()->mask_switch_to_pcon_in = mask;
 }
 
 bool
 fhandler_pty_common::to_be_read_from_pcon (void)
 {
-  return get_ttyp ()->switch_to_pcon_in &&
-    !get_ttyp ()->mask_switch_to_pcon_in;
+  return !get_ttyp ()->pcon_in_empty ||
+    (get_ttyp ()->switch_to_pcon_in && !get_ttyp ()->mask_switch_to_pcon_in);
 }
 
 void __reg3
@@ -1424,6 +1567,8 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
       mask_switch_to_pcon_in (true);
       reset_switch_to_pcon ();
     }
+  if (to_be_read_from_pcon ())
+    update_pcon_input_state (true);
 
   if (is_nonblocking () || !ptr) /* Indicating tcflush(). */
     time_to_wait = 0;
@@ -1523,60 +1668,81 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
 	    }
 	  goto out;
 	}
-      if (to_be_read_from_pcon ())
+      if (ptr && to_be_read_from_pcon ())
 	{
-	  if (!try_reattach_pcon ())
+	  if (get_readahead_valid ())
 	    {
-	      restore_reattach_pcon ();
-	      goto do_read_cyg;
+	      ReleaseMutex (input_mutex);
+	      totalread = get_readahead_into_buffer ((char *) ptr, len);
 	    }
-
-	  DWORD dwMode;
-	  GetConsoleMode (get_handle (), &dwMode);
-	  DWORD flags = ENABLE_VIRTUAL_TERMINAL_INPUT;
-	  if (get_ttyp ()->ti.c_lflag & ECHO)
-	    flags |= ENABLE_ECHO_INPUT;
-	  if (get_ttyp ()->ti.c_lflag & ICANON)
-	    flags |= ENABLE_LINE_INPUT;
-	  if (flags & ENABLE_ECHO_INPUT && !(flags & ENABLE_LINE_INPUT))
-	    flags &= ~ENABLE_ECHO_INPUT;
-	  if ((get_ttyp ()->ti.c_lflag & ISIG) &&
-	      !(get_ttyp ()->ti.c_iflag & IGNBRK))
-	    flags |= ENABLE_PROCESSED_INPUT;
-	  if (dwMode != flags)
-	    SetConsoleMode (get_handle (), flags);
-	  /* Read get_handle() instad of get_handle_cyg() */
-	  BOOL (WINAPI *ReadFunc)
-	    (HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED);
-	  ReadFunc = ReadFile_Orig ? ReadFile_Orig : ReadFile;
-	  DWORD rlen;
-	  if (!ReadFunc (get_handle (), ptr, len, &rlen, NULL))
+	  else
 	    {
-	      termios_printf ("read failed, %E");
+	      if (!try_reattach_pcon ())
+		{
+		  restore_reattach_pcon ();
+		  goto do_read_cyg;
+		}
+
+	      DWORD dwMode;
+	      GetConsoleMode (get_handle (), &dwMode);
+	      DWORD flags = ENABLE_VIRTUAL_TERMINAL_INPUT;
+	      if (dwMode != flags)
+		SetConsoleMode (get_handle (), flags);
+	      /* Read get_handle() instad of get_handle_cyg() */
+	      BOOL (WINAPI *ReadFunc)
+		(HANDLE, LPVOID, DWORD, LPDWORD, LPVOID);
+	      ReadFunc = ReadConsoleA_Orig ? ReadConsoleA_Orig : ReadConsoleA;
+	      DWORD rlen;
+	      readlen = MIN (len, sizeof (buf));
+	      if (!ReadFunc (get_handle (), buf, readlen, &rlen, NULL))
+		{
+		  termios_printf ("read failed, %E");
+		  SetConsoleMode (get_handle (), dwMode);
+		  restore_reattach_pcon ();
+		  ReleaseMutex (input_mutex);
+		  set_errno (EIO);
+		  totalread = -1;
+		  goto out;
+		}
+	      SetConsoleMode (get_handle (), dwMode);
+	      restore_reattach_pcon ();
 	      ReleaseMutex (input_mutex);
-	      set_errno (EIO);
-	      totalread = -1;
-	      goto out;
+
+	      ssize_t nlen;
+	      char *nbuf = convert_mb_str (get_ttyp ()->term_code_page,
+			     (size_t *) &nlen, GetConsoleCP (), buf, rlen);
+
+	      ssize_t ret;
+	      line_edit_status res =
+		line_edit (nbuf, nlen, get_ttyp ()->ti, &ret);
+
+	      mb_str_free (nbuf);
+
+	      if (res == line_edit_input_done || res == line_edit_ok)
+		totalread = get_readahead_into_buffer ((char *) ptr, len);
+	      else if (res > line_edit_signalled)
+		{
+		  set_sig_errno (EINTR);
+		  totalread = -1;
+		}
+	      else
+		{
+		  update_pcon_input_state (true);
+		  continue;
+		}
 	    }
-	  INPUT_RECORD inp[128];
-	  DWORD n;
-	  BOOL (WINAPI *PeekFunc)
-	    (HANDLE, PINPUT_RECORD, DWORD, LPDWORD);
-	  PeekFunc =
-	    PeekConsoleInputA_Orig ? PeekConsoleInputA_Orig : PeekConsoleInput;
-	  PeekFunc (get_handle (), inp, 128, &n);
-	  bool pipe_empty = true;
-	  while (n-- > 0)
-	    if (inp[n].EventType == KEY_EVENT && inp[n].Event.KeyEvent.bKeyDown)
-	      pipe_empty = false;
-	  if (pipe_empty)
-	    ResetEvent (input_available_event);
-	  ReleaseMutex (input_mutex);
-	  len = rlen;
 
-	  restore_reattach_pcon ();
+	  update_pcon_input_state (true);
 	  mask_switch_to_pcon_in (false);
-	  return;
+	  goto out;
+	}
+      if (!ptr && len == UINT_MAX && !get_ttyp ()->pcon_in_empty)
+	{
+	  FlushConsoleInputBuffer (get_handle ());
+	  get_ttyp ()->pcon_in_empty = true;
+	  DWORD n;
+	  if (bytes_available (n) && n == 0)
+	    ResetEvent (input_available_event);
 	}
 
 do_read_cyg:
@@ -1697,7 +1863,8 @@ out:
   termios_printf ("%d = read(%p, %lu)", totalread, ptr, len);
   len = (size_t) totalread;
   /* Push slave read as echo to pseudo console screen buffer. */
-  if (get_pseudo_console () && ptr0 && (get_ttyp ()->ti.c_lflag & ECHO))
+  if (get_pseudo_console () && ptr0 && totalread > 0 &&
+      (get_ttyp ()->ti.c_lflag & ECHO))
     {
       acquire_output_mutex (INFINITE);
       push_to_pcon_screenbuffer (ptr0, len, true);
@@ -2307,19 +2474,41 @@ fhandler_pty_master::write (const void *ptr, size_t len)
 
       WaitForSingleObject (input_mutex, INFINITE);
 
+      if (memchr (buf, '\003', nlen)) /* ^C intr key in pcon */
+	get_ttyp ()->req_flush_pcon_input = true;
+
       DWORD wLen;
       WriteFile (to_slave, buf, nlen, &wLen, NULL);
-
-      if (ti.c_lflag & ICANON)
-	{
-	  if (memchr (buf, '\r', nlen))
-	    SetEvent (input_available_event);
-	}
-      else
-	SetEvent (input_available_event);
+      get_ttyp ()->pcon_in_empty = false;
 
       ReleaseMutex (input_mutex);
 
+      /* Use line_edit () in order to set input_available_event. */
+      bool is_echo = tc ()->ti.c_lflag & ECHO;
+      if (!get_ttyp ()->mask_switch_to_pcon_in)
+	{
+	  tc ()->ti.c_lflag &= ~ECHO;
+	  ti.c_lflag &= ~ECHO;
+	}
+      ti.c_lflag &= ~ISIG;
+      line_edit (buf, nlen, ti, &ret);
+      if (is_echo)
+	tc ()->ti.c_lflag |= ECHO;
+      get_ttyp ()->read_retval = 1;
+
+      const char sigs[] = {
+	(char) ti.c_cc[VINTR],
+	(char) ti.c_cc[VQUIT],
+	(char) ti.c_cc[VSUSP],
+      };
+      if (tc ()->ti.c_lflag & ISIG)
+	for (size_t i=0; i<sizeof (sigs); i++)
+	  if (memchr (buf, sigs[i], nlen))
+	    {
+	      eat_readahead (-1);
+	      SetEvent (input_available_event);
+	    }
+
       mb_str_free (buf);
       return len;
     }
@@ -2716,11 +2905,17 @@ fhandler_pty_slave::fixup_after_attach (bool native_maybe, int fd_set)
 	  get_ttyp ()->num_pcon_attached_slaves ++;
 	}
 
+#if 0 /* This is for debug only. */
+      isHybrid = true;
+      get_ttyp ()->switch_to_pcon_in = true;
+      get_ttyp ()->switch_to_pcon_out = true;
+#endif
+
       if (pcon_attached_to == get_minor () && native_maybe)
 	{
 	  if (fd == 0)
 	    {
-	      FlushConsoleInputBuffer (get_handle ());
+	      pull_pcon_input ();
 	      DWORD mode =
 		ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT;
 	      SetConsoleMode (get_handle (), mode);
@@ -2764,13 +2959,6 @@ fhandler_pty_slave::fixup_after_fork (HANDLE parent)
 void
 fhandler_pty_slave::fixup_after_exec ()
 {
-  /* Native windows program does not reset event on read.
-     Therefore, reset here if no input is available. */
-  DWORD bytes_in_pipe;
-  if (!to_be_read_from_pcon () &&
-      bytes_available (bytes_in_pipe) && !bytes_in_pipe)
-    ResetEvent (input_available_event);
-
   reset_switch_to_pcon ();
 
   if (!close_on_exec ())
@@ -2988,6 +3176,33 @@ reply:
   return 0;
 }
 
+void
+fhandler_pty_master::transfer_input_to_pcon (void)
+{
+  WaitForSingleObject (input_mutex, INFINITE);
+  DWORD n;
+  size_t transfered = 0;
+  while (::bytes_available (n, from_master_cyg) && n)
+    {
+      char buf[1024];
+      ReadFile (from_master_cyg, buf, sizeof (buf), &n, 0);
+      char *p = buf;
+      while ((p = (char *) memchr (p, '\n', n - (p - buf))))
+	*p = '\r';
+      if (WriteFile (to_slave, buf, n, &n, 0))
+	transfered += n;
+    }
+  DWORD bytes_left = eat_readahead (-1);
+  if (bytes_left)
+    {
+      if (WriteFile (to_slave, rabuf (), bytes_left, &n, NULL))
+	transfered += n;
+    }
+  if (transfered)
+    get_ttyp ()->pcon_in_empty = false;
+  ReleaseMutex (input_mutex);
+}
+
 static DWORD WINAPI
 pty_master_thread (VOID *arg)
 {
@@ -3018,6 +3233,15 @@ fhandler_pty_master::pty_master_fwd_thread ()
 	      if (GetTickCount () - get_ttyp ()->pcon_last_time > time_to_wait)
 		SetEvent (get_ttyp ()->fwd_done);
 	      release_output_mutex ();
+	      /* Forcibly transfer input if it is requested by slave.
+		 This happens when input data should be transfered
+		 from the input pipe for cygwin apps to the input pipe
+		 for native apps. */
+	      if (get_ttyp ()->req_transfer_input_to_pcon)
+		{
+		  transfer_input_to_pcon ();
+		  get_ttyp ()->req_transfer_input_to_pcon = false;
+		}
 	      Sleep (1);
 	    }
 	}
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index 5048e54..b06441c 100644
--- a/winsup/cygwin/select.cc
+++ b/winsup/cygwin/select.cc
@@ -1195,6 +1195,8 @@ peek_pty_slave (select_record *s, bool from_select)
   fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
 
   ptys->reset_switch_to_pcon ();
+  if (ptys->to_be_read_from_pcon ())
+    ptys->update_pcon_input_state (true);
 
   if (s->read_selected)
     {
diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc
index a3d4a0f..0663dc5 100644
--- a/winsup/cygwin/tty.cc
+++ b/winsup/cygwin/tty.cc
@@ -247,6 +247,9 @@ tty::init ()
   need_redraw_screen = false;
   fwd_done = NULL;
   pcon_last_time = 0;
+  pcon_in_empty = true;
+  req_transfer_input_to_pcon = false;
+  req_flush_pcon_input = false;
 }
 
 HANDLE
diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h
index 755897d..a24afad 100644
--- a/winsup/cygwin/tty.h
+++ b/winsup/cygwin/tty.h
@@ -108,6 +108,9 @@ private:
   bool need_redraw_screen;
   HANDLE fwd_done;
   DWORD pcon_last_time;
+  bool pcon_in_empty;
+  bool req_transfer_input_to_pcon;
+  bool req_flush_pcon_input;
 
 public:
   HANDLE from_master () const { return _from_master; }


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

end of thread, other threads:[~2021-01-28 10:25 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-28 10:25 [newlib-cygwin] Cygwin: pty: Inherit typeahead data between two input pipes Corinna Vinschen
  -- strict thread matches above, loose matches on Subject: below --
2020-02-10 10:04 Corinna Vinschen

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