From 0a19f387862d12993475e6c1d02f5b1747aae5ec Mon Sep 17 00:00:00 2001 From: Charlie Fenton Date: Fri, 5 Oct 2007 13:28:11 +0000 Subject: [PATCH] V6 SCR: Merge code for Mac and Windows screensavers, adding full functionality for Windows SS. svn path=/trunk/boinc/; revision=13781 --- clientscr/Mac_Saver_Module.h | 225 +++++++ clientscr/mac_saver_module.cpp | 731 +++++++--------------- clientscr/screensaver.cpp | 336 +++++++++- clientscr/screensaver.h | 38 +- clientscr/screensaver_win.cpp | 357 +++++------ clientscr/screensaver_win.h | 39 +- mac_build/boinc.xcodeproj/project.pbxproj | 4 +- 7 files changed, 968 insertions(+), 762 deletions(-) create mode 100755 clientscr/Mac_Saver_Module.h diff --git a/clientscr/Mac_Saver_Module.h b/clientscr/Mac_Saver_Module.h new file mode 100755 index 0000000000..57db3b3e09 --- /dev/null +++ b/clientscr/Mac_Saver_Module.h @@ -0,0 +1,225 @@ +// Berkeley Open Infrastructure for Network Computing +// http://boinc.berkeley.edu +// Copyright (C) 2005 University of California +// +// This 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 2.1 of the License, or (at your option) any later version. +// +// This software 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. +// +// To view the GNU Lesser General Public License visit +// http://www.gnu.org/copyleft/lesser.html +// or write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +// Mac_Saver_Module.h +// BOINC_Saver_Module +// + +#ifndef _SCREENSAVER_MAC_H +#define _SCREENSAVER_MAC_H + +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + +int initBOINCSaver(Boolean ispreview); +int drawGraphics(GrafPtr aPort); +void drawPreview(GrafPtr aPort); +void closeBOINCSaver(void); + +#ifdef __cplusplus +} // extern "C" +#endif + + +//----------------------------------------------------------------------------- +// Name: class CScreensaver +// Desc: Screensaver class +//----------------------------------------------------------------------------- +class CScreensaver +{ +public: + CScreensaver(); + +#ifdef _WIN32 + virtual HRESULT Create( HINSTANCE hInstance ); + HRESULT DisplayErrorMsg( HRESULT hr ); +#elif defined(__APPLE__) + int Create(); +#endif + int Run(); + + + // + // Infrastructure layer + // +protected: +#ifdef _WIN32 + SaverMode ParseCommandLine( TCHAR* pstrCommandLine ); + VOID EnumMonitors( VOID ); + + int UtilGetRegKey(LPCTSTR name, DWORD &keyval); + int UtilSetRegKey(LPCTSTR name, DWORD value); + int UtilGetRegStartupStr(LPCTSTR name, LPTSTR str); + + BOOL IsConfigStartupBOINC(); + + BOOL CreateInfrastructureMutexes(); + + BOOL GetError( BOOL& bErrorMode, HRESULT& hrError, TCHAR* pszError, size_t iErrorSize ); + BOOL SetError( BOOL bErrorMode, HRESULT hrError ); + VOID UpdateErrorBoxText(); + virtual BOOL GetTextForError( HRESULT hr, TCHAR* pszError, DWORD dwNumChars ); + + + // Variables for non-fatal error management + HANDLE m_hErrorManagementMutex; + BOOL m_bErrorMode; // Whether to display an error + HRESULT m_hrError; // Error code to display + TCHAR m_szError[400]; // Error message text + + BOOL m_bBOINCConfigChecked; + BOOL m_bBOINCStartupConfigured; + DWORD m_dwBlankScreen; + DWORD m_dwBlankTime; +#elif defined(__APPLE__) + OSStatus initBOINCApp(void); + int GetBrandID(void); + char* PersistentFGets(char *buf, size_t buflen, FILE *f); + pid_t FindProcessPID(char* name, pid_t thePID); + OSErr GetpathToBOINCManagerApp(char* path, int maxLen); + bool SetError( bool bErrorMode, int hrError ); + void setBannerText(const char *msg, GrafPtr aPort); + void updateBannerText(char *msg, GrafPtr aPort); + void drawBanner(GrafPtr aPort); + void print_to_log_file(const char *format, ...); + void strip_cr(char *buf); + char m_gfx_Switcher_Path[MAXPATHLEN]; + bool m_bErrorMode; // Whether to display an error + unsigned int m_hrError; // Error code to display + + bool m_wasAlreadyRunning; + pid_t m_CoreClientPID; + int m_dwBlankScreen; + time_t m_dwBlankTime; +#endif + + + // + // Data management layer + // +protected: + bool CreateDataManagementThread(); + bool DestoryDataManagementThread(); + +#ifdef _WIN32 + DWORD WINAPI DataManagementProc(); + static DWORD WINAPI DataManagementProcStub( LPVOID lpParam ); + int launch_screensaver(RESULT* rp, HANDLE& graphics_application); + int terminate_screensaver(HANDLE& graphics_application, RESULT *worker_app); + HANDLE m_hDataManagementThread; + HANDLE m_hGraphicsApplication; +#elif defined(__APPLE__) + void* DataManagementProc(); + static void* DataManagementProcStub( void* param ); + int terminate_screensaver(int& graphics_application, RESULT *worker_app); + int launch_screensaver(RESULT* rp, int& graphics_application); + void HandleRPCError(void); + OSErr KillScreenSaver(void); + pthread_t m_hDataManagementThread; + pid_t m_hGraphicsApplication; +#endif + +// Determine if two RESULT pointers refer to the same task + bool is_same_task(RESULT* taska, RESULT* taskb); + +// Count the number of active graphics-capable apps + int count_active_graphic_apps(RESULTS& results, RESULT* exclude = NULL); + +// Choose a ramdom graphics application from the vector that +// was passed in. + + RESULT* get_random_graphics_app(RESULTS& results, RESULT* exclude = NULL); + + RPC_CLIENT *rpc; + CC_STATE state; + RESULTS results; + RESULT m_running_result; + bool m_updating_results; + int m_iLastResultShown; + time_t m_tLastResultChangeTime; + + bool m_bResetCoreState; + bool m_QuitDataManagementProc; + + + // + // Presentation layer + // +#ifdef _WIN32 +protected: + HRESULT CreateSaverWindow(); + VOID UpdateErrorBox(); + VOID InterruptSaver(); + VOID ChangePassword(); + + VOID DoConfig(); + HRESULT DoSaver(); + VOID DoPaint( HWND hwnd, HDC hdc, LPPAINTSTRUCT lpps ); + + void DrawTransparentBitmap(HDC hdc, HBITMAP hBitmap, LONG xStart, LONG yStart, COLORREF cTransparentColor); + + LRESULT SaverProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); + INT_PTR ConfigureDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); + + static LRESULT CALLBACK SaverProcStub( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); + static INT_PTR CALLBACK ConfigureDialogProcStub( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); + void ShutdownSaver(); +#elif defined(__APPLE__) + char m_MsgBuf[2048]; + char m_BannerText[2048]; + int m_BannerWidth; + StringPtr m_CurrentBannerMessage; + char* m_BrandText; +public: + int drawGraphics(GrafPtr aPort); + void drawPreview(GrafPtr aPort); + void ShutdownSaver(); +#endif + +protected: +#ifdef _WIN32 + SaverMode m_SaverMode; // sm_config, sm_full, sm_preview, etc. + BOOL m_bAllScreensSame; // If TRUE, show same image on all screens + HWND m_hWnd; // Focus window and device window on primary + HWND m_hWndParent; + HINSTANCE m_hInstance; + BOOL m_bWaitForInputIdle; // Used to pause when preview starts + DWORD m_dwSaverMouseMoveCount; + BOOL m_bIs9x; + BOOL m_bCheckingSaverPassword; + BOOL m_bWindowed; + + INTERNALMONITORINFO m_Monitors[MAX_DISPLAYS]; + DWORD m_dwNumMonitors; + RECT m_rcRenderTotal; // Rect of entire area to be rendered + RECT m_rcRenderCurDevice; // Rect of render area of current device + BOOL m_bPaintingInitialized; + + TCHAR m_strWindowTitle[200]; // Title for the app's window + + DWORD m_dwLastInputTimeAtStartup; +#endif +}; + +#endif diff --git a/clientscr/mac_saver_module.cpp b/clientscr/mac_saver_module.cpp index 435c5b41d0..4ed6c59c3c 100755 --- a/clientscr/mac_saver_module.cpp +++ b/clientscr/mac_saver_module.cpp @@ -38,34 +38,14 @@ #include "gui_rpc_client.h" #include "common_defs.h" #include "util.h" +#include "Mac_Saver_Module.h" #include "screensaver.h" - + //#include #ifdef __cplusplus extern "C" { #endif -void setFrame(Rect *frame); -int initBOINCSaver(Boolean ispreview); -OSStatus initBOINCApp(void); -int drawGraphics(GrafPtr aPort); -void drawPreview(GrafPtr aPort); -void closeBOINCSaver(void); -void setBannerText(const char *msg, GrafPtr aPort); -void updateBannerText(char *msg, GrafPtr aPort); -void drawBanner(GrafPtr aPort); -int GetBrandID(void); -void FlashIcon(void); -OSErr FindBOINCApplication(FSSpecPtr applicationFSSpecPtr); -pid_t FindProcessPID(char* name, pid_t thePID); -OSErr GetpathToBOINCManagerApp(char* path, int maxLen); -OSStatus RPCThread(void* param); -void HandleRPCError(void); -OSErr KillScreenSaver(void); -int Mac_launch_screensaver(RESULT* rp, int& graphics_application); -int Mac_terminate_screensaver(int& graphics_application, RESULT *worker_app, RPC_CLIENT* rpc); - - #ifdef __cplusplus } // extern "C" #endif @@ -102,7 +82,6 @@ void strip_cr(char *buf); #define NOBANNERFREQUENCY 4 /* Times per second to call drawGraphics if no banner */ #define STATUSUPDATEINTERVAL 5 /* seconds between status display updates */ #define TASK_RUN_CHECK_PERIOD 5 /* Seconds between safety check that task is actually running */ -#define GFX_CHANGE_PERIOD 600 /* if > 1 CPUs, change screensaver every 600 secs */ enum SaverState { SaverState_Idle, @@ -117,36 +96,19 @@ enum SaverState { }; +static CScreensaver* gspScreensaver = NULL; + extern int gGoToBlank; // True if we are to blank the screen extern int gBlankingTime; // Delay in minutes before blanking the screen extern CFStringRef gPathToBundleResources; -static time_t SaverStartTime; -static Boolean wasAlreadyRunning = false; -static pid_t CoreClientPID = nil; -static char msgBuf[2048], bannerText[2048]; -static int bannerWidth; static SaverState saverState = SaverState_Idle; -static StringPtr CurrentBannerMessage = 0; -static DISPLAY_INFO di; // Contains all NULLs for the Macintosh -static RPC_CLIENT *rpc; -MPQueueID gTerminationQueue; // This queue will report the completion of our threads -MPTaskID gRPC_thread_id; // IDs of the thread we create -int gClientSaverStatus = 0; // status determined by RPCThread -Boolean gQuitRPCThread = false; // Flag to tell RPC thread to exit gracefully -int gQuitCounter = 0; -long gBrandId = 0; -char * gBrandText = "BOINC"; +// int gQuitCounter = 0; RGBColor gBrandColor = {0xFFFF, 0xFFFF, 0xFFFF}; RGBColor gTextColor = {0xFFFF, 0xFFFF, 0xFFFF}; RGBColor gWhiteTextColor = {0xFFFF, 0xFFFF, 0xFFFF}; RGBColor gOrangeTextColor = {0xFFFF, 0x6262, 0x0000}; RGBColor gGrayTextColor = {0x9999, 0x9999, 0x9999}; -char gfx_Switcher_Path[MAXPATHLEN]; - -// Display first status update after 5 seconds -static int statusUpdateCounter = ((STATUSUPDATEINTERVAL-5) * BANNERFREQUENCY); -Boolean gStatusMessageUpdated = false; const char * CantLaunchCCMsg = "Unable to launch BOINC application."; const char * LaunchingCCMsg = "Launching BOINC application."; @@ -162,16 +124,59 @@ const char * BOINCTestmodeMsg = "BOINC screensaver is running, but cannot displ // Returns desired Animation Frequency (per second) or 0 for no change int initBOINCSaver(Boolean ispreview) { + if (ispreview) + return 8; + + gspScreensaver = new CScreensaver(); + + return gspScreensaver->Create(); +} + +int drawGraphics(GrafPtr aPort) { + return gspScreensaver->drawGraphics(aPort); +}; + + +void drawPreview(GrafPtr aPort) { + gspScreensaver->drawPreview(aPort); +}; + + +void closeBOINCSaver() { + gspScreensaver->ShutdownSaver(); +} + +CScreensaver::CScreensaver() { + + m_dwBlankScreen = 0; + m_dwBlankTime = 0; + m_bErrorMode = false; + m_hrError = 0; + + saverState = SaverState_Idle; + m_wasAlreadyRunning = false; + m_CoreClientPID = nil; + m_MsgBuf[0] = 0; + setBannerText(0, NULL); + m_BannerWidth = 0; + m_CurrentBannerMessage = 0; + m_QuitDataManagementProc = false; + m_BrandText = "BOINC"; + m_updating_results = false; + + m_hDataManagementThread = NULL; + m_hGraphicsApplication = NULL; + m_bResetCoreState = TRUE; + rpc = 0; +} + + +int CScreensaver::Create() { int newFrequency = 15; ProcessSerialNumber psn; ProcessInfoRec pInfo; OSStatus err; - if (ispreview) - return 8; - - setBannerText(0, NULL); - // Ugly workaround for a problem with the System Preferences app // For an unknown reason, when this screensaver is run using the // Test button in the System Prefs Screensaver control panel, the @@ -189,35 +194,27 @@ int initBOINCSaver(Boolean ispreview) { saverState = SaverState_ControlPanelTestMode; } - gBrandId = GetBrandID(); - switch(gBrandId) { - case 1: - gBrandText = "GridRepublic"; - gBrandColor = gOrangeTextColor; // Orange - gTextColor = gGrayTextColor; // Gray - break; - default: - gBrandText = "BOINC"; - gBrandColor = gWhiteTextColor; // White - gTextColor = gWhiteTextColor; // White - break; - } - // If there are multiple displays, initBOINCSaver may get called // multiple times (once for each display), so we need to guard // against launching multiple instances of the core client if (saverState == SaverState_Idle) { - SaverStartTime = time(0); - - CFStringGetCString(gPathToBundleResources, gfx_Switcher_Path, sizeof(gfx_Switcher_Path), kCFStringEncodingMacRoman); - strlcat(gfx_Switcher_Path, "/gfx_switcher", sizeof(gfx_Switcher_Path)); + // Calculate the estimated blank time by adding the starting + // time and and the user-specified time which is in minutes + m_dwBlankScreen = gGoToBlank; + if (gGoToBlank) + m_dwBlankTime = time(0) + (gBlankingTime * 60); + else + m_dwBlankTime = 0; + + CFStringGetCString(gPathToBundleResources, m_gfx_Switcher_Path, sizeof(m_gfx_Switcher_Path), kCFStringEncodingMacRoman); + strlcat(m_gfx_Switcher_Path, "/gfx_switcher", sizeof(m_gfx_Switcher_Path)); err = initBOINCApp(); if (saverState == SaverState_LaunchingCoreClient) { - gClientSaverStatus = 0; - gQuitRPCThread = false; + SetError(FALSE, 0); + m_QuitDataManagementProc = false; if (rpc == NULL) rpc = new RPC_CLIENT; newFrequency = NOBANNERFREQUENCY; @@ -227,32 +224,47 @@ int initBOINCSaver(Boolean ispreview) { } -OSStatus initBOINCApp() { +OSStatus CScreensaver::initBOINCApp() { char boincPath[2048]; pid_t myPid; int status; OSStatus err; static int retryCount = 0; + long brandId = 0; saverState = SaverState_CantLaunchCoreClient; - CoreClientPID = FindProcessPID("boinc", 0); - if (CoreClientPID) { - wasAlreadyRunning = true; + m_CoreClientPID = FindProcessPID("boinc", 0); + if (m_CoreClientPID) { + m_wasAlreadyRunning = true; saverState = SaverState_LaunchingCoreClient; return noErr; } - wasAlreadyRunning = false; + m_wasAlreadyRunning = false; if (++retryCount > 3) // Limit to 3 relaunches to prevent thrashing return -1; + brandId = GetBrandID(); + switch(brandId) { + case 1: + m_BrandText = "GridRepublic"; + gBrandColor = gOrangeTextColor; // Orange + gTextColor = gGrayTextColor; // Gray + break; + default: + m_BrandText = "BOINC"; + gBrandColor = gWhiteTextColor; // White + gTextColor = gWhiteTextColor; // White + break; + } + err = GetpathToBOINCManagerApp(boincPath, sizeof(boincPath)); if (err) { // If we couldn't find BOINCManager.app, try default path strcpy(boincPath, "/Applications/"); - if (gBrandId) - strcat(boincPath, gBrandText); + if (brandId) + strcat(boincPath, m_BrandText); else strcat(boincPath, "BOINCManager"); strcat(boincPath, ".app"); @@ -283,7 +295,7 @@ OSStatus initBOINCApp() { fflush(NULL); _exit(127); // execl error (execl should never return) } else { - CoreClientPID = myPid; // make this available globally + m_CoreClientPID = myPid; // make this available globally saverState = SaverState_LaunchingCoreClient; } @@ -292,12 +304,21 @@ OSStatus initBOINCApp() { // Returns new desired Animation Frequency (per second) or 0 for no change -int drawGraphics(GrafPtr aPort) { +int CScreensaver::drawGraphics(GrafPtr aPort) { + // Display first status update after 5 seconds + static int statusUpdateCounter = ((STATUSUPDATEINTERVAL-5) * BANNERFREQUENCY); CGrafPtr savePort; GDHandle saveGDH; int newFrequency = 15; pid_t myPid; + int iResultCount; + int iIndex; + unsigned int len; + RESULT* theResult; + PROJECT* pProject; + char statusBuf[256]; + double percent_done; OSStatus err; ObscureCursor(); @@ -310,102 +331,108 @@ int drawGraphics(GrafPtr aPort) { break; case SaverState_LaunchingCoreClient: - if (wasAlreadyRunning) + if (m_wasAlreadyRunning) setBannerText(ConnectingCCMsg, aPort); else setBannerText(LaunchingCCMsg, aPort); - myPid = FindProcessPID(NULL, CoreClientPID); + myPid = FindProcessPID(NULL, m_CoreClientPID); if (myPid) { saverState = SaverState_CoreClientRunning; rpc->init(NULL); // Initialize communications with Core Client // Set up a separate thread for communicating with Core Client - if (!MPLibraryIsLoaded()) { - saverState = SaverState_UnrecoverableError; - break; - } - - if (gTerminationQueue == NULL) { - err = MPCreateQueue(&gTerminationQueue); /* Create the queue which will report the completion of the task. */ - if (err) { - saverState = SaverState_UnrecoverableError; - break; - } - } - err = MPCreateTask(RPCThread, /* This is the task function. */ - 0, /* This is the parameter to the task function. */ - (50*1024), /* Stack size for thread. */ - gTerminationQueue, /* We'll use this to sense task completion. */ - 0, /* We won't use the first part of the termination message. */ - 0, /* We won't use the second part of the termination message. */ - 0, /* Use the normal task options. (Currently this *must* be zero!) */ - &gRPC_thread_id); /* Here's where the ID of the new task will go. */ - - if (err) { - MPDeleteQueue(gTerminationQueue); - saverState = SaverState_UnrecoverableError; - break; - } + CreateDataManagementThread(); // ToDo: Add a timeout after which we display error message } else // Take care of the possible race condition where the Core Client was in the // process of shutting down just as ScreenSaver started, so initBOINCApp() // found it already running but now it has shut down. - if (wasAlreadyRunning) // If we launched it, then just wait for it to start + if (m_wasAlreadyRunning) // If we launched it, then just wait for it to start saverState = SaverState_RelaunchCoreClient; break; case SaverState_CoreClientRunning: - // RPC called in RPCThread() + // RPC called in DataManagementProc() setBannerText(ConnectingCCMsg, aPort); + if (! m_bResetCoreState) { + saverState = SaverState_ConnectedToCoreClient; + } break; case SaverState_ConnectedToCoreClient: - switch (gClientSaverStatus) { + switch (m_hrError) { case 0: - break; // No status response yet from RPCThread - case SS_STATUS_BLANKED: + break; // No status response yet from DataManagementProc + case SCRAPPERR_SCREENSAVERBLANKED: default: setBannerText(0, aPort); // No text message break; - case SS_STATUS_BOINCSUSPENDED: + case SCRAPPERR_BOINCSUSPENDED: setBannerText(BOINCSuspendedMsg, aPort); break; - case SS_STATUS_NOAPPSEXECUTING: + case SCRAPPERR_BOINCNOAPPSEXECUTING: setBannerText(BOINCNoAppsExecutingMsg, aPort); break; - case SS_STATUS_NOPROJECTSDETECTED: + case SCRAPPERR_BOINCNOPROJECTSDETECTED: setBannerText(BOINCNoProjectsDetectedMsg, aPort); break; - case SS_STATUS_ENABLED: + case SCRAPPERR_SCREENSAVERRUNNING: #if ! ALWAYS_DISPLAY_PROGRESS_TEXT setBannerText(0, aPort); // No text message // Let the science app draw over our window break; #endif - case SS_STATUS_NOGRAPHICSAPPSEXECUTING: - if (msgBuf[0] == 0) { - strcpy(msgBuf, BOINCNoGraphicAppsExecutingMsg); - setBannerText(msgBuf, aPort); + case SCRAPPERR_BOINCNOGRAPHICSAPPSEXECUTING: + if (m_MsgBuf[0] == 0) { + strcpy(m_MsgBuf, BOINCNoGraphicAppsExecutingMsg); + setBannerText(m_MsgBuf, aPort); } - if (gStatusMessageUpdated) { - updateBannerText(msgBuf, aPort); - gStatusMessageUpdated = false; - } - // Handled in RPCThread() + + if ( (statusUpdateCounter >= (STATUSUPDATEINTERVAL * BANNERFREQUENCY) ) && !m_updating_results ) { + statusUpdateCounter = 0; + + strcpy(m_MsgBuf, BOINCNoGraphicAppsExecutingMsg); + + iResultCount = results.results.size(); + theResult = NULL; + for (iIndex = 0; iIndex < iResultCount; iIndex++) { + theResult = results.results.at(iIndex); + + // The get_state rpc is time-consuming, so we assume the list of + // attached projects does not change while the screensaver is active. + pProject = state.lookup_project(theResult->project_url); + if (pProject != NULL) { + percent_done = theResult->fraction_done * 100; + if (percent_done < 0.01) + len = sprintf(statusBuf, (" %s"), pProject->project_name.c_str()); + else // Display percent_done only after we have a valid value + len = sprintf(statusBuf, (" %s: %.2f%%"), + pProject->project_name.c_str(), percent_done); + + strlcat(m_MsgBuf, statusBuf, sizeof(m_MsgBuf)); + } // end if (pProject != NULL) +// else { // (pProject += NULL): re-synch with client +// goto Get_CC_State; // Should never happen +// } + } // end for() loop + + setBannerText(m_MsgBuf, aPort); + updateBannerText(m_MsgBuf, aPort); + } // end if (statusUpdateCounter > time to update) + break; #if 0 - case SS_STATUS_QUIT: + case SCRAPPERR_QUITSCREENSAVERREQUESTED: // setBannerText(BOINCExitedSaverMode, aPort); - // Wait 1 second to allow Give ScreenSaver engine to close us down + // Wait 1 second to allow ScreenSaver engine to close us down if (++gQuitCounter > (bannerText[0] ? BANNERFREQUENCY : NOBANNERFREQUENCY)) { closeBOINCSaver(); KillScreenSaver(); // Stop the ScreenSaver Engine } break; #endif - } // end switch (gClientSaverStatus) + } // end switch (m_hrError) break; case SaverState_ControlPanelTestMode: @@ -424,7 +451,7 @@ int drawGraphics(GrafPtr aPort) { break; // Should never get here; fixes compiler warning } // end switch (saverState) - if (bannerText[0]) { + if (m_BannerText[0]) { GetGWorld(&savePort, &saveGDH); SetPort(aPort); drawBanner(aPort); @@ -437,7 +464,7 @@ int drawGraphics(GrafPtr aPort) { } -void drawPreview(GrafPtr aPort) { +void CScreensaver::drawPreview(GrafPtr aPort) { SetPort(aPort); setBannerText(" BOINC", aPort); drawBanner(aPort); @@ -447,13 +474,8 @@ void drawPreview(GrafPtr aPort) { // If there are multiple displays, closeBOINCSaver may get called // multiple times (once for each display), so we need to guard // against any problems that may cause. -void closeBOINCSaver() { - gQuitRPCThread = true; // Tell RPC Thread to exit - if (gTerminationQueue) { // Wait up to 5 seconds for RPC thread to exit - MPWaitOnQueue(gTerminationQueue, NULL, NULL, NULL, kDurationMillisecond*5000); - MPDeleteQueue(gTerminationQueue); - gTerminationQueue = NULL; - } +void CScreensaver::ShutdownSaver() { + DestoryDataManagementThread(); if (rpc) { #if 0 // OS X calls closeBOINCSaver() when energy saver puts display @@ -463,7 +485,7 @@ void closeBOINCSaver() { // Also, under sandbox security, screensaver doesn't have access // to rpc password in gui_rpc_auth.cfg file, so core client won't // accept rpc->quit from screensaver. - if (CoreClientPID && (!wasAlreadyRunning)) { + if (m_CoreClientPID && (!m_wasAlreadyRunning)) { rpc->quit(); // Kill core client if we launched it } #endif @@ -473,271 +495,23 @@ void closeBOINCSaver() { setBannerText(0, NULL); - CoreClientPID = 0; - gQuitCounter = 0; - wasAlreadyRunning = false; - gQuitRPCThread = false; + m_CoreClientPID = 0; +// gQuitCounter = 0; + m_wasAlreadyRunning = false; + m_QuitDataManagementProc = false; saverState = SaverState_Idle; } -OSStatus RPCThread(void* param) { - int retval = 0; - CC_STATE state; - int suspend_reason = 0; - AbsoluteTime timeToUnblock; - time_t time_to_blank; - pid_t graphics_app_pid = 0; - char statusBuf[256]; - unsigned int len; - RESULTS results; - RESULT* theResult = NULL; - RESULT* graphics_app_result_ptr = NULL; - PROJECT* pProject; - RESULT previous_result; - RESULT* previous_result_ptr = NULL; - int iResultCount = 0; - int iIndex = 0; - double percent_done; - double launch_time = 0.0; - double last_change_time = 0.0; - double last_run_check_time = 0.0; - - // Calculate the estimated blank time by adding the starting - // time and and the user-specified time which is in minutes - if (gGoToBlank) - time_to_blank = SaverStartTime + (gBlankingTime * 60); - else - time_to_blank = 0; - - while (true) { - if ((gGoToBlank) && (time(0) > time_to_blank)) { - gClientSaverStatus = SS_STATUS_BLANKED; - gQuitRPCThread = true; - } - - if (gQuitRPCThread) { // If main thread has requested we exit - if (graphics_app_pid || graphics_app_result_ptr) { - Mac_terminate_screensaver(graphics_app_pid, graphics_app_result_ptr, rpc); - graphics_app_result_ptr = NULL; - graphics_app_pid = 0; - graphics_app_result_ptr = NULL; - previous_result_ptr = NULL; - } - MPExit(noErr); // Exit the thread - } - - timeToUnblock = AddDurationToAbsolute(durationSecond/2, UpTime()); - MPDelayUntil(&timeToUnblock); - - // Try and get the current state of the CC -//Get_CC_State: - retval = rpc->get_state(state); - MPYield(); - if (retval == noErr) - break; - - // CC may not yet be running - HandleRPCError(); - } - - saverState = SaverState_ConnectedToCoreClient; - - while (true) { - if ((gGoToBlank) && (time(0) > time_to_blank)) { - gClientSaverStatus = SS_STATUS_BLANKED; - gQuitRPCThread = true; - } - - if (gQuitRPCThread) { // If main thread has requested we exit - if (graphics_app_pid || graphics_app_result_ptr) { - Mac_terminate_screensaver(graphics_app_pid, graphics_app_result_ptr, rpc); - graphics_app_result_ptr = NULL; - graphics_app_pid = 0; - graphics_app_result_ptr = NULL; - previous_result_ptr = NULL; - } - MPExit(noErr); // Exit the thread - } - - timeToUnblock = AddDurationToAbsolute(durationSecond/2, UpTime()); - MPDelayUntil(&timeToUnblock); - - retval = rpc->get_screensaver_tasks(suspend_reason, results); - MPYield(); - if (retval) { - // rpc call returned error - HandleRPCError(); - continue; - } - - if (suspend_reason != 0) { - gClientSaverStatus = SS_STATUS_BOINCSUSPENDED; - if (graphics_app_pid || graphics_app_result_ptr) { - Mac_terminate_screensaver(graphics_app_pid, graphics_app_result_ptr, rpc); - if (graphics_app_pid == 0) { - graphics_app_result_ptr = NULL; - } else { - // waitpid test will clear graphics_app_pid and graphics_app_result_ptr - } - previous_result_ptr = NULL; - } - continue; - } - -#if SIMULATE_NO_GRAPHICS /* FOR TESTING */ - - gClientSaverStatus = SS_STATUS_NOGRAPHICSAPPSEXECUTING; - -#else /* NORMAL OPERATION */ - - // Is the current graphics app's associated task still running? - if ((graphics_app_pid) || (graphics_app_result_ptr)) { - - iResultCount = results.results.size(); - graphics_app_result_ptr = NULL; - - // Find the current task in the new results vector (if it still exists) - for (iIndex = 0; iIndex < iResultCount; iIndex++) { - theResult = results.results.at(iIndex); - - if (is_same_task(theResult, previous_result_ptr)) { - graphics_app_result_ptr = theResult; - previous_result = *theResult; - previous_result_ptr = &previous_result; - break; - } - } - - // V6 graphics only: if worker application has stopped running, Mac_terminate_screensaver - if ((graphics_app_result_ptr == NULL) && (graphics_app_pid != 0)) { -// if (previous_result_ptr) print_to_log_file("%s finished", previous_result.name.c_str()); - Mac_terminate_screensaver(graphics_app_pid, previous_result_ptr, rpc); - previous_result_ptr = NULL; - // waitpid test will clear graphics_app_pid - } -#if 0 - // Safety check that task is actually running - if (last_run_check_time && ((dtime() - last_run_check_time) > TASK_RUN_CHECK_PERIOD)) { - if (FindProcessPID(NULL, ? ? ?) == 0) { - Mac_terminate_screensaver(graphics_app_pid, graphics_app_result_ptr, rpc); - if (graphics_app_pid == 0) { - graphics_app_result_ptr = NULL; - // Save previous_result and previous_result_ptr for get_random_graphics_app() call - } else { - // waitpid test will clear graphics_app_pid and graphics_app_result_ptr - } - previous_result_ptr = NULL; - } - } -#endif - if (last_change_time && ((dtime() - last_change_time) > GFX_CHANGE_PERIOD)) { - if (count_active_graphic_apps(results, previous_result_ptr) > 0) { -// if (previous_result_ptr) print_to_log_file("time to change: %s", previous_result.name.c_str()); - Mac_terminate_screensaver(graphics_app_pid, graphics_app_result_ptr, rpc); - if (graphics_app_pid == 0) { - graphics_app_result_ptr = NULL; - // Save previous_result and previous_result_ptr for get_random_graphics_app() call - } else { - // waitpid test will clear graphics_app_pid and graphics_app_result_ptr - } - } - last_change_time = dtime(); - } - } - - // If no current graphics app, pick an active task at random and launch its graphics app - if ((graphics_app_pid == 0) && (graphics_app_result_ptr == NULL)) { - graphics_app_result_ptr = get_random_graphics_app(results, previous_result_ptr); - previous_result_ptr = NULL; - - if (graphics_app_result_ptr) { - retval = Mac_launch_screensaver(graphics_app_result_ptr, graphics_app_pid); - if (retval) { - graphics_app_pid = 0; - previous_result_ptr = NULL; - graphics_app_result_ptr = NULL; - } else { - gClientSaverStatus = SS_STATUS_ENABLED; - launch_time = dtime(); - last_change_time = launch_time; - last_run_check_time = launch_time; - // Make a local copy of current result, since original pointer - // may have been freed by the time we perform later tests - previous_result = *graphics_app_result_ptr; - previous_result_ptr = &previous_result; -// if (previous_result_ptr) print_to_log_file("launching %s", previous_result.name.c_str()); - } - } else { - if (state.projects.size() == 0) { - // We are not attached to any projects - gClientSaverStatus = SS_STATUS_NOPROJECTSDETECTED; - } else if (results.results.size() == 0) { - // We currently do not have any applications to run - gClientSaverStatus = SS_STATUS_NOAPPSEXECUTING; - } else { - // We currently do not have any graphics capable application - gClientSaverStatus = SS_STATUS_NOGRAPHICSAPPSEXECUTING; - } - } - } else { // End if ((graphics_app_pid == 0) && (graphics_app_result_ptr == NULL)) - // Is the graphics app still running? - if (graphics_app_pid) { - if (waitpid(graphics_app_pid, 0, WNOHANG) == graphics_app_pid) { - graphics_app_pid = 0; - graphics_app_result_ptr = NULL; - continue; - } - } - } -#endif // ! SIMULATE_NO_GRAPHICS - - if ((gClientSaverStatus == SS_STATUS_NOGRAPHICSAPPSEXECUTING) -#if ALWAYS_DISPLAY_PROGRESS_TEXT - || (gClientSaverStatus == SS_STATUS_ENABLED) -#endif - ) - { - if (statusUpdateCounter >= (STATUSUPDATEINTERVAL * BANNERFREQUENCY) ) { - statusUpdateCounter = 0; - - if (! gStatusMessageUpdated) { - strcpy(msgBuf, BOINCNoGraphicAppsExecutingMsg); - - iResultCount = results.results.size(); - - for (iIndex = 0; iIndex < iResultCount; iIndex++) { - theResult = results.results.at(iIndex); - - // The get_state rpc is time-consuming, so we assume the list of - // attached projects does not change while the screensaver is active. - pProject = state.lookup_project(theResult->project_url); - if (pProject != NULL) { - percent_done = theResult->fraction_done * 100; - if (percent_done < 0.01) - len = sprintf(statusBuf, (" %s"), pProject->project_name.c_str()); - else // Display percent_done only after we have a valid value - len = sprintf(statusBuf, (" %s: %.2f%%"), - pProject->project_name.c_str(), percent_done); - - strlcat(msgBuf, statusBuf, sizeof(msgBuf)); - } // end if (pProject != NULL) -// else { // (pProject += NULL): re-synch with client -// goto Get_CC_State; // Should never happen -// } - } // end for() loop - gStatusMessageUpdated = true; - - } // end if (! gStatusMessageUpdated) - - } // end if (statusUpdateCounter > time to update) - } // end if SS_STATUS_NOGRAPHICSAPPSEXECUTING - } // end while(true) - return noErr; // should never get here; it fixes compiler warning +// This function forwards to DataManagementProc, which has access to the +// "this" pointer. +// +void * CScreensaver::DataManagementProcStub(void* param) { + return gspScreensaver->DataManagementProc(); } -void HandleRPCError() { +void CScreensaver::HandleRPCError() { // Attempt to restart BOINC Client if needed, reinitialize the RPC client and state rpc->close(); @@ -747,30 +521,60 @@ void HandleRPCError() { // care of that and other situations where the Core Client quits unexpectedy. if (FindProcessPID("boinc", 0) == 0) { saverState = SaverState_RelaunchCoreClient; - MPExit(noErr); // Exit the thread + m_bResetCoreState = true; } rpc->init(NULL); // Otherwise just reinitialize the RPC client and state and keep trying // Error message after timeout? } - -void setBannerText(const char * msg, GrafPtr aPort) { - if (msg == 0) - bannerText[0] = 0; +bool CScreensaver::CreateDataManagementThread() { + int retval; - if ((char *)CurrentBannerMessage != msg) + if (m_hDataManagementThread == NULL) { + retval = pthread_create(&m_hDataManagementThread, NULL, DataManagementProcStub, 0); + if (retval) { + saverState = SaverState_UnrecoverableError; + return false; + } + } + return true; +} + + +bool CScreensaver::DestoryDataManagementThread() { + m_QuitDataManagementProc = true; // Tell DataManagementProc thread to exit + if (m_hDataManagementThread) { // Wait for DataManagementProc thread to exit + pthread_join(m_hDataManagementThread, NULL); + m_hDataManagementThread = NULL; + } + return true; +} + + +bool CScreensaver::SetError(bool bErrorMode, int hrError) { + m_bErrorMode = bErrorMode; + m_hrError = hrError; + return true; +} + + +void CScreensaver::setBannerText(const char * msg, GrafPtr aPort) { + if (msg == 0) + m_BannerText[0] = 0; + + if ((char *)m_CurrentBannerMessage != msg) updateBannerText((char *)msg, aPort); } -void updateBannerText(char *msg, GrafPtr aPort) { +void CScreensaver::updateBannerText(char *msg, GrafPtr aPort) { CGrafPtr savePort; RGBColor saveBackColor; Rect wRect; char *p, *s; - CurrentBannerMessage = (StringPtr)msg; + m_CurrentBannerMessage = (StringPtr)msg; if (aPort == NULL) return; @@ -789,28 +593,28 @@ void updateBannerText(char *msg, GrafPtr aPort) { TextSize(24); TextFace(bold); s = msg; - bannerText[0] = '\0'; + m_BannerText[0] = '\0'; do { p = strstr(s, "BOINC"); if (p == NULL) { - strcat(bannerText, s); + strcat(m_BannerText, s); } else { - strncat(bannerText, s, p - s); - strcat(bannerText, gBrandText); + strncat(m_BannerText, s, p - s); + strcat(m_BannerText, m_BrandText); s = p + 5; // s = p + strlen("BOINC"); } } while (p); - bannerWidth = TextWidth(bannerText, 0, strlen(bannerText)) + BANNER_GAP; - // Round up bannerWidth to an integral multiple of BANNERDELTA - bannerWidth = ((bannerWidth + BANNERDELTA - 1) / BANNERDELTA) * BANNERDELTA; + m_BannerWidth = TextWidth(m_BannerText, 0, strlen(m_BannerText)) + BANNER_GAP; + // Round up m_BannerWidth to an integral multiple of BANNERDELTA + m_BannerWidth = ((m_BannerWidth + BANNERDELTA - 1) / BANNERDELTA) * BANNERDELTA; } SetPort(savePort); } -void drawBanner(GrafPtr aPort) { +void CScreensaver::drawBanner(GrafPtr aPort) { CGrafPtr savePort; GDHandle saveGDH; RGBColor saveForeColor, saveBackColor; @@ -831,7 +635,7 @@ void drawBanner(GrafPtr aPort) { RGBForeColor(&gTextColor); BackColor(blackColor); GetPortBounds(aPort, &wRect); - if ( (bannerPos + bannerWidth) <= (wRect.left + BANNERDELTA) ) + if ( (bannerPos + m_BannerWidth) <= (wRect.left + BANNERDELTA) ) bannerPos = wRect.left; else bannerPos -= BANNERDELTA; @@ -846,21 +650,21 @@ void drawBanner(GrafPtr aPort) { do { MoveTo(x, y); - s = bannerText; + s = m_BannerText; do { - p = strstr(s, gBrandText); + p = strstr(s, m_BrandText); if (p == NULL) { DrawText(s, 0, strlen(s)); } else { DrawText(s, 0, p - s); RGBForeColor(&gBrandColor); - DrawText(gBrandText, 0, strlen(gBrandText)); - s = p + strlen(gBrandText); + DrawText(m_BrandText, 0, strlen(m_BrandText)); + s = p + strlen(m_BrandText); RGBForeColor(&gTextColor); } } while (p); - x+= bannerWidth; + x+= m_BannerWidth; } while (x < wRect.right); RGBForeColor(&saveForeColor); @@ -870,7 +674,7 @@ void drawBanner(GrafPtr aPort) { } -int GetBrandID() +int CScreensaver::GetBrandID() { char buf[1024]; long iBrandId; @@ -896,7 +700,7 @@ int GetBrandID() } -static char * PersistentFGets(char *buf, size_t buflen, FILE *f) { +char * CScreensaver::PersistentFGets(char *buf, size_t buflen, FILE *f) { char *p = buf; size_t len = buflen; size_t datalen = 0; @@ -915,7 +719,7 @@ static char * PersistentFGets(char *buf, size_t buflen, FILE *f) { } -pid_t FindProcessPID(char* name, pid_t thePID) +pid_t CScreensaver::FindProcessPID(char* name, pid_t thePID) { FILE *f; char buf[1024]; @@ -951,7 +755,7 @@ pid_t FindProcessPID(char* name, pid_t thePID) } -OSErr GetpathToBOINCManagerApp(char* path, int maxLen) +OSErr CScreensaver::GetpathToBOINCManagerApp(char* path, int maxLen) { CFStringRef bundleID = CFSTR("edu.berkeley.boinc"); OSType creator = 'BNC!'; @@ -967,7 +771,7 @@ OSErr GetpathToBOINCManagerApp(char* path, int maxLen) // Send a Quit AppleEvent to the process which called this module // (i.e., tell the ScreenSaver engine to quit) -OSErr KillScreenSaver() { +OSErr CScreensaver::KillScreenSaver() { ProcessSerialNumber thisPSN; pid_t thisPID; OSErr err = noErr; @@ -980,88 +784,7 @@ OSErr KillScreenSaver() { } -// Launch the graphics application -// -int Mac_launch_screensaver(RESULT* rp, pid_t& graphics_application) -{ - int retval = 0; - - if (!rp->graphics_exec_path.empty()) { - // V6 Graphics - // For unknown reasons, the graphics application exits with - // "RegisterProcess failed (error = -50)" unless we pass its - // full path twice in the argument list to execv. - char* argv[5]; - argv[0] = "gfx_Switcher_Path"; - argv[1] = "-launch_gfx"; - argv[2] = strrchr(rp->slot_path.c_str(), '/'); - if (*argv[2]) argv[2]++; // Point to the slot number in ascii - - argv[3] = "--fullscreen"; - argv[4] = 0; - - retval = run_program( - rp->slot_path.c_str(), - gfx_Switcher_Path, - 4, - argv, - 0, - graphics_application - ); - } else { - // V5 and Older - DISPLAY_INFO di; - - memset(di.window_station, 0, sizeof(di.window_station)); - memset(di.desktop, 0, sizeof(di.desktop)); - memset(di.display, 0, sizeof(di.display)); - - graphics_application = 0; - - retval = rpc->show_graphics( - rp->project_url.c_str(), - rp->name.c_str(), - MODE_FULLSCREEN, - di - ); - } - return retval; -} - - -int Mac_terminate_screensaver(int& graphics_application, RESULT *worker_app, RPC_CLIENT* rpc) { - int retval = 0; - char current_dir[MAXPATHLEN]; - char gfx_pid[16]; - pid_t dont_care; - - retval = terminate_screensaver(graphics_application, worker_app, rpc); - - if (graphics_application == 0) return retval; - - sprintf(gfx_pid, "%d", graphics_application); - getcwd( current_dir, sizeof(current_dir)); - - char* argv[4]; - argv[0] = "gfx_switcher"; - argv[1] = "-kill_gfx"; - argv[2] = gfx_pid; - argv[3] = 0; - - retval = run_program( - current_dir, - gfx_Switcher_Path, - 3, - argv, - 0, - dont_care - ); - - return retval; -} - - -void print_to_log_file(const char *format, ...) { +void CScreensaver::print_to_log_file(const char *format, ...) { #if CREATE_LOG FILE *f; va_list args; @@ -1093,7 +816,7 @@ void print_to_log_file(const char *format, ...) { } #if CREATE_LOG -void strip_cr(char *buf) +void CScreensaver::strip_cr(char *buf) { char *theCR; diff --git a/clientscr/screensaver.cpp b/clientscr/screensaver.cpp index 335e33f4b0..f87c0d9c79 100644 --- a/clientscr/screensaver.cpp +++ b/clientscr/screensaver.cpp @@ -20,22 +20,44 @@ #ifdef _WIN32 #include "boinc_win.h" + +#include +#include +#include +#include + +#include "boinc_ss.h" + #endif #include "diagnostics.h" #include "common_defs.h" #include "util.h" + +#ifdef _WIN32 #include "gui_rpc_client.h" +#include "screensaver_win.h" +#endif + +#ifdef __APPLE__ +#include +#include "gui_rpc_client.h" +#include "Mac_Saver_Module.h" +#endif #include "screensaver.h" -bool is_same_task(RESULT* taska, RESULT* taskb) { +// Flags for testing & debugging +#define SIMULATE_NO_GRAPHICS 0 + + +bool CScreensaver::is_same_task(RESULT* taska, RESULT* taskb) { if ((taska == NULL) || (taskb == NULL)) return false; if (taska->name != taskb->name) return false; if (taska->project_url != taskb->project_url) return false; return true; } -int count_active_graphic_apps(RESULTS& results, RESULT* exclude) { +int CScreensaver::count_active_graphic_apps(RESULTS& results, RESULT* exclude) { unsigned int i = 0; unsigned int graphics_app_count = 0; @@ -62,7 +84,7 @@ int count_active_graphic_apps(RESULTS& results, RESULT* exclude) { // Exclude the specified result unless it is the only candidate. // If exclude is NULL or an empty string, don't exclude any results. // -RESULT* get_random_graphics_app(RESULTS& results, RESULT* exclude) { +RESULT* CScreensaver::get_random_graphics_app(RESULTS& results, RESULT* exclude) { RESULT* rp = NULL; unsigned int i = 0; unsigned int graphics_app_count = 0; @@ -113,14 +135,39 @@ CLEANUP: // Launch the graphics application // #ifdef _WIN32 -int launch_screensaver(RESULT* rp, HANDLE& graphics_application, RPC_CLIENT* rpc) +int CScreensaver::launch_screensaver(RESULT* rp, HANDLE& graphics_application) #else -int launch_screensaver(RESULT* rp, int& graphics_application, RPC_CLIENT* rpc) +int CScreensaver::launch_screensaver(RESULT* rp, int& graphics_application) #endif { int retval = 0; if (!rp->graphics_exec_path.empty()) { // V6 Graphics +#ifdef __APPLE__ + // For sandbox security, use gfx_switcher to launch gfx app + // as user boinc_project and group boinc_project. + // + // For unknown reasons, the graphics application exits with + // "RegisterProcess failed (error = -50)" unless we pass its + // full path twice in the argument list to execv. + char* argv[5]; + argv[0] = "gfx_Switcher"; + argv[1] = "-launch_gfx"; + argv[2] = strrchr(rp->slot_path.c_str(), '/'); + if (*argv[2]) argv[2]++; // Point to the slot number in ascii + + argv[3] = "--fullscreen"; + argv[4] = 0; + + retval = run_program( + rp->slot_path.c_str(), + m_gfx_Switcher_Path, + 4, + argv, + 0, + graphics_application + ); +#else char* argv[3]; argv[0] = "app_graphics"; // not used argv[1] = "--fullscreen"; @@ -133,18 +180,19 @@ int launch_screensaver(RESULT* rp, int& graphics_application, RPC_CLIENT* rpc) 0, graphics_application ); +#endif } else { // V5 and Older DISPLAY_INFO di; -#ifdef __WXMSW__ +#ifdef _WIN32 graphics_application = NULL; - memset(di.window_station, 0, sizeof(window_station)); + memset(di.window_station, 0, sizeof(di.window_station)); memset(di.desktop, 0, sizeof(di.desktop)); memset(di.display, 0, sizeof(di.display)); - if (wxWIN95 != wxGetOsVersion(NULL, NULL)) { + if (!m_bIs9x) { // Retrieve the current window station and desktop names GetUserObjectInformation( GetProcessWindowStation(), @@ -181,15 +229,43 @@ int launch_screensaver(RESULT* rp, int& graphics_application, RPC_CLIENT* rpc) // Terminate the graphics application // #ifdef _WIN32 -int terminate_screensaver(HANDLE& graphics_application, RESULT *worker_app, RPC_CLIENT* rpc) +int CScreensaver::terminate_screensaver(HANDLE& graphics_application, RESULT *worker_app) #else -int terminate_screensaver(int& graphics_application, RESULT *worker_app, RPC_CLIENT* rpc) +int CScreensaver::terminate_screensaver(int& graphics_application, RESULT *worker_app) #endif { if (graphics_application) { // V6 Graphics kill_program(graphics_application); +#ifdef __APPLE + // For sandbox security, use gfx_switcher to launch gfx app + // as user boinc_project and group boinc_project. + int retval = 0; + char current_dir[MAXPATHLEN]; + char gfx_pid[16]; + pid_t dont_care; + + sprintf(gfx_pid, "%d", graphics_application); + getcwd( current_dir, sizeof(current_dir)); + + char* argv[4]; + argv[0] = "gfx_switcher"; + argv[1] = "-kill_gfx"; + argv[2] = gfx_pid; + argv[3] = 0; + + retval = run_program( + current_dir, + m_gfx_Switcher_Path, + 3, + argv, + 0, + dont_care + ); + return retval; +#endif + graphics_application = 0; } else { // V5 and Older DISPLAY_INFO di; @@ -210,3 +286,243 @@ int terminate_screensaver(int& graphics_application, RESULT *worker_app, RPC_CLI return 0; } + +#ifdef _WIN32 +DWORD WINAPI CScreensaver::DataManagementProc() { +#else +void *CScreensaver::DataManagementProc() { +#endif + int retval = 0; + int suspend_reason = 0; + RESULT* theResult = NULL; + RESULT* graphics_app_result_ptr = NULL; + RESULT previous_result; + RESULT* previous_result_ptr = NULL; + int iResultCount = 0; + int iIndex = 0; + double launch_time = 0.0; + double last_change_time = 0.0; + double last_run_check_time = 0.0; +#ifdef _WIN32 + time_t m_tThreadCreateTime = 0; + + BOINCTRACE(_T("CScreensaver::DataManagementProc - Display screen saver loading message\n")); + SetError(TRUE, SCRAPPERR_BOINCSCREENSAVERLOADING); + m_tThreadCreateTime = time(0); + + // Set the starting point for iterating through the results + m_iLastResultShown = 0; + m_tLastResultChangeTime = 0; +#endif + + while (true) { + if ((m_dwBlankScreen) && (time(0) > m_dwBlankTime)) { + BOINCTRACE(_T("CScreensaver::DataManagementProc - Time to blank\n")); + SetError(FALSE, SCRAPPERR_SCREENSAVERBLANKED); + m_QuitDataManagementProc = true; + } + + if (m_QuitDataManagementProc) { // If main thread has requested we exit + if (m_hGraphicsApplication || graphics_app_result_ptr) { + terminate_screensaver(m_hGraphicsApplication, graphics_app_result_ptr); + graphics_app_result_ptr = NULL; + m_hGraphicsApplication = 0; + graphics_app_result_ptr = NULL; + previous_result_ptr = NULL; + } + +#ifdef _WIN32 + TerminateThread(m_hDataManagementThread, 0); +#else + pthread_exit(0); // Exit the thread +#endif + } + + boinc_sleep(0.5); + + if (m_bResetCoreState) { + // Try and get the current state of the CC + retval = rpc->get_state(state); + if (retval) { + // CC may not yet be running + HandleRPCError(); + continue; + } + + m_bResetCoreState = false; + } + + BOINCTRACE(_T("CScreensaver::DataManagementProc - ErrorMode = '%d', ErrorCode = '%x'\n"), m_bErrorMode, m_hrError); + + if ((m_dwBlankScreen) && (time(0) > m_dwBlankTime)) { + SetError(FALSE, SCRAPPERR_SCREENSAVERBLANKED); + m_QuitDataManagementProc = true; + } + + if (m_QuitDataManagementProc) { // If main thread has requested we exit + if (m_hGraphicsApplication || graphics_app_result_ptr) { + terminate_screensaver(m_hGraphicsApplication, graphics_app_result_ptr); + graphics_app_result_ptr = NULL; + m_hGraphicsApplication = 0; + graphics_app_result_ptr = NULL; + previous_result_ptr = NULL; + } +#ifdef _WIN32 + TerminateThread(m_hDataManagementThread, 0); +#else + pthread_exit(0); // Exit the thread +#endif + } + + boinc_sleep(0.25); + + m_updating_results = true; + retval = rpc->get_screensaver_tasks(suspend_reason, results); + m_updating_results = false; + boinc_sleep(0.25); + if (retval) { + // rpc call returned error + HandleRPCError(); + m_bResetCoreState = true; + continue; + } + + if (suspend_reason != 0) { + SetError(TRUE, SCRAPPERR_BOINCSUSPENDED); + if (m_hGraphicsApplication || graphics_app_result_ptr) { + terminate_screensaver(m_hGraphicsApplication, graphics_app_result_ptr); + if (m_hGraphicsApplication == 0) { + graphics_app_result_ptr = NULL; + } else { + // waitpid test will clear m_hGraphicsApplication and graphics_app_result_ptr + } + previous_result_ptr = NULL; + } + continue; + } + +#if SIMULATE_NO_GRAPHICS /* FOR TESTING */ + + SetError(TRUE, SCRAPPERR_BOINCNOGRAPHICSAPPSEXECUTING); + +#else /* NORMAL OPERATION */ + + // Is the current graphics app's associated task still running? + if ((m_hGraphicsApplication) || (graphics_app_result_ptr)) { + + iResultCount = results.results.size(); + graphics_app_result_ptr = NULL; + + // Find the current task in the new results vector (if it still exists) + for (iIndex = 0; iIndex < iResultCount; iIndex++) { + theResult = results.results.at(iIndex); + + if (is_same_task(theResult, previous_result_ptr)) { + graphics_app_result_ptr = theResult; + previous_result = *theResult; + previous_result_ptr = &previous_result; + break; + } + } + + // V6 graphics only: if worker application has stopped running, terminate_screensaver + if ((graphics_app_result_ptr == NULL) && (m_hGraphicsApplication != 0)) { +// if (previous_result_ptr) print_to_log_file("%s finished", previous_result.name.c_str()); + terminate_screensaver(m_hGraphicsApplication, previous_result_ptr); + previous_result_ptr = NULL; + // waitpid test will clear m_hGraphicsApplication + } +#if 0 + // Safety check that task is actually running + if (last_run_check_time && ((dtime() - last_run_check_time) > TASK_RUN_CHECK_PERIOD)) { + if (FindProcessPID(NULL, ? ? ?) == 0) { + terminate_screensaver(m_hGraphicsApplication, graphics_app_result_ptr); + if (m_hGraphicsApplication == 0) { + graphics_app_result_ptr = NULL; + // Save previous_result and previous_result_ptr for get_random_graphics_app() call + } else { + // waitpid test will clear m_hGraphicsApplication and graphics_app_result_ptr + } + previous_result_ptr = NULL; + } + } +#endif + if (last_change_time && ((dtime() - last_change_time) > GFX_CHANGE_PERIOD)) { + if (count_active_graphic_apps(results, previous_result_ptr) > 0) { +// if (previous_result_ptr) print_to_log_file("time to change: %s", previous_result.name.c_str()); + terminate_screensaver(m_hGraphicsApplication, graphics_app_result_ptr); + if (m_hGraphicsApplication == 0) { + graphics_app_result_ptr = NULL; + // Save previous_result and previous_result_ptr for get_random_graphics_app() call + } else { + // waitpid test will clear m_hGraphicsApplication and graphics_app_result_ptr + } + } + last_change_time = dtime(); + } + } + + // If no current graphics app, pick an active task at random and launch its graphics app + if ((m_hGraphicsApplication == 0) && (graphics_app_result_ptr == NULL)) { + graphics_app_result_ptr = get_random_graphics_app(results, previous_result_ptr); + previous_result_ptr = NULL; + + if (graphics_app_result_ptr) { + retval = launch_screensaver(graphics_app_result_ptr, m_hGraphicsApplication); + if (retval) { + m_hGraphicsApplication = 0; + previous_result_ptr = NULL; + graphics_app_result_ptr = NULL; + } else { + SetError(FALSE, SCRAPPERR_SCREENSAVERRUNNING); + launch_time = dtime(); + last_change_time = launch_time; + last_run_check_time = launch_time; + // Make a local copy of current result, since original pointer + // may have been freed by the time we perform later tests + previous_result = *graphics_app_result_ptr; + previous_result_ptr = &previous_result; +// if (previous_result_ptr) print_to_log_file("launching %s", previous_result.name.c_str()); + } + } else { + if (state.projects.size() == 0) { + // We are not attached to any projects + SetError(TRUE, SCRAPPERR_BOINCNOPROJECTSDETECTED); + } else if (results.results.size() == 0) { + // We currently do not have any applications to run + SetError(TRUE, SCRAPPERR_BOINCNOAPPSEXECUTING); + } else { + // We currently do not have any graphics capable application + SetError(TRUE, SCRAPPERR_BOINCNOGRAPHICSAPPSEXECUTING); + } + } + } else { // End if ((m_hGraphicsApplication == 0) && (graphics_app_result_ptr == NULL)) + // Is the graphics app still running? + if (m_hGraphicsApplication) { +#ifdef _WIN32 + DWORD dwStatus = STILL_ACTIVE; + BOOL bRetVal = FALSE; + bRetVal = GetExitCodeProcess(m_hGraphicsApplication, &dwStatus); + BOINCTRACE(_T("CScreensaver::DataManagementProc - GetExitCodeProcess RetVal = '%d', Status = '%d'\n"), bRetVal, dwStatus); + if (bRetVal && (dwStatus != STILL_ACTIVE)) { + // Something has happened to the previously selected screensaver + // application. Start a different one. + m_hGraphicsApplication = 0; + graphics_app_result_ptr = NULL; + continue; + } else { + CheckForegroundWindow(); + } +#else + if (waitpid(m_hGraphicsApplication, 0, WNOHANG) == m_hGraphicsApplication) { + m_hGraphicsApplication = 0; + graphics_app_result_ptr = NULL; + continue; + } +#endif + } + } +#endif // ! SIMULATE_NO_GRAPHICS + } // end while(true) +// return noErr; // should never get here; it fixes compiler warning +} diff --git a/clientscr/screensaver.h b/clientscr/screensaver.h index 6c2e468c82..64f0353163 100644 --- a/clientscr/screensaver.h +++ b/clientscr/screensaver.h @@ -21,28 +21,28 @@ #ifndef __SCREENSAVER_H__ #define __SCREENSAVER_H__ -// Determine if two RESULT pointers refer to the same task -extern bool is_same_task(RESULT* taska, RESULT* taskb); -// Count the number of active graphics-capable apps -extern int count_active_graphic_apps(RESULTS& results, RESULT* exclude = NULL); +#define GFX_CHANGE_PERIOD 600 /* if > 1 CPUs, change screensaver every 600 secs */ -// Choose a ramdom graphics application from the vector that -// was passed in. -extern RESULT* get_random_graphics_app(RESULTS& results, RESULT* exclude = NULL); -// Launch the screensaver -#ifdef _WIN32 -extern int launch_screensaver(RESULT* rp, HANDLE& graphics_application, RPC_CLIENT* rpc); -#else -extern int launch_screensaver(RESULT* rp, int& graphics_application, RPC_CLIENT* rpc); -#endif +//----------------------------------------------------------------------------- +// Error / status codes +//----------------------------------------------------------------------------- -// Terminate the screensaver -#ifdef _WIN32 -extern int terminate_screensaver(HANDLE& graphics_application, RESULT *worker_app, RPC_CLIENT* rpc); -#else -extern int terminate_screensaver(int& graphics_application, RESULT *worker_app, RPC_CLIENT* rpc); -#endif +#define SCRAPPERR_BOINCNOTDETECTED 0x82000001 +#define SCRAPPERR_BOINCNOTDETECTEDSTARTUP 0x82000002 +#define SCRAPPERR_BOINCSUSPENDED 0x82000003 +#define SCRAPPERR_BOINCNOTGRAPHICSCAPABLE 0x82000004 +#define SCRAPPERR_BOINCNOAPPSEXECUTING 0x82000005 +#define SCRAPPERR_BOINCNOPROJECTSDETECTED 0x82000006 +#define SCRAPPERR_BOINCNOGRAPHICSAPPSEXECUTING 0x82000007 +#define SCRAPPERR_BOINCSCREENSAVERLOADING 0x82000008 +#define SCRAPPERR_BOINCAPPFOUNDGRAPHICSLOADING 0x82000009 +#define SCRAPPERR_BOINCSHUTDOWNEVENT 0x8200000a +#define SCRAPPERR_NOPREVIEW 0x8200000f +#define SCRAPPERR_DAEMONALLOWSNOGRAPHICS 0x82000010 +#define SCRAPPERR_SCREENSAVERRUNNING 0x82000011 +#define SCRAPPERR_SCREENSAVERBLANKED 0x82000012 +#define SCRAPPERR_QUITSCREENSAVERREQUESTED 0x82000013 #endif \ No newline at end of file diff --git a/clientscr/screensaver_win.cpp b/clientscr/screensaver_win.cpp index ede88f7ba6..c257c69ac7 100755 --- a/clientscr/screensaver_win.cpp +++ b/clientscr/screensaver_win.cpp @@ -27,9 +27,9 @@ #include #include #include -#define COMPILE_MULTIMON_STUBS -#include #include +#include +#define COMPILE_MULTIMON_STUBS #include "boinc_ss.h" #include "diagnostics.h" @@ -185,10 +185,11 @@ CScreensaver::CScreensaver() { m_dwBlankScreen = 0; m_dwBlankTime = 0; + rpc = 0; m_hDataManagementThread = NULL; m_hGraphicsApplication = NULL; - m_bScreensaverStarted = FALSE; m_bResetCoreState = TRUE; + m_QuitDataManagementProc = FALSE; m_bBOINCConfigChecked = FALSE; m_bBOINCStartupConfigured = FALSE; memset(&m_running_result, 0, sizeof(m_running_result)); @@ -197,6 +198,7 @@ CScreensaver::CScreensaver() { m_dwNumMonitors = 0; m_dwLastInputTimeAtStartup = 0; + m_tThreadCreateTime = 0; } @@ -261,7 +263,9 @@ HRESULT CScreensaver::Create(HINSTANCE hInstance) { return E_FAIL; } - // Create the screen saver window(s) + if (rpc == NULL) rpc = new RPC_CLIENT; + + // Create the screen saver window(s) if (m_SaverMode == sm_preview || m_SaverMode == sm_full ) { @@ -862,9 +866,7 @@ BOOL CScreensaver::SetError(BOOL bErrorMode, HRESULT hrError) { // Update the error message // VOID CScreensaver::UpdateErrorBoxText() { - RESULTS results; PROJECT* pProject; - TCHAR szBuffer[256]; bool bIsActive = false; bool bIsExecuting = false; bool bIsDownloaded = false; @@ -872,9 +874,9 @@ VOID CScreensaver::UpdateErrorBoxText() { size_t iIndex = 0; - // Load error string - GetTextForError(m_hrError, m_szError, sizeof(m_szError) / sizeof(TCHAR)); if (SCRAPPERR_BOINCNOGRAPHICSAPPSEXECUTING == m_hrError) { + if (m_updating_results) return; // results vector is currently being updated by rpc + iResultCount = results.results.size(); int iModIndex; for (iIndex = 0; iIndex < iResultCount; iIndex++) { @@ -911,6 +913,9 @@ VOID CScreensaver::UpdateErrorBoxText() { } } m_szError[ sizeof(m_szError) -1 ] = '\0'; + } else { + // Load error string + GetTextForError(m_hrError, m_szError, sizeof(m_szError) / sizeof(TCHAR)); } BOINCTRACE(_T("CScreensaver::UpdateErrorBoxText - Updated Text '%s'\n"), m_szError); } @@ -974,6 +979,8 @@ BOOL CScreensaver::GetTextForError( // BOOL CScreensaver::CreateDataManagementThread() { DWORD dwThreadID = 0; + BOINCTRACE(_T("CScreensaver::CreateDataManagementThread Start\n")); + m_QuitDataManagementProc = FALSE; m_hDataManagementThread = CreateThread( NULL, // default security attributes 0, // use default stack size @@ -995,9 +1002,18 @@ BOOL CScreensaver::CreateDataManagementThread() { // Terminate the thread that is used to talk to the daemon. // BOOL CScreensaver::DestoryDataManagementThread() { - if (!TerminateThread(m_hDataManagementThread, 0)) { - BOINCTRACE(_T("CScreensaver::DestoryDataManagementThread: Failed to terminate data management thread '%d'\n"), GetLastError()); - return FALSE; + m_QuitDataManagementProc = TRUE; // Tell RPC Thread to exit + + // Wait up to 5 seconds for DataManagementThread to exit + for (int i=0; i< 20; i++) { + DWORD dwStatus = STILL_ACTIVE; + BOOL bRetVal = FALSE; + + bRetVal = GetExitCodeThread(m_hDataManagementThread, &dwStatus); + BOINCTRACE(_T("CScreensaver::DestoryDataManagementThread - GetExitCodeThread RetVal = '%d', Status = '%d'\n"), bRetVal, dwStatus); + if (bRetVal && (dwStatus != STILL_ACTIVE)) { + break; + } } return TRUE; } @@ -1005,203 +1021,6 @@ BOOL CScreensaver::DestoryDataManagementThread() { -// Do what needs to be done to update the text that is displayed -// to the user -// -DWORD WINAPI CScreensaver::DataManagementProc() { - BOOL bForegroundWindowIsScreensaver; - HWND hwndBOINCGraphicsWindow = NULL; - HWND hwndForeWindow = NULL; - HWND hwndForeParent = NULL; - DWORD iMonitor = 0; - int iReturnValue = 0; - int iSuspendReason = 0; - time_t tThreadCreateTime = 0; - bool bScreenSaverStarting = false; - INTERNALMONITORINFO* pMonitorInfo = NULL; - - - BOINCTRACE(_T("CScreensaver::DataManagementProc - Display screen saver loading message\n")); - SetError(TRUE, SCRAPPERR_BOINCSCREENSAVERLOADING); - tThreadCreateTime = time(0); - - // Set the starting point for iterating through the results - m_iLastResultShown = 0; - m_tLastResultChangeTime = 0; - - while(1) { - bScreenSaverStarting = (3 >= (time(0) - tThreadCreateTime)); - - BOINCTRACE(_T("CScreensaver::DataManagementProc - ErrorMode = '%d', ErrorCode = '%x'\n"), m_bErrorMode, m_hrError); - - - // Lets try and get the current state of the CC - if (m_bResetCoreState) { - iReturnValue = rpc.get_state(state); - if (0 == iReturnValue) { - m_bResetCoreState = FALSE; - } - - BOINCTRACE(_T("CScreensaver::DataManagementProc - get_state iReturnValue = '%d'\n"), iReturnValue); - } - - - iReturnValue = rpc.get_screensaver_tasks(iSuspendReason, results); - BOINCTRACE(_T("CScreensaver::DataManagementProc - get_screensaver_tasks iReturnValue = '%d'\n"), iReturnValue); - if (0 != iReturnValue) { - // Attempt to reinitialize the RPC client and state - rpc.close(); - rpc.init(NULL); - m_bResetCoreState = TRUE; - - if (!m_bBOINCConfigChecked) { - m_bBOINCConfigChecked = TRUE; - m_bBOINCStartupConfigured = IsConfigStartupBOINC(); - } - - if (!bScreenSaverStarting) { - if (m_bBOINCStartupConfigured) { - SetError(TRUE, SCRAPPERR_BOINCNOTDETECTED); - } else { - SetError(TRUE, SCRAPPERR_BOINCNOTDETECTEDSTARTUP); - } - } - - } else { - - // Reset the error state. - SetError(FALSE, 0); - - // Start the screensaver if it hasn't been started already. - if (!m_bScreensaverStarted) { - - // Choose a random graphics application to start. - RESULT* rp = get_random_graphics_app(results); - - if (rp) { - int retval = launch_screensaver(rp, m_hGraphicsApplication, &rpc); - BOINCTRACE(_T("CScreensaver::DataManagementProc - launch_screensaver RetVal = '%d', m_hGraphicsApplication = '%d'\n"), retval, m_hGraphicsApplication); - if (!retval) { - m_bScreensaverStarted = TRUE; - m_running_result = *rp; - } - } else { - if (state.projects.size() == 0) { - // We are not attached to any projects - SetError(TRUE, SCRAPPERR_BOINCNOPROJECTSDETECTED); - } else if (results.results.size() == 0) { - // We currently do not have any applications to run - SetError(TRUE, SCRAPPERR_BOINCNOAPPSEXECUTING); - } else { - // We currently do not have any graphics capable application - SetError(TRUE, SCRAPPERR_BOINCNOGRAPHICSAPPSEXECUTING); - } - } - } else { - // Is the graphics app still running? - DWORD dwStatus = STILL_ACTIVE; - BOOL bRetVal = FALSE; - bRetVal = GetExitCodeProcess(m_hGraphicsApplication, &dwStatus); - BOINCTRACE(_T("CScreensaver::DataManagementProc - GetExitCodeProcess RetVal = '%d', Status = '%d'\n"), bRetVal, dwStatus); - if (bRetVal && (dwStatus != STILL_ACTIVE)) { - // Something has happened to the previously selected screensaver - // application. Start a different one. - m_bScreensaverStarted = FALSE; - } else { - // When running in screensaver mode the only two valid conditions for z-order - // is that either the screensaver or graphics application is the foreground - // application. If this is not true, then blow out of the screensaver. - hwndBOINCGraphicsWindow = FindWindow(BOINC_WINDOW_CLASS_NAME, NULL); - if (hwndBOINCGraphicsWindow) { - // Graphics Application. - hwndForeWindow = GetForegroundWindow(); - // If the graphics application is not the top most window try and force it - // to the top. - if (hwndForeWindow != hwndBOINCGraphicsWindow) { - BOINCTRACE(_T("CScreensaver::DataManagementProc - Graphics Window Detected but NOT the foreground window, bringing window to foreground.\n")); - SetForegroundWindow(hwndBOINCGraphicsWindow); - hwndForeWindow = GetForegroundWindow(); - if (hwndForeWindow != hwndBOINCGraphicsWindow) { - BOINCTRACE(_T("CScreensaver::DataManagementProc - Graphics Window Detected but NOT the foreground window, bringing window to foreground. (Final Try)\n")); - - // This may be needed on Windows 2000 or better machines - if (gspfnMyBroadcastSystemMessage) { - DWORD dwComponents = BSM_APPLICATIONS; - gspfnMyBroadcastSystemMessage( - BSF_ALLOWSFW, - &dwComponents, - WM_BOINCSFW, - NULL, - NULL - ); - } - } - } else { - // Science application has focus, and is visible. - // - // Some science application take a really long time to display something on their - // window, during this time the window will appear to eat keyboard and mouse event - // messages and not respond to other system events. These windows are considered - // ghost windows, normally they have an outline and can be moved around and resized. - // In the science application case where the borders are hidden from view, the - // window just takes on the background of the previous window which happens to be - // the black screensaver window owned by this process. - // - // Verify that their hasn't been any keyboard or mouse activity. If there has - // we should hide the window from this process and exit out of the screensaver to - // return control back to the user as quickly as possible. - BOINCTRACE(_T("CScreensaver::DataManagementProc - Graphics Window Detected and is the foreground window.\n")); - if (gspfnMyGetLastInputInfo) { - BOINCTRACE(_T("CScreensaver::DataManagementProc - Checking idle actvity.\n")); - LASTINPUTINFO lii; - lii.cbSize = sizeof(LASTINPUTINFO); - - gspfnMyGetLastInputInfo(&lii); - - if (m_dwLastInputTimeAtStartup != lii.dwTime) { - BOINCTRACE(_T("CScreensaver::DataManagementProc - Activity Detected.\n")); - ShowWindow(hwndBOINCGraphicsWindow, SW_MINIMIZE); - ShowWindow(hwndBOINCGraphicsWindow, SW_FORCEMINIMIZE); - SetError(TRUE, SCRAPPERR_BOINCSHUTDOWNEVENT); - SendMessage(m_Monitors[iMonitor].hWnd, WM_INTERRUPTSAVER, NULL, NULL); - } - } - } - } else { - // Graphics application does not exist. So check that one of the windows - // assigned to each monitor is the foreground window. - bForegroundWindowIsScreensaver = FALSE; - hwndForeWindow = GetForegroundWindow(); - hwndForeParent = GetParent(hwndForeWindow); - for(iMonitor = 0; iMonitor < m_dwNumMonitors; iMonitor++) { - pMonitorInfo = &m_Monitors[iMonitor]; - if ((pMonitorInfo->hWnd == hwndForeWindow) || - (pMonitorInfo->hWnd == hwndForeParent)) - { - bForegroundWindowIsScreensaver = TRUE; - } - } - if (!bForegroundWindowIsScreensaver) { - // This can happen because of a personal firewall notifications or some - // funky IM client that thinks it has to notify the user even when in - // screensaver mode. - BOINCTRACE(_T("CScreensaver::DataManagementProc - Unknown foreground window detected, shutdown the screensaver.\n")); - SetError(TRUE, SCRAPPERR_BOINCSHUTDOWNEVENT); - SendMessage(m_Monitors[0].hWnd, WM_INTERRUPTSAVER, NULL, NULL); - } - } - } - } - } - - BOINCTRACE(_T("CScreensaver::SaverProc - ErrorMode = '%d', ErrorCode = '%x'\n"), m_bErrorMode, m_hrError); - Sleep(1000); - } -} - - - - // This function forwards to DataManagementProc, which has access to the // "this" pointer. // @@ -1212,6 +1031,126 @@ DWORD WINAPI CScreensaver::DataManagementProcStub(LPVOID UNUSED(lpParam)) { +void CScreensaver::HandleRPCError() +{ + // Attempt to reinitialize the RPC client and state + rpc->close(); + rpc->init(NULL); + m_bResetCoreState = TRUE; + + if (!m_bBOINCConfigChecked) { + m_bBOINCConfigChecked = TRUE; + m_bBOINCStartupConfigured = IsConfigStartupBOINC(); + } + + if ((time(0) - m_tThreadCreateTime) > 3) { + if (m_bBOINCStartupConfigured) { + SetError(TRUE, SCRAPPERR_BOINCNOTDETECTED); + } else { + SetError(TRUE, SCRAPPERR_BOINCNOTDETECTEDSTARTUP); + } + } + +} + + + + +void CScreensaver::CheckForegroundWindow() +{ + BOOL bForegroundWindowIsScreensaver; + HWND hwndBOINCGraphicsWindow = NULL; + HWND hwndForeWindow = NULL; + HWND hwndForeParent = NULL; + DWORD iMonitor = 0; + INTERNALMONITORINFO* pMonitorInfo = NULL; + + // When running in screensaver mode the only two valid conditions for z-order + // is that either the screensaver or graphics application is the foreground + // application. If this is not true, then blow out of the screensaver. + hwndBOINCGraphicsWindow = FindWindow(BOINC_WINDOW_CLASS_NAME, NULL); + if (hwndBOINCGraphicsWindow) { + // Graphics Application. + hwndForeWindow = GetForegroundWindow(); + // If the graphics application is not the top most window try and force it + // to the top. + if (hwndForeWindow != hwndBOINCGraphicsWindow) { + BOINCTRACE(_T("CScreensaver::CheckForegroundWindow - Graphics Window Detected but NOT the foreground window, bringing window to foreground.\n")); + SetForegroundWindow(hwndBOINCGraphicsWindow); + hwndForeWindow = GetForegroundWindow(); + if (hwndForeWindow != hwndBOINCGraphicsWindow) { + BOINCTRACE(_T("CScreensaver::CheckForegroundWindow - Graphics Window Detected but NOT the foreground window, bringing window to foreground. (Final Try)\n")); + + // This may be needed on Windows 2000 or better machines + if (gspfnMyBroadcastSystemMessage) { + DWORD dwComponents = BSM_APPLICATIONS; + gspfnMyBroadcastSystemMessage( + BSF_ALLOWSFW, + &dwComponents, + WM_BOINCSFW, + NULL, + NULL + ); + } + } + } else { + // Science application has focus, and is visible. + // + // Some science application take a really long time to display something on their + // window, during this time the window will appear to eat keyboard and mouse event + // messages and not respond to other system events. These windows are considered + // ghost windows, normally they have an outline and can be moved around and resized. + // In the science application case where the borders are hidden from view, the + // window just takes on the background of the previous window which happens to be + // the black screensaver window owned by this process. + // + // Verify that their hasn't been any keyboard or mouse activity. If there has + // we should hide the window from this process and exit out of the screensaver to + // return control back to the user as quickly as possible. + BOINCTRACE(_T("CScreensaver::CheckForegroundWindow - Graphics Window Detected and is the foreground window.\n")); + if (gspfnMyGetLastInputInfo) { + BOINCTRACE(_T("CScreensaver::CheckForegroundWindow - Checking idle actvity.\n")); + LASTINPUTINFO lii; + lii.cbSize = sizeof(LASTINPUTINFO); + + gspfnMyGetLastInputInfo(&lii); + + if (m_dwLastInputTimeAtStartup != lii.dwTime) { + BOINCTRACE(_T("CScreensaver::CheckForegroundWindow - Activity Detected.\n")); + ShowWindow(hwndBOINCGraphicsWindow, SW_MINIMIZE); + ShowWindow(hwndBOINCGraphicsWindow, SW_FORCEMINIMIZE); + SetError(TRUE, SCRAPPERR_BOINCSHUTDOWNEVENT); + SendMessage(m_Monitors[iMonitor].hWnd, WM_INTERRUPTSAVER, NULL, NULL); + } + } + } + } else { + // Graphics application does not exist. So check that one of the windows + // assigned to each monitor is the foreground window. + bForegroundWindowIsScreensaver = FALSE; + hwndForeWindow = GetForegroundWindow(); + hwndForeParent = GetParent(hwndForeWindow); + for(iMonitor = 0; iMonitor < m_dwNumMonitors; iMonitor++) { + pMonitorInfo = &m_Monitors[iMonitor]; + if ((pMonitorInfo->hWnd == hwndForeWindow) || + (pMonitorInfo->hWnd == hwndForeParent)) + { + bForegroundWindowIsScreensaver = TRUE; + } + } + if (!bForegroundWindowIsScreensaver) { + // This can happen because of a personal firewall notifications or some + // funky IM client that thinks it has to notify the user even when in + // screensaver mode. + BOINCTRACE(_T("CScreensaver::CheckForegroundWindow - Unknown foreground window detected, shutdown the screensaver.\n")); + SetError(TRUE, SCRAPPERR_BOINCSHUTDOWNEVENT); + SendMessage(m_Monitors[0].hWnd, WM_INTERRUPTSAVER, NULL, NULL); + } + } +} + + + // Register and create the appropriate window(s) // HRESULT CScreensaver::CreateSaverWindow() { @@ -1582,7 +1521,7 @@ VOID CScreensaver::ShutdownSaver() { } // Kill the currently executing graphics application - terminate_screensaver(m_hGraphicsApplication, &m_running_result, &rpc); + terminate_screensaver(m_hGraphicsApplication, &m_running_result); // Post message to drop out of message loop // This can be called from the data management thread, so specifically diff --git a/clientscr/screensaver_win.h b/clientscr/screensaver_win.h index bea548b1b4..f044a1b6ad 100644 --- a/clientscr/screensaver_win.h +++ b/clientscr/screensaver_win.h @@ -7,23 +7,8 @@ #ifndef _SCREENSAVER_WIN_H #define _SCREENSAVER_WIN_H -//----------------------------------------------------------------------------- -// Error codes -//----------------------------------------------------------------------------- - -#define SCRAPPERR_BOINCNOTDETECTED 0x82000001 -#define SCRAPPERR_BOINCNOTDETECTEDSTARTUP 0x82000002 -#define SCRAPPERR_BOINCSUSPENDED 0x82000003 -#define SCRAPPERR_BOINCNOTGRAPHICSCAPABLE 0x82000004 -#define SCRAPPERR_BOINCNOAPPSEXECUTING 0x82000005 -#define SCRAPPERR_BOINCNOPROJECTSDETECTED 0x82000006 -#define SCRAPPERR_BOINCNOGRAPHICSAPPSEXECUTING 0x82000007 -#define SCRAPPERR_BOINCSCREENSAVERLOADING 0x82000008 -#define SCRAPPERR_BOINCAPPFOUNDGRAPHICSLOADING 0x82000009 -#define SCRAPPERR_BOINCSHUTDOWNEVENT 0x8200000a -#define SCRAPPERR_NOPREVIEW 0x8200000f -#define SCRAPPERR_DAEMONALLOWSNOGRAPHICS 0x82000010 +#include //----------------------------------------------------------------------------- // Constants @@ -173,19 +158,35 @@ protected: DWORD WINAPI DataManagementProc(); static DWORD WINAPI DataManagementProcStub( LPVOID lpParam ); + void CheckForegroundWindow(); + int terminate_screensaver(HANDLE& graphics_application, RESULT *worker_app); + int launch_screensaver(RESULT* rp, HANDLE& graphics_application); + void HandleRPCError(void); - RPC_CLIENT rpc; +// Determine if two RESULT pointers refer to the same task + bool is_same_task(RESULT* taska, RESULT* taskb); + +// Count the number of active graphics-capable apps + int count_active_graphic_apps(RESULTS& results, RESULT* exclude = NULL); + +// Choose a ramdom graphics application from the vector that +// was passed in. + + RESULT* get_random_graphics_app(RESULTS& results, RESULT* exclude = NULL); + + RPC_CLIENT* rpc; CC_STATE state; RESULTS results; RESULT m_running_result; + bool m_updating_results; HANDLE m_hDataManagementThread; HANDLE m_hGraphicsApplication; - BOOL m_bScreensaverStarted; BOOL m_bResetCoreState; + bool m_QuitDataManagementProc; int m_iLastResultShown; time_t m_tLastResultChangeTime; - + time_t m_tThreadCreateTime; // // Presentation layer diff --git a/mac_build/boinc.xcodeproj/project.pbxproj b/mac_build/boinc.xcodeproj/project.pbxproj index 7fb4dee0bb..b135937acd 100755 --- a/mac_build/boinc.xcodeproj/project.pbxproj +++ b/mac_build/boinc.xcodeproj/project.pbxproj @@ -1143,6 +1143,7 @@ DD9C94A30A5A3A4100AB0D10 /* BOINCBaseFrame.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = BOINCBaseFrame.h; path = ../clientgui/BOINCBaseFrame.h; sourceTree = SOURCE_ROOT; }; DDA12A910A369AB500FBDD12 /* SetupSecurity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SetupSecurity.h; path = ../clientgui/mac/SetupSecurity.h; sourceTree = SOURCE_ROOT; }; DDA12AAD0A369C5800FBDD12 /* SecurityUtility.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SecurityUtility.cpp; path = ../clientgui/mac/SecurityUtility.cpp; sourceTree = SOURCE_ROOT; }; + DDA290360CB5D80E00512BD8 /* Mac_Saver_Module.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Mac_Saver_Module.h; path = ../clientscr/Mac_Saver_Module.h; sourceTree = SOURCE_ROOT; }; DDA6BD030BD4551F008F7921 /* dyld_gdb.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_gdb.h; path = ../lib/mac/dyld_gdb.h; sourceTree = SOURCE_ROOT; }; DDA6BD040BD4551F008F7921 /* mac_backtrace.C */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = mac_backtrace.C; path = ../lib/mac/mac_backtrace.C; sourceTree = SOURCE_ROOT; }; DDA6BD050BD4551F008F7921 /* mac_backtrace.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = mac_backtrace.h; path = ../lib/mac/mac_backtrace.h; sourceTree = SOURCE_ROOT; }; @@ -1704,8 +1705,9 @@ isa = PBXGroup; children = ( DDB873960C85072500E0DE1F /* mac_saver_module.cpp */, - DDB873970C85072500E0DE1F /* Mac_Saver_ModuleView.h */, + DDA290360CB5D80E00512BD8 /* Mac_Saver_Module.h */, DDB873980C85072500E0DE1F /* Mac_Saver_ModuleView.m */, + DDB873970C85072500E0DE1F /* Mac_Saver_ModuleView.h */, DD7AEB490C87CE1300AC3B5C /* screensaver.cpp */, DD7AEB680C87CE6500AC3B5C /* screensaver.h */, DDFA60E30CB3396C0037B88C /* gfx_switcher.C */,