1 /*
2   Copyright 2012-2020 David Robillard <d@drobilla.net>
3 
4   Permission to use, copy, modify, and/or distribute this software for any
5   purpose with or without fee is hereby granted, provided that the above
6   copyright notice and this permission notice appear in all copies.
7 
8   THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16 
17 #include "stub.h"
18 #include "types.h"
19 #include "win.h"
20 
21 #include "pugl/gl.h"
22 
23 #include <windows.h>
24 
25 #include <GL/gl.h>
26 
27 #include <stdbool.h>
28 #include <stdlib.h>
29 
30 #define WGL_DRAW_TO_WINDOW_ARB 0x2001
31 #define WGL_ACCELERATION_ARB 0x2003
32 #define WGL_SUPPORT_OPENGL_ARB 0x2010
33 #define WGL_DOUBLE_BUFFER_ARB 0x2011
34 #define WGL_PIXEL_TYPE_ARB 0x2013
35 #define WGL_RED_BITS_ARB 0x2015
36 #define WGL_GREEN_BITS_ARB 0x2017
37 #define WGL_BLUE_BITS_ARB 0x2019
38 #define WGL_ALPHA_BITS_ARB 0x201b
39 #define WGL_DEPTH_BITS_ARB 0x2022
40 #define WGL_STENCIL_BITS_ARB 0x2023
41 #define WGL_FULL_ACCELERATION_ARB 0x2027
42 #define WGL_TYPE_RGBA_ARB 0x202b
43 #define WGL_SAMPLE_BUFFERS_ARB 0x2041
44 #define WGL_SAMPLES_ARB 0x2042
45 
46 #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
47 #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
48 #define WGL_CONTEXT_FLAGS_ARB 0x2094
49 #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
50 
51 #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
52 #define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
53 #define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001
54 
55 typedef HGLRC (*WglCreateContextAttribs)(HDC, HGLRC, const int*);
56 typedef BOOL (*WglSwapInterval)(int);
57 typedef BOOL (
58   *WglChoosePixelFormat)(HDC, const int*, const FLOAT*, UINT, int*, UINT*);
59 
60 typedef struct {
61   WglChoosePixelFormat    wglChoosePixelFormat;
62   WglCreateContextAttribs wglCreateContextAttribs;
63   WglSwapInterval         wglSwapInterval;
64 } PuglWinGlProcs;
65 
66 typedef struct {
67   PuglWinGlProcs procs;
68   HGLRC          hglrc;
69 } PuglWinGlSurface;
70 
71 // Struct to manage the fake window used during configuration
72 typedef struct {
73   HWND hwnd;
74   HDC  hdc;
75 } PuglFakeWindow;
76 
77 static PuglStatus
puglWinError(PuglFakeWindow * fakeWin,const PuglStatus status)78 puglWinError(PuglFakeWindow* fakeWin, const PuglStatus status)
79 {
80   if (fakeWin->hwnd) {
81     ReleaseDC(fakeWin->hwnd, fakeWin->hdc);
82     DestroyWindow(fakeWin->hwnd);
83   }
84 
85   return status;
86 }
87 
88 static PuglWinGlProcs
puglWinGlGetProcs(void)89 puglWinGlGetProcs(void)
90 {
91   const PuglWinGlProcs procs = {
92     (WglChoosePixelFormat)(wglGetProcAddress("wglChoosePixelFormatARB")),
93     (WglCreateContextAttribs)(wglGetProcAddress("wglCreateContextAttribsARB")),
94     (WglSwapInterval)(wglGetProcAddress("wglSwapIntervalEXT"))};
95 
96   return procs;
97 }
98 
99 static PuglStatus
puglWinGlConfigure(PuglView * view)100 puglWinGlConfigure(PuglView* view)
101 {
102   PuglInternals* impl = view->impl;
103 
104   // Set attributes to default if they are unset
105   // (There is no GLX_DONT_CARE equivalent on Windows)
106   if (view->hints[PUGL_DEPTH_BITS] == PUGL_DONT_CARE) {
107     view->hints[PUGL_DEPTH_BITS] = 0;
108   }
109   if (view->hints[PUGL_STENCIL_BITS] == PUGL_DONT_CARE) {
110     view->hints[PUGL_STENCIL_BITS] = 0;
111   }
112   if (view->hints[PUGL_SAMPLES] == PUGL_DONT_CARE) {
113     view->hints[PUGL_SAMPLES] = 1;
114   }
115   if (view->hints[PUGL_DOUBLE_BUFFER] == PUGL_DONT_CARE) {
116     view->hints[PUGL_DOUBLE_BUFFER] = 1;
117   }
118   if (view->hints[PUGL_SWAP_INTERVAL] == PUGL_DONT_CARE) {
119     view->hints[PUGL_SWAP_INTERVAL] = 1;
120   }
121 
122   // clang-format off
123   const int pixelAttrs[] = {
124     WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
125     WGL_ACCELERATION_ARB,   WGL_FULL_ACCELERATION_ARB,
126     WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
127     WGL_DOUBLE_BUFFER_ARB,  view->hints[PUGL_DOUBLE_BUFFER],
128     WGL_PIXEL_TYPE_ARB,     WGL_TYPE_RGBA_ARB,
129     WGL_SAMPLE_BUFFERS_ARB, view->hints[PUGL_SAMPLES] ? 1 : 0,
130     WGL_SAMPLES_ARB,        view->hints[PUGL_SAMPLES],
131     WGL_RED_BITS_ARB,       view->hints[PUGL_RED_BITS],
132     WGL_GREEN_BITS_ARB,     view->hints[PUGL_GREEN_BITS],
133     WGL_BLUE_BITS_ARB,      view->hints[PUGL_BLUE_BITS],
134     WGL_ALPHA_BITS_ARB,     view->hints[PUGL_ALPHA_BITS],
135     WGL_DEPTH_BITS_ARB,     view->hints[PUGL_DEPTH_BITS],
136     WGL_STENCIL_BITS_ARB,   view->hints[PUGL_STENCIL_BITS],
137     0,
138   };
139   // clang-format on
140 
141   PuglWinGlSurface* const surface =
142     (PuglWinGlSurface*)calloc(1, sizeof(PuglWinGlSurface));
143   impl->surface = surface;
144 
145   // Create fake window for getting at GL context
146   PuglStatus         st      = PUGL_SUCCESS;
147   PuglFakeWindow     fakeWin = {0, 0};
148   static const char* title   = "Pugl Configuration";
149   if ((st = puglWinCreateWindow(view, title, &fakeWin.hwnd, &fakeWin.hdc))) {
150     return puglWinError(&fakeWin, st);
151   }
152 
153   // Set pixel format for fake window
154   const PuglWinPFD fakePfd  = puglWinGetPixelFormatDescriptor(view->hints);
155   const int        fakePfId = ChoosePixelFormat(fakeWin.hdc, &fakePfd);
156   if (!fakePfId || !SetPixelFormat(fakeWin.hdc, fakePfId, &fakePfd)) {
157     return puglWinError(&fakeWin, PUGL_SET_FORMAT_FAILED);
158   }
159 
160   // Create fake GL context to get at the functions we need
161   HGLRC fakeRc = wglCreateContext(fakeWin.hdc);
162   if (!fakeRc) {
163     return puglWinError(&fakeWin, PUGL_CREATE_CONTEXT_FAILED);
164   }
165 
166   // Enter fake context and get extension functions
167   wglMakeCurrent(fakeWin.hdc, fakeRc);
168   surface->procs = puglWinGlGetProcs();
169 
170   if (surface->procs.wglChoosePixelFormat) {
171     // Choose pixel format based on attributes
172     UINT numFormats = 0;
173     if (!surface->procs.wglChoosePixelFormat(
174           fakeWin.hdc, pixelAttrs, NULL, 1u, &impl->pfId, &numFormats)) {
175       return puglWinError(&fakeWin, PUGL_SET_FORMAT_FAILED);
176     }
177 
178     DescribePixelFormat(impl->hdc, impl->pfId, sizeof(impl->pfd), &impl->pfd);
179   } else {
180     // Modern extensions not available, use basic pixel format
181     impl->pfd  = fakePfd;
182     impl->pfId = fakePfId;
183   }
184 
185   // Dispose of fake window and context
186   wglMakeCurrent(NULL, NULL);
187   wglDeleteContext(fakeRc);
188   ReleaseDC(fakeWin.hwnd, fakeWin.hdc);
189   DestroyWindow(fakeWin.hwnd);
190 
191   return PUGL_SUCCESS;
192 }
193 
194 static PuglStatus
puglWinGlCreate(PuglView * view)195 puglWinGlCreate(PuglView* view)
196 {
197   PuglInternals* const    impl    = view->impl;
198   PuglWinGlSurface* const surface = (PuglWinGlSurface*)impl->surface;
199   PuglStatus              st      = PUGL_SUCCESS;
200 
201   const int contextAttribs[] = {
202     WGL_CONTEXT_MAJOR_VERSION_ARB,
203     view->hints[PUGL_CONTEXT_VERSION_MAJOR],
204 
205     WGL_CONTEXT_MINOR_VERSION_ARB,
206     view->hints[PUGL_CONTEXT_VERSION_MINOR],
207 
208     WGL_CONTEXT_FLAGS_ARB,
209     (view->hints[PUGL_USE_DEBUG_CONTEXT] ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
210 
211     WGL_CONTEXT_PROFILE_MASK_ARB,
212     (view->hints[PUGL_USE_COMPAT_PROFILE]
213        ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB
214        : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB),
215 
216     0};
217 
218   // Create real window with desired pixel format
219   if ((st = puglWinCreateWindow(view, "Pugl", &impl->hwnd, &impl->hdc))) {
220     return st;
221   } else if (!SetPixelFormat(impl->hdc, impl->pfId, &impl->pfd)) {
222     ReleaseDC(impl->hwnd, impl->hdc);
223     DestroyWindow(impl->hwnd);
224     impl->hwnd = NULL;
225     impl->hdc  = NULL;
226     return PUGL_SET_FORMAT_FAILED;
227   }
228 
229   // Create GL context
230   if (surface->procs.wglCreateContextAttribs &&
231       !(surface->hglrc = surface->procs.wglCreateContextAttribs(
232           impl->hdc, 0, contextAttribs))) {
233     return PUGL_CREATE_CONTEXT_FAILED;
234   } else if (!(surface->hglrc = wglCreateContext(impl->hdc))) {
235     return PUGL_CREATE_CONTEXT_FAILED;
236   }
237 
238   // Enter context and set swap interval
239   wglMakeCurrent(impl->hdc, surface->hglrc);
240   const int swapInterval = view->hints[PUGL_SWAP_INTERVAL];
241   if (surface->procs.wglSwapInterval && swapInterval != PUGL_DONT_CARE) {
242     surface->procs.wglSwapInterval(swapInterval);
243   }
244 
245   return PUGL_SUCCESS;
246 }
247 
248 static PuglStatus
puglWinGlDestroy(PuglView * view)249 puglWinGlDestroy(PuglView* view)
250 {
251   PuglWinGlSurface* surface = (PuglWinGlSurface*)view->impl->surface;
252   if (surface) {
253     wglMakeCurrent(NULL, NULL);
254     wglDeleteContext(surface->hglrc);
255     free(surface);
256     view->impl->surface = NULL;
257   }
258 
259   return PUGL_SUCCESS;
260 }
261 
262 static PuglStatus
puglWinGlEnter(PuglView * view,const PuglEventExpose * expose)263 puglWinGlEnter(PuglView* view, const PuglEventExpose* expose)
264 {
265   PuglWinGlSurface* surface = (PuglWinGlSurface*)view->impl->surface;
266 
267   wglMakeCurrent(view->impl->hdc, surface->hglrc);
268 
269   if (expose) {
270     PAINTSTRUCT ps;
271     BeginPaint(view->impl->hwnd, &ps);
272   }
273 
274   return PUGL_SUCCESS;
275 }
276 
277 static PuglStatus
puglWinGlLeave(PuglView * view,const PuglEventExpose * expose)278 puglWinGlLeave(PuglView* view, const PuglEventExpose* expose)
279 {
280   if (expose) {
281     PAINTSTRUCT ps;
282     EndPaint(view->impl->hwnd, &ps);
283     SwapBuffers(view->impl->hdc);
284   }
285 
286   wglMakeCurrent(NULL, NULL);
287   return PUGL_SUCCESS;
288 }
289 
290 PuglGlFunc
puglGetProcAddress(const char * name)291 puglGetProcAddress(const char* name)
292 {
293   const PuglGlFunc func = (PuglGlFunc)wglGetProcAddress(name);
294 
295   /* Windows has the annoying property that wglGetProcAddress returns NULL
296      for functions from OpenGL 1.1, so we fall back to pulling them directly
297      from opengl32.dll */
298 
299   return func
300            ? func
301            : (PuglGlFunc)GetProcAddress(GetModuleHandle("opengl32.dll"), name);
302 }
303 
304 PuglStatus
puglEnterContext(PuglView * view)305 puglEnterContext(PuglView* view)
306 {
307   return view->backend->enter(view, NULL);
308 }
309 
310 PuglStatus
puglLeaveContext(PuglView * view)311 puglLeaveContext(PuglView* view)
312 {
313   return view->backend->leave(view, NULL);
314 }
315 
316 const PuglBackend*
puglGlBackend(void)317 puglGlBackend(void)
318 {
319   static const PuglBackend backend = {puglWinGlConfigure,
320                                       puglWinGlCreate,
321                                       puglWinGlDestroy,
322                                       puglWinGlEnter,
323                                       puglWinGlLeave,
324                                       puglStubGetContext};
325 
326   return &backend;
327 }
328