using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Loader; using System.Security.Cryptography; using Mono.Cecil; using Mono.Cecil.Cil; using XFESystem.Core; using XFESystem.Desktop; using XFESystem.Kernel; using Xunit; namespace XFESystem.Desktop.Platform.Tests; /// /// Executes the pinned driver's real boot-report differ and HID usage map. /// Only its final Report sink is redirected to the real desktop capture queue; /// this verifies the software boundary, not an xHCI/Corsair hardware transfer. /// public sealed class UsbHidShortcutTests { private sealed class Canvas : IDesktopCanvas { public int Width => 1280; public int Height => 800; public void Fill(uint rgb, int x, int y, int width, int height) { } public void Line(uint rgb, int x1, int y1, int x2, int y2) { } public void Text(string text, uint rgb, int x, int y) { } public void Present() { } } private sealed class Web : IBrowserService, IInteractiveBrowserFactory, IInteractiveBrowserSession { public readonly List Keys = []; public BrowserPage CurrentPage { get; private set; } = new("about:Start", "Test", ""); public long Revision => 0; public double DocumentHeight => 0; public IInteractiveBrowserSession CreatePageSession() => this; public BrowserPage Navigate(string address) => CurrentPage = new(address, "Test", ""); public BrowserPage NavigateEditor(string source, string path) => CurrentPage = new(path, "Test", source); public string? ReadEditorText() => CurrentPage.Text; public string ExecuteJavaScript(string source) => ""; public string PreviewMarkdown(string markdown) => markdown; public void Paint(IDesktopCanvas canvas, DesktopBounds viewport, double scrollY) { } public bool Pointer(BrowserPointerInput input) => false; public bool Key(BrowserKeyboardInput input) { Keys.Add(input); return true; } public bool AdvanceTime(long monotonicMilliseconds) => false; } private sealed class DriverBoundary : IDisposable { private delegate void ReportHandler(ReadOnlySpan report); private readonly AssemblyLoadContext context = new("PinnedUsbBootReports-" + Guid.NewGuid().ToString("N"), true); private readonly string packages, source, originalHash, patched; private readonly ReportHandler onReport; public DriverBoundary() { packages = Environment.GetEnvironmentVariable("NUGET_PACKAGES") ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages"); source = PackageAssembly("Cosmos.Kernel.Drivers"); originalHash = Hash(source); string directory = Path.Combine(AppContext.BaseDirectory, "PatchFixtures", Guid.NewGuid().ToString("N")); Directory.CreateDirectory(directory); patched = Path.Combine(directory, "Cosmos.Kernel.Drivers.dll"); using (var driver = AssemblyDefinition.ReadAssembly(source, new ReaderParameters { InMemory = true })) { var type = driver.MainModule.Types.Single(t => t.FullName == "Cosmos.Kernel.Drivers.Usb.Input.UsbKeyboard.UsbKeyboardState"); var output = type.Methods.Single(m => m.Name == "Report"); Assert.Equal(new[] { MetadataType.Byte, MetadataType.Boolean }, output.Parameters.Select(p => p.ParameterType.MetadataType)); output.Body = new Mono.Cecil.Cil.MethodBody(output); var il = output.Body.GetILProcessor(); il.Append(il.Create(OpCodes.Ldarg_1)); il.Append(il.Create(OpCodes.Ldarg_2)); il.Append(il.Create(OpCodes.Call, driver.MainModule.ImportReference(typeof(NativeKeyboardCapture).GetMethod(nameof(NativeKeyboardCapture.Observe))!))); il.Append(il.Create(OpCodes.Ret)); driver.Write(patched); } context.Resolving += (_, name) => name.Name == typeof(NativeKeyboardCapture).Assembly.GetName().Name ? typeof(NativeKeyboardCapture).Assembly : name.Name?.StartsWith("Cosmos.", StringComparison.Ordinal) == true ? context.LoadFromAssemblyPath(PackageAssembly(name.Name)) : null; var assembly = context.LoadFromAssemblyPath(patched); var stateType = assembly.GetType("Cosmos.Kernel.Drivers.Usb.Input.UsbKeyboard.UsbKeyboardState", true)!; var state = RuntimeHelpers.GetUninitializedObject(stateType); stateType.GetField("_previousReport", BindingFlags.Instance | BindingFlags.NonPublic)!.SetValue(state, new byte[8]); onReport = stateType.GetMethod("OnReport", BindingFlags.Instance | BindingFlags.NonPublic)!.CreateDelegate(state); while (NativeKeyboardCapture.TryRead(out _, out _, out _)) { } } public void Send(NativeDesktopInput router, DesktopSession session, byte modifiers, params byte[] usages) { byte[] report = new byte[8]; report[0] = modifiers; usages.AsSpan().CopyTo(report.AsSpan(2)); onReport(report); while (NativeKeyboardCapture.TryRead(out var scan, out var released, out var lost)) if (router.Translate(scan, released, lost, session, out var character) is { } key) session.Key(key, character); } private string PackageAssembly(string name) { string root = Path.Combine(packages, name.ToLowerInvariant(), "3.0.94"); string runtime = Path.Combine(root, "runtimes", "linux-x64", "lib", "net10.0", name + ".dll"); return File.Exists(runtime) ? runtime : Path.Combine(root, "lib", "net10.0", name + ".dll"); } private static string Hash(string path) => Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(path))); public void Dispose() { Assert.Equal(originalHash, Hash(source)); context.Unload(); } } [Theory] [InlineData((byte)0x08)] [InlineData((byte)0x80)] public void RealUsbGuiReportOpensStartOnRelease(byte modifier) { using var usb = new DriverBoundary(); var input = new NativeDesktopInput(); var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace()); usb.Send(input, desktop, modifier); Assert.False(desktop.IsStartMenuOpen); usb.Send(input, desktop, 0); Assert.True(desktop.IsStartMenuOpen); usb.Send(input, desktop, 0); Assert.True(desktop.IsStartMenuOpen); } [Theory] [InlineData((byte)0x04)] [InlineData((byte)0x40)] public void RealUsbAltTabWinsOverFocusedWebEditorAndCommitsOnAltRelease(byte modifier) { using var usb = new DriverBoundary(); var web = new Web(); var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace(), web); var files = desktop.Open(DesktopApplication.Files); var editor = desktop.Open(DesktopApplication.Editor, SystemPaths.Combine(SystemPaths.Documents, "Hello.cs")); desktop.Open(DesktopApplication.Browser); var input = new NativeDesktopInput(); usb.Send(input, desktop, modifier, 0x2B); Assert.Same(editor, desktop.ActiveWindow); Assert.True(desktop.IsWindowSwitching); usb.Send(input, desktop, modifier); usb.Send(input, desktop, modifier, 0x2B); Assert.Same(files, desktop.ActiveWindow); usb.Send(input, desktop, 0); Assert.False(desktop.IsWindowSwitching); Assert.DoesNotContain(web.Keys, key => key.Code == "Tab" || key.Key == "Tab"); Assert.DoesNotContain(web.Keys, key => key.Text.Contains('\t')); } [Theory] [InlineData((byte)0x08)] [InlineData((byte)0x80)] public void RealUsbWinEReleaseInSameReportDoesNotLeakUnmatchedEditorKeyup(byte modifier) { using var usb = new DriverBoundary(); var web = new Web(); var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace(), web); desktop.Open(DesktopApplication.Editor, SystemPaths.Combine(SystemPaths.Documents, "Hello.cs")); var input = new NativeDesktopInput(); usb.Send(input, desktop, modifier, 0x08); Assert.Equal(DesktopApplication.Files, desktop.ActiveWindow!.Application); // Re-focus a web editor before the combined modifier/key break arrives. desktop.Open(DesktopApplication.Editor, SystemPaths.Combine(SystemPaths.Documents, "Hello.cs")); usb.Send(input, desktop, 0); Assert.False(desktop.IsStartMenuOpen); Assert.DoesNotContain(web.Keys, key => key.Code == "KeyE"); } [Fact] public void RealUsbShiftAltTabReversesAndKeepsSwitchUntilBothAltsRelease() { using var usb = new DriverBoundary(); var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace()); var files = desktop.Open(DesktopApplication.Files); desktop.Open(DesktopApplication.Editor); desktop.Open(DesktopApplication.Terminal); var input = new NativeDesktopInput(); usb.Send(input, desktop, 0x46, 0x2B); // LShift + LAlt + RAlt + Tab Assert.Same(files, desktop.ActiveWindow); Assert.True(desktop.IsWindowSwitching); usb.Send(input, desktop, 0x40); Assert.True(desktop.IsWindowSwitching); usb.Send(input, desktop, 0); Assert.False(desktop.IsWindowSwitching); } [Fact] public void RealUsbWinDMinimizesAllWindowsWithoutOpeningStart() { using var usb = new DriverBoundary(); var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace()); var files = desktop.Open(DesktopApplication.Files); var editor = desktop.Open(DesktopApplication.Editor); var input = new NativeDesktopInput(); usb.Send(input, desktop, 0x08, 0x07); Assert.True(files.IsMinimized); Assert.True(editor.IsMinimized); usb.Send(input, desktop, 0); Assert.False(desktop.IsStartMenuOpen); } [Fact] public void UnsupportedWindowsChordDoesNotLeakItsBreakIntoWebEditor() { using var usb = new DriverBoundary(); var web = new Web(); var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace(), web); desktop.Open(DesktopApplication.Editor, SystemPaths.Combine(SystemPaths.Documents, "Hello.cs")); var input = new NativeDesktopInput(); usb.Send(input, desktop, 0x08, 0x14); // Win+Q has no command yet. usb.Send(input, desktop, 0); Assert.False(desktop.IsStartMenuOpen); Assert.DoesNotContain(web.Keys, key => key.Code == "KeyQ"); } [Fact] public void ControlEscapeIsSystemOwnedEvenWhenWebEditorHasFocus() { using var usb = new DriverBoundary(); var web = new Web(); var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace(), web); desktop.Open(DesktopApplication.Editor, SystemPaths.Combine(SystemPaths.Documents, "Hello.cs")); var input = new NativeDesktopInput(); usb.Send(input, desktop, 0x01, 0x29); Assert.True(desktop.IsStartMenuOpen); usb.Send(input, desktop, 0); Assert.DoesNotContain(web.Keys, key => key.Key == "Escape"); } [Fact] public void ShortcutDiagnosticsExposeOnlySystemCodesAndDispatch() { var trace = new List(); var input = new NativeDesktopInput { Trace = trace.Add }; var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace()); input.Translate(0x5B, false, false, desktop, out _); Assert.Equal(DesktopKey.OpenFiles, input.Translate(0x12, false, false, desktop, out _)); Assert.Contains(trace, line => line.Contains("XFE_NATIVE_SHORTCUT|Desktop|Scan|18|Released|0|Key|OpenFiles", StringComparison.Ordinal)); Assert.DoesNotContain(trace, line => line.Contains("KeyE", StringComparison.Ordinal)); } }