diff options
author | David Robillard <d@drobilla.net> | 2019-07-26 20:38:49 +0200 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2019-07-28 19:06:39 +0200 |
commit | b63194bf2536b156bbda688b3bf9917a5dcdc3f3 (patch) | |
tree | bda995f4e05f6d6cbdb35da0583cd977d5fd7ac5 /pugl | |
parent | 6a77f96642b201f614ce7eb67f3b6ec4e1e8c181 (diff) | |
download | pugl-b63194bf2536b156bbda688b3bf9917a5dcdc3f3.tar.gz pugl-b63194bf2536b156bbda688b3bf9917a5dcdc3f3.tar.bz2 pugl-b63194bf2536b156bbda688b3bf9917a5dcdc3f3.zip |
Windows: Factor out GL backend
Diffstat (limited to 'pugl')
-rw-r--r-- | pugl/pugl.h | 5 | ||||
-rw-r--r-- | pugl/pugl_win.c | 293 | ||||
-rw-r--r-- | pugl/pugl_win.h | 97 | ||||
-rw-r--r-- | pugl/pugl_win_gl.c | 302 |
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; +} |