mirror of
https://github.com/OpenEmu/OpenEmuKit.git
synced 2025-11-01 11:08:14 +00:00
feat: General improvements to OpenEmuKit
NOTE: Replaced `dispatch_async` to `CFRunLoopPerformBlock`, as it was always dispatching to the main queue. `dispatch_async` is not reentrant, meaning that a nested call to dispatch and wait would block forever. `CFRunLoopPerformBlock` allows this behaviour, to enable the synchronous calls such as `captureOutputImage` and `captureSourceImage`
This commit is contained in:
@@ -12,8 +12,6 @@
|
||||
0518D6E824F32DD10037101D /* NSEvent+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0518D6E724F32DD10037101D /* NSEvent+Combine.swift */; };
|
||||
0530C3B32512DF680050F90F /* OEScaledGameLayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0530C3B22512DF680050F90F /* OEScaledGameLayerView.swift */; };
|
||||
0547FD9824E7717E005C1FFC /* OEShadersModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0547FD9724E7717E005C1FFC /* OEShadersModel.swift */; };
|
||||
0547FDA424E78EA6005C1FFC /* OEXPCDebugSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 0547FD9F24E78EA6005C1FFC /* OEXPCDebugSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
0547FDA824E78EA6005C1FFC /* OEXPCDebugSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 0547FDA324E78EA6005C1FFC /* OEXPCDebugSupport.m */; };
|
||||
0547FDCA24E84498005C1FFC /* NSXPCConnection+HelperApp.h in Headers */ = {isa = PBXBuildFile; fileRef = 0547FDC824E84498005C1FFC /* NSXPCConnection+HelperApp.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
0547FDCB24E84498005C1FFC /* NSXPCConnection+HelperApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 0547FDC924E84498005C1FFC /* NSXPCConnection+HelperApp.m */; };
|
||||
0547FDCE24E850EB005C1FFC /* NSXPCListener+HelperApp.h in Headers */ = {isa = PBXBuildFile; fileRef = 0547FDCC24E850EB005C1FFC /* NSXPCListener+HelperApp.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@@ -26,6 +24,9 @@
|
||||
05713219259C33B9001CB13A /* NSFileManager+ExtendedAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 05713217259C33B9001CB13A /* NSFileManager+ExtendedAttributes.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
0571321A259C33B9001CB13A /* NSFileManager+ExtendedAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 05713218259C33B9001CB13A /* NSFileManager+ExtendedAttributes.m */; };
|
||||
0578484B25C1392400A842E7 /* ShaderCompilerOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0578484A25C1392300A842E7 /* ShaderCompilerOptions.swift */; };
|
||||
05AAD98726758240004466E3 /* XPCDebugSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05AAD98626758240004466E3 /* XPCDebugSupport.swift */; };
|
||||
05B48723266309600049A5F6 /* OEGameCoreManager+Synchronous.h in Headers */ = {isa = PBXBuildFile; fileRef = 05B48721266309600049A5F6 /* OEGameCoreManager+Synchronous.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
05B48724266309600049A5F6 /* OEGameCoreManager+Synchronous.m in Sources */ = {isa = PBXBuildFile; fileRef = 05B48722266309600049A5F6 /* OEGameCoreManager+Synchronous.m */; };
|
||||
05D828D524E9881E00BB975E /* OEXPCMatchMaking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05D828D424E9881E00BB975E /* OEXPCMatchMaking.swift */; };
|
||||
05D828DB24E98A8E00BB975E /* OEXPCMatchMaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05D828DA24E98A8E00BB975E /* OEXPCMatchMaker.swift */; };
|
||||
05E6958424CA5D4200ACFB35 /* OpenEmuKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 05E6958224CA5D4200ACFB35 /* OpenEmuKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@@ -100,8 +101,6 @@
|
||||
0530C3B22512DF680050F90F /* OEScaledGameLayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OEScaledGameLayerView.swift; sourceTree = "<group>"; };
|
||||
0533EA6624CDFD030095146E /* XADMaster.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XADMaster.framework; path = ../OpenEmu/XADMaster.framework; sourceTree = "<group>"; };
|
||||
0547FD9724E7717E005C1FFC /* OEShadersModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OEShadersModel.swift; sourceTree = "<group>"; };
|
||||
0547FD9F24E78EA6005C1FFC /* OEXPCDebugSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OEXPCDebugSupport.h; sourceTree = "<group>"; };
|
||||
0547FDA324E78EA6005C1FFC /* OEXPCDebugSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OEXPCDebugSupport.m; sourceTree = "<group>"; };
|
||||
0547FDC824E84498005C1FFC /* NSXPCConnection+HelperApp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSXPCConnection+HelperApp.h"; sourceTree = "<group>"; };
|
||||
0547FDC924E84498005C1FFC /* NSXPCConnection+HelperApp.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSXPCConnection+HelperApp.m"; sourceTree = "<group>"; };
|
||||
0547FDCC24E850EB005C1FFC /* NSXPCListener+HelperApp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSXPCListener+HelperApp.h"; sourceTree = "<group>"; };
|
||||
@@ -114,6 +113,9 @@
|
||||
05713217259C33B9001CB13A /* NSFileManager+ExtendedAttributes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSFileManager+ExtendedAttributes.h"; sourceTree = "<group>"; };
|
||||
05713218259C33B9001CB13A /* NSFileManager+ExtendedAttributes.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSFileManager+ExtendedAttributes.m"; sourceTree = "<group>"; };
|
||||
0578484A25C1392300A842E7 /* ShaderCompilerOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShaderCompilerOptions.swift; sourceTree = "<group>"; };
|
||||
05AAD98626758240004466E3 /* XPCDebugSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XPCDebugSupport.swift; sourceTree = "<group>"; };
|
||||
05B48721266309600049A5F6 /* OEGameCoreManager+Synchronous.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OEGameCoreManager+Synchronous.h"; sourceTree = "<group>"; };
|
||||
05B48722266309600049A5F6 /* OEGameCoreManager+Synchronous.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "OEGameCoreManager+Synchronous.m"; sourceTree = "<group>"; };
|
||||
05D828D424E9881E00BB975E /* OEXPCMatchMaking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OEXPCMatchMaking.swift; sourceTree = "<group>"; };
|
||||
05D828DA24E98A8E00BB975E /* OEXPCMatchMaker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OEXPCMatchMaker.swift; sourceTree = "<group>"; };
|
||||
05E6957F24CA5D4200ACFB35 /* OpenEmuKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OpenEmuKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -212,8 +214,7 @@
|
||||
05ED1CFB259D43E800A2D400 /* LaunchControl.swift */,
|
||||
05D828D424E9881E00BB975E /* OEXPCMatchMaking.swift */,
|
||||
05D828DA24E98A8E00BB975E /* OEXPCMatchMaker.swift */,
|
||||
0547FD9F24E78EA6005C1FFC /* OEXPCDebugSupport.h */,
|
||||
0547FDA324E78EA6005C1FFC /* OEXPCDebugSupport.m */,
|
||||
05AAD98626758240004466E3 /* XPCDebugSupport.swift */,
|
||||
0547FDC824E84498005C1FFC /* NSXPCConnection+HelperApp.h */,
|
||||
0547FDC924E84498005C1FFC /* NSXPCConnection+HelperApp.m */,
|
||||
0547FDCC24E850EB005C1FFC /* NSXPCListener+HelperApp.h */,
|
||||
@@ -342,6 +343,8 @@
|
||||
05E6BA2C24CCD76700ACFB35 /* Graphics */,
|
||||
05E6BA3324CCD78300ACFB35 /* Utilities */,
|
||||
05E6BA4324CCD7D700ACFB35 /* OEGameCoreManager.h */,
|
||||
05B48721266309600049A5F6 /* OEGameCoreManager+Synchronous.h */,
|
||||
05B48722266309600049A5F6 /* OEGameCoreManager+Synchronous.m */,
|
||||
05E6BA4224CCD7D600ACFB35 /* OEGameCoreManager_Internal.h */,
|
||||
05E6BA4424CCD7D800ACFB35 /* OEGameCoreManager.m */,
|
||||
0518D6E324F2BA4B0037101D /* OEGameStartupInfo.h */,
|
||||
@@ -445,10 +448,10 @@
|
||||
0547FDD224E85783005C1FFC /* OEXPCGameCoreManager.h in Headers */,
|
||||
05E6BA1F24CCD71D00ACFB35 /* OESystemPlugin.h in Headers */,
|
||||
05E6BA0724CCD49600ACFB35 /* OEGameHelperMetalLayer.h in Headers */,
|
||||
0547FDA424E78EA6005C1FFC /* OEXPCDebugSupport.h in Headers */,
|
||||
0547FDCE24E850EB005C1FFC /* NSXPCListener+HelperApp.h in Headers */,
|
||||
05E6BA2924CCD75900ACFB35 /* OEAudioUnit.h in Headers */,
|
||||
05E6BA3C24CCD79600ACFB35 /* OEThreadProxy.h in Headers */,
|
||||
05B48723266309600049A5F6 /* OEGameCoreManager+Synchronous.h in Headers */,
|
||||
05E6BA4A24CCD7DB00ACFB35 /* OEGameCoreManager.h in Headers */,
|
||||
05E6B9FE24CCD46900ACFB35 /* OEMTLGameRenderer.h in Headers */,
|
||||
05E6BA2024CCD71D00ACFB35 /* OECorePlugin.h in Headers */,
|
||||
@@ -555,11 +558,13 @@
|
||||
05E6BA2A24CCD75900ACFB35 /* OEAudioUnit.mm in Sources */,
|
||||
05E6BA3E24CCD79600ACFB35 /* OEThreadProxy.m in Sources */,
|
||||
05E6BA3D24CCD79600ACFB35 /* OELogging.m in Sources */,
|
||||
05AAD98726758240004466E3 /* XPCDebugSupport.swift in Sources */,
|
||||
0571321A259C33B9001CB13A /* NSFileManager+ExtendedAttributes.m in Sources */,
|
||||
05ED1CFC259D43E800A2D400 /* LaunchControl.swift in Sources */,
|
||||
0547FD9824E7717E005C1FFC /* OEShadersModel.swift in Sources */,
|
||||
05E6BA1D24CCD71D00ACFB35 /* OESystemPlugin.m in Sources */,
|
||||
0547FDCF24E850EB005C1FFC /* NSXPCListener+HelperApp.m in Sources */,
|
||||
05B48724266309600049A5F6 /* OEGameCoreManager+Synchronous.m in Sources */,
|
||||
05EA2F2224FFD16B00569EBA /* OEIntegralWindowResizingDelegate.swift in Sources */,
|
||||
0578484B25C1392400A842E7 /* ShaderCompilerOptions.swift in Sources */,
|
||||
05E6B9FA24CCD46900ACFB35 /* OEOpenGL3GameRenderer.m in Sources */,
|
||||
@@ -571,7 +576,6 @@
|
||||
05E6BA2224CCD71D00ACFB35 /* OECorePlugin.m in Sources */,
|
||||
05E6BA0524CCD49600ACFB35 /* OECoreVideoTexture.m in Sources */,
|
||||
05E6B9FD24CCD46900ACFB35 /* OEOpenGL2GameRenderer.m in Sources */,
|
||||
0547FDA824E78EA6005C1FFC /* OEXPCDebugSupport.m in Sources */,
|
||||
05D828DB24E98A8E00BB975E /* OEXPCMatchMaker.swift in Sources */,
|
||||
05E6BA0424CCD49600ACFB35 /* OEGameHelperMetalLayer.m in Sources */,
|
||||
0530C3B32512DF680050F90F /* OEScaledGameLayerView.swift in Sources */,
|
||||
|
||||
@@ -40,6 +40,7 @@ FOUNDATION_EXPORT const unsigned char OpenEmuKitVersionString[];
|
||||
#import <OpenEmuKit/OpenEmuHelperApp.h>
|
||||
#import <OpenEmuKit/OpenEmuXPCHelperApp.h>
|
||||
#import <OpenEmuKit/OEGameCoreManager.h>
|
||||
#import <OpenEmuKit/OEGameCoreManager+Synchronous.h>
|
||||
#import <OpenEmuKit/OEThreadGameCoreManager.h>
|
||||
#import <OpenEmuKit/OEXPCGameCoreManager.h>
|
||||
#import <OpenEmuKit/OEGameLayerView.h>
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef struct _OEGameCoreHelperSetupResult {
|
||||
OEIntSize screenSize;
|
||||
OEIntSize aspectSize;
|
||||
} OEGameCoreHelperSetupResult;
|
||||
|
||||
/*!
|
||||
* A protocol that defines the behaviour required to control an emulator core.
|
||||
*
|
||||
@@ -64,7 +69,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#pragma mark - Emulator control
|
||||
|
||||
- (void)setupEmulationWithCompletionHandler:(void(^)(OEIntSize screenSize, OEIntSize aspectSize))handler;
|
||||
- (void)setupEmulationWithCompletionHandler:(void(^)(OEGameCoreHelperSetupResult result))handler;
|
||||
- (void)startEmulationWithCompletionHandler:(void(^)(void))handler;
|
||||
- (void)resetEmulationWithCompletionHandler:(void(^)(void))handler;
|
||||
- (void)stopEmulationWithCompletionHandler:(void(^)(void))handler;
|
||||
@@ -101,6 +106,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol OEGameCoreOwner <NSObject>
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
// These actions are triggered from the game core via the OEGlobalEventsHandler protocol
|
||||
|
||||
/*! Notify the host application of the user request to save the current state
|
||||
*/
|
||||
- (void)saveState;
|
||||
- (void)loadState;
|
||||
- (void)quickSave;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019, OpenEmu Team
|
||||
// Copyright (c) 2021, OpenEmu Team
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,25 +22,24 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
@import Foundation;
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <OpenEmuKit/OpenEmuKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OEXPCCDebugSupport: NSObject
|
||||
|
||||
/*! Returns a boolean to indicate whether a debugger is currently attached to the process.
|
||||
/*! Category with APIs that run synchronously on the main thread
|
||||
*/
|
||||
@property (class, nonatomic, readonly) BOOL debuggerAttached;
|
||||
@interface OEGameCoreManager (Synchronous)
|
||||
|
||||
/*! Wait up to a specified number of nanoseconds for a debugger to attach.
|
||||
|
||||
@returns YES if a debugger attached
|
||||
/**
|
||||
* Capture an image of the final core video display buffer, which includes all shader effects.
|
||||
*/
|
||||
+ (BOOL)waitForDebuggerUntil:(NSUInteger)nanoseconds;
|
||||
- (NSBitmapImageRep *)captureOutputImage;
|
||||
|
||||
/*! Wait indefinitely until a debugger is attached
|
||||
/**
|
||||
* Capture an image of the raw core video display buffer with no effects.
|
||||
*/
|
||||
+ (void)waitForDebugger;
|
||||
- (NSBitmapImageRep *)captureSourceImage;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) 2021, OpenEmu Team
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the OpenEmu Team nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY OpenEmu Team ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY
|
||||
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#import "OEGameCoreManager+Synchronous.h"
|
||||
#import "OEGameCoreManager_Internal.h"
|
||||
#import <stdatomic.h>
|
||||
|
||||
@implementation OEGameCoreManager (Synchronous)
|
||||
|
||||
#define MAIN_INIT \
|
||||
__block atomic_bool done = false;
|
||||
|
||||
#define MAIN_BEGIN \
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
|
||||
|
||||
#define MAIN_END \
|
||||
atomic_store(&done, true); \
|
||||
CFRunLoopStop(CFRunLoopGetMain()); \
|
||||
});
|
||||
|
||||
#define MAIN_WAIT \
|
||||
while (atomic_load(&done) == false) \
|
||||
{ \
|
||||
if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10.0, NO) == kCFRunLoopRunFinished) break; \
|
||||
}
|
||||
|
||||
#pragma mark - Synchronous APIs
|
||||
|
||||
- (NSBitmapImageRep *)captureOutputImage
|
||||
{
|
||||
MAIN_INIT
|
||||
|
||||
__block NSBitmapImageRep *res = nil;
|
||||
[self.gameCoreHelper captureOutputImageWithCompletionHandler:^(NSBitmapImageRep * _Nonnull image) {
|
||||
MAIN_BEGIN
|
||||
res = image;
|
||||
MAIN_END
|
||||
}];
|
||||
|
||||
MAIN_WAIT
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
- (NSBitmapImageRep *)captureSourceImage
|
||||
{
|
||||
MAIN_INIT
|
||||
|
||||
__block NSBitmapImageRep *res = nil;
|
||||
[self.gameCoreHelper captureSourceImageWithCompletionHandler:^(NSBitmapImageRep * _Nonnull image) {
|
||||
MAIN_BEGIN
|
||||
res = image;
|
||||
MAIN_END
|
||||
}];
|
||||
|
||||
MAIN_WAIT
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -49,15 +49,13 @@ typedef NS_ERROR_ENUM(OEGameCoreErrorDomain, OEGameCoreManagerErrorCodes)
|
||||
* @details
|
||||
* The @c OEGameCoreManager is responsible for brokering communication between the
|
||||
* host and the game core.
|
||||
* @param queue The queue to execute completion handlers. If nil, handlers will be executed on the main queue.
|
||||
*/
|
||||
- (instancetype)initWithStartupInfo:(OEGameStartupInfo *)startupInfo corePlugin:(OECorePlugin *)plugin systemPlugin:(OESystemPlugin *)systemPlugin gameCoreOwner:(id<OEGameCoreOwner>)gameCoreOwner queue:(dispatch_queue_t _Nullable)queue;
|
||||
- (instancetype)initWithStartupInfo:(OEGameStartupInfo *)startupInfo corePlugin:(OECorePlugin *)plugin systemPlugin:(OESystemPlugin *)systemPlugin gameCoreOwner:(id<OEGameCoreOwner>)gameCoreOwner;
|
||||
|
||||
@property(readonly, copy) OEGameStartupInfo *startupInfo;
|
||||
@property(readonly, weak) OECorePlugin *plugin;
|
||||
@property(readonly, weak) OESystemPlugin *systemPlugin;
|
||||
@property(readonly, weak) id<OEGameCoreOwner> gameCoreOwner;
|
||||
@property(readonly) dispatch_queue_t queue;
|
||||
|
||||
#pragma mark - Abstract methods, must be overrode in subclasses
|
||||
|
||||
|
||||
+36
-39
@@ -39,7 +39,6 @@ NSString * const OEGameCoreErrorDomain = @"OEGameCoreErrorDomain";
|
||||
corePlugin:(OECorePlugin *)plugin
|
||||
systemPlugin:(OESystemPlugin *)systemPlugin
|
||||
gameCoreOwner:(id<OEGameCoreOwner>)gameCoreOwner
|
||||
queue:(dispatch_queue_t _Nullable)queue
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
@@ -47,7 +46,6 @@ NSString * const OEGameCoreErrorDomain = @"OEGameCoreErrorDomain";
|
||||
_plugin = plugin;
|
||||
_systemPlugin = systemPlugin;
|
||||
_gameCoreOwner = gameCoreOwner;
|
||||
_queue = queue ?: dispatch_get_main_queue();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -62,22 +60,22 @@ NSString * const OEGameCoreErrorDomain = @"OEGameCoreErrorDomain";
|
||||
[self doesNotImplementSelector:_cmd];
|
||||
}
|
||||
|
||||
- (void)loadROMWithCompletionHandler:(void(^)(void))completionHandler errorHandler:(void(^)(NSError *))errorHandler;
|
||||
- (void)loadROMWithCompletionHandler:(void(^)(void))completionHandler errorHandler:(void(^)(NSError *))errorHandler
|
||||
{
|
||||
[self doesNotImplementSelector:_cmd];
|
||||
}
|
||||
|
||||
- (void)setVolume:(CGFloat)value;
|
||||
- (void)setVolume:(CGFloat)value
|
||||
{
|
||||
[_gameCoreHelper setVolume:value];
|
||||
}
|
||||
|
||||
- (void)setPauseEmulation:(BOOL)pauseEmulation;
|
||||
- (void)setPauseEmulation:(BOOL)pauseEmulation
|
||||
{
|
||||
[_gameCoreHelper setPauseEmulation:pauseEmulation];
|
||||
}
|
||||
|
||||
- (void)setAudioOutputDeviceID:(AudioDeviceID)deviceID;
|
||||
- (void)setAudioOutputDeviceID:(AudioDeviceID)deviceID
|
||||
{
|
||||
[_gameCoreHelper setAudioOutputDeviceID:deviceID];
|
||||
}
|
||||
@@ -97,9 +95,9 @@ NSString * const OEGameCoreErrorDomain = @"OEGameCoreErrorDomain";
|
||||
[_gameCoreHelper insertFileAtURL:url completionHandler:
|
||||
^(BOOL success, NSError *error)
|
||||
{
|
||||
dispatch_async(self->_queue, ^{
|
||||
block(success, error);
|
||||
});
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
|
||||
block(success, error);
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -120,9 +118,8 @@ NSString * const OEGameCoreErrorDomain = @"OEGameCoreErrorDomain";
|
||||
|
||||
- (void)setShaderURL:(NSURL *)url parameters:(NSDictionary<NSString *, NSNumber *> *)parameters completionHandler:(void (^)(BOOL success, NSError * _Nullable error))block
|
||||
{
|
||||
__block __auto_type queue = _queue;
|
||||
[_gameCoreHelper setShaderURL:url parameters:parameters completionHandler:^(BOOL success, NSError *error) {
|
||||
dispatch_async(queue, ^{
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
|
||||
block(success, error);
|
||||
});
|
||||
}];
|
||||
@@ -133,79 +130,79 @@ NSString * const OEGameCoreErrorDomain = @"OEGameCoreErrorDomain";
|
||||
[_gameCoreHelper setShaderParameterValue:value forKey:key];
|
||||
}
|
||||
|
||||
- (void)setupEmulationWithCompletionHandler:(void(^)(OEIntSize screenSize, OEIntSize aspectSize))handler;
|
||||
- (void)setupEmulationWithCompletionHandler:(void(^)(OEGameCoreHelperSetupResult result))handler
|
||||
{
|
||||
[_gameCoreHelper setupEmulationWithCompletionHandler:^(OEIntSize screenSize, OEIntSize aspectSize) {
|
||||
dispatch_async(self->_queue, ^{
|
||||
handler(screenSize, aspectSize);
|
||||
[_gameCoreHelper setupEmulationWithCompletionHandler:^(OEGameCoreHelperSetupResult result) {
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
|
||||
handler(result);
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)startEmulationWithCompletionHandler:(void(^)(void))handler;
|
||||
- (void)startEmulationWithCompletionHandler:(void(^)(void))handler
|
||||
{
|
||||
[_gameCoreHelper startEmulationWithCompletionHandler:
|
||||
^{
|
||||
dispatch_async(self->_queue, ^{
|
||||
handler();
|
||||
});
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, handler);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)resetEmulationWithCompletionHandler:(void(^)(void))handler;
|
||||
- (void)resetEmulationWithCompletionHandler:(void(^)(void))handler
|
||||
{
|
||||
[_gameCoreHelper resetEmulationWithCompletionHandler:
|
||||
^{
|
||||
dispatch_async(self->_queue, ^{
|
||||
handler();
|
||||
});
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, handler);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)stopEmulationWithCompletionHandler:(void(^)(void))handler;
|
||||
- (void)stopEmulationWithCompletionHandler:(void(^)(void))handler
|
||||
{
|
||||
[_gameCoreHelper stopEmulationWithCompletionHandler:
|
||||
^{
|
||||
dispatch_async(self->_queue, ^{
|
||||
handler();
|
||||
[self stop];
|
||||
});
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
|
||||
handler();
|
||||
[self stop];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)saveStateToFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL success, NSError *error))block;
|
||||
- (void)saveStateToFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL success, NSError *error))block
|
||||
{
|
||||
[_gameCoreHelper saveStateToFileAtPath:fileName completionHandler:
|
||||
^(BOOL success, NSError *error)
|
||||
{
|
||||
dispatch_async(self->_queue, ^{
|
||||
block(success, error);
|
||||
});
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
|
||||
block(success, error);
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)loadStateFromFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL success, NSError *error))block;
|
||||
- (void)loadStateFromFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL success, NSError *error))block
|
||||
{
|
||||
[_gameCoreHelper loadStateFromFileAtPath:fileName completionHandler:
|
||||
^(BOOL success, NSError *error)
|
||||
{
|
||||
dispatch_async(self->_queue, ^{
|
||||
block(success, error);
|
||||
});
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
|
||||
block(success, error);
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)captureOutputImageWithCompletionHandler:(void (^)(NSBitmapImageRep *image))block
|
||||
{
|
||||
[_gameCoreHelper captureOutputImageWithCompletionHandler:^(NSBitmapImageRep *image) {
|
||||
block(image);
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
|
||||
block(image);
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)captureSourceImageWithCompletionHandler:(void (^)(NSBitmapImageRep *image))block
|
||||
{
|
||||
[_gameCoreHelper captureSourceImageWithCompletionHandler:^(NSBitmapImageRep *image) {
|
||||
block(image);
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
|
||||
block(image);
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -237,7 +234,7 @@ NSString * const OEGameCoreErrorDomain = @"OEGameCoreErrorDomain";
|
||||
- (void)_notifyGameCoreDidTerminate
|
||||
{
|
||||
__weak OEGameCoreManager *weakSelf = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
|
||||
OEGameCoreManager *strongSelf = weakSelf;
|
||||
if (!strongSelf)
|
||||
return;
|
||||
|
||||
@@ -31,6 +31,27 @@
|
||||
#import "OESystemPlugin.h"
|
||||
#import "OpenEmuHelperApp.h"
|
||||
|
||||
@interface OEThreadStartup: NSObject
|
||||
@property (nonatomic, nullable) void(^completionHandler)(void);
|
||||
@property (nonatomic, nullable) void(^errorHandler)(NSError *error);
|
||||
- (instancetype)initWithCompletionHandler:(void(^)(void))completion errorHandler:(void(^)(NSError *error))error;
|
||||
@end
|
||||
|
||||
@implementation OEThreadStartup
|
||||
|
||||
- (instancetype)initWithCompletionHandler:(void(^)(void))completion errorHandler:(void(^)(NSError *error))error
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_completionHandler = [completion copy];
|
||||
_errorHandler = [error copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation OEThreadGameCoreManager
|
||||
{
|
||||
NSThread *_helperThread;
|
||||
@@ -41,17 +62,14 @@
|
||||
|
||||
OEThreadProxy *_gameCoreOwnerProxy;
|
||||
|
||||
void(^_completionHandler)(void);
|
||||
void(^_errorHandler)(NSError *error);
|
||||
void(^_stopHandler)(void);
|
||||
}
|
||||
|
||||
- (void)loadROMWithCompletionHandler:(void(^)(void))completionHandler errorHandler:(void(^)(NSError *))errorHandler;
|
||||
- (void)loadROMWithCompletionHandler:(void(^)(void))completionHandler errorHandler:(void(^)(NSError *))errorHandler
|
||||
{
|
||||
_completionHandler = [completionHandler copy];
|
||||
_errorHandler = [errorHandler copy];
|
||||
OEThreadStartup *startup = [[OEThreadStartup alloc] initWithCompletionHandler:completionHandler errorHandler:errorHandler];
|
||||
|
||||
_helperThread = [[NSThread alloc] initWithTarget:self selector:@selector(_executionThread:) object:nil];
|
||||
_helperThread = [[NSThread alloc] initWithTarget:self selector:@selector(_executionThread:) object:startup];
|
||||
_helperThread.name = @"org.openemu.core-manager-thread";
|
||||
_helperThread.qualityOfService = NSQualityOfServiceUserInitiated;
|
||||
|
||||
@@ -63,7 +81,7 @@
|
||||
[_helperThread start];
|
||||
}
|
||||
|
||||
- (void)_executionThread:(id)object
|
||||
- (void)_executionThread:(OEThreadStartup *)startup
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
@@ -73,21 +91,22 @@
|
||||
NSError *error;
|
||||
if(![_helper loadWithStartupInfo:self.startupInfo error:&error])
|
||||
{
|
||||
if(_errorHandler != nil)
|
||||
if(startup.errorHandler != nil)
|
||||
{
|
||||
__block __auto_type errorHandler = _errorHandler;
|
||||
_errorHandler = nil;
|
||||
dispatch_async(self.queue, ^{
|
||||
__block __auto_type errorHandler = startup.errorHandler;
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
|
||||
errorHandler(error);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_completionHandler) {
|
||||
dispatch_async(self.queue, _completionHandler);
|
||||
_completionHandler = nil;
|
||||
if (startup.completionHandler) {
|
||||
__block __auto_type completionHandler = startup.completionHandler;
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, completionHandler);
|
||||
}
|
||||
|
||||
startup = nil;
|
||||
|
||||
_dummyTimer = [NSTimer scheduledTimerWithTimeInterval:1e9 repeats:YES block:^(NSTimer * _Nonnull timer) {}];
|
||||
|
||||
@@ -95,7 +114,7 @@
|
||||
|
||||
if(_stopHandler)
|
||||
{
|
||||
dispatch_async(self.queue, _stopHandler);
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, _stopHandler);
|
||||
_stopHandler = nil;
|
||||
}
|
||||
}
|
||||
@@ -116,8 +135,6 @@
|
||||
_helperProxy = nil;
|
||||
_helper = nil;
|
||||
_gameCoreOwnerProxy = nil;
|
||||
_completionHandler = nil;
|
||||
_errorHandler = nil;
|
||||
}
|
||||
|
||||
- (void)stop
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
// Copyright (c) 2019, OpenEmu Team
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the OpenEmu Team nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY OpenEmu Team ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY
|
||||
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#import "OEXPCDebugSupport.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#import <assert.h>
|
||||
#import <stdbool.h>
|
||||
#import <sys/types.h>
|
||||
#import <unistd.h>
|
||||
#import <sys/sysctl.h>
|
||||
#import <stdatomic.h>
|
||||
|
||||
|
||||
@implementation OEXPCCDebugSupport
|
||||
|
||||
+ (BOOL)debuggerAttached
|
||||
{
|
||||
int junk;
|
||||
int mib[4];
|
||||
struct kinfo_proc info;
|
||||
size_t size;
|
||||
|
||||
// Initialize the flags so that, if sysctl fails for some bizarre
|
||||
// reason, we get a predictable result.
|
||||
|
||||
info.kp_proc.p_flag = 0;
|
||||
|
||||
// Initialize mib, which tells sysctl the info we want, in this case
|
||||
// we're looking for information about a specific process ID.
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC;
|
||||
mib[2] = KERN_PROC_PID;
|
||||
mib[3] = getpid();
|
||||
|
||||
// Call sysctl.
|
||||
|
||||
size = sizeof(info);
|
||||
junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
|
||||
assert(junk == 0);
|
||||
|
||||
// We're being debugged if the P_TRACED flag is set.
|
||||
|
||||
return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
|
||||
}
|
||||
|
||||
+ (BOOL)OE_waitForDebuggerUntilTime:(dispatch_time_t)t
|
||||
{
|
||||
dispatch_queue_global_t queue = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
|
||||
|
||||
__block dispatch_semaphore_t sem = dispatch_semaphore_create(0);
|
||||
__block atomic_bool done;
|
||||
atomic_init(&done, false);
|
||||
|
||||
dispatch_async(queue, ^{
|
||||
while (!self.debuggerAttached && atomic_load(&done) == false)
|
||||
{
|
||||
usleep(100);
|
||||
}
|
||||
dispatch_semaphore_signal(sem);
|
||||
});
|
||||
|
||||
BOOL ok = dispatch_semaphore_wait(sem, t) == 0;
|
||||
atomic_store(&done, true);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
+ (BOOL)waitForDebuggerUntil:(NSUInteger)nanoseconds
|
||||
{
|
||||
return [self OE_waitForDebuggerUntilTime:dispatch_time(DISPATCH_TIME_NOW, nanoseconds)];
|
||||
}
|
||||
|
||||
+ (void)waitForDebugger
|
||||
{
|
||||
[self OE_waitForDebuggerUntilTime:DISPATCH_TIME_FOREVER];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -64,7 +64,7 @@
|
||||
return [NSBundle.mainBundle URLForAuxiliaryExecutable:name];
|
||||
}
|
||||
|
||||
- (void)loadROMWithCompletionHandler:(void(^)(void))completionHandler errorHandler:(void(^)(NSError *))errorHandler;
|
||||
- (void)loadROMWithCompletionHandler:(void(^)(void))completionHandler errorHandler:(void(^)(NSError *))errorHandler
|
||||
{
|
||||
NSError *error = nil;
|
||||
_helperConnection = [NSXPCConnection connectionWithServiceName:self.serviceName executableURL:self.executableURL error:&error];
|
||||
@@ -78,8 +78,7 @@
|
||||
code:OEGameCoreCouldNotLoadROMError
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
dispatch_async(self.queue, ^{
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
|
||||
errorHandler(error);
|
||||
});
|
||||
|
||||
@@ -120,10 +119,7 @@
|
||||
os_log_error(OE_LOG_HELPER, "Helper Connection (%p) failed with error: %{public}@",
|
||||
gameCoreHelperPointer, error);
|
||||
|
||||
dispatch_async(self.queue, ^{
|
||||
errorHandler(error);
|
||||
[self stop];
|
||||
});
|
||||
[self stop];
|
||||
}];
|
||||
|
||||
gameCoreHelperPointer = (__bridge void *)gameCoreHelper;
|
||||
@@ -135,7 +131,7 @@
|
||||
{
|
||||
if(error != nil)
|
||||
{
|
||||
dispatch_async(self.queue, ^{
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
|
||||
errorHandler(error);
|
||||
[self stop];
|
||||
});
|
||||
@@ -146,13 +142,11 @@
|
||||
}
|
||||
|
||||
[self setGameCoreHelper:gameCoreHelper];
|
||||
dispatch_async(self.queue, ^{
|
||||
completionHandler();
|
||||
});
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, completionHandler);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)stop{
|
||||
- (void)stop {
|
||||
[self setGameCoreHelper:nil];
|
||||
_gameCoreOwnerProxy = nil;
|
||||
[_helperConnection invalidate];
|
||||
|
||||
@@ -442,13 +442,18 @@ static os_log_t LOG_DISPLAY;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setupEmulationWithCompletionHandler:(void(^)(OEIntSize screenSize, OEIntSize aspectSize))handler
|
||||
- (void)setupEmulationWithCompletionHandler:(void(^)(OEGameCoreHelperSetupResult result))handler
|
||||
{
|
||||
[_gameCore setupEmulationWithCompletionHandler:^{
|
||||
[self setupGameCoreAudioAndVideo];
|
||||
|
||||
if(handler)
|
||||
handler(self->_previousScreenRect.size, self->_previousAspectSize);
|
||||
{
|
||||
handler((OEGameCoreHelperSetupResult){
|
||||
.screenSize = self->_previousScreenRect.size,
|
||||
.aspectSize = self->_previousAspectSize
|
||||
});
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2021, OpenEmu Team
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the OpenEmu Team nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY OpenEmu Team ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY
|
||||
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import Foundation
|
||||
import Darwin
|
||||
|
||||
@objc
|
||||
@objcMembers
|
||||
public class XPCDebugSupport: NSObject {
|
||||
|
||||
/// Returns a value indicating whether a debugger is attached to the current process
|
||||
public static var isDebuggerAttached: Bool {
|
||||
let keys = [CTL_KERN, KERN_PROC, KERN_PROC_PID, Int32(getpid())]
|
||||
return keys.withUnsafeBufferPointer { keysPtr -> Bool in
|
||||
var info = kinfo_proc()
|
||||
info.kp_proc.p_flag = 0
|
||||
|
||||
var size = MemoryLayout.size(ofValue: info)
|
||||
let res = Darwin.sysctl(UnsafeMutablePointer<Int32>(mutating: keysPtr.baseAddress), UInt32(keys.count), &info, &size, nil, 0)
|
||||
// Assume no debugger if sysctl fails
|
||||
guard res == 0 else { return false }
|
||||
|
||||
return (info.kp_proc.p_flag & P_TRACED) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait until a debuger is attached for a specific amount of time.
|
||||
/// - Parameter time: The time to wait for a debugger to be attached.
|
||||
/// - Returns: A value indicating whether the debugger is attached.
|
||||
@discardableResult public static func waitForDebugger(until time: Date = .distantFuture) -> Bool {
|
||||
while !isDebuggerAttached && Date() < time {
|
||||
// check every 100ms
|
||||
Thread.sleep(forTimeInterval: 0.100)
|
||||
}
|
||||
return isDebuggerAttached
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user