1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2008 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
17 //
18 // Contributor(s):
19 //     DirectX 8.1 Screen Saver Framework from Microsoft.
20 //     Microsoft Knowledge Base Article - 79212
21 //
22 
23 #include "boinc_win.h"
24 #include "boinc_ss.h"
25 #include "boinc_gl.h"
26 #include "diagnostics.h"
27 #include "common_defs.h"
28 #include "util.h"
29 #include "gui_rpc_client.h"
30 #include "screensaver.h"
31 #include "screensaver_win.h"
32 
33 #ifndef UNUSED
34 #define UNUSED(x)
35 #endif
36 
37 
38 static CScreensaver*            gspScreensaver = NULL;
39 
40 const UINT                      WM_SETTIMER = RegisterWindowMessage(TEXT("BOINCSetTimer"));
41 const UINT                      WM_INTERRUPTSAVER = RegisterWindowMessage(TEXT("BOINCInterruptScreensaver"));
42 const UINT                      WM_BOINCSFW = RegisterWindowMessage(TEXT("BOINCSetForegroundWindow"));
43 
44 
45 #define REG_ENABLE_BLANK        _T("Blank")
46 #define REG_BLANK_TIME          _T("Blank Time")
47 #define REG_DEFAULT_TIME        _T("Default Time")
48 #define REG_RUN_TIME            _T("Run Time")
49 #define REG_SWITCH_TIME         _T("Switch Time")
50 
51 
52 #define MAXSLIDERTIC            8     // Zero indexed arrays
53 const DWORD                     dwTableSliderPositionToTime[] = {1, 2, 5, 10, 15, 30, 45, 60, 0};
54 
55 
WinMain(HINSTANCE hInstance,HINSTANCE UNUSED (hPrevInstance),LPSTR UNUSED (lpCmdLine),int UNUSED (nCmdShow))56 INT WINAPI WinMain(
57     HINSTANCE hInstance, HINSTANCE UNUSED(hPrevInstance), LPSTR UNUSED(lpCmdLine), int UNUSED(nCmdShow)
58 ) {
59     HRESULT      hr;
60     CScreensaver BOINCSS;
61     int          retval;
62     WSADATA      wsdata;
63 
64 
65     // Initialize the CRT random number generator.
66     srand((unsigned int)time(0));
67 
68     // Initialize Windows Common Controls
69     INITCOMMONCONTROLSEX InitCtrls;
70     InitCtrls.dwSize = sizeof(InitCtrls);
71     InitCtrls.dwICC = ICC_WIN95_CLASSES;
72     InitCommonControlsEx(&InitCtrls);
73 
74     // Initialize the Windows sockets interface.
75     retval = WSAStartup(MAKEWORD(1, 1), &wsdata);
76     if (retval) {
77         BOINCTRACE("WinMain - Winsock Initialization Failure '%d'\n", retval);
78         return retval;
79     }
80 
81     // Initialize Screensaver
82     if (FAILED(hr = BOINCSS.Create(hInstance))) {
83         BOINCSS.DisplayErrorMsg(hr);
84         WSACleanup();
85         return 0;
86     }
87 
88     // Run Screensaver
89     retval = BOINCSS.Run();
90 
91     // Cleanup any existing screensaver objects and handles
92     BOINCTRACE("WinMain - Cleanup Screensaver Resources\n");
93     BOINCSS.Cleanup();
94 
95     // Cleanup the Windows sockets interface.
96     BOINCTRACE("WinMain - Cleanup Winsock Resources\n");
97     WSACleanup();
98 
99     return retval;
100 }
101 
102 
CScreensaver()103 CScreensaver::CScreensaver() {
104     gspScreensaver = this;
105 
106     m_dwSaverMouseMoveCount = 0;
107     m_hWnd = NULL;
108     m_hWndParent = NULL;
109 
110     m_bAllScreensSame = FALSE;
111     m_bWindowed = FALSE;
112     m_bWaitForInputIdle = FALSE;
113 
114     m_bErrorMode = FALSE;
115     m_hrError = S_OK;
116     m_szError[0] = _T('\0');
117     m_strBOINCInstallDirectory.clear();
118     m_strBOINCDataDirectory.clear();
119 
120     LoadString(NULL, IDS_DESCRIPTION, m_strWindowTitle, 200);
121 
122     m_bPaintingInitialized = FALSE;
123     m_dwBlankScreen = 0;
124     m_dwBlankTime = 0;
125 
126     rpc = NULL;
127     m_bConnected = false;
128     m_hDataManagementThread = NULL;
129     m_hGraphicsApplication = NULL;
130     m_bResetCoreState = TRUE;
131     m_bQuitDataManagementProc = FALSE;
132     m_bDataManagementProcStopped = FALSE;
133     memset(&m_running_result, 0, sizeof(m_running_result));
134 
135     ZeroMemory(m_Monitors, sizeof(m_Monitors));
136     m_dwNumMonitors = 0;
137 
138     m_dwLastInputTimeAtStartup = 0;
139     m_tThreadCreateTime = 0;
140 }
141 
142 
143 // Have the client program call this function before calling Run().
144 //
Create(HINSTANCE hInstance)145 HRESULT CScreensaver::Create(HINSTANCE hInstance) {
146     HRESULT hr;
147     struct  ss_periods periods;
148 
149     m_hInstance = hInstance;
150 
151 
152     // Retrieve the locations of the install directory and data directory
153 	UtilGetRegDirectoryStr(_T("DATADIR"), m_strBOINCDataDirectory);
154 	UtilGetRegDirectoryStr(_T("INSTALLDIR"), m_strBOINCInstallDirectory);
155 
156     if (!m_strBOINCDataDirectory.empty()) {
157         SetCurrentDirectory(m_strBOINCDataDirectory.c_str());
158     }
159 
160     // Initialize Diagnostics
161     int dwDiagnosticsFlags =
162 #ifdef _DEBUG
163         BOINC_DIAG_HEAPCHECKENABLED |
164         BOINC_DIAG_MEMORYLEAKCHECKENABLED |
165 #endif
166         BOINC_DIAG_DUMPCALLSTACKENABLED |
167         BOINC_DIAG_PERUSERLOGFILES |
168         BOINC_DIAG_REDIRECTSTDERR |
169         BOINC_DIAG_REDIRECTSTDOUT |
170         BOINC_DIAG_TRACETOSTDOUT;
171 
172     diagnostics_init(dwDiagnosticsFlags, "stdoutscr", "stderrscr");
173 
174 
175     // Parse the command line and do the appropriate thing
176     m_SaverMode = ParseCommandLine(GetCommandLine());
177 
178 
179     // Store last input value if it exists
180     LASTINPUTINFO lii;
181     lii.cbSize = sizeof(LASTINPUTINFO);
182     GetLastInputInfo(&lii);
183 
184     m_dwLastInputTimeAtStartup = lii.dwTime;
185 
186 
187     // Enumerate Monitors
188     EnumMonitors();
189 
190     // Get project-defined default values for GFXDefaultPeriod, GFXSciencePeriod, GFXChangePeriod
191     GetDefaultDisplayPeriods(periods);
192     m_bShow_default_ss_first = periods.Show_default_ss_first;
193 
194     // Get the last set of saved values, if not set
195     // use the configuration file, if not set, use defaults.
196     // Normalize on Seconds...
197     if (!UtilGetRegKey(REG_ENABLE_BLANK, m_dwBlankScreen)) {
198         m_dwBlankScreen = 0;
199     }
200     if (!UtilGetRegKey(REG_BLANK_TIME, m_dwBlankTime)) {
201         m_dwBlankTime = GFX_BLANK_PERIOD;
202     } else {
203         // Registry stores minutes
204         m_dwBlankTime *= 60;
205     }
206     if (!UtilGetRegKey(REG_DEFAULT_TIME, m_dwDefaultTime)) {
207         m_dwDefaultTime = (DWORD)periods.GFXDefaultPeriod;
208     } else {
209         // Registry stores minutes
210         m_dwDefaultTime *= 60;
211     }
212     if (!UtilGetRegKey(REG_RUN_TIME, m_dwRunTime)) {
213         m_dwRunTime = (DWORD)periods.GFXSciencePeriod;
214     } else {
215         // Registry stores minutes
216         m_dwRunTime *= 60;
217     }
218     if (!UtilGetRegKey(REG_SWITCH_TIME, m_dwSwitchTime)) {
219         m_dwSwitchTime = (DWORD)periods.GFXChangePeriod;
220     } else {
221         // Registry stores minutes
222         m_dwSwitchTime *= 60;
223     }
224 
225     // Set the legacy value if needed
226     if (!m_dwBlankScreen) {
227         m_dwBlankTime = 0;
228     }
229 
230     // Convert values as stored into floating point values to be used
231     //   at runtime
232     m_fGFXDefaultPeriod = (double)m_dwDefaultTime;
233     m_fGFXSciencePeriod = (double)m_dwRunTime;
234     m_fGFXChangePeriod = (double)m_dwSwitchTime;
235 
236     // Save the value back to the registry in case this is the first
237     // execution and so we need the default value later.
238     // Store in minutes
239     UtilSetRegKey(REG_ENABLE_BLANK, m_dwBlankScreen);
240     UtilSetRegKey(REG_BLANK_TIME, m_dwBlankTime ? m_dwBlankTime / 60 : 0);
241     UtilSetRegKey(REG_DEFAULT_TIME, m_dwDefaultTime ? m_dwDefaultTime / 60 : 0);
242     UtilSetRegKey(REG_RUN_TIME, m_dwRunTime ? m_dwRunTime / 60 : 0);
243     UtilSetRegKey(REG_SWITCH_TIME, m_dwSwitchTime ? m_dwSwitchTime / 60 : 0);
244 
245 
246     // Calculate the estimated blank time by adding the current time
247     //   and and the user specified time.
248     if (m_dwBlankTime > 0) {
249         m_dwBlankTime = (DWORD)time(0) + m_dwBlankTime;
250     }
251 
252     // Create the infrastructure mutexes so we can properly aquire them to report
253     //   errors
254     if (!CreateInfrastructureMutexes()) {
255         return E_FAIL;
256     }
257 
258 	if (rpc == NULL) rpc = new RPC_CLIENT;
259 
260     // Create the screen saver window(s)
261     if (m_SaverMode == sm_preview ||
262         m_SaverMode == sm_full
263     ) {
264         if (FAILED(hr = CreateSaverWindow())) {
265             SetError(TRUE, hr);
266         }
267     }
268 
269     if (m_SaverMode == sm_preview) {
270         // In preview mode, "pause" (enter a limited message loop) briefly
271         // before proceeding, so the display control panel knows to update itself.
272         m_bWaitForInputIdle = TRUE;
273 
274         // Post a message to mark the end of the initial group of window messages
275         PostMessage(m_hWnd, WM_SETTIMER, 0, 0);
276 
277         MSG msg;
278         while(m_bWaitForInputIdle) {
279             // If GetMessage returns FALSE, it's quitting time.
280             if (!GetMessage(&msg, m_hWnd, 0, 0)) {
281                 // Post the quit message to handle it later
282                 PostQuitMessage(0);
283                 break;
284             }
285 
286             TranslateMessage(&msg);
287             DispatchMessage(&msg);
288         }
289     }
290 
291     return S_OK;
292 }
293 
294 
295 
296 
297 // Starts main execution of the screen saver.
298 //
Run()299 HRESULT CScreensaver::Run() {
300     HOST_INFO hostinfo;
301     HRESULT hr;
302 
303     // Parse the command line and do the appropriate thing
304     switch (m_SaverMode) {
305         case sm_config:
306             if (m_bErrorMode) {
307                 DisplayErrorMsg(m_hrError);
308             } else {
309                 DoConfig();
310             }
311             break;
312         case sm_test:
313             rpc->init(NULL);
314             rpc->get_host_info(hostinfo);
315             rpc->close();
316             break;
317         case sm_preview:
318             // In Windows, preview mode is for the mini-view of the screensaver.
319             //   For BOINC we just display the icon, so there is no need to
320             //   startup the data management thread which in turn will
321             //   launch a graphics application.
322             if (FAILED(hr = DoSaver())) {
323                 DisplayErrorMsg(hr);
324             }
325             break;
326         case sm_full:
327             // Create the various required threads
328             if (!CreateInputActivityThread()) return E_FAIL;
329             if (!CreateGraphicsWindowPromotionThread()) {
330                 DestroyDataManagementThread();
331                 return E_FAIL;
332             }
333             if (!CreateDataManagementThread()) {
334                 DestroyDataManagementThread();
335                 DestroyGraphicsWindowPromotionThread();
336                 return E_FAIL;
337             }
338 
339             if (FAILED(hr = DoSaver())) {
340                 DisplayErrorMsg(hr);
341             }
342 
343             // Destroy the various required threads
344             //
345             DestroyDataManagementThread();
346             DestroyGraphicsWindowPromotionThread();
347             DestroyInputActivityThread();
348 
349             break;
350     }
351     return S_OK;
352 }
353 
354 
355 
356 
357 // Cleanup anything that needs cleaning.
358 //
Cleanup()359 HRESULT CScreensaver::Cleanup() {
360     BOINCTRACE("CScreensaver::Cleanup - Cleanup graphics application if running\n");
361     if (m_hGraphicsApplication) {
362         TerminateProcess(m_hGraphicsApplication, 0);
363         m_hGraphicsApplication = NULL;
364     }
365     BOINCTRACE("CScreensaver::Cleanup - Cleanup RPC client\n");
366     if (rpc) {
367         rpc->close();
368         delete rpc;
369         rpc = NULL;
370     }
371     return S_OK;
372 }
373 
374 
375 
376 
377 // Displays error messages in a message box
378 //
DisplayErrorMsg(HRESULT hr)379 HRESULT CScreensaver::DisplayErrorMsg(HRESULT hr) {
380     TCHAR strMsg[512];
381     GetTextForError(hr, strMsg, 512);
382     MessageBox(m_hWnd, strMsg, m_strWindowTitle, MB_ICONERROR | MB_OK);
383     return hr;
384 }
385 
386 
387 
388 
389 // Interpret command-line parameters passed to this app.
390 //
ParseCommandLine(TCHAR * pstrCommandLine)391 SaverMode CScreensaver::ParseCommandLine(TCHAR* pstrCommandLine) {
392     m_hWndParent = NULL;
393 
394 	BOINCTRACE("ParseCommandLine: '%s'\n", pstrCommandLine);
395 
396     // Skip the first part of the command line, which is the full path
397     // to the exe.  If it contains spaces, it will be contained in quotes.
398     if (*pstrCommandLine == _T('\"')) {
399         pstrCommandLine++;
400         while (*pstrCommandLine != _T('\0') && *pstrCommandLine != _T('\"')) {
401             pstrCommandLine++;
402         }
403         if (*pstrCommandLine == _T('\"')) {
404             pstrCommandLine++;
405         }
406     } else {
407         while (*pstrCommandLine != _T('\0') && *pstrCommandLine != _T(' ')) {
408             pstrCommandLine++;
409         }
410         if (*pstrCommandLine == _T(' ')) {
411             pstrCommandLine++;
412         }
413     }
414 
415     // Skip along to the first option delimiter "/" or "-"
416     while (*pstrCommandLine != _T('\0') && *pstrCommandLine != _T('/') && *pstrCommandLine != _T('-')) {
417         pstrCommandLine++;
418     }
419 
420     // If there wasn't one, then must be config mode
421     if (*pstrCommandLine == _T('\0')) {
422         return sm_config;
423     }
424 
425     // Otherwise see what the option was
426     switch (*(++pstrCommandLine)) {
427         case 'c':
428         case 'C':
429             pstrCommandLine++;
430             while (*pstrCommandLine && !isdigit(*pstrCommandLine)) {
431                 pstrCommandLine++;
432             }
433             if (isdigit(*pstrCommandLine)) {
434 #ifdef _WIN64
435                 m_hWndParent = (HWND)_atoi64(pstrCommandLine);
436 #else
437                 m_hWndParent = (HWND)_ttol(pstrCommandLine);
438 #endif
439             } else {
440                 m_hWndParent = NULL;
441             }
442             return sm_config;
443 
444         case 't':
445         case 'T':
446             return sm_test;
447 
448         case 'p':
449         case 'P':
450             // Preview-mode, so option is followed by the parent HWND in decimal
451             pstrCommandLine++;
452             while (*pstrCommandLine && !isdigit(*pstrCommandLine)) {
453                 pstrCommandLine++;
454             }
455             if (isdigit(*pstrCommandLine)) {
456 #ifdef _WIN64
457                 m_hWndParent = (HWND)_atoi64(pstrCommandLine);
458 #else
459                 m_hWndParent = (HWND)_ttol(pstrCommandLine);
460 #endif
461             }
462             return sm_preview;
463 
464         case 'a':
465         case 'A':
466             // Password change mode, so option is followed by parent HWND in decimal
467             pstrCommandLine++;
468             while (*pstrCommandLine && !isdigit(*pstrCommandLine)) {
469                 pstrCommandLine++;
470             }
471             if (isdigit(*pstrCommandLine)) {
472 #ifdef _WIN64
473                 m_hWndParent = (HWND)_atoi64(pstrCommandLine);
474 #else
475                 m_hWndParent = (HWND)_ttol(pstrCommandLine);
476 #endif
477             }
478             return sm_passwordchange;
479 
480         default:
481             // All other options => run the screensaver (typically this is "/s")
482             return sm_full;
483     }
484 }
485 
486 
487 
488 
489 // Determine HMONITOR, desktop rect, and other info for each monitor.
490 //       Note that EnumDisplayDevices enumerates monitors in the order
491 //       indicated on the Settings page of the Display control panel, which
492 //       is the order we want to list monitors in, as opposed to the order
493 //       used by D3D's GetAdapterInfo.
494 //
EnumMonitors(VOID)495 VOID CScreensaver::EnumMonitors(VOID) {
496     DWORD iDevice = 0;
497     DISPLAY_DEVICE_FULL dispdev;
498     DISPLAY_DEVICE_FULL dispdev2;
499     DEVMODE devmode;
500     dispdev.cb = sizeof(dispdev);
501     dispdev2.cb = sizeof(dispdev2);
502     devmode.dmSize = sizeof(devmode);
503     devmode.dmDriverExtra = 0;
504     INTERNALMONITORINFO* pMonitorInfoNew;
505     while(EnumDisplayDevices(NULL, iDevice, (DISPLAY_DEVICE*)&dispdev, 0)) {
506         // Ignore NetMeeting's mirrored displays
507         if ((dispdev.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) == 0) {
508             // To get monitor info for a display device, call EnumDisplayDevices
509             // a second time, passing dispdev.DeviceName (from the first call) as
510             // the first parameter.
511             EnumDisplayDevices(dispdev.DeviceName, 0, (DISPLAY_DEVICE*)&dispdev2, 0);
512 
513             pMonitorInfoNew = &m_Monitors[m_dwNumMonitors];
514             ZeroMemory(pMonitorInfoNew, sizeof(INTERNALMONITORINFO));
515             strncpy(
516                 pMonitorInfoNew->strDeviceName,
517                 dispdev.DeviceString,
518                 sizeof(pMonitorInfoNew->strDeviceName) * sizeof(TCHAR)
519             );
520             strncpy(
521                 pMonitorInfoNew->strMonitorName,
522                 dispdev2.DeviceString,
523                 sizeof(pMonitorInfoNew->strMonitorName) * sizeof(TCHAR)
524             );
525 
526             if (dispdev.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) {
527                 EnumDisplaySettings(dispdev.DeviceName, ENUM_CURRENT_SETTINGS, &devmode);
528                 if (dispdev.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
529                     // For some reason devmode.dmPosition is not always (0, 0)
530                     // for the primary display, so force it.
531                     pMonitorInfoNew->rcScreen.left = 0;
532                     pMonitorInfoNew->rcScreen.top = 0;
533                 } else {
534                     pMonitorInfoNew->rcScreen.left = devmode.dmPosition.x;
535                     pMonitorInfoNew->rcScreen.top = devmode.dmPosition.y;
536                 }
537                 pMonitorInfoNew->rcScreen.right = pMonitorInfoNew->rcScreen.left + devmode.dmPelsWidth;
538                 pMonitorInfoNew->rcScreen.bottom = pMonitorInfoNew->rcScreen.top + devmode.dmPelsHeight;
539                 pMonitorInfoNew->hMonitor = MonitorFromRect(&pMonitorInfoNew->rcScreen, MONITOR_DEFAULTTONULL);
540             }
541             m_dwNumMonitors++;
542             if (m_dwNumMonitors == MAX_DISPLAYS) {
543                 break;
544             }
545         }
546         iDevice++;
547     }
548 }
549 
550 
551 
552 
UtilGetRegKey(LPCTSTR name,DWORD & keyval)553 BOOL CScreensaver::UtilGetRegKey(LPCTSTR name, DWORD& keyval) {
554 	LONG  error;
555 	DWORD type = REG_DWORD;
556 	DWORD size = sizeof(DWORD);
557 	DWORD value;
558 	HKEY  boinc_key;
559 
560 	error = RegOpenKeyEx(
561         HKEY_CURRENT_USER,
562         _T("SOFTWARE\\Space Sciences Laboratory, U.C. Berkeley\\BOINC Screensaver"),
563 		0,
564         KEY_ALL_ACCESS,
565         &boinc_key
566     );
567 	if (error != ERROR_SUCCESS) return FALSE;
568 
569 	error = RegQueryValueEx(boinc_key, name, NULL, &type, (BYTE *)&value, &size);
570 
571 	keyval = value;
572 
573 	RegCloseKey(boinc_key);
574 
575 	if (error != ERROR_SUCCESS) return FALSE;
576 
577 	return TRUE;
578 }
579 
580 
581 
582 
UtilSetRegKey(LPCTSTR name,DWORD value)583 BOOL CScreensaver::UtilSetRegKey(LPCTSTR name, DWORD value) {
584 	LONG error;
585 	HKEY boinc_key;
586 
587 	error = RegCreateKeyEx(
588         HKEY_CURRENT_USER,
589         _T("SOFTWARE\\Space Sciences Laboratory, U.C. Berkeley\\BOINC Screensaver"),
590 		0,
591         NULL,
592         REG_OPTION_NON_VOLATILE,
593         KEY_READ | KEY_WRITE,
594         NULL,
595         &boinc_key,
596         NULL
597     );
598 	if (error != ERROR_SUCCESS) return FALSE;
599 
600 	error = RegSetValueEx(boinc_key, name, 0, REG_DWORD, (CONST BYTE *)&value, 4);
601 
602 	RegCloseKey(boinc_key);
603 
604 	return TRUE;
605 }
606 
607 
608 
609 
UtilGetRegDirectoryStr(LPCTSTR szTargetName,std::string & strDirectory)610 BOOL CScreensaver::UtilGetRegDirectoryStr(LPCTSTR szTargetName, std::string& strDirectory) {
611 	LONG    lReturnValue;
612 	HKEY    hkSetupHive;
613     LPTSTR  lpszRegistryValue = NULL;
614 	DWORD   dwSize = 0;
615 
616     // change the current directory to the boinc data directory if it exists
617 	lReturnValue = RegOpenKeyEx(
618         HKEY_LOCAL_MACHINE,
619         _T("SOFTWARE\\Space Sciences Laboratory, U.C. Berkeley\\BOINC Setup"),
620 		0,
621         KEY_READ,
622         &hkSetupHive
623     );
624     if (lReturnValue == ERROR_SUCCESS) {
625         // How large does our buffer need to be?
626         lReturnValue = RegQueryValueEx(
627             hkSetupHive,
628             szTargetName,
629             NULL,
630             NULL,
631             NULL,
632             &dwSize
633         );
634         if (lReturnValue != ERROR_FILE_NOT_FOUND) {
635             // Allocate the buffer space.
636             lpszRegistryValue = (LPTSTR) malloc(dwSize);
637             (*lpszRegistryValue) = NULL;
638 
639             // Now get the data
640             lReturnValue = RegQueryValueEx(
641                 hkSetupHive,
642                 szTargetName,
643                 NULL,
644                 NULL,
645                 (LPBYTE)lpszRegistryValue,
646                 &dwSize
647             );
648 
649             // Store the directory for later use.
650             strDirectory = lpszRegistryValue;
651         } else {
652             return FALSE;
653         }
654     } else {
655         return FALSE;
656     }
657 
658     // Cleanup
659 	if (hkSetupHive) RegCloseKey(hkSetupHive);
660 	return TRUE;
661 }
662 
663 
664 
665 
666 // Desc: Create the infrastructure for thread safe acccess to the infrastructure
667 //       layer of the screen saver.
668 //
CreateInfrastructureMutexes()669 BOOL CScreensaver::CreateInfrastructureMutexes() {
670     m_hErrorManagementMutex = CreateMutex(NULL, FALSE, NULL);
671     if (NULL == m_hErrorManagementMutex) {
672     	BOINCTRACE(_T("CScreensaver::CreateInfrastructureMutexes: Failed to create m_hErrorManagementMutex '%d'\n"), GetLastError());
673         return FALSE;
674     }
675     return TRUE;
676 }
677 
678 
679 
680 
681 // Provide a thread-safe implementation for retrieving the current
682 //       error condition.
683 //
GetError(BOOL & bErrorMode,HRESULT & hrError,TCHAR * pszError,size_t iErrorSize)684 BOOL CScreensaver::GetError(
685     BOOL& bErrorMode, HRESULT& hrError, TCHAR* pszError, size_t iErrorSize
686 ) {
687     DWORD dwWaitResult;
688     BOOL  bRetVal = FALSE;
689 
690     // Request ownership of mutex.
691     dwWaitResult = WaitForSingleObject(
692         m_hErrorManagementMutex,   // handle to mutex
693         5000L);                    // five-second time-out interval
694 
695     switch (dwWaitResult) {
696         // WAIT_OBJECT_0 - The thread got mutex ownership.
697         case WAIT_OBJECT_0:
698             bErrorMode = m_bErrorMode;
699             hrError = m_hrError;
700 
701             if (NULL != pszError) {
702                 strncpy(pszError, m_szError, iErrorSize);
703             }
704 
705             bRetVal = TRUE;
706             break;
707 
708         // WAIT_TIMEOUT - Cannot get mutex ownership due to time-out.
709         // WAIT_ABANDONED - Got ownership of the abandoned mutex object.
710         case WAIT_TIMEOUT:
711         case WAIT_ABANDONED:
712             break;
713     }
714     ReleaseMutex(m_hErrorManagementMutex);
715 
716     return bRetVal;
717 }
718 
719 
720 
721 
722 // Provide a thread-safe implementation for setting the current
723 //       error condition.  This API should only be called in the data management
724 //       thread, any other thread may cause a race condition.
725 //
SetError(BOOL bErrorMode,HRESULT hrError)726 BOOL CScreensaver::SetError(BOOL bErrorMode, HRESULT hrError) {
727     DWORD dwWaitResult;
728     BOOL  bRetVal = FALSE;
729 
730     // Request ownership of mutex.
731     dwWaitResult = WaitForSingleObject(
732         m_hErrorManagementMutex,   // handle to mutex
733         5000L                // five-second time-out interval
734     );
735 
736     switch (dwWaitResult) {
737         // WAIT_OBJECT_0 - The thread got mutex ownership.
738         case WAIT_OBJECT_0:
739             m_bErrorMode = bErrorMode;
740             m_hrError = hrError;
741 
742             // Update the error text, including a possible RPC call
743             //   to the daemon.
744             UpdateErrorBoxText();
745 
746             bRetVal = TRUE;
747             break;
748 
749         // WAIT_TIMEOUT - Cannot get mutex ownership due to time-out.
750         // WAIT_ABANDONED - Got ownership of the abandoned mutex object.
751         case WAIT_TIMEOUT:
752         case WAIT_ABANDONED:
753             break;
754     }
755     ReleaseMutex(m_hErrorManagementMutex);
756     return bRetVal;
757 }
758 
759 
760 
761 // Update the error message
762 //
UpdateErrorBoxText()763 VOID CScreensaver::UpdateErrorBoxText() {
764     // Load error string
765     GetTextForError(m_hrError, m_szError, sizeof(m_szError) / sizeof(TCHAR));
766     BOINCTRACE(_T("CScreensaver::UpdateErrorBoxText - HRESULT '%d' Updated Text '%s'\n"), m_hrError, m_szError);
767 }
768 
769 
770 
771 
772 // Translate an HRESULT error code into a string that can be displayed
773 //       to explain the error.  A class derived from CD3DScreensaver can
774 //       provide its own version of this function that provides app-specific
775 //       error translation instead of or in addition to calling this function.
776 //       This function returns TRUE if a specific error was translated, or
777 //       FALSE if no specific translation for the HRESULT was found (though
778 //       it still puts a generic string into pszError).
779 //
GetTextForError(HRESULT hr,TCHAR * pszError,DWORD dwNumChars)780 BOOL CScreensaver::GetTextForError(
781     HRESULT hr, TCHAR* pszError, DWORD dwNumChars
782 ) {
783     const DWORD dwErrorMap[][2] = {
784     //  HRESULT, stringID
785         (const DWORD)E_FAIL, IDS_ERR_GENERIC,
786         (const DWORD)E_OUTOFMEMORY, IDS_ERR_OUTOFMEMORY,
787 		SCRAPPERR_NOPREVIEW, IDS_ERR_NOPREVIEW,
788 		SCRAPPERR_BOINCSCREENSAVERLOADING, IDS_ERR_BOINCSCREENSAVERLOADING,
789 		SCRAPPERR_BOINCSHUTDOWNEVENT, IDS_ERR_BOINCSHUTDOWNEVENT,
790 		SCRAPPERR_BOINCAPPFOUNDGRAPHICSLOADING, IDS_ERR_BOINCAPPFOUNDGRAPHICSLOADING,
791         SCRAPPERR_BOINCNOTDETECTED, IDS_ERR_BOINCNOTDETECTED,
792         SCRAPPERR_BOINCNOGRAPHICSAPPSEXECUTING, IDS_ERR_BOINCSCREENSAVERLOADING
793     };
794     const DWORD dwErrorMapSize = sizeof(dwErrorMap) / sizeof(DWORD[2]);
795 
796     DWORD iError;
797     DWORD resid = 0;
798 
799     for(iError = 0; iError < dwErrorMapSize; iError++) {
800         if (hr == (HRESULT)dwErrorMap[iError][0]) {
801             resid = dwErrorMap[iError][1];
802         }
803     }
804     if (resid == 0) {
805         resid = IDS_ERR_GENERIC;
806     }
807 
808     LoadString(NULL, resid, pszError, dwNumChars);
809 
810     if (resid == IDS_ERR_GENERIC) {
811         return FALSE;
812     } else {
813         return TRUE;
814     }
815 }
816 
817 
818 
819 
820 // Create the thread that is used to monitor input activity.
821 //
CreateInputActivityThread()822 BOOL CScreensaver::CreateInputActivityThread() {
823     DWORD dwThreadID = 0;
824 
825     BOINCTRACE(_T("CScreensaver::CreateInputActivityThread Start\n"));
826 
827     m_hInputActivityThread = CreateThread(
828         NULL,                        // default security attributes
829         0,                           // use default stack size
830         InputActivityProcStub,       // thread function
831         NULL,                        // argument to thread function
832         0,                           // use default creation flags
833         &dwThreadID );               // returns the thread identifier
834 
835    if (m_hInputActivityThread == NULL) {
836     	BOINCTRACE(_T("CScreensaver::CreateInputActivityThread: Failed to create input activity thread '%d'\n"), GetLastError());
837         return FALSE;
838    }
839 
840    m_tThreadCreateTime = time(0);
841    return TRUE;
842 }
843 
844 
845 
846 
847 // Terminate the thread that is used to monitor input activity.
848 //
DestroyInputActivityThread()849 BOOL CScreensaver::DestroyInputActivityThread() {
850    	BOINCTRACE(_T("CScreensaver::DestroyInputActivityThread: Shutting Down...\n"));
851 
852     if (!TerminateThread(m_hInputActivityThread, 0)) {
853     	BOINCTRACE(_T("CScreensaver::DestroyInputActivityThread: Failed to terminate input activity thread '%d'\n"), GetLastError());
854         return FALSE;
855     }
856 
857     return TRUE;
858 }
859 
860 
861 
862 
863 // This function forwards to InputActivityProc, which has access to the
864 //   "this" pointer.
865 //
InputActivityProcStub(LPVOID UNUSED (lpParam))866 DWORD WINAPI CScreensaver::InputActivityProcStub(LPVOID UNUSED(lpParam)) {
867     return gspScreensaver->InputActivityProc();
868 }
869 
870 
871 
872 
873 // Some graphics applications take a really long time to display something on their
874 // window, during this time the window will appear to eat keyboard and mouse event
875 // messages and not respond to other system events.  These windows are considered
876 // ghost windows, normally they have an outline and can be moved around and resized.
877 // In the graphic applications case where the borders are hidden from view, the
878 // window just takes on the background of the previous window which happens to be
879 // the black screensaver window owned by this process.
880 //
881 // Verify that their hasn't been any keyboard or mouse activity.  If there has,
882 // we should hide the window from this process and exit out of the screensaver to
883 // return control back to the user as quickly as possible.
884 //
InputActivityProc()885 DWORD WINAPI CScreensaver::InputActivityProc() {
886     LASTINPUTINFO lii;
887     bool          bAutoBreak = false;
888     DWORD         dwCounter = 0;
889 
890     lii.cbSize = sizeof(LASTINPUTINFO);
891 
892     BOINCTRACE(_T("CScreensaver::InputActivityProc - Last Input Activity '%d'.\n"), m_dwLastInputTimeAtStartup);
893 
894     while(!bAutoBreak) {
895         if (GetLastInputInfo(&lii)) {
896             if (dwCounter > 4) {
897                 BOINCTRACE(_T("CScreensaver::InputActivityProc - Heartbeat.\n"));
898                 dwCounter = 0;
899             }
900             if (m_dwLastInputTimeAtStartup != lii.dwTime) {
901                 BOINCTRACE(_T("CScreensaver::InputActivityProc - Activity Detected.\n"));
902                 SetError(TRUE, SCRAPPERR_BOINCSHUTDOWNEVENT);
903                 FireInterruptSaverEvent();
904                 bAutoBreak = true;
905             }
906         } else {
907             BOINCTRACE(_T("CScreensaver::InputActivityProc - Failed to detect input activity.\n"));
908             fprintf(stdout, _T("Screen saver shutdown due to not being able to detect input activity.\n"));
909             fprintf(stdout, _T("Try rebooting, if the problem persists contact the BOINC.\n"));
910             fprintf(stdout, _T("development team.\n\n"));
911             SetError(TRUE, SCRAPPERR_BOINCSHUTDOWNEVENT);
912             FireInterruptSaverEvent();
913             bAutoBreak = true;
914         }
915         dwCounter++;
916         boinc_sleep(0.25);
917     }
918 
919     return 0;
920 }
921 
922 
923 
924 
925 // Create the thread that is used to promote the graphics window.
926 //
CreateGraphicsWindowPromotionThread()927 BOOL CScreensaver::CreateGraphicsWindowPromotionThread() {
928     DWORD dwThreadID = 0;
929     BOINCTRACE(_T("CScreensaver::CreateGraphicsWindowPromotionThread Start\n"));
930     m_hGraphicsWindowPromotionThread = CreateThread(
931         NULL,                        // default security attributes
932         0,                           // use default stack size
933         GraphicsWindowPromotionProcStub,       // thread function
934         NULL,                        // argument to thread function
935         0,                           // use default creation flags
936         &dwThreadID );               // returns the thread identifier
937 
938    if (m_hGraphicsWindowPromotionThread == NULL) {
939     	BOINCTRACE(_T("CScreensaver::CreateGraphicsWindowPromotionThread: Failed to create graphics window promotion thread '%d'\n"), GetLastError());
940         return FALSE;
941    }
942    return TRUE;
943 }
944 
945 
946 
947 
948 // Terminate the thread that is used to promote the graphics window.
949 //
DestroyGraphicsWindowPromotionThread()950 BOOL CScreensaver::DestroyGraphicsWindowPromotionThread() {
951    	BOINCTRACE(_T("CScreensaver::DestroyGraphicsWindowPromotionThread: Shutting Down...\n"));
952 
953     if (!TerminateThread(m_hGraphicsWindowPromotionThread, 0)) {
954     	BOINCTRACE(_T("CScreensaver::DestroyGraphicsWindowPromotionThread: Failed to terminate graphics window promotion thread '%d'\n"), GetLastError());
955         return FALSE;
956     }
957 
958     return TRUE;
959 }
960 
961 
962 
963 
964 // This function forwards to GraphicsWindowPromotionProc, which has access to the
965 //   "this" pointer.
966 //
GraphicsWindowPromotionProcStub(LPVOID UNUSED (lpParam))967 DWORD WINAPI CScreensaver::GraphicsWindowPromotionProcStub(LPVOID UNUSED(lpParam)) {
968     return gspScreensaver->GraphicsWindowPromotionProc();
969 }
970 
971 
972 
973 
974 // When running in screensaver mode the only two valid conditions for z-order
975 //   is that either the screensaver or graphics application is the foreground
976 //   application.  If this is not true, then blow out of the screensaver.
977 //
GraphicsWindowPromotionProc()978 DWORD WINAPI CScreensaver::GraphicsWindowPromotionProc() {
979     HWND    hwndBOINCGraphicsWindow = NULL;
980     HWND    hwndForeWindow = NULL;
981     HWND    hwndForeParent = NULL;
982     DWORD   iMonitor = 0;
983     INTERNALMONITORINFO* pMonitorInfo = NULL;
984     BOOL    bForegroundWindowIsScreensaver;
985 
986     while(true) {
987         hwndBOINCGraphicsWindow = FindWindow(BOINC_WINDOW_CLASS_NAME, NULL);
988         if (hwndBOINCGraphicsWindow) {
989             // Graphics Application found.
990 
991             // If the graphics application is not the top most window try and force it
992             //   to the top.
993             hwndForeWindow = GetForegroundWindow();
994             if (hwndForeWindow != hwndBOINCGraphicsWindow) {
995                 BOINCTRACE(_T("CScreensaver::GraphicsWindowPromotionProc - Graphics Window Detected but NOT the foreground window, bringing window to foreground.\n"));
996                 SetForegroundWindow(hwndBOINCGraphicsWindow);
997                 hwndForeWindow = GetForegroundWindow();
998                 if (hwndForeWindow != hwndBOINCGraphicsWindow) {
999                     BOINCTRACE(_T("CScreensaver::GraphicsWindowPromotionProc - Graphics Window Detected but NOT the foreground window, bringing window to foreground. (Final Try)\n"));
1000 
1001                     // This may be needed on Windows 2000 or better machines
1002                     //
1003                     // NOTE: This API appears to be a SendMessage() variant and as such
1004                     //   can lock up this thread if a graphics application deadlocks.
1005                     //
1006                     DWORD dwComponents = BSM_APPLICATIONS;
1007                     BroadcastSystemMessage(
1008                         BSF_ALLOWSFW,
1009                         &dwComponents,
1010                         WM_BOINCSFW,
1011                         NULL,
1012                         NULL
1013                     );
1014                 }
1015             }
1016         } else {
1017             // Graphics application does not exist. So check that one of the windows
1018             //   assigned to each monitor is the foreground window.
1019             bForegroundWindowIsScreensaver = FALSE;
1020             hwndForeWindow = GetForegroundWindow();
1021             hwndForeParent = GetParent(hwndForeWindow);
1022             for(iMonitor = 0; iMonitor < m_dwNumMonitors; iMonitor++) {
1023                 pMonitorInfo = &m_Monitors[iMonitor];
1024                 if ((pMonitorInfo->hWnd == hwndForeWindow) || (pMonitorInfo->hWnd == hwndForeParent))
1025                 {
1026                     bForegroundWindowIsScreensaver = TRUE;
1027                 }
1028             }
1029             if (!bForegroundWindowIsScreensaver) {
1030                 // This can happen because of a personal firewall notifications or some
1031                 //   funky IM client that thinks it has to notify the user even when in
1032                 //   screensaver mode.
1033                 BOINCTRACE(_T("CScreensaver::CheckForNotificationWindow - Unknown window detected\n"));
1034                 SetError(TRUE, SCRAPPERR_BOINCSHUTDOWNEVENT);
1035                 FireInterruptSaverEvent();
1036             }
1037         }
1038         boinc_sleep(1.0);
1039     }
1040 }
1041 
1042 
1043 
1044 
1045 // Create the thread that is used to talk to the daemon.
1046 //
CreateDataManagementThread()1047 BOOL CScreensaver::CreateDataManagementThread() {
1048     DWORD dwThreadID = 0;
1049 
1050     BOINCTRACE(_T("CScreensaver::CreateDataManagementThread Start\n"));
1051 
1052     m_hDataManagementThread = CreateThread(
1053         NULL,                        // default security attributes
1054         0,                           // use default stack size
1055         DataManagementProcStub,      // thread function
1056         NULL,                        // argument to thread function
1057         0,                           // use default creation flags
1058         &dwThreadID );               // returns the thread identifier
1059 
1060    if (m_hDataManagementThread == NULL) {
1061     	BOINCTRACE(_T("CScreensaver::CreateDataManagementThread: Failed to create data management thread '%d'\n"), GetLastError());
1062         return FALSE;
1063    }
1064 
1065    return TRUE;
1066 }
1067 
1068 
1069 
1070 
1071 // Terminate the thread that is used to talk to the daemon.
1072 //
DestroyDataManagementThread()1073 BOOL CScreensaver::DestroyDataManagementThread() {
1074     BOINCTRACE(_T("CScreensaver::DestoryDataManagementThread: Shutting down... \n"));
1075 
1076     m_bQuitDataManagementProc = true;
1077     for (int i = 0; i < 50; i++) {
1078         if (m_bDataManagementProcStopped) {
1079             BOINCTRACE(_T("CScreensaver::DestoryDataManagementThread: Thread gracefully shutdown \n"));
1080             return TRUE;
1081         }
1082         boinc_sleep(0.1);
1083     }
1084 
1085     BOINCTRACE(_T("CScreensaver::DestoryDataManagementThread: Terminating thread... \n"));
1086     if (!TerminateThread(m_hDataManagementThread, 0)) {
1087     	BOINCTRACE(_T("CScreensaver::DestoryDataManagementThread: Failed to terminate thread '%d'\n"), GetLastError());
1088         return FALSE;
1089     }
1090 
1091     return TRUE;
1092 }
1093 
1094 
1095 
1096 
1097 // This function forwards to DataManagementProc, which has access to the
1098 //       "this" pointer.
1099 //
DataManagementProcStub(LPVOID UNUSED (lpParam))1100 DWORD WINAPI CScreensaver::DataManagementProcStub(LPVOID UNUSED(lpParam)) {
1101     return gspScreensaver->DataManagementProc();
1102 }
1103 
1104 
1105 
1106 
HandleRPCError()1107 void CScreensaver::HandleRPCError()
1108 {
1109     rpc->close();
1110     m_bConnected = false;
1111 
1112     // Attempt to reinitialize the RPC client and state
1113     if (!rpc->init(NULL)) {
1114         m_bConnected = true;
1115         m_bResetCoreState = TRUE;
1116         return;
1117     }
1118 
1119     if ((time(0) - m_tThreadCreateTime) > 3) {
1120         SetError(TRUE, SCRAPPERR_BOINCNOTDETECTED);
1121     }
1122 }
1123 
1124 
1125 
1126 
1127 // Register and create the appropriate window(s)
1128 //
CreateSaverWindow()1129 HRESULT CScreensaver::CreateSaverWindow() {
1130     // Register an appropriate window class for the primary display
1131     WNDCLASS    cls;
1132     cls.hCursor        = LoadCursor(NULL, IDC_ARROW);
1133     cls.hIcon          = LoadIcon(m_hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON));
1134     cls.lpszMenuName   = NULL;
1135     cls.lpszClassName  = _T("BOINCPrimarySaverWndClass");
1136     cls.hbrBackground  = (HBRUSH) GetStockObject(BLACK_BRUSH);
1137     cls.hInstance      = m_hInstance;
1138     cls.style          = CS_VREDRAW|CS_HREDRAW;
1139     cls.lpfnWndProc    = SaverProcStub;
1140     cls.cbWndExtra     = 0;
1141     cls.cbClsExtra     = 0;
1142     RegisterClass(&cls);
1143 
1144     // Register an appropriate window class for the secondary display(s)
1145     WNDCLASS    cls2;
1146     cls2.hCursor        = LoadCursor(NULL, IDC_ARROW);
1147     cls2.hIcon          = LoadIcon(m_hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON));
1148     cls2.lpszMenuName   = NULL;
1149     cls2.lpszClassName  = _T("BOINCGenericSaverWndClass");
1150     cls2.hbrBackground  = (HBRUSH) GetStockObject(BLACK_BRUSH);
1151     cls2.hInstance      = m_hInstance;
1152     cls2.style          = CS_VREDRAW|CS_HREDRAW;
1153     cls2.lpfnWndProc    = GenericSaverProcStub;
1154     cls2.cbWndExtra     = 0;
1155     cls2.cbClsExtra     = 0;
1156     RegisterClass(&cls2);
1157 
1158     // Create the window
1159     RECT rc;
1160     DWORD dwStyle;
1161     switch (m_SaverMode) {
1162         case sm_preview:
1163             GetClientRect(m_hWndParent, &rc);
1164             dwStyle = WS_VISIBLE | WS_CHILD;
1165             AdjustWindowRect(&rc, dwStyle, FALSE);
1166             m_hWnd = CreateWindow(_T("BOINCPrimarySaverWndClass"),
1167                 m_strWindowTitle, dwStyle, rc.left, rc.top, rc.right-rc.left,
1168                 rc.bottom-rc.top, m_hWndParent, NULL, m_hInstance, this
1169             );
1170             m_Monitors[0].hWnd = m_hWnd;
1171             GetClientRect(m_hWnd, &m_rcRenderTotal);
1172             GetClientRect(m_hWnd, &m_rcRenderCurDevice);
1173             break;
1174 
1175         case sm_test:
1176             rc.left = rc.top = 50;
1177             rc.right = rc.left+600;
1178             rc.bottom = rc.top+400;
1179             dwStyle = WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU;
1180             AdjustWindowRect(&rc, dwStyle, FALSE);
1181             m_hWnd = CreateWindow(_T("BOINCPrimarySaverWndClass"),
1182                 m_strWindowTitle, dwStyle, rc.left, rc.top, rc.right-rc.left,
1183                 rc.bottom-rc.top, NULL, NULL, m_hInstance, this
1184             );
1185             m_Monitors[0].hWnd = m_hWnd;
1186             GetClientRect(m_hWnd, &m_rcRenderTotal);
1187             GetClientRect(m_hWnd, &m_rcRenderCurDevice);
1188 			SetTimer(m_hWnd, 2, 60000, NULL);
1189             break;
1190 
1191         case sm_full:
1192             dwStyle = WS_VISIBLE | WS_POPUP;
1193             m_hWnd = NULL;
1194             for(DWORD iMonitor = 0; iMonitor < m_dwNumMonitors; iMonitor++) {
1195                 INTERNALMONITORINFO* pMonitorInfo;
1196                 pMonitorInfo = &m_Monitors[iMonitor];
1197 				if (pMonitorInfo->hWnd == NULL) {
1198 					if (pMonitorInfo->hMonitor == NULL)
1199 						continue;
1200 					rc = pMonitorInfo->rcScreen;
1201 					if (0 == iMonitor) {
1202 						pMonitorInfo->hWnd = CreateWindowEx(NULL, _T("BOINCPrimarySaverWndClass"),
1203 							m_strWindowTitle, dwStyle, rc.left, rc.top, rc.right - rc.left,
1204 							rc.bottom - rc.top, NULL, NULL, m_hInstance, this);
1205 					} else {
1206 						pMonitorInfo->hWnd = CreateWindowEx(NULL, _T("BOINCGenericSaverWndClass"),
1207 							m_strWindowTitle, dwStyle, rc.left, rc.top, rc.right - rc.left,
1208 							rc.bottom - rc.top, NULL, NULL, m_hInstance, this);
1209 					}
1210 					if (pMonitorInfo->hWnd == NULL) {
1211 						return E_FAIL;
1212                     }
1213 
1214                     if (m_hWnd == NULL) {
1215 						m_hWnd = pMonitorInfo->hWnd;
1216                     }
1217 
1218 					SetTimer(pMonitorInfo->hWnd, 2, 250, NULL);
1219 				}
1220             }
1221     }
1222     if (m_hWnd == NULL) {
1223         return E_FAIL;
1224     }
1225 
1226     return S_OK;
1227 }
1228 
1229 
1230 
1231 
1232 // Run the screensaver graphics - may be preview, test or full-on mode
1233 //
DoSaver()1234 HRESULT CScreensaver::DoSaver() {
1235     // Flag as screensaver running if in full on mode
1236     if (m_SaverMode == sm_full) {
1237         BOOL bUnused;
1238         SystemParametersInfo(SPI_SCREENSAVERRUNNING, TRUE, &bUnused, 0);
1239     }
1240 
1241 
1242     // Message pump
1243     BOOL bGotMsg;
1244     MSG msg;
1245     msg.message = WM_NULL;
1246     while (msg.message != WM_QUIT) {
1247         bGotMsg = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
1248         if (bGotMsg) {
1249             TranslateMessage(&msg);
1250             DispatchMessage(&msg);
1251         } else {
1252             Sleep(10);
1253         }
1254     }
1255 
1256     return S_OK;
1257 }
1258 
1259 
1260 
1261 
DoConfig()1262 VOID CScreensaver::DoConfig() {
1263     DialogBox(NULL, MAKEINTRESOURCE(DLG_CONFIG), m_hWndParent, ConfigureDialogProcStub);
1264 }
1265 
1266 
1267 
1268 // Handle window messages for main screensaver window.
1269 //
SaverProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)1270 LRESULT CScreensaver::SaverProc(
1271     HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam
1272 ) {
1273 #ifdef _DEBUG
1274     DWORD dwMonitor = 0;
1275     for(DWORD iIndex = 0; iIndex < m_dwNumMonitors; iIndex++) {
1276 		if (hWnd == m_Monitors[iIndex].hWnd ) {
1277             dwMonitor = iIndex;
1278         }
1279     }
1280     BOINCTRACE(_T("CScreensaver::SaverProc [%d] hWnd '%d' uMsg '%X' wParam '%d' lParam '%d'\n"), dwMonitor, hWnd, uMsg, wParam, lParam);
1281 #endif
1282 
1283     switch (uMsg) {
1284         case WM_TIMER:
1285             BOINCTRACE(_T("CScreensaver::SaverProc Received WM_TIMER\n"));
1286 			switch (wParam) {
1287 				case 1:
1288 					// Initial idle time is done, proceed with initialization.
1289 					m_bWaitForInputIdle = FALSE;
1290 					KillTimer(hWnd, 1);
1291                     return 0;
1292                     break;
1293 				case 2:
1294                     // Update the position of the box every second so that it
1295                     //   does not end up off the visible area of the screen.
1296 				    UpdateErrorBox();
1297                     return 0;
1298                     break;
1299             }
1300             break;
1301         case WM_PAINT:
1302             {
1303 				BOOL    bErrorMode;
1304 				HRESULT hrError;
1305 				TCHAR	szError[400];
1306 				GetError(bErrorMode, hrError, szError, sizeof(szError)/sizeof(TCHAR));
1307 
1308 				// Show error message, if there is one
1309                 PAINTSTRUCT ps;
1310                 BeginPaint(hWnd, &ps);
1311 
1312                 // In preview mode, just fill
1313                 // the preview window with black, and the BOINC icon.
1314                 if (!bErrorMode && m_SaverMode == sm_preview) {
1315                     RECT rc;
1316                     GetClientRect(hWnd,&rc);
1317 				    FillRect(ps.hdc, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH));
1318 				    DrawIcon(ps.hdc, (rc.right / 2) - 16, (rc.bottom / 2) - 16,
1319 					    LoadIcon(m_hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON)));
1320                 } else {
1321                     DoPaint(hWnd, ps.hdc, &ps);
1322                 }
1323 
1324                 EndPaint(hWnd, &ps);
1325             }
1326 
1327             return 0;
1328             break;
1329 
1330         case WM_MOUSEMOVE:
1331             if (m_SaverMode != sm_test) {
1332                 static INT xPrev = -1;
1333                 static INT yPrev = -1;
1334                 INT xCur = LOWORD(lParam);
1335                 INT yCur = HIWORD(lParam);
1336                 if (xCur != xPrev || yCur != yPrev) {
1337                     xPrev = xCur;
1338                     yPrev = yCur;
1339                     m_dwSaverMouseMoveCount++;
1340                     if (m_dwSaverMouseMoveCount > 5) {
1341                         BOINCTRACE(_T("CScreensaver::SaverProc Received WM_MOUSEMOVE and time to InterruptSaver()\n"));
1342                         FireInterruptSaverEvent();
1343                     }
1344                 }
1345             }
1346             return 0;
1347             break;
1348 
1349         case WM_KEYDOWN:
1350         case WM_LBUTTONDOWN:
1351         case WM_RBUTTONDOWN:
1352         case WM_MBUTTONDOWN:
1353             BOINCTRACE(_T("CScreensaver::SaverProc Received WM_KEYDOWN | WM_LBUTTONDOWN | WM_RBUTTONDOWN | WM_MBUTTONDOWN\n"));
1354             if (m_SaverMode != sm_test) {
1355                 FireInterruptSaverEvent();
1356             }
1357             return 0;
1358             break;
1359 
1360         case WM_CLOSE:
1361             BOINCTRACE(_T("CScreensaver::SaverProc Received WM_CLOSE\n"));
1362             DestroyWindow(hWnd);
1363             return 0;
1364             break;
1365 
1366         case WM_DESTROY:
1367             BOINCTRACE(_T("CScreensaver::SaverProc Received WM_DESTROY\n"));
1368             if (m_SaverMode == sm_preview || m_SaverMode == sm_test) {
1369                 ShutdownSaver();
1370             }
1371             PostQuitMessage(0);
1372             return 0;
1373             break;
1374 
1375         case WM_SYSCOMMAND:
1376             BOINCTRACE(_T("CScreensaver::SaverProc Received WM_SYSCOMMAND\n"));
1377             if (m_SaverMode == sm_full) {
1378                 switch (wParam) {
1379                     case SC_NEXTWINDOW:
1380                     case SC_PREVWINDOW:
1381                     case SC_SCREENSAVE:
1382                     case SC_CLOSE:
1383                         return 0;
1384                 }
1385             }
1386             break;
1387 
1388         case WM_SETCURSOR:
1389             BOINCTRACE(_T("CScreensaver::SaverProc Received WM_SETCURSOR\n"));
1390             if (m_SaverMode == sm_full) {
1391                 // Hide cursor
1392                 SetCursor(NULL);
1393                 return TRUE;
1394             }
1395             break;
1396 
1397         case WM_POWERBROADCAST:
1398             BOINCTRACE(_T("CScreensaver::SaverProc Received WM_POWERBROADCAST\n"));
1399             if (wParam == PBT_APMQUERYSUSPEND)
1400                 FireInterruptSaverEvent();
1401             break;
1402     }
1403 
1404     if (WM_SETTIMER == uMsg) {
1405 
1406         BOINCTRACE(_T("CScreensaver::SaverProc Received WM_SETTIMER\n"));
1407         // All initialization messages have gone through.  Allow
1408         // 500ms of idle time, then proceed with initialization.
1409         SetTimer(hWnd, 1, 500, NULL);
1410         return 0;
1411 
1412     } else if (WM_INTERRUPTSAVER == uMsg) {
1413 
1414         BOINCTRACE(_T("CScreensaver::SaverProc Received WM_INTERRUPTSAVER\n"));
1415         CloseWindow(hWnd);
1416         ShutdownSaver();
1417         PostQuitMessage(0);
1418         return 0;
1419 
1420     }
1421 
1422     return DefWindowProc(hWnd, uMsg, wParam, lParam);
1423 }
1424 
1425 
1426 
1427 
1428 // Handle window messages for secondary screensaver windows.
1429 //
GenericSaverProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)1430 LRESULT CScreensaver::GenericSaverProc(
1431     HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam
1432 ) {
1433 #ifdef _DEBUG
1434     DWORD dwMonitor = 0;
1435     for(DWORD iIndex = 0; iIndex < m_dwNumMonitors; iIndex++) {
1436 		if (hWnd == m_Monitors[iIndex].hWnd ) {
1437             dwMonitor = iIndex;
1438         }
1439     }
1440     BOINCTRACE(_T("CScreensaver::GenericSaverProc [%d] hWnd '%d' uMsg '%X' wParam '%d' lParam '%d'\n"), dwMonitor, hWnd, uMsg, wParam, lParam);
1441 #endif
1442 
1443     switch (uMsg) {
1444         case WM_TIMER:
1445             BOINCTRACE(_T("CScreensaver::GenericSaverProc Received WM_TIMER\n"));
1446 			switch (wParam) {
1447 				case 1:
1448 					// Initial idle time is done, proceed with initialization.
1449 					m_bWaitForInputIdle = FALSE;
1450 					KillTimer(hWnd, 1);
1451                     return 0;
1452                     break;
1453 				case 2:
1454                     // Update the position of the box every second so that it
1455                     //   does not end up off the visible area of the screen.
1456 				    UpdateErrorBox();
1457                     return 0;
1458                     break;
1459             }
1460             break;
1461         case WM_PAINT:
1462             {
1463 				BOOL    bErrorMode;
1464 				HRESULT hrError;
1465 				TCHAR	szError[400];
1466 				GetError(bErrorMode, hrError, szError, sizeof(szError)/sizeof(TCHAR));
1467 
1468 				// Show error message, if there is one
1469                 PAINTSTRUCT ps;
1470                 BeginPaint(hWnd, &ps);
1471 
1472                 // In preview mode, just fill
1473                 // the preview window with black, and the BOINC icon.
1474                 if (!bErrorMode && m_SaverMode == sm_preview) {
1475                     RECT rc;
1476                     GetClientRect(hWnd,&rc);
1477 				    FillRect(ps.hdc, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH));
1478 				    DrawIcon(ps.hdc, (rc.right / 2) - 16, (rc.bottom / 2) - 16,
1479 					    LoadIcon(m_hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON)));
1480                 } else {
1481                     DoPaint(hWnd, ps.hdc, &ps);
1482                 }
1483 
1484                 EndPaint(hWnd, &ps);
1485             }
1486 
1487             return 0;
1488             break;
1489 
1490         case WM_MOUSEMOVE:
1491             if (m_SaverMode != sm_test) {
1492                 static INT xPrev = -1;
1493                 static INT yPrev = -1;
1494                 INT xCur = LOWORD(lParam);
1495                 INT yCur = HIWORD(lParam);
1496                 if (xCur != xPrev || yCur != yPrev) {
1497                     xPrev = xCur;
1498                     yPrev = yCur;
1499                     m_dwSaverMouseMoveCount++;
1500                     if (m_dwSaverMouseMoveCount > 5) {
1501                         BOINCTRACE(_T("CScreensaver::GenericSaverProc Received WM_MOUSEMOVE and time to InterruptSaver()\n"));
1502                         FireInterruptSaverEvent();
1503                     }
1504                 }
1505             }
1506             return 0;
1507             break;
1508 
1509         case WM_KEYDOWN:
1510         case WM_LBUTTONDOWN:
1511         case WM_RBUTTONDOWN:
1512         case WM_MBUTTONDOWN:
1513             BOINCTRACE(_T("CScreensaver::GenericSaverProc Received WM_KEYDOWN | WM_LBUTTONDOWN | WM_RBUTTONDOWN | WM_MBUTTONDOWN\n"));
1514             if (m_SaverMode != sm_test) {
1515                 FireInterruptSaverEvent();
1516             }
1517             return 0;
1518             break;
1519 
1520         case WM_SYSCOMMAND:
1521             BOINCTRACE(_T("CScreensaver::GenericSaverProc Received WM_SYSCOMMAND\n"));
1522             if (m_SaverMode == sm_full) {
1523                 switch (wParam) {
1524                     case SC_NEXTWINDOW:
1525                     case SC_PREVWINDOW:
1526                     case SC_SCREENSAVE:
1527                     case SC_CLOSE:
1528                         return 0;
1529                 }
1530             }
1531             break;
1532 
1533         case WM_SETCURSOR:
1534             BOINCTRACE(_T("CScreensaver::GenericSaverProc Received WM_SETCURSOR\n"));
1535             if (m_SaverMode == sm_full) {
1536                 // Hide cursor
1537                 SetCursor(NULL);
1538                 return TRUE;
1539             }
1540             break;
1541 
1542         case WM_POWERBROADCAST:
1543             BOINCTRACE(_T("CScreensaver::GenericSaverProc Received WM_POWERBROADCAST\n"));
1544             if (wParam == PBT_APMQUERYSUSPEND)
1545                 FireInterruptSaverEvent();
1546             break;
1547     }
1548 
1549     if (WM_SETTIMER == uMsg) {
1550 
1551         BOINCTRACE(_T("CScreensaver::GenericSaverProc Received WM_SETTIMER\n"));
1552         // All initialization messages have gone through.  Allow
1553         // 500ms of idle time, then proceed with initialization.
1554         SetTimer(hWnd, 1, 500, NULL);
1555         return 0;
1556 
1557     } else if (WM_INTERRUPTSAVER == uMsg) {
1558 
1559         BOINCTRACE(_T("CScreensaver::GenericSaverProc Received WM_INTERRUPTSAVER\n"));
1560         CloseWindow(hWnd);
1561         return 0;
1562 
1563     }
1564 
1565     return DefWindowProc(hWnd, uMsg, wParam, lParam);
1566 }
1567 
1568 
1569 
1570 
ConvertSliderPositionToTime(DWORD dwPosition)1571 DWORD CScreensaver::ConvertSliderPositionToTime( DWORD dwPosition ) {
1572     return dwTableSliderPositionToTime[dwPosition];
1573 }
1574 
1575 
1576 
1577 
ConvertTimeToSliderPosition(DWORD dwMinutes)1578 DWORD CScreensaver::ConvertTimeToSliderPosition( DWORD dwMinutes ) {
1579     DWORD dwPosition = 0;
1580     if (0 >= dwMinutes) {
1581         dwPosition = MAXSLIDERTIC;     // Never
1582     } else {
1583         for (unsigned int i = 0; i <= MAXSLIDERTIC - 1; i++) {
1584             if (dwTableSliderPositionToTime[i] <= dwMinutes) {
1585                 dwPosition = i;
1586             }
1587         }
1588     }
1589     return dwPosition;
1590 }
1591 
1592 
1593 
1594 
InitializeDefaultSlider(HWND hwndDlg,UINT uControl)1595 VOID CScreensaver::InitializeDefaultSlider( HWND hwndDlg, UINT uControl ) {
1596     HWND hwndSlider = GetDlgItem(hwndDlg, uControl);
1597     if (hwndSlider) {
1598         SendMessage(hwndSlider, TBM_SETRANGE, TRUE, MAKELONG(0, MAXSLIDERTIC));
1599         SendMessage(hwndSlider, TBM_SETLINESIZE, 0, 1);
1600         SendMessage(hwndSlider, TBM_SETPAGESIZE, 0, 1);
1601     }
1602 }
1603 
1604 
1605 
1606 
GetSliderPosition(HWND hwndDlg,UINT uControl)1607 DWORD CScreensaver::GetSliderPosition( HWND hwndDlg, UINT uControl ) {
1608     HWND hwndSlider = GetDlgItem(hwndDlg, uControl);
1609     if (hwndSlider) {
1610         return SendMessage(hwndSlider, TBM_GETPOS, 0, 0);
1611     }
1612     return 0;
1613 }
1614 
1615 
1616 
1617 
SetSliderPosition(HWND hwndDlg,UINT uControl,DWORD dwPosition)1618 VOID CScreensaver::SetSliderPosition( HWND hwndDlg, UINT uControl, DWORD dwPosition ) {
1619     HWND hwndSlider = GetDlgItem(hwndDlg, uControl);
1620     if (hwndSlider) {
1621         SendMessage(hwndSlider, TBM_SETPOS, TRUE, dwPosition);
1622     }
1623 }
1624 
1625 
1626 
1627 
ConfigureDialogProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM UNUSED (lParam))1628 INT_PTR CScreensaver::ConfigureDialogProc(
1629     HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM UNUSED(lParam)
1630 ){
1631     DWORD dwEnableBlankTime = 0;
1632     DWORD dwBlankTime = 0;
1633     DWORD dwDefaultTime = 0;
1634     DWORD dwRunTime = 0;
1635     DWORD dwSwitchTime = 0;
1636 
1637 	switch (uMsg) {
1638     case WM_INITDIALOG:
1639         // Initialize slider controls
1640         InitializeDefaultSlider(hWnd, IDC_SLIDER_BLANKTIME);
1641         InitializeDefaultSlider(hWnd, IDC_SLIDER_DEFAULTTIME);
1642         InitializeDefaultSlider(hWnd, IDC_SLIDER_RUNTIME);
1643         InitializeDefaultSlider(hWnd, IDC_SLIDER_SWITCHTIME);
1644 
1645         // Get the last set of saved values, if nothing is set
1646         // use defaults.
1647         if (!UtilGetRegKey(REG_ENABLE_BLANK, dwEnableBlankTime)) {
1648             dwEnableBlankTime = 0;
1649         }
1650         if (!UtilGetRegKey(REG_BLANK_TIME, dwBlankTime)) {
1651             dwBlankTime = GFX_BLANK_PERIOD ? GFX_BLANK_PERIOD / 60 : 0;
1652         }
1653         if (!UtilGetRegKey(REG_DEFAULT_TIME, dwDefaultTime)) {
1654             dwDefaultTime = GFX_DEFAULT_PERIOD ? GFX_DEFAULT_PERIOD / 60 : 0;
1655         }
1656         if (!UtilGetRegKey(REG_RUN_TIME, dwRunTime)) {
1657             dwRunTime = GFX_SCIENCE_PERIOD ? GFX_SCIENCE_PERIOD / 60 : 0;
1658         }
1659         if (!UtilGetRegKey(REG_SWITCH_TIME, dwSwitchTime)) {
1660             dwSwitchTime = GFX_CHANGE_PERIOD ? GFX_CHANGE_PERIOD / 60 : 0;
1661         }
1662 
1663         // Set the legacy value if needed
1664         if (!dwEnableBlankTime) {
1665             dwBlankTime = 0;
1666         }
1667 
1668         // Update Slider Controls with previously saved values
1669         SetSliderPosition(hWnd, IDC_SLIDER_BLANKTIME, ConvertTimeToSliderPosition(dwBlankTime));
1670         SetSliderPosition(hWnd, IDC_SLIDER_DEFAULTTIME, ConvertTimeToSliderPosition(dwDefaultTime));
1671         SetSliderPosition(hWnd, IDC_SLIDER_RUNTIME, ConvertTimeToSliderPosition(dwRunTime));
1672         SetSliderPosition(hWnd, IDC_SLIDER_SWITCHTIME, ConvertTimeToSliderPosition(dwSwitchTime));
1673 
1674         return TRUE;
1675     case WM_COMMAND:
1676         switch(LOWORD(wParam))
1677         {
1678         case IDOK:
1679             dwBlankTime = ConvertSliderPositionToTime(GetSliderPosition(hWnd, IDC_SLIDER_BLANKTIME));
1680             dwDefaultTime = ConvertSliderPositionToTime(GetSliderPosition(hWnd, IDC_SLIDER_DEFAULTTIME));
1681             dwRunTime = ConvertSliderPositionToTime(GetSliderPosition(hWnd, IDC_SLIDER_RUNTIME));
1682             dwSwitchTime = ConvertSliderPositionToTime(GetSliderPosition(hWnd, IDC_SLIDER_SWITCHTIME));
1683 
1684             // Set the legacy value if needed
1685             if (dwBlankTime) {
1686                 dwEnableBlankTime = 1;
1687             }
1688 
1689             UtilSetRegKey(REG_ENABLE_BLANK, dwEnableBlankTime);
1690             UtilSetRegKey(REG_BLANK_TIME, dwBlankTime);
1691             UtilSetRegKey(REG_DEFAULT_TIME, dwDefaultTime);
1692             UtilSetRegKey(REG_RUN_TIME, dwRunTime);
1693             UtilSetRegKey(REG_SWITCH_TIME, dwSwitchTime);
1694 
1695             // Fall Through
1696         case IDCANCEL:
1697             EndDialog(hWnd, wParam);
1698             return TRUE;
1699         }
1700 	}
1701 	return FALSE;
1702 }
1703 
1704 
1705 
1706 
1707 // This function forwards all window messages to SaverProc, which has
1708 //       access to the "this" pointer.
1709 //
SaverProcStub(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)1710 LRESULT CALLBACK CScreensaver::SaverProcStub(
1711     HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam
1712 ) {
1713     return gspScreensaver->SaverProc(hWnd, uMsg, wParam, lParam);
1714 }
1715 
1716 
1717 
1718 
1719 // This function forwards all window messages to GenericSaverProc, which has
1720 //       access to the "this" pointer.
1721 //
GenericSaverProcStub(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)1722 LRESULT CALLBACK CScreensaver::GenericSaverProcStub(
1723     HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam
1724 ) {
1725     return gspScreensaver->GenericSaverProc(hWnd, uMsg, wParam, lParam);
1726 }
1727 
1728 
1729 
1730 
ConfigureDialogProcStub(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)1731 INT_PTR CALLBACK CScreensaver::ConfigureDialogProcStub(
1732     HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam
1733 ) {
1734     return gspScreensaver->ConfigureDialogProc(hwndDlg, uMsg, wParam, lParam);
1735 }
1736 
1737 
1738 
1739 
ShutdownSaver()1740 VOID CScreensaver::ShutdownSaver() {
1741     BOINCTRACE(_T("CScreensaver::ShutdownSaver Function Begin\n"));
1742 
1743     // Unflag screensaver running if in full on mode
1744     if (m_SaverMode == sm_full) {
1745         BOOL bUnused;
1746         SystemParametersInfo(SPI_SCREENSAVERRUNNING, FALSE, &bUnused, 0);
1747     }
1748 
1749     // Kill the currently executing graphics application
1750     terminate_screensaver(m_hGraphicsApplication, &m_running_result);
1751 
1752     BOINCTRACE(_T("CScreensaver::ShutdownSaver Function End\n"));
1753 }
1754 
1755 
1756 
1757 
FireInterruptSaverEvent()1758 VOID CScreensaver::FireInterruptSaverEvent() {
1759     BOINCTRACE(_T("CScreensaver::FireInterruptSaverEvent Function Begin\n"));
1760 
1761     // Handle secondary curtains first
1762     for(DWORD iIndex = 1; iIndex < m_dwNumMonitors; iIndex++) {
1763 		if ( m_Monitors[iIndex].hWnd ) {
1764             PostMessage(m_Monitors[iIndex].hWnd, WM_INTERRUPTSAVER, NULL, NULL);
1765         }
1766     }
1767 
1768     // Handle primary curtain
1769     PostMessage(m_Monitors[0].hWnd, WM_INTERRUPTSAVER, NULL, NULL);
1770 
1771     BOINCTRACE(_T("CScreensaver::FireInterruptSaverEvent Function End\n"));
1772 }
1773 
1774 
1775 
1776 
1777 // Update the box that shows the error message
1778 //
UpdateErrorBox()1779 VOID CScreensaver::UpdateErrorBox() {
1780     INTERNALMONITORINFO* pMonitorInfo;
1781     HWND hwnd;
1782     RECT rcBounds;
1783     static DWORD dwTimeLast = 0;
1784     DWORD dwTimeNow;
1785     FLOAT fTimeDelta;
1786 
1787 
1788     // Update timing to determine how much to move error box
1789     if (dwTimeLast == 0) {
1790         dwTimeLast = timeGetTime();
1791     }
1792 
1793     dwTimeNow = timeGetTime();
1794     fTimeDelta = (FLOAT)(dwTimeNow - dwTimeLast) / 10000.0f;
1795     dwTimeLast = dwTimeNow;
1796 
1797     for(DWORD iMonitor = 0; iMonitor < m_dwNumMonitors; iMonitor++) {
1798         pMonitorInfo = &m_Monitors[iMonitor];
1799         hwnd = pMonitorInfo->hWnd;
1800         if (hwnd == NULL)
1801             continue;
1802         if (m_SaverMode == sm_full) {
1803             rcBounds = pMonitorInfo->rcScreen;
1804             ScreenToClient(hwnd, (POINT*)&rcBounds.left);
1805             ScreenToClient(hwnd, (POINT*)&rcBounds.right);
1806         } else {
1807             rcBounds = m_rcRenderTotal;
1808         }
1809 
1810         if (pMonitorInfo->widthError == 0) {
1811             if (m_SaverMode == sm_preview)                {
1812                 pMonitorInfo->widthError = (float) (rcBounds.right - rcBounds.left);
1813                 pMonitorInfo->heightError = (float) (rcBounds.bottom - rcBounds.top);
1814                 pMonitorInfo->xError = 0.0f;
1815                 pMonitorInfo->yError = 0.0f;
1816                 pMonitorInfo->xVelError = 0.0f;
1817                 pMonitorInfo->yVelError = 0.0f;
1818                 InvalidateRect(hwnd, NULL, FALSE);    // Invalidate the hwnd so it gets drawn
1819                 UpdateWindow(hwnd);
1820             } else {
1821                 pMonitorInfo->widthError = 454;
1822                 pMonitorInfo->heightError = 320;
1823                 pMonitorInfo->xError = (rcBounds.right + rcBounds.left - pMonitorInfo->widthError) / 2.0f;
1824                 pMonitorInfo->yError = (rcBounds.bottom + rcBounds.top - pMonitorInfo->heightError) / 2.0f;
1825                 pMonitorInfo->xVelError = (rcBounds.right - rcBounds.left) / 10.0f;
1826                 pMonitorInfo->yVelError = (rcBounds.bottom - rcBounds.top) / 20.0f;
1827             }
1828         } else {
1829             if (m_SaverMode != sm_preview) {
1830                 RECT rcOld;
1831                 RECT rcNew;
1832 
1833                 SetRect(&rcOld, (INT)pMonitorInfo->xError, (INT)pMonitorInfo->yError,
1834                     (INT)(pMonitorInfo->xError + pMonitorInfo->widthError),
1835                     (INT)(pMonitorInfo->yError + pMonitorInfo->heightError));
1836 
1837                 // Update rect velocity
1838                 if ((pMonitorInfo->xError + pMonitorInfo->xVelError * fTimeDelta +
1839                     pMonitorInfo->widthError > rcBounds.right && pMonitorInfo->xVelError > 0.0f) ||
1840                     (pMonitorInfo->xError + pMonitorInfo->xVelError * fTimeDelta <
1841                     rcBounds.left && pMonitorInfo->xVelError < 0.0f)
1842                 ) {
1843                     pMonitorInfo->xVelError = -pMonitorInfo->xVelError;
1844                 }
1845                 if ((pMonitorInfo->yError + pMonitorInfo->yVelError * fTimeDelta +
1846                     pMonitorInfo->heightError > rcBounds.bottom && pMonitorInfo->yVelError > 0.0f) ||
1847                     (pMonitorInfo->yError + pMonitorInfo->yVelError * fTimeDelta <
1848                     rcBounds.top && pMonitorInfo->yVelError < 0.0f)
1849                 ) {
1850                     pMonitorInfo->yVelError = -pMonitorInfo->yVelError;
1851                 }
1852                 // Update rect position
1853                 pMonitorInfo->xError += pMonitorInfo->xVelError * fTimeDelta;
1854                 pMonitorInfo->yError += pMonitorInfo->yVelError * fTimeDelta;
1855 
1856                 SetRect(&rcNew, (INT)pMonitorInfo->xError, (INT)pMonitorInfo->yError,
1857                     (INT)(pMonitorInfo->xError + pMonitorInfo->widthError),
1858                     (INT)(pMonitorInfo->yError + pMonitorInfo->heightError));
1859 
1860 				if ((dwTimeNow - pMonitorInfo->dwTimeLastUpdate) > 1000)
1861 				{
1862 					pMonitorInfo->dwTimeLastUpdate = dwTimeNow;
1863 
1864                     InvalidateRect(hwnd, NULL, TRUE);
1865                     UpdateWindow(hwnd);
1866 				}
1867             }
1868         }
1869     }
1870 }
1871 
1872 
1873 
1874 
DoPaint(HWND hwnd,HDC hdc,LPPAINTSTRUCT lpps)1875 VOID CScreensaver::DoPaint(HWND hwnd, HDC hdc, LPPAINTSTRUCT lpps) {
1876     DWORD iMonitor = 0;
1877     INTERNALMONITORINFO* pMonitorInfo = NULL;
1878     HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
1879 
1880     for(iMonitor = 0; iMonitor < m_dwNumMonitors; iMonitor++) {
1881         pMonitorInfo = &m_Monitors[iMonitor];
1882         if (pMonitorInfo->hMonitor == hMonitor)
1883             break;
1884     }
1885 
1886     if (iMonitor == m_dwNumMonitors) {
1887         return;
1888     }
1889 
1890     // Retrieve the latest piece of error information
1891     BOOL    bErrorMode;
1892     HRESULT hrError;
1893     TCHAR	szError[400];
1894     GetError(bErrorMode, hrError, szError, sizeof(szError)/sizeof(TCHAR));
1895 
1896 
1897     // Start drawing the goods
1898     RECT    rc;
1899     RECT    rc2;
1900     RECT    rcOrginal;
1901 	int		iTextHeight;
1902 
1903 	static HBRUSH	hbrushBlack = (HBRUSH)GetStockObject(BLACK_BRUSH);
1904 	static HBRUSH	hbrushRed = (HBRUSH)CreateSolidBrush(RGB(255,0,0));
1905 	static HBITMAP  hbmp = LoadBitmap(m_hInstance, MAKEINTRESOURCE(IDB_BOINC));
1906 
1907 
1908 	// Start off with a black screen and then draw on top of it.
1909 	FillRect(hdc, &lpps->rcPaint, hbrushBlack);
1910 
1911 
1912     // If the screensaver has switched to a blanked state or not in an error mode,
1913     // we should exit here so the screen has been erased to black.
1914     if (!bErrorMode) {
1915         return;
1916     }
1917 
1918 
1919     SetRect(&rc, (INT)pMonitorInfo->xError, (INT)pMonitorInfo->yError,
1920         (INT)(pMonitorInfo->xError + pMonitorInfo->widthError),
1921         (INT)(pMonitorInfo->yError + pMonitorInfo->heightError)
1922     );
1923 
1924     // This fill rect is useful when testing
1925     //FillRect(hdc, &rc, hbrushRed);
1926 
1927     rcOrginal = rc;
1928 
1929 
1930     // Draw the bitmap rectangle and copy the bitmap into
1931     // it. the bitmap is centered in the rectangle by adding 2
1932 	// to the left and top coordinates of the bitmap rectangle,
1933 	// and subtracting 4 from the right and bottom coordinates.
1934     BITMAP bm;
1935     GetObject(hbmp, sizeof(BITMAP), (LPSTR)&bm);
1936 
1937     HDC hdcTemp = CreateCompatibleDC(hdc);
1938     SelectObject(hdcTemp, hbmp);
1939 
1940 	long left = rc.left + (pMonitorInfo->widthError - 4 - bm.bmWidth)/2;
1941 	long top = rc.top + 2;
1942 
1943     POINT pt;
1944     pt.x = bm.bmWidth;
1945     pt.y = bm.bmHeight;
1946     DPtoLP(hdcTemp, &pt, 1);
1947 
1948     TransparentBlt(
1949         hdc, left, top, pt.x, pt.y, hdcTemp, 0, 0, pt.x, pt.y, RGB(255, 0, 255)
1950     );
1951     DeleteDC(hdcTemp);
1952 
1953 	// Draw text in the center of the frame
1954 	SetBkColor(hdc, RGB(0,0,0));           // Black
1955 	SetTextColor(hdc, RGB(255,255,255));   // White
1956 
1957 	// Set font
1958 	HFONT hFont;
1959     hFont = CreateFont(
1960         0,
1961         0,
1962         0,
1963         0,
1964         FW_DONTCARE,
1965         FALSE,
1966         FALSE,
1967         0,
1968         ANSI_CHARSET,
1969         OUT_DEFAULT_PRECIS,
1970         CLIP_DEFAULT_PRECIS,
1971         DEFAULT_QUALITY,
1972         DEFAULT_PITCH | FF_SWISS,
1973         "Arial Narrow"
1974     );
1975 
1976     if(hFont) SelectObject(hdc, hFont);
1977 
1978     // Try using the "Arial Narrow" font, if that fails use whatever
1979     // the system default font is.  Something is better than nothing.
1980     rc2 = rc;
1981     iTextHeight = DrawText(hdc, szError, -1, &rc, DT_CENTER | DT_CALCRECT);
1982 	rc = rc2;
1983 	rc2.top += bm.bmHeight+20;
1984     DrawText(hdc, szError, -1, &rc2, DT_CENTER);
1985 
1986     if(hFont) DeleteObject(hFont);
1987 }
1988 
1989