controller->mouse demo

This commit is contained in:
TrueZhuanjia
2025-11-19 14:35:56 +08:00
parent 85f4838067
commit d4d5146f66
4 changed files with 267 additions and 6 deletions
+4
View File
@@ -24,6 +24,7 @@
188973D32E386A670025E48C /* widgetTemplatesIPhone.bin in Resources */ = {isa = PBXBuildFile; fileRef = 188973D22E386A670025E48C /* widgetTemplatesIPhone.bin */; };
18951D1D2EBB6D2300A86E12 /* TouchPadGestureHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18951D1C2EBB6D2300A86E12 /* TouchPadGestureHandler.swift */; };
189AD29A2DE7446000E24B88 /* HostCollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 189AD2992DE7446000E24B88 /* HostCollectionViewController.m */; };
18A765BD2ECD51FB004784AA /* ControllerUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A765BC2ECD51FB004784AA /* ControllerUtil.swift */; };
18E4BCEA2E891895000A8657 /* MotionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18E4BCE92E891895000A8657 /* MotionHandler.swift */; };
840751B32BF49C050096B657 /* CustomTapGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 840751B22BF49C050096B657 /* CustomTapGestureRecognizer.m */; };
841BAC922DE35D6B00DA639E /* ThemeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 841BAC912DE35D6B00DA639E /* ThemeManager.m */; };
@@ -180,6 +181,7 @@
18951D1C2EBB6D2300A86E12 /* TouchPadGestureHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchPadGestureHandler.swift; sourceTree = "<group>"; };
189AD2962DE743B700E24B88 /* HostCollectionViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HostCollectionViewController.h; sourceTree = "<group>"; };
189AD2992DE7446000E24B88 /* HostCollectionViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HostCollectionViewController.m; sourceTree = "<group>"; };
18A765BC2ECD51FB004784AA /* ControllerUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControllerUtil.swift; sourceTree = "<group>"; };
18BF141D2E0D7A00009DEE4F /* VoidLink v1.0.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "VoidLink v1.0.xcdatamodel"; sourceTree = "<group>"; };
18E4BCE92E891895000A8657 /* MotionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MotionHandler.swift; sourceTree = "<group>"; };
693B3A9A218638CD00982F7B /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
@@ -713,6 +715,7 @@
988FCD40293B091B003050E2 /* KeyboardInputField.m */,
18416EA32EBB3DCE00AE0124 /* UITouchUtil.swift */,
18951D1C2EBB6D2300A86E12 /* TouchPadGestureHandler.swift */,
18A765BC2ECD51FB004784AA /* ControllerUtil.swift */,
1850C8292E667E4000845308 /* MicHandler.swift */,
18E4BCE92E891895000A8657 /* MotionHandler.swift */,
84E1CDEB2C4F67D300547904 /* CommandManager.swift */,
@@ -1038,6 +1041,7 @@
97E21BC12E21882D000D5DCA /* imgui_widgets.cpp in Sources */,
97E21BC22E21882D000D5DCA /* imgui.cpp in Sources */,
97E21BC32E21882D000D5DCA /* imgui_draw.cpp in Sources */,
18A765BD2ECD51FB004784AA /* ControllerUtil.swift in Sources */,
84E1CDEC2C4F67D300547904 /* CommandManager.swift in Sources */,
847D2FDB2DDA015F00937A62 /* MenuSectionView.m in Sources */,
FB53E1431BE5DC4400CD6ECE /* IdManager.m in Sources */,
+5
View File
@@ -55,6 +55,11 @@ typedef NS_ENUM(NSInteger, HapticEnginePreference) {
RumbleOff
};
typedef NS_ENUM(NSInteger, ControllerToMouseStick) {
LeftStickToMouse,
RightStickToMouse,
};
typedef NS_ENUM(NSInteger, FramePacingMode) {
FramePacingModeOff,
FramePacingModeLegacy,
+88 -6
View File
@@ -11,7 +11,7 @@
#import "ControllerSupport.h"
#import "VoidController.h"
#import "VoidLink-Swift.h"
#import "OnScreenControls.h"
#import "DataManager.h"
@@ -50,6 +50,15 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
float accumulatedScrollX;
float accumulatedScrollY;
ControllerToMouseStick controllerToMouseStick;
bool _stickToMouseEnabled;
float stickToMouseInputX;
float stickToMouseInputY;
float stickToMouseExpo;
float stickToMouseVelocity;
CADisplayLink *_displayLink;
OnScreenControls *_osc;
VoidController *_oscController;
NSMutableSet* _activeGCControllers;
@@ -930,6 +939,32 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
}
}
double rc_expo(double x, double gamma) {
if (x == 0.0) return 0.0;
double ax = fabs(x);
double y = pow(ax, gamma);
return x > 0 ? y : -y;
}
- (void)sendStickToMouseMoveEventWithStickX:(float)stickX stickY:(float)stickY expo:(float)expo {
CGFloat mouseDeltaX = stickToMouseVelocity*rc_expo(stickX, expo);
CGFloat mouseDeltaY = stickToMouseVelocity*rc_expo(stickY, expo);
// NSLog(@"mouseDeltaX %f, mouseDeltaY %f, %f, %ld", mouseDeltaX, mouseDeltaY, CACurrentMediaTime(), (long)_displayLink.preferredFramesPerSecond);
LiSendMouseMoveEvent(mouseDeltaX, -mouseDeltaY);
}
- (void)stopDisplayLink {
[_displayLink invalidate];
_displayLink = nil;
}
- (void)displayLinkCallBack {
if(!_stickToMouseEnabled) return;
[self sendStickToMouseMoveEventWithStickX:stickToMouseInputX stickY:stickToMouseInputY expo:stickToMouseExpo];
}
-(void) registerControllerCallbacks:(GCController*) controller
{
if (controller != NULL) {
@@ -974,12 +1009,41 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
}
}
controller.extendedGamepad.valueChangedHandler = ^(GCExtendedGamepad *gamepad, GCControllerElement *element) {
[ControllerUtil listenWithController:controller swapABXY:self->_swapABXYButtons handler:^(NSDictionary * buttonDict, GCExtendedGamepad * gamepad, GCControllerElement * element) {
VoidController* voidController = [self->_voidControllers objectForKey:[NSNumber numberWithInteger:gamepad.controller.playerIndex]];
short leftStickX, leftStickY;
short rightStickX, rightStickY;
unsigned char leftTrigger, rightTrigger;
for(NSNumber* buttonFlagId in buttonDict){
GCControllerButtonInput * button = (GCControllerButtonInput *)buttonDict[buttonFlagId];
UPDATE_BUTTON_FLAG(voidController, buttonFlagId.intValue, button.pressed);
}
leftStickX = self->controllerToMouseStick != LeftStickToMouse ? gamepad.leftThumbstick.xAxis.value * 0x7FFE : 0;
leftStickY = self->controllerToMouseStick != LeftStickToMouse ? gamepad.leftThumbstick.yAxis.value * 0x7FFE : 0;;
rightStickX = self->controllerToMouseStick != RightStickToMouse ? gamepad.rightThumbstick.xAxis.value * 0x7FFE : 0;
rightStickY = self->controllerToMouseStick != RightStickToMouse ? gamepad.rightThumbstick.yAxis.value * 0x7FFE : 0;
self->stickToMouseInputX = self->controllerToMouseStick == LeftStickToMouse ? gamepad.leftThumbstick.xAxis.value : gamepad.rightThumbstick.xAxis.value;
self->stickToMouseInputY = self->controllerToMouseStick == LeftStickToMouse ? gamepad.leftThumbstick.yAxis.value : gamepad.rightThumbstick.yAxis.value;
leftTrigger = gamepad.leftTrigger.value * 0xFF;
rightTrigger = gamepad.rightTrigger.value * 0xFF;
[self updateLeftStick:voidController x:leftStickX y:leftStickY];
[self updateRightStick:voidController x:rightStickX y:rightStickY];
[self updateTriggers:voidController left:leftTrigger right:rightTrigger];
[self updateFinished:voidController];
}];
/*
controller.extendedGamepad.valueChangedHandler = ^(GCExtendedGamepad *gamepad, GCControllerElement *element) {
VoidController* voidController = [self->_voidControllers objectForKey:[NSNumber numberWithInteger:gamepad.controller.playerIndex]];
short leftStickX, leftStickY;
short rightStickX, rightStickY;
unsigned char leftTrigger, rightTrigger;
if (self->_swapABXYButtons) {
UPDATE_BUTTON_FLAG(voidController, B_FLAG, gamepad.buttonA.pressed);
UPDATE_BUTTON_FLAG(voidController, A_FLAG, gamepad.buttonB.pressed);
@@ -1062,12 +1126,15 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
index:1];
}
}
leftStickX = self->controllerToMouseStick != LeftStickToMouse ? gamepad.leftThumbstick.xAxis.value * 0x7FFE : 0;
leftStickY = self->controllerToMouseStick != LeftStickToMouse ? gamepad.leftThumbstick.yAxis.value * 0x7FFE : 0;;
leftStickX = gamepad.leftThumbstick.xAxis.value * 0x7FFE;
leftStickY = gamepad.leftThumbstick.yAxis.value * 0x7FFE;
rightStickX = self->controllerToMouseStick != RightStickToMouse ? gamepad.rightThumbstick.xAxis.value * 0x7FFE : 0;
rightStickY = self->controllerToMouseStick != RightStickToMouse ? gamepad.rightThumbstick.yAxis.value * 0x7FFE : 0;
rightStickX = gamepad.rightThumbstick.xAxis.value * 0x7FFE;
rightStickY = gamepad.rightThumbstick.yAxis.value * 0x7FFE;
self->stickToMouseInputX = self->controllerToMouseStick == LeftStickToMouse ? gamepad.leftThumbstick.xAxis.value : gamepad.rightThumbstick.xAxis.value;
self->stickToMouseInputY = self->controllerToMouseStick == LeftStickToMouse ? gamepad.leftThumbstick.yAxis.value : gamepad.rightThumbstick.yAxis.value;
leftTrigger = gamepad.leftTrigger.value * 0xFF;
rightTrigger = gamepad.rightTrigger.value * 0xFF;
@@ -1077,6 +1144,7 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
[self updateTriggers:voidController left:leftTrigger right:rightTrigger];
[self updateFinished:voidController];
};
*/
}
} else {
Log(LOG_W, @"Tried to register controller callbacks on NULL controller");
@@ -1416,6 +1484,18 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
_oscEnabled = _oscEnabled || (OnScreenControlsLevel)[tempSettings.onscreenControls integerValue] != OnScreenControlsLevelOff || streamConfig.gyroMode != GyroModeOff;
_gyroSensitivity = tempSettings.gyroSensitivity.floatValue;
_stickToMouseEnabled = true;
controllerToMouseStick = LeftStickToMouse;
stickToMouseExpo = 1.1;
stickToMouseVelocity = 21*60/tempSettings.framerate.intValue;
stickToMouseInputX = 0;
stickToMouseInputY = 0;
[self stopDisplayLink];
if(_stickToMouseEnabled){
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkCallBack)];
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
}
- (void)resetGyroInputForController:(VoidController* )voidController{
@@ -1746,6 +1826,8 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
[self unregisterMouseCallbacks:mouse];
}
}
[self stopDisplayLink];
}
@end
+170
View File
@@ -0,0 +1,170 @@
//
// ControllerUtil.swift
// VoidLink
//
// Created by True砖家 on 2025/11/19.
// Copyright © 2025 True砖家 on Bilibili. All rights reserved.
//
import GameController
@objc enum ControllerButton: Int {
// Face buttons
case a = 0x1000
case b = 0x2000
case x = 0x4000
case y = 0x8000
// DPad
case dpadUp = 0x0001
case dpadDown = 0x0002
case dpadLeft = 0x0004
case dpadRight = 0x0008
// Shoulder
case leftShoulder = 0x0100
case rightShoulder = 0x0200
// Stick click
case leftStickButton = 0x0040
case rightStickButton = 0x0080
// Menu/Back/Special
case menu = 0x0010 // PLAY_FLAG
case back = 0x0020 // BACK_FLAG
case special = 0x0400
// Extended buttons (Sunshine only)
case paddle1 = 0x010000
case paddle2 = 0x020000
case paddle3 = 0x040000
case paddle4 = 0x080000
case touchpad = 0x100000
case misc = 0x200000
case leftTrigger, rightTrigger
}
@objc class ControllerUtil: NSObject {
@objc static func listen(
controller: GCController,
swapABXY: Bool,
handler: @escaping (_ buttonDict: NSDictionary,
_ gamepad: GCExtendedGamepad,
_ element: GCControllerElement) -> Void
) {
guard let gamepad = controller.extendedGamepad else { return }
// 内部生成 map
var tempMap: [NSNumber: GCControllerButtonInput] = [:]
let swiftMap = buildMapping(for: controller, swapABXY: swapABXY)
for (button, input) in swiftMap {
tempMap[NSNumber(value: button.rawValue)] = input
}
let buttonDict = tempMap as NSDictionary
// 单一 gamepad.valueChangedHandler
gamepad.valueChangedHandler = { gamepad, element in
handler(buttonDict, gamepad, element)
}
}
// MARK: - 构建按钮映射
private static func buildMapping(for controller: GCController, swapABXY:Bool)
-> [ControllerButton : GCControllerButtonInput]
{
var result: [ControllerButton : GCControllerButtonInput] = [:]
if let pad = controller.extendedGamepad {
// Face buttons
if swapABXY {
result[.a] = pad.buttonB
result[.b] = pad.buttonA
result[.x] = pad.buttonY
result[.y] = pad.buttonX
}
else{
result[.a] = pad.buttonA
result[.b] = pad.buttonB
result[.x] = pad.buttonX
result[.y] = pad.buttonY
}
// Shoulders & triggers
result[.leftShoulder] = pad.leftShoulder
result[.rightShoulder] = pad.rightShoulder
result[.leftTrigger] = pad.leftTrigger
result[.rightTrigger] = pad.rightTrigger
// Stick buttons
if #available(iOS 12.1, *) {
result[.leftStickButton] = pad.leftThumbstickButton
result[.rightStickButton] = pad.rightThumbstickButton
}
// DPad
result[.dpadUp] = pad.dpad.up
result[.dpadDown] = pad.dpad.down
result[.dpadLeft] = pad.dpad.left
result[.dpadRight] = pad.dpad.right
// Menu / Options / Share
if #available(iOS 13.0, *) {
result[.menu] = pad.buttonMenu
if let options = pad.buttonOptions { result[.back] = options }
}
if #available(iOS 14.0, tvOS 14.0, *) {
if let home = pad.buttonHome {
result[.special] = home
}
if let controller = pad.controller {
let profile = controller.physicalInputProfile
if let paddle1 = profile.buttons[GCInputXboxPaddleOne] {
result[.paddle1] = paddle1
}
if let paddle2 = profile.buttons[GCInputXboxPaddleTwo] {
result[.paddle2] = paddle2
}
if let paddle3 = profile.buttons[GCInputXboxPaddleThree] {
result[.paddle3] = paddle3
}
if let paddle4 = profile.buttons[GCInputXboxPaddleFour] {
result[.paddle4] = paddle4
}
if let touchpadBtn = profile.buttons[GCInputDualShockTouchpadButton] {
result[.touchpad] = touchpadBtn
}
if #available(iOS 15.0, tvOS 15.0, *) {
if let share = profile.buttons[GCInputButtonShare] {
result[.misc] = share
}
}
}
}
}
/*
if let micro = controller.microGamepad {
if #available(iOS 13.0, *) {
result[.menu] = micro.buttonMenu
result[.a] = micro.buttonA
result[.x] = micro.buttonX
result[.dpadUp] = micro.dpad.up
result[.dpadDown] = micro.dpad.down
result[.dpadLeft] = micro.dpad.left
result[.dpadRight] = micro.dpad.right
result[.menu] = micro.buttonMenu
}
}*/
return result
}
}