392 lines
10 KiB
C
392 lines
10 KiB
C
/*
|
|
* Copyright © 2014 Jon Turney
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
* Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include "windowsgl.h"
|
|
#include "windowsgl_internal.h"
|
|
|
|
#include "glapi.h"
|
|
#include "wgl.h"
|
|
|
|
#include <dlfcn.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <strings.h>
|
|
|
|
static struct _glapi_table *windows_api = NULL;
|
|
|
|
static void *
|
|
windows_get_dl_handle(void)
|
|
{
|
|
static void *dl_handle = NULL;
|
|
|
|
if (!dl_handle)
|
|
dl_handle = dlopen("cygnativeGLthunk.dll", RTLD_NOW);
|
|
|
|
return dl_handle;
|
|
}
|
|
|
|
static void
|
|
windows_glapi_create_table(void)
|
|
{
|
|
if (windows_api)
|
|
return;
|
|
|
|
windows_api = _glapi_create_table_from_handle(windows_get_dl_handle(), "gl");
|
|
assert(windows_api);
|
|
}
|
|
|
|
static void windows_glapi_set_dispatch(void)
|
|
{
|
|
windows_glapi_create_table();
|
|
_glapi_set_dispatch(windows_api);
|
|
}
|
|
|
|
windowsContext *
|
|
windows_create_context(int pxfi, windowsContext *shared)
|
|
{
|
|
windowsContext *gc;
|
|
|
|
gc = calloc(1, sizeof *gc);
|
|
if (gc == NULL)
|
|
return NULL;
|
|
|
|
// create a temporary, invisible window
|
|
#define GL_TEMP_WINDOW_CLASS "glTempWndClass"
|
|
{
|
|
static wATOM glTempWndClass = 0;
|
|
|
|
if (glTempWndClass == 0) {
|
|
WNDCLASSEX wc;
|
|
wc.cbSize = sizeof(WNDCLASSEX);
|
|
wc.style = CS_HREDRAW | CS_VREDRAW;
|
|
wc.lpfnWndProc = DefWindowProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = GetModuleHandle(NULL);
|
|
wc.hIcon = 0;
|
|
wc.hCursor = 0;
|
|
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = GL_TEMP_WINDOW_CLASS;
|
|
wc.hIconSm = 0;
|
|
RegisterClassEx(&wc);
|
|
}
|
|
}
|
|
|
|
HWND hwnd = CreateWindowExA(0,
|
|
GL_TEMP_WINDOW_CLASS,
|
|
"glWindow",
|
|
0,
|
|
0, 0, 0, 0,
|
|
NULL, NULL, GetModuleHandle(NULL), NULL);
|
|
HDC hdc = GetDC(hwnd);
|
|
|
|
// We must set the windows pixel format before we can create a WGL context
|
|
gc->pxfi = pxfi;
|
|
SetPixelFormat(hdc, gc->pxfi, NULL);
|
|
|
|
gc->ctx = wglCreateContext(hdc);
|
|
|
|
if (shared && gc->ctx)
|
|
wglShareLists(shared->ctx, gc->ctx);
|
|
|
|
ReleaseDC(hwnd, hdc);
|
|
DestroyWindow(hwnd);
|
|
|
|
if (!gc->ctx)
|
|
{
|
|
free(gc);
|
|
return NULL;
|
|
}
|
|
|
|
return gc;
|
|
}
|
|
|
|
windowsContext *
|
|
windows_create_context_attribs(int pxfi, windowsContext *shared, const int *attribList)
|
|
{
|
|
windowsContext *gc;
|
|
|
|
gc = calloc(1, sizeof *gc);
|
|
if (gc == NULL)
|
|
return NULL;
|
|
|
|
// create a temporary, invisible window
|
|
#define GL_TEMP_WINDOW_CLASS "glTempWndClass"
|
|
{
|
|
static wATOM glTempWndClass = 0;
|
|
|
|
if (glTempWndClass == 0) {
|
|
WNDCLASSEX wc;
|
|
wc.cbSize = sizeof(WNDCLASSEX);
|
|
wc.style = CS_HREDRAW | CS_VREDRAW;
|
|
wc.lpfnWndProc = DefWindowProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = GetModuleHandle(NULL);
|
|
wc.hIcon = 0;
|
|
wc.hCursor = 0;
|
|
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = GL_TEMP_WINDOW_CLASS;
|
|
wc.hIconSm = 0;
|
|
RegisterClassEx(&wc);
|
|
}
|
|
}
|
|
|
|
HWND hwnd = CreateWindowExA(0,
|
|
GL_TEMP_WINDOW_CLASS,
|
|
"glWindow",
|
|
0,
|
|
0, 0, 0, 0,
|
|
NULL, NULL, GetModuleHandle(NULL), NULL);
|
|
HDC hdc = GetDC(hwnd);
|
|
HGLRC shareContext = NULL;
|
|
if (shared)
|
|
shareContext = shared->ctx;
|
|
|
|
// We must set the windows pixel format before we can create a WGL context
|
|
gc->pxfi = pxfi;
|
|
SetPixelFormat(hdc, gc->pxfi, NULL);
|
|
|
|
gc->ctx = wglCreateContextAttribsARB(hdc, shareContext, attribList);
|
|
|
|
ReleaseDC(hwnd, hdc);
|
|
DestroyWindow(hwnd);
|
|
|
|
if (!gc->ctx)
|
|
{
|
|
free(gc);
|
|
return NULL;
|
|
}
|
|
|
|
return gc;
|
|
}
|
|
|
|
void
|
|
windows_destroy_context(windowsContext *context)
|
|
{
|
|
wglDeleteContext(context->ctx);
|
|
context->ctx = NULL;
|
|
free(context);
|
|
}
|
|
|
|
int windows_bind_context(windowsContext *context, windowsDrawable *draw, windowsDrawable *read)
|
|
{
|
|
HDC drawDc = draw->callbacks->getdc(draw);
|
|
|
|
if (!draw->pxfi)
|
|
{
|
|
SetPixelFormat(drawDc, context->pxfi, NULL);
|
|
draw->pxfi = context->pxfi;
|
|
}
|
|
|
|
if ((read != NULL) && (read != draw))
|
|
{
|
|
/*
|
|
If there is a separate read drawable, create a separate read DC, and
|
|
use the wglMakeContextCurrent extension to make the context current
|
|
drawing to one DC and reading from the other
|
|
|
|
Should only occur when WGL_ARB_make_current_read extension is present
|
|
*/
|
|
HDC readDc = read->callbacks->getdc(read);
|
|
|
|
BOOL ret = wglMakeContextCurrentARB(drawDc, readDc, context->ctx);
|
|
|
|
read->callbacks->releasedc(read, readDc);
|
|
|
|
if (!ret) {
|
|
printf("wglMakeContextCurrentARB error: %08x\n", (int)GetLastError());
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, just use wglMakeCurrent */
|
|
BOOL ret = wglMakeCurrent(drawDc, context->ctx);
|
|
if (!ret) {
|
|
printf("wglMakeCurrent error: %08x\n", (int)GetLastError());
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
draw->callbacks->releasedc(draw, drawDc);
|
|
|
|
windows_glapi_set_dispatch();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void windows_unbind_context(windowsContext * context)
|
|
{
|
|
wglMakeCurrent(NULL, NULL);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
|
|
void
|
|
windows_swap_buffers(windowsDrawable *draw)
|
|
{
|
|
HDC drawDc = GetDC(draw->hWnd);
|
|
SwapBuffers(drawDc);
|
|
ReleaseDC(draw->hWnd, drawDc);
|
|
}
|
|
|
|
|
|
typedef void (__stdcall * PFNGLADDSWAPHINTRECTWIN) (GLint x, GLint y,
|
|
GLsizei width,
|
|
GLsizei height);
|
|
|
|
static void
|
|
glAddSwapHintRectWIN(GLint x, GLint y, GLsizei width,
|
|
GLsizei height)
|
|
{
|
|
PFNGLADDSWAPHINTRECTWIN proc = (PFNGLADDSWAPHINTRECTWIN)wglGetProcAddress("glAddSwapHintRectWIN");
|
|
if (proc)
|
|
proc(x, y, width, height);
|
|
}
|
|
|
|
void
|
|
windows_copy_subbuffer(windowsDrawable *draw,
|
|
int x, int y, int width, int height)
|
|
{
|
|
glAddSwapHintRectWIN(x, y, width, height);
|
|
windows_swap_buffers(draw);
|
|
}
|
|
|
|
/*
|
|
* Helper function for calling a test function on an initial context
|
|
*/
|
|
static void
|
|
windows_call_with_context(void (*proc)(HDC, void *), void *args)
|
|
{
|
|
// create window class
|
|
#define WIN_GL_TEST_WINDOW_CLASS "GLTest"
|
|
{
|
|
static wATOM glTestWndClass = 0;
|
|
|
|
if (glTestWndClass == 0) {
|
|
WNDCLASSEX wc;
|
|
|
|
wc.cbSize = sizeof(WNDCLASSEX);
|
|
wc.style = CS_HREDRAW | CS_VREDRAW;
|
|
wc.lpfnWndProc = DefWindowProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = GetModuleHandle(NULL);
|
|
wc.hIcon = 0;
|
|
wc.hCursor = 0;
|
|
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = WIN_GL_TEST_WINDOW_CLASS;
|
|
wc.hIconSm = 0;
|
|
glTestWndClass = RegisterClassEx(&wc);
|
|
}
|
|
}
|
|
|
|
// create an invisible window for a scratch DC
|
|
HWND hwnd = CreateWindowExA(0,
|
|
WIN_GL_TEST_WINDOW_CLASS,
|
|
"GL Renderer Capabilities Test Window",
|
|
0, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(NULL),
|
|
NULL);
|
|
if (hwnd) {
|
|
HDC hdc = GetDC(hwnd);
|
|
|
|
// we must set a pixel format before we can create a context, just use the first one...
|
|
SetPixelFormat(hdc, 1, NULL);
|
|
HGLRC hglrc = wglCreateContext(hdc);
|
|
wglMakeCurrent(hdc, hglrc);
|
|
|
|
// call the test function
|
|
proc(hdc, args);
|
|
|
|
// clean up
|
|
wglMakeCurrent(NULL, NULL);
|
|
wglDeleteContext(hglrc);
|
|
ReleaseDC(hwnd, hdc);
|
|
DestroyWindow(hwnd);
|
|
}
|
|
}
|
|
|
|
static void
|
|
windows_check_render_test(HDC hdc, void *args)
|
|
{
|
|
int *result = (int *)args;
|
|
|
|
/* Rather than play linkage games using stdcall to ensure we get
|
|
glGetString from opengl32.dll here, use dlsym */
|
|
void *dlhandle = windows_get_dl_handle();
|
|
const char *(*proc)(int) = dlsym(dlhandle, "glGetString");
|
|
const char *gl_renderer = (const char *)proc(GL_RENDERER);
|
|
|
|
if ((!gl_renderer) || (strcasecmp(gl_renderer, "GDI Generic") == 0))
|
|
*result = FALSE;
|
|
else
|
|
*result = TRUE;
|
|
}
|
|
|
|
int
|
|
windows_check_renderer(void)
|
|
{
|
|
int result;
|
|
windows_call_with_context(windows_check_render_test, &result);
|
|
return result;
|
|
}
|
|
|
|
typedef struct {
|
|
char *gl_extensions;
|
|
char *wgl_extensions;
|
|
} windows_extensions_result;
|
|
|
|
static void
|
|
windows_extensions_test(HDC hdc, void *args)
|
|
{
|
|
windows_extensions_result *r = (windows_extensions_result *)args;
|
|
|
|
void *dlhandle = windows_get_dl_handle();
|
|
const char *(*proc)(int) = dlsym(dlhandle, "glGetString");
|
|
|
|
r->gl_extensions = strdup(proc(GL_EXTENSIONS));
|
|
|
|
wglResolveExtensionProcs();
|
|
r->wgl_extensions = strdup(wglGetExtensionsStringARB(hdc));
|
|
}
|
|
|
|
void
|
|
windows_extensions(char **gl_extensions, char **wgl_extensions)
|
|
{
|
|
windows_extensions_result result;
|
|
|
|
*gl_extensions = "";
|
|
*wgl_extensions = "";
|
|
|
|
windows_call_with_context(windows_extensions_test, &result);
|
|
|
|
*gl_extensions = result.gl_extensions;
|
|
*wgl_extensions = result.wgl_extensions;
|
|
}
|