mirror of
https://github.com/utmapp/UTM.git
synced 2026-10-12 02:24:01 +00:00
Only the Menu button could be mapped to the guest. Options and Home were left out because the system uses them for its own gestures (screenshots, recording, opening the game overlay), so a press never did anything useful for the guest. Add both to the gamepad settings with the same choices as the other buttons. They default to Disabled, which leaves the system gestures alone. Once a button is mapped we ask the system to give up its gesture through preferredSystemGestureState, which the system honours according to the user's Home button setting. Not tested with a physical controller. Assisted-by: Claude:claude-fable-5-1
176 lines
8.0 KiB
Objective-C
176 lines
8.0 KiB
Objective-C
//
|
|
// Copyright © 2020 osy. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
#import "VMCursor.h"
|
|
#import "VMScroll.h"
|
|
#import "VMDisplayMetalViewController+Private.h"
|
|
#import "VMDisplayMetalViewController+Gamepad.h"
|
|
#import "VMDisplayMetalViewController+Touch.h"
|
|
#import "CSDisplay.h"
|
|
#import "UTMLogging.h"
|
|
#import "UTM-Swift.h"
|
|
|
|
const CGFloat kThumbstickSpeedMultiplier = 1000; // in points per second
|
|
|
|
@implementation VMDisplayMetalViewController(Gamepad)
|
|
|
|
- (void)initGamepad {
|
|
// notifications for controller (dis)connect
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(controllerWasConnected:) name:GCControllerDidConnectNotification object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(controllerWasDisconnected:) name:GCControllerDidDisconnectNotification object:nil];
|
|
for (GCController *controller in [GCController controllers]) {
|
|
[self setupController:controller];
|
|
}
|
|
}
|
|
|
|
#pragma mark - Gamepad connection
|
|
|
|
- (void)controllerWasConnected:(NSNotification *)notification {
|
|
// a controller was connected
|
|
GCController *controller = (GCController *)notification.object;
|
|
UTMLog(@"Controller connected: %@", controller.vendorName);
|
|
[self setupController:controller];
|
|
}
|
|
|
|
- (void)controllerWasDisconnected:(NSNotification *)notification {
|
|
// a controller was disconnected
|
|
GCController *controller = (GCController *)notification.object;
|
|
UTMLog(@"Controller disconnected: %@", controller.vendorName);
|
|
}
|
|
|
|
- (void)setupController:(GCController *)controller {
|
|
GCExtendedGamepad *gamepad = controller.extendedGamepad;
|
|
__weak typeof(self) _self = self;
|
|
self.controller = controller;
|
|
UTMLog(@"active controller switched to: %@", controller.vendorName);
|
|
|
|
gamepad.leftTrigger.pressedChangedHandler = ^(GCControllerButtonInput * _Nonnull button, float value, BOOL pressed) {
|
|
[_self gamepadButton:@"GCButtonTriggerLeft" pressed:pressed];
|
|
};
|
|
|
|
gamepad.rightTrigger.pressedChangedHandler = ^(GCControllerButtonInput * _Nonnull button, float value, BOOL pressed) {
|
|
[_self gamepadButton:@"GCButtonTriggerRight" pressed:pressed];
|
|
};
|
|
|
|
gamepad.leftShoulder.pressedChangedHandler = ^(GCControllerButtonInput * _Nonnull button, float value, BOOL pressed) {
|
|
[_self gamepadButton:@"GCButtonShoulderLeft" pressed:pressed];
|
|
};
|
|
|
|
gamepad.rightShoulder.pressedChangedHandler = ^(GCControllerButtonInput * _Nonnull button, float value, BOOL pressed) {
|
|
[_self gamepadButton:@"GCButtonShoulderRight" pressed:pressed];
|
|
};
|
|
|
|
gamepad.buttonA.pressedChangedHandler = ^(GCControllerButtonInput * _Nonnull button, float value, BOOL pressed) {
|
|
[_self gamepadButton:@"GCButtonA" pressed:pressed];
|
|
};
|
|
|
|
gamepad.buttonB.pressedChangedHandler = ^(GCControllerButtonInput * _Nonnull button, float value, BOOL pressed) {
|
|
[_self gamepadButton:@"GCButtonB" pressed:pressed];
|
|
};
|
|
|
|
gamepad.buttonX.pressedChangedHandler = ^(GCControllerButtonInput * _Nonnull button, float value, BOOL pressed) {
|
|
[_self gamepadButton:@"GCButtonX" pressed:pressed];
|
|
};
|
|
|
|
gamepad.buttonY.pressedChangedHandler = ^(GCControllerButtonInput * _Nonnull button, float value, BOOL pressed) {
|
|
[_self gamepadButton:@"GCButtonY" pressed:pressed];
|
|
};
|
|
|
|
gamepad.dpad.up.pressedChangedHandler = ^(GCControllerButtonInput * _Nonnull button, float value, BOOL pressed) {
|
|
[_self gamepadButton:@"GCButtonDpadUp" pressed:pressed];
|
|
};
|
|
|
|
gamepad.dpad.left.pressedChangedHandler = ^(GCControllerButtonInput * _Nonnull button, float value, BOOL pressed) {
|
|
[_self gamepadButton:@"GCButtonDpadLeft" pressed:pressed];
|
|
};
|
|
|
|
gamepad.dpad.down.pressedChangedHandler = ^(GCControllerButtonInput * _Nonnull button, float value, BOOL pressed) {
|
|
[_self gamepadButton:@"GCButtonDpadDown" pressed:pressed];
|
|
};
|
|
|
|
gamepad.dpad.right.pressedChangedHandler = ^(GCControllerButtonInput * _Nonnull button, float value, BOOL pressed) {
|
|
[_self gamepadButton:@"GCButtonDpadRight" pressed:pressed];
|
|
};
|
|
|
|
gamepad.leftThumbstick.valueChangedHandler = ^(GCControllerDirectionPad * _Nonnull dpad, float xValue, float yValue) {
|
|
VMDisplayMetalViewController *s = _self;
|
|
CGPoint velocity = CGPointMake(xValue * kThumbstickSpeedMultiplier, -yValue * kThumbstickSpeedMultiplier);
|
|
[s.scroll startMovement:CGPointZero];
|
|
[s.scroll updateMovement:CGPointMake(xValue, yValue)];
|
|
[s.scroll endMovementWithVelocity:velocity resistance:0];
|
|
};
|
|
|
|
gamepad.rightThumbstick.valueChangedHandler = ^(GCControllerDirectionPad * _Nonnull dpad, float xValue, float yValue) {
|
|
NSInteger speed = [_self integerForSetting:@"GCThumbstickRightSpeed"];
|
|
VMDisplayMetalViewController *s = _self;
|
|
CGPoint center = s.cursor.center;
|
|
CGPoint start = CGPointMake(xValue * speed, -yValue * speed);
|
|
CGPoint velocity = CGPointMake(xValue * kThumbstickSpeedMultiplier, -yValue * kThumbstickSpeedMultiplier);
|
|
[s.cursor startMovement:center];
|
|
[s.cursor updateMovement:CGPointMake(center.x + start.x, center.y + start.y)];
|
|
[s.cursor endMovementWithVelocity:velocity resistance:0];
|
|
};
|
|
|
|
gamepad.buttonMenu.pressedChangedHandler = ^(GCControllerButtonInput * _Nonnull button, float value, BOOL pressed) {
|
|
[_self gamepadButton:@"GCButtonMenu" pressed:pressed];
|
|
};
|
|
|
|
gamepad.buttonOptions.pressedChangedHandler = ^(GCControllerButtonInput * _Nonnull button, float value, BOOL pressed) {
|
|
[_self gamepadButton:@"GCButtonOptions" pressed:pressed];
|
|
};
|
|
|
|
gamepad.buttonHome.pressedChangedHandler = ^(GCControllerButtonInput * _Nonnull button, float value, BOOL pressed) {
|
|
[_self gamepadButton:@"GCButtonHome" pressed:pressed];
|
|
};
|
|
|
|
[self setSystemGestureButtonsClaimed:YES];
|
|
}
|
|
|
|
- (void)setSystemGestureButtonsClaimed:(BOOL)isClaimed {
|
|
// the system uses these buttons for its own gestures, so we only ask for them while they are mapped to the guest
|
|
GCExtendedGamepad *gamepad = self.controller.extendedGamepad;
|
|
BOOL isOptionsClaimed = isClaimed && [self integerForSetting:@"GCButtonOptions"] != 0;
|
|
BOOL isHomeClaimed = isClaimed && [self integerForSetting:@"GCButtonHome"] != 0;
|
|
gamepad.buttonOptions.preferredSystemGestureState = isOptionsClaimed ? GCSystemGestureStateDisabled : GCSystemGestureStateEnabled;
|
|
gamepad.buttonHome.preferredSystemGestureState = isHomeClaimed ? GCSystemGestureStateDisabled : GCSystemGestureStateEnabled;
|
|
}
|
|
|
|
- (void)gamepadButton:(NSString *)identifier pressed:(BOOL)isPressed {
|
|
NSInteger value = [self integerForSetting:identifier];
|
|
UTMLog(@"GC button %@ (%ld) pressed:%d", identifier, value, isPressed);
|
|
switch (value) {
|
|
case 0:
|
|
break;
|
|
case -1:
|
|
[self.vmInput sendMouseButton:kCSInputButtonLeft mask:self.mouseButtonDown pressed:isPressed];
|
|
self.mouseLeftDown = isPressed;
|
|
break;
|
|
case -3:
|
|
[self.vmInput sendMouseButton:kCSInputButtonRight mask:self.mouseButtonDown pressed:isPressed];
|
|
self.mouseRightDown = isPressed;
|
|
break;
|
|
case -2:
|
|
[self.vmInput sendMouseButton:kCSInputButtonMiddle mask:self.mouseButtonDown pressed:isPressed];
|
|
self.mouseMiddleDown = isPressed;
|
|
break;
|
|
default:
|
|
[self sendExtendedKey:isPressed ? kCSInputKeyPress : kCSInputKeyRelease code:(int)value];
|
|
break;
|
|
}
|
|
}
|
|
|
|
@end
|