Fix Virtual Controller

This commit is contained in:
Stossy11 2024-12-08 22:12:19 +11:00
parent d64ef5eed9
commit 658bdd7bec
6 changed files with 112 additions and 13 deletions

View File

@ -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))

View File

@ -33,12 +33,15 @@ func waitforcontroller() {
}
let controllerView = ControllerView()
let hostingController = UIHostingController(rootView: controllerView)
hostingController.view.frame = window.bounds // Set the frame of the SwiftUI view
hostingController.view.backgroundColor = .clear
// 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 {
@ -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
}
}

View File

@ -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
}

View File

@ -87,6 +87,7 @@ struct ControllerView: View {
}
}
}
.padding()
}
}

View File

@ -43,9 +43,9 @@ public struct Joystick: View {
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)
}
}
}