/* 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 . */ #import #ifdef IOS #import #else #import #endif #include #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 #import #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" };