diff options
Diffstat (limited to 'scripts/windows_input')
| -rw-r--r-- | scripts/windows_input/Native.cs | 18 | ||||
| -rw-r--r-- | scripts/windows_input/README.md | 19 | ||||
| -rw-r--r-- | scripts/windows_input/report.py | 17 |
3 files changed, 39 insertions, 15 deletions
diff --git a/scripts/windows_input/Native.cs b/scripts/windows_input/Native.cs index 76cb8af7..9d635753 100644 --- a/scripts/windows_input/Native.cs +++ b/scripts/windows_input/Native.cs @@ -108,7 +108,15 @@ namespace HerdrInputGauntlet { [DllImport("user32.dll", SetLastError=true)] static extern uint SendInput(uint count, Input[] events, int size); [DllImport("user32.dll")] static extern bool GetWindowRect(IntPtr hwnd, out Rect rect); [DllImport("user32.dll")] static extern bool SetWindowPos(IntPtr hwnd, IntPtr after, int x, int y, int width, int height, uint flags); - [DllImport("user32.dll")] public static extern int CountClipboardFormats(); + [DllImport("user32.dll",EntryPoint="CountClipboardFormats",SetLastError=true)] static extern int NativeCountClipboardFormats(); + public static int CountClipboardFormats() { + int count=NativeCountClipboardFormats(); + if(count==0) { + int error=Marshal.GetLastWin32Error(); + if(error!=0) throw new Win32Exception(error,"Clipboard format count failed"); + } + return count; + } [DllImport("user32.dll")] public static extern uint GetClipboardSequenceNumber(); [DllImport("user32.dll")] static extern IntPtr GetClipboardOwner(); [DllImport("user32.dll")] static extern bool OpenClipboard(IntPtr owner); @@ -141,6 +149,14 @@ namespace HerdrInputGauntlet { if(result<0) Marshal.ThrowExceptionForHR(result); return checked((int)pid); } + public static int ClearClipboardForRun() { + if(!OpenClipboard(IntPtr.Zero)) throw new Exception("Clipboard busy; cannot start paste qualification"); + try { + int formats=CountClipboardFormats(); + if(formats!=0 && !EmptyClipboard()) throw new Exception("Clipboard clear failed"); + return formats; + } finally { CloseClipboard(); } + } public static uint SetEmptyClipboard(IntPtr owner,string text) { if(owner==IntPtr.Zero) throw new Exception("Clipboard owner is required"); diff --git a/scripts/windows_input/README.md b/scripts/windows_input/README.md index edebc8fc..61b76bd2 100644 --- a/scripts/windows_input/README.md +++ b/scripts/windows_input/README.md @@ -33,11 +33,14 @@ or mouse buttons while starting a run. Normal controller cancellation runs clean bootstrap/probe leases also expire if the controller disappears. Lease expiry is not a replacement for a secure isolated desktop. -Clipboard tests require an **empty clipboard**. Clear it yourself only after -saving anything you need. The runner will not replace existing text, images, -rich formats, or files. It writes its synthetic text while holding the clipboard -lock and clears it afterward only if its sequence number is unchanged. A later -user clipboard update is left alone. No original clipboard contents are logged. +`just test-windows-input` clears the clipboard once before the build when the +selected cases use it. **Save anything you need first:** existing text, images, +files, and other formats are discarded and cannot be restored. Direct script +calls need `-ClearClipboard` for the same behavior; without it, a nonempty +clipboard stops the run before the build. The runner writes its synthetic data +while holding the clipboard lock and clears it afterward only if its sequence +number is unchanged. A later user clipboard update is left alone. No original +clipboard contents are logged. ## Run @@ -70,7 +73,7 @@ Or invoke the script directly: ```powershell pwsh -NoProfile -File scripts/test_windows_input.ps1 ` - -ExePath 'C:\test-app\herdr.exe' -AllowInputInjection ` + -ExePath 'C:\test-app\herdr.exe' -AllowInputInjection -ClearClipboard ` -StablePath 'C:\TerminalStable\WindowsTerminal.exe' ` -PreviewPath 'C:\TerminalPreview\WindowsTerminal.exe' ``` @@ -83,7 +86,7 @@ arrays normally: ```powershell .\scripts\test_windows_input.ps1 -ExePath 'C:\test-app\herdr.exe' ` - -AllowInputInjection -Modes legacy,kitty -Cases mouse-interleave,mode-transitions ` + -AllowInputInjection -ClearClipboard -Modes legacy,kitty -Cases mouse-interleave,mode-transitions ` -Widths 120 -Heights 30 ``` @@ -92,7 +95,7 @@ server with the remote clipboard bridge active. It needs no SSH host. A focused clipboard-image qualification is: ```powershell -.\scripts\test_windows_input.ps1 -AllowInputInjection ` +.\scripts\test_windows_input.ps1 -AllowInputInjection -ClearClipboard ` -Channels stable -Paths direct,herdr-remote -Modes legacy ` -Cases clipboard-image,clipboard-mixed -Widths 80 -Heights 24 ``` diff --git a/scripts/windows_input/report.py b/scripts/windows_input/report.py index cf37f30e..74ce92cd 100644 --- a/scripts/windows_input/report.py +++ b/scripts/windows_input/report.py @@ -419,18 +419,23 @@ def qualification_matrix(result): return "MANUAL" if {row.get("case") for row in matched} != case_ids: return "PARTIAL" if "pass" in statuses else "INCONCLUSIVE" if "inconclusive" in statuses else "NOT TESTED" - if required_channels and {(row.get("host"), row.get("case")) for row in matched} != { - (channel, case_id) for channel in required_channels for case_id in case_ids}: + expected_pairs = {(channel, case_id) for channel in required_channels for case_id in case_ids} + if required_channels and {(row.get("host"), row.get("case")) for row in matched} != expected_pairs: return "PARTIAL" if case_ids == {"mode-transitions"} and path == "direct" and statuses <= {"unsupported", "inconclusive"}: return "X - mOK ignored" - if path == "direct" and modes == {"legacy"} and statuses == {"unsupported"}: + if path == "direct" and modes == {"legacy"} and statuses - {"not_run"} == {"unsupported"}: + if {(row.get("host"), row.get("case")) for row in matched if row.get("status") == "unsupported"} != expected_pairs: + return "PARTIAL" return "X - becomes Enter" if case_ids == {"shift-enter"} else "X - loses modifier" - def case_passed(case_id): - statuses_for_case = {row.get("status") for row in matched if row.get("case") == case_id} + def case_passed(case_id, channel=None): + statuses_for_case = {row.get("status") for row in matched if row.get("case") == case_id + and (channel is None or row.get("host") == channel)} return "pass" in statuses_for_case or (width == 80 and path == "direct" and modes == {"legacy"} - and case_id == "shift-enter" and statuses_for_case == {"unsupported"}) + and case_id == "shift-enter" and statuses_for_case - {"not_run"} == {"unsupported"}) per_case_passed = all(case_passed(case_id) for case_id in case_ids) + if "not_run" in statuses and all(case_passed(case_id, channel) for channel in required_channels for case_id in case_ids): + return "PARTIAL" allowed = {"pass", "inconclusive"} if width == 80 and path == "direct" and modes == {"legacy"}: allowed.add("unsupported") |
