From bb97ecab3add22cf774d3bb8bdb9d1be537cc524 Mon Sep 17 00:00:00 2001 From: "John F. Fay" Date: Fri, 6 Nov 2009 13:26:16 +0000 Subject: [PATCH] First attempt at adding John Tsiombikas' spaceball support for X11. It compiles under Windows but the demo program does not. See e-mail from John Tsiombikas dated 10/15/2009 9:43 AM. git-svn-id: svn+ssh://svn.code.sf.net/p/freeglut/code/trunk/freeglut/freeglut@837 7f0cb862-5218-0410-a997-914c9d46530a --- configure.ac | 2 +- progs/demos/Makefile.am | 2 +- progs/demos/demos.dsw | 12 + progs/demos/spaceball/Makefile.am | 6 + progs/demos/spaceball/spaceball.c | 173 +++++++++++++ progs/demos/spaceball/spaceball.dsp | 96 ++++++++ progs/demos/spaceball/vmath.c | 16 ++ progs/demos/spaceball/vmath.h | 31 +++ progs/demos/spaceball/vmath.inl | 68 ++++++ src/Makefile.am | 1 + src/freeglut_callbacks.c | 6 + src/freeglut_internal.h | 19 +- src/freeglut_main.c | 4 + src/freeglut_spaceball.c | 456 +++++++++++++++++++++++++++++++++++ src/freeglut_state.c | 4 + src/freeglut_window.c | 5 + 16 files changed, 896 insertions(+), 5 deletions(-) create mode 100644 progs/demos/spaceball/Makefile.am create mode 100644 progs/demos/spaceball/spaceball.c create mode 100644 progs/demos/spaceball/spaceball.dsp create mode 100644 progs/demos/spaceball/vmath.c create mode 100644 progs/demos/spaceball/vmath.h create mode 100644 progs/demos/spaceball/vmath.inl create mode 100644 src/freeglut_spaceball.c diff --git a/configure.ac b/configure.ac index c734815..2f81174 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/progs/demos/Makefile.am b/progs/demos/Makefile.am index 661dc33..31c1739 100644 --- a/progs/demos/Makefile.am +++ b/progs/demos/Makefile.am @@ -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 diff --git a/progs/demos/demos.dsw b/progs/demos/demos.dsw index f78466f..7231846 100644 --- a/progs/demos/demos.dsw +++ b/progs/demos/demos.dsw @@ -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 index 0000000..10b628c --- /dev/null +++ b/progs/demos/spaceball/Makefile.am @@ -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 index 0000000..ebdbc40 --- /dev/null +++ b/progs/demos/spaceball/spaceball.c @@ -0,0 +1,173 @@ +/* Spaceball demo + * + * Written by John Tsiombikas + * (converted from the libspnav cube example) + * + * Use the spaceball to move and rotate the colored cube. + * Pressing any button will reset the cube at its original location. + * + * Press escape or q to exit. + */ + +#include +#include +#include +#include +#include "vmath.h" + +void draw_cube(void); + +/* callbacks */ +void disp(void); +void reshape(int x, int y); +void keyb(unsigned char key, int x, int y); +void sbmot(int x, int y, int z); /* spaceball translation */ +void sbrot(int x, int y, int z); /* spaceball rotation */ +void sbbut(int bn, int state); /* spaceball button */ + +vec3_t pos = {0, 0, -6}; +quat_t rot = {0, 0, 0, 1}; + +int main(int argc, char **argv) +{ + glutInit(&argc, argv); + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); + glutCreateWindow("spaceball demo"); + + glutDisplayFunc(disp); + glutReshapeFunc(reshape); + glutKeyboardFunc(keyb); + glutSpaceballMotionFunc(sbmot); + glutSpaceballRotateFunc(sbrot); + glutSpaceballButtonFunc(sbbut); + + glEnable(GL_CULL_FACE); + + glutMainLoop(); + return 0; +} + +void disp(void) +{ + mat4_t xform; + + quat_to_mat(xform, rot); + + glClear(GL_COLOR_BUFFER_BIT); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(pos.x, pos.y, pos.z); + glMultTransposeMatrixf((float*)xform); + + draw_cube(); + + glutSwapBuffers(); +} + +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(); +} + +/* 45deg fov */ +#define FOV (M_PI / 4.0) + +void reshape(int x, int y) +{ + float aspect = (float)x / (float)y; + float halfy = tan(FOV / 2.0); + float halfx = halfy * aspect; + + glViewport(0, 0, x, y); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-halfx, halfx, -halfy, halfy, 1.0, 1000.0); +} + +void keyb(unsigned char key, int x, int y) +{ + switch(key) { + case 'q': + case 'Q': + case 27: + exit(0); + + case ' ': + /* reset initial view */ + pos = v3_cons(0, 0, -6); + rot = quat_cons(1, 0, 0, 0); + glutPostRedisplay(); + + default: + break; + } +} + +void sbmot(int x, int y, int z) +{ + pos.x += x * 0.001; + pos.y += y * 0.001; + pos.z -= z * 0.001; + glutPostRedisplay(); +} + +void sbrot(int x, int y, int z) +{ + float axis_len = sqrt(x * x + y * y + z * z); + rot = quat_rotate(rot, axis_len * 0.001, -x / axis_len, -y / axis_len, z / axis_len); + glutPostRedisplay(); +} + +void sbbut(int bn, int state) +{ + if(state == GLUT_DOWN) { + pos = v3_cons(0, 0, -6); + rot = quat_cons(1, 0, 0, 0); + glutPostRedisplay(); + } +} diff --git a/progs/demos/spaceball/spaceball.dsp b/progs/demos/spaceball/spaceball.dsp new file mode 100644 index 0000000..dd99c59 --- /dev/null +++ b/progs/demos/spaceball/spaceball.dsp @@ -0,0 +1,96 @@ +# Microsoft Developer Studio Project File - Name="spaceball" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=spaceball - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "spaceball.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "spaceball.mak" CFG="spaceball - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "spaceball - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "spaceball - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "spaceball - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# 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 +# 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 + +!ELSEIF "$(CFG)" == "spaceball - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# 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 +# 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 + +!ENDIF + +# Begin Target + +# Name "spaceball - Win32 Release" +# Name "spaceball - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/progs/demos/spaceball/vmath.c b/progs/demos/spaceball/vmath.c new file mode 100644 index 0000000..f4eb50f --- /dev/null +++ b/progs/demos/spaceball/vmath.c @@ -0,0 +1,16 @@ +#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); +} diff --git a/progs/demos/spaceball/vmath.h b/progs/demos/spaceball/vmath.h new file mode 100644 index 0000000..5010c07 --- /dev/null +++ b/progs/demos/spaceball/vmath.h @@ -0,0 +1,31 @@ +#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_ */ diff --git a/progs/demos/spaceball/vmath.inl b/progs/demos/spaceball/vmath.inl new file mode 100644 index 0000000..82bbea0 --- /dev/null +++ b/progs/demos/spaceball/vmath.inl @@ -0,0 +1,68 @@ +/* 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; +} diff --git a/src/Makefile.am b/src/Makefile.am index 027b3b9..683f33f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/freeglut_callbacks.c b/src/freeglut_callbacks.c index 3d9a02b..4915e19 100644 --- a/src/freeglut_callbacks.c +++ b/src/freeglut_callbacks.c @@ -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 ); } diff --git a/src/freeglut_internal.h b/src/freeglut_internal.h index 7b0a86e..5b2784a 100644 --- a/src/freeglut_internal.h +++ b/src/freeglut_internal.h @@ -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 ); diff --git a/src/freeglut_main.c b/src/freeglut_main.c index 938f264..0a45402 100644 --- a/src/freeglut_main.c +++ b/src/freeglut_main.c @@ -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 index 0000000..0bec833 --- /dev/null +++ b/src/freeglut_spaceball.c @@ -0,0 +1,456 @@ +/* Spaceball support for Linux. + * Written by John Tsiombikas + * + * This code supports 3Dconnexion's 6-dof space-whatever devices. + * It can communicate with either the proprietary 3Dconnexion daemon (3dxsrv) + * free spacenavd (http://spacenav.sourceforge.net), through the "standard" + * magellan X-based protocol. + */ + +#include +#include "freeglut_internal.h" + +#if TARGET_HOST_POSIX_X11 +#include + +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; + + +static int spnav_x11_open(Display *dpy, Window win); +static int spnav_x11_window(Window win); +static int spnav_x11_event(const XEvent *xev, spnav_event *event); +static int spnav_close(void); +static int spnav_fd(void); +static int spnav_remove_events(int type); + +static SFG_Window *spnav_win; +#endif + +static int sball_initialized; + + +void fgInitialiseSpaceball(void) +{ + if(sball_initialized) { + return; + } + +#if TARGET_HOST_POSIX_X11 + { + Window w; + + if(!fgStructure.CurrentWindow) { + fgWarning("fgInitialiseSpaceball: no current window!\n"); + return; + } + + w = fgStructure.CurrentWindow->Window.Handle; + if(spnav_x11_open(fgDisplay.Display, w) == -1) { + return; + } + } +#endif + + sball_initialized = 1; +} + +void fgSpaceballClose(void) +{ +#if TARGET_HOST_POSIX_X11 + spnav_close(); +#endif +} + +int fgHasSpaceball(void) +{ + if(!sball_initialized) { + fgInitialiseSpaceball(); + if(!sball_initialized) { + fgWarning("fgInitialiseSpaceball failed\n"); + return 0; + } + } + +#if TARGET_HOST_POSIX_X11 + /* XXX this function should somehow query the driver if there's a device + * plugged in, as opposed to just checking if there's a driver to talk to. + */ + return spnav_fd() == -1 ? 0 : 1; +#else + return 0; +#endif +} + +int fgSpaceballNumButtons(void) +{ + if(!sball_initialized) { + fgInitialiseSpaceball(); + if(!sball_initialized) { + fgWarning("fgInitialiseSpaceball failed\n"); + return 0; + } + } + +#if TARGET_HOST_POSIX_X11 + return 2; /* TODO implement this properly */ +#else + return 0; +#endif +} + +void fgSpaceballSetWindow(SFG_Window *window) +{ + if(!sball_initialized) { + fgInitialiseSpaceball(); + if(!sball_initialized) { + return; + } + } + +#if TARGET_HOST_POSIX_X11 + if(spnav_win != window) { + spnav_x11_window(window->Window.Handle); + spnav_win = window; + } +#endif +} + + +#if TARGET_HOST_POSIX_X11 +int fgIsSpaceballXEvent(const XEvent *xev) +{ + spnav_event sev; + + if(!sball_initialized) { + fgInitialiseSpaceball(); + if(!sball_initialized) { + return 0; + } + } + + return spnav_x11_event(xev, &sev); +} + +void fgSpaceballHandleXEvent(const XEvent *xev) +{ + spnav_event sev; + + if(!sball_initialized) { + fgInitialiseSpaceball(); + if(!sball_initialized) { + return; + } + } + + if(spnav_x11_event(xev, &sev)) { + switch(sev.type) { + case SPNAV_EVENT_MOTION: + if(sev.motion.x | sev.motion.y | sev.motion.z) { + INVOKE_WCB(*spnav_win, SpaceMotion, (sev.motion.x, sev.motion.y, sev.motion.z)); + } + if(sev.motion.rx | sev.motion.ry | sev.motion.rz) { + INVOKE_WCB(*spnav_win, SpaceRotation, (sev.motion.rx, sev.motion.ry, sev.motion.rz)); + } + spnav_remove_events(SPNAV_EVENT_MOTION); + break; + + case SPNAV_EVENT_BUTTON: + INVOKE_WCB(*spnav_win, SpaceButton, (sev.button.bnum, sev.button.press ? GLUT_DOWN : GLUT_UP)); + break; + + default: + break; + } + } +} + +/* +The following code is part of libspnav, part of the spacenav project (spacenav.sf.net) +Copyright (C) 2007-2009 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 + +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 + +struct event_node { + spnav_event event; + struct event_node *next; +}; + +static 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; +} + +static int spnav_close(void) +{ + if(dpy) { + spnav_x11_window(DefaultRootWindow(dpy)); + app_win = 0; + dpy = 0; + return 0; + } + return -1; +} + +static 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 spnav_fd(void) +{ + if(dpy) { + return ConnectionNumber(dpy); + } + return -1; +} + +/*static int spnav_wait_event(spnav_event *event) +{ + if(dpy) { + for(;;) { + XEvent xev; + XNextEvent(dpy, &xev); + + if(spnav_x11_event(&xev, event) > 0) { + return event->type; + } + } + } + return 0; +} + +static int spnav_poll_event(spnav_event *event) +{ + if(dpy) { + if(XPending(dpy)) { + XEvent xev; + XNextEvent(dpy, &xev); + + return spnav_x11_event(&xev, event); + } + } + return 0; +}*/ + +static Bool match_events(Display *dpy, XEvent *xev, char *arg) +{ + int evtype = *(int*)arg; + + if(xev->type != ClientMessage) { + return False; + } + + if(xev->xclient.message_type == 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; +} + +static int spnav_remove_events(int type) +{ + int rm_count = 0; + + if(dpy) { + XEvent xev; + + while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) { + rm_count++; + } + return rm_count; + } + return 0; +} + +static 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; +} + +static 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 /* TARGET_HOST_POSIX_X11 */ diff --git a/src/freeglut_state.c b/src/freeglut_state.c index 8a53450..a763d27 100644 --- a/src/freeglut_state.c +++ b/src/freeglut_state.c @@ -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; diff --git a/src/freeglut_window.c b/src/freeglut_window.c index a3d2830..1327b1e 100644 --- a/src/freeglut_window.c +++ b/src/freeglut_window.c @@ -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, -- 1.7.10.4