xref: /reactos/dll/cpl/desk/settings.c (revision ff293a18)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Display Control Panel
4  * FILE:            dll/cpl/desk/settings.c
5  * PURPOSE:         Settings property page
6  *
7  * PROGRAMMERS:     Trevor McCort (lycan359@gmail.com)
8  *                  Hervé Poussineau (hpoussin@reactos.org)
9  */
10 
11 #include "desk.h"
12 
13 typedef struct _SETTINGS_DATA
14 {
15     PDISPLAY_DEVICE_ENTRY DisplayDeviceList;
16     PDISPLAY_DEVICE_ENTRY CurrentDisplayDevice;
17     HBITMAP hSpectrumBitmaps[NUM_SPECTRUM_BITMAPS];
18     int cxSource[NUM_SPECTRUM_BITMAPS];
19     int cySource[NUM_SPECTRUM_BITMAPS];
20 } SETTINGS_DATA, *PSETTINGS_DATA;
21 
22 typedef struct _TIMEOUTDATA
23 {
24     TCHAR szRawBuffer[256];
25     TCHAR szCookedBuffer[256];
26     INT nTimeout;
27 } TIMEOUTDATA, *PTIMEOUTDATA;
28 
29 static VOID
30 UpdateDisplay(IN HWND hwndDlg, PSETTINGS_DATA pData, IN BOOL bUpdateThumb)
31 {
32     TCHAR Buffer[64];
33     TCHAR Pixel[64];
34     DWORD index;
35     HWND hwndMonSel;
36     MONSL_MONINFO info;
37 
38     LoadString(hApplet, IDS_PIXEL, Pixel, sizeof(Pixel) / sizeof(TCHAR));
39     _stprintf(Buffer, Pixel, pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth, pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight, Pixel);
40     SendDlgItemMessage(hwndDlg, IDC_SETTINGS_RESOLUTION_TEXT, WM_SETTEXT, 0, (LPARAM)Buffer);
41 
42     for (index = 0; index < pData->CurrentDisplayDevice->ResolutionsCount; index++)
43     {
44         if (pData->CurrentDisplayDevice->Resolutions[index].dmPelsWidth == pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth &&
45             pData->CurrentDisplayDevice->Resolutions[index].dmPelsHeight == pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight)
46         {
47             if (bUpdateThumb)
48                 SendDlgItemMessage(hwndDlg, IDC_SETTINGS_RESOLUTION, TBM_SETPOS, TRUE, index);
49             break;
50         }
51     }
52     if (LoadString(hApplet, (2900 + pData->CurrentDisplayDevice->CurrentSettings->dmBitsPerPel), Buffer, sizeof(Buffer) / sizeof(TCHAR)))
53         SendDlgItemMessage(hwndDlg, IDC_SETTINGS_BPP, CB_SELECTSTRING, (WPARAM)-1, (LPARAM)Buffer);
54 
55     hwndMonSel = GetDlgItem(hwndDlg, IDC_SETTINGS_MONSEL);
56     index = (INT)SendMessage(hwndMonSel, MSLM_GETCURSEL, 0, 0);
57     if (index != (DWORD)-1 && SendMessage(hwndMonSel, MSLM_GETMONITORINFO, index, (LPARAM)&info))
58     {
59         info.Size.cx = pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth;
60         info.Size.cy = pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight;
61         SendMessage(hwndMonSel, MSLM_SETMONITORINFO, index, (LPARAM)&info);
62     }
63 }
64 
65 static int
66 CompareSettings(PSETTINGS_ENTRY Entry, DWORD dmPelsWidth, DWORD dmPelsHeight,
67                 DWORD dmBitsPerPel, DWORD dmDisplayFrequency)
68 {
69     if (Entry->dmPelsWidth  == dmPelsWidth  &&
70         Entry->dmPelsHeight == dmPelsHeight &&
71         Entry->dmBitsPerPel == dmBitsPerPel &&
72         Entry->dmDisplayFrequency == dmDisplayFrequency)
73     {
74         return 0;
75     }
76     else
77     if ((Entry->dmPelsWidth  < dmPelsWidth) ||
78         (Entry->dmPelsWidth == dmPelsWidth && Entry->dmPelsHeight < dmPelsHeight) ||
79         (Entry->dmPelsWidth == dmPelsWidth && Entry->dmPelsHeight == dmPelsHeight &&
80          Entry->dmBitsPerPel < dmBitsPerPel))
81     {
82         return 1;
83     }
84     return -1;
85 }
86 
87 static PSETTINGS_ENTRY
88 GetPossibleSettings(IN LPCTSTR DeviceName, OUT DWORD* pSettingsCount, OUT PSETTINGS_ENTRY* CurrentSettings)
89 {
90     DEVMODE devmode;
91     DWORD NbSettings = 0;
92     DWORD iMode = 0;
93     DWORD dwFlags = 0;
94     PSETTINGS_ENTRY Settings = NULL;
95     HDC hDC;
96     PSETTINGS_ENTRY Current;
97     DWORD bpp, xres, yres;
98     DWORD curDispFreq;
99 
100     /* Get current settings */
101     *CurrentSettings = NULL;
102     hDC = CreateIC(NULL, DeviceName, NULL, NULL);
103     bpp = GetDeviceCaps(hDC, PLANES);
104     bpp *= GetDeviceCaps(hDC, BITSPIXEL);
105     xres = GetDeviceCaps(hDC, HORZRES);
106     yres = GetDeviceCaps(hDC, VERTRES);
107     DeleteDC(hDC);
108 
109     /* List all settings */
110     devmode.dmSize = (WORD)sizeof(devmode);
111     devmode.dmDriverExtra = 0;
112 
113     if (!EnumDisplaySettingsEx(DeviceName, ENUM_CURRENT_SETTINGS, &devmode, dwFlags))
114         return NULL;
115 
116     curDispFreq = devmode.dmDisplayFrequency;
117 
118     while (EnumDisplaySettingsEx(DeviceName, iMode, &devmode, dwFlags))
119     {
120         iMode++;
121 
122         if (devmode.dmPelsWidth < 640 ||
123             devmode.dmPelsHeight < 480 ||
124             devmode.dmDisplayFrequency != curDispFreq ||
125             (devmode.dmBitsPerPel != 4 &&
126              devmode.dmBitsPerPel != 8 &&
127              devmode.dmBitsPerPel != 16 &&
128              devmode.dmBitsPerPel != 24 &&
129              devmode.dmBitsPerPel != 32))
130         {
131             continue;
132         }
133 
134         Current = HeapAlloc(GetProcessHeap(), 0, sizeof(SETTINGS_ENTRY));
135         if (Current != NULL)
136         {
137             /* Sort resolutions by increasing height, and BPP */
138             PSETTINGS_ENTRY Previous = NULL;
139             PSETTINGS_ENTRY Next = Settings;
140             Current->dmPelsWidth = devmode.dmPelsWidth;
141             Current->dmPelsHeight = devmode.dmPelsHeight;
142             Current->dmBitsPerPel = devmode.dmBitsPerPel;
143             Current->dmDisplayFrequency = devmode.dmDisplayFrequency;
144             while (Next != NULL &&
145                    CompareSettings(Next, devmode.dmPelsWidth,
146                                    devmode.dmPelsHeight, devmode.dmBitsPerPel,
147                                    devmode.dmDisplayFrequency) > 0)
148             {
149                 Previous = Next;
150                 Next = Next->Flink;
151             }
152             Current->Blink = Previous;
153             Current->Flink = Next;
154             if (Previous == NULL)
155                 Settings = Current;
156             else
157                 Previous->Flink = Current;
158             if (Next != NULL)
159                 Next->Blink = Current;
160             if (devmode.dmPelsWidth == xres && devmode.dmPelsHeight == yres && devmode.dmBitsPerPel == bpp)
161             {
162                 *CurrentSettings = Current;
163             }
164             NbSettings++;
165         }
166     }
167 
168     *pSettingsCount = NbSettings;
169     return Settings;
170 }
171 
172 static BOOL
173 AddDisplayDevice(IN PSETTINGS_DATA pData, IN const DISPLAY_DEVICE *DisplayDevice)
174 {
175     PDISPLAY_DEVICE_ENTRY newEntry = NULL;
176     LPTSTR description = NULL;
177     LPTSTR name = NULL;
178     LPTSTR key = NULL;
179     LPTSTR devid = NULL;
180     SIZE_T descriptionSize, nameSize, keySize, devidSize;
181     PSETTINGS_ENTRY Current;
182     DWORD ResolutionsCount = 1;
183     DWORD i;
184 
185     newEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DISPLAY_DEVICE_ENTRY));
186     if (!newEntry) goto ByeBye;
187 
188     newEntry->Settings = GetPossibleSettings(DisplayDevice->DeviceName, &newEntry->SettingsCount, &newEntry->CurrentSettings);
189     if (!newEntry->Settings) goto ByeBye;
190 
191     newEntry->InitialSettings.dmPelsWidth = newEntry->CurrentSettings->dmPelsWidth;
192     newEntry->InitialSettings.dmPelsHeight = newEntry->CurrentSettings->dmPelsHeight;
193     newEntry->InitialSettings.dmBitsPerPel = newEntry->CurrentSettings->dmBitsPerPel;
194     newEntry->InitialSettings.dmDisplayFrequency = newEntry->CurrentSettings->dmDisplayFrequency;
195 
196     /* Count different resolutions */
197     for (Current = newEntry->Settings; Current != NULL; Current = Current->Flink)
198     {
199         if (Current->Flink != NULL &&
200             ((Current->dmPelsWidth != Current->Flink->dmPelsWidth) ||
201              (Current->dmPelsHeight != Current->Flink->dmPelsHeight)))
202         {
203             ResolutionsCount++;
204         }
205     }
206 
207     newEntry->Resolutions = HeapAlloc(GetProcessHeap(), 0, ResolutionsCount * sizeof(RESOLUTION_INFO));
208     if (!newEntry->Resolutions) goto ByeBye;
209 
210     newEntry->ResolutionsCount = ResolutionsCount;
211 
212     /* Fill resolutions infos */
213     for (Current = newEntry->Settings, i = 0; Current != NULL; Current = Current->Flink)
214     {
215         if (Current->Flink == NULL ||
216             (Current->Flink != NULL &&
217             ((Current->dmPelsWidth != Current->Flink->dmPelsWidth) ||
218              (Current->dmPelsHeight != Current->Flink->dmPelsHeight))))
219         {
220             newEntry->Resolutions[i].dmPelsWidth = Current->dmPelsWidth;
221             newEntry->Resolutions[i].dmPelsHeight = Current->dmPelsHeight;
222             i++;
223         }
224     }
225     descriptionSize = (_tcslen(DisplayDevice->DeviceString) + 1) * sizeof(TCHAR);
226     description = HeapAlloc(GetProcessHeap(), 0, descriptionSize);
227     if (!description) goto ByeBye;
228 
229     nameSize = (_tcslen(DisplayDevice->DeviceName) + 1) * sizeof(TCHAR);
230     name = HeapAlloc(GetProcessHeap(), 0, nameSize);
231     if (!name) goto ByeBye;
232 
233     keySize = (_tcslen(DisplayDevice->DeviceKey) + 1) * sizeof(TCHAR);
234     key = HeapAlloc(GetProcessHeap(), 0, keySize);
235     if (!key) goto ByeBye;
236 
237     devidSize = (_tcslen(DisplayDevice->DeviceID) + 1) * sizeof(TCHAR);
238     devid = HeapAlloc(GetProcessHeap(), 0, devidSize);
239     if (!devid) goto ByeBye;
240 
241     memcpy(description, DisplayDevice->DeviceString, descriptionSize);
242     memcpy(name, DisplayDevice->DeviceName, nameSize);
243     memcpy(key, DisplayDevice->DeviceKey, keySize);
244     memcpy(devid, DisplayDevice->DeviceID, devidSize);
245     newEntry->DeviceDescription = description;
246     newEntry->DeviceName = name;
247     newEntry->DeviceKey = key;
248     newEntry->DeviceID = devid;
249     newEntry->DeviceStateFlags = DisplayDevice->StateFlags;
250     newEntry->Flink = pData->DisplayDeviceList;
251     pData->DisplayDeviceList = newEntry;
252     return TRUE;
253 
254 ByeBye:
255     if (newEntry != NULL)
256     {
257         if (newEntry->Settings != NULL)
258         {
259             Current = newEntry->Settings;
260             while (Current != NULL)
261             {
262                 PSETTINGS_ENTRY Next = Current->Flink;
263                 HeapFree(GetProcessHeap(), 0, Current);
264                 Current = Next;
265             }
266         }
267         if (newEntry->Resolutions != NULL)
268             HeapFree(GetProcessHeap(), 0, newEntry->Resolutions);
269         HeapFree(GetProcessHeap(), 0, newEntry);
270     }
271     if (description != NULL)
272         HeapFree(GetProcessHeap(), 0, description);
273     if (name != NULL)
274         HeapFree(GetProcessHeap(), 0, name);
275     if (key != NULL)
276         HeapFree(GetProcessHeap(), 0, key);
277     return FALSE;
278 }
279 
280 static VOID
281 OnDisplayDeviceChanged(IN HWND hwndDlg, IN PSETTINGS_DATA pData, IN PDISPLAY_DEVICE_ENTRY pDeviceEntry)
282 {
283     PSETTINGS_ENTRY Current;
284     DWORD index;
285 
286     pData->CurrentDisplayDevice = pDeviceEntry; /* Update variable */
287 
288     /* Fill color depths combo box */
289     SendDlgItemMessage(hwndDlg, IDC_SETTINGS_BPP, CB_RESETCONTENT, 0, 0);
290     for (Current = pDeviceEntry->Settings; Current != NULL; Current = Current->Flink)
291     {
292         TCHAR Buffer[64];
293         if (LoadString(hApplet, (2900 + Current->dmBitsPerPel), Buffer, sizeof(Buffer) / sizeof(TCHAR)))
294         {
295             index = (DWORD) SendDlgItemMessage(hwndDlg, IDC_SETTINGS_BPP, CB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)Buffer);
296             if (index == (DWORD)CB_ERR)
297             {
298                 index = (DWORD) SendDlgItemMessage(hwndDlg, IDC_SETTINGS_BPP, CB_ADDSTRING, 0, (LPARAM)Buffer);
299                 SendDlgItemMessage(hwndDlg, IDC_SETTINGS_BPP, CB_SETITEMDATA, index, Current->dmBitsPerPel);
300             }
301         }
302     }
303 
304     /* Fill device description */
305     SendDlgItemMessage(hwndDlg, IDC_SETTINGS_DEVICE, WM_SETTEXT, 0, (LPARAM)pDeviceEntry->DeviceDescription);
306 
307     /* Fill resolutions slider */
308     SendDlgItemMessage(hwndDlg, IDC_SETTINGS_RESOLUTION, TBM_CLEARTICS, TRUE, 0);
309     SendDlgItemMessage(hwndDlg, IDC_SETTINGS_RESOLUTION, TBM_SETRANGE, TRUE, MAKELONG(0, pDeviceEntry->ResolutionsCount - 1));
310 
311     UpdateDisplay(hwndDlg, pData, TRUE);
312 }
313 
314 static VOID
315 SettingsOnInitDialog(IN HWND hwndDlg)
316 {
317     BITMAP bitmap;
318     DWORD Result = 0;
319     DWORD iDevNum = 0;
320     DWORD i;
321     DISPLAY_DEVICE displayDevice;
322     PSETTINGS_DATA pData;
323 
324     pData = HeapAlloc(GetProcessHeap(), 0, sizeof(SETTINGS_DATA));
325     if (pData == NULL)
326         return;
327 
328     SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pData);
329 
330     /* Get video cards list */
331     pData->DisplayDeviceList = NULL;
332     displayDevice.cb = sizeof(displayDevice);
333     while (EnumDisplayDevices(NULL, iDevNum, &displayDevice, 0x1))
334     {
335         if ((displayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) != 0)
336         {
337             if (AddDisplayDevice(pData, &displayDevice))
338                 Result++;
339         }
340         iDevNum++;
341     }
342 
343     if (Result == 0)
344     {
345         /* No adapter found */
346         EnableWindow(GetDlgItem(hwndDlg, IDC_SETTINGS_BPP), FALSE);
347         EnableWindow(GetDlgItem(hwndDlg, IDC_SETTINGS_RESOLUTION), FALSE);
348         EnableWindow(GetDlgItem(hwndDlg, IDC_SETTINGS_RESOLUTION_TEXT), FALSE);
349         EnableWindow(GetDlgItem(hwndDlg, IDC_SETTINGS_ADVANCED), FALSE);
350         ShowWindow(GetDlgItem(hwndDlg, IDC_SETTINGS_SPECTRUM), SW_HIDE);
351 
352         /* Do not initialize the color spectrum bitmaps */
353         memset(pData->hSpectrumBitmaps, 0, sizeof(pData->hSpectrumBitmaps));
354         return;
355     }
356     else if (Result == 1)
357     {
358         MONSL_MONINFO monitors;
359 
360         /* Single video adapter */
361         OnDisplayDeviceChanged(hwndDlg, pData, pData->DisplayDeviceList);
362 
363         monitors.Position.x = monitors.Position.y = 0;
364         monitors.Size.cx = pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth;
365         monitors.Size.cy = pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight;
366         monitors.Flags = 0;
367         SendDlgItemMessage(hwndDlg,
368                    IDC_SETTINGS_MONSEL,
369                    MSLM_SETMONITORSINFO,
370                    1,
371                    (LPARAM)&monitors);
372     }
373     else /* FIXME: Incomplete! */
374     {
375         PMONSL_MONINFO pMonitors;
376         DWORD i;
377 
378         OnDisplayDeviceChanged(hwndDlg, pData, pData->DisplayDeviceList);
379 
380         pMonitors = (PMONSL_MONINFO)HeapAlloc(GetProcessHeap(), 0, sizeof(MONSL_MONINFO) * Result);
381         if (pMonitors)
382         {
383             DWORD hack = 1280;
384             for (i = 0; i < Result; i++)
385             {
386                 pMonitors[i].Position.x = hack * i;
387                 pMonitors[i].Position.y = 0;
388                 pMonitors[i].Size.cx = pData->DisplayDeviceList->CurrentSettings->dmPelsWidth;
389                 pMonitors[i].Size.cy = pData->DisplayDeviceList->CurrentSettings->dmPelsHeight;
390                 pMonitors[i].Flags = 0;
391             }
392 
393             SendDlgItemMessage(hwndDlg,
394                        IDC_SETTINGS_MONSEL,
395                        MSLM_SETMONITORSINFO,
396                        Result,
397                        (LPARAM)pMonitors);
398 
399             HeapFree(GetProcessHeap(), 0, pMonitors);
400         }
401     }
402 
403     /* Initialize the color spectrum bitmaps */
404     for (i = 0; i < NUM_SPECTRUM_BITMAPS; i++)
405     {
406         pData->hSpectrumBitmaps[i] = LoadImageW(hApplet, MAKEINTRESOURCEW(IDB_SPECTRUM_4 + i), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
407 
408         if (pData->hSpectrumBitmaps[i] != NULL)
409         {
410             if (GetObjectW(pData->hSpectrumBitmaps[i], sizeof(BITMAP), &bitmap) != 0)
411             {
412                 pData->cxSource[i] = bitmap.bmWidth;
413                 pData->cySource[i] = bitmap.bmHeight;
414             }
415             else
416             {
417                 pData->cxSource[i] = 0;
418                 pData->cySource[i] = 0;
419             }
420         }
421     }
422 }
423 
424 /* Get the ID for SETTINGS_DATA::hSpectrumBitmaps */
425 static VOID
426 ShowColorSpectrum(IN HDC hDC, IN LPRECT client, IN DWORD BitsPerPel, IN PSETTINGS_DATA pData)
427 {
428     HDC hdcMem;
429     INT iBitmap;
430 
431     hdcMem = CreateCompatibleDC(hDC);
432 
433     if (!hdcMem)
434         return;
435 
436     switch(BitsPerPel)
437     {
438         case 4:  iBitmap = 0; break;
439         case 8:  iBitmap = 1; break;
440         default: iBitmap = 2;
441     }
442 
443     if (SelectObject(hdcMem, pData->hSpectrumBitmaps[iBitmap]))
444     {
445         StretchBlt(hDC,
446                client->left, client->top,
447                client->right - client->left,
448                client->bottom - client->top,
449                hdcMem, 0, 0,
450                pData->cxSource[iBitmap],
451                pData->cySource[iBitmap], SRCCOPY);
452     }
453 
454     DeleteDC(hdcMem);
455 }
456 
457 static VOID
458 OnBPPChanged(IN HWND hwndDlg, IN PSETTINGS_DATA pData)
459 {
460     /* If new BPP is not compatible with resolution:
461      * 1) try to find the nearest smaller matching resolution
462      * 2) otherwise, get the nearest bigger resolution
463      */
464     PSETTINGS_ENTRY Current;
465     DWORD dmNewBitsPerPel;
466     DWORD index;
467     HDC hSpectrumDC;
468     HWND hSpectrumControl;
469     RECT client;
470 
471     index = (DWORD) SendDlgItemMessage(hwndDlg, IDC_SETTINGS_BPP, CB_GETCURSEL, 0, 0);
472     dmNewBitsPerPel = (DWORD) SendDlgItemMessage(hwndDlg, IDC_SETTINGS_BPP, CB_GETITEMDATA, index, 0);
473 
474     /* Show a new spectrum bitmap */
475     hSpectrumControl = GetDlgItem(hwndDlg, IDC_SETTINGS_SPECTRUM);
476     hSpectrumDC = GetDC(hSpectrumControl);
477     if (hSpectrumDC == NULL)
478         return;
479 
480     GetClientRect(hSpectrumControl, &client);
481     ShowColorSpectrum(hSpectrumDC, &client, dmNewBitsPerPel, pData);
482     ReleaseDC(hSpectrumControl, hSpectrumDC);
483 
484     /* Find if new parameters are valid */
485     Current = pData->CurrentDisplayDevice->CurrentSettings;
486     if (dmNewBitsPerPel == Current->dmBitsPerPel)
487     {
488         /* No change */
489         return;
490     }
491 
492     PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
493 
494     if (dmNewBitsPerPel < Current->dmBitsPerPel)
495     {
496         Current = Current->Blink;
497         while (Current != NULL)
498         {
499             if (Current->dmBitsPerPel == dmNewBitsPerPel
500              && Current->dmPelsHeight == pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight
501              && Current->dmPelsWidth == pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth)
502             {
503                 pData->CurrentDisplayDevice->CurrentSettings = Current;
504                 UpdateDisplay(hwndDlg, pData, TRUE);
505                 return;
506             }
507             Current = Current->Blink;
508         }
509     }
510     else
511     {
512         Current = Current->Flink;
513         while (Current != NULL)
514         {
515             if (Current->dmBitsPerPel == dmNewBitsPerPel
516              && Current->dmPelsHeight == pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight
517              && Current->dmPelsWidth == pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth)
518             {
519                 pData->CurrentDisplayDevice->CurrentSettings = Current;
520                 UpdateDisplay(hwndDlg, pData, TRUE);
521                 return;
522             }
523             Current = Current->Flink;
524         }
525     }
526 
527     /* Search smaller resolution compatible with current color depth */
528     Current = pData->CurrentDisplayDevice->CurrentSettings->Blink;
529     while (Current != NULL)
530     {
531         if (Current->dmBitsPerPel == dmNewBitsPerPel)
532         {
533             pData->CurrentDisplayDevice->CurrentSettings = Current;
534             UpdateDisplay(hwndDlg, pData, TRUE);
535             return;
536         }
537         Current = Current->Blink;
538     }
539 
540     /* Search bigger resolution compatible with current color depth */
541     Current = pData->CurrentDisplayDevice->CurrentSettings->Flink;
542     while (Current != NULL)
543     {
544         if (Current->dmBitsPerPel == dmNewBitsPerPel)
545         {
546             pData->CurrentDisplayDevice->CurrentSettings = Current;
547             UpdateDisplay(hwndDlg, pData, TRUE);
548             return;
549         }
550         Current = Current->Flink;
551     }
552 
553     /* We shouldn't go there */
554 }
555 
556 static VOID
557 OnResolutionChanged(IN HWND hwndDlg, IN PSETTINGS_DATA pData, IN DWORD NewPosition,
558                     IN BOOL bUpdateThumb)
559 {
560     /* If new resolution is not compatible with color depth:
561      * 1) try to find the nearest bigger matching color depth
562      * 2) otherwise, get the nearest smaller color depth
563      */
564     PSETTINGS_ENTRY Current;
565     DWORD dmNewPelsHeight = pData->CurrentDisplayDevice->Resolutions[NewPosition].dmPelsHeight;
566     DWORD dmNewPelsWidth = pData->CurrentDisplayDevice->Resolutions[NewPosition].dmPelsWidth;
567     DWORD dmBitsPerPel;
568     DWORD dmDisplayFrequency;
569 
570     /* Find if new parameters are valid */
571     Current = pData->CurrentDisplayDevice->CurrentSettings;
572     if (dmNewPelsHeight == Current->dmPelsHeight && dmNewPelsWidth == Current->dmPelsWidth)
573     {
574         /* No change */
575         return;
576     }
577 
578     PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
579 
580     dmBitsPerPel = Current->dmBitsPerPel;
581     dmDisplayFrequency = Current->dmDisplayFrequency;
582 
583     if (CompareSettings(Current, dmNewPelsWidth,
584                         dmNewPelsHeight, dmBitsPerPel,
585                         dmDisplayFrequency) < 0)
586     {
587         Current = Current->Blink;
588         while (Current != NULL)
589         {
590             if (Current->dmPelsHeight == dmNewPelsHeight
591              && Current->dmPelsWidth  == dmNewPelsWidth
592              && Current->dmBitsPerPel == dmBitsPerPel)
593             {
594                 pData->CurrentDisplayDevice->CurrentSettings = Current;
595                 UpdateDisplay(hwndDlg, pData, bUpdateThumb);
596                 return;
597             }
598             Current = Current->Blink;
599         }
600     }
601     else
602     {
603         Current = Current->Flink;
604         while (Current != NULL)
605         {
606             if (Current->dmPelsHeight == dmNewPelsHeight
607              && Current->dmPelsWidth  == dmNewPelsWidth
608              && Current->dmBitsPerPel == dmBitsPerPel)
609             {
610                 pData->CurrentDisplayDevice->CurrentSettings = Current;
611                 UpdateDisplay(hwndDlg, pData, bUpdateThumb);
612                 return;
613             }
614             Current = Current->Flink;
615         }
616     }
617 
618     /* Search bigger color depth compatible with current resolution */
619     Current = pData->CurrentDisplayDevice->CurrentSettings->Flink;
620     while (Current != NULL)
621     {
622         if (dmNewPelsHeight == Current->dmPelsHeight && dmNewPelsWidth == Current->dmPelsWidth)
623         {
624             pData->CurrentDisplayDevice->CurrentSettings = Current;
625             UpdateDisplay(hwndDlg, pData, bUpdateThumb);
626             return;
627         }
628         Current = Current->Flink;
629     }
630 
631     /* Search smaller color depth compatible with current resolution */
632     Current = pData->CurrentDisplayDevice->CurrentSettings->Blink;
633     while (Current != NULL)
634     {
635         if (dmNewPelsHeight == Current->dmPelsHeight && dmNewPelsWidth == Current->dmPelsWidth)
636         {
637             pData->CurrentDisplayDevice->CurrentSettings = Current;
638             UpdateDisplay(hwndDlg, pData, bUpdateThumb);
639             return;
640         }
641         Current = Current->Blink;
642     }
643 
644     /* We shouldn't go there */
645 }
646 
647 /* Property sheet page callback */
648 UINT CALLBACK
649 SettingsPageCallbackProc(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp)
650 {
651     UINT Ret = 0;
652 
653     switch (uMsg)
654     {
655         case PSPCB_CREATE:
656             Ret = RegisterMonitorSelectionControl(hApplet);
657             break;
658 
659         case PSPCB_RELEASE:
660             UnregisterMonitorSelectionControl(hApplet);
661             break;
662     }
663 
664     return Ret;
665 }
666 
667 static INT_PTR CALLBACK
668 ConfirmDlgProc(IN HWND hwndDlg, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam)
669 {
670     PTIMEOUTDATA pData;
671 
672     pData = (PTIMEOUTDATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
673 
674     switch(uMsg)
675     {
676         case WM_INITDIALOG:
677             /* Allocate the local dialog data */
678             pData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TIMEOUTDATA));
679             if (pData == NULL)
680                 return FALSE;
681 
682             /* Link the dialog data to the dialog */
683             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pData);
684 
685             /* Timeout in seconds */
686             pData->nTimeout = 15;
687 
688             /* Load the raw timeout string */
689             LoadString(hApplet, IDS_TIMEOUTTEXT, pData->szRawBuffer, ARRAYSIZE(pData->szRawBuffer));
690 
691             /* Cook the timeout string and show it */
692             _stprintf(pData->szCookedBuffer, pData->szRawBuffer, pData->nTimeout);
693             SetDlgItemText(hwndDlg, IDC_TIMEOUTTEXT, pData->szCookedBuffer);
694 
695             /* Start the timer (ticks every second)*/
696             SetTimer(hwndDlg, 1, 1000, NULL);
697             break;
698 
699         case WM_TIMER:
700             /* Update the timepout value */
701             pData->nTimeout--;
702 
703             /* Update the timeout text */
704             _stprintf(pData->szCookedBuffer, pData->szRawBuffer, pData->nTimeout);
705             SetDlgItemText(hwndDlg, IDC_TIMEOUTTEXT, pData->szCookedBuffer);
706 
707             /* Kill the timer and return a 'No', if we ran out of time */
708             if (pData->nTimeout == 0)
709             {
710                 KillTimer(hwndDlg, 1);
711                 EndDialog(hwndDlg, IDNO);
712             }
713             break;
714 
715         case WM_COMMAND:
716             /* Kill the timer and return the clicked button id */
717             KillTimer(hwndDlg, 1);
718             EndDialog(hwndDlg, LOWORD(wParam));
719             break;
720 
721         case WM_DESTROY:
722             /* Free the local dialog data */
723             HeapFree(GetProcessHeap(), 0, pData);
724             break;
725     }
726 
727     return FALSE;
728 }
729 
730 static VOID
731 ApplyDisplaySettings(HWND hwndDlg, PSETTINGS_DATA pData)
732 {
733     TCHAR Message[1024], Title[256];
734     DEVMODE devmode;
735     LONG rc;
736 
737     RtlZeroMemory(&devmode, sizeof(devmode));
738     devmode.dmSize = (WORD)sizeof(devmode);
739     devmode.dmPelsWidth = pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth;
740     devmode.dmPelsHeight = pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight;
741     devmode.dmBitsPerPel = pData->CurrentDisplayDevice->CurrentSettings->dmBitsPerPel;
742     devmode.dmDisplayFrequency = pData->CurrentDisplayDevice->CurrentSettings->dmDisplayFrequency;
743     devmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
744 
745     rc = ChangeDisplaySettingsEx(pData->CurrentDisplayDevice->DeviceName,
746                                  &devmode,
747                                  NULL,
748                                  CDS_UPDATEREGISTRY,
749                                  NULL);
750     switch (rc)
751     {
752         case DISP_CHANGE_SUCCESSFUL:
753             break;
754 
755         case DISP_CHANGE_RESTART:
756             LoadString(hApplet, IDS_DISPLAY_SETTINGS, Title, sizeof(Title) / sizeof(TCHAR));
757             LoadString(hApplet, IDS_APPLY_NEEDS_RESTART, Message, sizeof(Message) / sizeof (TCHAR));
758             MessageBox(hwndDlg, Message, Title, MB_OK | MB_ICONINFORMATION);
759             return;
760 
761         case DISP_CHANGE_FAILED:
762         default:
763             LoadString(hApplet, IDS_DISPLAY_SETTINGS, Title, sizeof(Title) / sizeof(TCHAR));
764             LoadString(hApplet, IDS_APPLY_FAILED, Message, sizeof(Message) / sizeof (TCHAR));
765             MessageBox(hwndDlg, Message, Title, MB_OK | MB_ICONSTOP);
766             return;
767     }
768 
769     if (DialogBox(hApplet, MAKEINTRESOURCE(IDD_CONFIRMSETTINGS), hwndDlg, ConfirmDlgProc) == IDYES)
770     {
771         pData->CurrentDisplayDevice->InitialSettings.dmPelsWidth = pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth;
772         pData->CurrentDisplayDevice->InitialSettings.dmPelsHeight = pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight;
773         pData->CurrentDisplayDevice->InitialSettings.dmBitsPerPel = pData->CurrentDisplayDevice->CurrentSettings->dmBitsPerPel;
774         pData->CurrentDisplayDevice->InitialSettings.dmDisplayFrequency = pData->CurrentDisplayDevice->CurrentSettings->dmDisplayFrequency;
775     }
776     else
777     {
778         devmode.dmPelsWidth = pData->CurrentDisplayDevice->InitialSettings.dmPelsWidth;
779         devmode.dmPelsHeight = pData->CurrentDisplayDevice->InitialSettings.dmPelsHeight;
780         devmode.dmBitsPerPel = pData->CurrentDisplayDevice->InitialSettings.dmBitsPerPel;
781         devmode.dmDisplayFrequency = pData->CurrentDisplayDevice->InitialSettings.dmDisplayFrequency;
782 
783         rc = ChangeDisplaySettingsEx(pData->CurrentDisplayDevice->DeviceName,
784                                      &devmode,
785                                      NULL,
786                                      CDS_UPDATEREGISTRY,
787                                      NULL);
788         switch (rc)
789         {
790             case DISP_CHANGE_SUCCESSFUL:
791                 pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth = pData->CurrentDisplayDevice->InitialSettings.dmPelsWidth;
792                 pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight = pData->CurrentDisplayDevice->InitialSettings.dmPelsHeight;
793                 pData->CurrentDisplayDevice->CurrentSettings->dmBitsPerPel = pData->CurrentDisplayDevice->InitialSettings.dmBitsPerPel;
794                 pData->CurrentDisplayDevice->CurrentSettings->dmDisplayFrequency = pData->CurrentDisplayDevice->InitialSettings.dmDisplayFrequency;
795                 UpdateDisplay(hwndDlg, pData, TRUE);
796                 break;
797 
798             case DISP_CHANGE_RESTART:
799                 LoadString(hApplet, IDS_DISPLAY_SETTINGS, Title, sizeof(Title) / sizeof(TCHAR));
800                 LoadString(hApplet, IDS_APPLY_NEEDS_RESTART, Message, sizeof(Message) / sizeof (TCHAR));
801                 MessageBox(hwndDlg, Message, Title, MB_OK | MB_ICONINFORMATION);
802                 return;
803 
804             case DISP_CHANGE_FAILED:
805             default:
806                 LoadString(hApplet, IDS_DISPLAY_SETTINGS, Title, sizeof(Title) / sizeof(TCHAR));
807                 LoadString(hApplet, IDS_APPLY_FAILED, Message, sizeof(Message) / sizeof (TCHAR));
808                 MessageBox(hwndDlg, Message, Title, MB_OK | MB_ICONSTOP);
809                 return;
810         }
811     }
812 }
813 
814 /* Property page dialog callback */
815 INT_PTR CALLBACK
816 SettingsPageProc(IN HWND hwndDlg, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam)
817 {
818     PSETTINGS_DATA pData;
819 
820     pData = (PSETTINGS_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
821 
822     switch(uMsg)
823     {
824         case WM_INITDIALOG:
825         {
826             SettingsOnInitDialog(hwndDlg);
827             break;
828         }
829         case WM_DRAWITEM:
830         {
831             LPDRAWITEMSTRUCT lpDrawItem;
832             lpDrawItem = (LPDRAWITEMSTRUCT) lParam;
833 
834             if (lpDrawItem->CtlID == IDC_SETTINGS_SPECTRUM)
835                 ShowColorSpectrum(lpDrawItem->hDC, &lpDrawItem->rcItem, pData->CurrentDisplayDevice->CurrentSettings->dmBitsPerPel, pData);
836             break;
837         }
838         case WM_COMMAND:
839         {
840             DWORD controlId = LOWORD(wParam);
841             DWORD command   = HIWORD(wParam);
842 
843             if (controlId == IDC_SETTINGS_ADVANCED && command == BN_CLICKED)
844                 DisplayAdvancedSettings(hwndDlg, pData->CurrentDisplayDevice);
845             else if (controlId == IDC_SETTINGS_BPP && command == CBN_SELCHANGE)
846                 OnBPPChanged(hwndDlg, pData);
847             break;
848         }
849         case WM_HSCROLL:
850         {
851             switch (LOWORD(wParam))
852             {
853                 case TB_LINEUP:
854                 case TB_LINEDOWN:
855                 case TB_PAGEUP:
856                 case TB_PAGEDOWN:
857                 case TB_TOP:
858                 case TB_BOTTOM:
859                 case TB_ENDTRACK:
860                 {
861                     DWORD newPosition = (DWORD) SendDlgItemMessage(hwndDlg, IDC_SETTINGS_RESOLUTION, TBM_GETPOS, 0, 0);
862                     OnResolutionChanged(hwndDlg, pData, newPosition, TRUE);
863                     break;
864                 }
865 
866                 case TB_THUMBTRACK:
867                     OnResolutionChanged(hwndDlg, pData, HIWORD(wParam), FALSE);
868                     break;
869             }
870             break;
871         }
872         case WM_NOTIFY:
873         {
874             LPNMHDR lpnm = (LPNMHDR)lParam;
875             if (lpnm->code == (UINT)PSN_APPLY)
876             {
877                 if (pData->CurrentDisplayDevice->CurrentSettings->dmPelsWidth != pData->CurrentDisplayDevice->InitialSettings.dmPelsWidth
878                  || pData->CurrentDisplayDevice->CurrentSettings->dmPelsHeight != pData->CurrentDisplayDevice->InitialSettings.dmPelsHeight
879                  || pData->CurrentDisplayDevice->CurrentSettings->dmBitsPerPel != pData->CurrentDisplayDevice->InitialSettings.dmBitsPerPel)
880                 {
881                     /* Apply new settings */
882                     ApplyDisplaySettings(hwndDlg, pData);
883                 }
884             }
885             else if (lpnm->code == MSLN_MONITORCHANGED)
886             {
887                 PMONSL_MONNMMONITORCHANGING lpnmi = (PMONSL_MONNMMONITORCHANGING)lParam;
888                 PDISPLAY_DEVICE_ENTRY Current = pData->DisplayDeviceList;
889                 ULONG i;
890                 for (i = 0; i < lpnmi->hdr.Index; i++)
891                     Current = Current->Flink;
892                 OnDisplayDeviceChanged(hwndDlg, pData, Current);
893             }
894             break;
895         }
896 
897         case WM_CONTEXTMENU:
898         {
899             HWND hwndMonSel;
900             HMENU hPopup;
901             UINT uiCmd;
902             POINT pt, ptClient;
903             INT Index;
904 
905             pt.x = (SHORT)LOWORD(lParam);
906             pt.y = (SHORT)HIWORD(lParam);
907 
908             hwndMonSel = GetDlgItem(hwndDlg,
909                                     IDC_SETTINGS_MONSEL);
910             if ((HWND)wParam == hwndMonSel)
911             {
912                 if (pt.x == -1 && pt.y == -1)
913                 {
914                     RECT rcMon;
915 
916                     Index = (INT)SendMessage(hwndMonSel,
917                                              MSLM_GETCURSEL,
918                                              0,
919                                              0);
920 
921                     if (Index >= 0 &&
922                         (INT)SendMessage(hwndMonSel,
923                                          MSLM_GETMONITORRECT,
924                                          Index,
925                                          (LPARAM)&rcMon) > 0)
926                     {
927                         pt.x = rcMon.left + ((rcMon.right - rcMon.left) / 2);
928                         pt.y = rcMon.top + ((rcMon.bottom - rcMon.top) / 2);
929                     }
930                     else
931                         pt.x = pt.y = 0;
932 
933                     MapWindowPoints(hwndMonSel,
934                                     NULL,
935                                     &pt,
936                                     1);
937                 }
938                 else
939                 {
940                     ptClient = pt;
941                     MapWindowPoints(NULL,
942                                     hwndMonSel,
943                                     &ptClient,
944                                     1);
945 
946                     Index = (INT)SendMessage(hwndMonSel,
947                                              MSLM_HITTEST,
948                                              (WPARAM)&ptClient,
949                                              0);
950                 }
951 
952                 if (Index >= 0)
953                 {
954                     hPopup = LoadPopupMenu(hApplet,
955                                            MAKEINTRESOURCE(IDM_MONITOR_MENU));
956                     if (hPopup != NULL)
957                     {
958                         /* FIXME: Enable/Disable menu items */
959                         EnableMenuItem(hPopup,
960                                        ID_MENU_ATTACHED,
961                                        MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
962                         EnableMenuItem(hPopup,
963                                        ID_MENU_PRIMARY,
964                                        MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
965                         EnableMenuItem(hPopup,
966                                        ID_MENU_IDENTIFY,
967                                        MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
968                         EnableMenuItem(hPopup,
969                                        ID_MENU_PROPERTIES,
970                                        MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
971 
972                         uiCmd = (UINT)TrackPopupMenu(hPopup,
973                                                      TPM_RETURNCMD | TPM_RIGHTBUTTON,
974                                                      pt.x,
975                                                      pt.y,
976                                                      0,
977                                                      hwndDlg,
978                                                      NULL);
979 
980                         switch (uiCmd)
981                         {
982                             case ID_MENU_ATTACHED:
983                             case ID_MENU_PRIMARY:
984                             case ID_MENU_IDENTIFY:
985                             case ID_MENU_PROPERTIES:
986                                 /* FIXME: Implement */
987                                 break;
988                         }
989 
990                         DestroyMenu(hPopup);
991                     }
992                 }
993             }
994             break;
995         }
996 
997         case WM_DESTROY:
998         {
999             DWORD i;
1000             PDISPLAY_DEVICE_ENTRY Current = pData->DisplayDeviceList;
1001 
1002             while (Current != NULL)
1003             {
1004                 PDISPLAY_DEVICE_ENTRY Next = Current->Flink;
1005                 PSETTINGS_ENTRY CurrentSettings = Current->Settings;
1006                 while (CurrentSettings != NULL)
1007                 {
1008                     PSETTINGS_ENTRY NextSettings = CurrentSettings->Flink;
1009                     HeapFree(GetProcessHeap(), 0, CurrentSettings);
1010                     CurrentSettings = NextSettings;
1011                 }
1012                 HeapFree(GetProcessHeap(), 0, Current);
1013                 Current = Next;
1014             }
1015 
1016             for (i = 0; i < NUM_SPECTRUM_BITMAPS; i++)
1017             {
1018                 if (pData->hSpectrumBitmaps[i])
1019                     DeleteObject(pData->hSpectrumBitmaps[i]);
1020             }
1021 
1022             HeapFree(GetProcessHeap(), 0, pData);
1023         }
1024     }
1025     return FALSE;
1026 }
1027