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