On Thu, 27 Nov 2025 17:20:26 +0900
Takashi Yano via Cygwin <[email protected]> wrote:
> On Wed, 26 Nov 2025 19:50:07 +0100
> Thomas Wolff wrote:
> > 
> > Am 26.11.2025 um 11:32 schrieb Takashi Yano via Cygwin:
> > > On Fri, 21 Nov 2025 17:58:01 +0100
> > > Thomas Wolff wrote:
> > >
> > >> Am 21.11.2025 um 17:46 schrieb Corinna Vinschen via Cygwin:
> > >>> Hi Thomas,
> > >>>
> > >>> On Nov 21 16:46, Thomas Wolff via Cygwin wrote:
> > >>>> Am 21.11.2025 um 11:04 schrieb Takashi Yano via Cygwin:
> > >>>>> On Sun, 5 Oct 2025 10:15:55 +0200
> > >>>>> Thomas Wolff wrote:
> > >>>>>>>> The procedure seems to be:
> > >>>>>>>> try LoadLibrary("conpty.dll"), (if not successful, fallback to
> > >>>>>>>> GetModuleHandle("kernel32") instead) to retrieve GetProcAddress for
> > >>>>>>>> CreatePseudoConsole, ResizePseudoConsole, ClosePseudoConsole, and
> > >>>>>>>> then somehow (?) use those while calling CreateProcess.
> > >>>>>>>> The latter is woven into the cygwin library and I don't think it's 
> > >>>>>>>> a
> > >>>>>>>> good idea to clone that out of cygwin for a patched process 
> > >>>>>>>> creation
> > >>>>>>>> in mintty.
> > >>>>>>>>
> > >>>>>>>> My question/suggestion:
> > >>>>>>>> Can a cygwin mode switch to a selected conpty library instead of 
> > >>>>>>>> the
> > >>>>>>>> default one please?
> > >>> Where do you expect this conpty.dll to reside?
> > >>>
> > >>> If we do something like that, I would suggest to make sure that this
> > >>> conpty.dll is in a well-defined place. /bin or /lib might be a good
> > >>> idea. And if it exists in this defined place, it will be used by Cygwin.
> > >>>
> > >>> We can tweak the autoload mechanism to allow a primary DLL and a 
> > >>> fallback
> > >>> DLL, that drops the need to set the CYWIN env var.
> > >>>
> > >>> The mintty package could contain a post-install script, or some other
> > >>> script in /bin to allow a user with admin rights downloading the latest
> > >>> (or the most sensible) conpty.dll from MSFT.
> > >>>
> > >>> Would that make sense?
> > >>>
> > >>>
> > >>> Corinna
> > >>>
> > >> That makes much sense, yes, thank you. /bin/conpty.dll would probably be
> > >> a good place.
> > > I checked the source of conpty.dll
> > > https://github.com/microsoft/terminal/blob/main/src/winconpty/winconpty.cpp
> > >
> > > conpty.dll seems to launch OpenConsole.exe in the directory where
> > > WindowsTerminal is installed.
> > > However, BUILTIN\Users does not have permission to execute 
> > > OpenConsole.exe.
> > >
> > > Do you have some idea how to solve this problem?
> > > WindowsTerminal itself can launch OpenConsole.exe, so there should be
> > > some solutions, I think.
> > Did you install the nuget package? It can be extracted with zip, so you 
> > can install OpenConsole as a normal program...
> > I did not find a way, though, to invoke it explicitly so that it would 
> > solve the pty interworking problems.
> 
> Thanks.
> 
> I've made an experimental patch against fhandler/pty.cc and
> draft package named openconsole that installs official OpenConsole.exe
> binary into /usr/bin.
> 
> I confirmed the patch with openconsole package enables the mouse
> support for vim91 and neovim 0.8.0 (native windows binary) in
> pseudo console.

pty patch and openconsole package files are revised.

> Any comments and suggestions would be appreciated.


-- 
Takashi Yano <[email protected]>
From 28570bd464446eb327b588b623f70f8cf8f46b3d Mon Sep 17 00:00:00 2001
From: Takashi Yano <[email protected]>
Date: Thu, 27 Nov 2025 15:52:12 +0900
Subject: [PATCH v2] Cygwin: pty: Experimental OpenConsole.exe support

Signed-off-by: Takashi Yano <[email protected]>
---
 winsup/cygwin/fhandler/pty.cc | 281 ++++++++++++++++++++++++++++++----
 1 file changed, 251 insertions(+), 30 deletions(-)

diff --git a/winsup/cygwin/fhandler/pty.cc b/winsup/cygwin/fhandler/pty.cc
index 679068ea2..2c9af1742 100644
--- a/winsup/cygwin/fhandler/pty.cc
+++ b/winsup/cygwin/fhandler/pty.cc
@@ -33,6 +33,136 @@ details. */
 #define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE 0x00020016
 #endif /* PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE */
 
+static NTSTATUS
+create_handle (PHANDLE handle, PCWSTR device_name,
+              ACCESS_MASK desired_access, HANDLE parent,
+              BOOLEAN inheritable, ULONG open_options)
+{
+  ULONG flags = OBJ_CASE_INSENSITIVE;
+  if (inheritable)
+    flags |= OBJ_INHERIT;
+
+  UNICODE_STRING name;
+  RtlInitUnicodeString (&name, device_name);
+
+  OBJECT_ATTRIBUTES object_attributes;
+  InitializeObjectAttributes (&object_attributes, &name, flags, parent, NULL);
+
+  IO_STATUS_BLOCK io;
+  return NtOpenFile (handle, desired_access, &object_attributes, &io,
+                    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                    open_options);
+}
+
+extern "C" WINBASEAPI HRESULT WINAPI
+CreatePseudoConsole_new (COORD size, HANDLE h_input, HANDLE h_output,
+                        DWORD flags, HPCON *hpcon)
+{
+
+  HANDLE h_con_server, h_con_reference;
+  NTSTATUS status;
+  BOOL res;
+  HANDLE h_read_pipe, h_write_pipe;
+  BOOL inherit_cursor;
+  path_conv conhost ("/usr/bin/OpenConsole.exe");
+  size_t len;
+  HANDLE inherited_handles[4];
+  STARTUPINFOEXW si = {0, };
+  PROCESS_INFORMATION pi;
+  SIZE_T list_size = 0;
+  LPPROC_THREAD_ATTRIBUTE_LIST attr_list;
+  HPCON_INTERNAL *hpcon_internal;
+
+  status = create_handle (&h_con_server, L"\\Device\\ConDrv\\Server",
+                         GENERIC_ALL, NULL, TRUE, 0);
+  if (!NT_SUCCESS (status))
+    goto cleanup;
+  status = create_handle (&h_con_reference, L"\\Reference",
+                        GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
+                        h_con_server, FALSE, FILE_SYNCHRONOUS_IO_NONALERT);
+  if (!NT_SUCCESS (status))
+    goto cleanup_h_con_server;
+
+  res = CreatePipe (&h_read_pipe, &h_write_pipe, &sec_none, 0);
+  if (!res)
+    goto cleanup_h_con_reference;
+  res = SetHandleInformation (h_read_pipe,
+                             HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+  if (!res)
+    goto cleanup_pipe;
+
+  inherit_cursor = (flags & PSEUDOCONSOLE_INHERIT_CURSOR) ? TRUE : FALSE;
+
+  WCHAR cmd[MAX_PATH];
+  len = conhost.get_wide_win32_path_len ();
+  conhost.get_wide_win32_path (cmd);
+  __small_swprintf (cmd + len,
+                   L" --headless %W"
+                   "--width %d --height %d --signal 0x%x --server 0x%x",
+                   inherit_cursor ? L"--inheritcursor " : L"",
+                   size.X, size.Y, h_read_pipe, h_con_server);
+
+  si.StartupInfo.cb = sizeof (STARTUPINFOEXW);
+  si.StartupInfo.hStdInput = h_input;
+  si.StartupInfo.hStdOutput = h_output;
+  si.StartupInfo.hStdError = h_output;
+  si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+  inherited_handles[0] = h_con_server;
+  inherited_handles[1] = h_input;
+  inherited_handles[2] = h_output;
+  inherited_handles[3] = h_read_pipe;
+
+  InitializeProcThreadAttributeList (NULL, 1, 0, &list_size);
+  attr_list =
+    (LPPROC_THREAD_ATTRIBUTE_LIST) HeapAlloc (GetProcessHeap (), 0, list_size);
+  if (!attr_list)
+    goto cleanup_pipe;
+
+  si.lpAttributeList = attr_list;
+  InitializeProcThreadAttributeList (si.lpAttributeList, 1, 0, &list_size);
+  UpdateProcThreadAttribute (si.lpAttributeList, 0,
+                            PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+                            inherited_handles, sizeof (inherited_handles),
+                            NULL, NULL);
+
+
+  res = CreateProcessW (NULL, cmd, NULL, NULL,
+                       TRUE, EXTENDED_STARTUPINFO_PRESENT,
+                       NULL, NULL, &si.StartupInfo, &pi);
+  if (!res)
+    goto cleanup_heap;
+
+  hpcon_internal = (HPCON_INTERNAL *)
+    HeapAlloc (GetProcessHeap (), 0, sizeof (HPCON_INTERNAL));
+  if (!hpcon_internal)
+    goto cleanup_heap;
+  hpcon_internal->hWritePipe = h_write_pipe;
+  hpcon_internal->hConDrvReference = h_con_reference;
+  hpcon_internal->hConHostProcess = pi.hProcess;
+  *hpcon = (HPCON) hpcon_internal;
+
+  HeapFree (GetProcessHeap(), 0, attr_list);
+  CloseHandle (h_con_server);
+  CloseHandle (pi.hThread);
+  CloseHandle (pi.hProcess);
+
+  return S_OK;
+
+cleanup_heap:
+  HeapFree (GetProcessHeap(), 0, attr_list);
+cleanup_pipe:
+  CloseHandle (h_read_pipe);
+  CloseHandle (h_write_pipe);
+cleanup_h_con_reference:
+  CloseHandle (h_con_reference);
+cleanup_h_con_server:
+  CloseHandle (h_con_server);
+cleanup:
+  return E_FAIL;
+}
+
+
 extern "C" int sscanf (const char *, const char *, ...);
 
 #define close_maybe(h) \
@@ -2136,6 +2266,8 @@ fhandler_pty_master::close (int flag)
 ssize_t
 fhandler_pty_master::write (const void *ptr, size_t len)
 {
+  size_t towrite = len;
+
   ssize_t ret;
   char *p = (char *) ptr;
   termios &ti = tc ()->ti;
@@ -2152,7 +2284,7 @@ fhandler_pty_master::write (const void *ptr, size_t len)
         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 = strlen ("\033[32768;32868R");
+      static const int wpbuf_len = 64; /* for response to CSI6n nad CSIc */
       static char wpbuf[wpbuf_len];
       static int ixput = 0;
       static int state = 0;
@@ -2170,6 +2302,7 @@ fhandler_pty_master::write (const void *ptr, size_t len)
            }
          if (state == 1)
            {
+             towrite--;
              if (ixput < wpbuf_len)
                wpbuf[ixput++] = p[i];
              else
@@ -2181,21 +2314,27 @@ fhandler_pty_master::write (const void *ptr, size_t len)
                }
            }
          else
-           line_edit (p + i, 1, ti, &ret);
-         if (state == 1 && p[i] == 'R')
+           {
+             if (ixput)
+               line_edit (wpbuf, ixput, ti, &ret);
+             get_ttyp ()->pcon_start = false;
+             ptr = p + i;
+             break;
+           }
+         if (state == 1 && isalpha(p[i]))
            state = 2;
-       }
-      if (state == 2)
-       {
-         /* req_xfer_input is true if "ESC[6n" was sent just for
-            triggering transfer_input() in master. In this case,
-            the responce sequence should not be written. */
-         if (!get_ttyp ()->req_xfer_input)
-           WriteFile (to_slave_nat, wpbuf, ixput, &n, NULL);
-         ixput = 0;
-         state = 0;
-         get_ttyp ()->req_xfer_input = false;
-         get_ttyp ()->pcon_start = false;
+         if (state == 2)
+           {
+             /* req_xfer_input is true if "ESC[6n" was sent just for
+                triggering transfer_input() in master. In this case,
+                the responce sequence should not be written. */
+             if (!get_ttyp ()->req_xfer_input)
+               WriteFile (to_slave_nat, wpbuf, ixput, &n, NULL);
+             if (p[i] == 'R')
+               get_ttyp ()->req_xfer_input = false;
+             ixput = 0;
+             state = 0;
+           }
        }
       ReleaseMutex (input_mutex);
 
@@ -2219,8 +2358,8 @@ fhandler_pty_master::write (const void *ptr, size_t len)
            }
          get_ttyp ()->pcon_start_pid = 0;
        }
-
-      return len;
+      if (towrite == 0)
+       return len;
     }
 
   /* Write terminal input to to_slave_nat pipe instead of output_handle
@@ -2232,14 +2371,14 @@ fhandler_pty_master::write (const void *ptr, size_t len)
         is activated. */
       tmp_pathbuf tp;
       char *buf = (char *) ptr;
-      size_t nlen = len;
+      size_t nlen = towrite;
       if (get_ttyp ()->term_code_page != CP_UTF8)
        {
          static mbstate_t mbp;
          buf = tp.c_get ();
          nlen = NT_MAX_PATH;
          convert_mb_str (CP_UTF8, buf, &nlen,
-                         get_ttyp ()->term_code_page, (const char *) ptr, len,
+                         get_ttyp ()->term_code_page, (const char *) ptr, nlen,
                          &mbp);
        }
 
@@ -2255,8 +2394,50 @@ fhandler_pty_master::write (const void *ptr, size_t len)
            }
        }
 
+      char *bs_pos = (char *) memchr (buf, '\010' /* ^H */, nlen);
+      HANDLE pcon_owner = NULL;
+      HANDLE h_pcon_in = NULL;
+      DWORD resume_pid = 0;
+      if (bs_pos)
+       {
+         pcon_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE,
+                                   get_ttyp ()->nat_pipe_owner_pid);
+         DuplicateHandle (pcon_owner, get_ttyp ()->h_pcon_in,
+                          GetCurrentProcess (), &h_pcon_in,
+                          0, FALSE, DUPLICATE_SAME_ACCESS);
+         resume_pid =
+           attach_console_temporarily (get_ttyp()->nat_pipe_owner_pid);
+       }
+
       DWORD n;
-      WriteFile (to_slave_nat, buf, nlen, &n, NULL);
+      while (bs_pos)
+       {
+         if (bs_pos - buf > 0)
+           WriteFile (to_slave_nat, buf, bs_pos - buf, &n, NULL);
+         INPUT_RECORD r;
+         r.EventType = KEY_EVENT;
+         r.Event.KeyEvent.bKeyDown = 1;
+         r.Event.KeyEvent.wRepeatCount = 0;
+         r.Event.KeyEvent.wVirtualKeyCode = 0;
+         r.Event.KeyEvent.wVirtualScanCode = 0;
+         r.Event.KeyEvent.uChar.AsciiChar = '\010'; /* ^H */
+         r.Event.KeyEvent.dwControlKeyState = LEFT_CTRL_PRESSED;
+         WriteConsoleInput(h_pcon_in, &r, 1, &n);
+         r.Event.KeyEvent.bKeyDown = 0;
+         WriteConsoleInput(h_pcon_in, &r, 1, &n);
+         nlen -= bs_pos - buf + 1;
+         buf = bs_pos + 1;
+         bs_pos = (char *) memchr (buf, '\010' /* ^H */, nlen);
+       }
+      if (nlen > 0)
+       WriteFile (to_slave_nat, buf, nlen, &n, NULL);
+
+      if (resume_pid)
+       resume_from_temporarily_attach (resume_pid);
+      if (h_pcon_in)
+       CloseHandle(h_pcon_in);
+      if (pcon_owner)
+       CloseHandle(pcon_owner);
       ReleaseMutex (input_mutex);
 
       return len;
@@ -2302,11 +2483,30 @@ int
 fhandler_pty_master::tcgetattr (struct termios *t)
 {
   *t = cygwin_shared->tty[get_minor ()]->ti;
-  /* Workaround for rlwrap v0.40 or later */
-  if (get_ttyp ()->pcon_start)
-    t->c_lflag &= ~(ICANON | ECHO);
+
   if (get_ttyp ()->pcon_activated)
-    t->c_iflag &= ~ICRNL;
+    {
+      t->c_lflag &= ~(ICANON | ECHO);
+      t->c_iflag &= ~ICRNL;
+
+      HANDLE pcon_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE,
+                                      get_ttyp ()->nat_pipe_owner_pid);
+      HANDLE h_pcon_in;
+      DuplicateHandle (pcon_owner, get_ttyp ()->h_pcon_in,
+                      GetCurrentProcess (), &h_pcon_in,
+                      0, FALSE, DUPLICATE_SAME_ACCESS);
+      DWORD resume_pid =
+       attach_console_temporarily (get_ttyp()->nat_pipe_owner_pid);
+      DWORD mode;
+      GetConsoleMode (h_pcon_in, &mode);
+      resume_from_temporarily_attach (resume_pid);
+      CloseHandle (h_pcon_in);
+      CloseHandle (pcon_owner);
+      if (mode & ENABLE_LINE_INPUT)
+       t->c_lflag |= ICANON;
+      if (mode & ENABLE_ECHO_INPUT)
+       t->c_lflag |= ECHO;
+    }
   return 0;
 }
 
@@ -2680,7 +2880,7 @@ fhandler_pty_master::pty_master_fwd_thread (const 
master_fwd_thread_param_t *p)
          int state = 0;
          int start_at = 0;
          for (DWORD i=0; i<rlen; i++)
-           if (outbuf[i] == '\033')
+           if (state == 0 && outbuf[i] == '\033')
              {
                start_at = i;
                state = 1;
@@ -2688,12 +2888,14 @@ fhandler_pty_master::pty_master_fwd_thread (const 
master_fwd_thread_param_t *p)
              }
            else if ((state == 1 && outbuf[i] == ']') ||
                     (state == 2 && outbuf[i] == '0') ||
-                    (state == 3 && outbuf[i] == ';'))
+                    (state == 3 && outbuf[i] == ';') ||
+                    (state == 4 && outbuf[i] == '\033'))
              {
                state ++;
                continue;
              }
-           else if (state == 4 && outbuf[i] == '\a')
+           else if ((state == 4 && outbuf[i] == '\a')
+                    || (state == 5 && outbuf[i] == '\\'))
              {
                const char *helper_str = "\\bin\\cygwin-console-helper.exe";
                if (memmem (&outbuf[start_at], i + 1 - start_at,
@@ -3268,9 +3470,14 @@ fhandler_pty_slave::setup_pseudoconsole ()
       const DWORD inherit_cursor = 1;
       hpcon = NULL;
       SetLastError (ERROR_SUCCESS);
-      HRESULT res = CreatePseudoConsole (size, get_handle_nat (),
-                                        get_output_handle_nat (),
-                                        inherit_cursor, &hpcon);
+      /* Try OpenConsole.exe before conhost.exe */
+      HRESULT res = CreatePseudoConsole_new (size, get_handle_nat (),
+                                            get_output_handle_nat (),
+                                            inherit_cursor, &hpcon);
+      if (res != S_OK) /* Fallback to legacy conhost.exe */
+        res = CreatePseudoConsole (size, get_handle_nat (),
+                                  get_output_handle_nat (),
+                                  inherit_cursor, &hpcon);
       if (res != S_OK || GetLastError () == ERROR_PROC_NOT_FOUND)
        {
          if (res != S_OK)
@@ -3627,6 +3834,20 @@ fhandler_pty_slave::close_pseudoconsole (tty *ttyp, 
DWORD force_switch_to)
          ttyp->nat_pipe_owner_pid = 0;
          ttyp->pcon_start = false;
          ttyp->pcon_start_pid = 0;
+         do
+           {
+             pinfo p (ttyp->master_pid);
+             HANDLE pty_master = OpenProcess (PROCESS_DUP_HANDLE, FALSE,
+                                              p->dwProcessId);
+             HANDLE to_master_nat;
+             DuplicateHandle (pty_master, ttyp->to_master_nat (),
+                              GetCurrentProcess (), &to_master_nat,
+                              0, FALSE, DUPLICATE_SAME_ACCESS);
+             WriteFile (to_master_nat, "\033[?1004l", 8, NULL, NULL);
+             CloseHandle (to_master_nat);
+             CloseHandle (pty_master);
+           }
+         while (false);
        }
     }
   else
-- 
2.51.0

if [ $(uname -m) = "x86_64" ]
then
        POSTFIX="x64"
else
        POSTFIX="x86"
fi
VERSION=$(cat /etc/libopenconsole/version.txt)
cd /tmp
wget -q 
https://github.com/microsoft/terminal/releases/download/v${VERSION}/Microsoft.WindowsTerminal_${VERSION}_${POSTFIX}.zip
 -O - > Microsoft.WindowsTerminal_${VERSION}_${POSTFIX}.tmp
if sha256sum --status -c 
/etc/libopenconsole/Microsoft.WindowsTerminal_${VERSION}_${POSTFIX}.zip.sha256
then
        mv Microsoft.WindowsTerminal_${VERSION}_${POSTFIX}.tmp 
Microsoft.WindowsTerminal_${VERSION}_${POSTFIX}.zip
        unzip -jq Microsoft.WindowsTerminal_${VERSION}_${POSTFIX}.zip 
'*/OpenConsole.exe'
        mv OpenConsole.exe /usr/bin/.
else
        # Hash mismatch (or failed to download)
        rm Microsoft.WindowsTerminal_${VERSION}_${POSTFIX}.tmp
        exit 1
fi
chmod a+x /usr/bin/OpenConsole.exe
rm -f /usr/bin/OpenConsole.exe
NAME="openconsole"
VERSION=1.23.12811.0
RELEASE=1
CATEGORY="Libs"
SUMMARY="conhost.exe alternative"
DESCRIPTION="conhost.exe alternative for new features of pusedo console"
HOMEPAGE="https://github.com/microsoft/terminal/";
LICENSE="MIT"
ARCH="noarch" # This is noarch because it's just helper shell scrpits.
SRC_URI="${NAME}-${VERSION}.tar.xz"
CYGWIN_FILES="
        lib${NAME}.postinstall
        lib${NAME}.preremove
"

# Make dummy source file for prep
if [ ! -f ${SRC_URI} ]
then
        mkdir -p ${NAME}-${VERSION}
        tar acf ${SRC_URI} ${NAME}-${VERSION}
        rm -rf ${NAME}-${VERSION}
fi

PKG_NAMES="libopenconsole"
libopenconsole_CONTENTS="etc/"
libopenconsole_REQUIRES="wget unzip"

src_compile() {
        :
}

src_install() {
        mkdir -p ${D}/etc/lib${NAME}
        # Make sha256 hash
        wget -q 
https://github.com/microsoft/terminal/releases/download/v${VERSION}/Microsoft.WindowsTerminal_${VERSION}_x64.zip
 -O - | sha256sum | sed "s/-$/Microsoft.WindowsTerminal_${VERSION}_x64.tmp/" > 
${D}/etc/lib${NAME}/Microsoft.WindowsTerminal_${VERSION}_x64.zip.sha256
        wget -q 
https://github.com/microsoft/terminal/releases/download/v${VERSION}/Microsoft.WindowsTerminal_${VERSION}_x86.zip
 -O - | sha256sum | sed "s/-$/Microsoft.WindowsTerminal_${VERSION}_x86.tmp/" > 
${D}/etc/lib${NAME}/Microsoft.WindowsTerminal_${VERSION}_x86.zip.sha256
        # Make version text
        echo ${VERSION} > ${D}/etc/lib${NAME}/version.txt
}
-- 
Problem reports:      https://cygwin.com/problems.html
FAQ:                  https://cygwin.com/faq/
Documentation:        https://cygwin.com/docs.html
Unsubscribe info:     https://cygwin.com/ml/#unsubscribe-simple

Reply via email to