diff options
Diffstat (limited to 'pugl/pugl_win_gl.c')
-rw-r--r-- | pugl/pugl_win_gl.c | 302 |
1 files changed, 302 insertions, 0 deletions
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; +} |