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"
2024-05-22 22:30:24 -04:00
# include < compat / apple_compat . 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
2024-02-28 02:20:32 -05:00
# if TARGET_OS _TV
# import < TVServices / TVServices . h >
# import "../../pkg/apple/RetroArchTopShelfExtension/ContentProvider.h"
# endif
2024-04-29 21:30:15 -04:00
# if TARGET_OS _IOS
# import < MobileCoreServices / MobileCoreServices . h >
# import "../../../menu/menu_cbs.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"
2024-04-29 21:30:15 -04:00
# include "../../../content.h"
# include "../../../core_info.h"
2024-05-22 13:40:51 -04:00
# include "../../../defaults.h"
# include "../../../file_path_special.h"
2024-04-29 21:30:15 -04:00
# include "../../../menu/menu_cbs.h"
2023-05-11 22:30:06 -04:00
# include "../../../paths.h"
2021-01-18 21:21:17 +01:00
# include "../../../retroarch.h"
2024-04-29 21:30:15 -04:00
# include "../../../tasks/task_content.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"
2024-05-22 13:40:51 -04:00
# ifdef HAVE_MENU
# include "../../menu/menu_driver.h"
# endif
2024-05-23 17:53:50 -04:00
# if IOS
# import < UIKit / UIAccessibility . h >
extern bool RAIsVoiceOverRunning ( void )
{
return UIAccessibilityIsVoiceOverRunning ( ) ;
}
2024-05-23 21:30:45 -04:00
# elif OSX
# import < AppKit / AppKit . h >
extern bool RAIsVoiceOverRunning ( void )
{
if ( @ available ( macOS 10.13 , * ) )
return [ [ NSWorkspace sharedWorkspace ] isVoiceOverEnabled ] ;
return false ;
}
2024-05-23 17:53:50 -04:00
# endif
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 ) ;
2024-04-29 21:30:15 -04:00
void cocoa_file _load _with _detect _core ( const char * filename ) ;
2021-01-18 03:03:35 +01:00
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
2024-04-29 21:30:15 -04:00
# if TARGET_OS _IOS
, UIDocumentPickerDelegate
# endif
2022-03-07 08:09:49 -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
2023-08-16 04:18:55 +02:00
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
2023-08-21 20:05:34 -10:00
# if TARGET_OS _IOS
self . shouldLockCurrentInterfaceOrientation = NO ;
# endif
2023-05-09 18:32:55 -04:00
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 ( ) ;
2023-08-16 04:18:55 +02:00
if ( ! ( menu_st -> flags & MENU_ST _FLAG _ALIVE ) ) / * content * /
2023-05-09 18:32:55 -04:00
return false ;
2023-08-16 04:18:55 +02:00
if ( menu_st -> flags & MENU_ST _FLAG _INP _DLG _KB _DISPLAY ) / * search * /
2023-05-09 18:32:55 -04:00
return false ;
2023-08-16 04:18:55 +02:00
if ( menu_st -> selection_ptr ! = 0 ) / * not the first item * /
2023-05-09 18:32:55 -04:00
return false ;
2023-08-16 04:18:55 +02:00
if ( menu_st -> entries . list -> menu_stack [ 0 ] -> size ! = 1 ) / * submenu * /
2023-05-09 18:32:55 -04:00
return false ;
2023-08-16 04:18:55 +02:00
if ( ! string_is _equal ( menu_st -> entries . list -> menu_stack [ 0 ] -> list -> label , / * not on the main menu * /
2023-05-09 18:32:55 -04:00
msg_hash _to _str ( MENU_ENUM _LABEL _MAIN _MENU ) ) )
return false ;
return true ;
}
2024-05-30 01:39:17 -04:00
- ( bool ) isSiri : ( GCController * ) controller
2023-05-09 18:32:55 -04:00
{
2024-05-30 01:39:17 -04:00
return ( controller . microGamepad && ! controller . extendedGamepad && [ @ "Remote" isEqualToString : controller . vendorName ] ) ;
}
2023-05-09 18:32:55 -04:00
2024-05-30 01:39:17 -04:00
- ( bool ) didMicroGamepadPress : ( UIPressType ) type
{
2023-08-16 04:18:55 +02:00
/ * Are these presses that controllers send ? * /
2023-05-21 03:21:34 -04:00
if ( @ available ( tvOS 14.3 , * ) )
if ( type = = UIPressTypePageUp || type = = UIPressTypePageDown )
return true ;
2023-05-11 22:30:29 -04:00
2024-05-30 01:39:17 -04:00
NSArray < GCController * > * controllers = [ GCController controllers ] ;
bool foundSiri = false ;
bool nonSiriPress = false ;
for ( GCController * controller in controllers ) {
if ( [ self isSiri : controller ] )
{
foundSiri = true ;
if ( type = = UIPressTypeSelect )
return controller . microGamepad . buttonA . pressed ;
else if ( type = = UIPressTypePlayPause )
return controller . microGamepad . buttonX . pressed ;
}
else if ( controller . extendedGamepad )
2023-05-09 18:32:55 -04:00
{
2023-05-11 22:30:29 -04:00
if ( type = = UIPressTypeUpArrow )
2024-05-30 01:39:17 -04:00
nonSiriPress | = controller . extendedGamepad . dpad . up . pressed
|| controller . extendedGamepad . leftThumbstick . up . pressed
|| controller . extendedGamepad . rightThumbstick . up . pressed ;
2023-05-11 22:30:29 -04:00
else if ( type = = UIPressTypeDownArrow )
2024-05-30 01:39:17 -04:00
nonSiriPress | = controller . extendedGamepad . dpad . down . pressed
|| controller . extendedGamepad . leftThumbstick . down . pressed
|| controller . extendedGamepad . rightThumbstick . down . pressed ;
2023-05-11 22:30:29 -04:00
else if ( type = = UIPressTypeLeftArrow )
2024-05-30 01:39:17 -04:00
nonSiriPress | = controller . extendedGamepad . dpad . left . pressed
|| controller . extendedGamepad . leftShoulder . pressed
|| controller . extendedGamepad . leftTrigger . pressed
|| controller . extendedGamepad . leftThumbstick . left . pressed
|| controller . extendedGamepad . rightThumbstick . left . pressed ;
2023-05-11 22:30:29 -04:00
else if ( type = = UIPressTypeRightArrow )
2024-05-30 01:39:17 -04:00
nonSiriPress | = controller . extendedGamepad . dpad . right . pressed
|| controller . extendedGamepad . rightShoulder . pressed
|| controller . extendedGamepad . rightTrigger . pressed
|| controller . extendedGamepad . leftThumbstick . right . pressed
|| controller . extendedGamepad . rightThumbstick . right . pressed ;
2023-05-09 18:32:55 -04:00
else if ( type = = UIPressTypeSelect )
2024-05-30 01:39:17 -04:00
nonSiriPress | = controller . extendedGamepad . buttonA . pressed ;
2023-05-11 22:30:29 -04:00
else if ( type = = UIPressTypeMenu )
2024-05-30 01:39:17 -04:00
nonSiriPress | = controller . extendedGamepad . buttonB . pressed ;
2023-05-09 18:32:55 -04:00
else if ( type = = UIPressTypePlayPause )
2024-05-30 01:39:17 -04:00
nonSiriPress | = controller . extendedGamepad . buttonX . pressed ;
2023-05-09 18:32:55 -04:00
}
2024-05-30 01:39:17 -04:00
else
2023-05-09 18:32:55 -04:00
{
2024-05-30 01:39:17 -04:00
/ * we have a remote that is not extended . some of these remotes send
* spurious presses . the only way to get them to work properly is to
* make the siri remote work improperly . * /
nonSiriPress = true ;
2023-05-09 18:32:55 -04:00
}
}
2024-05-30 01:39:17 -04:00
if ( ! foundSiri || [ controllers count ] = = 1 )
return foundSiri ;
return ! nonSiriPress ;
2023-05-09 18:32:55 -04:00
}
2023-05-11 22:30:29 -04:00
- ( void ) sendKeyForPress : ( UIPressType ) type down : ( bool ) down
{
static NSDictionary < NSNumber * , NSArray < NSNumber * > * > * map ;
static dispatch_once _t once ;
dispatch_once ( & once , ^ {
map = @ {
@ ( UIPressTypeUpArrow ) : @ [ @ ( RETROK_UP ) , @ ( 0 ) ] ,
@ ( UIPressTypeDownArrow ) : @ [ @ ( RETROK_DOWN ) , @ ( 0 ) ] ,
@ ( UIPressTypeLeftArrow ) : @ [ @ ( RETROK_LEFT ) , @ ( 0 ) ] ,
@ ( UIPressTypeRightArrow ) : @ [ @ ( RETROK_RIGHT ) , @ ( 0 ) ] ,
@ ( UIPressTypeSelect ) : @ [ @ ( RETROK_z ) , @ ( ' z ' ) ] ,
@ ( UIPressTypeMenu ) : @ [ @ ( RETROK_x ) , @ ( ' x ' ) ] ,
@ ( UIPressTypePlayPause ) : @ [ @ ( RETROK_s ) , @ ( ' s ' ) ] ,
@ ( UIPressTypePageUp ) : @ [ @ ( RETROK_PAGEUP ) , @ ( 0 ) ] ,
@ ( UIPressTypePageDown ) : @ [ @ ( RETROK_PAGEDOWN ) , @ ( 0 ) ] ,
} ;
} ) ;
NSArray < NSNumber * > * keyvals = map [ @ ( type ) ] ;
if ( ! keyvals )
return ;
apple_direct _input _keyboard _event ( down , keyvals [ 0 ] . intValue ,
keyvals [ 1 ] . intValue , 0 , RETRO_DEVICE _KEYBOARD ) ;
}
2023-05-09 18:32:55 -04:00
- ( void ) pressesBegan : ( NSSet < UIPress * > * ) presses
withEvent : ( UIPressesEvent * ) event
{
2023-05-11 22:30:29 -04:00
for ( UIPress * press in presses )
{
2023-08-16 04:18:55 +02:00
/ * If we ' re at the top it doesn ' t matter who pressed it , we want to leave * /
2023-05-11 22:30:29 -04:00
if ( press . type = = UIPressTypeMenu && [ self menuIsAtTop ] )
[ super pressesBegan : presses withEvent : event ] ;
else if ( [ self didMicroGamepadPress : press . type ] )
[ self sendKeyForPress : press . type down : true ] ;
2023-05-09 18:32:55 -04:00
}
}
- ( void ) pressesEnded : ( NSSet < UIPress * > * ) presses withEvent : ( UIPressesEvent * ) event
{
for ( UIPress * press in presses ) {
2023-05-11 22:30:29 -04:00
if ( press . type = = UIPressTypeSelect || press . type = = UIPressTypePlayPause )
[ self sendKeyForPress : press . type down : false ] ;
else
dispatch_after ( dispatch_time ( DISPATCH_TIME _NOW , NSEC_PER _MSEC ) , dispatch_get _main _queue ( ) , ^ {
[ [ CocoaView get ] sendKeyForPress : press . type down : false ] ;
} ) ;
2023-05-09 18:32:55 -04:00
}
}
- ( 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
2024-04-29 21:30:15 -04:00
# if TARGET_OS _IOS
# pragma mark UIDocumentPickerViewController
- ( void ) documentPicker : ( UIDocumentPickerViewController * ) controller didPickDocumentAtURL : ( NSURL * ) url
{
NSFileManager * manager = [ NSFileManager defaultManager ] ;
NSString * filename = ( NSString * ) url . path . lastPathComponent ;
NSError * error = nil ;
settings_t * settings = config_get _ptr ( ) ;
char fullpath [ PATH_MAX _LENGTH ] = { 0 } ;
fill_pathname _join _special ( fullpath , settings -> paths . directory_core _assets , [ filename UTF8String ] , sizeof ( fullpath ) ) ;
NSString * destination = [ NSString stringWithUTF8String : fullpath ] ;
NSString * documentsDir = NSSearchPathForDirectoriesInDomains ( NSDocumentDirectory , NSUserDomainMask , YES ) . firstObject ;
/ * Copy file to documents directory if it ' s not already
* inside Documents directory * /
if ( ! [ [ url path ] containsString : documentsDir ] )
if ( ! [ manager fileExistsAtPath : destination ] )
[ manager copyItemAtPath : [ url path ] toPath : destination error : & error ] ;
if ( filebrowser_get _type ( ) = = FILEBROWSER_SCAN _FILE )
action_scan _file ( fullpath , NULL , 0 , 0 ) ;
else
{
cocoa_file _load _with _detect _core ( fullpath ) ;
}
}
- ( void ) documentPickerWasCancelled : ( UIDocumentPickerViewController * ) controller
{
}
- ( void ) showDocumentPicker
{
UIDocumentPickerViewController * documentPicker = [ [ UIDocumentPickerViewController alloc ]
initWithDocumentTypes : @ [ ( NSString * ) kUTTypeDirectory ,
( NSString * ) kUTTypeItem ]
inMode : UIDocumentPickerModeImport ] ;
documentPicker . delegate = self ;
documentPicker . modalPresentationStyle = UIModalPresentationFormSheet ;
[ self presentViewController : documentPicker animated : YES completion : nil ] ;
}
# 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
2023-07-08 22:15:40 -05:00
command_event ( CMD_EVENT _OVERLAY _UNLOAD , NULL ) ;
2022-02-21 21:21:34 -10:00
# 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
{
2023-08-16 04:18:55 +02:00
/ * This is for adjusting the view frame to account for
2020-09-15 19:48:43 +02:00
* the notch in iPhone X phones * /
if ( @ available ( iOS 11 , * ) )
{
2023-11-26 07:59:22 -05:00
settings_t * settings = config_get _ptr ( ) ;
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 ] ;
2023-11-26 07:59:22 -05:00
if ( settings -> bools . video_notch _write _over _enable )
{
self . view . frame = CGRectMake ( screenSize . origin . x ,
screenSize . origin . y ,
screenSize . size . width ,
screenSize . size . height ) ;
return ;
}
2020-09-15 19:48:43 +02:00
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
{
2023-08-21 20:05:34 -10:00
if ( @ available ( iOS 16 , * ) ) {
if ( self . shouldLockCurrentInterfaceOrientation ) {
return 1 < < self . lockInterfaceOrientation ;
} else {
return ( UIInterfaceOrientationMask ) apple_frontend _settings . orientation_flags ;
}
} else {
return ( UIInterfaceOrientationMask ) apple_frontend _settings . orientation_flags ;
}
}
/ * NOTE : This does not run on iOS 16 + * /
- ( BOOL ) shouldAutorotate {
if ( self . shouldLockCurrentInterfaceOrientation ) {
return NO ;
}
return YES ;
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 ;
2023-08-16 04:18:55 +02:00
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 ] ] ;
2023-08-16 04:18:55 +02:00
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
2024-04-29 21:30:15 -04:00
# if TARGET_OS _IOS
void ios_show _file _sheet ( void )
{
[ [ CocoaView get ] showDocumentPicker ] ;
}
# endif
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 ;
2023-08-16 04:18:55 +02:00
2021-01-18 15:41:30 +01:00
monitor_index = settings -> uints . video_monitor _index ;
2023-08-16 04:18:55 +02:00
2021-01-18 15:41:30 +01:00
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 ;
2023-08-16 04:18:55 +02:00
2021-01-18 15:41:30 +01:00
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 ;
2023-08-16 04:18:55 +02:00
2021-01-18 15:41:30 +01:00
selector = NSSelectorFromString ( BOXSTRING ( "nativeScale" ) ) ;
2023-08-16 04:18:55 +02:00
2021-01-18 15:41:30 +01:00
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 ;
}
2023-08-16 04:18:55 +02:00
2024-05-19 14:26:13 -04:00
# if TARGET_OS _TV
if ( ret < 1.0 f )
ret = 1.0 f ;
# endif
2021-01-18 15:41:30 +01:00
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: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
2023-05-11 22:30:06 -04:00
2023-05-21 03:21:34 -04:00
config_file _t * open_userdefaults _config _file ( void )
2023-05-11 22:30:06 -04:00
{
config_file _t * conf = NULL ;
NSString * backup = [ NSUserDefaults . standardUserDefaults stringForKey : @ FILE_PATH _MAIN _CONFIG ] ;
2023-09-14 12:11:00 -04:00
if ( [ backup length ] > 0 )
2023-05-21 03:21:34 -04:00
{
char * str = strdup ( backup . UTF8String ) ;
conf = config_file _new _from _string ( str , path_get ( RARCH_PATH _CONFIG ) ) ;
free ( str ) ;
2024-02-19 14:12:09 -05:00
/ * If we are falling back to the NSUserDefaults backup of the config file ,
* it ' s likely because the OS has deleted all of our cache , including our
* extracted assets . This will cause re - extraction * /
config_set _int ( conf , "bundle_assets_extract_last_version" , 0 ) ;
2023-05-21 03:21:34 -04:00
}
2023-05-11 22:30:06 -04:00
return conf ;
}
2023-05-21 03:21:34 -04:00
void write_userdefaults _config _file ( void )
2023-05-11 22:30:06 -04:00
{
NSString * conf = [ NSString stringWithContentsOfFile : [ NSString stringWithUTF8String : path_get ( RARCH_PATH _CONFIG ) ]
encoding : NSUTF8StringEncoding
error : nil ] ;
if ( conf )
[ NSUserDefaults . standardUserDefaults setObject : conf forKey : @ FILE_PATH _MAIN _CONFIG ] ;
}
2024-02-28 02:20:32 -05:00
# if TARGET_OS _TV
static NSDictionary * topshelfDictForEntry ( const struct playlist_entry * entry , gfx_thumbnail _path _data _t * path_data )
{
NSMutableDictionary * dict = [ NSMutableDictionary dictionaryWithDictionary : @ {
@ "id" : [ NSString stringWithUTF8String : entry -> path ] ,
2024-03-06 17:37:11 -05:00
@ "title" : [ NSString stringWithUTF8String :
( string_is _empty ( entry -> label ) ? path_basename ( entry -> path ) : entry -> label ) ] ,
2024-02-28 02:20:32 -05:00
} ] ;
if ( ! string_is _empty ( path_data -> content_db _name ) )
{
const char * img_name = NULL ;
if ( gfx_thumbnail _get _img _name ( path_data , & img_name , PLAYLIST_THUMBNAIL _FLAG _STD _NAME ) )
dict [ @ "img" ] = [ NSString stringWithFormat : @ "https://thumbnails.libretro.com/%s/Named_Boxarts/%s" ,
path_data -> content_db _name , img_name ] ;
}
NSURLComponents * play = [ [ NSURLComponents alloc ] initWithString : @ "retroarch://topshelf" ] ;
[ play setQueryItems : @ [
[ [ NSURLQueryItem alloc ] initWithName : @ "path" value : [ NSString stringWithUTF8String : entry -> path ] ] ,
[ [ NSURLQueryItem alloc ] initWithName : @ "core_path" value : [ NSString stringWithUTF8String : entry -> core_path ] ] ,
] ] ;
dict [ @ "play" ] = [ play string ] ;
return dict ;
}
void update_topshelf ( void )
{
if ( @ available ( tvOS 13.0 , * ) )
{
NSUserDefaults * ud = [ [ NSUserDefaults alloc ] initWithSuiteName : kRetroArchAppGroup ] ;
if ( ! ud )
return ;
NSMutableDictionary * contentDict = [ NSMutableDictionary dictionaryWithCapacity : 2 ] ;
const struct playlist_entry * entry ;
gfx_thumbnail _path _data _t * thumbnail_path _data = gfx_thumbnail _path _init ( ) ;
settings_t * settings = config_get _ptr ( ) ;
bool history_list _enable = settings -> bools . history_list _enable ;
if ( history_list _enable && playlist_size ( g_defaults . content_history ) > 0 )
{
NSMutableArray * array = [ NSMutableArray arrayWithCapacity : playlist_size ( g_defaults . content_history ) ] ;
NSString * key = [ NSString stringWithUTF8String : msg_hash _to _str ( MENU_ENUM _LABEL _VALUE _HISTORY _TAB ) ] ;
for ( size_t i = 0 ; i < 5 && i < playlist_size ( g_defaults . content_history ) ; i + + )
{
gfx_thumbnail _path _reset ( thumbnail_path _data ) ;
gfx_thumbnail _set _content _playlist ( thumbnail_path _data , g_defaults . content_history , i ) ;
playlist_get _index ( g_defaults . content_history , i , & entry ) ;
[ array addObject : topshelfDictForEntry ( entry , thumbnail_path _data ) ] ;
}
contentDict [ key ] = array ;
}
if ( playlist_size ( g_defaults . content_favorites ) > 0 )
{
NSMutableArray * array = [ NSMutableArray arrayWithCapacity : playlist_size ( g_defaults . content_favorites ) ] ;
NSString * key = [ NSString stringWithUTF8String : msg_hash _to _str ( MENU_ENUM _LABEL _VALUE _FAVORITES _TAB ) ] ;
for ( size_t i = 0 ; i < 5 && i < playlist_size ( g_defaults . content_favorites ) ; i + + )
{
gfx_thumbnail _path _reset ( thumbnail_path _data ) ;
gfx_thumbnail _set _content _playlist ( thumbnail_path _data , g_defaults . content_favorites , i ) ;
playlist_get _index ( g_defaults . content_favorites , i , & entry ) ;
[ array addObject : topshelfDictForEntry ( entry , thumbnail_path _data ) ] ;
}
contentDict [ key ] = array ;
}
[ ud setObject : contentDict forKey : @ "topshelf" ] ;
[ TVTopShelfContentProvider topShelfContentDidChange ] ;
}
}
# endif
2024-04-29 21:30:15 -04:00
void cocoa_file _load _with _detect _core ( const char * filename )
{
/ * largely copied from file_load _with _detect _core ( ) in menu_cbs _ok . c * /
core_info _list _t * list = NULL ;
const core_info _t * info = NULL ;
size_t supported = 0 ;
if ( path_is _compressed _file ( filename ) )
{
generic_action _ok _displaylist _push ( filename , NULL ,
msg_hash _to _str ( MENU_ENUM _LABEL _VALUE _DOWNLOADED _FILE _DETECT _CORE _LIST ) ,
FILE_TYPE _CARCHIVE , 0 , 0 , ACTION_OK _DL _COMPRESSED _ARCHIVE _PUSH _DETECT _CORE ) ;
return ;
}
core_info _get _list ( & list ) ;
core_info _list _get _supported _cores ( list , filename , & info , & supported ) ;
if ( supported > 1 )
{
struct menu_state * menu_st = menu_state _get _ptr ( ) ;
menu_handle _t * menu = menu_st -> driver_data ;
strlcpy ( menu -> deferred_path , filename , sizeof ( menu -> deferred_path ) ) ;
strlcpy ( menu -> detect_content _path , filename , sizeof ( menu -> detect_content _path ) ) ;
generic_action _ok _displaylist _push ( filename , NULL , NULL , FILE_TYPE _NONE , 0 , 0 , ACTION_OK _DL _DEFERRED _CORE _LIST ) ;
}
else if ( supported = = 1 )
{
content_ctx _info _t content_info ;
content_info . argc = 0 ;
content_info . argv = NULL ;
content_info . args = NULL ;
content_info . environ_get = NULL ;
task_push _load _content _with _new _core _from _menu (
info -> path , filename ,
& content_info ,
CORE_TYPE _PLAIN ,
NULL , NULL ) ;
}
}