First attempt at adding John Tsiombikas' spaceball support for X11. It compiles...
authorJohn F. Fay <johnffay@nettally.com>
Fri, 6 Nov 2009 13:26:16 +0000 (13:26 +0000)
committerJohn F. Fay <johnffay@nettally.com>
Fri, 6 Nov 2009 13:26:16 +0000 (13:26 +0000)
git-svn-id: svn+ssh://svn.code.sf.net/p/freeglut/code/trunk/freeglut/freeglut@837 7f0cb862-5218-0410-a997-914c9d46530a

16 files changed:
configure.ac
progs/demos/Makefile.am
progs/demos/demos.dsw
progs/demos/spaceball/Makefile.am [new file with mode: 0644]
progs/demos/spaceball/spaceball.c [new file with mode: 0644]
progs/demos/spaceball/spaceball.dsp [new file with mode: 0644]
progs/demos/spaceball/vmath.c [new file with mode: 0644]
progs/demos/spaceball/vmath.h [new file with mode: 0644]
progs/demos/spaceball/vmath.inl [new file with mode: 0644]
src/Makefile.am
src/freeglut_callbacks.c
src/freeglut_internal.h
src/freeglut_main.c
src/freeglut_spaceball.c [new file with mode: 0644]
src/freeglut_state.c
src/freeglut_window.c

index c734815..2f81174 100644 (file)
@@ -95,5 +95,5 @@ if test "x$enable_debug" = xyes; then
 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
index 661dc33..31c1739 100644 (file)
@@ -1,2 +1,2 @@
 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
index f78466f..7231846 100644 (file)
@@ -159,6 +159,18 @@ Package=<4>
 
 ###############################################################################
 
+Project: "spaceball"=.\spaceball\spaceball.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
 Global:
 
 Package=<5>
diff --git a/progs/demos/spaceball/Makefile.am b/progs/demos/spaceball/Makefile.am
new file mode 100644 (file)
index 0000000..10b628c
--- /dev/null
@@ -0,0 +1,6 @@
+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
diff --git a/progs/demos/spaceball/spaceball.c b/progs/demos/spaceball/spaceball.c
new file mode 100644 (file)
index 0000000..ebdbc40
--- /dev/null
@@ -0,0 +1,173 @@
+/* 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
diff --git a/progs/demos/spaceball/spaceball.dsp b/progs/demos/spaceball/spaceball.dsp
new file mode 100644 (file)
index 0000000..dd99c59
--- /dev/null
@@ -0,0 +1,96 @@
+# 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
diff --git a/progs/demos/spaceball/vmath.c b/progs/demos/spaceball/vmath.c
new file mode 100644 (file)
index 0000000..f4eb50f
--- /dev/null
@@ -0,0 +1,16 @@
+#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
diff --git a/progs/demos/spaceball/vmath.h b/progs/demos/spaceball/vmath.h
new file mode 100644 (file)
index 0000000..5010c07
--- /dev/null
@@ -0,0 +1,31 @@
+#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
diff --git a/progs/demos/spaceball/vmath.inl b/progs/demos/spaceball/vmath.inl
new file mode 100644 (file)
index 0000000..82bbea0
--- /dev/null
@@ -0,0 +1,68 @@
+/* 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
index 027b3b9..683f33f 100644 (file)
@@ -28,6 +28,7 @@ lib@LIBRARY@_la_SOURCES = freeglut_callbacks.c \
                         freeglut_geometry.c \
                         freeglut_init.c \
                         freeglut_input_devices.c \
+                        freeglut_spaceball.c \
                         freeglut_joystick.c \
                         freeglut_main.c \
                         freeglut_menu.c \
index 3d9a02b..4915e19 100644 (file)
@@ -301,6 +301,8 @@ void FGAPIENTRY glutWindowStatusFunc( void (* callback)( int ) )
 void FGAPIENTRY glutSpaceballMotionFunc( void (* callback)( int, int, int ) )
 {
     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballMotionFunc" );
+    fgInitialiseSpaceball();
+
     SET_CALLBACK( SpaceMotion );
 }
 
@@ -310,6 +312,8 @@ void FGAPIENTRY glutSpaceballMotionFunc( void (* callback)( int, int, int ) )
 void FGAPIENTRY glutSpaceballRotateFunc( void (* callback)( int, int, int ) )
 {
     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballRotateFunc" );
+    fgInitialiseSpaceball();
+
     SET_CALLBACK( SpaceRotation );
 }
 
@@ -319,6 +323,8 @@ void FGAPIENTRY glutSpaceballRotateFunc( void (* callback)( int, int, int ) )
 void FGAPIENTRY glutSpaceballButtonFunc( void (* callback)( int, int ) )
 {
     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballButtonFunc" );
+    fgInitialiseSpaceball();
+
     SET_CALLBACK( SpaceButton );
 }
 
index 7b0a86e..5b2784a 100644 (file)
@@ -564,9 +564,9 @@ enum
     /* 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,
@@ -853,6 +853,19 @@ int         fgInputDeviceDetect( void );
 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 );
 
index 938f264..0a45402 100644 (file)
@@ -980,6 +980,10 @@ void FGAPIENTRY glutMainLoopEvent( void )
         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 )
             {
diff --git a/src/freeglut_spaceball.c b/src/freeglut_spaceball.c
new file mode 100644 (file)
index 0000000..0bec833
--- /dev/null
@@ -0,0 +1,456 @@
+/* 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
index 8a53450..a763d27 100644 (file)
@@ -675,10 +675,14 @@ int FGAPIENTRY glutDeviceGet( GLenum eWhat )
         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;
 
index a3d2830..1327b1e 100644 (file)
@@ -593,12 +593,17 @@ void fgSetWindow ( SFG_Window *window )
 {
 #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,