Cocoa Port: Fix a bug where the Frame Advance and Frame Jump buttons in the Execution Control panel would cause the other buttons to enable/disable themselves inconsistently, but only if the .app was built on Xcode 10 or later.

- Apparently, KVO-based UI updates being made across threads are a big no-no in the macOS v10.14 SDK and later. So now we need to make sure that ALL KVO-based UI updates are done on the main thread only.
This commit is contained in:
rogerman 2021-08-27 13:35:10 -07:00
parent c7f85ba00a
commit b7c9b6b614
4 changed files with 91365 additions and 89500 deletions

View File

@ -1,6 +1,6 @@
/*
Copyright (C) 2011 Roger Manuel
Copyright (C) 2011-2018 DeSmuME team
Copyright (C) 2011-2021 DeSmuME team
This file is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -79,6 +79,7 @@ typedef struct
@property (assign) BOOL masterExecute;
@property (assign) BOOL isFrameSkipEnabled;
@property (assign) NSInteger coreState;
@property (assign) BOOL emulationPaused;
@property (assign) BOOL isSpeedLimitEnabled;
@property (assign) BOOL isCheatingEnabled;

View File

@ -1,6 +1,6 @@
/*
Copyright (C) 2011 Roger Manuel
Copyright (C) 2011-2018 DeSmuME team
Copyright (C) 2011-2021 DeSmuME team
This file is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -52,6 +52,7 @@ volatile bool execute = true;
@dynamic masterExecute;
@dynamic isFrameSkipEnabled;
@dynamic coreState;
@dynamic emulationPaused;
@dynamic isSpeedLimitEnabled;
@dynamic isCheatingEnabled;
@dynamic speedScalar;
@ -574,6 +575,8 @@ volatile bool execute = true;
- (void) setCoreState:(NSInteger)coreState
{
NSString *newFrameStatus = nil;
if (coreState == ExecutionBehavior_FrameJump)
{
uint64_t frameIndex = [self frameNumber];
@ -602,9 +605,7 @@ volatile bool execute = true;
}
pthread_mutex_lock(&threadParam.mutexThreadExecute);
execControl->SetExecutionBehavior((ExecutionBehavior)coreState);
pthread_rwlock_rdlock(&threadParam.rwlockOutputList);
switch ((ExecutionBehavior)coreState)
@ -616,7 +617,7 @@ volatile bool execute = true;
[cdsOutput setIdle:YES];
}
[self setFrameStatus:[NSString stringWithFormat:@"%llu", (unsigned long long)[self frameNumber]]];
newFrameStatus = [NSString stringWithFormat:@"%llu", (unsigned long long)[self frameNumber]];
[_fpsTimer invalidate];
_fpsTimer = nil;
break;
@ -629,7 +630,7 @@ volatile bool execute = true;
[cdsOutput setIdle:NO];
}
[self setFrameStatus:[NSString stringWithFormat:@"%llu", (unsigned long long)[self frameNumber]]];
newFrameStatus = [NSString stringWithFormat:@"%llu", (unsigned long long)[self frameNumber]];
[_fpsTimer invalidate];
_fpsTimer = nil;
break;
@ -642,7 +643,7 @@ volatile bool execute = true;
[cdsOutput setIdle:NO];
}
[self setFrameStatus:@"Executing..."];
newFrameStatus = @"Executing...";
if (_fpsTimer == nil)
{
@ -669,7 +670,7 @@ volatile bool execute = true;
}
}
[self setFrameStatus:[NSString stringWithFormat:@"Jumping to frame %llu.", (unsigned long long)execControl->GetFrameJumpTarget()]];
newFrameStatus = [NSString stringWithFormat:@"Jumping to frame %llu.", (unsigned long long)execControl->GetFrameJumpTarget()];
[_fpsTimer invalidate];
_fpsTimer = nil;
break;
@ -686,6 +687,22 @@ volatile bool execute = true;
[[self cdsGPU] respondToPauseState:(coreState == ExecutionBehavior_Pause)];
[[self cdsController] setHardwareMicPause:(coreState != ExecutionBehavior_Run)];
// This method affects UI updates, but can also be called from a thread that is different from
// the main thread. When compiling against the macOS v10.13 SDK and earlier, UI updates were allowed
// when doing KVO changes on other threads. However, the macOS v10.14 SDK and later now require that
// any KVO changes that affect UI updates MUST be performed on the main thread. Therefore, we need
// to push the UI-related stuff to the main thread here.
NSAutoreleasePool *tempPool = [[NSAutoreleasePool alloc] init];
NSDictionary *userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInteger:coreState], @"ExecutionState",
newFrameStatus, @"FrameStatusString",
nil];
[[NSNotificationCenter defaultCenter] postNotificationOnMainThreadName:@"org.desmume.DeSmuME.handleEmulatorExecutionState" object:self userInfo:userInfo];
[userInfo release];
[tempPool release];
}
- (NSInteger) coreState
@ -694,6 +711,19 @@ volatile bool execute = true;
return behavior;
}
- (void) setEmulationPaused:(BOOL)theState
{
// Do nothing. This is for KVO-compliance only.
// This method (actually its corresponding getter method) is really intended for
// UI updates only. If you want to pause the emulator, call setCoreState: and pass
// to it a value of ExecutionBehavior_Pause.
}
- (BOOL) emulationPaused
{
return (execControl->GetExecutionBehavior() == ExecutionBehavior_Pause) ? YES : NO;
}
- (void) setArm9ImageURL:(NSURL *)fileURL
{
const char *filePath = (fileURL != NULL) ? [[fileURL path] cStringUsingEncoding:NSUTF8StringEncoding] : NULL;

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*
Copyright (C) 2013-2018 DeSmuME Team
Copyright (C) 2013-2021 DeSmuME Team
This file is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -176,6 +176,11 @@
name:@"org.desmume.DeSmuME.handleNDSError"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleEmulatorExecutionState:)
name:@"org.desmume.DeSmuME.handleEmulatorExecutionState"
object:nil];
return self;
}
@ -2011,6 +2016,17 @@
contextInfo:nil];
}
- (void) handleEmulatorExecutionState:(NSNotification *)aNotification
{
CocoaDSCore *cdsCore = [aNotification object];
NSDictionary *userInfo = [aNotification userInfo];
ExecutionBehavior execState = (ExecutionBehavior)[(NSNumber *)[userInfo valueForKey:@"ExecutionState"] integerValue];
NSString *frameStatusString = (NSString *)[userInfo valueForKey:@"FrameStatusString"];
[cdsCore setEmulationPaused:(execState == ExecutionBehavior_Pause)];
[cdsCore setFrameStatus:frameStatusString];
}
- (void) addOutputToCore:(CocoaDSOutput *)theOutput
{
CocoaDSCore *cdsCore = (CocoaDSCore *)[cdsCoreController content];