Skip to content

Commit

Permalink
Merge pull request #5411 from BOINC/mac_Os14_SS_hotfix
Browse files Browse the repository at this point in the history
Mac screensaver: update for MacOS 14.0 Sonoma
  • Loading branch information
AenBleidd authored and Charlie Fenton committed Oct 29, 2023
1 parent 292704f commit ae077ff
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 104 deletions.
6 changes: 5 additions & 1 deletion clientscr/Mac_Saver_Module.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2020 University of California
// Copyright (C) 2023 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
Expand Down Expand Up @@ -40,6 +40,7 @@ int startBOINCSaver(void);
int getSSMessage(char **theMessage, int* coveredFreq);
void windowIsCovered();
void drawPreview(CGContextRef myContext);
void stopAllGFXApps(void);
void closeBOINCSaver(void);
void setDefaultDisplayPeriods(void);
bool getShow_default_ss_first();
Expand All @@ -61,6 +62,8 @@ extern char gUserName[64];
extern bool gIsMojave;
extern bool gIsCatalina;
extern bool gIsHighSierra;
extern bool gIsSonoma;
extern bool gCant_Use_Shared_Offscreen_Buffer;

#ifdef __cplusplus
} // extern "C"
Expand Down Expand Up @@ -164,6 +167,7 @@ class CScreensaver
int getSSMessage(char **theMessage, int* coveredFreq);
void windowIsCovered(void);
void drawPreview(CGContextRef myContext);
void Shared_Offscreen_Buffer_Unavailable(void);
void ShutdownSaver();
void markAsIncompatible(char *gfxAppName);
bool isIncompatible(char *appName);
Expand Down
5 changes: 4 additions & 1 deletion clientscr/Mac_Saver_ModuleView.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2020 University of California
// Copyright (C) 2023 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
Expand Down Expand Up @@ -77,6 +77,7 @@ int startBOINCSaver(void);
int getSSMessage(char **theMessage, int* coveredFreq);
void windowIsCovered();
void drawPreview(CGContextRef myContext);
void stopAllGFXApps(void);
void closeBOINCSaver(void);
void setDefaultDisplayPeriods(void);
bool getShow_default_ss_first();
Expand All @@ -98,6 +99,8 @@ void PrintBacktrace(void);
extern bool gIsCatalina;
extern bool gIsHighSierra;
extern bool gIsMojave;
extern bool gIsSonoma;
extern bool gCant_Use_Shared_Offscreen_Buffer;

#ifdef __cplusplus
} // extern "C"
Expand Down
185 changes: 110 additions & 75 deletions clientscr/Mac_Saver_ModuleView.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2022 University of California
// Copyright (C) 2023 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
Expand Down Expand Up @@ -136,7 +136,6 @@
NSRect gMovingRect;
float gImageXIndent;
float gTextBoxHeight;
CGFloat gActualTextBoxHeight;
NSPoint gCurrentPosition;
NSPoint gCurrentDelta;

Expand Down Expand Up @@ -196,6 +195,10 @@ void launchedGfxApp(char * appPath, pid_t thePID, int slot) {
imageView = nil;
}
}
} else {
if (gCant_Use_Shared_Offscreen_Buffer) {
stopAllGFXApps();
}
}
}

Expand All @@ -214,6 +217,7 @@ - (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview {
gIsMojave = (compareOSVersionTo(10, 14) >= 0);
gIsCatalina = (compareOSVersionTo(10, 15) >= 0);
gIsBigSur = (compareOSVersionTo(11, 0) >= 0);
gIsSonoma = (compareOSVersionTo(14, 0) >= 0);

if (gIsCatalina) {
// Under OS 10.15, isPreview is often true even when it shouldn't be
Expand Down Expand Up @@ -354,8 +358,6 @@ - (void)startAnimation {
gCurrentDelta.x = 1.0;
gCurrentDelta.y = 1.0;

gActualTextBoxHeight = MINTEXTBOXHEIGHT;

[ self setAnimationTimeInterval:1/8.0 ];
}

Expand Down Expand Up @@ -447,12 +449,32 @@ - (void)doPeriodicTasks {
NSUInteger n;
double maxWaitTime;
NSRect currentDrawingRect, eraseRect;
CGFloat actualTextBoxHeight = MINTEXTBOXHEIGHT;
NSPoint imagePosition;
char *msg;
CFStringRef cf_msg;
double timeToBlock, frameStartTime = getDTime();
HIThemeTextInfo textInfo;

NSWindow *myWindow = [ self window ];
NSRect windowFrame = [ myWindow frame ];

if (gIsSonoma) {
// 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.
if ((windowFrame.size.width > 500.) && (windowFrame.size.height > 500.)) {
if ([ [ self window ] level ] == 0) {
//TODO: more cleanup as in stopAnimation ??
closeBOINCSaver();
return;
}
}
}

if (myIsPreview) {
#if 1 // Currently drawRect just draws our logo in the preview window
if (gPreview_Image == NULL) {
Expand All @@ -474,8 +496,6 @@ - (void)doPeriodicTasks {
return;
}

NSWindow *myWindow = [ self window ];

#if ! DEBUG_UNDER_XCODE
// For unkown reasons, OS 10.7 Lion screensaver and later delay several seconds
// after user activity before calling stopAnimation, so we check user activity here
Expand All @@ -489,7 +509,6 @@ - (void)doPeriodicTasks {
}
}

NSRect windowFrame = [ myWindow frame ];
if ( (windowFrame.origin.x != 0) || (windowFrame.origin.y != 0) ) {
// Hide window on second display to aid in debugging
#ifdef _DEBUG
Expand Down Expand Up @@ -688,30 +707,18 @@ - (void)doPeriodicTasks {

if ( (msg != NULL) && (msg[0] != '\0') ) {

// Set direction of motion to "bounce" off edges of screen
if (currentDrawingRect.origin.x <= SAFETYBORDER) {
gCurrentDelta.x = (float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.;
gCurrentDelta.y = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.y)) / 16.;
}
if ( (currentDrawingRect.origin.x + currentDrawingRect.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 (currentDrawingRect.origin.y + gTextBoxHeight - gActualTextBoxHeight <= SAFETYBORDER) {
gCurrentDelta.y = (float)SSRandomIntBetween(MINDELTA, MAXDELTA) / 16.;
gCurrentDelta.x = (float)(SSRandomIntBetween(MINDELTA, MAXDELTA) * signof(gCurrentDelta.x)) / 16.;
}
if ( (currentDrawingRect.origin.y + currentDrawingRect.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.;
}
#if 0
// For testing
gCurrentDelta.x = 0;
gCurrentDelta.y = 0;
#endif
cf_msg = CFStringCreateWithCString(NULL, msg, kCFStringEncodingMacRoman);

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 (!isErased) {
[[NSColor blackColor] set];
Expand Down Expand Up @@ -751,7 +758,34 @@ - (void)doPeriodicTasks {
eraseRect = NSInsetRect(eraseRect, -1, -1);
NSRectFill(eraseRect);

isErased = true;
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.;
}
}

// Get the new drawing area
Expand All @@ -762,48 +796,33 @@ - (void)doPeriodicTasks {
imagePosition.y = (float) (int)gCurrentPosition.y;

[ 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
);

if ( (msg != NULL) && (msg[0] != '\0') ) {
cf_msg = CFStringCreateWithCString(NULL, msg, kCFStringEncodingMacRoman);

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);

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, &gActualTextBoxHeight, NULL);
gActualTextBoxHeight += TEXTBOXTOPBORDER;

CGFloat myWhiteComponents[] = {1.0, 1.0, 1.0, 1.0};
CGColorSpaceRef myColorSpace = CGColorSpaceCreateDeviceRGB ();
CGColorRef myTextColor = CGColorCreate(myColorSpace, myWhiteComponents);
CGContextSaveGState (myContext);
CGContextTranslateCTM (myContext, 0, viewBounds.origin.y + viewBounds.size.height);
CGContextScaleCTM (myContext, 1.0f, -1.0f);

CGContextSetFillColorWithColor(myContext, myTextColor);
CGFloat myWhiteComponents[] = {1.0, 1.0, 1.0, 1.0};
CGColorSpaceRef myColorSpace = CGColorSpaceCreateDeviceRGB ();
CGColorRef myTextColor = CGColorCreate(myColorSpace, myWhiteComponents);

HIThemeDrawTextBox(cf_msg, &bounds, &textInfo, myContext, kHIThemeOrientationNormal);
CGContextSetFillColorWithColor(myContext, myTextColor);

CGColorRelease(myTextColor);
CGColorSpaceRelease(myColorSpace);
CGContextRestoreGState (myContext);
CFRelease(cf_msg);
}
HIThemeDrawTextBox(cf_msg, &bounds, &textInfo, myContext, kHIThemeOrientationNormal);

gTextBoxHeight = MAXTEXTBOXHEIGHT + TEXTBOXTOPBORDER;
gMovingRect.size.height = [gBOINC_Logo size].height + gTextBoxHeight;
CGColorRelease(myTextColor);
CGColorSpaceRelease(myColorSpace);
CGContextRestoreGState (myContext);
CFRelease(cf_msg);

isErased = false;

Expand All @@ -813,7 +832,6 @@ - (void)doPeriodicTasks {
[[NSColor blackColor] set];
isErased = true;
NSRectFill(eraseRect);
gTextBoxHeight = MAXTEXTBOXHEIGHT;
gMovingRect.size.height = [gBOINC_Logo size].height + gTextBoxHeight;
}
}
Expand All @@ -831,7 +849,10 @@ - (void)doPeriodicTasks {
// Check for a new graphics app sending us data
if (UseSharedOffscreenBuffer() && gfxAppStartTime) {
if (mySharedGraphicsController) {
[mySharedGraphicsController testConnection];
if (!runningSharedGraphics) {
// wait for a connection from a gfx app
[mySharedGraphicsController testConnection];
}
}
}
}
Expand Down Expand Up @@ -1089,10 +1110,17 @@ - (void) testConnection
if (machErr == KERN_SUCCESS) {
serverPort = (NSMachPort*)[NSMachPort portWithMachPort:servicePortNum];
} else {
if (machErr == BOOTSTRAP_NOT_PRIVILEGED) {
// As of MacOS 14.0, the legacyScreenSave sandbox prevents using
// IOSurfaceLookupFromMachPort. I have filed bug report FB13300491
// with Apple and hope they will change this in future MacOS.
gCant_Use_Shared_Offscreen_Buffer = true;
stopAllGFXApps();
}
serverPort = MACH_PORT_NULL;
}

if(serverPort != MACH_PORT_NULL)
if ((serverPort != MACH_PORT_NULL) && (localPort == MACH_PORT_NULL))
{
// Create our own local port.
localPort = [[NSMachPort alloc] init];
Expand Down Expand Up @@ -1132,14 +1160,14 @@ - (void)portDied:(NSNotification *)notification
[serverPort invalidate];
// [serverPort release];
}
serverPort = nil;
serverPort = MACH_PORT_NULL;
[localPort removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

if ([localPort isValid]) {
[localPort invalidate];
}
// [localPort release];
localPort = nil;
localPort = MACH_PORT_NULL;

int i;
for(i = 0; i < NUM_IOSURFACE_BUFFERS; i++) {
Expand All @@ -1159,13 +1187,14 @@ - (void)portDied:(NSNotification *)notification
}
}

if ((serverPort == nil) && (localPort == nil)) {
if ((serverPort == MACH_PORT_NULL) && (localPort == MACH_PORT_NULL)) {
runningSharedGraphics = false;
[openGLView removeFromSuperview]; // Releases openGLView
openGLView = nil;
}
}
}

- (void)handleMachMessage:(void *)msg
{
union __ReplyUnion___MGCMGSServer_subsystem reply;
Expand Down Expand Up @@ -1402,6 +1431,12 @@ static bool UseSharedOffscreenBuffer() {
static bool needSharedGfxBuffer = false;

//return true; // FOR TESTING ONLY
// As of MacOS 14.0, the legacyScreenSaver sandbox prevents using
// IOSurfaceLookupFromMachPort. I have filed bug report FB13300491
// with Apple and hope they will change this in future MacOS.
if (gCant_Use_Shared_Offscreen_Buffer) {
return false;
}
if (alreadyTested) {
return needSharedGfxBuffer;
}
Expand Down
Loading

0 comments on commit ae077ff

Please sign in to comment.