public inbox for cygwin-developers@cygwin.com
 help / color / mirror / Atom feed
* New implementation of pseudo console support (experimental)
@ 2020-05-13 12:16 Takashi Yano
  2020-05-13 12:35 ` Thomas Wolff
  2020-05-14  9:28 ` Takashi Yano
  0 siblings, 2 replies; 73+ messages in thread
From: Takashi Yano @ 2020-05-13 12:16 UTC (permalink / raw)
  To: cygwin-developers

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

Hello everyone.

This time, I have experimentally implemented a new pseudo console
support. In this implementation, pseudo console is created for each
native console app.

The advantage and disadvantage of this implementation are as follows.

Advantage:
1) No performance degradation in pty output for cygwin process.
    https://sourceware.org/pipermail/cygwin/2020-February/243651.html
2) Free from the problem caused by difference of behaviour of control
   sequences between real terminal and pseudo console.
    https://sourceware.org/pipermail/cygwin/2019-December/243074.html
    https://sourceware.org/pipermail/cygwin/2020-February/243648.html
3) Free from the problem in cgdb and emacs gud.
    https://sourceware.org/pipermail/cygwin/2020-January/243394.html
    https://sourceware.org/pipermail/cygwin/2020-March/243939.html
4) Redrawing screen on executing native console apps is not necessary.
5) cygwin-console-helper is not necessary for the pseudo console
   support.

Disadvantage:
1) Pseudo console is disabled if one of stdin, stdout or stderr is
   redirected.
2) Pseudo console is disabled if the native console app is executed
   in background.
3) The cygwin program which call console API directly does not work.
4) The apps which use console API cannot be debugged with gdb.
5) Type ahead key inputs are discarded while native console app is
   executed.
6) cmd.exe hangs up on typing some keys if cmd.exe is executed in
   background. (same as cygwin 3.0.7)
7) Code page cannot be changed by chcp.com.

The patch attached is for the current git head.

Could you please test? Any comments and suggestions will be
appreciated.

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

[-- Attachment #2: pcon2.diff --]
[-- Type: text/plain, Size: 15129 bytes --]

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index ae64086df..6da92ce60 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -2264,6 +2264,7 @@ class fhandler_pty_common: public fhandler_termios
     return get_ttyp ()->h_pseudo_console;
   }
   bool to_be_read_from_pcon (void);
+  void resize_pseudo_console2 (struct winsize *);
 
  protected:
   BOOL process_opost_output (HANDLE h,
@@ -2355,6 +2356,9 @@ class fhandler_pty_slave: public fhandler_pty_common
   void wait_pcon_fwd (void);
   void pull_pcon_input (void);
   void update_pcon_input_state (bool need_lock);
+  bool setup_pseudoconsole2 (STARTUPINFOEXW *si);
+  void close_pseudoconsole2 (void);
+  void wait_pcon_fwd2 (void);
 };
 
 #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit))
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index 8547ec7c4..11b84d332 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -41,6 +41,8 @@ extern "C" {
   VOID WINAPI ClosePseudoConsole (HPCON);
 }
 
+#define USE_PCON_MODE2 true
+
 #define close_maybe(h) \
   do { \
     if (h && h != INVALID_HANDLE_VALUE) \
@@ -1295,6 +1297,7 @@ fhandler_pty_slave::reset_switch_to_pcon (void)
   get_ttyp ()->switch_to_pcon_in = false;
   get_ttyp ()->switch_to_pcon_out = false;
   init_console_handler (true);
+  get_ttyp ()->h_pseudo_console2 = NULL;
 }
 
 void
@@ -1554,8 +1557,8 @@ fhandler_pty_slave::mask_switch_to_pcon_in (bool mask)
 bool
 fhandler_pty_common::to_be_read_from_pcon (void)
 {
-  return !get_ttyp ()->pcon_in_empty ||
-    (get_ttyp ()->switch_to_pcon_in && !get_ttyp ()->mask_switch_to_pcon_in);
+  return !get_ttyp ()->pcon_in_empty || get_ttyp ()->h_pseudo_console2
+    || (get_ttyp ()->switch_to_pcon_in && !get_ttyp ()->mask_switch_to_pcon_in);
 }
 
 void __reg3
@@ -2059,6 +2062,8 @@ fhandler_pty_slave::ioctl (unsigned int cmd, void *arg)
 cleanup:
 	  restore_reattach_pcon ();
 	}
+      if (get_ttyp ()->h_pseudo_console2 && get_ttyp ()->pcon_pid)
+	resize_pseudo_console2 ((struct winsize *) arg);
 
       if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
 	  || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
@@ -2348,6 +2353,27 @@ fhandler_pty_common::close ()
   return 0;
 }
 
+void
+fhandler_pty_common::resize_pseudo_console2 (struct winsize *ws)
+{
+  COORD size;
+  size.X = ws->ws_col;
+  size.Y = ws->ws_row;
+  pinfo p (get_ttyp ()->pcon_pid);
+  if (p)
+    {
+      HPCON_INTERNAL hpcon_local;
+      HANDLE pcon_owner =
+	OpenProcess (PROCESS_DUP_HANDLE, FALSE, p->exec_dwProcessId);
+      DuplicateHandle (pcon_owner, get_ttyp ()->h_pcon_write_pipe,
+		       GetCurrentProcess (), &hpcon_local.hWritePipe,
+		       0, TRUE, DUPLICATE_SAME_ACCESS);
+      ResizePseudoConsole ((HPCON) &hpcon_local, size);
+      CloseHandle (pcon_owner);
+      CloseHandle (hpcon_local.hWritePipe);
+    }
+}
+
 void
 fhandler_pty_master::cleanup ()
 {
@@ -2497,11 +2523,57 @@ fhandler_pty_master::write (const void *ptr, size_t len)
 	get_ttyp ()->req_flush_pcon_input = true;
 
       DWORD wLen;
-      WriteFile (to_slave, buf, nlen, &wLen, NULL);
+      HANDLE write_to =
+	get_ttyp ()->h_pseudo_console2 ? get_output_handle () : to_slave;
+
+      if (get_ttyp ()->pcon2_start)
+	{
+	  /* Pseudo condole support mode-2 uses "CSI6n" to get cursor
+	     position. If the reply for "CSI6n" is divided into multiple
+	     writes, pseudo console sometimes does not recognize it.
+	     Therefore, put them together into wpbuf and write all at once. */
+	  static const int wpbuf_len = 64;
+	  static char wpbuf[wpbuf_len];
+	  static int ixput = 0;
+
+	  if (ixput == 0 && buf[0] != '\033')
+	    { /* fail-safe */
+	      WriteFile (write_to, "\033[1;1R", 6, &wLen, NULL); /* dummy */
+	      get_ttyp ()->pcon2_start = false;
+	    }
+	  else
+	    {
+	      if (ixput + nlen < wpbuf_len)
+		for (size_t i=0; i<nlen; i++)
+		  wpbuf[ixput++] = buf[i];
+	      else
+		{
+		  WriteFile (write_to, wpbuf, ixput, &wLen, NULL);
+		  ixput = 0;
+		  get_ttyp ()->pcon2_start = false;
+		  WriteFile (write_to, buf, nlen, &wLen, NULL);
+		}
+	      if (ixput && memchr (wpbuf, 'R', ixput))
+		{
+		  WriteFile (write_to, wpbuf, ixput, &wLen, NULL);
+		  ixput = 0;
+		  get_ttyp ()->pcon2_start = false;
+		}
+	      ReleaseMutex (input_mutex);
+	      mb_str_free (buf);
+	      return len;
+	    }
+	}
+      /* Workaround for rlwrap. Replace NL to CR. */
+      char *p = (char *) memchr (buf, '\n', nlen);
+      if (p) *p = '\r';
+
+      WriteFile (write_to, buf, nlen, &wLen, NULL);
       get_ttyp ()->pcon_in_empty = false;
 
       ReleaseMutex (input_mutex);
 
+#if !USE_PCON_MODE2
       /* 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)
@@ -2527,6 +2599,7 @@ fhandler_pty_master::write (const void *ptr, size_t len)
 	      eat_readahead (-1);
 	      SetEvent (input_available_event);
 	    }
+#endif /* USE_PCON_MODE2 */
 
       mb_str_free (buf);
       return len;
@@ -2608,6 +2681,8 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg)
 	  size.Y = ((struct winsize *) arg)->ws_row;
 	  ResizePseudoConsole (get_pseudo_console (), size);
 	}
+      if (get_ttyp ()->h_pseudo_console2 && get_ttyp ()->pcon_pid)
+	resize_pseudo_console2 ((struct winsize *) arg);
       if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
 	  || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
 	{
@@ -2884,6 +2959,19 @@ fhandler_pty_slave::wait_pcon_fwd (void)
   cygwait (get_ttyp ()->fwd_done, INFINITE);
 }
 
+void
+fhandler_pty_slave::wait_pcon_fwd2 (void)
+{
+  const int sleep_in_pcon = 16;
+  const int time_to_wait = sleep_in_pcon * 2 + 1/* margine */;
+  get_ttyp ()->pcon_last_time = GetTickCount ();
+  while (GetTickCount () - get_ttyp ()->pcon_last_time < time_to_wait)
+    {
+      int tw = time_to_wait - (GetTickCount () - get_ttyp ()->pcon_last_time);
+      cygwait (tw);
+    }
+}
+
 void
 fhandler_pty_slave::trigger_redraw_screen (void)
 {
@@ -3267,6 +3355,8 @@ fhandler_pty_master::pty_master_fwd_thread ()
 	      Sleep (1);
 	    }
 	}
+      if (USE_PCON_MODE2)
+	get_ttyp ()->pcon_last_time = GetTickCount ();
       if (!ReadFile (from_slave, outbuf, sizeof outbuf, &rlen, NULL))
 	{
 	  termios_printf ("ReadFile for forwarding failed, %E");
@@ -3274,8 +3364,9 @@ fhandler_pty_master::pty_master_fwd_thread ()
 	}
       ssize_t wlen = rlen;
       char *ptr = outbuf;
-      if (get_pseudo_console ())
+      if (get_pseudo_console () || get_ttyp ()->h_pseudo_console2)
 	{
+#if !USE_PCON_MODE2
 	  /* Avoid duplicating slave output which is already sent to
 	     to_master_cyg */
 	  if (!get_ttyp ()->switch_to_pcon_out)
@@ -3328,6 +3419,7 @@ fhandler_pty_master::pty_master_fwd_thread ()
 	      rlen -= 4;
 	    }
 	  wlen = rlen;
+#endif /* USE_PCON_MODE2 */
 
 	  size_t nlen;
 	  char *buf = convert_mb_str
@@ -3697,7 +3789,8 @@ fhandler_pty_master::setup ()
   t.winsize.ws_col = 80;
   t.winsize.ws_row = 25;
 
-  setup_pseudoconsole ();
+  if (!USE_PCON_MODE2)
+    setup_pseudoconsole ();
 
   t.set_from_master (from_master);
   t.set_from_master_cyg (from_master_cyg);
@@ -3859,3 +3952,100 @@ fhandler_pty_common::process_opost_output (HANDLE h, const void *ptr, ssize_t& l
   len -= towrite;
   return res;
 }
+
+bool
+fhandler_pty_slave::setup_pseudoconsole2 (STARTUPINFOEXW *si)
+{
+  if (!USE_PCON_MODE2)
+    return false;
+  if (disable_pcon)
+    return false;
+  /* If the legacy console mode is enabled, pseudo console seems
+     not to work as expected. To determine console mode, registry
+     key ForceV2 in HKEY_CURRENT_USER\Console is checked. */
+  reg_key reg (HKEY_CURRENT_USER, KEY_READ, L"Console", NULL);
+  if (reg.error ())
+    return false;
+  if (reg.get_dword (L"ForceV2", 1) == 0)
+    {
+      termios_printf ("Pseudo console is disabled "
+		      "because the legacy console mode is enabled.");
+      return false;
+    }
+
+  COORD size = {
+    (SHORT) get_ttyp ()->winsize.ws_col,
+    (SHORT) get_ttyp ()->winsize.ws_row
+  };
+  SetLastError (ERROR_SUCCESS);
+  HRESULT res = CreatePseudoConsole (size, get_handle (), get_output_handle (),
+				     1, &get_ttyp ()->h_pseudo_console2);
+  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_console2,
+				  sizeof (get_ttyp ()->h_pseudo_console2),
+				  NULL, NULL))
+    goto cleanup_heap;
+  si->StartupInfo.dwFlags = STARTF_USESTDHANDLES;
+  si->StartupInfo.hStdInput = NULL;
+  si->StartupInfo.hStdOutput = NULL;
+  si->StartupInfo.hStdError = NULL;
+
+  if (get_ttyp ()->pcon_pid == 0 ||
+      !pinfo (get_ttyp ()->pcon_pid))
+    get_ttyp ()->pcon_pid = myself->pid;
+  if (get_ttyp ()->h_pseudo_console2 && get_ttyp ()->pcon_pid == myself->pid)
+    {
+      HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_ttyp ()->h_pseudo_console2;
+      get_ttyp ()->h_pcon_write_pipe = hp->hWritePipe;
+    }
+  get_ttyp ()->pcon2_start = true;
+  return true;
+
+cleanup_heap:
+  HeapFree (GetProcessHeap (), 0, si->lpAttributeList);
+cleanup_pseudo_console:
+  if (get_ttyp ()->h_pseudo_console2)
+    {
+      HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_ttyp ()->h_pseudo_console2;
+      HANDLE tmp = hp->hConHostProcess;
+      ClosePseudoConsole (get_ttyp ()->h_pseudo_console2);
+      CloseHandle (tmp);
+    }
+fallback:
+  get_ttyp ()->h_pseudo_console2 = NULL;
+  return false;
+}
+
+void
+fhandler_pty_slave::close_pseudoconsole2 (void)
+{
+  if (get_ttyp ()->h_pseudo_console2)
+    {
+      HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_ttyp ()->h_pseudo_console2;
+      HANDLE tmp = hp->hConHostProcess;
+      ClosePseudoConsole (get_ttyp ()->h_pseudo_console2);
+      CloseHandle (tmp);
+      get_ttyp ()->h_pseudo_console2 = NULL;
+    }
+}
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index 3e8c8367a..d9ed8cfc4 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -194,6 +194,24 @@ handle (int fd, bool writing)
   return h;
 }
 
+static bool
+is_console_app (WCHAR *filename)
+{
+  HANDLE h;
+  const int id_offset = 92;
+  h = CreateFileW (filename, GENERIC_READ, FILE_SHARE_READ,
+		  NULL, OPEN_EXISTING, 0, NULL);
+  char buf[1024];
+  DWORD n;
+  ReadFile (h, buf, sizeof (buf), &n, 0);
+  CloseHandle (h);
+  char *p = (char *) memmem (buf, n, "PE\0\0", 4);
+  if (p && p + id_offset <= buf + n)
+    return p[id_offset] == '\003'; /* 02: GUI, 03: console */
+  else
+    return false;
+}
+
 int
 iscmd (const char *argv0, const char *what)
 {
@@ -583,6 +601,9 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
       /* Attach to pseudo console if pty salve is used */
       pid_restore = fhandler_console::get_console_process_id
 	(GetCurrentProcessId (), false);
+      int minor = -1;
+      int ptys_cnt = 0;
+      fhandler_pty_slave *ptys = NULL;
       for (int i = 0; i < 3; i ++)
 	{
 	  const int chk_order[] = {1, 0, 2};
@@ -590,7 +611,11 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 	  fhandler_base *fh = ::cygheap->fdtab[fd];
 	  if (fh && fh->get_major () == DEV_PTYS_MAJOR)
 	    {
-	      fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
+	      ptys = (fhandler_pty_slave *) fh;
+	      if (minor < 0)
+		minor = fh->get_minor ();
+	      if (minor == fh->get_minor ())
+		ptys_cnt++;
 	      if (ptys->get_pseudo_console ())
 		{
 		  DWORD helper_process_id = ptys->get_helper_process_id ();
@@ -632,6 +657,20 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
       if (!iscygwin ())
 	init_console_handler (myself->ctty > 0);
 
+      bool use_pcon_mode2 = false;
+      STARTUPINFOEXW si_pcon;
+      ZeroMemory (&si_pcon, sizeof (si_pcon));
+      STARTUPINFOW *si_tmp = &si;
+      if (!iscygwin () && ptys_cnt == 3 && ptys
+	  && ctty_pgid && ctty_pgid == myself->pgid
+	  && is_console_app (runpath))
+	if (ptys->setup_pseudoconsole2 (&si_pcon))
+	  {
+	    c_flags |= EXTENDED_STARTUPINFO_PRESENT;
+	    si_tmp = &si_pcon.StartupInfo;
+	    use_pcon_mode2 = true;
+	  }
+
     loop:
       /* When ruid != euid we create the new process under the current original
 	 account and impersonate in child, this way maintaining the different
@@ -660,7 +699,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 			       c_flags,
 			       envblock,	/* environment */
 			       NULL,
-			       &si,
+			       si_tmp,
 			       &pi);
 	}
       else
@@ -714,7 +753,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 			       c_flags,
 			       envblock,	/* environment */
 			       NULL,
-			       &si,
+			       si_tmp,
 			       &pi);
 	  if (hwst)
 	    {
@@ -727,6 +766,11 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 	      CloseDesktop (hdsk);
 	    }
 	}
+      if (use_pcon_mode2)
+	{
+	  DeleteProcThreadAttributeList (si_pcon.lpAttributeList);
+	  HeapFree (GetProcessHeap (), 0, si_pcon.lpAttributeList);
+	}
 
       if (mode != _P_OVERLAY)
 	SetHandleInformation (my_wr_proc_pipe, HANDLE_FLAG_INHERIT,
@@ -897,6 +941,12 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 		  && WaitForSingleObject (pi.hProcess, 0) == WAIT_TIMEOUT)
 		wait_for_myself ();
 	    }
+	  if (use_pcon_mode2)
+	    {
+	      WaitForSingleObject (pi.hProcess, INFINITE);
+	      ptys->wait_pcon_fwd2 ();
+	      ptys->close_pseudoconsole2 ();
+	    }
 	  myself.exit (EXITCODE_NOSET);
 	  break;
 	case _P_WAIT:
diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc
index 0663dc5ee..9bc1457d1 100644
--- a/winsup/cygwin/tty.cc
+++ b/winsup/cygwin/tty.cc
@@ -250,6 +250,8 @@ tty::init ()
   pcon_in_empty = true;
   req_transfer_input_to_pcon = false;
   req_flush_pcon_input = false;
+  h_pseudo_console2 = NULL;
+  pcon2_start = false;
 }
 
 HANDLE
diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h
index a24afad06..a7546715e 100644
--- a/winsup/cygwin/tty.h
+++ b/winsup/cygwin/tty.h
@@ -111,6 +111,9 @@ private:
   bool pcon_in_empty;
   bool req_transfer_input_to_pcon;
   bool req_flush_pcon_input;
+  HPCON h_pseudo_console2;
+  HANDLE h_pcon_write_pipe;
+  bool pcon2_start;
 
 public:
   HANDLE from_master () const { return _from_master; }

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

end of thread, other threads:[~2020-09-02 11:03 UTC | newest]

Thread overview: 73+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-13 12:16 New implementation of pseudo console support (experimental) Takashi Yano
2020-05-13 12:35 ` Thomas Wolff
2020-05-14  9:28 ` Takashi Yano
2020-05-14  9:34   ` Takashi Yano
2020-05-16  0:29     ` Takashi Yano
2020-05-16  7:47       ` Takashi Yano
2020-05-19 13:40         ` Takashi Yano
2020-05-25 10:53           ` Takashi Yano
2020-05-25 15:22             ` Corinna Vinschen
2020-05-25 19:16               ` Thomas Wolff
2020-05-26  1:00               ` Takashi Yano
2020-05-26  7:14                 ` Thomas Wolff
2020-05-26  9:21                   ` Takashi Yano
2020-05-26  9:32                     ` Thomas Wolff
2020-05-26  8:33                 ` Corinna Vinschen
2020-05-26  1:09             ` Takashi Yano
2020-05-28 15:40               ` Takashi Yano
2020-05-29 15:30                 ` Corinna Vinschen
2020-05-30  7:36                   ` Takashi Yano
2020-05-30 13:14                     ` Takashi Yano
2020-05-30 17:43                       ` Corinna Vinschen
2020-05-31  5:52                         ` Takashi Yano
2020-07-01 11:47                 ` Takashi Yano
2020-07-17 11:19                   ` Corinna Vinschen
2020-07-17 12:47                     ` Thomas Wolff
2020-07-17 14:59                       ` Thomas Wolff
2020-07-18  5:05                         ` Takashi Yano
2020-07-18 20:57                           ` Thomas Wolff
2020-07-23 17:17                             ` Takashi Yano
2020-07-27 17:10                               ` Thomas Wolff
2020-07-17 12:52                     ` Ken Brown
2020-07-18  5:07                       ` Takashi Yano
2020-07-18  5:30                     ` Takashi Yano
2020-07-20  8:06                       ` Corinna Vinschen
2020-07-21 18:17                         ` Takashi Yano
2020-07-22  8:45                           ` Takashi Yano
2020-07-22 11:49                             ` Corinna Vinschen
2020-07-22 12:13                               ` Ken Brown
2020-07-23  0:33                             ` Takashi Yano
2020-07-24  5:38                               ` Takashi Yano
2020-07-24 11:22                                 ` Takashi Yano
2020-08-02 12:01                                   ` Corinna Vinschen
2020-08-03  2:05                                     ` Takashi Yano
2020-08-03 10:50                                       ` Corinna Vinschen
2020-08-03  2:11                                   ` Takashi Yano
2020-08-03 12:23                                     ` Takashi Yano
2020-08-11 11:12                                       ` Takashi Yano
2020-08-13  9:58                                         ` Takashi Yano
2020-08-17 11:57                                           ` Takashi Yano
2020-08-19 11:39                                             ` Takashi Yano
2020-08-19 13:41                                               ` Corinna Vinschen
2020-08-19 15:43                                                 ` Thomas Wolff
2020-08-19 20:47                                                 ` Mark Geisert
2020-08-20  8:02                                                 ` Takashi Yano
2020-08-31 12:49                                                   ` Johannes Schindelin
2020-08-31 14:14                                                     ` Takashi Yano
     [not found]                                                     ` <20200831231253.332c66fdddb33ceed5f61db6@nifty.ne.jp>
2020-08-31 14:22                                                       ` Johannes Schindelin
2020-08-31 14:53                                                         ` Takashi Yano
2020-08-31 15:56                                                           ` Johannes Schindelin
2020-08-31 16:12                                                             ` Thomas Wolff
2020-08-31 17:39                                                               ` Thomas Wolff
2020-08-31 19:17                                                                 ` Johannes Schindelin
2020-08-31 19:37                                                                   ` Corinna Vinschen
2020-09-01  4:46                                                                     ` Johannes Schindelin
2020-09-01  9:23                                                                       ` Takashi Yano
2020-09-01  6:32                                                                         ` Johannes Schindelin
2020-09-01 22:33                                                                           ` Takashi Yano
2020-09-02  6:13                                                                             ` Johannes Schindelin
2020-09-01  9:42                                                                         ` Takashi Yano
2020-08-31 21:07                                                                   ` Thomas Wolff
2020-08-31 23:23                                                                     ` Takashi Yano
2020-09-01  5:00                                                                     ` Johannes Schindelin
2020-09-01  8:56                                                                       ` 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).