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