xref: /reactos/dll/cpl/desk/desk.c (revision 84344399)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Display Control Panel
4  * FILE:            dll/cpl/desk/desk.c
5  * PURPOSE:         ReactOS Display Control Panel
6  *
7  * PROGRAMMERS:     Trevor McCort (lycan359@gmail.com)
8  */
9 
10 #include "desk.h"
11 
12 #include <shellapi.h>
13 #include <cplext.h>
14 #include <winnls.h>
15 
16 #define NDEBUG
17 #include <debug.h>
18 
19 
20 /* Enable this for InstallScreenSaverW() to determine a possible full path
21  * to the specified screensaver file, verify its existence and use it.
22  * (NOTE: This is not Windows desk.cpl-compatible.) */
23 // #define CHECK_SCR_FULL_PATH
24 
25 
26 VOID WINAPI Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow);
27 
28 static LONG APIENTRY DisplayApplet(HWND hwnd, UINT uMsg, LPARAM wParam, LPARAM lParam);
29 
30 INT_PTR CALLBACK ThemesPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
31 INT_PTR CALLBACK BackgroundPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
32 INT_PTR CALLBACK ScreenSaverPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
33 INT_PTR CALLBACK AppearancePageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
34 INT_PTR CALLBACK SettingsPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
35 UINT CALLBACK SettingsPageCallbackProc(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp);
36 
37 HINSTANCE hApplet = NULL;
38 HWND hCPLWindow = NULL;
39 
40 /* Applets */
41 APPLET Applets[] =
42 {
43     {
44         IDC_DESK_ICON,
45         IDS_CPLNAME,
46         IDS_CPLDESCRIPTION,
47         DisplayApplet
48     }
49 };
50 
51 HMENU
52 LoadPopupMenu(IN HINSTANCE hInstance,
53               IN LPCTSTR lpMenuName)
54 {
55     HMENU hMenu, hSubMenu = NULL;
56 
57     hMenu = LoadMenu(hInstance,
58                      lpMenuName);
59 
60     if (hMenu != NULL)
61     {
62         hSubMenu = GetSubMenu(hMenu,
63                               0);
64         if (hSubMenu != NULL &&
65             !RemoveMenu(hMenu,
66                         0,
67                         MF_BYPOSITION))
68         {
69             hSubMenu = NULL;
70         }
71 
72         DestroyMenu(hMenu);
73     }
74 
75     return hSubMenu;
76 }
77 
78 static BOOL CALLBACK
79 DisplayAppletPropSheetAddPage(HPROPSHEETPAGE hpage, LPARAM lParam)
80 {
81     PROPSHEETHEADER *ppsh = (PROPSHEETHEADER *)lParam;
82     if (ppsh != NULL && ppsh->nPages < MAX_DESK_PAGES)
83     {
84         ppsh->phpage[ppsh->nPages++] = hpage;
85         return TRUE;
86     }
87 
88     return FALSE;
89 }
90 
91 static BOOL
92 InitPropSheetPage(PROPSHEETHEADER *ppsh, WORD idDlg, DLGPROC DlgProc, LPFNPSPCALLBACK pfnCallback)
93 {
94     HPROPSHEETPAGE hPage;
95     PROPSHEETPAGE psp;
96 
97     if (ppsh->nPages < MAX_DESK_PAGES)
98     {
99         ZeroMemory(&psp, sizeof(psp));
100         psp.dwSize = sizeof(psp);
101         psp.dwFlags = PSP_DEFAULT;
102         if (pfnCallback != NULL)
103             psp.dwFlags |= PSP_USECALLBACK;
104         psp.hInstance = hApplet;
105         psp.pszTemplate = MAKEINTRESOURCE(idDlg);
106         psp.pfnDlgProc = DlgProc;
107         psp.pfnCallback = pfnCallback;
108 
109         hPage = CreatePropertySheetPage(&psp);
110         if (hPage != NULL)
111         {
112             return DisplayAppletPropSheetAddPage(hPage, (LPARAM)ppsh);
113         }
114     }
115 
116     return FALSE;
117 }
118 
119 static const struct
120 {
121     WORD idDlg;
122     DLGPROC DlgProc;
123     LPFNPSPCALLBACK Callback;
124     LPWSTR Name;
125 } PropPages[] =
126 {
127     /* { IDD_THEMES, ThemesPageProc, NULL, L"Themes" }, */ /* TODO: */
128     { IDD_BACKGROUND, BackgroundPageProc, NULL, L"Desktop" },
129     { IDD_SCREENSAVER, ScreenSaverPageProc, NULL, L"Screen Saver" },
130     { IDD_APPEARANCE, AppearancePageProc, NULL, L"Appearance" },
131     { IDD_SETTINGS, SettingsPageProc, SettingsPageCallbackProc, L"Settings" },
132 };
133 
134 static int CALLBACK
135 PropSheetProc(HWND hwndDlg, UINT uMsg, LPARAM lParam)
136 {
137     // NOTE: This callback is needed to set large icon correctly.
138     HICON hIcon;
139     switch (uMsg)
140     {
141         case PSCB_INITIALIZED:
142         {
143             hIcon = LoadIconW(hApplet, MAKEINTRESOURCEW(IDC_DESK_ICON));
144             SendMessageW(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
145             break;
146         }
147     }
148     return 0;
149 }
150 
151 /* Display Applet */
152 static LONG APIENTRY
153 DisplayApplet(HWND hwnd, UINT uMsg, LPARAM wParam, LPARAM lParam)
154 {
155     HPROPSHEETPAGE hpsp[MAX_DESK_PAGES];
156     PROPSHEETHEADER psh;
157     HPSXA hpsxa = NULL;
158     UINT i;
159     LPWSTR *argv = NULL;
160     LPCWSTR pwszSelectedTab = NULL;
161     LPCWSTR pwszFile = NULL;
162     LPCWSTR pwszAction = NULL;
163     INT nPage = 0;
164     BITMAP bitmap;
165 
166     UNREFERENCED_PARAMETER(wParam);
167 
168     hCPLWindow = hwnd;
169 
170     if (uMsg == CPL_STARTWPARMSW && lParam)
171     {
172         int argc;
173         int i;
174 
175         nPage = _wtoi((PWSTR)lParam);
176 
177 #if 0
178         argv = CommandLineToArgvW((LPCWSTR)lParam, &argc);
179 #else
180         argv = CommandLineToArgvW(GetCommandLineW(), &argc);
181 #endif
182 
183         if (argv && argc)
184         {
185             for (i = 0; i<argc; i++)
186             {
187 #if 0
188                 if (argv[i][0] == L'@')
189                     pwszSelectedTab = &argv[i][1];
190 #else
191                 if (wcsncmp(argv[i], L"desk,@", 6) == 0)
192                     pwszSelectedTab = &argv[i][6];
193 #endif
194                 else if (wcsncmp(argv[i], L"/Action:", 8) == 0)
195                     pwszAction = &argv[i][8];
196                 else if (wcsncmp(argv[i], L"/file:", 6) == 0)
197                     pwszFile = &argv[i][6];
198             }
199         }
200     }
201 
202     if(pwszAction && wcsncmp(pwszAction, L"ActivateMSTheme", 15) == 0)
203     {
204         ActivateThemeFile(pwszFile);
205         goto cleanup;
206     }
207 
208     g_GlobalData.pwszFile = pwszFile;
209     g_GlobalData.pwszAction = pwszAction;
210     g_GlobalData.desktop_color = GetSysColor(COLOR_DESKTOP);
211 
212     /* Initialize the monitor preview bitmap, used on multiple pages */
213     g_GlobalData.hMonitorBitmap = LoadBitmapW(hApplet, MAKEINTRESOURCEW(IDC_MONITOR));
214     if (g_GlobalData.hMonitorBitmap != NULL)
215     {
216         GetObjectW(g_GlobalData.hMonitorBitmap, sizeof(bitmap), &bitmap);
217 
218         g_GlobalData.bmMonWidth = bitmap.bmWidth;
219         g_GlobalData.bmMonHeight = bitmap.bmHeight;
220     }
221 
222     ZeroMemory(&psh, sizeof(psh));
223     psh.dwSize = sizeof(psh);
224     psh.dwFlags = PSH_USECALLBACK | PSH_PROPTITLE | PSH_USEICONID;
225     psh.hwndParent = hCPLWindow;
226     psh.hInstance = hApplet;
227     psh.pszIcon = MAKEINTRESOURCEW(IDC_DESK_ICON);
228     psh.pszCaption = MAKEINTRESOURCEW(IDS_CPLNAME);
229     psh.nPages = 0;
230     psh.nStartPage = 0;
231     psh.phpage = hpsp;
232     psh.pfnCallback = PropSheetProc;
233 
234     /* Allow shell extensions to replace the background page */
235     hpsxa = SHCreatePropSheetExtArray(HKEY_LOCAL_MACHINE, REGSTR_PATH_CONTROLSFOLDER TEXT("\\Desk"), MAX_DESK_PAGES - psh.nPages);
236 
237     for (i = 0; i < _countof(PropPages); i++)
238     {
239         if (pwszSelectedTab && _wcsicmp(pwszSelectedTab, PropPages[i].Name) == 0)
240             psh.nStartPage = i;
241 
242         /* Override the background page if requested by a shell extension */
243         if (PropPages[i].idDlg == IDD_BACKGROUND && hpsxa != NULL &&
244             SHReplaceFromPropSheetExtArray(hpsxa, CPLPAGE_DISPLAY_BACKGROUND, DisplayAppletPropSheetAddPage, (LPARAM)&psh) != 0)
245         {
246             /* The shell extension added one or more pages to replace the background page.
247                Don't create the built-in page anymore! */
248             continue;
249         }
250 
251         InitPropSheetPage(&psh, PropPages[i].idDlg, PropPages[i].DlgProc, PropPages[i].Callback);
252     }
253 
254     /* NOTE: Don't call SHAddFromPropSheetExtArray here because this applet only allows
255              replacing the background page but not extending the applet by more pages */
256 
257     if (nPage != 0 && psh.nStartPage == 0)
258         psh.nStartPage = nPage;
259 
260     PropertySheet(&psh);
261 
262 cleanup:
263     DeleteObject(g_GlobalData.hMonitorBitmap);
264 
265     if (hpsxa != NULL)
266         SHDestroyPropSheetExtArray(hpsxa);
267 
268     if (argv)
269         LocalFree(argv);
270 
271     return TRUE;
272 }
273 
274 
275 /* Control Panel Callback */
276 LONG CALLBACK
277 CPlApplet(HWND hwndCPl, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
278 {
279     UINT i = (UINT)lParam1;
280 
281     switch (uMsg)
282     {
283         case CPL_INIT:
284             return TRUE;
285 
286         case CPL_GETCOUNT:
287             return _countof(Applets);
288 
289         case CPL_INQUIRE:
290             if (i < _countof(Applets))
291             {
292                 CPLINFO *CPlInfo = (CPLINFO*)lParam2;
293                 CPlInfo->lData = 0;
294                 CPlInfo->idIcon = Applets[i].idIcon;
295                 CPlInfo->idName = Applets[i].idName;
296                 CPlInfo->idInfo = Applets[i].idDescription;
297             }
298             else
299             {
300                 return TRUE;
301             }
302             break;
303 
304         case CPL_DBLCLK:
305             if (i < _countof(Applets))
306                 Applets[i].AppletProc(hwndCPl, uMsg, lParam1, lParam2);
307             else
308                 return TRUE;
309             break;
310 
311         case CPL_STARTWPARMSW:
312             if (i < _countof(Applets))
313                 return Applets[i].AppletProc(hwndCPl, uMsg, lParam1, lParam2);
314             break;
315     }
316 
317     return FALSE;
318 }
319 
320 void
321 WINAPI
322 InstallScreenSaverW(
323     IN HWND hWindow,
324     IN HANDLE hInstance,
325     IN LPCWSTR pszFile,
326     IN UINT nCmdShow)
327 {
328     LRESULT rc;
329     HKEY regKey;
330     INT Timeout;
331 #ifdef CHECK_SCR_FULL_PATH
332     HANDLE hFile;
333     WIN32_FIND_DATAW fdFile;
334 #endif
335     DWORD dwLen;
336     WCHAR szFullPath[MAX_PATH];
337 
338     if (!pszFile)
339     {
340         DPRINT1("InstallScreenSaver() null file\n");
341         SetLastError(ERROR_INVALID_PARAMETER);
342         return;
343     }
344     DPRINT("InstallScreenSaver() Installing screensaver %ls\n", pszFile);
345 
346 #ifdef CHECK_SCR_FULL_PATH
347     /* Retrieve the actual path to the file and verify whether it exists */
348     dwLen = GetFullPathNameW(pszFile, _countof(szFullPath), szFullPath, NULL);
349     if (dwLen == 0 || dwLen > _countof(szFullPath))
350     {
351         DPRINT1("InstallScreenSaver() File %ls not accessible\n", pszFile);
352         return;
353     }
354     hFile = FindFirstFile(szFullPath, &fdFile);
355     if (hFile == INVALID_HANDLE_VALUE)
356     {
357         DPRINT1("InstallScreenSaver() File %ls not found\n", pszFile);
358         return;
359     }
360     FindClose(hFile);
361     /* Use the full file path from now on */
362     pszFile = szFullPath;
363 #endif
364 
365     rc = RegOpenKeyExW(HKEY_CURRENT_USER,
366                        L"Control Panel\\Desktop",
367                        0,
368                        KEY_SET_VALUE,
369                        &regKey);
370     if (rc == ERROR_SUCCESS)
371     {
372         /* Set the screensaver */
373         SIZE_T Length = (wcslen(pszFile) + 1) * sizeof(WCHAR);
374         rc = RegSetValueExW(regKey,
375                             L"SCRNSAVE.EXE",
376                             0,
377                             REG_SZ,
378                             (PBYTE)pszFile,
379                             (DWORD)Length);
380         RegCloseKey(regKey);
381     }
382     if (rc != ERROR_SUCCESS)
383     {
384         DPRINT1("InstallScreenSaver() Could not change the current screensaver\n");
385         return;
386     }
387 
388     SystemParametersInfoW(SPI_SETSCREENSAVEACTIVE, TRUE, 0, SPIF_UPDATEINIFILE);
389 
390     /* If no screensaver timeout is present, default to 10 minutes (600 seconds) */
391     Timeout = 0;
392     if (!SystemParametersInfoW(SPI_GETSCREENSAVETIMEOUT, 0, &Timeout, 0) || (Timeout <= 0))
393         SystemParametersInfoW(SPI_SETSCREENSAVETIMEOUT, 600, 0, SPIF_UPDATEINIFILE);
394 
395     /* Retrieve the name of this current instance of desk.cpl */
396     dwLen = GetModuleFileNameW(hApplet, szFullPath, _countof(szFullPath));
397     if ((dwLen == 0) || (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
398     {
399         /* We failed, copy the default value */
400         StringCchCopyW(szFullPath, _countof(szFullPath), L"desk.cpl");
401     }
402 
403     /* Build the desk.cpl command-line to start the ScreenSaver page.
404      * Equivalent to: "desk.cpl,ScreenSaver,@ScreenSaver" */
405     rc = StringCchCatW(szFullPath, _countof(szFullPath), L",,1");
406     if (FAILED(rc))
407         return;
408 
409     /* Open the ScreenSaver page in this desk.cpl instance */
410     DPRINT("InstallScreenSaver() Starting '%ls'\n", szFullPath);
411     Control_RunDLLW(hWindow, hInstance, szFullPath, nCmdShow);
412 }
413 
414 void
415 WINAPI
416 InstallScreenSaverA(
417     IN HWND hWindow,
418     IN HANDLE hInstance,
419     IN LPCSTR pszFile,
420     IN UINT nCmdShow)
421 {
422     LPWSTR lpwString;
423     int nLength;
424 
425     if (!pszFile)
426     {
427         DPRINT1("InstallScreenSaver() null file\n");
428         SetLastError(ERROR_INVALID_PARAMETER);
429         return;
430     }
431 
432     /* Convert the string to unicode */
433     lpwString = NULL;
434     nLength = MultiByteToWideChar(CP_ACP, 0, pszFile, -1, NULL, 0);
435     if (nLength != 0)
436     {
437         lpwString = LocalAlloc(LMEM_FIXED, nLength * sizeof(WCHAR));
438         if (lpwString)
439         {
440             if (!MultiByteToWideChar(CP_ACP, 0, pszFile, -1, lpwString, nLength))
441             {
442                 LocalFree(lpwString);
443                 lpwString = NULL;
444             }
445         }
446     }
447     if (!lpwString)
448     {
449         DPRINT1("InstallScreenSaver() not enough memory to convert string to unicode\n");
450         return;
451     }
452 
453     /* Call the unicode function */
454     InstallScreenSaverW(hWindow, hInstance, lpwString, nCmdShow);
455 
456     LocalFree(lpwString);
457 }
458 
459 BOOL WINAPI
460 DllMain(HINSTANCE hInstDLL, DWORD dwReason, LPVOID lpvReserved)
461 {
462     UNREFERENCED_PARAMETER(lpvReserved);
463 
464     switch (dwReason)
465     {
466         case DLL_PROCESS_ATTACH:
467             CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
468             RegisterPreviewControl(hInstDLL);
469 //        case DLL_THREAD_ATTACH:
470             hApplet = hInstDLL;
471             break;
472 
473         case DLL_PROCESS_DETACH:
474             UnregisterPreviewControl(hInstDLL);
475             CoUninitialize();
476             break;
477     }
478 
479     return TRUE;
480 }
481