586 lines
17 KiB
C
586 lines
17 KiB
C
// TODO: Add all the error handling to the glx part
|
|
|
|
#include "openwindow.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
int initOpenGL() {
|
|
if (getOpenGLProcs() == 0) return -1;
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glEnable(GL_BLEND);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define OPENWINDOW_OPENGL_VERSSION_MAJOR 3
|
|
#define OPENWINDOW_OPENGL_VERSSION_MINOR 0
|
|
|
|
#ifndef _WIN32
|
|
|
|
#include <unistd.h>
|
|
|
|
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
|
|
|
|
void windowDefaultEventHandler(Window * w, XEvent xev) {
|
|
switch (xev.type)
|
|
{
|
|
case KeyPress:
|
|
case KeyRelease:
|
|
XQueryKeymap(w->x.display, w->io.keyboard.current);
|
|
break;
|
|
case MotionNotify:
|
|
w->io.mouse.x = xev.xmotion.x;
|
|
w->io.mouse.y = xev.xmotion.y;
|
|
break;
|
|
case ButtonPress:
|
|
w->io.mouse.current[xev.xbutton.button - 1] = true;
|
|
break;
|
|
case ButtonRelease:
|
|
w->io.mouse.current[xev.xbutton.button - 1] = false;
|
|
break;
|
|
case ConfigureNotify:
|
|
w->width = xev.xconfigure.width;
|
|
w->height = xev.xconfigure.height;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void windowEventHandler(Window * w, XEvent event) {
|
|
windowDefaultEventHandler(w, event);
|
|
}
|
|
|
|
bool checkGLXVersion(Window window) {
|
|
int glx_major, glx_minor;
|
|
|
|
if (!glXQueryVersion(window.x.display, &glx_major, &glx_minor)) {
|
|
return false;
|
|
}
|
|
|
|
return (glx_major == 1 && glx_minor >= 3) || ( glx_major > 1);
|
|
}
|
|
|
|
GLXFBConfig getBestFBConfig(Window window, GLXFBConfig * configs, size_t count) {
|
|
int best = 0, best_samp = -1;
|
|
|
|
for (size_t i = 0; i < count; ++i)
|
|
{
|
|
XVisualInfo * vi = glXGetVisualFromFBConfig(window.x.display, configs[i]);
|
|
|
|
if (!vi) continue;
|
|
|
|
int samp_buf, samples;
|
|
|
|
glXGetFBConfigAttrib(window.x.display, configs[i], GLX_SAMPLE_BUFFERS, &samp_buf);
|
|
glXGetFBConfigAttrib(window.x.display, configs[i], GLX_SAMPLES , &samples);
|
|
|
|
if (samp_buf && samples > best_samp)
|
|
best = i, best_samp = samples;
|
|
|
|
XFree(vi);
|
|
}
|
|
|
|
return configs[best];
|
|
}
|
|
|
|
int initGLX(Window * window)
|
|
{
|
|
if (!checkGLXVersion(*window))
|
|
return -1;
|
|
|
|
int attribs[] = {
|
|
GLX_X_RENDERABLE , True,
|
|
GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT,
|
|
GLX_RENDER_TYPE , GLX_RGBA_BIT,
|
|
GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR,
|
|
GLX_RED_SIZE , 8,
|
|
GLX_GREEN_SIZE , 8,
|
|
GLX_BLUE_SIZE , 8,
|
|
GLX_ALPHA_SIZE , 8,
|
|
GLX_DEPTH_SIZE , 24,
|
|
GLX_STENCIL_SIZE , 8,
|
|
GLX_DOUBLEBUFFER , True,
|
|
//GLX_SAMPLE_BUFFERS , 1,
|
|
//GLX_SAMPLES , 4,
|
|
None
|
|
};
|
|
|
|
int fbcount;
|
|
GLXFBConfig * configs = glXChooseFBConfig(window->x.display, DefaultScreen(window->x.display), attribs, &fbcount);
|
|
if (!configs || !fbcount) {
|
|
fprintf(stderr, "Failed to retrieve a framebuffer config\n");
|
|
return -1;
|
|
}
|
|
|
|
window->glx.config = getBestFBConfig(*window, configs, fbcount);
|
|
|
|
XFree(configs);
|
|
|
|
glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
|
|
glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
|
|
glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" );
|
|
|
|
int context_attribs[] = {
|
|
GLX_CONTEXT_MAJOR_VERSION_ARB, OPENWINDOW_OPENGL_VERSSION_MAJOR,
|
|
GLX_CONTEXT_MINOR_VERSION_ARB, OPENWINDOW_OPENGL_VERSSION_MINOR,
|
|
GLX_CONTEXT_PROFILE_MASK_ARB , GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
None
|
|
};
|
|
window->glx.context = glXCreateContextAttribsARB(window->x.display, window->glx.config, 0, True, context_attribs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int init(Window * window, const char * name, size_t width, size_t height) {
|
|
window->x.display = XOpenDisplay(NULL);
|
|
|
|
if (window->x.display == NULL) {
|
|
fprintf(stderr, "Failed to open xdisplay!\n");
|
|
return -1;
|
|
}
|
|
|
|
if (initGLX(window))
|
|
return -1;
|
|
|
|
XVisualInfo * vi = glXGetVisualFromFBConfig(window->x.display, window->glx.config);
|
|
|
|
XSetWindowAttributes wa;
|
|
|
|
wa.border_pixel = 0;
|
|
wa.colormap = window->x.cmap = XCreateColormap(window->x.display,
|
|
RootWindow(window->x.display, vi->screen),
|
|
vi->visual, AllocNone);
|
|
|
|
window->x.eventMask = wa.event_mask =
|
|
StructureNotifyMask |
|
|
ExposureMask |
|
|
PointerMotionMask |
|
|
KeyPressMask |
|
|
KeyReleaseMask |
|
|
ButtonPressMask |
|
|
ButtonReleaseMask;
|
|
|
|
X11Window root = RootWindow(window->x.display, vi->screen);
|
|
|
|
window->x.window = XCreateWindow(
|
|
window->x.display,
|
|
root,
|
|
0,
|
|
0,
|
|
width,
|
|
height,
|
|
0,
|
|
vi->depth,
|
|
InputOutput,
|
|
vi->visual,
|
|
CWBorderPixel|CWColormap|CWEventMask,
|
|
&wa);
|
|
|
|
if (window->x.window == None) {
|
|
fprintf(stderr, "Failed to create x11 window!\n");
|
|
return -1;
|
|
}
|
|
|
|
XFree(vi);
|
|
|
|
XMapWindow(window->x.display, window->x.window);
|
|
XStoreName(window->x.display, window->x.window, name);
|
|
|
|
XSync(window->x.display, False);
|
|
glXMakeCurrent(window->x.display, window->x.window, window->glx.context);
|
|
|
|
//TODO: Check for errors
|
|
PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)
|
|
glXGetProcAddress((const GLubyte *)"glXSwapIntervalEXT");
|
|
|
|
if (glXSwapIntervalEXT) glXSwapIntervalEXT(window->x.display, window->x.window, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
Window * openWindow(const char * name, size_t width, size_t height) {
|
|
Window * w = calloc(1, sizeof(Window));
|
|
w->fps = WINDOW_DEFAULT_FPS;
|
|
w->dt.t1 = calloc(1, sizeof(struct timespec));
|
|
w->dt.t2 = calloc(1, sizeof(struct timespec));
|
|
|
|
w->width = width;
|
|
w->height = height;
|
|
|
|
if (init(w, name, width, height) < 0) {
|
|
fprintf(stderr, "Failed to initialize window: X11 error!\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (initOpenGL() < 0) {
|
|
fprintf(stderr, "Failed to initialize window: GLAD/OPENGL error!\n");
|
|
exit(1);
|
|
}
|
|
|
|
return w;
|
|
}
|
|
|
|
void windowSetFps(Window * window, uint32_t fps) {
|
|
window->fps = fps;
|
|
}
|
|
|
|
double windowGetDeltaTime(Window *window) {
|
|
return window->dt.dt;
|
|
}
|
|
|
|
void windowGetSize(Window * window, int * width, int * height) {
|
|
*width = window->width;
|
|
*height = window->height;
|
|
}
|
|
|
|
void windowDraw(Window * window) {
|
|
glXSwapBuffers(window->x.display, window->x.window);
|
|
|
|
usleep(((float)1 / window->fps) * 1e6);
|
|
clock_gettime(CLOCK_MONOTONIC, window->dt.t2);
|
|
|
|
window->dt.dt = (double) (window->dt.t2->tv_sec - window->dt.t1->tv_sec) + (window->dt.t2->tv_nsec - window->dt.t1->tv_nsec) / 1e9;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, window->dt.t1);
|
|
}
|
|
|
|
void resetIOState(Window * window) {
|
|
memcpy(window->io.keyboard.last, window->io.keyboard.current, WINDOW_KEY_COUNT);
|
|
memcpy(window->io.mouse.last, window->io.mouse.current, WINDOW_MOUSE_BUTTON_COUNT);
|
|
}
|
|
|
|
void windowHandleEvents(Window * window) {
|
|
|
|
XEvent xev = {0};
|
|
|
|
resetIOState(window);
|
|
|
|
while (XPending(window->x.display)) {
|
|
if (XCheckWindowEvent(window->x.display, window->x.window, window->x.eventMask, &xev)) {
|
|
windowEventHandler(window, xev);
|
|
}
|
|
}
|
|
}
|
|
|
|
void closeWindow(Window * window) {
|
|
glXMakeCurrent(window->x.display, None, None);
|
|
glXDestroyContext(window->x.display, window->glx.context);
|
|
|
|
XFreeColormap(window->x.display, window->x.cmap);
|
|
XDestroyWindow(window->x.display, window->x.window);
|
|
XCloseDisplay(window->x.display);
|
|
|
|
free(window->dt.t1);
|
|
free(window->dt.t2);
|
|
|
|
free(window);
|
|
}
|
|
|
|
bool windowMousePressed(Window * window, int button) {
|
|
return !window->io.mouse.last[button - 1] && window->io.mouse.current[button - 1];
|
|
}
|
|
|
|
bool windowMouseHeld(Window * window, int button) {
|
|
return window->io.mouse.last[button - 1] && window->io.mouse.current[button - 1];
|
|
}
|
|
|
|
bool windowMouseReleased(Window * window, int button) {
|
|
return window->io.mouse.last[button - 1] && !window->io.mouse.current[button - 1];
|
|
}
|
|
|
|
void windowGetMousePosition(Window * window, int * x, int * y) {
|
|
if (x == NULL || y == NULL) return;
|
|
|
|
*x = window->io.mouse.x;
|
|
*y = window->io.mouse.y;
|
|
}
|
|
|
|
bool windowKeyPressed(Window * window, char * key) {
|
|
KeySym keysym = XStringToKeysym(key);
|
|
if (keysym == NoSymbol) return false;
|
|
|
|
KeyCode keycode = XKeysymToKeycode(window->x.display, keysym);
|
|
|
|
return !(window->io.keyboard.last[keycode >> 3] & (1 << (keycode & 7))) && (window->io.keyboard.current[keycode >> 3] & (1 << (keycode & 7)));
|
|
}
|
|
|
|
bool windowKeyHeld(Window * window, char * key) {
|
|
KeySym keysym = XStringToKeysym(key);
|
|
if (keysym == NoSymbol) return false;
|
|
|
|
KeyCode keycode = XKeysymToKeycode(window->x.display, keysym);
|
|
|
|
return (window->io.keyboard.last[keycode >> 3] & (1 << (keycode & 7))) && (window->io.keyboard.current[keycode >> 3] & (1 << (keycode & 7)));
|
|
}
|
|
|
|
bool windowKeyReleased(Window * window, char * key) {
|
|
KeySym keysym = XStringToKeysym(key);
|
|
if (keysym == NoSymbol) return false;
|
|
|
|
KeyCode keycode = XKeysymToKeycode(window->x.display, keysym);
|
|
|
|
return (window->io.keyboard.last[keycode >> 3] & (1 << (keycode & 7))) && !(window->io.keyboard.current[keycode >> 3] & (1 << (keycode & 7)));
|
|
}
|
|
|
|
bool windowKeyShift(Window * window) {
|
|
KeyCode sl = XKeysymToKeycode(window->x.display, XK_Shift_L);
|
|
KeyCode sr = XKeysymToKeycode(window->x.display, XK_Shift_R);
|
|
|
|
return (window->io.keyboard.last[sl >> 3] & (1 << (sl & 7))) || (window->io.keyboard.current[sr >> 3] & (1 << (sr & 7)));
|
|
}
|
|
|
|
bool windowKeyCtrl(Window * window) {
|
|
KeyCode cl = XKeysymToKeycode(window->x.display, XK_Control_L);
|
|
KeyCode cr = XKeysymToKeycode(window->x.display, XK_Control_R);
|
|
|
|
return (window->io.keyboard.last[cl >> 3] & (1 << (cl & 7))) || (window->io.keyboard.current[cr >> 3] & (1 << (cr & 7)));
|
|
}
|
|
|
|
bool windowKeyAlt(Window * window) {
|
|
KeyCode al = XKeysymToKeycode(window->x.display, XK_Alt_L);
|
|
KeyCode ar = XKeysymToKeycode(window->x.display, XK_Alt_R);
|
|
|
|
return (window->io.keyboard.last[al >> 3] & (1 << (al & 7))) || (window->io.keyboard.current[ar >> 3] & (1 << (ar & 7)));
|
|
}
|
|
|
|
#else
|
|
|
|
#include <windows.h>
|
|
#include <GL/wgl.h>
|
|
|
|
//TODO: Change this every time we open a window
|
|
#define WND_CLASS_NAME "OpenWindowWndClass"
|
|
|
|
void windowDefaultEventHandler(Window * window, unsigned int event, WPARAM wParam, LPARAM lParam) {
|
|
(void) wParam;
|
|
(void) lParam;
|
|
|
|
switch (event)
|
|
{
|
|
case WM_KEYDOWN:
|
|
case WM_KEYUP:
|
|
GetKeyboardState(window->io.current);
|
|
break;
|
|
case WM_TIMER:
|
|
InvalidateRect(window->wgl.window, NULL, FALSE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
LRESULT CALLBACK windowEventHandler(HWND hwnd, unsigned int event, WPARAM wParam, LPARAM lParam) {
|
|
Window * window = (Window *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
if (window == NULL) goto uninit;
|
|
|
|
windowDefaultEventHandler(window, event, wParam, lParam);
|
|
|
|
uninit:
|
|
return (DefWindowProc(hwnd, event, wParam, lParam));
|
|
}
|
|
|
|
int init(Window * window, const char * title, size_t width, size_t height) {
|
|
window->wgl.instance = GetModuleHandle(NULL);
|
|
WNDCLASSEX wcex;
|
|
|
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
wcex.style = CS_CLASSDC;
|
|
wcex.lpfnWndProc = windowEventHandler;
|
|
wcex.cbClsExtra = 0;
|
|
wcex.cbWndExtra = 0;
|
|
wcex.hInstance = window->wgl.instance;
|
|
wcex.hIcon = NULL;
|
|
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wcex.hbrBackground = 0;
|
|
wcex.lpszMenuName = NULL;
|
|
wcex.lpszClassName = WND_CLASS_NAME;
|
|
wcex.hIconSm = NULL;
|
|
|
|
RegisterClassEx(&wcex);
|
|
|
|
window->wgl.window = CreateWindow(
|
|
WND_CLASS_NAME,
|
|
title,
|
|
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
width,
|
|
height,
|
|
NULL,
|
|
NULL,
|
|
window->wgl.instance,
|
|
NULL
|
|
);
|
|
|
|
if (window->wgl.window == NULL) {
|
|
fprintf(stderr, "Failed to create window!\n");
|
|
return -1;
|
|
}
|
|
|
|
ShowWindow(window->wgl.window, SW_SHOW);
|
|
|
|
SetWindowLongPtr(window->wgl.window, GWLP_USERDATA, (LONG_PTR)window);
|
|
|
|
window->wgl.display = GetDC(window->wgl.window);
|
|
PIXELFORMATDESCRIPTOR pfd = {0};
|
|
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
|
|
pfd.nVersion = 1;
|
|
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
|
|
pfd.iPixelType = PFD_TYPE_RGBA;
|
|
pfd.cColorBits = 32;
|
|
pfd.cRedBits = 0;
|
|
pfd.cRedShift = 0;
|
|
pfd.cGreenBits = 0;
|
|
pfd.cGreenShift = 0;
|
|
pfd.cBlueBits = 0;
|
|
pfd.cBlueShift = 0;
|
|
pfd.cAlphaBits = 0;
|
|
pfd.cAlphaShift = 0;
|
|
pfd.cAccumBits = 0;
|
|
pfd.cAccumRedBits = 0;
|
|
pfd.cAccumGreenBits = 0;
|
|
pfd.cAccumBlueBits = 0;
|
|
pfd.cAccumAlphaBits = 0;
|
|
pfd.cDepthBits = 24;
|
|
pfd.cStencilBits = 8;
|
|
pfd.cAuxBuffers = 0;
|
|
pfd.iLayerType = PFD_MAIN_PLANE;
|
|
pfd.bReserved = 0;
|
|
pfd.dwLayerMask = 0;
|
|
pfd.dwVisibleMask = 0;
|
|
pfd.dwDamageMask = 0;
|
|
|
|
int format = ChoosePixelFormat(window->wgl.display, &pfd);
|
|
SetPixelFormat(window->wgl.display, format, &pfd);
|
|
|
|
HGLRC tempContext = wglCreateContext(window->wgl.display);
|
|
if (tempContext == NULL) return -1;
|
|
|
|
if (!wglMakeCurrent(window->wgl.display, tempContext)) return -1;
|
|
|
|
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)(void *)wglGetProcAddress("wglCreateContextAttribsARB");
|
|
if (wglCreateContextAttribsARB == NULL) return -1;
|
|
|
|
int attribs[] = {
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, OPENWINDOW_OPENGL_VERSSION_MAJOR,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, OPENWINDOW_OPENGL_VERSSION_MINOR,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
0
|
|
};
|
|
|
|
window->wgl.context = wglCreateContextAttribsARB(window->wgl.display, 0, attribs);
|
|
if (window->wgl.context == NULL) {
|
|
fprintf(stderr, "Failed to create wgl context!\n");
|
|
return -1;
|
|
}
|
|
|
|
wglMakeCurrent(window->wgl.display, NULL);
|
|
wglDeleteContext(tempContext);
|
|
|
|
wglMakeCurrent(window->wgl.display, window->wgl.context);
|
|
|
|
SetTimer(window->wgl.window, 1, (1 / window->fps) * 1000, NULL);
|
|
|
|
//TODO: Check for errors
|
|
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = (void *)wglGetProcAddress("wglSwapIntervalEXT");
|
|
if (wglSwapIntervalEXT) wglSwapIntervalEXT(0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
Window * openWindow(const char * name, size_t width, size_t height) {
|
|
Window * w = calloc(1, sizeof(Window));
|
|
w->fps = WINDOW_DEFAULT_FPS;
|
|
|
|
w->width = width;
|
|
w->height = height;
|
|
|
|
if (init(w, name, width, height) < 0) {
|
|
fprintf(stderr, "Failed to initialize wgl!\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (initOpenGL() < 0) {
|
|
fprintf(stderr, "Failed to initialize window: GLAD/OPENGL error!\n");
|
|
exit(1);
|
|
}
|
|
|
|
return w;
|
|
}
|
|
|
|
void windowSetFps(Window * window, uint32_t fps) {
|
|
window->fps = fps;
|
|
}
|
|
|
|
double windowGetDeltaTime(Window * window) {
|
|
return window->dt.dt;
|
|
}
|
|
|
|
void windowDraw(Window * window) {
|
|
SwapBuffers(window->wgl.display);
|
|
|
|
Sleep((uint32_t)(((double)1 / window->fps) * 1e3));
|
|
|
|
QueryPerformanceFrequency(&window->dt.freq);
|
|
QueryPerformanceCounter(&window->dt.t2);
|
|
|
|
window->dt.dt = (double) (window->dt.t2.QuadPart - window->dt.t1.QuadPart) / window->dt.freq.QuadPart;
|
|
|
|
QueryPerformanceCounter(&window->dt.t1);
|
|
}
|
|
|
|
void resetIOState(Window * window) {
|
|
memcpy(window->io.last, window->io.current, WINDOW_KEY_COUNT);
|
|
}
|
|
|
|
void windowHandleEvents(Window * window) {
|
|
(void) window;
|
|
|
|
resetIOState(window);
|
|
|
|
MSG msg = {0};
|
|
GetMessage(&msg, NULL, 0, 0);
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
void closeWindow(Window * window) {
|
|
wglMakeCurrent(NULL, NULL);
|
|
wglDeleteContext(window->wgl.context);
|
|
ReleaseDC(window->wgl.window, window->wgl.display);
|
|
|
|
DestroyWindow(window->wgl.window);
|
|
UnregisterClass(WND_CLASS_NAME, window->wgl.instance);
|
|
free(window);
|
|
}
|
|
|
|
bool windowKeyPressed(Window * window, int key) {
|
|
return !(window->io.last[key] & 0x80) && (window->io.current[key] & 0x80);
|
|
}
|
|
|
|
bool windowKeyHeld(Window * window, int key) {
|
|
return (window->io.last[key] & 0x80) && (window->io.current[key] & 0x80);
|
|
}
|
|
|
|
bool windowKeyReleased(Window * window, int key) {
|
|
return (window->io.last[key] & 0x80) && !(window->io.current[key] & 0x80);
|
|
}
|
|
|
|
bool windowKeyShift(Window * window) {
|
|
return window->io.current[VK_SHIFT] & 0x80;
|
|
}
|
|
|
|
bool windowKeyCtrl(Window * window) {
|
|
return window->io.current[VK_CONTROL] & 0x80;
|
|
}
|
|
|
|
bool windowKeyAlt(Window * window) {
|
|
return window->io.current[VK_MENU] & 0x80;
|
|
}
|
|
|
|
#endif // _WIN32
|
|
|
|
int getOpenGLProcs(void) {
|
|
return gladLoadGL();
|
|
}
|