- Siedler3 appears to have rather limited buffers for device/description
[wine] / dlls / ddraw / ddraw / dga2.c
1 /*      DirectDraw driver for XF86DGA2 primary surface
2  *
3  * Copyright 2000 TransGaming Technologies Inc.
4  */
5
6 #include "config.h"
7
8 #ifdef HAVE_LIBXXF86DGA2
9
10 #include "debugtools.h"
11 #include "ts_xlib.h"
12 #include "ts_xf86dga2.h"
13 #include "x11drv.h"
14 #include "ddraw.h"
15
16 #include <assert.h>
17 #include <stdlib.h>
18
19 #include "ddraw_private.h"
20 #include "ddraw/main.h"
21 #include "ddraw/user.h"
22 #include "ddraw/dga2.h"
23 #include "dclipper/main.h"
24 #include "dpalette/main.h"
25 #include "dsurface/main.h"
26 #include "dsurface/dib.h"
27 #include "dsurface/user.h"
28 #include "dsurface/dga2.h"
29
30 #include "options.h"
31
32 DEFAULT_DEBUG_CHANNEL(ddraw);
33
34 static ICOM_VTABLE(IDirectDraw7) XF86DGA2_DirectDraw_VTable;
35
36 static const DDDEVICEIDENTIFIER2 xf86dga2_device = 
37 {
38     "display",
39     "XF86DGA2 Driver",
40     { { 0x00010001, 0x00010001 } },
41     0, 0, 0, 0,
42     /* e2dcb020-dc60-11d1-8407-9714f5d50803 */
43     {0xe2dcb020,0xdc60,0x11d1,{0x84,0x07,0x97,0x14,0xf5,0xd5,0x08,0x03}},
44     0
45 };
46
47 HRESULT XF86DGA2_DirectDraw_Create(const GUID* pGUID, LPDIRECTDRAW7* pIface,
48                                    IUnknown* pUnkOuter, BOOL ex);
49 HRESULT XF86DGA2_DirectDraw_Initialize(IDirectDrawImpl*, const GUID*);
50
51 static const ddraw_driver xf86dga2_driver =
52 {
53     &xf86dga2_device,
54     20, /* XVidMode is 11 */
55     XF86DGA2_DirectDraw_Create,
56     XF86DGA2_DirectDraw_Initialize
57 };
58
59 static XDGAMode* modes;
60 static DWORD num_modes;
61 static int dga_event, dga_error;
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     /* FIXME: don't use PROFILE calls */
73     if (!PROFILE_GetWineIniBool("x11drv", "UseDGA", 1)) return FALSE;
74
75     if (!TSXDGAQueryExtension(display, &dga_event, &dga_error)) return FALSE;
76
77     if (!TSXDGAQueryVersion(display, &major, &minor)) return FALSE;
78
79     if (major < 2) return FALSE; /* only bother with DGA2 */
80
81     /* test that it works */
82     if (!TSXDGAOpenFramebuffer(display, DefaultScreen(display))) {
83         TRACE("disabling XF86DGA2 (insufficient permissions?)\n");
84         return FALSE;
85     }
86     TSXDGACloseFramebuffer(display, DefaultScreen(display));
87     
88     TRACE("getting XF86DGA2 mode list\n");
89     modes = TSXDGAQueryModes(display, DefaultScreen(display), &nmodes);
90     if (!modes) return FALSE;
91     num_modes = nmodes;
92
93     TRACE("enabling XF86DGA2\n");
94
95     return TRUE;
96 }
97
98 static void cleanup(void)
99 {
100     TSXFree(modes);
101 }
102
103 static XDGAMode* choose_mode(DWORD dwWidth, DWORD dwHeight,
104                        DWORD dwRefreshRate, DWORD dwFlags)
105 {
106     XDGAMode* best = NULL;
107     int i;
108
109     /* Choose the smallest mode that is large enough. */
110     for (i=0; i < num_modes; i++)
111     {
112         if (modes[i].viewportWidth >= dwWidth && modes[i].viewportHeight >= dwHeight)
113         {
114             if (best == NULL) best = &modes[i];
115             else
116             {
117                 if (modes[i].viewportWidth < best->viewportWidth
118                     || modes[i].viewportHeight < best->viewportHeight)
119                     best = &modes[i];
120             }
121         }
122     }
123
124     /* all modes were too small, use the largest */
125     if (!best)
126     {
127         TRACE("all modes too small\n");
128
129         for (i=1; i < num_modes; i++)
130         {
131             if (best == NULL) best = &modes[i];
132             else
133             {
134                 if (modes[i].viewportWidth > best->viewportWidth
135                     || modes[i].viewportHeight > best->viewportHeight)
136                     best = &modes[i];
137             }
138         }
139     }
140
141     TRACE("using %d %d for %lu %lu\n", best->viewportWidth, best->viewportHeight,
142           dwWidth, dwHeight);
143
144     return best;
145 }
146
147 BOOL DDRAW_XF86DGA2_Init(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv)
148 {
149     if (fdwReason == DLL_PROCESS_ATTACH)
150     {
151         if (initialize())
152             DDRAW_register_driver(&xf86dga2_driver);
153     }
154     else if (fdwReason == DLL_PROCESS_DETACH)
155     {
156         cleanup();
157     }
158
159     return TRUE;
160 }
161
162 /* Not called from the vtable. */
163 HRESULT XF86DGA2_DirectDraw_Construct(IDirectDrawImpl *This, BOOL ex)
164 {
165     HRESULT hr;
166
167     TRACE("\n");
168
169     hr = User_DirectDraw_Construct(This, ex);
170     if (FAILED(hr)) return hr;
171
172     This->final_release = XF86DGA2_DirectDraw_final_release;
173
174     This->create_primary    = XF86DGA2_DirectDraw_create_primary;
175     This->create_backbuffer = XF86DGA2_DirectDraw_create_backbuffer;
176
177     ICOM_INIT_INTERFACE(This, IDirectDraw7, XF86DGA2_DirectDraw_VTable);
178
179     return S_OK;
180 }
181
182 /* This function is called from DirectDrawCreate(Ex) on the most-derived
183  * class to start construction.
184  * Not called from the vtable. */
185 HRESULT XF86DGA2_DirectDraw_Create(const GUID* pGUID, LPDIRECTDRAW7* pIface,
186                                    IUnknown* pUnkOuter, BOOL ex)
187 {
188     HRESULT hr;
189     IDirectDrawImpl* This;
190
191     TRACE("\n");
192
193     assert(pUnkOuter == NULL);
194
195     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
196                      sizeof(IDirectDrawImpl)
197                      + sizeof(XF86DGA2_DirectDrawImpl));
198     if (This == NULL) return E_OUTOFMEMORY;
199
200     /* Note that this relation does *not* hold true if the DD object was
201      * CoCreateInstanced then Initialized. */
202     This->private = (XF86DGA2_DirectDrawImpl *)(This+1);
203
204     hr = XF86DGA2_DirectDraw_Construct(This, ex);
205     if (FAILED(hr))
206         HeapFree(GetProcessHeap(), 0, This);
207     else
208         *pIface = ICOM_INTERFACE(This, IDirectDraw7);
209
210     return hr;
211 }
212
213 /* This function is called from Uninit_DirectDraw_Initialize on the
214  * most-derived-class to start initialization.
215  * Not called from the vtable. */
216 HRESULT XF86DGA2_DirectDraw_Initialize(IDirectDrawImpl *This, const GUID* guid)
217 {
218     HRESULT hr;
219
220     TRACE("\n");
221
222     This->private = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
223                               sizeof(XF86DGA2_DirectDrawImpl));
224     if (This->private == NULL) return E_OUTOFMEMORY;
225
226     hr = XF86DGA2_DirectDraw_Construct(This, TRUE); /* XXX ex? */
227     if (FAILED(hr))
228     {
229         HeapFree(GetProcessHeap(), 0, This->private);
230         return hr;
231     }
232
233     return DD_OK;
234 }
235
236 /* Called from an internal function pointer. */
237 void XF86DGA2_DirectDraw_final_release(IDirectDrawImpl *This)
238 {
239     XF86DGA2_DDRAW_PRIV_VAR(priv, This);
240
241     if (priv->xf86dga2.current_mode) {
242         TSXDGASetMode(display, DefaultScreen(display), 0);
243         VirtualFree(priv->xf86dga2.current_mode->data, 0, MEM_RELEASE);
244         X11DRV_EVENT_SetInputMethod(X11DRV_INPUT_ABSOLUTE);
245         X11DRV_EVENT_SetDGAStatus(0, -1);
246         TSXFree(priv->xf86dga2.current_mode);
247         TSXDGACloseFramebuffer(display, DefaultScreen(display));
248         priv->xf86dga2.current_mode = NULL;
249     }
250
251     User_DirectDraw_final_release(This);
252 }
253
254 HRESULT XF86DGA2_DirectDraw_create_primary(IDirectDrawImpl* This,
255                                            const DDSURFACEDESC2* pDDSD,
256                                            LPDIRECTDRAWSURFACE7* ppSurf,
257                                            IUnknown* pUnkOuter)
258 {
259     if (This->cooperative_level & DDSCL_EXCLUSIVE)
260         return XF86DGA2_DirectDrawSurface_Create(This, pDDSD, ppSurf, pUnkOuter);
261     else
262         return User_DirectDrawSurface_Create(This, pDDSD, ppSurf, pUnkOuter);
263 }
264
265 HRESULT XF86DGA2_DirectDraw_create_backbuffer(IDirectDrawImpl* This,
266                                               const DDSURFACEDESC2* pDDSD,
267                                               LPDIRECTDRAWSURFACE7* ppSurf,
268                                               IUnknown* pUnkOuter,
269                                               IDirectDrawSurfaceImpl* primary)
270 {
271     if (This->cooperative_level & DDSCL_EXCLUSIVE)
272         return XF86DGA2_DirectDrawSurface_Create(This, pDDSD, ppSurf, pUnkOuter);
273     else
274         return User_DirectDrawSurface_Create(This, pDDSD, ppSurf, pUnkOuter);
275 }
276
277 HRESULT WINAPI
278 XF86DGA2_DirectDraw_GetDeviceIdentifier(LPDIRECTDRAW7 iface,
279                                         LPDDDEVICEIDENTIFIER2 pDDDI,
280                                         DWORD dwFlags)
281 {
282     *pDDDI = xf86dga2_device;
283     return DD_OK;
284 }
285
286 HRESULT WINAPI
287 XF86DGA2_DirectDraw_RestoreDisplayMode(LPDIRECTDRAW7 iface)
288 {
289     ICOM_THIS(IDirectDrawImpl, iface);
290     HRESULT hr;
291
292     TRACE("\n");
293
294     hr = Main_DirectDraw_RestoreDisplayMode(iface);
295     if (SUCCEEDED(hr))
296     {
297         XF86DGA2_DDRAW_PRIV_VAR(priv, This);
298
299         if (priv->xf86dga2.current_mode)
300         {
301             TSXDGASetMode(display, DefaultScreen(display), 0);
302             VirtualFree(priv->xf86dga2.current_mode->data, 0, MEM_RELEASE);
303             X11DRV_EVENT_SetInputMethod(X11DRV_INPUT_ABSOLUTE);
304             X11DRV_EVENT_SetDGAStatus(0, -1);
305             TSXFree(priv->xf86dga2.current_mode);
306             TSXDGACloseFramebuffer(display, DefaultScreen(display));
307             priv->xf86dga2.current_mode = NULL;
308         }
309     }
310
311     return hr;
312 }
313
314 HRESULT WINAPI
315 XF86DGA2_DirectDraw_SetDisplayMode(LPDIRECTDRAW7 iface, DWORD dwWidth,
316                                    DWORD dwHeight, DWORD dwBPP,
317                                    DWORD dwRefreshRate, DWORD dwFlags)
318 {
319     ICOM_THIS(IDirectDrawImpl, iface);
320
321     HRESULT hr;
322
323     TRACE("(%p)->(%ldx%ldx%ld,%ld Hz,%08lx)\n",This,dwWidth,dwHeight,dwBPP,dwRefreshRate,dwFlags);
324     hr = User_DirectDraw_SetDisplayMode(iface, dwWidth, dwHeight, dwBPP,
325                                         dwRefreshRate, dwFlags);
326
327     if (SUCCEEDED(hr))
328     {
329         XF86DGA2_DDRAW_PRIV_VAR(priv, This);
330         XDGADevice* old_mode = priv->xf86dga2.current_mode;
331         XDGAMode* new_mode;
332         int old_mode_num = old_mode ? old_mode->mode.num : 0;
333
334         new_mode = choose_mode(dwWidth, dwHeight, dwRefreshRate, dwFlags);
335
336         if (new_mode && new_mode->num != old_mode_num)
337         {
338             XDGADevice * nm = NULL;
339             if (old_mode || TSXDGAOpenFramebuffer(display, DefaultScreen(display)))
340                 nm = TSXDGASetMode(display, DefaultScreen(display), new_mode->num);
341             if (nm) {
342                 TSXDGASetViewport(display, DefaultScreen(display), 0, 0, XDGAFlipImmediate);
343                 if (old_mode) {
344                     VirtualFree(old_mode->data, 0, MEM_RELEASE);
345                     TSXFree(old_mode);
346                 } else {
347                     TSXDGASelectInput(display, DefaultScreen(display),
348                                       KeyPressMask|KeyReleaseMask|
349                                       ButtonPressMask|ButtonReleaseMask|
350                                       PointerMotionMask);
351                     X11DRV_EVENT_SetDGAStatus(This->window, dga_event);
352                     X11DRV_EVENT_SetInputMethod(X11DRV_INPUT_RELATIVE);
353                 }
354                 priv->xf86dga2.current_mode = nm;
355                 priv->xf86dga2.next_vofs = 0;
356                 TRACE("frame buffer at %p, pitch=%d, width=%d, height=%d\n", nm->data,
357                       nm->mode.bytesPerScanline, nm->mode.imageWidth, nm->mode.imageHeight);
358                 VirtualAlloc(nm->data, nm->mode.bytesPerScanline * nm->mode.imageHeight,
359                              MEM_RESERVE|MEM_SYSTEM, PAGE_READWRITE);
360             } else {
361                 /* argh */
362                 ERR("failed\n");
363                 /* XXX revert size data to previous mode */
364                 if (!old_mode)
365                     TSXDGACloseFramebuffer(display, DefaultScreen(display));
366             }
367         }
368     }
369
370     return hr;
371 }
372
373 static ICOM_VTABLE(IDirectDraw7) XF86DGA2_DirectDraw_VTable =
374 {
375     Main_DirectDraw_QueryInterface,
376     Main_DirectDraw_AddRef,
377     Main_DirectDraw_Release,
378     Main_DirectDraw_Compact,
379     Main_DirectDraw_CreateClipper,
380     Main_DirectDraw_CreatePalette,
381     Main_DirectDraw_CreateSurface,
382     Main_DirectDraw_DuplicateSurface,
383     User_DirectDraw_EnumDisplayModes,
384     Main_DirectDraw_EnumSurfaces,
385     Main_DirectDraw_FlipToGDISurface,
386     User_DirectDraw_GetCaps,
387     Main_DirectDraw_GetDisplayMode,
388     Main_DirectDraw_GetFourCCCodes,
389     Main_DirectDraw_GetGDISurface,
390     Main_DirectDraw_GetMonitorFrequency,
391     Main_DirectDraw_GetScanLine,
392     Main_DirectDraw_GetVerticalBlankStatus,
393     Main_DirectDraw_Initialize,
394     XF86DGA2_DirectDraw_RestoreDisplayMode,
395     Main_DirectDraw_SetCooperativeLevel,
396     XF86DGA2_DirectDraw_SetDisplayMode,
397     Main_DirectDraw_WaitForVerticalBlank,
398     Main_DirectDraw_GetAvailableVidMem,
399     Main_DirectDraw_GetSurfaceFromDC,
400     Main_DirectDraw_RestoreAllSurfaces,
401     Main_DirectDraw_TestCooperativeLevel,
402     XF86DGA2_DirectDraw_GetDeviceIdentifier,
403     Main_DirectDraw_StartModeTest,
404     Main_DirectDraw_EvaluateMode
405 };
406
407 #endif  /* HAVE_LIBXXF86DGA2 */