mirror of https://github.com/SourMesen/Mesen2
170 lines
6.0 KiB
Plaintext
170 lines
6.0 KiB
Plaintext
#import <Foundation/Foundation.h>
|
|
#import <Cocoa/Cocoa.h>
|
|
#import <GameController/GameController.h>
|
|
#import <CoreHaptics/CoreHaptics.h>
|
|
|
|
#include <iostream>
|
|
#include <stdint.h>
|
|
#include <optional>
|
|
|
|
#include "MacOSGameController.h"
|
|
//The MacOS SDK defines a global function 'Debugger', colliding with Mesen's Debugger class
|
|
//Redefine it temporarily so the headers don't cause compilation errors due to this
|
|
#define Debugger MesenDebugger
|
|
#include "Shared/MessageManager.h"
|
|
#include "Shared/Emulator.h"
|
|
#include "Shared/EmuSettings.h"
|
|
#undef Debugger
|
|
|
|
MacOSGameController::MacOSGameController(Emulator* emu, GCController* controller)
|
|
{
|
|
_emu = emu;
|
|
_controller = [controller retain];
|
|
_input = [[_controller extendedGamepad] retain];
|
|
|
|
[_input setValueChangedHandler:^ void (GCExtendedGamepad* input, GCControllerElement* element) {
|
|
if([input buttonA] == element) _buttonState[0] = [((GCControllerButtonInput*) element) isPressed];
|
|
if([input buttonB] == element) _buttonState[1] = [((GCControllerButtonInput*) element) isPressed];
|
|
if([input buttonX] == element) _buttonState[2] = [((GCControllerButtonInput*) element) isPressed];
|
|
if([input buttonY] == element) _buttonState[3] = [((GCControllerButtonInput*) element) isPressed];
|
|
if([input leftShoulder] == element) _buttonState[4] = [((GCControllerButtonInput*) element) isPressed];
|
|
if([input rightShoulder] == element) _buttonState[5] = [((GCControllerButtonInput*) element) isPressed];
|
|
if([input buttonMenu] == element) _buttonState[6] = [((GCControllerButtonInput*) element) isPressed];
|
|
if([input buttonOptions] == element) _buttonState[7] = [((GCControllerButtonInput*) element) isPressed];
|
|
if([input dpad] == element) HandleDpad((GCControllerDirectionPad*) element);
|
|
if([input leftTrigger] == element) _buttonState[12] = [((GCControllerButtonInput*) element) isPressed];
|
|
if([input rightTrigger] == element) _buttonState[13] = [((GCControllerButtonInput*) element) isPressed];
|
|
if([input leftThumbstickButton] == element) _buttonState[14] = [((GCControllerButtonInput*) element) isPressed];
|
|
if([input rightThumbstickButton] == element) _buttonState[15] = [((GCControllerButtonInput*) element) isPressed];
|
|
if([input leftThumbstick] == element) HandleThumbstick((GCControllerDirectionPad*) element, 0);
|
|
if([input rightThumbstick] == element) HandleThumbstick((GCControllerDirectionPad*) element, 1);
|
|
}];
|
|
|
|
_haptics = nil;
|
|
_player = nil;
|
|
if([_controller haptics] != nil) {
|
|
_haptics = [[_controller haptics] createEngineWithLocality:GCHapticsLocalityDefault];
|
|
NSError* error = nil;
|
|
[_haptics startAndReturnError:&error];
|
|
if(error) {
|
|
MessageManager::Log("[Input Device] Failed to initialize force feedback");
|
|
_haptics = nil;
|
|
} else {
|
|
[_haptics retain];
|
|
}
|
|
}
|
|
}
|
|
|
|
MacOSGameController::~MacOSGameController()
|
|
{
|
|
if(_haptics) {
|
|
if(_player) {
|
|
NSError* error = nil;
|
|
[_player stopAtTime:0.0 error:&error];
|
|
[_player release];
|
|
}
|
|
[_haptics stopWithCompletionHandler:^ void (NSError* error) {}];
|
|
[_haptics release];
|
|
}
|
|
[_input setValueChangedHandler:nil];
|
|
[_input release];
|
|
[_controller release];
|
|
}
|
|
|
|
void MacOSGameController::HandleDpad(GCControllerDirectionPad* dpad)
|
|
{
|
|
_buttonState[8] = [[dpad up] isPressed];
|
|
_buttonState[9] = [[dpad down] isPressed];
|
|
_buttonState[10] = [[dpad left] isPressed];
|
|
_buttonState[11] = [[dpad right] isPressed];
|
|
}
|
|
|
|
void MacOSGameController::HandleThumbstick(GCControllerDirectionPad* stick, int stickNumber)
|
|
{
|
|
double deadZoneRatio = _emu->GetSettings()->GetControllerDeadzoneRatio() * 0.4;
|
|
|
|
float xAxis = [[stick xAxis] value];
|
|
float yAxis = [[stick yAxis] value];
|
|
_buttonState[(stickNumber * 4) + 16] = xAxis > deadZoneRatio;
|
|
_buttonState[(stickNumber * 4) + 17] = xAxis < -deadZoneRatio;
|
|
_buttonState[(stickNumber * 4) + 18] = yAxis > deadZoneRatio;
|
|
_buttonState[(stickNumber * 4) + 19] = yAxis < -deadZoneRatio;
|
|
_axisState[(stickNumber * 2) + 0] = INT16_MAX * xAxis;
|
|
_axisState[(stickNumber * 2) + 1] = INT16_MAX * yAxis;
|
|
}
|
|
|
|
bool MacOSGameController::IsGameController(GCController* controller)
|
|
{
|
|
return _controller == controller;
|
|
}
|
|
|
|
bool MacOSGameController::IsButtonPressed(int buttonNumber)
|
|
{
|
|
if(buttonNumber < 0 || buttonNumber >= 24) {
|
|
return false;
|
|
}
|
|
return _buttonState[buttonNumber];
|
|
}
|
|
|
|
std::optional<int16_t> MacOSGameController::GetAxisPosition(int axis)
|
|
{
|
|
axis -= 24;
|
|
if(axis < 0 || axis >= 4) {
|
|
return std::nullopt;
|
|
}
|
|
return _axisState[axis];
|
|
}
|
|
|
|
void MacOSGameController::SetForceFeedback(uint16_t magnitudeRight, uint16_t magnitudeLeft)
|
|
{
|
|
NSError* error = nil;
|
|
|
|
if(_haptics == nil) {
|
|
return;
|
|
}
|
|
|
|
if(_player != nil) {
|
|
[_player stopAtTime:0.0 error:&error];
|
|
if(error) {
|
|
MessageManager::Log("[Input Device] Failed to stop force feedback effect");
|
|
return;
|
|
}
|
|
[_player release];
|
|
_player = nil;
|
|
}
|
|
|
|
//If magnitude is zero, only stop current effect
|
|
if(magnitudeRight == 0 && magnitudeLeft == 0) {
|
|
return;
|
|
}
|
|
|
|
double strength = (magnitudeRight < magnitudeLeft ? magnitudeLeft : magnitudeRight) / (double)UINT16_MAX;
|
|
CHHapticEventParameter* intensityPar = [[CHHapticEventParameter alloc] initWithParameterID:CHHapticEventParameterIDHapticIntensity value:strength];
|
|
CHHapticEventParameter* sharpnessPar = [[CHHapticEventParameter alloc] initWithParameterID:CHHapticEventParameterIDHapticSharpness value:0.6];
|
|
CHHapticEvent* event = [[CHHapticEvent alloc] initWithEventType:CHHapticEventTypeHapticContinuous parameters:@[intensityPar, sharpnessPar] relativeTime:0.0 duration:GCHapticDurationInfinite];
|
|
|
|
CHHapticPattern* pattern = [[CHHapticPattern alloc] initWithEvents:@[event] parameters:@[] error:&error];
|
|
[intensityPar release];
|
|
[sharpnessPar release];
|
|
[event release];
|
|
if(error) {
|
|
MessageManager::Log("[Input Device] Failed to create force feedback pattern");
|
|
[pattern release];
|
|
return;
|
|
}
|
|
|
|
_player = [_haptics createPlayerWithPattern:pattern error:&error];
|
|
[pattern release];
|
|
if(error) {
|
|
MessageManager::Log("[Input Device] Failed to create force feedback effect");
|
|
_player = nil;
|
|
return;
|
|
}
|
|
[_player retain];
|
|
|
|
[_player startAtTime:0.0 error:&error];
|
|
if(error) {
|
|
MessageManager::Log("[Input Device] Failed to start force feedback effect");
|
|
}
|
|
}
|