aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gl.c53
-rw-r--r--src/glx.c26
-rw-r--r--src/mgl.c86
-rw-r--r--src/window.c94
4 files changed, 250 insertions, 9 deletions
diff --git a/src/gl.c b/src/gl.c
new file mode 100644
index 0000000..c83ab49
--- /dev/null
+++ b/src/gl.c
@@ -0,0 +1,53 @@
+#include "../include/mgl/gl.h"
+#include <dlfcn.h>
+/*#include <GL/gl.h>*/
+#include <stdio.h>
+
+typedef struct {
+ void **func;
+ const char *name;
+} dlsym_assign;
+
+static void* dlsym_print_fail(void *handle, const char *name) {
+ dlerror();
+ void *sym = dlsym(handle, name);
+ char *err_str = dlerror();
+
+ if(!sym)
+ fprintf(stderr, "dlsym(handle, \"%s\") failed, error: %s\n", name, err_str ? err_str : "(null)");
+
+ return sym;
+}
+
+int mgl_gl_load(mgl_gl *self) {
+ const char *gl_path = "/usr/lib/libOpenGL.so.0";
+ self->handle = dlopen(gl_path, RTLD_LAZY);
+ if(!self->handle) {
+ fprintf(stderr, "dlopen(\"%s\", RTLD_LAZY) failed\n", gl_path);
+ return -1;
+ }
+
+ const dlsym_assign assign[] = {
+ { &self->glViewport, "glViewport" },
+ { &self->glClearColor, "glClearColor" },
+ { &self->glClear, "glClear" },
+ { NULL, NULL }
+ };
+
+ for(int i = 0; assign[i].func; ++i) {
+ *assign[i].func = dlsym_print_fail(self->handle, assign[i].name);
+ if(!assign[i].func) {
+ mgl_gl_unload(self);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void mgl_gl_unload(mgl_gl *self) {
+ if(self->handle) {
+ dlclose(self->handle);
+ self->handle = NULL;
+ }
+}
diff --git a/src/glx.c b/src/glx.c
index 4a5af9d..6712947 100644
--- a/src/glx.c
+++ b/src/glx.c
@@ -1,7 +1,12 @@
#include "../include/mgl/glx.h"
#include <dlfcn.h>
+/*#include <GL/glx.h>*/
#include <stdio.h>
-#include <GL/glx.h>
+
+typedef struct {
+ void **func;
+ const char *name;
+} dlsym_assign;
static void* dlsym_print_fail(void *handle, const char *name) {
dlerror();
@@ -22,10 +27,21 @@ int mgl_glx_load(mgl_glx *self) {
return -1;
}
- self->glXGetFBConfigs = (GLXFBConfig* (*)(Display*, int, int*))dlsym_print_fail(self->handle, "glXGetFBConfigs");
- if(!self->glXGetFBConfigs) {
- mgl_glx_unload(self);
- return -1;
+ const dlsym_assign assign[] = {
+ { &self->glXChooseVisual, "glXChooseVisual" },
+ { &self->glXCreateContext, "glXCreateContext" },
+ { &self->glXDestroyContext, "glXDestroyContext" },
+ { &self->glXMakeCurrent, "glXMakeCurrent" },
+ { &self->glXSwapBuffers, "glXSwapBuffers" },
+ { NULL, NULL }
+ };
+
+ for(int i = 0; assign[i].func; ++i) {
+ *assign[i].func = dlsym_print_fail(self->handle, assign[i].name);
+ if(!assign[i].func) {
+ mgl_glx_unload(self);
+ return -1;
+ }
}
return 0;
diff --git a/src/mgl.c b/src/mgl.c
index a7221d0..27d33ae 100644
--- a/src/mgl.c
+++ b/src/mgl.c
@@ -1,7 +1,26 @@
#include "../include/mgl/mgl.h"
-#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <stdlib.h>
#include <stdio.h>
+#define GLX_USE_GL 1
+#define GLX_BUFFER_SIZE 2
+#define GLX_LEVEL 3
+#define GLX_RGBA 4
+#define GLX_DOUBLEBUFFER 5
+#define GLX_STEREO 6
+#define GLX_AUX_BUFFERS 7
+#define GLX_RED_SIZE 8
+#define GLX_GREEN_SIZE 9
+#define GLX_BLUE_SIZE 10
+#define GLX_ALPHA_SIZE 11
+#define GLX_DEPTH_SIZE 12
+#define GLX_STENCIL_SIZE 13
+#define GLX_ACCUM_RED_SIZE 14
+#define GLX_ACCUM_GREEN_SIZE 15
+#define GLX_ACCUM_BLUE_SIZE 16
+#define GLX_ACCUM_ALPHA_SIZE 17
+
static mgl_context context;
static int init_count = 0;
static XErrorHandler prev_xerror = NULL;
@@ -12,6 +31,46 @@ static int ignore_xerror(Display *display, XErrorEvent *ee) {
return 0;
}
+static int glx_context_init() {
+ const int attr[] = {
+ GLX_RGBA,
+ GLX_DEPTH_SIZE, 0,
+ GLX_STENCIL_SIZE, 0,
+ GLX_RED_SIZE, 8,
+ GLX_GREEN_SIZE, 8,
+ GLX_BLUE_SIZE, 8,
+ GLX_ALPHA_SIZE, 0,
+ GLX_DOUBLEBUFFER, /* TODO: Add option to turn this off? */
+ None
+ };
+
+ context.visual_info = context.glx.glXChooseVisual(context.connection, DefaultScreen(context.connection), (int*)attr);
+ if(!context.visual_info) {
+ fprintf(stderr, "glXChooseVisual failed, no appropriate visual found\n");
+ return -1;
+ }
+
+ context.glx_context = context.glx.glXCreateContext(context.connection, context.visual_info, NULL, 1);
+ if(!context.glx_context) {
+ fprintf(stderr, "glXCreateContext failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void glx_context_deinit() {
+ if(context.glx_context) {
+ context.glx.glXDestroyContext(context.connection, context.glx_context);
+ context.glx_context = NULL;
+ }
+
+ if(context.visual_info) {
+ XFree(context.visual_info);
+ context.visual_info = NULL;
+ }
+}
+
int mgl_init(void) {
++init_count;
if(init_count == 1) {
@@ -23,20 +82,35 @@ int mgl_init(void) {
}
prev_xerror = XSetErrorHandler(ignore_xerror);
+
if(mgl_glx_load(&context.glx) != 0) {
mgl_deinit();
return -1;
}
+
+ if(mgl_gl_load(&context.gl) != 0) {
+ mgl_deinit();
+ return -1;
+ }
+
+ if(glx_context_init() != 0) {
+ mgl_deinit();
+ return -1;
+ }
}
return 0;
}
void mgl_deinit(void) {
if(init_count == 1) {
+ glx_context_deinit();
+ mgl_gl_unload(&context.gl);
mgl_glx_unload(&context.glx);
XSetErrorHandler(prev_xerror);
- XCloseDisplay(context.connection);
- context.connection = NULL;
+ if(context.connection) {
+ XCloseDisplay(context.connection);
+ context.connection = NULL;
+ }
}
if(init_count > 0)
@@ -44,5 +118,11 @@ void mgl_deinit(void) {
}
mgl_context* mgl_get_context(void) {
+#ifndef NDEBUG
+ if(init_count == 0) {
+ fprintf(stderr, "Error: mgl_get_context was called before mgl_init\n");
+ abort();
+ }
+#endif
return &context;
}
diff --git a/src/window.c b/src/window.c
index 39855da..9836907 100644
--- a/src/window.c
+++ b/src/window.c
@@ -1,13 +1,105 @@
#include "../include/mgl/window.h"
+#include "../include/mgl/mgl.h"
+#include <X11/Xutil.h>
+#include <stdio.h>
-void mgl_window_init(mgl_window *self) {
+#define GL_COLOR_BUFFER_BIT 0x00004000
+int mgl_window_create(mgl_window *self, const char *title, int width, int height, mgl_window_callback *callback, void *userdata) {
+ return mgl_window_create_with_params(self, title, width, height, DefaultRootWindow(mgl_get_context()->connection), callback, userdata);
+}
+
+int mgl_window_create_with_params(mgl_window *self, const char *title, int width, int height, unsigned long parent_window, mgl_window_callback *callback, void *userdata) {
+ self->window = 0;
+ self->callback = *callback;
+ self->callback_userdata = userdata;
+
+ mgl_context *context = mgl_get_context();
+
+ Colormap color_map = XCreateColormap(context->connection, DefaultRootWindow(context->connection), ((XVisualInfo*)context->visual_info)->visual, AllocNone);
+ if(!color_map) {
+ fprintf(stderr, "XCreateColormap failed\n");
+ return -1;
+ }
+
+ XSetWindowAttributes window_attr;
+ window_attr.colormap = color_map;
+ window_attr.event_mask = ExposureMask | KeyPressMask;
+
+ self->window = XCreateWindow(context->connection, parent_window, 0, 0, width, height, 0, ((XVisualInfo*)context->visual_info)->depth, InputOutput, ((XVisualInfo*)context->visual_info)->visual, CWColormap | CWEventMask, &window_attr);
+ //XFreeColormap(context->connection, color_map);
+ if(!self->window) {
+ fprintf(stderr, "XCreateWindow failed\n");
+ mgl_window_deinit(self);
+ return -1;
+ }
+
+ /* TODO: Test utf8 */
+ XStoreName(context->connection, self->window, title);
+ return 0;
}
void mgl_window_deinit(mgl_window *self) {
+ mgl_context *context = mgl_get_context();
+ if(self->window) {
+ XDestroyWindow(context->connection, self->window);
+ self->window = 0;
+ }
+}
+
+static void mgl_window_draw(mgl_window *self) {
+ mgl_context *context = mgl_get_context();
+
+ /* TODO: Get window size from window resize event instead */
+ XWindowAttributes gwa;
+ XGetWindowAttributes(context->connection, self->window, &gwa);
+
+ context->gl.glViewport(0, 0, gwa.width, gwa.height);
+ context->gl.glClear(GL_COLOR_BUFFER_BIT);
+ context->gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ if(self->callback.draw)
+ self->callback.draw(self, self->callback_userdata);
+ context->glx.glXSwapBuffers(context->connection, self->window);
}
void mgl_window_show(mgl_window *self) {
+ mgl_context *context = mgl_get_context();
+
+ XMapWindow(context->connection, self->window);
+ XFlush(context->connection);
+
+ /* TODO: Switch current when rendering to another window, and set current to NULL when destroying the currently selected context */
+ context->glx.glXMakeCurrent(context->connection, self->window, context->glx_context);
+ mgl_window_draw(self);
+}
+
+static void on_receive_x11_event(mgl_window *window, XEvent *xev) {
+ switch(xev->type) {
+ case Expose:
+ mgl_window_draw(window);
+ break;
+ }
+}
+
+void mgl_window_event_poll(mgl_window *self, int timeout_ms) {
+ Display *display = mgl_get_context()->connection;
+ const int x11_fd = ConnectionNumber(display);
+
+ fd_set in_fds;
+ FD_ZERO(&in_fds); /* TODO: Optimize */
+ FD_SET(x11_fd, &in_fds);
+
+ struct timeval tv;
+ tv.tv_sec = timeout_ms / 1000;
+ tv.tv_usec = (timeout_ms * 1000) - (tv.tv_sec * 1000 * 1000);
+ const int num_ready_fds = select(1 + x11_fd, &in_fds, NULL, NULL, &tv);
+ if(num_ready_fds > 0) {
+ XEvent xev;
+ while(XPending(display)) {
+ XNextEvent(display, &xev);
+ on_receive_x11_event(self, &xev);
+ }
+ }
}