[boinc] 01/01: New upstream version 7.8.3+dfsg

Gianfranco Costamagna locutusofborg at moszumanska.debian.org
Thu Oct 5 09:24:51 UTC 2017


This is an automated email from the git hooks/post-receive script.

locutusofborg pushed a commit to branch upstream
in repository boinc.

commit dbf44b51f8ce3294dca39505e6653de042171aaf
Author: Gianfranco Costamagna <costamagnagianfranco at yahoo.it>
Date:   Thu Oct 5 11:10:57 2017 +0200

    New upstream version 7.8.3+dfsg
---
 api/MultiGPUMig.defs             |  19 ++
 api/graphics2.cpp                |  21 +-
 api/graphics2_unix.cpp           |  52 +++-
 api/macglutfix.m                 | 355 +++++++++++++++++++++++++-
 api/x_opengl.h                   |  10 +-
 client/acct_mgr.cpp              | 115 ++++++---
 client/app_control.cpp           |  14 +-
 client/app_start.cpp             |   4 +-
 client/async_file.cpp            |   6 +-
 client/client_state.cpp          |  59 ++---
 client/client_state.h            |   2 +-
 client/cpu_sched.cpp             |   6 +-
 client/project.cpp               |   8 +-
 client/project.h                 |   8 +-
 clientscr/Mac_Saver_Module.h     |  33 ++-
 clientscr/Mac_Saver_ModuleView.h |  40 ++-
 clientscr/Mac_Saver_ModuleView.m | 530 +++++++++++++++++++++++++++++++++++----
 clientscr/mac_saver_module.cpp   |  62 ++++-
 clientscr/screensaver.cpp        |  68 ++++-
 clientscr/screensaver.h          |   3 +-
 configure.ac                     |   2 +-
 lib/cc_config.cpp                |   4 +-
 lib/filesys.cpp                  |   4 +-
 locale/fr/BOINC-Manager.mo       | Bin 80627 -> 80651 bytes
 version.log                      |   2 +-
 25 files changed, 1244 insertions(+), 183 deletions(-)

diff --git a/api/MultiGPUMig.defs b/api/MultiGPUMig.defs
new file mode 100644
index 0000000..00f1424
--- /dev/null
+++ b/api/MultiGPUMig.defs
@@ -0,0 +1,19 @@
+#include <mach/mach_types.defs>
+
+subsystem MGSServer 29000;
+userprefix      _MGC;         /* Routine prefixes for user access. */
+serverprefix    _MGS;         /* Routine prefixes for internal server access. */
+
+/* Client -> Server */
+routine CheckinClient(
+		server_port	: mach_port_t;
+	in	client_port	: mach_port_t;
+	out	client_index	: int32_t
+);
+
+/* Master -> Slave */
+routine DisplayFrame(
+		server_port	: mach_port_t;
+		frame_index	: int32_t;
+		iosurface_port    : mach_port_t
+);
diff --git a/api/graphics2.cpp b/api/graphics2.cpp
index 56b0fa9..d72d7bd 100644
--- a/api/graphics2.cpp
+++ b/api/graphics2.cpp
@@ -1,6 +1,6 @@
 // This file is part of BOINC.
 // http://boinc.berkeley.edu
-// Copyright (C) 2008 University of California
+// Copyright (C) 2017 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
@@ -31,6 +31,9 @@
 #include "shmem.h"
 #include "boinc_api.h"
 #include "graphics2.h"
+#ifdef __APPLE__
+#include "x_opengl.h"
+#endif
 
 double boinc_max_fps = 30.;
 double boinc_max_gfx_cpu_frac = 0.2;
@@ -84,7 +87,23 @@ bool throttled_app_render(int x, int y, double t) {
         if (boinc_max_gfx_cpu_frac) {
             boinc_calling_thread_cpu_time(t0);
         }
+        
+#ifdef __APPLE__
+        if (UseSharedOffscreenBuffer()) {
+            MacPrepareOffscreenBuffer();
+        }
+#endif
         app_graphics_render(x, y, t);
+
+#ifdef __APPLE__
+        if (UseSharedOffscreenBuffer()) {
+            MacPassOffscreenBufferToScreenSaver();
+
+//app_graphics_render(x, y, t); // For testing only
+        }
+
+#endif
+
         if (boinc_max_gfx_cpu_frac) {
             boinc_calling_thread_cpu_time(t1);
             total_render_time += t1 - t0;
diff --git a/api/graphics2_unix.cpp b/api/graphics2_unix.cpp
index 63f6c77..4659d44 100644
--- a/api/graphics2_unix.cpp
+++ b/api/graphics2_unix.cpp
@@ -1,6 +1,6 @@
 // This file is part of BOINC.
 // http://boinc.berkeley.edu
-// Copyright (C) 2008 University of California
+// Copyright (C) 2017 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
@@ -118,6 +118,11 @@ static void maybe_render() {
     
     
     if (throttled_app_render(new_width, new_height, dtime())) {
+#ifdef __APPLE__
+        if (UseSharedOffscreenBuffer()) {
+            return; // Don't try to send garbage to screen
+        }
+#endif
         glutSwapBuffers();
         if (! fullscreen) {
             // If user has changed window size, wait until it stops 
@@ -244,3 +249,48 @@ void boinc_graphics_loop(int argc, char** argv, const char* title) {
 #endif
     glutMainLoop();
 }
+
+#ifdef __APPLE__
+
+bool UseSharedOffscreenBuffer() {
+    static bool alreadyTested = false;
+    static bool needSharedGfxBuffer = false;
+
+//return true;    // FOR TESTING ONLY
+    if (alreadyTested) {
+        return needSharedGfxBuffer;
+    }
+    alreadyTested = true;
+    if (fullscreen) {
+        SInt32 major = -1;
+        SInt32 minor = -1;
+        char vers[100], *p1 = NULL;
+        FILE *f;
+        vers[0] = '\0';
+        f = popen("sw_vers -productVersion", "r");
+        if (f) {
+            fscanf(f, "%s", vers);
+            pclose(f);
+        }
+        if (vers[0] == '\0') {
+            fprintf(stderr, "popen(\"sw_vers -productVersion\" failed\n");
+            fflush(stderr);
+            return false;
+        }
+        // Extract the major system version number
+        major = atoi(vers);
+        if (major > 10) {   // OS 11.0 or later
+            needSharedGfxBuffer = true;
+            return true;
+        }
+        // Extract the minor system version number
+        p1 = strchr(vers, '.');
+        minor = atoi(p1+1);
+        if (minor > 12) {   // OS 10.13 or later
+            needSharedGfxBuffer = true;
+            return true;
+        }
+    }
+    return false;
+}
+#endif
diff --git a/api/macglutfix.m b/api/macglutfix.m
index 69d4ba5..08070ef 100644
--- a/api/macglutfix.m
+++ b/api/macglutfix.m
@@ -1,6 +1,6 @@
 // Berkeley Open Infrastructure for Network Computing
 // http://boinc.berkeley.edu
-// Copyright (C) 2005 University of California
+// Copyright (C) 2017 University of California
 //
 // This is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public
@@ -21,13 +21,30 @@
 //  macglutfix.m
 //
 
+#define CREATE_LOG 0    // Set to 1 for debugging
+
+#define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED
+
 #include <Cocoa/Cocoa.h>
+#include <mach/mach_time.h>
+#include <pthread.h>
+#import <OpenGL/CGLIOSurface.h>
+#import <GLKit/GLKit.h>
+#include <servers/bootstrap.h>
+#import "MultiGPUMig.h"
+#import "MultiGPUMigServer.h"
+#include "x_opengl.h"
+#include "boinc_gl.h"
+#include "boinc_glut.h"
+
+extern bool fullscreen; // set in graphics2_unix.cpp
 
 // For unknown reason, "boinc_api.h" gets a compile 
 // error here so just declare boinc_is_standalone()
 //#include "boinc_api.h"
 extern int boinc_is_standalone(void);
 
+// int set_realtime(int period, int computation, int constraint);
 void MacGLUTFix(bool isScreenSaver);
 void BringAppToFront(void);
 
@@ -54,11 +71,6 @@ void MacGLUTFix(bool isScreenSaver) {
         }
     }
 
-    // In screensaver mode, set our window's level just above 
-    // our BOINC screensaver's window level so it can appear 
-    // over it.  This doesn't interfere with the screensaver 
-    // password dialog because the dialog appears only after 
-    // our screensaver is closed.
     myContext = [ NSOpenGLContext currentContext ];
     if (myContext)
         myView = [ myContext view ];
@@ -66,17 +78,57 @@ void MacGLUTFix(bool isScreenSaver) {
         myWindow = [ myView window ];
     if (myWindow == nil)
         return;
-        
+    
     if (!isScreenSaver) {
         NSButton *closeButton = [myWindow standardWindowButton:NSWindowCloseButton ];
         [closeButton setEnabled:YES];
         [myWindow setDocumentEdited: NO];
         return;
     }
-        
-    if ([ myWindow level ] == GlutFullScreenWindowLevel)
-        [ myWindow setLevel:RealSaverLevel+20 ];
+
+    // As of OS 10.13, app windows can no longer appear on top of screensaver
+    // window, but we still use this method on older versions of OS X for
+    // compatibility with older project graphics apps.
+    if (!UseSharedOffscreenBuffer()) {
+        // In screensaver mode, set our window's level just above
+        // our BOINC screensaver's window level so it can appear
+        // over it.  This doesn't interfere with the screensaver
+        // password dialog because the dialog appears only after
+        // our screensaver is closed.
+        if ([ myWindow level ] == GlutFullScreenWindowLevel) {
+            [ myWindow setLevel:RealSaverLevel+20 ];
+        }
+    }
+}
+
+#if 0
+// NOT USED: See comments in animateOneFrame in Mac_Saver_ModuleView.m
+// <https://developer.apple.com/library/content/technotes/tn2169>
+int set_realtime(int period, int computation, int constraint) {
+    mach_timebase_info_data_t timebase_info;
+    mach_timebase_info(&timebase_info);
+ 
+    const uint64_t NANOS_PER_MSEC = 1000000ULL;
+    double clock2abs = ((double)timebase_info.denom / (double)timebase_info.numer) * NANOS_PER_MSEC;
+ 
+    thread_time_constraint_policy_data_t policy;
+    policy.period      = period;
+    policy.computation = (uint32_t)(computation * clock2abs); // computation ms of work
+    policy.constraint  = (uint32_t)(constraint * clock2abs);
+//    policy.preemptible = FALSE;
+    policy.preemptible = TRUE;
+
+    int kr = thread_policy_set(pthread_mach_thread_np(pthread_self()),
+                   THREAD_TIME_CONSTRAINT_POLICY,
+                   (thread_policy_t)&policy,
+                   THREAD_TIME_CONSTRAINT_POLICY_COUNT);
+    if (kr != KERN_SUCCESS) {
+        fprintf(stderr, "set_realtime() failed.\n");
+        return 0;
+    }
+    return 1;
 }
+#endif
 
 void BringAppToFront() {
     [ NSApp activateIgnoringOtherApps:YES ];
@@ -86,3 +138,286 @@ void HideThisApp() {
     [ NSApp hide:NSApp ];
 }
 
+// On OS 10.13 or later, use MachO comunication and IOSurfaceBuffer to
+// display the graphics output of our child graphics apps in our window.
+
+// Code adapted from Apple Developer Tech Support Sample Code MutiGPUIOSurface:
+// <https://developer.apple.com/library/content/samplecode/MultiGPUIOSurface>
+
+#define NUM_IOSURFACE_BUFFERS 2
+
+ at interface ServerController : NSObject <NSMachPortDelegate>
+{
+    NSMachPort *serverPort;
+	NSMachPort *localPort;
+    
+	uint32_t serverPortName;
+	uint32_t localPortName;
+    
+	NSMachPort *clientPort[16];
+	uint32_t clientPortNames[16];
+	uint32_t clientPortCount;
+}
+- (ServerController *)init;
+- (kern_return_t)checkInClient:(mach_port_t)client_port index:(int32_t *)client_index;
+- (void)portDied:(NSNotification *)notification;
+- (void)sendIOSurfaceMachPortToClients: (uint32_t)index withMachPort:(mach_port_t) iosurface_port;
+
+ at end
+
+static ServerController *myserverController;
+
+static uint32_t nextFrameIndex;
+static uint32_t currentFrameIndex;
+
+static IOSurfaceRef ioSurfaceBuffers[NUM_IOSURFACE_BUFFERS];
+static mach_port_t ioSurfaceMachPorts[NUM_IOSURFACE_BUFFERS];
+static GLuint textureNames[NUM_IOSURFACE_BUFFERS];
+static GLuint fboNames[NUM_IOSURFACE_BUFFERS];
+static GLuint depthBufferName;
+
+ at implementation ServerController
+
+- (ServerController *)init
+{
+	[[NSNotificationCenter defaultCenter] addObserver:self
+	    selector:@selector(portDied:) name:NSPortDidBecomeInvalidNotification object:nil];
+	
+    mach_port_t servicePortNum = MACH_PORT_NULL;
+    kern_return_t machErr;
+    char *portName = "edu.berkeley.boincsaver";
+    
+// NSMachBootstrapServer is deprecated in OS 10.13, so use bootstrap_look_up
+//	serverPort = [(NSMachPort *)([[NSMachBootstrapServer sharedInstance] servicePortWithName:@"edu.berkeley.boincsaver"]) retain];
+    machErr = bootstrap_check_in(bootstrap_port, portName, &servicePortNum);
+    if (machErr != KERN_SUCCESS) {
+        		[NSApp terminate:self];
+    }
+    serverPort = (NSMachPort*)[NSMachPort portWithMachPort:servicePortNum];
+	
+	// Create a local dummy reply port to use with the mig reply stuff
+	localPort = [[NSMachPort alloc] init];
+	
+	// Retrieve raw mach port names.
+	serverPortName = [serverPort machPort];
+	localPortName  = [localPort machPort];
+
+	[serverPort setDelegate:self];
+	[serverPort scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
+
+    // NOT USED: See comments in animateOneFrame in Mac_Saver_ModuleView.m
+#if 0
+    // This is an alternate method to get enough CPU cycles when we
+    // are running in the background behind ScreensaverEngine.app
+    set_realtime(0, 5, 33);
+    //set_realtime(0, 5, 10);
+    //set_realtime(33, 5, 33);
+    //set_realtime(30, 3, 6);
+    //set_realtime(30, 10, 20);
+#endif
+
+    return self;
+}
+
+- (void)portDied:(NSNotification *)notification
+{
+	NSPort *port = [notification object];
+	if(port == serverPort)
+	{
+		[NSApp terminate:self];
+	}
+	else
+	{		
+		int i;
+		for(i = 0; i < clientPortCount+1; i++)
+		{
+			if([clientPort[i] isEqual:port])
+			{
+				[clientPort[i] release];
+				clientPort[i] = nil;
+				clientPortNames[i] = 0;
+			}
+		}
+	}
+}
+
+- (void)handleMachMessage:(void *)msg
+{
+	union __ReplyUnion___MGCMGSServer_subsystem reply;
+	
+	mach_msg_header_t *reply_header = (void *)&reply;
+	kern_return_t kr;
+	
+	if(MGSServer_server(msg, reply_header) && reply_header->msgh_remote_port != MACH_PORT_NULL)
+	{
+		kr = mach_msg(reply_header, MACH_SEND_MSG, reply_header->msgh_size, 0, MACH_PORT_NULL,
+			     0, MACH_PORT_NULL);
+        if(kr != 0)
+			[NSApp terminate:nil];
+	}
+}
+
+- (kern_return_t)checkInClient:(mach_port_t)client_port index:(int32_t *)client_index
+{	
+	clientPortCount++;			// clients always start at index 1
+	clientPortNames[clientPortCount] = client_port;
+	clientPort[clientPortCount] = [[NSMachPort alloc] initWithMachPort:client_port];
+	
+	*client_index = clientPortCount;
+	return 0;
+}
+
+kern_return_t _MGSCheckinClient(mach_port_t server_port, mach_port_t client_port,
+			       int32_t *client_index)
+{
+	return [myserverController checkInClient:client_port index:client_index];
+}
+
+// For the MachO server, this is a no-op
+kern_return_t _MGSDisplayFrame(mach_port_t server_port, int32_t frame_index, uint32_t iosurface_port)
+{
+	return 0;
+}
+
+- (void)sendIOSurfaceMachPortToClients:(uint32_t)index withMachPort:(mach_port_t)iosurface_port
+{
+	int i;
+	for(i = 0; i < clientPortCount+1; i++)
+	{
+		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);
+		}
+	}
+}
+ at end
+
+
+void MacPrepareOffscreenBuffer() {
+	GLuint name, namef;
+
+    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)@{
+                (id)kIOSurfaceWidth: [NSNumber numberWithInt: w],
+                (id)kIOSurfaceHeight: [NSNumber numberWithInt: h],
+                (id)kIOSurfaceBytesPerElement: @4
+                });
+            ioSurfaceMachPorts[i] = IOSurfaceCreateMachPort(ioSurfaceBuffers[i]);
+        }
+    }
+    
+	if(!textureNames[nextFrameIndex])
+	{
+        NSOpenGLContext * myContext = [ NSOpenGLContext currentContext ];
+        NSView *myView = [ myContext view ];
+        GLsizei w = myView.bounds.size.width;
+        GLsizei h = myView.bounds.size.height;
+
+        CGLContextObj cgl_ctx = (CGLContextObj)[myContext CGLContextObj];
+        
+        glGenTextures(1, &name);
+        
+        glBindTexture(GL_TEXTURE_RECTANGLE, name);
+        // At the moment, CGLTexImageIOSurface2D requires the GL_TEXTURE_RECTANGLE target
+        CGLTexImageIOSurface2D(cgl_ctx, GL_TEXTURE_RECTANGLE, GL_RGBA, w, h, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
+                        ioSurfaceBuffers[nextFrameIndex], 0);
+        glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);	
+        
+        // Generate an FBO and bind the texture to it as a render target.
+        glBindTexture(GL_TEXTURE_RECTANGLE, 0);
+        
+        glGenFramebuffers(1, &namef);
+        glBindFramebuffer(GL_FRAMEBUFFER, namef);
+        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, name, 0);
+
+        if(!depthBufferName)
+        {
+            glGenRenderbuffers(1, &depthBufferName);
+            glRenderbufferStorage(GL_TEXTURE_RECTANGLE, GL_DEPTH, w, h);
+        }
+        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_RECTANGLE, depthBufferName);
+
+        glBindFramebuffer(GL_FRAMEBUFFER, 0);
+        
+        fboNames[nextFrameIndex] = namef;
+        textureNames[nextFrameIndex] = name;
+	}
+    currentFrameIndex = nextFrameIndex;
+	nextFrameIndex = (nextFrameIndex + 1) % NUM_IOSURFACE_BUFFERS;
+
+    glBindFramebuffer(GL_FRAMEBUFFER, fboNames[currentFrameIndex]);
+}
+
+void MacPassOffscreenBufferToScreenSaver() {
+    glutSwapBuffers();
+    [myserverController sendIOSurfaceMachPortToClients: currentFrameIndex
+                        withMachPort:ioSurfaceMachPorts[currentFrameIndex]];
+    glFlush();
+	glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+// Code for debugging:
+
+#if CREATE_LOG
+void strip_cr(char *buf)
+{
+    char *theCR;
+
+    theCR = strrchr(buf, '\n');
+    if (theCR)
+        *theCR = '\0';
+    theCR = strrchr(buf, '\r');
+    if (theCR)
+        *theCR = '\0';
+}
+#endif
+
+void print_to_log_file(const char *format, ...) {
+#if CREATE_LOG
+    va_list args;
+    char buf[256];
+    time_t t;
+    FILE *f;
+    if (fullscreen) {
+        // We can't write to our home directory if running as user / group boinc_project
+        f = fopen("/Users/Shared/test_log.txt", "a");
+    } else {
+        strlcpy(buf, getenv("HOME"), sizeof(buf));
+        strlcat(buf, "/Documents/test_log.txt", sizeof(buf));
+        f = fopen(buf, "a");
+        // freopen(buf, "a", stdout);
+        //freopen(buf, "a", stderr);
+    }
+    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);
+    fflush(f);
+    fclose(f);
+#endif
+}
+
diff --git a/api/x_opengl.h b/api/x_opengl.h
index 55a995e..bb38cee 100644
--- a/api/x_opengl.h
+++ b/api/x_opengl.h
@@ -1,6 +1,6 @@
 // This file is part of BOINC.
 // http://boinc.berkeley.edu
-// Copyright (C) 2008 University of California
+// Copyright (C) 2017 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
@@ -25,9 +25,15 @@ extern "C" {
 extern int xwin_glut_is_initialized();  
 
 #ifdef __APPLE__
-extern void MacGLUTFix(bool isScreenSaver);  
+extern void MacGLUTFix(bool isScreenSaver);
+extern void MacPrepareOffscreenBuffer(void);
+extern void MacPassOffscreenBufferToScreenSaver(void);
 extern void BringAppToFront(void);
 extern void HideThisApp(void);
+extern bool UseSharedOffscreenBuffer(void);
+
+extern void print_to_log_file(const char *format, ...);
+
 #endif
 
 #ifdef __cplusplus
diff --git a/client/acct_mgr.cpp b/client/acct_mgr.cpp
index 1064b75..af9d1cc 100644
--- a/client/acct_mgr.cpp
+++ b/client/acct_mgr.cpp
@@ -170,7 +170,9 @@ int ACCT_MGR_OP::do_rpc(
             "      <gpu_ec>%f</gpu_ec>\n"
             "      <gpu_time>%f</gpu_time>\n"
             "      <njobs_success>%d</njobs_success>\n"
-            "      <njobs_error>%d</njobs_error>\n",
+            "      <njobs_error>%d</njobs_error>\n"
+            "      <disk_usage>%f</disk_usage>\n"
+            "      <disk_share>%f</disk_share>\n",
             p->master_url,
             p->project_name,
             p->suspended_via_gui?1:0,
@@ -187,7 +189,9 @@ int ACCT_MGR_OP::do_rpc(
             p->gpu_ec,
             p->gpu_time,
             p->njobs_success,
-            p->njobs_error
+            p->njobs_error,
+            p->disk_usage,
+            p->disk_share
         );
         if (p->attached_via_acct_mgr) {
             fprintf(f,
@@ -458,54 +462,61 @@ void ACCT_MGR_OP::handle_reply(int http_op_retval) {
     bool verified;
     PROJECT* pp;
     bool sig_ok;
+    bool got_error = false;
 
-    if (http_op_retval == 0) {
+    // check for failures of HTTP OP, reply parse
+    //
+    if (http_op_retval) {
+        msg_printf(&ami, MSG_INFO, "AM RPC HTTP failure: %s",
+            boincerror(http_op_retval)
+        );
+        got_error = true;
+    } else {
         FILE* f = fopen(ACCT_MGR_REPLY_FILENAME, "r");
         if (f) {
             retval = parse(f);
+            if (retval) {
+                got_error = true;
+                msg_printf(&ami, MSG_INFO, "AM reply parse error");
+            }
             fclose(f);
         } else {
-            retval = ERR_FOPEN;
+            msg_printf(&ami, MSG_INFO, "AM reply file missing");
+            got_error = true;
         }
-    } else {
-        error_num = http_op_retval;
     }
 
-    gstate.acct_mgr_info.password_error = false;
-    if (error_num == ERR_BAD_PASSWD && !via_gui) {
-        gstate.acct_mgr_info.password_error = true;
-    }
-    // check both error_str and error_num since an account manager may only
-    // return a BOINC based error code for password failures or invalid
-    // email addresses
+    // if no errors so far, check for errors from AM
     //
-    if (error_str.size()) {
-        msg_printf(&ami, MSG_USER_ALERT,
-            "%s: %s",
-            _("Message from account manager"),
-            error_str.c_str()
-        );
-        if (!error_num) {
-            error_num = ERR_XML_PARSE;
-        }
-    } else if (error_num) {
-        if (error_num == http_op_retval) {
-            // if it was an HTTP error, don't notify the user;
-            // probably the acct mgr server is down
-            //
-            msg_printf(&ami, MSG_INFO,
-                "Account manager RPC failed: %s", boincerror(error_num)
+    if (!got_error) {
+        gstate.acct_mgr_info.password_error = false;
+        if (error_num == ERR_BAD_PASSWD && !via_gui) {
+            gstate.acct_mgr_info.password_error = true;
+        }
+
+        // Show error message from AM if available.
+        // check both error_str and error_num since an account manager may only
+        // return a BOINC based error code for password failures or invalid
+        // email addresses
+        //
+        if (error_str.size()) {
+            msg_printf(&ami, MSG_USER_ALERT,
+                "%s: %s",
+                _("Message from account manager"),
+                error_str.c_str()
             );
-        } else {
+            got_error = true;
+        } else if (error_num) {
             msg_printf(&ami, MSG_USER_ALERT,
                 "%s: %s",
                 _("Message from account manager"),
                 boincerror(error_num)
             );
+            got_error = true;
         }
     }
 
-    if (error_num) {
+    if (got_error) {
         gstate.acct_mgr_info.next_rpc_time =
             gstate.now
             + calculate_exponential_backoff(
@@ -516,6 +527,26 @@ void ACCT_MGR_OP::handle_reply(int http_op_retval) {
         gstate.acct_mgr_info.nfailures++;
         return;
     }
+
+    // The RPC was successful
+    //
+    // Detach projects that are
+    // - detach_when_done
+    // - done
+    // - attached via AM
+    //
+    while (1) {
+        bool found = false;
+        for (i=0; i<gstate.projects.size(); i++) {
+            PROJECT* p = gstate.projects[i];
+            if (p->detach_when_done && !gstate.nresults_for_project(p) && p->attached_via_acct_mgr) {
+                gstate.detach_project(p);
+                found = true;
+            }
+        }
+        if (!found) break;
+    }
+
     gstate.acct_mgr_info.nfailures = 0;
 
     msg_printf(NULL, MSG_INFO, "Account manager contact succeeded");
@@ -562,15 +593,6 @@ void ACCT_MGR_OP::handle_reply(int http_op_retval) {
         //
         for (i=0; i<accounts.size(); i++) {
             AM_ACCOUNT& acct = accounts[i];
-            retval = check_string_signature2(
-                acct.url.c_str(), acct.url_signature, ami.signing_key, verified
-            );
-            if (retval || !verified) {
-                msg_printf(NULL, MSG_INTERNAL_ERROR,
-                    "Bad signature for URL %s", acct.url.c_str()
-                );
-                continue;
-            }
             pp = gstate.lookup_project(acct.url.c_str());
             if (pp) {
                 if (acct.detach) {
@@ -606,12 +628,16 @@ void ACCT_MGR_OP::handle_reply(int http_op_retval) {
                     pp->attached_via_acct_mgr = true;
                     if (acct.dont_request_more_work.present) {
                         pp->dont_request_more_work = acct.dont_request_more_work.value;
+                    } else {
+                        pp->dont_request_more_work = false;
                     }
                     if (acct.detach_when_done.present) {
                         pp->detach_when_done = acct.detach_when_done.value;
                         if (pp->detach_when_done) {
                             pp->dont_request_more_work = true;
                         }
+                    } else {
+                        pp->detach_when_done = false;
                     }
 
                     // initiate a scheduler RPC if requested by AMS
@@ -661,6 +687,15 @@ void ACCT_MGR_OP::handle_reply(int http_op_retval) {
             } else {
                 // here we don't already have the project.
                 //
+                retval = check_string_signature2(
+                    acct.url.c_str(), acct.url_signature, ami.signing_key, verified
+                );
+                if (retval || !verified) {
+                    msg_printf(NULL, MSG_INTERNAL_ERROR,
+                        "Bad signature for URL %s", acct.url.c_str()
+                    );
+                    continue;
+                }
                 if (acct.authenticator.empty()) {
                     msg_printf(NULL, MSG_INFO,
                         "Account manager reply missing authenticator for %s",
diff --git a/client/app_control.cpp b/client/app_control.cpp
index 24009b4..b65f2bb 100644
--- a/client/app_control.cpp
+++ b/client/app_control.cpp
@@ -439,6 +439,7 @@ void ACTIVE_TASK::handle_exited_app(int stat) {
         );
     }
 #endif
+    char err_msg[4096];
     bool will_restart = false;
 
     get_app_status_msg();
@@ -491,12 +492,12 @@ void ACTIVE_TASK::handle_exited_app(int stat) {
         default:
             char szError[1024];
             set_task_state(PROCESS_EXITED, "handle_exited_app");
-            gstate.report_result_error(
-                *result,
+            sprintf(err_msg,
                 "%s - exit code %d (0x%x)",
                 windows_format_error_string(exit_code, szError, sizeof(szError)),
                 exit_code, exit_code
             );
+            gstate.report_result_error(*result, err_msg);
             if (log_flags.task_debug) {
                 msg_printf(result->project, MSG_INFO,
                     "[task] Process for %s exited",
@@ -528,12 +529,12 @@ void ACTIVE_TASK::handle_exited_app(int stat) {
                 }
                 if (result->exit_status) {
                     set_task_state(PROCESS_EXITED, "handle_exited_app");
-                    gstate.report_result_error(
-                        *result,
+                    sprintf(err_msg,
                         "process exited with code %d (0x%x, %d)",
                         result->exit_status, result->exit_status,
                         (-1<<8)|result->exit_status
                     );
+                    gstate.report_result_error(*result, err_msg);
                 } else {
                     if (finish_file_present()) {
                         set_task_state(PROCESS_EXITED, "handle_exited_app");
@@ -567,9 +568,8 @@ void ACTIVE_TASK::handle_exited_app(int stat) {
                 result->exit_status = stat;
                 set_task_state(PROCESS_WAS_SIGNALED, "handle_exited_app");
                 signal = got_signal;
-                gstate.report_result_error(
-                    *result, "process got signal %d", signal
-                );
+                sprintf(err_msg, "process got signal %d", signal);
+                gstate.report_result_error(*result, err_msg);
             }
         } else {
             result->exit_status = EXIT_UNKNOWN;
diff --git a/client/app_start.cpp b/client/app_start.cpp
index b0529d9..32eab51 100644
--- a/client/app_start.cpp
+++ b/client/app_start.cpp
@@ -1166,7 +1166,9 @@ error:
     // Verify it to trigger another download.
     //
     gstate.input_files_available(result, true);
-    gstate.report_result_error(*result, "couldn't start app: %s", buf);
+    char err_msg[4096];
+    sprintf(err_msg, "couldn't start app: %s", buf);
+    gstate.report_result_error(*result, err_msg);
     if (log_flags.task_debug) {
         msg_printf(wup->project, MSG_INFO,
             "[task] couldn't start app: %s", buf
diff --git a/client/async_file.cpp b/client/async_file.cpp
index e433466..c2c6071 100644
--- a/client/async_file.cpp
+++ b/client/async_file.cpp
@@ -158,10 +158,10 @@ int ASYNC_COPY::copy_chunk() {
 // handle the failure of a copy; error out the result
 //
 void ASYNC_COPY::error(int retval) {
+    char err_msg[4096];
     atp->set_task_state(PROCESS_COULDNT_START, "ASYNC_COPY::error");
-    gstate.report_result_error(
-        *(atp->result), "Couldn't copy file: %s", boincerror(retval)
-    );
+    sprintf(err_msg, "Couldn't copy file: %s", boincerror(retval));
+    gstate.report_result_error(*(atp->result), err_msg);
     gstate.request_schedule_cpus("start failed");
 }
 
diff --git a/client/client_state.cpp b/client/client_state.cpp
index a087716..f06da49 100644
--- a/client/client_state.cpp
+++ b/client/client_state.cpp
@@ -1416,28 +1416,24 @@ bool CLIENT_STATE::garbage_collect() {
     // because detach_project() calls garbage_collect_always(),
     // and we need to avoid infinite recursion
     //
-    if (acct_mgr_info.using_am()) {
-        // If we're using an AM,
-        // start an AM RPC rather than detaching the projects;
-        // the RPC completion handler will detach them.
-        // This way the AM will be informed of their work done.
-        //
-        for (unsigned i=0; i<projects.size(); i++) {
-            PROJECT* p = projects[i];
-            if (p->detach_when_done && !nresults_for_project(p)) {
-                acct_mgr_info.next_rpc_time = 0;
-                acct_mgr_info.poll();
-                break;
-            }
-        }
-    } else {
+    while (1) {
+        bool found = false;
         for (unsigned i=0; i<projects.size(); i++) {
             PROJECT* p = projects[i];
             if (p->detach_when_done && !nresults_for_project(p)) {
-                detach_project(p);
-                action = true;
+                // If we're using an AM,
+                // wait until the next successful RPC to detach project,
+                // so the AM will be informed of its work done.
+                //
+                if (!p->attached_via_acct_mgr) {
+                    msg_printf(p, MSG_INFO, "Detaching - no more tasks");
+                    detach_project(p);
+                    action = true;
+                    found = true;
+                }
             }
         }
+        if (!found) break;
     }
 #endif
     return action;
@@ -1545,14 +1541,12 @@ bool CLIENT_STATE::garbage_collect_always() {
             wup = rp->wup;
             if (wup->had_download_failure(failnum)) {
                 wup->get_file_errors(error_msgs);
-                report_result_error(
-                    *rp, "WU download error: %s", error_msgs.c_str()
-                );
+                string err_msg = "WU download error: " + error_msgs;
+                report_result_error(*rp, err_msg.c_str());
             } else if (rp->avp && rp->avp->had_download_failure(failnum)) {
                 rp->avp->get_file_errors(error_msgs);
-                report_result_error(
-                    *rp, "app_version download error: %s", error_msgs.c_str()
-                );
+                string err_msg = "app_version download error: " + error_msgs;
+                report_result_error(*rp, err_msg.c_str());
             }
         }
         bool found_error = false;
@@ -1585,7 +1579,8 @@ bool CLIENT_STATE::garbage_collect_always() {
                     atp->abort_task(ERR_RESULT_UPLOAD, "upload failure");
                 }
             }
-            report_result_error(*rp, "upload failure: %s", error_str.c_str());
+            string err_msg = "upload failure: " + error_str;
+            report_result_error(*rp, err_msg.c_str());
         }
 #endif
         rp->avp->ref_cnt++;
@@ -1835,10 +1830,8 @@ bool CLIENT_STATE::time_to_exit() {
 // - If result state is FILES_DOWNLOADED, change it to COMPUTE_ERROR
 //   so that we don't try to run it again.
 //
-int CLIENT_STATE::report_result_error(RESULT& res, const char* format, ...) {
-    char buf[4096],  err_msg[4096];
-        // The above store 1-line messages and short XML snippets.
-        // Shouldn't exceed a few hundred bytes.
+int CLIENT_STATE::report_result_error(RESULT& res, const char* err_msg) {
+    char buf[1024];
     unsigned int i;
     int failnum;
 
@@ -1851,18 +1844,14 @@ int CLIENT_STATE::report_result_error(RESULT& res, const char* format, ...) {
     res.set_ready_to_report();
     res.completed_time = now;
 
-    va_list va;
-    va_start(va, format);
-    vsnprintf(err_msg, sizeof(err_msg), format, va);
-    va_end(va);
-
     sprintf(buf, "Unrecoverable error for task %s", res.name);
 #ifndef SIM
     scheduler_op->project_rpc_backoff(res.project, buf);
 #endif
 
-    sprintf( buf, "<message>\n%s\n</message>\n", err_msg);
-    res.stderr_out.append(buf);
+    res.stderr_out.append("<message>\n");
+    res.stderr_out.append(err_msg);
+    res.stderr_out.append("</message>\n");
 
     switch(res.state()) {
     case RESULT_NEW:
diff --git a/client/client_state.h b/client/client_state.h
index 489a5be..d8d574f 100644
--- a/client/client_state.h
+++ b/client/client_state.h
@@ -265,7 +265,7 @@ struct CLIENT_STATE {
         APP*, char* platform, int ver, char* plan_class
     );
     int detach_project(PROJECT*);
-    int report_result_error(RESULT&, const char *format, ...);
+    int report_result_error(RESULT&, const char* err_msg);
     int reset_project(PROJECT*, bool detaching);
     bool no_gui_rpc;
     bool gui_rpc_unix_domain;
diff --git a/client/cpu_sched.cpp b/client/cpu_sched.cpp
index 2665bf4..2a3e8ad 100644
--- a/client/cpu_sched.cpp
+++ b/client/cpu_sched.cpp
@@ -1442,9 +1442,9 @@ bool CLIENT_STATE::enforce_run_list(vector<RESULT*>& run_list) {
                 continue;
             }
             if (retval) {
-                report_result_error(
-                    *(atp->result), "Couldn't start or resume: %d", retval
-                );
+                char err_msg[4096];
+                sprintf(err_msg, "Couldn't start or resume: %d", retval);
+                report_result_error(*(atp->result), err_msg);
                 request_schedule_cpus("start failed");
                 continue;
             }
diff --git a/client/project.cpp b/client/project.cpp
index e19e952..eab0706 100644
--- a/client/project.cpp
+++ b/client/project.cpp
@@ -332,6 +332,8 @@ int PROJECT::parse_state(XML_PARSER& xp) {
         if (xp.parse_double("cpu_time", cpu_time)) continue;
         if (xp.parse_double("gpu_ec", gpu_ec)) continue;
         if (xp.parse_double("gpu_time", gpu_time)) continue;
+        if (xp.parse_double("disk_usage", disk_usage)) continue;
+        if (xp.parse_double("disk_share", disk_share)) continue;
 #ifdef SIM
         if (xp.match_tag("available")) {
             available.parse(xp, "/available");
@@ -529,8 +531,10 @@ int PROJECT::write_state(MIOFILE& out, bool gui_rpc) {
             "    <cpu_ec>%f</cpu_ec>\n"
             "    <cpu_time>%f</cpu_time>\n"
             "    <gpu_ec>%f</gpu_ec>\n"
-            "    <gpu_time>%f</gpu_time>\n",
-            cpu_ec, cpu_time, gpu_ec, gpu_time
+            "    <gpu_time>%f</gpu_time>\n"
+            "    <disk_usage>%f</disk_usage>\n"
+            "    <disk_share>%f</disk_share>\n",
+            cpu_ec, cpu_time, gpu_ec, gpu_time, disk_usage, disk_share
         );
     }
     out.printf(
diff --git a/client/project.h b/client/project.h
index 3029efe..3c677a2 100644
--- a/client/project.h
+++ b/client/project.h
@@ -153,6 +153,10 @@ struct PROJECT : PROJ_AM {
         // Reasons are enumerated in lib/common_defs.h
     bool trickle_up_pending;
         // have trickle up to send
+    double disk_usage;
+        // computed by get_disk_usages()
+    double disk_share;
+        // computed by get_disk_shares();
 
     ///////  END OF ITEMS STORED IN client_state.xml
 
@@ -174,10 +178,6 @@ struct PROJECT : PROJ_AM {
         // to make sure they haven't been tampered with.
         // This provides only the illusion of security.
     bool use_symlinks;
-    double disk_usage;
-        // computed by get_disk_usages()
-    double disk_share;
-        // computed by get_disk_shares();
     bool report_results_immediately;
 
     // items sent in scheduler replies,
diff --git a/clientscr/Mac_Saver_Module.h b/clientscr/Mac_Saver_Module.h
index 6334c62..a72b215 100644
--- a/clientscr/Mac_Saver_Module.h
+++ b/clientscr/Mac_Saver_Module.h
@@ -1,6 +1,6 @@
 // This file is part of BOINC.
 // http://boinc.berkeley.edu
-// Copyright (C) 2008 University of California
+// Copyright (C) 2017 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
@@ -24,6 +24,8 @@
 
 #include <Carbon/Carbon.h>
 
+// The declarations below must be kept in sync with
+// the corresponding ones in Mac_Saver_ModuleView.h
 #ifdef _DEBUG
     #define _T(x) x
 #endif
@@ -43,12 +45,14 @@ bool            getShow_default_ss_first();
 double          getGFXDefaultPeriod();
 double          getGFXSciencePeriod();
 double          getGGFXChangePeriod();
+void            incompatibleGfxApp(char * appPath, pid_t pid, int slot);
 void            setShow_default_ss_first(bool value);
 void            setGFXDefaultPeriod(double value);
 void            setGFXSciencePeriod(double value);
 void            setGGFXChangePeriod(double value);
 double          getDTime();
 void            doBoinc_Sleep(double seconds);
+void            launchedGfxApp(char * appPath, pid_t thePID, int slot);
 void            print_to_log_file(const char *format, ...);
 void            strip_cr(char *buf);
 void            PrintBacktrace(void);
@@ -57,6 +61,9 @@ void            PrintBacktrace(void);
 }	// extern "C"
 #endif
 
+// The declarations above must be kept in sync with
+// the corresponding ones in Mac_Saver_ModuleView.h
+
 struct ss_periods
 {
     double          GFXDefaultPeriod;
@@ -86,8 +93,6 @@ protected:
     int             GetBrandID(void);
     char*           PersistentFGets(char *buf, size_t buflen, FILE *f);
     pid_t           FindProcessPID(char* name, pid_t thePID);
-    bool            SetError( bool bErrorMode, unsigned int hrError );
-    void            setSSMessageText(const char *msg);
     void            updateSSMessageText(char *msg);
     void            strip_cr(char *buf);
     char            m_gfx_Switcher_Path[PATH_MAX];
@@ -107,7 +112,7 @@ protected:
     bool            m_bScience_gfx_running;
     bool            m_bDefault_gfx_running;
     bool            m_bConnected;
-
+    std::vector<char*>   m_vIncompatibleGfxApps;
     //
     // Data management layer
     //
@@ -118,16 +123,13 @@ protected:
     void*           DataManagementProc();
     static void*    DataManagementProcStub( void* param );
     int             terminate_v6_screensaver(int& graphics_application);
-    int             terminate_screensaver(int& graphics_application, RESULT *worker_app);
     int             terminate_default_screensaver(int& graphics_application);
     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);
-    bool            HasProcessExited(pid_t pid, int &exitCode);
     pthread_t       m_hDataManagementThread;
-    pid_t           m_hGraphicsApplication;
 
 // Determine if two RESULT pointers refer to the same task
     bool            is_same_task(RESULT* taska, RESULT* taskb);
@@ -139,10 +141,6 @@ protected:
 //   was passed in.
 
     RESULT*         get_random_graphics_app(RESULTS& results, RESULT* exclude = NULL);
-
-    RPC_CLIENT      *rpc;
-    CC_STATE        state;
-    RESULTS         results;
  
     bool            m_bResetCoreState;
     bool            m_bQuitDataManagementProc;
@@ -162,12 +160,25 @@ public:
     void            windowIsCovered(void);
     void            drawPreview(CGContextRef myContext);
     void            ShutdownSaver();
+    void            markAsIncompatible(char *gfxAppName);
+    bool            isIncompatible(char *appName);
+    bool            SetError( bool bErrorMode, unsigned int hrError );
+    void            setSSMessageText(const char *msg);
+
+    int             terminate_screensaver(int& graphics_application, RESULT *worker_app);
+    bool            HasProcessExited(pid_t pid, int &exitCode);
+
+    CC_STATE        state;
+    RESULTS         results;
+    RPC_CLIENT      *rpc;
 
     double          m_fGFXDefaultPeriod;
     double          m_fGFXSciencePeriod;
     double          m_fGFXChangePeriod;
     bool            m_bShow_default_ss_first;
 
+    pid_t           m_hGraphicsApplication;
+
 protected:
 };
 
diff --git a/clientscr/Mac_Saver_ModuleView.h b/clientscr/Mac_Saver_ModuleView.h
index e2a634c..0bf2292 100644
--- a/clientscr/Mac_Saver_ModuleView.h
+++ b/clientscr/Mac_Saver_ModuleView.h
@@ -20,6 +20,7 @@
 //
 
 #import <ScreenSaver/ScreenSaver.h>
+#import <Cocoa/Cocoa.h>
 
 
 @interface BOINC_Saver_ModuleView : ScreenSaverView 
@@ -43,19 +44,54 @@
 
 @end
 
+ at interface SharedGraphicsController : NSObject <NSMachPortDelegate>
+
+ at property (NS_NONATOMIC_IOSONLY, readonly) GLuint currentTextureName;
+- (instancetype)init:(NSView*)saverView;
+- (void)portDied:(NSNotification *)notification;
+- (void)testConnection;
+
+ at end
+
+ at interface saverOpenGLView : NSOpenGLView
+
+- (GLuint)setupIOSurfaceTexture:(IOSurfaceRef)ioSurfaceBuffer;
+
+ at end
+
+// The declarations below must be kept in sync with
+// the corresponding ones in Mac_Saver_Module.h
+#ifdef _DEBUG
+    #define _T(x) x
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 void            initBOINCSaver(void);
 int             startBOINCSaver(void);
 int             getSSMessage(char **theMessage, int* coveredFreq);
 void            windowIsCovered();
 void            drawPreview(CGContextRef myContext);
 void            closeBOINCSaver(void);
+void            setDefaultDisplayPeriods(void);
+bool            getShow_default_ss_first();
 double          getGFXDefaultPeriod();
 double          getGFXSciencePeriod();
 double          getGGFXChangePeriod();
+void            incompatibleGfxApp(char * appPath, pid_t pid, int slot);
+void            setShow_default_ss_first(bool value);
 void            setGFXDefaultPeriod(double value);
 void            setGFXSciencePeriod(double value);
 void            setGGFXChangePeriod(double value);
-bool            validateNumericString(CFStringRef s);
 double          getDTime();
 void            doBoinc_Sleep(double seconds);
-extern void     print_to_log_file(const char *format, ...);
+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);
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
diff --git a/clientscr/Mac_Saver_ModuleView.m b/clientscr/Mac_Saver_ModuleView.m
index 50e36e7..565776a 100644
--- a/clientscr/Mac_Saver_ModuleView.m
+++ b/clientscr/Mac_Saver_ModuleView.m
@@ -20,14 +20,24 @@
 //  BOINC_Saver_Module
 //
 
+#define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED 1
+
 #import "Mac_Saver_ModuleView.h"
 #include <Carbon/Carbon.h>
 #include <AppKit/AppKit.h>
-#include <QTKit/QTKitDefines.h> // For NSInteger
 #include <IOKit/hidsystem/IOHIDLib.h>
 #include <IOKit/hidsystem/IOHIDParameter.h>
 #include <IOKit/hidsystem/event_status_driver.h>
+#import <OpenGL/gl.h>
+#import <GLKit/GLKit.h>
+#include <servers/bootstrap.h>
+//#import <IOSurface/IOSurface.h>
+//#import <OpenGL/gl3.h>
+//#import <OpenGL/CGLIOSurface.h>
+
 #include "mac_util.h"
+#import "MultiGPUMig.h"
+#import "MultiGPUMigServer.h"
 
 #ifndef NSInteger
 #if __LP64__ || NS_BUILD_32_LIKE_64
@@ -59,9 +69,6 @@ typedef float CGFloat;
 #define NSAlertStyleCritical NSCriticalAlertStyle
 #endif
 
-void print_to_log_file(const char *format, ...);
-void strip_cr(char *buf);
-
 static double gSS_StartTime = 0.0;
 mach_port_t gEventHandle = 0;
 
@@ -85,6 +92,16 @@ NSPoint gCurrentDelta;
 CGContextRef myContext;
 bool isErased;
 
+static SharedGraphicsController *mySharedGraphicsController;
+static bool runningSharedGraphics;
+static pid_t childPid;
+static char gfxAppPath[MAXPATHLEN];
+static int taskSlot;
+static NSRunningApplication *childApp;
+static double gfxAppStartTime;
+static bool UseSharedOffscreenBuffer(void);
+
+
 #define TEXTBOXMINWIDTH 400.0
 #define MINTEXTBOXHEIGHT 40.0
 #define MAXTEXTBOXHEIGHT 300.0
@@ -93,10 +110,20 @@ bool isErased;
 #define MINDELTA 8
 #define MAXDELTA 16
 
+// On OS 10.13+, assume graphics app is not compatible if no MachO connection after 5 seconds
+#define MAXWAITFORCONNECTION 5.0
+
 int signof(float x) {
     return (x > 0.0 ? 1 : -1);
 }
 
+void launchedGfxApp(char * appPath, pid_t thePID, int slot) {
+    strlcpy(gfxAppPath, appPath, sizeof(gfxAppPath));
+    childPid = thePID;
+    taskSlot = slot;
+    gfxAppStartTime = getDTime();
+}
+
 @implementation BOINC_Saver_ModuleView
 
 - (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview {
@@ -284,6 +311,16 @@ int signof(float x) {
         [ self setAnimationTimeInterval:1/30.0 ];
 #endif
         return;
+    } else {
+        NSWindow *myWindow = [ self window ];
+        NSRect windowFrame = [ myWindow frame ];
+        if ( (windowFrame.origin.x == 0) && (windowFrame.origin.y == 0) ) { // Main screen
+            // On OS 10.13 or later, use MachO comunication and IOSurfaceBuffer to
+            // display the graphics output of our child graphics apps in our window.
+            if (UseSharedOffscreenBuffer() && !mySharedGraphicsController) {
+                mySharedGraphicsController = [[SharedGraphicsController alloc] init:self] ;
+            }
+        }
     }
 
     // For unkown reasons, OS 10.7 Lion screensaver and later delay several seconds
@@ -304,59 +341,108 @@ int signof(float x) {
     if ( (windowFrame.origin.x != 0) || (windowFrame.origin.y != 0) ) {
         // Hide window on second display to aid in debugging
 #ifdef _DEBUG
+        // This technique no longer works on newer versions of OS X
         [ myWindow setLevel:kCGMinimumWindowLevel ];
         NSInteger alpha = 0;
         [ myWindow setAlphaValue:alpha ];   // For OS 10.6
+        [ myWindow orderOut:self];
 #endif
         return;         // We draw only to main screen
     }
 
+	// On OS 10.13 or later, use MachO comunication and IOSurfaceBuffer to
+	// display the graphics output of our child graphics apps in our window.
+    if (runningSharedGraphics) {
+        // Since ScreensaverEngine.app is running in the foreground, our child
+        // graphics app may not get enough CPU cycles for good animation.
+        // Calling [ NSApp activateIgnoringOtherApps:YES ] frequently from the
+        // child doesn't help. But activating our child frequently from the
+        // front process (this screensaver plugin) does appear to guarantee
+        // good animation.
+        //
+        // An alternate approach that also works is to have the child process
+        // tell the kernel it has real time constraints by calling
+        // thread_policy_set() with thread_policy_flavor_t set to
+        // THREAD_TIME_CONSTRAINT_POLICY as described in
+        // <https://developer.apple.com/library/content/technotes/tn2169>.
+        //
+        // But different graphics apps may have different time requirements,
+        // so it is difficult to know the best values to set in the
+        // thread_time_constraint_policy_data_t struct. If the graphics app asks
+        // for too much time, the worker apps will get less time, and if it asks
+        // for too little time the animation won't be smooth.
+        //
+        // So frequently activating the child app here seems to be best.
+        //
+        if (childApp) {
+             [ childApp activateWithOptions:NSApplicationActivateIgnoringOtherApps ];\
+        }
+        isErased = false;
+        return;
+    }
+
     NSRect viewBounds = [self bounds];
 
     newFrequency = getSSMessage(&msg, &coveredFreq);
 
-    // NOTE: My tests seem to confirm that the top window is always the first
-    // window returned by [NSWindow windowNumbersWithOptions:] However, Apple's
-    // documentation is unclear whether we can depend on this.  So I have 
-    // added some safety by doing two things:
-    // [1] Only use the windowNumbersWithOptions test when we have started
-    //     project graphics.
-    // [2] Assume that our window is covered 45 seconds after starting project 
-    //     graphics even if the windowNumbersWithOptions test did not indicate
-    //     that is so.
-    //
-    // getSSMessage() returns a non-zero value for coveredFreq only if we have started 
-    // project graphics.
-    //
-    // If we should use a different frequency when our window is covered by another 
-    // window, then check whether there is a window at a higher z-level than ours.
-
-    // Assuming our window(s) are initially the top window(s), determine our position
-    // in the window list when no graphics applications have covered us.
-    if (gTopWindowListIndex < 0) {
-        NSArray *theWindowList = [NSWindow windowNumbersWithOptions:NSWindowNumberListAllApplications];
-        myWindowNumber = [ myWindow windowNumber ];
-        gTopWindowListIndex = [theWindowList indexOfObjectIdenticalTo:[NSNumber numberWithInt:myWindowNumber]];
-    }
-
-    if (coveredFreq) {
-        if ( (msg != NULL) && (msg[0] != '\0') ) {
+    if (UseSharedOffscreenBuffer()) {
+        // If runningSharedGraphics is still false after MAXWAITFORCONNECTION,
+        // assume graphics app is not compatible with OS 10.13+ and kill it.
+        if (gfxAppStartTime) {
+            if ((getDTime() - gfxAppStartTime)> MAXWAITFORCONNECTION) {
+                incompatibleGfxApp(gfxAppPath, childPid, taskSlot);
+            }
+        }
+    // As of OS 10.13, app windows can no longer appear on top of screensaver
+    // window, but we still use this method on older versions of OS X for
+    // compatibility with older project graphics apps (those which have not
+    // yet been relinked with the updated libboinc_graphics2.a.)
+    } else {
+        // NOTE: My tests seem to confirm that the top window is always the first
+        // window returned by [NSWindow windowNumbersWithOptions:] However, Apple's
+        // documentation is unclear whether we can depend on this.  So I have
+        // added some safety by doing two things:
+        // [1] Only use the windowNumbersWithOptions test when we have started
+        //     project graphics.
+        // [2] Assume that our window is covered 45 seconds after starting project
+        //     graphics even if the windowNumbersWithOptions test did not indicate
+        //     that is so.
+        //
+        // getSSMessage() returns a non-zero value for coveredFreq only if we have started
+        // project graphics.
+        //
+        // If we should use a different frequency when our window is covered by another
+        // window, then check whether there is a window at a higher z-level than ours.
+
+        // Assuming our window(s) are initially the top window(s), determine our position
+        // in the window list when no graphics applications have covered us.
+        if (gTopWindowListIndex < 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();
+            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;
             }
-        } else {
-            newFrequency = coveredFreq;
         }
     }
-
+    
+    // Draw our moving BOINC logo and screensaver status text
+    
     // Clear the previous drawing area
     currentDrawingRect = gMovingRect;
     currentDrawingRect.origin.x = (float) ((int)gCurrentPosition.x);
@@ -464,10 +550,6 @@ int signof(float x) {
             HIThemeGetTextDimensions(cf_msg, (float)gMovingRect.size.width, &textInfo, NULL, &gActualTextBoxHeight, NULL);
             gActualTextBoxHeight += TEXTBOXTOPBORDER;
             
-            // Use only APIs available in Mac OS 10.3.9
-//            HIThemeSetTextFill(kThemeTextColorWhite, NULL, myContext, kHIThemeOrientationNormal);
-//            SetThemeTextColor(kThemeTextColorWhite, 32, true);
-
             CGFloat myWhiteComponents[] = {1.0, 1.0, 1.0, 1.0};
             CGColorSpaceRef myColorSpace = CGColorSpaceCreateDeviceRGB ();
             CGColorRef myTextColor = CGColorCreate(myColorSpace, myWhiteComponents);
@@ -507,6 +589,11 @@ int signof(float x) {
             doBoinc_Sleep(timeToBlock);
         }
     }
+    
+    // Check for a new graphics app sending us data
+    if (UseSharedOffscreenBuffer()) {
+        [mySharedGraphicsController testConnection];
+    }
 }
 
 - (BOOL)hasConfigureSheet {
@@ -649,3 +736,356 @@ Bad:
 }
 
 @end
+
+// On OS 10.13 or later, use MachO comunication and IOSurfaceBuffer to
+// display the graphics output of our child graphics apps in our window.
+// All code past this point is for that implementation.
+
+// Adapted from Apple Developer Tech Support Sample Code MutiGPUIOSurface:
+// <https://developer.apple.com/library/content/samplecode/MultiGPUIOSurface>
+
+#define NUM_IOSURFACE_BUFFERS 2
+
+ at interface SharedGraphicsController()
+{
+	NSMachPort *serverPort;
+	NSMachPort *localPort;
+    
+	uint32_t serverPortName;
+	uint32_t localPortName;
+    
+	int32_t clientIndex;
+	uint32_t nextFrameIndex;
+	
+    NSView *screenSaverView;
+    saverOpenGLView *openGLView;
+    
+	IOSurfaceRef _ioSurfaceBuffers[NUM_IOSURFACE_BUFFERS];
+    mach_port_t _ioSurfaceMachPorts[NUM_IOSURFACE_BUFFERS];
+	GLuint _textureNames[NUM_IOSURFACE_BUFFERS];
+}
+ at end
+
+static bool okToDraw;
+
+ at implementation SharedGraphicsController
+
+- (instancetype)init:(NSView*)saverView {
+    screenSaverView = saverView;
+    
+    [[NSNotificationCenter defaultCenter] addObserver:self 
+	    selector:@selector(portDied:) name:NSPortDidBecomeInvalidNotification object:nil];
+	
+    [self testConnection];
+    
+    return self;
+}
+
+
+- (void) testConnection
+{
+    mach_port_t servicePortNum = MACH_PORT_NULL;
+    kern_return_t machErr;
+    char *portName = "edu.berkeley.boincsaver";
+    
+	// Try to check in with master.
+// NSMachBootstrapServer is deprecated in OS 10.13, so use bootstrap_look_up
+//	serverPort = [(NSMachPort *)([[NSMachBootstrapServer sharedInstance] portForName:@"edu.berkeley.boincsaver"]) retain];
+	machErr = bootstrap_look_up(bootstrap_port, portName, &servicePortNum);
+    if (machErr == KERN_SUCCESS) {
+        serverPort = (NSMachPort*)[NSMachPort portWithMachPort:servicePortNum];
+    } else {
+        serverPort = MACH_PORT_NULL;
+    }
+
+	if(serverPort != MACH_PORT_NULL)
+	{
+		// Create our own local port.
+		localPort = [[NSMachPort alloc] init];
+		
+		// Retrieve raw mach port names.
+		serverPortName = [serverPort machPort];
+		localPortName  = [localPort machPort];
+		
+		// Register our local port with the current runloop.
+		[localPort setDelegate:self];
+		[localPort scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
+		 
+		// Check in with server.
+		int kr;
+		kr = _MGCCheckinClient(serverPortName, localPortName, &clientIndex);
+		if(kr != 0)
+			[NSApp terminate:nil];
+
+        openGLView = [[saverOpenGLView alloc] initWithFrame:[screenSaverView frame]];
+        
+        [screenSaverView addSubview:openGLView];
+
+        runningSharedGraphics = true;
+
+        if (childPid) {
+            gfxAppStartTime = 0.0;
+            childApp = [NSRunningApplication runningApplicationWithProcessIdentifier:childPid];
+        }
+    }
+}
+
+- (void)portDied:(NSNotification *)notification
+{
+	NSPort *port = [notification object];
+	if(port == serverPort) {
+        childApp = nil;
+        gfxAppStartTime = 0.0;
+        gfxAppPath[0] = '\0';
+
+        if ([serverPort isValid]) {
+            [serverPort invalidate];
+//            [serverPort release];
+        }
+        serverPort = nil;
+		[localPort removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
+
+        if ([localPort isValid]) {
+            [localPort invalidate];
+        }
+//        [localPort release];
+        localPort = nil;
+
+        int i;
+        for(i = 0; i < NUM_IOSURFACE_BUFFERS; i++) {
+            if (_ioSurfaceBuffers[i]) {
+                CFRelease(_ioSurfaceBuffers[i]);
+                _ioSurfaceBuffers[i] = nil;
+            }
+
+            // if (glIsTexture(_textureNames[i])) {
+                // glDeleteTextures(1, _textureNames[i]);
+            // }
+            _textureNames[i] = 0;
+            
+            if (_ioSurfaceMachPorts[i] != MACH_PORT_NULL) {
+                mach_port_deallocate(mach_task_self(), _ioSurfaceMachPorts[i]);
+                _ioSurfaceMachPorts[i] = MACH_PORT_NULL;
+            }
+        }
+
+        if ((serverPort == nil) && (localPort == nil)) {
+            runningSharedGraphics = false;
+            [openGLView removeFromSuperview];   // Releases openGLView
+        }
+	}
+}
+- (void)handleMachMessage:(void *)msg
+{
+	union __ReplyUnion___MGCMGSServer_subsystem reply;
+	
+	mach_msg_header_t *reply_header = (void *)&reply;
+	kern_return_t kr;
+	
+	if(MGSServer_server(msg, reply_header) && reply_header->msgh_remote_port != MACH_PORT_NULL)
+	{
+		kr = mach_msg(reply_header, MACH_SEND_MSG, reply_header->msgh_size, 0, MACH_PORT_NULL, 
+			     0, MACH_PORT_NULL);
+        if(kr != 0)
+			[NSApp terminate:nil];
+	}
+}
+
+- (kern_return_t)displayFrame:(int32_t)frameIndex surfacemachport:(mach_port_t)iosurface_port
+{
+	nextFrameIndex = frameIndex;
+
+	if(!_ioSurfaceBuffers[frameIndex])
+	{
+		_ioSurfaceBuffers[frameIndex] = IOSurfaceLookupFromMachPort(iosurface_port);
+        _ioSurfaceMachPorts[frameIndex] = iosurface_port;
+	}
+	if(!_textureNames[frameIndex])
+    {
+		_textureNames[frameIndex] = [openGLView setupIOSurfaceTexture:_ioSurfaceBuffers[frameIndex]];
+    }
+
+    okToDraw = true;    // Tell drawRect that we have real data to display
+
+	[openGLView setNeedsDisplay:YES];
+	[openGLView display];
+
+	return 0;
+}
+
+// For the MachO client, this is a no-op.
+kern_return_t _MGSCheckinClient(mach_port_t server_port, mach_port_t client_port,
+			       int32_t *client_index)
+{
+	return 0;
+}
+
+kern_return_t _MGSDisplayFrame(mach_port_t server_port, int32_t frame_index, mach_port_t iosurface_port)
+{
+	return [mySharedGraphicsController displayFrame:frame_index surfacemachport:iosurface_port];
+}
+
+- (GLuint)currentTextureName
+{
+	return _textureNames[nextFrameIndex];
+}
+
+ at end
+
+ at implementation saverOpenGLView
+
+- (instancetype)initWithFrame:(NSRect)frame {
+    NSOpenGLPixelFormatAttribute	attribs []	=
+    {
+//		NSOpenGLPFAWindow,
+		NSOpenGLPFADoubleBuffer,
+		NSOpenGLPFAAccelerated,
+		NSOpenGLPFANoRecovery,
+		NSOpenGLPFAColorSize,		(NSOpenGLPixelFormatAttribute)32,
+		NSOpenGLPFAAlphaSize,		(NSOpenGLPixelFormatAttribute)8,
+		NSOpenGLPFADepthSize,		(NSOpenGLPixelFormatAttribute)24,
+		(NSOpenGLPixelFormatAttribute) 0
+	};
+
+    NSOpenGLPixelFormat *pix_fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
+
+    if(!pix_fmt)
+       [ NSApp terminate:nil];
+
+	self = [super initWithFrame:frame pixelFormat:pix_fmt];
+
+
+	[[self openGLContext] makeCurrentContext];
+
+    // drawRect is apparently called due to the above code, causing the
+    // screen to flash unless we prevent any actual drawing, so tell
+    // drawRect that we do not yet have real data to display
+    okToDraw = false;
+
+	return self;
+}
+
+- (void)prepareOpenGL
+{
+    [super prepareOpenGL];
+}
+
+- (void)update
+{
+	// Override to do nothing.
+}
+
+// Create an IOSurface backed texture
+- (GLuint)setupIOSurfaceTexture:(IOSurfaceRef)ioSurfaceBuffer
+{
+	GLuint name;
+	CGLContextObj cgl_ctx = (CGLContextObj)[[self openGLContext] CGLContextObj];
+
+	glGenTextures(1, &name);
+	
+	glBindTexture(GL_TEXTURE_RECTANGLE, name);
+    // At the moment, CGLTexImageIOSurface2D requires the GL_TEXTURE_RECTANGLE target
+	CGLTexImageIOSurface2D(cgl_ctx, GL_TEXTURE_RECTANGLE, GL_RGBA, (GLsizei)self.bounds.size.width, (GLsizei)self.bounds.size.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
+					ioSurfaceBuffer, 0);
+
+	glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+	glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);	
+
+	return name;
+}
+
+- (BOOL)isOpaque
+{
+	return YES;
+}
+
+// Render a quad with the the IOSurface backed texture
+- (void)renderTextureFromIOSurfaceWithWidth:(GLsizei)logoWidth height:(GLsizei)logoHeight
+{
+    GLfloat quad[] = {
+        //x, y            s, t
+        (GLfloat)logoWidth, 0.0f,    0.0f, 0.0f,
+        0.0f, (GLfloat)logoHeight,   0.0f, 0.0f,
+        0.0f,  0.0f,     1.0f, 0.0f,
+        0.0f,  0.0f,     0.0f, 1.0f
+    };
+    
+    GLint		saveMatrixMode;
+
+    glGetIntegerv(GL_MATRIX_MODE, &saveMatrixMode);
+    glMatrixMode(GL_TEXTURE);
+    glPushMatrix();
+    glLoadMatrixf(quad);
+    glMatrixMode(saveMatrixMode);
+    
+    glBindTexture(GL_TEXTURE_RECTANGLE, [mySharedGraphicsController currentTextureName]);
+    glEnable(GL_TEXTURE_RECTANGLE);
+    
+    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+	//Draw textured quad
+	glBegin(GL_QUADS);
+		glTexCoord2f(0.0, 0.0);
+		glVertex3f(-1.0, -1.0, 0.0);
+		glTexCoord2f(1.0, 0.0);
+		glVertex3f(1.0, -1.0, 0.0);
+		glTexCoord2f(1.0, 1.0);
+		glVertex3f(1.0, 1.0, 0.0);
+		glTexCoord2f(0.0, 1.0);
+		glVertex3f(-1.0, 1.0, 0.0);
+	glEnd();
+    
+		glDisable(GL_TEXTURE_RECTANGLE);
+		
+		glGetIntegerv(GL_MATRIX_MODE, &saveMatrixMode);
+		glMatrixMode(GL_TEXTURE);
+		glPopMatrix();
+		glMatrixMode(saveMatrixMode);
+
+}
+
+- (void)drawRect:(NSRect)theRect
+{
+    glViewport(0, 0, (GLint)theRect.size.width, (GLint)theRect.size.height);
+
+    glClearColor(0.0, 0.0, 0.0, 0.0);
+
+    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+
+    // drawRect is apparently called before we have real data to display,
+    // causing the screen to flash unless we prevent any actual drawing.
+    if (!okToDraw) {
+        [[self openGLContext] flushBuffer];
+    return;
+}
+
+    // MachO client draws with current IO surface contents as texture
+    [self renderTextureFromIOSurfaceWithWidth:(GLsizei)self.bounds.size.width height:(GLsizei)self.bounds.size.height];
+
+    [[self openGLContext] flushBuffer];
+}
+
+ at end
+
+
+// On OS 10.13 or later, use MachO comunication and IOSurfaceBuffer to
+// display the graphics output of our child graphics apps in our window.
+static bool UseSharedOffscreenBuffer() {
+    static bool alreadyTested = false;
+    static bool needSharedGfxBuffer = false;
+
+//return true;    // FOR TESTING ONLY
+    if (alreadyTested) {
+        return needSharedGfxBuffer;
+    }
+    alreadyTested = true;
+    if (compareOSVersionTo(10, 13) >= 0) {
+        needSharedGfxBuffer = true;
+        return true;
+    }
+    return false;
+}
+
+
diff --git a/clientscr/mac_saver_module.cpp b/clientscr/mac_saver_module.cpp
index ff6dba9..8c67cdb 100644
--- a/clientscr/mac_saver_module.cpp
+++ b/clientscr/mac_saver_module.cpp
@@ -1,6 +1,6 @@
 // This file is part of BOINC.
 // http://boinc.berkeley.edu
-// Copyright (C) 2008 University of California
+// Copyright (C) 2017 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
@@ -106,6 +106,7 @@ const char *  CantLaunchDefaultGFXAppMsg = "Can't launch default screensaver mod
 const char *  DefaultGFXAppCantRPCMsg = "Default screensaver module couldn't connect to BOINC application";
 const char *  DefaultGFXAppCrashedMsg = "Default screensaver module had an unrecoverable error";
 const char *  RunningOnBatteryMsg = "Computing and screensaver disabled while running on battery power.";
+const char *  IncompatibleMsg = " is not compatible with this version of OS X.";
 
 //const char *  BOINCExitedSaverMode = "BOINC is no longer in screensaver mode.";
 
@@ -173,6 +174,62 @@ void closeBOINCSaver() {
 }
 
 
+void incompatibleGfxApp(char * appPath, pid_t pid, int slot){
+    char *p;
+    static char buf[1024];
+    static double msgstartTime = 0.0;
+    int retval;
+    bool gotAppName = false;
+    int exitStatus;
+    
+    if (gspScreensaver) {
+        if (msgstartTime == 0.0) {
+            msgstartTime = getDTime();
+            buf[0] = '\0';
+            
+            if (gspScreensaver->HasProcessExited(pid, exitStatus)) {
+                return;
+            }
+            
+            retval = gspScreensaver->rpc->get_state(gspScreensaver->state);
+            if (!retval) {
+                strlcpy(buf, "Screensaver ", sizeof(buf));
+                for (int i=0; i<gspScreensaver->state.results.size(); i++) {
+                    RESULT* r = gspScreensaver->state.results[i];
+                    if (r->slot == slot) {
+                        if (r->app) {
+                            if (r->app->user_friendly_name[0]) {
+                                strlcat(buf, "of application ", sizeof(buf));
+                                strlcat(buf, r->app->user_friendly_name, sizeof(buf));
+                                gotAppName = true;
+                            }
+                        }
+                    }
+                }
+            } // if (!retval)
+            
+            if (!gotAppName) {
+                p = strrchr(appPath, '/');
+                if (!p) p = appPath;
+                strlcat(buf, "\"", sizeof(buf));
+                strlcat(buf, p+1, sizeof(buf));
+                strlcat(buf, "\"", sizeof(buf));
+            }
+            strlcat(buf, IncompatibleMsg, sizeof(buf));
+            gspScreensaver->setSSMessageText(buf);
+            gspScreensaver->SetError(0, SCRAPPERR_GFXAPPINCOMPATIBLE);
+        }   // End if (msgstartTime == 0.0)
+
+        if (msgstartTime && (getDTime() - msgstartTime > 5.0)) {
+            gspScreensaver->markAsIncompatible(appPath);
+            launchedGfxApp("", 0, -1);
+            msgstartTime = 0.0;
+            gspScreensaver->terminate_screensaver(pid, NULL);
+        }
+    }
+}
+
+
 double getGFXDefaultPeriod() {
     if (gspScreensaver) {
         return gspScreensaver->m_fGFXDefaultPeriod;
@@ -536,6 +593,9 @@ int CScreensaver::getSSMessage(char **theMessage, int* coveredFreq) {
          case SCRAPPERR_DEFAULTGFXAPPCRASHED:
             setSSMessageText(DefaultGFXAppCrashedMsg);
             break;
+            case SCRAPPERR_GFXAPPINCOMPATIBLE:
+                // Message was set in incompatibleGfxApp()
+            break;
         default:
             // m_bErrorMode is TRUE if we should display moving logo (no graphics app is running)
             // m_bErrorMode is FALSE if a graphics app was launched and has not exit
diff --git a/clientscr/screensaver.cpp b/clientscr/screensaver.cpp
index 657e570..0e4b5c7 100644
--- a/clientscr/screensaver.cpp
+++ b/clientscr/screensaver.cpp
@@ -1,6 +1,6 @@
 // This file is part of BOINC.
 // http://boinc.berkeley.edu
-// Copyright (C) 2008 University of California
+// Copyright (C) 2017 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
@@ -28,6 +28,8 @@
 #ifdef __APPLE__
 #include <Carbon/Carbon.h>
 #include <sys/wait.h>
+#include <app_ipc.h>
+#include <malloc/malloc.h>
 #endif
 
 // Common application includes
@@ -83,22 +85,35 @@ bool CScreensaver::is_same_task(RESULT* taska, RESULT* taskb) {
 }
 
 int CScreensaver::count_active_graphic_apps(RESULTS& res, RESULT* exclude) {
-    unsigned int i = 0;
+    int i = 0;
     unsigned int graphics_app_count = 0;
 
     // Count the number of active graphics-capable apps excluding the specified result.
     // If exclude is NULL, don't exclude any results.
     //
-    for (i = 0; i < res.results.size(); i++) {
-        BOINCTRACE(_T("get_random_graphics_app -- active task detected\n"));
+    for (i = res.results.size()-1; i >=0 ; i--) {
+        BOINCTRACE(_T("count_active_graphic_apps -- active task detected\n"));
         BOINCTRACE(
-            _T("get_random_graphics_app -- name = '%s', path = '%s'\n"),
+            _T("count_active_graphic_apps -- name = '%s', path = '%s'\n"),
             res.results[i]->name, res.results[i]->graphics_exec_path
         );
 
         if (!strlen(res.results[i]->graphics_exec_path)) continue;
         if (is_same_task(res.results[i], exclude)) continue;
-        BOINCTRACE(_T("get_random_graphics_app -- active task detected w/graphics\n"));
+#ifdef __APPLE__
+        // Remove it from the vector if incompatible with current version of OS X
+        if (isIncompatible(res.results[i]->graphics_exec_path)) {
+            BOINCTRACE(
+                _T("count_active_graphic_apps -- removing incompatible name = '%s', path = '%s'\n"),
+                res.results[i]->name, res.results[i]->graphics_exec_path
+            );
+            RESULT *rp = res.results[i];
+            res.results.erase(res.results.begin()+i);
+            delete rp;
+            continue;
+        }
+#endif
+        BOINCTRACE(_T("count_active_graphic_apps -- active task detected w/graphics\n"));
 
         graphics_app_count++;
     }
@@ -161,10 +176,38 @@ CLEANUP:
 }
 
 
+#ifdef __APPLE__
+void CScreensaver::markAsIncompatible(char *gfxAppPath) {
+    char *buf = (char *)malloc(strlen(gfxAppPath)+1);
+    if (buf) {
+        strlcpy(buf, gfxAppPath, malloc_size(buf));
+        m_vIncompatibleGfxApps.push_back(buf);
+       BOINCTRACE(_T("markAsIncompatible -- path = '%s'\n"), gfxAppPath);
+    }
+}
+
+bool CScreensaver::isIncompatible(char *appPath) {
+    unsigned int i = 0;
+    for (i = 0; i < m_vIncompatibleGfxApps.size(); i++) {
+        BOINCTRACE(
+            _T("isIncompatible -- comparing incompatible path '%s' to candidate path %s\n"),
+            m_vIncompatibleGfxApps[i], appPath
+        );
+        if (strcmp(m_vIncompatibleGfxApps[i], appPath) == 0) {
+            return true;
+        }
+    }
+    return false;
+}
+
+#endif
+
+
 // Launch a project (science) graphics application
 //
 int CScreensaver::launch_screensaver(RESULT* rp, GFXAPP_ID& graphics_application) {
     int retval = 0;
+    
     if (strlen(rp->graphics_exec_path)) {
         // V6 Graphics
 #ifdef __APPLE__
@@ -191,6 +234,10 @@ int CScreensaver::launch_screensaver(RESULT* rp, GFXAPP_ID& graphics_application
             0,
             graphics_application
         );
+
+    if (graphics_application) {
+        launchedGfxApp(rp->graphics_exec_path, graphics_application, rp->slot);
+    }
 #else
         char* argv[3];
         argv[0] = "app_graphics";   // not used
@@ -245,6 +292,10 @@ int CScreensaver::terminate_v6_screensaver(GFXAPP_ID& graphics_application) {
         thePID
     );
     
+    if (graphics_application) {
+        launchedGfxApp("", 0, -1);
+    }
+    
     for (i=0; i<200; i++) {
         boinc_sleep(0.01);      // Wait 2 seconds max
         // Prevent gfx_switcher from becoming a zombie
@@ -322,6 +373,10 @@ int CScreensaver::launch_default_screensaver(char *dir_path, GFXAPP_ID& graphics
         graphics_application
     );
 
+    if (graphics_application) {
+        launchedGfxApp("boincscr", graphics_application, -1);
+    }
+
     BOINCTRACE(_T("launch_default_screensaver returned %d\n"), retval);
     
 #else
@@ -427,6 +482,7 @@ DataMgmtProcType CScreensaver::DataManagementProc() {
     m_bShow_default_ss_first = false;
 
 #ifdef __APPLE__
+    m_vIncompatibleGfxApps.clear();
     default_ss_dir_path = "/Library/Application Support/BOINC Data";
 #else
     default_ss_dir_path = (char*)m_strBOINCInstallDirectory.c_str();
diff --git a/clientscr/screensaver.h b/clientscr/screensaver.h
index 1e27b9c..5e8a8e4 100644
--- a/clientscr/screensaver.h
+++ b/clientscr/screensaver.h
@@ -1,6 +1,6 @@
 // This file is part of BOINC.
 // http://boinc.berkeley.edu
-// Copyright (C) 2008 University of California
+// Copyright (C) 2017 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
@@ -57,6 +57,7 @@ enum SS_PHASE {
 #define SCRAPPERR_CANTLAUNCHDEFAULTGFXAPP                   0x82000014
 #define SCRAPPERR_DEFAULTGFXAPPCANTCONNECT                  0x82000015
 #define SCRAPPERR_DEFAULTGFXAPPCRASHED                      0x82000016
+#define SCRAPPERR_GFXAPPINCOMPATIBLE                        0x82000017
 
 
 #endif
diff --git a/configure.ac b/configure.ac
index 0faba6c..9bd7cd4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -6,7 +6,7 @@ dnl not sure exactly what the minimum version is (but 2.13 wont work)
 AC_PREREQ(2.58)
 
 dnl Set the BOINC version here.  You can also use the set-version script.
-AC_INIT(BOINC, 7.8.2)
+AC_INIT(BOINC, 7.8.3)
 AC_CONFIG_MACRO_DIR([m4])
 LIBBOINC_VERSION=`echo ${PACKAGE_VERSION} | sed 's/\./:/g'`
 AC_SUBST([LIBBOINC_VERSION])
diff --git a/lib/cc_config.cpp b/lib/cc_config.cpp
index 5add778..8aa9632 100644
--- a/lib/cc_config.cpp
+++ b/lib/cc_config.cpp
@@ -393,7 +393,7 @@ int CC_CONFIG::parse_options(XML_PARSER& xp) {
             ignore_gpu_instance[PROC_TYPE_AMD_GPU].push_back(n);
             continue;
         }
-        if (xp.parse_int("ignore_intel_gpu_dev", n)) {
+        if (xp.parse_int("ignore_intel_dev", n)) {
             ignore_gpu_instance[PROC_TYPE_INTEL_GPU].push_back(n);
             continue;
         }
@@ -619,7 +619,7 @@ int CC_CONFIG::write(MIOFILE& out, LOG_FLAGS& log_flags) {
 
     for (i=0; i<ignore_gpu_instance[PROC_TYPE_INTEL_GPU].size(); ++i) {
         out.printf(
-            "        <ignore_intel_gpu_dev>%d</ignore_intel_gpu_dev>\n",
+            "        <ignore_intel_dev>%d</ignore_intel_dev>\n",
             ignore_gpu_instance[PROC_TYPE_INTEL_GPU][i]
         );
     }
diff --git a/lib/filesys.cpp b/lib/filesys.cpp
index 53c11c8..30fd5b8 100644
--- a/lib/filesys.cpp
+++ b/lib/filesys.cpp
@@ -547,9 +547,7 @@ int boinc_file_exists(const char* path) {
 #ifdef _WIN32
     // don't use _stat64 because it doesn't work with VS2015, XP client
     DWORD dwAttrib = GetFileAttributesA(path);
-    return (dwAttrib != INVALID_FILE_ATTRIBUTES
-        && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)
-    );
+    return dwAttrib != INVALID_FILE_ATTRIBUTES;
 #else
     struct stat buf;
     if (stat(path, &buf)) {
diff --git a/locale/fr/BOINC-Manager.mo b/locale/fr/BOINC-Manager.mo
index ef44b8a..bb67ff8 100644
Binary files a/locale/fr/BOINC-Manager.mo and b/locale/fr/BOINC-Manager.mo differ
diff --git a/version.log b/version.log
index 194172b..bb9f54c 100644
--- a/version.log
+++ b/version.log
@@ -1 +1 @@
-7.8.2
+7.8.3

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-boinc/boinc.git



More information about the pkg-boinc-commits mailing list