controller->mouse demo
This commit is contained in:
@@ -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 */,
|
||||
|
||||
@@ -55,6 +55,11 @@ typedef NS_ENUM(NSInteger, HapticEnginePreference) {
|
||||
RumbleOff
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, ControllerToMouseStick) {
|
||||
LeftStickToMouse,
|
||||
RightStickToMouse,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, FramePacingMode) {
|
||||
FramePacingModeOff,
|
||||
FramePacingModeLegacy,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user