duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

platform_misc_mac.mm (4277B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 // Normally, system includes come last. But apparently some of our macro names are redefined...
      5 #include <Cocoa/Cocoa.h>
      6 #include <IOKit/pwr_mgt/IOPMLib.h>
      7 #include <QuartzCore/QuartzCore.h>
      8 #include <cinttypes>
      9 #include <optional>
     10 #include <sys/sysctl.h>
     11 #include <vector>
     12 
     13 #include "metal_layer.h"
     14 #include "platform_misc.h"
     15 #include "window_info.h"
     16 
     17 #include "common/log.h"
     18 #include "common/small_string.h"
     19 
     20 Log_SetChannel(PlatformMisc);
     21 
     22 #if __has_feature(objc_arc)
     23 #error ARC should not be enabled.
     24 #endif
     25 
     26 static IOPMAssertionID s_prevent_idle_assertion = kIOPMNullAssertionID;
     27 
     28 bool PlatformMisc::InitializeSocketSupport(Error* error)
     29 {
     30   return true;
     31 }
     32 
     33 static bool SetScreensaverInhibitMacOS(bool inhibit)
     34 {
     35   if (inhibit)
     36   {
     37     const CFStringRef reason = CFSTR("System Running");
     38     if (IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep, kIOPMAssertionLevelOn, reason,
     39                                     &s_prevent_idle_assertion) != kIOReturnSuccess)
     40     {
     41       ERROR_LOG("IOPMAssertionCreateWithName() failed");
     42       return false;
     43     }
     44 
     45     return true;
     46   }
     47   else
     48   {
     49     IOPMAssertionRelease(s_prevent_idle_assertion);
     50     s_prevent_idle_assertion = kIOPMNullAssertionID;
     51     return true;
     52   }
     53 }
     54 
     55 static bool s_screensaver_suspended;
     56 
     57 void PlatformMisc::SuspendScreensaver()
     58 {
     59   if (s_screensaver_suspended)
     60 
     61     if (!SetScreensaverInhibitMacOS(true))
     62     {
     63       ERROR_LOG("Failed to suspend screensaver.");
     64       return;
     65     }
     66 
     67   s_screensaver_suspended = true;
     68 }
     69 
     70 void PlatformMisc::ResumeScreensaver()
     71 {
     72   if (!s_screensaver_suspended)
     73     return;
     74 
     75   if (!SetScreensaverInhibitMacOS(false))
     76     ERROR_LOG("Failed to resume screensaver.");
     77 
     78   s_screensaver_suspended = false;
     79 }
     80 
     81 template<typename T>
     82 static std::optional<T> sysctlbyname(const char* name)
     83 {
     84   T output = 0;
     85   size_t output_size = sizeof(output);
     86   if (sysctlbyname(name, &output, &output_size, nullptr, 0) != 0)
     87     return std::nullopt;
     88 
     89   return output;
     90 }
     91 
     92 size_t PlatformMisc::GetRuntimePageSize()
     93 {
     94   return sysctlbyname<u32>("hw.pagesize").value_or(0);
     95 }
     96 
     97 bool PlatformMisc::PlaySoundAsync(const char* path)
     98 {
     99   NSString* nspath = [[NSString alloc] initWithUTF8String:path];
    100   NSSound* sound = [[NSSound alloc] initWithContentsOfFile:nspath byReference:YES];
    101   const bool result = [sound play];
    102   [sound release];
    103   [nspath release];
    104   return result;
    105 }
    106 
    107 bool CocoaTools::CreateMetalLayer(WindowInfo* wi)
    108 {
    109   // Punt off to main thread if we're not calling from it already.
    110   if (![NSThread isMainThread])
    111   {
    112     bool ret;
    113     dispatch_sync(dispatch_get_main_queue(), [&ret, wi]() { ret = CreateMetalLayer(wi); });
    114     return ret;
    115   }
    116 
    117   CAMetalLayer* layer = [CAMetalLayer layer];
    118   if (layer == nil)
    119   {
    120     ERROR_LOG("Failed to create CAMetalLayer");
    121     return false;
    122   }
    123 
    124   NSView* view = (__bridge NSView*)wi->window_handle;
    125   [view setWantsLayer:TRUE];
    126   [view setLayer:layer];
    127   [layer setContentsScale:[[[view window] screen] backingScaleFactor]];
    128 
    129   wi->surface_handle = (__bridge void*)layer;
    130   return true;
    131 }
    132 
    133 void CocoaTools::DestroyMetalLayer(WindowInfo* wi)
    134 {
    135   if (!wi->surface_handle)
    136     return;
    137 
    138   // Punt off to main thread if we're not calling from it already.
    139   if (![NSThread isMainThread])
    140   {
    141     dispatch_sync(dispatch_get_main_queue(), [wi]() { DestroyMetalLayer(wi); });
    142     return;
    143   }
    144 
    145   NSView* view = (__bridge NSView*)wi->window_handle;
    146   CAMetalLayer* layer = (__bridge CAMetalLayer*)wi->surface_handle;
    147   [view setLayer:nil];
    148   [view setWantsLayer:NO];
    149   [layer release];
    150 }
    151 
    152 std::optional<float> CocoaTools::GetViewRefreshRate(const WindowInfo& wi)
    153 {
    154   if (![NSThread isMainThread])
    155   {
    156     std::optional<float> ret;
    157     dispatch_sync(dispatch_get_main_queue(), [&ret, wi]{ ret = GetViewRefreshRate(wi); });
    158     return ret;
    159   }
    160 
    161   std::optional<float> ret;
    162   NSView* const view = (__bridge NSView*)wi.window_handle;
    163   const u32 did = [[[[[view window] screen] deviceDescription] valueForKey:@"NSScreenNumber"] unsignedIntValue];
    164   if (CGDisplayModeRef mode = CGDisplayCopyDisplayMode(did))
    165   {
    166     ret = CGDisplayModeGetRefreshRate(mode);
    167     CGDisplayModeRelease(mode);
    168   }
    169 
    170   return ret;
    171 }