Gitlab@AEI Hannover will go down for maintenance on 2020-09-21 at 4:00 UTC. The service will be unavailable for up to 24 hours. If you have questions about this please contact atlas_admin@aei.mpg.de

Unverified Commit 7d99c879 authored by David Anderson's avatar David Anderson Committed by GitHub

Merge pull request #3369 from BOINC/mac_fixes_for_OS10_5_Catalina

mac: Fixes for screensaver under OS 10.15 Catalina
parents 48bfac3d a918b54b
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2017 University of California
// 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
......@@ -46,8 +46,10 @@ static int win=0;
static int checkparentcounter=0;
#ifdef __APPLE__
static bool need_show = false;
// Set to true to draw both directly to window and also to offscreen buffer
bool debugSharedOffscreenBuffer = false;
#endif
bool fullscreen;
......@@ -119,7 +121,7 @@ static void maybe_render() {
if (throttled_app_render(new_width, new_height, dtime())) {
#ifdef __APPLE__
if (UseSharedOffscreenBuffer()) {
if (UseSharedOffscreenBuffer() && !debugSharedOffscreenBuffer) {
return; // Don't waste cycles drawing to hidden window on screen
}
#endif
......
// Berkeley Open Infrastructure for Network Computing
// http://boinc.berkeley.edu
// Copyright (C) 2017 University of California
// Copyright (C) 2019 University of California
//
// This is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
......@@ -285,7 +285,6 @@ kern_return_t _MGSDisplayFrame(mach_port_t server_port, int32_t frame_index, uin
{
if(clientPortNames[i])
{
// print_to_log_file("BOINCSCR: about to call _MGCDisplayFrame with iosurface_port %d, IOSurfaceGetID %d and frameIndex %d", (int)iosurface_port, IOSurfaceGetID(ioSurfaceBuffers[index]), (int)index);
_MGCDisplayFrame(clientPortNames[i], index, iosurface_port);
}
}
......@@ -293,24 +292,31 @@ kern_return_t _MGSDisplayFrame(mach_port_t server_port, int32_t frame_index, uin
@end
// OpenGL apps built under Xcode 11 apparently use window dimensions based
// on the number of backing store pixels. That is, they double the window
// dimensiona for Retina displays (which have two pixels per point.) But
// OpenGL apps built under earlier versions of Xcode don't.
// Catalina assumes OpenGL apps work as built under Xcode 11, so it displays
// older builds at half width and height, unless we compensate in our code.
// This code is part of my attempt to ensure that BOINC graphics apps built on
// all versions of Xcode work proprly on different versions of OS X. See also
// [BOINC_Saver_ModuleView initWithFrame:] in clientscr/Mac_Saver_ModuleCiew.m
//
void MacPassOffscreenBufferToScreenSaver() {
NSOpenGLContext * myContext = [ NSOpenGLContext currentContext ];
NSView *myView = [ myContext view ];
GLsizei w = myView.bounds.size.width;
GLsizei h = myView.bounds.size.height;
int viewportRect[4];
GLsizei w, h;
GLuint name, namef;
glGetIntegerv(GL_VIEWPORT, (GLint*)viewportRect);
w = viewportRect[2];
h = viewportRect[3];
if (!myserverController) {
myserverController = [[[ServerController alloc] init] retain];
}
if (!ioSurfaceBuffers[0]) {
NSOpenGLContext * myContext = [ NSOpenGLContext currentContext ];
NSView *myView = [ myContext view ];
GLsizei w = myView.bounds.size.width;
GLsizei h = myView.bounds.size.height;
// Set up all of our iosurface buffers
for(int i = 0; i < NUM_IOSURFACE_BUFFERS; i++) {
ioSurfaceBuffers[i] = IOSurfaceCreate((CFDictionaryRef)@{
......
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2017 University of California
// 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
......@@ -30,6 +30,7 @@ extern void MacPassOffscreenBufferToScreenSaver(void);
extern void BringAppToFront(void);
extern void HideThisApp(void);
extern bool UseSharedOffscreenBuffer(void);
extern bool debugSharedOffscreenBuffer;
extern void print_to_log_file(const char *format, ...);
......
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
// 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
......@@ -173,6 +173,8 @@ struct ACTIVE_TASK {
double finish_file_time;
// time when we saw finish file in slot dir.
// Used to kill apps that hang after writing finished file
int graphics_pid;
// PID of running graphics app (Mac)
void set_task_state(int, const char*);
inline int task_state() {
......@@ -302,6 +304,7 @@ public:
active_tasks_v active_tasks;
ACTIVE_TASK* lookup_pid(int);
ACTIVE_TASK* lookup_result(RESULT*);
ACTIVE_TASK* lookup_slot(int);
void init();
bool poll();
void suspend_all(int reason);
......
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
// 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
......@@ -93,7 +93,10 @@ Commands:\n\
--read_cc_config\n\
--read_global_prefs_override\n\
--run_benchmarks\n\
--set_gpu_mode mode duration set GPU run mode for given duration\n\
--run_graphics_app id op run, test or stop graphics app\n\
op = run | runfullscreen | stop | test\n\
id = slot # for run or runfullscreen, process ID for stop or test\n\
--set_gpu_mode mode duration set GPU run mode for given duration\n\
mode = always | auto | never\n\
--set_host_info product_name\n\
--set_network_mode mode duration set network mode for given duration\n\
......@@ -542,6 +545,18 @@ int main(int argc, char** argv) {
retval = rpc.acct_mgr_rpc("", "", "");
} else if (!strcmp(cmd, "--run_benchmarks")) {
retval = rpc.run_benchmarks();
} else if (!strcmp(cmd, "--run_graphics_app")) {
int slot = 0;
if (!strcmp(argv[3], "test") || (!strcmp(argv[3], "stop"))) {
i = atoi(argv[2]);
} else {
slot = atoi(argv[2]);
i = 0;
}
retval = rpc.run_graphics_app(slot, i, argv[3]);
if (strcmp(argv[3], "stop") & !retval) {
printf("pid: %d\n", i);
}
} else if (!strcmp(cmd, "--get_project_config")) {
char* gpc_url = next_arg(argc, argv,i);
retval = rpc.get_project_config(string(gpc_url));
......
......@@ -304,11 +304,8 @@ int CLIENT_STATE::latest_version(APP* app, char* platform) {
// Find the ACTIVE_TASK in the current set with the matching PID
//
ACTIVE_TASK* ACTIVE_TASK_SET::lookup_pid(int pid) {
unsigned int i;
ACTIVE_TASK* atp;
for (i=0; i<active_tasks.size(); i++) {
atp = active_tasks[i];
for (unsigned int i=0; i<active_tasks.size(); i++) {
ACTIVE_TASK *atp = active_tasks[i];
if (atp->pid == pid) return atp;
}
return NULL;
......@@ -317,11 +314,8 @@ ACTIVE_TASK* ACTIVE_TASK_SET::lookup_pid(int pid) {
// Find the ACTIVE_TASK in the current set with the matching result
//
ACTIVE_TASK* ACTIVE_TASK_SET::lookup_result(RESULT* result) {
unsigned int i;
ACTIVE_TASK* atp;
for (i=0; i<active_tasks.size(); i++) {
atp = active_tasks[i];
for (unsigned int i=0; i<active_tasks.size(); i++) {
ACTIVE_TASK *atp = active_tasks[i];
if (atp->result == result) {
return atp;
}
......@@ -329,6 +323,16 @@ ACTIVE_TASK* ACTIVE_TASK_SET::lookup_result(RESULT* result) {
return NULL;
}
ACTIVE_TASK* ACTIVE_TASK_SET::lookup_slot(int slot) {
for (unsigned int i=0; i<active_tasks.size(); i++) {
ACTIVE_TASK *atp = active_tasks[i];
if (atp->slot == slot) {
return atp;
}
}
return NULL;
}
#ifndef SIM
// on startup, see if any active tasks have a finished file
// i.e. they finished as the client was shutting down
......
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2018 University of California
// 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
......@@ -22,6 +22,7 @@
#ifdef __APPLE__
#include <Carbon/Carbon.h>
#include <libproc.h>
#include "sandbox.h"
#endif
#ifdef _WIN32
......@@ -1332,6 +1333,201 @@ static void handle_get_daily_xfer_history(GUI_RPC_CONN& grc) {
daily_xfer_history.write_xml(grc.mfout);
}
// start, stop or get status of a graphics app on behalf of the screensaver.
// (needed for Mac OS X 10.5+)
//
// <slot>n</slot> { <run/> | <runfullscreen/> }
// <graphics_pid>p</graphics_pid> { <stop/> | <test/> }
//
// n is the slot number:
// if slot = -1, start the default screensaver
// p is the process id to stop or test
// test returns 0 for the pid if it has exited, else returns the child's pid
//
// As of 3 November 2019, only the "stop" verb is being used, because the client
// can't launch or test gfx apps outside the user session within which the client
// is running. So if another user is logged in and runs the screensaver, that
// sure would not see the graphics. But I'm leaving code for all the verbs for
// now as a possible starting point for future development.
//
// The stop verb still works because it calls kill_via_switcher(), which calls
// kill(pid, SIGKILL) after setting user and group to pbionc_project. That does
// work across different login sessions.
//
static void handle_run_graphics_app(GUI_RPC_CONN& grc) {
#ifndef __APPLE__
grc.mfout.printf("<error>run_graphics_app RPC is currently available only on Mac OS</error>\n");
#else
static int boincscr_pid = 0;
bool run = false;
bool runfullscreen = false;
bool stop = false;
bool test = false;
int slot = -2, retval;
int status;
pid_t p;
char* argv[5];
int argc;
int thePID = 0;
while (!grc.xp.get_tag()) {
if (grc.xp.match_tag("/run_graphics_app")) break;
if (grc.xp.parse_int("slot", slot)) continue;
if (grc.xp.parse_bool("run", run)) continue;
if (grc.xp.parse_bool("runfullscreen", runfullscreen)) continue;
if (grc.xp.parse_bool("stop", stop)) continue;
if (grc.xp.parse_bool("test", test)) continue;
if (grc.xp.parse_int("graphics_pid", thePID)) continue;
}
if (stop || test) {
if (thePID < 1) {
grc.mfout.printf("<error>missing or invalid process id</error>\n");
return;
}
} else if (run || runfullscreen) {
if (slot < -1) {
grc.mfout.printf("<error>missing or invalid slot</error>\n");
return;
}
} else {
grc.mfout.printf("<error>missing or invalid operation</error>\n");
return;
}
if (test) {
// returns 0 for the pid if it has exited, else returns the child's pid
p = waitpid(thePID, &status, WNOHANG);
if (p != 0) thePID = 0;
grc.mfout.printf(
"<graphics_pid>%d</graphics_pid>\n",
thePID
);
return;
}
if (stop) {
if (g_use_sandbox && (thePID != boincscr_pid )) {
retval = kill_via_switcher(thePID);
} else {
retval = kill_program(thePID);
}
if (retval) {
grc.mfout.printf("<error>attempt to kill graphics app failed</error>\n");
return;
}
if (thePID == boincscr_pid) boincscr_pid = 0;
grc.mfout.printf("<success/>\n");
return;
}
// start boincscr
//
if (slot == -1) {
char path[MAXPATHLEN];
#ifdef __APPLE__
safe_strcpy(path, "./boincscr");
#else
if (get_real_executable_path(path, sizeof(path))) {
grc.mfout.printf("<error>can't get client path</error>\n");
return;
}
char *p = strrchr(path, '/');
if (!p) {
grc.mfout.printf("<error>no / in client path</error>\n");
return;
}
safe_strcpy(p, "/boincscr");
#endif
argv[0] = (char*)"boincscr";
if (runfullscreen) {
argv[1] = (char*)"--fullscreen";
argc = 2;
} else {
argv[1] = 0;
argc = 1;
}
argv[2] = 0;
retval = run_program(NULL, path, argc, argv, 0, boincscr_pid);
if (retval) {
grc.mfout.printf("<error>couldn't run boincscr</error>\n");
return;
}
grc.mfout.printf(
"<graphics_pid>%d</graphics_pid>\n",
boincscr_pid
);
return;
} // end if (slot == -1)
// start a graphics app
//
ACTIVE_TASK* atp = gstate.active_tasks.lookup_slot(slot);
if (!atp) {
grc.mfout.printf("<error>no job in slot</error>\n");
return;
}
if (atp->scheduler_state != CPU_SCHED_SCHEDULED) {
grc.mfout.printf("<error>job not running</error>\n");
return;
}
if (!strlen(atp->app_version->graphics_exec_path)) {
grc.mfout.printf("<error>job has no graphics app</error>\n");
return;
}
if (g_use_sandbox) {
char current_dir[MAXPATHLEN], switcher_path[MAXPATHLEN];
getcwd( current_dir, sizeof(current_dir));
snprintf(switcher_path, sizeof(switcher_path),
"%s/%s/%s",
current_dir, SWITCHER_DIR, SWITCHER_FILE_NAME
);
argv[0] = const_cast<char*>(SWITCHER_FILE_NAME);
argv[1] = atp->app_version->graphics_exec_path;
argv[2] = atp->app_version->graphics_exec_file;
if (runfullscreen) {
argv[3] = (char*)"--fullscreen";
argc = 3;
} else {
argv[3] = 0;
argc = 2;
}
argv[4] = 0;
retval = run_program(
atp->slot_path, switcher_path,
argc, argv, 0, atp->graphics_pid
);
} else { // not g_use_sandbox
argv[0] = atp->app_version->graphics_exec_file;
if (runfullscreen) {
argv[1] = (char*)"--fullscreen";
argc = 2;
} else {
argv[2] = 0;
argc = 1;
}
argv[2] = 0;
retval = run_program(
atp->slot_path, atp->app_version->graphics_exec_path,
argc, argv, 0, atp->graphics_pid
);
}
if (retval) {
grc.mfout.printf("<error>couldn't run graphics app</error>\n");
return;
}
grc.mfout.printf(
"<graphics_pid>%d</graphics_pid>\n",
atp->graphics_pid
);
#endif // __APPLE__
}
// We use a different authentication scheme for HTTP because
// each request has its own connection.
// Send clients an "authentication ID".
......@@ -1599,6 +1795,7 @@ GUI_RPC gui_rpcs[] = {
GUI_RPC("project_reset", handle_project_reset, true, true, false),
GUI_RPC("project_update", handle_project_update, true, true, false),
GUI_RPC("retry_file_transfer", handle_retry_file_transfer, true, true, false),
GUI_RPC("run_graphics_app", handle_run_graphics_app, true, true, false),
};
// return nonzero only if we need to close the connection
......
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
// 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
......@@ -56,8 +56,10 @@ int main(int /*argc*/, char** argv) {
getcwd( current_dir, sizeof(current_dir));
fprintf(stderr, "current directory = %s\n", current_dir);
for (int i=0; i<argc; i++) {
int i = 0;
while(argv[i]) {
fprintf(stderr, "switcher arg %d: %s\n", i, argv[i]);
++i;
}
fflush(stderr);
#endif
......
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2018 University of California
// 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
......@@ -56,6 +56,10 @@ void launchedGfxApp(char * appPath, pid_t thePID, int slot);
void print_to_log_file(const char *format, ...);
void strip_cr(char *buf);
void PrintBacktrace(void);
extern bool gIsMojave;
extern bool gIsCatalina;
extern bool gIsHighSierra;
extern bool gUseLaunchAgent;
#ifdef __cplusplus
} // extern "C"
......@@ -84,18 +88,18 @@ public:
int Create();
int Run();
//
// Infrastructure layer
//
protected:
OSStatus initBOINCApp(void);
int GetBrandID(void);
char* PersistentFGets(char *buf, size_t buflen, FILE *f);
pid_t FindProcessPID(char* name, pid_t thePID);
pid_t getClientPID(void);
void updateSSMessageText(char *msg);
void strip_cr(char *buf);
char m_gfx_Switcher_Path[PATH_MAX];
char m_gfx_Cleanup_Path[PATH_MAX];
FILE* m_gfx_Cleanup_IPC;
void SetDiscreteGPU(bool setDiscrete);
void CheckDualGPUPowerSource();
bool Host_is_running_on_batteries();
......@@ -165,7 +169,7 @@ public:
bool SetError( bool bErrorMode, unsigned int hrError );
void setSSMessageText(const char *msg);
int terminate_v6_screensaver(int& graphics_application);
int terminate_v6_screensaver(int& graphics_application, RESULT* rp);
bool HasProcessExited(pid_t pid, int &exitCode);
CC_STATE state;
......
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2018 University of California
// 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
......@@ -95,6 +95,11 @@ void print_to_log_file(const char *format, ...);
void strip_cr(char *buf);
void PrintBacktrace(void);
extern bool gIsCatalina;
extern bool gIsHighSierra;
extern bool gIsMojave;
extern bool gUseLaunchAgent;
#ifdef __cplusplus
} // extern "C"
#endif
This diff is collapsed.
// 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 <http://www.gnu.org/licenses/>.
//
// 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 <Cocoa/Cocoa.h>
#include <stdio.h>
#include <pthread.h>
#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 <NSApplicationDelegate, NSUserNotificationCenterDelegate>
{
}
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
int retval = 0;