From 01576bd320d867a82faf13014ec1e6e78810a0c3 Mon Sep 17 00:00:00 2001 From: Charlie Fenton Date: Thu, 24 Jul 2025 05:04:06 -0700 Subject: [PATCH 1/8] Mac: Fix screensaver to run correctly on MacOS 26 --- client/check_security.cpp | 10 +- client/switcher.cpp | 4 +- .../mac/templates/ScreenSaver-Info.plist | 4 +- clientscr/Mac_Saver_Module.h | 3 +- clientscr/Mac_Saver_ModuleView.h | 2 + clientscr/Mac_Saver_ModuleView.m | 345 ++++++++++-------- clientscr/gfx_cleanup.mm | 11 +- clientscr/gfx_switcher.cpp | 7 +- clientscr/mac_saver_module.cpp | 57 +-- clientscr/screensaver.cpp | 35 +- 10 files changed, 276 insertions(+), 202 deletions(-) diff --git a/client/check_security.cpp b/client/check_security.cpp index 2a3545dcfb8..b966a86fec1 100644 --- a/client/check_security.cpp +++ b/client/check_security.cpp @@ -242,7 +242,15 @@ int use_sandbox, int isManager, char* path_to_error, int len // Require absolute owner and group boinc_master:boinc_master // Get the full path to BOINC Client inside this application's bundle // - retval = stat("/Applications/BOINCManager.app/Contents/Resources/boinc", &sbuf); + long brandId = 0; + FILE *f = fopen("/Library/Application Support/BOINC Data/Branding", "r"); + if (f) { + fscanf(f, "BrandId=%ld\n", &brandId); + fclose(f); + } + + snprintf(full_path, sizeof(full_path),"/%s/Contents/Resources/boinc", appPath[brandId]); + retval = stat(full_path, &sbuf); if (retval) return -1016; // Should never happen diff --git a/client/switcher.cpp b/client/switcher.cpp index 2d20acabbf7..ac158d53e99 100644 --- a/client/switcher.cpp +++ b/client/switcher.cpp @@ -278,7 +278,7 @@ static void print_to_log_file(const char *format, ...) { char buf[256]; time_t t; - f = fopen("/Users/Shared/test_log_gfx_switcher.txt", "a"); + f = fopen("/Users/Shared/test_log_switcher.txt", "a"); if (!f) return; // freopen(buf, "a", stdout); @@ -298,7 +298,7 @@ static void print_to_log_file(const char *format, ...) { fputs("\n", f); fflush(f); fclose(f); - chmod("/Users/Shared/test_log_gfx_switcher.txt", 0666); + chmod("/Users/Shared/test_log_switcher.txt", 0666); } static void strip_cr(char *buf) diff --git a/clientgui/mac/templates/ScreenSaver-Info.plist b/clientgui/mac/templates/ScreenSaver-Info.plist index 418373bd4d5..52958b2a49c 100644 --- a/clientgui/mac/templates/ScreenSaver-Info.plist +++ b/clientgui/mac/templates/ScreenSaver-Info.plist @@ -1,5 +1,5 @@ - + CFBundleDevelopmentRegion @@ -10,8 +10,6 @@ edu.berkeley.boincsaver CFBundleInfoDictionaryVersion 6.0 - CFBundleName - BOINCSaver CFBundlePackageType BNDL CFBundleSignature diff --git a/clientscr/Mac_Saver_Module.h b/clientscr/Mac_Saver_Module.h index 4145636fb9e..7598fc724d4 100644 --- a/clientscr/Mac_Saver_Module.h +++ b/clientscr/Mac_Saver_Module.h @@ -90,6 +90,7 @@ class CScreensaver { public: CScreensaver(); + ~CScreensaver(); int Create(); int Run(); @@ -137,7 +138,6 @@ class CScreensaver int launch_screensaver(RESULT* rp, int& graphics_application); int launch_default_screensaver(char *dir_path, int& graphics_application); void HandleRPCError(void); - int KillScreenSaver(void); void GetDefaultDisplayPeriods(struct ss_periods &periods); pthread_t m_hDataManagementThread; @@ -155,7 +155,6 @@ class CScreensaver bool m_bResetCoreState; bool m_bQuitDataManagementProc; bool m_bDataManagementProcStopped; - bool m_bV5_GFX_app_is_running; // diff --git a/clientscr/Mac_Saver_ModuleView.h b/clientscr/Mac_Saver_ModuleView.h index ed858af0cc3..718de4c8872 100644 --- a/clientscr/Mac_Saver_ModuleView.h +++ b/clientscr/Mac_Saver_ModuleView.h @@ -44,6 +44,7 @@ - (bool) setUpToUseCGWindowList; - (void) doPeriodicTasks; +- (void) advancePosition:(NSTimer*)timer; @end @@ -53,6 +54,7 @@ - (void)init:(NSView*)saverView; - (void)testConnection; - (void)portDied:(NSNotification *)notification; +- (void)closeServerPort; - (void)cleanUpOpenGL; @end diff --git a/clientscr/Mac_Saver_ModuleView.m b/clientscr/Mac_Saver_ModuleView.m index 746d7583f7f..96d0aa193cc 100644 --- a/clientscr/Mac_Saver_ModuleView.m +++ b/clientscr/Mac_Saver_ModuleView.m @@ -136,31 +136,39 @@ NSRect gMovingRect; float gImageXIndent; float gTextBoxHeight; -NSPoint gCurrentPosition; -NSPoint gCurrentDelta; CGContextRef myContext; bool isErased; +char gLastMsg[2048]; +HIThemeTextInfo gTextInfo; +NSPoint gCurrentDelta; +NSPoint gCurrentPosition; +NSPoint imagePosition; bool gIsBigSur = false; +NSRect myViewBounds; static SharedGraphicsController *mySharedGraphicsController; static bool runningSharedGraphics; static bool useCGWindowList; -static pid_t childPid; +static pid_t childPid; // pid of the currently running grapics app, not actually our chkld static int gfxAppWindowNum; static NSView *imageView; static char gfxAppPath[MAXPATHLEN]; static char gfxWuName[MAXPATHLEN]; static int taskSlot; -static NSRunningApplication *childApp; +static NSRunningApplication *childApp; // currently running grapics app, not actually our chkld static double gfxAppStartTime; static bool UseSharedOffscreenBuffer(void); static double lastGetSSMsgTime; -static pthread_t mainThreadID; static int CGWindowListTries; static int DPI_multiplier = 1; static bool myIsPreview; +static bool screensaverIsVisible = false; + +//static long counter = 0; // CAF +static NSTimer * myTimer = NULL; +//static CFMachPortRef eventTap = NULL; #define TEXTBOXMINWIDTH 400.0 #define MINTEXTBOXHEIGHT 40.0 @@ -195,11 +203,13 @@ void launchedGfxApp(char * appPath, char * wuName, pid_t thePID, int slot) { useCGWindowList = false; gfxAppStartTime = 0.0; gfxWuName[0] = '\0'; - if (mySharedGraphicsController) { - [ mySharedGraphicsController cleanUpOpenGL ]; + if (screensaverIsVisible) { // Must be called from main thread + if (mySharedGraphicsController) { + [ mySharedGraphicsController cleanUpOpenGL ]; + } } if (imageView) { - // removeFromSuperview must be called from main thread + // removeFromSuperview must be called from main thread if (NSThread.isMainThread) { [imageView removeFromSuperview]; // Releases imageView } else { @@ -216,6 +226,7 @@ void launchedGfxApp(char * appPath, char * wuName, pid_t thePID, int slot) { } } + @implementation BOINC_Saver_ModuleView // If there are multiple displays, this may get called @@ -241,6 +252,11 @@ - (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview { myIsPreview = isPreview; } + // Apparently, setAnimationTimeInterval now works only when called + // from initWithFrame and cannot be modified dynamically. I don't + // know if that was always true or if a more recent change. + [ self setAnimationTimeInterval:1.0/60.0 ]; + // OpenGL / GLUT apps which call glutFullScreen() and are built using // Xcode 11 apparently use window dimensions based on the number of // backing store pixels. That is, they double the window dimensions @@ -270,6 +286,8 @@ - (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview { } } + screensaverIsVisible = true; + gLastMsg[0] = '\0'; initBOINCSaver(); if (self) { @@ -283,6 +301,9 @@ - (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview { gPathToBundleResources = [ myBundle resourcePath ]; } + [ self setAutoresizesSubviews:YES ]; // make sure the subview resizes. + } + ScreenSaverDefaults *defaults = [ ScreenSaverDefaults defaultsForModuleWithName:mBundleID ]; // try to load the version key, used to see if we have any saved settings @@ -309,9 +330,10 @@ - (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview { [ defaults setInteger:period forKey:@"ChangePeriod" ]; // synchronize - [defaults synchronize]; } + [defaults synchronize]; + // get defaults... gGoToBlank = [ defaults integerForKey:@"GoToBlank" ]; gBlankingTime = [ defaults integerForKey:@"BlankingTime" ]; @@ -321,29 +343,19 @@ - (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview { setGFXSciencePeriod((double)(period * 60)); period = [ defaults integerForKey:@"ChangePeriod" ]; setGGFXChangePeriod((double)(period * 60)); - - [ self setAutoresizesSubviews:YES ]; // make sure the subview resizes. - } - } - - // Path to our copy of switcher utility application in this screensaver bundle - if (gPathToBundleResources == NULL) { - gPathToBundleResources = [ myBundle resourcePath ]; } return self; } + // If there are multiple displays, this may get called // multiple times (once for each display), so we need to guard // against any problems that may cause. - (void)startAnimation { - int newFrequency; gEventHandle = NXOpenEventStatus(); - mainThreadID = pthread_self(); - // Under OS 10.14 Mojave, [super drawRect:] is slow but not needed if we do this: [[self window] setBackgroundColor:[NSColor blackColor]]; @@ -372,16 +384,11 @@ - (void)startAnimation { gCurrentDelta.x = 1.0; gCurrentDelta.y = 1.0; - [ self setAnimationTimeInterval:1/8.0 ]; +// [ self setAnimationTimeInterval:1/8.0 ]; } [ super startAnimation ]; - if (myIsPreview) { - [ self setAnimationTimeInterval:1.0/8.0 ]; - return; - } - NSWindow *myWindow = [ self window ]; #if DEBUG_UNDER_XCODE [ myWindow setLevel:2030 ]; @@ -398,9 +405,12 @@ - (void)startAnimation { [mySharedGraphicsController init:self]; } - newFrequency = startBOINCSaver(); - if (newFrequency) { - [ self setAnimationTimeInterval:1.0/newFrequency ]; + startBOINCSaver(); + if (myTimer == NULL) { + myViewBounds = [self bounds]; + // See comments at advancePosition on why this is needed. + myTimer = [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval) 1./60. target:self selector:@selector(advancePosition:) + userInfo:nil repeats:YES]; } } gSS_StartTime = getDTime(); @@ -409,10 +419,12 @@ - (void)startAnimation { // If there are multiple displays, this may get called // multiple times (once for each display), so we need to guard // against any problems that may cause. +// +// stopAnimation never seems to be called in recent versions of MacOS +// so we must use the screensaverIsVisible test in doPeriodicTasks. - (void)stopAnimation { [ super stopAnimation ]; - - if (myIsPreview) return; + if (myIsPreview) return; #if ! DEBUG_UNDER_XCODE NSRect windowFrame = [ [ self window ] frame ]; if ( (windowFrame.origin.x != 0) || (windowFrame.origin.y != 0) ) { @@ -463,18 +475,17 @@ - (void)drawRect:(NSRect)rect { // multiple times (once for each display), so we need to guard // against any problems that may cause. - (void)doPeriodicTasks { - int newFrequency = 0; int coveredFreq = 0; NSRect theFrame = [ self frame ]; - NSUInteger n; double maxWaitTime; NSRect currentDrawingRect, eraseRect; CGFloat actualTextBoxHeight = MINTEXTBOXHEIGHT; - NSPoint imagePosition; char *msg; CFStringRef cf_msg; - double timeToBlock, frameStartTime = getDTime(); - HIThemeTextInfo textInfo; + + if (myTimer == NULL) { + return; + } NSWindow *myWindow = [ self window ]; NSRect windowFrame = [ myWindow frame ]; @@ -483,14 +494,31 @@ - (void)doPeriodicTasks { // Under MacOS 14 Sonoma, screensavers continue to run and "draw" invisibly // after they are dismissed by user activity, to allow them to be used as // wallpaper. Since we don't want the BOINC screensaver to be used as wallpaper, - // this would waste system resources. - // The only way I've found to determine that the screensaver has been dismissed - // by the user is this test of the window level. + // this would waste system resources, so we probably want to terminate the + // legacyScreenSaver when that happens. Also, legacyScreenSaver under MacOS 26 + // creates other problems; see comments at advancePosition: for details. + // + // The only way I've found to determine that the legacyScreenSaver has been + // dismissed by the user in all 3 OS versions (MacOS 14, MacOS 15 and MacOS 26) + // is testing the frontmost application: it is LoginWindow if and only if the + // screensaver is visible. if ((windowFrame.size.width > 500.) && (windowFrame.size.height > 500.)) { - if ([ [ self window ] level ] == 0) { -//TODO: more cleanup as in stopAnimation ?? - closeBOINCSaver(); - return; + if (screensaverIsVisible) { + if (strcmp([[[[ NSWorkspace sharedWorkspace] frontmostApplication] bundleIdentifier] UTF8String], "com.apple.loginwindow") != 0) { + screensaverIsVisible = false; + closeBOINCSaver(); + + if (myTimer) { + [ myTimer invalidate ]; + myTimer = NULL; + } + if (mySharedGraphicsController) { + [ mySharedGraphicsController cleanUpOpenGL ]; // Must be called from main thread + [ mySharedGraphicsController closeServerPort ]; // Must be called after cleanUpOpenGL + [ NSApp terminate:nil ]; // Comment out to let legacyScreensaver continue in background + } + return; + } } } } @@ -507,11 +535,9 @@ - (void)doPeriodicTasks { [ gPreview_Image setSize:theFrame.size ]; [ gPreview_Image drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0 ]; } - [ self setAnimationTimeInterval:1/1.0 ]; #else // Code for possible future use if we want to draw more in preview myContext = [[NSGraphicsContext currentContext] graphicsPort]; drawPreview(myContext); - [ self setAnimationTimeInterval:1/30.0 ]; #endif return; } @@ -521,7 +547,7 @@ - (void)doPeriodicTasks { // after user activity before calling stopAnimation, so we check user activity here if ((compareOSVersionTo(10, 7) >= 0) && ((getDTime() - gSS_StartTime) > 2.0)) { if (! gIsMojave) { - double idleTime = CGEventSourceSecondsSinceLastEventType + double idleTime = CGEventSourceSecondsSinceLastEventType (kCGEventSourceStateCombinedSessionState, kCGAnyInputEventType); if (idleTime < 1.5) { [ NSApp terminate:nil ]; @@ -623,7 +649,7 @@ - (void)doPeriodicTasks { lastGetSSMsgTime = timeNow; } windowIsCovered(); - [ self setAnimationTimeInterval:1.0 ]; +//// [ self setAnimationTimeInterval:1.0 ]; } return; @@ -643,8 +669,7 @@ - (void)doPeriodicTasks { NSRect viewBounds = [self bounds]; - newFrequency = getSSMessage(&msg, &coveredFreq); - + getSSMessage(&msg, &coveredFreq); if (UseSharedOffscreenBuffer()) { // If runningSharedGraphics is still false after MAXWAITFORCONNECTION, // assume the graphics app has not been built with MachO comunication @@ -714,24 +739,6 @@ - (void)doPeriodicTasks { myWindowNumber = [ myWindow windowNumber ]; gTopWindowListIndex = [theWindowList indexOfObjectIdenticalTo:[NSNumber numberWithInt:myWindowNumber]]; } - - if (coveredFreq) { - if ( (msg != NULL) && (msg[0] != '\0') ) { - NSArray *theWindowList = [NSWindow windowNumbersWithOptions:NSWindowNumberListAllApplications]; - n = [theWindowList count]; - if (gTopWindowListIndex < n) { - if ([(NSNumber*)[theWindowList objectAtIndex:gTopWindowListIndex] integerValue] != myWindowNumber) { - // Project graphics application has a window open above ours - // Don't waste CPU cycles since our window is obscured by application graphics - newFrequency = coveredFreq; - msg = NULL; - windowIsCovered(); - } - } - } else { - newFrequency = coveredFreq; - } - } } // Draw our moving BOINC logo and screensaver status text @@ -742,24 +749,37 @@ - (void)doPeriodicTasks { currentDrawingRect.origin.y += (float) ((int)gCurrentPosition.y - gTextBoxHeight); if ( (msg != NULL) && (msg[0] != '\0') ) { - cf_msg = CFStringCreateWithCString(NULL, msg, kCFStringEncodingMacRoman); + if (strncmp(msg, gLastMsg, sizeof(gLastMsg))) { + strlcpy(gLastMsg, msg, sizeof(gLastMsg)); + + CTFontRef myFont = CTFontCreateWithName(CFSTR("Helvetica"), 20, NULL); + HIThemeTextInfo theTextInfo = {kHIThemeTextInfoVersionOne, kThemeStateActive, kThemeSpecifiedFont, + kHIThemeTextHorizontalFlushLeft, kHIThemeTextVerticalFlushTop, + kHIThemeTextBoxOptionNone, kHIThemeTextTruncationNone, 0, false, + 0, myFont + }; + gTextInfo = theTextInfo; + + HIThemeGetTextDimensions(cf_msg, (float)gMovingRect.size.width, &gTextInfo, NULL, &actualTextBoxHeight, NULL); + gTextBoxHeight = actualTextBoxHeight + TEXTBOXTOPBORDER; + } - CTFontRef myFont = CTFontCreateWithName(CFSTR("Helvetica"), 20, NULL); - HIThemeTextInfo theTextInfo = {kHIThemeTextInfoVersionOne, kThemeStateActive, kThemeSpecifiedFont, - kHIThemeTextHorizontalFlushLeft, kHIThemeTextVerticalFlushTop, - kHIThemeTextBoxOptionNone, kHIThemeTextTruncationNone, 0, false, - 0, myFont - }; - textInfo = theTextInfo; - - HIThemeGetTextDimensions(cf_msg, (float)gMovingRect.size.width, &textInfo, NULL, &actualTextBoxHeight, NULL); - gTextBoxHeight = actualTextBoxHeight + TEXTBOXTOPBORDER; + if (myTimer == NULL) { + return; + } + // Under MacOS 14 and later, if we allow legacyScreenSaver to continue running in the + // background when user activity dismisses the screensaver, it appears to create multiple + // instances of startAnimation, animateOneFrame and (perhaps) drawRect upon each subsequnt + // activation of the screensaver. As a result it tries to draw the same image in the same + // location multiple times. Apparently only one of those instances does actually draw to + // the screen, but we have no way of knowing which instance, so we need to allow them all + // to go through this code to ensure the drawing actually happens. if (!isErased) { - [[NSColor blackColor] set]; + [[NSColor blackColor] set]; - // Erasing only 2 small rectangles reduces screensaver's CPU usage by about 25% + // Erasing only 2 small rectangles reduces screensaver's CPU usage by about 25% imagePosition.x = (float) ((int)gCurrentPosition.x + gImageXIndent); imagePosition.y = (float) (int)gCurrentPosition.y; eraseRect.origin.y = imagePosition.y; @@ -794,43 +814,9 @@ - (void)doPeriodicTasks { eraseRect = NSInsetRect(eraseRect, -1, -1); NSRectFill(eraseRect); - isErased = true; - // If text has changed and it now goes below bottom of screen, jump up to show it all. - if ((gCurrentPosition.y - gTextBoxHeight) <= SAFETYBORDER) { - gCurrentPosition.y = SAFETYBORDER + 1 + gTextBoxHeight; - if (gCurrentDelta.y < 0) { - gCurrentDelta.y = (float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; - } - } - - // Set direction of motion to "bounce" off edges of screen - if ( (gCurrentDelta.x < 0) && (gCurrentPosition.x <= SAFETYBORDER) ) { - gCurrentDelta.x = (float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; - gCurrentDelta.y = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.y)) / 16.; - } - if ( (gCurrentDelta.x > 0) && ( (gCurrentPosition.x + gMovingRect.size.width) >= - (viewBounds.origin.x + viewBounds.size.width - SAFETYBORDER) ) ){ - gCurrentDelta.x = -(float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; - gCurrentDelta.y = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.y)) / 16.; - } - if ( (gCurrentDelta.y < 0) && (gCurrentPosition.y - gTextBoxHeight <= SAFETYBORDER) ) { - gCurrentDelta.y = (float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; - gCurrentDelta.x = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.x)) / 16.; - } - if ( (gCurrentDelta.y > 0) && ( (gCurrentPosition.y + gMovingRect.size.height) >= - (viewBounds.origin.y + viewBounds.size.height - SAFETYBORDER) ) ) { - gCurrentDelta.y = -(float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; - gCurrentDelta.x = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.x)) / 16.; - } + isErased = true; } - // Get the new drawing area - gCurrentPosition.x += gCurrentDelta.x; - gCurrentPosition.y += gCurrentDelta.y; - - imagePosition.x = (float) ((int)gCurrentPosition.x + gImageXIndent); - imagePosition.y = (float) (int)gCurrentPosition.y; - [ gBOINC_Logo drawAtPoint:imagePosition fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0 ]; #if 0 // For testing @@ -853,7 +839,7 @@ - (void)doPeriodicTasks { CGContextSetFillColorWithColor(myContext, myTextColor); - HIThemeDrawTextBox(cf_msg, &bounds, &textInfo, myContext, kHIThemeOrientationNormal); + HIThemeDrawTextBox(cf_msg, &bounds, &gTextInfo, myContext, kHIThemeOrientationNormal); CGColorRelease(myTextColor); CGColorSpaceRelease(myColorSpace); @@ -872,16 +858,6 @@ - (void)doPeriodicTasks { } } - if (newFrequency) { - [ self setAnimationTimeInterval:(1.0/newFrequency) ]; - // setAnimationTimeInterval does not seem to be working, so we - // throttle the screensaver directly here. - timeToBlock = (1.0/newFrequency) - (getDTime() - frameStartTime); - if (timeToBlock > 0.0) { - doBoinc_Sleep(timeToBlock); - } - } - // Check for a new graphics app sending us data if (UseSharedOffscreenBuffer() && gfxAppStartTime) { if (mySharedGraphicsController) { @@ -912,10 +888,61 @@ - (void)animateOneFrame { } } + +// This code was previously part of doPeriodicTasks, which is called from AnimateOneFrame +// in MacOS before MacOS 10.14 Mojave and from drawRect (which is triggered by +// AnimateOneFrame) since then. +// But under MacOS 14 and later, If we allow legacyScreenSaver to continue running in the +// background when user activity dismisses the screensaver, it appears to create multiple +// instances of startAnimation, animateOneFrame and (perhaps) drawRect upon each subsequnt +// activation of the screensaver. As a result, doPeriodicTasks is called several times +// more often, causing the moving logo to move at several times the intended speed. +// We solve this by using our own NSTimer to adjust the position at which to draw the +// logo, while letting doPeriodicTasks prform the actual drawing. +- (void) advancePosition:(NSTimer*)timer { +{ + // If text has changed and it now goes below bottom of screen, jump up to show it all. + if ((gCurrentPosition.y - gTextBoxHeight) <= SAFETYBORDER) { + gCurrentPosition.y = SAFETYBORDER + 1 + gTextBoxHeight; + if (gCurrentDelta.y < 0) { + gCurrentDelta.y = (float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; + } + } + + // Set direction of motion to "bounce" off edges of screen + if ( (gCurrentDelta.x < 0) && (gCurrentPosition.x <= SAFETYBORDER) ) { + gCurrentDelta.x = (float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; + gCurrentDelta.y = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.y)) / 16.; + } + if ( (gCurrentDelta.x > 0) && ( (gCurrentPosition.x + gMovingRect.size.width) >= + (myViewBounds.origin.x + myViewBounds.size.width - SAFETYBORDER) ) ){ + gCurrentDelta.x = -(float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; + gCurrentDelta.y = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.y)) / 16.; + } + if ( (gCurrentDelta.y < 0) && (gCurrentPosition.y - gTextBoxHeight <= SAFETYBORDER) ) { + gCurrentDelta.y = (float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; + gCurrentDelta.x = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.x)) / 16.; + } + if ( (gCurrentDelta.y > 0) && ( (gCurrentPosition.y + gMovingRect.size.height) >= + (myViewBounds.origin.y + myViewBounds.size.height - SAFETYBORDER) ) ) { + gCurrentDelta.y = -(float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; + gCurrentDelta.x = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.x)) / 16.; + } + } + + // Get the new drawing area + gCurrentPosition.x += gCurrentDelta.x; + gCurrentPosition.y += gCurrentDelta.y; + imagePosition.x = (float) ((int)gCurrentPosition.x + gImageXIndent); + imagePosition.y = (float) (int)gCurrentPosition.y; +} + + - (BOOL)hasConfigureSheet { return YES; } + // Display the configuration sheet for the user to choose their settings - (NSWindow*)configureSheet { @@ -1143,9 +1170,9 @@ - (void) testConnection char *portNameV2 = "edu.berkeley.boincsaver-v2"; 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]; + // 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, portNameV1, &servicePortNum); if (machErr != KERN_SUCCESS) { if (machErr != BOOTSTRAP_NOT_PRIVILEGED) { @@ -1154,19 +1181,19 @@ - (void) testConnection // 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 + // As of MacOS 14.0, the legacyScreenSaver 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]; + [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]; + serverPort = (NSMachPort*)[NSMachPort portWithMachPort:servicePortNum options:NSMachPortDeallocateReceiveRight]; } if ((serverPort != MACH_PORT_NULL) && (localPort == MACH_PORT_NULL)) { @@ -1206,8 +1233,8 @@ - (void) testConnection - (void)cleanUpOpenGL { - if (gMach_bootstrap_unavailable_to_screensavers) { - [serverPort removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + if (gMach_bootstrap_unavailable_to_screensavers && (serverPort != MACH_PORT_NULL)) { + [serverPort removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; } childPid=0; childApp = nil; @@ -1217,10 +1244,14 @@ - (void)cleanUpOpenGL ) { if (openGLView) { if (NSThread.isMainThread) { - [openGLView removeFromSuperview]; // Releases openGLView + [openGLView removeFromSuperview]; // Releases openGLView } else { - dispatch_sync(dispatch_get_main_queue(), ^{ - [openGLView removeFromSuperview]; // Releases openGLView + static saverOpenGLView *temp = 0; // Static to allow asynchronous use + temp = openGLView; + // Both dispatch_sync and dispatch_async cause problems when + // called from CScreensaver::DataManagementProc thread + dispatch_sync(dispatch_get_main_queue(), ^{ + [temp removeFromSuperview]; // Releases openGLView }); } openGLView = nil; @@ -1229,7 +1260,7 @@ - (void)cleanUpOpenGL int i; for(i = 0; i < NUM_IOSURFACE_BUFFERS; i++) { if (_ioSurfaceBuffers[i]) { - CFRelease(_ioSurfaceBuffers[i]); + CFRelease(_ioSurfaceBuffers[i]); _ioSurfaceBuffers[i] = nil; } @@ -1245,8 +1276,10 @@ - (void)cleanUpOpenGL } runningSharedGraphics = false; // Do this last!! - if (gMach_bootstrap_unavailable_to_screensavers) { + if (screensaverIsVisible) { + if (gMach_bootstrap_unavailable_to_screensavers) { [serverPort scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + } } } } @@ -1258,26 +1291,33 @@ - (void)portDied:(NSNotification *)notification } NSPort *port = [notification object]; if(port == serverPort) { - childApp = nil; - gfxAppStartTime = 0.0; - gfxAppPath[0] = '\0'; - gfxWuName[0] = '\0'; + [ self closeServerPort ]; + [ self cleanUpOpenGL ]; + } +} + +- (void)closeServerPort +{ + childApp = nil; + gfxAppStartTime = 0.0; + gfxAppPath[0] = '\0'; + gfxWuName[0] = '\0'; - if ([serverPort isValid]) { - [serverPort invalidate]; + if ([serverPort isValid]) { + [serverPort invalidate]; // [serverPort release]; - } - serverPort = MACH_PORT_NULL; - [localPort removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + } + serverPort = MACH_PORT_NULL; + + if (localPort != MACH_PORT_NULL) { + [localPort removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; if ([localPort isValid]) { [localPort invalidate]; } -// [localPort release]; +// [localPort release]; localPort = MACH_PORT_NULL; - - [ self cleanUpOpenGL ]; - } + } } - (void)handleMachMessage:(void *)msg @@ -1317,7 +1357,6 @@ - (kern_return_t)displayFrame:(int32_t)frameIndex surfacemachport:(mach_port_t)i theframe.origin.x = theframe.origin.y = 0.0; theframe.size.width = IOSurfaceWidth; theframe.size.height = IOSurfaceHeight; - openGLView = [[saverOpenGLView alloc] initWithFrame:theframe]; [screenSaverView addSubview:openGLView]; } diff --git a/clientscr/gfx_cleanup.mm b/clientscr/gfx_cleanup.mm index 214506e18fd..7a8200ac6b5 100644 --- a/clientscr/gfx_cleanup.mm +++ b/clientscr/gfx_cleanup.mm @@ -73,8 +73,8 @@ #endif void killGfxApp(pid_t thePID) { -#if 0 print_to_log_file("in gfx_cleanup: killGfxApp()"); +#if 0 kill(thePID, SIGKILL); #else char buf[256]; @@ -118,7 +118,7 @@ void killGfxApp(pid_t thePID) { retval = rpc->run_graphics_app("test", p, userName); if (retval || (p==0)) break; } - print_to_log_file("in gfx_cleanup: killGfxApp(%d): rpc->run_graphics_app(test) returned pid %d, retval %d when i = %d", thePID, p, retval, i); + print_to_log_file("in gfx_cleanup: killGfxApp(%d): rpc->run_graphics_app(test) returned pid %d, retval %d when i = %d", thePID, p, retval, i); // Graphics apps called by screensaver or Manager (via Show // Graphics button) now write files in their slot directory as @@ -144,14 +144,16 @@ void killGfxApp(pid_t thePID) { print_to_log_file("in gfx_cleanup: Starting MonitorParent"); while (true) { boinc_sleep(0.25); // Test every 1/4 second + print_to_log_file("in gfx_cleanup: MonitorParent: getppd=%d, parentpid=%d", getppid(), parentPid); if (getppid() != parentPid) { if (GFX_PidFromScreensaver) { + print_to_log_file("in gfx_cleanup: MonitorParent: calling killGfxApp"); killGfxApp(GFX_PidFromScreensaver); } if (quit_MonitorParentThread) { return 0; } - print_to_log_file("in gfx_cleanup: parent died, exiting (child) after handling %d",GFX_PidFromScreensaver); + print_to_log_file("in gfx_cleanup: MonitorParent: parent died, exiting (child) after handling %d",GFX_PidFromScreensaver); #if USE_TIMER endTime = time(NULL); elapsedTime = endTime - startTime; @@ -189,10 +191,12 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification } if (ferror(stdin) && (errno != EINTR)) { fprintf(stderr, "in gfx_cleanup: fgets got error %d %s", errno, strerror(errno)); + print_to_log_file("in gfx_cleanup: fgets got error %d %s", errno, strerror(errno)); break; } if (!strcmp(buf, "Quit\n")) { + print_to_log_file("in gfx_cleanup: parent sent Quit to child"); break; } GFX_PidFromScreensaver = atoi(buf); @@ -200,6 +204,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification } if (GFX_PidFromScreensaver) { + print_to_log_file("in gfx_cleanup: calling killGfxApp"); killGfxApp(GFX_PidFromScreensaver); } diff --git a/clientscr/gfx_switcher.cpp b/clientscr/gfx_switcher.cpp index 8ad6bf52a11..6e2685afb46 100644 --- a/clientscr/gfx_switcher.cpp +++ b/clientscr/gfx_switcher.cpp @@ -164,6 +164,10 @@ int main(int argc, char** argv) { getcwd( current_dir, sizeof(current_dir)); print_to_log_file("current directory = %s", current_dir); print_to_log_file("user_name is %s, euid=%d, uid=%d, egid=%d, gid=%d", user_name, geteuid(), getuid(), getegid(), getgid()); + print_to_log_file("gfx_switcher pid = %d", getpid()) ; + pid_t ScreenSaverEngine_Pid = 0; + ScreenSaverEngine_Pid = getPidIfRunning("com.apple.ScreenSaver.Engine.legacyScreenSaver"); + print_to_log_file("main: ScreenSaverEngine_legacyScreenSaver_Pid=%d", ScreenSaverEngine_Pid); for (int i=0; iCreate(); + if (!created) { // CAF + created = true; // CAF + return gspScreensaver->Create(); + } // CAF } return TEXTLOGOFREQUENCY; } @@ -203,6 +218,8 @@ void closeBOINCSaver() { delete gspScreensaver; gspScreensaver = NULL; } +for (int i=1; i<32; ++i) +boinc_set_signal_handler(i, boinc_catch_signal); } @@ -369,8 +386,12 @@ CScreensaver::CScreensaver() { } +CScreensaver::~CScreensaver() { +} + + int CScreensaver::Create() { - // Ugly workaround for a problem with the System Preferences app + // Ugly workaround for a problem with the System Preferences app // For an unknown reason, when this screensaver is run using the // Test button in the System Prefs Screensaver control panel, the // control panel calls our stopAnimation function as soon as the @@ -567,7 +588,7 @@ int CScreensaver::getSSMessage(char **theMessage, int* coveredFreq) { // Wait 1 second to allow ScreenSaver engine to close us down if (++gQuitCounter > (m_MessageText[0] ? TEXTLOGOFREQUENCY : NOTEXTLOGOFREQUENCY)) { closeBOINCSaver(); - KillScreenSaver(); // Stop the ScreenSaver Engine + exit(0); // Stop the ScreenSaver Engine } break; #endif @@ -688,6 +709,7 @@ void CScreensaver::ShutdownSaver() { rpc->quit(); // Kill core client if we launched it } #endif + rpc->close(); delete rpc; rpc = NULL; } @@ -700,18 +722,14 @@ void CScreensaver::ShutdownSaver() { m_bQuitDataManagementProc = false; saverState = SaverState_Idle; retryCount = 0; + ScreenSaverStartTime = 0; + ScreenIsBlanked = false; + if (m_gfx_Cleanup_IPC) { fprintf(m_gfx_Cleanup_IPC, "Quit\n"); fflush(m_gfx_Cleanup_IPC); pclose(m_gfx_Cleanup_IPC); - } - - // Under MacOS 14.0 Sonoma, screensavers continue to run and "draw" invisibly - // after they are dismissed by user activity, to allow them to be used as - // wallpaper. Since we don't want the BOINC screensaver to be used as wallpaper, - // this would waste system resources. - if (gIsSonoma) { - KillScreenSaver(); + m_gfx_Cleanup_IPC = NULL; } } @@ -794,7 +812,6 @@ bool CScreensaver::DestroyDataManagementThread() { } boinc_sleep(0.1); } - m_hDataManagementThread = NULL; // Don't delay more if this routine is called again. if (m_hGraphicsApplication) { @@ -909,18 +926,6 @@ pid_t CScreensaver::getClientPID() { } -// Send a Quit AppleEvent to the process which called this module -// (i.e., tell the ScreenSaver engine to quit) -int CScreensaver::KillScreenSaver() { - pid_t thisPID; - int retval; - - thisPID = getpid(); - retval = kill(thisPID, SIGABRT); // SIGINT - return retval; -} - - bool CScreensaver::Host_is_running_on_batteries() { #if SIMULATE_AC_BATTERY_SWITCHING // Simulate 30 seconds on AC, 30 on battery, 30 AC, 30 battery, etc. diff --git a/clientscr/screensaver.cpp b/clientscr/screensaver.cpp index 06c55e10156..f84056c50cb 100644 --- a/clientscr/screensaver.cpp +++ b/clientscr/screensaver.cpp @@ -306,8 +306,10 @@ int CScreensaver::launch_screensaver(RESULT* rp, PROCESS_REF& graphics_applicati } // fprintf(stderr, "launch_screensaver got pid %d\n", graphics_application); // Inform our helper app what we launched - fprintf(m_gfx_Cleanup_IPC, "%d\n", graphics_application); - fflush(m_gfx_Cleanup_IPC); + if (m_gfx_Cleanup_IPC) { + fprintf(m_gfx_Cleanup_IPC, "%d\n", graphics_application); + fflush(m_gfx_Cleanup_IPC); + } if (graphics_application) { graphicsAppStartTime = getDTime(); @@ -363,12 +365,13 @@ int CScreensaver::terminate_v6_screensaver(PROCESS_REF graphics_application) { } 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); + if (m_gfx_Cleanup_IPC) { + fprintf(m_gfx_Cleanup_IPC, "0\n"); + fflush(m_gfx_Cleanup_IPC); + } for (int i=0; i<200; i++) { boinc_sleep(0.01); // Wait 2 seconds max @@ -376,11 +379,11 @@ int CScreensaver::terminate_v6_screensaver(PROCESS_REF graphics_application) { break; } } - // For safety, call kill_process() even under Apple sandbox security + + // 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); @@ -454,8 +457,10 @@ int CScreensaver::launch_default_screensaver(char *dir_path, PROCESS_REF& graphi } // fprintf(stderr, "launch_screensaver got pid %d\n", graphics_application); // Inform our helper app what we launched - fprintf(m_gfx_Cleanup_IPC, "%d\n", graphics_application); - fflush(m_gfx_Cleanup_IPC); + if (m_gfx_Cleanup_IPC) { + fprintf(m_gfx_Cleanup_IPC, "%d\n", graphics_application); + fflush(m_gfx_Cleanup_IPC); + } if (graphics_application) { launchedGfxApp("boincscr", "", graphics_application, -1); @@ -560,6 +565,7 @@ DataMgmtProcType CScreensaver::DataManagementProc() { m_bDefault_gfx_running = false; m_bShow_default_ss_first = false; graphics_app_result_ptr = NULL; + graphicsAppStartTime = 0; #ifdef __APPLE__ for (int i = 0; i < m_vIncompatibleGfxApps.size(); i++) { @@ -620,7 +626,7 @@ DataMgmtProcType CScreensaver::DataManagementProc() { // Are we supposed to exit the screensaver? if (m_bQuitDataManagementProc) { // If main thread has requested we exit - BOINCTRACE(_T("CScreensaver::DataManagementProc - Thread told to stop\n")); +BOINCTRACE(_T("CScreensaver::DataManagementProc - Thread told to stop\n")); if (m_hGraphicsApplication || graphics_app_result_ptr) { if (m_bDefault_gfx_running) { BOINCTRACE(_T("CScreensaver::DataManagementProc - Terminating default screensaver\n")); @@ -633,7 +639,14 @@ DataMgmtProcType CScreensaver::DataManagementProc() { previous_result_ptr = NULL; m_hGraphicsApplication = 0; } - BOINCTRACE(_T("CScreensaver::DataManagementProc - Stopping...\n")); + + ss_shmem->gfx_pid = 0; + ss_shmem->gfx_slot = -1; + ss_shmem->major_version = 0; + ss_shmem->minor_version = 0; + ss_shmem->release = 0; + + BOINCTRACE(_T("CScreensaver::DataManagementProc - Stopping...\n")); m_bDataManagementProcStopped = true; // Tell main thread that we exited return 0; // Exit the thread } From 152a8b629fded46f7845e1af7974c7d124bc59b2 Mon Sep 17 00:00:00 2001 From: Charlie Fenton Date: Thu, 24 Jul 2025 05:07:02 -0700 Subject: [PATCH 2/8] Mac Installer: under MacOS 26, set screensaver to BOINC after getting user OK --- .../mac/templates/Finish_Install-Info.plist | 4 +- .../mac/templates/PostInstall-Info.plist | 4 +- mac_build/boinc.xcodeproj/project.pbxproj | 134 ++++--- .../xcschemes/boinc_finish_install.xcscheme | 9 + mac_installer/PostInstall.cpp | 272 +++---------- mac_installer/finish_install.cpp | 363 ++++++++++++++---- mac_installer/release_boinc.sh | 34 +- mac_installer/release_brand.sh | 42 +- 8 files changed, 467 insertions(+), 395 deletions(-) diff --git a/clientgui/mac/templates/Finish_Install-Info.plist b/clientgui/mac/templates/Finish_Install-Info.plist index 05dd0adf866..aa6cc2ae2a5 100644 --- a/clientgui/mac/templates/Finish_Install-Info.plist +++ b/clientgui/mac/templates/Finish_Install-Info.plist @@ -1,5 +1,5 @@ - + CFBundleDevelopmentRegion @@ -8,6 +8,8 @@ BOINC_Finish_Install CFBundleIdentifier edu.berkeley.boinc.finish-install + CFBundleIconFile + MacInstaller.icns CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType diff --git a/clientgui/mac/templates/PostInstall-Info.plist b/clientgui/mac/templates/PostInstall-Info.plist index 1eba056f730..3bd537cccf2 100644 --- a/clientgui/mac/templates/PostInstall-Info.plist +++ b/clientgui/mac/templates/PostInstall-Info.plist @@ -1,11 +1,13 @@ - + CFBundleDevelopmentRegion English CFBundleExecutable PostInstall + CFBundleIconFile + MacInstaller.icns CFBundleIdentifier edu.berkeley.boinc.PostInstall CFBundleInfoDictionaryVersion diff --git a/mac_build/boinc.xcodeproj/project.pbxproj b/mac_build/boinc.xcodeproj/project.pbxproj index f03225bbb0d..74e7186c2e9 100644 --- a/mac_build/boinc.xcodeproj/project.pbxproj +++ b/mac_build/boinc.xcodeproj/project.pbxproj @@ -13,9 +13,9 @@ buildPhases = ( ); dependencies = ( - DDC04DA72E2908BC006AB01D /* PBXTargetDependency */, - DDC04D9F2E28C473006AB01D /* PBXTargetDependency */, - DDC04D9D2E28C44B006AB01D /* PBXTargetDependency */, + DDC04DA52E2908AB006AB01D /* PBXTargetDependency */, + DDA8694C2E1FCEE400642CD3 /* PBXTargetDependency */, + DDA8694A2E1FCE6A00642CD3 /* PBXTargetDependency */, DD67F1A32D5A2C3700A78699 /* PBXTargetDependency */, DDF9EC11144EB36E005D6144 /* PBXTargetDependency */, DDF9EC17144EB36E005D6144 /* PBXTargetDependency */, @@ -57,6 +57,7 @@ DD08648D19E0BE6D00994039 /* ProjectWelcomePage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD08648B19E0BE6D00994039 /* ProjectWelcomePage.cpp */; }; DD0925B51FB05490000902DF /* project_list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD0925B31FB05490000902DF /* project_list.cpp */; }; DD0A06F50869A2D2007CD86E /* prefs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD344BE407C5B1670043025C /* prefs.cpp */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + DD0AF61C2E21359200C46A81 /* mac_util.mm in Sources */ = {isa = PBXBuildFile; fileRef = DDBAA9B41DF1902B004C48FD /* mac_util.mm */; }; DD0C5A8B0816711400CEC5D7 /* boinc.jpg in Resources */ = {isa = PBXBuildFile; fileRef = DD0C5A8A0816711400CEC5D7 /* boinc.jpg */; }; DD0D32DD1E0ABEEE00A0FBAB /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDFE854A0B60CFD0009B43D9 /* AppKit.framework */; }; DD11E5B617D1A89700947975 /* MacAccessiblity.mm in Sources */ = {isa = PBXBuildFile; fileRef = DD1907FA17D1A2F100596F03 /* MacAccessiblity.mm */; }; @@ -219,6 +220,7 @@ DD5F656523606AED009ED2A2 /* hostinfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD344BB607C5AEEE0043025C /* hostinfo.cpp */; }; DD5F656623607472009ED2A2 /* gfx_cleanup in Resources */ = {isa = PBXBuildFile; fileRef = DD5F654123605B41009ED2A2 /* gfx_cleanup */; }; DD5FE1B517828887003339DF /* translate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DDF9350F176B0D0C00A2793C /* translate.cpp */; }; + DD612B522E2EA38500C634C8 /* PostInstall.app in Resources */ = {isa = PBXBuildFile; fileRef = DD1277B3081F3D67007B5DE1 /* PostInstall.app */; }; DD64A7782703379F005FE681 /* MacBitmapComboBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD64A774270329AA005FE681 /* MacBitmapComboBox.cpp */; }; DD64D8011F6BE5BA00FEEAAA /* MultiGPUMig.defs in Sources */ = {isa = PBXBuildFile; fileRef = DD64D8001F6BE5BA00FEEAAA /* MultiGPUMig.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; DD6617880A3FFD8C00FFEBEB /* check_security.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD6617870A3FFD8C00FFEBEB /* check_security.cpp */; }; @@ -462,6 +464,7 @@ DDD0CEAE1E1E36AE003BD920 /* mac_util.mm in Sources */ = {isa = PBXBuildFile; fileRef = DDBAA9B41DF1902B004C48FD /* mac_util.mm */; }; DDD0CEAF1E1E36E4003BD920 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDFE854A0B60CFD0009B43D9 /* AppKit.framework */; }; DDD3371310622D0800867C7D /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 20286C33FDCF999611CA2CEA /* Carbon.framework */; }; + DDD43F942E2D5BC60032DCDD /* MacInstaller.icns in Resources */ = {isa = PBXBuildFile; fileRef = DD531BC50C193D3800742E50 /* MacInstaller.icns */; }; DDD50AFA18C49D2300EC244E /* BOINCGUIApp.mm in Sources */ = {isa = PBXBuildFile; fileRef = DDCA2EBD18C49139007D5F6C /* BOINCGUIApp.mm */; }; DDD52DCA0C03CAE6009B5FC0 /* ViewProjects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DDD52DC40C03CAE6009B5FC0 /* ViewProjects.cpp */; }; DDD52DCC0C03CAE6009B5FC0 /* ViewTransfers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DDD52DC60C03CAE6009B5FC0 /* ViewTransfers.cpp */; }; @@ -552,7 +555,6 @@ DDEED39829ADFDC400DC3E5D /* finish_install.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD3EAAAE216A268500BC673C /* finish_install.cpp */; }; DDEED39929ADFDEC00DC3E5D /* mac_branding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD93854320F609C7008EDE5A /* mac_branding.cpp */; }; DDEED39B29ADFDFA00DC3E5D /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 20286C33FDCF999611CA2CEA /* Carbon.framework */; }; - DDEED3A029AE114D00DC3E5D /* BOINC_Finish_Install.app in Resources */ = {isa = PBXBuildFile; fileRef = DDEED38629ADFD7000DC3E5D /* BOINC_Finish_Install.app */; }; DDEED3A129AE116E00DC3E5D /* BOINC_Finish_Install.app in Resources */ = {isa = PBXBuildFile; fileRef = DDEED38629ADFD7000DC3E5D /* BOINC_Finish_Install.app */; }; DDF07768236C4D410046EE44 /* proxy_info.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD344BEF07C5B1770043025C /* proxy_info.cpp */; }; DDF07769236C4D6A0046EE44 /* hostinfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD344BB607C5AEEE0043025C /* hostinfo.cpp */; }; @@ -562,6 +564,8 @@ DDF0776E236C4E650046EE44 /* app_ipc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA8B6B1B046C364400A80164 /* app_ipc.cpp */; }; DDF0776F236C4E8A0046EE44 /* prefs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD344BE407C5B1670043025C /* prefs.cpp */; }; DDF0C980291D2C3E00E72E0C /* shmem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AAA31C97042157A800A80164 /* shmem.cpp */; }; + DDF2720E2E16A40300960554 /* translate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DDF9350F176B0D0C00A2793C /* translate.cpp */; }; + DDF2720F2E16AE6E00960554 /* filesys.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5EAD475031AEFF8018E201A /* filesys.cpp */; }; DDF5E23318B8673E005DEA6E /* uninstall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD4688590C1661970089F500 /* uninstall.cpp */; }; DDF5E23418B86803005DEA6E /* AddRemoveUser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DDD33709106224E800867C7D /* AddRemoveUser.cpp */; }; DDF5F85A10DD05DB006A50CD /* notice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DDE1372D10DC5E8D00161D6B /* notice.cpp */; }; @@ -734,6 +738,20 @@ remoteGlobalIDString = DD7748970A356C880025D05E; remoteInfo = SetUpSecurity; }; + DDA869492E1FCE6A00642CD3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 20286C28FDCF999611CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = DDEED38529ADFD7000DC3E5D; + remoteInfo = boinc_finish_install; + }; + DDA8694B2E1FCEE400642CD3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 20286C28FDCF999611CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = B13E2D0E265564D100D5C977; + remoteInfo = detect_rosetta_cpu; + }; DDB874660C850DB600E0DE1F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 20286C28FDCF999611CA2CEA /* Project object */; @@ -762,21 +780,7 @@ remoteGlobalIDString = DD35353007E1E05C00C4718D; remoteInfo = api_libboinc; }; - DDC04D9C2E28C44B006AB01D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 20286C28FDCF999611CA2CEA /* Project object */; - proxyType = 1; - remoteGlobalIDString = DDEED38529ADFD7000DC3E5D; - remoteInfo = boinc_finish_install; - }; - DDC04D9E2E28C473006AB01D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 20286C28FDCF999611CA2CEA /* Project object */; - proxyType = 1; - remoteGlobalIDString = B13E2D0E265564D100D5C977; - remoteInfo = detect_rosetta_cpu; - }; - DDC04DA62E2908BC006AB01D /* PBXContainerItemProxy */ = { + DDC04DA42E2908AB006AB01D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 20286C28FDCF999611CA2CEA /* Project object */; proxyType = 1; @@ -1084,6 +1088,7 @@ DD7BF7D80B8E7A9800A009F7 /* str_util.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = str_util.h; path = ../lib/str_util.h; sourceTree = SOURCE_ROOT; }; DD7BF7E70B8E7BBE00A009F7 /* work_fetch.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = work_fetch.cpp; sourceTree = ""; }; DD7C5E7508110AE3002FCE1E /* ScreenSaver.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScreenSaver.framework; path = /System/Library/Frameworks/ScreenSaver.framework; sourceTree = ""; }; + DD7FD7DD2E1F9ECC00390B0E /* Set_Screensaver.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Set_Screensaver.cpp; path = /Volumes/Dev/BOINC_GIT/boinc_trunk_fixes_for_macos26/mac_installer/Set_Screensaver.cpp; sourceTree = ""; }; DD80C83D0CBAEB4F00F1121D /* sandbox.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = sandbox.cpp; sourceTree = ""; }; DD80C83E0CBAEB4F00F1121D /* sandbox.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = sandbox.h; path = ../client/sandbox.h; sourceTree = SOURCE_ROOT; }; DD818294245ED4010076E5D0 /* boinc_ss_helper.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = boinc_ss_helper.sh; path = ../clientscr/boinc_ss_helper.sh; sourceTree = ""; }; @@ -1685,6 +1690,7 @@ DD3EAAAE216A268500BC673C /* finish_install.cpp */, DD1277C0081F3E73007B5DE1 /* PostInstall.cpp */, DDF9350F176B0D0C00A2793C /* translate.cpp */, + DD7FD7DD2E1F9ECC00390B0E /* Set_Screensaver.cpp */, DDF93510176B0D0C00A2793C /* translate.h */, DD4688590C1661970089F500 /* uninstall.cpp */, DDF1F47A09822C3400482C89 /* preinstall */, @@ -2250,7 +2256,7 @@ isa = PBXNativeTarget; buildConfigurationList = DD9E2371091CBDAE0048316E /* Build configuration list for PBXNativeTarget "PostInstall" */; buildPhases = ( - DD361D4E29E599E1000CC8D1 /* ShellScript */, + DD361D4E29E599E1000CC8D1 /* Run Script */, DD1277AF081F3D67007B5DE1 /* Resources */, DD1277B0081F3D67007B5DE1 /* Sources */, DD1277B1081F3D67007B5DE1 /* Frameworks */, @@ -2384,7 +2390,7 @@ isa = PBXNativeTarget; buildConfigurationList = DD67F1992D5A24FD00A78699 /* Build configuration list for PBXNativeTarget "Fix_BOINC_Users" */; buildPhases = ( - DDC04DA12E28C8CC006AB01D /* ShellScript */, + DD0AF61B2E21250C00C46A81 /* ShellScript */, DD67F1912D5A24FD00A78699 /* Sources */, DD67F1922D5A24FD00A78699 /* Frameworks */, DD67F1932D5A24FD00A78699 /* CopyFiles */, @@ -2480,7 +2486,7 @@ DD96AFF50811075000A06F22 /* Resources */, DD96AFF60811075000A06F22 /* Sources */, DD96AFF70811075000A06F22 /* Frameworks */, - DD5FD5990A0233D60093C19F /* ShellScript */, + DDC04DA02E28C6AF006AB01D /* ShellScript */, DD818296245EDA220076E5D0 /* ShellScript */, DDF9B764245C12AA00587EBE /* ShellScript */, ); @@ -2730,7 +2736,6 @@ buildActionMask = 2147483647; files = ( DD00F554176C081500850424 /* MacInstaller.icns in Resources */, - DDEED3A029AE114D00DC3E5D /* BOINC_Finish_Install.app in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2738,6 +2743,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + DD612B522E2EA38500C634C8 /* PostInstall.app in Resources */, DD531BC60C193D3800742E50 /* MacInstaller.icns in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2783,12 +2789,31 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + DDD43F942E2D5BC60032DCDD /* MacInstaller.icns in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + DD0AF61B2E21250C00C46A81 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "source \"${PROJECT_DIR}/checkDependentLibraryVersions.sh\"\nexit $?\n"; + }; DD1AFEB10A512D8700EE5B82 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2870,7 +2895,7 @@ shellPath = /bin/sh; shellScript = "source \"${PROJECT_DIR}/checkDependentLibraryVersions.sh\"\nexit $?\n"; }; - DD361D4E29E599E1000CC8D1 /* ShellScript */ = { + DD361D4E29E599E1000CC8D1 /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 2147483647; @@ -2880,6 +2905,7 @@ ); inputPaths = ( ); + name = "Run Script"; outputFileListPaths = ( ); outputPaths = ( @@ -3163,22 +3189,6 @@ shellPath = /bin/sh; shellScript = "# echo \"BuiltProductsDir = ${BUILT_PRODUCTS_DIR}\"\n# echo \"SRC ROOT = ${SRCROOT}\"\n# echo \"CONFIGURATION = ${CONFIGURATION}\"\n# echo \"PRODUCT_NAME = ${PRODUCT_NAME}\"\nmkdir -p \"${SRCROOT}/build/${CONFIGURATION}\"\nif [ \"${BUILT_PRODUCTS_DIR}/lib${PRODUCT_NAME}.a\" -nt \"${SRCROOT}/build/${CONFIGURATION}/lib${PRODUCT_NAME}.a\" ]; then\n cp -fp \"${BUILT_PRODUCTS_DIR}/lib${PRODUCT_NAME}.a\" \"${SRCROOT}/build/${CONFIGURATION}/lib${PRODUCT_NAME}.a\"\n if [ \"$CONFIGURATION\" = \"Deployment\" ]; then\n strip -S \"${SRCROOT}/build/${CONFIGURATION}/lib${PRODUCT_NAME}.a\"\n fi\nfi\n"; }; - DD5FD5990A0233D60093C19F /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${SRCROOT}/English.lproj/ScreenSaver-InfoPlist.strings", - ); - outputPaths = ( - "${BUILT_PRODUCTS_DIR}/BOINCSaver.saver/Contents/Resources/English.lproj/InfoPlist.strings", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "mkdir -p \"${BUILT_PRODUCTS_DIR}/BOINCSaver.saver/Contents/Resources/English.lproj\"\ncp -fpv \"${SRCROOT}/English.lproj/ScreenSaver-InfoPlist.strings\" \"${BUILT_PRODUCTS_DIR}/BOINCSaver.saver/Contents/Resources/English.lproj/InfoPlist.strings\"\n"; - }; DD5FD59B0A0234400093C19F /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -3305,7 +3315,7 @@ DD77897E2A459453005B0A0E /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; - buildActionMask = 2147483647; + buildActionMask = 8; files = ( ); inputFileListPaths = ( @@ -3316,7 +3326,7 @@ ); outputPaths = ( ); - runOnlyForDeploymentPostprocessing = 0; + runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; shellScript = "source \"${PROJECT_DIR}/checkDependentLibraryVersions.sh\"\nexit $?\n"; }; @@ -3374,12 +3384,13 @@ "$(SRCROOT)/English.lproj/Uninstaller-InfoPlist.strings", "$(SRCROOT)/BoincCmd-Info.plist", "$(SRCROOT)/Finish_install-Info.plist", + "$(SRCROOT)/Set_Screensaver-Info.plist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# echo \"BuiltProductsDir = ${BUILT_PRODUCTS_DIR}\"\n# echo \"CONFIGURATIONTEMPDIR = ${CONFIGURATION_TEMP_DIR}\"\n# echo \"SRC ROOT = ${SRCROOT}\"\n# echo \"architecture = ${ARCHS}\"\n# echo \"native architecture = ${ARCHS}\"\n codesign -f -o runtime -s - \"${BUILT_PRODUCTS_DIR}/SetVersion\"\n # Unfortunately, this does not work because other targets that\n # have SetVersion as a dependency only wait for SetVersion to\n # be built, not for its scripts to finish.\n# Run SetVersion in background so we can wait for it to finish running\n##\"${BUILT_PRODUCTS_DIR}/SetVersion\"\nwait\n\n"; + shellScript = "## This is now handled by byuld pre-action scripts s in the\n## schemes for those targets which have inf.plist files.\n## Those pre-action scri[ts call mac_build/Update_Info_Plists.sh\n##\n# echo \"BuiltProductsDir = ${BUILT_PRODUCTS_DIR}\"\n# echo \"CONFIGURATIONTEMPDIR = ${CONFIGURATION_TEMP_DIR}\"\n# echo \"SRC ROOT = ${SRCROOT}\"\n# echo \"architecture = ${ARCHS}\"\n# echo \"native architecture = ${ARCHS}\"\n codesign -f -o runtime -s - \"${BUILT_PRODUCTS_DIR}/SetVersion\"\n # Unfortunately, this does not work because other targets that\n # have SetVersion as a dependency only wait for SetVersion to\n # be built, not for its scripts to finish.\n# Run SetVersion in background so we can wait for it to finish running\n##\"${BUILT_PRODUCTS_DIR}/SetVersion\"\n##wait\n\n"; }; - DDC04DA12E28C8CC006AB01D /* ShellScript */ = { + DDC04DA02E28C6AF006AB01D /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 2147483647; @@ -3388,14 +3399,16 @@ inputFileListPaths = ( ); inputPaths = ( + "${SRCROOT}/English.lproj/ScreenSaver-InfoPlist.strings", ); outputFileListPaths = ( ); outputPaths = ( + "${BUILT_PRODUCTS_DIR}/BOINCSaver.saver/Contents/Resources/English.lproj/InfoPlist.strings", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "source \"${PROJECT_DIR}/checkDependentLibraryVersions.sh\"\nexit $?\n"; + shellScript = "mkdir -p \"${BUILT_PRODUCTS_DIR}/BOINCSaver.saver/Contents/Resources/English.lproj\"\ncp -fpv \"${SRCROOT}/English.lproj/ScreenSaver-InfoPlist.strings\" \"${BUILT_PRODUCTS_DIR}/BOINCSaver.saver/Contents/Resources/English.lproj/InfoPlist.strings\"\n"; }; DDC8FB2B1F6D398700BE55B8 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; @@ -4105,8 +4118,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - DDEED39929ADFDEC00DC3E5D /* mac_branding.cpp in Sources */, + DDF2720F2E16AE6E00960554 /* filesys.cpp in Sources */, DDEED39829ADFDC400DC3E5D /* finish_install.cpp in Sources */, + DDEED39929ADFDEC00DC3E5D /* mac_branding.cpp in Sources */, + DDF2720E2E16A40300960554 /* translate.cpp in Sources */, + DD0AF61C2E21359200C46A81 /* mac_util.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4253,6 +4269,16 @@ target = DD7748970A356C880025D05E /* SetUpSecurity */; targetProxy = DDA12AD00A369DEC00FBDD12 /* PBXContainerItemProxy */; }; + DDA8694A2E1FCE6A00642CD3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DDEED38529ADFD7000DC3E5D /* boinc_finish_install */; + targetProxy = DDA869492E1FCE6A00642CD3 /* PBXContainerItemProxy */; + }; + DDA8694C2E1FCEE400642CD3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B13E2D0E265564D100D5C977 /* detect_rosetta_cpu */; + targetProxy = DDA8694B2E1FCEE400642CD3 /* PBXContainerItemProxy */; + }; DDB874670C850DB600E0DE1F /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = DDB873E90C850BC800E0DE1F /* gfx2libboinc */; @@ -4273,20 +4299,10 @@ target = DD35353007E1E05C00C4718D /* api_libboinc */; targetProxy = DDBD681407FA830E0082C20D /* PBXContainerItemProxy */; }; - DDC04D9D2E28C44B006AB01D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = DDEED38529ADFD7000DC3E5D /* boinc_finish_install */; - targetProxy = DDC04D9C2E28C44B006AB01D /* PBXContainerItemProxy */; - }; - DDC04D9F2E28C473006AB01D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = B13E2D0E265564D100D5C977 /* detect_rosetta_cpu */; - targetProxy = DDC04D9E2E28C473006AB01D /* PBXContainerItemProxy */; - }; - DDC04DA72E2908BC006AB01D /* PBXTargetDependency */ = { + DDC04DA52E2908AB006AB01D /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = DDAEC9E007FA583B00A7BC36 /* SetVersion */; - targetProxy = DDC04DA62E2908BC006AB01D /* PBXContainerItemProxy */; + targetProxy = DDC04DA42E2908AB006AB01D /* PBXContainerItemProxy */; }; DDD095510A3EE09900C95BA4 /* PBXTargetDependency */ = { isa = PBXTargetDependency; @@ -4384,6 +4400,7 @@ DD1AFEB90A512D8700EE5B82 /* Deployment */ = { isa = XCBuildConfiguration; buildSettings = { + COPY_PHASE_STRIP = NO; INFOPLIST_FILE = "Installer-Info.plist"; OTHER_CFLAGS = ( "-D_THREAD_SAFE", @@ -5217,6 +5234,7 @@ isa = XCBuildConfiguration; buildSettings = { COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = "Finish_Install-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "edu.berkeley.boinc.finish-install"; diff --git a/mac_build/boinc.xcodeproj/xcshareddata/xcschemes/boinc_finish_install.xcscheme b/mac_build/boinc.xcodeproj/xcshareddata/xcschemes/boinc_finish_install.xcscheme index 8b8f55ba85b..9a4c6cdd6cb 100644 --- a/mac_build/boinc.xcodeproj/xcshareddata/xcschemes/boinc_finish_install.xcscheme +++ b/mac_build/boinc.xcodeproj/xcshareddata/xcschemes/boinc_finish_install.xcscheme @@ -11,6 +11,15 @@ + + + + diff --git a/mac_installer/PostInstall.cpp b/mac_installer/PostInstall.cpp index e747d96087f..45f493a67c6 100644 --- a/mac_installer/PostInstall.cpp +++ b/mac_installer/PostInstall.cpp @@ -66,6 +66,10 @@ #define USE_OSASCRIPT_FOR_ALL_LOGGED_IN_USERS false +// MPORTANT: The defnition of COPY_FINISH_INSTALL_TO_USER_DIRECTORY +// must match the one in Finish_install.cpp +#define COPY_FINISH_INSTALL_TO_USER_DIRECTORY false + #include #include @@ -111,7 +115,6 @@ Boolean myFilterProc(DialogRef theDialog, EventRecord *theEvent, DialogItemIndex int DeleteReceipt(void); Boolean IsRestartNeeded(); void CheckUserAndGroupConflicts(); -Boolean SetLoginItemOSAScript(long brandID, Boolean deleteLogInItem, char *userName); Boolean SetLoginItemLaunchAgent(long brandID, long oldBrandID, Boolean deleteLogInItem, passwd *pw); OSErr GetCurrentScreenSaverSelection(passwd *pw, char *moduleName, size_t maxLen); OSErr SetScreenSaverSelection(char *moduleName, char *modulePath, int type); @@ -135,7 +138,6 @@ int check_rosetta2_installed(); int optionally_install_rosetta2(); #endif // __arm64__ pid_t FindProcessPID(char* name, pid_t thePID, Boolean currentUserOnly); -static void SleepSeconds(double seconds); static OSErr QuitAppleEventHandler(const AppleEvent *appleEvt, AppleEvent* reply, UInt32 refcon); int callPosixSpawn(const char *cmd); void print_to_log(const char *format, ...); @@ -908,179 +910,6 @@ void CheckUserAndGroupConflicts() #endif // SANDBOX } -enum { - kSystemEventsCreator = 'sevs' -}; - -CFStringRef kSystemEventsBundleID = CFSTR("com.apple.systemevents"); -char *systemEventsAppName = "System Events"; - - -Boolean SetLoginItemOSAScript(long brandID, Boolean deleteLogInItem, char *userName) -{ - int i, j; - char cmd[2048]; - char systemEventsPath[1024]; - pid_t systemEventsPID; - OSErr err, err2; -#if USE_OSASCRIPT_FOR_ALL_LOGGED_IN_USERS - // NOTE: It may not be necessary to kill and relaunch the - // System Events application for each logged in user under High Sierra - Boolean isHighSierraOrLater = (compareOSVersionTo(10, 13) >= 0); -#endif - - fprintf(stdout, "Adjusting login items for user %s\n", userName); - fflush(stdout); - - // We must launch the System Events application for the target user - err = noErr; - systemEventsPath[0] = '\0'; - - err = GetPathToAppFromID(kSystemEventsCreator, kSystemEventsBundleID, systemEventsPath, sizeof(systemEventsPath)); - REPORT_ERROR(err); - -#if CREATE_LOG - if (err == noErr) { - print_to_log("SystemEvents is at %s\n", systemEventsPath); - } else { - print_to_log("GetPathToAppFromID(kSystemEventsCreator, kSystemEventsBundleID) returned error %d ", (int) err); - } -#endif - - if (err == noErr) { - // Find SystemEvents process. If found, quit it in case - // it is running under a different user. - fprintf(stdout, "Telling System Events to quit (at start of SetLoginItemOSAScript)\n"); - fflush(stdout); - systemEventsPID = FindProcessPID(systemEventsAppName, 0, false); - if (systemEventsPID != 0) { - err = kill(systemEventsPID, SIGKILL); - } - if (err != noErr) { - REPORT_ERROR(true); - fprintf(stdout, "(systemEventsPID, SIGKILL) returned error %d \n", (int) err); - fflush(stdout); - } - // Wait for the process to be gone - for (i=0; i<50; ++i) { // 5 seconds max delay - SleepSeconds(0.1); // 1/10 second - systemEventsPID = FindProcessPID(systemEventsAppName, 0, false); - if (systemEventsPID == 0) break; - } - if (i >= 50) { - REPORT_ERROR(true); - fprintf(stdout, "Failed to make System Events quit\n"); - fflush(stdout); - err = noErr; - goto cleanupSystemEvents; - } - sleep(4); - } - - if (systemEventsPath[0] != '\0') { - fprintf(stdout, "Launching SystemEvents for user %s\n", userName); - fflush(stdout); - - for (j=0; j<5; ++j) { - sprintf(cmd, "sudo -u \"%s\" open \"%s\"", userName, systemEventsPath); - err = callPosixSpawn(cmd); - if (err) { - REPORT_ERROR(true); - fprintf(stdout, "[2] Command: %s returned error %d (try %d of 5)\n", cmd, (int) err, j); - fflush(stdout); - } - // Wait for the process to start - for (i=0; i<50; ++i) { // 5 seconds max delay - SleepSeconds(0.1); // 1/10 second - systemEventsPID = FindProcessPID(systemEventsAppName, 0, false); - if (systemEventsPID != 0) break; - } - if (i < 50) break; // Exit j loop on success - } - if (j >= 5) { - fprintf(stdout, "Failed to launch System Events for user %s\n", userName); - REPORT_ERROR(true); - fflush(stdout); - err = noErr; - goto cleanupSystemEvents; - } - } - sleep(2); - - for (i=0; i= 50) { - REPORT_ERROR(true); - fprintf(stdout, "Failed to make System Events quit\n"); - fflush(stdout); - } - - sleep(4); - - return (err == noErr); -} - // Under OS 10.13 High Sierra, telling System Events to modify Login Items for // users who are not currently logged in no longer works, even when System Events @@ -1106,6 +935,9 @@ Boolean SetLoginItemLaunchAgent(long brandID, long oldBrandID, Boolean deleteLog // Create a LaunchAgent to finish installation for the specified user, replacing any LaunchAgent // created previously (such as by Uninstaller or by installing a differently branded BOINC.) + // deleteLogInItem will be true only if we are deleting BOINCManagr + // access for only this user, but installing for others. + // Create LaunchAgents directory for this user if it does not yet exist snprintf(s, sizeof(s), "/Users/%s/Library/LaunchAgents", pw->pw_name); if (stat(s, &sbuf) != 0) { @@ -1124,7 +956,15 @@ Boolean SetLoginItemLaunchAgent(long brandID, long oldBrandID, Boolean deleteLog fprintf(f, "\tedu.berkeley.fix_login_items\n"); fprintf(f, "\tProgramArguments\n"); fprintf(f, "\t\n"); +#if COPY_FINISH_INSTALL_TO_USER_DIRECTORY fprintf(f, "\t\t/Users/%s/Library/Application Support/BOINC/%s_Finish_Install.app/Contents/MacOS/%s_Finish_Install\n", pw->pw_name, brandName[brandID], brandName[brandID]); +#else + // For a reason I do't understand, setting the screensaver under MacOS 26 + // works if we use the BOINC_Finish_Install in the BOINC Data directory + // but not one at /Users/%s/Library/Application Support/BOINC/ so we no + // longer put one in the usr's directory tree. + fprintf(f, "\t\t/Library/Application Support/BOINC Data/%s_Finish_Install.app/Contents/MacOS/%s_Finish_Install\n", brandName[brandID], brandName[brandID]); +#endif if (deleteLogInItem) { fprintf(f, "\t\t-d\n"); fprintf(f, "\t\t%d\n", (int)oldBrandID); @@ -1598,8 +1438,6 @@ OSErr UpdateAllVisibleUsers(long brandID, long oldBrandID) #endif int i; int userIndex; - char path[MAXPATHLEN]; - Boolean hadLoginItemLaunchAgent = false; // char nameOfSkin[256]; // FindSkinName(nameOfSkin, sizeof(nameOfSkin)); @@ -1709,6 +1547,8 @@ OSErr UpdateAllVisibleUsers(long brandID, long oldBrandID) //automatically. I have filed bug report FB13270885 about this. // The response to my bug report is that it will be fixed in a // future rlease of MacOS. + // As of MacOS 26, we again can set the screensaver using Applescripts, + // but only for the current user, so we do it from BOINC_Finish_Install. // See also the comment at top of SetScreenSaverSelection(). if (compareOSVersionTo(14, 0) < 0) { if (! saverAlreadySetForAll) { @@ -1733,7 +1573,6 @@ OSErr UpdateAllVisibleUsers(long brandID, long oldBrandID) fflush(stdout); for (userIndex=0; userIndex<(int)human_user_names.size(); ++userIndex) { - hadLoginItemLaunchAgent = false; strlcpy(human_user_name, human_user_names[userIndex].c_str(), sizeof(human_user_name)); printf("[2] Checking user %s\n", human_user_name); @@ -1837,10 +1676,12 @@ OSErr UpdateAllVisibleUsers(long brandID, long oldBrandID) useOSASript = IsUserLoggedIn(pw->pw_name); } #endif - if (useOSASript) { + if (useOSASript) { snprintf(s, sizeof(s), "/Users/%s/Library/LaunchAgents/edu.berkeley.boinc.plist", pw->pw_name); boinc_delete_file(s); + // We no longer put a copy of %s_Finish_Install in the user's folder, + // but a previous BOINC installation might have put one there. for (i=0; i< NUMBRANDS; i++) { snprintf(s, sizeof(s), "rm -fR \"/Users/%s/Library/Application Support/BOINC/%s_Finish_Install.app\"", pw->pw_name, brandName[i]); err = callPosixSpawn(s); @@ -1850,6 +1691,7 @@ OSErr UpdateAllVisibleUsers(long brandID, long oldBrandID) fflush(stdout); } + // The uninstaller still does put a copy of %s_Finish_Uninstall in the user's folder, snprintf(s, sizeof(s), "rm -fR \"/Users/%s/Library/Application Support/BOINC/%s_Finish_Uninstall.app\"", pw->pw_name, brandName[i]); err = callPosixSpawn(s); REPORT_ERROR(err); @@ -1860,14 +1702,11 @@ OSErr UpdateAllVisibleUsers(long brandID, long oldBrandID) } if (compareOSVersionTo(10, 13) >= 0) { - snprintf(s, sizeof(s), "/Users/%s/Library/LaunchAgents/edu.berkeley.launchboincmanager.plist", pw->pw_name); - if (boinc_file_exists(s)) hadLoginItemLaunchAgent = true; - deleteLoginItem = true; // Use LaunchAgent to autostart BOINC Manager // The -i argument tells BOINC_Finish_Install not to "Launchctl load" our // LaunchAgent, because doing that launches the Manager immediately (before // we can finish setting things up) and the Manager starts incorrectly, // especially causing problems if starting in SimpleView. - snprintf(s, sizeof(s), "open \"/Library/Application Support/BOINC Data/%s_Finish_Install.app\" --args -i", brandName[brandID]); + snprintf(s, sizeof(s), "su -l \"%s\" -c '\"/Library/Application Support/BOINC Data/%s_Finish_Install.app/Contents/MacOS/%s_Finish_Install\" -i %d'", loginName, brandName[brandID], brandName[brandID], (int)brandID); err = callPosixSpawn(s); REPORT_ERROR(err); if (err) { @@ -1875,17 +1714,7 @@ OSErr UpdateAllVisibleUsers(long brandID, long oldBrandID) fflush(stdout); } } - // SetLoginItemOSAScript uses "System Events" which can trigger an aert which - // the user may find alraming. If we previously set a login item launch agent, - // we removed the old style login item at that time, so we avoid that alert. - if (!hadLoginItemLaunchAgent) { - printf("[2] calling SetLoginItemOSAScript for user %s, euid = %d, deleteLoginItem = %d\n", - pw->pw_name, geteuid(), deleteLoginItem); - fflush(stdout); - SetLoginItemOSAScript(brandID, deleteLoginItem, pw->pw_name); - } } else { - printf("[2] calling FixLaunchServicesDataBase for Finish_Install for user %s\n", pw->pw_name); fflush(stdout); FixLaunchServicesDataBase(pw->pw_uid, NULL, "edu.berkeley.boinc.finish-install"); @@ -1904,6 +1733,8 @@ OSErr UpdateAllVisibleUsers(long brandID, long oldBrandID) for (i=0; i< NUMBRANDS; i++) { // If we previously ran the installer for any brand but did not log in to // this user, remove the user's unused BOINC_Manager_Finish_Install file. + // We no longer put a copy of %s_Finish_Install in the user's folder, + // but a previous BOINC installation might have put one there. snprintf(s, sizeof(s), "rm -fR \"/Users/%s/Library/Application Support/BOINC/%s_Finish_Install.app\"", pw->pw_name, brandName[i]); err = callPosixSpawn(s); REPORT_ERROR(err); @@ -1914,6 +1745,7 @@ OSErr UpdateAllVisibleUsers(long brandID, long oldBrandID) // If we previously ran the installer for any brand but did not log in to // this user, remove the user's unused BOINC_Manager_Finish_Uninstall file. + // The uninstaller still does put a copy of %s_Finish_Uninstall in the user's folder, snprintf(s, sizeof(s), "rm -fR \"/Users/%s/Library/Application Support/BOINC/%s_Finish_Uninstall.app\"", pw->pw_name, brandName[i]); err = callPosixSpawn(s); REPORT_ERROR(err); @@ -1923,9 +1755,9 @@ OSErr UpdateAllVisibleUsers(long brandID, long oldBrandID) } } - getPathToThisApp(path, sizeof(path)); - snprintf(s, sizeof(s), "cp -fR \"%s/Contents/Resources/%s_Finish_Install.app\" \"/Users/%s/Library/Application Support/BOINC/\"", - path, brandName[brandID], pw->pw_name); +#if COPY_FINISH_INSTALL_TO_USER_DIRECTORY + snprintf(s, sizeof(s), "cp -fR \"/Library/Application Support/BOINC Data/%s_Finish_Install.app\" \"/Users/%s/Library/Application Support/BOINC/\"", + brandName[brandID], pw->pw_name); err = callPosixSpawn(s); REPORT_ERROR(err); if (err) { @@ -1949,6 +1781,7 @@ OSErr UpdateAllVisibleUsers(long brandID, long oldBrandID) printf("*** user %s: lsregister call returned error %d for %s_Finish_Install.app\n", pw->pw_name, err, brandName[brandID]); fflush(stdout); } +#endif printf("[2] calling SetLoginItemLaunchAgent for user %s, euid = %d, deleteLoginItem = %d\n", pw->pw_name, geteuid(), deleteLoginItem); @@ -1971,14 +1804,16 @@ OSErr UpdateAllVisibleUsers(long brandID, long oldBrandID) } SetSkinInSelectionAndShutdownBySystemFlagInUserPrefs(pw->pw_name, skinName[brandID]); - if (setSaverForAllUsers) { - seteuid(pw->pw_uid); // Temporarily set effective uid to this user - sprintf(s, "/Library/Screen Savers/%s.saver", saverName[brandID]); - err = SetScreenSaverSelection(saverName[brandID], s, 0); - seteuid(saved_uid); // Set effective uid back to privileged user - // This seems to work also: - // sprintf(s, "su -l \"%s\" -c 'defaults -currentHost write com.apple.screensaver moduleDict -dict moduleName \"%s\" path \"/Library/Screen Savers/%s.saver\" type 0'", pw->pw_name, saverName[brandID], s); - // callPosixSpawn(s); + if (compareOSVersionTo(14, 0) < 0) { + if (setSaverForAllUsers) { + seteuid(pw->pw_uid); // Temporarily set effective uid to this user + sprintf(s, "/Library/Screen Savers/%s.saver", saverName[brandID]); + err = SetScreenSaverSelection(saverName[brandID], s, 0); + seteuid(saved_uid); // Set effective uid back to privileged user + // This seems to work also: + // sprintf(s, "su -l \"%s\" -c 'defaults -currentHost write com.apple.screensaver moduleDict -dict moduleName \"%s\" path \"/Library/Screen Savers/%s.saver\" type 0'", pw->pw_name, saverName[brandID], s); + // callPosixSpawn(s); + } } if (compareOSVersionTo(10, 15) >= 0) { @@ -2055,9 +1890,11 @@ OSErr UpdateAllVisibleUsers(long brandID, long oldBrandID) // As of MacOS 14.0 Sonoma, this code no longer will detect the current screensaver, // and will need to be rewritten. See the comment at top of SetScreenSaverSelection(). -// It is unclear whether this will be fixed in a uture rlease of MacOS. -// This Applescript stoll works: +// It is unclear whether this will be fixed in a future rlease of MacOS. +// These Applescripts work again as of MacOS 26, but only for the current user, and +// they will trigger an alert asking for permission: // tell application "System Events" to set mysaver to name of current screen saver +// tell application "System Events" to set current screen saver to screen saver "BOINC Screen Saver"' OSErr GetCurrentScreenSaverSelection(passwd *pw, char *moduleName, size_t maxLen) { char buf[1024]; FILE *f; @@ -2100,7 +1937,10 @@ OSErr GetCurrentScreenSaverSelection(passwd *pw, char *moduleName, size_t maxLen // As of MacOS 14.0 Sonoma, we can't set the screensaver automatically. // I have filed bug report FB13270885 about this. After this is fixed, // probably need to put an AppleScript to do this in the launch agent -// we add for each user. See also: +// we add for each user. +// As of MacOS 26, we again can set the screensaver using Applescripts, +// but only for the current user, so we do it from BOINC_Finish_Install. +// See also: // https://forum.iscreensaver.com/t/understanding-the-macos-sonoma-screensaver-plist/718 OSErr SetScreenSaverSelection(char *moduleName, char *modulePath, int type) { OSErr err = noErr; @@ -2448,24 +2288,6 @@ pid_t FindProcessPID(char* name, pid_t thePID, Boolean currentUserOnly) } -// Uses usleep to sleep for full duration even if a signal is received -static void SleepSeconds(double seconds) { - double end_time = dtime() + seconds - 0.01; - // sleep() and usleep() can be interrupted by SIGALRM, - // so we may need multiple calls - // - while (1) { - if (seconds >= 1) { - sleep((unsigned int) seconds); - } else { - usleep((int)fmod(seconds*1000000, 1000000)); - } - seconds = end_time - dtime(); - if (seconds <= 0) break; - } -} - - static OSErr QuitAppleEventHandler( const AppleEvent *appleEvt, AppleEvent* reply, UInt32 refcon ) { gQuitFlag = true; diff --git a/mac_installer/finish_install.cpp b/mac_installer/finish_install.cpp index 654c13550dd..123a4e915e8 100644 --- a/mac_installer/finish_install.cpp +++ b/mac_installer/finish_install.cpp @@ -16,10 +16,10 @@ // along with BOINC. If not, see . // -// main.cpp +// Finish_install.cpp // boinc_Finish_Install -// Usage: boinc_Finish_Install [-d] [brandID] +// Usage: boinc_Finish_Install [-d | -m] | -i brandID | -a brandID] // // * Deletes Login Items of all possible branded and unbranded BOINC Managers for current user. // * If first argument is -d then also kills the application specified by the second argument. @@ -51,15 +51,30 @@ #include #include // getpwname, getpwuid, getuid #include +#include #include "mac_branding.h" +#include "translate.h" +#include "mac_util.h" + +#define MAX_LANGUAGES_TO_TRY 5 + +// MPORTANT: The defnition of COPY_FINISH_INSTALL_TO_USER_DIRECTORY +// must match the one in PostInstall.cpp +#define COPY_FINISH_INSTALL_TO_USER_DIRECTORY false + + +static char * Catalog_Name = (char *)"BOINC-Setup"; +static char * Catalogs_Dir = "/Library/Application Support/BOINC Data/locale/"; static int callPosixSpawn(const char *cmd); static Boolean MakeLaunchManagerLaunchAgent(long brandID, passwd *pw); static void FixLaunchServicesDataBase(int brandId, bool isUninstall); static Boolean IsUserActive(const char *userName); +void MaybeSetScreenSaver(int brandId); static char * PersistentFGets(char *buf, size_t buflen, FILE *f); -static int compareOSVersionTo(int toMajor, int toMinor); +static Boolean ShowMessage(Boolean askYesNo, const char *format, ...); +static void GetAndLoadPreferredLanguages(); static void print_to_log_file(const char *format, ...); int main(int argc, const char * argv[]) { @@ -71,8 +86,8 @@ int main(int argc, const char * argv[]) { bool isUninstall = false; int iBrandId = 0; bool calledFromInstaller = false; + bool createdByUninstaller = false; bool calledFromManager = false; - struct stat buf; // Wait until we are the active login (in case of fast user switching) userName = getenv("USER"); @@ -81,32 +96,20 @@ int main(int argc, const char * argv[]) { } pw = getpwuid(getuid()); - - // Using "System Events" which can trigger an alert which the user may find alraming. - // If we previously set a login item launch agent, we removed the old style login - // item at that time, so we can avoid that alert. - snprintf(cmd, sizeof(cmd), "/Users/%s/Library/LaunchAgents/edu.berkeley.launchboincmanager.plist", pw->pw_name); - if (!stat(cmd, &buf)) { - for (i=0; i= 0) { snprintf(cmd, sizeof(cmd), "launchctl unload \"/Users/%s/Library/LaunchAgents/edu.berkeley.launchboincmanager.plist\"", pw->pw_name); err = callPosixSpawn(cmd); if (err) { - print_to_log_file("Command: %s\n", cmd); - print_to_log_file("returned error %d\n", err); } sprintf(cmd, "rm -f \"/Users/%s/Library/LaunchAgents/edu.berkeley.launchboincmanager.plist\"", pw->pw_name); callPosixSpawn (cmd); if (err) { - print_to_log_file("Command: %s\n", cmd); - print_to_log_file("returned error %d\n", err); - } + } } } else { - if (compareOSVersionTo(10, 13) >= 0) { - bool success = MakeLaunchManagerLaunchAgent(iBrandId, pw); - if (!success) { - print_to_log_file("Command: %s\n", cmd); - print_to_log_file("MakeLaunchManagerLaunchAgent for %s failed\n", appName[iBrandId]); - } - } else { - snprintf(cmd, sizeof(cmd), "osascript -e 'tell application \"System Events\" to make new login item at end with properties {path:\"%s\", hidden:true, name:\"%s\"}'", appPath[iBrandId], appName[iBrandId]); - err = callPosixSpawn(cmd); - if (err) { - print_to_log_file("Command: %s\n", cmd); - print_to_log_file("Make new login item for %s returned error %d\n", appName[iBrandId], err); - } + bool success = MakeLaunchManagerLaunchAgent(iBrandId, pw); + if (!success) { + } + if (compareOSVersionTo(26, 0) >= 0) { + GetAndLoadPreferredLanguages(); + MaybeSetScreenSaver(iBrandId); } if (compareOSVersionTo(10, 13) >= 0) { snprintf(cmd, sizeof(cmd), "launchctl unload \"/Users/%s/Library/LaunchAgents/edu.berkeley.launchboincmanager.plist\"", pw->pw_name); err = callPosixSpawn(cmd); if (err) { - print_to_log_file("Command: %s\n", cmd); - print_to_log_file("returned error %d\n", err); } if (! (calledFromInstaller || calledFromManager)) { snprintf(cmd, sizeof(cmd), "launchctl load \"/Users/%s/Library/LaunchAgents/edu.berkeley.launchboincmanager.plist\"", pw->pw_name); err = callPosixSpawn(cmd); if (err) { - print_to_log_file("Command: %s\n", cmd); - print_to_log_file("returned error %d\n", err); } } @@ -174,8 +160,6 @@ int main(int argc, const char * argv[]) { snprintf(cmd, sizeof(cmd), "open -jg \"%s\"", appPath[iBrandId]); err = callPosixSpawn(cmd); if (err) { - print_to_log_file("Command: %s\n", cmd); - print_to_log_file("\"open -jg \"%s\" returned error %d\n", appPath[iBrandId], err); } } } @@ -198,8 +182,19 @@ int main(int argc, const char * argv[]) { // Delete per-user BOINC Manager and screensaver files, including this executable fprintf(f, "rm -fR \"/Users/%s/Library/Application Support/BOINC\"\n", pw->pw_name); } else { - // Delete only this executable - fprintf(f, "rm -fR \"/Users/%s/Library/Application Support/BOINC/%s_Finish_Install.app\"", pw->pw_name, brandName[iBrandId]); +#if ! COPY_FINISH_INSTALL_TO_USER_DIRECTORY + // The installer no longer put a copy of %s_Finish_Install in the user's folder; + // all users now call the one in the BOINC Data folder + // But the uninstaller stil puts a copy of %s_Finish_Uninstall in the user's + // folder (in case the user might have already deleted the BOINC Data directory.) + // So we don't want to delete ourselves unless we were created by the uninstaller. + // The uninstaller set up the launchagent to pass us a "u" to tell us self-delete. + if (createdByUninstaller) +#endif + { + // Delete only this executable + fprintf(f, "rm -fR \"/Users/%s/Library/Application Support/BOINC/%s_Finish_Install.app\"", pw->pw_name, brandName[iBrandId]); + } } fclose(f); sprintf(cmd, "sh \"%s\"", scriptName); @@ -362,41 +357,247 @@ static Boolean IsUserActive(const char *userName){ } -// Test OS version number on all versions of OS X without using deprecated Gestalt -// compareOSVersionTo(x, y) returns: -// -1 if the OS version we are running on is less than x.y -// 0 if the OS version we are running on is equal to x.y -// +1 if the OS version we are running on is lgreater than x.y -static int compareOSVersionTo(int toMajor, int toMinor) { - static SInt32 major = -1; - static SInt32 minor = -1; - - if (major < 0) { - char vers[100], *p1 = NULL; - FILE *f; - vers[0] = '\0'; - f = popen("sw_vers -productVersion", "r"); - if (f) { - fscanf(f, "%s", vers); - pclose(f); +// It would be nice to check whether our screen saver is already set for +// this user so we can ask wethr to set it only if necessary, but that +// triggrs a scary warning asking for permission to let System Events +// administer the Mac, so we don't. (If the user answers yes to setting +// the screensaver, then the code which sets the screensaver will show +// that warning, but this seems less scary than having it pop up for no +// apparent reason.) +void MaybeSetScreenSaver(int brandId){ + char buf[MAXPATHLEN]; + char mySaverName[1024]; + + if ((ShowMessage(true, + (char *)_("Do you want to set %s as your screensaver?"), + brandName[brandId], brandName[brandId])) == false) { + return; + } + + CFBundleRef myBundle = NULL; + mySaverName[0] = '\0'; + snprintf(buf, sizeof(buf), "/Library/Screen Savers/%s.saver", saverName[brandId]); + CFStringRef cfpath = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8); + if (cfpath) { + CFURLRef bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, cfpath, kCFURLPOSIXPathStyle, true); + if (bundleURL) { + myBundle = CFBundleCreate(kCFAllocatorDefault, bundleURL); + CFRelease(bundleURL); + } + CFRelease(cfpath); + } + + if (myBundle) { + CFStringRef bundleNameCF = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(myBundle, CFSTR("CFBundleName")); + if (bundleNameCF) { + strncpy(mySaverName, CFStringGetCStringPtr(bundleNameCF, kCFStringEncodingUTF8), sizeof(mySaverName)); } - if (vers[0] == '\0') { - print_to_log_file("popen(\"sw_vers -productVersion\" failed\n"); - return 0; + if(mySaverName[0] == '\0') { + strncpy(mySaverName, saverName[brandId], sizeof(mySaverName)); } - // Extract the major system version number - major = atoi(vers); - // Extract the minor system version number - p1 = strchr(vers, '.'); - minor = atoi(p1+1); + + // The AppleEvent call doesn't work unless we have opened the Sysem Preferences + // Wallpaper pane, even though that's not necessary if we do the osascript from + // Terminal.app or run the Applescript from the Script Editor or from an + // Applescript application created by the Script Editor. + // + // Use -n flag to open a new instance of the System Settings even if one is + // already running. We can then use the returned PID to quit only that instance. + sprintf(buf, "open -gj \"x-apple.systempreferences:com.apple.Wallpaper-Settings.extension\""); + callPosixSpawn(buf); + + sleep(1); // This delay seems to be needed, probably to give Wallpaper pane time to open + + sprintf(buf, "osascript -e 'tell application \"System Events\" to set current screen saver to screen saver \"%s\"'", mySaverName); + callPosixSpawn(buf); + + // TODO: find a way to kill only the new hidden instance of System Settigs that we opened + callPosixSpawn("killall \"System Settings\""); + + CFRelease(myBundle); } +} - if (major < toMajor) return -1; - if (major > toMajor) return 1; - // if (major == toMajor) compare minor version numbers - if (minor < toMinor) return -1; - if (minor > toMinor) return 1; - return 0; + +static void GetAndLoadPreferredLanguages() { + DIR *dirp; + struct dirent *dp; + char searchPath[MAXPATHLEN]; + struct stat sbuf; + CFMutableArrayRef supportedLanguages; + CFStringRef aLanguage; + char shortLanguage[32]; + CFArrayRef preferredLanguages; + int i, j, k; + char * language; + char *uscore; + + // Create an array of all our supported languages + supportedLanguages = CFArrayCreateMutable(kCFAllocatorDefault, 100, &kCFTypeArrayCallBacks); + + aLanguage = CFStringCreateWithCString(NULL, "en", kCFStringEncodingMacRoman); + CFArrayAppendValue(supportedLanguages, aLanguage); + CFRelease(aLanguage); + aLanguage = NULL; + + dirp = opendir(Catalogs_Dir); + if (!dirp) { + goto cleanup; + } + while (true) { + dp = readdir(dirp); + if (dp == NULL) + break; // End of list + + if (dp->d_name[0] == '.') + continue; // Ignore names beginning with '.' + + strlcpy(searchPath, Catalogs_Dir, sizeof(searchPath)); + strlcat(searchPath, dp->d_name, sizeof(searchPath)); + strlcat(searchPath, "/", sizeof(searchPath)); + strlcat(searchPath, Catalog_Name, sizeof(searchPath)); + strlcat(searchPath, ".mo", sizeof(searchPath)); + if (stat(searchPath, &sbuf) != 0) continue; + // printf("Adding %s to supportedLanguages array\n", dp->d_name); + aLanguage = CFStringCreateWithCString(NULL, dp->d_name, kCFStringEncodingMacRoman); + CFArrayAppendValue(supportedLanguages, aLanguage); + CFRelease(aLanguage); + aLanguage = NULL; + + // If it has a region code ("it_IT") also try without region code ("it") + // TODO: Find a more general solution + strlcpy(shortLanguage, dp->d_name, sizeof(shortLanguage)); + uscore = strchr(shortLanguage, '_'); + if (uscore) { + *uscore = '\0'; + aLanguage = CFStringCreateWithCString(NULL, shortLanguage, kCFStringEncodingMacRoman); + CFArrayAppendValue(supportedLanguages, aLanguage); + CFRelease(aLanguage); + aLanguage = NULL; + } + } + + closedir(dirp); + + for (i=0; i=0; --k) { + if (CFStringCompare(aLanguage, (CFStringRef)CFArrayGetValueAtIndex(supportedLanguages, k), 0) == kCFCompareEqualTo) { + CFArrayRemoveValueAtIndex(supportedLanguages, k); + } + } + + // Since the original strings are English, no + // further translation is needed for language en. + if (language) { + if (!strcmp(language, "en")) { + CFRelease(preferredLanguages); + preferredLanguages = NULL; + goto cleanup; + } + } + } + + CFRelease(preferredLanguages); + preferredLanguages = NULL; + + } + + if (!BOINCTranslationAddCatalog(Catalogs_Dir, "en", Catalog_Name)) { + printf("could not load catalog for langage en\n"); + } + +cleanup: + CFArrayRemoveAllValues(supportedLanguages); + CFRelease(supportedLanguages); + supportedLanguages = NULL; +#if CREATE_LOG + print_to_log_file("Exiting GetAndLoadPreferredLanguages"); +#endif +} + + +static Boolean ShowMessage(Boolean askYesNo, const char *format, ...) { + // CAUTION: vsprintf will produce undesirable results if the string + // contains a % character that is not a format specification! + // But CFString is OK! + + va_list args; + char s[1024]; + CFOptionFlags responseFlags; + CFURLRef myIconURLRef = NULL; + CFBundleRef myBundleRef; + + myBundleRef = CFBundleGetMainBundle(); + if (myBundleRef) { + myIconURLRef = CFBundleCopyResourceURL(myBundleRef, CFSTR("MacInstaller.icns"), NULL, NULL); + } + +#if 1 + va_start(args, format); + vsprintf(s, format, args); + va_end(args); +#else + strcpy(s, format); +#endif + + // If defaultButton is nil or an empty string, a default localized + // button title ("OK" in English) is used. + +#if 0 + enum { + kCFUserNotificationDefaultResponse = 0, + kCFUserNotificationAlternateResponse = 1, + kCFUserNotificationOtherResponse = 2, + kCFUserNotificationCancelResponse = 3 +}; +#endif + + CFStringRef myString = CFStringCreateWithCString(NULL, s, kCFStringEncodingUTF8); + CFStringRef yes = CFStringCreateWithCString(NULL, (char*)_((char*)"Yes"), kCFStringEncodingUTF8); + CFStringRef no = CFStringCreateWithCString(NULL, (char*)_((char*)"No"), kCFStringEncodingUTF8); + +// BringAppToFront(); + SInt32 retval = CFUserNotificationDisplayAlert(0.0, kCFUserNotificationPlainAlertLevel, + myIconURLRef, NULL, NULL, CFSTR(" "), myString, + askYesNo ? yes : NULL, askYesNo ? no : NULL, NULL, + &responseFlags); + + + if (myIconURLRef) CFRelease(myIconURLRef); + if (myString) CFRelease(myString); + if (yes) CFRelease(yes); + if (no) CFRelease(no); + + if (retval) return false; + return (responseFlags == kCFUserNotificationDefaultResponse); } diff --git a/mac_installer/release_boinc.sh b/mac_installer/release_boinc.sh index 1c07f6fa135..61216811f3a 100644 --- a/mac_installer/release_boinc.sh +++ b/mac_installer/release_boinc.sh @@ -62,6 +62,7 @@ ## Updated 4/30/23 code sign AddRemoveUser; eliminate old code signing workaround ## Updated 5/17/23 to add comments about notarize_BOINC.sh script ## Updated 2/12/25 to add support for Fix_BOINC_Users utility +## Updated 7/22/25 to add MacOS 26 support, no Finish_Install embedded in Postinstall. ## ## NOTE: This script requires Mac OS 10.7 or later, and uses XCode developer ## tools. So you must have installed XCode Developer Tools on the Mac @@ -351,22 +352,33 @@ echo "BrandId=0" > "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOS # Change BOINC_Finish_Install.app embedded in uninstaller to BOINC_Finish_Uninstall.app sudo mv "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall BOINC.app/Contents/Resources/BOINC_Finish_Install.app" "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall BOINC.app/Contents/Resources/BOINC_Finish_Uninstall.app" -# Change executable name of BOINC_Finish_Uninstall.app to match app name +# Change executable name of BOINC_Finish_Uninstall.app embedded in uninstaller to match app name sudo mv "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall BOINC.app/Contents/Resources/BOINC_Finish_Uninstall.app/Contents/MacOS/BOINC_Finish_Install" "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall BOINC.app/Contents/Resources/BOINC_Finish_Uninstall.app/Contents/MacOS/BOINC_Finish_Uninstall" -# Fix version number in Info.plist file of BOINC_Finish_Uninstall.app +# Fix version number in Info.plist file of BOINC_Finish_Uninstall.app embedded +# in uninstaller sudo plutil -replace CFBundleVersion -string `plutil -extract CFBundleVersion raw "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall BOINC.app/Contents/Info.plist"` "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall BOINC.app/Contents/Resources/BOINC_Finish_Uninstall.app/Contents/Info.plist" sudo plutil -remove CFBundleShortVersionString "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall BOINC.app/Contents/Resources/BOINC_Finish_Uninstall.app/Contents/Info.plist" -# Fix bundle name in Info.plist file of BOINC_Finish_Uninstall.app +# Fix bundle name in Info.plist file of BOINC_Finish_Uninstall.app embedded in uninstaller sudo plutil -replace CFBundleName -string "BOINC_Finish_Uninstall" "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall BOINC.app/Contents/Resources/BOINC_Finish_Uninstall.app/Contents/Info.plist" sudo plutil -replace CFBundleExecutable -string "BOINC_Finish_Uninstall" "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall BOINC.app/Contents/Resources/BOINC_Finish_Uninstall.app/Contents/Info.plist" +# If you re-enable this you must also change it in uninstall.cpp # Change Bundle ID of BOINC_Finish_Uninstall.app ###sudo plutil -replace CFBundleIdentifier -string edu.berkeley.boinc.finish-uninstall "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall BOINC.app/Contents/Resources/BOINC_Finish_Uninstall.app/Contents/Info.plist" +# Replace icon reference in Info.plist file of BOINC_Finish_Uninstall.app embedded +# in uninstaller +sudo plutil -replace CFBundleIconFile -string "MacUninstaller.icns" "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall BOINC.app/Contents/Resources/BOINC_Finish_Uninstall.app/Contents/Info.plist" + +# Replace icon in Info.plist file of BOINC_Finish_Uninstall.app embedded in uninstaller +sudo rm -dfR "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall BOINC.app/Contents/Resources/BOINC_Finish_Uninstall.app/Contents/Resources/MacInstaller.icns" + +sudo cp -fpRL "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall BOINC.app/Contents/Resources/MacUninstaller.icns" "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall BOINC.app/Contents/Resources/BOINC_Finish_Uninstall.app/Contents/Resources" + sudo chown -R root:admin ../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall\ BOINC.app sudo chmod -R u+r-w,g+r-w,o+r-w ../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall\ BOINC.app @@ -379,15 +391,10 @@ cp -fpRL "${BUILDPATH}/BOINC Installer.app" ../BOINC_Installer/New_Release_$1_$2 cp -fpR "${BUILDPATH}/PostInstall.app" "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC Installer.app/Contents/Resources" -echo "BrandId=0" > "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC Installer.app/Contents/Resources/PostInstall.app/Contents/Resources/Branding" - -# Fix version number in Info.plist file of BOINC_Finish_Install.app -sudo plutil -replace CFBundleVersion -string `plutil -extract CFBundleVersion raw "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC Installer.app/Contents/Info.plist"` "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC Installer.app/Contents/Resources/PostInstall.app/Contents/Resources/BOINC_Finish_Install.app/Contents/Info.plist" - -sudo plutil -remove CFBundleShortVersionString "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC Installer.app/Contents/Resources/PostInstall.app/Contents/Resources/BOINC_Finish_Install.app/Contents/Info.plist" +# Copy BOINC_Finish_Install.app into BOINC Data folder +cp -fpR "${BUILDPATH}/BOINC_Finish_Install.app" "../BOINC_Installer/Pkg_Root/Library/Application Support/BOINC Data/" -# Copy BOINC_Finish_Install.app into BOINC Data folder for possible use by AddRemoveUser -sudo cp -fpRL "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC Installer.app/Contents/Resources/PostInstall.app/Contents/Resources/BOINC_Finish_Install.app" "../BOINC_Installer/Pkg_Root/Library/Application Support/BOINC Data/" +echo "BrandId=0" > "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC Installer.app/Contents/Resources/PostInstall.app/Contents/Resources/Branding" ## If you wish to code sign the client, manager, installer and uninstaller, ## create a file ~/BOINCCodeSignIdentities.txt whose first line is the @@ -439,13 +446,10 @@ if [ -e "${HOME}/BOINCCodeSignIdentities.txt" ]; then # Code Sign the BOINC Manager if we have a signing identity sudo codesign -f -o runtime -s "${APPSIGNINGIDENTITY}" "../BOINC_Installer/Pkg_Root/Applications/BOINCManager.app" - # Code Sign BOINC_Finish_Install app embedded in the PostInstall app if we have a signing identity - sudo codesign -f -o runtime -s "${APPSIGNINGIDENTITY}" "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC Installer.app/Contents/Resources/PostInstall.app/Contents/Resources/BOINC_Finish_Install.app" - # Code Sign the PostInstall app embedded in the BOINC installer app if we have a signing identity sudo codesign -f -o runtime -s "${APPSIGNINGIDENTITY}" "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC Installer.app/Contents/Resources/PostInstall.app" - # Code Sign BOINC_Finish_Install app embedded in BOINC uninstaller app if we have a signing identity + # Code Sign BOINC_Finish_BOINC_Finish_Uninstall app embedded in BOINC uninstaller app if we have a signing identity sudo codesign -f -o runtime -s "${APPSIGNINGIDENTITY}" "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall BOINC.app/Contents/Resources/BOINC_Finish_Uninstall.app" # Code Sign the BOINC uninstaller app if we have a signing identity diff --git a/mac_installer/release_brand.sh b/mac_installer/release_brand.sh index c02ec5925e3..31b8c4cc2b5 100755 --- a/mac_installer/release_brand.sh +++ b/mac_installer/release_brand.sh @@ -191,7 +191,7 @@ if [ $Products_Have_arm64 = "yes" ]; then fi fi -for Executable in "boinc" "boinccmd" "switcher" "setprojectgrp" "boincscr" "Fix_BOINC_Users" "BOINCSaver.saver/Contents/MacOS/BOINCSaver" "Uninstall BOINC.app/Contents/MacOS/Uninstall BOINC" "BOINC Installer.app/Contents/MacOS/BOINC Installer" "PostInstall.app/Contents/MacOS/PostInstall" "BOINC_Finish_Install.app/Contents/MacOS/BOINC_Finish_Install" +for Executable in "boinc" "boinccmd" "switcher" "setprojectgrp" "boincscr" "Fix_BOINC_Users" "BOINCSaver.saver/Contents/MacOS/BOINCSaver" "Uninstall BOINC.app/Contents/MacOS/Uninstall BOINC" "BOINC Installer.app/Contents/MacOS/BOINC Installer" "PostInstall.app/Contents/MacOS/PostInstall" "BOINC_Finish_Install.app/Contents/MacOS/BOINC_Finish_Install" "AddRemoveUser" do Have_x86_64="no" Have_arm64="no" @@ -390,23 +390,27 @@ rm -rf "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNA mv "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/MacOS/BOINC Installer" "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/MacOS/${INSTALLERAPPNAME}" sed -i "" s/"BOINC Installer"/"${INSTALLERAPPNAME}"/g "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/Resources/English.lproj/InfoPlist.strings" -cp -fpR "${BUILDPATH}/PostInstall.app" "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/Resources" +# Copy BOINC_Finish_Install.app into BOINC Data folder +cp -fpR "${BUILDPATH}/BOINC_Finish_Install.app" "../BOINC_Installer/Pkg_Root/Library/Application Support/BOINC Data/" -# Rename BOINC_Finish_Install.app embedded in PostInstall.app -sudo mv "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/Resources/PostInstall.app/Contents/Resources/BOINC_Finish_Install.app" "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/Resources/PostInstall.app/Contents/Resources/${LONGBRANDNAME}_Finish_Install.app" +# Rename BOINC_Finish_Install.app +sudo mv "../BOINC_Installer/Pkg_Root/Library/Application Support/BOINC Data/BOINC_Finish_Install.app" "../BOINC_Installer/Pkg_Root/Library/Application Support/BOINC Data/${LONGBRANDNAME}_Finish_Install.app" # Change executable name of BOINC_Finish_Install.app to match app name -sudo mv "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/Resources/PostInstall.app/Contents/Resources/${LONGBRANDNAME}_Finish_Install.app/Contents/MacOS/BOINC_Finish_Install" "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/Resources/PostInstall.app/Contents/Resources/${LONGBRANDNAME}_Finish_Install.app/Contents/MacOS/${LONGBRANDNAME}_Finish_Install" +sudo mv "../BOINC_Installer/Pkg_Root/Library/Application Support/BOINC Data/${LONGBRANDNAME}_Finish_Install.app/Contents/MacOS/BOINC_Finish_Install" "../BOINC_Installer/Pkg_Root/Library/Application Support/BOINC Data/${LONGBRANDNAME}_Finish_Install.app/Contents/MacOS/${LONGBRANDNAME}_Finish_Install" + +sudo plutil -replace CFBundleExecutable -string "${LONGBRANDNAME}_Finish_Install" "../BOINC_Installer/Pkg_Root/Library/Application Support/BOINC Data/${LONGBRANDNAME}_Finish_Install.app/Contents/Info.plist" -# Fix version number in Info.plist file of BOINC_Finish_Install.app -sudo plutil -replace CFBundleVersion -string `plutil -extract CFBundleVersion raw "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/Info.plist"` "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/Resources/PostInstall.app/Contents/Resources/${LONGBRANDNAME}_Finish_Install.app/Contents/Info.plist" +# Replace icon reference in Info.plist file of ${LONGBRANDNAME}_Finish_Install.app +sudo plutil -replace CFBundleIconFile -string "${INSTALLERICON}.icns" "../BOINC_Installer/Pkg_Root/Library/Application Support/BOINC Data/${LONGBRANDNAME}_Finish_Install.app/Contents/Info.plist" -sudo plutil -remove CFBundleShortVersionString "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/Resources/PostInstall.app/Contents/Resources/${LONGBRANDNAME}_Finish_Install.app/Contents/Info.plist" +# Replace icon in ${LONGBRANDNAME}_Finish_Install.app +sudo rm -dfR "../BOINC_Installer/Pkg_Root/Library/Application Support/BOINC Data/${LONGBRANDNAME}_Finish_Install.app/Contents/Resources/MacInstaller.icns" -sudo plutil -replace CFBundleExecutable -string "${LONGBRANDNAME}_Finish_Install" "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/Resources/PostInstall.app/Contents/Resources/${LONGBRANDNAME}_Finish_Install.app/Contents/Info.plist" +sudo cp -fpRL "./clientgui/res/${INSTALLERICON}.icns" "../BOINC_Installer/Pkg_Root/Library/Application Support/BOINC Data/${LONGBRANDNAME}_Finish_Install.app/Contents/Resources/" -# Copy BOINC_Finish_Install.app into BOINC Data folder for possible use by AddRemoveUser -sudo cp -fpRL "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/Resources/PostInstall.app/Contents/Resources/${LONGBRANDNAME}_Finish_Install.app" "../BOINC_Installer/Pkg_Root/Library/Application Support/BOINC Data/" + +cp -fpR "${BUILDPATH}/PostInstall.app" "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/Resources" # Change BOINC_Finish_Install.app embedded in uninstaller to BOINC_Finish_Uninstall.app sudo mv "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/extras/${UNINSTALLERAPPNAME}.app/Contents/Resources/BOINC_Finish_Install.app" "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/extras/${UNINSTALLERAPPNAME}.app/Contents/Resources/${LONGBRANDNAME}_Finish_Uninstall.app" @@ -427,8 +431,21 @@ sudo plutil -replace CFBundleExecutable -string "${LONGBRANDNAME}_Finish_Uninsta # Change Bundle ID of BOINC_Finish_Uninstall.app ###sudo plutil -replace CFBundleIdentifier -string edu.berkeley.boinc.finish-uninstall "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/extras/${UNINSTALLERAPPNAME}.app/Contents/Resources/${LONGBRANDNAME}_Finish_Uninstall.app/Contents/Info.plist" +# Replace icon in ${LONGBRANDNAME}_Finish_Uninstall.app +# Replace MacInstaller.icns with ${UNINSTALLERICON}.icns, but renamed MacInstaller.icns +###cp -fpRL ./clientgui/res/${UNINSTALLERICON}.icns "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/extras/${UNINSTALLERAPPNAME}.app/Contents/Resources/${LONGBRANDNAME}_Finish_Uninstall.app/Contents/Resources/MacInstaller.icns" + +# Replace icon reference in Info.plist file of ${LONGBRANDNAME}_Finish_Uninstall.app +sudo plutil -replace CFBundleIconFile -string "${UNINSTALLERICON}.icns" "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/extras/${UNINSTALLERAPPNAME}.app/Contents/Resources/${LONGBRANDNAME}_Finish_Uninstall.app/Contents/Info.plist" + +# Replace icon in in ${LONGBRANDNAME}_Finish_Uninstall.app +sudo rm -dfR "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/extras/${UNINSTALLERAPPNAME}.app/Contents/Resources/${LONGBRANDNAME}_Finish_Uninstall.app/Contents/Resources/MacInstaller.icns" + +sudo cp -fpRL "./clientgui/res/${UNINSTALLERICON}.icns" "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/extras/${UNINSTALLERAPPNAME}.app/Contents/Resources/${LONGBRANDNAME}_Finish_Uninstall.app/Contents/Resources/" + echo ${BRANDING_INFO} > "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/Resources/PostInstall.app/Contents/Resources/Branding" +# Replace MacInstaller.icns with ${INSTALLERICON}.icns, but renamed MacInstaller.icns cp -fpRL ./clientgui/res/${INSTALLERICON}.icns "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/Resources/PostInstall.app/Contents/Resources/MacInstaller.icns" @@ -479,9 +496,6 @@ if [ -e "${HOME}/BOINCCodeSignIdentities.txt" ]; then # Code Sign the BOINC Manager if we have a signing identity sudo codesign -f -o runtime -s "${APPSIGNINGIDENTITY}" "../BOINC_Installer/Pkg_Root/Applications/${MANAGERAPPNAME}.app" - # Code Sign BOINC_Finish_Install app embedded in the PostInstall app if we have a signing identity - sudo codesign -f -o runtime -s "${APPSIGNINGIDENTITY}" "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/Resources/PostInstall.app/Contents/Resources/${LONGBRANDNAME}_Finish_Install.app" - # Code Sign the PostInstall app embedded in the BOINC installer app if we have a signing identity sudo codesign -f -o runtime -s "${APPSIGNINGIDENTITY}" "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/${INSTALLERAPPNAME}.app/Contents/Resources/PostInstall.app" From 93d4484663963745cac788aad2f8852a3a62cf4d Mon Sep 17 00:00:00 2001 From: Charlie Fenton Date: Thu, 24 Jul 2025 07:31:03 -0700 Subject: [PATCH 3/8] Fix trailing whitespace --- client/check_security.cpp | 2 +- mac_installer/finish_install.cpp | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/client/check_security.cpp b/client/check_security.cpp index b966a86fec1..669919b54ae 100644 --- a/client/check_security.cpp +++ b/client/check_security.cpp @@ -248,7 +248,7 @@ int use_sandbox, int isManager, char* path_to_error, int len fscanf(f, "BrandId=%ld\n", &brandId); fclose(f); } - + snprintf(full_path, sizeof(full_path),"/%s/Contents/Resources/boinc", appPath[brandId]); retval = stat(full_path, &sbuf); if (retval) diff --git a/mac_installer/finish_install.cpp b/mac_installer/finish_install.cpp index 123a4e915e8..c2ea60bf1c8 100644 --- a/mac_installer/finish_install.cpp +++ b/mac_installer/finish_install.cpp @@ -96,7 +96,7 @@ int main(int argc, const char * argv[]) { } pw = getpwuid(getuid()); - + for (i=1; id_name[0] == '.') continue; // Ignore names beginning with '.' - + strlcpy(searchPath, Catalogs_Dir, sizeof(searchPath)); strlcat(searchPath, dp->d_name, sizeof(searchPath)); strlcat(searchPath, "/", sizeof(searchPath)); @@ -463,7 +463,7 @@ static void GetAndLoadPreferredLanguages() { CFArrayAppendValue(supportedLanguages, aLanguage); CFRelease(aLanguage); aLanguage = NULL; - + // If it has a region code ("it_IT") also try without region code ("it") // TODO: Find a more general solution strlcpy(shortLanguage, dp->d_name, sizeof(shortLanguage)); @@ -476,12 +476,12 @@ static void GetAndLoadPreferredLanguages() { aLanguage = NULL; } } - + closedir(dirp); - + for (i=0; i Date: Thu, 24 Jul 2025 07:32:01 -0700 Subject: [PATCH 4/8] Mac Screensaver: don't try to draw moving logo when covered by project graphics --- clientscr/Mac_Saver_ModuleView.m | 273 ++++++++++++++++--------------- 1 file changed, 140 insertions(+), 133 deletions(-) diff --git a/clientscr/Mac_Saver_ModuleView.m b/clientscr/Mac_Saver_ModuleView.m index 96d0aa193cc..19007d08d2e 100644 --- a/clientscr/Mac_Saver_ModuleView.m +++ b/clientscr/Mac_Saver_ModuleView.m @@ -164,6 +164,7 @@ static int DPI_multiplier = 1; static bool myIsPreview; static bool screensaverIsVisible = false; +static bool logoIsCovered = false; //static long counter = 0; // CAF static NSTimer * myTimer = NULL; @@ -515,7 +516,7 @@ - (void)doPeriodicTasks { if (mySharedGraphicsController) { [ mySharedGraphicsController cleanUpOpenGL ]; // Must be called from main thread [ mySharedGraphicsController closeServerPort ]; // Must be called after cleanUpOpenGL - [ NSApp terminate:nil ]; // Comment out to let legacyScreensaver continue in background +// [ NSApp terminate:nil ]; // Comment out to let legacyScreensaver continue in background } return; } @@ -748,116 +749,117 @@ - (void)doPeriodicTasks { currentDrawingRect.origin.x = (float) ((int)gCurrentPosition.x); currentDrawingRect.origin.y += (float) ((int)gCurrentPosition.y - gTextBoxHeight); - if ( (msg != NULL) && (msg[0] != '\0') ) { - cf_msg = CFStringCreateWithCString(NULL, msg, kCFStringEncodingMacRoman); - if (strncmp(msg, gLastMsg, sizeof(gLastMsg))) { - strlcpy(gLastMsg, msg, sizeof(gLastMsg)); - - CTFontRef myFont = CTFontCreateWithName(CFSTR("Helvetica"), 20, NULL); - HIThemeTextInfo theTextInfo = {kHIThemeTextInfoVersionOne, kThemeStateActive, kThemeSpecifiedFont, - kHIThemeTextHorizontalFlushLeft, kHIThemeTextVerticalFlushTop, - kHIThemeTextBoxOptionNone, kHIThemeTextTruncationNone, 0, false, - 0, myFont - }; - gTextInfo = theTextInfo; - - HIThemeGetTextDimensions(cf_msg, (float)gMovingRect.size.width, &gTextInfo, NULL, &actualTextBoxHeight, NULL); - gTextBoxHeight = actualTextBoxHeight + TEXTBOXTOPBORDER; - } - - if (myTimer == NULL) { - return; - } + if (! logoIsCovered) { + if ( (msg != NULL) && (msg[0] != '\0') ) { + cf_msg = CFStringCreateWithCString(NULL, msg, kCFStringEncodingMacRoman); + if (strncmp(msg, gLastMsg, sizeof(gLastMsg))) { + strlcpy(gLastMsg, msg, sizeof(gLastMsg)); + + CTFontRef myFont = CTFontCreateWithName(CFSTR("Helvetica"), 20, NULL); + HIThemeTextInfo theTextInfo = {kHIThemeTextInfoVersionOne, kThemeStateActive, kThemeSpecifiedFont, + kHIThemeTextHorizontalFlushLeft, kHIThemeTextVerticalFlushTop, + kHIThemeTextBoxOptionNone, kHIThemeTextTruncationNone, 0, false, + 0, myFont + }; + gTextInfo = theTextInfo; + + HIThemeGetTextDimensions(cf_msg, (float)gMovingRect.size.width, &gTextInfo, NULL, &actualTextBoxHeight, NULL); + gTextBoxHeight = actualTextBoxHeight + TEXTBOXTOPBORDER; + } - // Under MacOS 14 and later, if we allow legacyScreenSaver to continue running in the - // background when user activity dismisses the screensaver, it appears to create multiple - // instances of startAnimation, animateOneFrame and (perhaps) drawRect upon each subsequnt - // activation of the screensaver. As a result it tries to draw the same image in the same - // location multiple times. Apparently only one of those instances does actually draw to - // the screen, but we have no way of knowing which instance, so we need to allow them all - // to go through this code to ensure the drawing actually happens. - if (!isErased) { - [[NSColor blackColor] set]; - - // Erasing only 2 small rectangles reduces screensaver's CPU usage by about 25% - imagePosition.x = (float) ((int)gCurrentPosition.x + gImageXIndent); - imagePosition.y = (float) (int)gCurrentPosition.y; - eraseRect.origin.y = imagePosition.y; - eraseRect.size.height = currentDrawingRect.size.height - gTextBoxHeight; - - if (gCurrentDelta.x > 0) { - eraseRect.origin.x = imagePosition.x - 1; - eraseRect.size.width = gCurrentDelta.x + 1; - } else { - eraseRect.origin.x = currentDrawingRect.origin.x + currentDrawingRect.size.width - gImageXIndent + gCurrentDelta.x - 1; - eraseRect.size.width = -gCurrentDelta.x + 1; + if (myTimer == NULL) { + return; } - eraseRect = NSInsetRect(eraseRect, -1, -1); - NSRectFill(eraseRect); + // Under MacOS 14 and later, if we allow legacyScreenSaver to continue running in the + // background when user activity dismisses the screensaver, it appears to create multiple + // instances of startAnimation, animateOneFrame and (perhaps) drawRect upon each subsequnt + // activation of the screensaver. As a result it tries to draw the same image in the same + // location multiple times. Apparently only one of those instances does actually draw to + // the screen, but we have no way of knowing which instance, so we need to allow them all + // to go through this code to ensure the drawing actually happens. + if (!isErased) { + [[NSColor blackColor] set]; + + // Erasing only 2 small rectangles reduces screensaver's CPU usage by about 25% + imagePosition.x = (float) ((int)gCurrentPosition.x + gImageXIndent); + imagePosition.y = (float) (int)gCurrentPosition.y; + eraseRect.origin.y = imagePosition.y; + eraseRect.size.height = currentDrawingRect.size.height - gTextBoxHeight; + + if (gCurrentDelta.x > 0) { + eraseRect.origin.x = imagePosition.x - 1; + eraseRect.size.width = gCurrentDelta.x + 1; + } else { + eraseRect.origin.x = currentDrawingRect.origin.x + currentDrawingRect.size.width - gImageXIndent + gCurrentDelta.x - 1; + eraseRect.size.width = -gCurrentDelta.x + 1; + } + + eraseRect = NSInsetRect(eraseRect, -1, -1); + NSRectFill(eraseRect); - eraseRect.origin.x = imagePosition.x; - eraseRect.size.width = currentDrawingRect.size.width - gImageXIndent - gImageXIndent; + eraseRect.origin.x = imagePosition.x; + eraseRect.size.width = currentDrawingRect.size.width - gImageXIndent - gImageXIndent; - if (gCurrentDelta.y > 0) { - eraseRect.origin.y = imagePosition.y; - eraseRect.size.height = gCurrentDelta.y + 1; - } else { - eraseRect.origin.y = imagePosition.y + currentDrawingRect.size.height - gTextBoxHeight - 1; - eraseRect.size.height = -gCurrentDelta.y + 1; - } - eraseRect = NSInsetRect(eraseRect, -1, -1); - NSRectFill(eraseRect); + if (gCurrentDelta.y > 0) { + eraseRect.origin.y = imagePosition.y; + eraseRect.size.height = gCurrentDelta.y + 1; + } else { + eraseRect.origin.y = imagePosition.y + currentDrawingRect.size.height - gTextBoxHeight - 1; + eraseRect.size.height = -gCurrentDelta.y + 1; + } + eraseRect = NSInsetRect(eraseRect, -1, -1); + NSRectFill(eraseRect); - eraseRect = currentDrawingRect; - eraseRect.size.height = gTextBoxHeight; - eraseRect = NSInsetRect(eraseRect, -1, -1); - NSRectFill(eraseRect); + eraseRect = currentDrawingRect; + eraseRect.size.height = gTextBoxHeight; + eraseRect = NSInsetRect(eraseRect, -1, -1); + NSRectFill(eraseRect); - isErased = true; - } + isErased = true; + } - [ gBOINC_Logo drawAtPoint:imagePosition fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0 ]; -#if 0 - // For testing - gCurrentDelta.x = 0; - gCurrentDelta.y = 0; -#endif - CGRect bounds = CGRectMake((float) ((int)gCurrentPosition.x), - viewBounds.size.height - imagePosition.y + TEXTBOXTOPBORDER, - gMovingRect.size.width, - MAXTEXTBOXHEIGHT - ); - - CGContextSaveGState (myContext); - CGContextTranslateCTM (myContext, 0, viewBounds.origin.y + viewBounds.size.height); - CGContextScaleCTM (myContext, 1.0f, -1.0f); - - CGFloat myWhiteComponents[] = {1.0, 1.0, 1.0, 1.0}; - CGColorSpaceRef myColorSpace = CGColorSpaceCreateDeviceRGB (); - CGColorRef myTextColor = CGColorCreate(myColorSpace, myWhiteComponents); - - CGContextSetFillColorWithColor(myContext, myTextColor); - - HIThemeDrawTextBox(cf_msg, &bounds, &gTextInfo, myContext, kHIThemeOrientationNormal); - - CGColorRelease(myTextColor); - CGColorSpaceRelease(myColorSpace); - CGContextRestoreGState (myContext); - CFRelease(cf_msg); - - isErased = false; - - } else { // Empty or NULL message - if (!isErased) { - eraseRect = NSInsetRect(currentDrawingRect, -1, -1); - [[NSColor blackColor] set]; - isErased = true; - NSRectFill(eraseRect); - gMovingRect.size.height = [gBOINC_Logo size].height + gTextBoxHeight; + [ gBOINC_Logo drawAtPoint:imagePosition fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0 ]; + #if 0 + // For testing + gCurrentDelta.x = 0; + gCurrentDelta.y = 0; + #endif + CGRect bounds = CGRectMake((float) ((int)gCurrentPosition.x), + viewBounds.size.height - imagePosition.y + TEXTBOXTOPBORDER, + gMovingRect.size.width, + MAXTEXTBOXHEIGHT + ); + + CGContextSaveGState (myContext); + CGContextTranslateCTM (myContext, 0, viewBounds.origin.y + viewBounds.size.height); + CGContextScaleCTM (myContext, 1.0f, -1.0f); + + CGFloat myWhiteComponents[] = {1.0, 1.0, 1.0, 1.0}; + CGColorSpaceRef myColorSpace = CGColorSpaceCreateDeviceRGB (); + CGColorRef myTextColor = CGColorCreate(myColorSpace, myWhiteComponents); + + CGContextSetFillColorWithColor(myContext, myTextColor); + + HIThemeDrawTextBox(cf_msg, &bounds, &gTextInfo, myContext, kHIThemeOrientationNormal); + + CGColorRelease(myTextColor); + CGColorSpaceRelease(myColorSpace); + CGContextRestoreGState (myContext); + CFRelease(cf_msg); + + isErased = false; + + } else { // Empty or NULL message + if (!isErased) { + eraseRect = NSInsetRect(currentDrawingRect, -1, -1); + [[NSColor blackColor] set]; + isErased = true; + NSRectFill(eraseRect); + gMovingRect.size.height = [gBOINC_Logo size].height + gTextBoxHeight; + } } } - // Check for a new graphics app sending us data if (UseSharedOffscreenBuffer() && gfxAppStartTime) { if (mySharedGraphicsController) { @@ -901,40 +903,43 @@ - (void)animateOneFrame { // logo, while letting doPeriodicTasks prform the actual drawing. - (void) advancePosition:(NSTimer*)timer { { - // If text has changed and it now goes below bottom of screen, jump up to show it all. - if ((gCurrentPosition.y - gTextBoxHeight) <= SAFETYBORDER) { - gCurrentPosition.y = SAFETYBORDER + 1 + gTextBoxHeight; - if (gCurrentDelta.y < 0) { - gCurrentDelta.y = (float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; - } - } - - // Set direction of motion to "bounce" off edges of screen - if ( (gCurrentDelta.x < 0) && (gCurrentPosition.x <= SAFETYBORDER) ) { - gCurrentDelta.x = (float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; - gCurrentDelta.y = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.y)) / 16.; - } - if ( (gCurrentDelta.x > 0) && ( (gCurrentPosition.x + gMovingRect.size.width) >= - (myViewBounds.origin.x + myViewBounds.size.width - SAFETYBORDER) ) ){ - gCurrentDelta.x = -(float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; - gCurrentDelta.y = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.y)) / 16.; - } - if ( (gCurrentDelta.y < 0) && (gCurrentPosition.y - gTextBoxHeight <= SAFETYBORDER) ) { - gCurrentDelta.y = (float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; - gCurrentDelta.x = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.x)) / 16.; - } - if ( (gCurrentDelta.y > 0) && ( (gCurrentPosition.y + gMovingRect.size.height) >= - (myViewBounds.origin.y + myViewBounds.size.height - SAFETYBORDER) ) ) { - gCurrentDelta.y = -(float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; - gCurrentDelta.x = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.x)) / 16.; - } + if (logoIsCovered) { + return; + } + // If text has changed and it now goes below bottom of screen, jump up to show it all. + if ((gCurrentPosition.y - gTextBoxHeight) <= SAFETYBORDER) { + gCurrentPosition.y = SAFETYBORDER + 1 + gTextBoxHeight; + if (gCurrentDelta.y < 0) { + gCurrentDelta.y = (float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; } + } + + // Set direction of motion to "bounce" off edges of screen + if ( (gCurrentDelta.x < 0) && (gCurrentPosition.x <= SAFETYBORDER) ) { + gCurrentDelta.x = (float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; + gCurrentDelta.y = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.y)) / 16.; + } + if ( (gCurrentDelta.x > 0) && ( (gCurrentPosition.x + gMovingRect.size.width) >= + (myViewBounds.origin.x + myViewBounds.size.width - SAFETYBORDER) ) ){ + gCurrentDelta.x = -(float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; + gCurrentDelta.y = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.y)) / 16.; + } + if ( (gCurrentDelta.y < 0) && (gCurrentPosition.y - gTextBoxHeight <= SAFETYBORDER) ) { + gCurrentDelta.y = (float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; + gCurrentDelta.x = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.x)) / 16.; + } + if ( (gCurrentDelta.y > 0) && ( (gCurrentPosition.y + gMovingRect.size.height) >= + (myViewBounds.origin.y + myViewBounds.size.height - SAFETYBORDER) ) ) { + gCurrentDelta.y = -(float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.; + gCurrentDelta.x = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.x)) / 16.; + } +} - // Get the new drawing area - gCurrentPosition.x += gCurrentDelta.x; - gCurrentPosition.y += gCurrentDelta.y; - imagePosition.x = (float) ((int)gCurrentPosition.x + gImageXIndent); - imagePosition.y = (float) (int)gCurrentPosition.y; + // Get the new drawing area + gCurrentPosition.x += gCurrentDelta.x; + gCurrentPosition.y += gCurrentDelta.y; + imagePosition.x = (float) ((int)gCurrentPosition.x + gImageXIndent); + imagePosition.y = (float) (int)gCurrentPosition.y; } @@ -1255,6 +1260,7 @@ - (void)cleanUpOpenGL }); } openGLView = nil; + logoIsCovered = false; } int i; @@ -1359,6 +1365,7 @@ - (kern_return_t)displayFrame:(int32_t)frameIndex surfacemachport:(mach_port_t)i theframe.size.height = IOSurfaceHeight; openGLView = [[saverOpenGLView alloc] initWithFrame:theframe]; [screenSaverView addSubview:openGLView]; + logoIsCovered = true; } if(!_textureNames[frameIndex]) From dca8e21bc59fff02d02dddb23bf340f450fdb514 Mon Sep 17 00:00:00 2001 From: Charlie Fenton Date: Fri, 25 Jul 2025 01:54:01 -0700 Subject: [PATCH 5/8] Fix Windows build errors --- clientscr/screensaver.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clientscr/screensaver.cpp b/clientscr/screensaver.cpp index f84056c50cb..904d442c8b4 100644 --- a/clientscr/screensaver.cpp +++ b/clientscr/screensaver.cpp @@ -565,9 +565,10 @@ DataMgmtProcType CScreensaver::DataManagementProc() { m_bDefault_gfx_running = false; m_bShow_default_ss_first = false; graphics_app_result_ptr = NULL; - graphicsAppStartTime = 0; #ifdef __APPLE__ + graphicsAppStartTime = 0; + for (int i = 0; i < m_vIncompatibleGfxApps.size(); i++) { if (m_vIncompatibleGfxApps[i]) { free(m_vIncompatibleGfxApps[i]); @@ -640,11 +641,13 @@ BOINCTRACE(_T("CScreensaver::DataManagementProc - Thread told to stop\n")); m_hGraphicsApplication = 0; } +#ifdef __APPLE__ ss_shmem->gfx_pid = 0; ss_shmem->gfx_slot = -1; ss_shmem->major_version = 0; ss_shmem->minor_version = 0; ss_shmem->release = 0; +#endif BOINCTRACE(_T("CScreensaver::DataManagementProc - Stopping...\n")); m_bDataManagementProcStopped = true; // Tell main thread that we exited From 611c0d2313c282a3573ede78186adf458e200812 Mon Sep 17 00:00:00 2001 From: Charlie Fenton Date: Fri, 25 Jul 2025 03:00:14 -0700 Subject: [PATCH 6/8] Fix trailing whitespace, implement Copilot suggestions --- clientscr/Mac_Saver_ModuleView.m | 4 ++-- mac_installer/PostInstall.cpp | 2 +- mac_installer/finish_install.cpp | 8 +++----- mac_installer/release_boinc.sh | 2 +- mac_installer/release_brand.sh | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/clientscr/Mac_Saver_ModuleView.m b/clientscr/Mac_Saver_ModuleView.m index 19007d08d2e..3dbaec19092 100644 --- a/clientscr/Mac_Saver_ModuleView.m +++ b/clientscr/Mac_Saver_ModuleView.m @@ -150,13 +150,13 @@ static SharedGraphicsController *mySharedGraphicsController; static bool runningSharedGraphics; static bool useCGWindowList; -static pid_t childPid; // pid of the currently running grapics app, not actually our chkld +static pid_t childPid; // pid of the currently running graphics app, not actually our chkld static int gfxAppWindowNum; static NSView *imageView; static char gfxAppPath[MAXPATHLEN]; static char gfxWuName[MAXPATHLEN]; static int taskSlot; -static NSRunningApplication *childApp; // currently running grapics app, not actually our chkld +static NSRunningApplication *childApp; // currently running graphics app, not actually our child static double gfxAppStartTime; static bool UseSharedOffscreenBuffer(void); static double lastGetSSMsgTime; diff --git a/mac_installer/PostInstall.cpp b/mac_installer/PostInstall.cpp index 45f493a67c6..baaa4f06c38 100644 --- a/mac_installer/PostInstall.cpp +++ b/mac_installer/PostInstall.cpp @@ -66,7 +66,7 @@ #define USE_OSASCRIPT_FOR_ALL_LOGGED_IN_USERS false -// MPORTANT: The defnition of COPY_FINISH_INSTALL_TO_USER_DIRECTORY +// IMPORTANT: The definition of COPY_FINISH_INSTALL_TO_USER_DIRECTORY // must match the one in Finish_install.cpp #define COPY_FINISH_INSTALL_TO_USER_DIRECTORY false diff --git a/mac_installer/finish_install.cpp b/mac_installer/finish_install.cpp index c2ea60bf1c8..14f4a35671a 100644 --- a/mac_installer/finish_install.cpp +++ b/mac_installer/finish_install.cpp @@ -59,7 +59,7 @@ #define MAX_LANGUAGES_TO_TRY 5 -// MPORTANT: The defnition of COPY_FINISH_INSTALL_TO_USER_DIRECTORY +// IMPORTANT: The definition of COPY_FINISH_INSTALL_TO_USER_DIRECTORY // must match the one in PostInstall.cpp #define COPY_FINISH_INSTALL_TO_USER_DIRECTORY false @@ -403,12 +403,11 @@ void MaybeSetScreenSaver(int brandId){ // // Use -n flag to open a new instance of the System Settings even if one is // already running. We can then use the returned PID to quit only that instance. - sprintf(buf, "open -gj \"x-apple.systempreferences:com.apple.Wallpaper-Settings.extension\""); - callPosixSpawn(buf); + callPosixSpawn("open -gj \"x-apple.systempreferences:com.apple.Wallpaper-Settings.extension\"")); sleep(1); // This delay seems to be needed, probably to give Wallpaper pane time to open - sprintf(buf, "osascript -e 'tell application \"System Events\" to set current screen saver to screen saver \"%s\"'", mySaverName); + snprintf(buf, sizeof(buf), "osascript -e 'tell application \"System Events\" to set current screen saver to screen saver \"%s\"'", mySaverName); callPosixSpawn(buf); // TODO: find a way to kill only the new hidden instance of System Settigs that we opened @@ -527,7 +526,6 @@ static void GetAndLoadPreferredLanguages() { CFRelease(preferredLanguages); preferredLanguages = NULL; - } if (!BOINCTranslationAddCatalog(Catalogs_Dir, "en", Catalog_Name)) { diff --git a/mac_installer/release_boinc.sh b/mac_installer/release_boinc.sh index 61216811f3a..5f3d328918a 100644 --- a/mac_installer/release_boinc.sh +++ b/mac_installer/release_boinc.sh @@ -449,7 +449,7 @@ if [ -e "${HOME}/BOINCCodeSignIdentities.txt" ]; then # Code Sign the PostInstall app embedded in the BOINC installer app if we have a signing identity sudo codesign -f -o runtime -s "${APPSIGNINGIDENTITY}" "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC Installer.app/Contents/Resources/PostInstall.app" - # Code Sign BOINC_Finish_BOINC_Finish_Uninstall app embedded in BOINC uninstaller app if we have a signing identity + # Code Sign BOINC_Finish_Uninstall app embedded in BOINC uninstaller app if we have a signing identity sudo codesign -f -o runtime -s "${APPSIGNINGIDENTITY}" "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/extras/Uninstall BOINC.app/Contents/Resources/BOINC_Finish_Uninstall.app" # Code Sign the BOINC uninstaller app if we have a signing identity diff --git a/mac_installer/release_brand.sh b/mac_installer/release_brand.sh index 31b8c4cc2b5..c0e2f9189df 100755 --- a/mac_installer/release_brand.sh +++ b/mac_installer/release_brand.sh @@ -438,7 +438,7 @@ sudo plutil -replace CFBundleExecutable -string "${LONGBRANDNAME}_Finish_Uninsta # Replace icon reference in Info.plist file of ${LONGBRANDNAME}_Finish_Uninstall.app sudo plutil -replace CFBundleIconFile -string "${UNINSTALLERICON}.icns" "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/extras/${UNINSTALLERAPPNAME}.app/Contents/Resources/${LONGBRANDNAME}_Finish_Uninstall.app/Contents/Info.plist" -# Replace icon in in ${LONGBRANDNAME}_Finish_Uninstall.app +# Replace icon in ${LONGBRANDNAME}_Finish_Uninstall.app sudo rm -dfR "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/extras/${UNINSTALLERAPPNAME}.app/Contents/Resources/${LONGBRANDNAME}_Finish_Uninstall.app/Contents/Resources/MacInstaller.icns" sudo cp -fpRL "./clientgui/res/${UNINSTALLERICON}.icns" "../BOINC_Installer/New_Release_${SHORTBRANDNAME}_$1_$2_$3/${SHORTBRANDNAME}_$1.$2.$3_macOSX_$arch/extras/${UNINSTALLERAPPNAME}.app/Contents/Resources/${LONGBRANDNAME}_Finish_Uninstall.app/Contents/Resources/" From bd8d1df040688c0a8b5b2f16490d43eda9375bbe Mon Sep 17 00:00:00 2001 From: Charlie Fenton Date: Fri, 25 Jul 2025 08:00:07 -0700 Subject: [PATCH 7/8] Fix compile error caused by typo --- mac_installer/finish_install.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mac_installer/finish_install.cpp b/mac_installer/finish_install.cpp index 14f4a35671a..695d8f4d6a1 100644 --- a/mac_installer/finish_install.cpp +++ b/mac_installer/finish_install.cpp @@ -403,7 +403,7 @@ void MaybeSetScreenSaver(int brandId){ // // Use -n flag to open a new instance of the System Settings even if one is // already running. We can then use the returned PID to quit only that instance. - callPosixSpawn("open -gj \"x-apple.systempreferences:com.apple.Wallpaper-Settings.extension\"")); + callPosixSpawn("open -gj \"x-apple.systempreferences:com.apple.Wallpaper-Settings.extension\""); sleep(1); // This delay seems to be needed, probably to give Wallpaper pane time to open From 9cc182360353cc9f7c9a7c490ca5f14be0cc5245 Mon Sep 17 00:00:00 2001 From: Charlie Fenton Date: Sat, 26 Jul 2025 03:15:15 -0700 Subject: [PATCH 8/8] Implement changes suggested by Copilot --- clientscr/mac_saver_module.cpp | 2 +- mac_installer/finish_install.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/clientscr/mac_saver_module.cpp b/clientscr/mac_saver_module.cpp index bbf665f51d3..5b22e832239 100644 --- a/clientscr/mac_saver_module.cpp +++ b/clientscr/mac_saver_module.cpp @@ -152,7 +152,7 @@ void initBOINCSaver() { temp.sa_handler = SIG_IGN; sigaction(SIGCHLD, &temp, NULL); -for (int i=1; i<32; ++i) +for (int i=1; i