aboutsummaryrefslogtreecommitdiffstats
path: root/pugl/pugl_win_gl.c
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/pugl_win_gl.c
parent6a77f96642b201f614ce7eb67f3b6ec4e1e8c181 (diff)
downloadpugl-b63194bf2536b156bbda688b3bf9917a5dcdc3f3.tar.gz
pugl-b63194bf2536b156bbda688b3bf9917a5dcdc3f3.tar.bz2
pugl-b63194bf2536b156bbda688b3bf9917a5dcdc3f3.zip
Windows: Factor out GL backend
Diffstat (limited to 'pugl/pugl_win_gl.c')
-rw-r--r--pugl/pugl_win_gl.c302
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;
+}