17ec681f3Smrg/**************************************************************************
27ec681f3Smrg *
37ec681f3Smrg * Copyright 2008-2009 VMware, Inc.
47ec681f3Smrg * All Rights Reserved.
57ec681f3Smrg *
67ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
77ec681f3Smrg * copy of this software and associated documentation files (the
87ec681f3Smrg * "Software"), to deal in the Software without restriction, including
97ec681f3Smrg * without limitation the rights to use, copy, modify, merge, publish,
107ec681f3Smrg * distribute, sub license, and/or sell copies of the Software, and to
117ec681f3Smrg * permit persons to whom the Software is furnished to do so, subject to
127ec681f3Smrg * the following conditions:
137ec681f3Smrg *
147ec681f3Smrg * The above copyright notice and this permission notice (including the
157ec681f3Smrg * next paragraph) shall be included in all copies or substantial portions
167ec681f3Smrg * of the Software.
177ec681f3Smrg *
187ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
197ec681f3Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
207ec681f3Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
217ec681f3Smrg * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
227ec681f3Smrg * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
237ec681f3Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
247ec681f3Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
257ec681f3Smrg *
267ec681f3Smrg **************************************************************************/
277ec681f3Smrg
287ec681f3Smrg#include <windows.h>
297ec681f3Smrg
307ec681f3Smrg#include "pipe/p_screen.h"
317ec681f3Smrg#include "pipe/p_state.h"
327ec681f3Smrg#include "util/u_memory.h"
337ec681f3Smrg#include "hud/hud_context.h"
347ec681f3Smrg#include "util/os_time.h"
357ec681f3Smrg#include "frontend/api.h"
367ec681f3Smrg
377ec681f3Smrg#include <GL/gl.h>
387ec681f3Smrg#include "gldrv.h"
397ec681f3Smrg#include "stw_framebuffer.h"
407ec681f3Smrg#include "stw_device.h"
417ec681f3Smrg#include "stw_winsys.h"
427ec681f3Smrg#include "stw_tls.h"
437ec681f3Smrg#include "stw_context.h"
447ec681f3Smrg#include "stw_st.h"
457ec681f3Smrg
467ec681f3Smrg
477ec681f3Smrg/**
487ec681f3Smrg * Search the framebuffer with the matching HWND while holding the
497ec681f3Smrg * stw_dev::fb_mutex global lock.
507ec681f3Smrg * If a stw_framebuffer is found, lock it and return the pointer.
517ec681f3Smrg * Else, return NULL.
527ec681f3Smrg */
537ec681f3Smrgstatic struct stw_framebuffer *
547ec681f3Smrgstw_framebuffer_from_hwnd_locked(HWND hwnd)
557ec681f3Smrg{
567ec681f3Smrg   struct stw_framebuffer *fb;
577ec681f3Smrg
587ec681f3Smrg   for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next)
597ec681f3Smrg      if (fb->hWnd == hwnd) {
607ec681f3Smrg         stw_framebuffer_lock(fb);
617ec681f3Smrg         assert(fb->mutex.RecursionCount == 1);
627ec681f3Smrg         return fb;
637ec681f3Smrg      }
647ec681f3Smrg
657ec681f3Smrg   return NULL;
667ec681f3Smrg}
677ec681f3Smrg
687ec681f3Smrg
697ec681f3Smrg/**
707ec681f3Smrg * Decrement the reference count on the given stw_framebuffer object.
717ec681f3Smrg * If the reference count hits zero, destroy the object.
727ec681f3Smrg *
737ec681f3Smrg * Note: Both stw_dev::fb_mutex and stw_framebuffer::mutex must already be
747ec681f3Smrg * locked.  After this function completes, the fb's mutex will be unlocked.
757ec681f3Smrg */
767ec681f3Smrgvoid
777ec681f3Smrgstw_framebuffer_release_locked(struct stw_framebuffer *fb,
787ec681f3Smrg                               struct st_context_iface *stctx)
797ec681f3Smrg{
807ec681f3Smrg   struct stw_framebuffer **link;
817ec681f3Smrg
827ec681f3Smrg   assert(fb);
837ec681f3Smrg   assert(stw_own_mutex(&fb->mutex));
847ec681f3Smrg   assert(stw_own_mutex(&stw_dev->fb_mutex) || fb->owner == STW_FRAMEBUFFER_EGL_WINDOW);
857ec681f3Smrg
867ec681f3Smrg   /* check the reference count */
877ec681f3Smrg   fb->refcnt--;
887ec681f3Smrg   if (fb->refcnt) {
897ec681f3Smrg      stw_framebuffer_unlock(fb);
907ec681f3Smrg      return;
917ec681f3Smrg   }
927ec681f3Smrg
937ec681f3Smrg   if (fb->owner != STW_FRAMEBUFFER_EGL_WINDOW) {
947ec681f3Smrg      /* remove this stw_framebuffer from the device's linked list */
957ec681f3Smrg      link = &stw_dev->fb_head;
967ec681f3Smrg      while (*link != fb)
977ec681f3Smrg         link = &(*link)->next;
987ec681f3Smrg      assert(*link);
997ec681f3Smrg      *link = fb->next;
1007ec681f3Smrg      fb->next = NULL;
1017ec681f3Smrg   }
1027ec681f3Smrg
1037ec681f3Smrg   if (fb->shared_surface)
1047ec681f3Smrg      stw_dev->stw_winsys->shared_surface_close(stw_dev->screen,
1057ec681f3Smrg                                                fb->shared_surface);
1067ec681f3Smrg
1077ec681f3Smrg   if (fb->winsys_framebuffer)
1087ec681f3Smrg      fb->winsys_framebuffer->destroy(fb->winsys_framebuffer, stctx ? stctx->pipe : NULL);
1097ec681f3Smrg
1107ec681f3Smrg   stw_st_destroy_framebuffer_locked(fb->stfb);
1117ec681f3Smrg
1127ec681f3Smrg   stw_framebuffer_unlock(fb);
1137ec681f3Smrg
1147ec681f3Smrg   DeleteCriticalSection(&fb->mutex);
1157ec681f3Smrg
1167ec681f3Smrg   FREE( fb );
1177ec681f3Smrg}
1187ec681f3Smrg
1197ec681f3Smrg
1207ec681f3Smrg/**
1217ec681f3Smrg * Query the size of the given framebuffer's on-screen window and update
1227ec681f3Smrg * the stw_framebuffer's width/height.
1237ec681f3Smrg */
1247ec681f3Smrgstatic void
1257ec681f3Smrgstw_framebuffer_get_size(struct stw_framebuffer *fb)
1267ec681f3Smrg{
1277ec681f3Smrg   LONG width, height;
1287ec681f3Smrg   RECT client_rect;
1297ec681f3Smrg   RECT window_rect;
1307ec681f3Smrg   POINT client_pos;
1317ec681f3Smrg
1327ec681f3Smrg   /*
1337ec681f3Smrg    * Sanity checking.
1347ec681f3Smrg    */
1357ec681f3Smrg   assert(fb->hWnd);
1367ec681f3Smrg   assert(fb->width && fb->height);
1377ec681f3Smrg   assert(fb->client_rect.right  == fb->client_rect.left + fb->width);
1387ec681f3Smrg   assert(fb->client_rect.bottom == fb->client_rect.top  + fb->height);
1397ec681f3Smrg
1407ec681f3Smrg   /*
1417ec681f3Smrg    * Get the client area size.
1427ec681f3Smrg    */
1437ec681f3Smrg   if (!GetClientRect(fb->hWnd, &client_rect)) {
1447ec681f3Smrg      return;
1457ec681f3Smrg   }
1467ec681f3Smrg
1477ec681f3Smrg   assert(client_rect.left == 0);
1487ec681f3Smrg   assert(client_rect.top == 0);
1497ec681f3Smrg   width  = client_rect.right  - client_rect.left;
1507ec681f3Smrg   height = client_rect.bottom - client_rect.top;
1517ec681f3Smrg
1527ec681f3Smrg   fb->minimized = width == 0 || height == 0;
1537ec681f3Smrg
1547ec681f3Smrg   if (width <= 0 || height <= 0) {
1557ec681f3Smrg      /*
1567ec681f3Smrg       * When the window is minimized GetClientRect will return zeros.  Simply
1577ec681f3Smrg       * preserve the current window size, until the window is restored or
1587ec681f3Smrg       * maximized again.
1597ec681f3Smrg       */
1607ec681f3Smrg      return;
1617ec681f3Smrg   }
1627ec681f3Smrg
1637ec681f3Smrg   if (width != fb->width || height != fb->height) {
1647ec681f3Smrg      fb->must_resize = TRUE;
1657ec681f3Smrg      fb->width = width;
1667ec681f3Smrg      fb->height = height;
1677ec681f3Smrg   }
1687ec681f3Smrg
1697ec681f3Smrg   client_pos.x = 0;
1707ec681f3Smrg   client_pos.y = 0;
1717ec681f3Smrg   if (ClientToScreen(fb->hWnd, &client_pos) &&
1727ec681f3Smrg       GetWindowRect(fb->hWnd, &window_rect)) {
1737ec681f3Smrg      fb->client_rect.left = client_pos.x - window_rect.left;
1747ec681f3Smrg      fb->client_rect.top  = client_pos.y - window_rect.top;
1757ec681f3Smrg   }
1767ec681f3Smrg
1777ec681f3Smrg   fb->client_rect.right  = fb->client_rect.left + fb->width;
1787ec681f3Smrg   fb->client_rect.bottom = fb->client_rect.top  + fb->height;
1797ec681f3Smrg
1807ec681f3Smrg#if 0
1817ec681f3Smrg   debug_printf("\n");
1827ec681f3Smrg   debug_printf("%s: hwnd = %p\n", __FUNCTION__, fb->hWnd);
1837ec681f3Smrg   debug_printf("%s: client_position = (%li, %li)\n",
1847ec681f3Smrg                __FUNCTION__, client_pos.x, client_pos.y);
1857ec681f3Smrg   debug_printf("%s: window_rect = (%li, %li) - (%li, %li)\n",
1867ec681f3Smrg                __FUNCTION__,
1877ec681f3Smrg                window_rect.left, window_rect.top,
1887ec681f3Smrg                window_rect.right, window_rect.bottom);
1897ec681f3Smrg   debug_printf("%s: client_rect = (%li, %li) - (%li, %li)\n",
1907ec681f3Smrg                __FUNCTION__,
1917ec681f3Smrg                fb->client_rect.left, fb->client_rect.top,
1927ec681f3Smrg                fb->client_rect.right, fb->client_rect.bottom);
1937ec681f3Smrg#endif
1947ec681f3Smrg}
1957ec681f3Smrg
1967ec681f3Smrg
1977ec681f3Smrg/**
1987ec681f3Smrg * @sa http://msdn.microsoft.com/en-us/library/ms644975(VS.85).aspx
1997ec681f3Smrg * @sa http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx
2007ec681f3Smrg */
2017ec681f3SmrgLRESULT CALLBACK
2027ec681f3Smrgstw_call_window_proc(int nCode, WPARAM wParam, LPARAM lParam)
2037ec681f3Smrg{
2047ec681f3Smrg   struct stw_tls_data *tls_data;
2057ec681f3Smrg   PCWPSTRUCT pParams = (PCWPSTRUCT)lParam;
2067ec681f3Smrg   struct stw_framebuffer *fb;
2077ec681f3Smrg
2087ec681f3Smrg   tls_data = stw_tls_get_data();
2097ec681f3Smrg   if (!tls_data)
2107ec681f3Smrg      return 0;
2117ec681f3Smrg
2127ec681f3Smrg   if (nCode < 0 || !stw_dev)
2137ec681f3Smrg       return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
2147ec681f3Smrg
2157ec681f3Smrg   /* We check that the stw_dev object is initialized before we try to do
2167ec681f3Smrg    * anything with it.  Otherwise, in multi-threaded programs there's a
2177ec681f3Smrg    * chance of executing this code before the stw_dev object is fully
2187ec681f3Smrg    * initialized.
2197ec681f3Smrg    */
2207ec681f3Smrg   if (stw_dev && stw_dev->initialized) {
2217ec681f3Smrg      if (pParams->message == WM_WINDOWPOSCHANGED) {
2227ec681f3Smrg         /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according
2237ec681f3Smrg          * to http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx
2247ec681f3Smrg          * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it
2257ec681f3Smrg          * can be masked out by the application.
2267ec681f3Smrg          */
2277ec681f3Smrg         LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam;
2287ec681f3Smrg         if ((lpWindowPos->flags & SWP_SHOWWINDOW) ||
2297ec681f3Smrg             !(lpWindowPos->flags & SWP_NOMOVE) ||
2307ec681f3Smrg             !(lpWindowPos->flags & SWP_NOSIZE)) {
2317ec681f3Smrg            fb = stw_framebuffer_from_hwnd( pParams->hwnd );
2327ec681f3Smrg            if (fb) {
2337ec681f3Smrg               /* Size in WINDOWPOS includes the window frame, so get the size
2347ec681f3Smrg                * of the client area via GetClientRect.
2357ec681f3Smrg                */
2367ec681f3Smrg               stw_framebuffer_get_size(fb);
2377ec681f3Smrg               stw_framebuffer_unlock(fb);
2387ec681f3Smrg            }
2397ec681f3Smrg         }
2407ec681f3Smrg      }
2417ec681f3Smrg      else if (pParams->message == WM_DESTROY) {
2427ec681f3Smrg         stw_lock_framebuffers(stw_dev);
2437ec681f3Smrg         fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd );
2447ec681f3Smrg         if (fb) {
2457ec681f3Smrg            struct stw_context *current_context = stw_current_context();
2467ec681f3Smrg            struct st_context_iface *ctx_iface = current_context &&
2477ec681f3Smrg               current_context->current_framebuffer == fb ? current_context->st : NULL;
2487ec681f3Smrg            stw_framebuffer_release_locked(fb, ctx_iface);
2497ec681f3Smrg         }
2507ec681f3Smrg         stw_unlock_framebuffers(stw_dev);
2517ec681f3Smrg      }
2527ec681f3Smrg   }
2537ec681f3Smrg
2547ec681f3Smrg   return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
2557ec681f3Smrg}
2567ec681f3Smrg
2577ec681f3Smrg
2587ec681f3Smrg/**
2597ec681f3Smrg * Create a new stw_framebuffer object which corresponds to the given
2607ec681f3Smrg * HDC/window.  If successful, we return the new stw_framebuffer object
2617ec681f3Smrg * with its mutex locked.
2627ec681f3Smrg */
2637ec681f3Smrgstruct stw_framebuffer *
2647ec681f3Smrgstw_framebuffer_create(HWND hWnd, int iPixelFormat, enum stw_framebuffer_owner owner)
2657ec681f3Smrg{
2667ec681f3Smrg   struct stw_framebuffer *fb;
2677ec681f3Smrg   const struct stw_pixelformat_info *pfi;
2687ec681f3Smrg
2697ec681f3Smrg   fb = CALLOC_STRUCT( stw_framebuffer );
2707ec681f3Smrg   if (fb == NULL)
2717ec681f3Smrg      return NULL;
2727ec681f3Smrg
2737ec681f3Smrg   fb->hWnd = hWnd;
2747ec681f3Smrg   fb->iPixelFormat = iPixelFormat;
2757ec681f3Smrg
2767ec681f3Smrg   if (stw_dev->stw_winsys->create_framebuffer)
2777ec681f3Smrg      fb->winsys_framebuffer =
2787ec681f3Smrg         stw_dev->stw_winsys->create_framebuffer(stw_dev->screen, hWnd, iPixelFormat);
2797ec681f3Smrg
2807ec681f3Smrg   /*
2817ec681f3Smrg    * We often need a displayable pixel format to make GDI happy. Set it
2827ec681f3Smrg    * here (always 1, i.e., out first pixel format) where appropriate.
2837ec681f3Smrg    */
2847ec681f3Smrg   fb->iDisplayablePixelFormat = iPixelFormat <= stw_dev->pixelformat_count
2857ec681f3Smrg      ? iPixelFormat : 1;
2867ec681f3Smrg   fb->owner = owner;
2877ec681f3Smrg
2887ec681f3Smrg   fb->pfi = pfi = stw_pixelformat_get_info( iPixelFormat );
2897ec681f3Smrg   fb->stfb = stw_st_create_framebuffer( fb );
2907ec681f3Smrg   if (!fb->stfb) {
2917ec681f3Smrg      FREE( fb );
2927ec681f3Smrg      return NULL;
2937ec681f3Smrg   }
2947ec681f3Smrg
2957ec681f3Smrg   fb->refcnt = 1;
2967ec681f3Smrg
2977ec681f3Smrg   /*
2987ec681f3Smrg    * Windows can be sometimes have zero width and or height, but we ensure
2997ec681f3Smrg    * a non-zero framebuffer size at all times.
3007ec681f3Smrg    */
3017ec681f3Smrg
3027ec681f3Smrg   fb->must_resize = TRUE;
3037ec681f3Smrg   fb->width  = 1;
3047ec681f3Smrg   fb->height = 1;
3057ec681f3Smrg   fb->client_rect.left   = 0;
3067ec681f3Smrg   fb->client_rect.top    = 0;
3077ec681f3Smrg   fb->client_rect.right  = fb->client_rect.left + fb->width;
3087ec681f3Smrg   fb->client_rect.bottom = fb->client_rect.top  + fb->height;
3097ec681f3Smrg
3107ec681f3Smrg   stw_framebuffer_get_size(fb);
3117ec681f3Smrg
3127ec681f3Smrg   InitializeCriticalSection(&fb->mutex);
3137ec681f3Smrg
3147ec681f3Smrg   /* This is the only case where we lock the stw_framebuffer::mutex before
3157ec681f3Smrg    * stw_dev::fb_mutex, since no other thread can know about this framebuffer
3167ec681f3Smrg    * and we must prevent any other thread from destroying it before we return.
3177ec681f3Smrg    */
3187ec681f3Smrg   stw_framebuffer_lock(fb);
3197ec681f3Smrg
3207ec681f3Smrg   if (owner != STW_FRAMEBUFFER_EGL_WINDOW) {
3217ec681f3Smrg      stw_lock_framebuffers(stw_dev);
3227ec681f3Smrg      fb->next = stw_dev->fb_head;
3237ec681f3Smrg      stw_dev->fb_head = fb;
3247ec681f3Smrg      stw_unlock_framebuffers(stw_dev);
3257ec681f3Smrg   }
3267ec681f3Smrg
3277ec681f3Smrg   return fb;
3287ec681f3Smrg}
3297ec681f3Smrg
3307ec681f3Smrg/**
3317ec681f3Smrg * Increase fb reference count.  The referenced framebuffer should be locked.
3327ec681f3Smrg *
3337ec681f3Smrg * It's not necessary to hold stw_dev::fb_mutex global lock.
3347ec681f3Smrg */
3357ec681f3Smrgvoid
3367ec681f3Smrgstw_framebuffer_reference_locked(struct stw_framebuffer *fb)
3377ec681f3Smrg{
3387ec681f3Smrg   if (fb) {
3397ec681f3Smrg      assert(stw_own_mutex(&fb->mutex));
3407ec681f3Smrg      fb->refcnt++;
3417ec681f3Smrg   }
3427ec681f3Smrg}
3437ec681f3Smrg
3447ec681f3Smrg/**
3457ec681f3Smrg * Release stw_framebuffer::mutex lock. This framebuffer must not be accessed
3467ec681f3Smrg * after calling this function, as it may have been deleted by another thread
3477ec681f3Smrg * in the meanwhile.
3487ec681f3Smrg */
3497ec681f3Smrgvoid
3507ec681f3Smrgstw_framebuffer_unlock(struct stw_framebuffer *fb)
3517ec681f3Smrg{
3527ec681f3Smrg   assert(fb);
3537ec681f3Smrg   assert(stw_own_mutex(&fb->mutex));
3547ec681f3Smrg   LeaveCriticalSection(&fb->mutex);
3557ec681f3Smrg}
3567ec681f3Smrg
3577ec681f3Smrg
3587ec681f3Smrg/**
3597ec681f3Smrg * Update the framebuffer's size if necessary.
3607ec681f3Smrg */
3617ec681f3Smrgvoid
3627ec681f3Smrgstw_framebuffer_update(struct stw_framebuffer *fb)
3637ec681f3Smrg{
3647ec681f3Smrg   assert(fb->stfb);
3657ec681f3Smrg   assert(fb->height);
3667ec681f3Smrg   assert(fb->width);
3677ec681f3Smrg
3687ec681f3Smrg   /* XXX: It would be nice to avoid checking the size again -- in theory
3697ec681f3Smrg    * stw_call_window_proc would have cought the resize and stored the right
3707ec681f3Smrg    * size already, but unfortunately threads created before the DllMain is
3717ec681f3Smrg    * called don't get a DLL_THREAD_ATTACH notification, and there is no way
3727ec681f3Smrg    * to know of their existing without using the not very portable PSAPI.
3737ec681f3Smrg    */
3747ec681f3Smrg   stw_framebuffer_get_size(fb);
3757ec681f3Smrg}
3767ec681f3Smrg
3777ec681f3Smrg
3787ec681f3Smrg/**
3797ec681f3Smrg * Try to free all stw_framebuffer objects associated with the device.
3807ec681f3Smrg */
3817ec681f3Smrgvoid
3827ec681f3Smrgstw_framebuffer_cleanup(void)
3837ec681f3Smrg{
3847ec681f3Smrg   struct stw_framebuffer *fb;
3857ec681f3Smrg   struct stw_framebuffer *next;
3867ec681f3Smrg
3877ec681f3Smrg   if (!stw_dev)
3887ec681f3Smrg      return;
3897ec681f3Smrg
3907ec681f3Smrg   stw_lock_framebuffers(stw_dev);
3917ec681f3Smrg
3927ec681f3Smrg   fb = stw_dev->fb_head;
3937ec681f3Smrg   while (fb) {
3947ec681f3Smrg      next = fb->next;
3957ec681f3Smrg
3967ec681f3Smrg      stw_framebuffer_lock(fb);
3977ec681f3Smrg      stw_framebuffer_release_locked(fb, NULL);
3987ec681f3Smrg
3997ec681f3Smrg      fb = next;
4007ec681f3Smrg   }
4017ec681f3Smrg   stw_dev->fb_head = NULL;
4027ec681f3Smrg
4037ec681f3Smrg   stw_unlock_framebuffers(stw_dev);
4047ec681f3Smrg}
4057ec681f3Smrg
4067ec681f3Smrg
4077ec681f3Smrg/**
4087ec681f3Smrg * Given an hdc, return the corresponding stw_framebuffer.
4097ec681f3Smrg * The returned stw_framebuffer will have its mutex locked.
4107ec681f3Smrg */
4117ec681f3Smrgstatic struct stw_framebuffer *
4127ec681f3Smrgstw_framebuffer_from_hdc_locked(HDC hdc)
4137ec681f3Smrg{
4147ec681f3Smrg   HWND hwnd;
4157ec681f3Smrg
4167ec681f3Smrg   hwnd = WindowFromDC(hdc);
4177ec681f3Smrg   if (!hwnd) {
4187ec681f3Smrg      return NULL;
4197ec681f3Smrg   }
4207ec681f3Smrg
4217ec681f3Smrg   return stw_framebuffer_from_hwnd_locked(hwnd);
4227ec681f3Smrg}
4237ec681f3Smrg
4247ec681f3Smrg
4257ec681f3Smrg/**
4267ec681f3Smrg * Given an HDC, return the corresponding stw_framebuffer.
4277ec681f3Smrg * The returned stw_framebuffer will have its mutex locked.
4287ec681f3Smrg */
4297ec681f3Smrgstruct stw_framebuffer *
4307ec681f3Smrgstw_framebuffer_from_hdc(HDC hdc)
4317ec681f3Smrg{
4327ec681f3Smrg   struct stw_framebuffer *fb;
4337ec681f3Smrg
4347ec681f3Smrg   if (!stw_dev)
4357ec681f3Smrg      return NULL;
4367ec681f3Smrg
4377ec681f3Smrg   stw_lock_framebuffers(stw_dev);
4387ec681f3Smrg   fb = stw_framebuffer_from_hdc_locked(hdc);
4397ec681f3Smrg   stw_unlock_framebuffers(stw_dev);
4407ec681f3Smrg
4417ec681f3Smrg   return fb;
4427ec681f3Smrg}
4437ec681f3Smrg
4447ec681f3Smrg
4457ec681f3Smrg/**
4467ec681f3Smrg * Given an HWND, return the corresponding stw_framebuffer.
4477ec681f3Smrg * The returned stw_framebuffer will have its mutex locked.
4487ec681f3Smrg */
4497ec681f3Smrgstruct stw_framebuffer *
4507ec681f3Smrgstw_framebuffer_from_hwnd(HWND hwnd)
4517ec681f3Smrg{
4527ec681f3Smrg   struct stw_framebuffer *fb;
4537ec681f3Smrg
4547ec681f3Smrg   stw_lock_framebuffers(stw_dev);
4557ec681f3Smrg   fb = stw_framebuffer_from_hwnd_locked(hwnd);
4567ec681f3Smrg   stw_unlock_framebuffers(stw_dev);
4577ec681f3Smrg
4587ec681f3Smrg   return fb;
4597ec681f3Smrg}
4607ec681f3Smrg
4617ec681f3Smrg
4627ec681f3SmrgBOOL APIENTRY
4637ec681f3SmrgDrvSetPixelFormat(HDC hdc, LONG iPixelFormat)
4647ec681f3Smrg{
4657ec681f3Smrg   uint count;
4667ec681f3Smrg   uint index;
4677ec681f3Smrg   struct stw_framebuffer *fb;
4687ec681f3Smrg
4697ec681f3Smrg   if (!stw_dev)
4707ec681f3Smrg      return FALSE;
4717ec681f3Smrg
4727ec681f3Smrg   index = (uint) iPixelFormat - 1;
4737ec681f3Smrg   count = stw_pixelformat_get_count(hdc);
4747ec681f3Smrg   if (index >= count)
4757ec681f3Smrg      return FALSE;
4767ec681f3Smrg
4777ec681f3Smrg   fb = stw_framebuffer_from_hdc_locked(hdc);
4787ec681f3Smrg   if (fb) {
4797ec681f3Smrg      /*
4807ec681f3Smrg       * SetPixelFormat must be called only once.  However ignore
4817ec681f3Smrg       * pbuffers, for which the framebuffer object is created first.
4827ec681f3Smrg       */
4837ec681f3Smrg      boolean bPbuffer = fb->owner == STW_FRAMEBUFFER_PBUFFER;
4847ec681f3Smrg
4857ec681f3Smrg      stw_framebuffer_unlock( fb );
4867ec681f3Smrg
4877ec681f3Smrg      return bPbuffer;
4887ec681f3Smrg   }
4897ec681f3Smrg
4907ec681f3Smrg   fb = stw_framebuffer_create(WindowFromDC(hdc), iPixelFormat, STW_FRAMEBUFFER_WGL_WINDOW);
4917ec681f3Smrg   if (!fb) {
4927ec681f3Smrg      return FALSE;
4937ec681f3Smrg   }
4947ec681f3Smrg
4957ec681f3Smrg   stw_framebuffer_unlock( fb );
4967ec681f3Smrg
4977ec681f3Smrg   /* Some applications mistakenly use the undocumented wglSetPixelFormat
4987ec681f3Smrg    * function instead of SetPixelFormat, so we call SetPixelFormat here to
4997ec681f3Smrg    * avoid opengl32.dll's wglCreateContext to fail */
5007ec681f3Smrg   if (GetPixelFormat(hdc) == 0) {
5017ec681f3Smrg      BOOL bRet = SetPixelFormat(hdc, iPixelFormat, NULL);
5027ec681f3Smrg      if (!bRet) {
5037ec681f3Smrg	  debug_printf("SetPixelFormat failed\n");
5047ec681f3Smrg      }
5057ec681f3Smrg   }
5067ec681f3Smrg
5077ec681f3Smrg   return TRUE;
5087ec681f3Smrg}
5097ec681f3Smrg
5107ec681f3Smrg
5117ec681f3Smrgint
5127ec681f3Smrgstw_pixelformat_get(HDC hdc)
5137ec681f3Smrg{
5147ec681f3Smrg   int iPixelFormat = 0;
5157ec681f3Smrg   struct stw_framebuffer *fb;
5167ec681f3Smrg
5177ec681f3Smrg   fb = stw_framebuffer_from_hdc(hdc);
5187ec681f3Smrg   if (fb) {
5197ec681f3Smrg      iPixelFormat = fb->iPixelFormat;
5207ec681f3Smrg      stw_framebuffer_unlock(fb);
5217ec681f3Smrg   }
5227ec681f3Smrg
5237ec681f3Smrg   return iPixelFormat;
5247ec681f3Smrg}
5257ec681f3Smrg
5267ec681f3Smrg
5277ec681f3SmrgBOOL APIENTRY
5287ec681f3SmrgDrvPresentBuffers(HDC hdc, LPPRESENTBUFFERS data)
5297ec681f3Smrg{
5307ec681f3Smrg   struct stw_framebuffer *fb;
5317ec681f3Smrg   struct stw_context *ctx;
5327ec681f3Smrg   struct pipe_screen *screen;
5337ec681f3Smrg   struct pipe_context *pipe;
5347ec681f3Smrg   struct pipe_resource *res;
5357ec681f3Smrg
5367ec681f3Smrg   if (!stw_dev)
5377ec681f3Smrg      return FALSE;
5387ec681f3Smrg
5397ec681f3Smrg   fb = stw_framebuffer_from_hdc( hdc );
5407ec681f3Smrg   if (fb == NULL)
5417ec681f3Smrg      return FALSE;
5427ec681f3Smrg
5437ec681f3Smrg   screen = stw_dev->screen;
5447ec681f3Smrg   ctx = stw_current_context();
5457ec681f3Smrg   pipe = ctx ? ctx->st->pipe : NULL;
5467ec681f3Smrg
5477ec681f3Smrg   res = (struct pipe_resource *)data->pPrivData;
5487ec681f3Smrg
5497ec681f3Smrg   if (data->hSurface != fb->hSharedSurface) {
5507ec681f3Smrg      if (fb->shared_surface) {
5517ec681f3Smrg         stw_dev->stw_winsys->shared_surface_close(screen, fb->shared_surface);
5527ec681f3Smrg         fb->shared_surface = NULL;
5537ec681f3Smrg      }
5547ec681f3Smrg
5557ec681f3Smrg      fb->hSharedSurface = data->hSurface;
5567ec681f3Smrg
5577ec681f3Smrg      if (data->hSurface &&
5587ec681f3Smrg         stw_dev->stw_winsys->shared_surface_open) {
5597ec681f3Smrg         fb->shared_surface =
5607ec681f3Smrg            stw_dev->stw_winsys->shared_surface_open(screen,
5617ec681f3Smrg                                                     fb->hSharedSurface);
5627ec681f3Smrg      }
5637ec681f3Smrg   }
5647ec681f3Smrg
5657ec681f3Smrg   if (!fb->minimized) {
5667ec681f3Smrg      if (fb->shared_surface) {
5677ec681f3Smrg         stw_dev->stw_winsys->compose(screen,
5687ec681f3Smrg                                      res,
5697ec681f3Smrg                                      fb->shared_surface,
5707ec681f3Smrg                                      &fb->client_rect,
5717ec681f3Smrg                                      data->ullPresentToken);
5727ec681f3Smrg      }
5737ec681f3Smrg      else {
5747ec681f3Smrg         stw_dev->stw_winsys->present( screen, pipe, res, hdc );
5757ec681f3Smrg      }
5767ec681f3Smrg   }
5777ec681f3Smrg
5787ec681f3Smrg   stw_framebuffer_update(fb);
5797ec681f3Smrg   stw_notify_current_locked(fb);
5807ec681f3Smrg
5817ec681f3Smrg   stw_framebuffer_unlock(fb);
5827ec681f3Smrg
5837ec681f3Smrg   return TRUE;
5847ec681f3Smrg}
5857ec681f3Smrg
5867ec681f3Smrg
5877ec681f3Smrg/**
5887ec681f3Smrg * Queue a composition.
5897ec681f3Smrg *
5907ec681f3Smrg * The stw_framebuffer object must have its mutex locked.  The mutex will
5917ec681f3Smrg * be unlocked here before returning.
5927ec681f3Smrg */
5937ec681f3SmrgBOOL
5947ec681f3Smrgstw_framebuffer_present_locked(HDC hdc,
5957ec681f3Smrg                               struct stw_framebuffer *fb,
5967ec681f3Smrg                               struct pipe_resource *res)
5977ec681f3Smrg{
5987ec681f3Smrg   if (fb->winsys_framebuffer) {
5997ec681f3Smrg      BOOL result = fb->winsys_framebuffer->present(fb->winsys_framebuffer);
6007ec681f3Smrg
6017ec681f3Smrg      stw_framebuffer_update(fb);
6027ec681f3Smrg      stw_notify_current_locked(fb);
6037ec681f3Smrg      stw_framebuffer_unlock(fb);
6047ec681f3Smrg
6057ec681f3Smrg      return result;
6067ec681f3Smrg   }
6077ec681f3Smrg   else if (stw_dev->callbacks.pfnPresentBuffers &&
6087ec681f3Smrg            stw_dev->stw_winsys->compose) {
6097ec681f3Smrg      PRESENTBUFFERSCB data;
6107ec681f3Smrg
6117ec681f3Smrg      memset(&data, 0, sizeof data);
6127ec681f3Smrg      data.nVersion = 2;
6137ec681f3Smrg      data.syncType = PRESCB_SYNCTYPE_NONE;
6147ec681f3Smrg      data.luidAdapter = stw_dev->AdapterLuid;
6157ec681f3Smrg      data.updateRect = fb->client_rect;
6167ec681f3Smrg      data.pPrivData = (void *)res;
6177ec681f3Smrg
6187ec681f3Smrg      stw_notify_current_locked(fb);
6197ec681f3Smrg      stw_framebuffer_unlock(fb);
6207ec681f3Smrg
6217ec681f3Smrg      return stw_dev->callbacks.pfnPresentBuffers(hdc, &data);
6227ec681f3Smrg   }
6237ec681f3Smrg   else {
6247ec681f3Smrg      struct pipe_screen *screen = stw_dev->screen;
6257ec681f3Smrg      struct stw_context *ctx = stw_current_context();
6267ec681f3Smrg      struct pipe_context *pipe = ctx ? ctx->st->pipe : NULL;
6277ec681f3Smrg
6287ec681f3Smrg      stw_dev->stw_winsys->present( screen, pipe, res, hdc );
6297ec681f3Smrg
6307ec681f3Smrg      stw_framebuffer_update(fb);
6317ec681f3Smrg      stw_notify_current_locked(fb);
6327ec681f3Smrg      stw_framebuffer_unlock(fb);
6337ec681f3Smrg
6347ec681f3Smrg      return TRUE;
6357ec681f3Smrg   }
6367ec681f3Smrg}
6377ec681f3Smrg
6387ec681f3Smrg
6397ec681f3Smrg/**
6407ec681f3Smrg * This is called just before issuing the buffer swap/present.
6417ec681f3Smrg * We query the current time and determine if we should sleep before
6427ec681f3Smrg * issuing the swap/present.
6437ec681f3Smrg * This is a bit of a hack and is certainly not very accurate but it
6447ec681f3Smrg * basically works.
6457ec681f3Smrg * This is for the WGL_ARB_swap_interval extension.
6467ec681f3Smrg */
6477ec681f3Smrgstatic void
6487ec681f3Smrgwait_swap_interval(struct stw_framebuffer *fb)
6497ec681f3Smrg{
6507ec681f3Smrg   /* Note: all time variables here are in units of microseconds */
6517ec681f3Smrg   int64_t cur_time = os_time_get_nano() / 1000;
6527ec681f3Smrg
6537ec681f3Smrg   if (fb->prev_swap_time != 0) {
6547ec681f3Smrg      /* Compute time since previous swap */
6557ec681f3Smrg      int64_t delta = cur_time - fb->prev_swap_time;
6567ec681f3Smrg      int64_t min_swap_period =
6577ec681f3Smrg         1.0e6 / stw_dev->refresh_rate * stw_dev->swap_interval;
6587ec681f3Smrg
6597ec681f3Smrg      /* If time since last swap is less than wait period, wait.
6607ec681f3Smrg       * Note that it's possible for the delta to be negative because of
6617ec681f3Smrg       * rollover.  See https://bugs.freedesktop.org/show_bug.cgi?id=102241
6627ec681f3Smrg       */
6637ec681f3Smrg      if ((delta >= 0) && (delta < min_swap_period)) {
6647ec681f3Smrg         float fudge = 1.75f;  /* emperical fudge factor */
6657ec681f3Smrg         int64_t wait = (min_swap_period - delta) * fudge;
6667ec681f3Smrg         os_time_sleep(wait);
6677ec681f3Smrg      }
6687ec681f3Smrg   }
6697ec681f3Smrg
6707ec681f3Smrg   fb->prev_swap_time = cur_time;
6717ec681f3Smrg}
6727ec681f3Smrg
6737ec681f3SmrgBOOL
6747ec681f3Smrgstw_framebuffer_swap_locked(HDC hdc, struct stw_framebuffer *fb)
6757ec681f3Smrg{
6767ec681f3Smrg   struct stw_context *ctx;
6777ec681f3Smrg   if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) {
6787ec681f3Smrg      stw_framebuffer_unlock(fb);
6797ec681f3Smrg      return TRUE;
6807ec681f3Smrg   }
6817ec681f3Smrg
6827ec681f3Smrg   ctx = stw_current_context();
6837ec681f3Smrg   if (ctx) {
6847ec681f3Smrg      if (ctx->hud) {
6857ec681f3Smrg         /* Display the HUD */
6867ec681f3Smrg         struct pipe_resource *back =
6877ec681f3Smrg            stw_get_framebuffer_resource(fb->stfb, ST_ATTACHMENT_BACK_LEFT);
6887ec681f3Smrg         if (back) {
6897ec681f3Smrg            hud_run(ctx->hud, NULL, back);
6907ec681f3Smrg         }
6917ec681f3Smrg      }
6927ec681f3Smrg
6937ec681f3Smrg      if (ctx->current_framebuffer == fb) {
6947ec681f3Smrg         /* flush current context */
6957ec681f3Smrg         stw_st_flush(ctx->st, fb->stfb, ST_FLUSH_END_OF_FRAME);
6967ec681f3Smrg      }
6977ec681f3Smrg   }
6987ec681f3Smrg
6997ec681f3Smrg   if (stw_dev->swap_interval != 0 && !fb->winsys_framebuffer) {
7007ec681f3Smrg      wait_swap_interval(fb);
7017ec681f3Smrg   }
7027ec681f3Smrg
7037ec681f3Smrg   return stw_st_swap_framebuffer_locked(hdc, ctx->st, fb->stfb);
7047ec681f3Smrg}
7057ec681f3Smrg
7067ec681f3SmrgBOOL APIENTRY
7077ec681f3SmrgDrvSwapBuffers(HDC hdc)
7087ec681f3Smrg{
7097ec681f3Smrg   struct stw_framebuffer *fb;
7107ec681f3Smrg
7117ec681f3Smrg   if (!stw_dev)
7127ec681f3Smrg      return FALSE;
7137ec681f3Smrg
7147ec681f3Smrg   fb = stw_framebuffer_from_hdc( hdc );
7157ec681f3Smrg   if (fb == NULL)
7167ec681f3Smrg      return FALSE;
7177ec681f3Smrg
7187ec681f3Smrg   return stw_framebuffer_swap_locked(hdc, fb);
7197ec681f3Smrg}
7207ec681f3Smrg
7217ec681f3Smrg
7227ec681f3SmrgBOOL APIENTRY
7237ec681f3SmrgDrvSwapLayerBuffers(HDC hdc, UINT fuPlanes)
7247ec681f3Smrg{
7257ec681f3Smrg   if (fuPlanes & WGL_SWAP_MAIN_PLANE)
7267ec681f3Smrg      return DrvSwapBuffers(hdc);
7277ec681f3Smrg
7287ec681f3Smrg   return FALSE;
7297ec681f3Smrg}
730