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_format.h"
31 #include "pipe/p_screen.h"
32 #include "util/u_format.h"
33 #include "util/u_memory.h"
34 #include "state_tracker/st_api.h"
35
36 #include "stw_icd.h"
37 #include "stw_framebuffer.h"
38 #include "stw_device.h"
39 #include "stw_winsys.h"
40 #include "stw_tls.h"
41 #include "stw_context.h"
42 #include "stw_st.h"
43
44
45 /**
46 * Search the framebuffer with the matching HWND while holding the
47 * stw_dev::fb_mutex global lock.
48 */
49 static INLINE struct stw_framebuffer *
stw_framebuffer_from_hwnd_locked(HWND hwnd)50 stw_framebuffer_from_hwnd_locked(
51 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 pipe_mutex_lock(fb->mutex);
58 break;
59 }
60
61 return fb;
62 }
63
64
65 /**
66 * Destroy this framebuffer. Both stw_dev::fb_mutex and stw_framebuffer::mutex
67 * must be held, by this order. If there are still references to the
68 * framebuffer, nothing will happen.
69 */
70 static INLINE void
stw_framebuffer_destroy_locked(struct stw_framebuffer * fb)71 stw_framebuffer_destroy_locked(
72 struct stw_framebuffer *fb )
73 {
74 struct stw_framebuffer **link;
75
76 /* check the reference count */
77 fb->refcnt--;
78 if (fb->refcnt) {
79 pipe_mutex_unlock( fb->mutex );
80 return;
81 }
82
83 link = &stw_dev->fb_head;
84 while (*link != fb)
85 link = &(*link)->next;
86 assert(*link);
87 *link = fb->next;
88 fb->next = NULL;
89
90 if(fb->shared_surface)
91 stw_dev->stw_winsys->shared_surface_close(stw_dev->screen, fb->shared_surface);
92
93 stw_st_destroy_framebuffer_locked(fb->stfb);
94
95 ReleaseDC(fb->hWnd, fb->hDC);
96
97 pipe_mutex_unlock( fb->mutex );
98
99 pipe_mutex_destroy( fb->mutex );
100
101 FREE( fb );
102 }
103
104
105 void
stw_framebuffer_release(struct stw_framebuffer * fb)106 stw_framebuffer_release(
107 struct stw_framebuffer *fb)
108 {
109 assert(fb);
110 pipe_mutex_unlock( fb->mutex );
111 }
112
113
114 static INLINE void
stw_framebuffer_get_size(struct stw_framebuffer * fb)115 stw_framebuffer_get_size( struct stw_framebuffer *fb )
116 {
117 LONG width, height;
118 RECT client_rect;
119 RECT window_rect;
120 POINT client_pos;
121
122 /*
123 * Sanity checking.
124 */
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
135 if (!GetClientRect(fb->hWnd, &client_rect)) {
136 return;
137 }
138
139 assert(client_rect.left == 0);
140 assert(client_rect.top == 0);
141 width = client_rect.right - client_rect.left;
142 height = client_rect.bottom - client_rect.top;
143
144 if (width <= 0 || height <= 0) {
145 /*
146 * When the window is minimized GetClientRect will return zeros. Simply
147 * preserve the current window size, until the window is restored or
148 * maximized again.
149 */
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(
194 int nCode,
195 WPARAM wParam,
196 LPARAM lParam )
197 {
198 struct stw_tls_data *tls_data;
199 PCWPSTRUCT pParams = (PCWPSTRUCT)lParam;
200 struct stw_framebuffer *fb;
201
202 tls_data = stw_tls_get_data();
203 if(!tls_data)
204 return 0;
205
206 if (nCode < 0 || !stw_dev)
207 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
208
209 if (pParams->message == WM_WINDOWPOSCHANGED) {
210 /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according to
211 * http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx
212 * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it
213 * can be masked out by the application. */
214 LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam;
215 if((lpWindowPos->flags & SWP_SHOWWINDOW) ||
216 !(lpWindowPos->flags & SWP_NOMOVE) ||
217 !(lpWindowPos->flags & SWP_NOSIZE)) {
218 fb = stw_framebuffer_from_hwnd( pParams->hwnd );
219 if(fb) {
220 /* Size in WINDOWPOS includes the window frame, so get the size
221 * of the client area via GetClientRect. */
222 stw_framebuffer_get_size(fb);
223 stw_framebuffer_release(fb);
224 }
225 }
226 }
227 else if (pParams->message == WM_DESTROY) {
228 pipe_mutex_lock( stw_dev->fb_mutex );
229 fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd );
230 if(fb)
231 stw_framebuffer_destroy_locked(fb);
232 pipe_mutex_unlock( stw_dev->fb_mutex );
233 }
234
235 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
236 }
237
238
239 struct stw_framebuffer *
stw_framebuffer_create(HDC hdc,int iPixelFormat)240 stw_framebuffer_create(
241 HDC hdc,
242 int iPixelFormat )
243 {
244 HWND hWnd;
245 struct stw_framebuffer *fb;
246 const struct stw_pixelformat_info *pfi;
247
248 /* We only support drawing to a window. */
249 hWnd = WindowFromDC( hdc );
250 if(!hWnd)
251 return NULL;
252
253 fb = CALLOC_STRUCT( stw_framebuffer );
254 if (fb == NULL)
255 return NULL;
256
257 /* Applications use, create, destroy device contexts, so the hdc passed is. We create our own DC
258 * because we need one for single buffered visuals.
259 */
260 fb->hDC = GetDC(hWnd);
261
262 fb->hWnd = hWnd;
263 fb->iPixelFormat = iPixelFormat;
264
265 /*
266 * We often need a displayable pixel format to make GDI happy. Set it here (always 1, i.e.,
267 * out first pixel format) where appropriat.
268 */
269 fb->iDisplayablePixelFormat = iPixelFormat <= stw_dev->pixelformat_count ? iPixelFormat : 1;
270
271 fb->pfi = pfi = stw_pixelformat_get_info( iPixelFormat );
272 fb->stfb = stw_st_create_framebuffer( fb );
273 if (!fb->stfb) {
274 FREE( fb );
275 return NULL;
276 }
277
278 fb->refcnt = 1;
279
280 /*
281 * Windows can be sometimes have zero width and or height, but we ensure
282 * a non-zero framebuffer size at all times.
283 */
284
285 fb->must_resize = TRUE;
286 fb->width = 1;
287 fb->height = 1;
288 fb->client_rect.left = 0;
289 fb->client_rect.top = 0;
290 fb->client_rect.right = fb->client_rect.left + fb->width;
291 fb->client_rect.bottom = fb->client_rect.top + fb->height;
292
293 stw_framebuffer_get_size(fb);
294
295 pipe_mutex_init( fb->mutex );
296
297 /* This is the only case where we lock the stw_framebuffer::mutex before
298 * stw_dev::fb_mutex, since no other thread can know about this framebuffer
299 * and we must prevent any other thread from destroying it before we return.
300 */
301 pipe_mutex_lock( fb->mutex );
302
303 pipe_mutex_lock( stw_dev->fb_mutex );
304 fb->next = stw_dev->fb_head;
305 stw_dev->fb_head = fb;
306 pipe_mutex_unlock( stw_dev->fb_mutex );
307
308 return fb;
309 }
310
311 /**
312 * Have ptr reference fb. The referenced framebuffer should be locked.
313 */
314 void
stw_framebuffer_reference(struct stw_framebuffer ** ptr,struct stw_framebuffer * fb)315 stw_framebuffer_reference(
316 struct stw_framebuffer **ptr,
317 struct stw_framebuffer *fb)
318 {
319 struct stw_framebuffer *old_fb = *ptr;
320
321 if (old_fb == fb)
322 return;
323
324 if (fb)
325 fb->refcnt++;
326 if (old_fb) {
327 pipe_mutex_lock(stw_dev->fb_mutex);
328
329 pipe_mutex_lock(old_fb->mutex);
330 stw_framebuffer_destroy_locked(old_fb);
331
332 pipe_mutex_unlock(stw_dev->fb_mutex);
333 }
334
335 *ptr = fb;
336 }
337
338
339 /**
340 * Update the framebuffer's size if necessary.
341 */
342 void
stw_framebuffer_update(struct stw_framebuffer * fb)343 stw_framebuffer_update(
344 struct stw_framebuffer *fb)
345 {
346 assert(fb->stfb);
347 assert(fb->height);
348 assert(fb->width);
349
350 /* XXX: It would be nice to avoid checking the size again -- in theory
351 * stw_call_window_proc would have cought the resize and stored the right
352 * size already, but unfortunately threads created before the DllMain is
353 * called don't get a DLL_THREAD_ATTACH notification, and there is no way
354 * to know of their existing without using the not very portable PSAPI.
355 */
356 stw_framebuffer_get_size(fb);
357 }
358
359
360 void
stw_framebuffer_cleanup(void)361 stw_framebuffer_cleanup( void )
362 {
363 struct stw_framebuffer *fb;
364 struct stw_framebuffer *next;
365
366 if (!stw_dev)
367 return;
368
369 pipe_mutex_lock( stw_dev->fb_mutex );
370
371 fb = stw_dev->fb_head;
372 while (fb) {
373 next = fb->next;
374
375 pipe_mutex_lock(fb->mutex);
376 stw_framebuffer_destroy_locked(fb);
377
378 fb = next;
379 }
380 stw_dev->fb_head = NULL;
381
382 pipe_mutex_unlock( stw_dev->fb_mutex );
383 }
384
385
386 /**
387 * Given an hdc, return the corresponding stw_framebuffer.
388 */
389 static INLINE struct stw_framebuffer *
stw_framebuffer_from_hdc_locked(HDC hdc)390 stw_framebuffer_from_hdc_locked(
391 HDC hdc )
392 {
393 HWND hwnd;
394
395 hwnd = WindowFromDC(hdc);
396 if (!hwnd) {
397 return NULL;
398 }
399
400 return stw_framebuffer_from_hwnd_locked(hwnd);
401 }
402
403
404 /**
405 * Given an hdc, return the corresponding stw_framebuffer.
406 */
407 struct stw_framebuffer *
stw_framebuffer_from_hdc(HDC hdc)408 stw_framebuffer_from_hdc(
409 HDC hdc )
410 {
411 struct stw_framebuffer *fb;
412
413 if (!stw_dev)
414 return NULL;
415
416 pipe_mutex_lock( stw_dev->fb_mutex );
417 fb = stw_framebuffer_from_hdc_locked(hdc);
418 pipe_mutex_unlock( stw_dev->fb_mutex );
419
420 return fb;
421 }
422
423
424 /**
425 * Given an hdc, return the corresponding stw_framebuffer.
426 */
427 struct stw_framebuffer *
stw_framebuffer_from_hwnd(HWND hwnd)428 stw_framebuffer_from_hwnd(
429 HWND hwnd )
430 {
431 struct stw_framebuffer *fb;
432
433 pipe_mutex_lock( stw_dev->fb_mutex );
434 fb = stw_framebuffer_from_hwnd_locked(hwnd);
435 pipe_mutex_unlock( stw_dev->fb_mutex );
436
437 return fb;
438 }
439
440
441 BOOL APIENTRY
DrvSetPixelFormat(HDC hdc,LONG iPixelFormat)442 DrvSetPixelFormat(
443 HDC hdc,
444 LONG iPixelFormat )
445 {
446 uint count;
447 uint index;
448 struct stw_framebuffer *fb;
449
450 if (!stw_dev)
451 return FALSE;
452
453 index = (uint) iPixelFormat - 1;
454 count = stw_pixelformat_get_count();
455 if (index >= count)
456 return FALSE;
457
458 fb = stw_framebuffer_from_hdc_locked(hdc);
459 if(fb) {
460 /*
461 * SetPixelFormat must be called only once. However ignore
462 * pbuffers, for which the framebuffer object is created first.
463 */
464 boolean bPbuffer = fb->bPbuffer;
465
466 stw_framebuffer_release( fb );
467
468 return bPbuffer;
469 }
470
471 fb = stw_framebuffer_create(hdc, iPixelFormat);
472 if(!fb) {
473 return FALSE;
474 }
475
476 stw_framebuffer_release( fb );
477
478 /* Some applications mistakenly use the undocumented wglSetPixelFormat
479 * function instead of SetPixelFormat, so we call SetPixelFormat here to
480 * avoid opengl32.dll's wglCreateContext to fail */
481 if (GetPixelFormat(hdc) == 0) {
482 BOOL bRet = SetPixelFormat(hdc, iPixelFormat, NULL);
483 assert(bRet);
484 }
485
486 return TRUE;
487 }
488
489
490 int
stw_pixelformat_get(HDC hdc)491 stw_pixelformat_get(
492 HDC hdc )
493 {
494 int iPixelFormat = 0;
495 struct stw_framebuffer *fb;
496
497 fb = stw_framebuffer_from_hdc(hdc);
498 if(fb) {
499 iPixelFormat = fb->iPixelFormat;
500 stw_framebuffer_release(fb);
501 }
502
503 return iPixelFormat;
504 }
505
506
507 BOOL APIENTRY
DrvPresentBuffers(HDC hdc,PGLPRESENTBUFFERSDATA data)508 DrvPresentBuffers(HDC hdc, PGLPRESENTBUFFERSDATA data)
509 {
510 struct stw_framebuffer *fb;
511 struct pipe_screen *screen;
512 struct pipe_resource *res;
513
514 if (!stw_dev)
515 return FALSE;
516
517 fb = stw_framebuffer_from_hdc( hdc );
518 if (fb == NULL)
519 return FALSE;
520
521 screen = stw_dev->screen;
522
523 res = (struct pipe_resource *)data->pPrivateData;
524
525 if(data->hSharedSurface != fb->hSharedSurface) {
526 if(fb->shared_surface) {
527 stw_dev->stw_winsys->shared_surface_close(screen, fb->shared_surface);
528 fb->shared_surface = NULL;
529 }
530
531 fb->hSharedSurface = data->hSharedSurface;
532
533 if(data->hSharedSurface &&
534 stw_dev->stw_winsys->shared_surface_open) {
535 fb->shared_surface = stw_dev->stw_winsys->shared_surface_open(screen, fb->hSharedSurface);
536 }
537 }
538
539 if(fb->shared_surface) {
540 stw_dev->stw_winsys->compose(screen,
541 res,
542 fb->shared_surface,
543 &fb->client_rect,
544 data->PresentHistoryToken);
545 }
546 else {
547 stw_dev->stw_winsys->present( screen, res, hdc );
548 }
549
550 stw_framebuffer_update(fb);
551 stw_notify_current_locked(fb);
552
553 stw_framebuffer_release(fb);
554
555 return TRUE;
556 }
557
558
559 /**
560 * Queue a composition.
561 *
562 * It will drop the lock on success.
563 */
564 BOOL
stw_framebuffer_present_locked(HDC hdc,struct stw_framebuffer * fb,struct pipe_resource * res)565 stw_framebuffer_present_locked(HDC hdc,
566 struct stw_framebuffer *fb,
567 struct pipe_resource *res)
568 {
569 if(stw_dev->callbacks.wglCbPresentBuffers &&
570 stw_dev->stw_winsys->compose) {
571 GLCBPRESENTBUFFERSDATA data;
572
573 memset(&data, 0, sizeof data);
574 data.magic1 = 2;
575 data.magic2 = 0;
576 data.AdapterLuid = stw_dev->AdapterLuid;
577 data.rect = fb->client_rect;
578 data.pPrivateData = (void *)res;
579
580 stw_notify_current_locked(fb);
581 stw_framebuffer_release(fb);
582
583 return stw_dev->callbacks.wglCbPresentBuffers(hdc, &data);
584 }
585 else {
586 struct pipe_screen *screen = stw_dev->screen;
587
588 stw_dev->stw_winsys->present( screen, res, hdc );
589
590 stw_framebuffer_update(fb);
591 stw_notify_current_locked(fb);
592 stw_framebuffer_release(fb);
593
594 return TRUE;
595 }
596 }
597
598
599 BOOL APIENTRY
DrvSwapBuffers(HDC hdc)600 DrvSwapBuffers(
601 HDC hdc )
602 {
603 struct stw_framebuffer *fb;
604
605 if (!stw_dev)
606 return FALSE;
607
608 fb = stw_framebuffer_from_hdc( hdc );
609 if (fb == NULL)
610 return FALSE;
611
612 if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) {
613 stw_framebuffer_release(fb);
614 return TRUE;
615 }
616
617 stw_flush_current_locked(fb);
618
619 return stw_st_swap_framebuffer_locked(hdc, fb->stfb);
620 }
621
622
623 BOOL APIENTRY
DrvSwapLayerBuffers(HDC hdc,UINT fuPlanes)624 DrvSwapLayerBuffers(
625 HDC hdc,
626 UINT fuPlanes )
627 {
628 if(fuPlanes & WGL_SWAP_MAIN_PLANE)
629 return DrvSwapBuffers(hdc);
630
631 return FALSE;
632 }
633