1 /* User-based primary surface driver
3 * Copyright 2000 TransGaming Technologies Inc.
13 #include "debugtools.h"
14 #include "ddraw_private.h"
15 #include "dsurface/main.h"
16 #include "dsurface/dib.h"
17 #include "dsurface/user.h"
18 #include "dsurface/wndproc.h"
20 DEFAULT_DEBUG_CHANNEL(ddraw);
22 /* if you use OWN_WINDOW, don't use SYNC_UPDATE, or you may get trouble */
23 /* #define SYNC_UPDATE */
27 static void User_create_own_window(IDirectDrawSurfaceImpl* This);
28 static void User_destroy_own_window(IDirectDrawSurfaceImpl* This);
31 static DWORD CALLBACK User_update_thread(LPVOID);
33 static void User_copy_to_screen(IDirectDrawSurfaceImpl* This, LPCRECT rc);
34 static void User_copy_from_screen(IDirectDrawSurfaceImpl* This, LPCRECT rc);
36 static ICOM_VTABLE(IDirectDrawSurface7) User_IDirectDrawSurface7_VTable;
39 User_DirectDrawSurface_Construct(IDirectDrawSurfaceImpl* This,
41 const DDSURFACEDESC2* pDDSD)
43 USER_PRIV_VAR(priv, This);
46 TRACE("(%p,%p,%p)\n",This,pDD,pDDSD);
47 hr = DIB_DirectDrawSurface_Construct(This, pDD, pDDSD);
48 if (FAILED(hr)) return hr;
50 ICOM_INIT_INTERFACE(This, IDirectDrawSurface7,
51 User_IDirectDrawSurface7_VTable);
53 This->final_release = User_DirectDrawSurface_final_release;
54 This->duplicate_surface = User_DirectDrawSurface_duplicate_surface;
56 This->lock_update = User_DirectDrawSurface_lock_update;
57 This->unlock_update = User_DirectDrawSurface_unlock_update;
59 This->flip_data = User_DirectDrawSurface_flip_data;
60 This->flip_update = User_DirectDrawSurface_flip_update;
62 This->get_dc = User_DirectDrawSurface_get_dc;
63 This->release_dc = User_DirectDrawSurface_release_dc;
65 This->set_palette = User_DirectDrawSurface_set_palette;
66 This->update_palette = User_DirectDrawSurface_update_palette;
68 This->get_display_window = User_DirectDrawSurface_get_display_window;
70 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
73 priv->user.update_event = CreateEventA(NULL, FALSE, FALSE, NULL);
74 priv->user.update_thread = CreateThread(NULL, 0, User_update_thread, This, 0, NULL);
77 User_create_own_window(This);
82 return DIB_DirectDrawSurface_alloc_dc(This, &priv->user.cached_dc);
86 User_DirectDrawSurface_Create(IDirectDrawImpl *pDD,
87 const DDSURFACEDESC2 *pDDSD,
88 LPDIRECTDRAWSURFACE7 *ppSurf,
91 IDirectDrawSurfaceImpl* This;
93 assert(pUnkOuter == NULL);
95 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
96 sizeof(*This) + sizeof(User_DirectDrawSurfaceImpl));
97 if (This == NULL) return E_OUTOFMEMORY;
99 This->private = (User_DirectDrawSurfaceImpl*)(This+1);
101 hr = User_DirectDrawSurface_Construct(This, pDD, pDDSD);
103 HeapFree(GetProcessHeap(), 0, This);
105 *ppSurf = ICOM_INTERFACE(This, IDirectDrawSurface7);
110 void User_DirectDrawSurface_final_release(IDirectDrawSurfaceImpl* This)
112 USER_PRIV_VAR(priv, This);
114 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
117 HANDLE event = priv->user.update_event;
118 priv->user.update_event = 0;
120 TRACE("waiting for update thread to terminate...\n");
122 /* dispatch any waiting sendmessages */
125 PeekMessageA(&msg, 0, 0, 0, PM_NOREMOVE);
127 /* to avoid deadlocks, allow SendMessages from update thread
128 * through while we wait for it */
129 while (MsgWaitForMultipleObjects(1, &priv->user.update_thread, FALSE,
130 INFINITE, QS_SENDMESSAGE) == WAIT_OBJECT_0+1)
133 PeekMessageA(&msg, 0, 0, 0, PM_NOREMOVE);
136 WaitForSingleObject(priv->user.update_thread,INFINITE);
138 TRACE("update thread terminated\n");
139 CloseHandle(priv->user.update_thread);
142 User_destroy_own_window(This);
146 DIB_DirectDrawSurface_free_dc(This, priv->user.cached_dc);
147 DIB_DirectDrawSurface_final_release(This);
150 void User_DirectDrawSurface_lock_update(IDirectDrawSurfaceImpl* This,
153 User_copy_from_screen(This, pRect);
156 void User_DirectDrawSurface_unlock_update(IDirectDrawSurfaceImpl* This,
160 User_copy_to_screen(This, pRect);
162 USER_PRIV_VAR(priv, This);
163 SetEvent(priv->user.update_event);
167 void User_DirectDrawSurface_set_palette(IDirectDrawSurfaceImpl* This,
168 IDirectDrawPaletteImpl* pal)
170 USER_PRIV_VAR(priv, This);
173 FIXME("selecting null palette\n");
174 /* I don't think selecting GDI object 0 will work, we should select
175 * the original palette, returned by the first SelectPalette */
176 SelectPalette(priv->user.cached_dc, 0, FALSE);
180 SelectPalette(priv->user.cached_dc, pal->hpal, FALSE);
182 DIB_DirectDrawSurface_set_palette(This, pal);
185 void User_DirectDrawSurface_update_palette(IDirectDrawSurfaceImpl* This,
186 IDirectDrawPaletteImpl* pal,
187 DWORD dwStart, DWORD dwCount,
188 LPPALETTEENTRY palent)
190 USER_PRIV_VAR(priv, This);
192 DIB_DirectDrawSurface_update_palette(This, pal, dwStart, dwCount, palent);
193 /* FIXME: realize palette on display window */
196 /* with async updates, it's probably cool to force an update */
197 SetEvent(priv->user.update_event);
201 HRESULT User_DirectDrawSurface_duplicate_surface(IDirectDrawSurfaceImpl* This,
202 LPDIRECTDRAWSURFACE7* ppDup)
204 return User_DirectDrawSurface_Create(This->ddraw_owner,
205 &This->surface_desc, ppDup, NULL);
208 void User_DirectDrawSurface_flip_data(IDirectDrawSurfaceImpl* front,
209 IDirectDrawSurfaceImpl* back)
211 USER_PRIV_VAR(front_priv, front);
212 USER_PRIV_VAR(back_priv, back);
216 tmp = front_priv->user.cached_dc;
217 front_priv->user.cached_dc = back_priv->user.cached_dc;
218 back_priv->user.cached_dc = tmp;
221 DIB_DirectDrawSurface_flip_data(front, back);
224 void User_DirectDrawSurface_flip_update(IDirectDrawSurfaceImpl* This)
227 assert(This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE);
228 User_copy_to_screen(This,NULL);
230 USER_PRIV_VAR(priv, This);
231 assert(This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE);
232 SetEvent(priv->user.update_event);
236 HRESULT User_DirectDrawSurface_get_dc(IDirectDrawSurfaceImpl* This, HDC* phDC)
238 USER_PRIV_VAR(priv, This);
240 *phDC = priv->user.cached_dc;
244 HRESULT User_DirectDrawSurface_release_dc(IDirectDrawSurfaceImpl* This,
250 /* Returns the window that hosts the display.
251 * *pt is set to the upper left position of the window relative to the
252 * upper left corner of the surface. */
253 static HWND get_display_window(IDirectDrawSurfaceImpl* This, LPPOINT pt)
255 memset(pt, 0, sizeof(*pt));
257 if (This->ddraw_owner->cooperative_level & DDSCL_FULLSCREEN)
260 USER_PRIV_VAR(priv, This);
261 SetWindowPos(priv->user.window, HWND_TOP, 0, 0, 0, 0,
262 SWP_DEFERERASE|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|
263 SWP_NOREDRAW|SWP_NOSENDCHANGING|SWP_NOSIZE);
264 return priv->user.window;
266 return This->ddraw_owner->window;
271 if (This->clipper != NULL)
273 /* looks silly, but since we don't have the clipper locked */
274 HWND hWnd = This->clipper->hWnd;
278 ClientToScreen(hWnd, pt);
283 static BOOL warn = 0;
284 if (!warn++) FIXME("clipper clip lists not supported\n");
286 return GetDesktopWindow();
291 static BOOL warn = 0;
292 if (!warn++) WARN("hosting on root\n");
294 return GetDesktopWindow();
299 HWND User_DirectDrawSurface_get_display_window(IDirectDrawSurfaceImpl* This)
302 return get_display_window(This, &offset);
306 static void User_create_own_window(IDirectDrawSurfaceImpl* This)
308 USER_PRIV_VAR(priv, This);
310 if (This->ddraw_owner->cooperative_level & DDSCL_FULLSCREEN)
312 DirectDrawSurface_RegisterClass();
313 priv->user.window = CreateWindowExA(WS_EX_TOPMOST,
314 "WINE_DDRAW", "DirectDraw",
317 This->surface_desc.dwWidth,
318 This->surface_desc.dwHeight,
321 SetWindowPos(priv->user.window, HWND_TOP, 0, 0, 0, 0,
322 SWP_DEFERERASE|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|
323 SWP_NOREDRAW|SWP_NOSENDCHANGING|SWP_NOSIZE|SWP_SHOWWINDOW);
324 UpdateWindow(priv->user.window);
328 static void User_destroy_own_window(IDirectDrawSurfaceImpl* This)
330 USER_PRIV_VAR(priv, This);
332 if (priv->user.window)
334 SetWindowPos(priv->user.window, 0, 0, 0, 0, 0,
335 SWP_DEFERERASE|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|SWP_NOZORDER|
336 SWP_NOREDRAW|SWP_NOSENDCHANGING|SWP_NOSIZE|SWP_HIDEWINDOW);
337 DestroyWindow(priv->user.window);
338 DirectDrawSurface_UnregisterClass();
339 priv->user.window = 0;
345 static DWORD CALLBACK User_update_thread(LPVOID arg)
347 IDirectDrawSurfaceImpl *This = (IDirectDrawSurfaceImpl *)arg;
348 USER_PRIV_VAR(priv, This);
349 volatile DWORD *pActive = (volatile DWORD *)&priv->user.update_event;
350 HANDLE event = *pActive;
353 User_create_own_window(This);
356 /* the point of this is that many games lock the primary surface
357 * multiple times per frame; this thread will then simply copy as
358 * often as it can without keeping the main thread waiting for
359 * each unlock, thus keeping the frame rate high */
362 DWORD ret = MsgWaitForMultipleObjects(1, &event, FALSE, INFINITE, QS_ALLINPUT);
365 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
367 switch (msg.message) {
369 DispatchMessageA(&msg);
372 /* since we risk getting keyboard messages posted to us,
373 * repost posted messages to cooperative window */
374 PostMessageA(This->ddraw_owner->window, msg.message, msg.wParam, msg.lParam);
378 DWORD ret = WaitForSingleObject(event, INFINITE);
380 if (ret == WAIT_OBJECT_0)
383 User_copy_to_screen(This, NULL);
387 else if (ret != WAIT_OBJECT_0+1) break;
391 User_destroy_own_window(This);
398 static void User_copy_to_screen(IDirectDrawSurfaceImpl* This, LPCRECT rc)
400 /* rc is unused. We copy the whole thing. */
402 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
409 if (FAILED(This->get_dc(This, &hSurfaceDC)))
412 hDisplayWnd = get_display_window(This, &offset);
413 hDisplayDC = GetDCEx(hDisplayWnd, 0, DCX_CLIPSIBLINGS);
415 /* FIXME: this doesn't work... if users really want to run
416 * X in 8bpp, then we need to call directly into display.drv
417 * (or Wine's equivalent), and force a private colormap
418 * without default entries. */
420 SelectPalette(hDisplayDC, This->palette->hpal, FALSE);
421 RealizePalette(hDisplayDC); /* sends messages => deadlocks */
425 BitBlt(hDisplayDC, 0, 0, This->surface_desc.dwWidth,
426 This->surface_desc.dwHeight, hSurfaceDC, offset.x, offset.y,
429 ReleaseDC(hDisplayWnd, hDisplayDC);
433 static void User_copy_from_screen(IDirectDrawSurfaceImpl* This, LPCRECT rc)
435 /* rc is unused. We copy the whole thing. */
437 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
440 HWND hDisplayWnd = get_display_window(This, &offset);
441 HDC hDisplayDC = GetDC(hDisplayWnd);
443 BitBlt(This->hDC, offset.x, offset.y, This->surface_desc.dwWidth,
444 This->surface_desc.dwHeight, hDisplayDC, 0, 0, SRCCOPY);
446 ReleaseDC(hDisplayWnd, hDisplayDC);
450 static ICOM_VTABLE(IDirectDrawSurface7) User_IDirectDrawSurface7_VTable =
452 Main_DirectDrawSurface_QueryInterface,
453 Main_DirectDrawSurface_AddRef,
454 Main_DirectDrawSurface_Release,
455 Main_DirectDrawSurface_AddAttachedSurface,
456 Main_DirectDrawSurface_AddOverlayDirtyRect,
457 DIB_DirectDrawSurface_Blt,
458 Main_DirectDrawSurface_BltBatch,
459 DIB_DirectDrawSurface_BltFast,
460 Main_DirectDrawSurface_DeleteAttachedSurface,
461 Main_DirectDrawSurface_EnumAttachedSurfaces,
462 Main_DirectDrawSurface_EnumOverlayZOrders,
463 Main_DirectDrawSurface_Flip,
464 Main_DirectDrawSurface_GetAttachedSurface,
465 Main_DirectDrawSurface_GetBltStatus,
466 Main_DirectDrawSurface_GetCaps,
467 Main_DirectDrawSurface_GetClipper,
468 Main_DirectDrawSurface_GetColorKey,
469 Main_DirectDrawSurface_GetDC,
470 Main_DirectDrawSurface_GetFlipStatus,
471 Main_DirectDrawSurface_GetOverlayPosition,
472 Main_DirectDrawSurface_GetPalette,
473 Main_DirectDrawSurface_GetPixelFormat,
474 Main_DirectDrawSurface_GetSurfaceDesc,
475 Main_DirectDrawSurface_Initialize,
476 Main_DirectDrawSurface_IsLost,
477 Main_DirectDrawSurface_Lock,
478 Main_DirectDrawSurface_ReleaseDC,
479 DIB_DirectDrawSurface_Restore,
480 Main_DirectDrawSurface_SetClipper,
481 Main_DirectDrawSurface_SetColorKey,
482 Main_DirectDrawSurface_SetOverlayPosition,
483 Main_DirectDrawSurface_SetPalette,
484 Main_DirectDrawSurface_Unlock,
485 Main_DirectDrawSurface_UpdateOverlay,
486 Main_DirectDrawSurface_UpdateOverlayDisplay,
487 Main_DirectDrawSurface_UpdateOverlayZOrder,
488 Main_DirectDrawSurface_GetDDInterface,
489 Main_DirectDrawSurface_PageLock,
490 Main_DirectDrawSurface_PageUnlock,
491 DIB_DirectDrawSurface_SetSurfaceDesc,
492 Main_DirectDrawSurface_SetPrivateData,
493 Main_DirectDrawSurface_GetPrivateData,
494 Main_DirectDrawSurface_FreePrivateData,
495 Main_DirectDrawSurface_GetUniquenessValue,
496 Main_DirectDrawSurface_ChangeUniquenessValue,
497 Main_DirectDrawSurface_SetPriority,
498 Main_DirectDrawSurface_GetPriority,
499 Main_DirectDrawSurface_SetLOD,
500 Main_DirectDrawSurface_GetLOD