PageRenderTime 46ms CodeModel.GetById 8ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

/src/ois/src/linux/LinuxForceFeedback.cpp

https://bitbucket.org/cabalistic/ogredeps/
C++ | 559 lines | 394 code | 96 blank | 69 comment | 37 complexity | 077dec65f3adf49c4034a80dd9ad4197 MD5 | raw file
  1/*
  2The zlib/libpng License
  3
  4Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
  5
  6This software is provided 'as-is', without any express or implied warranty. In no event will
  7the authors be held liable for any damages arising from the use of this software.
  8
  9Permission is granted to anyone to use this software for any purpose, including commercial
 10applications, and to alter it and redistribute it freely, subject to the following
 11restrictions:
 12
 13    1. The origin of this software must not be misrepresented; you must not claim that
 14		you wrote the original software. If you use this software in a product,
 15		an acknowledgment in the product documentation would be appreciated but is
 16		not required.
 17
 18    2. Altered source versions must be plainly marked as such, and must not be
 19		misrepresented as being the original software.
 20
 21    3. This notice may not be removed or altered from any source distribution.
 22*/
 23#include "linux/LinuxForceFeedback.h"
 24#include "OISException.h"
 25
 26#include <cstdlib>
 27#include <errno.h>
 28#include <memory.h>
 29
 30using namespace OIS;
 31
 32// 0 = No trace; 1 = Important traces; 2 = Debug traces
 33#define OIS_LINUX_JOYFF_DEBUG 1
 34
 35#ifdef OIS_LINUX_JOYFF_DEBUG
 36# include <iostream>
 37  using namespace std;
 38#endif
 39
 40//--------------------------------------------------------------//
 41LinuxForceFeedback::LinuxForceFeedback(int deviceID) :
 42	ForceFeedback(), mJoyStick(deviceID)
 43{
 44}
 45
 46//--------------------------------------------------------------//
 47LinuxForceFeedback::~LinuxForceFeedback()
 48{
 49	// Unload all effects.
 50	for(EffectList::iterator i = mEffectList.begin(); i != mEffectList.end(); ++i )
 51	{
 52		struct ff_effect *linEffect = i->second;
 53		if( linEffect )
 54			_unload(linEffect->id);
 55	}
 56
 57	mEffectList.clear();
 58}
 59
 60//--------------------------------------------------------------//
 61unsigned short LinuxForceFeedback::getFFMemoryLoad()
 62{
 63	int nEffects = -1;
 64	if (ioctl(mJoyStick, EVIOCGEFFECTS, &nEffects) == -1)
 65		OIS_EXCEPT(E_General, "Unknown error reading max number of uploaded effects.");
 66#if (OIS_LINUX_JOYFF_DEBUG > 1)
 67	cout << "LinuxForceFeedback("<< mJoyStick  
 68		 << ") : Read device max number of uploaded effects : " << nEffects << endl;
 69#endif
 70
 71	return (unsigned short int)(nEffects > 0 ? 100.0*mEffectList.size()/nEffects : 100);
 72}
 73
 74//--------------------------------------------------------------//
 75void LinuxForceFeedback::setMasterGain(float value)
 76{
 77	if (!mSetGainSupport)
 78	{
 79#if (OIS_LINUX_JOYFF_DEBUG > 0)
 80		cout << "LinuxForceFeedback("<< mJoyStick << ") : Setting master gain " 
 81			 << "is not supported by the device" << endl;
 82#endif
 83		return;
 84	}
 85
 86	struct input_event event;
 87
 88	memset(&event, 0, sizeof(event));
 89	event.type = EV_FF;
 90	event.code = FF_GAIN;
 91	if (value < 0.0)
 92		value = 0.0;
 93	else if (value > 1.0)
 94		value = 1.0;
 95	event.value = (__s32)(value * 0xFFFFUL);
 96
 97#if (OIS_LINUX_JOYFF_DEBUG > 0)
 98	cout << "LinuxForceFeedback("<< mJoyStick << ") : Setting master gain to " 
 99		 << value << " => " << event.value << endl;
100#endif
101
102	if (write(mJoyStick, &event, sizeof(event)) != sizeof(event)) {
103		OIS_EXCEPT(E_General, "Unknown error changing master gain.");
104	}
105}
106
107//--------------------------------------------------------------//
108void LinuxForceFeedback::setAutoCenterMode(bool enabled)
109{
110	if (!mSetAutoCenterSupport)
111	{
112#if (OIS_LINUX_JOYFF_DEBUG > 0)
113		cout << "LinuxForceFeedback("<< mJoyStick << ") : Setting auto-center mode " 
114			 << "is not supported by the device" << endl;
115#endif
116		return;
117	}
118
119	struct input_event event;
120
121	memset(&event, 0, sizeof(event));
122	event.type = EV_FF;
123	event.code = FF_AUTOCENTER;
124	event.value = (__s32)(enabled*0xFFFFFFFFUL);
125
126#if (OIS_LINUX_JOYFF_DEBUG > 0)
127	cout << "LinuxForceFeedback("<< mJoyStick << ") : Toggling auto-center to " 
128		 << enabled << " => 0x" << hex << event.value << dec << endl;
129#endif
130
131	if (write(mJoyStick, &event, sizeof(event)) != sizeof(event)) {
132		OIS_EXCEPT(E_General, "Unknown error toggling auto-center.");
133	}
134}
135
136//--------------------------------------------------------------//
137void LinuxForceFeedback::upload( const Effect* effect )
138{
139	switch( effect->force )
140	{
141		case OIS::Effect::ConstantForce: 
142			_updateConstantEffect(effect);	
143			break;
144		case OIS::Effect::ConditionalForce: 
145			_updateConditionalEffect(effect);
146			break;
147		case OIS::Effect::PeriodicForce: 
148			_updatePeriodicEffect(effect);
149			break;
150		case OIS::Effect::RampForce: 
151			_updateRampEffect(effect);	
152			break;
153		case OIS::Effect::CustomForce: 
154			//_updateCustomEffect(effect);
155			//break;
156		default: 
157			OIS_EXCEPT(E_NotImplemented, "Requested force not implemented yet, sorry!"); 
158			break;
159	}
160}
161
162//--------------------------------------------------------------//
163void LinuxForceFeedback::modify( const Effect* effect )
164{
165	upload(effect);
166}
167
168//--------------------------------------------------------------//
169void LinuxForceFeedback::remove( const Effect* effect )
170{
171	//Get the effect - if it exists
172	EffectList::iterator i = mEffectList.find(effect->_handle);
173	if( i != mEffectList.end() )
174	{
175		struct ff_effect *linEffect = i->second;
176		if( linEffect )
177		{
178			_stop(effect->_handle);
179
180			_unload(effect->_handle);
181
182			free(linEffect);
183
184			mEffectList.erase(i);
185		}
186		else
187			mEffectList.erase(i);
188	}
189}
190
191//--------------------------------------------------------------//
192// To Signed16/Unsigned15 safe conversions
193#define MaxUnsigned15Value 0x7FFF
194#define toUnsigned15(value) \
195	(__u16)((value) < 0 ? 0 : ((value) > MaxUnsigned15Value ? MaxUnsigned15Value : (value)))
196
197#define MaxSigned16Value  0x7FFF
198#define MinSigned16Value -0x7FFF
199#define toSigned16(value) \
200  (__s16)((value) < MinSigned16Value ? MinSigned16Value : ((value) > MaxSigned16Value ? MaxSigned16Value : (value)))
201
202// OIS to Linux duration
203#define LinuxInfiniteDuration 0xFFFF
204#define OISDurationUnitMS 1000 // OIS duration unit (microseconds), expressed in milliseconds (theLinux duration unit)
205
206// linux/input.h : All duration values are expressed in ms. Values above 32767 ms (0x7fff)
207//                 should not be used and have unspecified results.
208#define LinuxDuration(oisDuration) ((oisDuration) == Effect::OIS_INFINITE ? LinuxInfiniteDuration \
209									: toUnsigned15((oisDuration)/OISDurationUnitMS))
210
211
212// OIS to Linux levels
213#define OISMaxLevel 10000
214#define LinuxMaxLevel 0x7FFF
215
216// linux/input.h : Valid range for the attack and fade levels is 0x0000 - 0x7fff
217#define LinuxPositiveLevel(oisLevel) toUnsigned15(LinuxMaxLevel*(long)(oisLevel)/OISMaxLevel)
218
219#define LinuxSignedLevel(oisLevel) toSigned16(LinuxMaxLevel*(long)(oisLevel)/OISMaxLevel)
220
221
222//--------------------------------------------------------------//
223void LinuxForceFeedback::_setCommonProperties(struct ff_effect *event, 
224											  struct ff_envelope *ffenvelope, 
225											  const Effect* effect, const Envelope *envelope )
226{
227	memset(event, 0, sizeof(struct ff_effect));
228
229	if (envelope && ffenvelope && envelope->isUsed()) {
230		ffenvelope->attack_length = LinuxDuration(envelope->attackLength);
231		ffenvelope->attack_level = LinuxPositiveLevel(envelope->attackLevel);
232		ffenvelope->fade_length = LinuxDuration(envelope->fadeLength);
233		ffenvelope->fade_level = LinuxPositiveLevel(envelope->fadeLevel);
234	}
235	
236#if (OIS_LINUX_JOYFF_DEBUG > 1)
237	cout << endl;
238	if (envelope && ffenvelope)
239	{
240		cout << "  Enveloppe :" << endl
241			 << "    AttackLen : " << envelope->attackLength
242			 << " => " << ffenvelope->attack_length << endl 
243			 << "    AttackLvl : " << envelope->attackLevel
244			 << " => " << ffenvelope->attack_level << endl 
245			 << "    FadeLen   : " << envelope->fadeLength
246			 << " => " << ffenvelope->fade_length << endl
247			 << "    FadeLvl   : " << envelope->fadeLevel
248			 << " => " << ffenvelope->fade_level << endl;
249	}
250#endif
251	
252	event->direction = (__u16)(1 + (effect->direction*45.0+135.0)*0xFFFFUL/360.0);
253
254#if (OIS_LINUX_JOYFF_DEBUG > 1)
255	cout << "  Direction : " << Effect::getDirectionName(effect->direction)
256		 << " => 0x" << hex << event->direction << dec << endl;
257#endif
258
259	// TODO trigger_button 0 vs. -1
260	event->trigger.button = effect->trigger_button; // < 0 ? 0 : effect->trigger_button;
261	event->trigger.interval = LinuxDuration(effect->trigger_interval);
262
263#if (OIS_LINUX_JOYFF_DEBUG > 1)
264	cout << "  Trigger :" << endl
265		 << "    Button   : " << effect->trigger_button 
266		 << " => " << event->trigger.button << endl
267		 << "    Interval : " << effect->trigger_interval 
268		 << " => " << event->trigger.interval << endl;
269#endif
270
271	event->replay.length = LinuxDuration(effect->replay_length);
272	event->replay.delay = LinuxDuration(effect->replay_delay);
273
274#if (OIS_LINUX_JOYFF_DEBUG > 1)
275	cout << "  Replay :" << endl
276		 << "    Length : " << effect->replay_length 
277		 << " => " << event->replay.length << endl
278		 << "    Delay  : " << effect->replay_delay 
279		 << " => " << event->replay.delay << endl;
280#endif
281}
282
283//--------------------------------------------------------------//
284void LinuxForceFeedback::_updateConstantEffect( const Effect* eff )
285{
286	struct ff_effect event;
287
288	ConstantEffect *effect = static_cast<ConstantEffect*>(eff->getForceEffect());
289
290	_setCommonProperties(&event, &event.u.constant.envelope, eff, &effect->envelope);
291
292	event.type = FF_CONSTANT;
293	event.id = -1;
294
295	event.u.constant.level = LinuxSignedLevel(effect->level);
296
297#if (OIS_LINUX_JOYFF_DEBUG > 1)
298	cout << "  Level : " << effect->level
299		 << " => " << event.u.constant.level << endl;
300#endif
301
302	_upload(&event, eff);
303}
304
305//--------------------------------------------------------------//
306void LinuxForceFeedback::_updateRampEffect( const Effect* eff )
307{
308	struct ff_effect event;
309
310	RampEffect *effect = static_cast<RampEffect*>(eff->getForceEffect());
311
312	_setCommonProperties(&event, &event.u.constant.envelope, eff, &effect->envelope);
313
314	event.type = FF_RAMP;
315	event.id = -1;
316
317	event.u.ramp.start_level = LinuxSignedLevel(effect->startLevel);
318	event.u.ramp.end_level = LinuxSignedLevel(effect->endLevel);
319
320#if (OIS_LINUX_JOYFF_DEBUG > 1)
321	cout << "  StartLevel : " << effect->startLevel
322		 << " => " << event.u.ramp.start_level << endl
323		 << "  EndLevel   : " << effect->endLevel
324		 << " => " << event.u.ramp.end_level << endl;
325#endif
326
327	_upload(&event, eff);
328}
329
330//--------------------------------------------------------------//
331void LinuxForceFeedback::_updatePeriodicEffect( const Effect* eff )
332{
333	struct ff_effect event;
334
335	PeriodicEffect *effect = static_cast<PeriodicEffect*>(eff->getForceEffect());
336
337	_setCommonProperties(&event, &event.u.periodic.envelope, eff, &effect->envelope);
338
339	event.type = FF_PERIODIC;
340	event.id = -1;
341
342	switch( eff->type )
343	{
344		case OIS::Effect::Square:
345			event.u.periodic.waveform = FF_SQUARE;
346			break;
347		case OIS::Effect::Triangle:
348			event.u.periodic.waveform = FF_TRIANGLE;
349			break;
350		case OIS::Effect::Sine:
351			event.u.periodic.waveform = FF_SINE;
352			break;
353		case OIS::Effect::SawToothUp:
354			event.u.periodic.waveform = FF_SAW_UP;
355			break;
356		case OIS::Effect::SawToothDown:
357			event.u.periodic.waveform = FF_SAW_DOWN;
358			break;
359		// Note: No support for Custom periodic force effect for the moment
360		//case OIS::Effect::Custom:
361			//event.u.periodic.waveform = FF_CUSTOM;
362			//break;
363		default:
364			OIS_EXCEPT(E_General, "No such available effect for Periodic force!"); 
365			break;
366	}
367
368	event.u.periodic.period    = LinuxDuration(effect->period);
369	event.u.periodic.magnitude = LinuxPositiveLevel(effect->magnitude);
370	event.u.periodic.offset    = LinuxPositiveLevel(effect->offset);
371	event.u.periodic.phase     = (__u16)(effect->phase*event.u.periodic.period/36000.0); // ?????
372
373	// Note: No support for Custom periodic force effect for the moment
374	event.u.periodic.custom_len = 0;
375	event.u.periodic.custom_data = 0;
376
377#if (OIS_LINUX_JOYFF_DEBUG > 1)
378	cout << "  Magnitude : " << effect->magnitude
379		 << " => " << event.u.periodic.magnitude << endl
380		 << "  Period    : " << effect->period
381		 << " => " << event.u.periodic.period  << endl
382		 << "  Offset    : " << effect->offset
383		 << " => " << event.u.periodic.offset << endl
384		 << "  Phase     : " << effect->phase
385		 << " => " << event.u.periodic.phase << endl;
386#endif
387
388	_upload(&event, eff);
389}
390
391//--------------------------------------------------------------//
392void LinuxForceFeedback::_updateConditionalEffect( const Effect* eff )
393{
394	struct ff_effect event;
395
396	ConditionalEffect *effect = static_cast<ConditionalEffect*>(eff->getForceEffect());
397
398	_setCommonProperties(&event, NULL, eff, NULL);
399
400	switch( eff->type )
401	{
402		case OIS::Effect::Friction:
403			event.type = FF_FRICTION; 
404			break;
405		case OIS::Effect::Damper:
406			event.type = FF_DAMPER; 
407			break;
408		case OIS::Effect::Inertia:
409			event.type = FF_INERTIA; 
410			break;
411		case OIS::Effect::Spring:
412			event.type = FF_SPRING;
413			break;
414		default:
415			OIS_EXCEPT(E_General, "No such available effect for Conditional force!"); 
416			break;
417	}
418
419	event.id = -1;
420
421	event.u.condition[0].right_saturation = LinuxSignedLevel(effect->rightSaturation);
422	event.u.condition[0].left_saturation  = LinuxSignedLevel(effect->leftSaturation);
423	event.u.condition[0].right_coeff      = LinuxSignedLevel(effect->rightCoeff);
424	event.u.condition[0].left_coeff       = LinuxSignedLevel(effect->leftCoeff);
425	event.u.condition[0].deadband         = LinuxPositiveLevel(effect->deadband);// Unit ?? 
426	event.u.condition[0].center           = LinuxSignedLevel(effect->center); // Unit ?? TODO ?
427
428	// TODO support for second condition
429	event.u.condition[1] = event.u.condition[0];
430
431#if (OIS_LINUX_JOYFF_DEBUG > 1)
432	cout << "  Condition[0] : " << endl
433		 << "    RightSaturation  : " << effect->rightSaturation
434		 << " => " << event.u.condition[0].right_saturation << endl
435		 << "    LeftSaturation   : " << effect->leftSaturation
436		 << " => " << event.u.condition[0]. left_saturation << endl
437		 << "    RightCoefficient : " << effect->rightCoeff
438		 << " => " << event.u.condition[0].right_coeff << endl
439		 << "    LeftCoefficient : " << effect->leftCoeff
440		 << " => " << event.u.condition[0].left_coeff << endl
441		 << "    DeadBand        : " << effect->deadband
442		 << " => " << event.u.condition[0].deadband  << endl
443		 << "    Center          : " << effect->center
444		 << " => " << event.u.condition[0].center << endl;
445	cout << "  Condition[1] : Not implemented" << endl;
446#endif
447	_upload(&event, eff);
448}
449
450//--------------------------------------------------------------//
451void LinuxForceFeedback::_upload( struct ff_effect* ffeffect, const Effect* effect)
452{
453	struct ff_effect *linEffect = 0;
454
455	//Get the effect - if it exists
456	EffectList::iterator i = mEffectList.find(effect->_handle);
457	//It has been created already
458	if( i != mEffectList.end() )
459		linEffect = i->second;
460
461	if( linEffect == 0 )
462	{
463#if (OIS_LINUX_JOYFF_DEBUG > 1)
464		cout << endl << "LinuxForceFeedback("<< mJoyStick << ") : Adding new effect : " 
465			 << Effect::getEffectTypeName(effect->type) << endl;
466#endif
467
468		//This effect has not yet been created, so create it in the device
469		if (ioctl(mJoyStick, EVIOCSFF, ffeffect) == -1) {
470			// TODO device full check
471			// OIS_EXCEPT(E_DeviceFull, "Remove an effect before adding more!");
472			OIS_EXCEPT(E_General, "Unknown error creating effect (may be the device is full)->..");
473		}
474
475		// Save returned effect handle
476		effect->_handle = ffeffect->id;
477
478		// Save a copy of the uploaded effect for later simple modifications
479		linEffect = (struct ff_effect *)calloc(1, sizeof(struct ff_effect));
480		memcpy(linEffect, ffeffect, sizeof(struct ff_effect));
481
482		mEffectList[effect->_handle] = linEffect;
483
484		// Start playing the effect.
485		_start(effect->_handle);
486	}
487	else
488	{
489#if (OIS_LINUX_JOYFF_DEBUG > 1)
490		cout << endl << "LinuxForceFeedback("<< mJoyStick << ") : Replacing effect : " 
491			 << Effect::getEffectTypeName(effect->type) << endl;
492#endif
493
494		// Keep same id/handle, as this is just an update in the device.
495		ffeffect->id = effect->_handle;
496
497		// Update effect in the device.
498		if (ioctl(mJoyStick, EVIOCSFF, ffeffect) == -1) {
499			OIS_EXCEPT(E_General, "Unknown error updating an effect->..");
500		}
501
502		// Update local linEffect for next time.
503		memcpy(linEffect, ffeffect, sizeof(struct ff_effect));
504	}
505
506#if (OIS_LINUX_JOYFF_DEBUG > 1)
507	cout << "LinuxForceFeedback("<< mJoyStick 
508		 << ") : Effect handle : " << effect->_handle << endl;
509#endif
510}
511
512//--------------------------------------------------------------//
513void LinuxForceFeedback::_stop( int handle) {
514	struct input_event stop;
515
516	stop.type = EV_FF;
517	stop.code = handle;
518	stop.value = 0;
519
520#if (OIS_LINUX_JOYFF_DEBUG > 1)
521	cout << endl << "LinuxForceFeedback("<< mJoyStick 
522		 << ") : Stopping effect with handle " << handle << endl;
523#endif
524
525	if (write(mJoyStick, &stop, sizeof(stop)) != sizeof(stop)) {
526		OIS_EXCEPT(E_General, "Unknown error stopping effect->..");
527	}
528}
529
530//--------------------------------------------------------------//
531void LinuxForceFeedback::_start( int handle) {
532	struct input_event play;
533
534	play.type = EV_FF;
535	play.code = handle;
536	play.value = 1; // Play once.
537
538#if (OIS_LINUX_JOYFF_DEBUG > 1)
539	cout << endl << "LinuxForceFeedback("<< mJoyStick 
540		 << ") : Starting effect with handle " << handle << endl;
541#endif
542
543	if (write(mJoyStick, &play, sizeof(play)) != sizeof(play)) {
544		OIS_EXCEPT(E_General, "Unknown error playing effect->..");
545	}
546}
547
548//--------------------------------------------------------------//
549void LinuxForceFeedback::_unload( int handle)
550{
551#if (OIS_LINUX_JOYFF_DEBUG > 1)
552	cout << endl << "LinuxForceFeedback("<< mJoyStick 
553		 << ") : Removing effect with handle " << handle << endl;
554#endif
555
556	if (ioctl(mJoyStick, EVIOCRMFF, handle) == -1) {
557		OIS_EXCEPT(E_General, "Unknown error removing effect->..");
558	}
559}