efa778436c02c095c5c825440b95e68af7ccb7ee
[reactos.git] / reactos / dll / cpl / sysdm / general.c
1 /*
2 * PROJECT: ReactOS System Control Panel Applet
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/cpl/sysdm/general.c
5 * PURPOSE: General System Information
6 * COPYRIGHT: Copyright Thomas Weidenmueller <w3seek@reactos.org>
7 * Copyright 2006 Ged Murphy <gedmurphy@gmail.com>
8 * Copyright 2006-2007 Colin Finck <mail@colinfinck.de>
9 *
10 */
11
12 #include "precomp.h"
13
14 #include <winnls.h>
15 #include <powrprof.h>
16
17 #define ANIM_STEP 2
18 #define ANIM_TIME 50
19
20 typedef struct _IMGINFO
21 {
22 HBITMAP hBitmap;
23 INT cxSource;
24 INT cySource;
25 INT iPLanes;
26 INT iBits;
27 } IMGINFO, *PIMGINFO;
28
29 PIMGINFO pImgInfo = NULL;
30 BLENDFUNCTION BlendFunc = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
31
32 VOID ShowLastWin32Error(HWND hWndOwner)
33 {
34 LPTSTR lpMsg;
35 DWORD LastError;
36
37 LastError = GetLastError();
38
39 if ((LastError == 0) ||
40 !FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
41 FORMAT_MESSAGE_FROM_SYSTEM,
42 NULL,
43 LastError,
44 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
45 (LPTSTR)&lpMsg,
46 0,
47 NULL))
48 {
49 return;
50 }
51
52 MessageBox(hWndOwner, lpMsg, NULL, MB_OK | MB_ICONERROR);
53
54 LocalFree((LPVOID)lpMsg);
55 }
56
57
58 static VOID InitLogo(HWND hwndDlg)
59 {
60 BITMAP logoBitmap;
61 BITMAP maskBitmap;
62 BITMAPINFO bmpi;
63 HDC hDC = GetDC(hwndDlg);
64 HDC hDCLogo = CreateCompatibleDC(NULL);
65 HDC hDCMask = CreateCompatibleDC(NULL);
66 HBITMAP hMask, hLogo, hAlphaLogo = NULL;
67 COLORREF *pBits;
68 INT line, column;
69
70 ZeroMemory(pImgInfo, sizeof(*pImgInfo));
71 ZeroMemory(&bmpi, sizeof(bmpi));
72
73 hLogo = (HBITMAP)LoadImage(hApplet, MAKEINTRESOURCE(IDB_ROSBMP), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
74 hMask = (HBITMAP)LoadImage(hApplet, MAKEINTRESOURCE(IDB_ROSMASK), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
75
76 if (hLogo != NULL && hMask != NULL)
77 {
78 GetObject(hLogo, sizeof(BITMAP), &logoBitmap);
79 GetObject(hMask, sizeof(BITMAP), &maskBitmap);
80
81 if(logoBitmap.bmHeight != maskBitmap.bmHeight || logoBitmap.bmWidth != maskBitmap.bmWidth)
82 return;
83
84 pImgInfo->cxSource = logoBitmap.bmWidth;
85 pImgInfo->cySource = logoBitmap.bmHeight;
86
87 bmpi.bmiHeader.biSize = sizeof(BITMAPINFO);
88 bmpi.bmiHeader.biWidth = logoBitmap.bmWidth;
89 bmpi.bmiHeader.biHeight = logoBitmap.bmHeight;
90 bmpi.bmiHeader.biPlanes = 1;
91 bmpi.bmiHeader.biBitCount = 32;
92 bmpi.bmiHeader.biCompression = BI_RGB;
93 bmpi.bmiHeader.biSizeImage = 4 * logoBitmap.bmWidth * logoBitmap.bmHeight;
94
95 hAlphaLogo = CreateDIBSection(hDC, &bmpi, DIB_RGB_COLORS, (PVOID*)&pBits, 0, 0);
96
97 if(!hAlphaLogo)
98 return;
99
100 SelectObject(hDCLogo, hLogo);
101 SelectObject(hDCMask, hMask);
102
103 for(line = logoBitmap.bmHeight - 1; line >= 0; line--)
104 {
105 for(column = 0; column < logoBitmap.bmWidth; column++)
106 {
107 COLORREF alpha = GetPixel(hDCMask, column, line) & 0xFF;
108 COLORREF Color = GetPixel(hDCLogo, column, line);
109 DWORD r, g, b;
110
111 r = GetRValue(Color) * alpha / 255;
112 g = GetGValue(Color) * alpha / 255;
113 b = GetBValue(Color) * alpha / 255;
114
115 *pBits++ = b | g << 8 | r << 16 | alpha << 24;
116 }
117 }
118 }
119
120 pImgInfo->hBitmap = hAlphaLogo;
121 pImgInfo->cxSource = logoBitmap.bmWidth;
122 pImgInfo->cySource = logoBitmap.bmHeight;
123 pImgInfo->iBits = logoBitmap.bmBitsPixel;
124 pImgInfo->iPLanes = logoBitmap.bmPlanes;
125
126 DeleteObject(hLogo);
127 DeleteObject(hMask);
128 DeleteDC(hDCLogo);
129 DeleteDC(hDCMask);
130
131 }
132
133 LRESULT CALLBACK RosImageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
134 {
135 static UINT timerid = 0, top = 0, offset;
136 static HBITMAP hCreditsBitmap;
137
138 switch (uMsg)
139 {
140 case WM_LBUTTONDBLCLK:
141 if (wParam & (MK_CONTROL | MK_SHIFT))
142 {
143 if (timerid == 0)
144 {
145 HDC hCreditsDC, hLogoDC;
146 HFONT hFont;
147 NONCLIENTMETRICS ncm;
148 RECT rcCredits;
149 TCHAR szCredits[2048];
150 INT iDevsHeight;
151
152 top = 0;
153 offset = 0;
154 hCreditsDC = CreateCompatibleDC(GetDC(NULL));
155 hLogoDC = CreateCompatibleDC(hCreditsDC);
156
157 if (hCreditsDC == NULL || hLogoDC == NULL)
158 break;
159
160 SetRect(&rcCredits, 0, 0, 0, 0);
161
162 ncm.cbSize = sizeof(NONCLIENTMETRICS);
163 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0);
164
165 hFont = CreateFontIndirect(&ncm.lfMessageFont);
166 SelectObject(hCreditsDC, hFont);
167
168 LoadString(hApplet, IDS_DEVS, szCredits, sizeof(szCredits) / sizeof(TCHAR));
169 DrawText(hCreditsDC, szCredits, -1, &rcCredits, DT_CALCRECT);
170
171 iDevsHeight = rcCredits.bottom - rcCredits.top;
172
173 hCreditsBitmap = CreateBitmap(pImgInfo->cxSource, (2 * pImgInfo->cySource) + iDevsHeight + 1, pImgInfo->iPLanes, pImgInfo->iBits, NULL);
174
175 if(!hCreditsBitmap)
176 break;
177
178 SelectObject(hLogoDC, pImgInfo->hBitmap);
179 SelectObject(hCreditsDC, hCreditsBitmap);
180
181 offset += pImgInfo->cySource;
182
183 SetRect(&rcCredits, 0, 0, pImgInfo->cxSource, (2 * pImgInfo->cySource) + iDevsHeight + 1);
184 FillRect(hCreditsDC, &rcCredits, GetSysColorBrush(COLOR_3DFACE));
185
186 SetRect(&rcCredits, 0, offset, pImgInfo->cxSource, offset + iDevsHeight + 1);
187 SetBkMode(hCreditsDC, TRANSPARENT);
188
189 OffsetRect(&rcCredits, 1, 1);
190 SetTextColor(hCreditsDC, GetSysColor(COLOR_BTNSHADOW));
191 DrawText(hCreditsDC, szCredits, -1, &rcCredits, DT_CENTER);
192
193 OffsetRect(&rcCredits, -1, -1);
194 SetTextColor(hCreditsDC, GetSysColor(COLOR_WINDOWTEXT));
195 DrawText(hCreditsDC, szCredits, -1, &rcCredits, DT_CENTER);
196
197 offset += iDevsHeight;
198
199 AlphaBlend(hCreditsDC, 0, 0, pImgInfo->cxSource, pImgInfo->cySource, hLogoDC, 0, 0, pImgInfo->cxSource, pImgInfo->cySource, BlendFunc);
200 AlphaBlend(hCreditsDC, 0, offset, pImgInfo->cxSource, pImgInfo->cySource, hLogoDC, 0, 0, pImgInfo->cxSource, pImgInfo->cySource, BlendFunc);
201
202 DeleteDC(hLogoDC);
203 DeleteDC(hCreditsDC);
204
205 timerid = SetTimer(hwnd, 1, ANIM_TIME, NULL);
206 }
207 }
208 break;
209 case WM_LBUTTONDOWN:
210 if (timerid)
211 {
212 RECT rcCredits;
213 HDC hDC = GetDC(hwnd);
214
215 GetClientRect(hwnd, &rcCredits);
216 SetRect(&rcCredits, 0, 0, rcCredits.right, pImgInfo->cySource);
217 FillRect(hDC, &rcCredits, GetSysColorBrush(COLOR_3DFACE));
218
219 KillTimer(hwnd, timerid);
220 DeleteObject(hCreditsBitmap);
221 InvalidateRect(hwnd, NULL, FALSE);
222
223 top = 0;
224 timerid = 0;
225 }
226 break;
227 case WM_TIMER:
228 top += ANIM_STEP;
229
230 if (top > offset)
231 {
232 RECT rcCredits;
233 HDC hDC = GetDC(hwnd);
234
235 GetClientRect(hwnd, &rcCredits);
236 SetRect(&rcCredits, 0, 0, rcCredits.right, pImgInfo->cySource);
237 FillRect(hDC, &rcCredits, GetSysColorBrush(COLOR_3DFACE));
238
239 KillTimer(hwnd, timerid);
240 DeleteObject(hCreditsBitmap);
241
242 top = 0;
243 timerid = 0;
244 }
245
246 InvalidateRect(hwnd, NULL, FALSE);
247 break;
248 case WM_PAINT:
249 {
250 PAINTSTRUCT PS;
251 HDC hdcMem, hdc;
252 LONG left;
253
254 hdc = wParam != 0 ? (HDC)wParam : BeginPaint(hwnd, &PS);
255
256 GetClientRect(hwnd, &PS.rcPaint);
257
258 /* Position image in center of dialog */
259 left = (PS.rcPaint.right - pImgInfo->cxSource) / 2;
260 hdcMem = CreateCompatibleDC(hdc);
261
262 if (hdcMem != NULL)
263 {
264 if(timerid != 0)
265 {
266 SelectObject(hdcMem, hCreditsBitmap);
267 BitBlt(hdc, left, PS.rcPaint.top, PS.rcPaint.right - PS.rcPaint.left, PS.rcPaint.top + pImgInfo->cySource, hdcMem, 0, top, SRCCOPY);
268 }
269 else
270 {
271 SelectObject(hdcMem, pImgInfo->hBitmap);
272 AlphaBlend(hdc, left, PS.rcPaint.top, pImgInfo->cxSource, pImgInfo->cySource, hdcMem, 0, 0, pImgInfo->cxSource, pImgInfo->cySource, BlendFunc);
273 }
274
275 DeleteDC(hdcMem);
276 }
277
278 if (wParam == 0)
279 EndPaint(hwnd,&PS);
280 break;
281 }
282 }
283 return TRUE;
284 }
285
286 static VOID
287 SetRegTextData(HWND hwnd,
288 HKEY hKey,
289 LPTSTR Value,
290 UINT uID)
291 {
292 LPTSTR lpBuf = NULL;
293 DWORD BufSize = 0;
294 DWORD Type;
295
296 if (RegQueryValueEx(hKey,
297 Value,
298 NULL,
299 &Type,
300 NULL,
301 &BufSize) == ERROR_SUCCESS)
302 {
303 lpBuf = HeapAlloc(GetProcessHeap(),
304 0,
305 BufSize);
306 if (!lpBuf)
307 return;
308
309 if (RegQueryValueEx(hKey,
310 Value,
311 NULL,
312 &Type,
313 (PBYTE)lpBuf,
314 &BufSize) == ERROR_SUCCESS)
315 {
316 SetDlgItemText(hwnd,
317 uID,
318 lpBuf);
319 }
320
321 HeapFree(GetProcessHeap(),
322 0,
323 lpBuf);
324 }
325 }
326
327 static INT
328 SetProcNameString(HWND hwnd,
329 HKEY hKey,
330 LPTSTR Value,
331 UINT uID1,
332 UINT uID2)
333 {
334 LPTSTR lpBuf = NULL;
335 DWORD BufSize = 0;
336 DWORD Type;
337 INT Ret = 0;
338 TCHAR szBuf[31];
339 TCHAR* szLastSpace;
340 INT LastSpace = 0;
341
342 if (RegQueryValueEx(hKey,
343 Value,
344 NULL,
345 &Type,
346 NULL,
347 &BufSize) == ERROR_SUCCESS)
348 {
349 lpBuf = HeapAlloc(GetProcessHeap(),
350 0,
351 BufSize);
352 if (!lpBuf)
353 return 0;
354
355 if (RegQueryValueEx(hKey,
356 Value,
357 NULL,
358 &Type,
359 (PBYTE)lpBuf,
360 &BufSize) == ERROR_SUCCESS)
361 {
362 if (BufSize > ((30 + 1) * sizeof(TCHAR)))
363 {
364 /* Wrap the Processor Name String like XP does: *
365 * - Take the first 30 characters and look for the last space. *
366 * Then wrap the string after this space. *
367 * - If no space is found, wrap the string after character 30. *
368 * *
369 * For example the Processor Name String of a Pentium 4 is right-aligned. *
370 * With this wrapping the first line looks centered. */
371
372 _tcsncpy(szBuf, lpBuf, 30);
373 szBuf[30] = 0;
374 szLastSpace = _tcsrchr(szBuf, ' ');
375
376 if (szLastSpace == 0)
377 {
378 LastSpace = 30;
379 }
380 else
381 {
382 LastSpace = (szLastSpace - szBuf);
383 szBuf[LastSpace] = 0;
384 }
385
386 _tcsncpy(szBuf, lpBuf, LastSpace);
387
388 SetDlgItemText(hwnd,
389 uID1,
390 szBuf);
391
392 SetDlgItemText(hwnd,
393 uID2,
394 lpBuf+LastSpace+1);
395
396 /* Return the number of used lines */
397 Ret = 2;
398 }
399 else
400 {
401 SetDlgItemText(hwnd,
402 uID1,
403 lpBuf);
404
405 Ret = 1;
406 }
407 }
408
409 HeapFree(GetProcessHeap(),
410 0,
411 lpBuf);
412 }
413
414 return Ret;
415 }
416
417 static VOID
418 MakeFloatValueString(double* dFloatValue,
419 LPTSTR szOutput,
420 LPTSTR szAppend)
421 {
422 TCHAR szDecimalSeparator[4];
423
424 /* Get the decimal separator for the current locale */
425 if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, szDecimalSeparator, sizeof(szDecimalSeparator) / sizeof(TCHAR)) > 0)
426 {
427 UCHAR uDecimals;
428 UINT uIntegral;
429
430 /* Show the value with two decimals */
431 uIntegral = (UINT)*dFloatValue;
432 uDecimals = (UCHAR)((UINT)(*dFloatValue * 100) - uIntegral * 100);
433
434 wsprintf(szOutput, _T("%u%s%02u %s"), uIntegral, szDecimalSeparator, uDecimals, szAppend);
435 }
436 }
437
438 static VOID
439 SetProcSpeed(HWND hwnd,
440 HKEY hKey,
441 LPTSTR Value,
442 UINT uID)
443 {
444 TCHAR szBuf[64];
445 DWORD BufSize = sizeof(DWORD);
446 DWORD Type = REG_SZ;
447 PROCESSOR_POWER_INFORMATION ppi;
448
449 ZeroMemory(&ppi,
450 sizeof(ppi));
451
452 if ((CallNtPowerInformation(ProcessorInformation,
453 NULL,
454 0,
455 (PVOID)&ppi,
456 sizeof(ppi)) == STATUS_SUCCESS &&
457 ppi.CurrentMhz != 0) ||
458 RegQueryValueEx(hKey,
459 Value,
460 NULL,
461 &Type,
462 (PBYTE)&ppi.CurrentMhz,
463 &BufSize) == ERROR_SUCCESS)
464 {
465 if (ppi.CurrentMhz < 1000)
466 {
467 wsprintf(szBuf, _T("%lu MHz"), ppi.CurrentMhz);
468 }
469 else
470 {
471 double flt = ppi.CurrentMhz / 1000.0;
472 MakeFloatValueString(&flt, szBuf, _T("GHz"));
473 }
474
475 SetDlgItemText(hwnd,
476 uID,
477 szBuf);
478 }
479 }
480
481 static VOID
482 GetSystemInformation(HWND hwnd)
483 {
484 HKEY hKey;
485 TCHAR ProcKey[] = _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0");
486 MEMORYSTATUSEX MemStat;
487 TCHAR Buf[32];
488 INT CurMachineLine = IDC_MACHINELINE1;
489
490 /*
491 * Get Processor information
492 * although undocumented, this information is being pulled
493 * directly out of the registry instead of via setupapi as it
494 * contains all the info we need, and should remain static
495 */
496 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
497 ProcKey,
498 0,
499 KEY_READ,
500 &hKey) == ERROR_SUCCESS)
501 {
502 SetRegTextData(hwnd,
503 hKey,
504 _T("VendorIdentifier"),
505 CurMachineLine);
506 CurMachineLine++;
507
508 CurMachineLine += SetProcNameString(hwnd,
509 hKey,
510 _T("ProcessorNameString"),
511 CurMachineLine,
512 CurMachineLine + 1);
513
514 SetProcSpeed(hwnd,
515 hKey,
516 _T("~MHz"),
517 CurMachineLine);
518 CurMachineLine++;
519 }
520
521
522 /* Get total physical RAM */
523 MemStat.dwLength = sizeof(MemStat);
524 if (GlobalMemoryStatusEx(&MemStat))
525 {
526 TCHAR szStr[32];
527 double dTotalPhys;
528
529 if (MemStat.ullTotalPhys > 1024 * 1024 * 1024)
530 {
531 UINT i = 0;
532 static const UINT uStrId[] = {
533 IDS_GIGABYTE,
534 IDS_TERABYTE,
535 IDS_PETABYTE
536 };
537
538 // We're dealing with GBs or more
539 MemStat.ullTotalPhys /= 1024 * 1024;
540
541 if (MemStat.ullTotalPhys > 1024 * 1024)
542 {
543 // We're dealing with TBs or more
544 MemStat.ullTotalPhys /= 1024;
545 i++;
546
547 if (MemStat.ullTotalPhys > 1024 * 1024)
548 {
549 // We're dealing with PBs or more
550 MemStat.ullTotalPhys /= 1024;
551 i++;
552
553 dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
554 }
555 else
556 {
557 dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
558 }
559 }
560 else
561 {
562 dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
563 }
564
565 LoadString(hApplet, uStrId[i], szStr, sizeof(szStr) / sizeof(TCHAR));
566 MakeFloatValueString(&dTotalPhys, Buf, szStr);
567 }
568 else
569 {
570 // We're dealing with MBs, don't show any decimals
571 LoadString(hApplet, IDS_MEGABYTE, szStr, sizeof(szStr) / sizeof(TCHAR));
572 wsprintf(Buf, _T("%u %s"), (UINT)MemStat.ullTotalPhys / 1024 / 1024, szStr);
573 }
574
575 SetDlgItemText(hwnd, CurMachineLine, Buf);
576 }
577 }
578
579
580 /* Property page dialog callback */
581 INT_PTR CALLBACK
582 GeneralPageProc(HWND hwndDlg,
583 UINT uMsg,
584 WPARAM wParam,
585 LPARAM lParam)
586 {
587
588 UNREFERENCED_PARAMETER(lParam);
589 UNREFERENCED_PARAMETER(wParam);
590
591 switch (uMsg)
592 {
593 case WM_INITDIALOG:
594 pImgInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IMGINFO));
595 if (pImgInfo == NULL)
596 {
597 EndDialog(hwndDlg, 0);
598 return FALSE;
599 }
600
601 InitLogo(hwndDlg);
602 SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_ROSIMG), GWL_WNDPROC, (LONG)RosImageProc);
603 GetSystemInformation(hwndDlg);
604 break;
605
606 case WM_DESTROY:
607 HeapFree(GetProcessHeap(), 0, pImgInfo);
608 break;
609
610 case WM_COMMAND:
611 if (LOWORD(wParam) == IDC_LICENCE)
612 {
613 DialogBox(hApplet,
614 MAKEINTRESOURCE(IDD_LICENCE),
615 hwndDlg,
616 LicenceDlgProc);
617
618 return TRUE;
619 }
620 break;
621
622 case WM_DRAWITEM:
623 {
624 LPDRAWITEMSTRUCT lpDrawItem;
625 lpDrawItem = (LPDRAWITEMSTRUCT) lParam;
626 if (lpDrawItem->CtlID == IDC_ROSIMG)
627 {
628 HDC hdcMem;
629 LONG left;
630
631 /* Position image in centre of dialog */
632 left = (lpDrawItem->rcItem.right - pImgInfo->cxSource) / 2;
633
634 hdcMem = CreateCompatibleDC(lpDrawItem->hDC);
635 if (hdcMem != NULL)
636 {
637 SelectObject(hdcMem, pImgInfo->hBitmap);
638 BitBlt(lpDrawItem->hDC,
639 left,
640 lpDrawItem->rcItem.top,
641 lpDrawItem->rcItem.right - lpDrawItem->rcItem.left,
642 lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top,
643 hdcMem,
644 0,
645 0,
646 SRCCOPY);
647 DeleteDC(hdcMem);
648 }
649 }
650 return TRUE;
651 }
652
653 case WM_NOTIFY:
654 {
655 NMHDR *nmhdr = (NMHDR *)lParam;
656
657 if (nmhdr->idFrom == IDC_ROSHOMEPAGE_LINK && nmhdr->code == NM_CLICK)
658 {
659 PNMLINK nml = (PNMLINK)nmhdr;
660
661 ShellExecuteW(hwndDlg,
662 L"open",
663 nml->item.szUrl,
664 NULL,
665 NULL,
666 SW_SHOWNORMAL);
667 }
668 break;
669 }
670
671 }
672
673 return FALSE;
674 }