// This file is part of BOINC. // http://boinc.berkeley.edu // Copyright (C) 2019 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 // as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // BOINC is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with BOINC. If not, see . // // gfx_cleanup.mm // // Used by screensaver to work around a bug in OS 10.15 Catalina // - Detects when ScreensaverEngine exits without calling [ScreenSaverView stopAnimation] // - If that happens, it sends RPC to BOINC client to kill current graphics app. // Note: this can rarely happen in earlier versions, but is of main concern under // OS 10.13 and later, where it can cause an ugly white full-screen display // // Called by CScreensaver via popen(path, "w") // #import #include #include #include "gui_rpc_client.h" #include "util.h" #include "mac_util.h" #define CREATE_LOG 0 #define USE_TIMER 0 #if CREATE_LOG void print_to_log_file(const char *format, ...); #endif pid_t parentPid; int GFX_PidFromScreensaver = 0; pthread_t MonitorParentThread = 0; bool quit_MonitorParentThread = false; #if USE_TIMER time_t startTime = 0; time_t endTime = 0; time_t elapsedTime = 0; #endif void killGfxApp(pid_t thePID) { char passwd_buf[256]; RPC_CLIENT *rpc; int retval; chdir("/Library/Application Support/BOINC Data"); safe_strcpy(passwd_buf, ""); read_gui_rpc_password(passwd_buf); rpc = new RPC_CLIENT; if (rpc->init(NULL)) { // Initialize communications with Core Client fprintf(stderr, "in gfx_cleanup: killGfxApp(): rpc->init(NULL) failed"); return; } if (strlen(passwd_buf)) { retval = rpc->authorize(passwd_buf); if (retval) { fprintf(stderr, "in gfx_cleanup: killGfxApp(): authorization failure: %d\n", retval); rpc->close(); return; } } retval = rpc->run_graphics_app(0, thePID, "stop"); // fprintf(stderr, "in gfx_cleanup: killGfxApp(): rpc->run_graphics_app() returned retval=%d", retval); rpc->close(); } void * MonitorParent(void* param) { // fprintf(stderr, "in gfx_cleanup: Starting MonitorParent"); while (true) { boinc_sleep(0.25); // Test every 1/4 second if (getppid() != parentPid) { #if USE_TIMER endTime = time(NULL); #endif if (GFX_PidFromScreensaver) { killGfxApp(GFX_PidFromScreensaver); } if (quit_MonitorParentThread) { return 0; } // fprintf(stderr, "in gfx_cleanup: parent died, exiting (child) after handling %d, elapsed time=%d",GFX_PidFromScreensaver, (int) elapsedTime); exit(0); } } } NSWindow* myWindow; @interface AppDelegate : NSObject { } @end @implementation AppDelegate - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { int retval = 0; char buf[256]; retval = pthread_create(&MonitorParentThread, NULL, MonitorParent, 0); while (true) { fgets(buf, sizeof(buf), stdin); // fprintf(stderr, "in gfx_cleanup: parent sent %d to child buf=%s", GFX_PidFromScreensaver, buf); if (feof(stdin)) { // fprintf(stderr, "in gfx_cleanup: got eof"); break; } if (ferror(stdin) && (errno != EINTR)) { fprintf(stderr, "in gfx_cleanup: fgets got error %d %s", errno, strerror(errno)); break; } if (!strcmp(buf, "Quit\n")) { break; } GFX_PidFromScreensaver = atoi(buf); // fprintf(stderr, "in gfx_cleanup: parent sent %d to child buf=%s", GFX_PidFromScreensaver, buf); } if (GFX_PidFromScreensaver) { killGfxApp(GFX_PidFromScreensaver); } quit_MonitorParentThread = true; // [NSApp stop:self]; exit(0); } @end int main(int argc, char* argv[]) { // fprintf(stderr, "Entered gfx_cleanup"); #if USE_TIMER startTime = time(NULL); #endif parentPid = getppid(); bool cover_gfx_window = (compareOSVersionTo(10, 13) >= 0); // Create shared app instance [NSApplication sharedApplication]; // Because prpject graphics applications under OS 10.13+ draw to an IOSurface, // the application's own window is white, but is normally covered by the // ScreensaverEngine's window. If the ScreensaverEngine exits without first // calling [ScreenSaverView stopAnimation], the white fullscreen window will // briefly be visible until we kill the graphics app, causing an ugly and // annoying white flash. So we hide that with our own black fullscreen window // to prevent the white flash. if (cover_gfx_window) { NSArray *allScreens = [NSScreen screens]; NSRect windowRect = [ allScreens[0] frame ]; NSUInteger windowStyle = NSWindowStyleMaskBorderless; NSWindow *myWindow = [[NSWindow alloc] initWithContentRect:windowRect styleMask:windowStyle backing:NSBackingStoreBuffered defer:NO]; [myWindow setBackgroundColor:[NSColor blackColor]]; [ myWindow setLevel:kCGOverlayWindowLevel ]; // slightly above the graphics app's window [myWindow orderFrontRegardless]; } AppDelegate *myDelegate = [[AppDelegate alloc] init]; [ NSApp setDelegate:myDelegate]; [NSApp run]; #if USE_TIMER endTime = time(NULL); elapsedTime = endTime - startTime; #endif // fprintf(stderr, "exiting gfx_cleanup after handling %d, elapsed time=%d",GFX_PidFromScreensaver, (int)elapsedTime); return 0; } // print_to_log_file.c #if CREATE_LOG #include #include #include #include #include #ifdef unix //#include #endif void strip_cr(char *buf); #endif // CREATE_LOG #if CREATE_LOG // print_to_log_file - use for debugging. // prints time stamp plus a formatted string to log file. // calling syntax: same as printf. void print_to_log_file(const char *format, ...) { FILE *f; va_list args; char buf[256]; time_t t; #if 0 strcpy(buf, "/Library/Application Support/test_log.txt"); #else strcpy(buf, getenv("HOME")); strcat(buf, "/Library/Application Support/BOINC/test_log_gfx_cleanup.txt"); #endif f = fopen(buf, "a"); // f = fopen("/Library/Games_Demo/test_log.txt", "a"); if (!f) return; time(&t); strcpy(buf, asctime(localtime(&t))); strip_cr(buf); fputs(buf, f); fputs(" ", f); va_start(args, format); vfprintf(f, format, args); va_end(args); fputs("\n", f); fclose(f); } void strip_cr(char *buf) { char *theCR; theCR = strrchr(buf, '\n'); if (theCR) *theCR = '\0'; theCR = strrchr(buf, '\r'); if (theCR) *theCR = '\0'; } #else void print_to_log_file(const char *, ...) {} #endif // CREATE_LOG #ifdef __cplusplus extern "C" { #endif void PrintBacktrace(void) { // Dummy routine to satisfy linker } #ifdef __cplusplus } #endif