From 28570bd464446eb327b588b623f70f8cf8f46b3d Mon Sep 17 00:00:00 2001 From: Takashi Yano Date: Thu, 27 Nov 2025 15:52:12 +0900 Subject: [PATCH v2] Cygwin: pty: Experimental OpenConsole.exe support Signed-off-by: Takashi Yano --- 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; inat_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