voxelotl-engine/Sources/Voxelotl/Input/Mouse.swift

84 lines
2.8 KiB
Swift

import SDL3
public class Mouse {
public struct Buttons: OptionSet {
public let rawValue: UInt32
public init(rawValue: UInt32) { self.rawValue = rawValue }
static let left = Self(rawValue: UInt32(SDL_BUTTON_LEFT).buttonMask)
static let middle = Self(rawValue: UInt32(SDL_BUTTON_MIDDLE).buttonMask)
static let right = Self(rawValue: UInt32(SDL_BUTTON_RIGHT).buttonMask)
static let button4 = Self(rawValue: UInt32(SDL_BUTTON_X1).buttonMask)
static let button5 = Self(rawValue: UInt32(SDL_BUTTON_X2).buttonMask)
}
public static var capture: Bool {
get { self._instance.getCapture() }
set { self._instance.setCapture(newValue) }
}
public static var position: SIMD2<Float> { self._instance.getAbsolute() }
public static var relative: SIMD2<Float> { self._instance.getDelta() }
public static func down(_ btn: Buttons) -> Bool {
btn.isSubset(of: self._instance._btns)
}
public static func pressed(_ btn: Buttons) -> Bool {
btn.isSubset(of: self._instance._btns.intersection(self._instance._btnImpulse))
}
public static func released(_ btn: Buttons) -> Bool {
btn.isSubset(of: self._instance._btnImpulse.subtracting(self._instance._btns))
}
//MARK: - Private
private static let _instance = Mouse()
public static var instance: Mouse { self._instance }
private var _window: OpaquePointer!
private var _captured: Bool = false
private var _dpiScale: SIMD2<Float> = .one
private var _abs: SIMD2<Float> = .zero, _delta: SIMD2<Float> = .zero
private var _btns: Buttons = [], _btnImpulse: Buttons = []
private func getCapture() -> Bool { self._captured }
private func setCapture(_ toggle: Bool) {
if SDL_SetWindowRelativeMouseMode(self._window, toggle) && SDL_SetWindowMouseGrab(self._window, toggle) {
self._captured = toggle
}
}
internal func setDPI(scale: SIMD2<Float>) { self._dpiScale = scale }
private func getAbsolute() -> SIMD2<Float> { self._abs * self._dpiScale }
private func getDelta() -> SIMD2<Float> { self._delta }
internal func buttonEvent(btn: UInt32, state: Bool) {
if state {
self._btns.formUnion(.init(rawValue: btn.buttonMask))
} else {
self._btns.subtract(.init(rawValue: btn.buttonMask))
}
self._btnImpulse.formUnion(.init(rawValue: btn.buttonMask))
}
internal func motionEvent(absolute: SIMD2<Float>, relative: SIMD2<Float>) {
self._abs = absolute
self._delta += relative
}
internal func newFrame(_ window: OpaquePointer) {
self._window = window
let grabbedFlag = SDL_WindowFlags(SDL_WINDOW_MOUSE_GRABBED)
self._captured = (SDL_GetWindowFlags(window) & grabbedFlag) == grabbedFlag
self._delta = .zero
self._btnImpulse = []
}
}
fileprivate extension UInt32 {
var buttonMask: UInt32 { 1 &<< (self &- 1) }
}