diff --git a/gl-natives/pom.xml b/gl-natives/pom.xml
index 3f92aff..2b36aff 100644
--- a/gl-natives/pom.xml
+++ b/gl-natives/pom.xml
@@ -12,4 +12,31 @@
nar
OpenRS2 GL Natives
+
+
+
+
+ com.github.maven-nar
+ nar-maven-plugin
+
+
+
+
+ shared
+ GL
+
+
+ shared
+ GLX
+
+
+ shared
+ jawt
+
+
+
+
+
+
+
diff --git a/gl-natives/src/main/c/jaggl.c b/gl-natives/src/main/c/jaggl.c
new file mode 100644
index 0000000..910e241
--- /dev/null
+++ b/gl-natives/src/main/c/jaggl.c
@@ -0,0 +1,258 @@
+#include "jaggl_context.h"
+#include "jaggl_opengl.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#define JAGGL_LOCK(env) \
+ JAWT awt = { .version = JAWT_VERSION_1_4 }; \
+ bool awt_valid = JAWT_GetAWT(env, &awt); \
+ if (awt_valid) { \
+ awt.Lock(env); \
+ }
+
+#define JAGGL_UNLOCK(env) \
+ if (awt_valid) { \
+ awt.Unlock(env); \
+ }
+
+static Display *jaggl_display;
+static XVisualInfo *jaggl_visual_info;
+static VisualID jaggl_visual_id;
+static GLXContext jaggl_context;
+static GLXDrawable jaggl_drawable;
+static int jaggl_alpha_bits;
+static bool jaggl_double_buffered;
+
+static PFNGLXSWAPINTERVALSGIPROC jaggl_glXSwapIntervalSGI;
+
+static void jaggl_init_proc_table(void) {
+ jaggl_glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC) glXGetProcAddressARB((const GLubyte *) "glXSwapIntervalSGI");
+}
+
+JNIEXPORT jboolean JNICALL Java_jaggl_context_createContext(JNIEnv *env, jclass cls) {
+ JAGGL_LOCK(env);
+
+ GLXContext current = glXGetCurrentContext();
+ if (current) {
+ glXMakeCurrent(jaggl_display, None, NULL);
+ }
+
+ if (jaggl_context) {
+ glXDestroyContext(jaggl_display, jaggl_context);
+ jaggl_context = NULL;
+ }
+
+ jaggl_context = glXCreateContext(jaggl_display, jaggl_visual_info, NULL, True);
+
+ JAGGL_UNLOCK(env);
+ return jaggl_context != NULL;
+}
+
+JNIEXPORT jboolean JNICALL Java_jaggl_context_releaseContext(JNIEnv *env, jclass cls) {
+ JAGGL_LOCK(env);
+
+ jboolean result = JNI_TRUE;
+
+ GLXContext current = glXGetCurrentContext();
+ if (current) {
+ result = (jboolean) glXMakeCurrent(jaggl_display, None, NULL);
+ }
+
+ JAGGL_UNLOCK(env);
+ return result;
+}
+
+JNIEXPORT jboolean JNICALL Java_jaggl_context_destroy(JNIEnv *env, jclass cls) {
+ JAGGL_LOCK(env);
+
+ GLXContext current = glXGetCurrentContext();
+ if (current) {
+ glXMakeCurrent(jaggl_display, None, NULL);
+ }
+
+ if (jaggl_context) {
+ glXDestroyContext(jaggl_display, jaggl_context);
+ jaggl_context = NULL;
+ }
+
+ if (jaggl_visual_info) {
+ XFree(jaggl_visual_info);
+ jaggl_visual_info = NULL;
+ }
+
+ jaggl_display = None;
+
+ JAGGL_UNLOCK(env);
+ return JNI_TRUE;
+}
+
+JNIEXPORT jboolean JNICALL Java_jaggl_context_swapBuffers(JNIEnv *env, jclass cls) {
+ JAGGL_LOCK(env);
+
+ if (jaggl_double_buffered) {
+ glXSwapBuffers(jaggl_display, jaggl_drawable);
+ } else {
+ glFlush();
+ }
+
+ JAGGL_UNLOCK(env);
+ return JNI_TRUE;
+}
+
+JNIEXPORT jint JNICALL Java_jaggl_context_getLastError(JNIEnv *env, jclass cls) {
+ return 0;
+}
+
+JNIEXPORT void JNICALL Java_jaggl_context_setSwapInterval(JNIEnv *env, jclass cls, jint interval) {
+ JAGGL_LOCK(env);
+
+ if (jaggl_glXSwapIntervalSGI) {
+ jaggl_glXSwapIntervalSGI(interval);
+ }
+
+ JAGGL_UNLOCK(env);
+}
+
+JNIEXPORT jstring JNICALL Java_jaggl_context_getExtensionsString(JNIEnv *env, jclass cls) {
+ JAGGL_LOCK(env);
+
+ const char *extensions_str = glXQueryExtensionsString(jaggl_display, jaggl_visual_info->screen);
+ jstring extensions = (*env)->NewStringUTF(env, extensions_str);
+
+ JAGGL_UNLOCK(env);
+ return extensions;
+}
+
+JNIEXPORT jint JNICALL Java_jaggl_context_getAlphaBits(JNIEnv *env, jclass cls) {
+ return jaggl_alpha_bits;
+}
+
+JNIEXPORT jboolean JNICALL Java_jaggl_context_choosePixelFormat1(JNIEnv *env, jclass cls, jobject component, jint num_samples, jint alpha_bits) {
+ JAGGL_LOCK(env);
+
+ jboolean result = JNI_FALSE;
+
+ if (!awt_valid) {
+ goto awt_unlock;
+ }
+
+ JAWT_DrawingSurface *ds = awt.GetDrawingSurface(env, component);
+ if (!ds) {
+ goto awt_unlock;
+ }
+
+ jint lock_result = ds->Lock(ds);
+ if ((lock_result & JAWT_LOCK_ERROR) != 0) {
+ goto ds_free;
+ }
+
+ JAWT_DrawingSurfaceInfo *dsi = ds->GetDrawingSurfaceInfo(ds);
+ if (!dsi) {
+ goto ds_unlock;
+ }
+
+ JAWT_X11DrawingSurfaceInfo *platformInfo = (JAWT_X11DrawingSurfaceInfo *) dsi->platformInfo;
+ if (!platformInfo) {
+ goto dsi_free;
+ }
+
+ jaggl_display = platformInfo->display;
+ jaggl_drawable = platformInfo->drawable;
+ jaggl_visual_id = platformInfo->visualID;
+
+ if (!glXQueryExtension(jaggl_display, NULL, NULL)) {
+ goto dsi_free;
+ }
+
+ XWindowAttributes window_attribs;
+ if (XGetWindowAttributes(jaggl_display, jaggl_drawable, &window_attribs)) {
+ XVisualInfo visual_info_template = { .visualid = window_attribs.visual->visualid };
+ int matches;
+ jaggl_visual_info = XGetVisualInfo(jaggl_display, VisualIDMask, &visual_info_template, &matches);
+ if (jaggl_visual_info) {
+ int value;
+ glXGetConfig(jaggl_display, jaggl_visual_info, GLX_DOUBLEBUFFER, &value);
+ jaggl_double_buffered = value;
+
+ glXGetConfig(jaggl_display, jaggl_visual_info, GLX_ALPHA_SIZE, &value);
+ jaggl_alpha_bits = value;
+
+ result = JNI_TRUE;
+ goto dsi_free;
+ }
+ }
+
+ for (int i = 0; i < 2; i++) {
+ bool double_buffered = i == 0;
+ int attribs[] = {
+ GLX_RGBA,
+ GLX_RED_SIZE,
+ 8,
+ GLX_GREEN_SIZE,
+ 8,
+ GLX_BLUE_SIZE,
+ 8,
+ GLX_ALPHA_SIZE,
+ alpha_bits,
+ GLX_DEPTH_SIZE,
+ 24,
+ GLX_SAMPLE_BUFFERS,
+ num_samples ? True : False,
+ GLX_SAMPLES,
+ num_samples,
+ double_buffered ? GLX_DOUBLEBUFFER : None,
+ None
+ };
+ jaggl_visual_info = glXChooseVisual(jaggl_display, DefaultScreen(jaggl_display), attribs);
+ if (jaggl_visual_info) {
+ jaggl_double_buffered = double_buffered;
+ jaggl_alpha_bits = alpha_bits;
+
+ result = JNI_TRUE;
+ goto dsi_free;
+ }
+ }
+
+dsi_free:
+ ds->FreeDrawingSurfaceInfo(dsi);
+ds_unlock:
+ ds->Unlock(ds);
+ds_free:
+ awt.FreeDrawingSurface(ds);
+awt_unlock:
+ JAGGL_UNLOCK(env);
+ return result;
+}
+
+JNIEXPORT jboolean JNICALL Java_jaggl_context_makeCurrent1(JNIEnv *env, jclass cls) {
+ JAGGL_LOCK(env);
+
+ jboolean result = JNI_FALSE;
+
+ if (!jaggl_context) {
+ goto done;
+ }
+
+ GLXContext current = glXGetCurrentContext();
+ if (jaggl_context == current) {
+ result = JNI_TRUE;
+ goto done;
+ }
+
+ glXMakeCurrent(jaggl_display, None, NULL);
+
+ if (!glXMakeCurrent(jaggl_display, jaggl_drawable, jaggl_context)) {
+ goto done;
+ }
+
+ jaggl_init_proc_table();
+ result = JNI_TRUE;
+
+done:
+ JAGGL_UNLOCK(env);
+ return result;
+}