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 */ 49static INLINE struct stw_framebuffer * 50stw_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 */ 70static INLINE void 71stw_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 105void 106stw_framebuffer_release( 107 struct stw_framebuffer *fb) 108{ 109 assert(fb); 110 pipe_mutex_unlock( fb->mutex ); 111} 112 113 114static INLINE void 115stw_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 */ 192LRESULT CALLBACK 193stw_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 239struct stw_framebuffer * 240stw_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 */ 314void 315stw_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 */ 342void 343stw_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 360void 361stw_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 */ 389static INLINE struct stw_framebuffer * 390stw_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 */ 407struct stw_framebuffer * 408stw_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 */ 427struct stw_framebuffer * 428stw_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 441BOOL APIENTRY 442DrvSetPixelFormat( 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 490int 491stw_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 507BOOL APIENTRY 508DrvPresentBuffers(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 */ 564BOOL 565stw_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 599BOOL APIENTRY 600DrvSwapBuffers( 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 623BOOL APIENTRY 624DrvSwapLayerBuffers( 625 HDC hdc, 626 UINT fuPlanes ) 627{ 628 if(fuPlanes & WGL_SWAP_MAIN_PLANE) 629 return DrvSwapBuffers(hdc); 630 631 return FALSE; 632} 633