iOS: multiple controller/rumble support. GL: fix black screen after RTT
iOS: * multiple controllers support. Rumble support. * fix audio recording * declare CHD/GDI/CUE/CDI content types * start game when opening file URL * Use safe area for UI and virtual gamepad * Better icons
|
@ -1131,7 +1131,7 @@ if(NOT LIBRETRO)
|
|||
shell/apple/emulator-ios/emulator/AppDelegate.h
|
||||
shell/apple/emulator-ios/emulator/AppDelegate.mm
|
||||
shell/apple/emulator-ios/emulator/ios_main.mm
|
||||
shell/apple/emulator-ios/emulator/ios_mouse.h
|
||||
shell/apple/emulator-ios/emulator/ios_gamepad.h
|
||||
shell/apple/emulator-ios/emulator/FlycastViewController.h
|
||||
shell/apple/emulator-ios/emulator/FlycastViewController.mm
|
||||
shell/apple/emulator-ios/emulator/PadViewController.h
|
||||
|
@ -1184,13 +1184,15 @@ if(NOT LIBRETRO)
|
|||
find_library(GLKIT GLKit)
|
||||
find_library(GAMECONTROLLER GameController)
|
||||
find_library(AUDIOTOOLBOX AudioToolbox)
|
||||
find_library(AVFOUNDATION AVFoundation)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
${UIKIT}
|
||||
${FOUNDATION}
|
||||
${OPENGLES}
|
||||
${GLKIT}
|
||||
${GAMECONTROLLER}
|
||||
${AUDIOTOOLBOX})
|
||||
${AUDIOTOOLBOX}
|
||||
${AVFOUNDATION})
|
||||
|
||||
add_custom_target(Flycast.IPA ALL
|
||||
DEPENDS ${PROJECT_NAME}
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
const std::string& api_name() { return _api_name; }
|
||||
const std::string& name() { return _name; }
|
||||
int maple_port() const { return _maple_port; }
|
||||
void set_maple_port(int port) { _maple_port = port; }
|
||||
virtual void set_maple_port(int port) { _maple_port = port; }
|
||||
const std::string& unique_id() { return _unique_id; }
|
||||
virtual bool gamepad_btn_input(u32 code, bool pressed);
|
||||
bool gamepad_axis_input(u32 code, int value);
|
||||
|
|
|
@ -37,19 +37,19 @@ bool MiniUPnP::Init()
|
|||
#endif
|
||||
if (devlist == nullptr)
|
||||
{
|
||||
INFO_LOG(MODEM, "UPnP discover failed: error %d", error);
|
||||
WARN_LOG(MODEM, "UPnP discover failed: error %d", error);
|
||||
return false;
|
||||
}
|
||||
error = UPNP_GetValidIGD(devlist, &urls, &data, lanAddress, sizeof(lanAddress));
|
||||
freeUPNPDevlist(devlist);
|
||||
if (error != 1)
|
||||
{
|
||||
INFO_LOG(MODEM, "Internet Gateway not found: error %d", error);
|
||||
WARN_LOG(MODEM, "Internet Gateway not found: error %d", error);
|
||||
return false;
|
||||
}
|
||||
wanAddress[0] = 0;
|
||||
if (UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, wanAddress) != 0)
|
||||
INFO_LOG(MODEM, "Cannot determine external IP address");
|
||||
WARN_LOG(MODEM, "Cannot determine external IP address");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ bool MiniUPnP::AddPortMapping(int port, bool tcp)
|
|||
"86400"); // port map lease duration (in seconds) or zero for "as long as possible"
|
||||
if (error != 0)
|
||||
{
|
||||
INFO_LOG(MODEM, "Port %d redirection failed: error %d", port, error);
|
||||
WARN_LOG(MODEM, "Port %d redirection failed: error %d", port, error);
|
||||
return false;
|
||||
}
|
||||
mappedPorts.emplace_back(portStr, tcp);
|
||||
|
|
|
@ -80,7 +80,6 @@ static void coreaudio_init()
|
|||
#else
|
||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
#endif
|
||||
//desc.componentSubType = kAudioUnitSubType_GenericOutput;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
|
@ -194,7 +193,7 @@ static void coreaudio_term_record()
|
|||
|
||||
static bool coreaudio_init_record(u32 sampling_freq)
|
||||
{
|
||||
AudioStreamBasicDescription desc;
|
||||
AudioStreamBasicDescription desc{};
|
||||
desc.mFormatID = kAudioFormatLinearPCM;
|
||||
desc.mSampleRate = (double)sampling_freq;
|
||||
desc.mChannelsPerFrame = 1;
|
||||
|
@ -213,7 +212,7 @@ static bool coreaudio_init_record(u32 sampling_freq)
|
|||
&recordQueue);
|
||||
if (err != noErr)
|
||||
{
|
||||
INFO_LOG(AUDIO, "AudioQueueNewInput failed: %d", err);
|
||||
ERROR_LOG(AUDIO, "AudioQueueNewInput failed: %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -230,11 +229,11 @@ static bool coreaudio_init_record(u32 sampling_freq)
|
|||
err = AudioQueueStart(recordQueue, nullptr);
|
||||
if (err != noErr)
|
||||
{
|
||||
INFO_LOG(AUDIO, "AudioQueue init failed: %d", err);
|
||||
ERROR_LOG(AUDIO, "AudioQueue init failed: %d", err);
|
||||
coreaudio_term_record();
|
||||
return false;
|
||||
}
|
||||
DEBUG_LOG(AUDIO, "AudioQueue initialized - sample rate %f", desc.mSampleRate);
|
||||
INFO_LOG(AUDIO, "AudioQueue initialized - sample rate %f", desc.mSampleRate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -671,7 +671,7 @@ static void resize(int w, int h)
|
|||
}
|
||||
gl4CreateTextures(max_image_width, max_image_height);
|
||||
reshapeABuffer(max_image_width, max_image_height);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.origFbo);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -684,6 +684,12 @@ static bool RenderFrame(int width, int height)
|
|||
const glm::mat4& scissor_mat = matrices.GetScissorMatrix();
|
||||
ViewportMatrix = matrices.GetViewportMatrix();
|
||||
|
||||
#ifdef LIBRETRO
|
||||
gl.ofbo.origFbo = glsm_get_current_framebuffer();
|
||||
#else
|
||||
gl.ofbo.origFbo = 0;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&gl.ofbo.origFbo);
|
||||
#endif
|
||||
if (!is_rtt)
|
||||
gcflip = 0;
|
||||
else
|
||||
|
|
|
@ -1197,6 +1197,12 @@ bool RenderFrame(int width, int height)
|
|||
}
|
||||
|
||||
//setup render target first
|
||||
#ifdef LIBRETRO
|
||||
gl.ofbo.origFbo = glsm_get_current_framebuffer();
|
||||
#else
|
||||
gl.ofbo.origFbo = 0;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&gl.ofbo.origFbo);
|
||||
#endif
|
||||
if (is_rtt)
|
||||
{
|
||||
if (BindRTT() == 0)
|
||||
|
|
|
@ -368,9 +368,7 @@ void ReadRTTBuffer()
|
|||
}
|
||||
gl.rtt.texAddress = ~0;
|
||||
}
|
||||
#ifdef LIBRETRO
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, hw_render.get_current_framebuffer());
|
||||
#endif
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.origFbo);
|
||||
}
|
||||
|
||||
static void readAsyncPixelBuffer(u32 addr)
|
||||
|
@ -484,8 +482,6 @@ GLuint init_output_framebuffer(int width, int height)
|
|||
gl.ofbo.width = width;
|
||||
gl.ofbo.height = height;
|
||||
}
|
||||
gl.ofbo.origFbo = 0;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&gl.ofbo.origFbo);
|
||||
|
||||
if (gl.ofbo.fbo == 0)
|
||||
{
|
||||
|
|
|
@ -1,8 +1,24 @@
|
|||
/*
|
||||
Copyright (c) 2014 Lounge Katt. All rights reserved.
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
//
|
||||
// Created by Lounge Katt on 2/6/14.
|
||||
// Copyright (c) 2014 Lounge Katt. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
|
|
@ -1,24 +1,48 @@
|
|||
/*
|
||||
Copyright 2021 flyinghead
|
||||
Copyright (c) 2014 Lounge Katt. All rights reserved.
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
//
|
||||
// Created by Lounge Katt on 2/6/14.
|
||||
// Copyright (c) 2014 Lounge Katt. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
#include "emulator.h"
|
||||
#include "log/LogManager.h"
|
||||
#include "cfg/option.h"
|
||||
#include "rend/gui.h"
|
||||
|
||||
static bool emulatorRunning;
|
||||
|
||||
@implementation AppDelegate
|
||||
@implementation AppDelegate {
|
||||
NSURL *openedURL;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
if ([UIDevice currentDevice].systemVersion.floatValue >= 7.0f) // TODO: consider using the black variant for iOS 5.
|
||||
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
|
||||
else
|
||||
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];
|
||||
// Override point for customization after application launch.
|
||||
// Allow audio playing AND recording
|
||||
AVAudioSession *session = [AVAudioSession sharedInstance];
|
||||
NSError *error = nil;
|
||||
[session setCategory:AVAudioSessionCategoryPlayAndRecord
|
||||
withOptions:AVAudioSessionCategoryOptionMixWithOthers
|
||||
error:&error];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
@ -61,4 +85,21 @@ static bool emulatorRunning;
|
|||
LogManager::Shutdown();
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application openURL:(nonnull NSURL *)url options:(nonnull NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
|
||||
{
|
||||
if (!url.fileURL)
|
||||
return false;
|
||||
if (openedURL != nil)
|
||||
{
|
||||
[openedURL stopAccessingSecurityScopedResource];
|
||||
openedURL = nil;
|
||||
}
|
||||
if ([url startAccessingSecurityScopedResource])
|
||||
openedURL = url;
|
||||
gui_state = GuiState::Closed;
|
||||
gui_start_game(url.fileSystemRepresentation);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,12 +1,24 @@
|
|||
//
|
||||
// Copyright (c) 2015 reicast. All rights reserved.
|
||||
//
|
||||
/*
|
||||
Copyright 2021 flyinghead
|
||||
Copyright (c) 2015 reicast. All rights reserved.
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#import <GLKit/GLKit.h>
|
||||
#include "ios_mouse.h"
|
||||
|
||||
@interface EmulatorView : GLKView
|
||||
|
||||
@property IOSMouse *mouse;
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,19 +1,44 @@
|
|||
//
|
||||
// Copyright (c) 2015 reicast. All rights reserved.
|
||||
//
|
||||
/*
|
||||
Copyright 2021 flyinghead
|
||||
Copyright (c) 2015 reicast. All rights reserved.
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#import "EmulatorView.h"
|
||||
|
||||
#include "types.h"
|
||||
#include "rend/gui.h"
|
||||
#include "ios_gamepad.h"
|
||||
|
||||
@implementation EmulatorView
|
||||
@implementation EmulatorView {
|
||||
std::shared_ptr<IOSMouse> mouse;
|
||||
}
|
||||
|
||||
- (void)didMoveToSuperview
|
||||
{
|
||||
[super didMoveToSuperview];
|
||||
mouse = std::make_shared<IOSMouse>();
|
||||
GamepadDevice::Register(mouse);
|
||||
}
|
||||
|
||||
- (void)touchLocation:(UITouch*)touch;
|
||||
{
|
||||
float scale = self.contentScaleFactor;
|
||||
CGPoint location = [touch locationInView:touch.view];
|
||||
_mouse->setAbsPos(location.x * scale, location.y * scale, self.bounds.size.width * scale, self.bounds.size.height * scale);
|
||||
mouse->setAbsPos(location.x * scale, location.y * scale, self.bounds.size.width * scale, self.bounds.size.height * scale);
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
|
||||
|
@ -21,7 +46,7 @@
|
|||
UITouch *touch = [touches anyObject];
|
||||
[self touchLocation:touch];
|
||||
if (gui_is_open())
|
||||
_mouse->setButton(Mouse::LEFT_BUTTON, true);
|
||||
mouse->setButton(Mouse::LEFT_BUTTON, true);
|
||||
[super touchesBegan:touches withEvent:event];
|
||||
}
|
||||
|
||||
|
@ -30,7 +55,7 @@
|
|||
UITouch *touch = [touches anyObject];
|
||||
[self touchLocation:touch];
|
||||
if (gui_is_open())
|
||||
_mouse->setButton(Mouse::LEFT_BUTTON, false);
|
||||
mouse->setButton(Mouse::LEFT_BUTTON, false);
|
||||
[super touchesEnded:touches withEvent:event];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,25 @@
|
|||
/*
|
||||
Copyright 2021 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <GLKit/GLKit.h>
|
||||
#import "iCade-iOS/iCadeReaderView.h"
|
||||
|
||||
#include "ios_mouse.h"
|
||||
|
||||
@interface FlycastViewController : GLKViewController <iCadeEventDelegate>
|
||||
|
||||
- (void)handleKeyDown:(enum IOSButton)button;
|
||||
- (void)handleKeyUp:(enum IOSButton)button;
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,10 +1,25 @@
|
|||
//
|
||||
// Copyright (c) 2014 Karen Tsai (angelXwind). All rights reserved.
|
||||
//
|
||||
/*
|
||||
Copyright 2021 flyinghead
|
||||
Copyright (c) 2014 Karen Tsai (angelXwind). All rights reserved.
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#import "FlycastViewController.h"
|
||||
#import <GameController/GameController.h>
|
||||
#import <Network/Network.h>
|
||||
//#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||
|
||||
#import <OpenGLES/ES3/gl.h>
|
||||
#import <OpenGLES/ES3/glext.h>
|
||||
|
@ -14,24 +29,23 @@
|
|||
#import "EmulatorView.h"
|
||||
|
||||
#include "types.h"
|
||||
#include "input/gamepad_device.h"
|
||||
#include "wsi/context.h"
|
||||
#include "rend/mainui.h"
|
||||
#include "emulator.h"
|
||||
#include "log/LogManager.h"
|
||||
#include "stdclass.h"
|
||||
#include "cfg/option.h"
|
||||
#include "ios_mouse.h"
|
||||
#include "rend/gui.h"
|
||||
#include "ios_gamepad.h"
|
||||
|
||||
//@import AltKit;
|
||||
#import "AltKit/AltKit-Swift.h"
|
||||
|
||||
std::string iosJitStatus;
|
||||
static bool iosJitAuthorized;
|
||||
static std::shared_ptr<IOSMouse> mouse;
|
||||
static __unsafe_unretained FlycastViewController *flycastViewController;
|
||||
|
||||
std::map<GCController *, std::shared_ptr<IOSGamepad>> IOSGamepad::controllers;
|
||||
|
||||
void common_linux_setup();
|
||||
|
||||
@interface FlycastViewController () <UIDocumentPickerDelegate>
|
||||
|
@ -40,7 +54,6 @@ void common_linux_setup();
|
|||
@property (strong, nonatomic) PadViewController *padController;
|
||||
|
||||
@property (nonatomic) iCadeReaderView* iCadeReader;
|
||||
@property (nonatomic) GCController *gController __attribute__((weak_import));
|
||||
@property (nonatomic, strong) id connectObserver;
|
||||
@property (nonatomic, strong) id disconnectObserver;
|
||||
|
||||
|
@ -113,31 +126,32 @@ extern int screen_dpi;
|
|||
[EAGLContext setCurrentContext:self.context];
|
||||
|
||||
self.connectObserver = [[NSNotificationCenter defaultCenter] addObserverForName:GCControllerDidConnectNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
// GCController *controller = (GCController *)note.object;
|
||||
// IOSGame::addController(controller);
|
||||
if (GCController.controllers.count > 0) {
|
||||
[self toggleHardwareController:YES];
|
||||
}
|
||||
GCController *controller = note.object;
|
||||
IOSGamepad::addController(controller);
|
||||
#if !TARGET_OS_TV
|
||||
if (IOSGamepad::controllerConnected())
|
||||
[self.padController hideController];
|
||||
#endif
|
||||
}];
|
||||
self.disconnectObserver = [[NSNotificationCenter defaultCenter] addObserverForName:GCControllerDidDisconnectNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
// GCController *controller = (GCController *)note.object;
|
||||
// IOSGame::removeController(controller);
|
||||
if (GCController.controllers.count == 0) {
|
||||
[self toggleHardwareController:NO];
|
||||
}
|
||||
GCController *controller = note.object;
|
||||
IOSGamepad::removeController(controller);
|
||||
#if !TARGET_OS_TV
|
||||
if (!IOSGamepad::controllerConnected())
|
||||
[self.padController showController:self.view];
|
||||
#endif
|
||||
}];
|
||||
|
||||
if ([[GCController controllers] count]) {
|
||||
[self toggleHardwareController:YES];
|
||||
}
|
||||
|
||||
for (GCController *controller in [GCController controllers])
|
||||
IOSGamepad::addController(controller);
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
[self addChildViewController:self.padController];
|
||||
self.padController.view.frame = self.view.bounds;
|
||||
self.padController.view.translatesAutoresizingMaskIntoConstraints = YES;
|
||||
self.padController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleWidth;
|
||||
self.padController.handler = self;
|
||||
[self.padController hideController];
|
||||
self.padController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
if (IOSGamepad::controllerConnected())
|
||||
[self.padController hideController];
|
||||
#endif
|
||||
|
||||
self.iCadeReader = [[iCadeReaderView alloc] init];
|
||||
|
@ -158,11 +172,20 @@ extern int screen_dpi;
|
|||
InitRenderApi();
|
||||
mainui_init();
|
||||
mainui_enabled = true;
|
||||
|
||||
emuView.mouse = ::mouse.get();
|
||||
|
||||
[self altKitStart];
|
||||
}
|
||||
|
||||
- (BOOL)prefersStatusBarHidden
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
||||
{
|
||||
return UIStatusBarStyleLightContent;
|
||||
}
|
||||
|
||||
-(UIRectEdge)preferredScreenEdgesDeferringSystemGestures
|
||||
{
|
||||
return UIRectEdgeAll;
|
||||
|
@ -228,6 +251,14 @@ extern int screen_dpi;
|
|||
nw_path_monitor_start(self.monitor);
|
||||
}
|
||||
|
||||
- (void)viewSafeAreaInsetsDidChange
|
||||
{
|
||||
[super viewSafeAreaInsetsDidChange];
|
||||
float scale = self.view.contentScaleFactor;
|
||||
gui_set_insets(self.view.safeAreaInsets.left * scale, self.view.safeAreaInsets.right * scale,
|
||||
self.view.safeAreaInsets.top * scale, self.view.safeAreaInsets.bottom * scale);
|
||||
}
|
||||
|
||||
#pragma mark - GLKView and GLKViewController delegate methods
|
||||
|
||||
- (void)update
|
||||
|
@ -235,323 +266,29 @@ extern int screen_dpi;
|
|||
|
||||
}
|
||||
|
||||
- (void)toggleHardwareController:(BOOL)useHardware
|
||||
{
|
||||
if (useHardware)
|
||||
{
|
||||
[self.padController hideController];
|
||||
|
||||
#if TARGET_OS_TV
|
||||
for (GCController*c in GCController.controllers) {
|
||||
if ((c.gamepad != nil || c.extendedGamepad != nil) && c != _gController) {
|
||||
self.gController = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
self.gController = [GCController controllers].firstObject;
|
||||
#endif
|
||||
// TODO: Add multi player using gController.playerIndex and iterate all controllers
|
||||
|
||||
if (self.gController.extendedGamepad)
|
||||
{
|
||||
[self.gController.extendedGamepad.buttonA setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed)
|
||||
[self handleKeyDown:IOS_BTN_A];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_A];
|
||||
}];
|
||||
[self.gController.extendedGamepad.buttonB setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed)
|
||||
[self handleKeyDown:IOS_BTN_B];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_B];
|
||||
}];
|
||||
[self.gController.extendedGamepad.buttonX setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed)
|
||||
[self handleKeyDown:IOS_BTN_X];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_X];
|
||||
}];
|
||||
[self.gController.extendedGamepad.buttonY setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed)
|
||||
[self handleKeyDown:IOS_BTN_Y];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_Y];
|
||||
}];
|
||||
[self.gController.extendedGamepad.rightTrigger setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
rt[0] = (int)std::roundf(255.f * value);
|
||||
}];
|
||||
[self.gController.extendedGamepad.leftTrigger setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
lt[0] = (int)std::roundf(255.f * value);
|
||||
}];
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
[self.gController.extendedGamepad.buttonOptions setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed)
|
||||
[self handleKeyDown:IOS_BTN_OPTIONS];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_OPTIONS];
|
||||
}];
|
||||
[self.gController.extendedGamepad.leftShoulder setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed)
|
||||
[self handleKeyDown:IOS_BTN_L1];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_L1];
|
||||
}];
|
||||
} else {
|
||||
// Left shoulder for options/menu
|
||||
[self.gController.extendedGamepad.leftShoulder setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed)
|
||||
[self handleKeyDown:IOS_BTN_OPTIONS];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_OPTIONS];
|
||||
}];
|
||||
}
|
||||
if (@available(iOS 13.0, *)) {
|
||||
[self.gController.extendedGamepad.buttonMenu setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed)
|
||||
[self handleKeyDown:IOS_BTN_MENU];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_MENU];
|
||||
}];
|
||||
[self.gController.extendedGamepad.rightShoulder setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed)
|
||||
[self handleKeyDown:IOS_BTN_R1];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_R1];
|
||||
}];
|
||||
} else {
|
||||
// Right shoulder for menu/start
|
||||
[self.gController.extendedGamepad.rightShoulder setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed)
|
||||
[self handleKeyDown:IOS_BTN_MENU];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_MENU];
|
||||
}];
|
||||
}
|
||||
|
||||
[self.gController.extendedGamepad.dpad setValueChangedHandler:^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
|
||||
if (dpad.right.isPressed)
|
||||
[self handleKeyDown:IOS_BTN_RIGHT];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_RIGHT];
|
||||
if (dpad.left.isPressed)
|
||||
[self handleKeyDown:IOS_BTN_LEFT];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_LEFT];
|
||||
if (dpad.up.isPressed)
|
||||
[self handleKeyDown:IOS_BTN_UP];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_UP];
|
||||
if (dpad.down.isPressed)
|
||||
[self handleKeyDown:IOS_BTN_DOWN];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_DOWN];
|
||||
}];
|
||||
[self.gController.extendedGamepad.leftThumbstick.xAxis setValueChangedHandler:^(GCControllerAxisInput *axis, float value) {
|
||||
s8 v = (s8)(value * 127); //-127 ... + 127 range
|
||||
|
||||
joyx[0] = v;
|
||||
}];
|
||||
[self.gController.extendedGamepad.leftThumbstick.yAxis setValueChangedHandler:^(GCControllerAxisInput *axis, float value) {
|
||||
s8 v = (s8)(value * 127 * -1); //-127 ... + 127 range
|
||||
|
||||
joyy[0] = v;
|
||||
}];
|
||||
[self.gController.extendedGamepad.rightThumbstick.xAxis setValueChangedHandler:^(GCControllerAxisInput *axis, float value) {
|
||||
s8 v = (s8)(value * 127); //-127 ... + 127 range
|
||||
|
||||
joyrx[0] = v;
|
||||
}];
|
||||
[self.gController.extendedGamepad.rightThumbstick.yAxis setValueChangedHandler:^(GCControllerAxisInput *axis, float value) {
|
||||
s8 v = (s8)(value * 127 * -1); //-127 ... + 127 range
|
||||
|
||||
joyry[0] = v;
|
||||
}];
|
||||
}
|
||||
else if (self.gController.gamepad) {
|
||||
[self.gController.gamepad.buttonA setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed)
|
||||
[self handleKeyDown:IOS_BTN_A];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_A];
|
||||
}];
|
||||
[self.gController.gamepad.buttonB setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed)
|
||||
[self handleKeyDown:IOS_BTN_B];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_B];
|
||||
}];
|
||||
[self.gController.gamepad.buttonX setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed)
|
||||
[self handleKeyDown:IOS_BTN_X];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_X];
|
||||
}];
|
||||
[self.gController.gamepad.buttonY setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed)
|
||||
[self handleKeyDown:IOS_BTN_Y];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_Y];
|
||||
}];
|
||||
[self.gController.gamepad.dpad setValueChangedHandler:^(GCControllerDirectionPad *dpad, float xValue, float yValue){
|
||||
if (dpad.right.isPressed)
|
||||
[self handleKeyDown:IOS_BTN_RIGHT];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_RIGHT];
|
||||
if (dpad.left.isPressed)
|
||||
[self handleKeyDown:IOS_BTN_LEFT];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_LEFT];
|
||||
if (dpad.up.isPressed)
|
||||
[self handleKeyDown:IOS_BTN_UP];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_UP];
|
||||
if (dpad.down.isPressed)
|
||||
[self handleKeyDown:IOS_BTN_DOWN];
|
||||
else
|
||||
[self handleKeyUp:IOS_BTN_DOWN];
|
||||
}];
|
||||
|
||||
[self.gController.gamepad.rightShoulder setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed) {
|
||||
if (self.gController.gamepad.leftShoulder.pressed)
|
||||
[self handleKeyDown:IOS_BTN_MENU];
|
||||
else
|
||||
[self handleKeyDown:IOS_BTN_R2];
|
||||
}
|
||||
else {
|
||||
[self handleKeyUp:IOS_BTN_R2];
|
||||
[self handleKeyUp:IOS_BTN_MENU];
|
||||
}
|
||||
}];
|
||||
[self.gController.gamepad.leftShoulder setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed) {
|
||||
if (self.gController.gamepad.rightShoulder.pressed)
|
||||
[self handleKeyDown:IOS_BTN_MENU];
|
||||
else
|
||||
[self handleKeyDown:IOS_BTN_L2];
|
||||
}
|
||||
else {
|
||||
[self handleKeyUp:IOS_BTN_L2];
|
||||
[self handleKeyUp:IOS_BTN_MENU];
|
||||
}
|
||||
}];
|
||||
|
||||
//Add controller pause handler here
|
||||
}
|
||||
} else {
|
||||
self.gController = nil;
|
||||
[self.padController showController:self.view];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
|
||||
{
|
||||
if (dc_is_running() != [self.padController isControllerVisible] && self.gController == nil)
|
||||
#if !TARGET_OS_TV
|
||||
if (dc_is_running() != [self.padController isControllerVisible] && !IOSGamepad::controllerConnected())
|
||||
{
|
||||
if (dc_is_running())
|
||||
[self.padController showController:self.view];
|
||||
else
|
||||
[self.padController hideController];
|
||||
}
|
||||
#endif
|
||||
mainui_rend_frame();
|
||||
}
|
||||
|
||||
static DreamcastKey IosToDCKey[IOS_BTN_MAX] {
|
||||
EMU_BTN_NONE, // none
|
||||
DC_BTN_A,
|
||||
DC_BTN_B,
|
||||
DC_BTN_X,
|
||||
DC_BTN_Y,
|
||||
DC_DPAD_UP,
|
||||
DC_DPAD_DOWN,
|
||||
DC_DPAD_LEFT,
|
||||
DC_DPAD_RIGHT,
|
||||
DC_BTN_START, // menu
|
||||
EMU_BTN_MENU, // options
|
||||
EMU_BTN_NONE, // home
|
||||
EMU_BTN_NONE, // L1
|
||||
EMU_BTN_NONE, // R1
|
||||
EMU_BTN_NONE, // L3
|
||||
EMU_BTN_NONE, // R3
|
||||
EMU_BTN_TRIGGER_LEFT, // L2
|
||||
EMU_BTN_TRIGGER_RIGHT, // R2
|
||||
};
|
||||
|
||||
- (void)handleKeyDown:(enum IOSButton)button;
|
||||
{
|
||||
DreamcastKey dcKey = IosToDCKey[button];
|
||||
switch (dcKey) {
|
||||
case EMU_BTN_NONE:
|
||||
break;
|
||||
case EMU_BTN_MENU:
|
||||
gui_open_settings();
|
||||
break;
|
||||
case EMU_BTN_TRIGGER_LEFT:
|
||||
lt[0] = 0xff;
|
||||
break;
|
||||
case EMU_BTN_TRIGGER_RIGHT:
|
||||
rt[0] = 0xff;
|
||||
break;
|
||||
default:
|
||||
if (dcKey < EMU_BTN_TRIGGER_LEFT)
|
||||
kcode[0] &= ~dcKey;
|
||||
break;
|
||||
}
|
||||
// Open menu with UP + DOWN or LEFT + RIGHT
|
||||
if ((kcode[0] & (DC_DPAD_UP | DC_DPAD_DOWN)) == 0
|
||||
|| (kcode[0] & (DC_DPAD_LEFT | DC_DPAD_RIGHT)) == 0) {
|
||||
kcode[0] = ~0;
|
||||
gui_open_settings();
|
||||
}
|
||||
// Arcade shortcuts
|
||||
if (rt[0] > 0)
|
||||
{
|
||||
if ((kcode[0] & DC_BTN_A) == 0)
|
||||
// RT + A -> D (coin)
|
||||
kcode[0] &= ~DC_BTN_D;
|
||||
if ((kcode[0] & DC_BTN_B) == 0)
|
||||
// RT + B -> C (service)
|
||||
kcode[0] &= ~DC_BTN_C;
|
||||
if ((kcode[0] & DC_BTN_X) == 0)
|
||||
// RT + X -> Z (test)
|
||||
kcode[0] &= ~DC_BTN_Z;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleKeyUp:(enum IOSButton)button;
|
||||
{
|
||||
DreamcastKey dcKey = IosToDCKey[button];
|
||||
switch (dcKey) {
|
||||
case EMU_BTN_NONE:
|
||||
break;
|
||||
case EMU_BTN_TRIGGER_LEFT:
|
||||
lt[0] = 0;
|
||||
break;
|
||||
case EMU_BTN_TRIGGER_RIGHT:
|
||||
rt[0] = 0;
|
||||
break;
|
||||
default:
|
||||
if (dcKey < EMU_BTN_TRIGGER_LEFT)
|
||||
kcode[0] |= dcKey;
|
||||
break;
|
||||
}
|
||||
if (rt[0] == 0)
|
||||
kcode[0] |= DC_BTN_D | DC_BTN_C | DC_BTN_Z;
|
||||
else
|
||||
{
|
||||
if ((kcode[0] & DC_BTN_A) != 0)
|
||||
kcode[0] |= DC_BTN_D;
|
||||
if ((kcode[0] & DC_BTN_B) != 0)
|
||||
kcode[0] |= DC_BTN_C;
|
||||
if ((kcode[0] & DC_BTN_X) != 0)
|
||||
kcode[0] |= DC_BTN_Z;
|
||||
}
|
||||
}
|
||||
/*
|
||||
- (void)pickIosFile
|
||||
{
|
||||
UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"com.flyinghead.flycast.disk-image", @"com.pkware.zip-archive"] inMode:UIDocumentPickerModeOpen];
|
||||
picker.delegate = self;
|
||||
picker.allowsMultipleSelection = true;
|
||||
|
||||
[self presentViewController:picker animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)pickIosFolder
|
||||
{
|
||||
if (@available(iOS 14.0, *)) {
|
||||
|
@ -559,31 +296,29 @@ static DreamcastKey IosToDCKey[IOS_BTN_MAX] {
|
|||
picker.delegate = self;
|
||||
|
||||
[self presentViewController:picker animated:YES completion:nil];
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
NSLog(@"UIDocumentPickerViewController no iOS 14 :(");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls
|
||||
{
|
||||
for (NSURL *url in urls) {
|
||||
std::string path { url.absoluteString.UTF8String };
|
||||
if (path.substr(0, 8) == "file:///")
|
||||
config::ContentPath.get().push_back(path.substr(7));
|
||||
if (url.fileURL)
|
||||
{
|
||||
[url startAccessingSecurityScopedResource];
|
||||
gui_add_content_path(url.fileSystemRepresentation);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@end
|
||||
|
||||
void os_SetupInput()
|
||||
{
|
||||
mouse = std::make_shared<IOSMouse>();
|
||||
GamepadDevice::Register(mouse);
|
||||
}
|
||||
|
||||
void pickIosFolder()
|
||||
{
|
||||
// [flycastViewController pickIosFolder];
|
||||
}
|
||||
|
||||
void pickIosFile()
|
||||
{
|
||||
// [flycastViewController pickIosFile];
|
||||
}
|
||||
|
|
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 9.3 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 11 KiB |
|
@ -1,100 +1,116 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "40.png",
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "60.png",
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "3x"
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "58-1.png",
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "87.png",
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "80.png",
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "120.png",
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "120-1.png",
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"filename" : "180.png",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-180.png",
|
||||
"scale" : "3x"
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "20.png",
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "1x"
|
||||
"scale" : "1x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "40-1.png",
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "29.png",
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "1x"
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "58.png",
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "40-2.png",
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "1x"
|
||||
"scale" : "1x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "80-1.png",
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "76.png",
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "1x"
|
||||
"scale" : "1x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"filename" : "152.png",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-152.png",
|
||||
"scale" : "2x"
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "167.png",
|
||||
"idiom" : "ipad",
|
||||
"size" : "83.5x83.5",
|
||||
"scale" : "2x"
|
||||
"scale" : "2x",
|
||||
"size" : "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"filename" : "appstore.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"size" : "1024x1024",
|
||||
"scale" : "1x"
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 318 KiB |
|
@ -22,7 +22,7 @@
|
|||
</imageView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="hPj-CF-OCp"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<color key="backgroundColor" white="0.75086514261744963" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="38V-np-Q7Z" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
|
|
|
@ -1,10 +1,26 @@
|
|||
/*
|
||||
Copyright 2021 flyinghead
|
||||
Copyright (c) 2015 reicast. All rights reserved.
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
//
|
||||
// Created by Lounge Katt on 8/25/15.
|
||||
// Copyright (c) 2015 reicast. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "FlycastViewController.h"
|
||||
|
||||
@interface PadViewController : UIViewController
|
||||
|
||||
|
@ -13,8 +29,6 @@
|
|||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *joyXConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *joyYConstraint;
|
||||
|
||||
@property (nonatomic, strong) FlycastViewController *handler;
|
||||
|
||||
- (void) showController:(UIView *)parentView;
|
||||
- (void) hideController;
|
||||
- (BOOL) isControllerVisible;
|
||||
|
|
|
@ -1,20 +1,45 @@
|
|||
/*
|
||||
Copyright 2021 flyinghead
|
||||
Copyright (c) 2015 reicast. All rights reserved.
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
//
|
||||
// Created by Lounge Katt on 8/25/15.
|
||||
// Copyright (c) 2015 reicast. All rights reserved.
|
||||
//
|
||||
|
||||
#import "PadViewController.h"
|
||||
#import "EmulatorView.h"
|
||||
#include "ios_gamepad.h"
|
||||
|
||||
@interface PadViewController () {
|
||||
UITouch *joyTouch;
|
||||
CGPoint joyBias;
|
||||
std::shared_ptr<IOSVirtualGamepad> virtualGamepad;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation PadViewController
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
virtualGamepad = std::make_shared<IOSVirtualGamepad>();
|
||||
GamepadDevice::Register(virtualGamepad);
|
||||
}
|
||||
|
||||
- (void)showController:(UIView *)parentView
|
||||
{
|
||||
[parentView addSubview:self.view];
|
||||
|
@ -22,6 +47,7 @@
|
|||
|
||||
- (void)hideController
|
||||
{
|
||||
[self resetTouch];
|
||||
[self.view removeFromSuperview];
|
||||
}
|
||||
|
||||
|
@ -31,12 +57,21 @@
|
|||
|
||||
- (IBAction)keycodeDown:(id)sender
|
||||
{
|
||||
[self.handler handleKeyDown:(enum IOSButton)((UIButton *)sender).tag];
|
||||
virtualGamepad->gamepad_btn_input((u32)((UIButton *)sender).tag, true);
|
||||
}
|
||||
|
||||
- (IBAction)keycodeUp:(id)sender
|
||||
{
|
||||
[self.handler handleKeyUp:(enum IOSButton)((UIButton *)sender).tag];
|
||||
virtualGamepad->gamepad_btn_input((u32)((UIButton *)sender).tag, false);
|
||||
}
|
||||
|
||||
- (void)resetTouch
|
||||
{
|
||||
joyTouch = nil;
|
||||
self.joyXConstraint.constant = 0;
|
||||
self.joyYConstraint.constant = 0;
|
||||
virtualGamepad->gamepad_axis_input(IOS_AXIS_LX, 0);
|
||||
virtualGamepad->gamepad_axis_input(IOS_AXIS_LY, 0);
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
|
||||
|
@ -47,8 +82,8 @@
|
|||
if ([self.joystickBackground pointInside:loc withEvent:event]) {
|
||||
joyTouch = touch;
|
||||
joyBias = loc;
|
||||
joyx[0] = 0;
|
||||
joyy[0] = 0;
|
||||
virtualGamepad->gamepad_axis_input(IOS_AXIS_LX, 0);
|
||||
virtualGamepad->gamepad_axis_input(IOS_AXIS_LY, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -61,11 +96,7 @@
|
|||
if (joyTouch != nil) {
|
||||
for (UITouch *touch in touches) {
|
||||
if (touch == joyTouch) {
|
||||
joyTouch = nil;
|
||||
self.joyXConstraint.constant = 0;
|
||||
self.joyYConstraint.constant = 0;
|
||||
joyx[0] = 0;
|
||||
joyy[0] = 0;
|
||||
[self resetTouch];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -83,15 +114,10 @@
|
|||
pos.y -= joyBias.y;
|
||||
pos.x = std::max<CGFloat>(std::min<CGFloat>(25.0, pos.x), -25.0);
|
||||
pos.y = std::max<CGFloat>(std::min<CGFloat>(25.0, pos.y), -25.0);
|
||||
// 10% dead zone
|
||||
if (pos.x * pos.x + pos.y * pos.y < 2.5 * 2.5) {
|
||||
pos.x = 0;
|
||||
pos.y = 0;
|
||||
}
|
||||
self.joyXConstraint.constant = pos.x;
|
||||
self.joyYConstraint.constant = pos.y;
|
||||
joyx[0] = (s8)round(pos.x * 127.0 / 25.0);
|
||||
joyy[0] = (s8)round(pos.y * 127.0 / 25.0);
|
||||
virtualGamepad->gamepad_axis_input(IOS_AXIS_LX, (s8)std::round(pos.x * 127.0 / 25.0));
|
||||
virtualGamepad->gamepad_axis_input(IOS_AXIS_LY, (s8)std::round(pos.y * 127.0 / 25.0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -104,11 +130,7 @@
|
|||
if (joyTouch != nil) {
|
||||
for (UITouch *touch in touches) {
|
||||
if (touch == joyTouch) {
|
||||
joyTouch = nil;
|
||||
self.joyXConstraint.constant = 0;
|
||||
self.joyYConstraint.constant = 0;
|
||||
joyx[0] = 0;
|
||||
joyy[0] = 0;
|
||||
[self resetTouch];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina5_5" orientation="landscape" appearance="light"/>
|
||||
<device id="retina5_9" orientation="landscape" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
|
||||
|
@ -19,10 +19,10 @@
|
|||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view multipleTouchEnabled="YES" alpha="0.5" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3M7-1s-N5r">
|
||||
<rect key="frame" x="0.0" y="0.0" width="736" height="414"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="812" height="375"/>
|
||||
<subviews>
|
||||
<button opaque="NO" multipleTouchEnabled="YES" tag="16" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8Gl-Iv-u8L" userLabel="LT-Button">
|
||||
<rect key="frame" x="566" y="185" width="80" height="40"/>
|
||||
<button opaque="NO" multipleTouchEnabled="YES" tag="14" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8Gl-Iv-u8L" userLabel="LT-Button">
|
||||
<rect key="frame" x="598" y="146" width="80" height="40"/>
|
||||
<state key="normal">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
|
@ -32,14 +32,14 @@
|
|||
</connections>
|
||||
</button>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="RTrigger" translatesAutoresizingMaskIntoConstraints="NO" id="Cjn-zx-eSs">
|
||||
<rect key="frame" x="656" y="185" width="80" height="40"/>
|
||||
<rect key="frame" x="688" y="146" width="80" height="40"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="80" id="rBv-DR-Gwq"/>
|
||||
<constraint firstAttribute="height" constant="40" id="uA3-z1-wS7"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<button opaque="NO" multipleTouchEnabled="YES" tag="17" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="V8J-vG-dlF" userLabel="RT-Button">
|
||||
<rect key="frame" x="656" y="185" width="80" height="40"/>
|
||||
<button opaque="NO" multipleTouchEnabled="YES" tag="15" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="V8J-vG-dlF" userLabel="RT-Button">
|
||||
<rect key="frame" x="688" y="146" width="80" height="40"/>
|
||||
<state key="normal">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
|
@ -49,24 +49,24 @@
|
|||
</connections>
|
||||
</button>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="JoystickButton" translatesAutoresizingMaskIntoConstraints="NO" id="ivh-8r-bw3" userLabel="JoystickThumbpad">
|
||||
<rect key="frame" x="20" y="294" width="100" height="100"/>
|
||||
<rect key="frame" x="64" y="255" width="100" height="100"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="100" id="0eo-Wy-by0"/>
|
||||
<constraint firstAttribute="height" constant="100" id="hQl-cG-b0g"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="JoystickBackground" translatesAutoresizingMaskIntoConstraints="NO" id="OMP-L6-n0A">
|
||||
<rect key="frame" x="10" y="284" width="120" height="120"/>
|
||||
<rect key="frame" x="54" y="245" width="120" height="120"/>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="DPad" translatesAutoresizingMaskIntoConstraints="NO" id="FLe-Gr-hny">
|
||||
<rect key="frame" x="0.0" y="124" width="140" height="140"/>
|
||||
<rect key="frame" x="44" y="85" width="140" height="140"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="140" id="EF0-T5-Nk9"/>
|
||||
<constraint firstAttribute="height" constant="140" id="fly-c3-Ajo"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<button opaque="NO" multipleTouchEnabled="YES" tag="7" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rp6-Nd-1qa" userLabel="L-Button">
|
||||
<rect key="frame" x="0.0" y="174" width="46" height="40"/>
|
||||
<rect key="frame" x="44" y="135" width="46" height="40"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="46" id="MX4-af-OJg"/>
|
||||
<constraint firstAttribute="height" constant="40" id="yGw-c7-7x8"/>
|
||||
|
@ -80,7 +80,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" multipleTouchEnabled="YES" tag="8" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CVH-hw-R8F" userLabel="R-Button">
|
||||
<rect key="frame" x="94" y="174" width="46" height="40"/>
|
||||
<rect key="frame" x="138" y="135" width="46" height="40"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="40" id="9OW-42-b64"/>
|
||||
<constraint firstAttribute="width" constant="46" id="grQ-i7-YHe"/>
|
||||
|
@ -94,7 +94,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" multipleTouchEnabled="YES" tag="5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="WMD-Fv-ibu" userLabel="U-Button">
|
||||
<rect key="frame" x="50" y="124" width="40" height="46"/>
|
||||
<rect key="frame" x="94" y="85" width="40" height="46"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="46" id="2gU-xW-ddx"/>
|
||||
<constraint firstAttribute="width" constant="40" id="oUv-ex-E9I"/>
|
||||
|
@ -108,7 +108,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" multipleTouchEnabled="YES" tag="6" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="s7g-nq-lRU" userLabel="D-Button">
|
||||
<rect key="frame" x="50" y="218" width="40" height="46"/>
|
||||
<rect key="frame" x="94" y="179" width="40" height="46"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="40" id="3IG-k8-ER3"/>
|
||||
<constraint firstAttribute="height" constant="46" id="4vu-3O-H8J"/>
|
||||
|
@ -122,14 +122,14 @@
|
|||
</connections>
|
||||
</button>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ABXYPad" translatesAutoresizingMaskIntoConstraints="NO" id="xbP-E4-fCE">
|
||||
<rect key="frame" x="576" y="244" width="160" height="160"/>
|
||||
<rect key="frame" x="608" y="205" width="160" height="160"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="160" id="QFX-3I-lXd"/>
|
||||
<constraint firstAttribute="height" constant="160" id="lVn-RY-tWm"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<button opaque="NO" multipleTouchEnabled="YES" tag="3" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iwO-7q-c8H" userLabel="X-Button">
|
||||
<rect key="frame" x="576" y="294" width="60" height="60"/>
|
||||
<rect key="frame" x="608" y="255" width="60" height="60"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="60" id="4Yf-ri-Ccb"/>
|
||||
<constraint firstAttribute="width" constant="60" id="mnO-1S-Phd"/>
|
||||
|
@ -143,7 +143,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" multipleTouchEnabled="YES" tag="2" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7LB-OY-vh3" userLabel="B-Button">
|
||||
<rect key="frame" x="676" y="294" width="60" height="60"/>
|
||||
<rect key="frame" x="708" y="255" width="60" height="60"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="60" id="1YC-G4-kwg"/>
|
||||
<constraint firstAttribute="height" constant="60" id="LHC-wi-Yap"/>
|
||||
|
@ -157,7 +157,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" multipleTouchEnabled="YES" tag="4" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hGZ-v7-VA5" userLabel="Y-Button">
|
||||
<rect key="frame" x="626" y="244" width="60" height="60"/>
|
||||
<rect key="frame" x="658" y="205" width="60" height="60"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="60" id="gv4-it-7ly"/>
|
||||
<constraint firstAttribute="height" constant="60" id="hx2-N9-Lba"/>
|
||||
|
@ -171,7 +171,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" multipleTouchEnabled="YES" tag="1" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iKO-3z-Ias" userLabel="A-Button">
|
||||
<rect key="frame" x="626" y="344" width="60" height="60"/>
|
||||
<rect key="frame" x="658" y="305" width="60" height="60"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="60" id="Dgi-6d-cHy"/>
|
||||
<constraint firstAttribute="width" constant="60" id="L1S-2O-QBB"/>
|
||||
|
@ -185,14 +185,14 @@
|
|||
</connections>
|
||||
</button>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Start" translatesAutoresizingMaskIntoConstraints="NO" id="9K0-cV-7zu">
|
||||
<rect key="frame" x="328" y="364" width="80" height="40"/>
|
||||
<rect key="frame" x="366" y="310" width="80" height="40"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="40" id="2xE-Lt-nRt"/>
|
||||
<constraint firstAttribute="width" constant="80" id="BpH-iH-but"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<button opaque="NO" multipleTouchEnabled="YES" tag="9" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="VtI-tC-PSX" userLabel="S-Button">
|
||||
<rect key="frame" x="341" y="364" width="54" height="40"/>
|
||||
<rect key="frame" x="379" y="310" width="54" height="40"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="54" id="HjR-fP-Q1s"/>
|
||||
<constraint firstAttribute="height" constant="40" id="MK6-rm-vpj"/>
|
||||
|
@ -206,7 +206,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="LTrigger" translatesAutoresizingMaskIntoConstraints="NO" id="H57-MD-elm">
|
||||
<rect key="frame" x="566" y="185" width="80" height="40"/>
|
||||
<rect key="frame" x="598" y="146" width="80" height="40"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="40" id="3TN-BZ-DkR"/>
|
||||
<constraint firstAttribute="width" constant="80" id="Pj7-YU-3Ze"/>
|
||||
|
@ -218,7 +218,7 @@
|
|||
<constraints>
|
||||
<constraint firstItem="s7g-nq-lRU" firstAttribute="centerX" secondItem="FLe-Gr-hny" secondAttribute="centerX" id="4bj-Mc-SN7"/>
|
||||
<constraint firstItem="V8J-vG-dlF" firstAttribute="height" secondItem="Cjn-zx-eSs" secondAttribute="height" id="77t-Hb-AxC"/>
|
||||
<constraint firstAttribute="trailing" secondItem="xbP-E4-fCE" secondAttribute="trailing" id="84h-PU-NtS"/>
|
||||
<constraint firstItem="DJj-LJ-xnv" firstAttribute="trailing" secondItem="xbP-E4-fCE" secondAttribute="trailing" id="84h-PU-NtS"/>
|
||||
<constraint firstItem="OMP-L6-n0A" firstAttribute="top" secondItem="FLe-Gr-hny" secondAttribute="bottom" constant="20" id="9o9-R0-BDC"/>
|
||||
<constraint firstItem="ivh-8r-bw3" firstAttribute="centerY" secondItem="OMP-L6-n0A" secondAttribute="centerY" id="AGO-kZ-c5L"/>
|
||||
<constraint firstItem="8Gl-Iv-u8L" firstAttribute="height" secondItem="H57-MD-elm" secondAttribute="height" id="BMW-0T-VdJ"/>
|
||||
|
@ -247,10 +247,10 @@
|
|||
<constraint firstItem="Cjn-zx-eSs" firstAttribute="centerX" secondItem="V8J-vG-dlF" secondAttribute="centerX" id="lmY-iZ-lW5"/>
|
||||
<constraint firstAttribute="bottom" secondItem="xbP-E4-fCE" secondAttribute="bottom" constant="10" id="ner-TB-GTz"/>
|
||||
<constraint firstItem="ivh-8r-bw3" firstAttribute="centerX" secondItem="OMP-L6-n0A" secondAttribute="centerX" id="o7b-NF-aWi"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Cjn-zx-eSs" secondAttribute="trailing" id="qTc-91-kbO"/>
|
||||
<constraint firstItem="DJj-LJ-xnv" firstAttribute="trailing" secondItem="Cjn-zx-eSs" secondAttribute="trailing" id="qTc-91-kbO"/>
|
||||
<constraint firstItem="H57-MD-elm" firstAttribute="centerX" secondItem="8Gl-Iv-u8L" secondAttribute="centerX" id="sNB-9U-qXY"/>
|
||||
<constraint firstItem="V8J-vG-dlF" firstAttribute="width" secondItem="Cjn-zx-eSs" secondAttribute="width" id="sxe-fC-pvB"/>
|
||||
<constraint firstAttribute="bottom" secondItem="9K0-cV-7zu" secondAttribute="bottom" constant="10" id="wWR-HB-aLq"/>
|
||||
<constraint firstItem="DJj-LJ-xnv" firstAttribute="bottom" secondItem="9K0-cV-7zu" secondAttribute="bottom" constant="4" id="wWR-HB-aLq"/>
|
||||
<constraint firstItem="WMD-Fv-ibu" firstAttribute="centerX" secondItem="FLe-Gr-hny" secondAttribute="centerX" id="xqT-1Z-AQx"/>
|
||||
<constraint firstItem="rp6-Nd-1qa" firstAttribute="leading" secondItem="FLe-Gr-hny" secondAttribute="leading" id="zVb-6D-06l"/>
|
||||
<constraint firstItem="hGZ-v7-VA5" firstAttribute="top" secondItem="xbP-E4-fCE" secondAttribute="top" id="zgF-U5-iWT"/>
|
||||
|
|
|
@ -60,5 +60,65 @@
|
|||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<true/>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Flycast requires microphone access to emulate the Dreamcast microphone</string>
|
||||
<key>UISupportsDocumentBrowser</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Disk image</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.flyinghead.flycast.disk-image</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Zip archive</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.pkware.zip-archive</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Disk image</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.flyinghead.flycast.disk-image</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>chd</string>
|
||||
<string>gdi</string>
|
||||
<string>cue</string>
|
||||
<string>cdi</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -0,0 +1,538 @@
|
|||
/*
|
||||
Copyright 2021 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#import <GameController/GameController.h>
|
||||
#import <CoreHaptics/CoreHaptics.h>
|
||||
|
||||
#include <cmath>
|
||||
#include "input/gamepad_device.h"
|
||||
#include "rend/gui.h"
|
||||
|
||||
enum IOSButton {
|
||||
IOS_BTN_A = 1,
|
||||
IOS_BTN_B,
|
||||
IOS_BTN_X,
|
||||
IOS_BTN_Y,
|
||||
IOS_BTN_UP,
|
||||
IOS_BTN_DOWN,
|
||||
IOS_BTN_LEFT,
|
||||
IOS_BTN_RIGHT,
|
||||
IOS_BTN_MENU, // aka Start
|
||||
IOS_BTN_OPTIONS, // aka Back (xbox), Select (dualshock)
|
||||
IOS_BTN_HOME,
|
||||
IOS_BTN_L1,
|
||||
IOS_BTN_R1,
|
||||
IOS_BTN_L2,
|
||||
IOS_BTN_R2,
|
||||
IOS_BTN_L3,
|
||||
IOS_BTN_R3,
|
||||
IOS_BTN_SHARE,
|
||||
IOS_BTN_PADDLE1,
|
||||
IOS_BTN_PADDLE2,
|
||||
IOS_BTN_PADDLE3,
|
||||
IOS_BTN_PADDLE4,
|
||||
IOS_BTN_TOUCHPAD,
|
||||
|
||||
IOS_BTN_MAX
|
||||
};
|
||||
enum IOSAxis {
|
||||
IOS_AXIS_L1 = 1,
|
||||
IOS_AXIS_R1,
|
||||
IOS_AXIS_L2,
|
||||
IOS_AXIS_R2,
|
||||
IOS_AXIS_LX,
|
||||
IOS_AXIS_LY,
|
||||
IOS_AXIS_RX,
|
||||
IOS_AXIS_RY,
|
||||
};
|
||||
|
||||
static NSString *GCInputXboxShareButton = @"Button Share";
|
||||
|
||||
class DefaultIOSMapping : public InputMapping
|
||||
{
|
||||
public:
|
||||
DefaultIOSMapping()
|
||||
{
|
||||
name = "Default";
|
||||
set_button(DC_BTN_A, IOS_BTN_A);
|
||||
set_button(DC_BTN_B, IOS_BTN_B);
|
||||
set_button(DC_BTN_X, IOS_BTN_X);
|
||||
set_button(DC_BTN_Y, IOS_BTN_Y);
|
||||
set_button(DC_DPAD_UP, IOS_BTN_UP);
|
||||
set_button(DC_DPAD_DOWN, IOS_BTN_DOWN);
|
||||
set_button(DC_DPAD_LEFT, IOS_BTN_LEFT);
|
||||
set_button(DC_DPAD_RIGHT, IOS_BTN_RIGHT);
|
||||
set_button(DC_BTN_START, IOS_BTN_MENU);
|
||||
set_button(EMU_BTN_MENU, IOS_BTN_OPTIONS);
|
||||
|
||||
set_axis(DC_AXIS_X, IOS_AXIS_LX, false);
|
||||
set_axis(DC_AXIS_Y, IOS_AXIS_LY, false);
|
||||
set_axis(DC_AXIS_X2, IOS_AXIS_RX, false);
|
||||
set_axis(DC_AXIS_Y2, IOS_AXIS_RY, false);
|
||||
set_axis(DC_AXIS_LT, IOS_AXIS_L2, false);
|
||||
set_axis(DC_AXIS_RT, IOS_AXIS_R2, false);
|
||||
dirty = false;
|
||||
}
|
||||
};
|
||||
|
||||
class IOSGamepad : public GamepadDevice
|
||||
{
|
||||
public:
|
||||
IOSGamepad(int port, GCController *controller) : GamepadDevice(port, "iOS"), gcController(controller)
|
||||
{
|
||||
set_maple_port(port);
|
||||
if (gcController.vendorName != nullptr)
|
||||
_name = [gcController.vendorName UTF8String];
|
||||
else
|
||||
_name = "MFi Gamepad";
|
||||
//_unique_id = ?
|
||||
INFO_LOG(INPUT, "iOS: Opened joystick %d: '%s'", port, _name.c_str());
|
||||
loadMapping();
|
||||
|
||||
if (gcController.extendedGamepad != nullptr)
|
||||
{
|
||||
[gcController.extendedGamepad.buttonA setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_A, pressed);
|
||||
}];
|
||||
[gcController.extendedGamepad.buttonB setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_B, pressed);
|
||||
}];
|
||||
[gcController.extendedGamepad.buttonX setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_X, pressed);
|
||||
}];
|
||||
[gcController.extendedGamepad.buttonY setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_Y, pressed);
|
||||
}];
|
||||
[gcController.extendedGamepad.dpad setValueChangedHandler:^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
|
||||
gamepad_btn_input(IOS_BTN_RIGHT, dpad.right.isPressed);
|
||||
gamepad_btn_input(IOS_BTN_LEFT, dpad.left.isPressed);
|
||||
gamepad_btn_input(IOS_BTN_UP, dpad.up.isPressed);
|
||||
gamepad_btn_input(IOS_BTN_DOWN, dpad.down.isPressed);
|
||||
}];
|
||||
if (@available(iOS 12.1, *)) {
|
||||
[gcController.extendedGamepad.rightThumbstickButton setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_R3, pressed);
|
||||
}];
|
||||
[gcController.extendedGamepad.leftThumbstickButton setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_L3, pressed);
|
||||
}];
|
||||
}
|
||||
if (@available(iOS 13.0, *)) {
|
||||
[gcController.extendedGamepad.buttonOptions setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_OPTIONS, pressed);
|
||||
}];
|
||||
[gcController.extendedGamepad.buttonMenu setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_MENU, pressed);
|
||||
}];
|
||||
[gcController.extendedGamepad.leftShoulder setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_L1, pressed);
|
||||
}];
|
||||
[gcController.extendedGamepad.rightShoulder setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_R1, pressed);
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Left shoulder for options/menu
|
||||
[gcController.extendedGamepad.leftShoulder setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_OPTIONS, pressed);
|
||||
}];
|
||||
// Right shoulder for menu/start
|
||||
[gcController.extendedGamepad.rightShoulder setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_MENU, pressed);
|
||||
}];
|
||||
}
|
||||
if (@available(iOS 14.0, *)) {
|
||||
[gcController.extendedGamepad.buttonHome setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_HOME, pressed);
|
||||
}];
|
||||
}
|
||||
|
||||
[gcController.extendedGamepad.rightTrigger setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_R2, pressed);
|
||||
gamepad_axis_input(IOS_AXIS_R2, (int)std::roundf(255.f * value));
|
||||
}];
|
||||
[gcController.extendedGamepad.leftTrigger setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_L2, pressed);
|
||||
gamepad_axis_input(IOS_AXIS_L2, (int)std::roundf(255.f * value));
|
||||
}];
|
||||
[gcController.extendedGamepad.leftThumbstick.xAxis setValueChangedHandler:^(GCControllerAxisInput *axis, float value) {
|
||||
gamepad_axis_input(IOS_AXIS_LX, (int)std::roundf(127.f * value));
|
||||
}];
|
||||
[gcController.extendedGamepad.leftThumbstick.yAxis setValueChangedHandler:^(GCControllerAxisInput *axis, float value) {
|
||||
gamepad_axis_input(IOS_AXIS_LY, (int)std::roundf(-127.f * value));
|
||||
}];
|
||||
[gcController.extendedGamepad.rightThumbstick.xAxis setValueChangedHandler:^(GCControllerAxisInput *axis, float value) {
|
||||
gamepad_axis_input(IOS_AXIS_RX, (int)std::roundf(127.f * value));
|
||||
}];
|
||||
[gcController.extendedGamepad.rightThumbstick.yAxis setValueChangedHandler:^(GCControllerAxisInput *axis, float value) {
|
||||
gamepad_axis_input(IOS_AXIS_RY, (int)std::roundf(-127.f * value));
|
||||
}];
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Legacy gamepad
|
||||
[gcController.gamepad.buttonA setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_A, pressed);
|
||||
}];
|
||||
[gcController.gamepad.buttonB setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_B, pressed);
|
||||
}];
|
||||
[gcController.gamepad.buttonX setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_X, pressed);
|
||||
}];
|
||||
[gcController.gamepad.buttonY setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_Y, pressed);
|
||||
}];
|
||||
[gcController.gamepad.dpad setValueChangedHandler:^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
|
||||
gamepad_btn_input(IOS_BTN_RIGHT, dpad.right.isPressed);
|
||||
gamepad_btn_input(IOS_BTN_LEFT, dpad.left.isPressed);
|
||||
gamepad_btn_input(IOS_BTN_UP, dpad.up.isPressed);
|
||||
gamepad_btn_input(IOS_BTN_DOWN, dpad.down.isPressed);
|
||||
}];
|
||||
[gcController.gamepad.rightShoulder setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed) {
|
||||
if (gcController != nil && gcController.gamepad.leftShoulder.pressed)
|
||||
gamepad_btn_input(IOS_BTN_MENU, true);
|
||||
else
|
||||
gamepad_btn_input(IOS_BTN_R2, true);
|
||||
}
|
||||
else {
|
||||
gamepad_btn_input(IOS_BTN_R2, false);
|
||||
gamepad_btn_input(IOS_BTN_MENU, false);
|
||||
}
|
||||
}];
|
||||
[gcController.gamepad.leftShoulder setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if (pressed) {
|
||||
if (gcController != nil && gcController.gamepad.rightShoulder.pressed)
|
||||
gamepad_btn_input(IOS_BTN_MENU, true);
|
||||
else
|
||||
gamepad_btn_input(IOS_BTN_L2, true);
|
||||
}
|
||||
else {
|
||||
gamepad_btn_input(IOS_BTN_L2, false);
|
||||
gamepad_btn_input(IOS_BTN_MENU, false);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
if (@available(iOS 14.0, *)) {
|
||||
if (gcController.physicalInputProfile.buttons[GCInputXboxPaddleOne] != nil) {
|
||||
[gcController.physicalInputProfile.buttons[GCInputXboxPaddleOne] setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_PADDLE1, pressed);
|
||||
}];
|
||||
}
|
||||
if (gcController.physicalInputProfile.buttons[GCInputXboxPaddleTwo] != nil) {
|
||||
[gcController.physicalInputProfile.buttons[GCInputXboxPaddleTwo] setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_PADDLE2, pressed);
|
||||
}];
|
||||
}
|
||||
if (gcController.physicalInputProfile.buttons[GCInputXboxPaddleThree] != nil) {
|
||||
[gcController.physicalInputProfile.buttons[GCInputXboxPaddleThree] setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_PADDLE3, pressed);
|
||||
}];
|
||||
}
|
||||
if (gcController.physicalInputProfile.buttons[GCInputXboxPaddleFour] != nil) {
|
||||
[gcController.physicalInputProfile.buttons[GCInputXboxPaddleFour] setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_PADDLE4, pressed);
|
||||
}];
|
||||
}
|
||||
if (gcController.physicalInputProfile.buttons[GCInputXboxShareButton] != nil) {
|
||||
[gcController.physicalInputProfile.buttons[GCInputXboxShareButton] setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_SHARE, pressed);
|
||||
}];
|
||||
}
|
||||
if (gcController.physicalInputProfile.buttons[GCInputDualShockTouchpadButton] != nil) {
|
||||
[gcController.physicalInputProfile.buttons[GCInputDualShockTouchpadButton] setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
gamepad_btn_input(IOS_BTN_TOUCHPAD, pressed);
|
||||
}];
|
||||
}
|
||||
if (gcController.haptics != nullptr)
|
||||
{
|
||||
CHHapticEngine *hapticEngine = [gcController.haptics createEngineWithLocality:GCHapticsLocalityDefault];
|
||||
NSError *error = nil;
|
||||
[hapticEngine startAndReturnError:&error];
|
||||
if (error != nullptr)
|
||||
NSLog(@"Haptic engine error: \(error)");
|
||||
else {
|
||||
this->hapticEngine = hapticEngine;
|
||||
_rumble_enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~IOSGamepad() {
|
||||
if (hapticEngine != nullptr)
|
||||
[hapticEngine stopWithCompletionHandler:^(NSError * _Nullable error) {}];
|
||||
}
|
||||
|
||||
void set_maple_port(int port) override
|
||||
{
|
||||
GamepadDevice::set_maple_port(port);
|
||||
if (port <= 3 && gcController != nil)
|
||||
gcController.playerIndex = (GCControllerPlayerIndex)port;
|
||||
}
|
||||
|
||||
const char *get_button_name(u32 code) override
|
||||
{
|
||||
switch ((IOSButton)code) {
|
||||
case IOS_BTN_A:
|
||||
return "A";
|
||||
case IOS_BTN_B:
|
||||
return "B";
|
||||
case IOS_BTN_X:
|
||||
return "X";
|
||||
case IOS_BTN_Y:
|
||||
return "Y";
|
||||
case IOS_BTN_UP:
|
||||
return "DPad Up";
|
||||
case IOS_BTN_DOWN:
|
||||
return "DPad Down";
|
||||
case IOS_BTN_LEFT:
|
||||
return "DPad Left";
|
||||
case IOS_BTN_RIGHT:
|
||||
return "DPad Right";
|
||||
case IOS_BTN_MENU:
|
||||
return "Menu";
|
||||
case IOS_BTN_OPTIONS:
|
||||
return "Options";
|
||||
case IOS_BTN_HOME:
|
||||
return "Home";
|
||||
case IOS_BTN_L1:
|
||||
return "L Shoulder";
|
||||
case IOS_BTN_R1:
|
||||
return "R Shoulder";
|
||||
case IOS_BTN_L2:
|
||||
return "L Trigger";
|
||||
case IOS_BTN_R2:
|
||||
return "R Trigger";
|
||||
case IOS_BTN_L3:
|
||||
return "L Thumbstick";
|
||||
case IOS_BTN_R3:
|
||||
return "R Thumbstick";
|
||||
case IOS_BTN_SHARE:
|
||||
return "Share";
|
||||
case IOS_BTN_PADDLE1:
|
||||
return "Paddle 1";
|
||||
case IOS_BTN_PADDLE2:
|
||||
return "Paddle 2";
|
||||
case IOS_BTN_PADDLE3:
|
||||
return "Paddle 3";
|
||||
case IOS_BTN_PADDLE4:
|
||||
return "Paddle 4";
|
||||
case IOS_BTN_TOUCHPAD:
|
||||
return "Touchpad";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
const char *get_axis_name(u32 code) override
|
||||
{
|
||||
switch ((IOSAxis)code) {
|
||||
case IOS_AXIS_L1:
|
||||
return "L Shoulder";
|
||||
case IOS_AXIS_R1:
|
||||
return "R Shoulder";
|
||||
case IOS_AXIS_L2:
|
||||
return "L Trigger";
|
||||
case IOS_AXIS_R2:
|
||||
return "R Trigger";
|
||||
case IOS_AXIS_LX:
|
||||
return "L Stick X";
|
||||
case IOS_AXIS_LY:
|
||||
return "L Stick Y";
|
||||
case IOS_AXIS_RX:
|
||||
return "R Stick X";
|
||||
case IOS_AXIS_RY:
|
||||
return "R Stick Y";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<InputMapping> getDefaultMapping() override {
|
||||
return std::make_shared<DefaultIOSMapping>();
|
||||
}
|
||||
|
||||
void rumble(float power, float inclination, u32 duration_ms) override
|
||||
{
|
||||
NOTICE_LOG(INPUT, "rumble %.1f inc %f duration %d", power, inclination, duration_ms);
|
||||
if (@available(iOS 13.0, *)) {
|
||||
CHHapticEvent *event = [[CHHapticEvent alloc] initWithEventType:CHHapticEventTypeHapticContinuous
|
||||
parameters:@[
|
||||
[[CHHapticEventParameter alloc] initWithParameterID:CHHapticEventParameterIDHapticIntensity value:power]]
|
||||
relativeTime:0.0
|
||||
duration:duration_ms / 1000.0];
|
||||
NSError *error = nil;
|
||||
CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithEvents:@[event] parameters:@[] error:&error];
|
||||
if (error != nil)
|
||||
return;
|
||||
if (hapticPlayer != nil)
|
||||
[hapticPlayer stopAtTime:0 error:&error];
|
||||
hapticPlayer = [hapticEngine createPlayerWithPattern:pattern error:&error];
|
||||
if (error != nil)
|
||||
return;
|
||||
[hapticPlayer startAtTime:0 error:&error];
|
||||
}
|
||||
}
|
||||
|
||||
static void addController(GCController *controller)
|
||||
{
|
||||
if (controllers.count(controller) > 0)
|
||||
return;
|
||||
if (controller.extendedGamepad == nullptr && controller.gamepad == nullptr)
|
||||
return;
|
||||
int port = std::min((int)controllers.size(), 3);
|
||||
controllers[controller] = std::make_shared<IOSGamepad>(port, controller);
|
||||
GamepadDevice::Register(controllers[controller]);
|
||||
}
|
||||
|
||||
static void removeController(GCController *controller)
|
||||
{
|
||||
auto it = controllers.find(controller);
|
||||
if (it == controllers.end())
|
||||
return;
|
||||
GamepadDevice::Unregister(it->second);
|
||||
controllers.erase(it);
|
||||
}
|
||||
|
||||
static bool controllerConnected() {
|
||||
return !controllers.empty();
|
||||
}
|
||||
|
||||
protected:
|
||||
void load_axis_min_max(u32 axis) override
|
||||
{
|
||||
if (axis == IOS_AXIS_L1 || axis == IOS_AXIS_R1 || axis == IOS_AXIS_L2 || axis == IOS_AXIS_R2)
|
||||
{
|
||||
axis_min_values[axis] = 0;
|
||||
axis_ranges[axis] = 0xff;
|
||||
}
|
||||
else
|
||||
{
|
||||
axis_min_values[axis] = -127;
|
||||
axis_ranges[axis] = 254;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
GCController * __weak gcController = nullptr;
|
||||
CHHapticEngine *hapticEngine = nullptr;
|
||||
id<CHHapticPatternPlayer> hapticPlayer;
|
||||
static std::map<GCController *, std::shared_ptr<IOSGamepad>> controllers;
|
||||
};
|
||||
|
||||
class IOSVirtualGamepad : public GamepadDevice
|
||||
{
|
||||
public:
|
||||
IOSVirtualGamepad() : GamepadDevice(0, "iOS", false) {
|
||||
_name = "Virtual Gamepad";
|
||||
_unique_id = "ios-virtual-gamepad";
|
||||
input_mapper = getDefaultMapping();
|
||||
}
|
||||
|
||||
bool is_virtual_gamepad() override { return true; }
|
||||
|
||||
std::shared_ptr<InputMapping> getDefaultMapping() override {
|
||||
return std::make_shared<DefaultIOSMapping>();
|
||||
}
|
||||
|
||||
bool gamepad_btn_input(u32 code, bool pressed) override
|
||||
{
|
||||
if (pressed)
|
||||
buttonState |= 1 << code;
|
||||
else
|
||||
buttonState &= ~(1 << code);
|
||||
switch (code)
|
||||
{
|
||||
case IOS_BTN_L2:
|
||||
gamepad_axis_input(IOS_AXIS_L2, pressed ? 0xff : 0);
|
||||
return true;
|
||||
case IOS_BTN_R2:
|
||||
if (!pressed && maple_port() >= 0 && maple_port() <= 3)
|
||||
kcode[maple_port()] |= DC_BTN_C | DC_BTN_D | DC_BTN_Z;
|
||||
gamepad_axis_input(IOS_AXIS_R2, pressed ? 0xff : 0);
|
||||
return true;
|
||||
default:
|
||||
if ((buttonState & ((1 << IOS_BTN_UP) | (1 << IOS_BTN_DOWN))) == ((1 << IOS_BTN_UP) | (1 << IOS_BTN_DOWN))
|
||||
|| (buttonState & ((1 << IOS_BTN_LEFT) | (1 << IOS_BTN_RIGHT))) == ((1 << IOS_BTN_LEFT) | (1 << IOS_BTN_RIGHT)))
|
||||
{
|
||||
GamepadDevice::gamepad_btn_input(IOS_BTN_UP, false);
|
||||
GamepadDevice::gamepad_btn_input(IOS_BTN_DOWN, false);
|
||||
GamepadDevice::gamepad_btn_input(IOS_BTN_LEFT, false);
|
||||
GamepadDevice::gamepad_btn_input(IOS_BTN_RIGHT, false);
|
||||
buttonState = 0;
|
||||
gui_open_settings();
|
||||
return true;
|
||||
}
|
||||
// Arcade shortcuts
|
||||
if ((buttonState & (1 << IOS_BTN_R2)) != 0 && maple_port() >= 0 && maple_port() <= 3)
|
||||
{
|
||||
u32& keycode = kcode[maple_port()];
|
||||
switch (code) {
|
||||
case IOS_BTN_A:
|
||||
// RT + A -> D (coin)
|
||||
keycode = pressed ? keycode & ~DC_BTN_D : keycode | DC_BTN_D;
|
||||
break;
|
||||
case IOS_BTN_B:
|
||||
// RT + B -> C (service)
|
||||
keycode = pressed ? keycode & ~DC_BTN_C : keycode | DC_BTN_C;
|
||||
break;
|
||||
case IOS_BTN_X:
|
||||
// RT + X -> Z (test)
|
||||
keycode = pressed ? keycode & ~DC_BTN_Z : keycode | DC_BTN_Z;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return GamepadDevice::gamepad_btn_input(code, pressed);
|
||||
}
|
||||
}
|
||||
protected:
|
||||
void load_axis_min_max(u32 axis) override
|
||||
{
|
||||
if (axis == IOS_AXIS_L1 || axis == IOS_AXIS_R1 || axis == IOS_AXIS_L2 || axis == IOS_AXIS_R2)
|
||||
{
|
||||
axis_min_values[axis] = 0;
|
||||
axis_ranges[axis] = 0xff;
|
||||
}
|
||||
else
|
||||
{
|
||||
axis_min_values[axis] = -127;
|
||||
axis_ranges[axis] = 254;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
u32 buttonState = 0;
|
||||
};
|
||||
|
||||
class IOSMouse : public SystemMouse
|
||||
{
|
||||
public:
|
||||
IOSMouse() : SystemMouse("iOS")
|
||||
{
|
||||
_unique_id = "ios_mouse";
|
||||
loadMapping();
|
||||
}
|
||||
};
|
|
@ -1,7 +1,22 @@
|
|||
//
|
||||
// Copyright (c) 2014 Karen Tsai (angelXwind). All rights reserved.
|
||||
//
|
||||
/*
|
||||
Copyright 2021 flyinghead
|
||||
Copyright (c) 2014 Karen Tsai (angelXwind). All rights reserved.
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <string>
|
||||
|
@ -32,6 +47,9 @@ void os_CreateWindow() {
|
|||
void UpdateInputState() {
|
||||
}
|
||||
|
||||
void os_SetupInput() {
|
||||
}
|
||||
|
||||
std::string os_Locale(){
|
||||
return [[[NSLocale preferredLanguages] objectAtIndex:0] UTF8String];
|
||||
}
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
#pragma once
|
||||
#import <GameController/GameController.h>
|
||||
|
||||
#include "input/gamepad_device.h"
|
||||
|
||||
enum IOSButton {
|
||||
IOS_BTN_A = 1,
|
||||
IOS_BTN_B = 2,
|
||||
IOS_BTN_X = 3,
|
||||
IOS_BTN_Y = 4,
|
||||
IOS_BTN_UP = 5,
|
||||
IOS_BTN_DOWN = 6,
|
||||
IOS_BTN_LEFT = 7,
|
||||
IOS_BTN_RIGHT = 8,
|
||||
IOS_BTN_MENU = 9,
|
||||
IOS_BTN_OPTIONS = 10,
|
||||
IOS_BTN_HOME = 11,
|
||||
IOS_BTN_L1 = 12,
|
||||
IOS_BTN_R1 = 13,
|
||||
IOS_BTN_L3 = 14,
|
||||
IOS_BTN_R3 = 15,
|
||||
IOS_BTN_L2 = 16,
|
||||
IOS_BTN_R2 = 17,
|
||||
|
||||
IOS_BTN_MAX
|
||||
};
|
||||
|
||||
class IOSGamePad : public GamepadDevice
|
||||
{
|
||||
public:
|
||||
IOSGamePad(int port, GCController *controller) : GamepadDevice(port, "iOS"), gcController(controller)
|
||||
{
|
||||
gcController.playerIndex = (GCControllerPlayerIndex)port;
|
||||
if (gcController.vendorName != nullptr)
|
||||
_name = [gcController.vendorName UTF8String];
|
||||
//_unique_id = ?
|
||||
INFO_LOG(INPUT, "iOS: Opened joystick %d: '%s' unique_id=%s", port, _name.c_str(), _unique_id.c_str());
|
||||
loadMapping();
|
||||
}
|
||||
|
||||
static void addController(GCController *controller)
|
||||
{
|
||||
if (controllers.count(controller) > 0)
|
||||
return;
|
||||
if (controller.extendedGamepad == nil && controller.gamepad == nil)
|
||||
return;
|
||||
int port = std::min((int)controllers.size(), 3);
|
||||
controllers[controller] = std::make_shared<IOSGamePad>(port, controller);
|
||||
GamepadDevice::Register(controllers[controller]);
|
||||
}
|
||||
|
||||
static void removeController(GCController *controller)
|
||||
{
|
||||
auto it = controllers.find(controller);
|
||||
if (it == controllers.end())
|
||||
return;
|
||||
GamepadDevice::Unregister(it->second);
|
||||
controllers.erase(it);
|
||||
}
|
||||
|
||||
private:
|
||||
GCController *gcController = nullptr;
|
||||
static std::map<GCController *, std::shared_ptr<IOSGamePad>> controllers;
|
||||
};
|
||||
|
||||
class IOSMouse : public SystemMouse
|
||||
{
|
||||
public:
|
||||
IOSMouse() : SystemMouse("iOS")
|
||||
{
|
||||
_unique_id = "ios_mouse";
|
||||
loadMapping();
|
||||
}
|
||||
};
|
|
@ -1,8 +1,24 @@
|
|||
/*
|
||||
Copyright (c) 2014 Lounge Katt. All rights reserved.
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
//
|
||||
// Created by Lounge Katt on 2/6/14.
|
||||
// Copyright (c) 2014 Lounge Katt. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
|
|
@ -70,5 +70,59 @@
|
|||
<string>Flycast requires microphone access to emulate the Dreamcast microphone</string>
|
||||
<key>UISupportsDocumentBrowser</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Disk image</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.flyinghead.flycast.disk-image</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Zip archive</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.pkware.zip-archive</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Disk image</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.flyinghead.flycast.disk-image</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>chd</string>
|
||||
<string>gdi</string>
|
||||
<string>cue</string>
|
||||
<string>cdi</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -405,6 +405,7 @@
|
|||
AE32949826BAEF4300B2F53C /* FlycastStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AE32949726BAEF4300B2F53C /* FlycastStoryboard.storyboard */; };
|
||||
AE49B4D826C1BAC300FA182B /* unwind_info.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE49B4D726C1BAC300FA182B /* unwind_info.cpp */; };
|
||||
AE6AA6FF26BF2E91004B9D5F /* AltKit in Frameworks */ = {isa = PBXBuildFile; productRef = AE6AA6FE26BF2E91004B9D5F /* AltKit */; };
|
||||
AE837B8026D164BD00BC7A2E /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AE837B7F26D164BD00BC7A2E /* AVFoundation.framework */; };
|
||||
AE9F17E326BC800F00B8C6D0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AE9F17E226BC800F00B8C6D0 /* LaunchScreen.storyboard */; };
|
||||
EBDF375A1BB96ECD001191B5 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EBDF37591BB96ECD001191B5 /* AudioToolbox.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
@ -1414,12 +1415,13 @@
|
|||
AE32949226BAE58F00B2F53C /* FlycastViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = FlycastViewController.mm; path = emulator/FlycastViewController.mm; sourceTree = "<group>"; };
|
||||
AE32949426BAE5B900B2F53C /* FlycastViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FlycastViewController.h; path = emulator/FlycastViewController.h; sourceTree = "<group>"; };
|
||||
AE32949726BAEF4300B2F53C /* FlycastStoryboard.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = FlycastStoryboard.storyboard; sourceTree = "<group>"; };
|
||||
AE32949926BAFA3000B2F53C /* ios_mouse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ios_mouse.h; sourceTree = "<group>"; };
|
||||
AE32949926BAFA3000B2F53C /* ios_gamepad.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ios_gamepad.h; sourceTree = "<group>"; };
|
||||
AE49B4D726C1BAC300FA182B /* unwind_info.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = unwind_info.cpp; sourceTree = "<group>"; };
|
||||
AE49B51626C2E8FC00FA182B /* xbyak_mnemonic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbyak_mnemonic.h; sourceTree = "<group>"; };
|
||||
AE49B51726C2E8FC00FA182B /* xbyak_bin2hex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbyak_bin2hex.h; sourceTree = "<group>"; };
|
||||
AE49B51826C2E8FC00FA182B /* xbyak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbyak.h; sourceTree = "<group>"; };
|
||||
AE49B51926C2E8FC00FA182B /* xbyak_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbyak_util.h; sourceTree = "<group>"; };
|
||||
AE837B7F26D164BD00BC7A2E /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
AE9F17E226BC800F00B8C6D0 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
EBDF37571BB96E75001191B5 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
|
||||
EBDF37591BB96ECD001191B5 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
|
||||
|
@ -1432,6 +1434,7 @@
|
|||
files = (
|
||||
EBDF375A1BB96ECD001191B5 /* AudioToolbox.framework in Frameworks */,
|
||||
87D92F4E1B7A1B5700D8FD9E /* GameController.framework in Frameworks */,
|
||||
AE837B8026D164BD00BC7A2E /* AVFoundation.framework in Frameworks */,
|
||||
AE6AA6FF26BF2E91004B9D5F /* AltKit in Frameworks */,
|
||||
87C4AA561A4414070048DBF4 /* AssetsLibrary.framework in Frameworks */,
|
||||
87078A8F18A47FE90034C7A0 /* OpenGLES.framework in Frameworks */,
|
||||
|
@ -1466,6 +1469,7 @@
|
|||
87078A8518A47FE90034C7A0 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AE837B7F26D164BD00BC7A2E /* AVFoundation.framework */,
|
||||
EBDF37591BB96ECD001191B5 /* AudioToolbox.framework */,
|
||||
EBDF37571BB96E75001191B5 /* AudioUnit.framework */,
|
||||
87D92F4D1B7A1B5700D8FD9E /* GameController.framework */,
|
||||
|
@ -1484,7 +1488,7 @@
|
|||
children = (
|
||||
878B0CFB1B8BB5B400A8D1C5 /* Images.xcassets */,
|
||||
87D92EA71B7839E600D8FD9E /* ios_main.mm */,
|
||||
AE32949926BAFA3000B2F53C /* ios_mouse.h */,
|
||||
AE32949926BAFA3000B2F53C /* ios_gamepad.h */,
|
||||
AE32949726BAEF4300B2F53C /* FlycastStoryboard.storyboard */,
|
||||
AE9F17E226BC800F00B8C6D0 /* LaunchScreen.storyboard */,
|
||||
87078A9918A47FE90034C7A0 /* AppDelegate.h */,
|
||||
|
@ -1781,15 +1785,15 @@
|
|||
AE3258B926BAA8E100B2F53C /* maple */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AE3258BF26BAA8E100B2F53C /* maple_cfg.cpp */,
|
||||
AE3258BE26BAA8E100B2F53C /* maple_cfg.h */,
|
||||
AE3258C226BAA8E100B2F53C /* maple_devs.cpp */,
|
||||
AE3258C026BAA8E100B2F53C /* maple_devs.h */,
|
||||
AE3258BD26BAA8E100B2F53C /* maple_helper.cpp */,
|
||||
AE3258BA26BAA8E100B2F53C /* maple_helper.h */,
|
||||
AE3258BB26BAA8E100B2F53C /* maple_if.cpp */,
|
||||
AE3258BC26BAA8E100B2F53C /* maple_jvs.cpp */,
|
||||
AE3258BD26BAA8E100B2F53C /* maple_helper.cpp */,
|
||||
AE3258BE26BAA8E100B2F53C /* maple_cfg.h */,
|
||||
AE3258BF26BAA8E100B2F53C /* maple_cfg.cpp */,
|
||||
AE3258C026BAA8E100B2F53C /* maple_devs.h */,
|
||||
AE3258C126BAA8E100B2F53C /* maple_if.h */,
|
||||
AE3258C226BAA8E100B2F53C /* maple_devs.cpp */,
|
||||
AE3258BC26BAA8E100B2F53C /* maple_jvs.cpp */,
|
||||
);
|
||||
path = maple;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2966,22 +2970,22 @@
|
|||
AE32797E26BAA90900B2F53C /* oslib */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AE32798E26BAA90A00B2F53C /* audiobackend_alsa.cpp */,
|
||||
AE32798326BAA90A00B2F53C /* audiobackend_coreaudio.cpp */,
|
||||
AE32798C26BAA90A00B2F53C /* audiobackend_directsound.cpp */,
|
||||
AE32798B26BAA90A00B2F53C /* audiobackend_libao.cpp */,
|
||||
AE32798D26BAA90A00B2F53C /* audiobackend_null.cpp */,
|
||||
AE32798826BAA90A00B2F53C /* audiobackend_oboe.cpp */,
|
||||
AE32798126BAA90900B2F53C /* audiobackend_omx.cpp */,
|
||||
AE32798A26BAA90A00B2F53C /* audiobackend_oss.cpp */,
|
||||
AE32797F26BAA90900B2F53C /* audiobackend_pulseaudio.cpp */,
|
||||
AE32798026BAA90900B2F53C /* audiobackend_sdl2.cpp */,
|
||||
AE32798126BAA90900B2F53C /* audiobackend_omx.cpp */,
|
||||
AE32798226BAA90A00B2F53C /* oslib.h */,
|
||||
AE32798326BAA90A00B2F53C /* audiobackend_coreaudio.cpp */,
|
||||
AE32798426BAA90A00B2F53C /* audiostream.cpp */,
|
||||
AE32798526BAA90A00B2F53C /* host_context.h */,
|
||||
AE32798626BAA90A00B2F53C /* audiostream.h */,
|
||||
AE32798726BAA90A00B2F53C /* oslib.cpp */,
|
||||
AE32798826BAA90A00B2F53C /* audiobackend_oboe.cpp */,
|
||||
AE32798926BAA90A00B2F53C /* directory.h */,
|
||||
AE32798A26BAA90A00B2F53C /* audiobackend_oss.cpp */,
|
||||
AE32798B26BAA90A00B2F53C /* audiobackend_libao.cpp */,
|
||||
AE32798C26BAA90A00B2F53C /* audiobackend_directsound.cpp */,
|
||||
AE32798D26BAA90A00B2F53C /* audiobackend_null.cpp */,
|
||||
AE32798E26BAA90A00B2F53C /* audiobackend_alsa.cpp */,
|
||||
AE32798526BAA90A00B2F53C /* host_context.h */,
|
||||
AE32798726BAA90A00B2F53C /* oslib.cpp */,
|
||||
AE32798226BAA90A00B2F53C /* oslib.h */,
|
||||
);
|
||||
path = oslib;
|
||||
sourceTree = "<group>";
|
||||
|
|