spaceball support on X11
authorJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 30 May 2020 07:33:35 +0000 (10:33 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 30 May 2020 07:33:35 +0000 (10:33 +0300)
miniglut.c
miniglut.h
test.c

index 73fb9ec..32b3d12 100644 (file)
@@ -38,6 +38,7 @@ static GLXContext ctx;
 static Atom xa_wm_proto, xa_wm_del_win;
 static Atom xa_net_wm_state, xa_net_wm_state_fullscr;
 static Atom xa_motif_wm_hints;
+static Atom xa_motion_event, xa_button_press_event, xa_button_release_event, xa_command_event;
 static unsigned int evmask;
 
 static int have_netwm_fullscr(void);
@@ -69,6 +70,7 @@ struct ctx_info {
        int srgb;
 };
 
+static void cleanup(void);
 static void create_window(const char *title);
 static void get_window_pos(int *x, int *y);
 static void get_window_size(int *w, int *h);
@@ -106,7 +108,6 @@ static int quit;
 static int upd_pending;
 static int modstate;
 
-
 void glutInit(int *argc, char **argv)
 {
 #ifdef BUILD_X11
@@ -123,6 +124,11 @@ void glutInit(int *argc, char **argv)
                xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
        }
 
+       xa_motion_event = XInternAtom(dpy, "MotionEvent", True);
+       xa_button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
+       xa_button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
+       xa_command_event = XInternAtom(dpy, "CommandEvent", True);
+
        evmask = ExposureMask | StructureNotifyMask;
 
 #endif
@@ -298,7 +304,7 @@ void glutSpaceballRotateFunc(glut_cb_sbmotion func)
        cb_sball_rotate = func;
 }
 
-void glutSpaceballBittonFunc(glut_cb_sbbutton func)
+void glutSpaceballButtonFunc(glut_cb_sbbutton func)
 {
        cb_sball_button = func;
 }
@@ -412,8 +418,40 @@ int glutExtensionSupported(char *ext)
 
 /* --------------- UNIX/X11 implementation ----------------- */
 #ifdef BUILD_X11
+enum {
+    SPNAV_EVENT_ANY,  /* used by spnav_remove_events() */
+    SPNAV_EVENT_MOTION,
+    SPNAV_EVENT_BUTTON  /* includes both press and release */
+};
+
+struct spnav_event_motion {
+    int type;
+    int x, y, z;
+    int rx, ry, rz;
+    unsigned int period;
+    int *data;
+};
+
+struct spnav_event_button {
+    int type;
+    int press;
+    int bnum;
+};
+
+union spnav_event {
+    int type;
+    struct spnav_event_motion motion;
+    struct spnav_event_button button;
+};
+
+
 static void handle_event(XEvent *ev);
 
+static int spnav_window(Window win);
+static int spnav_event(const XEvent *xev, union spnav_event *event);
+static int spnav_remove_events(int type);
+
+
 void glutMainLoopEvent(void)
 {
        XEvent ev;
@@ -425,12 +463,12 @@ void glutMainLoopEvent(void)
        if(!upd_pending && !cb_idle) {
                XNextEvent(dpy, &ev);
                handle_event(&ev);
-               if(quit) return;
+               if(quit) goto end;
        }
        while(XPending(dpy)) {
                XNextEvent(dpy, &ev);
                handle_event(&ev);
-               if(quit) return;
+               if(quit) goto end;
        }
 
        if(cb_idle) {
@@ -441,6 +479,20 @@ void glutMainLoopEvent(void)
                upd_pending = 0;
                cb_display();
        }
+
+end:
+       if(quit) {
+               cleanup();
+       }
+}
+
+static void cleanup(void)
+{
+       if(win) {
+               spnav_window(root);
+               glXMakeCurrent(dpy, 0, 0);
+               XDestroyWindow(dpy, win);
+       }
 }
 
 static KeySym translate_keysym(KeySym sym)
@@ -467,6 +519,7 @@ static KeySym translate_keysym(KeySym sym)
 static void handle_event(XEvent *ev)
 {
        KeySym sym;
+       union spnav_event sev;
 
        switch(ev->type) {
        case MapNotify:
@@ -489,6 +542,28 @@ static void handle_event(XEvent *ev)
                                quit = 1;
                        }
                }
+               if(spnav_event(ev, &sev)) {
+                       switch(sev.type) {
+                       case SPNAV_EVENT_MOTION:
+                               if(cb_sball_motion) {
+                                       cb_sball_motion(sev.motion.x, sev.motion.y, sev.motion.z);
+                               }
+                               if(cb_sball_rotate) {
+                                       cb_sball_rotate(sev.motion.rx, sev.motion.ry, sev.motion.rz);
+                               }
+                               spnav_remove_events(SPNAV_EVENT_MOTION);
+                               break;
+
+                       case SPNAV_EVENT_BUTTON:
+                               if(cb_sball_button) {
+                                       cb_sball_button(sev.button.bnum + 1, sev.button.press ? GLUT_DOWN : GLUT_UP);
+                               }
+                               break;
+
+                       default:
+                               break;
+                       }
+               }
                break;
 
        case Expose:
@@ -820,6 +895,8 @@ static void create_window(const char *title)
 
        XSelectInput(dpy, win, evmask);
 
+       spnav_window(win);
+
        glutSetWindowTitle(title);
        glutSetIconTitle(title);
        XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
@@ -849,6 +926,149 @@ static void get_screen_size(int *scrw, int *scrh)
        *scrw = wattr.width;
        *scrh = wattr.height;
 }
+
+
+/* spaceball */
+enum {
+  CMD_APP_WINDOW = 27695,
+  CMD_APP_SENS
+};
+
+static Window get_daemon_window(Display *dpy);
+static int catch_badwin(Display *dpy, XErrorEvent *err);
+
+#define SPNAV_INITIALIZED      (xa_motion_event)
+
+static int spnav_window(Window win)
+{
+       int (*prev_xerr_handler)(Display*, XErrorEvent*);
+       XEvent xev;
+       Window daemon_win;
+
+       if(!SPNAV_INITIALIZED) {
+               return -1;
+       }
+
+       if(!(daemon_win = get_daemon_window(dpy))) {
+               return -1;
+       }
+
+       prev_xerr_handler = XSetErrorHandler(catch_badwin);
+
+       xev.type = ClientMessage;
+       xev.xclient.send_event = False;
+       xev.xclient.display = dpy;
+       xev.xclient.window = win;
+       xev.xclient.message_type = xa_command_event;
+       xev.xclient.format = 16;
+       xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
+       xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
+       xev.xclient.data.s[2] = CMD_APP_WINDOW;
+
+       XSendEvent(dpy, daemon_win, False, 0, &xev);
+       XSync(dpy, False);
+
+       XSetErrorHandler(prev_xerr_handler);
+       return 0;
+}
+
+static Bool match_events(Display *dpy, XEvent *xev, char *arg)
+{
+       int evtype = *(int*)arg;
+
+       if(xev->type != ClientMessage) {
+               return False;
+       }
+
+       if(xev->xclient.message_type == xa_motion_event) {
+               return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
+       }
+       if(xev->xclient.message_type == xa_button_press_event ||
+                       xev->xclient.message_type == xa_button_release_event) {
+               return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
+       }
+       return False;
+}
+
+static int spnav_remove_events(int type)
+{
+       int rm_count = 0;
+       XEvent xev;
+       while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
+               rm_count++;
+       }
+       return rm_count;
+}
+
+static int spnav_event(const XEvent *xev, union spnav_event *event)
+{
+       int i;
+       int xmsg_type;
+
+       xmsg_type = xev->xclient.message_type;
+
+       if(xmsg_type != xa_motion_event && xmsg_type != xa_button_press_event &&
+                       xmsg_type != xa_button_release_event) {
+               return 0;
+       }
+
+       if(xmsg_type == xa_motion_event) {
+               event->type = SPNAV_EVENT_MOTION;
+               event->motion.data = &event->motion.x;
+
+               for(i=0; i<6; i++) {
+                       event->motion.data[i] = xev->xclient.data.s[i + 2];
+               }
+               event->motion.period = xev->xclient.data.s[8];
+       } else {
+               event->type = SPNAV_EVENT_BUTTON;
+               event->button.press = xmsg_type == xa_button_press_event ? 1 : 0;
+               event->button.bnum = xev->xclient.data.s[2];
+       }
+       return event->type;
+}
+
+static int mglut_strcmp(const char *s1, const char *s2)
+{
+       while(*s1 && *s1 == *s2) {
+               s1++;
+               s2++;
+       }
+       return *s1 - *s2;
+}
+
+static Window get_daemon_window(Display *dpy)
+{
+       Window win;
+       XTextProperty wname;
+       Atom type;
+       int fmt;
+       unsigned long nitems, bytes_after;
+       unsigned char *prop;
+
+       XGetWindowProperty(dpy, root, xa_command_event, 0, 1, False, AnyPropertyType,
+                       &type, &fmt, &nitems, &bytes_after, &prop);
+       if(!prop) {
+               return 0;
+       }
+
+       win = *(Window*)prop;
+       XFree(prop);
+
+       if(!XGetWMName(dpy, win, &wname) || mglut_strcmp("Magellan Window", (char*)wname.value) != 0) {
+               return 0;
+       }
+
+       return win;
+}
+
+static int catch_badwin(Display *dpy, XErrorEvent *err)
+{
+       return 0;
+}
+
+
+
 #endif /* BUILD_X11 */
 
 
@@ -905,6 +1125,15 @@ void glutMainLoopEvent(void)
        }
 }
 
+static void cleanup(void)
+{
+       if(win) {
+               wglMakeCurrent(dc, 0);
+               wglDeleteContext(ctx);
+               UnregisterClass("MiniGLUT", hinst);
+       }
+}
+
 void glutSwapBuffers(void)
 {
        SwapBuffers(dc);
@@ -1071,8 +1300,7 @@ static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam
                break;
 
        case WM_DESTROY:
-               wglMakeCurrent(dc, 0);
-               wglDeleteContext(ctx);
+               cleanup();
                quit = 1;
                PostQuitMessage(0);
                break;
@@ -1390,18 +1618,21 @@ static int sys_write(int fd, const void *buf, int count)
 #include <stdlib.h>
 #include <math.h>
 
-#define mglut_sincos(a, s, c)  \
-       do { \
-               *(s) = sin(a); \
-               *(c) = cos(a); \
-       } while(0)
+void mglut_sincos(float angle, float *sptr, float *cptr)
+{
+       *sptr = sin(angle);
+       *cptr = cos(angle);
+}
 
-#define mglut_atan(x)  atan(x)
+float mglut_atan(float x)
+{
+       return atan(x);
+}
 
 #else  /* !MINIGLUT_USE_LIBC */
 
 #ifdef __GNUC__
-static void mglut_sincos(float angle, float *sptr, float *cptr)
+void mglut_sincos(float angle, float *sptr, float *cptr)
 {
        asm volatile(
                "flds %2\n\t"
@@ -1413,7 +1644,7 @@ static void mglut_sincos(float angle, float *sptr, float *cptr)
        );
 }
 
-static float mglut_atan(float x)
+float mglut_atan(float x)
 {
        float res;
        asm volatile(
@@ -1429,7 +1660,7 @@ static float mglut_atan(float x)
 #endif
 
 #ifdef _MSC_VER
-static void mglut_sincos(float angle, float *sptr, float *cptr)
+void mglut_sincos(float angle, float *sptr, float *cptr)
 {
        float s, c;
        __asm {
@@ -1442,7 +1673,7 @@ static void mglut_sincos(float angle, float *sptr, float *cptr)
        *cptr = c;
 }
 
-static float mglut_atan(float x)
+float mglut_atan(float x)
 {
        float res;
        __asm {
index 7b927dd..666e5fc 100644 (file)
@@ -163,7 +163,7 @@ void glutMotionFunc(glut_cb_motion func);
 void glutPassiveMotionFunc(glut_cb_motion func);
 void glutSpaceballMotionFunc(glut_cb_sbmotion func);
 void glutSpaceballRotateFunc(glut_cb_sbmotion func);
-void glutSpaceballBittonFunc(glut_cb_sbbutton func);
+void glutSpaceballButtonFunc(glut_cb_sbbutton func);
 
 int glutGet(unsigned int s);
 int glutGetModifiers(void);
diff --git a/test.c b/test.c
index 434e547..6e68405 100644 (file)
--- a/test.c
+++ b/test.c
@@ -1,3 +1,4 @@
+#include <math.h>
 #include "miniglut.h"
 
 void idle(void);
@@ -6,11 +7,23 @@ void reshape(int x, int y);
 void keypress(unsigned char key, int x, int y);
 void mouse(int bn, int st, int x, int y);
 void motion(int x, int y);
+void sball_motion(int x, int y, int z);
+void sball_rotate(int rx, int ry, int rz);
+void sball_button(int bn, int state);
+
+static void vcross(float *res, const float *a, const float *b);
+static void qmul(float *a, const float *b);
+static void qrotation(float *q, float angle, float x, float y, float z);
+static void qrotate(float *q, float angle, float x, float y, float z);
+static void mrotation_quat(float *m, const float *q);
+
 
 float cam_theta, cam_phi = 25, cam_dist = 8;
 int mouse_x, mouse_y;
 int bnstate[8];
 int anim;
+float torus_pos[3], torus_rot[4] = {0, 0, 0, 1};
+
 
 int main(int argc, char **argv)
 {
@@ -24,6 +37,9 @@ int main(int argc, char **argv)
        glutKeyboardFunc(keypress);
        glutMouseFunc(mouse);
        glutMotionFunc(motion);
+       glutSpaceballMotionFunc(sball_motion);
+       glutSpaceballRotateFunc(sball_rotate);
+       glutSpaceballButtonFunc(sball_button);
 
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_CULL_FACE);
@@ -43,8 +59,7 @@ void display(void)
 {
        long tm;
        float lpos[] = {-1, 2, 3, 0};
-
-       tm = glutGet(GLUT_ELAPSED_TIME);
+       float sbrot_xform[16];
 
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
@@ -58,6 +73,7 @@ void display(void)
 
        glPushMatrix();
        if(anim) {
+               tm = glutGet(GLUT_ELAPSED_TIME);
                glRotatef(tm / 10.0f, 1, 0, 0);
                glRotatef(tm / 10.0f, 0, 1, 0);
        }
@@ -67,7 +83,9 @@ void display(void)
        glutSolidSphere(0.4, 16, 8);
 
        glPushMatrix();
-       glTranslatef(-2.5, 0, 0);
+       glTranslatef(torus_pos[0] - 2.5, torus_pos[1], torus_pos[2]);
+       mrotation_quat(sbrot_xform, torus_rot);
+       glMultMatrixf(sbrot_xform);
        glutSolidCube(1.5);
        glPopMatrix();
 
@@ -164,3 +182,118 @@ void motion(int x, int y)
                glutPostRedisplay();
        }
 }
+
+void sball_motion(int x, int y, int z)
+{
+       torus_pos[0] += x * 0.001f;
+       torus_pos[1] += y * 0.001f;
+       torus_pos[2] -= z * 0.001f;
+       glutPostRedisplay();
+}
+
+static float rsqrt(float number)
+{
+       int i;
+       float x2, y;
+       static const float threehalfs = 1.5f;
+
+       x2 = number * 0.5f;
+       y = number;
+       i = *(int*)&y;
+       i = 0x5f3759df - (i >> 1);
+       y = *(float*)&i;
+       y *= threehalfs - (x2 * y * y);
+       y *= threehalfs - (x2 * y * y);
+       return y;
+}
+
+void sball_rotate(int rx, int ry, int rz)
+{
+       if(rx | ry | rz) {
+               float s = (float)rsqrt(rx * rx + ry * ry + rz * rz);
+               qrotate(torus_rot, 0.001f / s, rx * s, ry * s, -rz * s);
+               glutPostRedisplay();
+       }
+}
+
+void sball_button(int bn, int state)
+{
+       if(state == GLUT_DOWN) {
+               torus_pos[0] = torus_pos[1] = torus_pos[2] = 0;
+               torus_rot[0] = torus_rot[1] = torus_rot[2] = 0;
+               torus_rot[3] = 1;
+               glutPostRedisplay();
+       }
+}
+
+
+static void vcross(float *res, const float *a, const float *b)
+{
+       res[0] = a[1] * b[2] - a[2] * b[1];
+       res[1] = a[2] * b[0] - a[0] * b[2];
+       res[2] = a[0] * b[1] - a[1] * b[0];
+}
+
+static void qmul(float *a, const float *b)
+{
+       float x, y, z, dot;
+       float cross[3];
+
+       dot = a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
+       vcross(cross, a, b);
+
+       x = a[3] * b[0] + b[3] * a[0] + cross[0];
+       y = a[3] * b[1] + b[3] * a[1] + cross[1];
+       z = a[3] * b[2] + b[3] * a[2] + cross[2];
+       a[3] = a[3] * b[3] - dot;
+       a[0] = x;
+       a[1] = y;
+       a[2] = z;
+}
+
+void mglut_sincos(float angle, float *sptr, float *cptr);
+float mglut_tan(float x);
+
+static void qrotation(float *q, float angle, float x, float y, float z)
+{
+       float sa, ca;
+       mglut_sincos(angle * 0.5f, &sa, &ca);
+       q[3] = ca;
+       q[0] = x * sa;
+       q[1] = y * sa;
+       q[2] = z * sa;
+}
+
+static void qrotate(float *q, float angle, float x, float y, float z)
+{
+       float qrot[4];
+       qrotation(qrot, angle, x, y, z);
+       qmul(qrot, q);
+       q[0] = qrot[0];
+       q[1] = qrot[1];
+       q[2] = qrot[2];
+       q[3] = qrot[3];
+}
+
+static void mrotation_quat(float *m, const float *q)
+{
+       float xsq2 = 2.0f * q[0] * q[0];
+       float ysq2 = 2.0f * q[1] * q[1];
+       float zsq2 = 2.0f * q[2] * q[2];
+       float sx = 1.0f - ysq2 - zsq2;
+       float sy = 1.0f - xsq2 - zsq2;
+       float sz = 1.0f - xsq2 - ysq2;
+
+       m[3] = m[7] = m[11] = m[12] = m[13] = m[14] = 0.0f;
+       m[15] = 1.0f;
+
+       m[0] = sx;
+       m[1] = 2.0f * q[0] * q[1] + 2.0f * q[3] * q[2];
+       m[2] = 2.0f * q[2] * q[0] - 2.0f * q[3] * q[1];
+       m[4] = 2.0f * q[0] * q[1] - 2.0f * q[3] * q[2];
+       m[5] = sy;
+       m[6] = 2.0f * q[1] * q[2] + 2.0f * q[3] * q[0];
+       m[8] = 2.0f * q[2] * q[0] + 2.0f * q[3] * q[1];
+       m[9] = 2.0f * q[1] * q[2] - 2.0f * q[3] * q[0];
+       m[10] = sz;
+}