aboutsummaryrefslogtreecommitdiffstats
path: root/pugl
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2019-07-26 20:38:49 +0200
committerDavid Robillard <d@drobilla.net>2019-07-28 19:06:39 +0200
commitb63194bf2536b156bbda688b3bf9917a5dcdc3f3 (patch)
treebda995f4e05f6d6cbdb35da0583cd977d5fd7ac5 /pugl
parent6a77f96642b201f614ce7eb67f3b6ec4e1e8c181 (diff)
downloadpugl-b63194bf2536b156bbda688b3bf9917a5dcdc3f3.tar.gz
pugl-b63194bf2536b156bbda688b3bf9917a5dcdc3f3.tar.bz2
pugl-b63194bf2536b156bbda688b3bf9917a5dcdc3f3.zip
Windows: Factor out GL backend
Diffstat (limited to 'pugl')
-rw-r--r--pugl/pugl.h5
-rw-r--r--pugl/pugl_win.c293
-rw-r--r--pugl/pugl_win.h97
-rw-r--r--pugl/pugl_win_gl.c302
4 files changed, 427 insertions, 270 deletions
diff --git a/pugl/pugl.h b/pugl/pugl.h
index f34b4de..adeeb0e 100644
--- a/pugl/pugl.h
+++ b/pugl/pugl.h
@@ -74,7 +74,10 @@ typedef void* PuglHandle;
Return status code.
*/
typedef enum {
- PUGL_SUCCESS = 0
+ PUGL_SUCCESS,
+ PUGL_ERR_CREATE_WINDOW,
+ PUGL_ERR_SET_FORMAT,
+ PUGL_ERR_CREATE_CONTEXT,
} PuglStatus;
/**
diff --git a/pugl/pugl_win.c b/pugl/pugl_win.c
index d9cdf7d..0f18eba 100644
--- a/pugl/pugl_win.c
+++ b/pugl/pugl_win.c
@@ -15,16 +15,19 @@
*/
/**
- @file pugl_win.c Windows/WGL Pugl Implementation.
+ @file pugl_win.c Windows Pugl Implementation.
*/
#include "pugl/pugl_internal.h"
+#include "pugl/pugl_win.h"
+
+#ifdef PUGL_HAVE_GL
+#include "pugl/pugl_gl_backend.h"
+#endif
#include <windows.h>
#include <windowsx.h>
-#include <GL/gl.h>
-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -47,58 +50,8 @@
#define PUGL_RESIZE_TIMER_ID 9461
#define PUGL_URGENT_TIMER_ID 9462
-#define WGL_DRAW_TO_WINDOW_ARB 0x2001
-#define WGL_ACCELERATION_ARB 0x2003
-#define WGL_SUPPORT_OPENGL_ARB 0x2010
-#define WGL_DOUBLE_BUFFER_ARB 0x2011
-#define WGL_PIXEL_TYPE_ARB 0x2013
-#define WGL_COLOR_BITS_ARB 0x2014
-#define WGL_RED_BITS_ARB 0x2015
-#define WGL_GREEN_BITS_ARB 0x2017
-#define WGL_BLUE_BITS_ARB 0x2019
-#define WGL_ALPHA_BITS_ARB 0x201b
-#define WGL_DEPTH_BITS_ARB 0x2022
-#define WGL_STENCIL_BITS_ARB 0x2023
-#define WGL_FULL_ACCELERATION_ARB 0x2027
-#define WGL_TYPE_RGBA_ARB 0x202b
-#define WGL_SAMPLE_BUFFERS_ARB 0x2041
-#define WGL_SAMPLES_ARB 0x2042
-
-#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
-#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
-#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
-#define WGL_CONTEXT_FLAGS_ARB 0x2094
-#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
-
-#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
-#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
-
-struct PuglInternalsImpl {
- HWND hwnd;
- HDC hdc;
- HGLRC hglrc;
- DWORD refreshRate;
- double timerFrequency;
- bool flashing;
- bool resizing;
- bool mouseTracked;
-};
-
-// Scoped class to manage the fake window used during window creation
-typedef struct {
- HWND hwnd;
- HDC hdc;
-} PuglFakeWindow;
-
static const TCHAR* DEFAULT_CLASSNAME = "Pugl";
-static PuglFakeWindow
-puglMakeFakeWindow(HWND wnd)
-{
- const PuglFakeWindow fakeWin = {wnd, wnd ? GetDC(wnd) : 0};
- return fakeWin;
-}
-
LRESULT CALLBACK
wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
@@ -117,99 +70,30 @@ puglInitInternals(void)
void
puglEnterContext(PuglView* view, bool drawing)
{
-#ifdef PUGL_HAVE_GL
- if (view->ctx_type == PUGL_GL) {
- wglMakeCurrent(view->impl->hdc, view->impl->hglrc);
- }
-#endif
-
- if (drawing) {
- PAINTSTRUCT ps;
- BeginPaint(view->impl->hwnd, &ps);
- }
+ view->impl->backend->enter(view, drawing);
}
void
puglLeaveContext(PuglView* view, bool drawing)
{
- if (drawing) {
- PAINTSTRUCT ps;
- EndPaint(view->impl->hwnd, &ps);
-
-#ifdef PUGL_HAVE_GL
- if (view->ctx_type == PUGL_GL) {
- SwapBuffers(view->impl->hdc);
- }
- }
-#endif
-
- wglMakeCurrent(NULL, NULL);
-}
-
-static PIXELFORMATDESCRIPTOR
-puglGetPixelFormatDescriptor(const PuglHints* hints)
-{
- const int rgbBits = hints->red_bits + hints->green_bits + hints->blue_bits;
-
- PIXELFORMATDESCRIPTOR pfd;
- ZeroMemory(&pfd, sizeof(pfd));
- pfd.nSize = sizeof(pfd);
- pfd.nVersion = 1;
- pfd.dwFlags = PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL;
- pfd.dwFlags |= hints->double_buffer ? PFD_DOUBLEBUFFER : 0;
- pfd.iPixelType = PFD_TYPE_RGBA;
- pfd.cColorBits = (BYTE)rgbBits;
- pfd.cRedBits = (BYTE)hints->red_bits;
- pfd.cGreenBits = (BYTE)hints->green_bits;
- pfd.cBlueBits = (BYTE)hints->blue_bits;
- pfd.cAlphaBits = (BYTE)hints->alpha_bits;
- pfd.cDepthBits = (BYTE)hints->depth_bits;
- pfd.cStencilBits = (BYTE)hints->stencil_bits;
- pfd.iLayerType = PFD_MAIN_PLANE;
- return pfd;
-}
-
-static int
-puglWinError(PuglFakeWindow* fakeWin, const int status)
-{
- if (fakeWin->hwnd) {
- ReleaseDC(fakeWin->hwnd, fakeWin->hdc);
- DestroyWindow(fakeWin->hwnd);
- }
-
- return status;
-}
-
-static unsigned
-getWindowFlags(const PuglView* view)
-{
- return (WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
- (view->parent
- ? WS_CHILD
- : (WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX |
- (view->hints.resizable ? (WS_SIZEBOX | WS_MAXIMIZEBOX) : 0))));
-}
-
-static unsigned
-getWindowExFlags(const PuglView* view)
-{
- return WS_EX_NOINHERITLAYOUT | (view->parent ? 0u : WS_EX_APPWINDOW);
+ view->impl->backend->leave(view, drawing);
}
int
puglCreateWindow(PuglView* view, const char* title)
{
- typedef BOOL (*WglChoosePixelFormat)(
- HDC, const int*, const FLOAT*, UINT, int*, UINT*);
-
- typedef HGLRC (*WglCreateContextAttribs)(HDC, HGLRC, const int*);
-
- typedef BOOL (*WglSwapInterval)(int);
+ PuglInternals* impl = view->impl;
const char* className = view->windowClass ? view->windowClass : DEFAULT_CLASSNAME;
title = title ? title : "Window";
+ if (view->ctx_type == PUGL_GL) {
+#ifdef PUGL_HAVE_GL
+ impl->backend = puglGlBackend();
+#endif
+ }
+
// Get refresh rate for resize draw timer
DEVMODEA devMode = {0};
EnumDisplaySettingsA(NULL, ENUM_CURRENT_SETTINGS, &devMode);
@@ -230,140 +114,18 @@ puglCreateWindow(PuglView* view, const char* title)
return 1;
}
- // Calculate window flags
- const unsigned winFlags = getWindowFlags(view);
- const unsigned winExFlags = getWindowExFlags(view);
- if (view->hints.resizable) {
- if (view->min_width || view->min_height) {
- // Adjust the minimum window size to accomodate requested view size
- RECT mr = { 0, 0, view->min_width, view->min_height };
- AdjustWindowRectEx(&mr, winFlags, FALSE, winExFlags);
- view->min_width = mr.right - mr.left;
- view->min_height = mr.bottom - mr.top;
- }
- }
-
- // Adjust the window size to accomodate requested view size
- RECT wr = { 0, 0, view->width, view->height };
- AdjustWindowRectEx(&wr, winFlags, FALSE, winExFlags);
-
- // Create fake window for getting at GL context
- PuglFakeWindow fakeWin = puglMakeFakeWindow(
- CreateWindowEx(winExFlags,
- className, title,
- winFlags,
- CW_USEDEFAULT, CW_USEDEFAULT,
- wr.right-wr.left, wr.bottom-wr.top,
- (HWND)view->parent, NULL, NULL, NULL));
-
- if (!fakeWin.hwnd) {
- return puglWinError(&fakeWin, 2);
- }
-
- // Choose pixel format for fake window
- const PIXELFORMATDESCRIPTOR fakePfd = puglGetPixelFormatDescriptor(
- &view->hints);
- const int fakeFormatId = ChoosePixelFormat(fakeWin.hdc, &fakePfd);
- if (!fakeFormatId) {
- return puglWinError(&fakeWin, 3);
- } else if (!SetPixelFormat(fakeWin.hdc, fakeFormatId, &fakePfd)) {
- return puglWinError(&fakeWin, 4);
- }
-
- HGLRC fakeRc = wglCreateContext(fakeWin.hdc);
- if (!fakeRc) {
- return puglWinError(&fakeWin, 5);
- }
-
- wglMakeCurrent(fakeWin.hdc, fakeRc);
-
- WglChoosePixelFormat wglChoosePixelFormat = (WglChoosePixelFormat)(
- wglGetProcAddress("wglChoosePixelFormatARB"));
- WglCreateContextAttribs wglCreateContextAttribs = (WglCreateContextAttribs)(
- wglGetProcAddress("wglCreateContextAttribsARB"));
- WglSwapInterval wglSwapInterval = (WglSwapInterval)(
- wglGetProcAddress("wglSwapIntervalEXT"));
-
- PuglInternals* impl = view->impl;
-
- if (wglChoosePixelFormat && wglCreateContextAttribs) {
- // Now create real window
- impl->hwnd = CreateWindowEx(
- winExFlags,
- className, title,
- winFlags,
- CW_USEDEFAULT, CW_USEDEFAULT, wr.right-wr.left, wr.bottom-wr.top,
- (HWND)view->parent, NULL, NULL, NULL);
-
- impl->hdc = GetDC(impl->hwnd);
-
- const int pixelAttrs[] = {
- WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
- WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
- WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
- WGL_DOUBLE_BUFFER_ARB, view->hints.double_buffer,
- WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
- WGL_SAMPLE_BUFFERS_ARB, view->hints.samples ? 1 : 0,
- WGL_SAMPLES_ARB, view->hints.samples,
- WGL_RED_BITS_ARB, view->hints.red_bits,
- WGL_GREEN_BITS_ARB, view->hints.green_bits,
- WGL_BLUE_BITS_ARB, view->hints.blue_bits,
- WGL_ALPHA_BITS_ARB, view->hints.alpha_bits,
- WGL_DEPTH_BITS_ARB, view->hints.depth_bits,
- WGL_STENCIL_BITS_ARB, view->hints.stencil_bits,
- 0,
- };
-
- // Choose pixel format based on hints
- int pixelFormatId;
- UINT numFormats;
- if (!wglChoosePixelFormat(impl->hdc, pixelAttrs, NULL, 1u, &pixelFormatId, &numFormats)) {
- return puglWinError(&fakeWin, 6);
- }
-
- // Set desired pixel format
- PIXELFORMATDESCRIPTOR pfd;
- DescribePixelFormat(impl->hdc, pixelFormatId, sizeof(pfd), &pfd);
- if (!SetPixelFormat(impl->hdc, pixelFormatId, &pfd)) {
- return puglWinError(&fakeWin, 7);
- }
-
- // Create final GL context
- const int contextAttribs[] = {
- WGL_CONTEXT_MAJOR_VERSION_ARB, view->hints.context_version_major,
- WGL_CONTEXT_MINOR_VERSION_ARB, view->hints.context_version_minor,
- WGL_CONTEXT_PROFILE_MASK_ARB, (view->hints.use_compat_profile
- ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB
- : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB),
- 0
- };
-
- if (!(impl->hglrc = wglCreateContextAttribs(impl->hdc, 0, contextAttribs))) {
- return puglWinError(&fakeWin, 8);
- }
-
- // Switch to new context
- wglMakeCurrent(NULL, NULL);
- wglDeleteContext(fakeRc);
- if (!wglMakeCurrent(impl->hdc, impl->hglrc)) {
- return puglWinError(&fakeWin, 9);
- }
-
- ReleaseDC(fakeWin.hwnd, fakeWin.hdc);
- DestroyWindow(fakeWin.hwnd);
- } else {
- // Modern extensions not available, just use the original "fake" window
- impl->hwnd = fakeWin.hwnd;
- impl->hdc = fakeWin.hdc;
- impl->hglrc = fakeRc;
- fakeWin.hwnd = 0;
- fakeWin.hdc = 0;
+ if (!impl->backend->configure) {
+ return 1;
}
- if (wglSwapInterval) {
- wglSwapInterval(1);
+ int st = impl->backend->configure(view);
+ if (st || !impl->surface) {
+ return 2;
+ } else if ((st = impl->backend->create(view))) {
+ return 3;
}
+ SetWindowText(impl->hwnd, title);
SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view);
return 0;
@@ -392,8 +154,7 @@ void
puglDestroy(PuglView* view)
{
if (view) {
- wglMakeCurrent(NULL, NULL);
- wglDeleteContext(view->impl->hglrc);
+ view->impl->backend->destroy(view);
ReleaseDC(view->impl->hwnd, view->impl->hdc);
DestroyWindow(view->impl->hwnd);
UnregisterClass(view->windowClass ? view->windowClass : DEFAULT_CLASSNAME, NULL);
@@ -901,12 +662,6 @@ wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
}
}
-PuglGlFunc
-puglGetProcAddress(const char* name)
-{
- return (PuglGlFunc)wglGetProcAddress(name);
-}
-
double
puglGetTime(PuglView* view)
{
diff --git a/pugl/pugl_win.h b/pugl/pugl_win.h
new file mode 100644
index 0000000..edbd7c6
--- /dev/null
+++ b/pugl/pugl_win.h
@@ -0,0 +1,97 @@
+/*
+ Copyright 2012-2019 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "pugl/pugl_internal_types.h"
+
+#include <windows.h>
+
+#include <stdbool.h>
+
+typedef PIXELFORMATDESCRIPTOR PuglWinPFD;
+
+struct PuglInternalsImpl {
+ PuglWinPFD pfd;
+ int pfId;
+ HWND hwnd;
+ HDC hdc;
+ const PuglBackend* backend;
+ PuglSurface* surface;
+ DWORD refreshRate;
+ double timerFrequency;
+ bool flashing;
+ bool resizing;
+ bool mouseTracked;
+};
+
+static inline PuglWinPFD
+puglWinGetPixelFormatDescriptor(const PuglHints* const hints)
+{
+ const int rgbBits = hints->red_bits + hints->green_bits + hints->blue_bits;
+
+ PuglWinPFD pfd;
+ ZeroMemory(&pfd, sizeof(pfd));
+ pfd.nSize = sizeof(pfd);
+ pfd.nVersion = 1;
+ pfd.dwFlags = PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL;
+ pfd.dwFlags |= hints->double_buffer ? PFD_DOUBLEBUFFER : 0;
+ pfd.iPixelType = PFD_TYPE_RGBA;
+ pfd.cColorBits = (BYTE)rgbBits;
+ pfd.cRedBits = (BYTE)hints->red_bits;
+ pfd.cGreenBits = (BYTE)hints->green_bits;
+ pfd.cBlueBits = (BYTE)hints->blue_bits;
+ pfd.cAlphaBits = (BYTE)hints->alpha_bits;
+ pfd.cDepthBits = (BYTE)hints->depth_bits;
+ pfd.cStencilBits = (BYTE)hints->stencil_bits;
+ pfd.iLayerType = PFD_MAIN_PLANE;
+ return pfd;
+}
+
+static inline PuglStatus
+puglWinCreateWindow(const PuglView* const view,
+ const char* const title,
+ HWND* const hwnd,
+ HDC* const hdc)
+{
+ const char* className = view->windowClass ? view->windowClass : "Pugl";
+
+ const unsigned winFlags =
+ (WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
+ (view->parent
+ ? WS_CHILD
+ : (WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX |
+ (view->hints.resizable ? (WS_SIZEBOX | WS_MAXIMIZEBOX) : 0))));
+
+ const unsigned winExFlags =
+ WS_EX_NOINHERITLAYOUT | (view->parent ? 0u : WS_EX_APPWINDOW);
+
+ // Calculate total window size to accommodate requested view size
+ RECT wr = { 0, 0, view->width, view->height };
+ AdjustWindowRectEx(&wr, winFlags, FALSE, winExFlags);
+
+ // Create window and get drawing context
+ if (!(*hwnd = CreateWindowEx(winExFlags, className, title, winFlags,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ wr.right-wr.left, wr.bottom-wr.top,
+ (HWND)view->parent, NULL, NULL, NULL))) {
+ return PUGL_ERR_CREATE_WINDOW;
+ } else if (!(*hdc = GetDC(*hwnd))) {
+ DestroyWindow(*hwnd);
+ *hwnd = NULL;
+ return PUGL_ERR_CREATE_WINDOW;
+ }
+
+ return PUGL_SUCCESS;
+}
diff --git a/pugl/pugl_win_gl.c b/pugl/pugl_win_gl.c
new file mode 100644
index 0000000..d86a78b
--- /dev/null
+++ b/pugl/pugl_win_gl.c
@@ -0,0 +1,302 @@
+/*
+ Copyright 2012-2019 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "pugl/pugl_gl_backend.h"
+#include "pugl/pugl_internal_types.h"
+#include "pugl/pugl_win.h"
+
+#include <windows.h>
+
+#include <GL/gl.h>
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#define WGL_DRAW_TO_WINDOW_ARB 0x2001
+#define WGL_ACCELERATION_ARB 0x2003
+#define WGL_SUPPORT_OPENGL_ARB 0x2010
+#define WGL_DOUBLE_BUFFER_ARB 0x2011
+#define WGL_PIXEL_TYPE_ARB 0x2013
+#define WGL_COLOR_BITS_ARB 0x2014
+#define WGL_RED_BITS_ARB 0x2015
+#define WGL_GREEN_BITS_ARB 0x2017
+#define WGL_BLUE_BITS_ARB 0x2019
+#define WGL_ALPHA_BITS_ARB 0x201b
+#define WGL_DEPTH_BITS_ARB 0x2022
+#define WGL_STENCIL_BITS_ARB 0x2023
+#define WGL_FULL_ACCELERATION_ARB 0x2027
+#define WGL_TYPE_RGBA_ARB 0x202b
+#define WGL_SAMPLE_BUFFERS_ARB 0x2041
+#define WGL_SAMPLES_ARB 0x2042
+
+#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
+#define WGL_CONTEXT_FLAGS_ARB 0x2094
+#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
+
+#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
+
+typedef HGLRC (*WglCreateContextAttribs)(HDC, HGLRC, const int*);
+typedef BOOL (*WglSwapInterval)(int);
+typedef BOOL (*WglChoosePixelFormat)(
+ HDC, const int*, const FLOAT*, UINT, int*, UINT*);
+
+typedef struct {
+ WglChoosePixelFormat wglChoosePixelFormat;
+ WglCreateContextAttribs wglCreateContextAttribs;
+ WglSwapInterval wglSwapInterval;
+} PuglWinGlProcs;
+
+typedef struct {
+ PuglWinGlProcs procs;
+ HGLRC hglrc;
+} PuglWinGlSurface;
+
+// Struct to manage the fake window used during configuration
+typedef struct {
+ HWND hwnd;
+ HDC hdc;
+} PuglFakeWindow;
+
+static int
+puglWinError(PuglFakeWindow* fakeWin, const int status)
+{
+ if (fakeWin->hwnd) {
+ ReleaseDC(fakeWin->hwnd, fakeWin->hdc);
+ DestroyWindow(fakeWin->hwnd);
+ }
+
+ return status;
+}
+
+static PuglWinGlProcs puglWinGlGetProcs(void)
+{
+ const PuglWinGlProcs procs = {
+ (WglChoosePixelFormat)(
+ wglGetProcAddress("wglChoosePixelFormatARB")),
+ (WglCreateContextAttribs)(
+ wglGetProcAddress("wglCreateContextAttribsARB")),
+ (WglSwapInterval)(
+ wglGetProcAddress("wglSwapIntervalEXT"))
+ };
+
+ return procs;
+}
+
+static int
+puglWinGlConfigure(PuglView* view)
+{
+ PuglInternals* impl = view->impl;
+
+ const int pixelAttrs[] = {
+ WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
+ WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
+ WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
+ WGL_DOUBLE_BUFFER_ARB, view->hints.double_buffer,
+ WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
+ WGL_SAMPLE_BUFFERS_ARB, view->hints.samples ? 1 : 0,
+ WGL_SAMPLES_ARB, view->hints.samples,
+ WGL_RED_BITS_ARB, view->hints.red_bits,
+ WGL_GREEN_BITS_ARB, view->hints.green_bits,
+ WGL_BLUE_BITS_ARB, view->hints.blue_bits,
+ WGL_ALPHA_BITS_ARB, view->hints.alpha_bits,
+ WGL_DEPTH_BITS_ARB, view->hints.depth_bits,
+ WGL_STENCIL_BITS_ARB, view->hints.stencil_bits,
+ 0,
+ };
+
+ PuglWinGlSurface* const surface =
+ (PuglWinGlSurface*)calloc(1, sizeof(PuglWinGlSurface));
+ impl->surface = surface;
+
+ // Create fake window for getting at GL context
+ PuglStatus st = PUGL_SUCCESS;
+ PuglFakeWindow fakeWin = { 0, 0 };
+ if ((st = puglWinCreateWindow(view, "Pugl Configuration",
+ &fakeWin.hwnd, &fakeWin.hdc))) {
+ return puglWinError(&fakeWin, st);
+ }
+
+ // Set pixel format for fake window
+ const PuglWinPFD fakePfd = puglWinGetPixelFormatDescriptor(&view->hints);
+ const int fakePfId = ChoosePixelFormat(fakeWin.hdc, &fakePfd);
+ if (!fakePfId) {
+ return puglWinError(&fakeWin, PUGL_ERR_SET_FORMAT);
+ } else if (!SetPixelFormat(fakeWin.hdc, fakePfId, &fakePfd)) {
+ return puglWinError(&fakeWin, PUGL_ERR_SET_FORMAT);
+ }
+
+ // Create fake GL context to get at the functions we need
+ HGLRC fakeRc = wglCreateContext(fakeWin.hdc);
+ if (!fakeRc) {
+ return puglWinError(&fakeWin, PUGL_ERR_CREATE_CONTEXT);
+ }
+
+ // Enter fake context and get extension functions
+ wglMakeCurrent(fakeWin.hdc, fakeRc);
+ surface->procs = puglWinGlGetProcs();
+
+ if (surface->procs.wglChoosePixelFormat) {
+ // Choose pixel format based on attributes
+ UINT numFormats = 0;
+ if (!surface->procs.wglChoosePixelFormat(
+ fakeWin.hdc, pixelAttrs, NULL, 1u, &impl->pfId, &numFormats)) {
+ return puglWinError(&fakeWin, PUGL_ERR_SET_FORMAT);
+ }
+
+ DescribePixelFormat(
+ impl->hdc, impl->pfId, sizeof(impl->pfd), &impl->pfd);
+ } else {
+ // Modern extensions not available, use basic pixel format
+ impl->pfd = fakePfd;
+ impl->pfId = fakePfId;
+ }
+
+ // Dispose of fake window and context
+ wglMakeCurrent(NULL, NULL);
+ wglDeleteContext(fakeRc);
+ ReleaseDC(fakeWin.hwnd, fakeWin.hdc);
+ DestroyWindow(fakeWin.hwnd);
+
+ return 0;
+}
+
+static int
+puglWinGlCreate(PuglView* view)
+{
+ PuglInternals* const impl = view->impl;
+ PuglWinGlSurface* const surface = (PuglWinGlSurface*)impl->surface;
+ PuglStatus st = PUGL_SUCCESS;
+
+ const int contextAttribs[] = {
+ WGL_CONTEXT_MAJOR_VERSION_ARB, view->hints.context_version_major,
+ WGL_CONTEXT_MINOR_VERSION_ARB, view->hints.context_version_minor,
+ WGL_CONTEXT_PROFILE_MASK_ARB,
+ (view->hints.use_compat_profile
+ ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB
+ : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB),
+ 0
+ };
+
+ // Create real window with desired pixel format
+ if ((st = puglWinCreateWindow(view, "Pugl", &impl->hwnd, &impl->hdc))) {
+ return st;
+ } else if (!SetPixelFormat(impl->hdc, impl->pfId, &impl->pfd)) {
+ ReleaseDC(impl->hwnd, impl->hdc);
+ DestroyWindow(impl->hwnd);
+ impl->hwnd = NULL;
+ impl->hdc = NULL;
+ return PUGL_ERR_SET_FORMAT;
+ }
+
+ // Create GL context
+ if (surface->procs.wglCreateContextAttribs &&
+ !(surface->hglrc = surface->procs.wglCreateContextAttribs(
+ impl->hdc, 0, contextAttribs))) {
+ return PUGL_ERR_CREATE_CONTEXT;
+ } else if (!(surface->hglrc = wglCreateContext(impl->hdc))) {
+ return PUGL_ERR_CREATE_CONTEXT;
+ }
+
+ // Enter context and set swap interval
+ wglMakeCurrent(impl->hdc, surface->hglrc);
+ if (surface->procs.wglSwapInterval) {
+ surface->procs.wglSwapInterval(1);
+ }
+
+ return 0;
+}
+
+static int
+puglWinGlDestroy(PuglView* view)
+{
+ PuglWinGlSurface* surface = (PuglWinGlSurface*)view->impl->surface;
+ if (surface) {
+ wglMakeCurrent(NULL, NULL);
+ wglDeleteContext(surface->hglrc);
+ free(surface);
+ view->impl->surface = NULL;
+ }
+
+ return 0;
+}
+
+static int
+puglWinGlEnter(PuglView* view, bool drawing)
+{
+ PuglWinGlSurface* surface = (PuglWinGlSurface*)view->impl->surface;
+
+ wglMakeCurrent(view->impl->hdc, surface->hglrc);
+
+ if (drawing) {
+ PAINTSTRUCT ps;
+ BeginPaint(view->impl->hwnd, &ps);
+ }
+
+ return 0;
+}
+
+static int
+puglWinGlLeave(PuglView* view, bool drawing)
+{
+ if (drawing) {
+ PAINTSTRUCT ps;
+ EndPaint(view->impl->hwnd, &ps);
+ SwapBuffers(view->impl->hdc);
+ }
+
+ wglMakeCurrent(NULL, NULL);
+
+ return 0;
+}
+
+static int
+puglWinGlResize(PuglView* PUGL_UNUSED(view),
+ int PUGL_UNUSED(width),
+ int PUGL_UNUSED(height))
+{
+ return 0;
+}
+
+static void*
+puglWinGlGetContext(PuglView* PUGL_UNUSED(view))
+{
+ return NULL;
+}
+
+PuglGlFunc
+puglGetProcAddress(const char* name)
+{
+ return (PuglGlFunc)wglGetProcAddress(name);
+}
+
+const PuglBackend*
+puglGlBackend()
+{
+ static const PuglBackend backend = {
+ puglWinGlConfigure,
+ puglWinGlCreate,
+ puglWinGlDestroy,
+ puglWinGlEnter,
+ puglWinGlLeave,
+ puglWinGlResize,
+ puglWinGlGetContext
+ };
+
+ return &backend;
+}