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