import SDL3 public class Keyboard { public enum Keys { case a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z case leftBracket case right, left, up, down case space, tab } public static func down(_ key: Keys) -> Bool { keyState(key) & Self._DOWN == Self._DOWN } public static func pressed(_ key: Keys, repeat rep: Bool = false) -> Bool { var state = keyState(key) if rep { state &= ~Self._REPEAT } return state == Self._PRESS } public static func released(_ key: Keys) -> Bool { keyState(key) == Self._RELEASE } //MARK: - Private private static let _instance = Keyboard() public static var instance: Keyboard { _instance } @inline(__always) private static func keyState(_ key: Keys) -> UInt8 { self._instance._state[Int(key.sdlScancode.rawValue)] } private static let _UP = UInt8(0b000), _DOWN = UInt8(0b010), _IMPULSE = UInt8(0b001) private static let _REPEAT = UInt8(0b100) private static let _PRESS: UInt8 = _DOWN | _IMPULSE private static let _RELEASE: UInt8 = _UP | _IMPULSE private var _state = [UInt8](repeating: _UP, count: Int(SDL_SCANCODE_COUNT.rawValue)) internal func keyDownEvent(scan: SDL_Scancode, repeat rep: Bool) { var newState = Self._PRESS if rep { newState |= Self._REPEAT } self._state[Int(scan.rawValue)] = newState } internal func keyUpEvent(scan: SDL_Scancode) { self._state[Int(scan.rawValue)] = Self._RELEASE } internal func newFrame() { self._state = self._state.map({ $0 & ~(Self._IMPULSE | Self._REPEAT) }) } } internal extension Keyboard.Keys { var sdlKeycode: SDL_Keycode { switch self { case .a: SDLK_A case .b: SDLK_B case .c: SDLK_C case .d: SDLK_D case .e: SDLK_E case .f: SDLK_F case .g: SDLK_G case .h: SDLK_H case .i: SDLK_I case .j: SDLK_J case .k: SDLK_K case .l: SDLK_L case .m: SDLK_M case .n: SDLK_N case .o: SDLK_O case .p: SDLK_P case .q: SDLK_Q case .r: SDLK_R case .s: SDLK_S case .t: SDLK_T case .u: SDLK_U case .v: SDLK_V case .w: SDLK_W case .x: SDLK_X case .y: SDLK_Y case .z: SDLK_Z case .leftBracket: SDLK_LEFTBRACKET case .left: SDLK_LEFT case .right: SDLK_RIGHT case .up: SDLK_UP case .down: SDLK_DOWN case .space: SDLK_SPACE case .tab: SDLK_TAB } } var sdlScancode: SDL_Scancode { switch self { case .a: SDL_SCANCODE_A case .b: SDL_SCANCODE_B case .c: SDL_SCANCODE_C case .d: SDL_SCANCODE_D case .e: SDL_SCANCODE_E case .f: SDL_SCANCODE_F case .g: SDL_SCANCODE_G case .h: SDL_SCANCODE_H case .i: SDL_SCANCODE_I case .j: SDL_SCANCODE_J case .k: SDL_SCANCODE_K case .l: SDL_SCANCODE_L case .m: SDL_SCANCODE_M case .n: SDL_SCANCODE_N case .o: SDL_SCANCODE_O case .p: SDL_SCANCODE_P case .q: SDL_SCANCODE_Q case .r: SDL_SCANCODE_R case .s: SDL_SCANCODE_S case .t: SDL_SCANCODE_T case .u: SDL_SCANCODE_U case .v: SDL_SCANCODE_V case .w: SDL_SCANCODE_W case .x: SDL_SCANCODE_X case .y: SDL_SCANCODE_Y case .z: SDL_SCANCODE_Z case .leftBracket: SDL_SCANCODE_LEFTBRACKET case .left: SDL_SCANCODE_LEFT case .right: SDL_SCANCODE_RIGHT case .up: SDL_SCANCODE_UP case .down: SDL_SCANCODE_DOWN case .space: SDL_SCANCODE_SPACE case .tab: SDL_SCANCODE_TAB } } } fileprivate extension SDL_Keycode { init(scancode: SDL_Scancode) { self.init(scancode.rawValue | UInt32(SDLK_SCANCODE_MASK)) } }