Fix DGA2 mode setting to use the correct color depth.
[wine] / dlls / ddraw / ddraw / xvidmode.c
1 /*      DirectDraw driver for User-based primary surfaces
2  *      with XF86VidMode mode switching in full-screen mode.
3  *
4  * Copyright 2000 TransGaming Technologies Inc.
5  */
6
7 #include "config.h"
8
9 #ifdef HAVE_LIBXXF86VM
10
11 #include "debugtools.h"
12 #include "ts_xlib.h"
13 #include "ts_xf86vmode.h"
14 #include "x11drv.h"
15 #include "ddraw.h"
16
17 #include <assert.h>
18 #include <stdlib.h>
19
20 #include "ddraw_private.h"
21 #include "ddraw/main.h"
22 #include "ddraw/user.h"
23 #include "ddraw/xvidmode.h"
24 #include "dclipper/main.h"
25 #include "dpalette/main.h"
26 #include "dsurface/main.h"
27 #include "dsurface/dib.h"
28 #include "dsurface/user.h"
29 #include "options.h"
30
31 #include "win.h"
32
33 DEFAULT_DEBUG_CHANNEL(ddraw);
34
35 static ICOM_VTABLE(IDirectDraw7) XVidMode_DirectDraw_VTable;
36
37 static const DDDEVICEIDENTIFIER2 xvidmode_device = 
38 {
39     "display",
40     "XF86VidMode",
41     { { 0x00010001, 0x00010001 } },
42     0, 0, 0, 0,
43     /* 40c1b248-9d7d-4a29-b7d7-4cd8109f3d5d */
44     {0x40c1b248,0x9d7d,0x4a29,{0xd7,0xb7,0x4c,0xd8,0x10,0x9f,0x3d,0x5d}},
45     0
46 };
47
48 HRESULT XVidMode_DirectDraw_Create(const GUID* pGUID, LPDIRECTDRAW7* pIface,
49                                    IUnknown* pUnkOuter, BOOL ex);
50 HRESULT XVidMode_DirectDraw_Initialize(IDirectDrawImpl*, const GUID*);
51
52 static const ddraw_driver xvidmode_driver =
53 {
54     &xvidmode_device,
55     11, /* User is 10 */
56     XVidMode_DirectDraw_Create,
57     XVidMode_DirectDraw_Initialize
58 };
59
60 static XF86VidModeModeInfo** modes;
61 static DWORD num_modes;
62
63 /* Called from DllInit, which is synchronised so there are no threading
64  * concerns. */
65 static BOOL initialize(void)
66 {
67     int nmodes;
68     int major, minor;
69
70     if (X11DRV_GetXRootWindow() != DefaultRootWindow(display)) return FALSE;
71
72     if (!TSXF86VidModeQueryVersion(display, &major, &minor)) return FALSE;
73
74     if (!TSXF86VidModeGetAllModeLines(display, DefaultScreen(display), &nmodes,
75                                       &modes))
76         return FALSE;
77
78     num_modes = nmodes;
79
80     TRACE("enabling XVidMode\n");
81
82     return TRUE;
83 }
84
85 static void cleanup(void)
86 {
87     TSXFree(modes);
88 }
89
90 static HRESULT set_display_mode(XF86VidModeModeInfo* mode)
91 {
92     int screen = DefaultScreen(display);
93
94     TRACE("%d %d\n", mode->hdisplay, mode->vdisplay);
95
96     /* This is questionable. Programs should leave switching unlocked when
97      * they exit. So the only reason the display should be locked is if
98      * another really doesn't want switches to happen. Maybe it would be better
99      * to detect an XF86VideModeZoomLocked error. */
100     TSXF86VidModeLockModeSwitch(display, screen, False);
101
102     TSXSync(display, False);
103
104     TSXF86VidModeSwitchToMode(display, screen, mode);
105
106     TSXSync(display, False);
107
108 #if 0 /* doesn't work for me */
109     TSXF86VidModeSetViewPort(display, screen, 0, 0);
110 #else
111     TSXWarpPointer(display, None, RootWindow(display, screen), 0, 0, 0, 0, 0,
112                    0);
113 #endif
114
115     TSXFlush(display);
116
117     return S_OK;
118 }
119
120 static XF86VidModeModeInfo* choose_mode(DWORD dwWidth, DWORD dwHeight,
121                                         DWORD dwRefreshRate, DWORD dwFlags)
122 {
123     XF86VidModeModeInfo* best = NULL;
124     int i;
125
126     /* Choose the smallest mode that is large enough. */
127     for (i=0; i < num_modes; i++)
128     {
129         if (modes[i]->hdisplay >= dwWidth && modes[i]->vdisplay >= dwHeight)
130         {
131             if (best == NULL) best = modes[i];
132             else
133             {
134                 if (modes[i]->hdisplay < best->hdisplay
135                     || modes[i]->vdisplay < best->vdisplay)
136                     best = modes[i];
137             }
138         }
139     }
140
141     /* all modes were too small, use the largest */
142     if (best == NULL)
143     {
144         TRACE("all modes too small\n");
145
146         for (i=1; i < num_modes; i++)
147         {
148             if (best == NULL) best = modes[i];
149             else
150             {
151                 if (modes[i]->hdisplay > best->hdisplay
152                     || modes[i]->vdisplay > best->vdisplay)
153                     best = modes[i];
154             }
155         }
156     }
157
158     TRACE("using %d %d for %lu %lu\n", best->hdisplay, best->vdisplay,
159           dwWidth, dwHeight);
160
161     return best;
162 }
163
164 static XF86VidModeModeInfo* get_current_mode(void)
165 {
166     XF86VidModeModeLine line;
167     int dotclock;
168     int i;
169
170     TSXF86VidModeGetModeLine(display, DefaultScreen(display), &dotclock,
171                              &line);
172
173     for (i=0; i < num_modes; i++)
174     {
175         if (modes[i]->dotclock == dotclock
176             && modes[i]->hdisplay == line.hdisplay
177             && modes[i]->hsyncstart == line.hsyncstart
178             && modes[i]->hsyncend == line.hsyncend
179             && modes[i]->htotal == line.htotal
180             /* && modes[i]->hskew == line.hskew */
181             && modes[i]->vdisplay == line.vdisplay
182             && modes[i]->vsyncstart == line.vsyncstart
183             && modes[i]->vsyncend == line.vsyncend
184             && modes[i]->vtotal == line.vtotal
185             && modes[i]->flags == line.flags)
186             return modes[i];
187     }
188
189     WARN("this can't happen\n");
190     return modes[0]; /* should be the mode that X started in */
191 }
192
193 BOOL DDRAW_XVidMode_Init(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv)
194 {
195     if (fdwReason == DLL_PROCESS_ATTACH)
196     {
197         if (initialize())
198             DDRAW_register_driver(&xvidmode_driver);
199     }
200     else if (fdwReason == DLL_PROCESS_DETACH)
201     {
202         cleanup();
203     }
204
205     return TRUE;
206 }
207
208 /* Not called from the vtable. */
209 HRESULT XVidMode_DirectDraw_Construct(IDirectDrawImpl *This, BOOL ex)
210 {
211     XVIDMODE_DDRAW_PRIV_VAR(priv,This);
212     HRESULT hr;
213
214     TRACE("\n");
215
216     hr = User_DirectDraw_Construct(This, ex);
217     if (FAILED(hr)) return hr;
218
219     This->final_release = XVidMode_DirectDraw_final_release;
220
221     priv->xvidmode.original_mode = get_current_mode();
222     priv->xvidmode.current_mode = priv->xvidmode.original_mode;
223
224     ICOM_INIT_INTERFACE(This, IDirectDraw7, XVidMode_DirectDraw_VTable);
225
226     return S_OK;
227 }
228
229 /* This function is called from DirectDrawCreate(Ex) on the most-derived
230  * class to start construction.
231  * Not called from the vtable. */
232 HRESULT XVidMode_DirectDraw_Create(const GUID* pGUID, LPDIRECTDRAW7* pIface,
233                                    IUnknown* pUnkOuter, BOOL ex)
234 {
235     HRESULT hr;
236     IDirectDrawImpl* This;
237
238     TRACE("\n");
239
240     assert(pUnkOuter == NULL);
241
242     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
243                      sizeof(IDirectDrawImpl)
244                      + sizeof(XVidMode_DirectDrawImpl));
245     if (This == NULL) return E_OUTOFMEMORY;
246
247     /* Note that this relation does *not* hold true if the DD object was
248      * CoCreateInstanced then Initialized. */
249     This->private = (XVidMode_DirectDrawImpl *)(This+1);
250
251     hr = XVidMode_DirectDraw_Construct(This, ex);
252     if (FAILED(hr))
253         HeapFree(GetProcessHeap(), 0, This);
254     else
255         *pIface = ICOM_INTERFACE(This, IDirectDraw7);
256
257     return hr;
258 }
259
260 /* This function is called from Uninit_DirectDraw_Initialize on the
261  * most-derived-class to start initialization.
262  * Not called from the vtable. */
263 HRESULT XVidMode_DirectDraw_Initialize(IDirectDrawImpl *This, const GUID* guid)
264 {
265     HRESULT hr;
266
267     TRACE("\n");
268
269     This->private = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
270                               sizeof(XVidMode_DirectDrawImpl));
271     if (This->private == NULL) return E_OUTOFMEMORY;
272
273     hr = XVidMode_DirectDraw_Construct(This, TRUE); /* XXX ex? */
274     if (FAILED(hr))
275     {
276         HeapFree(GetProcessHeap(), 0, This->private);
277         return hr;
278     }
279
280     return DD_OK;
281 }
282
283 /* Called from an internal function pointer. */
284 void XVidMode_DirectDraw_final_release(IDirectDrawImpl *This)
285 {
286     XVIDMODE_DDRAW_PRIV_VAR(priv, This);
287
288     if (priv->xvidmode.current_mode != priv->xvidmode.original_mode)
289         set_display_mode(priv->xvidmode.original_mode);
290
291     User_DirectDraw_final_release(This);
292 }
293
294 HRESULT WINAPI
295 XVidMode_DirectDraw_GetDeviceIdentifier(LPDIRECTDRAW7 iface,
296                                         LPDDDEVICEIDENTIFIER2 pDDDI,
297                                         DWORD dwFlags)
298 {
299     *pDDDI = xvidmode_device;
300     return DD_OK;
301 }
302
303 HRESULT WINAPI
304 XVidMode_DirectDraw_RestoreDisplayMode(LPDIRECTDRAW7 iface)
305 {
306     ICOM_THIS(IDirectDrawImpl, iface);
307     HRESULT hr;
308
309     TRACE("\n");
310
311     hr = Main_DirectDraw_RestoreDisplayMode(iface);
312     if (SUCCEEDED(hr))
313     {
314         XVIDMODE_DDRAW_PRIV_VAR(priv, This);
315
316         if (priv->xvidmode.current_mode != priv->xvidmode.original_mode)
317         {
318             set_display_mode(priv->xvidmode.original_mode);
319             priv->xvidmode.current_mode = priv->xvidmode.original_mode;
320         }
321     }
322
323     return hr;
324 }
325
326 HRESULT WINAPI
327 XVidMode_DirectDraw_SetDisplayMode(LPDIRECTDRAW7 iface, DWORD dwWidth,
328                                    DWORD dwHeight, DWORD dwBPP,
329                                    DWORD dwRefreshRate, DWORD dwFlags)
330 {
331     ICOM_THIS(IDirectDrawImpl, iface);
332
333     HRESULT hr;
334
335     TRACE("(%p)->(%ldx%ldx%ld,%ld Hz,%08lx)\n",This,dwWidth,dwHeight,dwBPP,dwRefreshRate,dwFlags);
336     hr = User_DirectDraw_SetDisplayMode(iface, dwWidth, dwHeight, dwBPP,
337                                         dwRefreshRate, dwFlags);
338
339     if (SUCCEEDED(hr))
340     {
341         XVIDMODE_DDRAW_PRIV_VAR(priv, This);
342         XF86VidModeModeInfo* new_mode;
343         WND *tmpWnd = WIN_FindWndPtr(This->window);
344         Window x11Wnd = X11DRV_WND_GetXWindow(tmpWnd);
345         WIN_ReleaseWndPtr(tmpWnd);
346
347         new_mode = choose_mode(dwWidth, dwHeight, dwRefreshRate, dwFlags);
348
349         if (new_mode != NULL && new_mode != priv->xvidmode.current_mode)
350         {
351             priv->xvidmode.current_mode = new_mode;
352             set_display_mode(priv->xvidmode.current_mode);
353         }
354         if (PROFILE_GetWineIniBool( "x11drv", "DXGrab", 0)) {
355            /* Confine cursor movement (risky, but the user asked for it) */
356            TSXGrabPointer(display, x11Wnd, True, 0, GrabModeAsync, GrabModeAsync, x11Wnd, None, CurrentTime);
357         }
358     }
359
360     return hr;
361 }
362
363 static ICOM_VTABLE(IDirectDraw7) XVidMode_DirectDraw_VTable =
364 {
365     Main_DirectDraw_QueryInterface,
366     Main_DirectDraw_AddRef,
367     Main_DirectDraw_Release,
368     Main_DirectDraw_Compact,
369     Main_DirectDraw_CreateClipper,
370     Main_DirectDraw_CreatePalette,
371     Main_DirectDraw_CreateSurface,
372     Main_DirectDraw_DuplicateSurface,
373     User_DirectDraw_EnumDisplayModes,
374     Main_DirectDraw_EnumSurfaces,
375     Main_DirectDraw_FlipToGDISurface,
376     User_DirectDraw_GetCaps,
377     Main_DirectDraw_GetDisplayMode,
378     Main_DirectDraw_GetFourCCCodes,
379     Main_DirectDraw_GetGDISurface,
380     Main_DirectDraw_GetMonitorFrequency,
381     Main_DirectDraw_GetScanLine,
382     Main_DirectDraw_GetVerticalBlankStatus,
383     Main_DirectDraw_Initialize,
384     XVidMode_DirectDraw_RestoreDisplayMode,
385     Main_DirectDraw_SetCooperativeLevel,
386     XVidMode_DirectDraw_SetDisplayMode,
387     Main_DirectDraw_WaitForVerticalBlank,
388     Main_DirectDraw_GetAvailableVidMem,
389     Main_DirectDraw_GetSurfaceFromDC,
390     Main_DirectDraw_RestoreAllSurfaces,
391     Main_DirectDraw_TestCooperativeLevel,
392     XVidMode_DirectDraw_GetDeviceIdentifier,
393     Main_DirectDraw_StartModeTest,
394     Main_DirectDraw_EvaluateMode
395 };
396
397 #endif /* HAVE_LIBXXF86VM */