msvcrt: Added _set_fmode and _get_fmode implementation.
[wine] / dlls / winex11.drv / settings.c
1 /*
2  * Wine X11drv display settings functions
3  *
4  * Copyright 2003 Alexander James Pasadyn
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include <string.h>
23 #include <stdio.h>
24 #include <assert.h>
25
26 #define NONAMELESSUNION
27 #define NONAMELESSSTRUCT
28
29 #include "x11drv.h"
30
31 #include "windef.h"
32 #include "winreg.h"
33 #include "wingdi.h"
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(x11settings);
37
38 static struct x11drv_mode_info *dd_modes = NULL;
39 static unsigned int dd_mode_count = 0;
40 static unsigned int dd_max_modes = 0;
41 /* All Windows drivers seen so far either support 32 bit depths, or 24 bit depths, but never both. So if we have
42  * a 32 bit framebuffer, report 32 bit bpps, otherwise 24 bit ones.
43  */
44 static const unsigned int depths_24[]  = {8, 16, 24};
45 static const unsigned int depths_32[]  = {8, 16, 32};
46
47 /* pointers to functions that actually do the hard stuff */
48 static int (*pGetCurrentMode)(void);
49 static LONG (*pSetCurrentMode)(int mode);
50 static const char *handler_name;
51
52 /*
53  * Set the handlers for resolution changing functions
54  * and initialize the master list of modes
55  */
56 struct x11drv_mode_info *X11DRV_Settings_SetHandlers(const char *name,
57                                                      int (*pNewGCM)(void),
58                                                      LONG (*pNewSCM)(int),
59                                                      unsigned int nmodes,
60                                                      int reserve_depths)
61 {
62     handler_name = name;
63     pGetCurrentMode = pNewGCM;
64     pSetCurrentMode = pNewSCM;
65     TRACE("Resolution settings now handled by: %s\n", name);
66     if (reserve_depths)
67         /* leave room for other depths */
68         dd_max_modes = (3+1)*(nmodes);
69     else 
70         dd_max_modes = nmodes;
71
72     if (dd_modes) 
73     {
74         TRACE("Destroying old display modes array\n");
75         HeapFree(GetProcessHeap(), 0, dd_modes);
76     }
77     dd_modes = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*dd_modes) * dd_max_modes);
78     dd_mode_count = 0;
79     TRACE("Initialized new display modes array\n");
80     return dd_modes;
81 }
82
83 /* Add one mode to the master list */
84 void X11DRV_Settings_AddOneMode(unsigned int width, unsigned int height, unsigned int bpp, unsigned int freq)
85 {
86     struct x11drv_mode_info *info = &dd_modes[dd_mode_count];
87     DWORD dwBpp = screen_bpp;
88     if (dd_mode_count >= dd_max_modes)
89     {
90         ERR("Maximum modes (%d) exceeded\n", dd_max_modes);
91         return;
92     }
93     if (bpp == 0) bpp = dwBpp;
94     info->width         = width;
95     info->height        = height;
96     info->refresh_rate  = freq;
97     info->bpp           = bpp;
98     TRACE("initialized mode %d: %dx%dx%d @%d Hz (%s)\n", 
99           dd_mode_count, width, height, bpp, freq, handler_name);
100     dd_mode_count++;
101 }
102
103 /* copy all the current modes using the other color depths */
104 void X11DRV_Settings_AddDepthModes(void)
105 {
106     int i, j;
107     int existing_modes = dd_mode_count;
108     DWORD dwBpp = screen_bpp;
109     const DWORD *depths = screen_bpp == 32 ? depths_32 : depths_24;
110
111     for (j=0; j<3; j++)
112     {
113         if (depths[j] != dwBpp)
114         {
115             for (i=0; i < existing_modes; i++)
116             {
117                 X11DRV_Settings_AddOneMode(dd_modes[i].width, dd_modes[i].height,
118                                            depths[j], dd_modes[i].refresh_rate);
119             }
120         }
121     }
122 }
123
124 /* return the number of modes that are initialized */
125 unsigned int X11DRV_Settings_GetModeCount(void)
126 {
127     return dd_mode_count;
128 }
129
130 /***********************************************************************
131  * Default handlers if resolution switching is not enabled
132  *
133  */
134 static int X11DRV_nores_GetCurrentMode(void)
135 {
136     return 0;
137 }
138
139 static LONG X11DRV_nores_SetCurrentMode(int mode)
140 {
141     if (mode == 0) return DISP_CHANGE_SUCCESSFUL;
142     TRACE("Ignoring mode change request mode=%d\n", mode);
143     return DISP_CHANGE_FAILED;
144 }
145
146 /* default handler only gets the current X desktop resolution */
147 void X11DRV_Settings_Init(void)
148 {
149     X11DRV_Settings_SetHandlers("NoRes", 
150                                 X11DRV_nores_GetCurrentMode, 
151                                 X11DRV_nores_SetCurrentMode, 
152                                 1, 0);
153     X11DRV_Settings_AddOneMode(screen_width, screen_height, 0, 60);
154 }
155
156 static BOOL get_display_device_reg_key(char *key, unsigned len)
157 {
158     static const char display_device_guid_prop[] = "__wine_display_device_guid";
159     static const char video_path[] = "System\\CurrentControlSet\\Control\\Video\\{";
160     static const char display0[] = "}\\0000";
161     ATOM guid_atom;
162
163     assert(len >= sizeof(video_path) + sizeof(display0) + 40);
164
165     guid_atom = HandleToULong(GetPropA(GetDesktopWindow(), display_device_guid_prop));
166     if (!guid_atom) return FALSE;
167
168     memcpy(key, video_path, sizeof(video_path));
169
170     if (!GlobalGetAtomNameA(guid_atom, key + strlen(key), 40))
171         return FALSE;
172
173     strcat(key, display0);
174
175     TRACE("display device key %s\n", wine_dbgstr_a(key));
176     return TRUE;
177 }
178
179 static BOOL read_registry_settings(DEVMODEW *dm)
180 {
181     char wine_x11_reg_key[128];
182     HKEY hkey;
183     DWORD type, size;
184     BOOL ret = TRUE;
185
186     dm->dmFields = 0;
187
188     if (!get_display_device_reg_key(wine_x11_reg_key, sizeof(wine_x11_reg_key)))
189         return FALSE;
190
191     if (RegOpenKeyExA(HKEY_CURRENT_CONFIG, wine_x11_reg_key, 0, KEY_READ, &hkey))
192         return FALSE;
193
194 #define query_value(name, data) \
195     size = sizeof(DWORD); \
196     if (RegQueryValueExA(hkey, name, 0, &type, (LPBYTE)(data), &size) || \
197         type != REG_DWORD || size != sizeof(DWORD)) \
198         ret = FALSE
199
200     query_value("DefaultSettings.BitsPerPel", &dm->dmBitsPerPel);
201     dm->dmFields |= DM_BITSPERPEL;
202     query_value("DefaultSettings.XResolution", &dm->dmPelsWidth);
203     dm->dmFields |= DM_PELSWIDTH;
204     query_value("DefaultSettings.YResolution", &dm->dmPelsHeight);
205     dm->dmFields |= DM_PELSHEIGHT;
206     query_value("DefaultSettings.VRefresh", &dm->dmDisplayFrequency);
207     dm->dmFields |= DM_DISPLAYFREQUENCY;
208     query_value("DefaultSettings.Flags", &dm->u2.dmDisplayFlags);
209     dm->dmFields |= DM_DISPLAYFLAGS;
210     query_value("DefaultSettings.XPanning", &dm->u1.s2.dmPosition.x);
211     query_value("DefaultSettings.YPanning", &dm->u1.s2.dmPosition.y);
212     query_value("DefaultSettings.Orientation", &dm->u1.s2.dmDisplayOrientation);
213     query_value("DefaultSettings.FixedOutput", &dm->u1.s2.dmDisplayFixedOutput);
214
215 #undef query_value
216
217     RegCloseKey(hkey);
218     return ret;
219 }
220
221 static BOOL write_registry_settings(const DEVMODEW *dm)
222 {
223     char wine_x11_reg_key[128];
224     HKEY hkey;
225     BOOL ret = TRUE;
226
227     if (!get_display_device_reg_key(wine_x11_reg_key, sizeof(wine_x11_reg_key)))
228         return FALSE;
229
230     if (RegCreateKeyExA(HKEY_CURRENT_CONFIG, wine_x11_reg_key, 0, NULL,
231                         REG_OPTION_VOLATILE, KEY_WRITE, NULL, &hkey, NULL))
232         return FALSE;
233
234 #define set_value(name, data) \
235     if (RegSetValueExA(hkey, name, 0, REG_DWORD, (const BYTE*)(data), sizeof(DWORD))) \
236         ret = FALSE
237
238     set_value("DefaultSettings.BitsPerPel", &dm->dmBitsPerPel);
239     set_value("DefaultSettings.XResolution", &dm->dmPelsWidth);
240     set_value("DefaultSettings.YResolution", &dm->dmPelsHeight);
241     set_value("DefaultSettings.VRefresh", &dm->dmDisplayFrequency);
242     set_value("DefaultSettings.Flags", &dm->u2.dmDisplayFlags);
243     set_value("DefaultSettings.XPanning", &dm->u1.s2.dmPosition.x);
244     set_value("DefaultSettings.YPanning", &dm->u1.s2.dmPosition.y);
245     set_value("DefaultSettings.Orientation", &dm->u1.s2.dmDisplayOrientation);
246     set_value("DefaultSettings.FixedOutput", &dm->u1.s2.dmDisplayFixedOutput);
247
248 #undef set_value
249
250     RegCloseKey(hkey);
251     return ret;
252 }
253
254 /***********************************************************************
255  *              EnumDisplaySettingsEx  (X11DRV.@)
256  *
257  */
258 BOOL CDECL X11DRV_EnumDisplaySettingsEx( LPCWSTR name, DWORD n, LPDEVMODEW devmode, DWORD flags)
259 {
260     static const WCHAR dev_name[CCHDEVICENAME] =
261         { 'W','i','n','e',' ','X','1','1',' ','d','r','i','v','e','r',0 };
262
263     devmode->dmSize = FIELD_OFFSET(DEVMODEW, dmICMMethod);
264     devmode->dmSpecVersion = DM_SPECVERSION;
265     devmode->dmDriverVersion = DM_SPECVERSION;
266     memcpy(devmode->dmDeviceName, dev_name, sizeof(dev_name));
267     devmode->dmDriverExtra = 0;
268     devmode->u2.dmDisplayFlags = 0;
269     devmode->dmDisplayFrequency = 0;
270     devmode->u1.s2.dmPosition.x = 0;
271     devmode->u1.s2.dmPosition.y = 0;
272     devmode->u1.s2.dmDisplayOrientation = 0;
273     devmode->u1.s2.dmDisplayFixedOutput = 0;
274
275     if (n == ENUM_CURRENT_SETTINGS)
276     {
277         TRACE("mode %d (current) -- getting current mode (%s)\n", n, handler_name);
278         n = pGetCurrentMode();
279     }
280     if (n == ENUM_REGISTRY_SETTINGS)
281     {
282         TRACE("mode %d (registry) -- getting default mode (%s)\n", n, handler_name);
283         return read_registry_settings(devmode);
284     }
285     if (n < dd_mode_count)
286     {
287         devmode->dmPelsWidth = dd_modes[n].width;
288         devmode->dmPelsHeight = dd_modes[n].height;
289         devmode->dmBitsPerPel = dd_modes[n].bpp;
290         devmode->dmDisplayFrequency = dd_modes[n].refresh_rate;
291         devmode->dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL |
292                             DM_DISPLAYFLAGS;
293         if (devmode->dmDisplayFrequency)
294         {
295             devmode->dmFields |= DM_DISPLAYFREQUENCY;
296             TRACE("mode %d -- %dx%dx%dbpp @%d Hz (%s)\n", n,
297                   devmode->dmPelsWidth, devmode->dmPelsHeight, devmode->dmBitsPerPel,
298                   devmode->dmDisplayFrequency, handler_name);
299         }
300         else
301         {
302             TRACE("mode %d -- %dx%dx%dbpp (%s)\n", n,
303                   devmode->dmPelsWidth, devmode->dmPelsHeight, devmode->dmBitsPerPel, 
304                   handler_name);
305         }
306         return TRUE;
307     }
308     TRACE("mode %d -- not present (%s)\n", n, handler_name);
309     SetLastError(ERROR_NO_MORE_FILES);
310     return FALSE;
311 }
312
313 #define _X_FIELD(prefix, bits) if ((fields) & prefix##_##bits) {p+=sprintf(p, "%s%s", first ? "" : ",", #bits); first=FALSE;}
314 static const char * _CDS_flags(DWORD fields)
315 {
316     BOOL first = TRUE;
317     char buf[128];
318     char *p = buf;
319     _X_FIELD(CDS,UPDATEREGISTRY);_X_FIELD(CDS,TEST);_X_FIELD(CDS,FULLSCREEN);
320     _X_FIELD(CDS,GLOBAL);_X_FIELD(CDS,SET_PRIMARY);_X_FIELD(CDS,RESET);
321     _X_FIELD(CDS,SETRECT);_X_FIELD(CDS,NORESET);
322     *p = 0;
323     return wine_dbg_sprintf("%s", buf);
324 }
325 static const char * _DM_fields(DWORD fields)
326 {
327     BOOL first = TRUE;
328     char buf[128];
329     char *p = buf;
330     _X_FIELD(DM,BITSPERPEL);_X_FIELD(DM,PELSWIDTH);_X_FIELD(DM,PELSHEIGHT);
331     _X_FIELD(DM,DISPLAYFLAGS);_X_FIELD(DM,DISPLAYFREQUENCY);_X_FIELD(DM,POSITION);
332     *p = 0;
333     return wine_dbg_sprintf("%s", buf);
334 }
335 #undef _X_FIELD
336
337 /***********************************************************************
338  *              ChangeDisplaySettingsEx  (X11DRV.@)
339  *
340  */
341 LONG CDECL X11DRV_ChangeDisplaySettingsEx( LPCWSTR devname, LPDEVMODEW devmode,
342                                            HWND hwnd, DWORD flags, LPVOID lpvoid )
343 {
344     DWORD i, dwBpp = 0;
345     DEVMODEW dm;
346     BOOL def_mode = TRUE;
347
348     TRACE("(%s,%p,%p,0x%08x,%p)\n",debugstr_w(devname),devmode,hwnd,flags,lpvoid);
349     TRACE("flags=%s\n",_CDS_flags(flags));
350     if (devmode)
351     {
352         /* this is the minimal dmSize that XP accepts */
353         if (devmode->dmSize < FIELD_OFFSET(DEVMODEW, dmFields))
354             return DISP_CHANGE_FAILED;
355
356         TRACE("DM_fields=%s\n",_DM_fields(devmode->dmFields));
357         TRACE("width=%d height=%d bpp=%d freq=%d (%s)\n",
358               devmode->dmPelsWidth,devmode->dmPelsHeight,
359               devmode->dmBitsPerPel,devmode->dmDisplayFrequency, handler_name);
360
361         dwBpp = devmode->dmBitsPerPel;
362         if (devmode->dmFields & DM_BITSPERPEL) def_mode &= !dwBpp;
363         if (devmode->dmFields & DM_PELSWIDTH)  def_mode &= !devmode->dmPelsWidth;
364         if (devmode->dmFields & DM_PELSHEIGHT) def_mode &= !devmode->dmPelsHeight;
365         if (devmode->dmFields & DM_DISPLAYFREQUENCY) def_mode &= !devmode->dmDisplayFrequency;
366     }
367
368     if (def_mode || !dwBpp)
369     {
370         if (!X11DRV_EnumDisplaySettingsEx(devname, ENUM_REGISTRY_SETTINGS, &dm, 0))
371         {
372             ERR("Default mode not found!\n");
373             return DISP_CHANGE_BADMODE;
374         }
375         if (def_mode)
376         {
377             TRACE("Return to original display mode (%s)\n", handler_name);
378             devmode = &dm;
379         }
380         dwBpp = dm.dmBitsPerPel;
381     }
382
383     if ((devmode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
384     {
385         WARN("devmode doesn't specify the resolution: %04x\n", devmode->dmFields);
386         return DISP_CHANGE_BADMODE;
387     }
388
389     for (i = 0; i < dd_mode_count; i++)
390     {
391         if (devmode->dmFields & DM_BITSPERPEL)
392         {
393             if (dwBpp != dd_modes[i].bpp)
394                 continue;
395         }
396         if (devmode->dmFields & DM_PELSWIDTH)
397         {
398             if (devmode->dmPelsWidth != dd_modes[i].width)
399                 continue;
400         }
401         if (devmode->dmFields & DM_PELSHEIGHT)
402         {
403             if (devmode->dmPelsHeight != dd_modes[i].height)
404                 continue;
405         }
406         if ((devmode->dmFields & DM_DISPLAYFREQUENCY) && (dd_modes[i].refresh_rate != 0) &&
407             devmode->dmDisplayFrequency != 0)
408         {
409             if (devmode->dmDisplayFrequency != dd_modes[i].refresh_rate)
410                 continue;
411         }
412         /* we have a valid mode */
413         TRACE("Requested display settings match mode %d (%s)\n", i, handler_name);
414
415         if (flags & CDS_UPDATEREGISTRY)
416             write_registry_settings(devmode);
417
418         if (!(flags & (CDS_TEST | CDS_NORESET)))
419             return pSetCurrentMode(i);
420
421         return DISP_CHANGE_SUCCESSFUL;
422     }
423
424     /* no valid modes found */
425     ERR("No matching mode found %ux%ux%u @%u! (%s)\n",
426         devmode->dmPelsWidth, devmode->dmPelsHeight,
427         devmode->dmBitsPerPel, devmode->dmDisplayFrequency, handler_name);
428     return DISP_CHANGE_BADMODE;
429 }