import Foundation import SDL2 public class GamePad { public enum Axes { case leftStickX, leftStickY case rightStickX, rightStickY case leftTrigger, rightTrigger } public enum Buttons { case east, south, north, west case select, start, guide, mic, touchPad case leftStick, rightStick case leftBumper, rightBumper case dpadLeft, dpadRight, dpadUp, dpadDown case paddle1, paddle2, paddle3, paddle4 } public struct State { public func down(_ btn: Buttons) -> Bool { btnState(btn) & GamePad._DOWN == GamePad._DOWN } public func pressed(_ btn: Buttons) -> Bool { btnState(btn) == GamePad._PRESS } public func released(_ btn: Buttons) -> Bool { btnState(btn) == GamePad._RELEASE } public func axis(_ axis: Axes) -> Float { let axisRescale = 1.0 / Float(Int16.max) return Float(rawAxis(axis)) * axisRescale } @inline(__always) func rawAxis(_ axis: Axes) -> Int16 { _axes[Int(axis.sdlEnum.rawValue)] } @inline(__always) private func btnState(_ btn: Buttons) -> UInt8 { _btns[Int(btn.sdlEnum.rawValue)] } private let _btns: [UInt8], _axes: [Int16] internal init(btns: [UInt8], axes: [Int16]) { self._btns = btns self._axes = axes } } public var state: State { .init(btns: _btns, axes: _axes) } @inline(__always) public static var current: GamePad? { let input = Input.instance return if let id = input.firstPadID { input.getPad(id: id) } else { nil } } private static let _UP = UInt8(0b00), _DOWN = UInt8(0b10), _IMPULSE = UInt8(0b01) private static let _PRESS = _DOWN | _IMPULSE private static let _RELEASE = _UP | _IMPULSE private var _btns = [UInt8](repeating: 0, count: Int(SDL_CONTROLLER_BUTTON_MAX.rawValue)) private var _axes = [Int16](repeating: 0, count: Int(SDL_CONTROLLER_AXIS_MAX.rawValue)) private var _sdlPad: OpaquePointer internal init(pad: OpaquePointer) { self._sdlPad = pad } internal func newTick() { _btns = _btns.map({ $0 & ~Self._IMPULSE }) } internal func close() { SDL_GameControllerClose(_sdlPad) } internal func buttonPressEvent(_ btn: UInt8) { _btns[Int(btn)] = Self._PRESS } internal func buttonReleaseEvent(_ btn: UInt8) { _btns[Int(btn)] = Self._RELEASE } internal func axisEvent(_ axis: UInt8, _ value: Int16) { _axes[Int(axis)] = value } } public extension GamePad.State { var leftStick: Vec2f { Vec2f(axis(.leftStickX), axis(.leftStickY)) } var rightStick: Vec2f { Vec2f(axis(.rightStickX), axis(.rightStickY)) } } internal extension GamePad.Axes { var sdlEnum: SDL_GameControllerAxis { switch self { case .leftStickX: SDL_CONTROLLER_AXIS_LEFTX case .leftStickY: SDL_CONTROLLER_AXIS_LEFTY case .rightStickX: SDL_CONTROLLER_AXIS_RIGHTX case .rightStickY: SDL_CONTROLLER_AXIS_RIGHTY case .leftTrigger: SDL_CONTROLLER_AXIS_TRIGGERLEFT case .rightTrigger: SDL_CONTROLLER_AXIS_TRIGGERRIGHT } } } internal extension GamePad.Buttons { var sdlEnum: SDL_GameControllerButton { switch self { case .east: SDL_CONTROLLER_BUTTON_B case .south: SDL_CONTROLLER_BUTTON_A case .west: SDL_CONTROLLER_BUTTON_Y case .north: SDL_CONTROLLER_BUTTON_X case .start: SDL_CONTROLLER_BUTTON_START case .select: SDL_CONTROLLER_BUTTON_BACK case .guide: SDL_CONTROLLER_BUTTON_GUIDE case .mic: SDL_CONTROLLER_BUTTON_MISC1 case .touchPad: SDL_CONTROLLER_BUTTON_TOUCHPAD case .leftStick: SDL_CONTROLLER_BUTTON_LEFTSTICK case .rightStick: SDL_CONTROLLER_BUTTON_RIGHTSTICK case .leftBumper: SDL_CONTROLLER_BUTTON_LEFTSHOULDER case .rightBumper: SDL_CONTROLLER_BUTTON_RIGHTSHOULDER case .dpadUp: SDL_CONTROLLER_BUTTON_DPAD_UP case .dpadDown: SDL_CONTROLLER_BUTTON_DPAD_DOWN case .dpadLeft: SDL_CONTROLLER_BUTTON_DPAD_LEFT case .dpadRight: SDL_CONTROLLER_BUTTON_DPAD_RIGHT case .paddle1: SDL_CONTROLLER_BUTTON_PADDLE1 case .paddle2: SDL_CONTROLLER_BUTTON_PADDLE2 case .paddle3: SDL_CONTROLLER_BUTTON_PADDLE3 case .paddle4: SDL_CONTROLLER_BUTTON_PADDLE4 } } }