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 } 753