Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/presentation/src/external/ois/linux/LinuxForceFeedback.cpp @ 8600

Last change on this file since 8600 was 5781, checked in by rgrieder, 15 years ago

Reverted trunk again. We might want to find a way to delete these revisions again (x3n's changes are still available as diff in the commit mails).

  • Property svn:eol-style set to native
File size: 17.1 KB
Line 
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}
Note: See TracBrowser for help on using the repository browser.