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

XFESystem

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

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

namespace XFESystem.NativeKernel;

/// <summary>Value-only 32-byte PS/2 report ABI. Deltas are native pixels, positive Y points down.</summary>
[StructLayout(LayoutKind.Sequential, Size = 32)]
public struct NativeInputReport
{
    public uint Kind, KeyCode, Unicode, Modifiers;
    public int DeltaX, DeltaY;
    public uint Buttons;
    public int Wheel;
}

/// <summary>No managed references: this state can live in the zero-library kernel's owned storage.</summary>
public struct Ps2DecodeState
{
    public ulong HeldLow, HeldHigh, ExtendedHeldLow, ExtendedHeldHigh;
    public uint Modifiers, MouseButtons;
    public byte ExtendedPrefix, PauseIndex, MouseIndex, MouseHeader, MouseX, MouseY, MouseDeviceId;
}

/// <summary>Project-owned set-1 and negotiated PS/2 mouse decoder. Hardware I/O and queue ownership are separate.</summary>
public static unsafe class Ps2Decoder
{
    public const uint KeyDown = 1, KeyUp = 2, Pointer = 3, Lost = 4;
    public const uint Shift = 1, Control = 2, Alt = 4, Super = 8, CapsLock = 16, NumLock = 32;
    public const uint PauseKey = 0x245;

    public static void ResetKeyboard(ref Ps2DecodeState state)
    {
        // Retain known held lock keys until their real break; typematic after an overrun must not toggle locks twice.
        state.HeldLow &= 1UL << 0x3A;
        state.HeldHigh &= 1UL << (0x45 - 64);
        state.ExtendedHeldLow = state.ExtendedHeldHigh = 0;
        state.ExtendedPrefix = state.PauseIndex = 0;
        state.Modifiers &= CapsLock | NumLock;
    }

    public static void ResetMouse(ref Ps2DecodeState state) => state.MouseIndex = 0;

    // Only select a format after the disabled physical device's F2 reply.
    // Unrecognized IDs need a hardware reset before the driver enables input.
    public static bool SetMouseDeviceId(ref Ps2DecodeState state, byte deviceId)
    {
        ResetMouse(ref state);
        state.MouseDeviceId = deviceId is 0 or 3 ? deviceId : (byte)0;
        return deviceId is 0 or 3;
    }

    public static bool DecodeKeyboard(ref Ps2DecodeState state, byte scan, NativeInputReport* output)
    {
        if (output == null) return false;
        *output = default;
        if (scan is 0x00 or 0xFF)
        {
            ResetKeyboard(ref state); MakeLost(ref state, output); return true;
        }
        if (state.PauseIndex != 0)
        {
            byte expected = state.PauseIndex switch { 1 => 0x1D, 2 => 0x45, 3 => 0xE1, 4 => 0x9D, _ => 0xC5 };
            if (scan != expected)
            { ResetKeyboard(ref state); MakeLost(ref state, output); return true; }
            if (++state.PauseIndex < 6) return false;
            state.PauseIndex = 0;
            output->Kind = KeyDown; output->KeyCode = PauseKey; output->Modifiers = state.Modifiers;
            return true;
        }
        if (scan == 0xE1) { state.ExtendedPrefix = 0; state.PauseIndex = 1; return false; }
        if (scan == 0xE0) { state.ExtendedPrefix = 1; return false; }
        // Command acknowledgements are not keys. Driver initialization must consume BAT replies before normal decoding.
        if (scan is 0xFA or 0xFE) return false;
        bool extended = state.ExtendedPrefix != 0, released = (scan & 0x80) != 0;
        state.ExtendedPrefix = 0;
        uint code = (uint)(scan & 0x7F), key = code | (extended ? 0x100u : 0u);
        // PrintScreen's E0 2A/E0 AA fake Shift bytes must never change real modifiers.
        if (extended && code is 0x2A or 0x36) return false;
        bool previouslyHeld = IsHeld(ref state, code, extended);
        SetHeld(ref state, code, extended, !released);
        uint locks = state.Modifiers & (CapsLock | NumLock);
        if (!extended && !released && !previouslyHeld)
        {
            if (code == 0x3A) locks ^= CapsLock;
            if (code == 0x45) locks ^= NumLock;
        }
        state.Modifiers = locks;
        if (IsHeld(ref state, 0x2A, false) || IsHeld(ref state, 0x36, false)) state.Modifiers |= Shift;
        if (IsHeld(ref state, 0x1D, false) || IsHeld(ref state, 0x1D, true)) state.Modifiers |= Control;
        if (IsHeld(ref state, 0x38, false) || IsHeld(ref state, 0x38, true)) state.Modifiers |= Alt;
        if (IsHeld(ref state, 0x5B, true) || IsHeld(ref state, 0x5C, true)) state.Modifiers |= Super;
        output->Kind = released ? KeyUp : KeyDown; output->KeyCode = key; output->Modifiers = state.Modifiers;
        output->Unicode = released ? 0u : Character(key, state.Modifiers);
        return true;
    }

    public static bool DecodeMouse(ref Ps2DecodeState state, byte packetByte, NativeInputReport* output)
    {
        if (output == null) return false;
        *output = default;
        if (state.MouseIndex == 0)
        {
            if ((packetByte & 8) == 0) return false;
            state.MouseHeader = packetByte; state.MouseIndex = 1; return false;
        }
        if (state.MouseIndex == 1) { state.MouseX = packetByte; state.MouseIndex = 2; return false; }
        if (state.MouseIndex == 2)
        {
            state.MouseY = packetByte;
            if (state.MouseDeviceId == 3) { state.MouseIndex = 3; return false; }
        }
        state.MouseIndex = 0; state.MouseButtons = (uint)(state.MouseHeader & 7);
        output->Buttons = state.MouseButtons; output->Modifiers = state.Modifiers;
        if ((state.MouseHeader & 0xC0) != 0)
        { MakeLost(ref state, output); return true; }
        output->Kind = Pointer;
        output->DeltaX = state.MouseX - ((state.MouseHeader & 0x10) != 0 ? 256 : 0);
        output->DeltaY = -(state.MouseY - ((state.MouseHeader & 0x20) != 0 ? 256 : 0));
        // IntelliMouse ID3 uses a signed full byte: raw positive is down,
        // while the shared desktop Pointer contract uses positive wheel up.
        if (state.MouseDeviceId == 3) output->Wheel = -unchecked((sbyte)packetByte);
        return true;
    }

    private static void MakeLost(ref Ps2DecodeState state, NativeInputReport* output)
    {
        *output = default; output->Kind = Lost; output->Modifiers = state.Modifiers; output->Buttons = state.MouseButtons;
    }
    private static bool IsHeld(ref Ps2DecodeState state, uint code, bool extended)
    {
        ulong bits = extended ? code < 64 ? state.ExtendedHeldLow : state.ExtendedHeldHigh : code < 64 ? state.HeldLow : state.HeldHigh;
        return (bits & (1UL << (int)(code & 63))) != 0;
    }
    private static void SetHeld(ref Ps2DecodeState state, uint code, bool extended, bool held)
    {
        ulong bit = 1UL << (int)(code & 63);
        if (extended)
        {
            if (code < 64) state.ExtendedHeldLow = held ? state.ExtendedHeldLow | bit : state.ExtendedHeldLow & ~bit;
            else state.ExtendedHeldHigh = held ? state.ExtendedHeldHigh | bit : state.ExtendedHeldHigh & ~bit;
        }
        else if (code < 64) state.HeldLow = held ? state.HeldLow | bit : state.HeldLow & ~bit;
        else state.HeldHigh = held ? state.HeldHigh | bit : state.HeldHigh & ~bit;
    }

    /// <summary>US set-1 layout, Caps XOR Shift for letters. Extended scan codes retain their distinct identity.</summary>
    public static char Character(uint key, uint modifiers)
    {
        bool shift = (modifiers & Shift) != 0;
        if (key == 0x135) return '/';
        if ((key & 0x100) != 0 || key >= 0x200) return '\0';
        char value = key switch
        {
            0x10 => 'q', 0x11 => 'w', 0x12 => 'e', 0x13 => 'r', 0x14 => 't', 0x15 => 'y', 0x16 => 'u', 0x17 => 'i', 0x18 => 'o', 0x19 => 'p',
            0x1E => 'a', 0x1F => 's', 0x20 => 'd', 0x21 => 'f', 0x22 => 'g', 0x23 => 'h', 0x24 => 'j', 0x25 => 'k', 0x26 => 'l',
            0x2C => 'z', 0x2D => 'x', 0x2E => 'c', 0x2F => 'v', 0x30 => 'b', 0x31 => 'n', 0x32 => 'm',
            0x02 => shift ? '!' : '1', 0x03 => shift ? '@' : '2', 0x04 => shift ? '#' : '3', 0x05 => shift ? '$' : '4', 0x06 => shift ? '%' : '5',
            0x07 => shift ? '^' : '6', 0x08 => shift ? '&' : '7', 0x09 => shift ? '*' : '8', 0x0A => shift ? '(' : '9', 0x0B => shift ? ')' : '0',
            0x0C => shift ? '_' : '-', 0x0D => shift ? '+' : '=', 0x1A => shift ? '{' : '[', 0x1B => shift ? '}' : ']',
            0x27 => shift ? ':' : ';', 0x28 => shift ? '"' : '\'', 0x29 => shift ? '~' : '`', 0x2B => shift ? '|' : '\\',
            0x33 => shift ? '<' : ',', 0x34 => shift ? '>' : '.', 0x35 => shift ? '?' : '/', 0x39 => ' ',
            0x37 => '*', 0x4A => '-', 0x4E => '+',
            _ => '\0'
        };
        if (value is >= 'a' and <= 'z')
        { if (shift != ((modifiers & CapsLock) != 0)) value = (char)(value - 'a' + 'A'); return value; }
        if (value == '\0' && (modifiers & NumLock) != 0 && !shift)
            value = key switch { 0x47 => '7', 0x48 => '8', 0x49 => '9', 0x4B => '4', 0x4C => '5', 0x4D => '6', 0x4F => '1', 0x50 => '2', 0x51 => '3', 0x52 => '0', 0x53 => '.', _ => '\0' };
        return value;
    }
}