diff options
Diffstat (limited to 'pugl')
-rw-r--r-- | pugl/pugl.h | 154 | ||||
-rw-r--r-- | pugl/pugl_internal.h | 92 | ||||
-rw-r--r-- | pugl/pugl_x11.c | 242 |
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; +} |