aboutsummaryrefslogtreecommitdiffstats
path: root/pugl
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2012-04-28 23:34:25 +0000
committerDavid Robillard <d@drobilla.net>2012-04-28 23:34:25 +0000
commit43083a0ca80a876286b6a6ef39ec6f10c9b6f9bf (patch)
treecd835b2ce67291826979ce6fa8a15de046bc6151 /pugl
parent0ed09c95b5d94eb4e109bd402de00c46036b8239 (diff)
downloadpugl-43083a0ca80a876286b6a6ef39ec6f10c9b6f9bf.tar.gz
pugl-43083a0ca80a876286b6a6ef39ec6f10c9b6f9bf.tar.bz2
pugl-43083a0ca80a876286b6a6ef39ec6f10c9b6f9bf.zip
Initial import.
Diffstat (limited to 'pugl')
-rw-r--r--pugl/pugl.h154
-rw-r--r--pugl/pugl_internal.h92
-rw-r--r--pugl/pugl_x11.c242
3 files changed, 488 insertions, 0 deletions
diff --git a/pugl/pugl.h b/pugl/pugl.h
new file mode 100644
index 0000000..853a754
--- /dev/null
+++ b/pugl/pugl.h
@@ -0,0 +1,154 @@
+/*
+ Copyright 2012 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file pugl.h API for Pugl, a portable micro-framework for GL UIs.
+*/
+
+#ifndef PUGL_H_INCLUDED
+#define PUGL_H_INCLUDED
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#else
+# include <stdbool.h>
+#endif
+
+typedef struct PuglWindowImpl PuglWindow;
+
+/**
+ A native window handle.
+
+ For X11, this is a Window.
+*/
+typedef intptr_t PuglNativeWindow;
+
+typedef enum {
+ PUGL_SUCCESS = 0
+} PuglStatus;
+
+/**
+ Handle for opaque user data.
+*/
+typedef void* PuglHandle;
+
+typedef void (*PuglCloseFunc)(PuglWindow* handle);
+typedef void (*PuglDisplayFunc)(PuglWindow* handle);
+typedef void (*PuglKeyboardFunc)(PuglWindow* handle, bool press, uint32_t key);
+typedef void (*PuglMotionFunc)(PuglWindow* handle, int x, int y);
+typedef void (*PuglMouseFunc)(PuglWindow* handle,
+ int button, int state,
+ int x, int y);
+typedef void (*PuglReshapeFunc)(PuglWindow* handle, int width, int height);
+
+/**
+ Create a new GL window.
+ @param parent Parent window, or 0 for top level.
+ @param title Window title, or NULL.
+ @param width Window width in pixels.
+ @param height Window height in pixels.
+*/
+PuglWindow*
+puglCreate(PuglNativeWindow parent, const char* title, int width, int height);
+
+/**
+ Set the handle to be passed to all callbacks.
+
+ This is generally a pointer to a struct which contains all necessary state.
+ Everything needed in callbacks should be here, not in static variables.
+
+ Note the lack of this facility makes GLUT unsuitable for plugins or
+ non-trivial programs; this mistake is largely why Pugl exists.
+*/
+void
+puglSetHandle(PuglWindow* window, PuglHandle handle);
+
+/**
+ Get the handle to be passed to all callbacks.
+*/
+PuglHandle
+puglGetHandle(PuglWindow* window);
+
+/**
+ Set the function to call when the window is closed.
+*/
+void
+puglSetCloseFunc(PuglWindow* window, PuglCloseFunc closeFunc);
+
+/**
+ Set the display function which should draw the UI using GL.
+*/
+void
+puglSetDisplayFunc(PuglWindow* window, PuglDisplayFunc displayFunc);
+
+/**
+ Set the function to call on keyboard events.
+*/
+void
+puglSetKeyboardFunc(PuglWindow* window, PuglKeyboardFunc keyboardFunc);
+
+/**
+ Set the function to call on mouse motion.
+*/
+void
+puglSetMotionFunc(PuglWindow* window, PuglMotionFunc motionFunc);
+
+/**
+ Set the function to call on mouse button events.
+*/
+void
+puglSetMouseFunc(PuglWindow* window, PuglMouseFunc mouseFunc);
+
+/**
+ Set the function to call when the window size changes.
+*/
+void
+puglSetReshapeFunc(PuglWindow* window, PuglReshapeFunc reshapeFunc);
+
+/**
+ Return the native window handle.
+*/
+PuglNativeWindow
+puglGetNativeWindow(PuglWindow* win);
+
+/**
+ Process all pending window events.
+
+ This handles input events as well as rendering, so it should be called
+ regularly and rapidly enough to keep the UI responsive.
+*/
+PuglStatus
+puglProcessEvents(PuglWindow* win);
+
+/**
+ Request a redisplay on the next call to puglProcessEvents().
+*/
+void
+puglPostRedisplay(PuglWindow* win);
+
+/**
+ Destroy a GL window.
+*/
+void
+puglDestroy(PuglWindow* win);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* PUGL_H_INCLUDED */
diff --git a/pugl/pugl_internal.h b/pugl/pugl_internal.h
new file mode 100644
index 0000000..8a7b217
--- /dev/null
+++ b/pugl/pugl_internal.h
@@ -0,0 +1,92 @@
+/*
+ Copyright 2012 David Robillard <http://drobilla.net>
+ Copyright 2011-2012 Ben Loftis, Harrison Consoles
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file pugl_internal.h Private platform-independent definitions.
+
+ Note this file contains function definitions, so it must be compiled into
+ the final binary exactly once. Each platform specific implementation file
+ including it once should achieve this.
+*/
+
+#include "pugl.h"
+
+typedef struct PuglPlatformDataImpl PuglPlatformData;
+
+struct PuglWindowImpl {
+ PuglHandle handle;
+ PuglCloseFunc closeFunc;
+ PuglDisplayFunc displayFunc;
+ PuglKeyboardFunc keyboardFunc;
+ PuglMotionFunc motionFunc;
+ PuglMouseFunc mouseFunc;
+ PuglReshapeFunc reshapeFunc;
+
+ PuglPlatformData* impl;
+
+ int width;
+ int height;
+ bool redisplay;
+};
+
+void
+puglSetHandle(PuglWindow* window, PuglHandle handle)
+{
+ window->handle = handle;
+}
+
+PuglHandle
+puglGetHandle(PuglWindow* window)
+{
+ return window->handle;
+}
+
+void
+puglSetCloseFunc(PuglWindow* window, PuglCloseFunc closeFunc)
+{
+ window->closeFunc = closeFunc;
+}
+
+void
+puglSetDisplayFunc(PuglWindow* window, PuglDisplayFunc displayFunc)
+{
+ window->displayFunc = displayFunc;
+}
+
+void
+puglSetKeyboardFunc(PuglWindow* window, PuglKeyboardFunc keyboardFunc)
+{
+ window->keyboardFunc = keyboardFunc;
+}
+
+void
+puglSetMotionFunc(PuglWindow* window, PuglMotionFunc motionFunc)
+{
+ window->motionFunc = motionFunc;
+}
+
+void
+puglSetMouseFunc(PuglWindow* window, PuglMouseFunc mouseFunc)
+{
+ window->mouseFunc = mouseFunc;
+}
+
+void
+puglSetReshapeFunc(PuglWindow* window, PuglReshapeFunc reshapeFunc)
+{
+ window->reshapeFunc = reshapeFunc;
+}
diff --git a/pugl/pugl_x11.c b/pugl/pugl_x11.c
new file mode 100644
index 0000000..33f0b42
--- /dev/null
+++ b/pugl/pugl_x11.c
@@ -0,0 +1,242 @@
+/*
+ Copyright 2012 David Robillard <http://drobilla.net>
+ Copyright 2011-2012 Ben Loftis, Harrison Consoles
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <GL/gl.h>
+#include <GL/glx.h>
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+
+#include "pugl_internal.h"
+
+struct PuglPlatformDataImpl {
+ Display* display;
+ int screen;
+ Window win;
+ GLXContext ctx;
+ Bool doubleBuffered;
+};
+
+/**
+ Attributes for single-buffered RGBA with at least
+ 4 bits per color and a 16 bit depth buffer.
+*/
+static int attrListSgl[] = {
+ GLX_RGBA,
+ GLX_RED_SIZE, 4,
+ GLX_GREEN_SIZE, 4,
+ GLX_BLUE_SIZE, 4,
+ GLX_DEPTH_SIZE, 16,
+ None
+};
+
+/**
+ Attributes for double-buffered RGBA with at least
+ 4 bits per color and a 16 bit depth buffer.
+*/
+static int attrListDbl[] = {
+ GLX_RGBA, GLX_DOUBLEBUFFER,
+ GLX_RED_SIZE, 4,
+ GLX_GREEN_SIZE, 4,
+ GLX_BLUE_SIZE, 4,
+ GLX_DEPTH_SIZE, 16,
+ None
+};
+
+PuglWindow*
+puglCreate(PuglNativeWindow parent, const char* title, int width, int height)
+{
+ PuglWindow* win = (PuglWindow*)calloc(1, sizeof(PuglWindow));
+
+ win->impl = (PuglPlatformData*)calloc(1, sizeof(PuglPlatformData));
+
+ PuglPlatformData* impl = win->impl;
+
+ win->width = width;
+ win->height = height;
+ impl->display = XOpenDisplay(0);
+ impl->screen = DefaultScreen(impl->display);
+
+ XVisualInfo* vi = glXChooseVisual(impl->display, impl->screen, attrListDbl);
+ if (vi == NULL) {
+ vi = glXChooseVisual(impl->display, impl->screen, attrListSgl);
+ impl->doubleBuffered = False;
+ printf("singlebuffered rendering will be used, no doublebuffering available\n");
+ } else {
+ impl->doubleBuffered = True;
+ printf("doublebuffered rendering available\n");
+ }
+
+ int glxMajor, glxMinor;
+ glXQueryVersion(impl->display, &glxMajor, &glxMinor);
+ printf("GLX-Version %d.%d\n", glxMajor, glxMinor);
+
+ impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE);
+
+ Window xParent = parent
+ ? (Window)parent
+ : RootWindow(impl->display, impl->screen);
+
+ Colormap cmap = XCreateColormap(
+ impl->display, xParent, vi->visual, AllocNone);
+
+ XSetWindowAttributes attr;
+ memset(&attr, 0, sizeof(XSetWindowAttributes));
+ attr.colormap = cmap;
+ attr.border_pixel = 0;
+
+ attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask
+ | ButtonPressMask | PointerMotionMask | StructureNotifyMask;
+
+ impl->win = XCreateWindow(
+ impl->display, xParent,
+ 0, 0, win->width, win->height, 0, vi->depth, InputOutput, vi->visual,
+ CWBorderPixel | CWColormap | CWEventMask, &attr);
+
+ if (title) {
+ XStoreName(impl->display, impl->win, title);
+ }
+
+ if (!parent) {
+ Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True);
+ XSetWMProtocols(impl->display, impl->win, &wmDelete, 1);
+ }
+
+ XMapRaised(impl->display, impl->win);
+
+ if (glXIsDirect(impl->display, impl->ctx)) {
+ printf("DRI enabled\n");
+ } else {
+ printf("no DRI available\n");
+ }
+
+ return win;
+}
+
+void
+puglDestroy(PuglWindow* win)
+{
+ if (win->impl->ctx) {
+ if (!glXMakeCurrent(win->impl->display, None, NULL)) {
+ printf("Could not release drawing context.\n");
+ }
+ /* destroy the context */
+ glXDestroyContext(win->impl->display, win->impl->ctx);
+ win->impl->ctx = NULL;
+ }
+
+ XCloseDisplay(win->impl->display);
+ free(win);
+}
+
+void
+puglDisplay(PuglWindow* win)
+{
+ glXMakeCurrent(win->impl->display, win->impl->win, win->impl->ctx);
+ glViewport(0, 0, win->width, win->height);
+
+ if (win->displayFunc) {
+ win->displayFunc(win);
+ }
+
+ if (win->impl->doubleBuffered) {
+ glXSwapBuffers(win->impl->display, win->impl->win);
+ }
+}
+
+PuglStatus
+puglProcessEvents(PuglWindow* win)
+{
+ XEvent event;
+
+ /* handle the events in the queue */
+ while (XPending(win->impl->display) > 0) {
+ XNextEvent(win->impl->display, &event);
+ switch (event.type) {
+ case Expose:
+ if (event.xexpose.count != 0) {
+ break;
+ }
+ puglDisplay(win);
+ win->redisplay = false;
+ break;
+ case ConfigureNotify:
+ if ((event.xconfigure.width != win->width) ||
+ (event.xconfigure.height != win->height)) {
+ if (win->reshapeFunc) {
+ win->reshapeFunc(win,
+ event.xconfigure.width,
+ event.xconfigure.height);
+ }
+ }
+ break;
+ case MotionNotify:
+ if (win->motionFunc) {
+ win->motionFunc(win, event.xmotion.x, event.xmotion.y);
+ }
+ break;
+ case ButtonPress:
+ if (win->mouseFunc) {
+ win->mouseFunc(win,
+ event.xbutton.button, event.xbutton.state,
+ event.xbutton.x, event.xbutton.y);
+ }
+ break;
+ case KeyPress:
+ case KeyRelease:
+ if (win->keyboardFunc) {
+ KeySym sym = XKeycodeToKeysym(
+ win->impl->display, event.xkey.keycode, 0);
+ win->keyboardFunc(win, event.type == KeyPress, sym);
+ }
+ break;
+ case ClientMessage:
+ if (!strcmp(XGetAtomName(win->impl->display,
+ event.xclient.message_type),
+ "WM_PROTOCOLS")) {
+ if (win->closeFunc) {
+ win->closeFunc(win);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (win->redisplay) {
+ puglDisplay(win);
+ }
+
+ return PUGL_SUCCESS;
+}
+
+void
+puglPostRedisplay(PuglWindow* win)
+{
+ win->redisplay = true;
+}
+
+PuglNativeWindow
+puglGetNativeWindow(PuglWindow* win)
+{
+ return win->impl->win;
+}