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