1 /**************************************************************************
2 *
3 * Copyright 2008-2009 Vmware, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28 #include <windows.h>
29
30 #include "pipe/p_screen.h"
31 #include "util/u_memory.h"
32 #include "hud/hud_context.h"
33 #include "state_tracker/st_api.h"
34
35 #include "stw_icd.h"
36 #include "stw_framebuffer.h"
37 #include "stw_device.h"
38 #include "stw_winsys.h"
39 #include "stw_tls.h"
40 #include "stw_context.h"
41 #include "stw_st.h"
42
43
44 /**
45 * Search the framebuffer with the matching HWND while holding the
46 * stw_dev::fb_mutex global lock.
47 * If a stw_framebuffer is found, lock it and return the pointer.
48 * Else, return NULL.
49 */
50 static struct stw_framebuffer *
stw_framebuffer_from_hwnd_locked(HWND hwnd)51 stw_framebuffer_from_hwnd_locked(HWND hwnd)
52 {
53 struct stw_framebuffer *fb;
54
55 for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next)
56 if (fb->hWnd == hwnd) {
57 stw_framebuffer_lock(fb);
58 assert(fb->mutex.RecursionCount == 1);
59 return fb;
60 }
61
62 return NULL;
63 }
64
65
66 /**
67 * Decrement the reference count on the given stw_framebuffer object.
68 * If the reference count hits zero, destroy the object.
69 *
70 * Note: Both stw_dev::fb_mutex and stw_framebuffer::mutex must already be
71 * locked. After this function completes, the fb's mutex will be unlocked.
72 */
73 void
stw_framebuffer_release_locked(struct stw_framebuffer * fb)74 stw_framebuffer_release_locked(struct stw_framebuffer *fb)
75 {
76 struct stw_framebuffer **link;
77
78 assert(fb);
79 assert(stw_own_mutex(&fb->mutex));
80 assert(stw_own_mutex(&stw_dev->fb_mutex));
81
82 /* check the reference count */
83 fb->refcnt--;
84 if (fb->refcnt) {
85 stw_framebuffer_unlock(fb);
86 return;
87 }
88
89 /* remove this stw_framebuffer from the device's linked list */
90 link = &stw_dev->fb_head;
91 while (*link != fb)
92 link = &(*link)->next;
93 assert(*link);
94 *link = fb->next;
95 fb->next = NULL;
96
97 if (fb->shared_surface)
98 stw_dev->stw_winsys->shared_surface_close(stw_dev->screen,
99 fb->shared_surface);
100
101 stw_st_destroy_framebuffer_locked(fb->stfb);
102
103 stw_framebuffer_unlock(fb);
104
105 DeleteCriticalSection(&fb->mutex);
106
107 FREE( fb );
108 }
109
110
111 /**
112 * Query the size of the given framebuffer's on-screen window and update
113 * the stw_framebuffer's width/height.
114 */
115 static void
stw_framebuffer_get_size(struct stw_framebuffer * fb)116 stw_framebuffer_get_size(struct stw_framebuffer *fb)
117 {
118 LONG width, height;
119 RECT client_rect;
120 RECT window_rect;
121 POINT client_pos;
122
123 /*
124 * Sanity checking.
125 */
126 assert(fb->hWnd);
127 assert(fb->width && fb->height);
128 assert(fb->client_rect.right == fb->client_rect.left + fb->width);
129 assert(fb->client_rect.bottom == fb->client_rect.top + fb->height);
130
131 /*
132 * Get the client area size.
133 */
134 if (!GetClientRect(fb->hWnd, &client_rect)) {
135 return;
136 }
137
138 assert(client_rect.left == 0);
139 assert(client_rect.top == 0);
140 width = client_rect.right - client_rect.left;
141 height = client_rect.bottom - client_rect.top;
142
143 fb->minimized = width == 0 || height == 0;
144
145 if (width <= 0 || height <= 0) {
146 /*
147 * When the window is minimized GetClientRect will return zeros. Simply
148 * preserve the current window size, until the window is restored or
149 * maximized again.
150 */
151 return;
152 }
153
154 if (width != fb->width || height != fb->height) {
155 fb->must_resize = TRUE;
156 fb->width = width;
157 fb->height = height;
158 }
159
160 client_pos.x = 0;
161 client_pos.y = 0;
162 if (ClientToScreen(fb->hWnd, &client_pos) &&
163 GetWindowRect(fb->hWnd, &window_rect)) {
164 fb->client_rect.left = client_pos.x - window_rect.left;
165 fb->client_rect.top = client_pos.y - window_rect.top;
166 }
167
168 fb->client_rect.right = fb->client_rect.left + fb->width;
169 fb->client_rect.bottom = fb->client_rect.top + fb->height;
170
171 #if 0
172 debug_printf("\n");
173 debug_printf("%s: hwnd = %p\n", __FUNCTION__, fb->hWnd);
174 debug_printf("%s: client_position = (%li, %li)\n",
175 __FUNCTION__, client_pos.x, client_pos.y);
176 debug_printf("%s: window_rect = (%li, %li) - (%li, %li)\n",
177 __FUNCTION__,
178 window_rect.left, window_rect.top,
179 window_rect.right, window_rect.bottom);
180 debug_printf("%s: client_rect = (%li, %li) - (%li, %li)\n",
181 __FUNCTION__,
182 fb->client_rect.left, fb->client_rect.top,
183 fb->client_rect.right, fb->client_rect.bottom);
184 #endif
185 }
186
187
188 /**
189 * @sa http://msdn.microsoft.com/en-us/library/ms644975(VS.85).aspx
190 * @sa http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx
191 */
192 LRESULT CALLBACK
stw_call_window_proc(int nCode,WPARAM wParam,LPARAM lParam)193 stw_call_window_proc(int nCode, WPARAM wParam, LPARAM lParam)
194 {
195 struct stw_tls_data *tls_data;
196 PCWPSTRUCT pParams = (PCWPSTRUCT)lParam;
197 struct stw_framebuffer *fb;
198
199 tls_data = stw_tls_get_data();
200 if (!tls_data)
201 return 0;
202
203 if (nCode < 0 || !stw_dev)
204 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
205
206 /* We check that the stw_dev object is initialized before we try to do
207 * anything with it. Otherwise, in multi-threaded programs there's a
208 * chance of executing this code before the stw_dev object is fully
209 * initialized.
210 */
211 if (stw_dev && stw_dev->initialized) {
212 if (pParams->message == WM_WINDOWPOSCHANGED) {
213 /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according
214 * to http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx
215 * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it
216 * can be masked out by the application.
217 */
218 LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam;
219 if ((lpWindowPos->flags & SWP_SHOWWINDOW) ||
220 !(lpWindowPos->flags & SWP_NOMOVE) ||
221 !(lpWindowPos->flags & SWP_NOSIZE)) {
222 fb = stw_framebuffer_from_hwnd( pParams->hwnd );
223 if (fb) {
224 /* Size in WINDOWPOS includes the window frame, so get the size
225 * of the client area via GetClientRect.
226 */
227 stw_framebuffer_get_size(fb);
228 stw_framebuffer_unlock(fb);
229 }
230 }
231 }
232 else if (pParams->message == WM_DESTROY) {
233 stw_lock_framebuffers(stw_dev);
234 fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd );
235 if (fb)
236 stw_framebuffer_release_locked(fb);
237 stw_unlock_framebuffers(stw_dev);
238 }
239 }
240
241 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
242 }
243
244
245 /**
246 * Create a new stw_framebuffer object which corresponds to the given
247 * HDC/window. If successful, we return the new stw_framebuffer object
248 * with its mutex locked.
249 */
250 struct stw_framebuffer *
stw_framebuffer_create(HDC hdc,int iPixelFormat)251 stw_framebuffer_create(HDC hdc, int iPixelFormat)
252 {
253 HWND hWnd;
254 struct stw_framebuffer *fb;
255 const struct stw_pixelformat_info *pfi;
256
257 /* We only support drawing to a window. */
258 hWnd = WindowFromDC( hdc );
259 if (!hWnd)
260 return NULL;
261
262 fb = CALLOC_STRUCT( stw_framebuffer );
263 if (fb == NULL)
264 return NULL;
265
266 fb->hWnd = hWnd;
267 fb->iPixelFormat = iPixelFormat;
268
269 /*
270 * We often need a displayable pixel format to make GDI happy. Set it
271 * here (always 1, i.e., out first pixel format) where appropriate.
272 */
273 fb->iDisplayablePixelFormat = iPixelFormat <= stw_dev->pixelformat_count
274 ? iPixelFormat : 1;
275
276 fb->pfi = pfi = stw_pixelformat_get_info( iPixelFormat );
277 fb->stfb = stw_st_create_framebuffer( fb );
278 if (!fb->stfb) {
279 FREE( fb );
280 return NULL;
281 }
282
283 fb->refcnt = 1;
284
285 /*
286 * Windows can be sometimes have zero width and or height, but we ensure
287 * a non-zero framebuffer size at all times.
288 */
289
290 fb->must_resize = TRUE;
291 fb->width = 1;
292 fb->height = 1;
293 fb->client_rect.left = 0;
294 fb->client_rect.top = 0;
295 fb->client_rect.right = fb->client_rect.left + fb->width;
296 fb->client_rect.bottom = fb->client_rect.top + fb->height;
297
298 stw_framebuffer_get_size(fb);
299
300 InitializeCriticalSection(&fb->mutex);
301
302 /* This is the only case where we lock the stw_framebuffer::mutex before
303 * stw_dev::fb_mutex, since no other thread can know about this framebuffer
304 * and we must prevent any other thread from destroying it before we return.
305 */
306 stw_framebuffer_lock(fb);
307
308 stw_lock_framebuffers(stw_dev);
309 fb->next = stw_dev->fb_head;
310 stw_dev->fb_head = fb;
311 stw_unlock_framebuffers(stw_dev);
312
313 return fb;
314 }
315
316
317 /**
318 * Update the framebuffer's size if necessary.
319 */
320 void
stw_framebuffer_update(struct stw_framebuffer * fb)321 stw_framebuffer_update(struct stw_framebuffer *fb)
322 {
323 assert(fb->stfb);
324 assert(fb->height);
325 assert(fb->width);
326
327 /* XXX: It would be nice to avoid checking the size again -- in theory
328 * stw_call_window_proc would have cought the resize and stored the right
329 * size already, but unfortunately threads created before the DllMain is
330 * called don't get a DLL_THREAD_ATTACH notification, and there is no way
331 * to know of their existing without using the not very portable PSAPI.
332 */
333 stw_framebuffer_get_size(fb);
334 }
335
336
337 /**
338 * Try to free all stw_framebuffer objects associated with the device.
339 */
340 void
stw_framebuffer_cleanup(void)341 stw_framebuffer_cleanup(void)
342 {
343 struct stw_framebuffer *fb;
344 struct stw_framebuffer *next;
345
346 if (!stw_dev)
347 return;
348
349 stw_lock_framebuffers(stw_dev);
350
351 fb = stw_dev->fb_head;
352 while (fb) {
353 next = fb->next;
354
355 stw_framebuffer_lock(fb);
356 stw_framebuffer_release_locked(fb);
357
358 fb = next;
359 }
360 stw_dev->fb_head = NULL;
361
362 stw_unlock_framebuffers(stw_dev);
363 }
364
365
366 /**
367 * Given an hdc, return the corresponding stw_framebuffer.
368 * The returned stw_framebuffer will have its mutex locked.
369 */
370 static struct stw_framebuffer *
stw_framebuffer_from_hdc_locked(HDC hdc)371 stw_framebuffer_from_hdc_locked(HDC hdc)
372 {
373 HWND hwnd;
374
375 hwnd = WindowFromDC(hdc);
376 if (!hwnd) {
377 return NULL;
378 }
379
380 return stw_framebuffer_from_hwnd_locked(hwnd);
381 }
382
383
384 /**
385 * Given an HDC, return the corresponding stw_framebuffer.
386 * The returned stw_framebuffer will have its mutex locked.
387 */
388 struct stw_framebuffer *
stw_framebuffer_from_hdc(HDC hdc)389 stw_framebuffer_from_hdc(HDC hdc)
390 {
391 struct stw_framebuffer *fb;
392
393 if (!stw_dev)
394 return NULL;
395
396 stw_lock_framebuffers(stw_dev);
397 fb = stw_framebuffer_from_hdc_locked(hdc);
398 stw_unlock_framebuffers(stw_dev);
399
400 return fb;
401 }
402
403
404 /**
405 * Given an HWND, return the corresponding stw_framebuffer.
406 * The returned stw_framebuffer will have its mutex locked.
407 */
408 struct stw_framebuffer *
stw_framebuffer_from_hwnd(HWND hwnd)409 stw_framebuffer_from_hwnd(HWND hwnd)
410 {
411 struct stw_framebuffer *fb;
412
413 stw_lock_framebuffers(stw_dev);
414 fb = stw_framebuffer_from_hwnd_locked(hwnd);
415 stw_unlock_framebuffers(stw_dev);
416
417 return fb;
418 }
419
420
421 BOOL APIENTRY
DrvSetPixelFormat(HDC hdc,LONG iPixelFormat)422 DrvSetPixelFormat(HDC hdc, LONG iPixelFormat)
423 {
424 uint count;
425 uint index;
426 struct stw_framebuffer *fb;
427
428 if (!stw_dev)
429 return FALSE;
430
431 index = (uint) iPixelFormat - 1;
432 count = stw_pixelformat_get_count();
433 if (index >= count)
434 return FALSE;
435
436 fb = stw_framebuffer_from_hdc_locked(hdc);
437 if (fb) {
438 /*
439 * SetPixelFormat must be called only once. However ignore
440 * pbuffers, for which the framebuffer object is created first.
441 */
442 boolean bPbuffer = fb->bPbuffer;
443
444 stw_framebuffer_unlock( fb );
445
446 return bPbuffer;
447 }
448
449 fb = stw_framebuffer_create(hdc, iPixelFormat);
450 if (!fb) {
451 return FALSE;
452 }
453
454 stw_framebuffer_unlock( fb );
455
456 /* Some applications mistakenly use the undocumented wglSetPixelFormat
457 * function instead of SetPixelFormat, so we call SetPixelFormat here to
458 * avoid opengl32.dll's wglCreateContext to fail */
459 if (GetPixelFormat(hdc) == 0) {
460 BOOL bRet = SetPixelFormat(hdc, iPixelFormat, NULL);
461 if (!bRet) {
462 debug_printf("SetPixelFormat failed\n");
463 }
464 }
465
466 return TRUE;
467 }
468
469
470 int
stw_pixelformat_get(HDC hdc)471 stw_pixelformat_get(HDC hdc)
472 {
473 int iPixelFormat = 0;
474 struct stw_framebuffer *fb;
475
476 fb = stw_framebuffer_from_hdc(hdc);
477 if (fb) {
478 iPixelFormat = fb->iPixelFormat;
479 stw_framebuffer_unlock(fb);
480 }
481
482 return iPixelFormat;
483 }
484
485
486 BOOL APIENTRY
DrvPresentBuffers(HDC hdc,PGLPRESENTBUFFERSDATA data)487 DrvPresentBuffers(HDC hdc, PGLPRESENTBUFFERSDATA data)
488 {
489 struct stw_framebuffer *fb;
490 struct pipe_screen *screen;
491 struct pipe_resource *res;
492
493 if (!stw_dev)
494 return FALSE;
495
496 fb = stw_framebuffer_from_hdc( hdc );
497 if (fb == NULL)
498 return FALSE;
499
500 screen = stw_dev->screen;
501
502 res = (struct pipe_resource *)data->pPrivateData;
503
504 if (data->hSharedSurface != fb->hSharedSurface) {
505 if (fb->shared_surface) {
506 stw_dev->stw_winsys->shared_surface_close(screen, fb->shared_surface);
507 fb->shared_surface = NULL;
508 }
509
510 fb->hSharedSurface = data->hSharedSurface;
511
512 if (data->hSharedSurface &&
513 stw_dev->stw_winsys->shared_surface_open) {
514 fb->shared_surface =
515 stw_dev->stw_winsys->shared_surface_open(screen,
516 fb->hSharedSurface);
517 }
518 }
519
520 if (!fb->minimized) {
521 if (fb->shared_surface) {
522 stw_dev->stw_winsys->compose(screen,
523 res,
524 fb->shared_surface,
525 &fb->client_rect,
526 data->PresentHistoryToken);
527 }
528 else {
529 stw_dev->stw_winsys->present( screen, res, hdc );
530 }
531 }
532
533 stw_framebuffer_update(fb);
534 stw_notify_current_locked(fb);
535
536 stw_framebuffer_unlock(fb);
537
538 return TRUE;
539 }
540
541
542 /**
543 * Queue a composition.
544 *
545 * The stw_framebuffer object must have its mutex locked. The mutex will
546 * be unlocked here before returning.
547 */
548 BOOL
stw_framebuffer_present_locked(HDC hdc,struct stw_framebuffer * fb,struct pipe_resource * res)549 stw_framebuffer_present_locked(HDC hdc,
550 struct stw_framebuffer *fb,
551 struct pipe_resource *res)
552 {
553 if (stw_dev->callbacks.wglCbPresentBuffers &&
554 stw_dev->stw_winsys->compose) {
555 GLCBPRESENTBUFFERSDATA data;
556
557 memset(&data, 0, sizeof data);
558 data.magic1 = 2;
559 data.magic2 = 0;
560 data.AdapterLuid = stw_dev->AdapterLuid;
561 data.rect = fb->client_rect;
562 data.pPrivateData = (void *)res;
563
564 stw_notify_current_locked(fb);
565 stw_framebuffer_unlock(fb);
566
567 return stw_dev->callbacks.wglCbPresentBuffers(hdc, &data);
568 }
569 else {
570 struct pipe_screen *screen = stw_dev->screen;
571
572 stw_dev->stw_winsys->present( screen, res, hdc );
573
574 stw_framebuffer_update(fb);
575 stw_notify_current_locked(fb);
576 stw_framebuffer_unlock(fb);
577
578 return TRUE;
579 }
580 }
581
582
583 BOOL APIENTRY
DrvSwapBuffers(HDC hdc)584 DrvSwapBuffers(HDC hdc)
585 {
586 struct stw_context *ctx;
587 struct stw_framebuffer *fb;
588
589 if (!stw_dev)
590 return FALSE;
591
592 fb = stw_framebuffer_from_hdc( hdc );
593 if (fb == NULL)
594 return FALSE;
595
596 if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) {
597 stw_framebuffer_unlock(fb);
598 return TRUE;
599 }
600
601 ctx = stw_current_context();
602 if (ctx) {
603 if (ctx->hud) {
604 /* Display the HUD */
605 struct pipe_resource *back =
606 stw_get_framebuffer_resource(fb->stfb, ST_ATTACHMENT_BACK_LEFT);
607 if (back) {
608 hud_draw(ctx->hud, back);
609 }
610 }
611
612 if (ctx->current_framebuffer == fb) {
613 /* flush current context */
614 ctx->st->flush(ctx->st, ST_FLUSH_END_OF_FRAME, NULL);
615 }
616 }
617
618 return stw_st_swap_framebuffer_locked(hdc, fb->stfb);
619 }
620
621
622 BOOL APIENTRY
DrvSwapLayerBuffers(HDC hdc,UINT fuPlanes)623 DrvSwapLayerBuffers(HDC hdc, UINT fuPlanes)
624 {
625 if (fuPlanes & WGL_SWAP_MAIN_PLANE)
626 return DrvSwapBuffers(hdc);
627
628 return FALSE;
629 }
630