From e4fd818aa242889c9dcdabae82ebd5b8631074a3 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 25 Aug 2008 09:55:03 +0000 Subject: [PATCH] Final merge of Google Summer of Code 2008 work... Force Feedback for SDL by Edgar Simo, mentored by Ryan C. Gordon --- Makefile.in | 2 +- Makefile.minimal | 2 + configure.in | 40 + include/SDL.h | 17 +- include/SDL_config.h.in | 5 + include/SDL_config_dreamcast.h | 1 + include/SDL_config_macosx.h | 1 + include/SDL_config_minimal.h | 3 + include/SDL_config_os2.h | 1 + include/SDL_config_win32.h | 4 +- include/SDL_haptic.h | 1154 +++++++++++++++++++ src/SDL.c | 40 + src/haptic/SDL_haptic.c | 698 ++++++++++++ src/haptic/SDL_syshaptic.h | 201 ++++ src/haptic/darwin/SDL_syshaptic.c | 1317 +++++++++++++++++++++ src/haptic/dummy/SDL_syshaptic.c | 194 ++++ src/haptic/linux/SDL_syshaptic.c | 956 ++++++++++++++++ src/haptic/win32/SDL_syshaptic.c | 1392 +++++++++++++++++++++++ src/joystick/SDL_joystick.c | 28 +- src/joystick/SDL_joystick_c.h | 4 + src/joystick/darwin/SDL_sysjoystick.c | 75 +- src/joystick/darwin/SDL_sysjoystick_c.h | 87 ++ src/joystick/linux/SDL_sysjoystick.c | 38 +- src/joystick/linux/SDL_sysjoystick_c.h | 55 + src/joystick/win32/SDL_dxjoystick.c | 72 +- src/joystick/win32/SDL_dxjoystick_c.h | 81 ++ src/video/win32/SDL_win32window.c | 68 ++ test/Makefile.in | 5 +- test/testhaptic.c | 266 +++++ 29 files changed, 6642 insertions(+), 165 deletions(-) create mode 100644 include/SDL_haptic.h create mode 100644 src/haptic/SDL_haptic.c create mode 100644 src/haptic/SDL_syshaptic.h create mode 100644 src/haptic/darwin/SDL_syshaptic.c create mode 100644 src/haptic/dummy/SDL_syshaptic.c create mode 100644 src/haptic/linux/SDL_syshaptic.c create mode 100644 src/haptic/win32/SDL_syshaptic.c create mode 100644 src/joystick/darwin/SDL_sysjoystick_c.h create mode 100644 src/joystick/linux/SDL_sysjoystick_c.h create mode 100644 src/joystick/win32/SDL_dxjoystick_c.h create mode 100644 test/testhaptic.c diff --git a/Makefile.in b/Makefile.in index 758400f96..59a7a7a16 100644 --- a/Makefile.in +++ b/Makefile.in @@ -42,7 +42,7 @@ SDLMAIN_OBJECTS = @SDLMAIN_OBJECTS@ DIST = acinclude.m4 autogen.sh Borland.html Borland.zip BUGS build-scripts configure configure.in COPYING CREDITS docs docs.html include INSTALL Makefile.dc Makefile.minimal Makefile.in README* sdl-config.in sdl.m4 sdl.pc.in SDL.qpg.in SDL.spec SDL.spec.in src test TODO VisualC.html VisualC VisualCE Watcom-OS2.zip Watcom-Win32.zip WhatsNew Xcode -HDRS = SDL.h SDL_audio.h SDL_cdrom.h SDL_compat.h SDL_cpuinfo.h SDL_endian.h SDL_error.h SDL_events.h SDL_joystick.h SDL_keyboard.h SDL_keysym.h SDL_loadso.h SDL_main.h SDL_mouse.h SDL_mutex.h SDL_name.h SDL_opengl.h SDL_pixels.h SDL_platform.h SDL_quit.h SDL_rect.h SDL_rwops.h SDL_scancode.h SDL_stdinc.h SDL_surface.h SDL_syswm.h SDL_thread.h SDL_timer.h SDL_types.h SDL_version.h SDL_video.h begin_code.h close_code.h +HDRS = SDL.h SDL_audio.h SDL_cdrom.h SDL_compat.h SDL_cpuinfo.h SDL_endian.h SDL_error.h SDL_events.h SDL_haptic.h SDL_joystick.h SDL_keyboard.h SDL_keysym.h SDL_loadso.h SDL_main.h SDL_mouse.h SDL_mutex.h SDL_name.h SDL_opengl.h SDL_pixels.h SDL_platform.h SDL_quit.h SDL_rect.h SDL_rwops.h SDL_scancode.h SDL_stdinc.h SDL_surface.h SDL_syswm.h SDL_thread.h SDL_timer.h SDL_types.h SDL_version.h SDL_video.h begin_code.h close_code.h LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ diff --git a/Makefile.minimal b/Makefile.minimal index 827621c3f..c8057894a 100644 --- a/Makefile.minimal +++ b/Makefile.minimal @@ -15,6 +15,7 @@ SOURCES = \ src/events/*.c \ src/file/*.c \ src/joystick/*.c \ + src/haptic/*.c \ src/stdlib/*.c \ src/thread/*.c \ src/timer/*.c \ @@ -22,6 +23,7 @@ SOURCES = \ src/audio/dummy/*.c \ src/video/dummy/*.c \ src/joystick/dummy/*.c \ + src/haptic/dummy/*.c \ src/cdrom/dummy/*.c \ src/thread/generic/*.c \ src/timer/dummy/*.c \ diff --git a/configure.in b/configure.in index 28151aea6..fc43fcd8c 100644 --- a/configure.in +++ b/configure.in @@ -235,6 +235,14 @@ if test x$enable_joystick != xyes; then else SOURCES="$SOURCES $srcdir/src/joystick/*.c" fi +AC_ARG_ENABLE(haptic, +AC_HELP_STRING([--enable-haptic], [Enable the haptic (force feedback) subsystem [[default=yes]]]), + , enable_haptic=yes) +if test x$enable_haptic != xyes; then + AC_DEFINE(SDL_HAPTIC_DISABLED) +else + SOURCES="$SOURCES $srcdir/src/haptic/*.c" +fi AC_ARG_ENABLE(cdrom, AC_HELP_STRING([--enable-cdrom], [Enable the cdrom subsystem [[default=yes]]]), , enable_cdrom=yes) @@ -2179,6 +2187,18 @@ case "$host" in ;; esac fi + # Set up files for the haptic library + if test x$enable_haptic = xyes; then + if test x$use_input_events = xyes; then + case $ARCH in + linux) + AC_DEFINE(SDL_HAPTIC_LINUX) + SOURCES="$SOURCES $srcdir/src/haptic/linux/*.c" + have_haptic=yes + ;; + esac + fi + fi # Set up files for the cdrom library if test x$enable_cdrom = xyes; then case $ARCH in @@ -2316,6 +2336,13 @@ AC_HELP_STRING([--enable-render-d3d], [enable the Direct3D render driver [[defau fi have_joystick=yes fi + if test x$enable_haptic = xyes; then + if test x$have_dinput = xyes; then + AC_DEFINE(SDL_HAPTIC_DINPUT) + SOURCES="$SOURCES $srcdir/src/haptic/win32/SDL_syshaptic.c" + have_haptic=yes + fi + fi # Set up files for the cdrom library if test x$enable_cdrom = xyes; then AC_DEFINE(SDL_CDROM_WIN32) @@ -2457,6 +2484,13 @@ AC_HELP_STRING([--enable-render-d3d], [enable the Direct3D render driver [[defau SOURCES="$SOURCES $srcdir/src/joystick/darwin/*.c" have_joystick=yes fi + # Set up files for the haptic library + if test x$enable_haptic = xyes; then + AC_DEFINE(SDL_HAPTIC_IOKIT) + SOURCES="$SOURCES $srcdir/src/haptic/darwin/*.c" + have_haptic=yes + EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,-framework,ForceFeedback" + fi # Set up files for the cdrom library if test x$enable_cdrom = xyes; then AC_DEFINE(SDL_CDROM_MACOSX) @@ -2565,6 +2599,12 @@ if test x$enable_joystick = xyes; then SOURCES="$SOURCES $srcdir/src/joystick/dummy/*.c" fi fi +if test x$have_haptic != xyes; then + if test x$enable_haptic = xyes; then + AC_DEFINE(SDL_HAPTIC_DISABLED) + fi + SOURCES="$SOURCES $srcdir/src/haptic/dummy/*.c" +fi if test x$have_cdrom != xyes; then if test x$enable_cdrom = xyes; then AC_DEFINE(SDL_CDROM_DISABLED) diff --git a/include/SDL.h b/include/SDL.h index e079d2882..35528fe14 100644 --- a/include/SDL.h +++ b/include/SDL.h @@ -104,14 +104,15 @@ extern "C" { /* These are the flags which may be passed to SDL_Init() -- you should specify the subsystems which you will be using in your application. */ -#define SDL_INIT_TIMER 0x00000001 -#define SDL_INIT_AUDIO 0x00000010 -#define SDL_INIT_VIDEO 0x00000020 -#define SDL_INIT_CDROM 0x00000100 -#define SDL_INIT_JOYSTICK 0x00000200 -#define SDL_INIT_NOPARACHUTE 0x00100000 /* Don't catch fatal signals */ -#define SDL_INIT_EVENTTHREAD 0x01000000 /* Not supported on all OS's */ -#define SDL_INIT_EVERYTHING 0x0000FFFF +#define SDL_INIT_TIMER 0x00000001 +#define SDL_INIT_AUDIO 0x00000010 +#define SDL_INIT_VIDEO 0x00000020 +#define SDL_INIT_CDROM 0x00000100 +#define SDL_INIT_JOYSTICK 0x00000200 +#define SDL_INIT_HAPTIC 0x00001000 +#define SDL_INIT_NOPARACHUTE 0x00100000 /* Don't catch fatal signals */ +#define SDL_INIT_EVENTTHREAD 0x01000000 /* Not supported on all OS's */ +#define SDL_INIT_EVERYTHING 0x0000FFFF /* This function loads the SDL dynamically linked library and initializes * the subsystems specified by 'flags' (and those satisfying dependencies) diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in index cf1a61c06..648c9a7dc 100644 --- a/include/SDL_config.h.in +++ b/include/SDL_config.h.in @@ -149,6 +149,7 @@ #undef SDL_EVENTS_DISABLED #undef SDL_FILE_DISABLED #undef SDL_JOYSTICK_DISABLED +#undef SDL_HAPTIC_DISABLED #undef SDL_LOADSO_DISABLED #undef SDL_THREADS_DISABLED #undef SDL_TIMERS_DISABLED @@ -215,6 +216,10 @@ #undef SDL_JOYSTICK_WINMM #undef SDL_JOYSTICK_USBHID #undef SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H +#undef SDL_HAPTIC_DUMMY +#undef SDL_HAPTIC_LINUX +#undef SDL_HAPTIC_IOKIT +#undef SDL_HAPTIC_DINPUT /* Enable various shared object loading systems */ #undef SDL_LOADSO_BEOS diff --git a/include/SDL_config_dreamcast.h b/include/SDL_config_dreamcast.h index 9cbeea316..f01bb09b1 100644 --- a/include/SDL_config_dreamcast.h +++ b/include/SDL_config_dreamcast.h @@ -89,6 +89,7 @@ typedef unsigned long uintptr_t; /* Enable various input drivers */ #define SDL_JOYSTICK_DC 1 +#define SDL_HAPTIC_DUMMY 1 /* Enable various shared object loading systems */ #define SDL_LOADSO_DUMMY 1 diff --git a/include/SDL_config_macosx.h b/include/SDL_config_macosx.h index 65418b4ca..b806dca19 100644 --- a/include/SDL_config_macosx.h +++ b/include/SDL_config_macosx.h @@ -99,6 +99,7 @@ /* Enable various input drivers */ #define SDL_JOYSTICK_IOKIT 1 +#define SDL_HAPTIC_IOKIT 1 /* Enable various shared object loading systems */ #ifdef __ppc__ diff --git a/include/SDL_config_minimal.h b/include/SDL_config_minimal.h index 78b6148ca..8f862f38b 100644 --- a/include/SDL_config_minimal.h +++ b/include/SDL_config_minimal.h @@ -47,6 +47,9 @@ typedef unsigned long uintptr_t; /* Enable the stub joystick driver (src/joystick/dummy/\*.c) */ #define SDL_JOYSTICK_DISABLED 1 +/* Enable the stub haptic driver (src/haptic/dummy/\*.c) */ +#define SDL_HAPTIC_DUMMY 1 + /* Enable the stub shared object loader (src/loadso/dummy/\*.c) */ #define SDL_LOADSO_DISABLED 1 diff --git a/include/SDL_config_os2.h b/include/SDL_config_os2.h index c7f576d24..bc8bcac99 100644 --- a/include/SDL_config_os2.h +++ b/include/SDL_config_os2.h @@ -118,6 +118,7 @@ typedef unsigned long long uint64_t; /* Enable various input drivers */ #define SDL_JOYSTICK_OS2 1 +#define SDL_HAPTIC_DUMMY 1 /* Enable various shared object loading systems */ #define SDL_LOADSO_OS2 1 diff --git a/include/SDL_config_win32.h b/include/SDL_config_win32.h index d843054d3..840f84dba 100644 --- a/include/SDL_config_win32.h +++ b/include/SDL_config_win32.h @@ -139,8 +139,10 @@ typedef unsigned int uintptr_t; /* Enable various input drivers */ #ifdef _WIN32_WCE #define SDL_JOYSTICK_DISABLED 1 +#define SDL_HAPTIC_DUMMY 1 #else -#define SDL_JOYSTICK_WINMM 1 +#define SDL_JOYSTICK_DINPUT 1 +#define SDL_HAPTIC_DINPUT 1 #endif /* Enable various shared object loading systems */ diff --git a/include/SDL_haptic.h b/include/SDL_haptic.h new file mode 100644 index 000000000..6d748c2d0 --- /dev/null +++ b/include/SDL_haptic.h @@ -0,0 +1,1154 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 2008 Edgar Simo + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/** + * \file SDL_haptic.h + * + * \brief The SDL Haptic subsystem allows you to control haptic (force feedback) + * devices. + * + * The basic usage is as follows: + * - Initialize the Subsystem (SDL_INIT_HAPTIC). + * - Open a Haptic Device. + * - SDL_HapticOpen(...) to open from index. + * - SDL_HapticOpenFromJoystick(...) to open from an existing joystick. + * - Create an effect (SDL_HapticEffect). + * - Upload the effect with SDL_HapticNewEffect(...). + * - Run the effect with SDL_HapticRunEffect(...). + * - (optional) Free the effect with SDL_HapticDestroyEffect(...). + * - Close the haptic device with SDL_HapticClose(...). + * + * Example: + * + * \code + * int test_haptic( SDL_Joystick * joystick ) { + * SDL_Haptic *haptic; + * SDL_HapticEffect effect; + * int effect_id; + * + * // Open the device + * haptic = SDL_HapticOpenFromJoystick( joystick ); + * if (haptic == NULL) return -1; // Most likely joystick isn't haptic + * + * // See if it can do sine waves + * if ((SDL_HapticQuery(haptic) & SDL_HAPTIC_SINE)==0) { + * SDL_HapticClose(haptic); // No sine effect + * return -1; + * } + * + * // Create the effect + * memset( &effect, 0, sizeof(SDL_HapticEffect) ); // 0 is safe default + * effect.type = SDL_HAPTIC_SINE; + * effect.periodic.direction.type = SDL_HAPTIC_POLAR; // Polar coordinates + * effect.periodic.direction.dir[0] = 18000; // Force comes from south + * effect.periodic.period = 1000; // 1000 ms + * effect.periodic.magnitude = 20000; // 20000/32767 strength + * effect.periodic.length = 5000; // 5 seconds long + * effect.periodic.attack_length = 1000; // Takes 1 second to get max strength + * effect.periodic.fade_length = 1000; // Takes 1 second to fade away + * + * // Upload the effect + * effect_id = SDL_HapticNewEffect( haptic, &effect ); + * + * // Test the effect + * SDL_HapticRunEffect( haptic, effect_id, 1 ); + * SDL_Delay( 5000); // Wait for the effect to finish + * + * // We destroy the effect, although closing the device also does this + * SDL_HapticDestroyEffect( haptic, effect_id ); + * + * // Close the device + * SDL_HapticClose(haptic); + * + * return 0; // Success + * } + * \endcode + * + * \author Edgar Simo Serra + */ + +#ifndef _SDL_haptic_h +#define _SDL_haptic_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_joystick.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { + /* *INDENT-ON* */ +#endif /* __cplusplus */ + +/** + * \typedef SDL_Haptic + * + * \brief The haptic structure used to identify an SDL haptic. + * + * \sa SDL_HapticOpen + * \sa SDL_HapticOpenFromJoystick + * \sa SDL_HapticClose + */ +struct _SDL_Haptic; +typedef struct _SDL_Haptic SDL_Haptic; + + +/* + * Different haptic features a device can have. + */ +/** + * \def SDL_HAPTIC_CONSTANT + * + * \brief Constant haptic effect. + * + * \sa SDL_HapticCondition + */ +#define SDL_HAPTIC_CONSTANT (1<<0) /* Constant effect supported */ +/** + * \def SDL_HAPTIC_SINE + * + * \brief Periodic haptic effect that simulates sine waves. + * + * \sa SDL_HapticPeriodic + */ +#define SDL_HAPTIC_SINE (1<<1) /* Sine wave effect supported */ +/** + * \def SDL_HAPTIC_SQUARE + * + * \brief Periodic haptic effect that simulates square waves. + * + * \sa SDL_HapticPeriodic + */ +#define SDL_HAPTIC_SQUARE (1<<2) /* Square wave effect supported */ +/** + * \def SDL_HAPTIC_TRIANGLE + * + * \brief Periodic haptic effect that simulates triangular waves. + * + * \sa SDL_HapticPeriodic + */ +#define SDL_HAPTIC_TRIANGLE (1<<3) /* Triangle wave effect supported */ +/** + * \def SDL_HAPTIC_SAWTOOTHUP + * + * \brief Periodic haptic effect that simulates saw tooth up waves. + * + * \sa SDL_HapticPeriodic + */ +#define SDL_HAPTIC_SAWTOOTHUP (1<<4) /* Sawtoothup wave effect supported */ +/** + * \def SDL_HAPTIC_SAWTOOTHDOWN + * + * \brief Periodic haptic effect that simulates saw tooth down waves. + * + * \sa SDL_HapticPeriodic + */ +#define SDL_HAPTIC_SAWTOOTHDOWN (1<<5) /* Sawtoothdown wave effect supported */ +/** + * \def SDL_HAPTIC_RAMP + * + * \brief Ramp haptic effect. + * + * \sa SDL_HapticRamp + */ +#define SDL_HAPTIC_RAMP (1<<6) /* Ramp effect supported */ +/** + * \def SDL_HAPTIC_SPRING + * + * \brief Condition haptic effect that simulates a spring. Effect is based on the + * axes position. + * + * \sa SDL_HapticCondition + */ +#define SDL_HAPTIC_SPRING (1<<7) /* Spring effect supported - uses axes position */ +/** + * \def SDL_HAPTIC_DAMPER + * + * \brief Condition haptic effect that simulates dampening. Effect is based on the + * axes velocity. + * + * \sa SDL_HapticCondition + */ +#define SDL_HAPTIC_DAMPER (1<<8) /* Damper effect supported - uses axes velocity */ +/** + * \def SDL_HAPTIC_INERTIA + * + * \brief Condition haptic effect that simulates inertia. Effect is based on the axes + * acceleration. + * + * \sa SDL_HapticCondition + */ +#define SDL_HAPTIC_INERTIA (1<<9) /* Inertia effect supported - uses axes acceleration */ +/** + * \def SDL_HAPTIC_FRICTION + * + * \brief Condition haptic effect that simulates friction. Effect is based on the axes + * movement. + * + * \sa SDL_HapticCondition + */ +#define SDL_HAPTIC_FRICTION (1<<10) /* Friction effect supported - uses axes movement */ +/** + * \def SDL_HAPTIC_CUSTOM + * + * \brief User defined custom haptic effect. + */ +#define SDL_HAPTIC_CUSTOM (1<<11) /* Custom effect is supported */ +/* These last two are features the device has, not effects */ +/** + * \def SDL_HAPTIC_GAIN + * + * \brief Device supports setting the global gain. + * + * \sa SDL_HapticSetGain + */ +#define SDL_HAPTIC_GAIN (1<<12) /* Device can set global gain */ +/** + * \def SDL_HAPTIC_AUTOCENTER + * + * \brief Device supports setting autocenter. + * + * \sa SDL_HapticSetAutocenter + */ +#define SDL_HAPTIC_AUTOCENTER (1<<13) /* Device can set autocenter */ +/** + * \def SDL_HAPTIC_STATUS + * + * \brief Device can be queried for effect status. + * + * \sa SDL_HapticGetEffectStatus + */ +#define SDL_HAPTIC_STATUS (1<<14) /* Device can be queried for effect status */ +/** + * \def SDL_HAPTIC_PAUSE + * + * \brief Device can be paused. + * + * \sa SDL_HapticPause + * \sa SDL_HapticUnpause + */ +#define SDL_HAPTIC_PAUSE (1<<15) /* Device can be paused. */ + + +/* + * Direction encodings + */ +/** + * \def SDL_HAPTIC_POLAR + * + * \brief Uses polar coordinates for the direction. + * + * \sa SDL_HapticDirection + */ +#define SDL_HAPTIC_POLAR 0 +/** + * \def SDL_HAPTIC_CARTESIAN + * + * \brief Uses cartesian coordinates for the direction. + * + * \sa SDL_HapticDirection + */ +#define SDL_HAPTIC_CARTESIAN 1 +/** + * \def SDL_HAPTIC_SPHERICAL + * + * \brief Uses spherical coordinates for the direction. + * + * \sa SDL_HapticDirection + */ +#define SDL_HAPTIC_SPHERICAL 2 + + +/* + * Misc defines. + */ +/** + * \def SDL_HAPTIC_INFINITY + * + * \brief Used to play a device an infinite number of times. + * + * \sa SDL_HapticRunEffect + */ +#define SDL_HAPTIC_INFINITY 4294967295U + + +/** + * \struct SDL_HapticDirection + * + * \brief Structure that represents a haptic direction. + * + * Directions can be specified by: + * - SDL_HAPTIC_POLAR : Specified by polar coordinates. + * - SDL_HAPTIC_CARTESIAN : Specified by cartesian coordinates. + * - SDL_HAPTIC_SPHERICAL : Specified by spherical coordinates. + * + * Cardinal directions of the haptic device are relative to the positioning + * of the device. North is considered to be away from the user. + * + * The following diagram represents the cardinal directions: + * \code + * .--. + * |__| .-------. + * |=.| |.-----.| + * |--| || || + * | | |'-----'| + * |__|~')_____(' + * [ COMPUTER ] + * + * + * North (0,-1) + * ^ + * | + * | + * (1,0) West <----[ HAPTIC ]----> East (-1,0) + * | + * | + * v + * South (0,1) + * + * + * [ USER ] + * \|||/ + * (o o) + * ---ooO-(_)-Ooo--- + * \endcode + * + * If type is SDL_HAPTIC_POLAR, direction is encoded by hundredths of a + * degree starting north and turning clockwise. SDL_HAPTIC_POLAR only uses + * the first dir parameter. The cardinal directions would be: + * - North: 0 (0 degrees) + * - East: 9000 (90 degrees) + * - South: 18000 (180 degrees) + * - West: 27000 (270 degrees) + * + * If type is SDL_HAPTIC_CARTESIAN, direction is encoded by three positions + * (X axis, Y axis and Z axis (with 3 axes)). SDL_HAPTIC_CARTESIAN uses + * the first three dir parameters. The cardinal directions would be: + * - North: 0,-1, 0 + * - East: -1, 0, 0 + * - South: 0, 1, 0 + * - West: 1, 0, 0 + * The Z axis represents the height of the effect if supported, otherwise + * it's unused. In cartesian encoding (1,2) would be the same as (2,4), you + * can use any multiple you want, only the direction matters. + * + * If type is SDL_HAPTIC_SPHERICAL, direction is encoded by two rotations. + * The first two dir parameters are used. The dir parameters are as follows + * (all values are in hundredths of degrees): + * 1) Degrees from (1, 0) rotated towards (0, 1). + * 2) Degrees towards (0, 0, 1) (device needs at least 3 axes). + * + * + * Example of force coming from the south with all encodings (force coming + * from the south means the user will have to pull the stick to counteract): + * \code + * SDL_HapticDirection direction; + * + * // Cartesian directions + * direction.type = SDL_HAPTIC_CARTESIAN; // Using cartesian direction encoding. + * direction.dir[0] = 0; // X position + * direction.dir[1] = 1; // Y position + * // Assuming the device has 2 axes, we don't need to specify third parameter. + * + * // Polar directions + * direction.type = SDL_HAPTIC_POLAR; // We'll be using polar direction encoding. + * direction.dir[0] = 18000; // Polar only uses first parameter + * + * // Spherical coordinates + * direction.type = SDL_HAPTIC_SPHERICAL; // Spherical encoding + * direction.dir[0] = 9000; // Since we only have two axes we don't need more parameters. + * \endcode + * + * \sa SDL_HAPTIC_POLAR + * \sa SDL_HAPTIC_CARTESIAN + * \sa SDL_HAPTIC_SPHERICAL + * \sa SDL_HapticEffect + * \sa SDL_HapticNumAxes + */ +typedef struct SDL_HapticDirection +{ + Uint8 type; /**< The type of encoding. */ + Uint16 dir[3]; /**< The encoded direction. */ +} SDL_HapticDirection; + + +/** + * \struct SDL_HapticConstant + * + * \brief A structure containing a template for a Constant effect. + * + * The struct is exclusive to the SDL_HAPTIC_CONSTANT effect. + * + * A constant effect applies a constant force in the specified direction + * to the joystick. + * + * \sa SDL_HAPTIC_CONSTANT + * \sa SDL_HapticEffect + */ +typedef struct SDL_HapticConstant +{ + /* Header */ + Uint16 type; /**< SDL_HAPTIC_CONSTANT */ + SDL_HapticDirection direction; /**< Direction of the effect. */ + + /* Replay */ + Uint32 length; /**< Duration of the effect. */ + Uint16 delay; /**< Delay before starting the effect. */ + + /* Trigger */ + Uint16 button; /**< Button that triggers the effect. */ + Uint16 interval; /**< How soon it can be triggered again after button. */ + + /* Constant */ + Sint16 level; /**< Strength of the constant effect. */ + + /* Envelope */ + Uint16 attack_length; /**< Duration of the attack. */ + Uint16 attack_level; /**< Level at the start of the attack. */ + Uint16 fade_length; /**< Duration of the fade. */ + Uint16 fade_level; /**< Level at the end of the fade. */ +} SDL_HapticConstant; +/** + * \struct SDL_HapticPeriodic + * + * \brief A structure containing a template for a Periodic effect. + * + * The struct handles the following effects: + * - SDL_HAPTIC_SINE + * - SDL_HAPTIC_SQUARE + * - SDL_HAPTIC_TRIANGLE + * - SDL_HAPTIC_SAWTOOTHUP + * - SDL_HAPTIC_SAWTOOTHDOWN + * + * A periodic effect consists in a wave-shaped effect that repeats itself + * over time. The type determines the shape of the wave and the parameters + * determine the dimensions of the wave. + * + * Phase is given by hundredth of a cyle meaning that giving the phase a value + * of 9000 will displace it 25% of it's period. Here are sample values: + * - 0: No phase displacement. + * - 9000: Displaced 25% of it's period. + * - 18000: Displaced 50% of it's period. + * - 27000: Displaced 75% of it's period. + * - 36000: Displaced 100% of it's period, same as 0, but 0 is preffered. + * + * Examples: + * \code + * SDL_HAPTIC_SINE + * __ __ __ __ + * / \ / \ / \ / + * / \__/ \__/ \__/ + * + * SDL_HAPTIC_SQUARE + * __ __ __ __ __ + * | | | | | | | | | | + * | |__| |__| |__| |__| | + * + * SDL_HAPTIC_TRIANGLE + * /\ /\ /\ /\ /\ + * / \ / \ / \ / \ / + * / \/ \/ \/ \/ + * + * SDL_HAPTIC_SAWTOOTHUP + * /| /| /| /| /| /| /| + * / | / | / | / | / | / | / | + * / |/ |/ |/ |/ |/ |/ | + * + * SDL_HAPTIC_SAWTOOTHDOWN + * \ |\ |\ |\ |\ |\ |\ | + * \ | \ | \ | \ | \ | \ | \ | + * \| \| \| \| \| \| \| + * \endcode + * + * \sa SDL_HAPTIC_SINE + * \sa SDL_HAPTIC_SQUARE + * \sa SDL_HAPTIC_TRIANGLE + * \sa SDL_HAPTIC_SAWTOOTHUP + * \sa SDL_HAPTIC_SAWTOOTHDOWN + * \sa SDL_HapticEffect + */ +typedef struct SDL_HapticPeriodic +{ + /* Header */ + Uint16 type; /**< SDL_HAPTIC_SINE, SDL_HAPTIC_SQUARE, + SDL_HAPTIC_TRIANGLE, SDL_HAPTIC_SAWTOOTHUP or + SDL_HAPTIC_SAWTOOTHDOWN */ + SDL_HapticDirection direction; /**< Direction of the effect. */ + + /* Replay */ + Uint32 length; /**< Duration of the effect. */ + Uint16 delay; /**< Delay before starting the effect. */ + + /* Trigger */ + Uint16 button; /**< Button that triggers the effect. */ + Uint16 interval; /**< How soon it can be triggered again after button. */ + + /* Periodic */ + Uint16 period; /**< Period of the wave. */ + Sint16 magnitude; /**< Peak value. */ + Sint16 offset; /**< Mean value of the wave. */ + Uint16 phase; /**< Horizontal shift given by hundredth of a cycle. */ + + /* Envelope */ + Uint16 attack_length; /**< Duration of the attack. */ + Uint16 attack_level; /**< Level at the start of the attack. */ + Uint16 fade_length; /**< Duration of the fade. */ + Uint16 fade_level; /**< Level at the end of the fade. */ +} SDL_HapticPeriodic; +/** + * \struct SDL_HapticCondition + * + * \brief A structure containing a template for a Condition effect. + * + * The struct handles the following effects: + * - SDL_HAPTIC_SPRING: Effect based on axes position. + * - SDL_HAPTIC_DAMPER: Effect based on axes velocity. + * - SDL_HAPTIC_INERTIA: Effect based on axes acceleration. + * - SDL_HAPTIC_FRICTION: Effect based on axes movement. + * + * Direction is handled by condition internals instead of a direction member. + * The condition effect specific members have three parameters. The first + * refers to the X axis, the second refers to the Y axis and the third + * refers to the Z axis. The right terms refer to the positive side of the + * axis and the left terms refer to the negative side of the axis. Please + * refer to the SDL_HapticDirection diagram for which side is positive and + * which is negative. + * + * \sa SDL_HapticDirection + * \sa SDL_HAPTIC_SPRING + * \sa SDL_HAPTIC_DAMPER + * \sa SDL_HAPTIC_INERTIA + * \sa SDL_HAPTIC_FRICTION + * \sa SDL_HapticEffect + */ +typedef struct SDL_HapticCondition +{ + /* Header */ + Uint16 type; /**< SDL_HAPTIC_SPRING, SDL_HAPTIC_DAMPER, + SDL_HAPTIC_INERTIA or SDL_HAPTIC_FRICTION */ + SDL_HapticDirection direction; /**< Direction of the effect - Not used ATM. */ + + /* Replay */ + Uint32 length; /**< Duration of the effect. */ + Uint16 delay; /**< Delay before starting the effect. */ + + /* Trigger */ + Uint16 button; /**< Button that triggers the effect. */ + Uint16 interval; /**< How soon it can be triggered again after button. */ + + /* Condition */ + Uint16 right_sat[3]; /**< Level when joystick is to the positive side. */ + Uint16 left_sat[3]; /**< Level when joystick is to the negative side. */ + Sint16 right_coeff[3]; /**< How fast to increase the force towards the positive side. */ + Sint16 left_coeff[3]; /**< How fast to increase the force towards the negative side. */ + Uint16 deadband[3]; /**< Size of the dead zone. */ + Sint16 center[3]; /**< Position of the dead zone. */ +} SDL_HapticCondition; +/** + * \struct SDL_HapticRamp + * + * \brief A structure containing a template for a Ramp effect. + * + * This struct is exclusively for the SDL_HAPTIC_RAMP effect. + * + * The ramp effect starts at start strength and ends at end strength. + * It augments in linear fashion. If you use attack and fade with a ramp + * they effects get added to the ramp effect making the effect become + * quadratic instead of linear. + * + * \sa SDL_HAPTIC_RAMP + * \sa SDL_HapticEffect + */ +typedef struct SDL_HapticRamp +{ + /* Header */ + Uint16 type; /**< SDL_HAPTIC_RAMP */ + SDL_HapticDirection direction; /**< Direction of the effect. */ + + /* Replay */ + Uint32 length; /**< Duration of the effect. */ + Uint16 delay; /**< Delay before starting the effect. */ + + /* Trigger */ + Uint16 button; /**< Button that triggers the effect. */ + Uint16 interval; /**< How soon it can be triggered again after button. */ + + /* Ramp */ + Sint16 start; /**< Beginning strength level. */ + Sint16 end; /**< Ending strength level. */ + + /* Envelope */ + Uint16 attack_length; /**< Duration of the attack. */ + Uint16 attack_level; /**< Level at the start of the attack. */ + Uint16 fade_length; /**< Duration of the fade. */ + Uint16 fade_level; /**< Level at the end of the fade. */ +} SDL_HapticRamp; +/** + * \struct SDL_HapticCustom + * + * \brief A structure containing a template for the SDL_HAPTIC_CUSTOM effect. + * + * A custom force feedback effect is much like a periodic effect, where the + * application can define it's exact shape. You will have to allocate the + * data yourself. Data should consist of channels * samples Uint16 samples. + * + * If channels is one, the effect is rotated using the defined direction. + * Otherwise it uses the samples in data for the different axes. + * + * \sa SDL_HAPTIC_CUSTOM + * \sa SDL_HapticEffect + */ +typedef struct SDL_HapticCustom +{ + /* Header */ + Uint16 type; /**< SDL_HAPTIC_CUSTOM */ + SDL_HapticDirection direction; /**< Direction of the effect. */ + + /* Replay */ + Uint32 length; /**< Duration of the effect. */ + Uint16 delay; /**< Delay before starting the effect. */ + + /* Trigger */ + Uint16 button; /**< Button that triggers the effect. */ + Uint16 interval; /**< How soon it can be triggered again after button. */ + + /* Custom */ + Uint8 channels; /**< Axes to use, minimum of one. */ + Uint16 period; /**< Sample periods. */ + Uint16 samples; /**< Amount of samples. */ + Uint16 *data; /**< Should contain channels*samples items. */ + + /* Envelope */ + Uint16 attack_length; /**< Duration of the attack. */ + Uint16 attack_level; /**< Level at the start of the attack. */ + Uint16 fade_length; /**< Duration of the fade. */ + Uint16 fade_level; /**< Level at the end of the fade. */ +} SDL_HapticCustom; +/** + * \union SDL_HapticEffect + * + * \brief The generic template for any haptic effect. + * + * All values max at 32767 (0x7FFF). Signed values also can be negative. + * Time values unless specified otherwise are in milliseconds. + * + * You can also pass SDL_HAPTIC_INFINITY to length instead of a 0-32767 value. + * Neither delay, interval, attack_length nor fade_length support + * SDL_HAPTIC_INFINITY. Fade will also not be used since effect never ends. + * + * Additionally, the SDL_HAPTIC_RAMP effect does not support a duration of + * SDL_HAPTIC_INFINITY. + * + * Button triggers may not be supported on all devices, it is advised to not + * use them if possible. Buttons start at index 1 instead of index 0 like + * they joystick. + * + * If both attack_length and fade_level are 0, the envelope is not used, + * otherwise both values are used. + * + * Common parts: + * \code + * // Replay - All effects have this + * Uint32 length; // Duration of effect (ms). + * Uint16 delay; // Delay before starting effect. + * + * // Trigger - All effects have this + * Uint16 button; // Button that triggers effect. + * Uint16 interval; // How soon before effect can be triggered again. + * + * // Envelope - All effects except condition effects have this + * Uint16 attack_length; // Duration of the attack (ms). + * Uint16 attack_level; // Level at the start of the attack. + * Uint16 fade_length; // Duration of the fade out (ms). + * Uint16 fade_level; // Level at the end of the fade. + * \endcode + * + * + * Here we have an example of a constant effect evolution in time: + * + * \code + * Strength + * ^ + * | + * | effect level --> _________________ + * | / \ + * | / \ + * | / \ + * | / \ + * | attack_level --> | \ + * | | | <--- fade_level + * | + * +--------------------------------------------------> Time + * [--] [---] + * attack_length fade_length + * + * [------------------][-----------------------] + * delay length + * \endcode + * + * Note either the attack_level or the fade_level may be above the actual + * effect level. + * + * \sa SDL_HapticConstant + * \sa SDL_HapticPeriodic + * \sa SDL_HapticCondition + * \sa SDL_HapticRamp + * \sa SDL_HapticCustom + */ +typedef union SDL_HapticEffect +{ + /* Common for all force feedback effects */ + Uint16 type; /**< Effect type. */ + SDL_HapticConstant constant; /**< Constant effect. */ + SDL_HapticPeriodic periodic; /**< Periodic effect. */ + SDL_HapticCondition condition; /**< Condition effect. */ + SDL_HapticRamp ramp; /**< Ramp effect. */ + SDL_HapticCustom custom; /**< Custom effect. */ +} SDL_HapticEffect; + + +/* Function prototypes */ +/** + * \fn int SDL_NumHaptics(void) + * + * \brief Count the number of joysticks attached to the system. + * + * \return Number of haptic devices detected on the system. + */ +extern DECLSPEC int SDLCALL SDL_NumHaptics(void); + +/** + * \fn const char * SDL_HapticName(int device_index) + * + * \brief Get the implementation dependent name of a Haptic device. + * This can be called before any joysticks are opened. + * If no name can be found, this function returns NULL. + * + * \param device_index Index of the device to get it's name. + * \return Name of the device or NULL on error. + * + * \sa SDL_NumHaptics + */ +extern DECLSPEC const char *SDLCALL SDL_HapticName(int device_index); + +/** + * \fn SDL_Haptic * SDL_HapticOpen(int device_index) + * + * \brief Opens a Haptic device for usage - the index passed as an + * argument refers to the N'th Haptic device on this system. + * + * When opening a haptic device, it's gain will be set to maximum and + * autocenter will be disabled. To modify these values use + * SDL_HapticSetGain and SDL_HapticSetAutocenter + * + * \param device_index Index of the device to open. + * \return Device identifier or NULL on error. + * + * \sa SDL_HapticIndex + * \sa SDL_HapticOpenFromMouse + * \sa SDL_HapticOpenFromJoystick + * \sa SDL_HapticClose + * \sa SDL_HapticSetGain + * \sa SDL_HapticSetAutocenter + * \sa SDL_HapticPause + * \sa SDL_HapticStopAll + */ +extern DECLSPEC SDL_Haptic *SDLCALL SDL_HapticOpen(int device_index); + +/** + * \fn int SDL_HapticOpened(int device_index) + * + * \brief Checks if the haptic device at index has been opened. + * + * \param device_index Index to check to see if it has been opened. + * \return 1 if it has been opened or 0 if it hasn't. + * + * \sa SDL_HapticOpen + * \sa SDL_HapticIndex + */ +extern DECLSPEC int SDLCALL SDL_HapticOpened(int device_index); + +/** + * \fn int SDL_HapticIndex(SDL_Haptic * haptic) + * + * \brief Gets the index of a haptic device. + * + * \param haptic Haptic device to get the index of. + * \return The index of the haptic device or -1 on error. + * + * \sa SDL_HapticOpen + * \sa SDL_HapticOpened + */ +extern DECLSPEC int SDLCALL SDL_HapticIndex(SDL_Haptic * haptic); + +/** + * \fn int SDL_MouseIsHaptic(void) + * + * \brief Gets whether or not the current mouse has haptic capabilities. + * + * \return SDL_TRUE if the mouse is haptic, SDL_FALSE if it isn't. + * + * \sa SDL_HapticOpenFromMouse + */ +extern DECLSPEC int SDLCALL SDL_MouseIsHaptic(void); + +/** + * \fn SDL_Haptic * SDL_HapticOpenFromMouse(void) + * + * \brief Tries to open a haptic device from the current mouse. + * + * \return The haptic device identifier or NULL on error. + * + * \sa SDL_MouseIsHaptic + * \sa SDL_HapticOpen + */ +extern DECLSPEC SDL_Haptic *SDLCALL SDL_HapticOpenFromMouse(void); + +/** + * \fn int SDL_JoystickIsHaptic(SDL_Joystick * joystick) + * + * \brief Checks to see if a joystick has haptic features. + * + * \param joystick Joystick to test for haptic capabilities. + * \return SDL_TRUE if the joystick is haptic, SDL_FALSE if it isn't + * or -1 if an error ocurred. + * + * \sa SDL_HapticOpenFromJoystick + */ +extern DECLSPEC int SDLCALL SDL_JoystickIsHaptic(SDL_Joystick * joystick); + +/** + * \fn SDL_Haptic * SDL_HapticOpenFromJoystick(SDL_Joystick * joystick) + * + * \brief Opens a Haptic device for usage from a Joystick device. Still has + * to be closed seperately to the joystick. + * + * When opening from a joystick you should first close the haptic device before + * closing the joystick device. If not, on some implementations the haptic + * device will also get unallocated and you'll be unable to use force feedback + * on that device. + * + * \param joystick Joystick to create a haptic device from. + * \return A valid haptic device identifier on success or NULL on error. + * + * \sa SDL_HapticOpen + * \sa SDL_HapticClose + */ +extern DECLSPEC SDL_Haptic *SDLCALL SDL_HapticOpenFromJoystick(SDL_Joystick * + joystick); + +/** + * \fn void SDL_HapticClose(SDL_Haptic * haptic) + * + * \brief Closes a Haptic device previously opened with SDL_HapticOpen. + * + * \param haptic Haptic device to close. + */ +extern DECLSPEC void SDLCALL SDL_HapticClose(SDL_Haptic * haptic); + +/** + * \fn int SDL_HapticNumEffects(SDL_Haptic * haptic) + * + * \brief Returns the number of effects a haptic device can store. + * + * On some platforms this isn't fully supported, and therefore is an + * aproximation. Always check to see if your created effect was actually + * created and do not rely solely on HapticNumEffects. + * + * \param haptic The haptic device to query effect max. + * \return The number of effects the haptic device can store or + * -1 on error. + * + * \sa SDL_HapticNumEffectsPlaying + * \sa SDL_HapticQuery + */ +extern DECLSPEC int SDLCALL SDL_HapticNumEffects(SDL_Haptic * haptic); + +/** + * \fn int SDL_HapticNumEffectsPlaying(SDL_Haptic * haptic) + * + * \brief Returns the number of effects a haptic device can play at the same time. + * + * This is not supported on all platforms, but will always return a value. Added + * here for the sake of completness. + * + * \param haptic The haptic device to query maximum playing effect.s + * \return The number of effects the haptic device can play at the same time + * or -1 on error. + * + * \sa SDL_HapticNumEffects + * \sa SDL_HapticQuery + */ +extern DECLSPEC int SDLCALL SDL_HapticNumEffectsPlaying(SDL_Haptic * haptic); + +/** + * \fn unsigned int SDL_HapticQuery(SDL_Haptic * haptic) + * + * \brief Gets the haptic devices supported features in bitwise matter. + * + * Example: + * \code + * if (SDL_HapticQueryEffects(haptic) & SDL_HAPTIC_CONSTANT) { + * printf("We have constant haptic effect!"); + * } + * \endcode + * + * + * \param haptic The haptic device to query. + * \return Haptic features in bitwise manner (OR'd). + * + * \sa SDL_HapticNumEffects + * \sa SDL_HapticEffectSupported + */ +extern DECLSPEC unsigned int SDLCALL SDL_HapticQuery(SDL_Haptic * haptic); + + +/** + * \fn int SDL_HapticNumAxes(SDL_Haptic * haptic) + * + * \brief Gets the number of haptic axes the device has. + * + * \sa SDL_HapticDirection + */ +extern DECLSPEC int SDLCALL SDL_HapticNumAxes(SDL_Haptic * haptic); + +/** + * \fn int SDL_HapticEffectSupported(SDL_Haptic * haptic, SDL_HapticEffect * effect) + * + * \brief Checks to see if effect is supported by haptic. + * + * \param haptic Haptic device to check on. + * \param effect Effect to check to see if it is supported. + * \return SDL_TRUE if effect is supported, SDL_FALSE if it isn't or + * -1 on error. + * + * \sa SDL_HapticQuery + * \sa SDL_HapticNewEffect + */ +extern DECLSPEC int SDLCALL SDL_HapticEffectSupported(SDL_Haptic * haptic, + SDL_HapticEffect * + effect); + +/** + * \fn int SDL_HapticNewEffect(SDL_Haptic * haptic, SDL_HapticEffect * effect) + * + * \brief Creates a new haptic effect on the device. + * + * \param haptic Haptic device to create the effect on. + * \param effect Properties of the effect to create. + * \return The id of the effect on success or -1 on error. + * + * \sa SDL_HapticUpdateEffect + * \sa SDL_HapticRunEffect + * \sa SDL_HapticDestroyEffect + */ +extern DECLSPEC int SDLCALL SDL_HapticNewEffect(SDL_Haptic * haptic, + SDL_HapticEffect * effect); + +/** + * \fn int SDL_HapticUpdateEffect(SDL_Haptic * haptic, int effect, SDL_HapticEffect * data) + * + * \brief Updates the properties of an effect. + * + * Can be used dynamically, although behaviour when dynamically changing + * direction may be strange. Specifically the effect may reupload itself + * and start playing from the start. You cannot change the type either when + * running UpdateEffect. + * + * \param haptic Haptic device that has the effect. + * \param effect Effect to update. + * \param data New effect properties to use. + * \return The id of the effect on success or -1 on error. + * + * \sa SDL_HapticNewEffect + * \sa SDL_HapticRunEffect + * \sa SDL_HapticDestroyEffect + */ +extern DECLSPEC int SDLCALL SDL_HapticUpdateEffect(SDL_Haptic * haptic, + int effect, + SDL_HapticEffect * data); + +/** + * \fn int SDL_HapticRunEffect(SDL_Haptic * haptic, int effect, Uint32 iterations) + * + * \brief Runs the haptic effect on it's assosciated haptic device. + * + * If iterations are SDL_HAPTIC_INFINITY, it'll run the effect over and over + * repeating the envelope (attack and fade) every time. If you only want the + * effect to last forever, set SDL_HAPTIC_INFINITY in the effect's length + * parameter. + * + * \param haptic Haptic device to run the effect on. + * \param effect Identifier of the haptic effect to run. + * \param iterations Number of iterations to run the effect. Use + * SDL_HAPTIC_INFINITY for infinity. + * \return 0 on success or -1 on error. + * + * \sa SDL_HapticStopEffect + * \sa SDL_HapticDestroyEffect + * \sa SDL_HapticGetEffectStatus + */ +extern DECLSPEC int SDLCALL SDL_HapticRunEffect(SDL_Haptic * haptic, + int effect, + Uint32 iterations); + +/** + * \fn int SDL_HapticStopEffect(SDL_Haptic * haptic, int effect) + * + * \brief Stops the haptic effect on it's assosciated haptic device. + * + * \param haptic Haptic device to stop the effect on. + * \param effect Identifier of the effect to stop. + * \return 0 on success or -1 on error. + * + * \sa SDL_HapticRunEffect + * \sa SDL_HapticDestroyEffect + */ +extern DECLSPEC int SDLCALL SDL_HapticStopEffect(SDL_Haptic * haptic, + int effect); + +/** + * \fn void SDL_HapticDestroyEffect(SDL_Haptic * haptic, int effect) + * + * \brief Destroys a haptic effect on the device. This will stop the effect + * if it's running. Effects are automatically destroyed when the device is + * closed. + * + * \param haptic Device to destroy the effect on. + * \param effect Identifier of the effect to destroy. + * + * \sa SDL_HapticNewEffect + */ +extern DECLSPEC void SDLCALL SDL_HapticDestroyEffect(SDL_Haptic * haptic, + int effect); + +/** + * \fn int SDL_HapticGetEffectStatus(SDL_Haptic *haptic, int effect) + * + * \brief Gets the status of the current effect on the haptic device. + * + * Device must support the SDL_HAPTIC_STATUS feature. + * + * \param haptic Haptic device to query the effect status on. + * \param effect Identifier of the effect to query it's status. + * \return 0 if it isn't playing, SDL_HAPTIC_PLAYING if it is playing + * or -1 on error. + * + * \sa SDL_HapticRunEffect + * \sa SDL_HapticStopEffect + */ +extern DECLSPEC int SDLCALL SDL_HapticGetEffectStatus(SDL_Haptic * haptic, + int effect); + +/** + * \fn int SDL_HapticSetGain(SDL_Haptic * haptic, int gain) + * + * \brief Sets the global gain of the device. Gain should be between 0 and 100. + * + * Device must support the SDL_HAPTIC_GAIN feature. + * + * The user may specify the maxmimum gain by setting the environment variable + * SDL_HAPTIC_GAIN_MAX which should be between 0 and 100. All calls to + * SDL_HapticSetGain will scale linearly using SDL_HAPTIC_GAIN_MAX as the + * maximum. + * + * \param haptic Haptic device to set the gain on. + * \param gain Value to set the gain to, should be between 0 and 100. + * \return 0 on success or -1 on error. + * + * \sa SDL_HapticQuery + */ +extern DECLSPEC int SDLCALL SDL_HapticSetGain(SDL_Haptic * haptic, int gain); + +/** + * \fn int SDL_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) + * + * \brief Sets the global autocenter of the device. Autocenter should be between + * 0 and 100. Setting it to 0 will disable autocentering. + * + * Device must support the SDL_HAPTIC_AUTOCENTER feature. + * + * \param haptic Haptic device to set autocentering on. + * \param autocenter Value to set autocenter to, 0 disables autocentering. + * \return 0 on success or -1 on error. + * + * \sa SDL_HapticQuery + */ +extern DECLSPEC int SDLCALL SDL_HapticSetAutocenter(SDL_Haptic * haptic, + int autocenter); + +/** + * \fn extern DECLSPEC int SDLCALL SDL_HapticPause(SDL_Haptic * haptic) + * + * \brief Pauses a haptic device. + * + * Device must support the SDL_HAPTIC_PAUSE feature. Call SDL_HapticUnpause + * to resume playback. + * + * Do not modify the effects nor add new ones while the device is paused. + * That can cause all sorts of weird errors. + * + * \param haptic Haptic device to pause. + * \return 0 on success or -1 on error. + * + * \sa SDL_HapticUnpause + */ +extern DECLSPEC int SDLCALL SDL_HapticPause(SDL_Haptic * haptic); + +/** + * \fn extern DECLSPEC int SDLCALL SDL_HapticUnpause(SDL_Haptic * haptic) + * + * \brief Unpauses a haptic device. + * + * Call to unpause after SDL_HapticPause. + * + * \param haptic Haptic device to pause. + * \return 0 on success or -1 on error. + * + * \sa SDL_HapticPause + */ +extern DECLSPEC int SDLCALL SDL_HapticUnpause(SDL_Haptic * haptic); + +/** + * \fn extern DECSLPEC int SDLCALL SDL_HapticStopAll(SDL_Haptic * haptic) + * + * \brief Stops all the currently playing effects on a haptic device. + * + * \param haptic Haptic device to stop. + * \return 0 on success or -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_HapticStopAll(SDL_Haptic * haptic); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_haptic_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/SDL.c b/src/SDL.c index d2858673b..55f826071 100644 --- a/src/SDL.c +++ b/src/SDL.c @@ -38,6 +38,10 @@ extern int SDL_JoystickInit(void); extern void SDL_JoystickQuit(void); #endif +#if !SDL_HAPTIC_DISABLED +extern int SDL_HapticInit(void); +extern int SDL_HapticQuit(void); +#endif #if !SDL_CDROM_DISABLED extern int SDL_CDROMInit(void); extern void SDL_CDROMQuit(void); @@ -47,6 +51,10 @@ extern void SDL_StartTicks(void); extern int SDL_TimerInit(void); extern void SDL_TimerQuit(void); #endif +#if defined(__WIN32__) +extern int SDL_HelperWindowCreate(void); +extern int SDL_HelperWindowDestroy(void); +#endif /* The initialized subsystems */ static Uint32 SDL_initialized = 0; @@ -123,6 +131,22 @@ SDL_InitSubSystem(Uint32 flags) } #endif +#if !SDL_HAPTIC_DISABLED + /* Initialize the haptic subsystem */ + if ((flags & SDL_INIT_HAPTIC) && !(SDL_initialized & SDL_INIT_HAPTIC)) { + if (SDL_HapticInit() < 0) { + return (-1); + } + SDL_initialized |= SDL_INIT_HAPTIC; + } +#else + if (flags & SDL_INIT_HAPTIC) { + SDL_SetError("SDL not built with haptic (force feedback) support"); + return (-1); + } +#endif + + #if !SDL_CDROM_DISABLED /* Initialize the CD-ROM subsystem */ if ((flags & SDL_INIT_CDROM) && !(SDL_initialized & SDL_INIT_CDROM)) { @@ -152,6 +176,12 @@ SDL_Init(Uint32 flags) /* Clear the error message */ SDL_ClearError(); +#if defined(__WIN32__) + if (SDL_HelperWindowCreate() < 0) { + return -1; + } +#endif + /* Initialize the desired subsystems */ if (SDL_InitSubSystem(flags) < 0) { return (-1); @@ -180,6 +210,12 @@ SDL_QuitSubSystem(Uint32 flags) SDL_initialized &= ~SDL_INIT_JOYSTICK; } #endif +#if !SDL_HAPTIC_DISABLED + if ((flags & SDL_initialized & SDL_INIT_HAPTIC)) { + SDL_HapticQuit(); + SDL_initialized &= ~SDL_INIT_HAPTIC; + } +#endif #if !SDL_TIMERS_DISABLED if ((flags & SDL_initialized & SDL_INIT_TIMER)) { SDL_TimerQuit(); @@ -217,6 +253,10 @@ SDL_Quit(void) printf("[SDL_Quit] : Enter! Calling QuitSubSystem()\n"); fflush(stdout); #endif + +#if defined(__WIN32__) + SDL_HelperWindowDestroy(); +#endif SDL_QuitSubSystem(SDL_INIT_EVERYTHING); #ifdef CHECK_LEAKS diff --git a/src/haptic/SDL_haptic.c b/src/haptic/SDL_haptic.c new file mode 100644 index 000000000..9f99b0f16 --- /dev/null +++ b/src/haptic/SDL_haptic.c @@ -0,0 +1,698 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 2008 Edgar Simo + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include "SDL_syshaptic.h" +#include "../joystick/SDL_joystick_c.h" /* For SDL_PrivateJoystickValid */ + + +Uint8 SDL_numhaptics = 0; +SDL_Haptic **SDL_haptics = NULL; + + +/* + * Initializes the Haptic devices. + */ +int +SDL_HapticInit(void) +{ + int arraylen; + int status; + + SDL_numhaptics = 0; + status = SDL_SYS_HapticInit(); + if (status >= 0) { + arraylen = (status + 1) * sizeof(*SDL_haptics); + SDL_haptics = (SDL_Haptic **) SDL_malloc(arraylen); + if (SDL_haptics == NULL) { /* Out of memory. */ + SDL_numhaptics = 0; + } else { + SDL_memset(SDL_haptics, 0, arraylen); + SDL_numhaptics = status; + } + status = 0; + } + + return status; +} + + +/* + * Checks to see if the haptic device is valid + */ +static int +ValidHaptic(SDL_Haptic * haptic) +{ + int i; + int valid; + + valid = 0; + for (i = 0; i < SDL_numhaptics; i++) { + if (SDL_haptics[i] == haptic) { + valid = 1; + break; + } + } + + return valid; +} + + +/* + * Returns the number of available devices. + */ +int +SDL_NumHaptics(void) +{ + return SDL_numhaptics; +} + + +/* + * Gets the name of a Haptic device by index. + */ +const char * +SDL_HapticName(int device_index) +{ + if ((device_index < 0) || (device_index >= SDL_numhaptics)) { + SDL_SetError("Haptic: There are %d haptic devices available", + SDL_numhaptics); + return NULL; + } + return SDL_SYS_HapticName(device_index); +} + + +/* + * Opens a Haptic device. + */ +SDL_Haptic * +SDL_HapticOpen(int device_index) +{ + int i; + SDL_Haptic *haptic; + + if ((device_index < 0) || (device_index >= SDL_numhaptics)) { + SDL_SetError("Haptic: There are %d haptic devices available", + SDL_numhaptics); + return NULL; + } + + /* If the haptic is already open, return it */ + for (i = 0; SDL_haptics[i]; i++) { + if (device_index == SDL_haptics[i]->index) { + haptic = SDL_haptics[i]; + ++haptic->ref_count; + return haptic; + } + } + + /* Create the haptic device */ + haptic = (SDL_Haptic *) SDL_malloc((sizeof *haptic)); + if (haptic == NULL) { + SDL_OutOfMemory(); + return NULL; + } + + /* Initialize the haptic device */ + SDL_memset(haptic, 0, (sizeof *haptic)); + haptic->index = device_index; + if (SDL_SYS_HapticOpen(haptic) < 0) { + SDL_free(haptic); + return NULL; + } + + /* Disable autocenter and set gain to max. */ + if (haptic->supported & SDL_HAPTIC_GAIN) + SDL_HapticSetGain(haptic, 100); + if (haptic->supported & SDL_HAPTIC_AUTOCENTER) + SDL_HapticSetAutocenter(haptic, 0); + + /* Add haptic to list */ + ++haptic->ref_count; + for (i = 0; SDL_haptics[i]; i++) + /* Skip to next haptic */ ; + SDL_haptics[i] = haptic; + + return haptic; +} + + +/* + * Returns 1 if the device has been opened. + */ +int +SDL_HapticOpened(int device_index) +{ + int i, opened; + + opened = 0; + for (i = 0; SDL_haptics[i]; i++) { + if (SDL_haptics[i]->index == (Uint8) device_index) { + opened = 1; + break; + } + } + return opened; +} + + +/* + * Returns the index to a haptic device. + */ +int +SDL_HapticIndex(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + return haptic->index; +} + + +/* + * Returns SDL_TRUE if mouse is haptic, SDL_FALSE if it isn't. + */ +int +SDL_MouseIsHaptic(void) +{ + if (SDL_SYS_HapticMouse() < 0) + return SDL_FALSE; + return SDL_TRUE; +} + + +/* + * Returns the haptic device if mouse is haptic or NULL elsewise. + */ +SDL_Haptic * +SDL_HapticOpenFromMouse(void) +{ + int device_index; + + device_index = SDL_SYS_HapticMouse(); + + if (device_index < 0) { + SDL_SetError("Haptic: Mouse isn't a haptic device."); + return NULL; + } + + return SDL_HapticOpen(device_index); +} + + +/* + * Returns SDL_TRUE if joystick has haptic features. + */ +int +SDL_JoystickIsHaptic(SDL_Joystick * joystick) +{ + int ret; + + /* Must be a valid joystick */ + if (!SDL_PrivateJoystickValid(&joystick)) { + return -1; + } + + ret = SDL_SYS_JoystickIsHaptic(joystick); + + if (ret > 0) + return SDL_TRUE; + else if (ret == 0) + return SDL_FALSE; + else + return -1; +} + + +/* + * Opens a haptic device from a joystick. + */ +SDL_Haptic * +SDL_HapticOpenFromJoystick(SDL_Joystick * joystick) +{ + int i; + SDL_Haptic *haptic; + + /* Must be a valid joystick */ + if (!SDL_PrivateJoystickValid(&joystick)) { + return NULL; + } + + /* Joystick must be haptic */ + if (SDL_SYS_JoystickIsHaptic(joystick) <= 0) { + return NULL; + } + + /* Check to see if joystick's haptic is already open */ + for (i = 0; SDL_haptics[i]; i++) { + if (SDL_SYS_JoystickSameHaptic(SDL_haptics[i], joystick)) { + haptic = SDL_haptics[i]; + ++haptic->ref_count; + return haptic; + } + } + + /* Create the haptic device */ + haptic = (SDL_Haptic *) SDL_malloc((sizeof *haptic)); + if (haptic == NULL) { + SDL_OutOfMemory(); + return NULL; + } + + /* Initialize the haptic device */ + SDL_memset(haptic, 0, sizeof(SDL_Haptic)); + if (SDL_SYS_HapticOpenFromJoystick(haptic, joystick) < 0) { + SDL_free(haptic); + return NULL; + } + + /* Add haptic to list */ + ++haptic->ref_count; + for (i = 0; SDL_haptics[i]; i++) + /* Skip to next haptic */ ; + SDL_haptics[i] = haptic; + + return haptic; +} + + +/* + * Closes a SDL_Haptic device. + */ +void +SDL_HapticClose(SDL_Haptic * haptic) +{ + int i; + + /* Must be valid */ + if (!ValidHaptic(haptic)) { + return; + } + + /* Check if it's still in use */ + if (--haptic->ref_count < 0) { + return; + } + + /* Close it, properly removing effects if needed */ + for (i = 0; i < haptic->neffects; i++) { + if (haptic->effects[i].hweffect != NULL) { + SDL_HapticDestroyEffect(haptic, i); + } + } + SDL_SYS_HapticClose(haptic); + + /* Remove from the list */ + for (i = 0; SDL_haptics[i]; ++i) { + if (haptic == SDL_haptics[i]) { + SDL_haptics[i] = NULL; + SDL_memcpy(&SDL_haptics[i], &SDL_haptics[i + 1], + (SDL_numhaptics - i) * sizeof(haptic)); + break; + } + } + + /* Free */ + SDL_free(haptic); +} + +/* + * Cleans up after the subsystem. + */ +void +SDL_HapticQuit(void) +{ + SDL_SYS_HapticQuit(); + if (SDL_haptics != NULL) { + SDL_free(SDL_haptics); + SDL_haptics = NULL; + } + SDL_numhaptics = 0; +} + +/* + * Returns the number of effects a haptic device has. + */ +int +SDL_HapticNumEffects(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + return haptic->neffects; +} + + +/* + * Returns the number of effects a haptic device can play. + */ +int +SDL_HapticNumEffectsPlaying(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + return haptic->nplaying; +} + + +/* + * Returns supported effects by the device. + */ +unsigned int +SDL_HapticQuery(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + return haptic->supported; +} + + +/* + * Returns the number of axis on the device. + */ +int +SDL_HapticNumAxes(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + return haptic->naxes; +} + +/* + * Checks to see if the device can support the effect. + */ +int +SDL_HapticEffectSupported(SDL_Haptic * haptic, SDL_HapticEffect * effect) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + if ((haptic->supported & effect->type) != 0) + return SDL_TRUE; + return SDL_FALSE; +} + +/* + * Creates a new haptic effect. + */ +int +SDL_HapticNewEffect(SDL_Haptic * haptic, SDL_HapticEffect * effect) +{ + int i; + + /* Check for device validity. */ + if (!ValidHaptic(haptic)) { + return -1; + } + + /* Check to see if effect is supported */ + if (SDL_HapticEffectSupported(haptic, effect) == SDL_FALSE) { + SDL_SetError("Haptic: Effect not supported by haptic device."); + return -1; + } + + /* See if there's a free slot */ + for (i = 0; i < haptic->neffects; i++) { + if (haptic->effects[i].hweffect == NULL) { + + /* Now let the backend create the real effect */ + if (SDL_SYS_HapticNewEffect(haptic, &haptic->effects[i], effect) + != 0) { + return -1; /* Backend failed to create effect */ + } + + SDL_memcpy(&haptic->effects[i].effect, effect, + sizeof(SDL_HapticEffect)); + return i; + } + } + + SDL_SetError("Haptic: Device has no free space left."); + return -1; +} + +/* + * Checks to see if an effect is valid. + */ +static int +ValidEffect(SDL_Haptic * haptic, int effect) +{ + if ((effect < 0) || (effect >= haptic->neffects)) { + SDL_SetError("Haptic: Invalid effect identifier."); + return 0; + } + return 1; +} + +/* + * Updates an effect. + */ +int +SDL_HapticUpdateEffect(SDL_Haptic * haptic, int effect, + SDL_HapticEffect * data) +{ + if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { + return -1; + } + + /* Can't change type dynamically. */ + if (data->type != haptic->effects[effect].effect.type) { + SDL_SetError("Haptic: Updating effect type is illegal."); + return -1; + } + + /* Updates the effect */ + if (SDL_SYS_HapticUpdateEffect(haptic, &haptic->effects[effect], data) < + 0) { + return -1; + } + + SDL_memcpy(&haptic->effects[effect].effect, data, + sizeof(SDL_HapticEffect)); + return 0; +} + + +/* + * Runs the haptic effect on the device. + */ +int +SDL_HapticRunEffect(SDL_Haptic * haptic, int effect, Uint32 iterations) +{ + if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { + return -1; + } + + /* Run the effect */ + if (SDL_SYS_HapticRunEffect(haptic, &haptic->effects[effect], iterations) + < 0) { + return -1; + } + + return 0; +} + +/* + * Stops the haptic effect on the device. + */ +int +SDL_HapticStopEffect(SDL_Haptic * haptic, int effect) +{ + if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { + return -1; + } + + /* Stop the effect */ + if (SDL_SYS_HapticStopEffect(haptic, &haptic->effects[effect]) < 0) { + return -1; + } + + return 0; +} + +/* + * Gets rid of a haptic effect. + */ +void +SDL_HapticDestroyEffect(SDL_Haptic * haptic, int effect) +{ + if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { + return; + } + + /* Not allocated */ + if (haptic->effects[effect].hweffect == NULL) { + return; + } + + SDL_SYS_HapticDestroyEffect(haptic, &haptic->effects[effect]); +} + +/* + * Gets the status of a haptic effect. + */ +int +SDL_HapticGetEffectStatus(SDL_Haptic * haptic, int effect) +{ + if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { + return -1; + } + + if ((haptic->supported & SDL_HAPTIC_STATUS) == 0) { + SDL_SetError("Haptic: Device does not support status queries."); + return -1; + } + + return SDL_SYS_HapticGetEffectStatus(haptic, &haptic->effects[effect]); +} + +/* + * Sets the global gain of the device. + */ +int +SDL_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + const char *env; + int real_gain, max_gain; + + if (!ValidHaptic(haptic)) { + return -1; + } + + if ((haptic->supported & SDL_HAPTIC_GAIN) == 0) { + SDL_SetError("Haptic: Device does not support setting gain."); + return -1; + } + + if ((gain < 0) || (gain > 100)) { + SDL_SetError("Haptic: Gain must be between 0 and 100."); + return -1; + } + + /* We use the envvar to get the maximum gain. */ + env = SDL_getenv("SDL_HAPTIC_GAIN_MAX"); + if (env != NULL) { + max_gain = SDL_atoi(env); + + /* Check for sanity. */ + if (max_gain < 0) + max_gain = 0; + else if (max_gain > 100) + max_gain = 100; + + /* We'll scale it linearly with SDL_HAPTIC_GAIN_MAX */ + real_gain = (gain * max_gain) / 100; + } else { + real_gain = gain; + } + + if (SDL_SYS_HapticSetGain(haptic, real_gain) < 0) { + return -1; + } + + return 0; +} + +/* + * Makes the device autocenter, 0 disables. + */ +int +SDL_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + if ((haptic->supported & SDL_HAPTIC_AUTOCENTER) == 0) { + SDL_SetError("Haptic: Device does not support setting autocenter."); + return -1; + } + + if ((autocenter < 0) || (autocenter > 100)) { + SDL_SetError("Haptic: Autocenter must be between 0 and 100."); + return -1; + } + + if (SDL_SYS_HapticSetAutocenter(haptic, autocenter) < 0) { + return -1; + } + + return 0; +} + +/* + * Pauses the haptic device. + */ +int +SDL_HapticPause(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + if ((haptic->supported & SDL_HAPTIC_PAUSE) == 0) { + SDL_SetError("Haptic: Device does not support setting pausing."); + return -1; + } + + return SDL_SYS_HapticPause(haptic); +} + +/* + * Unpauses the haptic device. + */ +int +SDL_HapticUnpause(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + if ((haptic->supported & SDL_HAPTIC_PAUSE) == 0) { + return 0; /* Not going to be paused, so we pretend it's unpaused. */ + } + + return SDL_SYS_HapticUnpause(haptic); +} + +/* + * Stops all the currently playing effects. + */ +int +SDL_HapticStopAll(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + return SDL_SYS_HapticStopAll(haptic); +} diff --git a/src/haptic/SDL_syshaptic.h b/src/haptic/SDL_syshaptic.h new file mode 100644 index 000000000..9542a0d75 --- /dev/null +++ b/src/haptic/SDL_syshaptic.h @@ -0,0 +1,201 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 2008 Edgar Simo + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#include "SDL_config.h" + +#include "SDL_haptic.h" + + +/* + * Number of haptic devices on the system. + */ +extern Uint8 SDL_numhaptics; + + +struct haptic_effect +{ + SDL_HapticEffect effect; /* The current event */ + struct haptic_hweffect *hweffect; /* The hardware behind the event */ +}; + +/* + * The real SDL_Haptic struct. + */ +struct _SDL_Haptic +{ + Uint8 index; /* Stores index it is attached to */ + + struct haptic_effect *effects; /* Allocated effects */ + int neffects; /* Maximum amount of effects */ + int nplaying; /* Maximum amount of effects to play at the same time */ + unsigned int supported; /* Supported effects */ + int naxes; /* Number of axes on the device. */ + + struct haptic_hwdata *hwdata; /* Driver dependent */ + int ref_count; /* Count for multiple opens */ +}; + +/* + * Scans the system for haptic devices. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticInit(void); + +/* + * Gets the device dependent name of the haptic device + */ +extern const char *SDL_SYS_HapticName(int index); + +/* + * Opens the haptic device for usage. The haptic device should have + * the index value set previously. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticOpen(SDL_Haptic * haptic); + +/* + * Returns the index of the haptic core pointer or -1 if none is found. + */ +int SDL_SYS_HapticMouse(void); + +/* + * Checks to see if the joystick has haptic capabilities. + * + * Returns >0 if haptic capabilities are detected, 0 if haptic + * capabilities aren't detected and -1 on error. + */ +extern int SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick); + +/* + * Opens the haptic device for usage using the same device as + * the joystick. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, + SDL_Joystick * joystick); +/* + * Checks to see if haptic device and joystick device are the same. + * + * Returns 1 if they are the same, 0 if they aren't. + */ +extern int SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, + SDL_Joystick * joystick); + +/* + * Closes a haptic device after usage. + */ +extern void SDL_SYS_HapticClose(SDL_Haptic * haptic); + +/* + * Performs a cleanup on the haptic subsystem. + */ +extern void SDL_SYS_HapticQuit(void); + +/* + * Creates a new haptic effect on the haptic device using base + * as a template for the effect. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, + SDL_HapticEffect * base); + +/* + * Updates the haptic effect on the haptic device using data + * as a template. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, + SDL_HapticEffect * data); + +/* + * Runs the effect on the haptic device. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, + Uint32 iterations); + +/* + * Stops the effect on the haptic device. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, + struct haptic_effect *effect); + +/* + * Cleanups up the effect on the haptic device. + */ +extern void SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, + struct haptic_effect *effect); + +/* + * Queries the device for the status of effect. + * + * Returns 0 if device is stopped, >0 if device is playing and + * -1 on error. + */ +extern int SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, + struct haptic_effect *effect); + +/* + * Sets the global gain of the haptic device. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain); + +/* + * Sets the autocenter feature of the haptic device. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter); + +/* + * Pauses the haptic device. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticPause(SDL_Haptic * haptic); + +/* + * Unpauses the haptic device. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticUnpause(SDL_Haptic * haptic); + +/* + * Stops all the currently playing haptic effects on the device. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticStopAll(SDL_Haptic * haptic); diff --git a/src/haptic/darwin/SDL_syshaptic.c b/src/haptic/darwin/SDL_syshaptic.c new file mode 100644 index 000000000..579984861 --- /dev/null +++ b/src/haptic/darwin/SDL_syshaptic.c @@ -0,0 +1,1317 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 2008 Edgar Simo + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifdef SDL_HAPTIC_IOKIT + +#include "SDL_haptic.h" +#include "../SDL_syshaptic.h" +#include "SDL_joystick.h" +#include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */ +#include "../../joystick/darwin/SDL_sysjoystick_c.h" /* For joystick hwdata */ + +#include +#include +#include +#include + + +#define MAX_HAPTICS 32 + + +/* + * List of available haptic devices. + */ +static struct +{ + char name[256]; /* Name of the device. */ + + io_service_t dev; /* Node we use to create the device. */ + SDL_Haptic *haptic; /* Haptic currently assosciated with it. */ + + /* Usage pages for determining if it's a mouse or not. */ + long usage; + long usagePage; +} SDL_hapticlist[MAX_HAPTICS]; + + +/* + * Haptic system hardware data. + */ +struct haptic_hwdata +{ + FFDeviceObjectReference device; /* Hardware device. */ + UInt8 axes[3]; +}; + + +/* + * Haptic system effect data. + */ +struct haptic_hweffect +{ + FFEffectObjectReference ref; /* Reference. */ + struct FFEFFECT effect; /* Hardware effect. */ +}; + +/* + * Prototypes. + */ +static void SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type); +static int HIDGetDeviceProduct(io_service_t dev, char *name); + + +/* + * Like strerror but for force feedback errors. + */ +static const char * +FFStrError(HRESULT err) +{ + switch (err) { + case FFERR_DEVICEFULL: + return "device full"; + /* This should be valid, but for some reason isn't defined... */ + /*case FFERR_DEVICENOTREG: + return "device not registered"; */ + case FFERR_DEVICEPAUSED: + return "device paused"; + case FFERR_DEVICERELEASED: + return "device released"; + case FFERR_EFFECTPLAYING: + return "effect playing"; + case FFERR_EFFECTTYPEMISMATCH: + return "effect type mismatch"; + case FFERR_EFFECTTYPENOTSUPPORTED: + return "effect type not supported"; + case FFERR_GENERIC: + return "undetermined error"; + case FFERR_HASEFFECTS: + return "device has effects"; + case FFERR_INCOMPLETEEFFECT: + return "incomplete effect"; + case FFERR_INTERNAL: + return "internal fault"; + case FFERR_INVALIDDOWNLOADID: + return "invalid download id"; + case FFERR_INVALIDPARAM: + return "invalid parameter"; + case FFERR_MOREDATA: + return "more data"; + case FFERR_NOINTERFACE: + return "interface not supported"; + case FFERR_NOTDOWNLOADED: + return "effect is not downloaded"; + case FFERR_NOTINITIALIZED: + return "object has not been initialized"; + case FFERR_OUTOFMEMORY: + return "out of memory"; + case FFERR_UNPLUGGED: + return "device is unplugged"; + case FFERR_UNSUPPORTED: + return "function call unsupported"; + case FFERR_UNSUPPORTEDAXIS: + return "axis unsupported"; + + default: + return "unknown error"; + } +} + + +/* + * Initializes the haptic subsystem. + */ +int +SDL_SYS_HapticInit(void) +{ + int numhaptics; + IOReturn result; + io_iterator_t iter; + CFDictionaryRef match; + io_service_t device; + CFMutableDictionaryRef hidProperties; + CFTypeRef refCF; + + /* Clear all the memory. */ + SDL_memset(SDL_hapticlist, 0, sizeof(SDL_hapticlist)); + + /* Get HID devices. */ + match = IOServiceMatching(kIOHIDDeviceKey); + if (match == NULL) { + SDL_SetError("Haptic: Failed to get IOServiceMatching."); + return -1; + } + + /* Now search I/O Registry for matching devices. */ + result = IOServiceGetMatchingServices(kIOMasterPortDefault, match, &iter); + if (result != kIOReturnSuccess) { + SDL_SetError("Haptic: Couldn't create a HID object iterator."); + return -1; + } + /* IOServiceGetMatchingServices consumes dictionary. */ + + if (!IOIteratorIsValid(iter)) { /* No iterator. */ + numhaptics = 0; + return 0; + } + + numhaptics = 0; + while ((device = IOIteratorNext(iter)) != IO_OBJECT_NULL) { + + /* Check for force feedback. */ + if (FFIsForceFeedback(device) == FF_OK) { + + /* Set basic device data. */ + HIDGetDeviceProduct(device, SDL_hapticlist[numhaptics].name); + SDL_hapticlist[numhaptics].dev = device; + SDL_hapticlist[numhaptics].haptic = NULL; + + /* Set usage pages. */ + hidProperties = 0; + refCF = 0; + result = IORegistryEntryCreateCFProperties(device, + &hidProperties, + kCFAllocatorDefault, + kNilOptions); + if ((result == KERN_SUCCESS) && hidProperties) { + refCF = + CFDictionaryGetValue(hidProperties, + CFSTR(kIOHIDPrimaryUsagePageKey)); + if (refCF) { + if (!CFNumberGetValue(refCF, kCFNumberLongType, + &SDL_hapticlist[numhaptics]. + usagePage)) + SDL_SetError + ("Haptic: Recieving device's usage page."); + refCF = + CFDictionaryGetValue(hidProperties, + CFSTR(kIOHIDPrimaryUsageKey)); + if (refCF) { + if (!CFNumberGetValue(refCF, kCFNumberLongType, + &SDL_hapticlist[numhaptics]. + usage)) + SDL_SetError("Haptic: Recieving device's usage."); + } + } + CFRelease(hidProperties); + } + + /* Device has been added. */ + numhaptics++; + } else { /* Free the unused device. */ + IOObjectRelease(device); + } + + /* Reached haptic limit. */ + if (numhaptics >= MAX_HAPTICS) + break; + } + IOObjectRelease(iter); + + return numhaptics; +} + + +/* + * Return the name of a haptic device, does not need to be opened. + */ +const char * +SDL_SYS_HapticName(int index) +{ + return SDL_hapticlist[index].name; +} + +/* + * Gets the device's product name. + */ +static int +HIDGetDeviceProduct(io_service_t dev, char *name) +{ + CFMutableDictionaryRef hidProperties, usbProperties; + io_registry_entry_t parent1, parent2; + kern_return_t ret; + + hidProperties = usbProperties = 0; + + ret = IORegistryEntryCreateCFProperties(dev, &hidProperties, + kCFAllocatorDefault, kNilOptions); + if ((ret != KERN_SUCCESS) || !hidProperties) { + SDL_SetError("Haptic: Unable to create CFProperties."); + return -1; + } + + /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also + * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties + */ + if ((KERN_SUCCESS == + IORegistryEntryGetParentEntry(dev, kIOServicePlane, &parent1)) + && (KERN_SUCCESS == + IORegistryEntryGetParentEntry(parent1, kIOServicePlane, &parent2)) + && (KERN_SUCCESS == + IORegistryEntryCreateCFProperties(parent2, &usbProperties, + kCFAllocatorDefault, + kNilOptions))) { + if (usbProperties) { + CFTypeRef refCF = 0; + /* get device info + * try hid dictionary first, if fail then go to usb dictionary + */ + + + /* Get product name */ + refCF = + CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDProductKey)); + if (!refCF) + refCF = + CFDictionaryGetValue(usbProperties, + CFSTR("USB Product Name")); + if (refCF) { + if (!CFStringGetCString(refCF, name, 256, + CFStringGetSystemEncoding())) { + SDL_SetError + ("Haptic: CFStringGetCString error retrieving pDevice->product."); + return -1; + } + } + + CFRelease(usbProperties); + } else { + SDL_SetError + ("Haptic: IORegistryEntryCreateCFProperties failed to create usbProperties."); + return -1; + } + + /* Release stuff. */ + if (kIOReturnSuccess != IOObjectRelease(parent2)) { + SDL_SetError("Haptic: IOObjectRelease error with parent2."); + } + if (kIOReturnSuccess != IOObjectRelease(parent1)) { + SDL_SetError("Haptic: IOObjectRelease error with parent1."); + } + } else { + SDL_SetError("Haptic: Error getting registry entries."); + return -1; + } + + return 0; +} + + +#define FF_TEST(ff, s) \ +if (features.supportedEffects & (ff)) supported |= (s) +/* + * Gets supported features. + */ +static unsigned int +GetSupportedFeatures(SDL_Haptic * haptic) +{ + HRESULT ret; + FFDeviceObjectReference device; + FFCAPABILITIES features; + unsigned int supported; + Uint32 val; + + device = haptic->hwdata->device; + + ret = FFDeviceGetForceFeedbackCapabilities(device, &features); + if (ret != FF_OK) { + SDL_SetError("Haptic: Unable to get device's supported features."); + return -1; + } + + supported = 0; + + /* Get maximum effects. */ + haptic->neffects = features.storageCapacity; + haptic->nplaying = features.playbackCapacity; + + /* Test for effects. */ + FF_TEST(FFCAP_ET_CONSTANTFORCE, SDL_HAPTIC_CONSTANT); + FF_TEST(FFCAP_ET_RAMPFORCE, SDL_HAPTIC_RAMP); + FF_TEST(FFCAP_ET_SQUARE, SDL_HAPTIC_SQUARE); + FF_TEST(FFCAP_ET_SINE, SDL_HAPTIC_SINE); + FF_TEST(FFCAP_ET_TRIANGLE, SDL_HAPTIC_TRIANGLE); + FF_TEST(FFCAP_ET_SAWTOOTHUP, SDL_HAPTIC_SAWTOOTHUP); + FF_TEST(FFCAP_ET_SAWTOOTHDOWN, SDL_HAPTIC_SAWTOOTHDOWN); + FF_TEST(FFCAP_ET_SPRING, SDL_HAPTIC_SPRING); + FF_TEST(FFCAP_ET_DAMPER, SDL_HAPTIC_DAMPER); + FF_TEST(FFCAP_ET_INERTIA, SDL_HAPTIC_INERTIA); + FF_TEST(FFCAP_ET_FRICTION, SDL_HAPTIC_FRICTION); + FF_TEST(FFCAP_ET_CUSTOMFORCE, SDL_HAPTIC_CUSTOM); + + /* Check if supports gain. */ + ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_FFGAIN, + &val, sizeof(val)); + if (ret == FF_OK) + supported |= SDL_HAPTIC_GAIN; + else if (ret != FFERR_UNSUPPORTED) { + SDL_SetError("Haptic: Unable to get if device supports gain: %s.", + FFStrError(ret)); + return -1; + } + + /* Checks if supports autocenter. */ + ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_AUTOCENTER, + &val, sizeof(val)); + if (ret == FF_OK) + supported |= SDL_HAPTIC_AUTOCENTER; + else if (ret != FFERR_UNSUPPORTED) { + SDL_SetError + ("Haptic: Unable to get if device supports autocenter: %s.", + FFStrError(ret)); + return -1; + } + + /* Check for axes, we have an artificial limit on axes */ + haptic->naxes = ((features.numFfAxes) > 3) ? 3 : features.numFfAxes; + /* Actually store the axes we want to use */ + SDL_memcpy(haptic->hwdata->axes, features.ffAxes, + haptic->naxes * sizeof(Uint8)); + + /* Always supported features. */ + supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE; + + haptic->supported = supported; + return 0;; +} + + +/* + * Opens the haptic device from the file descriptor. + */ +static int +SDL_SYS_HapticOpenFromService(SDL_Haptic * haptic, io_service_t service) +{ + HRESULT ret; + int ret2; + + /* Allocate the hwdata */ + haptic->hwdata = (struct haptic_hwdata *) + SDL_malloc(sizeof(*haptic->hwdata)); + if (haptic->hwdata == NULL) { + SDL_OutOfMemory(); + goto creat_err; + } + SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); + + /* Open the device */ + ret = FFCreateDevice(service, &haptic->hwdata->device); + if (ret != FF_OK) { + SDL_SetError("Haptic: Unable to create device from service: %s.", + FFStrError(ret)); + goto creat_err; + } + + /* Get supported features. */ + ret2 = GetSupportedFeatures(haptic); + if (haptic->supported < 0) { + goto open_err; + } + + + /* Reset and then enable actuators. */ + ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, + FFSFFC_RESET); + if (ret != FF_OK) { + SDL_SetError("Haptic: Unable to reset device: %s.", FFStrError(ret)); + goto open_err; + } + ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, + FFSFFC_SETACTUATORSON); + if (ret != FF_OK) { + SDL_SetError("Haptic: Unable to enable actuators: %s.", + FFStrError(ret)); + goto open_err; + } + + + /* Allocate effects memory. */ + haptic->effects = (struct haptic_effect *) + SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); + if (haptic->effects == NULL) { + SDL_OutOfMemory(); + goto open_err; + } + /* Clear the memory */ + SDL_memset(haptic->effects, 0, + sizeof(struct haptic_effect) * haptic->neffects); + + return 0; + + /* Error handling */ + open_err: + FFReleaseDevice(haptic->hwdata->device); + creat_err: + if (haptic->hwdata != NULL) { + free(haptic->hwdata); + haptic->hwdata = NULL; + } + return -1; + +} + + +/* + * Opens a haptic device for usage. + */ +int +SDL_SYS_HapticOpen(SDL_Haptic * haptic) +{ + return SDL_SYS_HapticOpenFromService(haptic, + SDL_hapticlist[haptic->index].dev); +} + + +/* + * Opens a haptic device from first mouse it finds for usage. + */ +int +SDL_SYS_HapticMouse(void) +{ + int i; + + for (i = 0; i < SDL_numhaptics; i++) { + if ((SDL_hapticlist[i].usagePage == kHIDPage_GenericDesktop) && + (SDL_hapticlist[i].usage == kHIDUsage_GD_Mouse)) + return i; + } + + return -1; +} + + +/* + * Checks to see if a joystick has haptic features. + */ +int +SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick) +{ + if (joystick->hwdata->ffservice != 0) + return SDL_TRUE; + return SDL_FALSE; +} + + +/* + * Checks to see if the haptic device and joystick and in reality the same. + */ +int +SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + if (IOObjectIsEqualTo((io_object_t) haptic->hwdata->device, + joystick->hwdata->ffservice)) + return 1; + return 0; +} + + +/* + * Opens a SDL_Haptic from a SDL_Joystick. + */ +int +SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + return SDL_SYS_HapticOpenFromService(haptic, joystick->hwdata->ffservice); +} + + +/* + * Closes the haptic device. + */ +void +SDL_SYS_HapticClose(SDL_Haptic * haptic) +{ + if (haptic->hwdata) { + + /* Free Effects. */ + SDL_free(haptic->effects); + haptic->effects = NULL; + haptic->neffects = 0; + + /* Clean up */ + FFReleaseDevice(haptic->hwdata->device); + + /* Free */ + SDL_free(haptic->hwdata); + haptic->hwdata = NULL; + } +} + + +/* + * Clean up after system specific haptic stuff + */ +void +SDL_SYS_HapticQuit(void) +{ + int i; + + for (i = 0; i < SDL_numhaptics; i++) { + /* Opened and not closed haptics are leaked, this is on purpose. + * Close your haptic devices after usage. */ + + /* Free the io_service_t */ + IOObjectRelease(SDL_hapticlist[i].dev); + } +} + + +/* + * Converts an SDL trigger button to an FFEFFECT trigger button. + */ +static DWORD +FFGetTriggerButton(Uint16 button) +{ + DWORD dwTriggerButton; + + dwTriggerButton = FFEB_NOTRIGGER; + + if (button != 0) { + dwTriggerButton = FFJOFS_BUTTON(button - 1); + } + + return dwTriggerButton; +} + + +/* + * Sets the direction. + */ +static int +SDL_SYS_SetDirection(FFEFFECT * effect, SDL_HapticDirection * dir, int naxes) +{ + LONG *rglDir; + + /* Handle no axes a part. */ + if (naxes == 0) { + effect->dwFlags |= FFEFF_SPHERICAL; /* Set as default. */ + effect->rglDirection = NULL; + return 0; + } + + /* Has axes. */ + rglDir = SDL_malloc(sizeof(LONG) * naxes); + if (rglDir == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_memset(rglDir, 0, sizeof(LONG) * naxes); + effect->rglDirection = rglDir; + + switch (dir->type) { + case SDL_HAPTIC_POLAR: + effect->dwFlags |= FFEFF_POLAR; + rglDir[0] = dir->dir[0]; + return 0; + case SDL_HAPTIC_CARTESIAN: + effect->dwFlags |= FFEFF_CARTESIAN; + rglDir[0] = dir->dir[0]; + if (naxes > 1) + rglDir[1] = dir->dir[1]; + if (naxes > 2) + rglDir[2] = dir->dir[2]; + return 0; + case SDL_HAPTIC_SPHERICAL: + effect->dwFlags |= FFEFF_SPHERICAL; + rglDir[0] = dir->dir[0]; + if (naxes > 1) + rglDir[1] = dir->dir[1]; + if (naxes > 2) + rglDir[2] = dir->dir[2]; + return 0; + + default: + SDL_SetError("Haptic: Unknown direction type."); + return -1; + } +} + + +/* Clamps and converts. */ +#define CCONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF) +/* Just converts. */ +#define CONVERT(x) (((x)*10000) / 0x7FFF) +/* + * Creates the FFEFFECT from a SDL_HapticEffect. + */ +static int +SDL_SYS_ToFFEFFECT(SDL_Haptic * haptic, FFEFFECT * dest, + SDL_HapticEffect * src) +{ + int i; + FFCONSTANTFORCE *constant; + FFPERIODIC *periodic; + FFCONDITION *condition; /* Actually an array of conditions - one per axis. */ + FFRAMPFORCE *ramp; + FFCUSTOMFORCE *custom; + FFENVELOPE *envelope; + SDL_HapticConstant *hap_constant; + SDL_HapticPeriodic *hap_periodic; + SDL_HapticCondition *hap_condition; + SDL_HapticRamp *hap_ramp; + SDL_HapticCustom *hap_custom; + DWORD *axes; + + /* Set global stuff. */ + SDL_memset(dest, 0, sizeof(FFEFFECT)); + dest->dwSize = sizeof(FFEFFECT); /* Set the structure size. */ + dest->dwSamplePeriod = 0; /* Not used by us. */ + dest->dwGain = 10000; /* Gain is set globally, not locally. */ + dest->dwFlags = FFEFF_OBJECTOFFSETS; /* Seems obligatory. */ + + /* Envelope. */ + envelope = SDL_malloc(sizeof(FFENVELOPE)); + if (envelope == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_memset(envelope, 0, sizeof(FFENVELOPE)); + dest->lpEnvelope = envelope; + envelope->dwSize = sizeof(FFENVELOPE); /* Always should be this. */ + + /* Axes. */ + dest->cAxes = haptic->naxes; + if (dest->cAxes > 0) { + axes = SDL_malloc(sizeof(DWORD) * dest->cAxes); + if (axes == NULL) { + SDL_OutOfMemory(); + return -1; + } + axes[0] = haptic->hwdata->axes[0]; /* Always at least one axis. */ + if (dest->cAxes > 1) { + axes[1] = haptic->hwdata->axes[1]; + } + if (dest->cAxes > 2) { + axes[2] = haptic->hwdata->axes[2]; + } + dest->rgdwAxes = axes; + } + + + /* The big type handling switch, even bigger then linux's version. */ + switch (src->type) { + case SDL_HAPTIC_CONSTANT: + hap_constant = &src->constant; + constant = SDL_malloc(sizeof(FFCONSTANTFORCE)); + if (constant == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_memset(constant, 0, sizeof(FFCONSTANTFORCE)); + + /* Specifics */ + constant->lMagnitude = CONVERT(hap_constant->level); + dest->cbTypeSpecificParams = sizeof(FFCONSTANTFORCE); + dest->lpvTypeSpecificParams = constant; + + /* Generics */ + dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = FFGetTriggerButton(hap_constant->button); + dest->dwTriggerRepeatInterval = hap_constant->interval; + dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes) + < 0) { + return -1; + } + + /* Envelope */ + if ((hap_constant->attack_length == 0) + && (hap_constant->fade_length == 0)) { + SDL_free(envelope); + dest->lpEnvelope = NULL; + } else { + envelope->dwAttackLevel = CCONVERT(hap_constant->attack_level); + envelope->dwAttackTime = hap_constant->attack_length * 1000; + envelope->dwFadeLevel = CCONVERT(hap_constant->fade_level); + envelope->dwFadeTime = hap_constant->fade_length * 1000; + } + + break; + + case SDL_HAPTIC_SINE: + case SDL_HAPTIC_SQUARE: + case SDL_HAPTIC_TRIANGLE: + case SDL_HAPTIC_SAWTOOTHUP: + case SDL_HAPTIC_SAWTOOTHDOWN: + hap_periodic = &src->periodic; + periodic = SDL_malloc(sizeof(FFPERIODIC)); + if (periodic == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_memset(periodic, 0, sizeof(FFPERIODIC)); + + /* Specifics */ + periodic->dwMagnitude = CONVERT(hap_periodic->magnitude); + periodic->lOffset = CONVERT(hap_periodic->offset); + periodic->dwPhase = hap_periodic->phase; + periodic->dwPeriod = hap_periodic->period * 1000; + dest->cbTypeSpecificParams = sizeof(FFPERIODIC); + dest->lpvTypeSpecificParams = periodic; + + /* Generics */ + dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = FFGetTriggerButton(hap_periodic->button); + dest->dwTriggerRepeatInterval = hap_periodic->interval; + dest->dwStartDelay = hap_periodic->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes) + < 0) { + return -1; + } + + /* Envelope */ + if ((hap_periodic->attack_length == 0) + && (hap_periodic->fade_length == 0)) { + SDL_free(envelope); + dest->lpEnvelope = NULL; + } else { + envelope->dwAttackLevel = CCONVERT(hap_periodic->attack_level); + envelope->dwAttackTime = hap_periodic->attack_length * 1000; + envelope->dwFadeLevel = CCONVERT(hap_periodic->fade_level); + envelope->dwFadeTime = hap_periodic->fade_length * 1000; + } + + break; + + case SDL_HAPTIC_SPRING: + case SDL_HAPTIC_DAMPER: + case SDL_HAPTIC_INERTIA: + case SDL_HAPTIC_FRICTION: + hap_condition = &src->condition; + condition = SDL_malloc(sizeof(FFCONDITION) * dest->cAxes); + if (condition == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_memset(condition, 0, sizeof(FFCONDITION)); + + /* Specifics */ + for (i = 0; i < dest->cAxes; i++) { + condition[i].lOffset = CONVERT(hap_condition->center[i]); + condition[i].lPositiveCoefficient = + CONVERT(hap_condition->right_coeff[i]); + condition[i].lNegativeCoefficient = + CONVERT(hap_condition->left_coeff[i]); + condition[i].dwPositiveSaturation = + CCONVERT(hap_condition->right_sat[i]); + condition[i].dwNegativeSaturation = + CCONVERT(hap_condition->left_sat[i]); + condition[i].lDeadBand = CCONVERT(hap_condition->deadband[i]); + } + dest->cbTypeSpecificParams = sizeof(FFCONDITION) * dest->cAxes; + dest->lpvTypeSpecificParams = condition; + + /* Generics */ + dest->dwDuration = hap_condition->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = FFGetTriggerButton(hap_condition->button); + dest->dwTriggerRepeatInterval = hap_condition->interval; + dest->dwStartDelay = hap_condition->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes) + < 0) { + return -1; + } + + /* Envelope - Not actually supported by most CONDITION implementations. */ + SDL_free(dest->lpEnvelope); + dest->lpEnvelope = NULL; + + break; + + case SDL_HAPTIC_RAMP: + hap_ramp = &src->ramp; + ramp = SDL_malloc(sizeof(FFRAMPFORCE)); + if (ramp == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_memset(ramp, 0, sizeof(FFRAMPFORCE)); + + /* Specifics */ + ramp->lStart = CONVERT(hap_ramp->start); + ramp->lEnd = CONVERT(hap_ramp->end); + dest->cbTypeSpecificParams = sizeof(FFRAMPFORCE); + dest->lpvTypeSpecificParams = ramp; + + /* Generics */ + dest->dwDuration = hap_ramp->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = FFGetTriggerButton(hap_ramp->button); + dest->dwTriggerRepeatInterval = hap_ramp->interval; + dest->dwStartDelay = hap_ramp->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) { + return -1; + } + + /* Envelope */ + if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) { + SDL_free(envelope); + dest->lpEnvelope = NULL; + } else { + envelope->dwAttackLevel = CCONVERT(hap_ramp->attack_level); + envelope->dwAttackTime = hap_ramp->attack_length * 1000; + envelope->dwFadeLevel = CCONVERT(hap_ramp->fade_level); + envelope->dwFadeTime = hap_ramp->fade_length * 1000; + } + + break; + + case SDL_HAPTIC_CUSTOM: + hap_custom = &src->custom; + custom = SDL_malloc(sizeof(FFCUSTOMFORCE)); + if (custom == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_memset(custom, 0, sizeof(FFCUSTOMFORCE)); + + /* Specifics */ + custom->cChannels = hap_custom->channels; + custom->dwSamplePeriod = hap_custom->period * 1000; + custom->cSamples = hap_custom->samples; + custom->rglForceData = + SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels); + for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) { /* Copy data. */ + custom->rglForceData[i] = CCONVERT(hap_custom->data[i]); + } + dest->cbTypeSpecificParams = sizeof(FFCUSTOMFORCE); + dest->lpvTypeSpecificParams = custom; + + /* Generics */ + dest->dwDuration = hap_custom->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = FFGetTriggerButton(hap_custom->button); + dest->dwTriggerRepeatInterval = hap_custom->interval; + dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) < + 0) { + return -1; + } + + /* Envelope */ + if ((hap_custom->attack_length == 0) + && (hap_custom->fade_length == 0)) { + SDL_free(envelope); + dest->lpEnvelope = NULL; + } else { + envelope->dwAttackLevel = CCONVERT(hap_custom->attack_level); + envelope->dwAttackTime = hap_custom->attack_length * 1000; + envelope->dwFadeLevel = CCONVERT(hap_custom->fade_level); + envelope->dwFadeTime = hap_custom->fade_length * 1000; + } + + break; + + + default: + SDL_SetError("Haptic: Unknown effect type."); + return -1; + } + + return 0; +} + + +/* + * Frees an FFEFFECT allocated by SDL_SYS_ToFFEFFECT. + */ +static void +SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type) +{ + FFCUSTOMFORCE *custom; + + if (effect->lpEnvelope != NULL) { + SDL_free(effect->lpEnvelope); + effect->lpEnvelope = NULL; + } + if (effect->rgdwAxes != NULL) { + SDL_free(effect->rgdwAxes); + effect->rgdwAxes = NULL; + } + if (effect->lpvTypeSpecificParams != NULL) { + if (type == SDL_HAPTIC_CUSTOM) { /* Must free the custom data. */ + custom = (FFCUSTOMFORCE *) effect->lpvTypeSpecificParams; + SDL_free(custom->rglForceData); + custom->rglForceData = NULL; + } + SDL_free(effect->lpvTypeSpecificParams); + effect->lpvTypeSpecificParams = NULL; + } + if (effect->rglDirection != NULL) { + SDL_free(effect->rglDirection); + effect->rglDirection = NULL; + } +} + + +/* + * Gets the effect type from the generic SDL haptic effect wrapper. + */ +CFUUIDRef +SDL_SYS_HapticEffectType(Uint16 type) +{ + switch (type) { + case SDL_HAPTIC_CONSTANT: + return kFFEffectType_ConstantForce_ID; + + case SDL_HAPTIC_RAMP: + return kFFEffectType_RampForce_ID; + + case SDL_HAPTIC_SQUARE: + return kFFEffectType_Square_ID; + + case SDL_HAPTIC_SINE: + return kFFEffectType_Sine_ID; + + case SDL_HAPTIC_TRIANGLE: + return kFFEffectType_Triangle_ID; + + case SDL_HAPTIC_SAWTOOTHUP: + return kFFEffectType_SawtoothUp_ID; + + case SDL_HAPTIC_SAWTOOTHDOWN: + return kFFEffectType_SawtoothDown_ID; + + case SDL_HAPTIC_SPRING: + return kFFEffectType_Spring_ID; + + case SDL_HAPTIC_DAMPER: + return kFFEffectType_Damper_ID; + + case SDL_HAPTIC_INERTIA: + return kFFEffectType_Inertia_ID; + + case SDL_HAPTIC_FRICTION: + return kFFEffectType_Friction_ID; + + case SDL_HAPTIC_CUSTOM: + return kFFEffectType_CustomForce_ID; + + default: + SDL_SetError("Haptic: Unknown effect type."); + return NULL; + } +} + + +/* + * Creates a new haptic effect. + */ +int +SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + SDL_HapticEffect * base) +{ + HRESULT ret; + CFUUIDRef type; + + /* Alloc the effect. */ + effect->hweffect = (struct haptic_hweffect *) + SDL_malloc(sizeof(struct haptic_hweffect)); + if (effect->hweffect == NULL) { + SDL_OutOfMemory(); + goto err_hweffect; + } + + /* Get the type. */ + type = SDL_SYS_HapticEffectType(base->type); + if (type == NULL) { + goto err_hweffect; + } + + /* Get the effect. */ + if (SDL_SYS_ToFFEFFECT(haptic, &effect->hweffect->effect, base) < 0) { + goto err_effectdone; + } + + /* Create the actual effect. */ + ret = FFDeviceCreateEffect(haptic->hwdata->device, type, + &effect->hweffect->effect, + &effect->hweffect->ref); + if (ret != FF_OK) { + SDL_SetError("Haptic: Unable to create effect: %s.", FFStrError(ret)); + goto err_effectdone; + } + + return 0; + + err_effectdone: + SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, base->type); + err_hweffect: + if (effect->hweffect != NULL) { + SDL_free(effect->hweffect); + effect->hweffect = NULL; + } + return -1; +} + + +/* + * Updates an effect. + */ +int +SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, + SDL_HapticEffect * data) +{ + HRESULT ret; + FFEffectParameterFlag flags; + FFEFFECT temp; + + /* Get the effect. */ + SDL_memset(&temp, 0, sizeof(FFEFFECT)); + if (SDL_SYS_ToFFEFFECT(haptic, &temp, data) < 0) { + goto err_update; + } + + /* Set the flags. Might be worthwhile to diff temp with loaded effect and + * only change those parameters. */ + flags = FFEP_DIRECTION | + FFEP_DURATION | + FFEP_ENVELOPE | + FFEP_STARTDELAY | + FFEP_TRIGGERBUTTON | + FFEP_TRIGGERREPEATINTERVAL | FFEP_TYPESPECIFICPARAMS; + + /* Create the actual effect. */ + ret = FFEffectSetParameters(effect->hweffect->ref, &temp, flags); + if (ret != FF_OK) { + SDL_SetError("Haptic: Unable to update effect: %s.", FFStrError(ret)); + goto err_update; + } + + /* Copy it over. */ + SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, data->type); + SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(FFEFFECT)); + + return 0; + + err_update: + SDL_SYS_HapticFreeFFEFFECT(&temp, data->type); + return -1; +} + + +/* + * Runs an effect. + */ +int +SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + Uint32 iterations) +{ + HRESULT ret; + Uint32 iter; + + /* Check if it's infinite. */ + if (iterations == SDL_HAPTIC_INFINITY) { + iter = FF_INFINITE; + } else + iter = iterations; + + /* Run the effect. */ + ret = FFEffectStart(effect->hweffect->ref, iter, 0); + if (ret != FF_OK) { + SDL_SetError("Haptic: Unable to run the effect: %s.", + FFStrError(ret)); + return -1; + } + + return 0; +} + + +/* + * Stops an effect. + */ +int +SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + HRESULT ret; + + ret = FFEffectStop(effect->hweffect->ref); + if (ret != FF_OK) { + SDL_SetError("Haptic: Unable to stop the effect: %s.", + FFStrError(ret)); + return -1; + } + + return 0; +} + + +/* + * Frees the effect. + */ +void +SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + HRESULT ret; + + ret = + FFDeviceReleaseEffect(haptic->hwdata->device, effect->hweffect->ref); + if (ret != FF_OK) { + SDL_SetError("Haptic: Error removing the effect from the device: %s.", + FFStrError(ret)); + } + SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, + effect->effect.type); + SDL_free(effect->hweffect); + effect->hweffect = NULL; +} + + +/* + * Gets the status of a haptic effect. + */ +int +SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, + struct haptic_effect *effect) +{ + HRESULT ret; + FFEffectStatusFlag status; + + ret = FFEffectGetEffectStatus(effect->hweffect->ref, &status); + if (ret != FF_OK) { + SDL_SetError("Haptic: Unable to get effect status: %s.", + FFStrError(ret)); + return -1; + } + + if (status == 0) + return SDL_FALSE; + return SDL_TRUE; /* Assume it's playing or emulated. */ +} + + +/* + * Sets the gain. + */ +int +SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + HRESULT ret; + Uint32 val; + + val = gain * 100; /* Mac OS X uses 0 to 10,000 */ + ret = + FFDeviceSetForceFeedbackProperty(haptic->hwdata->device, + FFPROP_FFGAIN, &val); + if (ret != FF_OK) { + SDL_SetError("Haptic: Error setting gain: %s.", FFStrError(ret)); + return -1; + } + + return 0; +} + + +/* + * Sets the autocentering. + */ +int +SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + HRESULT ret; + Uint32 val; + + /* Mac OS X only has 0 (off) and 1 (on) */ + if (autocenter == 0) + val = 0; + else + val = 1; + + ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device, + FFPROP_AUTOCENTER, &val); + if (ret != FF_OK) { + SDL_SetError("Haptic: Error setting autocenter: %s.", + FFStrError(ret)); + return -1; + } + + return 0; +} + + +/* + * Pauses the device. + */ +int +SDL_SYS_HapticPause(SDL_Haptic * haptic) +{ + HRESULT ret; + + ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, + FFSFFC_PAUSE); + if (ret != FF_OK) { + SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret)); + return -1; + } + + return 0; +} + + +/* + * Unpauses the device. + */ +int +SDL_SYS_HapticUnpause(SDL_Haptic * haptic) +{ + HRESULT ret; + + ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, + FFSFFC_CONTINUE); + if (ret != FF_OK) { + SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret)); + return -1; + } + + return 0; +} + + +/* + * Stops all currently playing effects. + */ +int +SDL_SYS_HapticStopAll(SDL_Haptic * haptic) +{ + HRESULT ret; + + ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, + FFSFFC_STOPALL); + if (ret != FF_OK) { + SDL_SetError("Haptic: Error stopping device: %s.", FFStrError(ret)); + return -1; + } + + return 0; +} + + +#endif /* SDL_HAPTIC_IOKIT */ diff --git a/src/haptic/dummy/SDL_syshaptic.c b/src/haptic/dummy/SDL_syshaptic.c new file mode 100644 index 000000000..36eebe1d7 --- /dev/null +++ b/src/haptic/dummy/SDL_syshaptic.c @@ -0,0 +1,194 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 2008 Edgar Simo + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#if defined(SDL_HAPTIC_DUMMY) || defined(SDL_HAPTIC_DISABLED) + +#include "SDL_haptic.h" +#include "../SDL_syshaptic.h" + + +static int +SDL_SYS_LogicError(void) +{ + SDL_SetError("Logic error: No haptic devices available.");; +} + + +int +SDL_SYS_HapticInit(void) +{ + return 0; +} + + +const char * +SDL_SYS_HapticName(int index) +{ + SDL_SYS_LogicError(); + return NULL; +} + + +int +SDL_SYS_HapticOpen(SDL_Haptic * haptic) +{ + SDL_SYS_LogicError(); + return -1; +} + + +int +SDL_SYS_HapticMouse(void) +{ + return -1; +} + + +int +SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick) +{ + return 0; +} + + +int +SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + SDL_SYS_LogicError(); + return -1; +} + + +int +SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + return 0; +} + + +void +SDL_SYS_HapticClose(SDL_Haptic * haptic) +{ + return; +} + + +void +SDL_SYS_HapticQuit(void) +{ + return; +} + + +int +SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, SDL_HapticEffect * base) +{ + SDL_SYS_LogicError(); + return -1; +} + + +int +SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, + SDL_HapticEffect * data) +{ + SDL_SYS_LogicError(); + return -1; +} + + +int +SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + Uint32 iterations) +{ + SDL_SYS_LogicError(); + return -1; +} + + +int +SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + SDL_SYS_LogicError(); + return -1; +} + + +void +SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + SDL_SYS_LogicError(); + return; +} + + +int +SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, + struct haptic_effect *effect) +{ + SDL_SYS_LogicError(); + return -1; +} + + +int +SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + SDL_SYS_LogicError(); + return -1; +} + + +int +SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + SDL_SYS_LogicError(); + return -1; +} + +int +SDL_SYS_HapticPause(SDL_Haptic * haptic) +{ + SDL_SYS_LogicError(); + return -1; +} + +int +SDL_SYS_HapticUnpause(SDL_Haptic * haptic) +{ + SDL_SYS_LogicError(); + return -1; +} + +int +SDL_SYS_HapticStopAll(SDL_Haptic * haptic) +{ + SDL_SYS_LogicError(); + return -1; +} + + + +#endif /* SDL_HAPTIC_DUMMY || SDL_HAPTIC_DISABLED */ diff --git a/src/haptic/linux/SDL_syshaptic.c b/src/haptic/linux/SDL_syshaptic.c new file mode 100644 index 000000000..dc6534106 --- /dev/null +++ b/src/haptic/linux/SDL_syshaptic.c @@ -0,0 +1,956 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 2008 Edgar Simo + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifdef SDL_HAPTIC_LINUX + +#include "SDL_haptic.h" +#include "../SDL_syshaptic.h" +#include "SDL_joystick.h" +#include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */ +#include "../../joystick/linux/SDL_sysjoystick_c.h" /* For joystick hwdata */ + +#include /* close */ +#include /* Force feedback linux stuff. */ +#include /* O_RDWR */ +#include /* INT_MAX */ +#include /* errno, strerror */ +#include /* atan2 */ + +/* Just in case. */ +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + + +#define MAX_HAPTICS 32 /* It's doubtful someone has more then 32 evdev */ + + +/* + * List of available haptic devices. + */ +static struct +{ + char *fname; /* Dev path name (like /dev/input/event1) */ + SDL_Haptic *haptic; /* Assosciated haptic. */ +} SDL_hapticlist[MAX_HAPTICS]; + + +/* + * Haptic system hardware data. + */ +struct haptic_hwdata +{ + int fd; /* File descriptor of the device. */ + char *fname; /* Points to the name in SDL_hapticlist. */ +}; + + +/* + * Haptic system effect data. + */ +struct haptic_hweffect +{ + struct ff_effect effect; /* The linux kernel effect structure. */ +}; + + + +#define test_bit(nr, addr) \ + (((1UL << ((nr) & 31)) & (((const unsigned int *) addr)[(nr) >> 5])) != 0) +#define EV_TEST(ev,f) \ + if (test_bit((ev), features)) ret |= (f); +/* + * Test whether a device has haptic properties. + * Returns available properties or 0 if there are none. + */ +static int +EV_IsHaptic(int fd) +{ + unsigned int ret; + unsigned long features[1 + FF_MAX / sizeof(unsigned long)]; + + /* Ask device for what it has. */ + ret = 0; + if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(features)), features) < 0) { + SDL_SetError("Haptic: Unable to get device's features: %s", + strerror(errno)); + return -1; + } + + /* Convert supported features to SDL_HAPTIC platform-neutral features. */ + EV_TEST(FF_CONSTANT, SDL_HAPTIC_CONSTANT); + EV_TEST(FF_SINE, SDL_HAPTIC_SINE); + EV_TEST(FF_SQUARE, SDL_HAPTIC_SQUARE); + EV_TEST(FF_TRIANGLE, SDL_HAPTIC_TRIANGLE); + EV_TEST(FF_SAW_UP, SDL_HAPTIC_SAWTOOTHUP); + EV_TEST(FF_SAW_DOWN, SDL_HAPTIC_SAWTOOTHDOWN); + EV_TEST(FF_RAMP, SDL_HAPTIC_RAMP); + EV_TEST(FF_SPRING, SDL_HAPTIC_SPRING); + EV_TEST(FF_FRICTION, SDL_HAPTIC_FRICTION); + EV_TEST(FF_DAMPER, SDL_HAPTIC_DAMPER); + EV_TEST(FF_INERTIA, SDL_HAPTIC_INERTIA); + EV_TEST(FF_CUSTOM, SDL_HAPTIC_CUSTOM); + EV_TEST(FF_GAIN, SDL_HAPTIC_GAIN); + EV_TEST(FF_AUTOCENTER, SDL_HAPTIC_AUTOCENTER); + + /* Return what it supports. */ + return ret; +} + + +/* + * Tests whether a device is a mouse or not. + */ +static int +EV_IsMouse(int fd) +{ + unsigned long argp[40]; + + /* Ask for supported features. */ + if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(argp)), argp) < 0) { + return -1; + } + + /* Currently we only test for BTN_MOUSE which can give fake positives. */ + if (test_bit(BTN_MOUSE, argp) != 0) { + return 1; + } + + return 0; +} + +/* + * Initializes the haptic subsystem by finding available devices. + */ +int +SDL_SYS_HapticInit(void) +{ + const char joydev_pattern[] = "/dev/input/event%d"; + dev_t dev_nums[MAX_HAPTICS]; + char path[PATH_MAX]; + struct stat sb; + int fd; + int i, j, k; + int duplicate; + int numhaptics; + + numhaptics = 0; + + /* + * Limit amount of checks to MAX_HAPTICS since we may or may not have + * permission to some or all devices. + */ + i = 0; + for (j = 0; j < MAX_HAPTICS; ++j) { + + snprintf(path, PATH_MAX, joydev_pattern, i++); + + /* check to see if file exists */ + if (stat(path, &sb) != 0) + break; + + /* check for duplicates */ + duplicate = 0; + for (k = 0; (k < numhaptics) && !duplicate; ++k) { + if (sb.st_rdev == dev_nums[k]) { + duplicate = 1; + } + } + if (duplicate) { + continue; + } + + /* try to open */ + fd = open(path, O_RDWR, 0); + if (fd < 0) + continue; + +#ifdef DEBUG_INPUT_EVENTS + printf("Checking %s\n", path); +#endif + + /* see if it works */ + if (EV_IsHaptic(fd) > 0) { + SDL_hapticlist[numhaptics].fname = SDL_strdup(path); + SDL_hapticlist[numhaptics].haptic = NULL; + dev_nums[numhaptics] = sb.st_rdev; + ++numhaptics; + } + close(fd); + } + + return numhaptics; +} + + +/* + * Gets the name from a file descriptor. + */ +static const char * +SDL_SYS_HapticNameFromFD(int fd) +{ + static char namebuf[128]; + + /* We use the evdev name ioctl. */ + if (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) <= 0) { + return NULL; + } + + return namebuf; +} + + +/* + * Return the name of a haptic device, does not need to be opened. + */ +const char * +SDL_SYS_HapticName(int index) +{ + int fd; + const char *name; + + /* Open the haptic device. */ + name = NULL; + fd = open(SDL_hapticlist[index].fname, O_RDONLY, 0); + + if (fd >= 0) { + + name = SDL_SYS_HapticNameFromFD(fd); + if (name == NULL) { + /* No name found, return device character device */ + name = SDL_hapticlist[index].fname; + } + } + close(fd); + + return name; +} + + +/* + * Opens the haptic device from the file descriptor. + */ +static int +SDL_SYS_HapticOpenFromFD(SDL_Haptic * haptic, int fd) +{ + const char *name; + + /* Allocate the hwdata */ + haptic->hwdata = (struct haptic_hwdata *) + SDL_malloc(sizeof(*haptic->hwdata)); + if (haptic->hwdata == NULL) { + SDL_OutOfMemory(); + goto open_err; + } + SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); + + /* Set the data. */ + haptic->hwdata->fd = fd; + haptic->supported = EV_IsHaptic(fd); + haptic->naxes = 2; /* Hardcoded for now, not sure if it's possible to find out. */ + + /* Set the effects */ + if (ioctl(fd, EVIOCGEFFECTS, &haptic->neffects) < 0) { + SDL_SetError("Haptic: Unable to query device memory: %s", + strerror(errno)); + goto open_err; + } + haptic->nplaying = haptic->neffects; /* Linux makes no distinction. */ + haptic->effects = (struct haptic_effect *) + SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); + if (haptic->effects == NULL) { + SDL_OutOfMemory(); + goto open_err; + } + /* Clear the memory */ + SDL_memset(haptic->effects, 0, + sizeof(struct haptic_effect) * haptic->neffects); + + return 0; + + /* Error handling */ + open_err: + close(fd); + if (haptic->hwdata != NULL) { + free(haptic->hwdata); + haptic->hwdata = NULL; + } + return -1; +} + + +/* + * Opens a haptic device for usage. + */ +int +SDL_SYS_HapticOpen(SDL_Haptic * haptic) +{ + int fd; + int ret; + + /* Open the character device */ + fd = open(SDL_hapticlist[haptic->index].fname, O_RDWR, 0); + if (fd < 0) { + SDL_SetError("Haptic: Unable to open %s: %s", + SDL_hapticlist[haptic->index], strerror(errno)); + return -1; + } + + /* Try to create the haptic. */ + ret = SDL_SYS_HapticOpenFromFD(haptic, fd); /* Already closes on error. */ + if (ret < 0) { + return -1; + } + + /* Set the fname. */ + haptic->hwdata->fname = SDL_hapticlist[haptic->index].fname; + return 0; +} + + +/* + * Opens a haptic device from first mouse it finds for usage. + */ +int +SDL_SYS_HapticMouse(void) +{ + int fd; + int i; + + for (i = 0; i < SDL_numhaptics; i++) { + + /* Open the device. */ + fd = open(SDL_hapticlist[i].fname, O_RDWR, 0); + if (fd < 0) { + SDL_SetError("Haptic: Unable to open %s: %s", + SDL_hapticlist[i], strerror(errno)); + return -1; + } + + /* Is it a mouse? */ + if (EV_IsMouse(fd)) { + close(fd); + return i; + } + + close(fd); + } + + return -1; +} + + +/* + * Checks to see if a joystick has haptic features. + */ +int +SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick) +{ + return EV_IsHaptic(joystick->hwdata->fd); +} + + +/* + * Checks to see if the haptic device and joystick and in reality the same. + */ +int +SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + /* We are assuming linux is using evdev which should trump the old + * joystick methods. */ + if (SDL_strcmp(joystick->hwdata->fname, haptic->hwdata->fname) == 0) { + return 1; + } + return 0; +} + + +/* + * Opens a SDL_Haptic from a SDL_Joystick. + */ +int +SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + int i; + int fd; + int ret; + + /* Find the joystick in the haptic list. */ + for (i = 0; i < MAX_HAPTICS; i++) { + if (SDL_hapticlist[i].fname != NULL) { + if (SDL_strcmp(SDL_hapticlist[i].fname, joystick->hwdata->fname) + == 0) { + haptic->index = i; + } + } + } + + fd = open(joystick->hwdata->fname, O_RDWR, 0); + ret = SDL_SYS_HapticOpenFromFD(haptic, fd); /* Already closes on error. */ + if (ret < 0) { + return -1; + } + + haptic->hwdata->fname = SDL_hapticlist[haptic->index].fname; + return 0; +} + + +/* + * Closes the haptic device. + */ +void +SDL_SYS_HapticClose(SDL_Haptic * haptic) +{ + if (haptic->hwdata) { + + /* Free effects. */ + SDL_free(haptic->effects); + haptic->effects = NULL; + haptic->neffects = 0; + + /* Clean up */ + close(haptic->hwdata->fd); + + /* Free */ + SDL_free(haptic->hwdata); + haptic->hwdata = NULL; + } + + /* Clear the rest. */ + SDL_memset(haptic, 0, sizeof(SDL_Haptic)); +} + + +/* + * Clean up after system specific haptic stuff + */ +void +SDL_SYS_HapticQuit(void) +{ + int i; + + for (i = 0; SDL_hapticlist[i].fname != NULL; i++) { + /* Opened and not closed haptics are leaked, this is on purpose. + * Close your haptic devices after usage. */ + + SDL_free(SDL_hapticlist[i].fname); + } + SDL_hapticlist[0].fname = NULL; +} + + +/* + * Converts an SDL button to a ff_trigger button. + */ +static Uint16 +SDL_SYS_ToButton(Uint16 button) +{ + Uint16 ff_button; + + ff_button = 0; + + /* + * Not sure what the proper syntax is because this actually isn't implemented + * in the current kernel from what I've seen (2.6.26). + */ + if (button != 0) { + ff_button = BTN_GAMEPAD + button - 1; + } + + return ff_button; +} + + +/* + * Returns the ff_effect usable direction from a SDL_HapticDirection. + */ +static Uint16 +SDL_SYS_ToDirection(SDL_HapticDirection * dir) +{ + Uint32 tmp; + float f; /* Ideally we'd use fixed point math instead of floats... */ + + switch (dir->type) { + case SDL_HAPTIC_POLAR: + /* Linux directions are inverted. */ + tmp = (((18000 + dir->dir[0]) % 36000) * 0xFFFF) / 36000; + return (Uint16) tmp; + + case SDL_HAPTIC_CARTESIAN: + /* We must invert "x" and "y" to go clockwise. */ + f = atan2(dir->dir[0], dir->dir[1]); + tmp = (int) (f * 18000. / M_PI) % 36000; + tmp = (tmp * 0xFFFF) / 36000; + return (Uint16) tmp; + + case SDL_HAPTIC_SPHERICAL: + tmp = (36000 - dir->dir[0]) + 27000; /* Convert to polars */ + tmp = (((18000 + tmp) % 36000) * 0xFFFF) / 36000; + return (Uint16) tmp; + + default: + SDL_SetError("Haptic: Unsupported direction type."); + return (Uint16) - 1; + } + + return 0; +} + + +#define CLAMP(x) (((x) > 32767) ? 32767 : x) +/* + * Initializes the linux effect struct from a haptic_effect. + * Values above 32767 (for unsigned) are unspecified so we must clamp. + */ +static int +SDL_SYS_ToFFEffect(struct ff_effect *dest, SDL_HapticEffect * src) +{ + Uint32 tmp; + SDL_HapticConstant *constant; + SDL_HapticPeriodic *periodic; + SDL_HapticCondition *condition; + SDL_HapticRamp *ramp; + + /* Clear up */ + SDL_memset(dest, 0, sizeof(struct ff_effect)); + + switch (src->type) { + case SDL_HAPTIC_CONSTANT: + constant = &src->constant; + + /* Header */ + dest->type = FF_CONSTANT; + dest->direction = SDL_SYS_ToDirection(&constant->direction); + if (dest->direction == (Uint16) - 1) + return -1; + + /* Replay */ + dest->replay.length = (constant->length == SDL_HAPTIC_INFINITY) ? + 0 : CLAMP(constant->length); + dest->replay.delay = CLAMP(constant->delay); + + /* Trigger */ + dest->trigger.button = SDL_SYS_ToButton(constant->button); + dest->trigger.interval = CLAMP(constant->interval); + + /* Constant */ + dest->u.constant.level = constant->level; + + /* Envelope */ + dest->u.constant.envelope.attack_length = + CLAMP(constant->attack_length); + dest->u.constant.envelope.attack_level = + CLAMP(constant->attack_level); + dest->u.constant.envelope.fade_length = CLAMP(constant->fade_length); + dest->u.constant.envelope.fade_level = CLAMP(constant->fade_level); + + break; + + case SDL_HAPTIC_SINE: + case SDL_HAPTIC_SQUARE: + case SDL_HAPTIC_TRIANGLE: + case SDL_HAPTIC_SAWTOOTHUP: + case SDL_HAPTIC_SAWTOOTHDOWN: + periodic = &src->periodic; + + /* Header */ + dest->type = FF_PERIODIC; + dest->direction = SDL_SYS_ToDirection(&periodic->direction); + if (dest->direction == (Uint16) - 1) + return -1; + + /* Replay */ + dest->replay.length = (periodic->length == SDL_HAPTIC_INFINITY) ? + 0 : CLAMP(periodic->length); + dest->replay.delay = CLAMP(periodic->delay); + + /* Trigger */ + dest->trigger.button = SDL_SYS_ToButton(periodic->button); + dest->trigger.interval = CLAMP(periodic->interval); + + /* Periodic */ + if (periodic->type == SDL_HAPTIC_SINE) + dest->u.periodic.waveform = FF_SINE; + else if (periodic->type == SDL_HAPTIC_SQUARE) + dest->u.periodic.waveform = FF_SQUARE; + else if (periodic->type == SDL_HAPTIC_TRIANGLE) + dest->u.periodic.waveform = FF_TRIANGLE; + else if (periodic->type == SDL_HAPTIC_SAWTOOTHUP) + dest->u.periodic.waveform = FF_SAW_UP; + else if (periodic->type == SDL_HAPTIC_SAWTOOTHDOWN) + dest->u.periodic.waveform = FF_SAW_DOWN; + dest->u.periodic.period = CLAMP(periodic->period); + dest->u.periodic.magnitude = periodic->magnitude; + dest->u.periodic.offset = periodic->offset; + /* Phase is calculated based of offset from period and then clamped. */ + tmp = ((periodic->phase % 36000) * dest->u.periodic.period) / 36000; + dest->u.periodic.phase = CLAMP(tmp); + + /* Envelope */ + dest->u.periodic.envelope.attack_length = + CLAMP(periodic->attack_length); + dest->u.periodic.envelope.attack_level = + CLAMP(periodic->attack_level); + dest->u.periodic.envelope.fade_length = CLAMP(periodic->fade_length); + dest->u.periodic.envelope.fade_level = CLAMP(periodic->fade_level); + + break; + + case SDL_HAPTIC_SPRING: + case SDL_HAPTIC_DAMPER: + case SDL_HAPTIC_INERTIA: + case SDL_HAPTIC_FRICTION: + condition = &src->condition; + + /* Header */ + if (condition->type == SDL_HAPTIC_SPRING) + dest->type = FF_SPRING; + else if (condition->type == SDL_HAPTIC_DAMPER) + dest->type = FF_DAMPER; + else if (condition->type == SDL_HAPTIC_INERTIA) + dest->type = FF_INERTIA; + else if (condition->type == SDL_HAPTIC_FRICTION) + dest->type = FF_FRICTION; + dest->direction = 0; /* Handled by the condition-specifics. */ + + /* Replay */ + dest->replay.length = (condition->length == SDL_HAPTIC_INFINITY) ? + 0 : CLAMP(condition->length); + dest->replay.delay = CLAMP(condition->delay); + + /* Trigger */ + dest->trigger.button = SDL_SYS_ToButton(condition->button); + dest->trigger.interval = CLAMP(condition->interval); + + /* Condition */ + /* X axis */ + dest->u.condition[0].right_saturation = + CLAMP(condition->right_sat[0]); + dest->u.condition[0].left_saturation = CLAMP(condition->left_sat[0]); + dest->u.condition[0].right_coeff = condition->right_coeff[0]; + dest->u.condition[0].left_coeff = condition->left_coeff[0]; + dest->u.condition[0].deadband = CLAMP(condition->deadband[0]); + dest->u.condition[0].center = condition->center[0]; + /* Y axis */ + dest->u.condition[1].right_saturation = + CLAMP(condition->right_sat[1]); + dest->u.condition[1].left_saturation = CLAMP(condition->left_sat[1]); + dest->u.condition[1].right_coeff = condition->right_coeff[1]; + dest->u.condition[1].left_coeff = condition->left_coeff[1]; + dest->u.condition[1].deadband = CLAMP(condition->deadband[1]); + dest->u.condition[1].center = condition->center[1]; + + /* + * There is no envelope in the linux force feedback api for conditions. + */ + + break; + + case SDL_HAPTIC_RAMP: + ramp = &src->ramp; + + /* Header */ + dest->type = FF_RAMP; + dest->direction = SDL_SYS_ToDirection(&ramp->direction); + if (dest->direction == (Uint16) - 1) + return -1; + + /* Replay */ + dest->replay.length = (ramp->length == SDL_HAPTIC_INFINITY) ? + 0 : CLAMP(ramp->length); + dest->replay.delay = CLAMP(ramp->delay); + + /* Trigger */ + dest->trigger.button = SDL_SYS_ToButton(ramp->button); + dest->trigger.interval = CLAMP(ramp->interval); + + /* Ramp */ + dest->u.ramp.start_level = ramp->start; + dest->u.ramp.end_level = ramp->end; + + /* Envelope */ + dest->u.ramp.envelope.attack_length = CLAMP(ramp->attack_length); + dest->u.ramp.envelope.attack_level = CLAMP(ramp->attack_level); + dest->u.ramp.envelope.fade_length = CLAMP(ramp->fade_length); + dest->u.ramp.envelope.fade_level = CLAMP(ramp->fade_level); + + break; + + + default: + SDL_SetError("Haptic: Unknown effect type."); + return -1; + } + + return 0; +} + + +/* + * Creates a new haptic effect. + */ +int +SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + SDL_HapticEffect * base) +{ + struct ff_effect *linux_effect; + + /* Allocate the hardware effect */ + effect->hweffect = (struct haptic_hweffect *) + SDL_malloc(sizeof(struct haptic_hweffect)); + if (effect->hweffect == NULL) { + SDL_OutOfMemory(); + return -1; + } + + /* Prepare the ff_effect */ + linux_effect = &effect->hweffect->effect; + if (SDL_SYS_ToFFEffect(linux_effect, base) != 0) { + goto new_effect_err; + } + linux_effect->id = -1; /* Have the kernel give it an id */ + + /* Upload the effect */ + if (ioctl(haptic->hwdata->fd, EVIOCSFF, linux_effect) < 0) { + SDL_SetError("Haptic: Error uploading effect to the device: %s", + strerror(errno)); + goto new_effect_err; + } + + return 0; + + new_effect_err: + free(effect->hweffect); + effect->hweffect = NULL; + return -1; +} + + +/* + * Updates an effect. + * + * Note: Dynamically updating the direction can in some cases force + * the effect to restart and run once. + */ +int +SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, + SDL_HapticEffect * data) +{ + struct ff_effect linux_effect; + + /* Create the new effect */ + if (SDL_SYS_ToFFEffect(&linux_effect, data) != 0) { + return -1; + } + linux_effect.id = effect->hweffect->effect.id; + + /* See if it can be uploaded. */ + if (ioctl(haptic->hwdata->fd, EVIOCSFF, &linux_effect) < 0) { + SDL_SetError("Haptic: Error updating the effect: %s", + strerror(errno)); + return -1; + } + + /* Copy the new effect into memory. */ + SDL_memcpy(&effect->hweffect->effect, &linux_effect, + sizeof(struct ff_effect)); + + return effect->hweffect->effect.id; +} + + +/* + * Runs an effect. + */ +int +SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + Uint32 iterations) +{ + struct input_event run; + + /* Prepare to run the effect */ + run.type = EV_FF; + run.code = effect->hweffect->effect.id; + /* We don't actually have infinity here, so we just do INT_MAX which is pretty damn close. */ + run.value = (iterations > INT_MAX) ? INT_MAX : iterations; + + if (write(haptic->hwdata->fd, (const void *) &run, sizeof(run)) < 0) { + SDL_SetError("Haptic: Unable to run the effect: %s", strerror(errno)); + return -1; + } + + return 0; +} + + +/* + * Stops an effect. + */ +int +SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + struct input_event stop; + + stop.type = EV_FF; + stop.code = effect->hweffect->effect.id; + stop.value = 0; + + if (write(haptic->hwdata->fd, (const void *) &stop, sizeof(stop)) < 0) { + SDL_SetError("Haptic: Unable to stop the effect: %s", + strerror(errno)); + return -1; + } + + return 0; +} + + +/* + * Frees the effect. + */ +void +SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + if (ioctl(haptic->hwdata->fd, EVIOCRMFF, effect->hweffect->effect.id) < 0) { + SDL_SetError("Haptic: Error removing the effect from the device: %s", + strerror(errno)); + } + SDL_free(effect->hweffect); + effect->hweffect = NULL; +} + + +/* + * Gets the status of a haptic effect. + */ +int +SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, + struct haptic_effect *effect) +{ +#if 0 /* Not supported atm. */ + struct input_event ie; + + ie.type = EV_FF; + ie.type = EV_FF_STATUS; + ie.code = effect->hweffect->effect.id; + + if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) { + SDL_SetError("Haptic: Error getting device status."); + return -1; + } + + return 0; +#endif + + return -1; +} + + +/* + * Sets the gain. + */ +int +SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + struct input_event ie; + + ie.type = EV_FF; + ie.code = FF_GAIN; + ie.value = (0xFFFFUL * gain) / 100; + + if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) { + SDL_SetError("Haptic: Error setting gain: %s", strerror(errno)); + return -1; + } + + return 0; +} + + +/* + * Sets the autocentering. + */ +int +SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + struct input_event ie; + + ie.type = EV_FF; + ie.code = FF_AUTOCENTER; + ie.value = (0xFFFFUL * autocenter) / 100; + + if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) { + SDL_SetError("Haptic: Error setting autocenter: %s", strerror(errno)); + return -1; + } + + return 0; +} + + +/* + * Pausing is not supported atm by linux. + */ +int +SDL_SYS_HapticPause(SDL_Haptic * haptic) +{ + return -1; +} + + +/* + * Unpausing is not supported atm by linux. + */ +int +SDL_SYS_HapticUnpause(SDL_Haptic * haptic) +{ + return -1; +} + + +/* + * Stops all the currently playing effects. + */ +int +SDL_SYS_HapticStopAll(SDL_Haptic * haptic) +{ + int i, ret; + + /* Linux does not support this natively so we have to loop. */ + for (i = 0; i < haptic->neffects; i++) { + if (haptic->effects[i].hweffect != NULL) { + ret = SDL_SYS_HapticStopEffect(haptic, &haptic->effects[i]); + if (ret < 0) { + SDL_SetError + ("Haptic: Error while trying to stop all playing effects."); + return -1; + } + } + } + return 0; +} + + +#endif /* SDL_HAPTIC_LINUX */ diff --git a/src/haptic/win32/SDL_syshaptic.c b/src/haptic/win32/SDL_syshaptic.c new file mode 100644 index 000000000..32cde559d --- /dev/null +++ b/src/haptic/win32/SDL_syshaptic.c @@ -0,0 +1,1392 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 2008 Edgar Simo + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifdef SDL_HAPTIC_DINPUT + +#include "SDL_haptic.h" +#include "../SDL_syshaptic.h" +#include "SDL_joystick.h" +#include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */ +#include "../../joystick/win32/SDL_dxjoystick_c.h" /* For joystick hwdata */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define DIRECTINPUT_VERSION 0x0700 /* Need at least DirectX 7 for dwStartDelay */ +#include +#include +#ifdef _MSC_VER +# pragma comment (lib, "dinput8.lib") +# pragma comment (lib, "dxguid.lib") +# pragma comment (lib, "dxerr.lib") +#endif /* _MSC_VER */ + + /* an ISO hack for VisualC++ */ +#ifdef _MSC_VER +#define snprintf _snprintf +#endif /* _MSC_VER */ + + +#define MAX_HAPTICS 32 + + +/* + * List of available haptic devices. + */ +static struct +{ + DIDEVICEINSTANCE instance; + SDL_Haptic *haptic; + DIDEVCAPS capabilities; +} SDL_hapticlist[MAX_HAPTICS]; + + +/* + * Haptic system hardware data. + */ +struct haptic_hwdata +{ + LPDIRECTINPUTDEVICE2 device; + DWORD axes[3]; /* Axes to use. */ + int is_joystick; /* Device is loaded as joystick. */ +}; + + +/* + * Haptic system effect data. + */ +struct haptic_hweffect +{ + DIEFFECT effect; + LPDIRECTINPUTEFFECT ref; +}; + + +/* + * Internal stuff. + */ +static LPDIRECTINPUT dinput = NULL; + + +/* + * External stuff. + */ +extern HWND SDL_HelperWindow; + + +/* + * Prototypes. + */ +static void DI_SetError(const char *str, HRESULT err); +static int DI_GUIDIsSame(const GUID * a, const GUID * b); +static int SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic, + DIDEVICEINSTANCE instance); +static int SDL_SYS_HapticOpenFromDevice2(SDL_Haptic * haptic, + LPDIRECTINPUTDEVICE2 device2); +static DWORD DIGetTriggerButton(Uint16 button); +static int SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir, + int naxes); +static int SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest, + SDL_HapticEffect * src); +static void SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type); +static REFGUID SDL_SYS_HapticEffectType(SDL_HapticEffect * effect); +/* Callbacks. */ +static BOOL CALLBACK EnumHapticsCallback(const DIDEVICEINSTANCE * + pdidInstance, VOID * pContext); +static BOOL CALLBACK DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv); + + +/* + * Like SDL_SetError but for DX error codes. + */ +static void +DI_SetError(const char *str, HRESULT err) +{ + SDL_SetError("Haptic: %s - %s: %s", str, + DXGetErrorString(err), DXGetErrorDescription(err)); +} + + +/* + * Checks to see if two GUID are the same. + */ +static int +DI_GUIDIsSame(const GUID * a, const GUID * b) +{ + if (((a)->Data1 == (b)->Data1) && + ((a)->Data2 == (b)->Data2) && + ((a)->Data3 == (b)->Data3) && + (SDL_strcmp((a)->Data4, (b)->Data4) == 0)) + return 1; + return 0; +} + + +/* + * Initializes the haptic subsystem. + */ +int +SDL_SYS_HapticInit(void) +{ + HRESULT ret; + HINSTANCE instance; + + if (dinput != NULL) { /* Already open. */ + SDL_SetError("Haptic: SubSystem already open."); + return -1; + } + + /* Clear all the memory. */ + SDL_memset(SDL_hapticlist, 0, sizeof(SDL_hapticlist)); + + SDL_numhaptics = 0; + + ret = CoInitialize(NULL); + if (FAILED(ret)) { + DI_SetError("Coinitialize", ret); + return -1; + } + + ret = CoCreateInstance(&CLSID_DirectInput, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectInput, &dinput); + if (FAILED(ret)) { + DI_SetError("CoCreateInstance", ret); + return -1; + } + + /* Because we used CoCreateInstance, we need to Initialize it, first. */ + instance = GetModuleHandle(NULL); + if (instance == NULL) { + SDL_SetError("GetModuleHandle() failed with error code %d.", + GetLastError()); + return -1; + } + ret = IDirectInput_Initialize(dinput, instance, DIRECTINPUT_VERSION); + if (FAILED(ret)) { + DI_SetError("Initializing DirectInput device", ret); + return -1; + } + + /* Look for haptic devices. */ + ret = IDirectInput_EnumDevices(dinput, 0, /* Not sure if this is legal, but gets all devices. */ + EnumHapticsCallback, + NULL, + DIEDFL_FORCEFEEDBACK | + DIEDFL_ATTACHEDONLY); + if (FAILED(ret)) { + DI_SetError("Enumerating DirectInput devices", ret); + return -1; + } + + return SDL_numhaptics; +} + +/* + * Callback to find the haptic devices. + */ +static BOOL CALLBACK +EnumHapticsCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext) +{ + HRESULT ret; + LPDIRECTINPUTDEVICE device; + + /* Copy the instance over, useful for creating devices. */ + SDL_memcpy(&SDL_hapticlist[SDL_numhaptics].instance, pdidInstance, + sizeof(DIDEVICEINSTANCE)); + + /* Open the device */ + ret = IDirectInput_CreateDevice(dinput, &pdidInstance->guidInstance, + &device, NULL); + if (FAILED(ret)) { + /* DI_SetError("Creating DirectInput device",ret); */ + return DIENUM_CONTINUE; + } + + /* Get capabilities. */ + SDL_hapticlist[SDL_numhaptics].capabilities.dwSize = sizeof(DIDEVCAPS); + ret = IDirectInputDevice_GetCapabilities(device, + &SDL_hapticlist[SDL_numhaptics]. + capabilities); + if (FAILED(ret)) { + /* DI_SetError("Getting device capabilities",ret); */ + IDirectInputDevice_Release(device); + return DIENUM_CONTINUE; + } + + /* Close up device and count it. */ + IDirectInputDevice_Release(device); + SDL_numhaptics++; + + /* Watch out for hard limit. */ + if (SDL_numhaptics >= MAX_HAPTICS) + return DIENUM_STOP; + + return DIENUM_CONTINUE; +} + + +/* + * Return the name of a haptic device, does not need to be opened. + */ +const char * +SDL_SYS_HapticName(int index) +{ + return SDL_hapticlist[index].instance.tszProductName; +} + + +/* + * Callback to get all supported effects. + */ +#define EFFECT_TEST(e,s) \ +if (DI_GUIDIsSame(&pei->guid, &(e))) \ + haptic->supported |= (s) +static BOOL CALLBACK +DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv) +{ + /* Prepare the haptic device. */ + SDL_Haptic *haptic = (SDL_Haptic *) pv; + + /* Get supported. */ + EFFECT_TEST(GUID_Spring, SDL_HAPTIC_SPRING); + EFFECT_TEST(GUID_Damper, SDL_HAPTIC_DAMPER); + EFFECT_TEST(GUID_Inertia, SDL_HAPTIC_INERTIA); + EFFECT_TEST(GUID_Friction, SDL_HAPTIC_FRICTION); + EFFECT_TEST(GUID_ConstantForce, SDL_HAPTIC_CONSTANT); + EFFECT_TEST(GUID_CustomForce, SDL_HAPTIC_CUSTOM); + EFFECT_TEST(GUID_Sine, SDL_HAPTIC_SINE); + EFFECT_TEST(GUID_Square, SDL_HAPTIC_SQUARE); + EFFECT_TEST(GUID_Triangle, SDL_HAPTIC_TRIANGLE); + EFFECT_TEST(GUID_SawtoothUp, SDL_HAPTIC_SAWTOOTHUP); + EFFECT_TEST(GUID_SawtoothDown, SDL_HAPTIC_SAWTOOTHDOWN); + EFFECT_TEST(GUID_RampForce, SDL_HAPTIC_RAMP); + + /* Check for more. */ + return DIENUM_CONTINUE; +} + + +/* + * Callback to get supported axes. + */ +static BOOL CALLBACK +DI_DeviceObjectCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef) +{ + SDL_Haptic *haptic = (SDL_Haptic *) pvRef; + + if ((dev->dwType & DIDFT_AXIS) && (dev->dwFlags & DIDOI_FFACTUATOR)) { + + haptic->hwdata->axes[haptic->naxes] = dev->dwOfs; + haptic->naxes++; + + /* Currently using the artificial limit of 3 axes. */ + if (haptic->naxes >= 3) { + return DIENUM_STOP; + } + } + + return DIENUM_CONTINUE; +} + + +/* + * Opens the haptic device from the file descriptor. + * + * Steps: + * - Open temporary DirectInputDevice interface. + * - Create DirectInputDevice2 interface. + * - Release DirectInputDevice interface. + * - Call SDL_SYS_HapticOpenFromDevice2 + */ +static int +SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic, DIDEVICEINSTANCE instance) +{ + HRESULT ret; + int ret2; + LPDIRECTINPUTDEVICE device; + + /* Allocate the hwdata */ + haptic->hwdata = (struct haptic_hwdata *) + SDL_malloc(sizeof(*haptic->hwdata)); + if (haptic->hwdata == NULL) { + SDL_OutOfMemory(); + goto creat_err; + } + SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); + + /* Open the device */ + ret = IDirectInput_CreateDevice(dinput, &instance.guidInstance, + &device, NULL); + if (FAILED(ret)) { + DI_SetError("Creating DirectInput device", ret); + goto creat_err; + } + + /* Now get the IDirectInputDevice2 interface, instead. */ + ret = IDirectInputDevice_QueryInterface(device, + &IID_IDirectInputDevice2, + (LPVOID *) & haptic->hwdata-> + device); + /* Done with the temporary one now. */ + IDirectInputDevice_Release(device); + if (FAILED(ret)) { + DI_SetError("Querying DirectInput interface", ret); + goto creat_err; + } + + ret2 = SDL_SYS_HapticOpenFromDevice2(haptic, haptic->hwdata->device); + if (ret2 < 0) { + goto query_err; + } + + return 0; + + query_err: + IDirectInputDevice2_Release(haptic->hwdata->device); + creat_err: + if (haptic->hwdata != NULL) { + SDL_free(haptic->hwdata); + haptic->hwdata = NULL; + } + return -1; +} + + +/* + * Opens the haptic device from the file descriptor. + * + * Steps: + * - Set cooperative level. + * - Set data format. + * - Acquire exclusiveness. + * - Reset actuators. + * - Get supported featuers. + */ +static int +SDL_SYS_HapticOpenFromDevice2(SDL_Haptic * haptic, + LPDIRECTINPUTDEVICE2 device2) +{ + HRESULT ret; + DIPROPDWORD dipdw; + + /* We'll use the device2 from now on. */ + haptic->hwdata->device = device2; + + /* Grab it exclusively to use force feedback stuff. */ + ret = IDirectInputDevice2_SetCooperativeLevel(haptic->hwdata->device, + SDL_HelperWindow, + DISCL_EXCLUSIVE | + DISCL_BACKGROUND); + if (FAILED(ret)) { + DI_SetError("Setting cooperative level to exclusive", ret); + goto acquire_err; + } + + /* Set data format. */ + ret = IDirectInputDevice2_SetDataFormat(haptic->hwdata->device, + &c_dfDIJoystick2); + if (FAILED(ret)) { + DI_SetError("Setting data format", ret); + goto acquire_err; + } + + /* Get number of axes. */ + ret = IDirectInputDevice2_EnumObjects(haptic->hwdata->device, + DI_DeviceObjectCallback, + haptic, DIDFT_AXIS); + if (FAILED(ret)) { + DI_SetError("Getting device axes", ret); + goto acquire_err; + } + + /* Acquire the device. */ + ret = IDirectInputDevice2_Acquire(haptic->hwdata->device); + if (FAILED(ret)) { + DI_SetError("Acquiring DirectInput device", ret); + goto acquire_err; + } + + /* Reset all actuators - just in case. */ + ret = IDirectInputDevice2_SendForceFeedbackCommand(haptic->hwdata->device, + DISFFC_RESET); + if (FAILED(ret)) { + DI_SetError("Resetting device", ret); + goto acquire_err; + } + + /* Enabling actuators. */ + ret = IDirectInputDevice2_SendForceFeedbackCommand(haptic->hwdata->device, + DISFFC_SETACTUATORSON); + if (FAILED(ret)) { + DI_SetError("Enabling actuators", ret); + goto acquire_err; + } + + /* Get supported effects. */ + ret = IDirectInputDevice2_EnumEffects(haptic->hwdata->device, + DI_EffectCallback, haptic, + DIEFT_ALL); + if (FAILED(ret)) { + DI_SetError("Enumerating supported effects", ret); + goto acquire_err; + } + if (haptic->supported == 0) { /* Error since device supports nothing. */ + SDL_SetError("Haptic: Internal error on finding supported effects."); + goto acquire_err; + } + + /* Check autogain and autocenter. */ + dipdw.diph.dwSize = sizeof(DIPROPDWORD); + dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = 10000; + ret = IDirectInputDevice2_SetProperty(haptic->hwdata->device, + DIPROP_FFGAIN, &dipdw.diph); + if (!FAILED(ret)) { /* Gain is supported. */ + haptic->supported |= SDL_HAPTIC_GAIN; + } + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = DIPROPAUTOCENTER_OFF; + ret = IDirectInputDevice2_SetProperty(haptic->hwdata->device, + DIPROP_AUTOCENTER, &dipdw.diph); + if (!FAILED(ret)) { /* Autocenter is supported. */ + haptic->supported |= SDL_HAPTIC_AUTOCENTER; + } + + /* Status is always supported. */ + haptic->supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE; + + /* Check maximum effects. */ + haptic->neffects = 128; /* This is not actually supported as thus under windows, + there is no way to tell the number of EFFECTS that a + device can hold, so we'll just use a "random" number + instead and put warnings in SDL_haptic.h */ + haptic->nplaying = 128; /* Even more impossible to get this then neffects. */ + + /* Prepare effects memory. */ + haptic->effects = (struct haptic_effect *) + SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); + if (haptic->effects == NULL) { + SDL_OutOfMemory(); + goto acquire_err; + } + /* Clear the memory */ + SDL_memset(haptic->effects, 0, + sizeof(struct haptic_effect) * haptic->neffects); + + return 0; + + /* Error handling */ + acquire_err: + IDirectInputDevice2_Unacquire(haptic->hwdata->device); + return -1; + +} + + +/* + * Opens a haptic device for usage. + */ +int +SDL_SYS_HapticOpen(SDL_Haptic * haptic) +{ + return SDL_SYS_HapticOpenFromInstance(haptic, + SDL_hapticlist[haptic->index]. + instance); +} + + +/* + * Opens a haptic device from first mouse it finds for usage. + */ +int +SDL_SYS_HapticMouse(void) +{ + int i; + + /* Grab the first mouse haptic device we find. */ + for (i = 0; i < SDL_numhaptics; i++) { + if (SDL_hapticlist[i].capabilities.dwDevType == DIDEVTYPE_MOUSE) { + return i; + } + } + + return -1; +} + + +/* + * Checks to see if a joystick has haptic features. + */ +int +SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick) +{ + if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) { + return SDL_TRUE; + } + + return SDL_FALSE; +} + + +/* + * Checks to see if the haptic device and joystick and in reality the same. + */ +int +SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + HRESULT ret; + DIDEVICEINSTANCE hap_instance, joy_instance; + + /* Get the device instances. */ + ret = IDirectInputDevice2_GetDeviceInfo(haptic->hwdata->device, + &hap_instance); + if (FAILED(ret)) { + return 0; + } + ret = IDirectInputDevice2_GetDeviceInfo(joystick->hwdata->InputDevice, + &joy_instance); + if (FAILED(ret)) { + return 0; + } + + if (DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance)) + return 1; + + return 0; +} + + +/* + * Opens a SDL_Haptic from a SDL_Joystick. + */ +int +SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + int ret; + + /* Allocate the hwdata */ + haptic->hwdata = (struct haptic_hwdata *) + SDL_malloc(sizeof(*haptic->hwdata)); + if (haptic->hwdata == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); + + /* Now open the device. */ + ret = + SDL_SYS_HapticOpenFromDevice2(haptic, joystick->hwdata->InputDevice); + if (ret < 0) { + return -1; + } + + /* It's using the joystick device. */ + haptic->hwdata->is_joystick = 1; + + return 0; +} + + +/* + * Closes the haptic device. + */ +void +SDL_SYS_HapticClose(SDL_Haptic * haptic) +{ + if (haptic->hwdata) { + + /* Free effects. */ + SDL_free(haptic->effects); + haptic->effects = NULL; + haptic->neffects = 0; + + /* Clean up */ + IDirectInputDevice2_Unacquire(haptic->hwdata->device); + /* Only release if isn't grabbed by a joystick. */ + if (haptic->hwdata->is_joystick == 0) { + IDirectInputDevice2_Release(haptic->hwdata->device); + } + + /* Free */ + SDL_free(haptic->hwdata); + haptic->hwdata = NULL; + } +} + + +/* + * Clean up after system specific haptic stuff + */ +void +SDL_SYS_HapticQuit(void) +{ + IDirectInput_Release(dinput); + dinput = NULL; +} + + +/* + * Converts an SDL trigger button to an DIEFFECT trigger button. + */ +static DWORD +DIGetTriggerButton(Uint16 button) +{ + DWORD dwTriggerButton; + + dwTriggerButton = DIEB_NOTRIGGER; + + if (button != 0) { + dwTriggerButton = DIJOFS_BUTTON(button - 1); + } + + return dwTriggerButton; +} + + +/* + * Sets the direction. + */ +static int +SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir, int naxes) +{ + LONG *rglDir; + + /* Handle no axes a part. */ + if (naxes == 0) { + effect->dwFlags |= DIEFF_SPHERICAL; /* Set as default. */ + effect->rglDirection = NULL; + return 0; + } + + /* Has axes. */ + rglDir = SDL_malloc(sizeof(LONG) * naxes); + if (rglDir == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_memset(rglDir, 0, sizeof(LONG) * naxes); + effect->rglDirection = rglDir; + + switch (dir->type) { + case SDL_HAPTIC_POLAR: + effect->dwFlags |= DIEFF_POLAR; + rglDir[0] = dir->dir[0]; + return 0; + case SDL_HAPTIC_CARTESIAN: + effect->dwFlags |= DIEFF_CARTESIAN; + rglDir[0] = dir->dir[0]; + if (naxes > 1) + rglDir[1] = dir->dir[1]; + if (naxes > 2) + rglDir[2] = dir->dir[2]; + return 0; + case SDL_HAPTIC_SPHERICAL: + effect->dwFlags |= DIEFF_SPHERICAL; + rglDir[0] = dir->dir[0]; + if (naxes > 1) + rglDir[1] = dir->dir[1]; + if (naxes > 2) + rglDir[2] = dir->dir[2]; + return 0; + + default: + SDL_SetError("Haptic: Unknown direction type."); + return -1; + } +} + +#define CONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF) +/* + * Creates the DIEFFECT from a SDL_HapticEffect. + */ +static int +SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest, + SDL_HapticEffect * src) +{ + int i; + DICONSTANTFORCE *constant; + DIPERIODIC *periodic; + DICONDITION *condition; /* Actually an array of conditions - one per axis. */ + DIRAMPFORCE *ramp; + DICUSTOMFORCE *custom; + DIENVELOPE *envelope; + SDL_HapticConstant *hap_constant; + SDL_HapticPeriodic *hap_periodic; + SDL_HapticCondition *hap_condition; + SDL_HapticRamp *hap_ramp; + SDL_HapticCustom *hap_custom; + DWORD *axes; + + /* Set global stuff. */ + SDL_memset(dest, 0, sizeof(DIEFFECT)); + dest->dwSize = sizeof(DIEFFECT); /* Set the structure size. */ + dest->dwSamplePeriod = 0; /* Not used by us. */ + dest->dwGain = 10000; /* Gain is set globally, not locally. */ + dest->dwFlags = DIEFF_OBJECTOFFSETS; /* Seems obligatory. */ + + /* Envelope. */ + envelope = SDL_malloc(sizeof(DIENVELOPE)); + if (envelope == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_memset(envelope, 0, sizeof(DIENVELOPE)); + dest->lpEnvelope = envelope; + envelope->dwSize = sizeof(DIENVELOPE); /* Always should be this. */ + + /* Axes. */ + dest->cAxes = haptic->naxes; + if (dest->cAxes > 0) { + axes = SDL_malloc(sizeof(DWORD) * dest->cAxes); + if (axes == NULL) { + SDL_OutOfMemory(); + return -1; + } + axes[0] = haptic->hwdata->axes[0]; /* Always at least one axis. */ + if (dest->cAxes > 1) { + axes[1] = haptic->hwdata->axes[1]; + } + if (dest->cAxes > 2) { + axes[2] = haptic->hwdata->axes[2]; + } + dest->rgdwAxes = axes; + } + + + /* The big type handling switch, even bigger then linux's version. */ + switch (src->type) { + case SDL_HAPTIC_CONSTANT: + hap_constant = &src->constant; + constant = SDL_malloc(sizeof(DICONSTANTFORCE)); + if (constant == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_memset(constant, 0, sizeof(DICONSTANTFORCE)); + + /* Specifics */ + constant->lMagnitude = CONVERT(hap_constant->level); + dest->cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + dest->lpvTypeSpecificParams = constant; + + /* Generics */ + dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = DIGetTriggerButton(hap_constant->button); + dest->dwTriggerRepeatInterval = hap_constant->interval; + dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes) + < 0) { + return -1; + } + + /* Envelope */ + if ((hap_constant->attack_length == 0) + && (hap_constant->fade_length == 0)) { + SDL_free(dest->lpEnvelope); + dest->lpEnvelope = NULL; + } else { + envelope->dwAttackLevel = CONVERT(hap_constant->attack_level); + envelope->dwAttackTime = hap_constant->attack_length * 1000; + envelope->dwFadeLevel = CONVERT(hap_constant->fade_level); + envelope->dwFadeTime = hap_constant->fade_length * 1000; + } + + break; + + case SDL_HAPTIC_SINE: + case SDL_HAPTIC_SQUARE: + case SDL_HAPTIC_TRIANGLE: + case SDL_HAPTIC_SAWTOOTHUP: + case SDL_HAPTIC_SAWTOOTHDOWN: + hap_periodic = &src->periodic; + periodic = SDL_malloc(sizeof(DIPERIODIC)); + if (periodic == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_memset(periodic, 0, sizeof(DIPERIODIC)); + + /* Specifics */ + periodic->dwMagnitude = CONVERT(hap_periodic->magnitude); + periodic->lOffset = CONVERT(hap_periodic->offset); + periodic->dwPhase = hap_periodic->phase; + periodic->dwPeriod = hap_periodic->period * 1000; + dest->cbTypeSpecificParams = sizeof(DIPERIODIC); + dest->lpvTypeSpecificParams = periodic; + + /* Generics */ + dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = DIGetTriggerButton(hap_periodic->button); + dest->dwTriggerRepeatInterval = hap_periodic->interval; + dest->dwStartDelay = hap_periodic->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes) + < 0) { + return -1; + } + + /* Envelope */ + if ((hap_periodic->attack_length == 0) + && (hap_periodic->fade_length == 0)) { + SDL_free(dest->lpEnvelope); + dest->lpEnvelope = NULL; + } else { + envelope->dwAttackLevel = CONVERT(hap_periodic->attack_level); + envelope->dwAttackTime = hap_periodic->attack_length * 1000; + envelope->dwFadeLevel = CONVERT(hap_periodic->fade_level); + envelope->dwFadeTime = hap_periodic->fade_length * 1000; + } + + break; + + case SDL_HAPTIC_SPRING: + case SDL_HAPTIC_DAMPER: + case SDL_HAPTIC_INERTIA: + case SDL_HAPTIC_FRICTION: + hap_condition = &src->condition; + condition = SDL_malloc(sizeof(DICONDITION) * dest->cAxes); + if (condition == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_memset(condition, 0, sizeof(DICONDITION)); + + /* Specifics */ + for (i = 0; i < (int) dest->cAxes; i++) { + condition[i].lOffset = CONVERT(hap_condition->center[i]); + condition[i].lPositiveCoefficient = + CONVERT(hap_condition->right_coeff[i]); + condition[i].lNegativeCoefficient = + CONVERT(hap_condition->left_coeff[i]); + condition[i].dwPositiveSaturation = + CONVERT(hap_condition->right_sat[i]); + condition[i].dwNegativeSaturation = + CONVERT(hap_condition->left_sat[i]); + condition[i].lDeadBand = CONVERT(hap_condition->deadband[i]); + } + dest->cbTypeSpecificParams = sizeof(DICONDITION) * dest->cAxes; + dest->lpvTypeSpecificParams = condition; + + /* Generics */ + dest->dwDuration = hap_condition->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = DIGetTriggerButton(hap_condition->button); + dest->dwTriggerRepeatInterval = hap_condition->interval; + dest->dwStartDelay = hap_condition->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes) + < 0) { + return -1; + } + + /* Envelope - Not actually supported by most CONDITION implementations. */ + SDL_free(dest->lpEnvelope); + dest->lpEnvelope = NULL; + + break; + + case SDL_HAPTIC_RAMP: + hap_ramp = &src->ramp; + ramp = SDL_malloc(sizeof(DIRAMPFORCE)); + if (ramp == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_memset(ramp, 0, sizeof(DIRAMPFORCE)); + + /* Specifics */ + ramp->lStart = CONVERT(hap_ramp->start); + ramp->lEnd = CONVERT(hap_ramp->end); + dest->cbTypeSpecificParams = sizeof(DIRAMPFORCE); + dest->lpvTypeSpecificParams = ramp; + + /* Generics */ + dest->dwDuration = hap_ramp->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = DIGetTriggerButton(hap_ramp->button); + dest->dwTriggerRepeatInterval = hap_ramp->interval; + dest->dwStartDelay = hap_ramp->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) { + return -1; + } + + /* Envelope */ + if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) { + SDL_free(dest->lpEnvelope); + dest->lpEnvelope = NULL; + } else { + envelope->dwAttackLevel = CONVERT(hap_ramp->attack_level); + envelope->dwAttackTime = hap_ramp->attack_length * 1000; + envelope->dwFadeLevel = CONVERT(hap_ramp->fade_level); + envelope->dwFadeTime = hap_ramp->fade_length * 1000; + } + + break; + + case SDL_HAPTIC_CUSTOM: + hap_custom = &src->custom; + custom = SDL_malloc(sizeof(DICUSTOMFORCE)); + if (custom == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_memset(custom, 0, sizeof(DICUSTOMFORCE)); + + /* Specifics */ + custom->cChannels = hap_custom->channels; + custom->dwSamplePeriod = hap_custom->period * 1000; + custom->cSamples = hap_custom->samples; + custom->rglForceData = + SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels); + for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) { /* Copy data. */ + custom->rglForceData[i] = CONVERT(hap_custom->data[i]); + } + dest->cbTypeSpecificParams = sizeof(DICUSTOMFORCE); + dest->lpvTypeSpecificParams = custom; + + /* Generics */ + dest->dwDuration = hap_custom->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = DIGetTriggerButton(hap_custom->button); + dest->dwTriggerRepeatInterval = hap_custom->interval; + dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) < + 0) { + return -1; + } + + /* Envelope */ + if ((hap_custom->attack_length == 0) + && (hap_custom->fade_length == 0)) { + SDL_free(dest->lpEnvelope); + dest->lpEnvelope = NULL; + } else { + envelope->dwAttackLevel = CONVERT(hap_custom->attack_level); + envelope->dwAttackTime = hap_custom->attack_length * 1000; + envelope->dwFadeLevel = CONVERT(hap_custom->fade_level); + envelope->dwFadeTime = hap_custom->fade_length * 1000; + } + + break; + + + default: + SDL_SetError("Haptic: Unknown effect type."); + return -1; + } + + return 0; +} + + +/* + * Frees an DIEFFECT allocated by SDL_SYS_ToDIEFFECT. + */ +static void +SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type) +{ + DICUSTOMFORCE *custom; + + if (effect->lpEnvelope != NULL) { + SDL_free(effect->lpEnvelope); + effect->lpEnvelope = NULL; + } + if (effect->rgdwAxes != NULL) { + SDL_free(effect->rgdwAxes); + effect->rgdwAxes = NULL; + } + if (effect->lpvTypeSpecificParams != NULL) { + if (type == SDL_HAPTIC_CUSTOM) { /* Must free the custom data. */ + custom = (DICUSTOMFORCE *) effect->lpvTypeSpecificParams; + SDL_free(custom->rglForceData); + custom->rglForceData = NULL; + } + SDL_free(effect->lpvTypeSpecificParams); + effect->lpvTypeSpecificParams = NULL; + } + if (effect->rglDirection != NULL) { + SDL_free(effect->rglDirection); + effect->rglDirection = NULL; + } +} + + +/* + * Gets the effect type from the generic SDL haptic effect wrapper. + */ +static REFGUID +SDL_SYS_HapticEffectType(SDL_HapticEffect * effect) +{ + switch (effect->type) { + case SDL_HAPTIC_CONSTANT: + return &GUID_ConstantForce; + + case SDL_HAPTIC_RAMP: + return &GUID_RampForce; + + case SDL_HAPTIC_SQUARE: + return &GUID_Square; + + case SDL_HAPTIC_SINE: + return &GUID_Sine; + + case SDL_HAPTIC_TRIANGLE: + return &GUID_Triangle; + + case SDL_HAPTIC_SAWTOOTHUP: + return &GUID_SawtoothUp; + + case SDL_HAPTIC_SAWTOOTHDOWN: + return &GUID_SawtoothDown; + + case SDL_HAPTIC_SPRING: + return &GUID_Spring; + + case SDL_HAPTIC_DAMPER: + return &GUID_Damper; + + case SDL_HAPTIC_INERTIA: + return &GUID_Inertia; + + case SDL_HAPTIC_FRICTION: + return &GUID_Friction; + + case SDL_HAPTIC_CUSTOM: + return &GUID_CustomForce; + + default: + SDL_SetError("Haptic: Unknown effect type."); + return NULL; + } +} + + +/* + * Creates a new haptic effect. + */ +int +SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + SDL_HapticEffect * base) +{ + HRESULT ret; + + /* Get the type. */ + REFGUID type = SDL_SYS_HapticEffectType(base); + if (type == NULL) { + goto err_hweffect; + } + + /* Alloc the effect. */ + effect->hweffect = (struct haptic_hweffect *) + SDL_malloc(sizeof(struct haptic_hweffect)); + if (effect->hweffect == NULL) { + SDL_OutOfMemory(); + goto err_hweffect; + } + + /* Get the effect. */ + if (SDL_SYS_ToDIEFFECT(haptic, &effect->hweffect->effect, base) < 0) { + goto err_effectdone; + } + + /* Create the actual effect. */ + ret = IDirectInputDevice2_CreateEffect(haptic->hwdata->device, type, + &effect->hweffect->effect, + &effect->hweffect->ref, NULL); + if (FAILED(ret)) { + DI_SetError("Unable to create effect", ret); + goto err_effectdone; + } + + return 0; + + err_effectdone: + SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, base->type); + err_hweffect: + if (effect->hweffect != NULL) { + SDL_free(effect->hweffect); + effect->hweffect = NULL; + } + return -1; +} + + +/* + * Updates an effect. + */ +int +SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, + SDL_HapticEffect * data) +{ + HRESULT ret; + DWORD flags; + DIEFFECT temp; + + /* Get the effect. */ + SDL_memset(&temp, 0, sizeof(DIEFFECT)); + if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) { + goto err_update; + } + + /* Set the flags. Might be worthwhile to diff temp with loaded effect and + * only change those parameters. */ + flags = DIEP_DIRECTION | + DIEP_DURATION | + DIEP_ENVELOPE | + DIEP_STARTDELAY | + DIEP_TRIGGERBUTTON | + DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS; + + /* Create the actual effect. */ + ret = + IDirectInputEffect_SetParameters(effect->hweffect->ref, &temp, flags); + if (FAILED(ret)) { + DI_SetError("Unable to update effect", ret); + goto err_update; + } + + /* Copy it over. */ + SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, data->type); + SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(DIEFFECT)); + + return 0; + + err_update: + SDL_SYS_HapticFreeDIEFFECT(&temp, data->type); + return -1; +} + + +/* + * Runs an effect. + */ +int +SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + Uint32 iterations) +{ + HRESULT ret; + DWORD iter; + + /* Check if it's infinite. */ + if (iterations == SDL_HAPTIC_INFINITY) { + iter = INFINITE; + } else + iter = iterations; + + /* Run the effect. */ + ret = IDirectInputEffect_Start(effect->hweffect->ref, iter, 0); + if (FAILED(ret)) { + DI_SetError("Running the effect", ret); + return -1; + } + + return 0; +} + + +/* + * Stops an effect. + */ +int +SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + HRESULT ret; + + ret = IDirectInputEffect_Stop(effect->hweffect->ref); + if (FAILED(ret)) { + DI_SetError("Unable to stop effect", ret); + return -1; + } + + return 0; +} + + +/* + * Frees the effect. + */ +void +SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + HRESULT ret; + + ret = IDirectInputEffect_Unload(effect->hweffect->ref); + if (FAILED(ret)) { + DI_SetError("Removing effect from the device", ret); + } + SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, + effect->effect.type); + SDL_free(effect->hweffect); + effect->hweffect = NULL; +} + + +/* + * Gets the status of a haptic effect. + */ +int +SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, + struct haptic_effect *effect) +{ + HRESULT ret; + DWORD status; + + ret = IDirectInputEffect_GetEffectStatus(effect->hweffect->ref, &status); + if (FAILED(ret)) { + DI_SetError("Getting effect status", ret); + return -1; + } + + if (status == 0) + return SDL_FALSE; + return SDL_TRUE; +} + + +/* + * Sets the gain. + */ +int +SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + HRESULT ret; + DIPROPDWORD dipdw; + + /* Create the weird structure thingy. */ + dipdw.diph.dwSize = sizeof(DIPROPDWORD); + dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = gain * 100; /* 0 to 10,000 */ + + /* Try to set the autocenter. */ + ret = IDirectInputDevice2_SetProperty(haptic->hwdata->device, + DIPROP_FFGAIN, &dipdw.diph); + if (FAILED(ret)) { + DI_SetError("Setting gain", ret); + return -1; + } + + return 0; +} + + +/* + * Sets the autocentering. + */ +int +SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + HRESULT ret; + DIPROPDWORD dipdw; + + /* Create the weird structure thingy. */ + dipdw.diph.dwSize = sizeof(DIPROPDWORD); + dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF : + DIPROPAUTOCENTER_ON; + + /* Try to set the autocenter. */ + ret = IDirectInputDevice2_SetProperty(haptic->hwdata->device, + DIPROP_AUTOCENTER, &dipdw.diph); + if (FAILED(ret)) { + DI_SetError("Setting autocenter", ret); + return -1; + } + + return 0; +} + + +/* + * Pauses the device. + */ +int +SDL_SYS_HapticPause(SDL_Haptic * haptic) +{ + HRESULT ret; + + /* Pause the device. */ + ret = IDirectInputDevice2_SendForceFeedbackCommand(haptic->hwdata->device, + DISFFC_PAUSE); + if (FAILED(ret)) { + DI_SetError("Pausing the device", ret); + return -1; + } + + return 0; +} + + +/* + * Pauses the device. + */ +int +SDL_SYS_HapticUnpause(SDL_Haptic * haptic) +{ + HRESULT ret; + + /* Unpause the device. */ + ret = IDirectInputDevice2_SendForceFeedbackCommand(haptic->hwdata->device, + DISFFC_CONTINUE); + if (FAILED(ret)) { + DI_SetError("Pausing the device", ret); + return -1; + } + + return 0; +} + + +/* + * Stops all the playing effects on the device. + */ +int +SDL_SYS_HapticUnpause(SDL_Haptic * haptic) +{ + HRESULT ret; + + /* Try to stop the effects. */ + ret = IDirectInputDevice2_SendForceFeedbackCommand(haptic->hwdata->device, + DISFFC_STOPALL); + if (FAILED(ret)) { + DI_SetError("Stopping the device", ret); + return -1; + } + + return 0; +} + + +#endif /* SDL_HAPTIC_DINPUT */ diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index c230da858..964689d4d 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -193,8 +193,12 @@ SDL_JoystickOpened(int device_index) return (opened); } -static int -ValidJoystick(SDL_Joystick ** joystick) + +/* + * Checks to make sure the joystick is valid. + */ +int +SDL_PrivateJoystickValid(SDL_Joystick ** joystick) { int valid; @@ -216,7 +220,7 @@ ValidJoystick(SDL_Joystick ** joystick) int SDL_JoystickIndex(SDL_Joystick * joystick) { - if (!ValidJoystick(&joystick)) { + if (!SDL_PrivateJoystickValid(&joystick)) { return (-1); } return (joystick->index); @@ -228,7 +232,7 @@ SDL_JoystickIndex(SDL_Joystick * joystick) int SDL_JoystickNumAxes(SDL_Joystick * joystick) { - if (!ValidJoystick(&joystick)) { + if (!SDL_PrivateJoystickValid(&joystick)) { return (-1); } return (joystick->naxes); @@ -240,7 +244,7 @@ SDL_JoystickNumAxes(SDL_Joystick * joystick) int SDL_JoystickNumHats(SDL_Joystick * joystick) { - if (!ValidJoystick(&joystick)) { + if (!SDL_PrivateJoystickValid(&joystick)) { return (-1); } return (joystick->nhats); @@ -252,7 +256,7 @@ SDL_JoystickNumHats(SDL_Joystick * joystick) int SDL_JoystickNumBalls(SDL_Joystick * joystick) { - if (!ValidJoystick(&joystick)) { + if (!SDL_PrivateJoystickValid(&joystick)) { return (-1); } return (joystick->nballs); @@ -264,7 +268,7 @@ SDL_JoystickNumBalls(SDL_Joystick * joystick) int SDL_JoystickNumButtons(SDL_Joystick * joystick) { - if (!ValidJoystick(&joystick)) { + if (!SDL_PrivateJoystickValid(&joystick)) { return (-1); } return (joystick->nbuttons); @@ -278,7 +282,7 @@ SDL_JoystickGetAxis(SDL_Joystick * joystick, int axis) { Sint16 state; - if (!ValidJoystick(&joystick)) { + if (!SDL_PrivateJoystickValid(&joystick)) { return (0); } if (axis < joystick->naxes) { @@ -298,7 +302,7 @@ SDL_JoystickGetHat(SDL_Joystick * joystick, int hat) { Uint8 state; - if (!ValidJoystick(&joystick)) { + if (!SDL_PrivateJoystickValid(&joystick)) { return (0); } if (hat < joystick->nhats) { @@ -318,7 +322,7 @@ SDL_JoystickGetBall(SDL_Joystick * joystick, int ball, int *dx, int *dy) { int retval; - if (!ValidJoystick(&joystick)) { + if (!SDL_PrivateJoystickValid(&joystick)) { return (-1); } @@ -347,7 +351,7 @@ SDL_JoystickGetButton(SDL_Joystick * joystick, int button) { Uint8 state; - if (!ValidJoystick(&joystick)) { + if (!SDL_PrivateJoystickValid(&joystick)) { return (0); } if (button < joystick->nbuttons) { @@ -367,7 +371,7 @@ SDL_JoystickClose(SDL_Joystick * joystick) { int i; - if (!ValidJoystick(&joystick)) { + if (!SDL_PrivateJoystickValid(&joystick)) { return; } diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h index d489ac234..0c9b31e6a 100644 --- a/src/joystick/SDL_joystick_c.h +++ b/src/joystick/SDL_joystick_c.h @@ -36,4 +36,8 @@ extern int SDL_PrivateJoystickHat(SDL_Joystick * joystick, Uint8 hat, Uint8 value); extern int SDL_PrivateJoystickButton(SDL_Joystick * joystick, Uint8 button, Uint8 state); + +/* Internal sanity checking functions */ +extern int SDL_PrivateJoystickValid(SDL_Joystick ** joystick); + /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/darwin/SDL_sysjoystick.c b/src/joystick/darwin/SDL_sysjoystick.c index bd0f68746..8bb21fe05 100644 --- a/src/joystick/darwin/SDL_sysjoystick.c +++ b/src/joystick/darwin/SDL_sysjoystick.c @@ -48,59 +48,14 @@ #include #include /* for NewPtrClear, DisposePtr */ +/* For force feedback testing. */ +#include +#include + #include "SDL_joystick.h" #include "../SDL_sysjoystick.h" #include "../SDL_joystick_c.h" - -struct recElement -{ - IOHIDElementCookie cookie; /* unique value which identifies element, will NOT change */ - long min; /* reported min value possible */ - long max; /* reported max value possible */ -#if 0 - /* TODO: maybe should handle the following stuff somehow? */ - - long scaledMin; /* reported scaled min value possible */ - long scaledMax; /* reported scaled max value possible */ - long size; /* size in bits of data return from element */ - Boolean relative; /* are reports relative to last report (deltas) */ - Boolean wrapping; /* does element wrap around (one value higher than max is min) */ - Boolean nonLinear; /* are the values reported non-linear relative to element movement */ - Boolean preferredState; /* does element have a preferred state (such as a button) */ - Boolean nullState; /* does element have null state */ -#endif /* 0 */ - - /* runtime variables used for auto-calibration */ - long minReport; /* min returned value */ - long maxReport; /* max returned value */ - - struct recElement *pNext; /* next element in list */ -}; -typedef struct recElement recElement; - -struct joystick_hwdata -{ - IOHIDDeviceInterface **interface; /* interface to device, NULL = no interface */ - - char product[256]; /* name of product */ - long usage; /* usage page from IOUSBHID Parser.h which defines general usage */ - long usagePage; /* usage within above page from IOUSBHID Parser.h which defines specific usage */ - - long axes; /* number of axis (calculated, not reported by device) */ - long buttons; /* number of buttons (calculated, not reported by device) */ - long hats; /* number of hat switches (calculated, not reported by device) */ - long elements; /* number of total elements (shouldbe total of above) (calculated, not reported by device) */ - - recElement *firstAxis; - recElement *firstButton; - recElement *firstHat; - - int removed; - int uncentered; - - struct joystick_hwdata *pNext; /* next device */ -}; -typedef struct joystick_hwdata recDevice; +#include "SDL_sysjoystick_c.h" /* Linked list of all available devices */ @@ -594,6 +549,12 @@ HIDDisposeDevice(recDevice ** ppDevice) /* save next device prior to disposing of this device */ pDeviceNext = (*ppDevice)->pNext; + /* free posible io_service_t */ + if ((*ppDevice)->ffservice) { + IOObjectRelease((*ppDevice)->ffservice); + (*ppDevice)->ffservice = 0; + } + /* free element lists */ HIDDisposeElementList(&(*ppDevice)->firstAxis); HIDDisposeElementList(&(*ppDevice)->firstButton); @@ -686,12 +647,6 @@ SDL_SYS_JoystickInit(void) if (!device) continue; - /* dump device object, it is no longer needed */ - result = IOObjectRelease(ioHIDDeviceObject); -/* if (KERN_SUCCESS != result) - HIDReportErrorNum ("IOObjectRelease error with ioHIDDeviceObject.", result); -*/ - /* Filter device list to non-keyboard/mouse stuff */ if ((device->usagePage != kHIDPage_GenericDesktop) || ((device->usage != kHIDUsage_GD_Joystick && @@ -704,6 +659,14 @@ SDL_SYS_JoystickInit(void) continue; } + /* We have to do some storage of the io_service_t for + * SDL_HapticOpenFromJoystick */ + if (FFIsForceFeedback(ioHIDDeviceObject) == FF_OK) { + device->ffservice = ioHIDDeviceObject; + } else { + device->ffservice = 0; + } + /* Add device to the end of the list */ if (lastDevice) lastDevice->pNext = device; diff --git a/src/joystick/darwin/SDL_sysjoystick_c.h b/src/joystick/darwin/SDL_sysjoystick_c.h new file mode 100644 index 000000000..e78c4b689 --- /dev/null +++ b/src/joystick/darwin/SDL_sysjoystick_c.h @@ -0,0 +1,87 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef SDL_JOYSTICK_IOKIT_H + + +#if MAC_OS_X_VERSION_MIN_REQUIRED == 1030 +#include "10.3.9-FIX/IOHIDLib.h" +#else +#include +#endif +#include + + +struct recElement +{ + IOHIDElementCookie cookie; /* unique value which identifies element, will NOT change */ + long min; /* reported min value possible */ + long max; /* reported max value possible */ +#if 0 + /* TODO: maybe should handle the following stuff somehow? */ + + long scaledMin; /* reported scaled min value possible */ + long scaledMax; /* reported scaled max value possible */ + long size; /* size in bits of data return from element */ + Boolean relative; /* are reports relative to last report (deltas) */ + Boolean wrapping; /* does element wrap around (one value higher than max is min) */ + Boolean nonLinear; /* are the values reported non-linear relative to element movement */ + Boolean preferredState; /* does element have a preferred state (such as a button) */ + Boolean nullState; /* does element have null state */ +#endif /* 0 */ + + /* runtime variables used for auto-calibration */ + long minReport; /* min returned value */ + long maxReport; /* max returned value */ + + struct recElement *pNext; /* next element in list */ +}; +typedef struct recElement recElement; + +struct joystick_hwdata +{ + io_service_t ffservice; /* Interface for force feedback, 0 = no ff */ + IOHIDDeviceInterface **interface; /* interface to device, NULL = no interface */ + + char product[256]; /* name of product */ + long usage; /* usage page from IOUSBHID Parser.h which defines general usage */ + long usagePage; /* usage within above page from IOUSBHID Parser.h which defines specific usage */ + + long axes; /* number of axis (calculated, not reported by device) */ + long buttons; /* number of buttons (calculated, not reported by device) */ + long hats; /* number of hat switches (calculated, not reported by device) */ + long elements; /* number of total elements (shouldbe total of above) (calculated, not reported by device) */ + + recElement *firstAxis; + recElement *firstButton; + recElement *firstHat; + + int removed; + int uncentered; + + struct joystick_hwdata *pNext; /* next device */ +}; +typedef struct joystick_hwdata recDevice; + + +#endif /* SDL_JOYSTICK_IOKIT_H */ diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c index faec85383..c31dace49 100644 --- a/src/joystick/linux/SDL_sysjoystick.c +++ b/src/joystick/linux/SDL_sysjoystick.c @@ -31,13 +31,11 @@ #include #include /* For the definition of PATH_MAX */ #include -#if SDL_INPUT_LINUXEV -#include -#endif #include "SDL_joystick.h" #include "../SDL_sysjoystick.h" #include "../SDL_joystick_c.h" +#include "SDL_sysjoystick_c.h" /* Special joystick configurations */ static struct @@ -278,35 +276,6 @@ static struct } SDL_joylist[MAX_JOYSTICKS]; -/* The private structure used to keep track of a joystick */ -struct joystick_hwdata -{ - int fd; - /* The current linux joystick driver maps hats to two axes */ - struct hwdata_hat - { - int axis[2]; - } *hats; - /* The current linux joystick driver maps balls to two axes */ - struct hwdata_ball - { - int axis[2]; - } *balls; - - /* Support for the Linux 2.4 unified input interface */ -#if SDL_INPUT_LINUXEV - SDL_bool is_hid; - Uint8 key_map[KEY_MAX - BTN_MISC]; - Uint8 abs_map[ABS_MAX]; - struct axis_correct - { - int used; - int coef[3]; - } abs_correct[ABS_MAX]; -#endif -}; - - #ifndef NO_LOGICAL_JOYSTICKS static int @@ -805,6 +774,7 @@ int SDL_SYS_JoystickOpen(SDL_Joystick * joystick) { int fd; + char *fname; SDL_logical_joydecl(int realindex); SDL_logical_joydecl(SDL_Joystick * realjoy = NULL); @@ -818,13 +788,16 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick) return (-1); fd = realjoy->hwdata->fd; + fname = realjoy->hwdata->fname; } else { fd = open(SDL_joylist[joystick->index].fname, O_RDONLY, 0); + fname = SDL_joylist[joystick->index].fname; } SDL_joylist[joystick->index].joy = joystick; #else fd = open(SDL_joylist[joystick->index].fname, O_RDONLY, 0); + fname = SDL_joylist[joystick->index].fname; #endif if (fd < 0) { @@ -840,6 +813,7 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick) } SDL_memset(joystick->hwdata, 0, sizeof(*joystick->hwdata)); joystick->hwdata->fd = fd; + joystick->hwdata->fname = fname; /* Set the joystick to non-blocking read mode */ fcntl(fd, F_SETFL, O_NONBLOCK); diff --git a/src/joystick/linux/SDL_sysjoystick_c.h b/src/joystick/linux/SDL_sysjoystick_c.h new file mode 100644 index 000000000..d598e9729 --- /dev/null +++ b/src/joystick/linux/SDL_sysjoystick_c.h @@ -0,0 +1,55 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#if SDL_INPUT_LINUXEV +#include +#endif + +/* The private structure used to keep track of a joystick */ +struct joystick_hwdata +{ + int fd; + char *fname; /* Used in haptic subsystem */ + + /* The current linux joystick driver maps hats to two axes */ + struct hwdata_hat + { + int axis[2]; + } *hats; + /* The current linux joystick driver maps balls to two axes */ + struct hwdata_ball + { + int axis[2]; + } *balls; + + /* Support for the Linux 2.4 unified input interface */ +#if SDL_INPUT_LINUXEV + SDL_bool is_hid; + Uint8 key_map[KEY_MAX - BTN_MISC]; + Uint8 abs_map[ABS_MAX]; + struct axis_correct + { + int used; + int coef[3]; + } abs_correct[ABS_MAX]; +#endif +}; diff --git a/src/joystick/win32/SDL_dxjoystick.c b/src/joystick/win32/SDL_dxjoystick.c index 0392eb6c8..76c81af9c 100644 --- a/src/joystick/win32/SDL_dxjoystick.c +++ b/src/joystick/win32/SDL_dxjoystick.c @@ -38,20 +38,7 @@ #include "SDL_joystick.h" #include "../SDL_sysjoystick.h" #include "../SDL_joystick_c.h" - -#define WIN32_LEAN_AND_MEAN -#include - -#define DIRECTINPUT_VERSION 0x0500 -#include -#ifdef _MSC_VER - /* Used for the c_dfDIJoystick2 symbol (no imports are used) */ -# pragma comment (lib, "dinput.lib") -#endif -#include /* From DirectX SDK 9c */ -#ifdef _MSC_VER -# pragma comment (lib, "dxerr9.lib") -#endif +#include "SDL_dxjoystick_c.h" /* an ISO hack for VisualC++ */ #ifdef _MSC_VER @@ -60,14 +47,12 @@ #define INPUT_QSIZE 32 /* Buffer up to 32 input messages */ #define MAX_JOYSTICKS 8 -#define MAX_INPUTS 256 /* each joystick can have up to 256 inputs */ #define AXIS_MIN -32768 /* minimum value for axis coordinate */ #define AXIS_MAX 32767 /* maximum value for axis coordinate */ #define JOY_AXIS_THRESHOLD (((AXIS_MAX)-(AXIS_MIN))/100) /* 1% motion */ /* external variables referenced. */ -extern HINSTANCE SDL_Instance; -extern HWND SDL_Window; +extern HWND SDL_HelperWindow; /* local variables */ @@ -95,39 +80,12 @@ static int SDL_PrivateJoystickButton_Int(SDL_Joystick * joystick, Uint8 button, Uint8 state); -/* local types */ -typedef enum Type -{ BUTTON, AXIS, HAT } Type; - -typedef struct input_t -{ - /* DirectInput offset for this input type: */ - DWORD ofs; - - /* Button, axis or hat: */ - Type type; - - /* SDL input offset: */ - Uint8 num; -} input_t; - -/* The private structure used to keep track of a joystick */ -struct joystick_hwdata -{ - LPDIRECTINPUTDEVICE2 InputDevice; - DIDEVCAPS Capabilities; - int buffered; - - input_t Inputs[MAX_INPUTS]; - int NumInputs; -}; - /* Convert a DirectInput return code to a text message */ static void SetDIerror(const char *function, HRESULT code) { SDL_SetError("%s() [%s]: %s", function, - DXGetErrorString9(code), DXGetErrorDescription9(code)); + DXGetErrorString(code), DXGetErrorDescription(code)); } @@ -140,6 +98,7 @@ int SDL_SYS_JoystickInit(void) { HRESULT result; + HINSTANCE instance; SYS_NumJoysticks = 0; @@ -158,8 +117,13 @@ SDL_SYS_JoystickInit(void) } /* Because we used CoCreateInstance, we need to Initialize it, first. */ - result = - IDirectInput_Initialize(dinput, SDL_Instance, DIRECTINPUT_VERSION); + instance = GetModuleHandle(NULL); + if (instance == NULL) { + SDL_SetError("GetModuleHandle() failed with error code %d.", + GetLastError()); + return (-1); + } + result = IDirectInput_Initialize(dinput, instance, DIRECTINPUT_VERSION); if (FAILED(result)) { SetDIerror("IDirectInput::Initialize", result); @@ -178,8 +142,8 @@ SDL_SYS_JoystickInit(void) static BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext) { - memcpy(&SYS_Joystick[SYS_NumJoysticks], pdidInstance, - sizeof(DIDEVICEINSTANCE)); + SDL_memcpy(&SYS_Joystick[SYS_NumJoysticks], pdidInstance, + sizeof(DIDEVICEINSTANCE)); SYS_NumJoysticks++; if (SYS_NumJoysticks >= MAX_JOYSTICKS) @@ -208,19 +172,19 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick) LPDIRECTINPUTDEVICE device; DIPROPDWORD dipdw; - ZeroMemory(&dipdw, sizeof(DIPROPDWORD)); + SDL_memset(&dipdw, 0, sizeof(DIPROPDWORD)); dipdw.diph.dwSize = sizeof(DIPROPDWORD); dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); /* allocate memory for system specific hardware data */ joystick->hwdata = - (struct joystick_hwdata *) malloc(sizeof(struct joystick_hwdata)); + (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata)); if (joystick->hwdata == NULL) { SDL_OutOfMemory(); return (-1); } - ZeroMemory(joystick->hwdata, sizeof(struct joystick_hwdata)); + SDL_memset(joystick->hwdata, 0, sizeof(struct joystick_hwdata)); joystick->hwdata->buffered = 1; joystick->hwdata->Capabilities.dwSize = sizeof(DIDEVCAPS); @@ -250,7 +214,7 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick) * though. */ result = IDirectInputDevice2_SetCooperativeLevel(joystick->hwdata-> - InputDevice, SDL_Window, + InputDevice, SDL_HelperWindow, DISCL_EXCLUSIVE | DISCL_BACKGROUND); if (FAILED(result)) { @@ -635,7 +599,7 @@ SDL_SYS_JoystickClose(SDL_Joystick * joystick) if (joystick->hwdata != NULL) { /* free system specific hardware data */ - free(joystick->hwdata); + SDL_free(joystick->hwdata); } } diff --git a/src/joystick/win32/SDL_dxjoystick_c.h b/src/joystick/win32/SDL_dxjoystick_c.h new file mode 100644 index 000000000..b7ae05da6 --- /dev/null +++ b/src/joystick/win32/SDL_dxjoystick_c.h @@ -0,0 +1,81 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef SDL_JOYSTICK_DINPUT_H + +/* DirectInput joystick driver; written by Glenn Maynard, based on Andrei de + * A. Formiga's WINMM driver. + * + * Hats and sliders are completely untested; the app I'm writing this for mostly + * doesn't use them and I don't own any joysticks with them. + * + * We don't bother to use event notification here. It doesn't seem to work + * with polled devices, and it's fine to call IDirectInputDevice2_GetDeviceData and + * let it return 0 events. */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define DIRECTINPUT_VERSION 0x0700 /* Need version 7 for force feedback. */ +#include +#ifdef _MSC_VER + /* Used for the c_dfDIJoystick2 symbol (no imports are used) */ +# pragma comment (lib, "dinput.lib") +#endif +#include +#ifdef _MSC_VER +# pragma comment (lib, "dxerr.lib") +#endif + + +#define MAX_INPUTS 256 /* each joystick can have up to 256 inputs */ + + +/* local types */ +typedef enum Type +{ BUTTON, AXIS, HAT } Type; + +typedef struct input_t +{ + /* DirectInput offset for this input type: */ + DWORD ofs; + + /* Button, axis or hat: */ + Type type; + + /* SDL input offset: */ + Uint8 num; +} input_t; + +/* The private structure used to keep track of a joystick */ +struct joystick_hwdata +{ + LPDIRECTINPUTDEVICE2 InputDevice; + DIDEVCAPS Capabilities; + int buffered; + + input_t Inputs[MAX_INPUTS]; + int NumInputs; +}; + +#endif /* SDL_JOYSTICK_DINPUT_H */ diff --git a/src/video/win32/SDL_win32window.c b/src/video/win32/SDL_win32window.c index 239582f8f..85580a5da 100644 --- a/src/video/win32/SDL_win32window.c +++ b/src/video/win32/SDL_win32window.c @@ -47,6 +47,12 @@ extern HCTX *g_hCtx; /* the table of tablet event contexts, each windows has to have it's own tablet context */ int highestId = 0; /* the highest id of the tablet context */ +/* Fake window to help with DirectInput events. */ +HWND SDL_HelperWindow = NULL; +static const char *SDL_HelperWindowClassName = "SDLHelperWindowInputCatcher"; +static const char *SDL_HelperWindowName = "SDLHelperWindowInputMsgWindow"; +static ATOM SDL_HelperWindowClass = 0; + static int SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, SDL_bool created) { @@ -470,4 +476,66 @@ WIN_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info) } } + +/* + * Creates a HelperWindow used for DirectInput events. + */ +int +SDL_HelperWindowCreate(void) +{ + HINSTANCE hInstance = GetModuleHandleA(NULL); + WNDCLASSEX wce; + + /* Create the class. */ + SDL_memset(&wce, 0, sizeof(wce)); + wce.cbSize = sizeof(WNDCLASSEX); + wce.lpfnWndProc = DefWindowProcA; + wce.lpszClassName = (LPCWSTR) SDL_HelperWindowClassName; + wce.hInstance = hInstance; + + /* Register the class. */ + SDL_HelperWindowClass = RegisterClassExA(&wce); + if (SDL_HelperWindowClass == 0) { + SDL_SetError("Unable to create Helper Window Class: error %d.", + GetLastError()); + return -1; + } + + /* Create the window. */ + SDL_HelperWindow = CreateWindowExA(0, SDL_HelperWindowClassName, + SDL_HelperWindowName, + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, HWND_MESSAGE, NULL, + hInstance, NULL); + if (SDL_HelperWindow == NULL) { + SDL_SetError("Unable to create Helper Window: error %d.", + GetLastError()); + return -1; + } + + return 0; +} + + +/* + * Destroys the HelperWindow previously created with SDL_HelperWindowCreate. + */ +void +SDL_HelperWindowDestroy(void) +{ + /* Destroy the window. */ + if (SDL_HelperWindow) { + DestroyWindow(SDL_HelperWindow); + SDL_HelperWindow = NULL; + } + + /* Unregister the class. */ + if (SDL_HelperWindowClass) { + UnregisterClassA(SDL_HelperWindowClassName, GetModuleHandleA(NULL)); + SDL_HelperWindowClass = 0; + } +} + + /* vi: set ts=4 sw=4 expandtab: */ diff --git a/test/Makefile.in b/test/Makefile.in index 7eb384b04..723ee65cf 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -7,7 +7,7 @@ EXE = @EXE@ CFLAGS = @CFLAGS@ LIBS = @LIBS@ -TARGETS = checkkeys$(EXE) graywin$(EXE) loopwave$(EXE) testaudioinfo$(EXE) testmultiaudio$(EXE) testalpha$(EXE) testbitmap$(EXE) testblitspeed$(EXE) testcdrom$(EXE) testcursor$(EXE) testdyngl$(EXE) testerror$(EXE) testfile$(EXE) testgamma$(EXE) testgl$(EXE) testgl2$(EXE) testhread$(EXE) testiconv$(EXE) testjoystick$(EXE) testkeys$(EXE) testlock$(EXE) testoverlay2$(EXE) testoverlay$(EXE) testpalette$(EXE) testplatform$(EXE) testsem$(EXE) testsprite$(EXE) testsprite2$(EXE) testtimer$(EXE) testver$(EXE) testvidinfo$(EXE) testwin$(EXE) testwm$(EXE) testwm2$(EXE) threadwin$(EXE) torturethread$(EXE) testloadso$(EXE) +TARGETS = checkkeys$(EXE) graywin$(EXE) loopwave$(EXE) testaudioinfo$(EXE) testmultiaudio$(EXE) testalpha$(EXE) testbitmap$(EXE) testblitspeed$(EXE) testcdrom$(EXE) testcursor$(EXE) testdyngl$(EXE) testerror$(EXE) testfile$(EXE) testgamma$(EXE) testgl$(EXE) testgl2$(EXE) testhread$(EXE) testiconv$(EXE) testjoystick$(EXE) testkeys$(EXE) testlock$(EXE) testoverlay2$(EXE) testoverlay$(EXE) testpalette$(EXE) testplatform$(EXE) testsem$(EXE) testsprite$(EXE) testsprite2$(EXE) testtimer$(EXE) testver$(EXE) testvidinfo$(EXE) testwin$(EXE) testwm$(EXE) testwm2$(EXE) threadwin$(EXE) torturethread$(EXE) testloadso$(EXE) testhaptic$(EXE) all: Makefile $(TARGETS) @@ -125,6 +125,9 @@ torturethread$(EXE): $(srcdir)/torturethread.c testloadso$(EXE): $(srcdir)/testloadso.c $(CC) -o $@ $? $(CFLAGS) $(LIBS) +testhaptic$(EXE): $(srcdir)/testhaptic.c + $(CC) -o $@ $? $(CFLAGS) $(LIBS) + clean: rm -f $(TARGETS) diff --git a/test/testhaptic.c b/test/testhaptic.c new file mode 100644 index 000000000..18d920433 --- /dev/null +++ b/test/testhaptic.c @@ -0,0 +1,266 @@ +/* +Copyright (c) 2008, Edgar Simo Serra +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of the Simple Directmedia Layer (SDL) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. +*/ + +/* + * includes + */ +#include "SDL.h" +#include "SDL_haptic.h" + +#include /* printf */ +#include /* strstr */ + + + +static SDL_Haptic *haptic; + + +/* + * prototypes + */ +static void abort_execution(void); +static void HapticPrintSupported(SDL_Haptic * haptic); + + +/** + * @brief The entry point of this force feedback demo. + * @param[in] argc Number of arguments. + * @param[in] argv Array of argc arguments. + */ +int +main(int argc, char **argv) +{ + int i; + char *name; + SDL_HapticEffect efx[5]; + int id[5]; + int nefx; + unsigned int supported; + + name = NULL; + if (argc > 1) { + name = argv[1]; + if ((strcmp(name, "--help") == 0) || (strcmp(name, "-h") == 0)) { + printf("USAGE: %s [device name]\n" + "If device name is specified, it will try to find a device whose name\n" + "contains device name for testing.\n", argv[0]); + return 0; + } + } + + /* Initialize the force feedbackness */ + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_JOYSTICK | + SDL_INIT_HAPTIC); + printf("%d Haptic devices detected.\n", SDL_NumHaptics()); + if (SDL_NumHaptics() > 0) { + /* We'll just use the first force feedback device found */ + if (name == NULL) { + i = 0; + } + /* Try to find matching device */ + else { + for (i = 0; i < SDL_NumHaptics(); i++) { + if (strstr(SDL_HapticName(i), name) != NULL) + break; + } + + if (i >= SDL_NumHaptics()) { + printf("Unable to find device matching '%s', aborting.\n", + name); + return 1; + } + } + + haptic = SDL_HapticOpen(i); + if (haptic == NULL) { + perror("Unable to create the haptic device"); + return 1; + } + printf("Device: %s\n", SDL_HapticName(i)); + HapticPrintSupported(haptic); + } else { + printf("No Haptic devices found!\n"); + return 1; + } + + /* We only want force feedback errors. */ + SDL_ClearError(); + + /* Create effects. */ + memset(&efx, 0, sizeof(efx)); + nefx = 0; + supported = SDL_HapticQuery(haptic); + + printf("\nUploading effects\n"); + /* First we'll try a SINE effect. */ + if (supported & SDL_HAPTIC_SINE) { + printf(" effect %d: Sine Wave\n", nefx); + efx[nefx].type = SDL_HAPTIC_SINE; + efx[nefx].periodic.period = 1000; + efx[nefx].periodic.magnitude = 0x4000; + efx[nefx].periodic.length = 5000; + efx[nefx].periodic.attack_length = 1000; + efx[nefx].periodic.fade_length = 1000; + id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]); + if (id[nefx] < 0) { + printf("UPLOADING EFFECT ERROR: %s\n", SDL_GetError()); + abort_execution(); + } + nefx++; + } + /* Now we'll try a SAWTOOTHUP */ + if (supported & SDL_HAPTIC_SAWTOOTHUP) { + printf(" effect %d: Sawtooth Up\n", nefx); + efx[nefx].type = SDL_HAPTIC_SQUARE; + efx[nefx].periodic.period = 500; + efx[nefx].periodic.magnitude = 0x5000; + efx[nefx].periodic.length = 5000; + efx[nefx].periodic.attack_length = 1000; + efx[nefx].periodic.fade_length = 1000; + id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]); + if (id[nefx] < 0) { + printf("UPLOADING EFFECT ERROR: %s\n", SDL_GetError()); + abort_execution(); + } + nefx++; + } + /* Now the classical constant effect. */ + if (supported & SDL_HAPTIC_CONSTANT) { + printf(" effect %d: Constant Force\n", nefx); + efx[nefx].type = SDL_HAPTIC_CONSTANT; + efx[nefx].constant.direction.type = SDL_HAPTIC_POLAR; + efx[nefx].constant.direction.dir[0] = 20000; /* Force comes from the south-west. */ + efx[nefx].constant.length = 5000; + efx[nefx].constant.level = 0x6000; + efx[nefx].constant.attack_length = 1000; + efx[nefx].constant.fade_length = 1000; + id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]); + if (id[nefx] < 0) { + printf("UPLOADING EFFECT ERROR: %s\n", SDL_GetError()); + abort_execution(); + } + nefx++; + } + /* The cute spring effect. */ + if (supported & SDL_HAPTIC_SPRING) { + printf(" effect %d: Condition Spring\n", nefx); + efx[nefx].type = SDL_HAPTIC_SPRING; + efx[nefx].condition.length = 5000; + for (i = 0; i < SDL_HapticNumAxes(haptic); i++) { + efx[nefx].condition.right_sat[i] = 0x7FFF; + efx[nefx].condition.left_sat[i] = 0x7FFF; + efx[nefx].condition.right_coeff[i] = 0x2000; + efx[nefx].condition.left_coeff[i] = 0x2000; + efx[nefx].condition.center[i] = 0x1000; /* Displace the center for it to move. */ + } + id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]); + if (id[nefx] < 0) { + printf("UPLOADING EFFECT ERROR: %s\n", SDL_GetError()); + abort_execution(); + } + nefx++; + } + /* The pretty awesome inertia effect. */ + if (supported & SDL_HAPTIC_INERTIA) { + printf(" effect %d: Condition Inertia\n", nefx); + efx[nefx].type = SDL_HAPTIC_SPRING; + efx[nefx].condition.length = 5000; + for (i = 0; i < SDL_HapticNumAxes(haptic); i++) { + efx[nefx].condition.right_sat[i] = 0x7FFF; + efx[nefx].condition.left_sat[i] = 0x7FFF; + efx[nefx].condition.right_coeff[i] = 0x2000; + efx[nefx].condition.left_coeff[i] = 0x2000; + } + id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]); + if (id[nefx] < 0) { + printf("UPLOADING EFFECT ERROR: %s\n", SDL_GetError()); + abort_execution(); + } + nefx++; + } + + printf + ("\nNow playing effects for 5 seconds each with 1 second delay between\n"); + for (i = 0; i < nefx; i++) { + printf(" Playing effect %d\n", i); + SDL_HapticRunEffect(haptic, id[i], 1); + SDL_Delay(6000); /* Effects only have length 5000 */ + } + + /* Quit */ + if (haptic != NULL) + SDL_HapticClose(haptic); + SDL_Quit(); + + return 0; +} + + +/* + * Cleans up a bit. + */ +static void +abort_execution(void) +{ + printf("\nAborting program execution.\n"); + + SDL_HapticClose(haptic); + SDL_Quit(); + + exit(1); +} + + +/* + * Displays information about the haptic device. + */ +static void +HapticPrintSupported(SDL_Haptic * haptic) +{ + unsigned int supported; + + supported = SDL_HapticQuery(haptic); + printf(" Supported effects [%d effects, %d playing]:\n", + SDL_HapticNumEffects(haptic), SDL_HapticNumEffectsPlaying(haptic)); + if (supported & SDL_HAPTIC_CONSTANT) + printf(" constant\n"); + if (supported & SDL_HAPTIC_SINE) + printf(" sine\n"); + if (supported & SDL_HAPTIC_SQUARE) + printf(" square\n"); + if (supported & SDL_HAPTIC_TRIANGLE) + printf(" triangle\n"); + if (supported & SDL_HAPTIC_SAWTOOTHUP) + printf(" sawtoothup\n"); + if (supported & SDL_HAPTIC_SAWTOOTHDOWN) + printf(" sawtoothdown\n"); + if (supported & SDL_HAPTIC_RAMP) + printf(" ramp\n"); + if (supported & SDL_HAPTIC_FRICTION) + printf(" friction\n"); + if (supported & SDL_HAPTIC_SPRING) + printf(" spring\n"); + if (supported & SDL_HAPTIC_DAMPER) + printf(" damper\n"); + if (supported & SDL_HAPTIC_INERTIA) + printf(" intertia\n"); + if (supported & SDL_HAPTIC_CUSTOM) + printf(" custom\n"); + printf(" Supported capabilities:\n"); + if (supported & SDL_HAPTIC_GAIN) + printf(" gain\n"); + if (supported & SDL_HAPTIC_AUTOCENTER) + printf(" autocenter\n"); + if (supported & SDL_HAPTIC_STATUS) + printf(" status\n"); +}