xref: /reactos/dll/cpl/sysdm/general.c (revision c2c66aff)
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     if (LastError == ERROR_SUCCESS)
39         return;
40 
41     if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
42                        FORMAT_MESSAGE_FROM_SYSTEM |
43                        FORMAT_MESSAGE_IGNORE_INSERTS,
44                        NULL,
45                        LastError,
46                        LANG_USER_DEFAULT,
47                        (LPTSTR)&lpMsg,
48                        0, NULL))
49     {
50         return;
51     }
52 
53     MessageBox(hWndOwner, lpMsg, NULL, MB_OK | MB_ICONERROR);
54     LocalFree(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 }
584