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