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 "pipe/p_state.h"
32 #include "util/u_memory.h"
33 #include "hud/hud_context.h"
34 #include "util/os_time.h"
35 #include "frontend/api.h"
36
37 #include <GL/gl.h>
38 #include "gldrv.h"
39 #include "stw_framebuffer.h"
40 #include "stw_device.h"
41 #include "stw_winsys.h"
42 #include "stw_tls.h"
43 #include "stw_context.h"
44 #include "stw_st.h"
45
46
47 /**
48 * Search the framebuffer with the matching HWND while holding the
49 * stw_dev::fb_mutex global lock.
50 * If a stw_framebuffer is found, lock it and return the pointer.
51 * Else, return NULL.
52 */
53 static struct stw_framebuffer *
stw_framebuffer_from_hwnd_locked(HWND hwnd)54 stw_framebuffer_from_hwnd_locked(HWND hwnd)
55 {
56 struct stw_framebuffer *fb;
57
58 for (fb = stw_dev->fb_head; fb != NULL; fb = fb->next)
59 if (fb->hWnd == hwnd) {
60 stw_framebuffer_lock(fb);
61 assert(fb->mutex.RecursionCount == 1);
62 return fb;
63 }
64
65 return NULL;
66 }
67
68
69 /**
70 * Decrement the reference count on the given stw_framebuffer object.
71 * If the reference count hits zero, destroy the object.
72 *
73 * Note: Both stw_dev::fb_mutex and stw_framebuffer::mutex must already be
74 * locked. After this function completes, the fb's mutex will be unlocked.
75 */
76 void
stw_framebuffer_release_locked(struct stw_framebuffer * fb,struct st_context_iface * stctx)77 stw_framebuffer_release_locked(struct stw_framebuffer *fb,
78 struct st_context_iface *stctx)
79 {
80 struct stw_framebuffer **link;
81
82 assert(fb);
83 assert(stw_own_mutex(&fb->mutex));
84 assert(stw_own_mutex(&stw_dev->fb_mutex) || fb->owner == STW_FRAMEBUFFER_EGL_WINDOW);
85
86 /* check the reference count */
87 fb->refcnt--;
88 if (fb->refcnt) {
89 stw_framebuffer_unlock(fb);
90 return;
91 }
92
93 if (fb->owner != STW_FRAMEBUFFER_EGL_WINDOW) {
94 /* remove this stw_framebuffer from the device's linked list */
95 link = &stw_dev->fb_head;
96 while (*link != fb)
97 link = &(*link)->next;
98 assert(*link);
99 *link = fb->next;
100 fb->next = NULL;
101 }
102
103 if (fb->shared_surface)
104 stw_dev->stw_winsys->shared_surface_close(stw_dev->screen,
105 fb->shared_surface);
106
107 if (fb->winsys_framebuffer)
108 fb->winsys_framebuffer->destroy(fb->winsys_framebuffer, stctx ? stctx->pipe : NULL);
109
110 stw_st_destroy_framebuffer_locked(fb->stfb);
111
112 stw_framebuffer_unlock(fb);
113
114 DeleteCriticalSection(&fb->mutex);
115
116 FREE( fb );
117 }
118
119
120 /**
121 * Query the size of the given framebuffer's on-screen window and update
122 * the stw_framebuffer's width/height.
123 */
124 static void
stw_framebuffer_get_size(struct stw_framebuffer * fb)125 stw_framebuffer_get_size(struct stw_framebuffer *fb)
126 {
127 LONG width, height;
128 RECT client_rect;
129 RECT window_rect;
130 POINT client_pos;
131
132 /*
133 * Sanity checking.
134 */
135 assert(fb->hWnd);
136 assert(fb->width && fb->height);
137 assert(fb->client_rect.right == fb->client_rect.left + fb->width);
138 assert(fb->client_rect.bottom == fb->client_rect.top + fb->height);
139
140 /*
141 * Get the client area size.
142 */
143 if (!GetClientRect(fb->hWnd, &client_rect)) {
144 return;
145 }
146
147 assert(client_rect.left == 0);
148 assert(client_rect.top == 0);
149 width = client_rect.right - client_rect.left;
150 height = client_rect.bottom - client_rect.top;
151
152 fb->minimized = width == 0 || height == 0;
153
154 if (width <= 0 || height <= 0) {
155 /*
156 * When the window is minimized GetClientRect will return zeros. Simply
157 * preserve the current window size, until the window is restored or
158 * maximized again.
159 */
160 return;
161 }
162
163 if (width != fb->width || height != fb->height) {
164 fb->must_resize = TRUE;
165 fb->width = width;
166 fb->height = height;
167 }
168
169 client_pos.x = 0;
170 client_pos.y = 0;
171 if (ClientToScreen(fb->hWnd, &client_pos) &&
172 GetWindowRect(fb->hWnd, &window_rect)) {
173 fb->client_rect.left = client_pos.x - window_rect.left;
174 fb->client_rect.top = client_pos.y - window_rect.top;
175 }
176
177 fb->client_rect.right = fb->client_rect.left + fb->width;
178 fb->client_rect.bottom = fb->client_rect.top + fb->height;
179
180 #if 0
181 debug_printf("\n");
182 debug_printf("%s: hwnd = %p\n", __FUNCTION__, fb->hWnd);
183 debug_printf("%s: client_position = (%li, %li)\n",
184 __FUNCTION__, client_pos.x, client_pos.y);
185 debug_printf("%s: window_rect = (%li, %li) - (%li, %li)\n",
186 __FUNCTION__,
187 window_rect.left, window_rect.top,
188 window_rect.right, window_rect.bottom);
189 debug_printf("%s: client_rect = (%li, %li) - (%li, %li)\n",
190 __FUNCTION__,
191 fb->client_rect.left, fb->client_rect.top,
192 fb->client_rect.right, fb->client_rect.bottom);
193 #endif
194 }
195
196
197 /**
198 * @sa http://msdn.microsoft.com/en-us/library/ms644975(VS.85).aspx
199 * @sa http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx
200 */
201 LRESULT CALLBACK
stw_call_window_proc(int nCode,WPARAM wParam,LPARAM lParam)202 stw_call_window_proc(int nCode, WPARAM wParam, LPARAM lParam)
203 {
204 struct stw_tls_data *tls_data;
205 PCWPSTRUCT pParams = (PCWPSTRUCT)lParam;
206 struct stw_framebuffer *fb;
207
208 tls_data = stw_tls_get_data();
209 if (!tls_data)
210 return 0;
211
212 if (nCode < 0 || !stw_dev)
213 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
214
215 /* We check that the stw_dev object is initialized before we try to do
216 * anything with it. Otherwise, in multi-threaded programs there's a
217 * chance of executing this code before the stw_dev object is fully
218 * initialized.
219 */
220 if (stw_dev && stw_dev->initialized) {
221 if (pParams->message == WM_WINDOWPOSCHANGED) {
222 /* We handle WM_WINDOWPOSCHANGED instead of WM_SIZE because according
223 * to http://blogs.msdn.com/oldnewthing/archive/2008/01/15/7113860.aspx
224 * WM_SIZE is generated from WM_WINDOWPOSCHANGED by DefWindowProc so it
225 * can be masked out by the application.
226 */
227 LPWINDOWPOS lpWindowPos = (LPWINDOWPOS)pParams->lParam;
228 if ((lpWindowPos->flags & SWP_SHOWWINDOW) ||
229 !(lpWindowPos->flags & SWP_NOMOVE) ||
230 !(lpWindowPos->flags & SWP_NOSIZE)) {
231 fb = stw_framebuffer_from_hwnd( pParams->hwnd );
232 if (fb) {
233 /* Size in WINDOWPOS includes the window frame, so get the size
234 * of the client area via GetClientRect.
235 */
236 stw_framebuffer_get_size(fb);
237 stw_framebuffer_unlock(fb);
238 }
239 }
240 }
241 else if (pParams->message == WM_DESTROY) {
242 stw_lock_framebuffers(stw_dev);
243 fb = stw_framebuffer_from_hwnd_locked( pParams->hwnd );
244 if (fb) {
245 struct stw_context *current_context = stw_current_context();
246 struct st_context_iface *ctx_iface = current_context &&
247 current_context->current_framebuffer == fb ? current_context->st : NULL;
248 stw_framebuffer_release_locked(fb, ctx_iface);
249 }
250 stw_unlock_framebuffers(stw_dev);
251 }
252 }
253
254 return CallNextHookEx(tls_data->hCallWndProcHook, nCode, wParam, lParam);
255 }
256
257
258 /**
259 * Create a new stw_framebuffer object which corresponds to the given
260 * HDC/window. If successful, we return the new stw_framebuffer object
261 * with its mutex locked.
262 */
263 struct stw_framebuffer *
stw_framebuffer_create(HWND hWnd,int iPixelFormat,enum stw_framebuffer_owner owner)264 stw_framebuffer_create(HWND hWnd, int iPixelFormat, enum stw_framebuffer_owner owner)
265 {
266 struct stw_framebuffer *fb;
267 const struct stw_pixelformat_info *pfi;
268
269 fb = CALLOC_STRUCT( stw_framebuffer );
270 if (fb == NULL)
271 return NULL;
272
273 fb->hWnd = hWnd;
274 fb->iPixelFormat = iPixelFormat;
275
276 if (stw_dev->stw_winsys->create_framebuffer)
277 fb->winsys_framebuffer =
278 stw_dev->stw_winsys->create_framebuffer(stw_dev->screen, hWnd, iPixelFormat);
279
280 /*
281 * We often need a displayable pixel format to make GDI happy. Set it
282 * here (always 1, i.e., out first pixel format) where appropriate.
283 */
284 fb->iDisplayablePixelFormat = iPixelFormat <= stw_dev->pixelformat_count
285 ? iPixelFormat : 1;
286 fb->owner = owner;
287
288 fb->pfi = pfi = stw_pixelformat_get_info( iPixelFormat );
289 fb->stfb = stw_st_create_framebuffer( fb );
290 if (!fb->stfb) {
291 FREE( fb );
292 return NULL;
293 }
294
295 fb->refcnt = 1;
296
297 /*
298 * Windows can be sometimes have zero width and or height, but we ensure
299 * a non-zero framebuffer size at all times.
300 */
301
302 fb->must_resize = TRUE;
303 fb->width = 1;
304 fb->height = 1;
305 fb->client_rect.left = 0;
306 fb->client_rect.top = 0;
307 fb->client_rect.right = fb->client_rect.left + fb->width;
308 fb->client_rect.bottom = fb->client_rect.top + fb->height;
309
310 stw_framebuffer_get_size(fb);
311
312 InitializeCriticalSection(&fb->mutex);
313
314 /* This is the only case where we lock the stw_framebuffer::mutex before
315 * stw_dev::fb_mutex, since no other thread can know about this framebuffer
316 * and we must prevent any other thread from destroying it before we return.
317 */
318 stw_framebuffer_lock(fb);
319
320 if (owner != STW_FRAMEBUFFER_EGL_WINDOW) {
321 stw_lock_framebuffers(stw_dev);
322 fb->next = stw_dev->fb_head;
323 stw_dev->fb_head = fb;
324 stw_unlock_framebuffers(stw_dev);
325 }
326
327 return fb;
328 }
329
330 /**
331 * Increase fb reference count. The referenced framebuffer should be locked.
332 *
333 * It's not necessary to hold stw_dev::fb_mutex global lock.
334 */
335 void
stw_framebuffer_reference_locked(struct stw_framebuffer * fb)336 stw_framebuffer_reference_locked(struct stw_framebuffer *fb)
337 {
338 if (fb) {
339 assert(stw_own_mutex(&fb->mutex));
340 fb->refcnt++;
341 }
342 }
343
344 /**
345 * Release stw_framebuffer::mutex lock. This framebuffer must not be accessed
346 * after calling this function, as it may have been deleted by another thread
347 * in the meanwhile.
348 */
349 void
stw_framebuffer_unlock(struct stw_framebuffer * fb)350 stw_framebuffer_unlock(struct stw_framebuffer *fb)
351 {
352 assert(fb);
353 assert(stw_own_mutex(&fb->mutex));
354 LeaveCriticalSection(&fb->mutex);
355 }
356
357
358 /**
359 * Update the framebuffer's size if necessary.
360 */
361 void
stw_framebuffer_update(struct stw_framebuffer * fb)362 stw_framebuffer_update(struct stw_framebuffer *fb)
363 {
364 assert(fb->stfb);
365 assert(fb->height);
366 assert(fb->width);
367
368 /* XXX: It would be nice to avoid checking the size again -- in theory
369 * stw_call_window_proc would have cought the resize and stored the right
370 * size already, but unfortunately threads created before the DllMain is
371 * called don't get a DLL_THREAD_ATTACH notification, and there is no way
372 * to know of their existing without using the not very portable PSAPI.
373 */
374 stw_framebuffer_get_size(fb);
375 }
376
377
378 /**
379 * Try to free all stw_framebuffer objects associated with the device.
380 */
381 void
stw_framebuffer_cleanup(void)382 stw_framebuffer_cleanup(void)
383 {
384 struct stw_framebuffer *fb;
385 struct stw_framebuffer *next;
386
387 if (!stw_dev)
388 return;
389
390 stw_lock_framebuffers(stw_dev);
391
392 fb = stw_dev->fb_head;
393 while (fb) {
394 next = fb->next;
395
396 stw_framebuffer_lock(fb);
397 stw_framebuffer_release_locked(fb, NULL);
398
399 fb = next;
400 }
401 stw_dev->fb_head = NULL;
402
403 stw_unlock_framebuffers(stw_dev);
404 }
405
406
407 /**
408 * Given an hdc, return the corresponding stw_framebuffer.
409 * The returned stw_framebuffer will have its mutex locked.
410 */
411 static struct stw_framebuffer *
stw_framebuffer_from_hdc_locked(HDC hdc)412 stw_framebuffer_from_hdc_locked(HDC hdc)
413 {
414 HWND hwnd;
415
416 hwnd = WindowFromDC(hdc);
417 if (!hwnd) {
418 return NULL;
419 }
420
421 return stw_framebuffer_from_hwnd_locked(hwnd);
422 }
423
424
425 /**
426 * Given an HDC, return the corresponding stw_framebuffer.
427 * The returned stw_framebuffer will have its mutex locked.
428 */
429 struct stw_framebuffer *
stw_framebuffer_from_hdc(HDC hdc)430 stw_framebuffer_from_hdc(HDC hdc)
431 {
432 struct stw_framebuffer *fb;
433
434 if (!stw_dev)
435 return NULL;
436
437 stw_lock_framebuffers(stw_dev);
438 fb = stw_framebuffer_from_hdc_locked(hdc);
439 stw_unlock_framebuffers(stw_dev);
440
441 return fb;
442 }
443
444
445 /**
446 * Given an HWND, return the corresponding stw_framebuffer.
447 * The returned stw_framebuffer will have its mutex locked.
448 */
449 struct stw_framebuffer *
stw_framebuffer_from_hwnd(HWND hwnd)450 stw_framebuffer_from_hwnd(HWND hwnd)
451 {
452 struct stw_framebuffer *fb;
453
454 stw_lock_framebuffers(stw_dev);
455 fb = stw_framebuffer_from_hwnd_locked(hwnd);
456 stw_unlock_framebuffers(stw_dev);
457
458 return fb;
459 }
460
461
462 BOOL APIENTRY
DrvSetPixelFormat(HDC hdc,LONG iPixelFormat)463 DrvSetPixelFormat(HDC hdc, LONG iPixelFormat)
464 {
465 uint count;
466 uint index;
467 struct stw_framebuffer *fb;
468
469 if (!stw_dev)
470 return FALSE;
471
472 index = (uint) iPixelFormat - 1;
473 count = stw_pixelformat_get_count(hdc);
474 if (index >= count)
475 return FALSE;
476
477 fb = stw_framebuffer_from_hdc_locked(hdc);
478 if (fb) {
479 /*
480 * SetPixelFormat must be called only once. However ignore
481 * pbuffers, for which the framebuffer object is created first.
482 */
483 boolean bPbuffer = fb->owner == STW_FRAMEBUFFER_PBUFFER;
484
485 stw_framebuffer_unlock( fb );
486
487 return bPbuffer;
488 }
489
490 fb = stw_framebuffer_create(WindowFromDC(hdc), iPixelFormat, STW_FRAMEBUFFER_WGL_WINDOW);
491 if (!fb) {
492 return FALSE;
493 }
494
495 stw_framebuffer_unlock( fb );
496
497 /* Some applications mistakenly use the undocumented wglSetPixelFormat
498 * function instead of SetPixelFormat, so we call SetPixelFormat here to
499 * avoid opengl32.dll's wglCreateContext to fail */
500 if (GetPixelFormat(hdc) == 0) {
501 BOOL bRet = SetPixelFormat(hdc, iPixelFormat, NULL);
502 if (!bRet) {
503 debug_printf("SetPixelFormat failed\n");
504 }
505 }
506
507 return TRUE;
508 }
509
510
511 int
stw_pixelformat_get(HDC hdc)512 stw_pixelformat_get(HDC hdc)
513 {
514 int iPixelFormat = 0;
515 struct stw_framebuffer *fb;
516
517 fb = stw_framebuffer_from_hdc(hdc);
518 if (fb) {
519 iPixelFormat = fb->iPixelFormat;
520 stw_framebuffer_unlock(fb);
521 }
522
523 return iPixelFormat;
524 }
525
526
527 BOOL APIENTRY
DrvPresentBuffers(HDC hdc,LPPRESENTBUFFERS data)528 DrvPresentBuffers(HDC hdc, LPPRESENTBUFFERS data)
529 {
530 struct stw_framebuffer *fb;
531 struct stw_context *ctx;
532 struct pipe_screen *screen;
533 struct pipe_context *pipe;
534 struct pipe_resource *res;
535
536 if (!stw_dev)
537 return FALSE;
538
539 fb = stw_framebuffer_from_hdc( hdc );
540 if (fb == NULL)
541 return FALSE;
542
543 screen = stw_dev->screen;
544 ctx = stw_current_context();
545 pipe = ctx ? ctx->st->pipe : NULL;
546
547 res = (struct pipe_resource *)data->pPrivData;
548
549 if (data->hSurface != fb->hSharedSurface) {
550 if (fb->shared_surface) {
551 stw_dev->stw_winsys->shared_surface_close(screen, fb->shared_surface);
552 fb->shared_surface = NULL;
553 }
554
555 fb->hSharedSurface = data->hSurface;
556
557 if (data->hSurface &&
558 stw_dev->stw_winsys->shared_surface_open) {
559 fb->shared_surface =
560 stw_dev->stw_winsys->shared_surface_open(screen,
561 fb->hSharedSurface);
562 }
563 }
564
565 if (!fb->minimized) {
566 if (fb->shared_surface) {
567 stw_dev->stw_winsys->compose(screen,
568 res,
569 fb->shared_surface,
570 &fb->client_rect,
571 data->ullPresentToken);
572 }
573 else {
574 stw_dev->stw_winsys->present( screen, pipe, res, hdc );
575 }
576 }
577
578 stw_framebuffer_update(fb);
579 stw_notify_current_locked(fb);
580
581 stw_framebuffer_unlock(fb);
582
583 return TRUE;
584 }
585
586
587 /**
588 * Queue a composition.
589 *
590 * The stw_framebuffer object must have its mutex locked. The mutex will
591 * be unlocked here before returning.
592 */
593 BOOL
stw_framebuffer_present_locked(HDC hdc,struct stw_framebuffer * fb,struct pipe_resource * res)594 stw_framebuffer_present_locked(HDC hdc,
595 struct stw_framebuffer *fb,
596 struct pipe_resource *res)
597 {
598 if (fb->winsys_framebuffer) {
599 BOOL result = fb->winsys_framebuffer->present(fb->winsys_framebuffer);
600
601 stw_framebuffer_update(fb);
602 stw_notify_current_locked(fb);
603 stw_framebuffer_unlock(fb);
604
605 return result;
606 }
607 else if (stw_dev->callbacks.pfnPresentBuffers &&
608 stw_dev->stw_winsys->compose) {
609 PRESENTBUFFERSCB data;
610
611 memset(&data, 0, sizeof data);
612 data.nVersion = 2;
613 data.syncType = PRESCB_SYNCTYPE_NONE;
614 data.luidAdapter = stw_dev->AdapterLuid;
615 data.updateRect = fb->client_rect;
616 data.pPrivData = (void *)res;
617
618 stw_notify_current_locked(fb);
619 stw_framebuffer_unlock(fb);
620
621 return stw_dev->callbacks.pfnPresentBuffers(hdc, &data);
622 }
623 else {
624 struct pipe_screen *screen = stw_dev->screen;
625 struct stw_context *ctx = stw_current_context();
626 struct pipe_context *pipe = ctx ? ctx->st->pipe : NULL;
627
628 stw_dev->stw_winsys->present( screen, pipe, res, hdc );
629
630 stw_framebuffer_update(fb);
631 stw_notify_current_locked(fb);
632 stw_framebuffer_unlock(fb);
633
634 return TRUE;
635 }
636 }
637
638
639 /**
640 * This is called just before issuing the buffer swap/present.
641 * We query the current time and determine if we should sleep before
642 * issuing the swap/present.
643 * This is a bit of a hack and is certainly not very accurate but it
644 * basically works.
645 * This is for the WGL_ARB_swap_interval extension.
646 */
647 static void
wait_swap_interval(struct stw_framebuffer * fb)648 wait_swap_interval(struct stw_framebuffer *fb)
649 {
650 /* Note: all time variables here are in units of microseconds */
651 int64_t cur_time = os_time_get_nano() / 1000;
652
653 if (fb->prev_swap_time != 0) {
654 /* Compute time since previous swap */
655 int64_t delta = cur_time - fb->prev_swap_time;
656 int64_t min_swap_period =
657 1.0e6 / stw_dev->refresh_rate * stw_dev->swap_interval;
658
659 /* If time since last swap is less than wait period, wait.
660 * Note that it's possible for the delta to be negative because of
661 * rollover. See https://bugs.freedesktop.org/show_bug.cgi?id=102241
662 */
663 if ((delta >= 0) && (delta < min_swap_period)) {
664 float fudge = 1.75f; /* emperical fudge factor */
665 int64_t wait = (min_swap_period - delta) * fudge;
666 os_time_sleep(wait);
667 }
668 }
669
670 fb->prev_swap_time = cur_time;
671 }
672
673 BOOL
stw_framebuffer_swap_locked(HDC hdc,struct stw_framebuffer * fb)674 stw_framebuffer_swap_locked(HDC hdc, struct stw_framebuffer *fb)
675 {
676 struct stw_context *ctx;
677 if (!(fb->pfi->pfd.dwFlags & PFD_DOUBLEBUFFER)) {
678 stw_framebuffer_unlock(fb);
679 return TRUE;
680 }
681
682 ctx = stw_current_context();
683 if (ctx) {
684 if (ctx->hud) {
685 /* Display the HUD */
686 struct pipe_resource *back =
687 stw_get_framebuffer_resource(fb->stfb, ST_ATTACHMENT_BACK_LEFT);
688 if (back) {
689 hud_run(ctx->hud, NULL, back);
690 }
691 }
692
693 if (ctx->current_framebuffer == fb) {
694 /* flush current context */
695 stw_st_flush(ctx->st, fb->stfb, ST_FLUSH_END_OF_FRAME);
696 }
697 }
698
699 if (stw_dev->swap_interval != 0 && !fb->winsys_framebuffer) {
700 wait_swap_interval(fb);
701 }
702
703 return stw_st_swap_framebuffer_locked(hdc, ctx->st, fb->stfb);
704 }
705
706 BOOL APIENTRY
DrvSwapBuffers(HDC hdc)707 DrvSwapBuffers(HDC hdc)
708 {
709 struct stw_framebuffer *fb;
710
711 if (!stw_dev)
712 return FALSE;
713
714 fb = stw_framebuffer_from_hdc( hdc );
715 if (fb == NULL)
716 return FALSE;
717
718 return stw_framebuffer_swap_locked(hdc, fb);
719 }
720
721
722 BOOL APIENTRY
DrvSwapLayerBuffers(HDC hdc,UINT fuPlanes)723 DrvSwapLayerBuffers(HDC hdc, UINT fuPlanes)
724 {
725 if (fuPlanes & WGL_SWAP_MAIN_PLANE)
726 return DrvSwapBuffers(hdc);
727
728 return FALSE;
729 }
730