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