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);
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);
static int upd_pending;
static int modstate;
-
void glutInit(int *argc, char **argv)
{
#ifdef BUILD_X11
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
cb_sball_rotate = func;
}
-void glutSpaceballBittonFunc(glut_cb_sbbutton func)
+void glutSpaceballButtonFunc(glut_cb_sbbutton func)
{
cb_sball_button = func;
}
/* --------------- 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;
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) {
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)
static void handle_event(XEvent *ev)
{
KeySym sym;
+ union spnav_event sev;
switch(ev->type) {
case MapNotify:
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:
XSelectInput(dpy, win, evmask);
+ spnav_window(win);
+
glutSetWindowTitle(title);
glutSetIconTitle(title);
XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
*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 */
}
}
+static void cleanup(void)
+{
+ if(win) {
+ wglMakeCurrent(dc, 0);
+ wglDeleteContext(ctx);
+ UnregisterClass("MiniGLUT", hinst);
+ }
+}
+
void glutSwapBuffers(void)
{
SwapBuffers(dc);
break;
case WM_DESTROY:
- wglMakeCurrent(dc, 0);
- wglDeleteContext(ctx);
+ cleanup();
quit = 1;
PostQuitMessage(0);
break;
#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"
);
}
-static float mglut_atan(float x)
+float mglut_atan(float x)
{
float res;
asm volatile(
#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 {
*cptr = c;
}
-static float mglut_atan(float x)
+float mglut_atan(float x)
{
float res;
__asm {
+#include <math.h>
#include "miniglut.h"
void idle(void);
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)
{
glutKeyboardFunc(keypress);
glutMouseFunc(mouse);
glutMotionFunc(motion);
+ glutSpaceballMotionFunc(sball_motion);
+ glutSpaceballRotateFunc(sball_rotate);
+ glutSpaceballButtonFunc(sball_button);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
{
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);
glPushMatrix();
if(anim) {
+ tm = glutGet(GLUT_ELAPSED_TIME);
glRotatef(tm / 10.0f, 1, 0, 0);
glRotatef(tm / 10.0f, 0, 1, 0);
}
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();
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;
+}