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

XFESystem

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

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Threading;

namespace XFESystem.Kernel;

/// <summary>Bounded raw make/break reports; the original Cosmos decoder still runs.</summary>
public static class NativeKeyboardCapture
{
    private const int Capacity = 512;
    private static readonly int[] reports = new int[Capacity];
    private static long published, consumed;
    private static int overflow, writerGate;

    // Touch before enabling keyboard delivery when the platform controls initialization.
    public static void Prepare() { _ = reports.Length; }

    // O(1), read-only, and safe to sample between software raster rows.
    public static bool HasPendingReports => Volatile.Read(ref overflow) != 0 ||
        Volatile.Read(ref published) != Volatile.Read(ref consumed);

    // Called by the project-local IL hook at the start of HandleScanCode.
    // No allocation, blocking, logging or desktop mutation in interrupt context.
    public static void Observe(byte scanCode, bool released)
    {
        // Never spin in an interrupt or overwrite an unread slot. A nested/concurrent
        // writer is treated as loss, which the consumer handles by resetting modifiers.
        if (Interlocked.CompareExchange(ref writerGate, 1, 0) != 0)
        { Volatile.Write(ref overflow, 1); return; }
        try
        {
            long head = published;
            if (unchecked(head - Volatile.Read(ref consumed)) >= Capacity)
            { Volatile.Write(ref overflow, 1); return; }
            reports[(int)(head & (Capacity - 1))] = scanCode | (released ? 256 : 0);
            Volatile.Write(ref published, unchecked(head + 1));
        }
        finally { Volatile.Write(ref writerGate, 0); }
    }

    public static bool TryRead(out byte scanCode, out bool released, out bool lostReports)
    {
        lostReports = false;
        scanCode = 0; released = false;
        if (Volatile.Read(ref overflow) != 0)
        {
            // Do not replay old modifier makes after a lost break. The gate also
            // excludes an in-flight writer whose report belongs to the lost batch.
            if (Interlocked.CompareExchange(ref writerGate, 1, 0) != 0) return false;
            try
            {
                Volatile.Write(ref consumed, published);
                Interlocked.Exchange(ref overflow, 0);
            }
            finally { Volatile.Write(ref writerGate, 0); }
            lostReports = true; released = true;
            return true; // Explicit loss sentinel; Translate resets without decoding a key.
        }
        long head = Volatile.Read(ref published);
        long tail = consumed;
        if (tail == head) return false;
        int report = reports[(int)(tail & (Capacity - 1))];
        Volatile.Write(ref consumed, unchecked(tail + 1));
        scanCode = (byte)report; released = (report & 256) != 0;
        return true;
    }
}