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, DWLP_USER); 489 } 490 491 switch (uMsg) 492 { 493 case WM_INITDIALOG: 494 This = (PDESKMONITOR)((LPCPROPSHEETPAGE)lParam)->lParam; 495 This->hwndDlg = hwndDlg; 496 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)This); 497 498 InitMonitorDialog(This); 499 Ret = TRUE; 500 break; 501 502 case WM_COMMAND: 503 switch (LOWORD(wParam)) 504 { 505 case IDC_MONITORPROPERTIES: 506 ShowMonitorProperties(This); 507 break; 508 509 case IDC_MONITORLIST: 510 if (HIWORD(wParam) == LBN_SELCHANGE) 511 UpdateMonitorSelection(This); 512 break; 513 514 case IDC_PRUNINGCHECK: 515 if (HIWORD(wParam) == BN_CLICKED) 516 UpdatePruningSelection(This); 517 break; 518 519 case IDC_REFRESHRATE: 520 if (HIWORD(wParam) == CBN_SELCHANGE) 521 UpdateRefreshRateSelection(This); 522 break; 523 } 524 break; 525 526 case WM_NOTIFY: 527 { 528 NMHDR *nmh = (NMHDR *)lParam; 529 530 switch (nmh->code) 531 { 532 case PSN_APPLY: 533 { 534 SetWindowLongPtr(hwndDlg, 535 DWLP_MSGRESULT, 536 ApplyMonitorChanges(This)); 537 break; 538 } 539 540 case PSN_RESET: 541 ResetMonitorChanges(This); 542 break; 543 544 case PSN_SETACTIVE: 545 UpdateRefreshFrequencyList(This); 546 break; 547 } 548 break; 549 } 550 } 551 552 return Ret; 553 } 554 555 static VOID 556 IDeskMonitor_Destroy(PDESKMONITOR This) 557 { 558 PDESKMONINFO pmi, pminext; 559 560 if (This->pdtobj != NULL) 561 { 562 IDataObject_Release(This->pdtobj); 563 This->pdtobj = NULL; 564 } 565 566 if (This->DeskExtInterface != NULL) 567 { 568 LocalFree((HLOCAL)This->DeskExtInterface); 569 This->DeskExtInterface = NULL; 570 } 571 572 if (This->lpDisplayDevice != NULL) 573 { 574 LocalFree((HLOCAL)This->lpDisplayDevice); 575 This->lpDisplayDevice = NULL; 576 } 577 578 /* Free all monitors */ 579 pmi = This->Monitors; 580 This->Monitors = NULL; 581 while (pmi != NULL) 582 { 583 pminext = pmi->Next; 584 LocalFree((HLOCAL)pmi); 585 pmi = pminext; 586 } 587 } 588 589 ULONG 590 IDeskMonitor_AddRef(PDESKMONITOR This) 591 { 592 ULONG ret; 593 594 ret = InterlockedIncrement((PLONG)&This->ref); 595 if (ret == 1) 596 InterlockedIncrement(&dll_refs); 597 598 return ret; 599 } 600 601 ULONG 602 IDeskMonitor_Release(PDESKMONITOR This) 603 { 604 ULONG ret; 605 606 ret = InterlockedDecrement((PLONG)&This->ref); 607 if (ret == 0) 608 { 609 IDeskMonitor_Destroy(This); 610 InterlockedDecrement(&dll_refs); 611 612 HeapFree(GetProcessHeap(), 613 0, 614 This); 615 } 616 617 return ret; 618 } 619 620 HRESULT STDMETHODCALLTYPE 621 IDeskMonitor_QueryInterface(PDESKMONITOR This, 622 REFIID iid, 623 PVOID *pvObject) 624 { 625 *pvObject = NULL; 626 627 if (IsEqualIID(iid, 628 &IID_IShellPropSheetExt) || 629 IsEqualIID(iid, 630 &IID_IUnknown)) 631 { 632 *pvObject = impl_to_interface(This, IShellPropSheetExt); 633 } 634 else if (IsEqualIID(iid, 635 &IID_IShellExtInit)) 636 { 637 *pvObject = impl_to_interface(This, IShellExtInit); 638 } 639 else if (IsEqualIID(iid, 640 &IID_IClassFactory)) 641 { 642 *pvObject = impl_to_interface(This, IClassFactory); 643 } 644 else 645 { 646 DPRINT1("IDeskMonitor::QueryInterface(%p,%p): E_NOINTERFACE\n", iid, pvObject); 647 return E_NOINTERFACE; 648 } 649 650 IDeskMonitor_AddRef(This); 651 return S_OK; 652 } 653 654 HRESULT 655 IDeskMonitor_Initialize(PDESKMONITOR This, 656 LPCITEMIDLIST pidlFolder, 657 IDataObject *pdtobj, 658 HKEY hkeyProgID) 659 { 660 DPRINT1("IDeskMonitor::Initialize(%p,%p,%p)\n", pidlFolder, pdtobj, hkeyProgID); 661 662 if (pdtobj != NULL) 663 { 664 IDataObject_AddRef(pdtobj); 665 This->pdtobj = pdtobj; 666 667 /* Get a copy of the desk.cpl extension interface */ 668 This->DeskExtInterface = QueryDeskCplExtInterface(This->pdtobj); 669 if (This->DeskExtInterface != NULL) 670 return S_OK; 671 } 672 673 return S_FALSE; 674 } 675 676 HRESULT 677 IDeskMonitor_AddPages(PDESKMONITOR This, 678 LPFNADDPROPSHEETPAGE pfnAddPage, 679 LPARAM lParam) 680 { 681 HPROPSHEETPAGE hpsp; 682 PROPSHEETPAGE psp; 683 684 DPRINT1("IDeskMonitor::AddPages(%p,%p)\n", pfnAddPage, lParam); 685 686 psp.dwSize = sizeof(psp); 687 psp.dwFlags = PSP_DEFAULT; 688 psp.hInstance = hInstance; 689 psp.pszTemplate = MAKEINTRESOURCE(IDD_MONITOR); 690 psp.pfnDlgProc = MonitorDlgProc; 691 psp.lParam = (LPARAM)This; 692 693 hpsp = CreatePropertySheetPage(&psp); 694 if (hpsp != NULL && pfnAddPage(hpsp, lParam)) 695 return S_OK; 696 697 return S_FALSE; 698 } 699 700 HRESULT 701 IDeskMonitor_ReplacePage(PDESKMONITOR This, 702 EXPPS uPageID, 703 LPFNADDPROPSHEETPAGE pfnReplacePage, 704 LPARAM lParam) 705 { 706 DPRINT1("IDeskMonitor::ReplacePage(%u,%p,%p)\n", uPageID, pfnReplacePage, lParam); 707 return E_NOTIMPL; 708 } 709 710 HRESULT 711 IDeskMonitor_Constructor(REFIID riid, 712 LPVOID *ppv) 713 { 714 PDESKMONITOR This; 715 HRESULT hRet = E_OUTOFMEMORY; 716 717 DPRINT1("IDeskMonitor::Constructor(%p,%p)\n", riid, ppv); 718 719 This = HeapAlloc(GetProcessHeap(), 720 0, 721 sizeof(*This)); 722 if (This != NULL) 723 { 724 ZeroMemory(This, 725 sizeof(*This)); 726 727 IDeskMonitor_InitIface(This); 728 729 hRet = IDeskMonitor_QueryInterface(This, 730 riid, 731 ppv); 732 if (!SUCCEEDED(hRet)) 733 IDeskMonitor_Release(This); 734 } 735 736 return hRet; 737 } 738 739 BOOL WINAPI 740 DllMain(HINSTANCE hinstDLL, 741 DWORD dwReason, 742 LPVOID lpvReserved) 743 { 744 switch (dwReason) 745 { 746 case DLL_PROCESS_ATTACH: 747 hInstance = hinstDLL; 748 DisableThreadLibraryCalls(hInstance); 749 break; 750 } 751 752 return TRUE; 753 } 754