libspnav-0.2.2/0000755000175000017500000000000011353512370013175 5ustar nuclearnuclearlibspnav-0.2.2/Makefile.in0000644000175000017500000000157711353511653015257 0ustar nuclearnuclear obj = spnav.o $(magellan_obj) hdr = spnav.h spnav_magellan.h spnav_config.h lib_a = libspnav.a soname = libspnav.so.0 lib_so = $(soname).1 CC = gcc AR = ar CFLAGS = $(opt) $(dbg) -std=c89 -fpic -pedantic -Wall -fno-strict-aliasing -I. .PHONY: all all: $(lib_a) $(lib_so) $(lib_a): $(obj) $(AR) rcs $@ $(obj) $(lib_so): $(obj) $(CC) -shared -Wl,-soname,$(soname) -o $@ $(obj) %.o: $(srcdir)/%.c $(CC) $(CFLAGS) -c $< -o $@ .PHONY: clean clean: rm -f $(obj) .PHONY: cleandist distclean: rm -f $(obj) $(lib_a) $(lib_so) Makefile .PHONY: install install: $(lib_a) $(lib_so) cp $(lib_a) $(PREFIX)/$(libdir)/$(lib_a) cp $(lib_so) $(PREFIX)/$(libdir)/$(lib_so) for h in $(hdr); do cp -p $(srcdir)/$$h $(PREFIX)/include/; done .PHONY: uninstall uninstall: rm -f $(PREFIX)/$(libdir)/$(lib_a) rm -f $(PREFIX)/$(libdir)/$(lib_so) for i in $(hdr); do rm -f $(PREFIX)/include/$$i; done libspnav-0.2.2/spnav.h0000644000175000017500000001100211353511653014472 0ustar nuclearnuclear/* This file is part of libspnav, part of the spacenav project (spacenav.sf.net) Copyright (C) 2007-2010 John Tsiombikas Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SPACENAV_H_ #define SPACENAV_H_ #include #ifdef USE_X11 #include #endif 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; }; typedef union spnav_event { int type; struct spnav_event_motion motion; struct spnav_event_button button; } spnav_event; #ifdef __cplusplus extern "C" { #endif /* Open connection to the daemon via AF_UNIX socket. * The unix domain socket interface is an alternative to the original magellan * protocol, and it is *NOT* compatible with the 3D connexion driver. If you wish * to remain compatible, use the X11 protocol (spnav_x11_open, see below). * Returns -1 on failure. */ int spnav_open(void); /* Close connection to the daemon. Use it for X11 or AF_UNIX connections. * Returns -1 on failure */ int spnav_close(void); /* Retrieves the file descriptor used for communication with the daemon, for * use with select() by the application, if so required. * If the X11 mode is used, the socket used to communicate with the X server is * returned, so the result of this function is always reliable. * If AF_UNIX mode is used, the fd of the socket is returned or -1 if * no connection is open / failure occured. */ int spnav_fd(void); /* TODO: document */ int spnav_sensitivity(double sens); /* blocks waiting for space-nav events. returns 0 if an error occurs */ int spnav_wait_event(spnav_event *event); /* checks the availability of space-nav events (non-blocking) * returns the event type if available, or 0 otherwise. */ int spnav_poll_event(spnav_event *event); /* Removes any pending events from the specified type, or all pending events * events if the type argument is SPNAV_EVENT_ANY. Returns the number of * removed events. */ int spnav_remove_events(int type); #ifdef USE_X11 /* Opens a connection to the daemon, using the original magellan X11 protocol. * Any application using this protocol should be compatible with the proprietary * 3D connexion driver too. */ int spnav_x11_open(Display *dpy, Window win); /* Sets the application window, that is to receive events by the driver. * * NOTE: Any number of windows can be registered for events, when using the * free spnavd daemon. I suspect that the proprietary 3D connexion daemon only * sends events to one window at a time, thus this function replaces the window * that receives events. If compatibility with 3dxsrv is required, do not * assume that you can register multiple windows. */ int spnav_x11_window(Window win); /* Examines an arbitrary X11 event. If it's a spnav event, it returns the event * type (SPNAV_EVENT_MOTION or SPNAV_EVENT_BUTTON) and fills in the spnav_event * structure passed through "event" accordingly. Otherwise, it returns 0. */ int spnav_x11_event(const XEvent *xev, spnav_event *event); #endif #ifdef __cplusplus } #endif #endif /* SPACENAV_H_ */ libspnav-0.2.2/examples/0000755000175000017500000000000011353512331015010 5ustar nuclearnuclearlibspnav-0.2.2/examples/cube/0000755000175000017500000000000011353512331015726 5ustar nuclearnuclearlibspnav-0.2.2/examples/cube/vmath.inl0000644000175000017500000000342611144460326017562 0ustar nuclearnuclear/* vector functions */ static inline vec3_t v3_cons(float x, float y, float z) { vec3_t res; res.x = x; res.y = y; res.z = z; return res; } static inline vec3_t quat_vec(quat_t q) { vec3_t v; v.x = q.x; v.y = q.y; v.z = q.z; return v; } static inline float v3_dot(vec3_t v1, vec3_t v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; } /* quaternion functions */ static inline quat_t quat_cons(float s, float x, float y, float z) { quat_t q; q.x = x; q.y = y; q.z = z; q.w = s; return q; } static inline quat_t quat_mul(quat_t q1, quat_t q2) { quat_t res; vec3_t v1 = quat_vec(q1); vec3_t v2 = quat_vec(q2); res.w = q1.w * q2.w - v3_dot(v1, v2); res.x = v2.x * q1.w + v1.x * q2.w + (v1.y * v2.z - v1.z * v2.y); res.y = v2.y * q1.w + v1.y * q2.w + (v1.z * v2.x - v1.x * v2.z); res.z = v2.z * q1.w + v1.z * q2.w + (v1.x * v2.y - v1.y * v2.x); return res; } static inline void quat_to_mat(mat4_t res, quat_t q) { m4_cons(res, 1.0 - 2.0 * q.y*q.y - 2.0 * q.z*q.z, 2.0 * q.x * q.y + 2.0 * q.w * q.z, 2.0 * q.z * q.x - 2.0 * q.w * q.y, 0, 2.0 * q.x * q.y - 2.0 * q.w * q.z, 1.0 - 2.0 * q.x*q.x - 2.0 * q.z*q.z, 2.0 * q.y * q.z + 2.0 * q.w * q.x, 0, 2.0 * q.z * q.x + 2.0 * q.w * q.y, 2.0 * q.y * q.z - 2.0 * q.w * q.x, 1.0 - 2.0 * q.x*q.x - 2.0 * q.y*q.y, 0, 0, 0, 0, 1); } /* matrix functions */ static inline void m4_cons(mat4_t m, float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44) { m[0][0] = m11; m[0][1] = m12; m[0][2] = m13; m[0][3] = m14; m[1][0] = m21; m[1][1] = m22; m[1][2] = m23; m[1][3] = m24; m[2][0] = m31; m[2][1] = m32; m[2][2] = m33; m[2][3] = m34; m[3][0] = m41; m[3][1] = m42; m[3][2] = m43; m[3][3] = m44; } libspnav-0.2.2/examples/cube/Makefile0000644000175000017500000000032711144371537017401 0ustar nuclearnuclearobj = cube.o vmath.o bin = cube CC = gcc CFLAGS = -pedantic -Wall -g -I../.. LDFLAGS = -L../.. -lX11 -lGL -lGLU -lm -lspnav $(bin): $(obj) $(CC) -o $@ $(obj) $(LDFLAGS) .PHONY: clean clean: rm -f $(obj) $(bin) libspnav-0.2.2/examples/cube/cube.c0000644000175000017500000001501511353511653017020 0ustar nuclearnuclear/* This example demonstrates how to use libspnav to get space navigator input, * and use that to rotate and translate a 3D cube. The magellan X11 protocol is * used (spnav_x11_open) which is compatible with both spacenavd and * 3Dconnexion's 3dxsrv. * * The code is a bit cluttered with X11 and GLX calls, so the interesting bits * are marked with XXX comments. */ #include #include #include #include #include #include #include #include #include "vmath.h" #define SQ(x) ((x) * (x)) 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); 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; int main(void) { if(!(dpy = XOpenDisplay(0))) { fprintf(stderr, "failed to connect to the X server"); return 1; } if(create_gfx(512, 512) == -1) { return 1; } /* XXX: This actually registers our window with the driver for receiving * motion/button events through the 3dxsrv-compatible X11 protocol. */ if(spnav_x11_open(dpy, win) == -1) { fprintf(stderr, "failed to connect to the space navigator daemon\n"); return 1; } glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); for(;;) { XEvent xev; XNextEvent(dpy, &xev); if(handle_event(&xev) != 0) { destroy_gfx(); XCloseDisplay(dpy); return 0; } if(redisplay) { redraw(); redisplay = 0; } } 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; spnav_event spev; 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 ClientMessage: /* XXX check if the event is a spacenav event */ if(spnav_x11_event(xev, &spev)) { /* if so deal with motion and button events */ if(spev.type == SPNAV_EVENT_MOTION) { /* apply axis/angle rotation to the quaternion */ float axis_len = sqrt(SQ(spev.motion.rx) + SQ(spev.motion.ry) + SQ(spev.motion.rz)); rot = quat_rotate(rot, axis_len * 0.001, -spev.motion.rx / axis_len, -spev.motion.ry / axis_len, spev.motion.rz / axis_len); /* add translation */ pos.x += spev.motion.x * 0.001; pos.y += spev.motion.y * 0.001; pos.z -= spev.motion.z * 0.001; redisplay = 1; } else { /* on button press, reset the cube */ if(spev.button.press) { pos = v3_cons(0, 0, -6); rot = quat_cons(1, 0, 0, 0); redisplay = 1; } } /* finally remove any other queued motion events */ spnav_remove_events(SPNAV_EVENT_MOTION); } else if(xev->xclient.message_type == wm_prot) { if(xev->xclient.data.l[0] == wm_del_win) { return 1; } } 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; } libspnav-0.2.2/examples/cube/vmath.c0000644000175000017500000000046511144371537017227 0ustar nuclearnuclear#include #include "vmath.h" quat_t quat_rotate(quat_t q, float angle, float x, float y, float z) { quat_t rq; float half_angle = angle * 0.5; float sin_half = sin(half_angle); rq.w = cos(half_angle); rq.x = x * sin_half; rq.y = y * sin_half; rq.z = z * sin_half; return quat_mul(q, rq); } libspnav-0.2.2/examples/cube/vmath.h0000644000175000017500000000161711144460326017227 0ustar nuclearnuclear#ifndef VMATH_H_ #define VMATH_H_ typedef struct { float x, y, z; } vec3_t; typedef struct { float x, y, z, w; } vec4_t; typedef vec4_t quat_t; typedef float mat4_t[4][4]; /* vector functions */ static inline vec3_t v3_cons(float x, float y, float z); static inline float v3_dot(vec3_t v1, vec3_t v2); /* quaternion functions */ static inline quat_t quat_cons(float s, float x, float y, float z); static inline vec3_t quat_vec(quat_t q); static inline quat_t quat_mul(quat_t q1, quat_t q2); static inline void quat_to_mat(mat4_t res, quat_t q); quat_t quat_rotate(quat_t q, float angle, float x, float y, float z); /* matrix functions */ static inline void m4_cons(mat4_t m, float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44); #include "vmath.inl" #endif /* VMATH_H_ */ libspnav-0.2.2/examples/simple/0000755000175000017500000000000011353512331016301 5ustar nuclearnuclearlibspnav-0.2.2/examples/simple/Makefile0000644000175000017500000000050511144371537017752 0ustar nuclearnuclearCC = gcc CFLAGS = -pedantic -Wall -g -I../.. LDFLAGS = -L../.. -lspnav -lX11 .PHONY: all all: simple_x11 simple_af_unix simple_x11: simple.c $(CC) $(CFLAGS) -DBUILD_X11 -o $@ $< $(LDFLAGS) simple_af_unix: simple.c $(CC) $(CFLAGS) -DBUILD_AF_UNIX -o $@ $< $(LDFLAGS) .PHONY: clean clean: rm -f simple_x11 simple_af_unix libspnav-0.2.2/examples/simple/simple.c0000644000175000017500000000332411144371537017751 0ustar nuclearnuclear#include #include #include #include #include void sig(int s) { spnav_close(); exit(0); } int main(void) { #if defined(BUILD_X11) Display *dpy; Window win; unsigned long bpix; #endif spnav_event sev; signal(SIGINT, sig); #if defined(BUILD_X11) if(!(dpy = XOpenDisplay(0))) { fprintf(stderr, "failed to connect to the X server\n"); return 1; } bpix = BlackPixel(dpy, DefaultScreen(dpy)); win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 1, 1, 0, bpix, bpix); /* This actually registers our window with the driver for receiving * motion/button events through the 3dxsrv-compatible X11 protocol. */ if(spnav_x11_open(dpy, win) == -1) { fprintf(stderr, "failed to connect to the space navigator daemon\n"); return 1; } #elif defined(BUILD_AF_UNIX) if(spnav_open()==-1) { fprintf(stderr, "failed to connect to the space navigator daemon\n"); return 1; } #else #error Unknown build type! #endif /* spnav_wait_event() and spnav_poll_event(), will silently ignore any non-spnav X11 events. * * If you need to handle other X11 events you will have to use a regular XNextEvent() loop, * and pass any ClientMessage events to spnav_x11_event, which will return the event type or * zero if it's not an spnav event (see spnav.h). */ while(spnav_wait_event(&sev)) { if(sev.type == SPNAV_EVENT_MOTION) { printf("got motion event: t(%d, %d, %d) ", sev.motion.x, sev.motion.y, sev.motion.z); printf("r(%d, %d, %d)\n", sev.motion.rx, sev.motion.ry, sev.motion.rz); } else { /* SPNAV_EVENT_BUTTON */ printf("got button %s event b(%d)\n", sev.button.press ? "press" : "release", sev.button.bnum); } } spnav_close(); return 0; } libspnav-0.2.2/spnav_config.h0000644000175000017500000000013710642353337016031 0ustar nuclearnuclear#ifndef SPNAV_CONFIG_H_ #define SPNAV_CONFIG_H_ #define USE_X11 #endif /* SPNAV_CONFIG_H_ */ libspnav-0.2.2/spnav_magellan.h0000644000175000017500000000565311353511653016351 0ustar nuclearnuclear/* This file is part of libspnav, part of the spacenav project (spacenav.sf.net) Copyright (C) 2007-2010 John Tsiombikas Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* spnav_magellan.h and spnav_magellan.c provide source-level compatibility * with the original proprietary Magellan SDK provided by 3Dconnexion. * It is actually implemented as a wrapper on top of the new spnav interface * (see spnav.h). */ #ifndef SPACENAV_MAGELLAN_H_ #define SPACENAV_MAGELLAN_H_ #include enum { MagellanInputMotionEvent = 1, MagellanInputButtonPressEvent = 2, MagellanInputButtonReleaseEvent = 3 }; enum { MagellanX, MagellanY, MagellanZ, MagellanA, MagellanB, MagellanC }; typedef union { int data[7]; int button; } MagellanIntUnion; typedef struct { int type; MagellanIntUnion u; } MagellanIntEvent; typedef struct { int MagellanType; int MagellanButton; double MagellanData[6]; int MagellanPeriod; } MagellanFloatEvent; #ifdef __cplusplus extern "C" { #endif int MagellanInit(Display *dpy, Window win); int MagellanClose(Display *dpy); int MagellanSetWindow(Display *dpy, Window win); int MagellanApplicationSensitivity(Display *dpy, double sens); int MagellanInputEvent(Display *dpy, XEvent *event, MagellanIntEvent *mag_event); int MagellanTranslateEvent(Display *dpy, XEvent *event, MagellanFloatEvent *mag_event, double tscale, double rscale); int MagellanRemoveMotionEvents(Display *dpy); int MagellanRotationMatrix(double mat[4][4], double x, double y, double z); int MagellanMultiplicationMatrix(double mat_a[4][4], double mat_b[4][4], double mat_c[4][4]); #ifdef __cplusplus } #endif #endif /* SPACENAV_MAGELLAN_H_ */ libspnav-0.2.2/spnav_magellan.c0000644000175000017500000000710511353511653016336 0ustar nuclearnuclear/* This file is part of libspnav, part of the spacenav project (spacenav.sf.net) Copyright (C) 2007-2010 John Tsiombikas Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* spnav_magellan.h and spnav_magellan.c provide source-level compatibility * with the original proprietary Magellan SDK provided by 3Dconnexion. * It is actually implemented as a wrapper on top of the new spnav interface * (see spnav.h). */ #include "spnav_magellan.h" #include "spnav.h" int MagellanInit(Display *dpy, Window win) { return spnav_x11_open(dpy, win) == -1 ? 0 : 1; } int MagellanClose(Display *dpy) { return spnav_close() == -1 ? 0 : 1; } int MagellanSetWindow(Display *dpy, Window win) { return spnav_x11_window(win) == -1 ? 0 : 1; } int MagellanApplicationSensitivity(Display *dpy, double sens) { return spnav_sensitivity(sens) == -1 ? 0 : 1; } int MagellanInputEvent(Display *dpy, XEvent *xev, MagellanIntEvent *mev) { int i; spnav_event event; if(!spnav_x11_event(xev, &event)) { return 0; } if(event.type == SPNAV_EVENT_MOTION) { mev->type = MagellanInputMotionEvent; for(i=0; i<6; i++) { mev->u.data[i] = event.motion.data[i]; } mev->u.data[6] = event.motion.period * 1000 / 60; } else { mev->type = event.button.press ? MagellanInputButtonPressEvent : MagellanInputButtonReleaseEvent; mev->u.button = event.button.bnum; } return mev->type; } int MagellanTranslateEvent(Display *dpy, XEvent *xev, MagellanFloatEvent *mev, double tscale, double rscale) { int i; spnav_event event; if(!spnav_x11_event(xev, &event)) { return 0; } if(event.type == SPNAV_EVENT_MOTION) { mev->MagellanType = MagellanInputMotionEvent; for(i=0; i<6; i++) { mev->MagellanData[i] = (double)event.motion.data[i] * (i < 3 ? tscale : rscale); } mev->MagellanPeriod = event.motion.period; } else { mev->MagellanType = event.button.press ? MagellanInputButtonPressEvent : MagellanInputButtonReleaseEvent; mev->MagellanButton = event.button.bnum; } return mev->MagellanType; } int MagellanRemoveMotionEvents(Display *dpy) { return spnav_remove_events(SPNAV_EVENT_MOTION); } int MagellanRotationMatrix(double mat[4][4], double x, double y, double z) { return 0; /* TODO */ } int MagellanMultiplicationMatrix(double mat_a[4][4], double mat_b[4][4], double mat_c[4][4]) { return 0; /* TODO */ } libspnav-0.2.2/README0000644000175000017500000000443711353512367014073 0ustar nuclearnuclearlibspnav - 0.2.2 ---------------- 1. About The libspnav library is provided as a replacement of the magellan library. It provides a cleaner, and more orthogonal interface. libspnav supports both the original X11 protocol for communicating with the driver, and the new alternative non-X protocol. Programs that choose to use the X11 protocol, are automatically compatible with either the free spacenavd driver or the official 3dxserv, as if they were using the magellan SDK. Also, libspnav provides a magellan API wrapper on top of the new API. So, any applications that were using the magellan library, can switch to libspnav without any changes. And programmers that are familliar with the magellan API can continue using it with a free library without the restrictions of the official SDK. 2. Installation Configure, make, and make install as usual. 3. License libspnav is part of the spacenav project (spacenav.sf.net) Copyright (C) 2007-2010 John Tsiombikas Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. libspnav-0.2.2/spnav.c0000644000175000017500000002665211353511653014506 0ustar nuclearnuclear/* This file is part of libspnav, part of the spacenav project (spacenav.sf.net) Copyright (C) 2007-2010 John Tsiombikas Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include "spnav.h" #define SPNAV_SOCK_PATH "/var/run/spnav.sock" #ifdef USE_X11 #include #include static Window get_daemon_window(Display *dpy); static int catch_badwin(Display *dpy, XErrorEvent *err); static Display *dpy; static Window app_win; static Atom motion_event, button_press_event, button_release_event, command_event; enum { CMD_APP_WINDOW = 27695, CMD_APP_SENS }; #define IS_OPEN (dpy || (sock != -1)) #else #define IS_OPEN (sock != -1) #endif struct event_node { spnav_event event; struct event_node *next; }; /* only used for non-X mode, with spnav_remove_events */ static struct event_node *ev_queue, *ev_queue_tail; /* AF_UNIX socket used for alternative communication with daemon */ static int sock = -1; int spnav_open(void) { int s; struct sockaddr_un addr; if(IS_OPEN) { return -1; } if(!(ev_queue = malloc(sizeof *ev_queue))) { return -1; } ev_queue->next = 0; ev_queue_tail = ev_queue; if((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) { return -1; } memset(&addr, 0, sizeof addr); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, SPNAV_SOCK_PATH, sizeof(addr.sun_path)); if(connect(s, (struct sockaddr*)&addr, sizeof addr) == -1) { perror("connect failed"); return -1; } sock = s; return 0; } #ifdef USE_X11 int spnav_x11_open(Display *display, Window win) { if(IS_OPEN) { return -1; } dpy = display; motion_event = XInternAtom(dpy, "MotionEvent", True); button_press_event = XInternAtom(dpy, "ButtonPressEvent", True); button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True); command_event = XInternAtom(dpy, "CommandEvent", True); if(!motion_event || !button_press_event || !button_release_event || !command_event) { dpy = 0; return -1; /* daemon not started */ } if(spnav_x11_window(win) == -1) { dpy = 0; return -1; /* daemon not started */ } app_win = win; return 0; } #endif int spnav_close(void) { if(!IS_OPEN) { return -1; } if(sock) { while(ev_queue) { void *tmp = ev_queue; ev_queue = ev_queue->next; free(tmp); } close(sock); sock = 0; return 0; } #ifdef USE_X11 if(dpy) { spnav_x11_window(DefaultRootWindow(dpy)); app_win = 0; dpy = 0; return 0; } #endif return -1; } #ifdef USE_X11 int spnav_x11_window(Window win) { int (*prev_xerr_handler)(Display*, XErrorEvent*); XEvent xev; Window daemon_win; if(!IS_OPEN) { 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 = 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 int x11_sensitivity(double sens) { int (*prev_xerr_handler)(Display*, XErrorEvent*); XEvent xev; Window daemon_win; float fsens; unsigned int isens; if(!(daemon_win = get_daemon_window(dpy))) { return -1; } fsens = sens; isens = *(unsigned int*)&fsens; prev_xerr_handler = XSetErrorHandler(catch_badwin); xev.type = ClientMessage; xev.xclient.send_event = False; xev.xclient.display = dpy; xev.xclient.window = app_win; xev.xclient.message_type = command_event; xev.xclient.format = 16; xev.xclient.data.s[0] = isens & 0xffff; xev.xclient.data.s[1] = (isens & 0xffff0000) >> 16; xev.xclient.data.s[2] = CMD_APP_SENS; XSendEvent(dpy, daemon_win, False, 0, &xev); XSync(dpy, False); XSetErrorHandler(prev_xerr_handler); return 0; } #endif int spnav_sensitivity(double sens) { #ifdef USE_X11 if(dpy) { return x11_sensitivity(sens); } #endif if(sock) { ssize_t bytes; float fval = sens; while((bytes = write(sock, &fval, sizeof fval)) <= 0 && errno == EINTR); if(bytes <= 0) { return -1; } return 0; } return -1; } int spnav_fd(void) { #ifdef USE_X11 if(dpy) { return ConnectionNumber(dpy); } #endif return sock; } /* Checks both the event queue and the daemon socket for pending events. * In either case, it returns immediately with true/false values (doesn't block). */ static int event_pending(int s) { fd_set rd_set; struct timeval tv; if(ev_queue->next) { return 1; } FD_ZERO(&rd_set); FD_SET(s, &rd_set); /* don't block, just poll */ tv.tv_sec = tv.tv_usec = 0; if(select(s + 1, &rd_set, 0, 0, &tv) > 0) { return 1; } return 0; } /* If there are events waiting in the event queue, dequeue one and * return that, otherwise read one from the daemon socket. * This might block unless we called event_pending() first and it returned true. */ static int read_event(int s, spnav_event *event) { int i, rd; int data[8]; /* if we have a queued event, deliver that one */ if(ev_queue->next) { struct event_node *node = ev_queue->next; ev_queue->next = ev_queue->next->next; /* dequeued the last event, must update tail pointer */ if(ev_queue_tail == node) { ev_queue_tail = ev_queue; } memcpy(event, &node->event, sizeof *event); free(node); return event->type; } /* otherwise read one from the connection */ do { rd = read(s, data, sizeof data); } while(rd == -1 && errno == EINTR); if(rd <= 0) { return 0; } if(data[0] < 0 || data[0] > 2) { return 0; } event->type = data[0] ? SPNAV_EVENT_BUTTON : SPNAV_EVENT_MOTION; if(event->type == SPNAV_EVENT_MOTION) { event->motion.data = &event->motion.x; for(i=0; i<6; i++) { event->motion.data[i] = data[i + 1]; } event->motion.period = data[7]; } else { event->button.press = data[0] == 1 ? 1 : 0; event->button.bnum = data[1]; } return event->type; } int spnav_wait_event(spnav_event *event) { #ifdef USE_X11 if(dpy) { for(;;) { XEvent xev; XNextEvent(dpy, &xev); if(spnav_x11_event(&xev, event) > 0) { return event->type; } } } #endif if(sock) { if(read_event(sock, event) > 0) { return event->type; } } return 0; } int spnav_poll_event(spnav_event *event) { #ifdef USE_X11 if(dpy) { if(XPending(dpy)) { XEvent xev; XNextEvent(dpy, &xev); return spnav_x11_event(&xev, event); } return 0; } #endif if(sock) { if(event_pending(sock)) { if(read_event(sock, event) > 0) { return event->type; } } } return 0; } #ifdef USE_X11 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 == motion_event) { return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False; } if(xev->xclient.message_type == button_press_event || xev->xclient.message_type == button_release_event) { return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False; } return False; } #endif /* Appends an event to an event list. * Tailptr must be a pointer to the tail pointer of the list. NULL means * append to the global event queue. */ static int enqueue_event(spnav_event *event, struct event_node **tailptr) { struct event_node *node; if(!(node = malloc(sizeof *node))) { return -1; } node->event = *event; node->next = 0; if(!tailptr) { tailptr = &ev_queue_tail; } (*tailptr)->next = node; *tailptr = node; return 0; } int spnav_remove_events(int type) { int rm_count = 0; #ifdef USE_X11 if(dpy) { XEvent xev; while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) { rm_count++; } return rm_count; } #endif if(sock) { struct event_node *tmplist, *tmptail; if(!(tmplist = tmptail = malloc(sizeof *tmplist))) { return -1; } tmplist->next = 0; /* while there are events in the event queue, or the daemon socket */ while(event_pending(sock)) { spnav_event event; read_event(sock, &event); /* remove next event */ if(event.type != type) { /* We don't want to drop this one, wrong type. Keep the event * in the temporary list, for deferred reinsertion */ enqueue_event(&event, &tmptail); } else { rm_count++; } } /* reinsert any events we removed that we didn't mean to */ while(tmplist->next) { struct event_node *node = tmplist->next; enqueue_event(&node->event, 0); free(tmplist); tmplist = node; } return rm_count; } return 0; } #ifdef USE_X11 int spnav_x11_event(const XEvent *xev, spnav_event *event) { int i; int xmsg_type; if(xev->type != ClientMessage) { return 0; } xmsg_type = xev->xclient.message_type; if(xmsg_type != motion_event && xmsg_type != button_press_event && xmsg_type != button_release_event) { return 0; } if(xmsg_type == 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 == button_press_event ? 1 : 0; event->button.bnum = xev->xclient.data.s[2]; } return event->type; } static Window get_daemon_window(Display *dpy) { Window win, root_win; XTextProperty wname; Atom type; int fmt; unsigned long nitems, bytes_after; unsigned char *prop; root_win = DefaultRootWindow(dpy); XGetWindowProperty(dpy, root_win, 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) || strcmp("Magellan Window", (char*)wname.value) != 0) { return 0; } return win; } int catch_badwin(Display *dpy, XErrorEvent *err) { char buf[256]; if(err->error_code == BadWindow) { /* do nothing? */ } else { XGetErrorText(dpy, err->error_code, buf, sizeof buf); fprintf(stderr, "Caught unexpected X error: %s\n", buf); } return 0; } #endif libspnav-0.2.2/configure0000755000175000017500000000426411231166353015113 0ustar nuclearnuclear#!/bin/sh echo 'configuring spacenav library...' PREFIX=/usr/local OPT=yes DBG=yes X11=yes srcdir="`dirname "$0"`" libdir=lib #if [ "`uname -m`" = 'x86_64' ]; then # libdir=lib64 #fi for arg; do case "$arg" in --prefix=*) value=`echo $arg | sed 's/--prefix=//'` PREFIX=${value:-$prefix} ;; --enable-opt) OPT=yes;; --disable-opt) OPT=no;; --enable-debug) DBG=yes;; --disable-debug) DBG=no;; --enable-x11) X11=yes;; --disable-x11) X11=no;; --help) echo 'usage: ./configure [options]' echo 'options:' echo ' --prefix=: installation path (default: /usr/local)' echo ' --enable-x11: enable X11 communication mode (default)' echo ' --disable-x11: disable X11 communication mode' echo ' --enable-opt: enable speed optimizations (default)' echo ' --disable-opt: disable speed optimizations' echo ' --enable-debug: include debugging symbols (default)' echo ' --disable-debug: do not include debugging symbols' echo 'all invalid options are silently ignored' exit 0 ;; esac done echo " prefix: $PREFIX" echo " optimize for speed: $OPT" echo " include debugging symbols: $DBG" echo " x11 communication method: $X11" echo "" if [ "$X11" = "no" ]; then echo "WARNING: you have disabled the X11 interface, the resulting library won't be compatible with the proprietary 3Dconnexion daemon (3dxserv)!" echo "" fi # create Makefile echo 'creating Makefile ...' echo "PREFIX = $PREFIX" >Makefile echo "srcdir = $srcdir" >>Makefile echo "libdir = $libdir" >>Makefile if [ "$DBG" = 'yes' ]; then echo 'dbg = -g' >>Makefile fi if [ "$OPT" = 'yes' ]; then echo 'opt = -O3' >>Makefile fi if [ "$X11" = 'yes' ]; then echo 'magellan_obj = spnav_magellan.o' >>Makefile echo 'xlib = -lX11' >>Makefile fi cat "$srcdir/Makefile.in" >>Makefile # create spnav_config.h echo 'creating spnav_config.h ...' echo '#ifndef SPNAV_CONFIG_H_' >spnav_config.h echo '#define SPNAV_CONFIG_H_' >>spnav_config.h echo '' >>spnav_config.h if [ "$X11" = 'yes' ]; then echo '#define USE_X11' >>spnav_config.h echo '' >>spnav_config.h fi echo '#endif /* SPNAV_CONFIG_H_ */' >>spnav_config.h #done echo '' echo 'Done. You can now type make (or gmake) to compile libspnav.' echo ''