diff --git a/clientscr/Mac_Saver_Module.h b/clientscr/Mac_Saver_Module.h index 7b8c27ce63..6f13876d67 100644 --- a/clientscr/Mac_Saver_Module.h +++ b/clientscr/Mac_Saver_Module.h @@ -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" diff --git a/clientscr/Mac_Saver_ModuleView.h b/clientscr/Mac_Saver_ModuleView.h index 08a2b64eb0..3b7dc2d96b 100644 --- a/clientscr/Mac_Saver_ModuleView.h +++ b/clientscr/Mac_Saver_ModuleView.h @@ -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" diff --git a/clientscr/Mac_Saver_ModuleView.m b/clientscr/Mac_Saver_ModuleView.m index a92c1c120e..848fec6077 100644 --- a/clientscr/Mac_Saver_ModuleView.m +++ b/clientscr/Mac_Saver_ModuleView.m @@ -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; } diff --git a/clientscr/gfx_cleanup.mm b/clientscr/gfx_cleanup.mm index 9ed6455961..214506e18f 100644 --- a/clientscr/gfx_cleanup.mm +++ b/clientscr/gfx_cleanup.mm @@ -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(); diff --git a/clientscr/gfx_switcher.cpp b/clientscr/gfx_switcher.cpp index c0b4efdb73..1e18cc62e0 100644 --- a/clientscr/gfx_switcher.cpp +++ b/clientscr/gfx_switcher.cpp @@ -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; } diff --git a/clientscr/mac_saver_module.cpp b/clientscr/mac_saver_module.cpp index 283effcd35..b67cc92ac3 100644 --- a/clientscr/mac_saver_module.cpp +++ b/clientscr/mac_saver_module.cpp @@ -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 { diff --git a/clientscr/screensaver.cpp b/clientscr/screensaver.cpp index 6a5a750d44..c3ca1c7ed7 100644 --- a/clientscr/screensaver.cpp +++ b/clientscr/screensaver.cpp @@ -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