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 Foundation
|
||||||
|
import CoreHaptics
|
||||||
|
import UIKit
|
||||||
|
|
||||||
class VirtualController {
|
class VirtualController {
|
||||||
private var instanceID: SDL_JoystickID = -1
|
private var instanceID: SDL_JoystickID = -1
|
||||||
private var controller: OpaquePointer?
|
private var controller: OpaquePointer?
|
||||||
|
|
||||||
|
public let controllername = "MeloNX Touch Controller"
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
setupVirtualController()
|
setupVirtualController()
|
||||||
}
|
}
|
||||||
@ -22,7 +26,45 @@ class VirtualController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create virtual controller
|
// 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 {
|
if instanceID < 0 {
|
||||||
print("Failed to create virtual joystick: \(String(cString: SDL_GetError()))")
|
print("Failed to create virtual joystick: \(String(cString: SDL_GetError()))")
|
||||||
return
|
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) {
|
func updateAxisValue(value: Sint16, forAxis axis: SDL_GameControllerAxis) {
|
||||||
guard controller != nil else { return }
|
guard controller != nil else { return }
|
||||||
let joystick = SDL_JoystickFromInstanceID(instanceID)
|
let joystick = SDL_JoystickFromInstanceID(instanceID)
|
||||||
SDL_JoystickSetVirtualAxis(joystick, axis.rawValue, value)
|
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)
|
// Convert float values (-1.0 to 1.0) to SDL axis values (-32768 to 32767)
|
||||||
let scaledX = Sint16(x * 32767.0)
|
var scaleFactor = 32767.0
|
||||||
let scaledY = Sint16(y * 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 {
|
if stick == .right {
|
||||||
updateAxisValue(value: scaledX, forAxis: SDL_GameControllerAxis(SDL_CONTROLLER_AXIS_RIGHTX.rawValue))
|
updateAxisValue(value: scaledX, forAxis: SDL_GameControllerAxis(SDL_CONTROLLER_AXIS_RIGHTX.rawValue))
|
||||||
|
@ -33,13 +33,16 @@ func waitforcontroller() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let controllerView = ControllerView()
|
let controllerView = ControllerView()
|
||||||
|
|
||||||
let hostingController = UIHostingController(rootView: 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
|
hostingController.view.backgroundColor = .clear
|
||||||
|
containerView.addSubview(hostingController.view)
|
||||||
|
|
||||||
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
|
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
|
||||||
if findGCControllerView(in: window) == nil {
|
if findGCControllerView(in: window) == nil {
|
||||||
window.addSubview(hostingController.view)
|
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
|
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in
|
||||||
controllersList = Ryujinx.shared.getConnectedControllers()
|
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
|
self.onscreencontroller = onscreen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +87,7 @@ struct ControllerView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.padding()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,13 +39,13 @@ public struct Joystick: View {
|
|||||||
locksInPlace: false)
|
locksInPlace: false)
|
||||||
.onChange(of: self.joystickMonitor.xyPoint) { newValue in
|
.onChange(of: self.joystickMonitor.xyPoint) { newValue in
|
||||||
let scaledX = Float(newValue.x)
|
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))")
|
print("Joystick Position: (\(scaledX), \(scaledY))")
|
||||||
|
|
||||||
if iscool != nil {
|
if iscool != nil {
|
||||||
Ryujinx.shared.virtualController.thumbstickMoved(.right, x: scaledX, y: scaledY)
|
Ryujinx.shared.virtualController.thumbstickMoved(.right, x: newValue.x, y: newValue.y)
|
||||||
} else {
|
} 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