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: https://svn.code.sf.net/p/freeglut/code/trunk@837 7f0cb862-5218-0410-a997-914c9d46530a
This commit is contained in:
fayjf 2009-11-06 13:26:16 +00:00
parent 755316c21d
commit 803c4f86a2
17 changed files with 903 additions and 5 deletions

7
.gitattributes vendored
View File

@ -94,6 +94,12 @@ freeglut/freeglut/progs/demos/smooth_opengl3/Makefile.am -text
freeglut/freeglut/progs/demos/smooth_opengl3/smooth_opengl3.c -text freeglut/freeglut/progs/demos/smooth_opengl3/smooth_opengl3.c -text
freeglut/freeglut/progs/demos/smooth_opengl3/smooth_opengl3.dsp -text freeglut/freeglut/progs/demos/smooth_opengl3/smooth_opengl3.dsp -text
freeglut/freeglut/progs/demos/smooth_opengl3/smooth_opengl3Static.dsp -text freeglut/freeglut/progs/demos/smooth_opengl3/smooth_opengl3Static.dsp -text
freeglut/freeglut/progs/demos/spaceball/Makefile.am -text
freeglut/freeglut/progs/demos/spaceball/spaceball.c -text
freeglut/freeglut/progs/demos/spaceball/spaceball.dsp -text
freeglut/freeglut/progs/demos/spaceball/vmath.c -text
freeglut/freeglut/progs/demos/spaceball/vmath.h -text
freeglut/freeglut/progs/demos/spaceball/vmath.inl -text
freeglut/freeglut/src/Makefile.am svn_keywords=Author+Date+Id+Revision freeglut/freeglut/src/Makefile.am svn_keywords=Author+Date+Id+Revision
freeglut/freeglut/src/freeglut_callbacks.c svn_keywords=Author+Date+Id+Revision freeglut/freeglut/src/freeglut_callbacks.c svn_keywords=Author+Date+Id+Revision
freeglut/freeglut/src/freeglut_cursor.c svn_keywords=Author+Date+Id+Revision freeglut/freeglut/src/freeglut_cursor.c svn_keywords=Author+Date+Id+Revision
@ -112,6 +118,7 @@ freeglut/freeglut/src/freeglut_main.c svn_keywords=Author+Date+Id+Revision
freeglut/freeglut/src/freeglut_menu.c svn_keywords=Author+Date+Id+Revision freeglut/freeglut/src/freeglut_menu.c svn_keywords=Author+Date+Id+Revision
freeglut/freeglut/src/freeglut_misc.c svn_keywords=Author+Date+Id+Revision freeglut/freeglut/src/freeglut_misc.c svn_keywords=Author+Date+Id+Revision
freeglut/freeglut/src/freeglut_overlay.c svn_keywords=Author+Date+Id+Revision freeglut/freeglut/src/freeglut_overlay.c svn_keywords=Author+Date+Id+Revision
freeglut/freeglut/src/freeglut_spaceball.c -text
freeglut/freeglut/src/freeglut_state.c svn_keywords=Author+Date+Id+Revision freeglut/freeglut/src/freeglut_state.c svn_keywords=Author+Date+Id+Revision
freeglut/freeglut/src/freeglut_stroke_mono_roman.c svn_keywords=Author+Date+Id+Revision freeglut/freeglut/src/freeglut_stroke_mono_roman.c svn_keywords=Author+Date+Id+Revision
freeglut/freeglut/src/freeglut_stroke_roman.c svn_keywords=Author+Date+Id+Revision freeglut/freeglut/src/freeglut_stroke_roman.c svn_keywords=Author+Date+Id+Revision

View File

@ -95,5 +95,5 @@ if test "x$enable_debug" = xyes; then
fi fi
# Generate output. # 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 AC_OUTPUT

View File

@ -1,2 +1,2 @@
EXTRA_DIST = demos.dsw 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

View File

@ -159,6 +159,18 @@ Package=<4>
############################################################################### ###############################################################################
Project: "spaceball"=.\spaceball\spaceball.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Global: Global:
Package=<5> Package=<5>

View File

@ -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)

View File

@ -0,0 +1,173 @@
/* Spaceball demo
*
* Written by John Tsiombikas <nuclear@member.fsf.org>
* (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 <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/freeglut.h>
#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();
}
}

View File

@ -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

View File

@ -0,0 +1,16 @@
#include <math.h>
#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);
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -28,6 +28,7 @@ lib@LIBRARY@_la_SOURCES = freeglut_callbacks.c \
freeglut_geometry.c \ freeglut_geometry.c \
freeglut_init.c \ freeglut_init.c \
freeglut_input_devices.c \ freeglut_input_devices.c \
freeglut_spaceball.c \
freeglut_joystick.c \ freeglut_joystick.c \
freeglut_main.c \ freeglut_main.c \
freeglut_menu.c \ freeglut_menu.c \

View File

@ -301,6 +301,8 @@ void FGAPIENTRY glutWindowStatusFunc( void (* callback)( int ) )
void FGAPIENTRY glutSpaceballMotionFunc( void (* callback)( int, int, int ) ) void FGAPIENTRY glutSpaceballMotionFunc( void (* callback)( int, int, int ) )
{ {
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballMotionFunc" ); FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballMotionFunc" );
fgInitialiseSpaceball();
SET_CALLBACK( SpaceMotion ); SET_CALLBACK( SpaceMotion );
} }
@ -310,6 +312,8 @@ void FGAPIENTRY glutSpaceballMotionFunc( void (* callback)( int, int, int ) )
void FGAPIENTRY glutSpaceballRotateFunc( void (* callback)( int, int, int ) ) void FGAPIENTRY glutSpaceballRotateFunc( void (* callback)( int, int, int ) )
{ {
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballRotateFunc" ); FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballRotateFunc" );
fgInitialiseSpaceball();
SET_CALLBACK( SpaceRotation ); SET_CALLBACK( SpaceRotation );
} }
@ -319,6 +323,8 @@ void FGAPIENTRY glutSpaceballRotateFunc( void (* callback)( int, int, int ) )
void FGAPIENTRY glutSpaceballButtonFunc( void (* callback)( int, int ) ) void FGAPIENTRY glutSpaceballButtonFunc( void (* callback)( int, int ) )
{ {
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballButtonFunc" ); FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballButtonFunc" );
fgInitialiseSpaceball();
SET_CALLBACK( SpaceButton ); SET_CALLBACK( SpaceButton );
} }

View File

@ -564,9 +564,9 @@ enum
/* Presently ignored */ /* Presently ignored */
CB_Select, CB_Select,
CB_OverlayDisplay, CB_OverlayDisplay,
CB_SpaceMotion, CB_SpaceMotion, /* presently implemented only on UNIX/X11 */
CB_SpaceRotation, CB_SpaceRotation, /* presently implemented only on UNIX/X11 */
CB_SpaceButton, CB_SpaceButton, /* presently implemented only on UNIX/X11 */
CB_Dials, CB_Dials,
CB_ButtonBox, CB_ButtonBox,
CB_TabletMotion, CB_TabletMotion,
@ -853,6 +853,19 @@ int fgInputDeviceDetect( void );
void fgInitialiseInputDevices( void ); void fgInitialiseInputDevices( void );
void fgInputDeviceClose( 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 */ /* Setting the cursor for a given window */
void fgSetCursor ( SFG_Window *window, int cursorID ); void fgSetCursor ( SFG_Window *window, int cursorID );

View File

@ -980,6 +980,10 @@ void FGAPIENTRY glutMainLoopEvent( void )
switch( event.type ) switch( event.type )
{ {
case ClientMessage: case ClientMessage:
if(fgIsSpaceballXEvent(&event)) {
fgSpaceballHandleXEvent(&event);
break;
}
/* Destroy the window when the WM_DELETE_WINDOW message arrives */ /* Destroy the window when the WM_DELETE_WINDOW message arrives */
if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.DeleteWindow ) if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.DeleteWindow )
{ {

View File

@ -0,0 +1,456 @@
/* Spaceball support for Linux.
* Written by John Tsiombikas <nuclear@member.fsf.org>
*
* 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 <GL/freeglut.h>
#include "freeglut_internal.h"
#if TARGET_HOST_POSIX_X11
#include <X11/Xlib.h>
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 <nuclear@member.fsf.org>
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
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 */

View File

@ -675,10 +675,14 @@ int FGAPIENTRY glutDeviceGet( GLenum eWhat )
return 0; return 0;
case GLUT_HAS_SPACEBALL: case GLUT_HAS_SPACEBALL:
return fgHasSpaceball();
case GLUT_HAS_TABLET: case GLUT_HAS_TABLET:
return 0; return 0;
case GLUT_NUM_SPACEBALL_BUTTONS: case GLUT_NUM_SPACEBALL_BUTTONS:
return fgSpaceballNumButtons();
case GLUT_NUM_TABLET_BUTTONS: case GLUT_NUM_TABLET_BUTTONS:
return 0; return 0;

View File

@ -593,12 +593,17 @@ void fgSetWindow ( SFG_Window *window )
{ {
#if TARGET_HOST_POSIX_X11 #if TARGET_HOST_POSIX_X11
if ( window ) if ( window )
{
glXMakeContextCurrent( glXMakeContextCurrent(
fgDisplay.Display, fgDisplay.Display,
window->Window.Handle, window->Window.Handle,
window->Window.Handle, window->Window.Handle,
window->Window.Context window->Window.Context
); );
/* also register this window to receive spaceball events */
fgSpaceballSetWindow(window);
}
#elif TARGET_HOST_MS_WINDOWS #elif TARGET_HOST_MS_WINDOWS
if( fgStructure.CurrentWindow ) if( fgStructure.CurrentWindow )
ReleaseDC( fgStructure.CurrentWindow->Window.Handle, ReleaseDC( fgStructure.CurrentWindow->Window.Handle,