XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
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;

/// <summary>
/// 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.
/// </summary>
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<BrowserKeyboardInput> 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<byte> 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<ReportHandler>(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<string>(); 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));
    }
}