--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <X11/Xlib.h>
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <GL/glx.h>
+#include "vmath.h"
+#include "device.h"
+
+int create_gfx(int xsz, int ysz);
+void destroy_gfx(void);
+void set_window_title(const char *title);
+void redraw(void);
+void draw_cube(void);
+int handle_event(XEvent *xev);
+int handle_dev_event(device_event *ev);
+
+Display *dpy;
+Atom wm_prot, wm_del_win;
+GLXContext ctx;
+Window win;
+
+vec3_t pos = {0, 0, -6};
+quat_t rot = {0, 0, 0, 1}; /* that's 1 + 0i + 0j + 0k */
+
+int redisplay;
+
+/* serial 6dof device */
+struct device *dev;
+
+int main(void)
+{
+ int xsock, devfd, maxfd;
+
+ register_all();
+ set_port("/dev/ttyS0");
+
+ if(!(dev = dev_init(0))) {
+ fprintf(stderr, "failed to initialize 6dof serial device at /dev/ttyS0\n");
+ return 1;
+ }
+ if((devfd = dev->start()) == -1) {
+ return 1;
+ }
+
+ if(!(dpy = XOpenDisplay(0))) {
+ fprintf(stderr, "failed to connect to the X server\n");
+ return 1;
+ }
+ xsock = ConnectionNumber(dpy);
+
+ if(create_gfx(512, 512) == -1) {
+ return 1;
+ }
+
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+
+ maxfd = xsock > devfd ? xsock : devfd;
+
+ for(;;) {
+ fd_set rdset;
+
+ FD_ZERO(&rdset);
+ FD_SET(xsock, &rdset);
+ FD_SET(devfd, &rdset);
+
+ while(select(maxfd + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR);
+
+ if(FD_ISSET(xsock, &rdset)) {
+ while(XPending(dpy)) {
+ XEvent xev;
+ XNextEvent(dpy, &xev);
+
+ if(handle_event(&xev) != 0) {
+ goto end;
+ }
+ }
+ }
+
+ if(FD_ISSET(devfd, &rdset)) {
+ device_event ev;
+
+ while(dev->getevent(&ev)) {
+ handle_dev_event(&ev);
+ }
+ }
+
+ if(redisplay) {
+ redraw();
+ redisplay = 0;
+ }
+ }
+
+end:
+ destroy_gfx();
+ XCloseDisplay(dpy);
+ return 0;
+}
+
+int create_gfx(int xsz, int ysz)
+{
+ int scr;
+ Window root;
+ XVisualInfo *vis;
+ XSetWindowAttributes xattr;
+ unsigned int events;
+ XClassHint class_hint;
+
+ int attr[] = {
+ GLX_RGBA, GLX_DOUBLEBUFFER,
+ GLX_RED_SIZE, 8,
+ GLX_GREEN_SIZE, 8,
+ GLX_BLUE_SIZE, 8,
+ GLX_DEPTH_SIZE, 24,
+ None
+ };
+
+ wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False);
+ wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+
+ scr = DefaultScreen(dpy);
+ root = RootWindow(dpy, scr);
+
+ if(!(vis = glXChooseVisual(dpy, scr, attr))) {
+ fprintf(stderr, "requested GLX visual is not available\n");
+ return -1;
+ }
+
+ if(!(ctx = glXCreateContext(dpy, vis, 0, True))) {
+ fprintf(stderr, "failed to create GLX context\n");
+ XFree(vis);
+ return -1;
+ }
+
+ xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
+ xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone);
+
+ if(!(win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput,
+ vis->visual, CWColormap | CWBackPixel | CWBorderPixel, &xattr))) {
+ fprintf(stderr, "failed to create X window\n");
+ return -1;
+ }
+ XFree(vis);
+
+ /* set the window event mask */
+ events = ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask |
+ ButtonReleaseMask | ButtonPressMask | PointerMotionMask;
+ XSelectInput(dpy, win, events);
+
+ XSetWMProtocols(dpy, win, &wm_del_win, 1);
+
+ set_window_title("libspnav cube");
+
+ class_hint.res_name = "cube";
+ class_hint.res_class = "cube";
+ XSetClassHint(dpy, win, &class_hint);
+
+ if(glXMakeCurrent(dpy, win, ctx) == False) {
+ fprintf(stderr, "glXMakeCurrent failed\n");
+ glXDestroyContext(dpy, ctx);
+ XDestroyWindow(dpy, win);
+ return -1;
+ }
+
+ XMapWindow(dpy, win);
+ XFlush(dpy);
+
+ return 0;
+}
+
+void destroy_gfx(void)
+{
+ glXDestroyContext(dpy, ctx);
+ XDestroyWindow(dpy, win);
+ glXMakeCurrent(dpy, None, 0);
+}
+
+void set_window_title(const char *title)
+{
+ XTextProperty wm_name;
+
+ XStringListToTextProperty((char**)&title, 1, &wm_name);
+ XSetWMName(dpy, win, &wm_name);
+ XSetWMIconName(dpy, win, &wm_name);
+ XFree(wm_name.value);
+}
+
+void redraw(void)
+{
+ mat4_t xform;
+
+ quat_to_mat(xform, rot);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glTranslatef(pos.x, pos.y, pos.z);
+ glMultTransposeMatrixf((float*)xform);
+
+ draw_cube();
+
+ glXSwapBuffers(dpy, win);
+}
+
+void draw_cube(void)
+{
+ glBegin(GL_QUADS);
+ /* face +Z */
+ glNormal3f(0, 0, 1);
+ glColor3f(1, 0, 0);
+ glVertex3f(-1, -1, 1);
+ glVertex3f(1, -1, 1);
+ glVertex3f(1, 1, 1);
+ glVertex3f(-1, 1, 1);
+ /* face +X */
+ glNormal3f(1, 0, 0);
+ glColor3f(0, 1, 0);
+ glVertex3f(1, -1, 1);
+ glVertex3f(1, -1, -1);
+ glVertex3f(1, 1, -1);
+ glVertex3f(1, 1, 1);
+ /* face -Z */
+ glNormal3f(0, 0, -1);
+ glColor3f(0, 0, 1);
+ glVertex3f(1, -1, -1);
+ glVertex3f(-1, -1, -1);
+ glVertex3f(-1, 1, -1);
+ glVertex3f(1, 1, -1);
+ /* face -X */
+ glNormal3f(-1, 0, 0);
+ glColor3f(1, 1, 0);
+ glVertex3f(-1, -1, -1);
+ glVertex3f(-1, -1, 1);
+ glVertex3f(-1, 1, 1);
+ glVertex3f(-1, 1, -1);
+ /* face +Y */
+ glNormal3f(0, 1, 0);
+ glColor3f(0, 1, 1);
+ glVertex3f(-1, 1, 1);
+ glVertex3f(1, 1, 1);
+ glVertex3f(1, 1, -1);
+ glVertex3f(-1, 1, -1);
+ /* face -Y */
+ glNormal3f(0, -1, 0);
+ glColor3f(1, 0, 1);
+ glVertex3f(-1, -1, -1);
+ glVertex3f(1, -1, -1);
+ glVertex3f(1, -1, 1);
+ glVertex3f(-1, -1, 1);
+ glEnd();
+}
+
+int handle_event(XEvent *xev)
+{
+ static int win_mapped;
+ KeySym sym;
+
+ switch(xev->type) {
+ case MapNotify:
+ win_mapped = 1;
+ break;
+
+ case UnmapNotify:
+ win_mapped = 0;
+ break;
+
+ case Expose:
+ if(win_mapped && xev->xexpose.count == 0) {
+ redraw();
+ }
+ break;
+
+ case KeyPress:
+ sym = XLookupKeysym((XKeyEvent*)&xev->xkey, 0);
+ if((sym & 0xff) == 27) {
+ return 1;
+ }
+ break;
+
+ case ConfigureNotify:
+ {
+ int x = xev->xconfigure.width;
+ int y = xev->xconfigure.height;
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective(45.0, (float)x / (float)y, 1.0, 1000.0);
+
+ glViewport(0, 0, x, y);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+#define TX(ev) ((ev)->motion.motion[0])
+#define TY(ev) ((ev)->motion.motion[1])
+#define TZ(ev) ((ev)->motion.motion[2])
+#define RX(ev) ((ev)->motion.motion[3])
+#define RY(ev) ((ev)->motion.motion[4])
+#define RZ(ev) ((ev)->motion.motion[5])
+
+int handle_dev_event(device_event *ev)
+{
+ switch(ev->type) {
+ case DEV_EV_MOTION:
+ if(RX(ev) | RY(ev) | RZ(ev)) {
+ float axis_len = sqrt(RX(ev) * RX(ev) + RY(ev) * RY(ev) + RZ(ev) * RZ(ev));
+ if(axis_len != 0.0) {
+ rot = quat_rotate(rot, axis_len * 0.001, -RX(ev) / axis_len,
+ -RY(ev) / axis_len, -RZ(ev) / axis_len);
+ }
+ }
+
+ pos.x += TX(ev) * 0.001;
+ pos.y += TY(ev) * 0.001;
+ pos.z += TZ(ev) * 0.001;
+ redisplay = 1;
+ break;
+
+ case DEV_EV_BUTTON:
+ if(ev->button.pressed) {
+ pos = v3_cons(0, 0, -6);
+ rot = quat_cons(1, 0, 0, 0);
+ redisplay = 1;
+ }
+ break;
+ }
+
+ return 0;
+}