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.

Any comments and suggestions would be appreciated.

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

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

diff --git a/winsup/cygwin/fhandler/pty.cc b/winsup/cygwin/fhandler/pty.cc
index 679068ea2..b9e9ce1a7 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) \
@@ -2182,20 +2312,24 @@ 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 (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')
+               {
+                 if (!get_ttyp ()->req_xfer_input)
+                   get_ttyp ()->pcon_start = false;
+                 get_ttyp ()->req_xfer_input = false;
+               }
+             ixput = 0;
+             state = 0;
+           }
        }
       ReleaseMutex (input_mutex);
 
@@ -2255,8 +2389,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;
@@ -2680,7 +2856,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 +2864,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 +3446,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 +3810,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

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"

# 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
}
diff --git 
a/src/openconsole-1.23.12811.0/CYGWIN-PATCHES/libopenconsole.postinstall 
b/src/openconsole-1.23.12811.0/CYGWIN-PATCHES/libopenconsole.postinstall
new file mode 100644
index 0000000..003eca5
--- /dev/null
+++ b/src/openconsole-1.23.12811.0/CYGWIN-PATCHES/libopenconsole.postinstall
@@ -0,0 +1,20 @@
+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
diff --git 
a/src/openconsole-1.23.12811.0/CYGWIN-PATCHES/libopenconsole.preremove 
b/src/openconsole-1.23.12811.0/CYGWIN-PATCHES/libopenconsole.preremove
new file mode 100644
index 0000000..39e4383
--- /dev/null
+++ b/src/openconsole-1.23.12811.0/CYGWIN-PATCHES/libopenconsole.preremove
@@ -0,0 +1 @@
+rm -f /usr/bin/OpenConsole.exe
-- 
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