Mac: screensaver fixes for MacOS 14

This commit is contained in:
Charlie Fenton 2024-05-03 01:12:27 -07:00
parent 9464ab7158
commit 8924ba43b6
7 changed files with 305 additions and 133 deletions

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2023 University of California
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -47,14 +47,15 @@ bool getShow_default_ss_first();
double getGFXDefaultPeriod();
double getGFXSciencePeriod();
double getGGFXChangePeriod();
void incompatibleGfxApp(char * appPath, pid_t pid, int slot);
void incompatibleGfxApp(char * appPath, char * wuName, pid_t pid, int slot);
void setShow_default_ss_first(bool value);
void setGFXDefaultPeriod(double value);
void setGFXSciencePeriod(double value);
void setGGFXChangePeriod(double value);
double getDTime();
void doBoinc_Sleep(double seconds);
void launchedGfxApp(char * appPath, pid_t thePID, int slot);
void launchedGfxApp(char * appPath, char * wuName, pid_t thePID, int slot);
int compareBOINCLibVersionTo(int toMajor, int toMinor);
void print_to_log_file(const char *format, ...);
void strip_cr(char *buf);
void PrintBacktrace(void);
@ -63,7 +64,8 @@ extern bool gIsMojave;
extern bool gIsCatalina;
extern bool gIsHighSierra;
extern bool gIsSonoma;
extern bool gCant_Use_Shared_Offscreen_Buffer;
extern bool gMach_bootstrap_unavailable_to_screensavers;
extern mach_port_name_t commsPort;
#ifdef __cplusplus
} // extern "C"

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2023 University of California
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -51,8 +51,9 @@
@property (NS_NONATOMIC_IOSONLY, readonly) GLuint currentTextureName;
- (void)init:(NSView*)saverView;
- (void)portDied:(NSNotification *)notification;
- (void)testConnection;
- (void)portDied:(NSNotification *)notification;
- (void)cleanUpOpenGL;
@end
@ -84,14 +85,15 @@ bool getShow_default_ss_first();
double getGFXDefaultPeriod();
double getGFXSciencePeriod();
double getGGFXChangePeriod();
void incompatibleGfxApp(char * appPath, pid_t pid, int slot);
void incompatibleGfxApp(char * appPath, char * wuName, pid_t pid, int slot);
void setShow_default_ss_first(bool value);
void setGFXDefaultPeriod(double value);
void setGFXSciencePeriod(double value);
void setGGFXChangePeriod(double value);
double getDTime();
void doBoinc_Sleep(double seconds);
void launchedGfxApp(char * appPath, pid_t thePID, int slot);
void launchedGfxApp(char * appPath, char * wuName, pid_t thePID, int slot);
int compareBOINCLibVersionTo(int toMajor, int toMinor);
void print_to_log_file(const char *format, ...);
void strip_cr(char *buf);
void PrintBacktrace(void);
@ -100,7 +102,9 @@ extern bool gIsCatalina;
extern bool gIsHighSierra;
extern bool gIsMojave;
extern bool gIsSonoma;
extern bool gCant_Use_Shared_Offscreen_Buffer;
extern bool gMach_bootstrap_unavailable_to_screensavers;
extern mach_port_name_t commsPort;
#ifdef __cplusplus
} // extern "C"

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2023 University of California
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -150,6 +150,7 @@ static pid_t childPid;
static int gfxAppWindowNum;
static NSView *imageView;
static char gfxAppPath[MAXPATHLEN];
static char gfxWuName[MAXPATHLEN];
static int taskSlot;
static NSRunningApplication *childApp;
static double gfxAppStartTime;
@ -175,12 +176,16 @@ static bool myIsPreview;
#define MAXWAITFORCONNECTIONCATALINA 12.0
#define MAX_CGWINDOWLIST_TRIES 3
#define MAJORBOINCGFXLIBNEEDEDFORSONOMA 8
#define MINORBOINCGFXLIBNEEDEDFORSONOMA 1
int signof(float x) {
return (x > 0.0 ? 1 : -1);
}
void launchedGfxApp(char * appPath, pid_t thePID, int slot) {
void launchedGfxApp(char * appPath, char * wuName, pid_t thePID, int slot) {
strlcpy(gfxAppPath, appPath, sizeof(gfxAppPath));
strlcpy(gfxWuName, wuName, sizeof(gfxWuName));
childPid = thePID;
taskSlot = slot;
gfxAppStartTime = getDTime();
@ -188,16 +193,24 @@ void launchedGfxApp(char * appPath, pid_t thePID, int slot) {
if (thePID == 0) {
useCGWindowList = false;
gfxAppStartTime = 0.0;
gfxWuName[0] = '\0';
if (mySharedGraphicsController) {
[ mySharedGraphicsController cleanUpOpenGL ];
}
if (imageView) {
// removeFromSuperview must be called from main thread
if (pthread_equal(mainThreadID, pthread_self())) {
if (NSThread.isMainThread) {
[imageView removeFromSuperview]; // Releases imageView
imageView = nil;
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[imageView removeFromSuperview]; // Releases imageView
});
}
imageView = nil;
}
} else {
if (gCant_Use_Shared_Offscreen_Buffer) {
stopAllGFXApps();
} else { // thePID != 0
if (childPid) {
childApp = [NSRunningApplication runningApplicationWithProcessIdentifier:childPid];
}
}
}
@ -408,7 +421,13 @@ void launchedGfxApp(char * appPath, pid_t thePID, int slot) {
if (imageView) {
useCGWindowList = false;
// removeFromSuperview must be called from main thread
[imageView removeFromSuperview]; // Releases imageView
if (NSThread.isMainThread) {
[imageView removeFromSuperview]; // Releases imageView
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[imageView removeFromSuperview]; // Releases imageView
});
}
imageView = nil;
}
@ -563,7 +582,7 @@ void launchedGfxApp(char * appPath, pid_t thePID, int slot) {
//
if (childApp) {
if (![ childApp activateWithOptions:NSApplicationActivateIgnoringOtherApps ]) {
launchedGfxApp("", 0, -1); // Graphics app is no longer running
launchedGfxApp("", "", 0, -1); // Graphics app is no longer running
} else if (useCGWindowList) {
// As a safety precaution, prevent terminating gfx app while copying its window
pthread_mutex_lock(&saver_mutex);
@ -611,7 +630,13 @@ void launchedGfxApp(char * appPath, pid_t thePID, int slot) {
if (imageView && !useCGWindowList) {
// removeFromSuperview must be called from main thread
[imageView removeFromSuperview]; // Releases imageView
if (NSThread.isMainThread) {
[imageView removeFromSuperview]; // Releases imageView
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[imageView removeFromSuperview]; // Releases imageView
});
}
imageView = nil;
}
@ -631,16 +656,26 @@ void launchedGfxApp(char * appPath, pid_t thePID, int slot) {
maxWaitTime = gIsCatalina ? MAXWAITFORCONNECTIONCATALINA : MAXWAITFORCONNECTION;
if ((getDTime() - gfxAppStartTime) > maxWaitTime) {
if (gIsCatalina) {
if (gMach_bootstrap_unavailable_to_screensavers) {
// Is the gfx app built with a new enough BOINC graphics library version?
if (compareBOINCLibVersionTo(MAJORBOINCGFXLIBNEEDEDFORSONOMA, MINORBOINCGFXLIBNEEDEDFORSONOMA) >= 0) {
runningSharedGraphics = true;
if (childPid) {
gfxAppStartTime = 0.0;
childApp = [NSRunningApplication runningApplicationWithProcessIdentifier:childPid];
}
}
}
// CGWindowListCopyWindowInfo and CGWindowListCreateImage can copy
// windows between user boinc_project and the user running the
// screensaver only if OS < 10.15 (before Catalina)
incompatibleGfxApp(gfxAppPath, childPid, taskSlot);
incompatibleGfxApp(gfxAppPath, gfxWuName, childPid, taskSlot);
} else {
if (++CGWindowListTries > MAX_CGWINDOWLIST_TRIES) {
// After displaying message for 5 seconds, incompatibleGfxApp
// will call launchedGfxApp("", 0, -1) which will clear
// gfxAppStartTime and CGWindowListTries
incompatibleGfxApp(gfxAppPath, childPid, taskSlot);
incompatibleGfxApp(gfxAppPath, gfxWuName, childPid, taskSlot);
} else {
if ([self setUpToUseCGWindowList]) {
CGWindowListTries = 0;
@ -1088,12 +1123,14 @@ static bool okToDraw;
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSPortDidBecomeInvalidNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(portDied:) name:NSPortDidBecomeInvalidNotification object:nil];
openGLView = nil;
[self testConnection];
if (! gMach_bootstrap_unavailable_to_screensavers) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(portDied:) name:NSPortDidBecomeInvalidNotification object:nil];
}
}
@ -1101,60 +1138,129 @@ static bool okToDraw;
{
mach_port_t servicePortNum = MACH_PORT_NULL;
kern_return_t machErr;
char *portName = "edu.berkeley.boincsaver";
char *portNameV1 = "edu.berkeley.boincsaver";
char *portNameV2 = "edu.berkeley.boincsaver-v2";
// Try to check in with master.
if ((!gMach_bootstrap_unavailable_to_screensavers) || (serverPort == MACH_PORT_NULL)) {
// Try to check in with master.
// NSMachBootstrapServer is deprecated in OS 10.13, so use bootstrap_look_up
// serverPort = [(NSMachPort *)([[NSMachBootstrapServer sharedInstance] portForName:@"edu.berkeley.boincsaver"]) retain];
machErr = bootstrap_look_up(bootstrap_port, portName, &servicePortNum);
if (machErr == KERN_SUCCESS) {
serverPort = (NSMachPort*)[NSMachPort portWithMachPort:servicePortNum];
} else {
if (machErr == BOOTSTRAP_NOT_PRIVILEGED) {
// As of MacOS 14.0, the legacyScreenSave sandbox prevents using
// IOSurfaceLookupFromMachPort. I have filed bug report FB13300491
// with Apple and hope they will change this in future MacOS.
gCant_Use_Shared_Offscreen_Buffer = true;
stopAllGFXApps();
machErr = bootstrap_look_up(bootstrap_port, portNameV1, &servicePortNum);
if (machErr != KERN_SUCCESS) {
if (machErr != BOOTSTRAP_NOT_PRIVILEGED) {
// bootstrap_look_up() returned error other than BOOTSTRAP_NOT_PRIVILEGED
// machErr is probably BOOTSTRAP_UNKNOWN_SERVICE because no gfx app is running
// Keep trying bootstrap_look_up() until we get a connection from gfx app
serverPort = MACH_PORT_NULL;
} else { // bootstrap_look_up() returned error BOOTSTRAP_NOT_PRIVILEGED
// As of MacOS 14.0, the legacyScreenSave sandbox prevents using
// bootstrap_look_up. I have filed bug report FB13300491 with
// Apple and hope they will change this in a future MacOS.
machErr = bootstrap_check_in(bootstrap_port, portNameV2, &servicePortNum);
if (machErr != KERN_SUCCESS) {
[NSApp terminate:nil];
}
gMach_bootstrap_unavailable_to_screensavers = true;
} // bootstrap_look_up() returned error BOOTSTRAP_NOT_PRIVILEGED
} // bootstrap_look_up() did not return KERN_SUCCESS
if (machErr == KERN_SUCCESS) {
serverPort = (NSMachPort*)[NSMachPort portWithMachPort:servicePortNum];
}
if ((serverPort != MACH_PORT_NULL) && (localPort == MACH_PORT_NULL)) {
// Retrieve raw mach port names.
serverPortName = [serverPort machPort];
if (gMach_bootstrap_unavailable_to_screensavers) {
// Register server port with the current runloop.
[serverPort setDelegate:self];
[serverPort scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
} else { // (! gMach_bootstrap_unavailable_to_screensavers)
// Create our own local port.
localPort = [[NSMachPort alloc] init];
localPortName = [localPort machPort];
// Register our local port with the current runloop.
[localPort setDelegate:self];
[localPort scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
// Check in with server.
int kr;
kr = _MGCCheckinClient(serverPortName, localPortName, &clientIndex);
if(kr != 0) {
[NSApp terminate:nil];
}
runningSharedGraphics = true;
if (childPid) {
gfxAppStartTime = 0.0;
childApp = [NSRunningApplication runningApplicationWithProcessIdentifier:childPid];
}
} // (! gMach_bootstrap_unavailable_to_screensavers)
} // ((serverPort != MACH_PORT_NULL) && (localPort == MACH_PORT_NULL))
} // ((!gMach_bootstrap_unavailable_to_screensavers) || (serverPort == MACH_PORT_NULL))
}
- (void)cleanUpOpenGL
{
if (gMach_bootstrap_unavailable_to_screensavers) {
[serverPort removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
childPid=0;
childApp = nil;
if (gMach_bootstrap_unavailable_to_screensavers ||
((serverPort == MACH_PORT_NULL) && (localPort == MACH_PORT_NULL))
) {
if (openGLView) {
if (NSThread.isMainThread) {
[openGLView removeFromSuperview]; // Releases openGLView
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[openGLView removeFromSuperview]; // Releases openGLView
});
}
openGLView = nil;
}
int i;
for(i = 0; i < NUM_IOSURFACE_BUFFERS; i++) {
if (_ioSurfaceBuffers[i]) {
CFRelease(_ioSurfaceBuffers[i]);
_ioSurfaceBuffers[i] = nil;
}
// if (glIsTexture(_textureNames[i])) {
// glDeleteTextures(1, _textureNames[i]);
// }
_textureNames[i] = 0;
if (_ioSurfaceMachPorts[i] != MACH_PORT_NULL) {
mach_port_deallocate(mach_task_self(), _ioSurfaceMachPorts[i]);
_ioSurfaceMachPorts[i] = MACH_PORT_NULL;
}
serverPort = MACH_PORT_NULL;
}
if ((serverPort != MACH_PORT_NULL) && (localPort == MACH_PORT_NULL))
{
// Create our own local port.
localPort = [[NSMachPort alloc] init];
// Retrieve raw mach port names.
serverPortName = [serverPort machPort];
localPortName = [localPort machPort];
// Register our local port with the current runloop.
[localPort setDelegate:self];
[localPort scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
// Check in with server.
int kr;
kr = _MGCCheckinClient(serverPortName, localPortName, &clientIndex);
if(kr != 0)
[NSApp terminate:nil];
runningSharedGraphics = true;
if (childPid) {
gfxAppStartTime = 0.0;
childApp = [NSRunningApplication runningApplicationWithProcessIdentifier:childPid];
runningSharedGraphics = false; // Do this last!!
if (gMach_bootstrap_unavailable_to_screensavers) {
[serverPort scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
}
}
- (void)portDied:(NSNotification *)notification
{
if (gMach_bootstrap_unavailable_to_screensavers) {
return; // This should never be called if using MacOS 14 workaround
}
NSPort *port = [notification object];
if(port == serverPort) {
childApp = nil;
gfxAppStartTime = 0.0;
gfxAppPath[0] = '\0';
gfxWuName[0] = '\0';
if ([serverPort isValid]) {
[serverPort invalidate];
@ -1169,29 +1275,7 @@ static bool okToDraw;
// [localPort release];
localPort = MACH_PORT_NULL;
int i;
for(i = 0; i < NUM_IOSURFACE_BUFFERS; i++) {
if (_ioSurfaceBuffers[i]) {
CFRelease(_ioSurfaceBuffers[i]);
_ioSurfaceBuffers[i] = nil;
}
// if (glIsTexture(_textureNames[i])) {
// glDeleteTextures(1, _textureNames[i]);
// }
_textureNames[i] = 0;
if (_ioSurfaceMachPorts[i] != MACH_PORT_NULL) {
mach_port_deallocate(mach_task_self(), _ioSurfaceMachPorts[i]);
_ioSurfaceMachPorts[i] = MACH_PORT_NULL;
}
}
if ((serverPort == MACH_PORT_NULL) && (localPort == MACH_PORT_NULL)) {
runningSharedGraphics = false;
[openGLView removeFromSuperview]; // Releases openGLView
openGLView = nil;
}
[ self cleanUpOpenGL ];
}
}
@ -1206,13 +1290,16 @@ static bool okToDraw;
{
kr = mach_msg(reply_header, MACH_SEND_MSG, reply_header->msgh_size, 0, MACH_PORT_NULL,
0, MACH_PORT_NULL);
if(kr != 0)
if(kr != 0) {
[NSApp terminate:nil];
}
}
}
- (kern_return_t)displayFrame:(int32_t)frameIndex surfacemachport:(mach_port_t)iosurface_port
{
if (!childPid) return 0;
nextFrameIndex = frameIndex;
if(!_ioSurfaceBuffers[frameIndex])
@ -1239,11 +1326,21 @@ static bool okToDraw;
_textureNames[frameIndex] = [openGLView setupIOSurfaceTexture:_ioSurfaceBuffers[frameIndex]];
}
okToDraw = true; // Tell drawRect that we have real data to display
if (openGLView) {
okToDraw = true; // Tell drawRect that we have real data to display
[openGLView setNeedsDisplay:YES];
[openGLView display];
[openGLView setNeedsDisplay:YES];
[openGLView display];
}
if (gMach_bootstrap_unavailable_to_screensavers && (runningSharedGraphics == false)) {
// We have now heard from gfx app so connection has been established
runningSharedGraphics = true;
if (childPid) {
gfxAppStartTime = 0.0;
childApp = [NSRunningApplication runningApplicationWithProcessIdentifier:childPid];
}
}
return 0;
}
@ -1283,8 +1380,9 @@ kern_return_t _MGSDisplayFrame(mach_port_t server_port, int32_t frame_index, mac
NSOpenGLPixelFormat *pix_fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
if(!pix_fmt)
if(!pix_fmt) {
[ NSApp terminate:nil];
}
self = [super initWithFrame:frame pixelFormat:pix_fmt];
@ -1431,12 +1529,6 @@ static bool UseSharedOffscreenBuffer() {
static bool needSharedGfxBuffer = false;
//return true; // FOR TESTING ONLY
// As of MacOS 14.0, the legacyScreenSaver sandbox prevents using
// IOSurfaceLookupFromMachPort. I have filed bug report FB13300491
// with Apple and hope they will change this in future MacOS.
if (gCant_Use_Shared_Offscreen_Buffer) {
return false;
}
if (alreadyTested) {
return needSharedGfxBuffer;
}

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2023 University of California
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -51,9 +51,17 @@ int GFX_PidFromScreensaver = 0;
pthread_t MonitorParentThread = 0;
bool quit_MonitorParentThread = false;
// struct ss_shmem_data must be kept in sync in these files:
// screensaver.cpp
// gfx_switcher.cpp
// gfx_cleanup.mm
// graphics2_unix.cpp
struct ss_shmem_data {
pid_t gfx_pid;
int gfx_slot;
int major_version;
int minor_version;
int release;
};
static struct ss_shmem_data* ss_shmem = NULL;
@ -122,6 +130,9 @@ void killGfxApp(pid_t thePID) {
if (ss_shmem) {
rpc->run_graphics_app("stop", ss_shmem->gfx_slot, "");
ss_shmem->gfx_slot = -1;
ss_shmem->major_version = 0;
ss_shmem->minor_version = 0;
ss_shmem->release = 0;
}
rpc->close();

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2023 University of California
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -82,9 +82,17 @@ static void strip_cr(char *buf);
void * MonitorScreenSaverEngine(void* param);
// struct ss_shmem_data must be kept in sync in these files:
// screensaver.cpp
// gfx_switcher.cpp
// gfx_cleanup.mm
// graphics2_unix.cpp
struct ss_shmem_data {
pid_t gfx_pid;
int gfx_slot;
int major_version;
int minor_version;
int release;
};
static struct ss_shmem_data* ss_shmem = NULL;
@ -222,6 +230,9 @@ int main(int argc, char** argv) {
pthread_cancel(monitorScreenSaverEngineThread);
if (ss_shmem != 0) {
ss_shmem->gfx_pid = 0;
ss_shmem->major_version = 0;
ss_shmem->minor_version = 0;
ss_shmem->release = 0;
}
return 0;
}
@ -287,6 +298,9 @@ int main(int argc, char** argv) {
pthread_cancel(monitorScreenSaverEngineThread);
if (ss_shmem != 0) {
ss_shmem ->gfx_pid = 0;
ss_shmem->major_version = 0;
ss_shmem->minor_version = 0;
ss_shmem->release = 0;
}
return 0;
}

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2023 University of California
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -112,8 +112,8 @@ bool gIsCatalina = false; // OS 10.15 or later
bool gIsSonoma = false; // OS 14.0 or later
// As of MacOS 14.0, the legacyScreenSave sandbox
// prevents using IOSurfaceLookupFromMachPort.
bool gCant_Use_Shared_Offscreen_Buffer = false;
// prevents using bootstrap_look_up.
bool gMach_bootstrap_unavailable_to_screensavers = false;
const char * CantLaunchCCMsg = "Unable to launch BOINC application.";
const char * LaunchingCCMsg = "Launching BOINC application.";
@ -128,7 +128,6 @@ const char * DefaultGFXAppCrashedMsg = "Default screensaver module had an unrec
const char * RunningOnBatteryMsg = "Computing and screensaver disabled while running on battery power.";
const char * IncompatibleMsg = "Could not connect to screensaver ";
const char * CCNotRunningMsg = "BOINC is not running.";
const char * NoScreenSaverGraphicsThisMacOS = "BOINC can't show screensaver graphics on this version of MacOS";
//const char * BOINCExitedSaverMode = "BOINC is no longer in screensaver mode.";
@ -207,7 +206,7 @@ void closeBOINCSaver() {
}
void incompatibleGfxApp(char * appPath, pid_t pid, int slot){
void incompatibleGfxApp(char * appPath, char * wuName, pid_t pid, int slot){
char *p;
static char buf[1024];
static double msgstartTime = 0.0;
@ -253,8 +252,8 @@ void incompatibleGfxApp(char * appPath, pid_t pid, int slot){
} // End if (msgstartTime == 0.0)
if (msgstartTime && (getDTime() - msgstartTime > 5.0)) {
gspScreensaver->markAsIncompatible(appPath);
launchedGfxApp("", 0, -1);
gspScreensaver->markAsIncompatible(wuName);
launchedGfxApp("", "", 0, -1);
msgstartTime = 0.0;
gspScreensaver->terminate_v6_screensaver(pid);
}
@ -356,7 +355,6 @@ CScreensaver::CScreensaver() {
GetDefaultDisplayPeriods(periods);
m_bShow_default_ss_first = periods.Show_default_ss_first;
m_fGFXDefaultPeriod = periods.GFXDefaultPeriod;
m_fGFXSciencePeriod = periods.GFXSciencePeriod;
m_fGFXChangePeriod = periods.GFXChangePeriod;
@ -479,11 +477,6 @@ int CScreensaver::getSSMessage(char **theMessage, int* coveredFreq) {
return NOTEXTLOGOFREQUENCY;
}
if (gCant_Use_Shared_Offscreen_Buffer) {
gspScreensaver->setSSMessageText(NoScreenSaverGraphicsThisMacOS);
goto msgIsSet;
}
CheckDualGPUPowerSource();
switch (saverState) {
@ -654,7 +647,6 @@ int CScreensaver::getSSMessage(char **theMessage, int* coveredFreq) {
}
}
msgIsSet:
if (m_MessageText[0]) {
newFrequency = TEXTLOGOFREQUENCY;
} else {

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2023 University of California
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -102,13 +102,23 @@ extern pthread_mutex_t saver_mutex;
RESULT* graphics_app_result_ptr = NULL;
#ifdef __APPLE__
// struct ss_shmem_data must be kept in sync in these files:
// screensaver.cpp
// gfx_switcher.cpp
// gfx_cleanup.mm
// graphics2_unix.cpp
struct ss_shmem_data {
pid_t gfx_pid;
int gfx_slot;
int major_version;
int minor_version;
int release;
};
static struct ss_shmem_data* ss_shmem = NULL;
bool canAccessProjectGFXApps = true;
static double graphicsAppStartTime;
#endif
bool CScreensaver::is_same_task(RESULT* taska, RESULT* taskb) {
@ -136,10 +146,10 @@ int CScreensaver::count_active_graphic_apps(RESULTS& res, RESULT* exclude) {
if (is_same_task(res.results[i], exclude)) continue;
#ifdef __APPLE__
// Remove it from the vector if incompatible with current version of OS X
if (isIncompatible(res.results[i]->graphics_exec_path)) {
if (isIncompatible(res.results[i]->wu_name)) {
BOINCTRACE(
_T("count_active_graphic_apps -- removing incompatible name = '%s', path = '%s'\n"),
res.results[i]->name, res.results[i]->graphics_exec_path
res.results[i]->wu_name, res.results[i]->graphics_exec_path
);
RESULT *rp = res.results[i];
res.results.erase(res.results.begin()+i);
@ -211,23 +221,23 @@ CLEANUP:
#ifdef __APPLE__
void CScreensaver::markAsIncompatible(char *gfxAppPath) {
char *buf = (char *)malloc(strlen(gfxAppPath)+1);
void CScreensaver::markAsIncompatible(char *wuName) {
char *buf = (char *)malloc(strlen(wuName)+1);
if (buf) {
strlcpy(buf, gfxAppPath, malloc_size(buf));
strlcpy(buf, wuName, malloc_size(buf));
m_vIncompatibleGfxApps.push_back(buf);
BOINCTRACE(_T("markAsIncompatible -- path = '%s'\n"), gfxAppPath);
BOINCTRACE(_T("markAsIncompatible -- wuName = '%s'\n"), wuName);
}
}
bool CScreensaver::isIncompatible(char *appPath) {
bool CScreensaver::isIncompatible(char *wuName) {
unsigned int i = 0;
for (i = 0; i < m_vIncompatibleGfxApps.size(); i++) {
BOINCTRACE(
_T("isIncompatible -- comparing incompatible path '%s' to candidate path %s\n"),
m_vIncompatibleGfxApps[i], appPath
m_vIncompatibleGfxApps[i], wuName
);
if (strcmp(m_vIncompatibleGfxApps[i], appPath) == 0) {
if (strcmp(m_vIncompatibleGfxApps[i], wuName) == 0) {
return true;
}
}
@ -266,7 +276,7 @@ int CScreensaver::launch_screensaver(RESULT* rp, PROCESS_REF& graphics_applicati
// V6 Graphics
#ifdef __APPLE__
graphics_application = 0;
graphicsAppStartTime = 0;
// As of OS 10.15 (Catalina) screensavers can no longer:
// - launch apps that run setuid or setgid
// - launch apps downloaded from the Internet which have not been
@ -300,7 +310,8 @@ int CScreensaver::launch_screensaver(RESULT* rp, PROCESS_REF& graphics_applicati
fflush(m_gfx_Cleanup_IPC);
if (graphics_application) {
launchedGfxApp(rp->graphics_exec_path, graphics_application, rp->slot);
graphicsAppStartTime = getDTime();
launchedGfxApp(rp->graphics_exec_path, rp->wu_name, graphics_application, rp->slot);
}
#else
char* argv[3];
@ -324,6 +335,7 @@ int CScreensaver::launch_screensaver(RESULT* rp, PROCESS_REF& graphics_applicati
//
int CScreensaver::terminate_v6_screensaver(PROCESS_REF graphics_application) {
#ifdef __APPLE__
static bool in_terminate_v6_screensaver = false;
pid_t thePID;
// As of OS 10.15 (Catalina) screensavers can no longer launch apps
@ -338,31 +350,41 @@ int CScreensaver::terminate_v6_screensaver(PROCESS_REF graphics_application) {
// able to just call kill_program(graphics_application).
//
int ignore;
if (graphics_application == 0) return 0;
graphicsAppStartTime = 0; // Prevent triggering markAsIncompatible in HasProcessExited()
// MUTEX may help prevent crashes when terminating an older gfx app which
// we were displaying using CGWindowListCreateImage under OS X >= 10.13
// Also prevents reentry when called from our other thread
pthread_mutex_lock(&saver_mutex);
thePID = graphics_application;
// fprintf(stderr, "stopping pid %d\n", thePID);
if (in_terminate_v6_screensaver) {
pthread_mutex_unlock(&saver_mutex);
return 0;
}
in_terminate_v6_screensaver = true;
// print_to_log_file( "stopping pid %d\n", thePID);
rpc->run_graphics_app("stop", thePID, gUserName);
// Inform our helper app that we have stopped current graphics app
fprintf(m_gfx_Cleanup_IPC, "0\n");
fflush(m_gfx_Cleanup_IPC);
launchedGfxApp("", 0, -1);
for (int i=0; i<200; i++) {
boinc_sleep(0.01); // Wait 2 seconds max
if (HasProcessExited(graphics_application, ignore)) {
break;
}
}
// For safety, call kill_process() even under Apple sandbox security
if (graphics_application) {
kill_process(graphics_application);
}
launchedGfxApp("", "", 0, -1);
pthread_mutex_unlock(&saver_mutex);
in_terminate_v6_screensaver = false;
#endif
#ifdef _WIN32
@ -377,10 +399,10 @@ int CScreensaver::terminate_v6_screensaver(PROCESS_REF graphics_application) {
kill_process(graphics_application);
}
}
#endif
// For safety, call kill_process() even under Apple sandbox security
kill_process(graphics_application);
#endif
return 0;
}
@ -436,7 +458,7 @@ int CScreensaver::launch_default_screensaver(char *dir_path, PROCESS_REF& graphi
fflush(m_gfx_Cleanup_IPC);
if (graphics_application) {
launchedGfxApp("boincscr", graphics_application, -1);
launchedGfxApp("boincscr", "", graphics_application, -1);
}
BOINCTRACE(_T("launch_default_screensaver returned %d\n"), retval);
@ -540,6 +562,9 @@ DataMgmtProcType CScreensaver::DataManagementProc() {
graphics_app_result_ptr = NULL;
#ifdef __APPLE__
for (int i = 0; i < m_vIncompatibleGfxApps.size(); i++) {
free(m_vIncompatibleGfxApps[i]);
}
m_vIncompatibleGfxApps.clear();
default_ss_dir_path = "/Library/Application Support/BOINC Data";
char shmem_name[MAXPATHLEN];
@ -551,6 +576,9 @@ DataMgmtProcType CScreensaver::DataManagementProc() {
chmod(shmem_name, 0666);
ss_shmem->gfx_pid = 0;
ss_shmem->gfx_slot = -1;
ss_shmem->major_version = 0;
ss_shmem->minor_version = 0;
ss_shmem->release = 0;
}
if (!IsUserMemberOfGroup(gUserName, "boinc_master")) canAccessProjectGFXApps = false;
@ -680,11 +708,15 @@ DataMgmtProcType CScreensaver::DataManagementProc() {
// BOINCTRACE(_T("CScreensaver::Ending Default phase: now=%f, default_phase_start_time=%f, default_saver_duration_in_science_phase=%f\n"),
// dtime(), default_phase_start_time, default_saver_duration_in_science_phase);
ss_phase = SCIENCE_SS_PHASE;
default_phase_start_time = 0;
default_phase_start_time = 0;
default_saver_duration_in_science_phase = 0;
science_phase_start_time = dtime();
if (m_bDefault_gfx_running) {
default_saver_start_time_in_science_phase = science_phase_start_time;
for (int i = 0; i < m_vIncompatibleGfxApps.size(); i++) {
free(m_vIncompatibleGfxApps[i]);
}
m_vIncompatibleGfxApps.clear();
}
switch_to_default_gfx = false;
}
@ -945,7 +977,7 @@ DataMgmtProcType CScreensaver::DataManagementProc() {
m_bDefault_gfx_running = false;
m_bScience_gfx_running = false;
#ifdef __APPLE__
launchedGfxApp("", 0, -1);
launchedGfxApp("", "", 0, -1);
#endif
continue;
}
@ -976,6 +1008,12 @@ bool CScreensaver::HasProcessExited(pid_t pid, int &exitCode) {
if (ss_shmem) {
if (ss_shmem->gfx_pid != 0) return false;
if (ss_shmem->gfx_slot > -1) { // -1 means Default GFX, which has no slot number
if (graphicsAppStartTime && ((getDTime() - graphicsAppStartTime) < 2)) {
if (graphics_app_result_ptr && graphics_app_result_ptr->graphics_exec_path) {
markAsIncompatible(graphics_app_result_ptr->wu_name);
}
}
// Graphics apps called by screensaver or Manager (via Show
// Graphics button) now write files in their slot directory as
// the logged in user, not boinc_master. This ugly hack tells
@ -983,6 +1021,9 @@ bool CScreensaver::HasProcessExited(pid_t pid, int &exitCode) {
rpc->run_graphics_app("stop", ss_shmem->gfx_slot, "");
ss_shmem->gfx_pid = 0;
ss_shmem->gfx_slot = -1;
ss_shmem->major_version = 0;
ss_shmem->minor_version = 0;
ss_shmem->release = 0;
}
}
@ -1034,3 +1075,19 @@ void CScreensaver::GetDefaultDisplayPeriods(struct ss_periods &periods) {
periods.GFXChangePeriod
);
}
#ifdef __APPLE__
// compareBOINCLibVersionTo(x, y) returns:
// -1 if the library version is less than x.y
// 0 if the library version is equal to x.y
// +1 if the library version is lgreater than x.y
int compareBOINCLibVersionTo(int toMajor, int toMinor) {
if (ss_shmem->major_version < toMajor) return -1;
if (ss_shmem->major_version > toMajor) return 1;
// if (major == toMajor) compare minor version numbers
if (ss_shmem->minor_version < toMinor) return -1;
if (ss_shmem->minor_version > toMinor) return 1;
return 0;
}
#endif