forked from MeloNX/MeloNX
Fix Virtual Controller
This commit is contained in:
parent
d64ef5eed9
commit
658bdd7bec
Binary file not shown.
@ -6,11 +6,15 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreHaptics
|
||||
import UIKit
|
||||
|
||||
class VirtualController {
|
||||
private var instanceID: SDL_JoystickID = -1
|
||||
private var controller: OpaquePointer?
|
||||
|
||||
public let controllername = "MeloNX Touch Controller"
|
||||
|
||||
init() {
|
||||
setupVirtualController()
|
||||
}
|
||||
@ -22,7 +26,45 @@ class VirtualController {
|
||||
}
|
||||
|
||||
// Create virtual controller
|
||||
instanceID = SDL_JoystickAttachVirtual(SDL_JoystickType(SDL_JOYSTICK_TYPE_GAMECONTROLLER.rawValue), 6, 15, 1)
|
||||
var joystickDesc = SDL_VirtualJoystickDesc(
|
||||
version: UInt16(SDL_VIRTUAL_JOYSTICK_DESC_VERSION),
|
||||
type: Uint16(SDL_JOYSTICK_TYPE_GAMECONTROLLER.rawValue),
|
||||
naxes: 6,
|
||||
nbuttons: 15,
|
||||
nhats: 1,
|
||||
vendor_id: 0,
|
||||
product_id: 0,
|
||||
padding: 0,
|
||||
button_mask: 0,
|
||||
axis_mask: 0,
|
||||
name: controllername.withCString { $0 },
|
||||
userdata: nil,
|
||||
Update: { userdata in
|
||||
// Update joystick state here
|
||||
},
|
||||
SetPlayerIndex: { userdata, playerIndex in
|
||||
print("Player index set to \(playerIndex)")
|
||||
},
|
||||
Rumble: { userdata, lowFreq, highFreq in
|
||||
print("Rumble with \(lowFreq), \(highFreq)")
|
||||
VirtualController.rumble(lowFreq: Float(lowFreq), highFreq: Float(highFreq))
|
||||
return 0
|
||||
},
|
||||
RumbleTriggers: { userdata, leftRumble, rightRumble in
|
||||
print("Trigger rumble with \(leftRumble), \(rightRumble)")
|
||||
return 0
|
||||
},
|
||||
SetLED: { userdata, red, green, blue in
|
||||
print("Set LED to RGB(\(red), \(green), \(blue))")
|
||||
return 0
|
||||
},
|
||||
SendEffect: { userdata, data, size in
|
||||
print("Effect sent with size \(size)")
|
||||
return 0
|
||||
}
|
||||
)
|
||||
|
||||
instanceID = SDL_JoystickAttachVirtualEx(&joystickDesc)// SDL_JoystickAttachVirtual(SDL_JoystickType(SDL_JOYSTICK_TYPE_GAMECONTROLLER.rawValue), 6, 15, 1)
|
||||
if instanceID < 0 {
|
||||
print("Failed to create virtual joystick: \(String(cString: SDL_GetError()))")
|
||||
return
|
||||
@ -38,16 +80,58 @@ class VirtualController {
|
||||
}
|
||||
}
|
||||
|
||||
static func rumble(lowFreq: Float, highFreq: Float) {
|
||||
do {
|
||||
// Low-frequency haptic pattern
|
||||
let lowFreqPattern = try CHHapticPattern(events: [
|
||||
CHHapticEvent(eventType: .hapticTransient, parameters: [
|
||||
CHHapticEventParameter(parameterID: .hapticIntensity, value: lowFreq),
|
||||
CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.5)
|
||||
], relativeTime: 0, duration: 0.2)
|
||||
], parameters: [])
|
||||
|
||||
// High-frequency haptic pattern
|
||||
let highFreqPattern = try CHHapticPattern(events: [
|
||||
CHHapticEvent(eventType: .hapticTransient, parameters: [
|
||||
CHHapticEventParameter(parameterID: .hapticIntensity, value: highFreq),
|
||||
CHHapticEventParameter(parameterID: .hapticSharpness, value: 1.0)
|
||||
], relativeTime: 0.2, duration: 0.2)
|
||||
], parameters: [])
|
||||
|
||||
// Create and start the haptic engine
|
||||
let engine = try CHHapticEngine()
|
||||
try engine.start()
|
||||
|
||||
// Create and play the low-frequency player
|
||||
let lowFreqPlayer = try engine.makePlayer(with: lowFreqPattern)
|
||||
try lowFreqPlayer.start(atTime: 0)
|
||||
|
||||
// Create and play the high-frequency player after a short delay
|
||||
let highFreqPlayer = try engine.makePlayer(with: highFreqPattern)
|
||||
try highFreqPlayer.start(atTime: 0.2)
|
||||
|
||||
} catch {
|
||||
print("Error creating haptic patterns: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func updateAxisValue(value: Sint16, forAxis axis: SDL_GameControllerAxis) {
|
||||
guard controller != nil else { return }
|
||||
let joystick = SDL_JoystickFromInstanceID(instanceID)
|
||||
SDL_JoystickSetVirtualAxis(joystick, axis.rawValue, value)
|
||||
}
|
||||
|
||||
func thumbstickMoved(_ stick: ThumbstickType, x: Float, y: Float) {
|
||||
func thumbstickMoved(_ stick: ThumbstickType, x: Double, y: Double) {
|
||||
// Convert float values (-1.0 to 1.0) to SDL axis values (-32768 to 32767)
|
||||
let scaledX = Sint16(x * 32767.0)
|
||||
let scaledY = Sint16(y * 32767.0)
|
||||
var scaleFactor = 32767.0
|
||||
if UIDevice.current.systemName.contains("iPadOS") {
|
||||
scaleFactor /= (160 * 1.2)
|
||||
} else {
|
||||
scaleFactor /= 160
|
||||
}
|
||||
let scaledX = Int16(min(32767.0, max(-32768.0, x * scaleFactor)))
|
||||
let scaledY = Int16(min(32767.0, max(-32768.0, y * scaleFactor)))
|
||||
|
||||
if stick == .right {
|
||||
updateAxisValue(value: scaledX, forAxis: SDL_GameControllerAxis(SDL_CONTROLLER_AXIS_RIGHTX.rawValue))
|
||||
|
@ -33,13 +33,16 @@ func waitforcontroller() {
|
||||
}
|
||||
|
||||
let controllerView = ControllerView()
|
||||
|
||||
let hostingController = UIHostingController(rootView: controllerView)
|
||||
|
||||
hostingController.view.frame = window.bounds // Set the frame of the SwiftUI view
|
||||
|
||||
// Create the custom container
|
||||
let containerView = TransparentHostingContainerView(frame: window.bounds)
|
||||
containerView.backgroundColor = .clear
|
||||
|
||||
hostingController.view.frame = containerView.bounds
|
||||
hostingController.view.backgroundColor = .clear
|
||||
|
||||
|
||||
containerView.addSubview(hostingController.view)
|
||||
|
||||
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
|
||||
if findGCControllerView(in: window) == nil {
|
||||
window.addSubview(hostingController.view)
|
||||
@ -50,3 +53,14 @@ func waitforcontroller() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TransparentHostingContainerView: UIView {
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
// Check if the point is within the subviews of this container
|
||||
let view = super.hitTest(point, with: event)
|
||||
|
||||
// Return nil if the touch is outside visible content (passes through to views below)
|
||||
return view === self ? nil : view
|
||||
}
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ struct ContentView: View {
|
||||
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in
|
||||
controllersList = Ryujinx.shared.getConnectedControllers()
|
||||
|
||||
if let onscreen = controllersList.first(where: { $0.name == "Virtual Controller" }) {
|
||||
if let onscreen = controllersList.first(where: { $0.name == Ryujinx.shared.virtualController.controllername }) {
|
||||
self.onscreencontroller = onscreen
|
||||
}
|
||||
|
||||
|
@ -87,6 +87,7 @@ struct ControllerView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,13 +39,13 @@ public struct Joystick: View {
|
||||
locksInPlace: false)
|
||||
.onChange(of: self.joystickMonitor.xyPoint) { newValue in
|
||||
let scaledX = Float(newValue.x)
|
||||
let scaledY = Float(newValue.y) // my dumbass broke this by having -y instead of y :/
|
||||
let scaledY = Float(newValue.y) // my dumbass broke this by having -y instead of y :/
|
||||
print("Joystick Position: (\(scaledX), \(scaledY))")
|
||||
|
||||
if iscool != nil {
|
||||
Ryujinx.shared.virtualController.thumbstickMoved(.right, x: scaledX, y: scaledY)
|
||||
Ryujinx.shared.virtualController.thumbstickMoved(.right, x: newValue.x, y: newValue.y)
|
||||
} else {
|
||||
Ryujinx.shared.virtualController.thumbstickMoved(.left, x: scaledX, y: scaledY)
|
||||
Ryujinx.shared.virtualController.thumbstickMoved(.left, x: newValue.x, y: newValue.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user