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 * 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 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 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 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 * 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 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 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 * 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 * 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 * 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 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 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 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 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 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 623 DrvSwapLayerBuffers(HDC hdc, UINT fuPlanes) 624 { 625 if (fuPlanes & WGL_SWAP_MAIN_PLANE) 626 return DrvSwapBuffers(hdc); 627 628 return FALSE; 629 } 630