forked from MeloNX/MeloNX
Fix for virtual controller detach | pass gamepad haptic engine
This commit is contained in:
parent
ac4e5d394e
commit
500f3d5b9e
@ -11,15 +11,22 @@ import GameController
|
|||||||
class NativeController: Hashable {
|
class NativeController: Hashable {
|
||||||
private var instanceID: SDL_JoystickID = -1
|
private var instanceID: SDL_JoystickID = -1
|
||||||
private var controller: OpaquePointer?
|
private var controller: OpaquePointer?
|
||||||
private let nativeController: GCController
|
private var nativeController: GCController
|
||||||
|
private let controllerHaptics: CHHapticEngine?
|
||||||
|
|
||||||
public var controllername: String { "GC - \(nativeController.vendorName ?? "Unknown")" }
|
public var controllername: String { "GC - \(nativeController.vendorName ?? "Unknown")" }
|
||||||
|
|
||||||
init(_ controller: GCController) {
|
init(_ controller: GCController) {
|
||||||
nativeController = controller
|
nativeController = controller
|
||||||
|
controllerHaptics = nativeController.haptics?.createEngine(withLocality: .default)
|
||||||
|
try? controllerHaptics?.start()
|
||||||
setupHandheldController()
|
setupHandheldController()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
cleanup()
|
||||||
|
}
|
||||||
|
|
||||||
private func setupHandheldController() {
|
private func setupHandheldController() {
|
||||||
if SDL_WasInit(Uint32(SDL_INIT_GAMECONTROLLER)) == 0 {
|
if SDL_WasInit(Uint32(SDL_INIT_GAMECONTROLLER)) == 0 {
|
||||||
SDL_InitSubSystem(Uint32(SDL_INIT_GAMECONTROLLER))
|
SDL_InitSubSystem(Uint32(SDL_INIT_GAMECONTROLLER))
|
||||||
@ -37,7 +44,7 @@ class NativeController: Hashable {
|
|||||||
button_mask: 0,
|
button_mask: 0,
|
||||||
axis_mask: 0,
|
axis_mask: 0,
|
||||||
name: (controllername as NSString).utf8String,
|
name: (controllername as NSString).utf8String,
|
||||||
userdata: nil,
|
userdata: Unmanaged.passUnretained(self).toOpaque(),
|
||||||
Update: { userdata in
|
Update: { userdata in
|
||||||
// Update joystick state here
|
// Update joystick state here
|
||||||
},
|
},
|
||||||
@ -46,7 +53,9 @@ class NativeController: Hashable {
|
|||||||
},
|
},
|
||||||
Rumble: { userdata, lowFreq, highFreq in
|
Rumble: { userdata, lowFreq, highFreq in
|
||||||
print("Rumble with \(lowFreq), \(highFreq)")
|
print("Rumble with \(lowFreq), \(highFreq)")
|
||||||
VirtualController.rumble(lowFreq: Float(lowFreq), highFreq: Float(highFreq))
|
guard let userdata else { return 0 }
|
||||||
|
let _self = Unmanaged<NativeController>.fromOpaque(userdata).takeUnretainedValue()
|
||||||
|
VirtualController.rumble(lowFreq: Float(lowFreq), highFreq: Float(highFreq), engine: _self.controllerHaptics)
|
||||||
return 0
|
return 0
|
||||||
},
|
},
|
||||||
RumbleTriggers: { userdata, leftRumble, rightRumble in
|
RumbleTriggers: { userdata, leftRumble, rightRumble in
|
||||||
@ -211,16 +220,13 @@ class NativeController: Hashable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cleanup() {
|
func cleanup() {
|
||||||
if let controller = controller {
|
if let controller {
|
||||||
|
SDL_JoystickDetachVirtual(instanceID)
|
||||||
SDL_GameControllerClose(controller)
|
SDL_GameControllerClose(controller)
|
||||||
self.controller = nil
|
self.controller = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
|
||||||
cleanup()
|
|
||||||
}
|
|
||||||
|
|
||||||
func hash(into hasher: inout Hasher) {
|
func hash(into hasher: inout Hasher) {
|
||||||
hasher.combine(nativeController)
|
hasher.combine(nativeController)
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ class VirtualController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func rumble(lowFreq: Float, highFreq: Float) {
|
static func rumble(lowFreq: Float, highFreq: Float, engine: CHHapticEngine? = nil) {
|
||||||
do {
|
do {
|
||||||
// Low-frequency haptic pattern
|
// Low-frequency haptic pattern
|
||||||
let lowFreqPattern = try CHHapticPattern(events: [
|
let lowFreqPattern = try CHHapticPattern(events: [
|
||||||
@ -96,9 +96,23 @@ class VirtualController {
|
|||||||
], relativeTime: 0.2, duration: 0.2)
|
], relativeTime: 0.2, duration: 0.2)
|
||||||
], parameters: [])
|
], parameters: [])
|
||||||
|
|
||||||
// Create and start the haptic engine
|
// Mutable engine
|
||||||
let engine = try CHHapticEngine()
|
var engine = engine
|
||||||
try engine.start()
|
|
||||||
|
// If no engine passed, use device engine
|
||||||
|
if engine == nil {
|
||||||
|
// Create and start the haptic engine
|
||||||
|
if hapticEngine == nil {
|
||||||
|
hapticEngine = try CHHapticEngine()
|
||||||
|
try hapticEngine?.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
engine = hapticEngine
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let engine else {
|
||||||
|
return print("Error creating haptic patterns: hapticEngine is nil")
|
||||||
|
}
|
||||||
|
|
||||||
// Create and play the low-frequency player
|
// Create and play the low-frequency player
|
||||||
let lowFreqPlayer = try engine.makePlayer(with: lowFreqPattern)
|
let lowFreqPlayer = try engine.makePlayer(with: lowFreqPattern)
|
||||||
@ -113,6 +127,8 @@ class VirtualController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static var hapticEngine: CHHapticEngine?
|
||||||
|
|
||||||
|
|
||||||
func updateAxisValue(value: Sint16, forAxis axis: SDL_GameControllerAxis) {
|
func updateAxisValue(value: Sint16, forAxis axis: SDL_GameControllerAxis) {
|
||||||
guard controller != nil else { return }
|
guard controller != nil else { return }
|
||||||
|
@ -165,6 +165,7 @@ struct ContentView: View {
|
|||||||
queue: .main) { notification in
|
queue: .main) { notification in
|
||||||
if let controller = notification.object as? GCController {
|
if let controller = notification.object as? GCController {
|
||||||
print("Controller disconnected: \(controller.productCategory)")
|
print("Controller disconnected: \(controller.productCategory)")
|
||||||
|
nativeControllers[controller]?.cleanup()
|
||||||
nativeControllers[controller] = nil
|
nativeControllers[controller] = nil
|
||||||
refreshControllersList()
|
refreshControllersList()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user