wined3d: Filter window messages generated by switching to fullscreen and back.
[wine] / dlls / wined3d / swapchain.c
1 /*
2  *IDirect3DSwapChain9 implementation
3  *
4  *Copyright 2002-2003 Jason Edmeades
5  *Copyright 2002-2003 Raphael Junqueira
6  *Copyright 2005 Oliver Stieber
7  *Copyright 2007-2008 Stefan Dösinger for CodeWeavers
8  *
9  *This library is free software; you can redistribute it and/or
10  *modify it under the terms of the GNU Lesser General Public
11  *License as published by the Free Software Foundation; either
12  *version 2.1 of the License, or (at your option) any later version.
13  *
14  *This library is distributed in the hope that it will be useful,
15  *but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *Lesser General Public License for more details.
18  *
19  *You should have received a copy of the GNU Lesser General Public
20  *License along with this library; if not, write to the Free Software
21  *Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25 #include "wined3d_private.h"
26
27
28 /*TODO: some of the additional parameters may be required to
29     set the gamma ramp (for some weird reason microsoft have left swap gammaramp in device
30     but it operates on a swapchain, it may be a good idea to move it to IWineD3DSwapChain for IWineD3D)*/
31
32
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
34 WINE_DECLARE_DEBUG_CHANNEL(fps);
35
36 #define GLINFO_LOCATION This->device->adapter->gl_info
37
38 /*IWineD3DSwapChain parts follow: */
39 static void WINAPI IWineD3DSwapChainImpl_Destroy(IWineD3DSwapChain *iface)
40 {
41     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
42     WINED3DDISPLAYMODE mode;
43     unsigned int i;
44
45     TRACE("Destroying swapchain %p\n", iface);
46
47     IWineD3DSwapChain_SetGammaRamp(iface, 0, &This->orig_gamma);
48
49     /* Release the swapchain's draw buffers. Make sure This->backBuffer[0] is
50      * the last buffer to be destroyed, FindContext() depends on that. */
51     if (This->frontBuffer)
52     {
53         IWineD3DSurface_SetContainer(This->frontBuffer, 0);
54         if (IWineD3DSurface_Release(This->frontBuffer))
55         {
56             WARN("(%p) Something's still holding the front buffer (%p).\n",
57                     This, This->frontBuffer);
58         }
59         This->frontBuffer = NULL;
60     }
61
62     if (This->backBuffer)
63     {
64         UINT i = This->presentParms.BackBufferCount;
65
66         while (i--)
67         {
68             IWineD3DSurface_SetContainer(This->backBuffer[i], 0);
69             if (IWineD3DSurface_Release(This->backBuffer[i]))
70                 WARN("(%p) Something's still holding back buffer %u (%p).\n",
71                         This, i, This->backBuffer[i]);
72         }
73         HeapFree(GetProcessHeap(), 0, This->backBuffer);
74         This->backBuffer = NULL;
75     }
76
77     for (i = 0; i < This->num_contexts; ++i)
78     {
79         context_destroy(This->device, This->context[i]);
80     }
81     /* Restore the screen resolution if we rendered in fullscreen
82      * This will restore the screen resolution to what it was before creating the swapchain. In case of d3d8 and d3d9
83      * this will be the original desktop resolution. In case of d3d7 this will be a NOP because ddraw sets the resolution
84      * before starting up Direct3D, thus orig_width and orig_height will be equal to the modes in the presentation params
85      */
86     if(This->presentParms.Windowed == FALSE && This->presentParms.AutoRestoreDisplayMode) {
87         mode.Width = This->orig_width;
88         mode.Height = This->orig_height;
89         mode.RefreshRate = 0;
90         mode.Format = This->orig_fmt;
91         IWineD3DDevice_SetDisplayMode((IWineD3DDevice *)This->device, 0, &mode);
92     }
93
94     wined3d_unregister_window(This->win_handle);
95
96     HeapFree(GetProcessHeap(), 0, This->context);
97     HeapFree(GetProcessHeap(), 0, This);
98 }
99
100 /* A GL context is provided by the caller */
101 static inline void swapchain_blit(IWineD3DSwapChainImpl *This, struct wined3d_context *context)
102 {
103     RECT window;
104     IWineD3DDeviceImpl *device = This->device;
105     IWineD3DSurfaceImpl *backbuffer = ((IWineD3DSurfaceImpl *) This->backBuffer[0]);
106     UINT w = backbuffer->currentDesc.Width;
107     UINT h = backbuffer->currentDesc.Height;
108     GLenum gl_filter;
109     const struct wined3d_gl_info *gl_info = context->gl_info;
110
111     GetClientRect(This->win_handle, &window);
112     if(w == window.right && h == window.bottom) gl_filter = GL_NEAREST;
113     else gl_filter = GL_LINEAR;
114
115     if(gl_info->supported[EXT_FRAMEBUFFER_BLIT])
116     {
117         ENTER_GL();
118         context_bind_fbo(context, GL_READ_FRAMEBUFFER, &context->src_fbo);
119         context_attach_surface_fbo(context, GL_READ_FRAMEBUFFER, 0, This->backBuffer[0]);
120         context_attach_depth_stencil_fbo(context, GL_READ_FRAMEBUFFER, NULL, FALSE);
121
122         context_bind_fbo(context, GL_DRAW_FRAMEBUFFER, NULL);
123         context_set_draw_buffer(context, GL_BACK);
124
125         glDisable(GL_SCISSOR_TEST);
126         IWineD3DDeviceImpl_MarkStateDirty(This->device, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
127
128         /* Note that the texture is upside down */
129         gl_info->fbo_ops.glBlitFramebuffer(0, 0, w, h,
130                                            window.left, window.bottom, window.right, window.top,
131                                            GL_COLOR_BUFFER_BIT, gl_filter);
132         checkGLcall("Swapchain present blit(EXT_framebuffer_blit)\n");
133         LEAVE_GL();
134     }
135     else
136     {
137         struct wined3d_context *context2;
138         float tex_left = 0;
139         float tex_top = 0;
140         float tex_right = w;
141         float tex_bottom = h;
142
143         context2 = context_acquire(This->device, This->backBuffer[0], CTXUSAGE_BLIT);
144
145         if(backbuffer->Flags & SFLAG_NORMCOORD)
146         {
147             tex_left /= w;
148             tex_right /= w;
149             tex_top /= h;
150             tex_bottom /= h;
151         }
152
153         ENTER_GL();
154         context_bind_fbo(context2, GL_DRAW_FRAMEBUFFER, NULL);
155
156         /* Set up the texture. The surface is not in a IWineD3D*Texture container,
157          * so there are no d3d texture settings to dirtify
158          */
159         device->blitter->set_shader((IWineD3DDevice *) device, backbuffer->resource.format_desc,
160                                     backbuffer->texture_target, backbuffer->pow2Width,
161                                     backbuffer->pow2Height);
162         glTexParameteri(backbuffer->texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
163         glTexParameteri(backbuffer->texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
164
165         context_set_draw_buffer(context, GL_BACK);
166
167         /* Set the viewport to the destination rectandle, disable any projection
168          * transformation set up by CTXUSAGE_BLIT, and draw a (-1,-1)-(1,1) quad.
169          *
170          * Back up viewport and matrix to avoid breaking last_was_blit
171          *
172          * Note that CTXUSAGE_BLIT set up viewport and ortho to match the surface
173          * size - we want the GL drawable(=window) size.
174          */
175         glPushAttrib(GL_VIEWPORT_BIT);
176         glViewport(window.left, window.top, window.right, window.bottom);
177         glMatrixMode(GL_PROJECTION);
178         glPushMatrix();
179         glLoadIdentity();
180
181         glBegin(GL_QUADS);
182             /* bottom left */
183             glTexCoord2f(tex_left, tex_bottom);
184             glVertex2i(-1, -1);
185
186             /* top left */
187             glTexCoord2f(tex_left, tex_top);
188             glVertex2i(-1, 1);
189
190             /* top right */
191             glTexCoord2f(tex_right, tex_top);
192             glVertex2i(1, 1);
193
194             /* bottom right */
195             glTexCoord2f(tex_right, tex_bottom);
196             glVertex2i(1, -1);
197         glEnd();
198
199         glPopMatrix();
200         glPopAttrib();
201
202         device->blitter->unset_shader((IWineD3DDevice *) device);
203         checkGLcall("Swapchain present blit(manual)\n");
204         LEAVE_GL();
205
206         context_release(context2);
207     }
208 }
209
210 static HRESULT WINAPI IWineD3DSwapChainImpl_Present(IWineD3DSwapChain *iface, CONST RECT *pSourceRect, CONST RECT *pDestRect, HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion, DWORD dwFlags) {
211     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
212     struct wined3d_context *context;
213     unsigned int sync;
214     int retval;
215
216     context = context_acquire(This->device, This->backBuffer[0], CTXUSAGE_RESOURCELOAD);
217
218     /* Render the cursor onto the back buffer, using our nifty directdraw blitting code :-) */
219     if (This->device->bCursorVisible && This->device->cursorTexture)
220     {
221         IWineD3DSurfaceImpl cursor;
222         RECT destRect =
223         {
224             This->device->xScreenSpace - This->device->xHotSpot,
225             This->device->yScreenSpace - This->device->yHotSpot,
226             This->device->xScreenSpace + This->device->cursorWidth - This->device->xHotSpot,
227             This->device->yScreenSpace + This->device->cursorHeight - This->device->yHotSpot,
228         };
229         TRACE("Rendering the cursor. Creating fake surface at %p\n", &cursor);
230         /* Build a fake surface to call the Blitting code. It is not possible to use the interface passed by
231          * the application because we are only supposed to copy the information out. Using a fake surface
232          * allows to use the Blitting engine and avoid copying the whole texture -> render target blitting code.
233          */
234         memset(&cursor, 0, sizeof(cursor));
235         cursor.lpVtbl = &IWineD3DSurface_Vtbl;
236         cursor.resource.ref = 1;
237         cursor.resource.device = This->device;
238         cursor.resource.pool = WINED3DPOOL_SCRATCH;
239         cursor.resource.format_desc = getFormatDescEntry(WINED3DFMT_B8G8R8A8_UNORM, context->gl_info);
240         cursor.resource.resourceType = WINED3DRTYPE_SURFACE;
241         cursor.texture_name = This->device->cursorTexture;
242         cursor.texture_target = GL_TEXTURE_2D;
243         cursor.texture_level = 0;
244         cursor.currentDesc.Width = This->device->cursorWidth;
245         cursor.currentDesc.Height = This->device->cursorHeight;
246         cursor.glRect.left = 0;
247         cursor.glRect.top = 0;
248         cursor.glRect.right = cursor.currentDesc.Width;
249         cursor.glRect.bottom = cursor.currentDesc.Height;
250         /* The cursor must have pow2 sizes */
251         cursor.pow2Width = cursor.currentDesc.Width;
252         cursor.pow2Height = cursor.currentDesc.Height;
253         /* The surface is in the texture */
254         cursor.Flags |= SFLAG_INTEXTURE;
255         /* DDBLT_KEYSRC will cause BltOverride to enable the alpha test with GL_NOTEQUAL, 0.0,
256          * which is exactly what we want :-)
257          */
258         if (This->presentParms.Windowed) {
259             MapWindowPoints(NULL, This->win_handle, (LPPOINT)&destRect, 2);
260         }
261         IWineD3DSurface_Blt(This->backBuffer[0], &destRect, (IWineD3DSurface *)&cursor,
262                 NULL, WINEDDBLT_KEYSRC, NULL, WINED3DTEXF_POINT);
263     }
264
265     if (This->device->logo_surface)
266     {
267         /* Blit the logo into the upper left corner of the drawable. */
268         IWineD3DSurface_BltFast(This->backBuffer[0], 0, 0, This->device->logo_surface, NULL, WINEDDBLTFAST_SRCCOLORKEY);
269     }
270
271     if (pSourceRect || pDestRect) FIXME("Unhandled present rects %s/%s\n", wine_dbgstr_rect(pSourceRect), wine_dbgstr_rect(pDestRect));
272     /* TODO: If only source rect or dest rect are supplied then clip the window to match */
273     TRACE("presetting HDC %p\n", This->context[0]->hdc);
274
275     /* Don't call checkGLcall, as glGetError is not applicable here */
276     if (hDestWindowOverride && This->win_handle != hDestWindowOverride) {
277         IWineD3DSwapChain_SetDestWindowOverride(iface, hDestWindowOverride);
278     }
279
280     /* Rendering to a window of different size, presenting partial rectangles,
281      * or rendering to a different window needs help from FBO_blit or a textured
282      * draw. Render the swapchain to a FBO in the future.
283      *
284      * Note that FBO_blit from the backbuffer to the frontbuffer cannot solve
285      * all these issues - this fails if the window is smaller than the backbuffer.
286      */
287     if(This->presentParms.Windowed && !This->render_to_fbo &&
288        wined3d_settings.offscreen_rendering_mode == ORM_FBO)
289     {
290         RECT window_size;
291         GetClientRect(This->win_handle, &window_size);
292         if(window_size.bottom != This->presentParms.BackBufferHeight ||
293            window_size.right != This->presentParms.BackBufferWidth)
294         {
295             TRACE("Window size changed from %ux%u to %ux%u, switching to render-to-fbo mode\n",
296                 This->presentParms.BackBufferWidth, This->presentParms.BackBufferHeight,
297                 window_size.right, window_size.bottom);
298
299             IWineD3DSurface_LoadLocation(This->backBuffer[0], SFLAG_INTEXTURE, NULL);
300             IWineD3DSurface_ModifyLocation(This->backBuffer[0], SFLAG_INDRAWABLE, FALSE);
301             This->render_to_fbo = TRUE;
302
303             /* Force the context manager to update the render target configuration next draw */
304             context->current_rt = NULL;
305         }
306     }
307
308     if(This->render_to_fbo)
309     {
310         /* This codepath should only be hit with the COPY swapeffect. Otherwise a backbuffer-
311          * window size mismatch is impossible(fullscreen) and src and dst rectangles are
312          * not allowed(they need the COPY swapeffect)
313          *
314          * The DISCARD swap effect is ok as well since any backbuffer content is allowed after
315          * the swap
316          */
317         if(This->presentParms.SwapEffect == WINED3DSWAPEFFECT_FLIP )
318         {
319             FIXME("Render-to-fbo with WINED3DSWAPEFFECT_FLIP\n");
320         }
321         swapchain_blit(This, context);
322     }
323
324     SwapBuffers(This->context[0]->hdc); /* TODO: cycle through the swapchain buffers */
325
326     TRACE("SwapBuffers called, Starting new frame\n");
327     /* FPS support */
328     if (TRACE_ON(fps))
329     {
330         DWORD time = GetTickCount();
331         This->frames++;
332         /* every 1.5 seconds */
333         if (time - This->prev_time > 1500) {
334             TRACE_(fps)("%p @ approx %.2ffps\n", This, 1000.0*This->frames/(time - This->prev_time));
335             This->prev_time = time;
336             This->frames = 0;
337         }
338     }
339
340 #if defined(FRAME_DEBUGGING)
341 {
342     if (GetFileAttributesA("C:\\D3DTRACE") != INVALID_FILE_ATTRIBUTES) {
343         if (!isOn) {
344             isOn = TRUE;
345             FIXME("Enabling D3D Trace\n");
346             __WINE_SET_DEBUGGING(__WINE_DBCL_TRACE, __wine_dbch_d3d, 1);
347 #if defined(SHOW_FRAME_MAKEUP)
348             FIXME("Singe Frame snapshots Starting\n");
349             isDumpingFrames = TRUE;
350             ENTER_GL();
351             glClear(GL_COLOR_BUFFER_BIT);
352             LEAVE_GL();
353 #endif
354
355 #if defined(SINGLE_FRAME_DEBUGGING)
356         } else {
357 #if defined(SHOW_FRAME_MAKEUP)
358             FIXME("Singe Frame snapshots Finishing\n");
359             isDumpingFrames = FALSE;
360 #endif
361             FIXME("Singe Frame trace complete\n");
362             DeleteFileA("C:\\D3DTRACE");
363             __WINE_SET_DEBUGGING(__WINE_DBCL_TRACE, __wine_dbch_d3d, 0);
364 #endif
365         }
366     } else {
367         if (isOn) {
368             isOn = FALSE;
369 #if defined(SHOW_FRAME_MAKEUP)
370             FIXME("Single Frame snapshots Finishing\n");
371             isDumpingFrames = FALSE;
372 #endif
373             FIXME("Disabling D3D Trace\n");
374             __WINE_SET_DEBUGGING(__WINE_DBCL_TRACE, __wine_dbch_d3d, 0);
375         }
376     }
377 }
378 #endif
379
380     /* This is disabled, but the code left in for debug purposes.
381      *
382      * Since we're allowed to modify the new back buffer on a D3DSWAPEFFECT_DISCARD flip,
383      * we can clear it with some ugly color to make bad drawing visible and ease debugging.
384      * The Debug runtime does the same on Windows. However, a few games do not redraw the
385      * screen properly, like Max Payne 2, which leaves a few pixels undefined.
386      *
387      * Tests show that the content of the back buffer after a discard flip is indeed not
388      * reliable, so no game can depend on the exact content. However, it resembles the
389      * old contents in some way, for example by showing fragments at other locations. In
390      * general, the color theme is still intact. So Max payne, which draws rather dark scenes
391      * gets a dark background image. If we clear it with a bright ugly color, the game's
392      * bug shows up much more than it does on Windows, and the players see single pixels
393      * with wrong colors.
394      * (The Max Payne bug has been confirmed on Windows with the debug runtime)
395      */
396     if (FALSE && This->presentParms.SwapEffect == WINED3DSWAPEFFECT_DISCARD) {
397         TRACE("Clearing the color buffer with cyan color\n");
398
399         IWineD3DDevice_Clear((IWineD3DDevice *)This->device, 0, NULL,
400                 WINED3DCLEAR_TARGET, 0xff00ffff, 1.0f, 0);
401     }
402
403     if(!This->render_to_fbo &&
404        ( ((IWineD3DSurfaceImpl *) This->frontBuffer)->Flags   & SFLAG_INSYSMEM ||
405          ((IWineD3DSurfaceImpl *) This->backBuffer[0])->Flags & SFLAG_INSYSMEM ) ) {
406         /* Both memory copies of the surfaces are ok, flip them around too instead of dirtifying
407          * Doesn't work with render_to_fbo because we're not flipping
408          */
409         IWineD3DSurfaceImpl *front = (IWineD3DSurfaceImpl *) This->frontBuffer;
410         IWineD3DSurfaceImpl *back = (IWineD3DSurfaceImpl *) This->backBuffer[0];
411
412         if(front->resource.size == back->resource.size) {
413             DWORD fbflags;
414             flip_surface(front, back);
415
416             /* Tell the front buffer surface that is has been modified. However,
417              * the other locations were preserved during that, so keep the flags.
418              * This serves to update the emulated overlay, if any
419              */
420             fbflags = front->Flags;
421             IWineD3DSurface_ModifyLocation(This->frontBuffer, SFLAG_INDRAWABLE, TRUE);
422             front->Flags = fbflags;
423         } else {
424             IWineD3DSurface_ModifyLocation((IWineD3DSurface *) front, SFLAG_INDRAWABLE, TRUE);
425             IWineD3DSurface_ModifyLocation((IWineD3DSurface *) back, SFLAG_INDRAWABLE, TRUE);
426         }
427     } else {
428         IWineD3DSurface_ModifyLocation(This->frontBuffer, SFLAG_INDRAWABLE, TRUE);
429         /* If the swapeffect is DISCARD, the back buffer is undefined. That means the SYSMEM
430          * and INTEXTURE copies can keep their old content if they have any defined content.
431          * If the swapeffect is COPY, the content remains the same. If it is FLIP however,
432          * the texture / sysmem copy needs to be reloaded from the drawable
433          */
434         if(This->presentParms.SwapEffect == WINED3DSWAPEFFECT_FLIP) {
435             IWineD3DSurface_ModifyLocation(This->backBuffer[0], SFLAG_INDRAWABLE, TRUE);
436         }
437     }
438
439     if (This->device->stencilBufferTarget)
440     {
441         if (This->presentParms.Flags & WINED3DPRESENTFLAG_DISCARD_DEPTHSTENCIL
442                 || ((IWineD3DSurfaceImpl *)This->device->stencilBufferTarget)->Flags & SFLAG_DISCARD)
443         {
444             surface_modify_ds_location(This->device->stencilBufferTarget, SFLAG_DS_DISCARDED);
445         }
446     }
447
448     if (This->presentParms.PresentationInterval != WINED3DPRESENT_INTERVAL_IMMEDIATE
449             && context->gl_info->supported[SGI_VIDEO_SYNC])
450     {
451         retval = GL_EXTCALL(glXGetVideoSyncSGI(&sync));
452         if(retval != 0) {
453             ERR("glXGetVideoSyncSGI failed(retval = %d\n", retval);
454         }
455
456         switch(This->presentParms.PresentationInterval) {
457             case WINED3DPRESENT_INTERVAL_DEFAULT:
458             case WINED3DPRESENT_INTERVAL_ONE:
459                 if(sync <= This->vSyncCounter) {
460                     retval = GL_EXTCALL(glXWaitVideoSyncSGI(1, 0, &This->vSyncCounter));
461                 } else {
462                     This->vSyncCounter = sync;
463                 }
464                 break;
465             case WINED3DPRESENT_INTERVAL_TWO:
466                 if(sync <= This->vSyncCounter + 1) {
467                     retval = GL_EXTCALL(glXWaitVideoSyncSGI(2, This->vSyncCounter & 0x1, &This->vSyncCounter));
468                 } else {
469                     This->vSyncCounter = sync;
470                 }
471                 break;
472             case WINED3DPRESENT_INTERVAL_THREE:
473                 if(sync <= This->vSyncCounter + 2) {
474                     retval = GL_EXTCALL(glXWaitVideoSyncSGI(3, This->vSyncCounter % 0x3, &This->vSyncCounter));
475                 } else {
476                     This->vSyncCounter = sync;
477                 }
478                 break;
479             case WINED3DPRESENT_INTERVAL_FOUR:
480                 if(sync <= This->vSyncCounter + 3) {
481                     retval = GL_EXTCALL(glXWaitVideoSyncSGI(4, This->vSyncCounter & 0x3, &This->vSyncCounter));
482                 } else {
483                     This->vSyncCounter = sync;
484                 }
485                 break;
486             default:
487                 FIXME("Unknown presentation interval %08x\n", This->presentParms.PresentationInterval);
488         }
489     }
490
491     context_release(context);
492
493     TRACE("returning\n");
494     return WINED3D_OK;
495 }
496
497 static HRESULT WINAPI IWineD3DSwapChainImpl_SetDestWindowOverride(IWineD3DSwapChain *iface, HWND window) {
498     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
499     WINED3DLOCKED_RECT r;
500     BYTE *mem;
501
502     if(window == This->win_handle) return WINED3D_OK;
503
504     TRACE("Performing dest override of swapchain %p from window %p to %p\n", This, This->win_handle, window);
505     if (This->context[0] == This->device->contexts[0])
506     {
507         /* The primary context 'owns' all the opengl resources. Destroying and recreating that context requires downloading
508          * all opengl resources, deleting the gl resources, destroying all other contexts, then recreating all other contexts
509          * and reload the resources
510          */
511         delete_opengl_contexts((IWineD3DDevice *)This->device, iface);
512         This->win_handle = window;
513         create_primary_opengl_context((IWineD3DDevice *)This->device, iface);
514     }
515     else
516     {
517         This->win_handle = window;
518
519         /* The old back buffer has to be copied over to the new back buffer. A lockrect - switchcontext - unlockrect
520          * would suffice in theory, but it is rather nasty and may cause troubles with future changes of the locking code
521          * So lock read only, copy the surface out, then lock with the discard flag and write back
522          */
523         IWineD3DSurface_LockRect(This->backBuffer[0], &r, NULL, WINED3DLOCK_READONLY);
524         mem = HeapAlloc(GetProcessHeap(), 0, r.Pitch * ((IWineD3DSurfaceImpl *) This->backBuffer[0])->currentDesc.Height);
525         memcpy(mem, r.pBits, r.Pitch * ((IWineD3DSurfaceImpl *) This->backBuffer[0])->currentDesc.Height);
526         IWineD3DSurface_UnlockRect(This->backBuffer[0]);
527
528         context_destroy(This->device, This->context[0]);
529         This->context[0] = context_create(This->device, (IWineD3DSurfaceImpl *)This->frontBuffer,
530                 This->win_handle, FALSE /* pbuffer */, &This->presentParms);
531         context_release(This->context[0]);
532
533         IWineD3DSurface_LockRect(This->backBuffer[0], &r, NULL, WINED3DLOCK_DISCARD);
534         memcpy(r.pBits, mem, r.Pitch * ((IWineD3DSurfaceImpl *) This->backBuffer[0])->currentDesc.Height);
535         HeapFree(GetProcessHeap(), 0, mem);
536         IWineD3DSurface_UnlockRect(This->backBuffer[0]);
537     }
538     return WINED3D_OK;
539 }
540
541 static const IWineD3DSwapChainVtbl IWineD3DSwapChain_Vtbl =
542 {
543     /* IUnknown */
544     IWineD3DBaseSwapChainImpl_QueryInterface,
545     IWineD3DBaseSwapChainImpl_AddRef,
546     IWineD3DBaseSwapChainImpl_Release,
547     /* IWineD3DSwapChain */
548     IWineD3DBaseSwapChainImpl_GetParent,
549     IWineD3DSwapChainImpl_Destroy,
550     IWineD3DBaseSwapChainImpl_GetDevice,
551     IWineD3DSwapChainImpl_Present,
552     IWineD3DSwapChainImpl_SetDestWindowOverride,
553     IWineD3DBaseSwapChainImpl_GetFrontBufferData,
554     IWineD3DBaseSwapChainImpl_GetBackBuffer,
555     IWineD3DBaseSwapChainImpl_GetRasterStatus,
556     IWineD3DBaseSwapChainImpl_GetDisplayMode,
557     IWineD3DBaseSwapChainImpl_GetPresentParameters,
558     IWineD3DBaseSwapChainImpl_SetGammaRamp,
559     IWineD3DBaseSwapChainImpl_GetGammaRamp
560 };
561
562 static LONG fullscreen_style(LONG style)
563 {
564     /* Make sure the window is managed, otherwise we won't get keyboard input. */
565     style |= WS_POPUP | WS_SYSMENU;
566     style &= ~(WS_CAPTION | WS_THICKFRAME);
567
568     return style;
569 }
570
571 static LONG fullscreen_exstyle(LONG exstyle)
572 {
573     /* Filter out window decorations. */
574     exstyle &= ~(WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE);
575
576     return exstyle;
577 }
578
579 void swapchain_setup_fullscreen_window(IWineD3DSwapChainImpl *swapchain, UINT w, UINT h)
580 {
581     IWineD3DDeviceImpl *device = swapchain->device;
582     HWND window = swapchain->win_handle;
583     BOOL filter_messages;
584     LONG style, exstyle;
585
586     TRACE("Setting up window %p for fullscreen mode.\n", window);
587
588     if (device->style || device->exStyle)
589     {
590         ERR("Changing the window style for window %p, but another style (%08x, %08x) is already stored.\n",
591                 window, device->style, device->exStyle);
592     }
593
594     device->style = GetWindowLongW(window, GWL_STYLE);
595     device->exStyle = GetWindowLongW(window, GWL_EXSTYLE);
596
597     style = fullscreen_style(device->style);
598     exstyle = fullscreen_exstyle(device->exStyle);
599
600     TRACE("Old style was %08x, %08x, setting to %08x, %08x.\n",
601             device->style, device->exStyle, style, exstyle);
602
603     filter_messages = swapchain->filter_messages;
604     swapchain->filter_messages = TRUE;
605
606     SetWindowLongW(window, GWL_STYLE, style);
607     SetWindowLongW(window, GWL_EXSTYLE, exstyle);
608     SetWindowPos(window, HWND_TOP, 0, 0, w, h, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
609
610     swapchain->filter_messages = filter_messages;
611 }
612
613 void swapchain_restore_fullscreen_window(IWineD3DSwapChainImpl *swapchain)
614 {
615     IWineD3DDeviceImpl *device = swapchain->device;
616     HWND window = swapchain->win_handle;
617     BOOL filter_messages;
618     LONG style, exstyle;
619
620     if (!device->style && !device->exStyle) return;
621
622     TRACE("Restoring window style of window %p to %08x, %08x.\n",
623             window, device->style, device->exStyle);
624
625     style = GetWindowLongW(window, GWL_STYLE);
626     exstyle = GetWindowLongW(window, GWL_EXSTYLE);
627
628     filter_messages = swapchain->filter_messages;
629     swapchain->filter_messages = TRUE;
630
631     /* Only restore the style if the application didn't modify it during the
632      * fullscreen phase. Some applications change it before calling Reset()
633      * when switching between windowed and fullscreen modes (HL2), some
634      * depend on the original style (Eve Online). */
635     if (style == fullscreen_style(device->style) && exstyle == fullscreen_exstyle(device->exStyle))
636     {
637         SetWindowLongW(window, GWL_STYLE, device->style);
638         SetWindowLongW(window, GWL_EXSTYLE, device->exStyle);
639     }
640     SetWindowPos(window, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
641
642     swapchain->filter_messages = filter_messages;
643
644     /* Delete the old values. */
645     device->style = 0;
646     device->exStyle = 0;
647 }
648
649
650 HRESULT swapchain_init(IWineD3DSwapChainImpl *swapchain, WINED3DSURFTYPE surface_type,
651         IWineD3DDeviceImpl *device, WINED3DPRESENT_PARAMETERS *present_parameters, IUnknown *parent)
652 {
653     const struct wined3d_adapter *adapter = device->adapter;
654     const struct GlPixelFormatDesc *format_desc;
655     BOOL displaymode_set = FALSE;
656     WINED3DDISPLAYMODE mode;
657     RECT client_rect;
658     HWND window;
659     HRESULT hr;
660     UINT i;
661
662     if (present_parameters->BackBufferCount > WINED3DPRESENT_BACK_BUFFER_MAX)
663     {
664         FIXME("The application requested %u back buffers, this is not supported.\n",
665                 present_parameters->BackBufferCount);
666         return WINED3DERR_INVALIDCALL;
667     }
668
669     if (present_parameters->BackBufferCount > 1)
670     {
671         FIXME("The application requested more than one back buffer, this is not properly supported.\n"
672                 "Please configure the application to use double buffering (1 back buffer) if possible.\n");
673     }
674
675     switch (surface_type)
676     {
677         case SURFACE_GDI:
678             swapchain->lpVtbl = &IWineGDISwapChain_Vtbl;
679             break;
680
681         case SURFACE_OPENGL:
682             swapchain->lpVtbl = &IWineD3DSwapChain_Vtbl;
683             break;
684
685         case SURFACE_UNKNOWN:
686             FIXME("Caller tried to create a SURFACE_UNKNOWN swapchain.\n");
687             return WINED3DERR_INVALIDCALL;
688     }
689
690     window = present_parameters->hDeviceWindow ? present_parameters->hDeviceWindow : device->createParms.hFocusWindow;
691
692     if (!wined3d_register_window(window, swapchain))
693     {
694         ERR("Failed to register window %p.\n", window);
695         return E_FAIL;
696     }
697
698     swapchain->device = device;
699     swapchain->parent = parent;
700     swapchain->ref = 1;
701     swapchain->win_handle = window;
702
703     if (!present_parameters->Windowed && window)
704     {
705         swapchain_setup_fullscreen_window(swapchain, present_parameters->BackBufferWidth,
706                 present_parameters->BackBufferHeight);
707     }
708
709     IWineD3D_GetAdapterDisplayMode(device->wined3d, adapter->ordinal, &mode);
710     swapchain->orig_width = mode.Width;
711     swapchain->orig_height = mode.Height;
712     swapchain->orig_fmt = mode.Format;
713     format_desc = getFormatDescEntry(mode.Format, &adapter->gl_info);
714
715     GetClientRect(window, &client_rect);
716     if (present_parameters->Windowed
717             && (!present_parameters->BackBufferWidth || !present_parameters->BackBufferHeight
718             || present_parameters->BackBufferFormat == WINED3DFMT_UNKNOWN))
719     {
720
721         if (!present_parameters->BackBufferWidth)
722         {
723             present_parameters->BackBufferWidth = client_rect.right;
724             TRACE("Updating width to %u.\n", present_parameters->BackBufferWidth);
725         }
726
727         if (!present_parameters->BackBufferHeight)
728         {
729             present_parameters->BackBufferHeight = client_rect.bottom;
730             TRACE("Updating height to %u.\n", present_parameters->BackBufferHeight);
731         }
732
733         if (present_parameters->BackBufferFormat == WINED3DFMT_UNKNOWN)
734         {
735             present_parameters->BackBufferFormat = swapchain->orig_fmt;
736             TRACE("Updating format to %s.\n", debug_d3dformat(swapchain->orig_fmt));
737         }
738     }
739     swapchain->presentParms = *present_parameters;
740
741     if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
742             && (present_parameters->BackBufferWidth != client_rect.right
743             || present_parameters->BackBufferHeight != client_rect.bottom))
744     {
745         TRACE("Rendering to FBO. Backbuffer %ux%u, window %ux%u.\n",
746                 present_parameters->BackBufferWidth,
747                 present_parameters->BackBufferHeight,
748                 client_rect.right, client_rect.bottom);
749         swapchain->render_to_fbo = TRUE;
750     }
751
752     TRACE("Creating front buffer.\n");
753     hr = IWineD3DDeviceParent_CreateRenderTarget(device->device_parent, parent,
754             swapchain->presentParms.BackBufferWidth, swapchain->presentParms.BackBufferHeight,
755             swapchain->presentParms.BackBufferFormat, swapchain->presentParms.MultiSampleType,
756             swapchain->presentParms.MultiSampleQuality, TRUE /* Lockable */, &swapchain->frontBuffer);
757     if (FAILED(hr))
758     {
759         WARN("Failed to create front buffer, hr %#x.\n", hr);
760         goto err;
761     }
762
763     IWineD3DSurface_SetContainer(swapchain->frontBuffer, (IWineD3DBase *)swapchain);
764     ((IWineD3DSurfaceImpl *)swapchain->frontBuffer)->Flags |= SFLAG_SWAPCHAIN;
765     if (surface_type == SURFACE_OPENGL)
766     {
767         IWineD3DSurface_ModifyLocation(swapchain->frontBuffer, SFLAG_INDRAWABLE, TRUE);
768     }
769
770     /* MSDN says we're only allowed a single fullscreen swapchain per device,
771      * so we should really check to see if there is a fullscreen swapchain
772      * already. Does a single head count as full screen? */
773
774     if (!present_parameters->Windowed)
775     {
776         WINED3DDISPLAYMODE mode;
777
778         /* Change the display settings */
779         mode.Width = present_parameters->BackBufferWidth;
780         mode.Height = present_parameters->BackBufferHeight;
781         mode.Format = present_parameters->BackBufferFormat;
782         mode.RefreshRate = present_parameters->FullScreen_RefreshRateInHz;
783
784         hr = IWineD3DDevice_SetDisplayMode((IWineD3DDevice *)device, 0, &mode);
785         if (FAILED(hr))
786         {
787             WARN("Failed to set display mode, hr %#x.\n", hr);
788             goto err;
789         }
790         displaymode_set = TRUE;
791     }
792
793     swapchain->context = HeapAlloc(GetProcessHeap(), 0, sizeof(swapchain->context));
794     if (!swapchain->context)
795     {
796         ERR("Failed to create the context array.\n");
797         hr = E_OUTOFMEMORY;
798         goto err;
799     }
800     swapchain->num_contexts = 1;
801
802     if (surface_type == SURFACE_OPENGL)
803     {
804         swapchain->context[0] = context_create(device, (IWineD3DSurfaceImpl *)swapchain->frontBuffer,
805                 window, FALSE /* pbuffer */, present_parameters);
806         if (!swapchain->context[0])
807         {
808             WARN("Failed to create context.\n");
809             hr = WINED3DERR_NOTAVAILABLE;
810             goto err;
811         }
812
813         swapchain->context[0]->render_offscreen = swapchain->render_to_fbo;
814     }
815     else
816     {
817         swapchain->context[0] = NULL;
818     }
819
820     if (swapchain->presentParms.BackBufferCount > 0)
821     {
822         swapchain->backBuffer = HeapAlloc(GetProcessHeap(), 0,
823                 sizeof(*swapchain->backBuffer) * swapchain->presentParms.BackBufferCount);
824         if (!swapchain->backBuffer)
825         {
826             ERR("Failed to allocate backbuffer array memory.\n");
827             hr = E_OUTOFMEMORY;
828             goto err;
829         }
830
831         for (i = 0; i < swapchain->presentParms.BackBufferCount; ++i)
832         {
833             TRACE("Creating back buffer %u.\n", i);
834             hr = IWineD3DDeviceParent_CreateRenderTarget(device->device_parent, parent,
835                     swapchain->presentParms.BackBufferWidth, swapchain->presentParms.BackBufferHeight,
836                     swapchain->presentParms.BackBufferFormat, swapchain->presentParms.MultiSampleType,
837                     swapchain->presentParms.MultiSampleQuality, TRUE /* Lockable */, &swapchain->backBuffer[i]);
838             if (FAILED(hr))
839             {
840                 WARN("Failed to create back buffer %u, hr %#x.\n", i, hr);
841                 goto err;
842             }
843
844             IWineD3DSurface_SetContainer(swapchain->backBuffer[i], (IWineD3DBase *)swapchain);
845             ((IWineD3DSurfaceImpl *)swapchain->backBuffer[i])->Flags |= SFLAG_SWAPCHAIN;
846
847             if (surface_type == SURFACE_OPENGL)
848             {
849                 ENTER_GL();
850                 glDrawBuffer(GL_BACK);
851                 checkGLcall("glDrawBuffer(GL_BACK)");
852                 LEAVE_GL();
853             }
854         }
855     }
856     else
857     {
858         /* Single buffering - draw to front buffer */
859         if (surface_type == SURFACE_OPENGL)
860         {
861             ENTER_GL();
862             glDrawBuffer(GL_FRONT);
863             checkGLcall("glDrawBuffer(GL_FRONT)");
864             LEAVE_GL();
865         }
866     }
867
868     if (swapchain->context[0]) context_release(swapchain->context[0]);
869
870     /* Swapchains share the depth/stencil buffer, so only create a single depthstencil surface. */
871     if (present_parameters->EnableAutoDepthStencil && surface_type == SURFACE_OPENGL)
872     {
873         TRACE("Creating depth/stencil buffer.\n");
874         if (!device->auto_depth_stencil_buffer)
875         {
876             hr = IWineD3DDeviceParent_CreateDepthStencilSurface(device->device_parent, parent,
877                     swapchain->presentParms.BackBufferWidth, swapchain->presentParms.BackBufferHeight,
878                     swapchain->presentParms.AutoDepthStencilFormat, swapchain->presentParms.MultiSampleType,
879                     swapchain->presentParms.MultiSampleQuality, FALSE /* FIXME: Discard */,
880                     &device->auto_depth_stencil_buffer);
881             if (FAILED(hr))
882             {
883                 WARN("Failed to create the auto depth stencil, hr %#x.\n", hr);
884                 goto err;
885             }
886
887             IWineD3DSurface_SetContainer(device->auto_depth_stencil_buffer, NULL);
888         }
889     }
890
891     IWineD3DSwapChain_GetGammaRamp((IWineD3DSwapChain *)swapchain, &swapchain->orig_gamma);
892
893     return WINED3D_OK;
894
895 err:
896     if (displaymode_set)
897     {
898         DEVMODEW devmode;
899
900         ClipCursor(NULL);
901
902         /* Change the display settings */
903         memset(&devmode, 0, sizeof(devmode));
904         devmode.dmSize = sizeof(devmode);
905         devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
906         devmode.dmBitsPerPel = format_desc->byte_count * 8;
907         devmode.dmPelsWidth = swapchain->orig_width;
908         devmode.dmPelsHeight = swapchain->orig_height;
909         ChangeDisplaySettingsExW(adapter->DeviceName, &devmode, NULL, CDS_FULLSCREEN, NULL);
910     }
911
912     if (swapchain->backBuffer)
913     {
914         for (i = 0; i < swapchain->presentParms.BackBufferCount; ++i)
915         {
916             if (swapchain->backBuffer[i]) IWineD3DSurface_Release(swapchain->backBuffer[i]);
917         }
918         HeapFree(GetProcessHeap(), 0, swapchain->backBuffer);
919     }
920
921     if (swapchain->context && swapchain->context[0])
922     {
923         context_release(swapchain->context[0]);
924         context_destroy(device, swapchain->context[0]);
925     }
926
927     if (swapchain->frontBuffer) IWineD3DSurface_Release(swapchain->frontBuffer);
928
929     wined3d_unregister_window(window);
930
931     return hr;
932 }
933
934 struct wined3d_context *swapchain_create_context_for_thread(IWineD3DSwapChain *iface)
935 {
936     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *) iface;
937     struct wined3d_context **newArray;
938     struct wined3d_context *ctx;
939
940     TRACE("Creating a new context for swapchain %p, thread %d\n", This, GetCurrentThreadId());
941
942     ctx = context_create(This->device, (IWineD3DSurfaceImpl *)This->frontBuffer,
943             This->context[0]->win_handle, FALSE /* pbuffer */, &This->presentParms);
944     if (!ctx)
945     {
946         ERR("Failed to create a new context for the swapchain\n");
947         return NULL;
948     }
949     context_release(ctx);
950
951     newArray = HeapAlloc(GetProcessHeap(), 0, sizeof(*newArray) * This->num_contexts + 1);
952     if(!newArray) {
953         ERR("Out of memory when trying to allocate a new context array\n");
954         context_destroy(This->device, ctx);
955         return NULL;
956     }
957     memcpy(newArray, This->context, sizeof(*newArray) * This->num_contexts);
958     HeapFree(GetProcessHeap(), 0, This->context);
959     newArray[This->num_contexts] = ctx;
960     This->context = newArray;
961     This->num_contexts++;
962
963     TRACE("Returning context %p\n", ctx);
964     return ctx;
965 }
966
967 void get_drawable_size_swapchain(struct wined3d_context *context, UINT *width, UINT *height)
968 {
969     IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)context->current_rt;
970     /* The drawable size of an onscreen drawable is the surface size.
971      * (Actually: The window size, but the surface is created in window size) */
972     *width = surface->currentDesc.Width;
973     *height = surface->currentDesc.Height;
974 }
975
976 LRESULT swapchain_process_message(IWineD3DSwapChainImpl *swapchain, HWND window,
977         UINT message, WPARAM wparam, LPARAM lparam, WNDPROC proc)
978 {
979     if (swapchain->filter_messages)
980     {
981         TRACE("Filtering message: window %p, message %#x, wparam %#lx, lparam %#lx.\n",
982                 window, message, wparam, lparam);
983         return DefWindowProcW(window, message, wparam, lparam);
984     }
985
986     if (message == WM_DESTROY)
987     {
988         TRACE("unregister window %p.\n", window);
989         wined3d_unregister_window(window);
990     }
991
992     return CallWindowProcW(proc, window, message, wparam, lparam);
993 }