* Sync up to trunk head (r64377).
[reactos.git] / 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 SetRegTextData(HWND hwnd, HKEY hKey, LPTSTR Value, UINT uID)
287 {
288 LPTSTR lpBuf = NULL;
289 DWORD BufSize = 0;
290 DWORD Type;
291
292 if (RegQueryValueEx(hKey, Value, NULL, &Type, NULL, &BufSize) == ERROR_SUCCESS)
293 {
294 lpBuf = HeapAlloc(GetProcessHeap(), 0, BufSize);
295
296 if (!lpBuf)
297 return;
298
299 if (RegQueryValueEx(hKey, Value, NULL, &Type, (PBYTE)lpBuf, &BufSize) == ERROR_SUCCESS)
300 SetDlgItemText(hwnd, uID, lpBuf);
301
302 HeapFree(GetProcessHeap(), 0, lpBuf);
303 }
304 }
305
306 static INT SetProcNameString(HWND hwnd, HKEY hKey, LPTSTR Value, UINT uID1, UINT uID2)
307 {
308 LPTSTR lpBuf = NULL;
309 DWORD BufSize = 0;
310 DWORD Type;
311 INT Ret = 0;
312 TCHAR szBuf[31];
313 TCHAR* szLastSpace;
314 INT LastSpace = 0;
315
316 if (RegQueryValueEx(hKey, Value, NULL, &Type, NULL, &BufSize) == ERROR_SUCCESS)
317 {
318 lpBuf = HeapAlloc(GetProcessHeap(), 0, BufSize);
319
320 if (!lpBuf)
321 return 0;
322
323 if (RegQueryValueEx(hKey, Value, NULL, &Type, (PBYTE)lpBuf, &BufSize) == ERROR_SUCCESS)
324 {
325 if (BufSize > ((30 + 1) * sizeof(TCHAR)))
326 {
327 /* Wrap the Processor Name String like XP does: *
328 * - Take the first 30 characters and look for the last space. *
329 * Then wrap the string after this space. *
330 * - If no space is found, wrap the string after character 30. *
331 * *
332 * For example the Processor Name String of a Pentium 4 is right-aligned. *
333 * With this wrapping the first line looks centered. */
334
335 _tcsncpy(szBuf, lpBuf, 30);
336 szBuf[30] = 0;
337 szLastSpace = _tcsrchr(szBuf, ' ');
338
339 if (szLastSpace == 0)
340 {
341 LastSpace = 30;
342 }
343 else
344 {
345 LastSpace = (szLastSpace - szBuf);
346 szBuf[LastSpace] = 0;
347 }
348
349 _tcsncpy(szBuf, lpBuf, LastSpace);
350
351 SetDlgItemText(hwnd, uID1, szBuf);
352 SetDlgItemText(hwnd, uID2, lpBuf+LastSpace+1);
353
354 /* Return the number of used lines */
355 Ret = 2;
356 }
357 else
358 {
359 SetDlgItemText(hwnd, uID1, lpBuf);
360 Ret = 1;
361 }
362 }
363
364 HeapFree(GetProcessHeap(), 0, lpBuf);
365 }
366
367 return Ret;
368 }
369
370 static VOID MakeFloatValueString(DOUBLE* dFloatValue, LPTSTR szOutput, LPTSTR szAppend)
371 {
372 TCHAR szDecimalSeparator[4];
373
374 /* Get the decimal separator for the current locale */
375 if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, szDecimalSeparator, sizeof(szDecimalSeparator) / sizeof(TCHAR)) > 0)
376 {
377 UCHAR uDecimals;
378 UINT uIntegral;
379
380 /* Show the value with two decimals */
381 uIntegral = (UINT)*dFloatValue;
382 uDecimals = (UCHAR)((UINT)(*dFloatValue * 100) - uIntegral * 100);
383
384 wsprintf(szOutput, _T("%u%s%02u %s"), uIntegral, szDecimalSeparator, uDecimals, szAppend);
385 }
386 }
387
388 static VOID SetProcSpeed(HWND hwnd, HKEY hKey, LPTSTR Value, UINT uID)
389 {
390 TCHAR szBuf[64];
391 DWORD BufSize = sizeof(DWORD);
392 DWORD Type = REG_SZ;
393 PROCESSOR_POWER_INFORMATION ppi;
394
395 ZeroMemory(&ppi, sizeof(ppi));
396
397 if ((CallNtPowerInformation(ProcessorInformation,
398 NULL,
399 0,
400 (PVOID)&ppi,
401 sizeof(ppi)) == STATUS_SUCCESS &&
402 ppi.CurrentMhz != 0) || RegQueryValueEx(hKey, Value, NULL, &Type, (PBYTE)&ppi.CurrentMhz, &BufSize) == ERROR_SUCCESS)
403 {
404 if (ppi.CurrentMhz < 1000)
405 {
406 wsprintf(szBuf, _T("%lu MHz"), ppi.CurrentMhz);
407 }
408 else
409 {
410 double flt = ppi.CurrentMhz / 1000.0;
411 MakeFloatValueString(&flt, szBuf, _T("GHz"));
412 }
413
414 SetDlgItemText(hwnd, uID, szBuf);
415 }
416 }
417
418 static VOID GetSystemInformation(HWND hwnd)
419 {
420 HKEY hKey;
421 TCHAR ProcKey[] = _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0");
422 MEMORYSTATUSEX MemStat;
423 TCHAR Buf[32];
424 INT CurMachineLine = IDC_MACHINELINE1;
425
426 /*
427 * Get Processor information
428 * although undocumented, this information is being pulled
429 * directly out of the registry instead of via setupapi as it
430 * contains all the info we need, and should remain static
431 */
432 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, ProcKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
433 {
434 SetRegTextData(hwnd, hKey, _T("VendorIdentifier"), CurMachineLine);
435 CurMachineLine++;
436
437 CurMachineLine += SetProcNameString(hwnd,
438 hKey,
439 _T("ProcessorNameString"),
440 CurMachineLine,
441 CurMachineLine + 1);
442
443 SetProcSpeed(hwnd, hKey, _T("~MHz"), CurMachineLine);
444 CurMachineLine++;
445 }
446
447 /* Get total physical RAM */
448 MemStat.dwLength = sizeof(MemStat);
449
450 if (GlobalMemoryStatusEx(&MemStat))
451 {
452 TCHAR szStr[32];
453 double dTotalPhys;
454
455 if (MemStat.ullTotalPhys > 1024 * 1024 * 1024)
456 {
457 UINT i = 0;
458 static const UINT uStrId[] = { IDS_GIGABYTE, IDS_TERABYTE, IDS_PETABYTE};
459
460 // We're dealing with GBs or more
461 MemStat.ullTotalPhys /= 1024 * 1024;
462
463 if (MemStat.ullTotalPhys > 1024 * 1024)
464 {
465 // We're dealing with TBs or more
466 MemStat.ullTotalPhys /= 1024;
467 i++;
468
469 if (MemStat.ullTotalPhys > 1024 * 1024)
470 {
471 // We're dealing with PBs or more
472 MemStat.ullTotalPhys /= 1024;
473 i++;
474
475 dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
476 }
477 else
478 {
479 dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
480 }
481 }
482 else
483 {
484 dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
485 }
486
487 LoadString(hApplet, uStrId[i], szStr, sizeof(szStr) / sizeof(TCHAR));
488 MakeFloatValueString(&dTotalPhys, Buf, szStr);
489 }
490 else
491 {
492 // We're dealing with MBs, don't show any decimals
493 LoadString(hApplet, IDS_MEGABYTE, szStr, sizeof(szStr) / sizeof(TCHAR));
494 wsprintf(Buf, _T("%u %s"), (UINT)MemStat.ullTotalPhys / 1024 / 1024, szStr);
495 }
496
497 SetDlgItemText(hwnd, CurMachineLine, Buf);
498 }
499 }
500
501
502 /* Property page dialog callback */
503 INT_PTR CALLBACK GeneralPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
504 {
505 UNREFERENCED_PARAMETER(lParam);
506 UNREFERENCED_PARAMETER(wParam);
507
508 switch (uMsg)
509 {
510 case WM_INITDIALOG:
511 pImgInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IMGINFO));
512 if (pImgInfo == NULL)
513 {
514 EndDialog(hwndDlg, 0);
515 return FALSE;
516 }
517
518 InitLogo(hwndDlg);
519 SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_ROSIMG), GWL_WNDPROC, (LONG)RosImageProc);
520 GetSystemInformation(hwndDlg);
521 break;
522
523 case WM_DESTROY:
524 HeapFree(GetProcessHeap(), 0, pImgInfo);
525 break;
526
527 case WM_COMMAND:
528 if (LOWORD(wParam) == IDC_LICENCE)
529 {
530 DialogBox(hApplet, MAKEINTRESOURCE(IDD_LICENCE), hwndDlg, LicenceDlgProc);
531
532 return TRUE;
533 }
534 break;
535
536 case WM_DRAWITEM:
537 {
538 LPDRAWITEMSTRUCT lpDrawItem = (LPDRAWITEMSTRUCT) lParam;
539
540 if (lpDrawItem->CtlID == IDC_ROSIMG)
541 {
542 HDC hdcMem;
543 LONG left;
544
545 /* Position image in centre of dialog */
546 left = (lpDrawItem->rcItem.right - pImgInfo->cxSource) / 2;
547
548 hdcMem = CreateCompatibleDC(lpDrawItem->hDC);
549 if (hdcMem != NULL)
550 {
551 SelectObject(hdcMem, pImgInfo->hBitmap);
552 BitBlt(lpDrawItem->hDC,
553 left,
554 lpDrawItem->rcItem.top,
555 lpDrawItem->rcItem.right - lpDrawItem->rcItem.left,
556 lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top,
557 hdcMem,
558 0,
559 0,
560 SRCCOPY);
561 DeleteDC(hdcMem);
562 }
563 }
564 return TRUE;
565 }
566
567 case WM_NOTIFY:
568 {
569 NMHDR *nmhdr = (NMHDR *)lParam;
570
571 if (nmhdr->idFrom == IDC_ROSHOMEPAGE_LINK && nmhdr->code == NM_CLICK)
572 {
573 PNMLINK nml = (PNMLINK)nmhdr;
574
575 ShellExecuteW(hwndDlg, L"open", nml->item.szUrl, NULL, NULL, SW_SHOWNORMAL);
576 }
577 break;
578 }
579
580 }
581
582 return FALSE;
583 }