fi
# Generate output.
-AC_CONFIG_FILES([Makefile doc/Makefile include/GL/Makefile include/Makefile progs/Makefile progs/demos/CallbackMaker/Makefile progs/demos/Fractals/Makefile progs/demos/Fractals_random/Makefile progs/demos/Lorenz/Makefile progs/demos/Makefile progs/demos/One/Makefile progs/demos/shapes/Makefile progs/demos/smooth_opengl3/Makefile src/Makefile])
+AC_CONFIG_FILES([Makefile doc/Makefile include/GL/Makefile include/Makefile progs/Makefile progs/demos/CallbackMaker/Makefile progs/demos/Fractals/Makefile progs/demos/Fractals_random/Makefile progs/demos/Lorenz/Makefile progs/demos/Makefile progs/demos/One/Makefile progs/demos/shapes/Makefile progs/demos/smooth_opengl3/Makefile progs/demos/spaceball/Makefile src/Makefile])
AC_OUTPUT
EXTRA_DIST = demos.dsw
-SUBDIRS = CallbackMaker Fractals Fractals_random Lorenz One shapes smooth_opengl3
+SUBDIRS = CallbackMaker Fractals Fractals_random Lorenz One shapes smooth_opengl3 spaceball
###############################################################################
+Project: "spaceball"=.\spaceball\spaceball.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
Global:
Package=<5>
--- /dev/null
+EXTRA_DIST = spaceball.c vmath.c vmath.h vmath.inl
+
+noinst_PROGRAMS = spaceball
+spaceball_SOURCES = spaceball.c vmath.c
+spaceball_LDFLAGS = -export-dynamic ../../../src/lib@LIBRARY@.la
+spaceball_CFLAGS = -I$(top_srcdir)/include $(X_CFLAGS)
\ No newline at end of file
--- /dev/null
+/* Spaceball demo\r
+ *\r
+ * Written by John Tsiombikas <nuclear@member.fsf.org>\r
+ * (converted from the libspnav cube example)\r
+ *\r
+ * Use the spaceball to move and rotate the colored cube.\r
+ * Pressing any button will reset the cube at its original location.\r
+ *\r
+ * Press escape or q to exit.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <math.h>\r
+#include <GL/freeglut.h>\r
+#include "vmath.h"\r
+\r
+void draw_cube(void);\r
+\r
+/* callbacks */\r
+void disp(void);\r
+void reshape(int x, int y);\r
+void keyb(unsigned char key, int x, int y);\r
+void sbmot(int x, int y, int z); /* spaceball translation */\r
+void sbrot(int x, int y, int z); /* spaceball rotation */\r
+void sbbut(int bn, int state); /* spaceball button */\r
+\r
+vec3_t pos = {0, 0, -6};\r
+quat_t rot = {0, 0, 0, 1};\r
+\r
+int main(int argc, char **argv)\r
+{\r
+ glutInit(&argc, argv);\r
+ glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);\r
+ glutCreateWindow("spaceball demo");\r
+\r
+ glutDisplayFunc(disp);\r
+ glutReshapeFunc(reshape);\r
+ glutKeyboardFunc(keyb);\r
+ glutSpaceballMotionFunc(sbmot);\r
+ glutSpaceballRotateFunc(sbrot);\r
+ glutSpaceballButtonFunc(sbbut);\r
+\r
+ glEnable(GL_CULL_FACE);\r
+\r
+ glutMainLoop();\r
+ return 0;\r
+}\r
+\r
+void disp(void)\r
+{\r
+ mat4_t xform;\r
+\r
+ quat_to_mat(xform, rot);\r
+\r
+ glClear(GL_COLOR_BUFFER_BIT);\r
+\r
+ glMatrixMode(GL_MODELVIEW);\r
+ glLoadIdentity();\r
+ glTranslatef(pos.x, pos.y, pos.z);\r
+ glMultTransposeMatrixf((float*)xform);\r
+\r
+ draw_cube();\r
+\r
+ glutSwapBuffers();\r
+}\r
+\r
+void draw_cube(void)\r
+{\r
+ glBegin(GL_QUADS);\r
+ /* face +Z */\r
+ glNormal3f(0, 0, 1);\r
+ glColor3f(1, 0, 0);\r
+ glVertex3f(-1, -1, 1);\r
+ glVertex3f(1, -1, 1);\r
+ glVertex3f(1, 1, 1);\r
+ glVertex3f(-1, 1, 1);\r
+ /* face +X */\r
+ glNormal3f(1, 0, 0);\r
+ glColor3f(0, 1, 0);\r
+ glVertex3f(1, -1, 1);\r
+ glVertex3f(1, -1, -1);\r
+ glVertex3f(1, 1, -1);\r
+ glVertex3f(1, 1, 1);\r
+ /* face -Z */\r
+ glNormal3f(0, 0, -1);\r
+ glColor3f(0, 0, 1);\r
+ glVertex3f(1, -1, -1);\r
+ glVertex3f(-1, -1, -1);\r
+ glVertex3f(-1, 1, -1);\r
+ glVertex3f(1, 1, -1);\r
+ /* face -X */\r
+ glNormal3f(-1, 0, 0);\r
+ glColor3f(1, 1, 0);\r
+ glVertex3f(-1, -1, -1);\r
+ glVertex3f(-1, -1, 1);\r
+ glVertex3f(-1, 1, 1);\r
+ glVertex3f(-1, 1, -1);\r
+ /* face +Y */\r
+ glNormal3f(0, 1, 0);\r
+ glColor3f(0, 1, 1);\r
+ glVertex3f(-1, 1, 1);\r
+ glVertex3f(1, 1, 1);\r
+ glVertex3f(1, 1, -1);\r
+ glVertex3f(-1, 1, -1);\r
+ /* face -Y */\r
+ glNormal3f(0, -1, 0);\r
+ glColor3f(1, 0, 1);\r
+ glVertex3f(-1, -1, -1);\r
+ glVertex3f(1, -1, -1);\r
+ glVertex3f(1, -1, 1);\r
+ glVertex3f(-1, -1, 1);\r
+ glEnd();\r
+}\r
+\r
+/* 45deg fov */\r
+#define FOV (M_PI / 4.0)\r
+\r
+void reshape(int x, int y)\r
+{\r
+ float aspect = (float)x / (float)y;\r
+ float halfy = tan(FOV / 2.0);\r
+ float halfx = halfy * aspect;\r
+\r
+ glViewport(0, 0, x, y);\r
+\r
+ glMatrixMode(GL_PROJECTION);\r
+ glLoadIdentity();\r
+ glFrustum(-halfx, halfx, -halfy, halfy, 1.0, 1000.0);\r
+}\r
+\r
+void keyb(unsigned char key, int x, int y)\r
+{\r
+ switch(key) {\r
+ case 'q':\r
+ case 'Q':\r
+ case 27:\r
+ exit(0);\r
+\r
+ case ' ':\r
+ /* reset initial view */\r
+ pos = v3_cons(0, 0, -6);\r
+ rot = quat_cons(1, 0, 0, 0);\r
+ glutPostRedisplay();\r
+\r
+ default:\r
+ break;\r
+ }\r
+}\r
+\r
+void sbmot(int x, int y, int z)\r
+{\r
+ pos.x += x * 0.001;\r
+ pos.y += y * 0.001;\r
+ pos.z -= z * 0.001;\r
+ glutPostRedisplay();\r
+}\r
+\r
+void sbrot(int x, int y, int z)\r
+{\r
+ float axis_len = sqrt(x * x + y * y + z * z);\r
+ rot = quat_rotate(rot, axis_len * 0.001, -x / axis_len, -y / axis_len, z / axis_len);\r
+ glutPostRedisplay();\r
+}\r
+\r
+void sbbut(int bn, int state)\r
+{\r
+ if(state == GLUT_DOWN) {\r
+ pos = v3_cons(0, 0, -6);\r
+ rot = quat_cons(1, 0, 0, 0);\r
+ glutPostRedisplay();\r
+ }\r
+}\r
--- /dev/null
+# Microsoft Developer Studio Project File - Name="spaceball" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Console Application" 0x0103\r
+\r
+CFG=spaceball - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "spaceball.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "spaceball.mak" CFG="spaceball - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "spaceball - Win32 Release" (based on "Win32 (x86) Console Application")\r
+!MESSAGE "spaceball - Win32 Debug" (based on "Win32 (x86) Console Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+RSC=rc.exe\r
+\r
+!IF "$(CFG)" == "spaceball - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "Release"\r
+# PROP Intermediate_Dir "Release"\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD BASE RSC /l 0x409 /d "NDEBUG"\r
+# ADD RSC /l 0x409 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\r
+\r
+!ELSEIF "$(CFG)" == "spaceball - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug"\r
+# PROP BASE Intermediate_Dir "Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "Debug"\r
+# PROP Intermediate_Dir "Debug"\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD BASE RSC /l 0x409 /d "_DEBUG"\r
+# ADD RSC /l 0x409 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "spaceball - Win32 Release"\r
+# Name "spaceball - Win32 Debug"\r
+# Begin Group "Source Files"\r
+\r
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"\r
+# End Group\r
+# Begin Group "Header Files"\r
+\r
+# PROP Default_Filter "h;hpp;hxx;hm;inl"\r
+# End Group\r
+# Begin Group "Resource Files"\r
+\r
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"\r
+# End Group\r
+# End Target\r
+# End Project\r
--- /dev/null
+#include <math.h>\r
+#include "vmath.h"\r
+\r
+quat_t quat_rotate(quat_t q, float angle, float x, float y, float z)\r
+{\r
+ quat_t rq;\r
+ float half_angle = angle * 0.5;\r
+ float sin_half = sin(half_angle);\r
+\r
+ rq.w = cos(half_angle);\r
+ rq.x = x * sin_half;\r
+ rq.y = y * sin_half;\r
+ rq.z = z * sin_half;\r
+\r
+ return quat_mul(q, rq);\r
+}\r
--- /dev/null
+#ifndef VMATH_H_\r
+#define VMATH_H_\r
+\r
+typedef struct { float x, y, z; } vec3_t;\r
+typedef struct { float x, y, z, w; } vec4_t;\r
+\r
+typedef vec4_t quat_t;\r
+\r
+typedef float mat4_t[4][4];\r
+\r
+/* vector functions */\r
+static inline vec3_t v3_cons(float x, float y, float z);\r
+static inline float v3_dot(vec3_t v1, vec3_t v2);\r
+\r
+/* quaternion functions */\r
+static inline quat_t quat_cons(float s, float x, float y, float z);\r
+static inline vec3_t quat_vec(quat_t q);\r
+static inline quat_t quat_mul(quat_t q1, quat_t q2);\r
+static inline void quat_to_mat(mat4_t res, quat_t q);\r
+quat_t quat_rotate(quat_t q, float angle, float x, float y, float z);\r
+\r
+/* matrix functions */\r
+static inline void m4_cons(mat4_t m,\r
+ float m11, float m12, float m13, float m14,\r
+ float m21, float m22, float m23, float m24,\r
+ float m31, float m32, float m33, float m34,\r
+ float m41, float m42, float m43, float m44);\r
+\r
+#include "vmath.inl"\r
+\r
+#endif /* VMATH_H_ */\r
--- /dev/null
+/* vector functions */\r
+static inline vec3_t v3_cons(float x, float y, float z)\r
+{\r
+ vec3_t res;\r
+ res.x = x;\r
+ res.y = y;\r
+ res.z = z;\r
+ return res;\r
+}\r
+\r
+static inline vec3_t quat_vec(quat_t q)\r
+{\r
+ vec3_t v;\r
+ v.x = q.x;\r
+ v.y = q.y;\r
+ v.z = q.z;\r
+ return v;\r
+}\r
+\r
+static inline float v3_dot(vec3_t v1, vec3_t v2)\r
+{\r
+ return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;\r
+}\r
+\r
+/* quaternion functions */\r
+static inline quat_t quat_cons(float s, float x, float y, float z)\r
+{\r
+ quat_t q;\r
+ q.x = x;\r
+ q.y = y;\r
+ q.z = z;\r
+ q.w = s;\r
+ return q;\r
+}\r
+\r
+static inline quat_t quat_mul(quat_t q1, quat_t q2)\r
+{\r
+ quat_t res;\r
+ vec3_t v1 = quat_vec(q1);\r
+ vec3_t v2 = quat_vec(q2);\r
+\r
+ res.w = q1.w * q2.w - v3_dot(v1, v2);\r
+ res.x = v2.x * q1.w + v1.x * q2.w + (v1.y * v2.z - v1.z * v2.y);\r
+ res.y = v2.y * q1.w + v1.y * q2.w + (v1.z * v2.x - v1.x * v2.z);\r
+ res.z = v2.z * q1.w + v1.z * q2.w + (v1.x * v2.y - v1.y * v2.x);\r
+ return res;\r
+}\r
+\r
+static inline void quat_to_mat(mat4_t res, quat_t q)\r
+{\r
+ 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,\r
+ 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,\r
+ 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,\r
+ 0, 0, 0, 1);\r
+}\r
+\r
+/* matrix functions */\r
+static inline void m4_cons(mat4_t m,\r
+ float m11, float m12, float m13, float m14,\r
+ float m21, float m22, float m23, float m24,\r
+ float m31, float m32, float m33, float m34,\r
+ float m41, float m42, float m43, float m44)\r
+{\r
+ m[0][0] = m11; m[0][1] = m12; m[0][2] = m13; m[0][3] = m14;\r
+ m[1][0] = m21; m[1][1] = m22; m[1][2] = m23; m[1][3] = m24;\r
+ m[2][0] = m31; m[2][1] = m32; m[2][2] = m33; m[2][3] = m34;\r
+ m[3][0] = m41; m[3][1] = m42; m[3][2] = m43; m[3][3] = m44;\r
+}\r
freeglut_geometry.c \
freeglut_init.c \
freeglut_input_devices.c \
+ freeglut_spaceball.c \
freeglut_joystick.c \
freeglut_main.c \
freeglut_menu.c \
void FGAPIENTRY glutSpaceballMotionFunc( void (* callback)( int, int, int ) )
{
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballMotionFunc" );
+ fgInitialiseSpaceball();
+
SET_CALLBACK( SpaceMotion );
}
void FGAPIENTRY glutSpaceballRotateFunc( void (* callback)( int, int, int ) )
{
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballRotateFunc" );
+ fgInitialiseSpaceball();
+
SET_CALLBACK( SpaceRotation );
}
void FGAPIENTRY glutSpaceballButtonFunc( void (* callback)( int, int ) )
{
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballButtonFunc" );
+ fgInitialiseSpaceball();
+
SET_CALLBACK( SpaceButton );
}
/* Presently ignored */
CB_Select,
CB_OverlayDisplay,
- CB_SpaceMotion,
- CB_SpaceRotation,
- CB_SpaceButton,
+ CB_SpaceMotion, /* presently implemented only on UNIX/X11 */
+ CB_SpaceRotation, /* presently implemented only on UNIX/X11 */
+ CB_SpaceButton, /* presently implemented only on UNIX/X11 */
CB_Dials,
CB_ButtonBox,
CB_TabletMotion,
void fgInitialiseInputDevices( void );
void fgInputDeviceClose( void );
+/* spaceball device functions, defined in freeglut_spaceball.c */
+void fgInitialiseSpaceball( void );
+void fgSpaceballClose( void );
+void fgSpaceballSetWindow( SFG_Window *window );
+
+int fgHasSpaceball( void );
+int fgSpaceballNumButtons( void );
+
+#if TARGET_HOST_POSIX_X11
+int fgIsSpaceballXEvent( const XEvent *ev );
+void fgSpaceballHandleXEvent( const XEvent *ev );
+#endif
+
/* Setting the cursor for a given window */
void fgSetCursor ( SFG_Window *window, int cursorID );
switch( event.type )
{
case ClientMessage:
+ if(fgIsSpaceballXEvent(&event)) {
+ fgSpaceballHandleXEvent(&event);
+ break;
+ }
/* Destroy the window when the WM_DELETE_WINDOW message arrives */
if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.DeleteWindow )
{
--- /dev/null
+/* Spaceball support for Linux.\r
+ * Written by John Tsiombikas <nuclear@member.fsf.org>\r
+ *\r
+ * This code supports 3Dconnexion's 6-dof space-whatever devices.\r
+ * It can communicate with either the proprietary 3Dconnexion daemon (3dxsrv)\r
+ * free spacenavd (http://spacenav.sourceforge.net), through the "standard"\r
+ * magellan X-based protocol.\r
+ */\r
+\r
+#include <GL/freeglut.h>\r
+#include "freeglut_internal.h"\r
+\r
+#if TARGET_HOST_POSIX_X11\r
+#include <X11/Xlib.h>\r
+\r
+enum {\r
+ SPNAV_EVENT_ANY, /* used by spnav_remove_events() */\r
+ SPNAV_EVENT_MOTION,\r
+ SPNAV_EVENT_BUTTON /* includes both press and release */\r
+};\r
+\r
+struct spnav_event_motion {\r
+ int type;\r
+ int x, y, z;\r
+ int rx, ry, rz;\r
+ unsigned int period;\r
+ int *data;\r
+};\r
+\r
+struct spnav_event_button {\r
+ int type;\r
+ int press;\r
+ int bnum;\r
+};\r
+\r
+typedef union spnav_event {\r
+ int type;\r
+ struct spnav_event_motion motion;\r
+ struct spnav_event_button button;\r
+} spnav_event;\r
+\r
+\r
+static int spnav_x11_open(Display *dpy, Window win);\r
+static int spnav_x11_window(Window win);\r
+static int spnav_x11_event(const XEvent *xev, spnav_event *event);\r
+static int spnav_close(void);\r
+static int spnav_fd(void);\r
+static int spnav_remove_events(int type);\r
+\r
+static SFG_Window *spnav_win;\r
+#endif\r
+\r
+static int sball_initialized;\r
+\r
+\r
+void fgInitialiseSpaceball(void)\r
+{\r
+ if(sball_initialized) {\r
+ return;\r
+ }\r
+\r
+#if TARGET_HOST_POSIX_X11\r
+ {\r
+ Window w;\r
+ \r
+ if(!fgStructure.CurrentWindow) {\r
+ fgWarning("fgInitialiseSpaceball: no current window!\n");\r
+ return;\r
+ }\r
+\r
+ w = fgStructure.CurrentWindow->Window.Handle;\r
+ if(spnav_x11_open(fgDisplay.Display, w) == -1) {\r
+ return;\r
+ }\r
+ }\r
+#endif\r
+\r
+ sball_initialized = 1;\r
+}\r
+\r
+void fgSpaceballClose(void)\r
+{\r
+#if TARGET_HOST_POSIX_X11\r
+ spnav_close();\r
+#endif\r
+}\r
+\r
+int fgHasSpaceball(void)\r
+{\r
+ if(!sball_initialized) {\r
+ fgInitialiseSpaceball();\r
+ if(!sball_initialized) {\r
+ fgWarning("fgInitialiseSpaceball failed\n");\r
+ return 0;\r
+ }\r
+ }\r
+\r
+#if TARGET_HOST_POSIX_X11\r
+ /* XXX this function should somehow query the driver if there's a device\r
+ * plugged in, as opposed to just checking if there's a driver to talk to.\r
+ */\r
+ return spnav_fd() == -1 ? 0 : 1;\r
+#else\r
+ return 0;\r
+#endif\r
+}\r
+\r
+int fgSpaceballNumButtons(void)\r
+{\r
+ if(!sball_initialized) {\r
+ fgInitialiseSpaceball();\r
+ if(!sball_initialized) {\r
+ fgWarning("fgInitialiseSpaceball failed\n");\r
+ return 0;\r
+ }\r
+ }\r
+\r
+#if TARGET_HOST_POSIX_X11\r
+ return 2; /* TODO implement this properly */\r
+#else\r
+ return 0;\r
+#endif\r
+}\r
+\r
+void fgSpaceballSetWindow(SFG_Window *window)\r
+{\r
+ if(!sball_initialized) {\r
+ fgInitialiseSpaceball();\r
+ if(!sball_initialized) {\r
+ return;\r
+ }\r
+ }\r
+\r
+#if TARGET_HOST_POSIX_X11\r
+ if(spnav_win != window) {\r
+ spnav_x11_window(window->Window.Handle);\r
+ spnav_win = window;\r
+ }\r
+#endif\r
+}\r
+\r
+\r
+#if TARGET_HOST_POSIX_X11\r
+int fgIsSpaceballXEvent(const XEvent *xev)\r
+{\r
+ spnav_event sev;\r
+\r
+ if(!sball_initialized) {\r
+ fgInitialiseSpaceball();\r
+ if(!sball_initialized) {\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ return spnav_x11_event(xev, &sev);\r
+}\r
+\r
+void fgSpaceballHandleXEvent(const XEvent *xev)\r
+{\r
+ spnav_event sev;\r
+\r
+ if(!sball_initialized) {\r
+ fgInitialiseSpaceball();\r
+ if(!sball_initialized) {\r
+ return;\r
+ }\r
+ }\r
+\r
+ if(spnav_x11_event(xev, &sev)) {\r
+ switch(sev.type) {\r
+ case SPNAV_EVENT_MOTION:\r
+ if(sev.motion.x | sev.motion.y | sev.motion.z) {\r
+ INVOKE_WCB(*spnav_win, SpaceMotion, (sev.motion.x, sev.motion.y, sev.motion.z));\r
+ }\r
+ if(sev.motion.rx | sev.motion.ry | sev.motion.rz) {\r
+ INVOKE_WCB(*spnav_win, SpaceRotation, (sev.motion.rx, sev.motion.ry, sev.motion.rz));\r
+ }\r
+ spnav_remove_events(SPNAV_EVENT_MOTION);\r
+ break;\r
+\r
+ case SPNAV_EVENT_BUTTON:\r
+ INVOKE_WCB(*spnav_win, SpaceButton, (sev.button.bnum, sev.button.press ? GLUT_DOWN : GLUT_UP));\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+/*\r
+The following code is part of libspnav, part of the spacenav project (spacenav.sf.net)\r
+Copyright (C) 2007-2009 John Tsiombikas <nuclear@member.fsf.org>\r
+\r
+Redistribution and use in source and binary forms, with or without\r
+modification, are permitted provided that the following conditions are met:\r
+\r
+1. Redistributions of source code must retain the above copyright notice, this\r
+ list of conditions and the following disclaimer.\r
+2. Redistributions in binary form must reproduce the above copyright notice,\r
+ this list of conditions and the following disclaimer in the documentation\r
+ and/or other materials provided with the distribution.\r
+3. The name of the author may not be used to endorse or promote products\r
+ derived from this software without specific prior written permission.\r
+\r
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\r
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\r
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\r
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT\r
+OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\r
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY\r
+OF SUCH DAMAGE.\r
+*/\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <errno.h>\r
+\r
+#include <X11/Xlib.h>\r
+#include <X11/Xutil.h>\r
+\r
+static Window get_daemon_window(Display *dpy);\r
+static int catch_badwin(Display *dpy, XErrorEvent *err);\r
+\r
+static Display *dpy;\r
+static Window app_win;\r
+static Atom motion_event, button_press_event, button_release_event, command_event;\r
+\r
+enum {\r
+ CMD_APP_WINDOW = 27695,\r
+ CMD_APP_SENS\r
+};\r
+\r
+#define IS_OPEN dpy\r
+\r
+struct event_node {\r
+ spnav_event event;\r
+ struct event_node *next;\r
+};\r
+\r
+static int spnav_x11_open(Display *display, Window win)\r
+{\r
+ if(IS_OPEN) {\r
+ return -1;\r
+ }\r
+\r
+ dpy = display;\r
+\r
+ motion_event = XInternAtom(dpy, "MotionEvent", True);\r
+ button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);\r
+ button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);\r
+ command_event = XInternAtom(dpy, "CommandEvent", True);\r
+\r
+ if(!motion_event || !button_press_event || !button_release_event || !command_event) {\r
+ dpy = 0;\r
+ return -1; /* daemon not started */\r
+ }\r
+\r
+ if(spnav_x11_window(win) == -1) {\r
+ dpy = 0;\r
+ return -1; /* daemon not started */\r
+ }\r
+\r
+ app_win = win;\r
+ return 0;\r
+}\r
+\r
+static int spnav_close(void)\r
+{\r
+ if(dpy) {\r
+ spnav_x11_window(DefaultRootWindow(dpy));\r
+ app_win = 0;\r
+ dpy = 0;\r
+ return 0;\r
+ }\r
+ return -1;\r
+}\r
+\r
+static int spnav_x11_window(Window win)\r
+{\r
+ int (*prev_xerr_handler)(Display*, XErrorEvent*);\r
+ XEvent xev;\r
+ Window daemon_win;\r
+\r
+ if(!IS_OPEN) {\r
+ return -1;\r
+ }\r
+\r
+ if(!(daemon_win = get_daemon_window(dpy))) {\r
+ return -1;\r
+ }\r
+\r
+ prev_xerr_handler = XSetErrorHandler(catch_badwin);\r
+\r
+ xev.type = ClientMessage;\r
+ xev.xclient.send_event = False;\r
+ xev.xclient.display = dpy;\r
+ xev.xclient.window = win;\r
+ xev.xclient.message_type = command_event;\r
+ xev.xclient.format = 16;\r
+ xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;\r
+ xev.xclient.data.s[1] = (unsigned int)win & 0xffff;\r
+ xev.xclient.data.s[2] = CMD_APP_WINDOW;\r
+\r
+ XSendEvent(dpy, daemon_win, False, 0, &xev);\r
+ XSync(dpy, False);\r
+\r
+ XSetErrorHandler(prev_xerr_handler);\r
+ return 0;\r
+}\r
+\r
+static int spnav_fd(void)\r
+{\r
+ if(dpy) {\r
+ return ConnectionNumber(dpy);\r
+ }\r
+ return -1;\r
+}\r
+\r
+/*static int spnav_wait_event(spnav_event *event)\r
+{\r
+ if(dpy) {\r
+ for(;;) {\r
+ XEvent xev;\r
+ XNextEvent(dpy, &xev);\r
+\r
+ if(spnav_x11_event(&xev, event) > 0) {\r
+ return event->type;\r
+ }\r
+ }\r
+ }\r
+ return 0;\r
+}\r
+\r
+static int spnav_poll_event(spnav_event *event)\r
+{\r
+ if(dpy) {\r
+ if(XPending(dpy)) {\r
+ XEvent xev;\r
+ XNextEvent(dpy, &xev);\r
+\r
+ return spnav_x11_event(&xev, event);\r
+ }\r
+ }\r
+ return 0;\r
+}*/\r
+\r
+static Bool match_events(Display *dpy, XEvent *xev, char *arg)\r
+{\r
+ int evtype = *(int*)arg;\r
+\r
+ if(xev->type != ClientMessage) {\r
+ return False;\r
+ }\r
+\r
+ if(xev->xclient.message_type == motion_event) {\r
+ return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;\r
+ }\r
+ if(xev->xclient.message_type == button_press_event ||\r
+ xev->xclient.message_type == button_release_event) {\r
+ return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;\r
+ }\r
+ return False;\r
+}\r
+\r
+static int spnav_remove_events(int type)\r
+{\r
+ int rm_count = 0;\r
+\r
+ if(dpy) {\r
+ XEvent xev;\r
+\r
+ while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {\r
+ rm_count++;\r
+ }\r
+ return rm_count;\r
+ }\r
+ return 0;\r
+}\r
+\r
+static int spnav_x11_event(const XEvent *xev, spnav_event *event)\r
+{\r
+ int i;\r
+ int xmsg_type;\r
+\r
+ if(xev->type != ClientMessage) {\r
+ return 0;\r
+ }\r
+\r
+ xmsg_type = xev->xclient.message_type;\r
+\r
+ if(xmsg_type != motion_event && xmsg_type != button_press_event &&\r
+ xmsg_type != button_release_event) {\r
+ return 0;\r
+ }\r
+\r
+ if(xmsg_type == motion_event) {\r
+ event->type = SPNAV_EVENT_MOTION;\r
+ event->motion.data = &event->motion.x;\r
+\r
+ for(i=0; i<6; i++) {\r
+ event->motion.data[i] = xev->xclient.data.s[i + 2];\r
+ }\r
+ event->motion.period = xev->xclient.data.s[8];\r
+ } else {\r
+ event->type = SPNAV_EVENT_BUTTON;\r
+ event->button.press = xmsg_type == button_press_event ? 1 : 0;\r
+ event->button.bnum = xev->xclient.data.s[2];\r
+ }\r
+ return event->type;\r
+}\r
+\r
+\r
+static Window get_daemon_window(Display *dpy)\r
+{\r
+ Window win, root_win;\r
+ XTextProperty wname;\r
+ Atom type;\r
+ int fmt;\r
+ unsigned long nitems, bytes_after;\r
+ unsigned char *prop;\r
+\r
+ root_win = DefaultRootWindow(dpy);\r
+\r
+ XGetWindowProperty(dpy, root_win, command_event, 0, 1, False, AnyPropertyType, &type, &fmt, &nitems, &bytes_after, &prop);\r
+ if(!prop) {\r
+ return 0;\r
+ }\r
+\r
+ win = *(Window*)prop;\r
+ XFree(prop);\r
+\r
+ if(!XGetWMName(dpy, win, &wname) || strcmp("Magellan Window", (char*)wname.value) != 0) {\r
+ return 0;\r
+ }\r
+\r
+ return win;\r
+}\r
+\r
+static int catch_badwin(Display *dpy, XErrorEvent *err)\r
+{\r
+ char buf[256];\r
+\r
+ if(err->error_code == BadWindow) {\r
+ /* do nothing? */\r
+ } else {\r
+ XGetErrorText(dpy, err->error_code, buf, sizeof buf);\r
+ fprintf(stderr, "Caught unexpected X error: %s\n", buf);\r
+ }\r
+ return 0;\r
+}\r
+\r
+#endif /* TARGET_HOST_POSIX_X11 */\r
return 0;
case GLUT_HAS_SPACEBALL:
+ return fgHasSpaceball();
+
case GLUT_HAS_TABLET:
return 0;
case GLUT_NUM_SPACEBALL_BUTTONS:
+ return fgSpaceballNumButtons();
+
case GLUT_NUM_TABLET_BUTTONS:
return 0;
{
#if TARGET_HOST_POSIX_X11
if ( window )
+ {
glXMakeContextCurrent(
fgDisplay.Display,
window->Window.Handle,
window->Window.Handle,
window->Window.Context
);
+
+ /* also register this window to receive spaceball events */
+ fgSpaceballSetWindow(window);
+ }
#elif TARGET_HOST_MS_WINDOWS
if( fgStructure.CurrentWindow )
ReleaseDC( fgStructure.CurrentWindow->Window.Handle,