aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md6
-rw-r--r--include/mgl/gl.h14
-rw-r--r--include/mgl/glx.h10
-rw-r--r--include/mgl/mgl.h4
-rw-r--r--include/mgl/window.h17
-rw-r--r--project.conf3
-rw-r--r--src/gl.c53
-rw-r--r--src/glx.c26
-rw-r--r--src/mgl.c86
-rw-r--r--src/window.c94
-rw-r--r--tests/main.c31
-rw-r--r--tests/project.conf3
12 files changed, 331 insertions, 16 deletions
diff --git a/README.md b/README.md
index b96efe3..80de338 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,11 @@
# Minimal Graphics Library
+Written in C.
# Dependencies
## Build
`xlib`
## Runtime
-`libglvnd (libGLX.so, libOpenGL.so)` \ No newline at end of file
+`libglvnd (libGLX.so, libOpenGL.so)`
+
+# TODO
+vsync \ No newline at end of file
diff --git a/include/mgl/gl.h b/include/mgl/gl.h
new file mode 100644
index 0000000..1791059
--- /dev/null
+++ b/include/mgl/gl.h
@@ -0,0 +1,14 @@
+#ifndef MGL_GL_H
+#define MGL_GL_H
+
+typedef struct {
+ void *handle;
+ void (*glViewport)(int x, int y, int width, int height);
+ void (*glClearColor)(float red, float green, float blue, float alpha);
+ void (*glClear)(unsigned int mask);
+} mgl_gl;
+
+int mgl_gl_load(mgl_gl *self);
+void mgl_gl_unload(mgl_gl *self);
+
+#endif /* MGL_GL_H */
diff --git a/include/mgl/glx.h b/include/mgl/glx.h
index dfb736d..4197402 100644
--- a/include/mgl/glx.h
+++ b/include/mgl/glx.h
@@ -1,12 +1,18 @@
#ifndef MGL_GLX_H
#define MGL_GLX_H
+typedef struct _XVisualInfo _XVisualInfo;
typedef struct _XDisplay Display;
-typedef struct __GLXFBConfigRec *GLXFBConfig;
+typedef struct __GLXcontextRec *GLXContext;
+typedef unsigned long GLXDrawable;
typedef struct {
void *handle;
- GLXFBConfig* (*glXGetFBConfigs)(Display *dpy, int screen, int *nelements);
+ _XVisualInfo* (*glXChooseVisual)(Display *dpy, int screen, int *attribList);
+ GLXContext (*glXCreateContext)(Display *dpy, _XVisualInfo *vis, GLXContext shareList, int direct);
+ void (*glXDestroyContext)(Display *dpy, GLXContext ctx);
+ int (*glXMakeCurrent)(Display *dpy, GLXDrawable drawable, GLXContext ctx);
+ void (*glXSwapBuffers)(Display *dpy, GLXDrawable drawable);
} mgl_glx;
int mgl_glx_load(mgl_glx *self);
diff --git a/include/mgl/mgl.h b/include/mgl/mgl.h
index d896497..606c14b 100644
--- a/include/mgl/mgl.h
+++ b/include/mgl/mgl.h
@@ -2,13 +2,17 @@
#define MGL_MGL_H
#include "glx.h"
+#include "gl.h"
/* Display* on x11 */
typedef void* mgl_connection;
typedef struct {
mgl_connection connection;
+ GLXContext glx_context;
+ _XVisualInfo *visual_info;
mgl_glx glx;
+ mgl_gl gl;
} mgl_context;
/*
diff --git a/include/mgl/window.h b/include/mgl/window.h
index bd29936..c6dc9e2 100644
--- a/include/mgl/window.h
+++ b/include/mgl/window.h
@@ -1,12 +1,23 @@
#ifndef MGL_WINDOW_H
#define MGL_WINDOW_H
+typedef struct mgl_window mgl_window;
+
typedef struct {
- int booba;
-} mgl_window;
+ void (*draw)(mgl_window *window, void *userdata);
+} mgl_window_callback;
+
+struct mgl_window {
+ unsigned long window;
+ mgl_window_callback callback;
+ void *callback_userdata;
+};
-void mgl_window_init(mgl_window *self);
+int mgl_window_create(mgl_window *self, const char *title, int width, int height, mgl_window_callback *callback, void *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);
void mgl_window_deinit(mgl_window *self);
+
void mgl_window_show(mgl_window *self);
+void mgl_window_event_poll(mgl_window *self, int timeout_ms);
#endif /* MGL_WINDOW_H */
diff --git a/project.conf b/project.conf
index f9d0fdc..62353a2 100644
--- a/project.conf
+++ b/project.conf
@@ -4,5 +4,8 @@ type = "static"
version = "0.1.0"
platforms = ["posix"]
+[config]
+expose_include_dirs = ["include"]
+
[dependencies]
x11 = ">=1" \ No newline at end of file
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);
+ }
+ }
}
diff --git a/tests/main.c b/tests/main.c
index ff1570b..e6a4f5a 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -1,6 +1,35 @@
#include <stdio.h>
+#include <mgl/mgl.h>
+#include <mgl/window.h>
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+static void draw(mgl_window *window, void *userdata) {
+ glBegin(GL_QUADS);
+ glColor3f(1., 0., 0.); glVertex3f(-.75, -.75, 0.);
+ glColor3f(0., 1., 0.); glVertex3f( .75, -.75, 0.);
+ glColor3f(0., 0., 1.); glVertex3f( .75, .75, 0.);
+ glColor3f(1., 1., 0.); glVertex3f(-.75, .75, 0.);
+ glEnd();
+}
int main(int argc, char **argv) {
- printf("hello, world!\n");
+ if(mgl_init() != 0)
+ return 1;
+
+ mgl_window_callback window_callback;
+ window_callback.draw = draw;
+
+ mgl_window window;
+ if(mgl_window_create(&window, "mgl", 1280, 720, &window_callback, NULL) != 0)
+ return 1;
+
+ mgl_window_show(&window);
+ for(;;) {
+ mgl_window_event_poll(&window, 500);
+ }
+
+ mgl_window_deinit(&window);
+ mgl_deinit();
return 0;
}
diff --git a/tests/project.conf b/tests/project.conf
new file mode 100644
index 0000000..3c15235
--- /dev/null
+++ b/tests/project.conf
@@ -0,0 +1,3 @@
+[dependencies]
+gl = ">=1"
+glu = ">=9"