mirror of
https://github.com/libretro/RetroArch
synced 2025-02-26 15:39:55 +00:00
apple: display server, including ProMotion support (#16963)
This commit is contained in:
parent
0c6a93311c
commit
60ee32b879
193
gfx/display_servers/dispserv_apple.m
Normal file
193
gfx/display_servers/dispserv_apple.m
Normal file
@ -0,0 +1,193 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
*
|
||||
* 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 <TargetConditionals.h>
|
||||
#ifdef IOS
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#import <AppKit/AppKit.h>
|
||||
#endif
|
||||
#include <stddef.h>
|
||||
#include "../verbosity.h"
|
||||
#include "../video_display_server.h"
|
||||
#include "../video_driver.h"
|
||||
#include "../../ui/drivers/cocoa/apple_platform.h"
|
||||
#include "../../ui/drivers/cocoa/cocoa_common.h"
|
||||
|
||||
#ifdef OSX
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
#endif
|
||||
|
||||
#ifdef OSX
|
||||
static bool apple_display_server_set_window_opacity(void *data, unsigned opacity)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
bool windowed_full = settings->bools.video_windowed_fullscreen;
|
||||
NSWindow *window = ((RetroArch_OSX*)[[NSApplication sharedApplication] delegate]).window;
|
||||
if (windowed_full || !window.keyWindow)
|
||||
return false;
|
||||
window.alphaValue = (CGFloat)opacity / (CGFloat)100.0f;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool apple_display_server_set_window_progress(void *data, int progress, bool finished)
|
||||
{
|
||||
static NSProgressIndicator *indicator;
|
||||
static dispatch_once_t once;
|
||||
dispatch_once(&once, ^{
|
||||
NSDockTile *dockTile = [NSApp dockTile];
|
||||
NSImageView *iv = [[NSImageView alloc] init];
|
||||
[iv setImage:[[NSApplication sharedApplication] applicationIconImage]];
|
||||
[dockTile setContentView:iv];
|
||||
|
||||
indicator = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(0, 0, dockTile.size.width, 20)];
|
||||
indicator.indeterminate = NO;
|
||||
indicator.minValue = 0;
|
||||
indicator.maxValue = 100;
|
||||
indicator.doubleValue = 0;
|
||||
|
||||
// Create a custom view for the dock tile
|
||||
[iv addSubview:indicator];
|
||||
});
|
||||
if (finished)
|
||||
indicator.doubleValue = (double)-1;
|
||||
else
|
||||
indicator.doubleValue = (double)progress;
|
||||
indicator.hidden = finished;
|
||||
[[NSApp dockTile] display];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool apple_display_server_set_window_decorations(void *data, bool on)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
bool windowed_full = settings->bools.video_windowed_fullscreen;
|
||||
NSWindow *window = ((RetroArch_OSX*)[[NSApplication sharedApplication] delegate]).window;
|
||||
if (windowed_full)
|
||||
return false;
|
||||
if (on)
|
||||
window.styleMask |= NSWindowStyleMaskTitled;
|
||||
else
|
||||
window.styleMask &= ~NSWindowStyleMaskTitled;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool apple_display_server_set_resolution(void *data,
|
||||
unsigned width, unsigned height, int int_hz, float hz,
|
||||
int center, int monitor_index, int xoffset, int padjust)
|
||||
{
|
||||
#if (defined(OSX) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 140000)
|
||||
if (@available(macOS 14, *))
|
||||
[CocoaView get].displayLink.preferredFrameRateRange = CAFrameRateRangeMake(hz * 0.9, hz * 1.2, hz);
|
||||
#elif defined(IOS)
|
||||
#if (TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000) || (TARGET_OS_TV && __TV_OS_VERSION_MAX_ALLOWED >= 150000)
|
||||
if (@available(iOS 15, tvOS 15, *))
|
||||
[CocoaView get].displayLink.preferredFrameRateRange = CAFrameRateRangeMake(hz * 0.9, hz * 1.2, hz);
|
||||
else
|
||||
#endif
|
||||
[CocoaView get].displayLink.preferredFramesPerSecond = hz;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static void *apple_display_server_get_resolution_list(
|
||||
void *data, unsigned *len)
|
||||
{
|
||||
unsigned j = 0;
|
||||
struct video_display_config *conf = NULL;
|
||||
|
||||
unsigned width, height;
|
||||
NSMutableSet *rates = [NSMutableSet set];
|
||||
double currentRate;
|
||||
|
||||
#ifdef OSX
|
||||
NSRect bounds = [CocoaView get].bounds;
|
||||
float scale = cocoa_screen_get_backing_scale_factor();
|
||||
width = bounds.size.width * scale;
|
||||
height = bounds.size.height * scale;
|
||||
|
||||
CGDirectDisplayID mainDisplayID = CGMainDisplayID();
|
||||
CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(mainDisplayID);
|
||||
currentRate = CGDisplayModeGetRefreshRate(currentMode);
|
||||
CFRelease(currentMode);
|
||||
CFArrayRef displayModes = CGDisplayCopyAllDisplayModes(mainDisplayID, NULL);
|
||||
for (CFIndex i = 0; i < CFArrayGetCount(displayModes); i++)
|
||||
{
|
||||
CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(displayModes, i);
|
||||
double refreshRate = CGDisplayModeGetRefreshRate(mode);
|
||||
if (refreshRate > 0)
|
||||
[rates addObject:@(refreshRate)];
|
||||
}
|
||||
CFRelease(displayModes);
|
||||
#else
|
||||
CGRect bounds = [CocoaView get].view.bounds;
|
||||
float scale = cocoa_screen_get_native_scale();
|
||||
width = bounds.size.width * scale;
|
||||
height = bounds.size.height * scale;
|
||||
|
||||
UIScreen *mainScreen = [UIScreen mainScreen];
|
||||
currentRate = mainScreen.maximumFramesPerSecond;
|
||||
#if !TARGET_OS_TV
|
||||
if (@available(iOS 15, *))
|
||||
[rates addObjectsFromArray:@[@(24), @(30), @(40), @(48), @(60), @(120)]];
|
||||
else
|
||||
#endif
|
||||
[rates addObject:@(mainScreen.maximumFramesPerSecond)];
|
||||
#endif
|
||||
|
||||
NSArray *sorted = [[rates allObjects] sortedArrayUsingSelector:@selector(compare:)];
|
||||
*len = (unsigned)[sorted count];
|
||||
RARCH_LOG("Available screen refresh rates: %s\n", [[NSString stringWithFormat:@"%@", sorted] UTF8String]);
|
||||
|
||||
if (!(conf = (struct video_display_config*)calloc(*len, sizeof(struct video_display_config))))
|
||||
return NULL;
|
||||
|
||||
for (j = 0; j < *len; j++)
|
||||
{
|
||||
NSNumber *rate = sorted[j];
|
||||
conf[j].width = width;
|
||||
conf[j].height = height;
|
||||
conf[j].bpp = 32;
|
||||
conf[j].refreshrate = [rate unsignedIntValue];
|
||||
conf[j].refreshrate_float = [rate floatValue];
|
||||
conf[j].interlaced = false;
|
||||
conf[j].dblscan = false;
|
||||
conf[j].idx = j;
|
||||
conf[j].current = ([rate doubleValue] == currentRate);
|
||||
}
|
||||
return conf;
|
||||
}
|
||||
|
||||
const video_display_server_t dispserv_apple = {
|
||||
NULL, /* init */
|
||||
NULL, /* destroy */
|
||||
#ifdef OSX
|
||||
apple_display_server_set_window_opacity,
|
||||
apple_display_server_set_window_progress,
|
||||
apple_display_server_set_window_decorations,
|
||||
#else
|
||||
NULL, /* set_window_opacity */
|
||||
NULL, /* set_window_progress */
|
||||
NULL, /* set_window_decorations */
|
||||
#endif
|
||||
apple_display_server_set_resolution,
|
||||
apple_display_server_get_resolution_list,
|
||||
NULL, /* get_output_options */
|
||||
NULL, /* set_screen_orientation */
|
||||
NULL, /* get_screen_orientation */
|
||||
NULL, /* get_flags */
|
||||
"apple"
|
||||
};
|
@ -232,6 +232,19 @@ static void cocoa_gl_gfx_ctx_get_video_size(void *data,
|
||||
}
|
||||
#endif
|
||||
|
||||
static float cocoa_gl_gfx_ctx_get_refresh_rate(void *data)
|
||||
{
|
||||
#ifdef OSX
|
||||
CGDirectDisplayID mainDisplayID = CGMainDisplayID();
|
||||
CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(mainDisplayID);
|
||||
float currentRate = CGDisplayModeGetRefreshRate(currentMode);
|
||||
CFRelease(currentMode);
|
||||
return currentRate;
|
||||
#else
|
||||
return [UIScreen mainScreen].maximumFramesPerSecond;
|
||||
#endif
|
||||
}
|
||||
|
||||
static gfx_ctx_proc_t cocoa_gl_gfx_ctx_get_proc_address(const char *symbol_name)
|
||||
{
|
||||
return (gfx_ctx_proc_t)CFBundleGetFunctionPointerForName(
|
||||
@ -532,7 +545,7 @@ const gfx_ctx_driver_t gfx_ctx_cocoagl = {
|
||||
#else
|
||||
cocoa_gl_gfx_ctx_get_video_size,
|
||||
#endif
|
||||
NULL, /* get_refresh_rate */
|
||||
cocoa_gl_gfx_ctx_get_refresh_rate,
|
||||
NULL, /* get_video_output_size */
|
||||
NULL, /* get_video_output_prev */
|
||||
NULL, /* get_video_output_next */
|
||||
|
@ -138,6 +138,19 @@ static void cocoa_vk_gfx_ctx_get_video_size(void *data,
|
||||
}
|
||||
#endif
|
||||
|
||||
static float cocoa_vk_gfx_ctx_get_refresh_rate(void *data)
|
||||
{
|
||||
#ifdef OSX
|
||||
CGDirectDisplayID mainDisplayID = CGMainDisplayID();
|
||||
CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(mainDisplayID);
|
||||
float currentRate = CGDisplayModeGetRefreshRate(currentMode);
|
||||
CFRelease(currentMode);
|
||||
return currentRate;
|
||||
#else
|
||||
return [UIScreen mainScreen].maximumFramesPerSecond;
|
||||
#endif
|
||||
}
|
||||
|
||||
static gfx_ctx_proc_t cocoa_vk_gfx_ctx_get_proc_address(const char *symbol_name)
|
||||
{
|
||||
return NULL;
|
||||
@ -353,7 +366,7 @@ const gfx_ctx_driver_t gfx_ctx_cocoavk = {
|
||||
#else
|
||||
cocoa_vk_gfx_ctx_get_video_size,
|
||||
#endif
|
||||
NULL, /* get_refresh_rate */
|
||||
cocoa_vk_gfx_ctx_get_refresh_rate,
|
||||
NULL, /* get_video_output_size */
|
||||
NULL, /* get_video_output_prev */
|
||||
NULL, /* get_video_output_next */
|
||||
|
@ -104,6 +104,7 @@ extern const video_display_server_t dispserv_win32;
|
||||
extern const video_display_server_t dispserv_x11;
|
||||
extern const video_display_server_t dispserv_kms;
|
||||
extern const video_display_server_t dispserv_android;
|
||||
extern const video_display_server_t dispserv_apple;
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
|
@ -1103,6 +1103,8 @@ void* video_display_server_init(enum rarch_display_type type)
|
||||
default:
|
||||
#if defined(ANDROID)
|
||||
current_display_server = &dispserv_android;
|
||||
#elif defined(__APPLE__)
|
||||
current_display_server = &dispserv_apple;
|
||||
#else
|
||||
current_display_server = &dispserv_null;
|
||||
#endif
|
||||
|
@ -27,6 +27,8 @@
|
||||
#define HAVE_COMPRESSION 1
|
||||
#endif
|
||||
|
||||
#include "../gfx/display_servers/dispserv_apple.m"
|
||||
|
||||
#if defined(HAVE_COCOATOUCH) || defined(HAVE_COCOA) || defined(HAVE_COCOA_METAL)
|
||||
|
||||
#include "../ui/drivers/cocoa/cocoa_common.m"
|
||||
|
@ -13461,7 +13461,9 @@ static bool setting_append_list(
|
||||
general_write_handler,
|
||||
general_read_handler,
|
||||
SD_FLAG_NONE);
|
||||
#ifndef OSX
|
||||
MENU_SETTINGS_LIST_CURRENT_ADD_CMD(list, list_info, CMD_EVENT_REINIT);
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
|
||||
CONFIG_BOOL(
|
||||
@ -14170,7 +14172,7 @@ static bool setting_append_list(
|
||||
SD_FLAG_NONE
|
||||
);
|
||||
|
||||
#if !defined(RARCH_MOBILE)
|
||||
#if !defined(RARCH_MOBILE) || defined(IOS)
|
||||
{
|
||||
#if defined(HAVE_STEAM) && defined(HAVE_MIST)
|
||||
bool on_deck = false;
|
||||
|
@ -6,6 +6,8 @@
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>ALTDeviceID</key>
|
||||
<string>$(TARGET_DEVICE_IDENTIFIER)</string>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
|
@ -16,6 +16,8 @@ extern void ios_show_file_sheet(void);
|
||||
|
||||
#ifdef __OBJC__
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#ifdef HAVE_METAL
|
||||
#import <Metal/Metal.h>
|
||||
#import <MetalKit/MetalKit.h>
|
||||
@ -53,9 +55,6 @@ typedef enum apple_view_type
|
||||
* the displays should not sleep.
|
||||
*/
|
||||
- (bool)setDisableDisplaySleep:(bool)disable;
|
||||
#if defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)
|
||||
- (void)updateWindowedMode;
|
||||
#endif
|
||||
@end
|
||||
|
||||
#endif
|
||||
@ -76,6 +75,8 @@ void rarch_stop_draw_observer(void);
|
||||
@end
|
||||
#endif
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface RetroArch_iOS : UINavigationController<ApplePlatform, UIApplicationDelegate,
|
||||
UINavigationControllerDelegate> {
|
||||
UIView *_renderView;
|
||||
@ -94,7 +95,11 @@ UINavigationControllerDelegate> {
|
||||
|
||||
- (void)refreshSystemConfig;
|
||||
@end
|
||||
|
||||
#else
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#if defined(HAVE_COCOA_METAL)
|
||||
@interface RetroArch_OSX : NSObject<ApplePlatform, NSApplicationDelegate> {
|
||||
#elif (defined(__MACH__) && defined(MAC_OS_X_VERSION_MAX_ALLOWED) && (MAC_OS_X_VERSION_MAX_ALLOWED < 101200))
|
||||
|
@ -18,6 +18,7 @@
|
||||
#define __COCOA_COMMON_SHARED_H
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <QuartzCore/QuartzCore.h>
|
||||
|
||||
#if defined(HAVE_COCOATOUCH)
|
||||
#include <UIKit/UIKit.h>
|
||||
@ -73,6 +74,8 @@
|
||||
@property(readwrite) UIInterfaceOrientation lockInterfaceOrientation;
|
||||
#endif
|
||||
|
||||
@property(nonatomic,readwrite) CADisplayLink *displayLink;
|
||||
|
||||
+ (CocoaView*)get;
|
||||
@end
|
||||
|
||||
@ -87,6 +90,10 @@ void get_ios_version(int *major, int *minor);
|
||||
- (void)display;
|
||||
#endif
|
||||
|
||||
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 140000
|
||||
@property(nonatomic,readwrite) CADisplayLink *displayLink API_AVAILABLE(macos(14.0));
|
||||
#endif
|
||||
|
||||
@end
|
||||
#endif
|
||||
|
||||
|
@ -104,6 +104,12 @@ void cocoa_file_load_with_detect_core(const char *filename);
|
||||
- (void)scrollWheel:(NSEvent *)theEvent { }
|
||||
#endif
|
||||
|
||||
#if !defined(OSX) || __MAC_OS_X_VERSION_MAX_ALLOWED >= 140000
|
||||
-(void)step:(CADisplayLink*)target API_AVAILABLE(macos(14.0), ios(3.1), tvos(3.1))
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
+ (CocoaView*)get
|
||||
{
|
||||
CocoaView *view = (BRIDGE CocoaView*)nsview_get_ptr();
|
||||
@ -111,6 +117,21 @@ void cocoa_file_load_with_detect_core(const char *filename);
|
||||
{
|
||||
view = [CocoaView new];
|
||||
nsview_set_ptr(view);
|
||||
#if defined(IOS)
|
||||
view.displayLink = [CADisplayLink displayLinkWithTarget:view selector:@selector(step:)];
|
||||
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000 || __TV_OS_VERSION_MAX_ALLOWED >= 150000
|
||||
if (@available(iOS 15.0, tvOS 15.0, *))
|
||||
[view.displayLink setPreferredFrameRateRange:CAFrameRateRangeMake(60, 120, 120)];
|
||||
#endif
|
||||
[view.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
#elif defined(OSX) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 140000
|
||||
if (@available(macOS 14.0, *))
|
||||
{
|
||||
view.displayLink = [view displayLinkWithTarget:view selector:@selector(step:)];
|
||||
view.displayLink.preferredFrameRateRange = CAFrameRateRangeMake(60, 120, 120);
|
||||
[view.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
@ -533,7 +533,9 @@ static ui_application_t ui_application_cocoa = {
|
||||
|
||||
- (void)windowDidBecomeKey:(NSNotification *)notification
|
||||
{
|
||||
[apple_platform updateWindowedMode];
|
||||
settings_t *settings = config_get_ptr();
|
||||
video_display_server_set_window_opacity(settings->uints.video_window_opacity);
|
||||
video_display_server_set_window_decorations(settings->bools.video_window_show_decorations);
|
||||
}
|
||||
|
||||
- (void)windowDidMove:(NSNotification *)notification
|
||||
@ -720,7 +722,6 @@ static ui_application_t ui_application_cocoa = {
|
||||
if (is_fullscreen)
|
||||
[self.window toggleFullScreen:self];
|
||||
[self updateWindowedSize:mode];
|
||||
[self updateWindowedMode];
|
||||
}
|
||||
|
||||
/* HACK(sgc): ensure MTKView posts a drawable resize event */
|
||||
@ -754,24 +755,6 @@ static ui_application_t ui_application_cocoa = {
|
||||
[self.window setContentSize:NSMakeSize(mode.width, mode.height)];
|
||||
}
|
||||
|
||||
- (void)updateWindowedMode
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
bool windowed_full = settings->bools.video_windowed_fullscreen;
|
||||
bool show_decorations = settings->bools.video_window_show_decorations;
|
||||
CGFloat opacity = (CGFloat)settings->uints.video_window_opacity / (CGFloat)100.0;
|
||||
|
||||
if (windowed_full || !self.window.keyWindow)
|
||||
return;
|
||||
|
||||
if (show_decorations)
|
||||
self.window.styleMask |= NSWindowStyleMaskTitled;
|
||||
else
|
||||
self.window.styleMask &= ~NSWindowStyleMaskTitled;
|
||||
|
||||
self.window.alphaValue = opacity;
|
||||
}
|
||||
|
||||
- (void)setCursorVisible:(bool)v
|
||||
{
|
||||
if (v)
|
||||
|
Loading…
x
Reference in New Issue
Block a user