Skip to content

Rebase to Cygwin runtime v3.6.9#130

Merged
dscho merged 111 commits intogit-for-windows:mainfrom
dscho:rebase-to-3.6.9
Apr 25, 2026
Merged

Rebase to Cygwin runtime v3.6.9#130
dscho merged 111 commits intogit-for-windows:mainfrom
dscho:rebase-to-3.6.9

Conversation

@dscho
Copy link
Copy Markdown
Member

@dscho dscho commented Apr 23, 2026

This is quite a big one:

  • We backported many Pseudo Console fixes,
  • Tons of updates to the AGENTS.md (also because the original contribution was structured into a basic commit followed by a couple of fixup!s for easier review)
  • Tons of squashed fixes to the UI tests.
  • ee961da (Use MB_CUR_MAX == 6 by default, 2019-11-21) was dropped (as per 4c22b03 (fixup! Use MB_CUR_MAX == 6 by default, 2026-03-04))
  • The GitHub Actions updates squashed fixes targeting MSYS2 and Cygwin into the same commit, which is now fixed.
Range-diff
  • 1: 90c6e2f = 1: 8ff94c8 Fix msys library name in import libraries

  • 2: eb0623c = 2: 53bb5e0 Rename dll from cygwin to msys

  • 3: 2d57400 = 3: 11aaade Add functionality for converting UNIX paths in arguments and environment variables to Windows form for native Win32 applications.

  • 4: 3e3b14f = 4: e848126 Add functionality for changing OS name via MSYSTEM environment variables.

  • 5: 019fb8c = 5: 95031f0 - Move root to /usr. - Change sorting mount points. - By default mount without ACLs. - Can read /etc/fstab with short mount point format.

  • 6: 025bc50 = 6: e3afe9b Instead of creating Cygwin symlinks, use deep copy by default

  • 7: 384856e = 7: 04cef96 Automatically rewrite TERM=msys to TERM=cygwin

  • 8: add7911 = 8: 191e1b8 Do not convert environment for strace

  • 9: 34d346c = 9: d95ef8a strace.cc: Don't set MSYS=noglob

  • 10: e36f87e = 10: d515963 Add debugging for strace make_command_line

  • 11: 42b2b9f = 11: 0f379db strace --quiet: be really quiet

  • 12: 9dd7fc9 = 12: c260ea2 path_conv: special-case root directory to have trailing slash

  • 13: a5545b3 = 13: 2a753ad When converting to a Unix path, avoid double trailing slashes

  • 14: 4774fb2 = 14: ce71ac2 msys2_path_conv: pass PC_NOFULL to path_conv

  • 15: 793bb2a = 15: b2b3e79 path-conversion: Introduce ability to switch off conversion.

  • 16: 0fe7d40 = 16: 3b74813 dcrt0.cc: Untangle allow_glob from winshell

  • 17: da4617d = 17: b58ab85 dcrt0.cc (globify): Don't quote literal strings differently when dos_spec

  • 18: 17bfb25 = 18: ec43e0c Add debugging for build_argv

  • 19: 3d7efd8 = 19: fb2eb43 environ.cc: New facility/environment variable MSYS2_ENV_CONV_EXCL

  • 20: a7b6522 = 20: ca37d02 Introduce the enable_pcon value for MSYS

  • 21: f6c8e15 = 21: 33b14c0 popen: call /usr/bin/sh instead of /bin/sh

  • 22: 4c0a1a8 = 22: d2263a9 Disable the 'cygwin' GitHub workflow

  • 23: 79bf3a1 = 23: cd14361 CI: add a GHA for doing a basic build test

  • 24: 54cf1de = 24: 1f25400 Set up a GitHub Action to keep in sync with Cygwin

  • 25: 4b6770b = 25: 7a40bbe Expose full command-lines to other Win32 processes by default

  • 26: a0c3117 = 26: 9abc090 Add a helper to obtain a function's address in kernel32.dll

  • 27: 752cf15 ! 27: b2413eb Emulate GenerateConsoleCtrlEvent() upon Ctrl+C

    @@ winsup/cygwin/exceptions.cc: details. */
         macros are for access into CONTEXT, the _MC_foo ones for access into
     @@ winsup/cygwin/exceptions.cc: exit_sig:
      dosig:
    -   if (have_execed)
    +   if (have_execed && (ch_spawn.iscygwin () || !is_stop_or_cont (si.si_signo)))
          {
     -      sigproc_printf ("terminating captive process");
     -      if (::cygheap->ctty)
  • 28: e841010 = 28: 5e19738 kill: kill Win32 processes more gently

  • 29: 488d8d8 = 29: 450800b Cygwin: make option for native inner link handling.

  • 30: b89a0ae = 30: a0d53e4 docs: skip building texinfo and PDF files

  • 31: c3d121f = 31: b0fcf02 install-libs: depend on the "toollibs"

  • 32: bbe177f = 32: 4e9612f POSIX-ify the SHELL variable

  • 33: a0f611a = 33: 79885de Handle ORIGINAL_PATH just like PATH

  • 34: 370a8b4 = 34: 2f1036a uname: allow setting the system name to CYGWIN

  • 35: 871d236 = 35: 8fdf344 Pass environment variables with empty values

  • 36: f29cf3d = 36: d064b00 Optionally disallow empty environment values again

  • 37: 443ed30 = 37: 9c78766 build_env(): respect the MSYS environment variable

  • 38: 306baf5 = 38: 66cfb0c Revert "Cygwin: Enable dynamicbase on the Cygwin DLL by default"

  • 39: 35c1c7c = 39: 546c331 Avoid sharing cygheaps across Cygwin versions

  • 40: c9aee64 = 40: adee13c uname: report msys2-runtime commit hash, too

  • 41: 6e9c4de = 41: cb25225 Cygwin: Adjust CWD magic to accommodate for the latest Windows previews

  • -: ------- > 42: aa532e7 Cygwin: Fix segfault when XSAVE area sizes are unaligned

  • 45: 520e5c9 = 43: d399a0d fixup! Add functionality for converting UNIX paths in arguments and environment variables to Windows form for native Win32 applications.

  • 46: ea6fb84 = 44: 1c925ce Mention the extremely useful small_printf() function

  • 47: 37e7445 = 45: 23d0d08 Allow native symlinks to non-existing targets in 'nativestrict' mode

  • 48: 0ee34fb = 46: 38393ae WIP Handle 8-bit characters under LOCALE=C

  • 43: 74ca85b = 47: 18a5a8a Make paths' WCS->MBS conversion explicit

  • 50: 143d323 = 48: 28130fd msys2-runtime: restore fast path for current user primary group

  • 51: 3502563 = 49: 5e1d3bf Change the default base address for x86_64

  • 73: 1e0ff37 ! 50: 53b3656 Add AGENTS.md with comprehensive project context for AI agents

    @@ AGENTS.md (new)
     +- Commit messages should explain context, intent, and justification in prose (not bullet points)
     +- For the rebase workflow, commit messages follow specific patterns (e.g., `Start the merging-rebase to ...`) that tooling depends on — do not alter these patterns
     +
    ++### Cygwin Commit Message Format
    ++
    ++Commits that modify code under `winsup/cygwin/` should follow the Cygwin project's commit message conventions, as established by the upstream maintainers (Corinna Vinschen, Takashi Yano, et al.):
    ++
    ++- **Subject prefix**: `Cygwin: <area>: <description>`, where `<area>` is the subsystem (e.g. `pty`, `flock`, `termios`, `uinfo`, `path`, `spawn`). Example: `Cygwin: pty: Fix jumbled keystrokes by removing the per-keystroke pipe transfer`. Both upper-case and lower-case after the prefix are used upstream; there is no strict rule.
    ++- **`Fixes:` trailer**: When a commit fixes a bug introduced by a specific earlier commit, reference it with `Fixes: <12-char-hash> ("<original subject>")`. Example: `Fixes: acc44e09d1d0 ("Cygwin: pty: Add missing input transfer when switch_to_pcon_in state.")`
    ++- **`Addresses:` trailer**: Reference the user-visible bug report URL. Example: `Addresses: https://github.com/git-for-windows/git/issues/5632`
    ++- **Trailer ordering**: `Addresses:`, then `Fixes:`, then `Assisted-by:` / `Reviewed-by:` / `Reported-by:`, then `Signed-off-by:` last — following the pattern seen in upstream Cygwin commits.
    ++
     +## PTY Architecture — Pipes, State Machine, and Input Routing
     +
     +This section documents the internal architecture of the pseudo-terminal (PTY) implementation in `winsup/cygwin/fhandler/pty.cc`. Understanding this is essential for debugging any issue involving terminal input/output, keystroke handling, signal delivery, and process foreground/background transitions.
    @@ AGENTS.md (new)
     +
     +For **output**, there is a corresponding pair (`to_master` / `to_master_nat`) plus a forwarding thread (`master_fwd_thread`) that copies output from the nat pipe's slave side (`from_slave_nat`) to the cyg pipe's master side (`to_master`), so the terminal emulator (mintty) always reads from one place.
     +
    ++### The Designed Keystroke Lifecycle
    ++
    ++Understanding the full lifecycle of a keystroke is essential. The design intent is that **no keystroke is ever lost**, regardless of what the foreground process does with it. The lifecycle differs between Cygwin and native foreground processes:
    ++
    ++**When a Cygwin process is in the foreground (e.g., bash):**
    ++1. Terminal emulator writes keystroke via `master::write()`
    ++2. `master::write()` calls `line_edit()` which applies POSIX line discipline
    ++3. `accept_input()` writes processed bytes to the cyg pipe
    ++4. The Cygwin slave (bash) reads from the cyg pipe
    ++
    ++**When a native process is in the foreground with pcon active:**
    ++1. Terminal emulator writes keystroke via `master::write()`
    ++2. `master::write()` fast path writes directly to `to_slave_nat` (nat pipe)
    ++3. Conhost (the pseudo console host) reads from the nat pipe, converts the byte stream to `INPUT_RECORD` events, and stores them in its console input buffer
    ++4. If the native process reads stdin: it gets `INPUT_RECORD` events via `ReadConsoleInput()`
    ++5. If the native process does NOT read stdin (common for background tasks): the `INPUT_RECORD` events accumulate in conhost's buffer
    ++6. When the native process exits: `cleanup_for_non_cygwin_app()` calls `transfer_input(to_cyg)`, which reads all pending `INPUT_RECORD` events from the console buffer via `ReadConsoleInputA()`, converts them back to bytes, and writes them to the cyg pipe
    ++7. Bash's readline then receives these bytes as if they had been typed directly
    ++
    ++**Step 6 is critical and easy to overlook.** Keystrokes that go to the nat pipe during a native process's lifetime are NOT consumed by the native app (unless it explicitly reads them). They accumulate in conhost's input buffer and are transferred back to bash at cleanup. The transfer happens via `ReadConsoleInputA()` (raw event reads, not cooked/line-edited), so backspaces, escape sequences, and control characters are preserved as-is.
    ++
    ++**Consequence for debugging:** If keystrokes appear reordered at bash's readline after a native process exits, the problem is that some bytes went to the cyg pipe (directly to readline) while others went to the nat pipe (and were transferred back later). The bytes that went directly arrive first; the transferred bytes arrive second. This split delivery causes reordering. The fix must ensure that ALL keystrokes go through the same pipe during the native process's lifetime.
    ++
     +### The Pseudo Console (pcon)
     +
     +When `MSYS=disable_pcon` is NOT set (the default), the runtime uses Windows' `CreatePseudoConsole()` API to give native console applications a real console to talk to. The pseudo console is created on demand when a non-Cygwin process becomes the foreground process, and torn down when it exits. This is what allows programs like `cmd.exe`, `powershell.exe`, or any MinGW-built program to work correctly inside a mintty terminal, which has no native Win32 console of its own.
    @@ AGENTS.md (new)
     +Key state fields in the `tty` structure (shared memory, in `tty.h`):
     +
     +- **`pcon_activated`** (`bool`): True when a pseudo console is currently active.
    -+- **`pcon_start`** (`bool`): True during pseudo console initialization.
    ++- **`pcon_start`** (`bool`): True during pseudo console initialization (CSI6n exchange).
     +- **`pcon_start_pid`** (`pid_t`): PID of the process that initiated pcon setup.
     +
     +### The Input State Machine
     +
     +The field **`pty_input_state`** (type `xfer_dir`, in `tty.h:137`) tracks which pipe pair currently "owns" the input. It has two values:
     +
    -+- **`to_cyg`**: Input is flowing to the Cygwin pipe. The master's `write()` uses the `line_edit()` → `accept_input()` path, which writes to `to_slave` (cyg pipe).
    ++- **`to_cyg`**: Input is flowing to the Cygwin pipe. The master's `write()` uses the `line_edit()` -> `accept_input()` path, which writes to `to_slave` (cyg pipe).
     +- **`to_nat`**: Input is flowing to the native pipe. The master's `write()` writes directly to `to_slave_nat` (nat pipe), or through the pseudo console.
     +
    -+The state transitions happen via **`transfer_input()`** (pty.cc, around line 3905), which:
    ++The state transitions happen via **`transfer_input()`**, which:
     +1. Reads all pending data from the "source" pipe (the one being abandoned).
     +2. Writes that data into the "destination" pipe (the one being switched to).
     +3. Sets `pty_input_state` to the new direction.
     +
    -+This ensures data already buffered in one pipe is not lost when switching. **Any code that changes `pty_input_state` without calling `transfer_input()` risks losing or reordering data.** This invariant is critical and has been the root cause of past bugs.
    ++This ensures data already buffered in one pipe is not lost when switching.
    ++
    ++**When transferred input goes to the cyg pipe (to_cyg direction),** it must pass through `line_edit()` to apply POSIX line discipline. This is handled by the `input_transferred_to_cyg` event: the slave signals this event after the transfer, and the master's forward thread wakes up, reads the transferred bytes from the cyg pipe, and processes them through `line_edit()`. This ensures consistent line discipline regardless of whether input arrived via direct typing or via transfer.
     +
     +### Related State Fields
     +
    -+- **`switch_to_nat_pipe`** (`bool`): Set to true when a non-Cygwin process is detected in the foreground. This is a prerequisite for `to_be_read_from_nat_pipe()` returning true.
    ++- **`switch_to_nat_pipe`** (`bool`): Set to true when a non-Cygwin process is detected in the foreground. This is a prerequisite for `to_be_read_from_nat_pipe()` returning true. It stays true for the entire duration of the native session, including during brief transitions when `pcon_activated` may flicker.
     +- **`nat_pipe_owner_pid`** (`DWORD`): PID of the process that "owns" the nat pipe setup. Used to detect when the owner has exited (for cleanup).
     +
     +### The `to_be_read_from_nat_pipe()` Function
     +
    -+This function (pty.cc, around line 1288) determines whether the current foreground process is a native (non-Cygwin) app. It checks:
    ++This function determines whether keystroke input should go to the nat pipe. Its design intent is simple: return true whenever a native process session is active (`switch_to_nat_pipe` is set) and no Cygwin process is actively reading from the slave (the `TTY_SLAVE_READING` event does not exist).
     +
    -+1. `switch_to_nat_pipe` must be true.
    -+2. A named event `TTY_SLAVE_READING` must NOT exist (its existence means a Cygwin process is actively reading from the slave, indicating a Cygwin foreground).
    -+3. `nat_fg(pgid)` returns true (the foreground process group contains a native process).
    ++The function is synchronized with `pipe_sw_mutex` to avoid reading inconsistent state during pipe switching. If the mutex cannot be acquired and `pcon_start` is set (meaning pseudo console initialization is in progress), the function returns false so that the CSI6n response bytes go through `line_edit()` to the cyg pipe where the initialization code expects them.
     +
    -+**This function reads shared state without holding any mutex.** Its return value can therefore change between consecutive calls within the same function, which is an important consideration for callers that make multiple decisions based on the foreground state.
    ++**Important design principle:** This function should NOT check `nat_fg()` (whether the native process is still in the foreground process group). Such a check creates a gap between native process exit and cleanup where keystrokes fall through to `line_edit()` (cyg pipe) instead of going to the nat pipe. This gap causes keystroke reordering: bytes that go directly to the cyg pipe during the gap arrive at readline before bytes that are transferred from the nat pipe at cleanup. The correct approach is to keep routing to the nat pipe as long as `switch_to_nat_pipe` is set, regardless of the native process's foreground status. The `switch_to_nat_pipe` flag is only cleared during cleanup, after `transfer_input(to_cyg)` has moved all pending data back to the cyg pipe.
     +
     +### Mutexes and Synchronization
     +
    -+Two cross-process named mutexes protect different aspects of the PTY state. Understanding which mutex protects what — and the fact that they are independent — is essential for diagnosing race conditions.
    ++Three cross-process named mutexes protect different aspects of the PTY state:
     +
     +- **`input_mutex`**: Protects the input data path. Held by `master::write()` while routing input to a pipe, by `transfer_input()` while moving data between pipes, and by `line_edit()` / `accept_input()`.
    -+- **`pipe_sw_mutex`**: Protects pipe switching state — creation/destruction of the pseudo console, changes to `switch_to_nat_pipe`, `nat_pipe_owner_pid`. This is a DIFFERENT mutex from `input_mutex`.
    ++- **`pipe_sw_mutex`**: Protects pipe switching state — creation/destruction of the pseudo console, changes to `switch_to_nat_pipe`, `nat_pipe_owner_pid`. Also acquired by `to_be_read_from_nat_pipe()` to read consistent state. The consistent lock ordering is: `pipe_sw_mutex` first, then `input_mutex`.
    ++- **`attach_mutex`**: Protects console attachment/detachment operations. Used during `transfer_input()` to prevent races when reading console input records via `ReadConsoleInputA()`, and in `get_winpid_to_hand_over()` to prevent the master process from being misidentified during temporary console attachment.
    ++
    ++Because these are **cross-process** named mutexes, they are shared via the kernel between the master (terminal emulator) and slave (bash and its children) processes. Operations that look local in the source code actually have system-wide synchronization effects.
    ++
    ++### The `master::write()` Input Routing
    ++
    ++When the terminal emulator (mintty) sends a keystroke, it calls `master::write()`. The function has three code paths:
     +
    -+Because these are separate mutexes, it is possible for one process to modify the pipe switching state (under `pipe_sw_mutex`) while another process is in the middle of writing input (under `input_mutex`). Any code that modifies `pty_input_state` or `pcon_activated` must carefully consider whether it also needs `input_mutex` to avoid creating a window where the master's write path makes inconsistent decisions.
    ++1. **pcon_start handler**: Active during pseudo console initialization (CSI6n exchange). Accumulates ESC sequence bytes and routes the CSI6n response to the slave. Non-response bytes go through `line_edit()`. This path is only active during the brief initialization window.
     +
    -+Additionally, because these are **cross-process** named mutexes, they are shared via the kernel between the master (terminal emulator) and slave (bash and its children) processes. Operations that look local in the source code actually have system-wide synchronization effects.
    ++2. **Fast path** (pcon+nat): Active when `to_be_read_from_nat_pipe()` AND `pcon_activated` AND `pty_input_state == to_nat`. Writes directly to `to_slave_nat` via `WriteFile()`, with signal processing and charset conversion. This is the steady-state path for native apps.
     +
    -+### The `master::write()` Input Routing (pty.cc, around line 2240)
    ++3. **Fallthrough** (`line_edit`): All other cases. Input goes through POSIX line discipline and `accept_input()` routes to the appropriate pipe based on `pty_input_state`.
     +
    -+When the terminal emulator (mintty) sends a keystroke, it calls `master::write()`. After acquiring `input_mutex`, the function decides which path to take:
    ++### Pseudo Console Oscillation
     +
    -+1. **Path 1 — pcon+nat** (line ~2245): If `to_be_read_from_nat_pipe()` AND `pcon_activated` AND `pty_input_state == to_nat` → write directly to `to_slave_nat`. This is the fast path for native apps with pcon.
    ++When a native process spawns short-lived Cygwin children (e.g. `git.exe` calling `cygpath` via `--format`), the pseudo console activates and deactivates in rapid succession:
     +
    -+2. **Path 2 — non-pcon transfer** (line ~2288): If `to_be_read_from_nat_pipe()` AND NOT `pcon_activated` AND `pty_input_state == to_cyg` → call `transfer_input(to_nat)` to move cyg pipe data to nat pipe, then fall through to line_edit.
    ++1. Native process in foreground: `pcon_activated=true`, `pty_input_state=to_nat`
    ++2. Cygwin child starts: `setpgid_aux()` fires, transfers data to cyg pipe, `pcon_activated=false`, `pty_input_state=to_cyg`
    ++3. Cygwin child exits (milliseconds later): native process regains foreground, pcon reactivates
     +
    -+3. **Path 3 — line_edit** (line ~2300): The default/fallthrough path. Calls `line_edit()` which processes the input through terminal line discipline and then calls `accept_input()`, which writes to either the cyg or nat pipe based on the current `pty_input_state`.
    ++**The key design principle for handling oscillation:** keystrokes must always go to the nat pipe while `switch_to_nat_pipe` is true, regardless of `pcon_activated` or foreground status flickering. When keystrokes reach the nat pipe while the pcon is temporarily deactivated, they go through the raw pipe (not via conhost). When `transfer_input` runs at cleanup, it moves them back. This is safe because the keystrokes stay in the nat pipe in chronological order.
     +
    -+The conditions checked at each step involve multiple shared-memory fields (`to_be_read_from_nat_pipe()`, `pcon_activated`, `pty_input_state`). If any of these fields changes between consecutive calls to `master::write()` — or worse, between the check and the write within a single call — input can end up in the wrong pipe.
    ++The bugs that cause keystroke reordering are always of the form: some bytes go to the cyg pipe (via `line_edit` fallthrough) while others go to the nat pipe (via the fast path), and the two sets arrive at bash's readline in the wrong order. The fix is to prevent the split: either ALL bytes go to one pipe, or the routing decision is properly synchronized so that no bytes leak to the wrong pipe.
     +
     +### Key Functions for State Transitions
     +
    -+- **`setup_for_non_cygwin_app()`** (~line 4150): Called when a non-Cygwin process becomes foreground. Sets up the pseudo console and switches input to nat pipe.
    -+- **`cleanup_for_non_cygwin_app()`** (~line 4184): Called when the non-Cygwin process exits. Tears down pcon, transfers input back to cyg pipe.
    -+- **`reset_switch_to_nat_pipe()`** (~line 1091): Cleanup function called from various slave-side operations (e.g., `bg_check()`, `setpgid_aux()`). Detects when the nat pipe owner has exited and resets state. This function is particularly subtle because it runs in the slave process and modifies shared state that the master relies on.
    -+- **`mask_switch_to_nat_pipe()`** (~line 1249): Temporarily masks/unmasks the nat pipe switching. Used when a Cygwin process starts/stops reading from the slave.
    -+- **`setpgid_aux()`** (~line 4214): Called when the foreground process group changes. May trigger pipe switching.
    ++- **`setup_for_non_cygwin_app()`**: Called when a non-Cygwin process is about to be spawned. Sets up the pseudo console and switches input to nat pipe. Holds `pipe_sw_mutex` during the entire setup to prevent the master from seeing inconsistent state.
    ++- **`cleanup_for_non_cygwin_app()`**: Called when the non-Cygwin process exits. First calls `transfer_input(to_cyg)` to move all pending input from the nat pipe (conhost's console buffer) back to the cyg pipe. Then tears down the pcon via `close_pseudoconsole()`. The transfer must happen BEFORE the pcon is closed (while the console is still accessible).
    ++- **`reset_switch_to_nat_pipe()`**: Cleanup function called from `bg_check()` and `setpgid_aux()`. Detects when the nat pipe owner has exited and resets state. Only performs cleanup when no other process owns the nat pipe and the owner is dead. Does NOT clean up when the owner is self (bash) or alive, to avoid tearing down active sessions.
    ++- **`transfer_input()`**: Moves pending data between the cyg and nat pipes. When transferring to cyg with pcon active, reads `INPUT_RECORD` events from the console via `ReadConsoleInputA()`. When transferring to cyg, signals `input_transferred_to_cyg` so the master's forward thread can apply `line_edit()` to the transferred bytes.
    ++- **`setpgid_aux()`**: Called when the foreground process group changes. Triggers `transfer_input` in the appropriate direction. Releases `pipe_sw_mutex` before acquiring `input_mutex` to maintain consistent lock ordering.
     +
     +### Debugging Tips
     +
     +When investigating PTY-related bugs, keep these patterns in mind:
     +
    -+- **Data in two pipes**: If characters are lost, duplicated, or reordered, check whether data ended up split across the cyg and nat pipes due to a state transition during input.
    -+- **Cross-process state changes**: The master and slave processes share state through the `tty` structure in shared memory. A state change in the slave (e.g., `reset_switch_to_nat_pipe()`) is immediately visible to the master, without any notification. Look for races where the master reads state, acts on it, but the state changed between the read and the action.
    -+- **Mutex coverage gaps**: Check whether every modification of `pty_input_state`, `pcon_activated`, and `switch_to_nat_pipe` is protected by the appropriate mutex. The existence of two separate mutexes (`input_mutex` and `pipe_sw_mutex`) means that holding one does not protect against changes guarded by the other.
    -+- **`transfer_input()` must accompany state changes**: Whenever `pty_input_state` is changed, any data buffered in the old pipe must be transferred to the new one. Forgetting this step causes data loss or reordering.
    -+- **Tracing**: For timing-sensitive bugs, in-process tracing with lock-free per-thread buffers (using Windows TLS and `QueryPerformanceCounter`) is effective. Avoid file I/O during reproduction — accumulate in memory and dump at process exit. See the `ui-tests/` directory for AutoHotKey-based reproducers that can drive mintty programmatically.
    ++- **Trace the full keystroke lifecycle**: Do not stop at "the keystroke goes to pipe X." Follow it all the way to where bash's readline receives it, including any `transfer_input` calls at cleanup. The most common bugs involve bytes being split across the two pipes and arriving at readline out of order.
    ++- **Check the routing decision in `to_be_read_from_nat_pipe()`**: This function is the gatekeeper for all routing decisions. If it returns the wrong value, keystrokes go to the wrong pipe. Verify that it holds `pipe_sw_mutex` while reading state, and that it does not have unnecessary checks (like `nat_fg()`) that create gaps during transitions.
    ++- **Study existing upstream patches before writing fixes**: Takashi Yano is the upstream PTY maintainer and understands the state machine deeply. When he proposes patches on cygwin-patches@, apply and test his full series before attempting alternative fixes. His patches form cohesive sets where individual patches depend on each other for correct behavior. Cherry-picking individual patches from his series will break invariants.
    ++- **Never remove `transfer_input()` calls without understanding what they transfer**: The transfers at `setpgid_aux()`, `cleanup_for_non_cygwin_app()`, and the pcon_start completion block each serve specific purposes. Removing them loses data. The correct fix for reordering bugs is to ensure keystrokes consistently go to one pipe (typically by fixing the routing decision), not to remove the transfer that reunites split data.
    ++- **The `pcon_start` handler is only for CSI6n**: During pcon initialization, `pcon_start=true` tells `master::write()` to enter a special handler that accumulates the CSI6n response. Non-CSI bytes in this handler go through `line_edit()` to the cyg pipe. This is correct and intentional: during the brief CSI6n exchange, the pcon is not yet ready to receive user input, so `line_edit()` buffers it for bash. The pcon_start handler is NOT a general-purpose input router and should not be modified to route bytes to the nat pipe.
    ++- **Tracing**: For timing-sensitive bugs, use a memory-mapped ring buffer (not per-event file I/O, which changes timings). The master process (mintty) is a MinGW program; C++ static destructors in msys-2.0.dll do NOT fire when it exits. Use `CreateFileMapping` + `MapViewOfFile` for trace buffers that persist after process termination. Use `QueryPerformanceCounter` for microsecond timestamps. Trace across both master and slave processes using separate per-PID files.
     +
     +## Packaging
     +
    @@ AGENTS.md (new)
     +- **Cygwin project**: https://cygwin.com — upstream source, FAQ, user's guide
     +- **Cygwin source**: https://github.com/cygwin/cygwin (mirror of `sourceware.org/git/newlib-cygwin.git`)
     +- **Cygwin announcements**: https://inbox.sourceware.org/cygwin-announce — release announcements
    ++- **Cygwin mailing lists**: https://inbox.sourceware.org/cygwin/ (general), https://inbox.sourceware.org/cygwin-patches/ (patches), https://inbox.sourceware.org/cygwin-developers/ (internals) — essential for understanding why specific code was added; commit messages often reference these discussions
     +- **MSYS2 project**: https://www.msys2.org — documentation, package management
     +- **MSYS2 runtime source**: https://github.com/msys2/msys2-runtime
     +- **MSYS2 packages**: https://github.com/msys2/MSYS2-packages — package recipes including `msys2-runtime`
  • 66: 40e2b5d ! 51: 4bd00d6 Cygwin: CI: update Actions versions

    @@ Metadata
      ## Commit message ##
         Cygwin: CI: update Actions versions
     
    -    Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
    -    - [Release notes](https://github.com/actions/checkout/releases)
    -    - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
    -    - [Commits](https://github.com/actions/checkout/compare/v5...v6)
    -
    -    Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
    -    - [Release notes](https://github.com/actions/upload-artifact/releases)
    -    - [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)
    +    Essentially, all of these major version updates bump the requirements to
    +    Node.JS 24.
     
         Originally-authored-by: dependabot[bot] <support@github.com>
         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
    @@ .github/workflows/cygwin.yml: jobs:
            HAS_SSH_KEY: ${{ secrets.SSH_KEY != '' }}
      
          steps:
    --    - uses: actions/checkout@v5
    +-    - uses: actions/checkout@v3
     +    - uses: actions/checkout@v6
      
          # install build tools
    @@ .github/workflows/cygwin.yml: jobs:
            run: |
              icacls . /inheritance:r
              icacls . /grant Administrators:F
    --    - uses: actions/checkout@v5
    +-    - uses: actions/checkout@v3
     +    - uses: actions/checkout@v6
      
          # install cygwin and build tools
    @@ .github/workflows/cygwin.yml: jobs:
      
          # upload test logs to facilitate investigation of problems
          - name: Upload test logs
    --      uses: actions/upload-artifact@v5
    +-      uses: actions/upload-artifact@v4
     +      uses: actions/upload-artifact@v6
            with:
              name: testlogs
  • 42: 63747e4 ! 52: b72ad68 fixup! CI: add a GHA for doing a basic build test

    @@ .github/workflows/build.yaml: jobs:
              with:
                name: install
                path: ${{ steps.msys2.outputs.msys2-location }}
    -
    - ## .github/workflows/cygwin.yml ##
    -@@ .github/workflows/cygwin.yml: jobs:
    -       HAS_SSH_KEY: ${{ secrets.SSH_KEY != '' }}
    - 
    -     steps:
    --    - uses: actions/checkout@v3
    -+    - uses: actions/checkout@v5
    - 
    -     # install build tools
    -     - name: Install build tools
    -@@ .github/workflows/cygwin.yml: jobs:
    -       run: |
    -         icacls . /inheritance:r
    -         icacls . /grant Administrators:F
    --    - uses: actions/checkout@v3
    -+    - uses: actions/checkout@v5
    - 
    -     # install cygwin and build tools
    -     - name: Install Cygwin
  • 44: ee9f333 = 53: 9e748a0 fixup! CI: add a GHA for doing a basic build test

  • 49: ee961da < -: ------- Use MB_CUR_MAX == 6 by default

  • 52: 7292892 = 54: 11e29af dependabot: help keeping GitHub Actions versions up to date

  • 53: 0871d6d = 55: c18d12c Do not try to sync with Cygwin

  • 54: 3a476d9 = 56: a85ceb4 ci: run Git's entire test suite

  • 55: ad7fedc ! 57: 7eb77ae Start implementing UI-based tests by adding an AutoHotKey library

    @@ ui-tests/ui-test-library.ahk (new)
     +    return Result
     +}
     +
    -+; This function is quite the hack. It assumes that the Windows Terminal is the active window,
    -+; then drags the mouse diagonally across the window to select all text and then copies it.
    -+;
    -+; This is fragile! If any other window becomes active, or if the mouse is moved,
    -+; the function will not work as intended.
    -+;
    -+; An alternative would be to use `ControlSend`, e.g.
    -+; `ControlSend '+^a', 'Windows.UI.Input.InputSite.WindowClass1', 'ahk_id ' . hwnd
    -+; This _kinda_ works, the text is selected (all text, in fact), but the PowerShell itself
    -+; _also_ processes the keyboard events and therefore they leave ugly and unintended
    -+; `^Ac` characters in the prompt. So that alternative is not really usable.
    -+CaptureTextFromWindowsTerminal(winTitle := '') {
    ++; Capture the Windows Terminal buffer via the exportBuffer action (Ctrl+Shift+F12).
    ++; Requires a portable WT with settings.json that maps Ctrl+Shift+F12 to exportBuffer
    ++; writing to <script-dir>/wt-buffer-export.txt.
    ++CaptureBufferFromWindowsTerminal(winTitle := '') {
    ++    static exportFile := A_ScriptDir . '\wt-buffer-export.txt'
    ++    if FileExist(exportFile)
    ++        FileDelete exportFile
     +    if winTitle != ''
     +        WinActivate winTitle
    -+    ControlGetPos &cx, &cy, &cw, &ch, 'Windows.UI.Composition.DesktopWindowContentBridge1', "A"
    -+    titleBarHeight := 54
    -+    scrollBarWidth := 28
    -+    pad := 8
    -+
    -+    SavedClipboard := ClipboardAll
    -+    A_Clipboard := ''
    -+    SendMode('Event')
    -+    if winTitle != ''
    -+        WinActivate winTitle
    -+    MouseMove cx + pad, cy + titleBarHeight + pad
    -+    if winTitle != ''
    -+        WinActivate winTitle
    -+    MouseClickDrag 'Left', , , cx + cw - scrollBarWidth, cy + ch - pad, , ''
    -+    if winTitle != ''
    -+        WinActivate winTitle
    -+    MouseClick 'Right'
    -+    ClipWait()
    -+    Result := A_Clipboard
    -+    Clipboard := SavedClipboard
    -+    return Result
    ++    Sleep 200
    ++    Send '^+{F12}'
    ++    deadline := A_TickCount + 3000
    ++    while !FileExist(exportFile) && A_TickCount < deadline
    ++        Sleep 50
    ++    if !FileExist(exportFile)
    ++        return ''
    ++    Sleep 100
    ++    return FileRead(exportFile)
     +}
     +
     +WaitForRegExInWindowsTerminal(regex, errorMessage, successMessage, timeout := 5000, winTitle := '') {
    @@ ui-tests/ui-test-library.ahk (new)
     +    ; Wait for the regex to match in the terminal output
     +    while true
     +    {
    -+        capturedText := CaptureTextFromWindowsTerminal(winTitle)
    ++        capturedText := CaptureBufferFromWindowsTerminal(winTitle)
     +        if RegExMatch(capturedText, regex)
     +            break
     +        Sleep 100
    @@ ui-tests/ui-test-library.ahk (new)
     +            Info('Captured text:`n' . capturedText)
     +            ExitWithError errorMessage
     +        }
    -+        if winTitle != ''
    -+            WinActivate winTitle
    -+        MouseClick 'WheelDown', , , 20
     +    }
     +    Info(successMessage)
     +}
  • 74: 78d4be1 ! 58: 2a344f8 amend! Start implementing UI-based tests by adding an AutoHotKey library

    @@ Metadata
     Author: Johannes Schindelin <johannes.schindelin@gmx.de>
     
      ## Commit message ##
    -    amend! Start implementing UI-based tests by adding an AutoHotKey library
    -
         Start implementing UI-based tests by adding an AutoHotKey library
     
         AutoHotKey is not only a convenient way to add keyboard shortcuts for
    @@ ui-tests/ui-test-library.ahk: CleanUpWorkTree() {
      }
      
      closeWindow := false
    +@@ ui-tests/ui-test-library.ahk: WaitForRegExInWindowsTerminal(regex, errorMessage, successMessage, timeout := 50
    +     while true
    +     {
    +         capturedText := CaptureBufferFromWindowsTerminal(winTitle)
    +-        if RegExMatch(capturedText, regex)
    +-            break
    ++        if RegExMatch(capturedText, regex, &matchObj)
    ++        {
    ++            Info(successMessage)
    ++            return matchObj
    ++        }
    +         Sleep 100
    +         if A_TickCount > timeout {
    +             Info('Captured text:`n' . capturedText)
    +             ExitWithError errorMessage
    +         }
    +     }
    +-    Info(successMessage)
    + }
    + \ No newline at end of file
  • 56: ec92134 ! 59: ae49b26 ci: add an AutoHotKey-based integration test

    @@ Commit message
           quite a bit to get the syntax right.
     
         - Windows Terminal does not use classical Win32 controls that AutoHotKey
    -      knows well. In particular, there is no easy way to capture the text
    -      that is shown in the Terminal. I tried the (pretty excellent!) [OCR
    -      for AutoHotKey](https://github.com/Descolada/OCR), but it uses UWP OCR
    -      which does not recognize constructs like "C:\Users

unneradmin>"

  •  because it is not English (or any other human language). I ended up
    
  •  with a pretty inelegant method of selecting the text via mouse
    
  •  movements and then copying that into the clipboard. This stops
    
  •  scrolling and I worked around that by emulating the mouse wheel
    
  •  afterwards.
    
    • Since Windows Terminal does not use classical Win32 controls, it is
  •  relatively hard to get to the exact bounding box of the text, as there
    
  •  is no convenient way to determine the size of the title bar or the
    
  •  amount of padding around the text. I ended up hard-coding those
    
  •  values, I'm not proud of that, but at least it works.
    
  •  knows well. To capture the terminal text, we use Windows Terminal's
    
  •  exportBuffer action, which writes the entire scrollback to a file on a
    
  •  keybinding (Ctrl+Shift+F12). This requires running Windows Terminal in
    
  •  portable mode with a settings.json that defines the action, which the
    
  •  setup script takes care of.
    
    • Despite my expectations, ExitApp would not actually exit AutoHotKey
      before the spawned process exits and/or the associated window is
      @@ Commit message
      For good measure, run this test both on windows-2022 (corresponding to
      Windows 10) and on windows-2025 (corresponding to Windows 11).
  • Note that this does not use the naive method to capture text from

  • Windows Terminal, emulating mouse movements, dragging across the entire

  • window with finicky pixel calculations for title bar height, scroll bar

  • width and padding, then right-clicks to copy. This would be fragile:

  • if the window geometry changes, if another window gets focus, or if the

  • title bar height differs between OS versions, the capture silently gets

  • the wrong text.

  • Windows Terminal's exportBuffer action avoids all of that by writing the

  • complete scrollback buffer to a file on a keybinding, with no dependence

  • on pixel positions or window focus. To use it, Windows Terminal must

  • run in portable mode with a settings.json that defines the action and

  • keybinding. So that's what we do here.

  • We add setup-portable-wt.ps1, which downloads Windows Terminal

  • (when not already present), creates the .portable marker and writes

  • settings.json with Ctrl+Shift+F12 bound to exportBuffer. It accepts

  • a -DestDir parameter so CI can use $RUNNER_TEMP while local

  • development uses $TEMP. When running inside GitHub Actions it also

  • appends the Windows Terminal directory to $GITHUB_PATH.

  • Co-authored-by: Eu-Pin Tien eu-pin.tien@diamond.ac.uk

  • Assisted-by: Claude Opus 4.6
    Signed-off-by: Johannes Schindelin johannes.schindelin@gmx.de

  • Converted ui-tests workflow into a test matrix that uses both the windows-2022 and windows-2025 runners.
  • .github/workflows/build.yaml

@@ .github/workflows/build.yaml: jobs:
     with:

@@ .github/workflows/ui-tests.yml (new)
+ $p = Get-ChildItem -Recurse "${env:RUNNER_TEMP}�rtifacts" | where {$_.Name -eq "msys-2.0.dll"} | Select -ExpandProperty VersionInfo | Select -First 1 -ExpandProperty FileName
+ cp $p "c:/Program Files/Git/usr/bin/msys-2.0.dll"
+
++ - uses: actions/checkout@v6
++ with:
++ sparse-checkout: |
++ ui-tests
++
+ - uses: actions/cache/restore@v5
+ id: restore-wt
+ with:
+ key: wt-${{ env.WT_VERSION }}
+ path: ${{ runner.temp }}/wt.zip
-+ - name: Download Windows Terminal
-+ if: steps.restore-wt.outputs.cache-hit != 'true'
-+ shell: bash
++ - name: Install and configure portable Windows Terminal
++ working-directory: ui-tests
+ run: |
-+ curl -fLo "$RUNNER_TEMP/wt.zip"
-+ https://github.com/microsoft/terminal/releases/download/v$WT_VERSION/Microsoft.WindowsTerminal_${WT_VERSION}_x64.zip
++ powershell -File setup-portable-wt.ps1 -WtVersion $env:WT_VERSION -DestDir $env:RUNNER_TEMP
+ - uses: actions/cache/save@v5
+ if: steps.restore-wt.outputs.cache-hit != 'true'
+ with:
+ key: wt-${{ env.WT_VERSION }}
+ path: ${{ runner.temp }}/wt.zip
-+ - name: Install Windows Terminal
-+ shell: bash
-+ working-directory: ${{ runner.temp }}
-+ run: |
-+ "$WINDIR/system32/tar.exe" -xf "$RUNNER_TEMP/wt.zip" &&
-+ cygpath -aw terminal-$WT_VERSION >>$GITHUB_PATH
+ - uses: actions/cache/restore@v5
+ id: restore-ahk
+ with:
@@ .github/workflows/ui-tests.yml (new)
+ cygpath -aw "$RUNNER_TEMP/ahk" >>$GITHUB_PATH
+ - uses: actions/setup-node@v6 # the hook uses node for the background process
+
-+ - uses: actions/checkout@v6
-+ with:
-+ sparse-checkout: |
-+ ui-tests
+ - name: Run UI tests
+ id: ui-tests
+ timeout-minutes: 10
@@ .github/workflows/ui-tests.yml (new)
+ $exitCode = 0
+ & "${env:RUNNER_TEMP}�hk\AutoHotKey64.exe" /ErrorStdOut /force ui-tests�ackground-hook.ahk "$PWD�g-hook" 2>&1 | Out-Default
+ if (!$?) { $exitCode = 1; echo "::error::Test failed!" } else { echo "::notice::Test log" }
-+ type bg-hook.log
+ exit $exitCode
+ - name: Show logs
+ if: always()
@@ .github/workflows/ui-tests.yml (new)
## ui-tests/.gitattributes (new) ##
@@
+.ahk eol=lf
++
.ps1 eol=lf

 ## ui-tests/background-hook.ahk (new) ##
@@

@@ ui-tests/background-hook.ahk (new)
+; Verify that CursorUp shows the previous command
+Send('{Up}')
+Sleep 150
-+Text := CaptureTextFromWindowsTerminal()
++Text := CaptureBufferFromWindowsTerminal()
+if not RegExMatch(Text, 'git commit --allow-empty -m zOMG *$')
+ ExitWithError 'Cursor Up did not work: ' Text
+Info('Match!')
@@ ui-tests/background-hook.ahk (new)
+Sleep 50
+CleanUpWorkTree()
\ No newline at end of file
+

  • ui-tests/setup-portable-wt.ps1 (new)

+@@
++# Configures a portable Windows Terminal for the UI tests.
++#
++# Downloads WT if needed, then creates .portable marker and settings.json
++# with exportBuffer bound to Ctrl+Shift+F12. The export file lands in the
++# script's own directory (ui-tests/) so it gets uploaded as build artifact.
++#
++# The portable WT uses its own settings directory (next to the executable)
++# so it never touches the user's installed Windows Terminal configuration.
++
++param(
++ [string]$WtVersion = $env:WT_VERSION,
++ [string]$DestDir = $env:TEMP
++)
++
++if (-not $WtVersion) { $WtVersion = '1.22.11141.0' }
++
++$wtDir = "$DestDir erminal-$WtVersion"
++$wtExe = "$wtDir\wt.exe"
++
++# Download if the directory doesn't contain wt.exe yet
++if (-not (Test-Path $wtExe)) {
++ $wtZip = "$DestDir\wt.zip"
++ if (-not (Test-Path $wtZip)) {
++ $url = "https://github.com/microsoft/terminal/releases/download/v$WtVersion/Microsoft.WindowsTerminal_${WtVersion}_x64.zip"
++ Write-Host "Downloading Windows Terminal $WtVersion ..."
++ curl.exe -fLo $wtZip $url
++ if ($LASTEXITCODE -ne 0) { throw "Download failed" }
++ }
++ Write-Host "Extracting ..."
++ & "$env:WINDIR\system32 ar.exe" -C $DestDir -xf $wtZip
++ if ($LASTEXITCODE -ne 0) { throw "Extract failed" }
++}
++
++# Create .portable marker so WT reads settings from settings\ next to wt.exe
++$portableMarker = "$wtDir.portable"
++if (-not (Test-Path $portableMarker)) {
++ Set-Content -Path $portableMarker -Value ""
++}
++
++# Write settings.json with exportBuffer action
++$settingsDir = "$wtDir\settings"
++if (-not (Test-Path $settingsDir)) { New-Item -ItemType Directory -Path $settingsDir -Force | Out-Null }
++
++$bufferExportPath = ($PSScriptRoot + '\wt-buffer-export.txt') -replace '', '/'
++
++$settings = @"
++{
++ "`$schema": "https://aka.ms/terminal-profiles-schema",
++ "actions": [
++ {
++ "command": {
++ "action": "exportBuffer",
++ "path": "$bufferExportPath"
++ },
++ "id": "User.TestExportBuffer"
++ },
++ {
++ "command": { "action": "copy", "singleLine": false },
++ "id": "User.copy"
++ },
++ { "command": "paste", "id": "User.paste" }
++ ],
++ "copyFormatting": "none",
++ "copyOnSelect": false,
++ "defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
++ "keybindings": [
++ { "id": "User.TestExportBuffer", "keys": "ctrl+shift+f12" },
++ { "id": null, "keys": "ctrl+v" },
++ { "id": null, "keys": "ctrl+c" }
++ ],
++ "profiles": {
++ "defaults": {},
++ "list": [
++ {
++ "commandline": "%SystemRoot%\System32\WindowsPowerShell�1.0\powershell.exe",
++ "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
++ "hidden": false,
++ "name": "Windows PowerShell"
++ }
++ ]
++ },
++ "schemes": [],
++ "themes": []
++}
++"@
++
++Set-Content -Path "$settingsDir\settings.json" -Value $settings
++
++# Add WT to PATH if running in GitHub Actions
++if ($env:GITHUB_PATH) {
++ $wtDir | Out-File -Append -FilePath $env:GITHUB_PATH
++}
++
++Write-Host "Portable WT ready at: $wtDir"
++Write-Host " exportBuffer path: $bufferExportPath"
++Write-Host " exportBuffer key: Ctrl+Shift+F12"


* 57:  d0d28f3 ! 60:  c695b96 ci(ui-tests): upload the test logs

``````diff
@@ .github/workflows/ui-tests.yml: jobs:
 -          & "${env:RUNNER_TEMP}�hk\AutoHotKey64.exe" /ErrorStdOut /force ui-tests�ackground-hook.ahk "$PWD�g-hook" 2>&1 | Out-Default
 +          & "${env:RUNNER_TEMP}�hk\AutoHotKey64.exe" /ErrorStdOut /force background-hook.ahk "$PWD�g-hook" 2>&1 | Out-Default
            if (!$?) { $exitCode = 1; echo "::error::Test failed!" } else { echo "::notice::Test log" }
-           type bg-hook.log
            exit $exitCode
        - name: Show logs
          if: always()
  • 58: 5fe6836 = 61: 98f3ccf ci(ui-tests): take a screenshot when canceled

  • 59: 681f35e ! 62: 2886c18 ui-tests: verify that a sleep in Windows Terminal can be interrupted

    @@ Commit message
     
      ## .github/workflows/ui-tests.yml ##
     @@ .github/workflows/ui-tests.yml: jobs:
    +           $exitCode = 0
                & "${env:RUNNER_TEMP}�hk\AutoHotKey64.exe" /ErrorStdOut /force background-hook.ahk "$PWD�g-hook" 2>&1 | Out-Default
                if (!$?) { $exitCode = 1; echo "::error::Test failed!" } else { echo "::notice::Test log" }
    -           type bg-hook.log
     +          & "${env:RUNNER_TEMP}�hk\AutoHotKey64.exe" /ErrorStdOut /force ctrl-c.ahk "$PWD

github-cygwin and others added 30 commits March 2, 2026 20:43
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
In Windows 11, it seems that conhost.exe crashes on WriteFile() of
zero-length data to to_slave_nat. This patch skip WriteFile() if
the data length is 0.

Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
In Windows 11, pseudo console uses CSI?9001h which lets the terminal
enter win32-input-mode in console. As a result, two problems happen.

1) If non-cygwin app is running in script command on the console,
   Ctrl-C terminates not only the non-cygwin app, but also script
   command without cleanup for the pseudo console.

2) Some remnants sequences from win32-input-mode occasionally
   appears on the shell in script command on the console.

This patch fixes them by omit CSI?9001h to prevent the terminal
from entering win32-input-mode.

Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Suggested-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
If the native (non-cygwin) app is started as background process,
the input should be kept in to_cyg mode because input data should
go to the cygwin shell that start the non-cygwin app. However,
currently it is switched to to_nat mode in a short time and reverted
to to_cyg mode just after that.

With this patch, to avoid this behaviour, switching to to_nat mode
is inhibited by checking PGID of the process, that is newly created
for a background process and differs from PGID of the tty.

Fixes: Fixes: 9fc746d ("Cygwin: pty: Fix transferring type-ahead input between input pipes.")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
(cherry picked from commit db6269c2aee5f797cb8e2c71ac902e50acd29b20)
Previously, CSI6n was not handled correctly if the some sequences
are appended after the response for CSI6n. Especially, if the
appended sequence is a ESC sequence, which is longer than the
expected maximum length of the CSI6n response, the sequence will
not be written atomically.

Moreover, when the terminal's CSI 6n response and subsequent data
(e.g. keystrokes) arrive in the same write buffer, master::write()
processes all of it inside the pcon_start loop and returns early.
Bytes after the 'R' terminator go through per-byte line_edit() in
that loop instead of falling through to the `nat` pipe fast path
or the normal bulk `line_edit()` call. Due to this behaviour,
the chance of code conversion to the terminal code page for the
subsequent data in `to_be_read_from_nat_pipe()` case, will be lost.

Fix this by breaking out of the loop when 'R' is found and letting
the remaining data fall through to the normal write paths, which
are now reachable because `pcon_start` has been cleared.

Fixes: f206417 ("Cygwin: pty: Reduce unecessary input transfer.")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Co-authored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
(cherry picked from commit b4942fffd91cb91ea040bfb2778761cc6663a2b2)
The nat pipe ownership hand-over mechanism relies on the console
process list - the set of processes attached to a console, enumerable
via `GetConsoleProcessList()`. For non-cygwin process in pcon_activated
case, this list contains all processes attached to the pseudo console.
Otherwise, it contains all processes attached to the invisible console.

04f386e (Cygwin: console: Inherit pcon hand over from parent pty,
2024-10-31) added a last-resort fallback in `get_winpid_to_hand_over()`
that hands nat pipe ownership to any process in the console process
list, including Cygwin processes. This fallback is needed when a
Cygwin process on the pseudo console (that might be exec'ed from non-
cygwin process) must take over management of an active pseudo console
after the original owner exits.

When the pseudo console is disabled, this fallback incorrectly finds a
Cygwin process (such as the shell) and assigns it nat pipe ownership,
because both the original nat pipe owner and the shell are assosiated
with the same invisible console. Since there is no console for that
process to manage, ownership never gets released, input stays stuck on
the nat pipe.

Only the third (last-resort) call in the cascade needs guarding: the
first two calls filter for native (non-Cygwin) processes via the `nat`
parameter, and handing ownership to another native process is fine
regardless of pcon state. It is only the fallback to Cygwin processes
that is dangerous without an active pseudo console.

Guard the fallback with a `pcon_activated` check, since handing nat
pipe ownership to a Cygwin process only makes sense when there is an
active pseudo console for it to manage.

Fixes: 04f386e ("Cygwin: console: Inherit pcon hand over from parent pty")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
(cherry picked from commit d1129facdc50a2968925dbbad64c03bde298ef67)
The current code waits for `sendsig` by `for` loop in sigproc.cc,
however, the wait time might be insufficient for recent CPU.
The current code is as follows.

   for (int i = 0; !p->sendsig && i < 10000; i++)
     yield ();

Due to this problem, in tcsh, the following command occasionally
cannot be terminated by Ctrl-C. This is because, SIGCONT does not
wake-up `sleep` process correctly.

  $ cat | sleep 100 &
  $ fg
  $ (type Ctrl-C)

With this patch, the wait time for `sendsig` is guaranteed to be
up to 100ms instead of looping for 10000 times.

Fixes: d584454 ("* sigproc.cc (sig_send): Wait for dwProcessId to be non-zero as well as sendsig.")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Corinna Vinschen <corinna@vinschen.de>
(cherry picked from commit f65c9f0c44444689b732a61ea37589293f70ae64)
Waiting for `sendsig` to be non-zero for non-cygwin process is
pointless, because it never becomes non-zero (see spawn.cc).
Do not wait `sendsig` for a non-cygwin process.

Fixes: d584454 ("* sigproc.cc (sig_send): Wait for dwProcessId to be non-zero as well as sendsig.")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Corinna Vinschen <corinna@vinschen.de>
(cherry picked from commit 90556ffea1b6c005333066630e548839bed2cbbf)
Currently, the following command in bash cannot make `cat | cmd`
foreground correctly, and also cannot be terminated by Ctrl-C.

  $ cat |cmd &
  $ fg
  $ (Ctrl-C)

This is because, bash does not recognize the process `cmd` as stopped
by SIGTTIN, and does not send SIGCONT not only to `cmd` but also to
`cat`.

To solve this problem, this patch implements fake stop/cont for non-
cygwin process such as `cmd`. Even with this patch, the process `cmd`
does not enter into stopped state because non-cygwin process itself
does not handle cygwin signal, but the stub process for `cmd` enters
into stopped state instead by SIGTTIN.

Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Corinna Vinschen <corinna@vinschen.de>
(cherry picked from commit f02f97a626aa69433651de7ce7a65f3efc010a77)
"CSI?1004h" is the sequence that enables focus event report. This
can be handle correctly by pseudo console, however, if the pty
input is not connected to pseudo console, the focus event responses
such as "CSI I/O" are sent to the foreground process. Due to this,
`cat` receives these responses unexpectedly in the command below.

$ cmd &
$ cat

This seems to happen since Windows 11.

To avoid this, this patch removes "CSI?1004h/l" from pseudo console
output.

Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Corinna Vinschen <corinna@vinschen.de>
Currently, the first transfer_input() after Ctrl-C does not work
because discard_input flag remains asserted. This can cause loosing
typeahead input for non-cygwin app after Ctrl-C. With this patch,
the discard_input flag is cleared on master write() because the
input is new valid input after discarding input.

Fixes: 4e16e57 ("Cygwin: pty: Discard input already accepted on interrupt.")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Corinna Vinschen <corinna@vinschen.de>
(cherry picked from commit 50a87729d603cf7bf959f8cffc80371527a0537a)
Currently, pipe_sw_mutex is held in the process which is running
in console inherited from pseudo console until the process ends.
Due to this behaviour, the process may cause deadlock when it
attempts to acquire input_mutex in set_input_mode() called via
close_ctty(). This deadlock occurs because the pty master
acquire input_mutex first and acquire pipe_sw_mutex next while
the process exiting acquire pipe_sw_mutex first.

To avoid this deadlock, this patch releases pipe_sw_mutex in
pcon_hand_over_proc(). In addition, pointless pipe_sw_mutex
acquire/release is drppped in pcon_hand_over_proc().

Fixes: 04f386e ("Cygwin: console: Inherit pcon hand over from parent pty")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Corinna Vinschen <corinna@vinschen.de>
(cherry picked from commit 9ef8e3ad3bec51afddb26e472857962bd1b028e3)
Cygwin maintains POSIX line discipline for its own processes:
input goes through `line_edit()` before reaching the reading process.
Native (non-Cygwin) processes must not receive line-edited input;
they expect raw console input instead. To support both, the PTY keeps
two independent pipe pairs for input: a "cyg" pipe for Cygwin processes
and a "nat" pipe for native ones. The runtime switches between the two
as the foreground process changes.

The PTY tracks which process "owns" the nat pipe session via the
shared-memory field `nat_pipe_owner_pid`. Only one process is the
owner at any time. When `setup_for_non_cygwin_app()` finds that the
current owner is still alive, it leaves ownership with that process
rather than claiming it for the new one.

This means that a Cygwin-spawned native process can go through
`cleanup_for_non_cygwin_app()` without being the nat pipe owner.
Before this fix, that cleanup called `transfer_input(to_cyg)`
unconditionally, draining the pseudo console's input buffer even
though another process still owned the session. Keystrokes that the
user had typed were moved to the cyg pipe prematurely, so the actual
owner found an empty console input buffer and appeared to lose all
input.

When looking for the next owner of the console in
`cleanup_for_non_cygwin_app()` (via `get_winpid_to_hand_over()`),
and when transferring the input back to the cyg pipe, guard both with
a `nat_pipe_owner_self()` check so that only the actual owner performs
these operations. Non-owner processes skip straight to detaching from
the pseudo console without disturbing the input buffer.

Fixes: f9542a2 ("Cygwin: pty: Re-fix the last bug regarding nat-pipe.")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
(cherry picked from commit b790b19dccb7b93f2ec78dbc8353f6da58776083)
In Windows 11, key event with wRepeatCount == 0 is fixed-up to
wRepeatCount == 1 in conhost.exe.
https://github.com/microsoft/terminal/blob/v1.25.622.0/src/host/inputBuffer.cpp#L406

The console master thread (`cons_master_thread`) reads INPUT_RECORDs
from the console input buffer, processes signal-generating events,
and writes the remaining records back. After the writeback, it peeks
the buffer and uses `inrec_eq()` to verify that conhost stored the
records faithfully. On Windows 11, conhost normalizes `wRepeatCount`
from 0 to 1 on readback, causing `inrec_eq()` to report a mismatch
and triggering an unnecessary fixup path. Treat 0 and 1 as equivalent
for comparison purposes.

Addresses: git-for-windows/git#5632
Fixes: ff4440f ("Cygwin: console: Introduce new thread which handles input signal.")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
(cherry picked from commit 95b477fb2df76beca1469decf71242c24b223ac5)
In Windows 11, pseudo console has an undesired key conversion that
the Ctrl-H is translated into Ctrl-Backspace (not Backspace).
The reverse VT input path in conhost's `_DoControlCharacter()` maps
the byte 0x08 to a Ctrl+Backspace key event (VK_BACK with
LEFT_CTRL_PRESSED and character 0x7F). This was introduced in PR #3935
(Jan 2020) to make Ctrl+Backspace delete whole words. In September
2022, PR #13894 rewrote the forward path to properly implement DECBKM
(Backarrow Key Mode), but the reverse path was never updated to match,
breaking the roundtrip.

Due to this behaviour, inrec_eq() in cons_master_thread() fails to
compare backspace/Ctrl-H events in the input record sequence. This
patch is a workaround for the issue that replaces Ctrl-H with backspace
(0x7f), which will be translated into Ctrl-H in pseudo console.

Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
(cherry picked from commit 9ac383f5c2139107570e69a051ab06e56414f91d)
If the console is originating from pseudo console, the input into
console is coming from PTY master. This is because:

When the pseudo console is active, and a cygwin process is started
from non-cygwin process, `cons_master_thread()` runs inside the
Cygwin process that inherited the pseudo console from its parent
PTY. It reads all `INPUT_RECORD`s from the console input buffer via
`ReadConsoleInputW()`, processes signal-generating events (e.g. Ctrl+C),
and writes the remaining records back via `WriteConsoleInputW()`.
Meanwhile, the PTY master process (e.g. mintty) calls
`fhandler_pty_master::write()`, which writes keystrokes to `to_slave_nat`
(one end of the nat pipe). Conhost reads from the other end of that
pipe, parses the byte stream through its VT input path, and inserts
the resulting `INPUT_RECORD`s into the console input buffer.

If `cons_master_thread()` reads the buffer and removes a signal record
while conhost is simultaneously inserting new records from the PTY
master's write, the verify step (`inrec_eq()`) finds records in the
buffer that were not part of the original read, reports a mismatch, and
enters the fixup path. That fixup path itself can disturb the record
order, turning what was merely an interference into an actual problem.
Acquiring the PTY's `input_mutex` in `cons_master_thread()` prevents
`fhandler_pty_master::write()` from feeding new bytes into the pipe
while the read-process-writeback-verify cycle is in progress.

Use parent input_mutex as well as input_mutex in console device in
cons_master_thread().

Fixes: 04f386e ("Cygwin: console: Inherit pcon hand over from parent pty")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
(cherry picked from commit c4fb720afcf120a028f776dde47cacd2d91f3a14)
When keystrokes travel through the nat pipe during a native process
session, they bypass POSIX line discipline entirely. When they are
transferred back to the cyg pipe at cleanup (via
`transfer_input(to_cyg)`), they arrive as raw bytes. If the
terminal is in canonical mode at that point, VERASE and VKILL
characters in those raw bytes have no effect because `line_edit()`
was never applied to them. The result: backspace typed while a
native process was running fails to erase the preceding character
once the input reaches bash's readline.

The fix applies `line_edit()` to the transferred bytes before they
reach the reading process. The right place to do this is the
master's forward thread (`pty_master_fwd_thread()`), because it
runs in the master process alongside `fhandler_pty_master::write()`
and shares access to the readahead buffer and `line_edit()` state.
Calling `line_edit()` from the slave (where `transfer_input()` runs)
would not work because that state belongs to the master.

To coordinate: `transfer_input(to_cyg)` writes the raw bytes to
the cyg pipe's slave end (`to_slave`), then signals a new
cross-process event (`input_transferred_to_cyg`) and spin-waits
for the forward thread to clear it. The forward thread is converted
from synchronous to overlapped I/O so it can wait on both the
`from_slave_nat` read completion and the transfer event
simultaneously. When the event fires, it reads the transferred
bytes from the cyg pipe's master end (`from_master`), processes
them through `line_edit()`, and clears the event.

The spin-wait in `transfer_input()` holds `input_mutex` (from its
caller), which blocks `fhandler_pty_master::write()` from injecting
new keystrokes until the forward thread has finished applying
`line_edit()` to the transferred bytes.

Fixes: 10d083c ("Cygwin: pty: Inherit typeahead data between two input pipes.")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
(cherry picked from commit a0b38a81b9be482a0058e8f4f5477eeae2f2c012)
The master process (e.g. mintty) temporarily attaches to the pseudo
console's conhost in `transfer_input()` so it can read
INPUT_RECORDs via `ReadConsoleInputA()`. During that brief window,
`get_console_process_id()` inside `get_winpid_to_hand_over()` calls
`GetConsoleProcessList()`, which sees the master among the console's
attached processes and may select it as the handover target. That is
wrong because the master will detach immediately after the read.

Until now, `attach_mutex` was a process-local unnamed mutex, so
the slave's `get_winpid_to_hand_over()` could not serialize with
the master's temporary attachment. Make `attach_mutex` a
cross-process named mutex (`ATTACH_MUTEX`) shared within the PTY,
and acquire it around the `get_console_process_id()` calls in
`get_winpid_to_hand_over()`. This ensures the console process list
enumeration never observes the master while it is temporarily
attached.

Fixes: 1e6c51d ("Cygwin: pty: Reorganize the code path of setting up and closing pcon.")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
(cherry picked from commit 3adbd41f5babda5a42430fb25d9a02205c416542)
`to_be_read_from_nat_pipe()` reads several shared-memory fields
(`switch_to_nat_pipe`, `pcon_activated`, `pty_input_state`) to
decide whether keystrokes should go to the nat pipe. It is called
from `master::write()` on every keystroke. Without synchronization,
the slave can be in the middle of a pipe switch (changing these
fields in `setup_for_non_cygwin_app()`, `cleanup_for_non_cygwin_app()`,
or `setpgid_aux()`) while the master reads a half-updated snapshot,
making an inconsistent routing decision that sends keystrokes to the
wrong pipe.

Guard `to_be_read_from_nat_pipe()` with `pipe_sw_mutex` so it
always reads a consistent state. The spin-wait at entry handles the
pseudo console initialization case: when `pipe_sw_mutex` is held by
the slave during `setup_pseudoconsole()` and `pcon_start` is set,
the function returns false immediately, routing keystrokes to the
cyg pipe through `line_edit()` where the CSI6n response handler
expects them.

Acquiring `pipe_sw_mutex` inside `to_be_read_from_nat_pipe()`
creates a lock ordering constraint: `master::write()` holds
`input_mutex` before calling `to_be_read_from_nat_pipe()`, so the
master's lock order is `input_mutex` then `pipe_sw_mutex`.
Previously, `cleanup_for_non_cygwin_app()` and `setpgid_aux()`
acquired `pipe_sw_mutex` first and then `input_mutex` (for
`transfer_input()`), which is the reverse order and would deadlock.
Restructure both functions to release `pipe_sw_mutex` before
acquiring `input_mutex`, maintaining a consistent lock order
throughout.

Fixes: bb42852 ("Cygwin: pty: Implement new pseudo console support.")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
(cherry picked from commit 2f705b879fac45e5cd761fad8dd7de6b388aee2a)
While a non-cygwin app has exited but the stub process has not yet
terminated, `nat_fg()` returns false because no non-cygwin app is
running. In this window, pty input goes to the cyg pipe. Due to
this, the keystroke order is swapped unexpectedly:

1) start non-cygwin app
2) press 'a' ('a' goes to nat pipe)
3) non-cygwin app exits
4) press 'b' ('b' goes to cyg pipe)
5) the stub process for non-cygwin app transfers input in nat pipe
   to cyg pipe ('a' goes to cyg pipe)
6) the result in the cyg pipe is "ba"

Fix this by dropping the `nat_fg()` check from
`to_be_read_from_nat_pipe()`. The function now returns true when
`!pcon_start && switch_to_nat_pipe && !masked`. Each component has
a specific purpose:

- `!pcon_start`: keystrokes go through the CSI6n response handler
  during pseudo console initialization rather than the fast path.
- `switch_to_nat_pipe`: this session-level flag stays true from
  `setup_for_non_cygwin_app()` through `cleanup_for_non_cygwin_app()`,
  spanning the entire native process lifetime including the post-exit
  cleanup window.
- `!masked` (`TTY_SLAVE_READING` event does not exist): keystrokes
  go to the Cygwin pipe when a Cygwin process is actively reading
  from the slave, since that process expects POSIX-processed input.

Removing `nat_fg()` is safe because conhost's input buffer
accumulates keystrokes as INPUT_RECORDs during the post-exit
window, and `transfer_input(to_cyg)` in `cleanup_for_non_cygwin_app()`
reads them back via `ReadConsoleInputA()` and writes them to the
cyg pipe. Those transferred bytes then go through `line_edit()` in
the master's forward thread (via `input_transferred_to_cyg` from an
earlier patch in this series), ensuring proper POSIX line discipline
processing.

Additionally, add a `nat_fg()` check to the disable_pcon transfer
path in `master::write()`. That transfer moves cyg pipe data to
the nat pipe when a Cygwin child exits and a native process
regains the foreground with pcon disabled. Without pcon, there is
no conhost buffer to accumulate keystrokes (the nat pipe is a raw
pipe), so keystrokes must only go there when a native process is
genuinely in the foreground and ready to read them. The `nat_fg()`
guard prevents the transfer from stealing readline's data during
the post-exit window.

Fixes: f206417 ("Cygwin: pty: Reduce unecessary input transfer.")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
(cherry picked from commit 6413c19fd85bd7d6dc42368e22cebe86b730b3b6)
If non-cygwin app is started in GDB and terminating it normally,
re-running the non-cygwin app might fail in setup_pseudoconsole().

The error is something like:

$ gdb ./winsleep
GNU gdb (GDB) (Cygwin 15.2-1) 15.2
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-cygwin".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./winsleep...
(gdb) run
Starting program: /home/yano/winsleep
[New Thread 49324.0x14178]
[Thread 49324.0x14178 exited with code 0]
[Inferior 1 (process 49324) exited normally]
(gdb) run
Starting program: /home/yano/winsleep
      0 [] gdb 294 fhandler_pty_slave::setup_pseudoconsole: CreatePseudoConsole() failed. 00000057 80070057
                           [New Thread 86480.0xfd4]
[Thread 86480.0xfd4 exited with code 0]
[Inferior 1 (process 86480) exited normally]
(gdb)

The essential problem is lack of restoring nat handles for *ALL* the
PTY-slave instances after closing pseudo console in GDB.

Restoring handles from pseudo console handles to simple pipe handles
is not necessary in normal non-cygwin apps because pseudo console is
setup in the stub process for the non-cygwin app and the stub process
exits after the app is terminated.

However, for GDB, pseudo console is setup in GDB process in hooked
CreateProcess() because GDB does not use exec() to run an inferior
(debuggee). Therefore, after the inferior exits, nat handle must be
restored to simple pipe handles.

The current code restores only handles in the PTY-slave instance
that has called fhandler_pty_slave::reset_switch_to_nat_pipe(). If
this instance is different from the instance that will setup pseudo
console, the nat handles are not restored correctly, then call to
CreatePseudoConsole() causes error.

To solve this issue, restore nat handles in all the PTY-slave
instances to simple pipe handles when the inferior exits with this
patch.

In addition, if ctty is PTY-slave, fixup handles in it as well.

Fixes: 8aeb3f3 ("Cygwin: pty: Make apps using console APIs be able to debug with gdb.")
Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <johannes.schindelin@gmx.de>
(cherry picked from commit c8f08427661b1f054e2fed93b4c1ce5ad00d882e)
If the 'for' loop in pcon_start handling in master write() does not
break, 'ptr' and 'len' loose the chance to fixup the value. In this
case, all data in 'ptr' are processed, so the 'len' should be 0.
1 byte is consistently consumed in each iteration in the 'for' loop,
so this patch fixups 'ptr' and 'len' in every iterations instead of
fixing-up at break.

Fixes: 9d7440036580 ("Cygwin: pty: Fix handling of data after CSI6n response")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <johannes.schindelin@gmx.de>
(cherry picked from commit c8bbec4c551f9b606305d87a165909facf442e0b)
Currently, if the CSI6n response is divided into "CSI10;2" and "R",
and another thread call master write() with "c", the data written to
nat pipe will be interleaved like "CSI10;2cR". The first "CSI10;2"
make the 'state' 1, and in state == 1, all the data written goes
to 'wpbuf[]'. This may break startup of pseudo console.

With this patch, the thread ID of the thread that write the first ESC
char to 'wpbuf[]' is stored in 'wp_tid', and only if the thread ID
matches 'wp_tid' will be written to 'wpbuf[]'.

Fixes: bb42852 ("Cygwin: pty: Implement new pseudo console support.")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <johannes.schindelin@gmx.de>
(cherry picked from commit 2fa51041ac547189a9f04e7c83466c178fe06994)
Currently, the cleanup path of setup_pseudoconsole() is missing
DeleteProcThreadAttributeList() call, while microsoft's document
requires that and the normal path has it.
https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-initializeprocthreadattributelist

This patch adds DeleteProcThreadAttributeList() call to the cleanup
path.

Fixes: bb42852 ("Cygwin: pty: Implement new pseudo console support.")
Suggested-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Corinna Vinschen <corinna@vinschen.de>
(cherry picked from commit 11a835fbe481018f7c50cee25e109df558da7ec2)
At some point in the past, GDB sets terminal pgid to inferior pid
when the inferior is running. Moreover, the inferior is non-cygwin
process, GDB sets the terminal pgid to windows pid of the inferior.
Due to this behaviour, Ctrl-C does not work if the inferior is a
non-cygwin app. This is because, the current code sends Ctrl-C to
GDB only when GDB's pgid equeals to terminal pgid. This patch omit
checking pgid when recognizing GDB process whose inferior is non-
cygwin app. This patch also fixes the issue that the cygwin debuggee
under strace cannot be terminated by Ctrl-C.

In addition, to improve the readabiliby of the code, this patch
introduces inline functions such as:
is_foreground_special_process (),
is_gdb_with_foreground_non_cygwin_inferior (), etc.,
instead of complicated conditions in 'if' clauses.

Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Reviewed-by: Corinna Vinschen <corinna@vinschen.de>
(cherry picked from commit 10a3de565b1c26a3c600148fa6e478e3501cbbc2)
Currently, poll() sometimes returns -1 with errno == 0 if the fd is
not opened. This is due to lack of setting errno when peek() fails.
This patch ensure to set errno to thread_errno if peek() returns -1.

Addresses: https://cygwin.com/pipermail/cygwin/2026-April/259602.html
Fixes: 8382778 ("Cygwin: console: fix select() behaviour")
Reported-by: Nahor <nahor.j+cygwin@gmail.com>
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Corinna Vinschen <corinna@vinschen.de>
(cherry picked from commit 6a588c29b562c6da08061f68d71733c78fbfefc3)
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
Cygwin's speclib doesn't handle dashes or dots. However, we are about to
rename the output file name from `cygwin1.dll` to `msys-2.0.dll`.

Let's preemptively fix up all the import libraries that would link
against `msys_2_0.dll` to correctly link against `msys-2.0.dll` instead.
dscho and others added 23 commits April 23, 2026 08:40
This seems to have been necessary in 2015 to address the expectations of
some tests in Git's test suite, but seems no longer to be necessary?!?

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This topic branch fixes the problem where a UTF-16 command-line was
converted to UTF-8 in an incorrect way (because Cygwin treated it as if
it was a file name and applied some magic that is intended to allow for
otherwise invalid file names on Windows).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
msys2-runtime: restore fast path for current user primary group
Workaround certain anti-malware programs

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
AI-assisted coding is a reality nowadays, as is the all-too-common
practice to toss a task over the wall to an AI coding agent and accept
its outcome without even bothering to verify. To get better results with
either approach (explicitly avoiding to characterize the latter to be
even remotely okay), let's add an AGENTS.md file to give AI a "leg up".

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
AutoHotKey is not only a convenient way to add keyboard shortcuts for
functionality (or applications) that does not come with shortcuts, but
it is in general a powerful language to remote control GUI elements.

We will use this language to implement a couple of automated tests that
should hopefully prevent regressions as we have experienced in the past
(for example, a regression that was fixed and immediately re-broken,
which went unnoticed for months).

So let's start by adding a library of useful functions, to be extended
as needed.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
One particularly important part of Git for Windows' MSYS2 runtime is
that it is used to run Git's tests, and regressions happened there: For
example, the first iteration of MSYS2 runtime v3.5.5 caused plenty of
hangs. This was realized unfortunately only after deploying the
msys2-runtime Pacman package, and some painful vacation-time scrambling
was required to revert to v3.5.4.This was realized unfortunately only
after deploying the msys2-runtime Pacman package, and some painful
vacation-time scrambling was required to revert to v3.5.4.

To verify that this does not happen anymore, let's reuse what
`setup-git-for-windows-sdk` uses in Git's very own CI:

- determine the latest successful `ci-artifacts` workflow run in
  git-for-windows/git-sdk-64

- download its Git files and build artifacts

- download its minimal-sdk

- overwrite the MSYS2 runtime in the minimal-sdk

- run the test suite and the assorted validations just like the
  `ci-artifacts` workflow (from which these jobs are copied)

This obviously adds a hefty time penalty (around 7 minutes!) to every
MSYS2 runtime PR in the git-for-windows org. Happily, these days we
don't need many of those, and the balance between things like the v3.5.5
scramble and waiting a little longer for the CI to finish is clearly in
favor of the latter.

Co-authored-by: Jeremy Drake <github@jdrake.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This is a forked repository...

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
AutoHotKey is not only a convenient way to add keyboard shortcuts for
functionality (or applications) that does not come with shortcuts, but
it is in general a powerful language to remote control GUI elements.

We will use this language to implement a couple of automated tests that
should hopefully prevent regressions as we have experienced in the past
(for example, a regression that was fixed and immediately re-broken,
which went unnoticed for months).

So let's start by adding a library of useful functions, to be extended
as needed.

Note: As AutoHotKey is a GUI application, it does not expect to have
stdout/stderr attached to it, therefore the `Info()` function added in
this commit writes all the messages into `.log` files adjacent to the
per-test working directories. But AutoHotKey _can_ have stdout/stderr
attached to it, via redirection. In PowerShell, for example, appending
`| Out-Default` to the invocation will make stdout/stderr available to
AutoHotKey scripts (via the unintuitive syntax `FileAppend "text`n",
"*"` (and `"**"` for stderr). The `Info()` function will detect when
stdout is available and if it is, will also write to it, in addition to
the `.log` file.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The issue reported in microsoft/git#730 was
fixed, but due to missing tests for the issue a regression slipped in
within mere weeks.

Let's add an integration test that will (hopefully) prevent this issue
from regressing again.

This integration test is implement as an AutoHotKey script. It might
look unnatural to use a script language designed to implement global
keyboard shortcuts, but it is a quite powerful approach. While
there are miles between the ease of developing AutoHotKey scripts and
developing, say, Playwright tests, there is a decent integration into VS
Code (including single-step debugging), and AutoHotKey's own development
and community are quite vibrant and friendly.

I had looked at alternatives to AutoHotKey, such as WinAppDriver,
SikuliX, nut.js and AutoIt, in particular searching for a solution that
would have a powerful recording feature similar to Playwright, but did
not find any that is 1) mature, 2) well-maintained, 3) open source and
4) would be easy to integrate into a GitHub workflow. In the end,
AutoHotKey appeared my clearest preference.

So how is the test implemented? It lives in `ui-test/` and requires
AutoHotKey v2 as well as Windows Terminal (the Legacy Prompt would not
reproduce the problem). It then follows the reproducer I gave to the
Cygwin team:

1. initialize a Git repository
2. install a `pre-commit` hook
3. this hook shall spawn a non-Cygwin/MSYS2 process in the background
4. that background process shall print to the console after Git exits
5. open a Command Prompt in Windows Terminal
6. run `git commit`
7. wait until the background process is done printing
8. press the Cursor Up key
9. observe that the Command Prompt does not react (in the test, it
   _does_ expect a reaction: the previous command in the command
   history should be shown, i.e. `git commit`)

In my reproducer, I then also suggested to press the Enter key and to
observe that now the "More ?" prompt is shown, but no input is accepted,
until Ctrl+Z is pressed. Naturally, the test should not expect _that_
;-)

There were a couple of complications I needed to face when developing
this test:

- I did not find any easy macro recorder for AutoHotKey that I liked. It
  would not have helped much, anyway, because intentions are hard to
  record.

- Before I realized that there is excellent AutoHotKey support in VS
  Code via the AutoHotKey++ and AutoHotKey Debug extensions, I struggled
  quite a bit to get the syntax right.

- Windows Terminal does not use classical Win32 controls that AutoHotKey
  knows well. To capture the terminal text, we use Windows Terminal's
  exportBuffer action, which writes the entire scrollback to a file on a
  keybinding (Ctrl+Shift+F12). This requires running Windows Terminal in
  portable mode with a settings.json that defines the action, which the
  setup script takes care of.

- Despite my expectations, `ExitApp` would not actually exit AutoHotKey
  before the spawned process exits and/or the associated window is
  closed.

For good measure, run this test both on windows-2022 (corresponding to
Windows 10) and on windows-2025 (corresponding to Windows 11).

Note that this does not use the naive method to capture text from
Windows Terminal, emulating mouse movements, dragging across the entire
window with finicky pixel calculations for title bar height, scroll bar
width and padding, then right-clicks to copy. This would be fragile:
if the window geometry changes, if another window gets focus, or if the
title bar height differs between OS versions, the capture silently gets
the wrong text.

Windows Terminal's exportBuffer action avoids all of that by writing the
complete scrollback buffer to a file on a keybinding, with no dependence
on pixel positions or window focus. To use it, Windows Terminal must
run in portable mode with a settings.json that defines the action and
keybinding. So that's what we do here.

We add `setup-portable-wt.ps1`, which downloads Windows Terminal
(when not already present), creates the .portable marker and writes
settings.json with Ctrl+Shift+F12 bound to `exportBuffer`. It accepts
a `-DestDir` parameter so CI can use `$RUNNER_TEMP` while local
development uses `$TEMP`. When running inside GitHub Actions it also
appends the Windows Terminal directory to `$GITHUB_PATH`.

Co-authored-by: Eu-Pin Tien <eu-pin.tien@diamond.ac.uk>
Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The test logs are quite interesting to have, and not only those: In case
of a fatal failure, the test directory is valuable information, too.
Let's always upload them as build artifacts.

For convenience, let's just reuse the `ui-tests/` directory as the place
to put all of those files; Technically, we do not need the files in
there that are tracked by Git, but practically speaking, it is neat to
have them packaged in the same `.zip` file as the test logs and stuff.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Sometimes the logs are empty and it is highly unclear what has happened.
In such a scenario, a picture is indeed worth more than a thousand
words.

Note that this commit is more complicated than anyone would like, for
two reasons:

- While PowerShell is the right tool for the job, a PowerShell step in
  GitHub Actions will pop up a Terminal window, _hiding_ what we want to
  screenshot. To work around that, I tried to run things in a Bash step.
  _Also_ opens a Terminal window! Node.js to the rescue.

- _Of course_ it is complicated to take a screenshot. The challenge is
  to figure out the dimensions of the screen, which should be as easy as
  looking at `[System.Windows.Forms.Screen]::PrimaryScreen`'s `Bounds`
  attribute.

  Easy peasy, right? No, it's not. Most machines nowadays have a
  _ridiculous_ resolution which is why most setups have a _zoom factor_.
  Getting to that factor should be trivial, by calling
  `GetDeviceCaps(hDC, LOGPIXELSX)`, but that's not working in modern
  Windows! There is a per-monitor display scaling ("DPI"). But even
  _that_ is hard to get at, calling `GetDpiForMonitor()` will still
  return 96 DPI (i.e. 100% zoom) because PowerShell is not marked as
  _Per-Monitor DPI Aware_. Since we do not want to write a manifest into
  the same directory as `powershell.exe` resides, we have to jump
  through yet another hoop to get that.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The Ctrl+C way to interrupt run-away processes is highly important. It
was recently broken in multiple ways in the Cygwin runtime (and hence
also in the MSYS2 runtime).

Let's add some integration tests that will catch regressions.

It is admittedly less than ideal to add _integration_ tests; While
imitating exactly what the end user does looks appealing at first,
excellent tests impress by how quickly they allow regressions not only
to be identified but also to be fixed. Even worse: all integration
tests, by virtue of working in a broader environment than, say, unit
tests, incur the price of sometimes catching unactionable bugs, i.e.
bugs in software that is both outside of our control as well as not the
target of our testing at all.

Nevertheless, seeing as Cygwin did not add any unit tests for those
Ctrl+C fixes (which is understandable, given how complex testing for
Ctrl+C without UI testing would be), it is better to have integration
tests than no tests at all.

So here goes: This commit introduces a test that verifies that the MSYS2
`sleep.exe` can be interrupted when run from PowerShell in a Windows
Terminal. This was broken in v3.6.0 and fixed in 7674c51 (Cygwin:
console: Set ENABLE_PROCESSED_INPUT when disable_master_thread,
2025-07-01).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This was the actual use case that was broken and necessitated the fix in
7674c51 (Cygwin: console: Set ENABLE_PROCESSED_INPUT when
disable_master_thread, 2025-07-01).

It does require an SSH server, which Git for Windows no longer ships.
Therefore, this test uses the `sshd.exe` of OpenSSH for Windows
(https://github.com/powershell/Win32-OpenSSH) in conjunction with Git
for Windows' `ssh.exe` (because using OpenSSH for Windows' variant of
`ssh.exe` would not exercise the MSYS2 runtime and therefore not
demonstrate a regression, should it surface in the future).

To avoid failing the test because OpenSSH for Windows is not available,
the test case is guarded by the environment variable
`OPENSSH_FOR_WINDOWS_DIRECTORY` which needs to point to a directory that
contains a working `sshd.exe`.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
In the previous commit, I added a new UI test that generates a somewhat
large repository for testing the clone via SSH. Since that repository is
created in the test directory, that would inflate the `ui-tests` build
artifact rather dramatically. So let's create the repository outside of
that directory.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The fixes of 7674c51 (Cygwin: console: Set ENABLE_PROCESSED_INPUT
when disable_master_thread, 2025-07-01) were unfortunately not complete;
There were still a couple of edge cases where Ctrl+C was unable to
interrupt processes. Let's add a demonstration of that issue.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
In 0ae6a6f (Cygwin: pipe: Fix SSH hang with non-cygwin pipe reader,
2025-06-27), a quite problematic bug was fixed where somewhat large-ish
repositories could not be cloned via SSH anymore.

This fix was not accompanied by a corresponding test case in Cygwin's
test suite, i.e. there is no automated way to ensure that there won't be
any regressions on that bug (and therefore it would fall onto end users
to deal with those).

This constitutes what Michael C. Feathers famously characterized as
"legacy code" in his book "Working Effectively with Legacy Code":

    To me, legacy code is simply code without tests. I've gotten some
    grief for this definition. What do tests have to do with whether
    code is bad? To me, the answer is straightforward, and it is a point
    that I elaborate throughout the book:

	Code without tests is bad code. It doesn’t matter how well
	written it is; it doesn’t matter how pretty or object-oriented
	or well-encapsulated it is. With tests, we can change the
	behavior of our code quickly and verifiably. Without them, we
	really don’t know if our code is getting better or worse.

Just to drive this point home, let me pull out Exhibit A: The bug fix in
question, which is the latest (and hopefully last) commit in a _long_
chain of bug fixes that fix bugs introduced by preceding bug fixes:

- 9e4d308 (Cygwin: pipe: Adopt FILE_SYNCHRONOUS_IO_NONALERT flag for
  read pipe., 2021-11-10) fixed a bug where Cygwin hung by mistake while
  piping output from one .NET program as input to another .NET program
  (potentially introduced by 3651990 (Cygwin: pipe: Avoid false EOF
  while reading output of C# programs., 2021-11-07), which was itself a
  bug fix). It introduced a bug that was fixed by...
- fc691d0 (Cygwin: pipe: Make sure to set read pipe non-blocking for
  cygwin apps., 2024-03-11). Which introduced a bug that was purportedly
  fixed by...
- 7ed9adb (Cygwin: pipe: Switch pipe mode to blocking mode by
  default, 2024-09-05). Which introduced a bug that was fixed by...
- cbfaeba (Cygwin: pipe: Fix incorrect write length in raw_write(),
  2024-11-06). Which introduced a bug that was fixed by... the SSH hang
  fix in 0ae6a6f (Cygwin: pipe: Fix SSH hang with non-cygwin pipe
  reader, 2025-06-27).

There is not only the common thread here that each of these bug fixes
introduced a new bug, but also the common thread that none of the
commits introduced new test cases into the test suite that could
potentially have helped prevent future breakages in this code.

So let's at least add an integration test here.

Side note: I am quite unhappy with introducing integration tests. I know
there are a lot of fans out there, but I cannot help wondering whether
they favor the convenience of writing tests quickly over the vast cost
of making debugging any regression a highly cumbersome and unenjoyable
affair (try single-stepping through a test case that requires several
processes to be orchestrated in unison). Also, integration tests have
the large price of introducing moving parts outside the code base that
is actually to be tested, opening the door for breakages caused by
software (or infrastructure, think: network glitches!) that are
completely outside the power or responsibility of the poor engineer
tasked with fixing the breakages. Nevertheless, I have been unable
despite days of trying to wrap my head around the issue to figure out a
way to reproduce the `fhandler_pipe_fifo::raw_write()` hang without
involving a MINGW `git.exe` and an MSYS2/Cygwin `ssh.exe`. So: It's the
best I could do with any reasonable amount of effort. It's better to
have integration tests that would demonstrate regressions than not
having any tests for that at all.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
On hosted GitHub Actions runners, there is always this Log window
visible on the Desktop, and due to some magic logic, this window is
sometimes in the foreground on the `windows-2025` runners.

Let's minimize it so that it is out of the way and does not interfere
with the AutoHotKey-based UI tests.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The existing UI test infrastructure only supports Windows Terminal,
but the keystroke reordering bug reported in
git-for-windows/git#5632 manifests most
reliably in mintty, which uses a different PTY code path. To write a
reproducer for that bug, we need library functions that can launch
mintty and read back what it displayed.

An initial attempt used mintty's `-l` flag to write a terminal log
file, then read back that log with ANSI escape sequences stripped.
This approach turned out to be unreliable: mintty buffers its log
output, so content that is already visible on screen (such as the
`$ ` prompt) may not have been flushed to the log file yet. Polling
for a prompt that is already displayed but not yet logged leads to
an indefinite wait.

Instead, LaunchMintty() configures mintty's Ctrl+F5 keybinding to
trigger the `export-html` action, which writes an HTML snapshot of the
current screen to a file. This is instantaneous and always reflects
exactly what is on screen. The function uses window-class enumeration
to identify the newly-created mintty window among any pre-existing
instances and returns its handle.

CaptureBufferFromMintty() sends Ctrl+F5 to trigger the export, reads
the resulting HTML file, extracts the `<body>` content, strips HTML
tags, and decodes common entities to return plain text suitable for
substring matching. It accepts an optional window title to activate
the correct mintty instance before sending the keystroke. Note that
AHK's ControlSend cannot be used here because mintty passes the raw
keycodes through to the terminal session rather than interpreting
them as window-level shortcuts, so WinActivate followed by Send is
the only way to trigger the export action.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This adds an AutoHotKey test that reliably reproduces the keystroke
reordering bug described at
git-for-windows/git#5632 where characters
typed into a bash prompt arrive in the wrong order.

The root cause lies in the MSYS2 runtime's PTY input handling: when a
non-MSYS2 process (such as PowerShell or cmd.exe) runs in the
foreground of a PTY, the transfer_input() function in pty.cc can
reorder bytes across pipe buffer boundaries. This is particularly
visible when backspace characters get separated from the characters
they were meant to delete.

The test exploits this by launching a PowerShell process that
saturates all CPU cores with tight cmd.exe loops while simultaneously
running an MSYS2 sleep.exe in the foreground. While this stress
process runs, the test rapidly types characters interleaved with
backspaces at 1ms key delay. It walks through the full alphanumeric
test string in chunks of two characters, appending two sacrificial
characters (",;", chosen for visual dissimilarity with the letters so
that they can be spotted very easily in the middle of the output)
after each chunk followed by two backspaces to delete them.

If the PTY delivers the bytes in order, the backspaces cleanly remove
the ",;" and the result matches the original test string. If
transfer_input() reorders bytes across buffer boundaries, the
backspaces land in the wrong position and the "," or ";" characters
leak through, producing visibly jumbled output like
"GH,ABCDEFIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".

Using small chunks maximizes the number of buffer boundaries that
the PTY must handle, which makes the reordering more dramatic and
reliable across different environments.

The cpu-stress.ps1 helper script uses cygpath to locate sleep.exe
via its POSIX path so that the script works in both the full SDK
(where MSYS2 tools live under a Git SDK directory) and in CI (where
they live under C:\Program Files\Git).

With the current runtime, the bug triggers on the very first
iteration in every run tested.

The fix for keystroke reordering that occurs when the pseudo console
oscillates between creation and teardown removes a
transfer_input(to_nat) call from the "non-pcon xfer" code path in
master::write().  That code path was specifically added in Cygwin commit
acc44e0 ("Cygwin: pty: Add missing input transfer when
switch_to_pcon_in state", 2021-12-11) to fix a different input routing
bug that manifested only when the pseudo console was disabled:

  https://inbox.sourceware.org/cygwin-patches/20211212130347.10080-1-takashi.yano@nifty.ne.jp/

That patch was in response to a report about spurious ANSI escape
sequences ("ghost-typing") appearing in the terminal after quitting
vim when git.exe (a native process) spawned vim.exe (a Cygwin
process) with the pseudo console disabled:

  https://inbox.sourceware.org/cygwin-patches/nycvar.QRO.7.76.6.2112092345060.90@tvgsbejvaqbjf.bet/
  git-for-windows/git#3579

The transfer was originally introduced as part of a broader effort to
preserve typeahead across the two input pipes, starting with Cygwin
commit 10d083c ("Cygwin: pty: Inherit typeahead data between two
input pipes", 2021-01-28):

  https://inbox.sourceware.org/cygwin-patches/20210128032614.1678-2-takashi.yano@nifty.ne.jp/

which itself was prompted by a user report about typed characters
disappearing while native programs were running:

  https://inbox.sourceware.org/cygwin/7e3d947e-b178-30a3-589f-b48e6003fbb3@googlemail.com/

Since removing that transfer could theoretically regress the
disable_pcon scenario, extend the existing keystroke-order test to
also run with MSYS=disable_pcon set.  The test reuses the same
RunKeystrokeTest() helper and stress command, just with fewer
iterations since the disable_pcon code path is simpler and more
deterministic.

An early iteration of the "Fix out-of-order keystrokes" patch series had
a bug where it would prevent native processes from receiving _any_
keystrokes under certain circumstances. Let's also specifically verify
that this is _not_ the case, and prevent regressing on it.

Takashi Yano's "Add workaround for handling of backspace when pcon
enabled" patch works around a conhost.exe bug where byte 0x08
(Ctrl+H) is mapped to a Ctrl+Backspace key event, which performs
word-wise deletion instead of single-character deletion. When asked
how the bug was originally discovered
(https://inbox.sourceware.org/cygwin-patches/c4dc071d-fa7e-ed2e-0c14-3fddb5240f1c@gmx.de/),
Takashi explained that Ctrl+H on cmd.exe running in pseudo console
erases a word, not a character, on Windows 11
(https://inbox.sourceware.org/cygwin-patches/20260328191514.360fed717ef42a086bac019b@nifty.ne.jp/).
This was confirmed in a MinTTY session: launching cmd.exe, typing
"abc" and pressing Ctrl+H deletes all three characters instead of
just "c"
(https://inbox.sourceware.org/cygwin-patches/463c3df7-3810-ed9a-9f7c-c2cf4fd6a7b7@gmx.de/).

Add a verification step inside the existing cmd.exe test phase: after
confirming the long test string arrived unjumbled, type "echo
Expresso" followed by Ctrl+H and Enter. If Ctrl+H correctly deletes
only the trailing "o", cmd.exe executes "echo Express" and prints
"Express". If conhost.exe mistranslates the keypress into
Ctrl+Backspace, the entire word "Expresso" is erased, cmd.exe runs
bare "echo" and prints "ECHO is on." instead, causing the test to
fail.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
There have been way too many regressions in Cygwin as of late, in
particular in the console handling. The worst part? Many of those bugs
were introduced _in bug fix patches_!

Here are a bunch of tests that are designed to help Git for Windows
increase confidence in upgrades to newer Cygwin versions.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
@dscho
Copy link
Copy Markdown
Member Author

dscho commented Apr 24, 2026

/open pr

The workflow run was started

@dscho dscho linked an issue Apr 25, 2026 that may be closed by this pull request
@dscho dscho merged commit 6abc3bb into git-for-windows:main Apr 25, 2026
31 checks passed
@dscho dscho deleted the rebase-to-3.6.9 branch April 25, 2026 11:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[New cygwin version] cygwin-3.6.9