1b8e80941Smrg/*
2b8e80941Smrg * Copyright © 2014 Jon Turney
3b8e80941Smrg *
4b8e80941Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5b8e80941Smrg * copy of this software and associated documentation files (the "Software"),
6b8e80941Smrg * to deal in the Software without restriction, including without limitation
7b8e80941Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8b8e80941Smrg * and/or sell copies of the Software, and to permit persons to whom the
9b8e80941Smrg * Software is furnished to do so, subject to the following conditions:
10b8e80941Smrg *
11b8e80941Smrg * The above copyright notice and this permission notice (including the next
12b8e80941Smrg * paragraph) shall be included in all copies or substantial portions of the
13b8e80941Smrg * Software.
14b8e80941Smrg *
15b8e80941Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16b8e80941Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17b8e80941Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18b8e80941Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19b8e80941Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20b8e80941Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21b8e80941Smrg * IN THE SOFTWARE.
22b8e80941Smrg */
23b8e80941Smrg
24b8e80941Smrg#include "windowsgl.h"
25b8e80941Smrg#include "windowsgl_internal.h"
26b8e80941Smrg
27b8e80941Smrg#include "glapi.h"
28b8e80941Smrg#include "wgl.h"
29b8e80941Smrg
30b8e80941Smrg#include <dlfcn.h>
31b8e80941Smrg#include <assert.h>
32b8e80941Smrg#include <stdio.h>
33b8e80941Smrg#include <strings.h>
34b8e80941Smrg
35b8e80941Smrgstatic struct _glapi_table *windows_api = NULL;
36b8e80941Smrg
37b8e80941Smrgstatic void *
38b8e80941Smrgwindows_get_dl_handle(void)
39b8e80941Smrg{
40b8e80941Smrg   static void *dl_handle = NULL;
41b8e80941Smrg
42b8e80941Smrg   if (!dl_handle)
43b8e80941Smrg      dl_handle = dlopen("cygnativeGLthunk.dll", RTLD_NOW);
44b8e80941Smrg
45b8e80941Smrg   return dl_handle;
46b8e80941Smrg}
47b8e80941Smrg
48b8e80941Smrgstatic void
49b8e80941Smrgwindows_glapi_create_table(void)
50b8e80941Smrg{
51b8e80941Smrg   if (windows_api)
52b8e80941Smrg      return;
53b8e80941Smrg
54b8e80941Smrg   windows_api = _glapi_create_table_from_handle(windows_get_dl_handle(), "gl");
55b8e80941Smrg   assert(windows_api);
56b8e80941Smrg}
57b8e80941Smrg
58b8e80941Smrgstatic void windows_glapi_set_dispatch(void)
59b8e80941Smrg{
60b8e80941Smrg   windows_glapi_create_table();
61b8e80941Smrg   _glapi_set_dispatch(windows_api);
62b8e80941Smrg}
63b8e80941Smrg
64b8e80941SmrgwindowsContext *
65b8e80941Smrgwindows_create_context(int pxfi, windowsContext *shared)
66b8e80941Smrg{
67b8e80941Smrg   windowsContext *gc;
68b8e80941Smrg
69b8e80941Smrg   gc = calloc(1, sizeof *gc);
70b8e80941Smrg   if (gc == NULL)
71b8e80941Smrg      return NULL;
72b8e80941Smrg
73b8e80941Smrg   // create a temporary, invisible window
74b8e80941Smrg#define GL_TEMP_WINDOW_CLASS "glTempWndClass"
75b8e80941Smrg   {
76b8e80941Smrg      static wATOM glTempWndClass = 0;
77b8e80941Smrg
78b8e80941Smrg      if (glTempWndClass == 0) {
79b8e80941Smrg         WNDCLASSEX wc;
80b8e80941Smrg         wc.cbSize = sizeof(WNDCLASSEX);
81b8e80941Smrg         wc.style = CS_HREDRAW | CS_VREDRAW;
82b8e80941Smrg         wc.lpfnWndProc = DefWindowProc;
83b8e80941Smrg         wc.cbClsExtra = 0;
84b8e80941Smrg         wc.cbWndExtra = 0;
85b8e80941Smrg         wc.hInstance = GetModuleHandle(NULL);
86b8e80941Smrg         wc.hIcon = 0;
87b8e80941Smrg         wc.hCursor = 0;
88b8e80941Smrg         wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
89b8e80941Smrg         wc.lpszMenuName = NULL;
90b8e80941Smrg         wc.lpszClassName = GL_TEMP_WINDOW_CLASS;
91b8e80941Smrg         wc.hIconSm = 0;
92b8e80941Smrg         RegisterClassEx(&wc);
93b8e80941Smrg      }
94b8e80941Smrg   }
95b8e80941Smrg
96b8e80941Smrg   HWND hwnd = CreateWindowExA(0,
97b8e80941Smrg                               GL_TEMP_WINDOW_CLASS,
98b8e80941Smrg                               "glWindow",
99b8e80941Smrg                               0,
100b8e80941Smrg                               0, 0, 0, 0,
101b8e80941Smrg                               NULL, NULL, GetModuleHandle(NULL), NULL);
102b8e80941Smrg   HDC hdc = GetDC(hwnd);
103b8e80941Smrg
104b8e80941Smrg   // We must set the windows pixel format before we can create a WGL context
105b8e80941Smrg   gc->pxfi = pxfi;
106b8e80941Smrg   SetPixelFormat(hdc, gc->pxfi, NULL);
107b8e80941Smrg
108b8e80941Smrg   gc->ctx = wglCreateContext(hdc);
109b8e80941Smrg
110b8e80941Smrg   if (shared && gc->ctx)
111b8e80941Smrg      wglShareLists(shared->ctx, gc->ctx);
112b8e80941Smrg
113b8e80941Smrg   ReleaseDC(hwnd, hdc);
114b8e80941Smrg   DestroyWindow(hwnd);
115b8e80941Smrg
116b8e80941Smrg   if (!gc->ctx)
117b8e80941Smrg   {
118b8e80941Smrg     free(gc);
119b8e80941Smrg     return NULL;
120b8e80941Smrg   }
121b8e80941Smrg
122b8e80941Smrg   return gc;
123b8e80941Smrg}
124b8e80941Smrg
125b8e80941SmrgwindowsContext *
126b8e80941Smrgwindows_create_context_attribs(int pxfi, windowsContext *shared, const int *attribList)
127b8e80941Smrg{
128b8e80941Smrg   windowsContext *gc;
129b8e80941Smrg
130b8e80941Smrg   gc = calloc(1, sizeof *gc);
131b8e80941Smrg   if (gc == NULL)
132b8e80941Smrg      return NULL;
133b8e80941Smrg
134b8e80941Smrg   // create a temporary, invisible window
135b8e80941Smrg#define GL_TEMP_WINDOW_CLASS "glTempWndClass"
136b8e80941Smrg   {
137b8e80941Smrg      static wATOM glTempWndClass = 0;
138b8e80941Smrg
139b8e80941Smrg      if (glTempWndClass == 0) {
140b8e80941Smrg         WNDCLASSEX wc;
141b8e80941Smrg         wc.cbSize = sizeof(WNDCLASSEX);
142b8e80941Smrg         wc.style = CS_HREDRAW | CS_VREDRAW;
143b8e80941Smrg         wc.lpfnWndProc = DefWindowProc;
144b8e80941Smrg         wc.cbClsExtra = 0;
145b8e80941Smrg         wc.cbWndExtra = 0;
146b8e80941Smrg         wc.hInstance = GetModuleHandle(NULL);
147b8e80941Smrg         wc.hIcon = 0;
148b8e80941Smrg         wc.hCursor = 0;
149b8e80941Smrg         wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
150b8e80941Smrg         wc.lpszMenuName = NULL;
151b8e80941Smrg         wc.lpszClassName = GL_TEMP_WINDOW_CLASS;
152b8e80941Smrg         wc.hIconSm = 0;
153b8e80941Smrg         RegisterClassEx(&wc);
154b8e80941Smrg      }
155b8e80941Smrg   }
156b8e80941Smrg
157b8e80941Smrg   HWND hwnd = CreateWindowExA(0,
158b8e80941Smrg                               GL_TEMP_WINDOW_CLASS,
159b8e80941Smrg                               "glWindow",
160b8e80941Smrg                               0,
161b8e80941Smrg                               0, 0, 0, 0,
162b8e80941Smrg                               NULL, NULL, GetModuleHandle(NULL), NULL);
163b8e80941Smrg   HDC hdc = GetDC(hwnd);
164b8e80941Smrg   HGLRC shareContext = NULL;
165b8e80941Smrg   if (shared)
166b8e80941Smrg      shareContext = shared->ctx;
167b8e80941Smrg
168b8e80941Smrg   // We must set the windows pixel format before we can create a WGL context
169b8e80941Smrg   gc->pxfi = pxfi;
170b8e80941Smrg   SetPixelFormat(hdc, gc->pxfi, NULL);
171b8e80941Smrg
172b8e80941Smrg   gc->ctx = wglCreateContextAttribsARB(hdc, shareContext, attribList);
173b8e80941Smrg
174b8e80941Smrg   ReleaseDC(hwnd, hdc);
175b8e80941Smrg   DestroyWindow(hwnd);
176b8e80941Smrg
177b8e80941Smrg   if (!gc->ctx)
178b8e80941Smrg   {
179b8e80941Smrg     free(gc);
180b8e80941Smrg     return NULL;
181b8e80941Smrg   }
182b8e80941Smrg
183b8e80941Smrg   return gc;
184b8e80941Smrg}
185b8e80941Smrg
186b8e80941Smrgvoid
187b8e80941Smrgwindows_destroy_context(windowsContext *context)
188b8e80941Smrg{
189b8e80941Smrg   wglDeleteContext(context->ctx);
190b8e80941Smrg   context->ctx = NULL;
191b8e80941Smrg   free(context);
192b8e80941Smrg}
193b8e80941Smrg
194b8e80941Smrgint windows_bind_context(windowsContext *context, windowsDrawable *draw, windowsDrawable *read)
195b8e80941Smrg{
196b8e80941Smrg   HDC drawDc = draw->callbacks->getdc(draw);
197b8e80941Smrg
198b8e80941Smrg   if (!draw->pxfi)
199b8e80941Smrg   {
200b8e80941Smrg      SetPixelFormat(drawDc, context->pxfi, NULL);
201b8e80941Smrg      draw->pxfi = context->pxfi;
202b8e80941Smrg   }
203b8e80941Smrg
204b8e80941Smrg   if ((read != NULL) &&  (read != draw))
205b8e80941Smrg   {
206b8e80941Smrg      /*
207b8e80941Smrg        If there is a separate read drawable, create a separate read DC, and
208b8e80941Smrg        use the wglMakeContextCurrent extension to make the context current
209b8e80941Smrg        drawing to one DC and reading from the other
210b8e80941Smrg
211b8e80941Smrg        Should only occur when WGL_ARB_make_current_read extension is present
212b8e80941Smrg      */
213b8e80941Smrg      HDC readDc = read->callbacks->getdc(read);
214b8e80941Smrg
215b8e80941Smrg      BOOL ret = wglMakeContextCurrentARB(drawDc, readDc, context->ctx);
216b8e80941Smrg
217b8e80941Smrg      read->callbacks->releasedc(read, readDc);
218b8e80941Smrg
219b8e80941Smrg      if (!ret) {
220b8e80941Smrg         printf("wglMakeContextCurrentARB error: %08x\n", GetLastError());
221b8e80941Smrg         return FALSE;
222b8e80941Smrg      }
223b8e80941Smrg   }
224b8e80941Smrg   else
225b8e80941Smrg   {
226b8e80941Smrg      /* Otherwise, just use wglMakeCurrent */
227b8e80941Smrg      BOOL ret = wglMakeCurrent(drawDc, context->ctx);
228b8e80941Smrg      if (!ret) {
229b8e80941Smrg         printf("wglMakeCurrent error: %08x\n", GetLastError());
230b8e80941Smrg         return FALSE;
231b8e80941Smrg      }
232b8e80941Smrg   }
233b8e80941Smrg
234b8e80941Smrg   draw->callbacks->releasedc(draw, drawDc);
235b8e80941Smrg
236b8e80941Smrg   windows_glapi_set_dispatch();
237b8e80941Smrg
238b8e80941Smrg   return TRUE;
239b8e80941Smrg}
240b8e80941Smrg
241b8e80941Smrgvoid windows_unbind_context(windowsContext * context)
242b8e80941Smrg{
243b8e80941Smrg   wglMakeCurrent(NULL, NULL);
244b8e80941Smrg}
245b8e80941Smrg
246b8e80941Smrg/*
247b8e80941Smrg *
248b8e80941Smrg */
249b8e80941Smrg
250b8e80941Smrgvoid
251b8e80941Smrgwindows_swap_buffers(windowsDrawable *draw)
252b8e80941Smrg{
253b8e80941Smrg   HDC drawDc = GetDC(draw->hWnd);
254b8e80941Smrg   SwapBuffers(drawDc);
255b8e80941Smrg   ReleaseDC(draw->hWnd, drawDc);
256b8e80941Smrg}
257b8e80941Smrg
258b8e80941Smrg
259b8e80941Smrgtypedef void (__stdcall * PFNGLADDSWAPHINTRECTWIN) (GLint x, GLint y,
260b8e80941Smrg                                                    GLsizei width,
261b8e80941Smrg                                                    GLsizei height);
262b8e80941Smrg
263b8e80941Smrgstatic void
264b8e80941SmrgglAddSwapHintRectWIN(GLint x, GLint y, GLsizei width,
265b8e80941Smrg                            GLsizei height)
266b8e80941Smrg{
267b8e80941Smrg   PFNGLADDSWAPHINTRECTWIN proc = (PFNGLADDSWAPHINTRECTWIN)wglGetProcAddress("glAddSwapHintRectWIN");
268b8e80941Smrg   if (proc)
269b8e80941Smrg      proc(x, y, width, height);
270b8e80941Smrg}
271b8e80941Smrg
272b8e80941Smrgvoid
273b8e80941Smrgwindows_copy_subbuffer(windowsDrawable *draw,
274b8e80941Smrg                      int x, int y, int width, int height)
275b8e80941Smrg{
276b8e80941Smrg   glAddSwapHintRectWIN(x, y, width, height);
277b8e80941Smrg   windows_swap_buffers(draw);
278b8e80941Smrg}
279b8e80941Smrg
280b8e80941Smrg/*
281b8e80941Smrg * Helper function for calling a test function on an initial context
282b8e80941Smrg */
283b8e80941Smrgstatic void
284b8e80941Smrgwindows_call_with_context(void (*proc)(HDC, void *), void *args)
285b8e80941Smrg{
286b8e80941Smrg   // create window class
287b8e80941Smrg#define WIN_GL_TEST_WINDOW_CLASS "GLTest"
288b8e80941Smrg   {
289b8e80941Smrg      static wATOM glTestWndClass = 0;
290b8e80941Smrg
291b8e80941Smrg      if (glTestWndClass == 0) {
292b8e80941Smrg         WNDCLASSEX wc;
293b8e80941Smrg
294b8e80941Smrg         wc.cbSize = sizeof(WNDCLASSEX);
295b8e80941Smrg         wc.style = CS_HREDRAW | CS_VREDRAW;
296b8e80941Smrg         wc.lpfnWndProc = DefWindowProc;
297b8e80941Smrg         wc.cbClsExtra = 0;
298b8e80941Smrg         wc.cbWndExtra = 0;
299b8e80941Smrg         wc.hInstance = GetModuleHandle(NULL);
300b8e80941Smrg         wc.hIcon = 0;
301b8e80941Smrg         wc.hCursor = 0;
302b8e80941Smrg         wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
303b8e80941Smrg         wc.lpszMenuName = NULL;
304b8e80941Smrg         wc.lpszClassName = WIN_GL_TEST_WINDOW_CLASS;
305b8e80941Smrg         wc.hIconSm = 0;
306b8e80941Smrg         glTestWndClass = RegisterClassEx(&wc);
307b8e80941Smrg      }
308b8e80941Smrg   }
309b8e80941Smrg
310b8e80941Smrg   // create an invisible window for a scratch DC
311b8e80941Smrg   HWND hwnd = CreateWindowExA(0,
312b8e80941Smrg                               WIN_GL_TEST_WINDOW_CLASS,
313b8e80941Smrg                               "GL Renderer Capabilities Test Window",
314b8e80941Smrg                               0, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(NULL),
315b8e80941Smrg                               NULL);
316b8e80941Smrg   if (hwnd) {
317b8e80941Smrg      HDC hdc = GetDC(hwnd);
318b8e80941Smrg
319b8e80941Smrg      // we must set a pixel format before we can create a context, just use the first one...
320b8e80941Smrg      SetPixelFormat(hdc, 1, NULL);
321b8e80941Smrg      HGLRC hglrc = wglCreateContext(hdc);
322b8e80941Smrg      wglMakeCurrent(hdc, hglrc);
323b8e80941Smrg
324b8e80941Smrg      // call the test function
325b8e80941Smrg      proc(hdc, args);
326b8e80941Smrg
327b8e80941Smrg      // clean up
328b8e80941Smrg      wglMakeCurrent(NULL, NULL);
329b8e80941Smrg      wglDeleteContext(hglrc);
330b8e80941Smrg      ReleaseDC(hwnd, hdc);
331b8e80941Smrg      DestroyWindow(hwnd);
332b8e80941Smrg   }
333b8e80941Smrg}
334b8e80941Smrg
335b8e80941Smrgstatic void
336b8e80941Smrgwindows_check_render_test(HDC hdc, void *args)
337b8e80941Smrg{
338b8e80941Smrg   int *result = (int *)args;
339b8e80941Smrg
340b8e80941Smrg   /* Rather than play linkage games using stdcall to ensure we get
341b8e80941Smrg      glGetString from opengl32.dll here, use dlsym */
342b8e80941Smrg   void *dlhandle = windows_get_dl_handle();
343b8e80941Smrg   const char *(*proc)(int) = dlsym(dlhandle, "glGetString");
344b8e80941Smrg   const char *gl_renderer = (const char *)proc(GL_RENDERER);
345b8e80941Smrg
346b8e80941Smrg   if ((!gl_renderer) || (strcasecmp(gl_renderer, "GDI Generic") == 0))
347b8e80941Smrg      *result = FALSE;
348b8e80941Smrg   else
349b8e80941Smrg      *result = TRUE;
350b8e80941Smrg}
351b8e80941Smrg
352b8e80941Smrgint
353b8e80941Smrgwindows_check_renderer(void)
354b8e80941Smrg{
355b8e80941Smrg   int result;
356b8e80941Smrg   windows_call_with_context(windows_check_render_test, &result);
357b8e80941Smrg   return result;
358b8e80941Smrg}
359b8e80941Smrg
360b8e80941Smrgtypedef struct {
361b8e80941Smrg   char *gl_extensions;
362b8e80941Smrg   char *wgl_extensions;
363b8e80941Smrg} windows_extensions_result;
364b8e80941Smrg
365b8e80941Smrgstatic void
366b8e80941Smrgwindows_extensions_test(HDC hdc, void *args)
367b8e80941Smrg{
368b8e80941Smrg   windows_extensions_result *r = (windows_extensions_result *)args;
369b8e80941Smrg
370b8e80941Smrg   void *dlhandle = windows_get_dl_handle();
371b8e80941Smrg   const char *(*proc)(int) = dlsym(dlhandle, "glGetString");
372b8e80941Smrg
373b8e80941Smrg   r->gl_extensions = strdup(proc(GL_EXTENSIONS));
374b8e80941Smrg
375b8e80941Smrg   wglResolveExtensionProcs();
376b8e80941Smrg   r->wgl_extensions = strdup(wglGetExtensionsStringARB(hdc));
377b8e80941Smrg}
378b8e80941Smrg
379b8e80941Smrgvoid
380b8e80941Smrgwindows_extensions(char **gl_extensions, char **wgl_extensions)
381b8e80941Smrg{
382b8e80941Smrg   windows_extensions_result result;
383b8e80941Smrg
384b8e80941Smrg   *gl_extensions = "";
385b8e80941Smrg   *wgl_extensions = "";
386b8e80941Smrg
387b8e80941Smrg   windows_call_with_context(windows_extensions_test, &result);
388b8e80941Smrg
389b8e80941Smrg   *gl_extensions = result.gl_extensions;
390b8e80941Smrg   *wgl_extensions = result.wgl_extensions;
391b8e80941Smrg}
392b8e80941Smrg
393b8e80941Smrgvoid windows_setTexBuffer(windowsContext *context, int textureTarget,
394b8e80941Smrg                         int textureFormat, windowsDrawable *drawable)
395b8e80941Smrg{
396b8e80941Smrg   // not yet implemented
397b8e80941Smrg}
398b8e80941Smrg
399b8e80941Smrgvoid windows_releaseTexBuffer(windowsContext *context, int textureTarget,
400b8e80941Smrg                             windowsDrawable *drawable)
401b8e80941Smrg{
402b8e80941Smrg   // not yet implemented
403b8e80941Smrg}
404