Sync with trunk revision 64099.
[reactos.git] / dll / shellext / deskmon / deskmon.c
1 #include "precomp.h"
2
3 #include <tchar.h>
4
5 #define NDEBUG
6 #include <debug.h>
7
8 #include "resource.h"
9
10 static HINSTANCE hInstance;
11
12 #ifdef UNICODE
13 typedef INT_PTR (WINAPI *PDEVICEPROPERTIES)(HWND,LPCWSTR,LPCWSTR,BOOL);
14 #define FUNC_DEVICEPROPERTIES "DevicePropertiesW"
15 #else
16 typedef INT_PTR (WINAPI *PDEVICEPROPERTIES)(HWND,LPCSTR,LPCSTR,BOOL);
17 #define FUNC_DEVICEPROPERTIES "DevicePropertiesA"
18 #endif
19
20 static LPTSTR
21 GetMonitorDevInstID(LPCTSTR lpDeviceID)
22 {
23 /* FIXME: Implement, allocate returned string with LocalAlloc! */
24 return NULL;
25 }
26
27 static VOID
28 ShowMonitorProperties(PDESKMONITOR This)
29 {
30 HMODULE hDevMgr;
31 PDEVICEPROPERTIES pDeviceProperties;
32 LPTSTR lpDevInstID;
33
34 if (This->SelMonitor != NULL)
35 {
36 lpDevInstID = GetMonitorDevInstID(This->SelMonitor->dd.DeviceID);
37 if (lpDevInstID != NULL)
38 {
39 hDevMgr = LoadLibrary(TEXT("devmgr.dll"));
40 if (hDevMgr != NULL)
41 {
42 pDeviceProperties = (PDEVICEPROPERTIES)GetProcAddress(hDevMgr,
43 FUNC_DEVICEPROPERTIES);
44 if (pDeviceProperties != NULL)
45 {
46 pDeviceProperties(This->hwndDlg,
47 NULL,
48 This->SelMonitor->dd.DeviceID,
49 FALSE);
50 }
51
52 FreeLibrary(hDevMgr);
53 }
54
55 LocalFree((HLOCAL)lpDevInstID);
56 }
57 }
58 }
59
60 static VOID
61 UpdateMonitorSelection(PDESKMONITOR This)
62 {
63 INT i;
64
65 if (This->dwMonitorCount > 1)
66 {
67 This->SelMonitor = NULL;
68
69 i = (INT)SendDlgItemMessage(This->hwndDlg,
70 IDC_MONITORLIST,
71 LB_GETCURSEL,
72 0,
73 0);
74 if (i >= 0)
75 {
76 This->SelMonitor = (PDESKMONINFO)SendDlgItemMessage(This->hwndDlg,
77 IDC_MONITORLIST,
78 LB_GETITEMDATA,
79 (WPARAM)i,
80 0);
81 }
82 }
83 else
84 This->SelMonitor = This->Monitors;
85
86 EnableWindow(GetDlgItem(This->hwndDlg,
87 IDC_MONITORPROPERTIES),
88 This->SelMonitor != NULL);
89 }
90
91 static VOID
92 UpdatePruningControls(PDESKMONITOR This)
93 {
94 EnableWindow(GetDlgItem(This->hwndDlg,
95 IDC_PRUNINGCHECK),
96 This->bModesPruned && !This->bKeyIsReadOnly);
97 CheckDlgButton(This->hwndDlg,
98 IDC_PRUNINGCHECK,
99 (This->bModesPruned && This->bPruningOn) ? BST_CHECKED : BST_UNCHECKED);
100 }
101
102 static VOID
103 GetPruningSettings(PDESKMONITOR This)
104 {
105 BOOL bModesPruned = FALSE, bKeyIsReadOnly = FALSE, bPruningOn = FALSE;
106
107 if (This->DeskExtInterface != NULL)
108 {
109 This->DeskExtInterface->GetPruningMode(This->DeskExtInterface->Context,
110 &bModesPruned,
111 &bKeyIsReadOnly,
112 &bPruningOn);
113 }
114
115 /* Check the boolean values against zero before assigning it to the bitfields! */
116 This->bModesPruned = (bModesPruned != FALSE);
117 This->bKeyIsReadOnly = (bKeyIsReadOnly != FALSE);
118 This->bPruningOn = (bPruningOn != FALSE);
119
120 UpdatePruningControls(This);
121 }
122
123 static VOID
124 UpdateRefreshFrequencyList(PDESKMONITOR This)
125 {
126 PDEVMODEW lpCurrentMode, lpMode;
127 TCHAR szBuffer[64];
128 DWORD dwIndex;
129 INT i;
130 BOOL bHasDef = FALSE;
131 BOOL bAdded = FALSE;
132
133 /* Fill the refresh rate combo box */
134 SendDlgItemMessage(This->hwndDlg,
135 IDC_REFRESHRATE,
136 CB_RESETCONTENT,
137 0,
138 0);
139
140 lpCurrentMode = This->DeskExtInterface->GetCurrentMode(This->DeskExtInterface->Context);
141 dwIndex = 0;
142
143 do
144 {
145 lpMode = This->DeskExtInterface->EnumAllModes(This->DeskExtInterface->Context,
146 dwIndex++);
147 if (lpMode != NULL &&
148 lpMode->dmBitsPerPel == lpCurrentMode->dmBitsPerPel &&
149 lpMode->dmPelsWidth == lpCurrentMode->dmPelsWidth &&
150 lpMode->dmPelsHeight == lpCurrentMode->dmPelsHeight)
151 {
152 /* We're only interested in refresh rates for the current resolution and color depth */
153
154 if (lpMode->dmDisplayFrequency <= 1)
155 {
156 /* Default hardware frequency */
157 if (bHasDef)
158 continue;
159
160 bHasDef = TRUE;
161
162 if (!LoadString(hInstance,
163 IDS_USEDEFFRQUENCY,
164 szBuffer,
165 sizeof(szBuffer) / sizeof(szBuffer[0])))
166 {
167 szBuffer[0] = TEXT('\0');
168 }
169 }
170 else
171 {
172 TCHAR szFmt[64];
173
174 if (!LoadString(hInstance,
175 IDS_FREQFMT,
176 szFmt,
177 sizeof(szFmt) / sizeof(szFmt[0])))
178 {
179 szFmt[0] = TEXT('\0');
180 }
181
182 _sntprintf(szBuffer,
183 sizeof(szBuffer) / sizeof(szBuffer[0]),
184 szFmt,
185 lpMode->dmDisplayFrequency);
186 }
187
188 i = (INT)SendDlgItemMessage(This->hwndDlg,
189 IDC_REFRESHRATE,
190 CB_ADDSTRING,
191 0,
192 (LPARAM)szBuffer);
193 if (i >= 0)
194 {
195 bAdded = TRUE;
196
197 SendDlgItemMessage(This->hwndDlg,
198 IDC_REFRESHRATE,
199 CB_SETITEMDATA,
200 (WPARAM)i,
201 (LPARAM)lpMode);
202
203 if (lpMode->dmDisplayFrequency == lpCurrentMode->dmDisplayFrequency)
204 {
205 SendDlgItemMessage(This->hwndDlg,
206 IDC_REFRESHRATE,
207 CB_SETCURSEL,
208 (WPARAM)i,
209 0);
210 }
211 }
212 }
213
214 } while (lpMode != NULL);
215
216 EnableWindow(GetDlgItem(This->hwndDlg,
217 IDS_MONITORSETTINGSGROUP),
218 bAdded);
219 EnableWindow(GetDlgItem(This->hwndDlg,
220 IDS_REFRESHRATELABEL),
221 bAdded);
222 EnableWindow(GetDlgItem(This->hwndDlg,
223 IDC_REFRESHRATE),
224 bAdded);
225
226 GetPruningSettings(This);
227 }
228
229 static VOID
230 InitMonitorDialog(PDESKMONITOR This)
231 {
232 PDESKMONINFO pmi, pminext, *pmilink;
233 DISPLAY_DEVICE dd;
234 BOOL bRet;
235 INT i;
236 DWORD dwIndex;
237
238 /* Free all allocated monitors */
239 pmi = This->Monitors;
240 This->Monitors = NULL;
241 while (pmi != NULL)
242 {
243 pminext = pmi->Next;
244 LocalFree((HLOCAL)pmi);
245 pmi = pminext;
246 }
247
248 This->SelMonitor = NULL;
249 This->dwMonitorCount = 0;
250
251 if (This->lpDisplayDevice != NULL)
252 LocalFree((HLOCAL)This->lpDisplayDevice);
253
254 This->lpDisplayDevice = QueryDeskCplString(This->pdtobj,
255 RegisterClipboardFormat(DESK_EXT_DISPLAYDEVICE));
256
257 if (This->DeskExtInterface != NULL)
258 {
259 if (This->lpDisplayDevice != NULL)
260 {
261 /* Enumerate all monitors */
262 dwIndex = 0;
263 pmilink = &This->Monitors;
264
265 do
266 {
267 dd.cb = sizeof(dd);
268 bRet = EnumDisplayDevices(This->lpDisplayDevice,
269 dwIndex++,
270 &dd,
271 0);
272 if (bRet)
273 {
274 pmi = LocalAlloc(LMEM_FIXED,
275 sizeof(*pmi));
276 if (pmi != NULL)
277 {
278 CopyMemory(&pmi->dd,
279 &dd,
280 sizeof(dd));
281 pmi->Next = NULL;
282 *pmilink = pmi;
283 pmilink = &pmi->Next;
284
285 This->dwMonitorCount++;
286 }
287 }
288 } while (bRet);
289 }
290
291 This->lpDevModeOnInit = This->DeskExtInterface->GetCurrentMode(This->DeskExtInterface->Context);
292 }
293 else
294 This->lpDevModeOnInit = NULL;
295
296 This->lpSelDevMode = This->lpDevModeOnInit;
297
298 /* Setup the UI depending on how many monitors are attached */
299 if (This->dwMonitorCount == 0)
300 {
301 LPTSTR lpMonitorName;
302
303 /* This is a fallback, let's hope that desk.cpl can provide us with a monitor name */
304 lpMonitorName = QueryDeskCplString(This->pdtobj,
305 RegisterClipboardFormat(DESK_EXT_MONITORNAME));
306
307 SetDlgItemText(This->hwndDlg,
308 IDC_MONITORNAME,
309 lpMonitorName);
310
311 if (lpMonitorName != NULL)
312 LocalFree((HLOCAL)lpMonitorName);
313 }
314 else if (This->dwMonitorCount == 1)
315 {
316 This->SelMonitor = This->Monitors;
317 SetDlgItemText(This->hwndDlg,
318 IDC_MONITORNAME,
319 This->Monitors->dd.DeviceString);
320 }
321 else
322 {
323 SendDlgItemMessage(This->hwndDlg,
324 IDC_MONITORLIST,
325 LB_RESETCONTENT,
326 0,
327 0);
328
329 pmi = This->Monitors;
330 while (pmi != NULL)
331 {
332 i = (INT)SendDlgItemMessage(This->hwndDlg,
333 IDC_MONITORLIST,
334 LB_ADDSTRING,
335 0,
336 (LPARAM)pmi->dd.DeviceString);
337 if (i >= 0)
338 {
339 SendDlgItemMessage(This->hwndDlg,
340 IDC_MONITORLIST,
341 LB_SETITEMDATA,
342 (WPARAM)i,
343 (LPARAM)pmi);
344
345 if (This->SelMonitor == NULL)
346 {
347 SendDlgItemMessage(This->hwndDlg,
348 IDC_MONITORLIST,
349 LB_SETCURSEL,
350 (WPARAM)i,
351 0);
352
353 This->SelMonitor = pmi;
354 }
355 }
356
357 pmi = pmi->Next;
358 }
359 }
360
361 /* Show/Hide controls */
362 ShowWindow(GetDlgItem(This->hwndDlg,
363 IDC_MONITORNAME),
364 (This->dwMonitorCount <= 1 ? SW_SHOW : SW_HIDE));
365 ShowWindow(GetDlgItem(This->hwndDlg,
366 IDC_MONITORLIST),
367 (This->dwMonitorCount > 1 ? SW_SHOW : SW_HIDE));
368
369 UpdateRefreshFrequencyList(This);
370 UpdateMonitorSelection(This);
371 }
372
373 static VOID
374 UpdatePruningSelection(PDESKMONITOR This)
375 {
376 BOOL bPruningOn;
377
378 if (This->DeskExtInterface != NULL && This->bModesPruned && !This->bKeyIsReadOnly)
379 {
380 bPruningOn = IsDlgButtonChecked(This->hwndDlg,
381 IDC_PRUNINGCHECK) != BST_UNCHECKED;
382
383 if (bPruningOn != This->bPruningOn)
384 {
385 /* Tell desk.cpl to turn on/off pruning mode */
386 This->bPruningOn = bPruningOn;
387 This->DeskExtInterface->SetPruningMode(This->DeskExtInterface->Context,
388 bPruningOn);
389
390 /* Fill the refresh rate combobox again, we now receive a filtered
391 or unfiltered device mode list from desk.cpl (depending on whether
392 pruning is active or not) */
393 UpdateRefreshFrequencyList(This);
394
395 (void)PropSheet_Changed(GetParent(This->hwndDlg),
396 This->hwndDlg);
397 }
398 }
399 }
400
401 static VOID
402 UpdateRefreshRateSelection(PDESKMONITOR This)
403 {
404 PDEVMODEW lpCurrentDevMode;
405 INT i;
406
407 if (This->DeskExtInterface != NULL)
408 {
409 i = (INT)SendDlgItemMessage(This->hwndDlg,
410 IDC_REFRESHRATE,
411 CB_GETCURSEL,
412 0,
413 0);
414 if (i >= 0)
415 {
416 lpCurrentDevMode = This->lpSelDevMode;
417 This->lpSelDevMode = (PDEVMODEW)SendDlgItemMessage(This->hwndDlg,
418 IDC_REFRESHRATE,
419 CB_GETITEMDATA,
420 (WPARAM)i,
421 0);
422
423 if (This->lpSelDevMode != NULL && This->lpSelDevMode != lpCurrentDevMode)
424 {
425 This->DeskExtInterface->SetCurrentMode(This->DeskExtInterface->Context,
426 This->lpSelDevMode);
427
428 UpdateRefreshFrequencyList(This);
429
430 (void)PropSheet_Changed(GetParent(This->hwndDlg),
431 This->hwndDlg);
432 }
433 }
434 }
435 }
436
437 static LONG
438 ApplyMonitorChanges(PDESKMONITOR This)
439 {
440 LONG lChangeRet;
441
442 if (This->DeskExtInterface != NULL)
443 {
444 /* Change the display settings through desk.cpl */
445 lChangeRet = DeskCplExtDisplaySaveSettings(This->DeskExtInterface,
446 This->hwndDlg);
447 if (lChangeRet == DISP_CHANGE_SUCCESSFUL)
448 {
449 /* Save the new mode */
450 This->lpDevModeOnInit = This->DeskExtInterface->GetCurrentMode(This->DeskExtInterface->Context);
451 This->lpSelDevMode = This->lpDevModeOnInit;
452 return PSNRET_NOERROR;
453 }
454 else if (lChangeRet == DISP_CHANGE_RESTART)
455 {
456 /* Notify desk.cpl that the user needs to reboot */
457 PropSheet_RestartWindows(GetParent(This->hwndDlg));
458 return PSNRET_NOERROR;
459 }
460 }
461
462 InitMonitorDialog(This);
463
464 return PSNRET_INVALID_NOCHANGEPAGE;
465 }
466
467 static VOID
468 ResetMonitorChanges(PDESKMONITOR This)
469 {
470 if (This->DeskExtInterface != NULL && This->lpDevModeOnInit != NULL)
471 {
472 This->DeskExtInterface->SetCurrentMode(This->DeskExtInterface->Context,
473 This->lpDevModeOnInit);
474 }
475 }
476
477 static INT_PTR CALLBACK
478 MonitorDlgProc(HWND hwndDlg,
479 UINT uMsg,
480 WPARAM wParam,
481 LPARAM lParam)
482 {
483 PDESKMONITOR This;
484 INT_PTR Ret = 0;
485
486 if (uMsg != WM_INITDIALOG)
487 {
488 This = (PDESKMONITOR)GetWindowLongPtr(hwndDlg,
489 DWL_USER);
490 }
491
492 switch (uMsg)
493 {
494 case WM_INITDIALOG:
495 This = (PDESKMONITOR)((LPCPROPSHEETPAGE)lParam)->lParam;
496 This->hwndDlg = hwndDlg;
497 SetWindowLongPtr(hwndDlg,
498 DWL_USER,
499 (LONG_PTR)This);
500
501 InitMonitorDialog(This);
502 Ret = TRUE;
503 break;
504
505 case WM_COMMAND:
506 switch (LOWORD(wParam))
507 {
508 case IDC_MONITORPROPERTIES:
509 ShowMonitorProperties(This);
510 break;
511
512 case IDC_MONITORLIST:
513 if (HIWORD(wParam) == LBN_SELCHANGE)
514 UpdateMonitorSelection(This);
515 break;
516
517 case IDC_PRUNINGCHECK:
518 if (HIWORD(wParam) == BN_CLICKED)
519 UpdatePruningSelection(This);
520 break;
521
522 case IDC_REFRESHRATE:
523 if (HIWORD(wParam) == CBN_SELCHANGE)
524 UpdateRefreshRateSelection(This);
525 break;
526 }
527 break;
528
529 case WM_NOTIFY:
530 {
531 NMHDR *nmh = (NMHDR *)lParam;
532
533 switch (nmh->code)
534 {
535 case PSN_APPLY:
536 {
537 SetWindowLongPtr(hwndDlg,
538 DWL_MSGRESULT,
539 ApplyMonitorChanges(This));
540 break;
541 }
542
543 case PSN_RESET:
544 ResetMonitorChanges(This);
545 break;
546
547 case PSN_SETACTIVE:
548 UpdateRefreshFrequencyList(This);
549 break;
550 }
551 break;
552 }
553 }
554
555 return Ret;
556 }
557
558 static VOID
559 IDeskMonitor_Destroy(PDESKMONITOR This)
560 {
561 PDESKMONINFO pmi, pminext;
562
563 if (This->pdtobj != NULL)
564 {
565 IDataObject_Release(This->pdtobj);
566 This->pdtobj = NULL;
567 }
568
569 if (This->DeskExtInterface != NULL)
570 {
571 LocalFree((HLOCAL)This->DeskExtInterface);
572 This->DeskExtInterface = NULL;
573 }
574
575 if (This->lpDisplayDevice != NULL)
576 {
577 LocalFree((HLOCAL)This->lpDisplayDevice);
578 This->lpDisplayDevice = NULL;
579 }
580
581 /* Free all monitors */
582 pmi = This->Monitors;
583 This->Monitors = NULL;
584 while (pmi != NULL)
585 {
586 pminext = pmi->Next;
587 LocalFree((HLOCAL)pmi);
588 pmi = pminext;
589 }
590 }
591
592 ULONG
593 IDeskMonitor_AddRef(PDESKMONITOR This)
594 {
595 ULONG ret;
596
597 ret = InterlockedIncrement((PLONG)&This->ref);
598 if (ret == 1)
599 InterlockedIncrement(&dll_refs);
600
601 return ret;
602 }
603
604 ULONG
605 IDeskMonitor_Release(PDESKMONITOR This)
606 {
607 ULONG ret;
608
609 ret = InterlockedDecrement((PLONG)&This->ref);
610 if (ret == 0)
611 {
612 IDeskMonitor_Destroy(This);
613 InterlockedDecrement(&dll_refs);
614
615 HeapFree(GetProcessHeap(),
616 0,
617 This);
618 }
619
620 return ret;
621 }
622
623 HRESULT STDMETHODCALLTYPE
624 IDeskMonitor_QueryInterface(PDESKMONITOR This,
625 REFIID iid,
626 PVOID *pvObject)
627 {
628 *pvObject = NULL;
629
630 if (IsEqualIID(iid,
631 &IID_IShellPropSheetExt) ||
632 IsEqualIID(iid,
633 &IID_IUnknown))
634 {
635 *pvObject = impl_to_interface(This, IShellPropSheetExt);
636 }
637 else if (IsEqualIID(iid,
638 &IID_IShellExtInit))
639 {
640 *pvObject = impl_to_interface(This, IShellExtInit);
641 }
642 else if (IsEqualIID(iid,
643 &IID_IClassFactory))
644 {
645 *pvObject = impl_to_interface(This, IClassFactory);
646 }
647 else
648 {
649 DPRINT1("IDeskMonitor::QueryInterface(%p,%p): E_NOINTERFACE\n", iid, pvObject);
650 return E_NOINTERFACE;
651 }
652
653 IDeskMonitor_AddRef(This);
654 return S_OK;
655 }
656
657 HRESULT
658 IDeskMonitor_Initialize(PDESKMONITOR This,
659 LPCITEMIDLIST pidlFolder,
660 IDataObject *pdtobj,
661 HKEY hkeyProgID)
662 {
663 DPRINT1("IDeskMonitor::Initialize(%p,%p,%p)\n", pidlFolder, pdtobj, hkeyProgID);
664
665 if (pdtobj != NULL)
666 {
667 IDataObject_AddRef(pdtobj);
668 This->pdtobj = pdtobj;
669
670 /* Get a copy of the desk.cpl extension interface */
671 This->DeskExtInterface = QueryDeskCplExtInterface(This->pdtobj);
672 if (This->DeskExtInterface != NULL)
673 return S_OK;
674 }
675
676 return S_FALSE;
677 }
678
679 HRESULT
680 IDeskMonitor_AddPages(PDESKMONITOR This,
681 LPFNADDPROPSHEETPAGE pfnAddPage,
682 LPARAM lParam)
683 {
684 HPROPSHEETPAGE hpsp;
685 PROPSHEETPAGE psp;
686
687 DPRINT1("IDeskMonitor::AddPages(%p,%p)\n", pfnAddPage, lParam);
688
689 psp.dwSize = sizeof(psp);
690 psp.dwFlags = PSP_DEFAULT;
691 psp.hInstance = hInstance;
692 psp.pszTemplate = MAKEINTRESOURCE(IDD_MONITOR);
693 psp.pfnDlgProc = MonitorDlgProc;
694 psp.lParam = (LPARAM)This;
695
696 hpsp = CreatePropertySheetPage(&psp);
697 if (hpsp != NULL && pfnAddPage(hpsp, lParam))
698 return S_OK;
699
700 return S_FALSE;
701 }
702
703 HRESULT
704 IDeskMonitor_ReplacePage(PDESKMONITOR This,
705 EXPPS uPageID,
706 LPFNADDPROPSHEETPAGE pfnReplacePage,
707 LPARAM lParam)
708 {
709 DPRINT1("IDeskMonitor::ReplacePage(%u,%p,%p)\n", uPageID, pfnReplacePage, lParam);
710 return E_NOTIMPL;
711 }
712
713 HRESULT
714 IDeskMonitor_Constructor(REFIID riid,
715 LPVOID *ppv)
716 {
717 PDESKMONITOR This;
718 HRESULT hRet = E_OUTOFMEMORY;
719
720 DPRINT1("IDeskMonitor::Constructor(%p,%p)\n", riid, ppv);
721
722 This = HeapAlloc(GetProcessHeap(),
723 0,
724 sizeof(*This));
725 if (This != NULL)
726 {
727 ZeroMemory(This,
728 sizeof(*This));
729
730 IDeskMonitor_InitIface(This);
731
732 hRet = IDeskMonitor_QueryInterface(This,
733 riid,
734 ppv);
735 if (!SUCCEEDED(hRet))
736 IDeskMonitor_Release(This);
737 }
738
739 return hRet;
740 }
741
742 BOOL WINAPI
743 DllMain(HINSTANCE hinstDLL,
744 DWORD dwReason,
745 LPVOID lpvReserved)
746 {
747 switch (dwReason)
748 {
749 case DLL_PROCESS_ATTACH:
750 hInstance = hinstDLL;
751 DisableThreadLibraryCalls(hInstance);
752 break;
753 }
754
755 return TRUE;
756 }