Force Feedback patch... brave testers needed

Taylor Richards mtrs at bellsouth.net
Fri Apr 4 22:09:36 EST 2003


Here is the first patch.  The effects still need some work to be true to
the windows effects but I don't want to spend all of my time on that
yet.  Device memory can be in short supply so the effects are loaded
only when they are played.  They could probably use better unloading
though so if you get any error messages let me know.  More work needs to
be done but I want to make sure that what I've got so far works.

This should works on current kernels and distribution kernels.  It will
not currently work with kernels that have been patched with a newer FF
patch.  This code will support both once it's done.  Please test this
and pass or fail please let me know what force feedback device you have,
distribution and kernel version.

All of this assumes that your joystick is supported by the drivers and
that your system is setup correctly (input event interface, etc.).  The
website for the FF drivers is http://user.it.uu.se/~johannd/projects/ff/
in case you need to look at the documentation.  The current drivers from
that site will NOT work with this so don't bother upgrading if this test
doesn't work.


Taylor

-- 
Taylor Richards <mtrs at bellsouth.net>
-------------- next part --------------
--- /home/tazman/src/cvs/freespace2/Makefile	2003-02-27 00:50:34.000000000 +0500
+++ Makefile	2003-03-31 14:08:41.000000000 +0500
@@ -110,6 +110,7 @@
 	./src/io/key.cpp \
 	./src/io/keycontrol.cpp \
 	./src/io/joy-unix.cpp \
+	./src/io/joy_ff-unix.cpp \
 	./src/io/mouse.cpp \
 	./src/io/timer.cpp \
 	./src/jumpnode/jumpnode.cpp \
diff -uNr /home/tazman/src/cvs/freespace2/src/io/joy_ff-unix.cpp src/io/joy_ff-unix.cpp
--- /home/tazman/src/cvs/freespace2/src/io/joy_ff-unix.cpp	1970-01-01 05:00:00.000000000 +0500
+++ src/io/joy_ff-unix.cpp	2003-04-05 07:43:23.000000000 +0500
@@ -0,0 +1,879 @@
+/*
+ * Copyright (C) Volition, Inc. 1999.  All rights reserved.
+ *
+ * All source code herein is the property of Volition, Inc. You may not sell 
+ * or otherwise commercially exploit the source or things you created based on
+ * the source.
+ */
+
+/*
+ * $Logfile: /Freespace2/code/Io/Joy_ff.cpp $
+ * $Revision: 1.3 $
+ * $Date: 2002/06/09 04:41:21 $
+ * $Author: relnev $
+ *
+ * Code for joystick Force Feedback.
+ *
+ * $Log: joy_ff.cpp,v $
+ * Revision 1.3  2002/06/09 04:41:21  relnev
+ * added copyright header
+ *
+ * Revision 1.2  2002/05/07 03:16:46  theoddone33
+ * The Great Newline Fix
+ *
+ * Revision 1.1.1.1  2002/05/03 03:28:09  root
+ * Initial import.
+ *
+ * 
+ * 4     1/06/99 2:24p Dave
+ * Stubs and release build fixes.
+ * 
+ * 3     10/09/98 2:57p Dave
+ * Starting splitting up OS stuff.
+ * 
+ * 2     10/07/98 10:53a Dave
+ * Initial checkin.
+ * 
+ * 1     10/07/98 10:49a Dave
+ * 
+ * 13    5/20/98 5:47p Sandeep
+ * 
+ * 12    5/20/98 3:52p Allender
+ * fixed compiler warnings
+ * 
+ * 11    5/20/98 11:06a Hoffoss
+ * Fixed typo.
+ * 
+ * 10    5/20/98 10:57a Hoffoss
+ * Made directional hit effect FF a toggle in launcher, and made calibrate
+ * not freeze launcher until process ends.
+ * 
+ * 9     5/18/98 4:53p Hoffoss
+ * Some force feedback tweaks and pilot initializations there should have
+ * been happening, but weren't, and not are!
+ * 
+ * 8     5/17/98 6:28p Hoffoss
+ * Added some stuff.
+ * 
+ * 7     5/17/98 5:45p Hoffoss
+ * Adjusted ship handling Force Feedback to not be as strong.  Poor
+ * Sandeep's wrist can't handle it. :)
+ * 
+ * 6     5/08/98 5:31p Hoffoss
+ * Isolated the joystick force feedback code more from dependence on other
+ * libraries.
+ * 
+ * 5     5/08/98 12:27p Hoffoss
+ * Polished up the error handling and such a bit.
+ * 
+ * 4     5/08/98 9:54a Hoffoss
+ * Generalized some of the effect stuff to try some swapping of effects.
+ * (going to start next, but checking this in in case I want to revert
+ * back to this state).
+ * 
+ * 3     5/07/98 12:24a Hoffoss
+ * Finished up sidewinder force feedback support.
+ * 
+ * 2     5/04/98 11:08p Hoffoss
+ * Expanded on Force Feedback code, and moved it all into Joy_ff.cpp.
+ * Updated references everywhere to it.
+ * 
+ * $NoKeywords: $
+ */
+
+#ifdef __gnu_linux__
+#include <linux/input.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#endif
+
+#include "vecmat.h"
+#include "osregistry.h"
+#include "joy_ff.h"
+#include "osapi.h"
+
+
+#define NUM_EFFECTS		11
+
+#define HITEFFECT1		0
+#define HITEFFECT2		1
+#define SHOOTEFFECT		2
+#define SECSHOOTEFFECT	3
+#define SPRING			4
+#define AFTERBURN1		5
+#define AFTERBURN2		6
+#define DOCK			7
+#define EXPLODE			8
+#define DEATHROLL1		9
+#define DEATHROLL2		10
+
+
+#define BITS_PER_LONG (sizeof(long) * 8)
+#define OFF(x)  ((x)%BITS_PER_LONG)
+#define BIT(x)  (1UL<<OFF(x))
+#define LONG(x) ((x)/BITS_PER_LONG)
+#define test_bit(bit, array)    ((array[LONG(bit)] >> OFF(bit)) & 1)
+
+#ifdef __gnu_linux__
+struct ff_effect effect[NUM_EFFECTS];
+struct input_event play, stop, ie;
+#endif
+
+int joy_ff_handling_scaler;
+int Joy_ff_enabled = 0;
+int Joy_ff_directional_hit_effect_enabled = 1;
+
+static int jd;	// Joystick device
+static char device_name[MAX_PATH];
+
+int joy_ff_create_effects();
+void joy_ff_stop_effects();
+void joy_ff_stop_single_effect();
+void joy_ff_afterburn_off();
+int joy_ff_create_single_effect(int effect_num);
+//void init_periodic_effect_struct(di_periodic_effect_struct *effect, int type, int dur, int per, int ang = 0, int mag = 10000, int att = 0, int fade = 0);
+
+
+int joy_ff_init()
+{
+#ifdef __gnu_linux__
+	int i, ff_enabled;
+	int found_one = 0;
+	ulong features[4];
+
+	Joy_ff_enabled = 0;		// Assume no force feedback
+	ff_enabled = os_config_read_uint(NULL, "EnableJoystickFF", 0);
+
+	if (ff_enabled) {
+		
+		Joy_ff_directional_hit_effect_enabled = os_config_read_uint(NULL, "EnableHitEffect", 1);
+
+		// look through some event devices to try and find one with FF
+		for (i=0; i<5; i++) {
+			sprintf(device_name, "/dev/input/event%i", i);
+			fprintf(stderr, "%s\n", device_name);
+
+			jd = open(device_name, O_RDWR);
+
+			if (!jd) {
+				break;
+			}
+
+			// Grab device features
+			if (ioctl(jd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features) == -1) {
+				// skip to next device if this was an error
+				break;
+			}
+
+			// See if this device actually has FF capabilities
+			if (test_bit(FF_ABS(0), features)) {
+				fprintf(stderr, "It's GOOD!\n");
+				found_one = 1;
+				break;
+			} else {
+				fprintf(stderr, "Nope!\n");
+			}
+		}
+
+		if (!found_one) {
+			fprintf(stderr, "Compatible Force Feedback device not found...\n");
+			return -1;
+		}
+
+		if (joy_ff_create_effects()) {
+			return -1;
+		}
+
+		Joy_ff_enabled = 1;
+	}
+#endif
+	return 0;
+}
+
+void joy_ff_shutdown()
+{
+#ifdef __gnu_linux__
+	int i;
+
+	if (Joy_ff_enabled) {
+		// stop all currently playing effects
+		for (i=0; i<NUM_EFFECTS; i++) {
+			stop.type = EV_FF;
+			stop.code =  effect[i].id;
+			stop.value = 0;
+
+			if (write(jd, (const void*) &stop, sizeof(stop)) == -1) {
+				fprintf(stderr, "Couldn't stop the effect!!\n");
+			}
+
+			// unload all effects that are still in device memory
+			if (ioctl(jd, EVIOCRMFF, effect[i].id) == -1) {
+				nprintf(("Joystick", "Unable to unload FF effect %i\n", i));
+			}
+		}
+	}
+#endif
+}
+
+void joy_ff_start_effect(int effect_num)
+{
+#ifdef __gnu_linux__
+	// create the effects by themselves to save device memory
+	if (joy_ff_create_single_effect(effect_num) == -1)
+		return;
+
+	// start playing
+	play.type = EV_FF;
+	play.code = effect[effect_num].id;
+	play.value = 1;
+
+	if (write(jd, (const void*) &play, sizeof(play)) == -1) {
+		fprintf(stderr, "Couldn't play the effect!!\n");
+	}
+#endif
+}
+
+int joy_ff_create_effects()
+{
+	return 0;
+}
+
+int joy_ff_create_single_effect(int effect_num)
+{
+#ifdef __gnu_linux__
+
+	switch (effect_num) {
+		case HITEFFECT1:
+			// HitEffect1
+			effect[HITEFFECT1].type = FF_CONSTANT;
+			effect[HITEFFECT1].id = -1;
+			effect[HITEFFECT1].u.constant.level = 0x4000;	// Magnitude
+			effect[HITEFFECT1].u.constant.direction = 0;	// Angle
+			effect[HITEFFECT1].u.constant.shape.attack_length = 0;	// Attack time
+			effect[HITEFFECT1].u.constant.shape.attack_level = 100;	// Attack level
+			effect[HITEFFECT1].u.constant.shape.fade_length = 1200;	// Fade time
+			effect[HITEFFECT1].u.constant.shape.fade_level = 1;	// Fade level
+			effect[HITEFFECT1].trigger.button = 0;
+			effect[HITEFFECT1].trigger.interval = 0;
+			effect[HITEFFECT1].replay.length = 3000;  // Duration
+			effect[HITEFFECT1].replay.delay = 0;
+
+			// load the effect
+			if (ioctl(jd, EVIOCSFF, &effect[effect_num]) == -1) {
+				fprintf(stderr, "Unable to upload effect: %i\n", effect_num);
+				return -1;
+			}
+			break;
+
+		case HITEFFECT2:
+			// HitEffect #2
+			effect[HITEFFECT2].type = FF_PERIODIC;
+			effect[HITEFFECT2].id = -1;
+			effect[HITEFFECT2].u.periodic.waveform = FF_SINE;
+			effect[HITEFFECT2].u.periodic.period = 1000;	// Period
+			effect[HITEFFECT2].u.periodic.magnitude = 0x4000;	// Magnitude
+			effect[HITEFFECT2].u.periodic.offset = 0;
+			effect[HITEFFECT2].u.periodic.phase = 0;
+			effect[HITEFFECT2].u.periodic.direction = 90;	// Angle
+			effect[HITEFFECT2].u.periodic.shape.attack_length = 1000;	// Attack time
+			effect[HITEFFECT2].u.periodic.shape.attack_level = 0;
+			effect[HITEFFECT2].u.periodic.shape.fade_length = 1000;	// Fade time
+			effect[HITEFFECT2].u.periodic.shape.fade_level = 0;
+			effect[HITEFFECT2].trigger.button = 0;
+			effect[HITEFFECT2].trigger.interval = 0;
+			effect[HITEFFECT2].replay.length = 3000;  // Duration
+			effect[HITEFFECT2].replay.delay = 0;
+
+			// load the effect
+			if (ioctl(jd, EVIOCSFF, &effect[effect_num]) == -1) {
+				fprintf(stderr, "Unable to upload effect: %i\n", effect_num);
+				return -1;
+			}
+			break;
+
+		case SHOOTEFFECT:
+			// ShootEffect
+			effect[SHOOTEFFECT].type = FF_PERIODIC;
+			effect[SHOOTEFFECT].id = -1;
+			effect[SHOOTEFFECT].u.periodic.waveform = FF_SAW_DOWN;
+			effect[SHOOTEFFECT].u.periodic.period = 2000;	// Period
+			effect[SHOOTEFFECT].u.periodic.magnitude = 0x4000;	// Magnitude
+			effect[SHOOTEFFECT].u.periodic.offset = 0;
+			effect[SHOOTEFFECT].u.periodic.phase = 0;
+			effect[SHOOTEFFECT].u.periodic.direction = 0x0000;	// Angle
+			effect[SHOOTEFFECT].u.periodic.shape.attack_length = 0;	// Attack time
+			effect[SHOOTEFFECT].u.periodic.shape.attack_level = 0;
+			effect[SHOOTEFFECT].u.periodic.shape.fade_length = 1200;	// Fade time
+			effect[SHOOTEFFECT].u.periodic.shape.fade_level = 0;
+			effect[SHOOTEFFECT].trigger.button = 0;
+			effect[SHOOTEFFECT].trigger.interval = 0;
+			effect[SHOOTEFFECT].replay.length = 1600;  // Duration
+			effect[SHOOTEFFECT].replay.delay = 0;
+
+			// load the effect
+			if (ioctl(jd, EVIOCSFF, &effect[effect_num]) == -1) {
+				fprintf(stderr, "Unable to upload effect: %i\n", effect_num);
+				return -1;
+			}
+			break;
+
+		case SECSHOOTEFFECT:
+			// SecShootEffect
+			effect[SECSHOOTEFFECT].type = FF_CONSTANT;
+			effect[SECSHOOTEFFECT].id = -1;
+			effect[SECSHOOTEFFECT].u.constant.level = 0x4000;	// Magnitude
+			effect[SECSHOOTEFFECT].u.constant.direction = 0;	// Angle
+			effect[SECSHOOTEFFECT].u.constant.shape.attack_length = 500;	// Attack time
+			effect[SECSHOOTEFFECT].u.constant.shape.attack_level = 100;	// Attack level
+			effect[SECSHOOTEFFECT].u.constant.shape.fade_length = 1000;	// Fade time
+			effect[SECSHOOTEFFECT].u.constant.shape.fade_level = 1;	// Fade level
+			effect[SECSHOOTEFFECT].trigger.button = 0;
+			effect[SECSHOOTEFFECT].trigger.interval = 0;
+			effect[SECSHOOTEFFECT].replay.length = 2000;  // Duration
+			effect[SECSHOOTEFFECT].replay.delay = 0;
+
+			// load the effect
+			if (ioctl(jd, EVIOCSFF, &effect[effect_num]) == -1) {
+				fprintf(stderr, "Unable to upload effect: %i\n", effect_num);
+				return -1;
+			}
+			break;
+
+		case SPRING:
+			// Spring
+			effect[SPRING].type = FF_SPRING;
+			effect[SPRING].id = -1;
+			effect[SPRING].u.interactive.axis = BIT(ABS_X) | BIT(ABS_Y);
+			effect[SPRING].u.interactive.direction = 0;
+			effect[SPRING].u.interactive.right_saturation = 0;
+			effect[SPRING].u.interactive.left_saturation = 0;
+			effect[SPRING].u.interactive.right_coeff = 1;
+			effect[SPRING].u.interactive.left_coeff = 1;
+			effect[SPRING].u.interactive.deadband = 0;
+			effect[SPRING].u.interactive.center = 0;
+			effect[SPRING].trigger.button = 0;
+			effect[SPRING].trigger.interval = 0;
+			effect[SPRING].replay.length = 0;
+			effect[SPRING].replay.delay = 0;
+
+			// load the effect
+			if (ioctl(jd, EVIOCSFF, &effect[effect_num]) == -1) {
+				fprintf(stderr, "Unable to upload effect: %i\n", effect_num);
+				return -1;
+			}
+			break;
+
+		case AFTERBURN1:
+			// Afterburner 1
+			effect[AFTERBURN1].type = FF_PERIODIC;
+			effect[AFTERBURN1].id = -1;
+			effect[AFTERBURN1].u.periodic.waveform = FF_SINE;
+			effect[AFTERBURN1].u.periodic.period = 200;	// Period
+			effect[AFTERBURN1].u.periodic.magnitude = 80;	// Magnitude
+			effect[AFTERBURN1].u.periodic.offset = 0;
+			effect[AFTERBURN1].u.periodic.phase = 0;
+			effect[AFTERBURN1].u.periodic.direction = 0;	// Angle
+			effect[AFTERBURN1].u.periodic.shape.attack_length = 0;	// Attack time
+			effect[AFTERBURN1].u.periodic.shape.attack_level = 0;
+			effect[AFTERBURN1].u.periodic.shape.fade_length = 0;	// Fade time
+			effect[AFTERBURN1].u.periodic.shape.fade_level = 0;
+			effect[AFTERBURN1].trigger.button = 0;
+			effect[AFTERBURN1].trigger.interval = 0;
+			effect[AFTERBURN1].replay.length = 0;  // Duration
+			effect[AFTERBURN1].replay.delay = 0;
+
+			// load the effect
+			if (ioctl(jd, EVIOCSFF, &effect[effect_num]) == -1) {
+				fprintf(stderr, "Unable to upload effect: %i\n", effect_num);
+				return -1;
+			}
+			break;
+
+		case AFTERBURN2:
+			// Afterburner 2
+			effect[AFTERBURN2].type = FF_PERIODIC;
+			effect[AFTERBURN2].id = -1;
+			effect[AFTERBURN2].u.periodic.waveform = FF_SINE;
+			effect[AFTERBURN2].u.periodic.period = 1200;	// Period
+			effect[AFTERBURN2].u.periodic.magnitude = 44;	// Magnitude
+			effect[AFTERBURN2].u.periodic.offset = 0;
+			effect[AFTERBURN2].u.periodic.phase = 0;
+			effect[AFTERBURN2].u.periodic.direction = 90;	// Angle
+			effect[AFTERBURN2].u.periodic.shape.attack_length = 0;	// Attack time
+			effect[AFTERBURN2].u.periodic.shape.attack_level = 0;
+			effect[AFTERBURN2].u.periodic.shape.fade_length = 0;	// Fade time
+			effect[AFTERBURN2].u.periodic.shape.fade_level = 0;
+			effect[AFTERBURN2].trigger.button = 0;
+			effect[AFTERBURN2].trigger.interval = 0;
+			effect[AFTERBURN2].replay.length = 0;  // Duration
+			effect[AFTERBURN2].replay.delay = 0;
+
+			// load the effect
+			if (ioctl(jd, EVIOCSFF, &effect[effect_num]) == -1) {
+				fprintf(stderr, "Unable to upload effect: %i\n", effect_num);
+				return -1;
+			}
+			break;
+
+		case DOCK:
+			// Dock
+			effect[DOCK].type = FF_PERIODIC;
+			effect[DOCK].id = -1;
+			effect[DOCK].u.periodic.waveform = FF_SQUARE;
+			effect[DOCK].u.periodic.period = 1000;	// Period
+			effect[DOCK].u.periodic.magnitude = 40;	// Magnitude
+			effect[DOCK].u.periodic.offset = 0;
+			effect[DOCK].u.periodic.phase = 0;
+			effect[DOCK].u.periodic.direction = 90;	// Angle
+			effect[DOCK].u.periodic.shape.attack_length = 0;	// Attack time
+			effect[DOCK].u.periodic.shape.attack_level = 0;
+			effect[DOCK].u.periodic.shape.fade_length = 0;	// Fade time
+			effect[DOCK].u.periodic.shape.fade_level = 0;
+			effect[DOCK].trigger.button = 0;
+			effect[DOCK].trigger.interval = 0;
+			effect[DOCK].replay.length = 1250;  // Duration
+			effect[DOCK].replay.delay = 0;
+
+			// load the effect
+			if (ioctl(jd, EVIOCSFF, &effect[effect_num]) == -1) {
+				fprintf(stderr, "Unable to upload effect: %i\n", effect_num);
+				return -1;
+			}
+			break;
+
+		case EXPLODE:
+			// Explode
+			effect[EXPLODE].type = FF_PERIODIC;
+			effect[EXPLODE].id = -1;
+			effect[EXPLODE].u.periodic.waveform = FF_SAW_DOWN;
+			effect[EXPLODE].u.periodic.period = 200;	// Period
+			effect[EXPLODE].u.periodic.magnitude = 0x4000;	// Magnitude
+			effect[EXPLODE].u.periodic.offset = 0;
+			effect[EXPLODE].u.periodic.phase = 0;
+			effect[EXPLODE].u.periodic.direction = 90;	// Angle
+			effect[EXPLODE].u.periodic.shape.attack_length = 0;	// Attack time
+			effect[EXPLODE].u.periodic.shape.attack_level = 0;
+			effect[EXPLODE].u.periodic.shape.fade_length = 5000;	// Fade time
+			effect[EXPLODE].u.periodic.shape.fade_level = 0;
+			effect[EXPLODE].trigger.button = 0;
+			effect[EXPLODE].trigger.interval = 0;
+			effect[EXPLODE].replay.length = 5000;  // Duration
+			effect[EXPLODE].replay.delay = 0;
+
+			// load the effect
+			if (ioctl(jd, EVIOCSFF, &effect[effect_num]) == -1) {
+				fprintf(stderr, "Unable to upload effect: %i\n", effect_num);
+				return -1;
+			}
+			break;
+
+		case DEATHROLL1:
+			// Deathroll 1
+			effect[DEATHROLL1].type = FF_PERIODIC;
+			effect[DEATHROLL1].id = -1;
+			effect[DEATHROLL1].u.periodic.waveform = FF_SINE;
+			effect[DEATHROLL1].u.periodic.period = 2000;	// Period
+			effect[DEATHROLL1].u.periodic.magnitude = 0x4000;	// Magnitude
+			effect[DEATHROLL1].u.periodic.offset = 0;
+			effect[DEATHROLL1].u.periodic.phase = 0;
+			effect[DEATHROLL1].u.periodic.direction = 0;	// Angle
+			effect[DEATHROLL1].u.periodic.shape.attack_length = 20000;	// Attack time
+			effect[DEATHROLL1].u.periodic.shape.attack_level = 0;
+			effect[DEATHROLL1].u.periodic.shape.fade_length = 0;	// Fade time
+			effect[DEATHROLL1].u.periodic.shape.fade_level = 0;
+			effect[DEATHROLL1].trigger.button = 0;
+			effect[DEATHROLL1].trigger.interval = 0;
+			effect[DEATHROLL1].replay.length = 0;  // Duration
+			effect[DEATHROLL1].replay.delay = 0;
+
+			// load the effect
+			if (ioctl(jd, EVIOCSFF, &effect[effect_num]) == -1) {
+				fprintf(stderr, "Unable to upload effect: %i\n", effect_num);
+				return -1;
+			}
+			break;
+
+		case DEATHROLL2:
+			// Deathroll 2
+			effect[DEATHROLL2].type = FF_PERIODIC;
+			effect[DEATHROLL2].id = -1;
+			effect[DEATHROLL2].u.periodic.waveform = FF_SINE;
+			effect[DEATHROLL2].u.periodic.period = 2000;	// Period
+			effect[DEATHROLL2].u.periodic.magnitude = 0x4000;	// Magnitude
+			effect[DEATHROLL2].u.periodic.offset = 0;
+			effect[DEATHROLL2].u.periodic.phase = 0;
+			effect[DEATHROLL2].u.periodic.direction = 90;	// Angle
+			effect[DEATHROLL2].u.periodic.shape.attack_length = 20000;	// Attack time
+			effect[DEATHROLL2].u.periodic.shape.attack_level = 0;
+			effect[DEATHROLL2].u.periodic.shape.fade_length = 0;	// Fade time
+			effect[DEATHROLL2].u.periodic.shape.fade_level = 0;
+			effect[DEATHROLL2].trigger.button = 0;
+			effect[DEATHROLL2].trigger.interval = 0;
+			effect[DEATHROLL2].replay.length = 0;  // Duration
+			effect[DEATHROLL2].replay.delay = 0;
+
+			// load the effect
+			if (ioctl(jd, EVIOCSFF, &effect[effect_num]) == -1) {
+				fprintf(stderr, "Unable to upload effect: %i\n", effect_num);
+				return -1;
+			}
+			break;
+	}
+
+	return 0;
+
+#endif
+}
+
+void joy_ff_stop_effects()
+{
+	joy_ff_afterburn_off();
+}
+
+void joy_ff_stop_single_effect(int effect_num)
+{
+#ifdef __gnu_linux__
+	stop.type = EV_FF;
+	stop.code = effect[effect_num].id;
+	stop.value = 0;
+
+	if (write(jd, (const void*) &stop, sizeof(stop)) == -1) {
+		fprintf(stderr, "Couldn't stop the effect!!\n");
+	}
+
+	// remove it from device memory
+	if (ioctl(jd, EVIOCRMFF, effect[effect_num].id) == -1) {
+		fprintf(stderr, "Couldn't remove from memory: %i\n", effect_num);
+	}
+#endif
+}
+
+void joy_ff_mission_init(vector v)
+{
+#ifdef __gnu_linux__
+	v.xyz.z = 0.0f;
+//	joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) - 1.3f) * 10.5f);
+	joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) + 1.3f) * 5.0f);
+//	joy_ff_handling_scaler = (int) (vm_vec_mag(&v) * 7.5f);
+#endif
+}
+
+void joy_ff_adjust_handling(int speed)
+{
+	STUB_FUNCTION;
+}
+
+void joy_ff_change_effect(int effect_num, int gain = -1, int dur = 0, int flags = -1)
+{
+	STUB_FUNCTION;
+}
+
+int joy_ff_effect_playing(int effect_num)
+{
+#ifdef __gnu_linux__
+	play.type = EV_FF;
+	play.code = effect[effect_num].id;
+	
+	if (play.value)
+		return 1;
+
+#endif
+	return 0;
+}
+
+void joy_ff_docked()
+{
+#ifdef __gnu_linux__
+	if (joy_ff_effect_playing(DOCK))
+		joy_ff_stop_single_effect(DOCK);
+	
+	joy_ff_start_effect(DOCK);
+#endif
+}
+
+void joy_ff_play_reload_effect()
+{
+#ifdef __gnu_linux__
+//	if (effect[DOCK].id) {
+		if (joy_ff_effect_playing(DOCK))
+			joy_ff_stop_single_effect(DOCK);
+
+		int gain = 50;
+
+		if (gain >= 0 && gain <= 100) {
+			ie.type = EV_FF;
+			ie.code = FF_GAIN;
+			ie.value = 0xFFFFUL * gain / 100;
+			if (write(jd, &ie, sizeof(ie)) == -1)
+				fprintf(stderr, "Can't set gain!\n");
+		}
+
+		joy_ff_start_effect(DOCK);
+//	}
+#endif
+}
+
+int Joy_ff_afterburning = 0;
+
+void joy_ff_afterburn_on()
+{
+#ifdef __gnu_linux__
+	if (joy_ff_effect_playing(AFTERBURN1)) {
+		joy_ff_stop_single_effect(AFTERBURN1);
+
+//		joy_ff_change_effect(AFTERBURN1, 50, INFINITE, DIEP_DURATION | DIEP_GAIN);
+		joy_ff_start_effect(AFTERBURN1);
+	}
+
+	if (joy_ff_effect_playing(AFTERBURN2)) {
+		joy_ff_stop_single_effect(AFTERBURN2);
+
+//		joy_ff_change_effect(AFTERBURN2, 50, INFINITE, DIEP_DURATION | DIEP_GAIN);
+		joy_ff_start_effect(AFTERBURN2);
+	}
+
+	nprintf(("Joystick", "FF: Afterburn started\n"));
+	Joy_ff_afterburning = 1;
+#endif
+}
+
+void joy_ff_afterburn_off()
+{
+#ifdef __gnu_linux__
+	if (!Joy_ff_afterburning)
+		return;
+
+	if (joy_ff_effect_playing(AFTERBURN1))
+		joy_ff_stop_single_effect(AFTERBURN1);
+	
+	if (joy_ff_effect_playing(AFTERBURN2))
+		joy_ff_stop_single_effect(AFTERBURN2);
+
+	Joy_ff_afterburning = 0;
+	nprintf(("Joystick", "FF: Afterburn stopped\n"));
+#endif
+}
+
+void joy_ff_deathroll()
+{
+#ifdef __gnu_linux__
+	if (joy_ff_effect_playing(DEATHROLL1))
+		joy_ff_stop_single_effect(DEATHROLL1);
+	
+	if (joy_ff_effect_playing(DEATHROLL2))
+		joy_ff_stop_single_effect(DEATHROLL2);
+
+	joy_ff_start_effect(DEATHROLL1);
+	joy_ff_start_effect(DEATHROLL2);
+#endif
+}
+
+void joy_ff_explode()
+{
+#ifdef __gnu_linux__
+	if (joy_ff_effect_playing(DEATHROLL1))
+		joy_ff_stop_single_effect(DEATHROLL1);
+	
+	if (joy_ff_effect_playing(DEATHROLL2))
+		joy_ff_stop_single_effect(DEATHROLL2);
+	
+	if (joy_ff_effect_playing(EXPLODE))
+		joy_ff_stop_single_effect(EXPLODE);
+
+	joy_ff_start_effect(EXPLODE);
+#endif
+}
+
+void joy_ff_fly_by(int mag)
+{
+#ifdef __gnu_linux__
+	int gain;
+
+	if (Joy_ff_afterburning)
+		return;
+
+	gain = mag * 12 + 40;
+	if (gain > 100)
+		gain = 100;
+
+	if (joy_ff_effect_playing(AFTERBURN1)) {
+		joy_ff_stop_single_effect(AFTERBURN1);
+	
+//		joy_ff_change_effect(AFTERBURN1, gain, 60 * mag + 4000, DIEP_DURATION | DIEP_GAIN);
+		joy_ff_start_effect(AFTERBURN1);
+	}
+
+	if (joy_ff_effect_playing(AFTERBURN2)) {
+		joy_ff_stop_single_effect(AFTERBURN2);
+
+//		joy_ff_change_effect(AFTERBURN2, gain, 60 * mag + 4000, DIEP_DURATION | DIEP_GAIN);
+		joy_ff_start_effect(AFTERBURN2);
+	}
+#endif
+}
+
+void joy_reacquire_ff()
+{
+	if (!Joy_ff_enabled)
+		return;
+
+	STUB_FUNCTION;
+}
+
+void joy_unacquire_ff()
+{
+}
+
+void joy_ff_play_dir_effect(float x, float y)
+{
+#ifdef __gnu_linux__
+	int idegs, imag;
+	float degs;
+
+	if (!Joy_ff_enabled)
+		return;
+
+//	if (!effect[HITEFFECT1].id || !effect[HITEFFECT2].id)
+//		return;
+
+	if (joy_ff_effect_playing(HITEFFECT1) || joy_ff_effect_playing(HITEFFECT2)) {
+		nprintf(("Joystick", "FF: HitEffect already playing.  Skipping\n"));
+		return;
+	}
+
+	if (Joy_ff_directional_hit_effect_enabled) {
+		if (x > 8000.0f)
+			x = 8000.0f;
+		else if (x < -8000.0f)
+			x = -8000.0f;
+
+		if (y > 8000.0f)
+			y = 8000.0f;
+		else if (y < -8000.0f)
+			y = -8000.0f;
+
+		imag = (int) fl_sqrt(x * x + y * y);
+		if (imag > 10000)
+			imag = 10000;
+
+		degs = (float)atan2(x, y);
+		idegs = (int) (degs * 18000.0f / PI) + 90;
+		while (idegs < 0)
+			idegs += 36000;
+
+		while (idegs >= 36000)
+			idegs -= 36000;
+/*
+		if (joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect1, idegs, imag), "HitEffect1") == DIERR_INPUTLOST) {
+			joy_reacquire_ff();
+			joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect1, idegs, imag), "HitEffect1");
+		}
+*/
+		idegs += 9000;
+		if (idegs >= 36000)
+			idegs -= 36000;
+/*
+		if (joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect2, idegs, imag), "HitEffect2") == DIERR_INPUTLOST) {
+			joy_reacquire_ff();
+			joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect2, idegs, imag), "HitEffect2");
+		}
+*/
+	}
+
+	joy_ff_start_effect(HITEFFECT1);
+	joy_ff_start_effect(HITEFFECT2);
+	//nprintf(("Joystick", "FF: Dir: %d, Mag = %d\n", idegs, imag));
+#endif
+}
+
+void joy_ff_play_vector_effect(vector *v, float scaler)
+{
+#ifdef __gnu_linux__
+	vector vf;
+	float x, y;
+
+//	nprintf(("Joystick", "FF: vec = { %f, %f, %f } s = %f\n", v->x, v->y, v->z, scaler));
+	vm_vec_copy_scale(&vf, v, scaler);
+	x = vf.xyz.x;
+	vf.xyz.x = 0.0f;
+
+	if (vf.xyz.y + vf.xyz.z < 0)
+		y = -vm_vec_mag(&vf);
+	else
+		y = vm_vec_mag(&vf);
+
+	joy_ff_play_dir_effect(-x, -y);
+#endif
+}
+
+static int secondary_ff_level = 0;
+
+void joy_ff_play_secondary_shoot(int gain)
+{
+#ifdef __gnu_linux__
+	if (!Joy_ff_enabled)
+		return;
+
+//	if (!effect[SECSHOOTEFFECT].id)
+//		return;
+
+	gain = gain + 25;
+	if (gain > 100)
+		gain = 100;
+
+	if (gain != secondary_ff_level) {
+		if (gain >= 0 && gain <= 100) {
+			ie.type = EV_FF;
+			ie.code = FF_GAIN;
+			ie.value = 0xFFFFUL * gain / 100;
+			if (write(jd, &ie, sizeof(ie)) == -1)
+				fprintf(stderr, "Can't set gain!\n");
+		}
+
+		effect[SECSHOOTEFFECT].replay.length = 1500 + gain * 25;
+
+		secondary_ff_level = gain;
+		nprintf(("Joystick", "FF: Secondary force = %d\n", gain));
+	}
+
+	joy_ff_stop_single_effect(SECSHOOTEFFECT);
+	joy_ff_start_effect(SECSHOOTEFFECT);
+#endif
+}
+
+static int primary_ff_level = 0;
+
+void joy_ff_play_primary_shoot(int gain)
+{
+#ifdef __gnu_linux__
+	if (!Joy_ff_enabled)
+		return;
+
+//	if (!effect[SHOOTEFFECT].id)
+//		return;
+
+	if (gain > 100)
+		gain = 100;
+
+	if (gain != primary_ff_level) {
+
+		if (gain >= 0 && gain <= 100) {
+			ie.type = EV_FF;
+			ie.code = FF_GAIN;
+			ie.value = 0xFFFFUL * gain / 100;
+			if (write(jd, &ie, sizeof(ie)) == -1)
+				fprintf(stderr, "Can't set gain!\n");
+		}
+		
+		primary_ff_level = gain;
+		nprintf(("Joystick", "FF: Primary force = %d\n", gain));
+		fprintf(stderr, "Primary force = %d\n", gain);
+
+	}
+
+	if (joy_ff_effect_playing(SHOOTEFFECT))
+		joy_ff_stop_single_effect(SHOOTEFFECT);
+
+	joy_ff_start_effect(SHOOTEFFECT);
+#endif
+}
diff -uNr /home/tazman/src/cvs/freespace2/src/io/joy-unix.cpp src/io/joy-unix.cpp
--- /home/tazman/src/cvs/freespace2/src/io/joy-unix.cpp	2002-06-17 05:28:52.000000000 +0500
+++ src/io/joy-unix.cpp	2003-04-04 23:27:31.000000000 +0500
@@ -56,6 +56,8 @@
 	sdljoy = NULL;
 	
 	SDL_QuitSubSystem (SDL_INIT_JOYSTICK);
+	
+	joy_ff_shutdown();
 }
 
 void joy_get_caps (int max)
@@ -396,6 +398,8 @@
 		}
 	}
 
+	joy_ff_init();
+
 	return joy_num_sticks;
 }
 
@@ -424,73 +428,3 @@
 	
 	return 1;
 }
-
-void joy_ff_adjust_handling(int speed)
-{
-//	STUB_FUNCTION;
-}
-
-void joy_ff_afterburn_off()
-{
-//	STUB_FUNCTION;
-}
-
-void joy_ff_afterburn_on()
-{
-//	STUB_FUNCTION;
-}
-
-void joy_ff_deathroll()
-{
-//	STUB_FUNCTION;
-}
-
-void joy_ff_docked()
-{
-//	STUB_FUNCTION;
-}
-
-void joy_ff_explode()
-{
-//	STUB_FUNCTION;
-}
-
-void joy_ff_fly_by(int mag)
-{
-//	STUB_FUNCTION;
-}
-
-void joy_ff_mission_init(vector v)
-{
-//	STUB_FUNCTION;
-}
-
-void joy_ff_play_dir_effect(float x, float y)
-{
-//	STUB_FUNCTION;
-}
-
-void joy_ff_play_primary_shoot(int gain)
-{
-//	STUB_FUNCTION;
-}
-
-void joy_ff_play_reload_effect()
-{
-//	STUB_FUNCTION;
-}
-
-void joy_ff_play_secondary_shoot(int gain)
-{
-//	STUB_FUNCTION;
-}
-
-void joy_ff_play_vector_effect(vector *v, float scaler)
-{
-//	STUB_FUNCTION;
-}
-
-void joy_ff_stop_effects()
-{
-	joy_ff_afterburn_off();
-}


More information about the freespace2 mailing list