From cb203e513b915c2befaa3122650600fbc882b06c Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Sat, 30 May 2020 10:33:35 +0300 Subject: [PATCH] spaceball support on X11 --- miniglut.c | 263 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- miniglut.h | 2 +- test.c | 139 +++++++++++++++++++++++++++++++- 3 files changed, 384 insertions(+), 20 deletions(-) diff --git a/miniglut.c b/miniglut.c index 73fb9ec..32b3d12 100644 --- a/miniglut.c +++ b/miniglut.c @@ -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 #include -#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 { diff --git a/miniglut.h b/miniglut.h index 7b927dd..666e5fc 100644 --- a/miniglut.h +++ b/miniglut.h @@ -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 --- a/test.c +++ b/test.c @@ -1,3 +1,4 @@ +#include #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; +} -- 1.7.10.4