2015-04-05 05:36:53 +02:00
/ * RetroArch - A frontend for libretro .
* Copyright ( C ) 2013 -2014 - Jason Fetters
2017-01-22 13:40:32 +01:00
* Copyright ( C ) 2011 -2017 - Daniel De Matteis
2019-02-03 15:49:35 -08:00
*
2015-04-05 05:36:53 +02:00
* RetroArch 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 Found -
* ation , either version 3 of the License , or ( at your option ) any later version .
*
* RetroArch 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 RetroArch .
* If not , see < http : // www . gnu . org / licenses / > .
* /
# import < AvailabilityMacros . h >
# include < sys / stat . h >
2021-01-22 22:20:38 +01:00
2015-04-20 12:52:16 +02:00
# include "cocoa_common.h"
2021-01-19 04:38:07 +01:00
# include "apple_platform.h"
2016-06-07 16:47:48 +02:00
# include "../ui_cocoa.h"
2016-09-08 11:59:44 +02:00
2021-01-22 22:20:38 +01:00
# ifdef HAVE_COCOATOUCH
# import "../../../pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.h"
# import "WebServer.h"
2022-03-07 08:09:49 -10:00
# ifdef HAVE_IOS _SWIFT
2022-02-21 21:21:34 -10:00
# import "RetroArch-Swift.h"
# endif
2021-01-22 22:20:38 +01:00
# endif
2016-09-08 11:59:44 +02:00
2021-01-18 21:21:17 +01:00
# include "../../../configuration.h"
# include "../../../retroarch.h"
2016-06-08 07:41:59 +02:00
# include "../../../verbosity.h"
2015-04-05 05:36:53 +02:00
2022-02-21 21:21:34 -10:00
# include "../../input/drivers/cocoa_input.h"
# include "../../input/drivers_keyboard/keyboard_event_apple.h"
2022-07-23 09:24:46 +01:00
# if defined ( HAVE_COCOA _METAL ) || defined ( HAVE_COCOATOUCH )
id < ApplePlatform > apple_platform ;
# else
id apple_platform ;
# endif
2022-02-21 21:21:34 -10:00
2021-01-18 19:17:12 +01:00
static CocoaView * g_instance ;
2019-11-20 06:25:40 +01:00
# ifdef HAVE_COCOATOUCH
2021-01-18 03:03:35 +01:00
void * glkitview_init ( void ) ;
2022-02-21 21:21:34 -10:00
@ interface CocoaView ( ) < GCDWebUploaderDelegate , UIGestureRecognizerDelegate
# ifdef HAVE_IOS _TOUCHMOUSE
2022-03-07 08:09:49 -10:00
, EmulatorTouchMouseHandlerDelegate
2022-02-21 21:21:34 -10:00
# endif
2022-03-07 08:09:49 -10:00
>
2022-02-21 21:21:34 -10:00
2019-01-26 13:18:32 -10:00
@ end
# endif
2015-04-20 20:39:39 +02:00
@ implementation CocoaView
2016-10-05 02:00:11 +02:00
2021-01-24 03:56:05 +01:00
# if defined ( OSX )
2020-12-28 07:52:08 +01:00
# ifdef HAVE_COCOA _METAL
2020-09-15 19:48:43 +02:00
- ( BOOL ) layer : ( CALayer * ) layer shouldInheritContentsScale : ( CGFloat ) newScale fromWindow : ( NSWindow * ) window { return YES ; }
2019-02-09 21:10:28 +01:00
# endif
2020-09-15 20:50:20 +02:00
- ( void ) scrollWheel : ( NSEvent * ) theEvent { }
2016-10-05 02:00:11 +02:00
# endif
2015-04-20 20:39:39 +02:00
+ ( CocoaView * ) get
2015-04-05 05:36:53 +02:00
{
2019-09-22 11:19:54 +02:00
CocoaView * view = ( BRIDGE CocoaView * ) nsview_get _ptr ( ) ;
if ( ! view )
{
2019-11-20 06:25:40 +01:00
view = [ CocoaView new ] ;
nsview_set _ptr ( view ) ;
2019-09-22 11:19:54 +02:00
}
return view ;
2015-04-05 05:36:53 +02:00
}
- ( id ) init
{
self = [ super init ] ;
2019-02-03 15:49:35 -08:00
2021-01-24 03:56:05 +01:00
# if defined ( OSX )
[ self setAutoresizingMask : NSViewWidthSizable | NSViewHeightSizable ] ;
NSArray * array = [ NSArray arrayWithObjects : NSColorPboardType , NSFilenamesPboardType , nil ] ;
[ self registerForDraggedTypes : array ] ;
# endif
# if defined ( HAVE_COCOA )
ui_window _cocoa _t cocoa_view ;
cocoa_view . data = ( CocoaView * ) self ;
2021-01-19 04:51:38 +01:00
# endif
2021-01-24 03:56:05 +01:00
# if defined ( OSX )
video_driver _display _type _set ( RARCH_DISPLAY _OSX ) ;
video_driver _display _set ( 0 ) ;
video_driver _display _userdata _set ( ( uintptr_t ) self ) ;
# endif
2019-02-03 15:49:35 -08:00
2023-05-09 18:32:55 -04:00
# if TARGET_OS _TV
/ * This causes all inputs to be handled by both mfi and uikit .
*
* For "extended gamepads" the only button we want to handle is ' cancel '
* ( buttonB ) , and only when the cancel button wouldn ' t do anything .
* /
self . controllerUserInteractionEnabled = YES ;
# endif
2021-01-24 03:56:05 +01:00
return self ;
}
2016-06-03 19:21:22 +02:00
2023-05-09 18:32:55 -04:00
# if TARGET_OS _TV
- ( bool ) menuIsAtTop
{
struct menu_state * menu_st = menu_state _get _ptr ( ) ;
if ( ! ( menu_st -> flags & MENU_ST _FLAG _ALIVE ) ) // content
return false ;
if ( menu_st -> flags & MENU_ST _FLAG _INP _DLG _KB _DISPLAY ) // search
return false ;
if ( menu_st -> selection_ptr ! = 0 ) // not the first item
return false ;
if ( menu_st -> entries . list -> menu_stack [ 0 ] -> size ! = 1 ) // submenu
return false ;
if ( ! string_is _equal ( menu_st -> entries . list -> menu_stack [ 0 ] -> list -> label , // not on the main menu
msg_hash _to _str ( MENU_ENUM _LABEL _MAIN _MENU ) ) )
return false ;
return true ;
}
- ( bool ) didMicroGamepadPress : ( UIPressType ) type
{
if ( type ! = UIPressTypeMenu &&
type ! = UIPressTypeSelect &&
type ! = UIPressTypePlayPause )
return false ;
NSArray < GCController * > * controllers = [ GCController controllers ] ;
if ( [ controllers count ] = = 1 )
return ! controllers [ 0 ] . extendedGamepad ;
bool microPress = false ;
bool extendedPress = false ;
for ( GCController * controller in [ GCController controllers ] ) {
// the microGamepad does not always know if Menu has been pressed ,
// so we have to check all the extended gamepads as well
if ( controller . extendedGamepad )
{
if ( type = = UIPressTypeMenu )
extendedPress | = controller . extendedGamepad . buttonB . pressed ;
else if ( type = = UIPressTypeSelect )
extendedPress | = controller . extendedGamepad . buttonA . pressed ;
else if ( type = = UIPressTypePlayPause )
extendedPress | = controller . extendedGamepad . buttonX . pressed ;
}
else
{
if ( type = = UIPressTypeSelect )
extendedPress | = controller . extendedGamepad . buttonA . pressed ;
else if ( type = = UIPressTypePlayPause )
extendedPress | = controller . extendedGamepad . buttonX . pressed ;
else if ( @ available ( tvOS 13 , * ) ) {
if ( type = = UIPressTypeMenu )
extendedPress | = controller . microGamepad . buttonMenu . pressed ||
controller . microGamepad . buttonMenu . isPressed ;
}
}
}
return microPress || ! extendedPress ;
}
- ( void ) pressesBegan : ( NSSet < UIPress * > * ) presses
withEvent : ( UIPressesEvent * ) event
{
for ( UIPress * press in presses ) {
switch ( press . type )
{
case UIPressTypePlayPause :
if ( [ self didMicroGamepadPress : press . type ] )
apple_direct _input _keyboard _event ( true , RETROK_s , ' s ' , 0 , RETRO_DEVICE _KEYBOARD ) ;
break ;
case UIPressTypeSelect :
if ( [ self didMicroGamepadPress : press . type ] )
apple_direct _input _keyboard _event ( true , RETROK_z , ' z ' , 0 , RETRO_DEVICE _KEYBOARD ) ;
break ;
case UIPressTypeMenu :
if ( [ self menuIsAtTop ] )
{
// if we ' re at the top it doesn ' t matter who pressed it , we want to leave
[ super pressesBegan : presses withEvent : event ] ;
}
else if ( [ self didMicroGamepadPress : press . type ] )
apple_direct _input _keyboard _event ( true , RETROK_x , 0 , 0 , RETRO_DEVICE _KEYBOARD ) ;
break ;
default :
break ;
}
}
}
- ( void ) pressesEnded : ( NSSet < UIPress * > * ) presses withEvent : ( UIPressesEvent * ) event
{
for ( UIPress * press in presses ) {
switch ( press . type )
{
case UIPressTypePlayPause :
apple_direct _input _keyboard _event ( false , RETROK_s , ' s ' , 0 , RETRO_DEVICE _KEYBOARD ) ;
break ;
case UIPressTypeSelect :
apple_direct _input _keyboard _event ( false , RETROK_z , ' z ' , 0 , RETRO_DEVICE _KEYBOARD ) ;
break ;
case UIPressTypeMenu :
dispatch_after ( dispatch_time ( DISPATCH_TIME _NOW , NSEC_PER _MSEC ) , dispatch_get _main _queue ( ) , ^ {
apple_direct _input _keyboard _event ( false , RETROK_x , 0 , 0 , RETRO_DEVICE _KEYBOARD ) ;
} ) ;
break ;
default :
break ;
}
}
}
- ( void ) touchesBegan : ( NSSet < UITouch * > * ) touches withEvent : ( UIEvent * ) event
{
}
- ( void ) touchesMoved : ( NSSet < UITouch * > * ) touches withEvent : ( UIEvent * ) event
{
}
- ( void ) touchesEnded : ( NSSet < UITouch * > * ) touches withEvent : ( UIEvent * ) event
{
}
- ( void ) touchesEstimatedPropertiesUpdated : ( NSSet < UITouch * > * ) touches
{
}
- ( void ) touchesCancelled : ( NSSet < UITouch * > * ) touches withEvent : ( UIEvent * ) event
{
}
- ( void ) handleSiriSwipe : ( id ) sender
{
UISwipeGestureRecognizer * gestureRecognizer = ( UISwipeGestureRecognizer * ) sender ;
unsigned code ;
switch ( gestureRecognizer . direction )
{
case UISwipeGestureRecognizerDirectionUp : code = RETROK_UP ; break ;
case UISwipeGestureRecognizerDirectionDown : code = RETROK_DOWN ; break ;
case UISwipeGestureRecognizerDirectionLeft : code = RETROK_LEFT ; break ;
case UISwipeGestureRecognizerDirectionRight : code = RETROK_RIGHT ; break ;
}
apple_direct _input _keyboard _event ( true , code , 0 , 0 , RETRO_DEVICE _KEYBOARD ) ;
dispatch_after ( dispatch_time ( DISPATCH_TIME _NOW , 1 * NSEC_PER _MSEC ) , dispatch_get _main _queue ( ) , ^ {
apple_direct _input _keyboard _event ( false , code , 0 , 0 , RETRO_DEVICE _KEYBOARD ) ;
} ) ;
}
# endif
2021-01-24 03:56:05 +01:00
# if defined ( OSX )
- ( void ) setFrame : ( NSRect ) frameRect
{
[ super setFrame : frameRect ] ;
/ * forward declarations * /
# if defined ( HAVE_OPENGL )
void cocoa_gl _gfx _ctx _update ( void ) ;
cocoa_gl _gfx _ctx _update ( ) ;
2021-01-24 03:53:10 +01:00
# endif
2021-01-24 03:56:05 +01:00
}
2019-02-03 15:49:35 -08:00
2021-01-24 03:56:05 +01:00
/ * Stop the annoying sound when pressing a key . * /
- ( BOOL ) acceptsFirstResponder { return YES ; }
- ( BOOL ) isFlipped { return YES ; }
- ( void ) keyDown : ( NSEvent * ) theEvent { }
- ( NSDragOperation ) draggingEntered : ( id < NSDraggingInfo > ) sender
{
NSDragOperation sourceDragMask = [ sender draggingSourceOperationMask ] ;
NSPasteboard * pboard = [ sender draggingPasteboard ] ;
if ( [ [ pboard types ] containsObject : NSFilenamesPboardType ] )
{
if ( sourceDragMask & NSDragOperationCopy )
return NSDragOperationCopy ;
}
return NSDragOperationNone ;
2016-06-03 19:21:22 +02:00
}
2021-01-24 03:56:05 +01:00
- ( BOOL ) performDragOperation : ( id < NSDraggingInfo > ) sender
{
2021-11-05 22:42:09 +01:00
# if 0
2021-01-24 03:56:05 +01:00
NSPasteboard * pboard = [ sender draggingPasteboard ] ;
if ( [ [ pboard types ] containsObject : NSURLPboardType ] )
{
NSURL * fileURL = [ NSURL URLFromPasteboard : pboard ] ;
NSString * s = [ fileURL path ] ;
}
2021-11-05 22:42:09 +01:00
# endif
2021-01-24 03:56:05 +01:00
return YES ;
}
- ( void ) draggingExited : ( id < NSDraggingInfo > ) sender { [ self setNeedsDisplay : YES ] ; }
# elif TARGET_OS _IOS
2020-09-15 19:48:43 +02:00
- ( void ) showNativeMenu
{
2019-10-06 09:41:53 -10:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
2020-07-20 15:38:24 -10:00
command_event ( CMD_EVENT _MENU _TOGGLE , NULL ) ;
2019-10-06 09:41:53 -10:00
} ) ;
2019-06-14 08:35:33 -10:00
}
2022-02-21 21:21:34 -10:00
# ifdef HAVE_IOS _CUSTOMKEYBOARD
2022-03-07 08:09:49 -10:00
- ( void ) toggleCustomKeyboardUsingSwipe : ( id ) sender {
UISwipeGestureRecognizer * gestureRecognizer = ( UISwipeGestureRecognizer * ) sender ;
[ self . keyboardController . view setHidden : gestureRecognizer . direction = = UISwipeGestureRecognizerDirectionDown ] ;
2022-02-21 21:21:34 -10:00
[ self updateOverlayAndFocus ] ;
}
2022-03-07 08:09:49 -10:00
- ( void ) toggleCustomKeyboard {
[ self . keyboardController . view setHidden : ! self . keyboardController . view . isHidden ] ;
2022-02-21 21:21:34 -10:00
[ self updateOverlayAndFocus ] ;
}
2022-03-07 08:09:49 -10:00
# endif
2022-02-21 21:21:34 -10:00
- ( void ) updateOverlayAndFocus
{
# ifdef HAVE_IOS _CUSTOMKEYBOARD
int cmdData = self . keyboardController . view . isHidden ? 0 : 1 ;
command_event ( CMD_EVENT _GAME _FOCUS _TOGGLE , & cmdData ) ;
2022-10-26 17:36:58 +02:00
if ( self . keyboardController . view . isHidden )
2022-02-21 21:21:34 -10:00
command_event ( CMD_EVENT _OVERLAY _INIT , NULL ) ;
2022-10-26 17:36:58 +02:00
else
2022-02-21 21:21:34 -10:00
command_event ( CMD_EVENT _OVERLAY _DEINIT , NULL ) ;
# endif
}
2020-09-15 19:48:43 +02:00
- ( BOOL ) prefersHomeIndicatorAutoHidden { return YES ; }
- ( void ) viewWillTransitionToSize : ( CGSize ) size withTransitionCoordinator : ( id < UIViewControllerTransitionCoordinator > ) coordinator
2018-11-27 09:29:30 -10:00
{
[ super viewWillTransitionToSize : size withTransitionCoordinator : coordinator ] ;
2020-09-16 10:09:03 +02:00
if ( @ available ( iOS 11 , * ) )
{
2018-11-27 09:29:30 -10:00
[ coordinator animateAlongsideTransition : ^ ( id < UIViewControllerTransitionCoordinatorContext > _Nonnull context ) {
[ self adjustViewFrameForSafeArea ] ;
} completion : ^ ( id < UIViewControllerTransitionCoordinatorContext > _Nonnull context ) {
} ] ;
}
}
2020-09-15 19:48:43 +02:00
- ( void ) adjustViewFrameForSafeArea
{
/ * This is for adjusting the view frame to account for
* the notch in iPhone X phones * /
if ( @ available ( iOS 11 , * ) )
{
2021-01-18 15:41:30 +01:00
RAScreen * screen = ( BRIDGE RAScreen * ) cocoa_screen _get _chosen ( ) ;
2020-09-15 19:48:43 +02:00
CGRect screenSize = [ screen bounds ] ;
UIEdgeInsets inset = [ [ UIApplication sharedApplication ] delegate ] . window . safeAreaInsets ;
UIInterfaceOrientation orientation = [ [ UIApplication sharedApplication ] statusBarOrientation ] ;
switch ( orientation )
{
case UIInterfaceOrientationPortrait :
self . view . frame = CGRectMake ( screenSize . origin . x ,
screenSize . origin . y + inset . top ,
screenSize . size . width ,
screenSize . size . height - inset . top ) ;
break ;
case UIInterfaceOrientationLandscapeLeft :
self . view . frame = CGRectMake ( screenSize . origin . x + inset . right ,
screenSize . origin . y ,
screenSize . size . width - inset . right * 2 ,
screenSize . size . height ) ;
break ;
case UIInterfaceOrientationLandscapeRight :
self . view . frame = CGRectMake ( screenSize . origin . x + inset . left ,
screenSize . origin . y ,
screenSize . size . width - inset . left * 2 ,
screenSize . size . height ) ;
break ;
default :
self . view . frame = screenSize ;
break ;
}
}
2018-11-27 09:29:30 -10:00
}
2019-02-09 21:10:28 +01:00
2015-04-05 05:36:53 +02:00
- ( void ) viewWillLayoutSubviews
{
2018-11-27 09:29:30 -10:00
[ self adjustViewFrameForSafeArea ] ;
2022-02-21 21:21:34 -10:00
# ifdef HAVE_IOS _CUSTOMKEYBOARD
[ self . view bringSubviewToFront : self . keyboardController . view ] ;
# endif
2022-03-07 08:09:49 -10:00
# if HAVE_IOS _SWIFT
[ self . view bringSubviewToFront : self . helperBarView ] ;
# endif
2015-04-05 05:36:53 +02:00
}
/ * NOTE : This version runs on iOS6 + . * /
2022-02-21 21:21:34 -10:00
- ( UIInterfaceOrientationMask ) supportedInterfaceOrientations
2015-04-05 05:36:53 +02:00
{
2022-02-21 21:21:34 -10:00
return ( UIInterfaceOrientationMask ) apple_frontend _settings . orientation_flags ;
2015-04-05 05:36:53 +02:00
}
/ * NOTE : This version runs on iOS2 - iOS5 , but not iOS6 + . * /
- ( BOOL ) shouldAutorotateToInterfaceOrientation : ( UIInterfaceOrientation ) interfaceOrientation
{
2020-09-17 21:57:35 +02:00
unsigned orientation_flags = apple_frontend _settings . orientation_flags ;
2015-04-05 05:36:53 +02:00
switch ( interfaceOrientation )
{
case UIInterfaceOrientationPortrait :
2020-09-17 21:57:35 +02:00
return ( orientation_flags
2020-09-15 19:48:43 +02:00
& UIInterfaceOrientationMaskPortrait ) ;
2015-04-05 05:36:53 +02:00
case UIInterfaceOrientationPortraitUpsideDown :
2020-09-17 21:57:35 +02:00
return ( orientation_flags
2020-09-15 19:48:43 +02:00
& UIInterfaceOrientationMaskPortraitUpsideDown ) ;
2015-04-05 05:36:53 +02:00
case UIInterfaceOrientationLandscapeLeft :
2020-09-17 21:57:35 +02:00
return ( orientation_flags
2020-09-15 19:48:43 +02:00
& UIInterfaceOrientationMaskLandscapeLeft ) ;
2015-04-05 05:36:53 +02:00
case UIInterfaceOrientationLandscapeRight :
2020-09-17 21:57:35 +02:00
return ( orientation_flags
2020-09-15 19:48:43 +02:00
& UIInterfaceOrientationMaskLandscapeRight ) ;
2015-04-05 05:36:53 +02:00
default :
2020-09-17 21:57:35 +02:00
break ;
2015-04-05 05:36:53 +02:00
}
2019-02-03 15:49:35 -08:00
2020-09-17 21:57:35 +02:00
return ( orientation_flags
& UIInterfaceOrientationMaskAll ) ;
2015-04-05 05:36:53 +02:00
}
# endif
2021-01-24 03:56:05 +01:00
# ifdef HAVE_COCOATOUCH
2022-02-21 21:21:34 -10:00
# pragma mark - UIViewController Lifecycle
- ( void ) loadView {
# if defined ( HAVE_COCOA _METAL )
self . view = [ UIView new ] ;
# else
self . view = ( BRIDGE GLKView * ) glkitview_init ( ) ;
# endif
}
- ( void ) viewDidLoad {
[ super viewDidLoad ] ;
# if TARGET_OS _IOS
UISwipeGestureRecognizer * swipe = [ [ UISwipeGestureRecognizer alloc ] initWithTarget : self action : @ selector ( showNativeMenu ) ] ;
2022-11-23 05:08:15 +01:00
swipe . numberOfTouchesRequired = 4 ;
swipe . delegate = self ;
swipe . direction = UISwipeGestureRecognizerDirectionDown ;
2022-02-21 21:21:34 -10:00
[ self . view addGestureRecognizer : swipe ] ;
# ifdef HAVE_IOS _TOUCHMOUSE
2022-03-07 08:09:49 -10:00
[ self setupMouseSupport ] ;
2022-02-21 21:21:34 -10:00
# endif
# ifdef HAVE_IOS _CUSTOMKEYBOARD
[ self setupEmulatorKeyboard ] ;
2022-03-07 08:09:49 -10:00
UISwipeGestureRecognizer * showKeyboardSwipe = [ [ UISwipeGestureRecognizer alloc ] initWithTarget : self action : @ selector ( toggleCustomKeyboardUsingSwipe : ) ] ;
2022-11-23 05:08:15 +01:00
showKeyboardSwipe . numberOfTouchesRequired = 3 ;
showKeyboardSwipe . direction = UISwipeGestureRecognizerDirectionUp ;
showKeyboardSwipe . delegate = self ;
2022-02-21 21:21:34 -10:00
[ self . view addGestureRecognizer : showKeyboardSwipe ] ;
2022-03-07 08:09:49 -10:00
UISwipeGestureRecognizer * hideKeyboardSwipe = [ [ UISwipeGestureRecognizer alloc ] initWithTarget : self action : @ selector ( toggleCustomKeyboardUsingSwipe : ) ] ;
2022-11-23 05:08:15 +01:00
hideKeyboardSwipe . numberOfTouchesRequired = 3 ;
hideKeyboardSwipe . direction = UISwipeGestureRecognizerDirectionDown ;
hideKeyboardSwipe . delegate = self ;
2022-02-21 21:21:34 -10:00
[ self . view addGestureRecognizer : hideKeyboardSwipe ] ;
# endif
2022-03-07 08:09:49 -10:00
# if __IPHONE _OS _VERSION _MIN _REQUIRED >= 130000
[ self setupHelperBar ] ;
# endif
2023-05-09 18:32:55 -04:00
# elif TARGET_OS _TV
UISwipeGestureRecognizer * siriSwipeUp = [ [ UISwipeGestureRecognizer alloc ] initWithTarget : self action : @ selector ( handleSiriSwipe : ) ] ;
siriSwipeUp . direction = UISwipeGestureRecognizerDirectionUp ;
siriSwipeUp . delegate = self ;
[ self . view addGestureRecognizer : siriSwipeUp ] ;
UISwipeGestureRecognizer * siriSwipeDown = [ [ UISwipeGestureRecognizer alloc ] initWithTarget : self action : @ selector ( handleSiriSwipe : ) ] ;
siriSwipeDown . direction = UISwipeGestureRecognizerDirectionDown ;
siriSwipeDown . delegate = self ;
[ self . view addGestureRecognizer : siriSwipeDown ] ;
UISwipeGestureRecognizer * siriSwipeLeft = [ [ UISwipeGestureRecognizer alloc ] initWithTarget : self action : @ selector ( handleSiriSwipe : ) ] ;
siriSwipeLeft . direction = UISwipeGestureRecognizerDirectionLeft ;
siriSwipeLeft . delegate = self ;
[ self . view addGestureRecognizer : siriSwipeLeft ] ;
UISwipeGestureRecognizer * siriSwipeRight = [ [ UISwipeGestureRecognizer alloc ] initWithTarget : self action : @ selector ( handleSiriSwipe : ) ] ;
siriSwipeRight . direction = UISwipeGestureRecognizerDirectionRight ;
siriSwipeRight . delegate = self ;
[ self . view addGestureRecognizer : siriSwipeRight ] ;
2022-02-21 21:21:34 -10:00
# endif
}
- ( BOOL ) gestureRecognizer : ( UIGestureRecognizer * ) gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer : ( UIGestureRecognizer * ) otherGestureRecognizer {
return YES ;
}
2019-01-26 13:18:32 -10:00
- ( void ) viewDidAppear : ( BOOL ) animated
{
# if TARGET_OS _IOS
2020-09-16 10:09:03 +02:00
if ( @ available ( iOS 11.0 , * ) )
2019-01-26 13:18:32 -10:00
[ self setNeedsUpdateOfHomeIndicatorAutoHidden ] ;
# endif
}
2020-09-16 10:09:03 +02:00
- ( void ) viewWillAppear : ( BOOL ) animated
{
2019-01-26 13:18:32 -10:00
[ super viewWillAppear : animated ] ;
# if TARGET_OS _TV
[ [ WebServer sharedInstance ] startUploader ] ;
[ WebServer sharedInstance ] . webUploader . delegate = self ;
# endif
}
2022-02-21 21:21:34 -10:00
# if TARGET_OS _IOS && HAVE_IOS _TOUCHMOUSE
# pragma mark EmulatorTouchMouseHandlerDelegate
2022-03-07 08:09:49 -10:00
2022-10-26 17:36:58 +02:00
- ( void ) handleMouseClickWithIsLeftClick : ( BOOL ) isLeftClick isPressed : ( BOOL ) isPressed
{
2022-02-21 21:21:34 -10:00
cocoa_input _data _t * apple = ( cocoa_input _data _t * ) input_state _get _ptr ( ) -> current_data ;
2022-10-26 17:36:58 +02:00
if ( ! apple )
2022-02-21 21:21:34 -10:00
return ;
NSUInteger buttonIndex = isLeftClick ? 0 : 1 ;
2022-10-26 17:36:58 +02:00
if ( isPressed )
2022-02-21 21:21:34 -10:00
apple -> mouse_buttons | = ( 1 < < buttonIndex ) ;
2022-10-26 17:36:58 +02:00
else
2022-02-21 21:21:34 -10:00
apple -> mouse_buttons & = ~ ( 1 < < buttonIndex ) ;
}
2022-10-26 17:36:58 +02:00
- ( void ) handleMouseMoveWithX : ( CGFloat ) x y : ( CGFloat ) y
{
2022-11-23 05:08:15 +01:00
cocoa_input _data _t * apple = ( cocoa_input _data _t * ) input_state _get _ptr ( ) -> current_data ;
if ( ! apple )
return ;
apple -> mouse_rel _x = ( int16_t ) x ;
apple -> mouse_rel _y = ( int16_t ) y ;
/ * use location position to track pointer * /
if ( @ available ( iOS 13.4 , * ) )
{
2022-11-18 13:45:36 -05:00
apple -> window_pos _x = 0 ;
apple -> window_pos _y = 0 ;
}
2022-02-21 21:21:34 -10:00
}
2022-11-18 13:45:36 -05:00
- ( void ) handlePointerMoveWithX : ( CGFloat ) x y : ( CGFloat ) y
{
cocoa_input _data _t * apple = ( cocoa_input _data _t * )
input_state _get _ptr ( ) -> current_data ;
if ( ! apple )
return ;
apple -> window_pos _x = ( int16_t ) x ;
apple -> window_pos _y = ( int16_t ) y ;
}
2022-02-21 21:21:34 -10:00
# endif
2019-01-26 13:18:32 -10:00
# pragma mark GCDWebServerDelegate
2020-09-16 10:09:03 +02:00
- ( void ) webServerDidCompleteBonjourRegistration : ( GCDWebServer * ) server
{
2019-01-26 13:18:32 -10:00
NSMutableString * servers = [ [ NSMutableString alloc ] init ] ;
2020-09-15 19:48:43 +02:00
if ( server . serverURL ! = nil )
2019-01-26 13:18:32 -10:00
[ servers appendString : [ NSString stringWithFormat : @ "%@" , server . serverURL ] ] ;
2020-09-15 19:48:43 +02:00
if ( servers . length > 0 )
2019-01-26 13:18:32 -10:00
[ servers appendString : @ "\n\n" ] ;
2020-09-15 19:48:43 +02:00
if ( server . bonjourServerURL ! = nil )
2019-01-26 13:18:32 -10:00
[ servers appendString : [ NSString stringWithFormat : @ "%@" , server . bonjourServerURL ] ] ;
2020-09-16 10:12:43 +02:00
# if TARGET_OS _TV || TARGET_OS _IOS
2023-02-15 05:59:06 -05:00
settings_t * settings = config_get _ptr ( ) ;
if ( ! settings -> bools . gcdwebserver_alert )
return ;
static dispatch_once _t onceToken ;
dispatch_once ( & onceToken , ^ {
UIAlertController * alert = [ UIAlertController alertControllerWithTitle : @ "Welcome to RetroArch" message : [ NSString stringWithFormat : @ "To transfer files from your computer, go to one of these addresses on your web browser:\n\n%@" , servers ] preferredStyle : UIAlertControllerStyleAlert ] ;
2020-09-16 10:12:43 +02:00
# if TARGET_OS _TV
2023-02-15 05:59:06 -05:00
[ alert addAction : [ UIAlertAction actionWithTitle : @ "OK"
style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * _Nonnull action ) {
rarch_start _draw _observer ( ) ;
} ] ] ;
[ alert addAction : [ UIAlertAction actionWithTitle : @ "Don't Show Again"
style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * _Nonnull action ) {
rarch_start _draw _observer ( ) ;
configuration_set _bool ( settings , settings -> bools . gcdwebserver_alert , false ) ;
} ] ] ;
2019-01-26 13:18:32 -10:00
# elif TARGET_OS _IOS
2023-02-15 05:59:06 -05:00
[ alert addAction : [ UIAlertAction actionWithTitle : @ "Stop Server" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * _Nonnull action ) {
[ [ WebServer sharedInstance ] webUploader ] . delegate = nil ;
[ [ WebServer sharedInstance ] stopUploader ] ;
} ] ] ;
2020-09-16 10:12:43 +02:00
# endif
2023-02-15 05:59:06 -05:00
[ self presentViewController : alert animated : YES completion : ^ {
rarch_stop _draw _observer ( ) ;
} ] ;
} ) ;
2019-01-26 13:18:32 -10:00
# endif
}
2022-02-21 21:21:34 -10:00
2019-09-22 10:28:51 +02:00
# endif
2019-01-26 13:18:32 -10:00
2015-04-05 05:36:53 +02:00
@ end
2021-01-18 15:41:30 +01:00
void * cocoa_screen _get _chosen ( void )
{
unsigned monitor_index ;
settings_t * settings = config_get _ptr ( ) ;
NSArray * screens = [ RAScreen screens ] ;
if ( ! screens || ! settings )
return NULL ;
monitor_index = settings -> uints . video_monitor _index ;
if ( monitor_index >= screens . count )
return ( BRIDGE void * ) screens ;
return ( ( BRIDGE void * ) [ screens objectAtIndex : monitor_index ] ) ;
}
bool cocoa_has _focus ( void * data )
{
# if defined ( HAVE_COCOATOUCH )
return ( [ [ UIApplication sharedApplication ] applicationState ]
= = UIApplicationStateActive ) ;
# else
return [ NSApp isActive ] ;
# endif
}
void cocoa_show _mouse ( void * data , bool state )
{
2021-01-24 03:56:05 +01:00
# ifdef OSX
2021-01-18 15:41:30 +01:00
if ( state )
[ NSCursor unhide ] ;
else
[ NSCursor hide ] ;
# endif
}
2021-01-24 03:56:05 +01:00
# ifdef OSX
# if MAC_OS _X _VERSION _10 _7
/ * NOTE : backingScaleFactor only available on MacOS X 10.7 and up . * /
float cocoa_screen _get _backing _scale _factor ( void )
{
static float
backing_scale _def = 0.0 f ;
if ( backing_scale _def = = 0.0 f )
{
RAScreen * screen = ( BRIDGE RAScreen * ) cocoa_screen _get _chosen ( ) ;
if ( ! screen )
return 1.0 f ;
backing_scale _def = [ screen backingScaleFactor ] ;
}
return backing_scale _def ;
}
# else
float cocoa_screen _get _backing _scale _factor ( void ) { return 1.0 f ; }
# endif
# else
2021-01-18 15:41:30 +01:00
static float get_from _selector (
2022-11-23 05:08:15 +01:00
Class obj_class , id obj_id , SEL selector , CGFloat * ret )
2021-01-18 15:41:30 +01:00
{
NSInvocation * invocation = [ NSInvocation invocationWithMethodSignature :
[ obj_class instanceMethodSignatureForSelector : selector ] ] ;
[ invocation setSelector : selector ] ;
[ invocation setTarget : obj_id ] ;
[ invocation invoke ] ;
[ invocation getReturnValue : ret ] ;
RELEASE ( invocation ) ;
return * ret ;
}
/ * NOTE : nativeScale only available on iOS 8.0 and up . * /
float cocoa_screen _get _native _scale ( void )
{
SEL selector ;
static CGFloat ret = 0.0 f ;
RAScreen * screen = NULL ;
if ( ret ! = 0.0 f )
return ret ;
2022-11-23 05:08:15 +01:00
if ( ! ( screen = ( BRIDGE RAScreen * ) cocoa_screen _get _chosen ( ) ) )
2021-01-18 15:41:30 +01:00
return 0.0 f ;
selector = NSSelectorFromString ( BOXSTRING ( "nativeScale" ) ) ;
if ( [ screen respondsToSelector : selector ] )
ret = ( float ) get_from _selector (
2022-11-23 05:08:15 +01:00
[ screen class ] , screen , selector , & ret ) ;
2021-01-18 15:41:30 +01:00
else
{
ret = 1.0 f ;
selector = NSSelectorFromString ( BOXSTRING ( "scale" ) ) ;
if ( [ screen respondsToSelector : selector ] )
ret = screen . scale ;
}
return ret ;
}
# endif
2021-01-18 19:17:12 +01:00
void * nsview_get _ptr ( void )
{
2021-01-24 03:56:05 +01:00
# if defined ( OSX )
2021-01-18 19:17:12 +01:00
video_driver _display _type _set ( RARCH_DISPLAY _OSX ) ;
video_driver _display _set ( 0 ) ;
video_driver _display _userdata _set ( ( uintptr_t ) g_instance ) ;
# endif
return ( BRIDGE void * ) g_instance ;
}
void nsview_set _ptr ( CocoaView * p ) { g_instance = p ; }
CocoaView * cocoaview_get ( void )
{
# if defined ( HAVE_COCOA _METAL )
return ( CocoaView * ) apple_platform . renderView ;
# elif defined ( HAVE_COCOA )
return g_instance ;
# else
/ * TODO / FIXME - implement * /
return NULL ;
# endif
}
2021-01-18 19:28:36 +01:00
2021-01-24 03:56:05 +01:00
# ifdef OSX
2021-01-18 19:28:36 +01:00
void cocoa_update _title ( void * data )
{
const ui_window _t * window = ui_companion _driver _get _window _ptr ( ) ;
if ( window )
{
char title [ 128 ] ;
title [ 0 ] = ' \ 0 ' ;
video_driver _get _window _title ( title , sizeof ( title ) ) ;
if ( title [ 0 ] )
window -> set_title ( ( void * ) video_driver _display _userdata _get ( ) , title ) ;
}
}
2021-01-18 19:33:06 +01:00
bool cocoa_get _metrics (
void * data , enum display_metric _types type ,
float * value )
{
RAScreen * screen = ( BRIDGE RAScreen * ) cocoa_screen _get _chosen ( ) ;
NSDictionary * desc = [ screen deviceDescription ] ;
CGSize display_physical _size = CGDisplayScreenSize (
[ [ desc objectForKey : @ "NSScreenNumber" ] unsignedIntValue ] ) ;
float physical_width = display_physical _size . width ;
float physical_height = display_physical _size . height ;
switch ( type )
{
case DISPLAY_METRIC _MM _WIDTH :
* value = physical_width ;
break ;
case DISPLAY_METRIC _MM _HEIGHT :
* value = physical_height ;
break ;
case DISPLAY_METRIC _DPI :
{
NSSize disp_pixel _size = [ [ desc objectForKey : NSDeviceSize ] sizeValue ] ;
float dispwidth = disp_pixel _size . width ;
float scale = cocoa_screen _get _backing _scale _factor ( ) ;
float dpi = ( dispwidth / physical_width ) * 25.4 f * scale ;
* value = dpi ;
}
break ;
case DISPLAY_METRIC _NONE :
default :
* value = 0 ;
return false ;
}
return true ;
}
2021-01-24 03:56:05 +01:00
# else
bool cocoa_get _metrics (
void * data , enum display_metric _types type ,
float * value )
{
RAScreen * screen = ( BRIDGE RAScreen * ) cocoa_screen _get _chosen ( ) ;
float scale = cocoa_screen _get _native _scale ( ) ;
CGRect screen_rect = [ screen bounds ] ;
float physical_width = screen_rect . size . width * scale ;
float physical_height = screen_rect . size . height * scale ;
float dpi = 160 * scale ;
NSInteger idiom_type = UI_USER _INTERFACE _IDIOM ( ) ;
2021-01-18 19:33:06 +01:00
2021-01-24 03:56:05 +01:00
switch ( idiom_type )
{
case -1 : / * UIUserInterfaceIdiomUnspecified * /
/ * TODO * /
break ;
case UIUserInterfaceIdiomPad :
dpi = 132 * scale ;
break ;
case UIUserInterfaceIdiomPhone :
{
CGFloat maxSize = fmaxf ( physical_width , physical_height ) ;
/ * Larger iPhones : iPhone Plus , X , XR , XS , XS Max , 11 , 11 Pro Max * /
if ( maxSize >= 2208.0 )
dpi = 81 * scale ;
else
dpi = 163 * scale ;
}
break ;
case UIUserInterfaceIdiomTV :
case UIUserInterfaceIdiomCarPlay :
/ * TODO * /
break ;
}
2021-01-18 19:33:06 +01:00
2021-01-24 03:56:05 +01:00
switch ( type )
{
case DISPLAY_METRIC _MM _WIDTH :
* value = physical_width ;
break ;
case DISPLAY_METRIC _MM _HEIGHT :
* value = physical_height ;
break ;
case DISPLAY_METRIC _DPI :
* value = dpi ;
break ;
case DISPLAY_METRIC _NONE :
default :
* value = 0 ;
return false ;
}
2021-01-18 19:33:06 +01:00
2021-01-24 03:56:05 +01:00
return true ;
}
2021-01-18 19:28:36 +01:00
# endif