openwindow/src/openwindow.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();
}