1 /* User-based primary surface driver
3 * Copyright 2000-2001 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 HWND get_display_window(IDirectDrawSurfaceImpl* This, LPPOINT pt);
38 static ICOM_VTABLE(IDirectDrawSurface7) User_IDirectDrawSurface7_VTable;
41 User_DirectDrawSurface_Construct(IDirectDrawSurfaceImpl* This,
43 const DDSURFACEDESC2* pDDSD)
45 USER_PRIV_VAR(priv, This);
48 TRACE("(%p,%p,%p)\n",This,pDD,pDDSD);
49 hr = DIB_DirectDrawSurface_Construct(This, pDD, pDDSD);
50 if (FAILED(hr)) return hr;
52 ICOM_INIT_INTERFACE(This, IDirectDrawSurface7,
53 User_IDirectDrawSurface7_VTable);
55 This->final_release = User_DirectDrawSurface_final_release;
56 This->duplicate_surface = User_DirectDrawSurface_duplicate_surface;
58 This->lock_update = User_DirectDrawSurface_lock_update;
59 This->unlock_update = User_DirectDrawSurface_unlock_update;
61 This->flip_data = User_DirectDrawSurface_flip_data;
62 This->flip_update = User_DirectDrawSurface_flip_update;
64 This->get_dc = User_DirectDrawSurface_get_dc;
65 This->release_dc = User_DirectDrawSurface_release_dc;
67 This->set_palette = User_DirectDrawSurface_set_palette;
68 This->update_palette = User_DirectDrawSurface_update_palette;
70 This->get_gamma_ramp = User_DirectDrawSurface_get_gamma_ramp;
71 This->set_gamma_ramp = User_DirectDrawSurface_set_gamma_ramp;
73 This->get_display_window = User_DirectDrawSurface_get_display_window;
75 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
78 DirectDrawSurface_RegisterClass();
81 priv->user.update_event = CreateEventA(NULL, FALSE, FALSE, NULL);
82 priv->user.update_thread = CreateThread(NULL, 0, User_update_thread, This, 0, NULL);
84 if (This->ddraw_owner->cooperative_level & DDSCL_FULLSCREEN) {
85 /* wait for window creation (or update thread destruction) */
86 while (WaitForMultipleObjects(1, &priv->user.update_thread, FALSE, 10) == WAIT_TIMEOUT)
87 if (This->more.lpDDRAWReserved) break;
88 if (!This->more.lpDDRAWReserved) {
89 ERR("window creation failed\n");
95 User_create_own_window(This);
98 if (!This->more.lpDDRAWReserved)
99 This->more.lpDDRAWReserved = (LPVOID)pDD->window;
102 return DIB_DirectDrawSurface_alloc_dc(This, &priv->user.cached_dc);
106 User_DirectDrawSurface_Create(IDirectDrawImpl *pDD,
107 const DDSURFACEDESC2 *pDDSD,
108 LPDIRECTDRAWSURFACE7 *ppSurf,
111 IDirectDrawSurfaceImpl* This;
113 assert(pUnkOuter == NULL);
115 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
116 sizeof(*This) + sizeof(User_DirectDrawSurfaceImpl));
117 if (This == NULL) return E_OUTOFMEMORY;
119 This->private = (User_DirectDrawSurfaceImpl*)(This+1);
121 hr = User_DirectDrawSurface_Construct(This, pDD, pDDSD);
123 HeapFree(GetProcessHeap(), 0, This);
125 *ppSurf = ICOM_INTERFACE(This, IDirectDrawSurface7);
130 void User_DirectDrawSurface_final_release(IDirectDrawSurfaceImpl* This)
132 USER_PRIV_VAR(priv, This);
134 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
137 HANDLE event = priv->user.update_event;
138 priv->user.update_event = 0;
140 TRACE("waiting for update thread to terminate...\n");
142 /* dispatch any waiting sendmessages */
145 PeekMessageA(&msg, 0, 0, 0, PM_NOREMOVE);
147 /* to avoid deadlocks, allow SendMessages from update thread
148 * through while we wait for it */
149 while (MsgWaitForMultipleObjects(1, &priv->user.update_thread, FALSE,
150 INFINITE, QS_SENDMESSAGE) == WAIT_OBJECT_0+1)
153 PeekMessageA(&msg, 0, 0, 0, PM_NOREMOVE);
156 WaitForSingleObject(priv->user.update_thread,INFINITE);
158 TRACE("update thread terminated\n");
159 CloseHandle(priv->user.update_thread);
162 User_destroy_own_window(This);
165 This->more.lpDDRAWReserved = 0;
167 DirectDrawSurface_UnregisterClass();
170 DIB_DirectDrawSurface_free_dc(This, priv->user.cached_dc);
171 DIB_DirectDrawSurface_final_release(This);
174 void User_DirectDrawSurface_lock_update(IDirectDrawSurfaceImpl* This,
177 User_copy_from_screen(This, pRect);
180 void User_DirectDrawSurface_unlock_update(IDirectDrawSurfaceImpl* This,
184 User_copy_to_screen(This, pRect);
186 USER_PRIV_VAR(priv, This);
187 SetEvent(priv->user.update_event);
191 void User_DirectDrawSurface_set_palette(IDirectDrawSurfaceImpl* This,
192 IDirectDrawPaletteImpl* pal)
194 USER_PRIV_VAR(priv, This);
197 FIXME("selecting null palette\n");
198 /* I don't think selecting GDI object 0 will work, we should select
199 * the original palette, returned by the first SelectPalette */
200 SelectPalette(priv->user.cached_dc, 0, FALSE);
204 SelectPalette(priv->user.cached_dc, pal->hpal, FALSE);
206 DIB_DirectDrawSurface_set_palette(This, pal);
209 void User_DirectDrawSurface_update_palette(IDirectDrawSurfaceImpl* This,
210 IDirectDrawPaletteImpl* pal,
211 DWORD dwStart, DWORD dwCount,
212 LPPALETTEENTRY palent)
214 USER_PRIV_VAR(priv, This);
216 DIB_DirectDrawSurface_update_palette(This, pal, dwStart, dwCount, palent);
217 /* FIXME: realize palette on display window */
220 /* with async updates, it's probably cool to force an update */
221 SetEvent(priv->user.update_event);
225 HRESULT User_DirectDrawSurface_duplicate_surface(IDirectDrawSurfaceImpl* This,
226 LPDIRECTDRAWSURFACE7* ppDup)
228 return User_DirectDrawSurface_Create(This->ddraw_owner,
229 &This->surface_desc, ppDup, NULL);
232 BOOL User_DirectDrawSurface_flip_data(IDirectDrawSurfaceImpl* front,
233 IDirectDrawSurfaceImpl* back,
236 USER_PRIV_VAR(front_priv, front);
237 USER_PRIV_VAR(back_priv, back);
241 tmp = front_priv->user.cached_dc;
242 front_priv->user.cached_dc = back_priv->user.cached_dc;
243 back_priv->user.cached_dc = tmp;
246 return DIB_DirectDrawSurface_flip_data(front, back, dwFlags);
249 void User_DirectDrawSurface_flip_update(IDirectDrawSurfaceImpl* This, DWORD dwFlags)
252 assert(This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE);
253 User_copy_to_screen(This,NULL);
255 USER_PRIV_VAR(priv, This);
256 assert(This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE);
257 SetEvent(priv->user.update_event);
261 HRESULT User_DirectDrawSurface_get_dc(IDirectDrawSurfaceImpl* This, HDC* phDC)
263 USER_PRIV_VAR(priv, This);
265 *phDC = priv->user.cached_dc;
269 HRESULT User_DirectDrawSurface_release_dc(IDirectDrawSurfaceImpl* This,
275 HRESULT User_DirectDrawSurface_get_gamma_ramp(IDirectDrawSurfaceImpl* This,
277 LPDDGAMMARAMP lpGammaRamp)
279 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
285 hDisplayWnd = get_display_window(This, &offset);
286 hDisplayDC = GetDCEx(hDisplayWnd, 0, DCX_CLIPSIBLINGS|DCX_CACHE);
287 hr = GetDeviceGammaRamp(hDisplayDC, lpGammaRamp) ? DD_OK : DDERR_UNSUPPORTED;
288 ReleaseDC(hDisplayWnd, hDisplayDC);
291 return Main_DirectDrawSurface_get_gamma_ramp(This, dwFlags, lpGammaRamp);
294 HRESULT User_DirectDrawSurface_set_gamma_ramp(IDirectDrawSurfaceImpl* This,
296 LPDDGAMMARAMP lpGammaRamp)
298 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
304 hDisplayWnd = get_display_window(This, &offset);
305 hDisplayDC = GetDCEx(hDisplayWnd, 0, DCX_CLIPSIBLINGS|DCX_CACHE);
306 hr = SetDeviceGammaRamp(hDisplayDC, lpGammaRamp) ? DD_OK : DDERR_UNSUPPORTED;
307 ReleaseDC(hDisplayWnd, hDisplayDC);
310 return Main_DirectDrawSurface_set_gamma_ramp(This, dwFlags, lpGammaRamp);
313 /* Returns the window that hosts the display.
314 * *pt is set to the upper left position of the window relative to the
315 * upper left corner of the surface. */
316 static HWND get_display_window(IDirectDrawSurfaceImpl* This, LPPOINT pt)
318 memset(pt, 0, sizeof(*pt));
320 if (This->ddraw_owner->cooperative_level & DDSCL_FULLSCREEN)
323 USER_PRIV_VAR(priv, This);
325 SetWindowPos(priv->user.window, HWND_TOP, 0, 0, 0, 0,
326 SWP_DEFERERASE|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|
327 SWP_NOREDRAW|SWP_NOSENDCHANGING|SWP_NOSIZE);
329 return priv->user.window;
331 return This->ddraw_owner->window;
336 if (This->clipper != NULL)
338 /* looks silly, but since we don't have the clipper locked */
339 HWND hWnd = This->clipper->hWnd;
343 ClientToScreen(hWnd, pt);
348 static BOOL warn = 0;
349 if (!warn++) FIXME("clipper clip lists not supported\n");
351 return GetDesktopWindow();
356 static BOOL warn = 0;
357 if (!warn++) WARN("hosting on root\n");
359 return GetDesktopWindow();
364 HWND User_DirectDrawSurface_get_display_window(IDirectDrawSurfaceImpl* This)
367 return get_display_window(This, &offset);
371 static void User_create_own_window(IDirectDrawSurfaceImpl* This)
373 USER_PRIV_VAR(priv, This);
375 if (This->ddraw_owner->cooperative_level & DDSCL_FULLSCREEN)
377 priv->user.window = CreateWindowExA(WS_EX_TOPMOST |
380 "WINE_DDRAW", "DirectDraw",
383 This->surface_desc.dwWidth,
384 This->surface_desc.dwHeight,
387 This->more.lpDDRAWReserved = (LPVOID)priv->user.window;
388 SetWindowPos(priv->user.window, HWND_TOP, 0, 0, 0, 0,
389 SWP_DEFERERASE|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|
390 SWP_NOREDRAW|SWP_NOSENDCHANGING|SWP_NOSIZE|SWP_SHOWWINDOW);
391 UpdateWindow(priv->user.window);
395 static void User_destroy_own_window(IDirectDrawSurfaceImpl* This)
397 USER_PRIV_VAR(priv, This);
399 if (priv->user.window)
401 SetWindowPos(priv->user.window, 0, 0, 0, 0, 0,
402 SWP_DEFERERASE|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|SWP_NOZORDER|
403 SWP_NOREDRAW|SWP_NOSENDCHANGING|SWP_NOSIZE|SWP_HIDEWINDOW);
404 This->more.lpDDRAWReserved = NULL;
405 DestroyWindow(priv->user.window);
406 priv->user.window = 0;
412 static DWORD CALLBACK User_update_thread(LPVOID arg)
414 IDirectDrawSurfaceImpl *This = (IDirectDrawSurfaceImpl *)arg;
415 USER_PRIV_VAR(priv, This);
416 volatile DWORD *pActive = (volatile DWORD *)&priv->user.update_event;
417 HANDLE event = *pActive;
420 User_create_own_window(This);
423 /* the point of this is that many games lock the primary surface
424 * multiple times per frame; this thread will then simply copy as
425 * often as it can without keeping the main thread waiting for
426 * each unlock, thus keeping the frame rate high */
429 DWORD ret = MsgWaitForMultipleObjects(1, &event, FALSE, INFINITE, QS_ALLINPUT);
432 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
434 switch (msg.message) {
436 DispatchMessageA(&msg);
439 /* since we risk getting keyboard messages posted to us,
440 * repost posted messages to cooperative window */
441 PostMessageA(This->ddraw_owner->window, msg.message, msg.wParam, msg.lParam);
445 DWORD ret = WaitForSingleObject(event, INFINITE);
447 if (ret == WAIT_OBJECT_0)
450 User_copy_to_screen(This, NULL);
454 else if (ret != WAIT_OBJECT_0+1) break;
458 User_destroy_own_window(This);
465 static void User_copy_to_screen(IDirectDrawSurfaceImpl* This, LPCRECT rc)
467 /* rc is unused. We copy the whole thing. */
469 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
476 if (FAILED(This->get_dc(This, &hSurfaceDC)))
479 hDisplayWnd = get_display_window(This, &offset);
480 hDisplayDC = GetDCEx(hDisplayWnd, 0, DCX_CLIPSIBLINGS|DCX_CACHE);
482 /* FIXME: this doesn't work... if users really want to run
483 * X in 8bpp, then we need to call directly into display.drv
484 * (or Wine's equivalent), and force a private colormap
485 * without default entries. */
487 SelectPalette(hDisplayDC, This->palette->hpal, FALSE);
488 RealizePalette(hDisplayDC); /* sends messages => deadlocks */
492 BitBlt(hDisplayDC, 0, 0, This->surface_desc.dwWidth,
493 This->surface_desc.dwHeight, hSurfaceDC, offset.x, offset.y,
496 ReleaseDC(hDisplayWnd, hDisplayDC);
500 static void User_copy_from_screen(IDirectDrawSurfaceImpl* This, LPCRECT rc)
502 /* rc is unused. We copy the whole thing. */
504 if (This->surface_desc.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
507 HWND hDisplayWnd = get_display_window(This, &offset);
508 HDC hDisplayDC = GetDC(hDisplayWnd);
510 BitBlt(This->hDC, offset.x, offset.y, This->surface_desc.dwWidth,
511 This->surface_desc.dwHeight, hDisplayDC, 0, 0, SRCCOPY);
513 ReleaseDC(hDisplayWnd, hDisplayDC);
517 static ICOM_VTABLE(IDirectDrawSurface7) User_IDirectDrawSurface7_VTable =
519 Main_DirectDrawSurface_QueryInterface,
520 Main_DirectDrawSurface_AddRef,
521 Main_DirectDrawSurface_Release,
522 Main_DirectDrawSurface_AddAttachedSurface,
523 Main_DirectDrawSurface_AddOverlayDirtyRect,
524 DIB_DirectDrawSurface_Blt,
525 Main_DirectDrawSurface_BltBatch,
526 DIB_DirectDrawSurface_BltFast,
527 Main_DirectDrawSurface_DeleteAttachedSurface,
528 Main_DirectDrawSurface_EnumAttachedSurfaces,
529 Main_DirectDrawSurface_EnumOverlayZOrders,
530 Main_DirectDrawSurface_Flip,
531 Main_DirectDrawSurface_GetAttachedSurface,
532 Main_DirectDrawSurface_GetBltStatus,
533 Main_DirectDrawSurface_GetCaps,
534 Main_DirectDrawSurface_GetClipper,
535 Main_DirectDrawSurface_GetColorKey,
536 Main_DirectDrawSurface_GetDC,
537 Main_DirectDrawSurface_GetFlipStatus,
538 Main_DirectDrawSurface_GetOverlayPosition,
539 Main_DirectDrawSurface_GetPalette,
540 Main_DirectDrawSurface_GetPixelFormat,
541 Main_DirectDrawSurface_GetSurfaceDesc,
542 Main_DirectDrawSurface_Initialize,
543 Main_DirectDrawSurface_IsLost,
544 Main_DirectDrawSurface_Lock,
545 Main_DirectDrawSurface_ReleaseDC,
546 DIB_DirectDrawSurface_Restore,
547 Main_DirectDrawSurface_SetClipper,
548 Main_DirectDrawSurface_SetColorKey,
549 Main_DirectDrawSurface_SetOverlayPosition,
550 Main_DirectDrawSurface_SetPalette,
551 Main_DirectDrawSurface_Unlock,
552 Main_DirectDrawSurface_UpdateOverlay,
553 Main_DirectDrawSurface_UpdateOverlayDisplay,
554 Main_DirectDrawSurface_UpdateOverlayZOrder,
555 Main_DirectDrawSurface_GetDDInterface,
556 Main_DirectDrawSurface_PageLock,
557 Main_DirectDrawSurface_PageUnlock,
558 DIB_DirectDrawSurface_SetSurfaceDesc,
559 Main_DirectDrawSurface_SetPrivateData,
560 Main_DirectDrawSurface_GetPrivateData,
561 Main_DirectDrawSurface_FreePrivateData,
562 Main_DirectDrawSurface_GetUniquenessValue,
563 Main_DirectDrawSurface_ChangeUniquenessValue,
564 Main_DirectDrawSurface_SetPriority,
565 Main_DirectDrawSurface_GetPriority,
566 Main_DirectDrawSurface_SetLOD,
567 Main_DirectDrawSurface_GetLOD