Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core4/src/core/input/KeyBinder.cc @ 4017

Last change on this file since 4017 was 3306, checked in by rgrieder, 15 years ago

Keybindings should now map to the correct joy stick by device name (like "WingMan Action Pad" or so).

  • Property svn:eol-style set to native
File size: 18.4 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Reto Grieder
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29/**
30 @file
31 @brief Implementation of the different input handlers.
32 */
33
34#include "KeyBinder.h"
35
36#include "util/Convert.h"
37#include "util/Debug.h"
38#include "core/ConfigValueIncludes.h"
39#include "core/CoreIncludes.h"
40#include "core/ConfigFileManager.h"
41#include "InputCommands.h"
42#include "JoyStick.h"
43
44namespace orxonox
45{
46    /**
47    @brief
48        Constructor that does as little as necessary.
49    */
50    KeyBinder::KeyBinder()
51        : deriveTime_(0.0f)
52    {
53        mouseRelative_[0] = 0;
54        mouseRelative_[1] = 0;
55        mousePosition_[0] = 0;
56        mousePosition_[1] = 0;
57
58        joyStickButtons_.reserve(1000);
59        joyStickAxes_.reserve(1000);
60
61        RegisterRootObject(KeyBinder);
62
63        // intialise all buttons and half axes to avoid creating everything with 'new'
64        // keys
65        for (unsigned int i = 0; i < KeyCode::numberOfKeys; i++)
66        {
67            std::string keyname = KeyCode::ByString[i];
68            if (!keyname.empty())
69                keys_[i].name_ = std::string("Key") + keyname;
70            else
71                keys_[i].name_ = "";
72            keys_[i].paramCommandBuffer_ = &paramCommandBuffer_;
73            keys_[i].groupName_ = "Keys";
74        }
75        // mouse buttons plus 4 mouse wheel buttons only 'generated' by KeyBinder
76        const char* const mouseWheelNames[] = { "Wheel1Down", "Wheel1Up", "Wheel2Down", "Wheel2Up" };
77        for (unsigned int i = 0; i < numberOfMouseButtons_; i++)
78        {
79            std::string nameSuffix;
80            if (i < MouseButtonCode::numberOfButtons)
81                nameSuffix = MouseButtonCode::ByString[i];
82            else
83                nameSuffix = mouseWheelNames[i - MouseButtonCode::numberOfButtons];
84            mouseButtons_[i].name_ = nameSuffix;
85            mouseButtons_[i].paramCommandBuffer_ = &paramCommandBuffer_;
86            mouseButtons_[i].groupName_ = "MouseButtons";
87        }
88        // mouse axes
89        for (unsigned int i = 0; i < MouseAxisCode::numberOfAxes * 2; i++)
90        {
91            mouseAxes_[i].name_ = MouseAxisCode::ByString[i / 2];
92            if (i & 1)
93                mouseAxes_[i].name_ += "Pos";
94            else
95                mouseAxes_[i].name_ += "Neg";
96            mouseAxes_[i].paramCommandBuffer_ = &paramCommandBuffer_;
97            mouseAxes_[i].groupName_ = "MouseAxes";
98        }
99
100        // We might not even load any bindings at all (KeyDetector for instance)
101        this->configFile_ = ConfigFileType::NoType;
102
103        // initialise joy sticks separatly to allow for reloading
104        this->JoyStickQuantityChanged(this->getJoyStickList());
105
106        // set them here to use allHalfAxes_
107        setConfigValues();
108    }
109
110    /**
111    @brief
112        Destructor
113    */
114    KeyBinder::~KeyBinder()
115    {
116        // almost no destructors required because most of the arrays are static.
117        clearBindings(); // does some destruction work
118    }
119
120    /**
121    @brief
122        Loader for the key bindings, managed by config values.
123    */
124    void KeyBinder::setConfigValues()
125    {
126        SetConfigValue(analogThreshold_, 0.05f)
127            .description("Threshold for analog axes until which the state is 0.");
128        SetConfigValue(bFilterAnalogNoise_, false)
129            .description("Specifies whether to filter small analog values like joy stick fluctuations.");
130        SetConfigValue(mouseSensitivity_, 1.0f)
131            .description("Mouse sensitivity.");
132        SetConfigValue(bDeriveMouseInput_, false)
133            .description("Whether or not to derive moues movement for the absolute value.");
134        SetConfigValue(derivePeriod_, 0.05f)
135            .description("Accuracy of the mouse input deriver. The higher the more precise, but laggier.");
136        SetConfigValue(mouseSensitivityDerived_, 1.0f)
137            .description("Mouse sensitivity if mouse input is derived.");
138        SetConfigValue(mouseWheelStepSize_, 120)
139            .description("Equals one step of the mousewheel.");
140        SetConfigValue(buttonThreshold_, 0.80f)
141            .description("Threshold for analog axes until which the button is not pressed.")
142            .callback(this, &KeyBinder::buttonThresholdChanged);
143    }
144
145    void KeyBinder::buttonThresholdChanged()
146    {
147        for (unsigned int i = 0; i < allHalfAxes_.size(); i++)
148            if (!allHalfAxes_[i]->bButtonThresholdUser_)
149                allHalfAxes_[i]->buttonThreshold_ = this->buttonThreshold_;
150    }
151
152    void KeyBinder::JoyStickQuantityChanged(const std::vector<JoyStick*>& joyStickList)
153    {
154        unsigned int oldValue = joySticks_.size();
155        joySticks_ = joyStickList;
156
157        // initialise joy stick bindings
158        initialiseJoyStickBindings();
159
160        // collect all Buttons and HalfAxes again
161        compilePointerLists();
162
163        // load the bindings if required
164        if (configFile_ != ConfigFileType::NoType)
165        {
166            for (unsigned int iDev = oldValue; iDev < joySticks_.size(); ++iDev)
167            {
168                for (unsigned int i = 0; i < JoyStickButtonCode::numberOfButtons; ++i)
169                    joyStickButtons_[iDev][i].readConfigValue(this->configFile_);
170                for (unsigned int i = 0; i < JoyStickAxisCode::numberOfAxes * 2; ++i)
171                    joyStickAxes_[iDev][i].readConfigValue(this->configFile_);
172            }
173        }
174
175        // Set the button threshold for potential new axes
176        buttonThresholdChanged();
177    }
178
179    void KeyBinder::initialiseJoyStickBindings()
180    {
181        this->joyStickAxes_.resize(joySticks_.size());
182        this->joyStickButtons_.resize(joySticks_.size());
183
184        // reinitialise all joy stick binings (doesn't overwrite the old ones)
185        for (unsigned int iDev = 0; iDev < joySticks_.size(); iDev++)
186        {
187            std::string deviceName = joySticks_[iDev]->getDeviceName();
188            // joy stick buttons
189            for (unsigned int i = 0; i < JoyStickButtonCode::numberOfButtons; i++)
190            {
191                joyStickButtons_[iDev][i].name_ = JoyStickButtonCode::ByString[i];
192                joyStickButtons_[iDev][i].paramCommandBuffer_ = &paramCommandBuffer_;
193                joyStickButtons_[iDev][i].groupName_ = "JoyStickButtons_" + deviceName;
194            }
195            // joy stick axes
196            for (unsigned int i = 0; i < JoyStickAxisCode::numberOfAxes * 2; i++)
197            {
198                joyStickAxes_[iDev][i].name_ = JoyStickAxisCode::ByString[i / 2];
199                if (i & 1)
200                    joyStickAxes_[iDev][i].name_ += "Pos";
201                else
202                    joyStickAxes_[iDev][i].name_ += "Neg";
203                joyStickAxes_[iDev][i].paramCommandBuffer_ = &paramCommandBuffer_;
204                joyStickAxes_[iDev][i].groupName_ = "JoyStickAxes_" + deviceName;
205            }
206        }
207    }
208
209    void KeyBinder::compilePointerLists()
210    {
211        allButtons_.clear();
212        allHalfAxes_.clear();
213
214        // Note: Don't include the dummy keys which don't actually exist in OIS but have a number
215        for (unsigned int i = 0; i < KeyCode::numberOfKeys; i++)
216            if (!keys_[i].name_.empty())
217                allButtons_[keys_[i].groupName_ + "." + keys_[i].name_] = keys_ + i;
218        for (unsigned int i = 0; i < numberOfMouseButtons_; i++)
219            allButtons_[mouseButtons_[i].groupName_ + "." + mouseButtons_[i].name_] = mouseButtons_ + i;
220        for (unsigned int i = 0; i < MouseAxisCode::numberOfAxes * 2; i++)
221        {
222            allButtons_[mouseAxes_[i].groupName_ + "." + mouseAxes_[i].name_] = mouseAxes_ + i;
223            allHalfAxes_.push_back(mouseAxes_ + i);
224        }
225        for (unsigned int iDev = 0; iDev < joySticks_.size(); iDev++)
226        {
227            for (unsigned int i = 0; i < JoyStickButtonCode::numberOfButtons; i++)
228                allButtons_[joyStickButtons_[iDev][i].groupName_ + "." + joyStickButtons_[iDev][i].name_] = &(joyStickButtons_[iDev][i]);
229            for (unsigned int i = 0; i < JoyStickAxisCode::numberOfAxes * 2; i++)
230            {
231                allButtons_[joyStickAxes_[iDev][i].groupName_ + "." + joyStickAxes_[iDev][i].name_] = &(joyStickAxes_[iDev][i]);
232                allHalfAxes_.push_back(&(joyStickAxes_[iDev][i]));
233            }
234        }
235    }
236
237    /**
238    @brief
239        Loads the key and button bindings.
240    @return
241        True if loading succeeded.
242    */
243    void KeyBinder::loadBindings(const std::string& filename)
244    {
245        COUT(3) << "KeyBinder: Loading key bindings..." << std::endl;
246
247        if (filename.empty())
248            return;
249
250        if (this->configFile_ == ConfigFileType::NoType)
251        {
252            // Get a new ConfigFileType from the ConfigFileManager
253            this->configFile_ = ConfigFileManager::getInstance().getNewConfigFileType();
254        }
255
256        ConfigFileManager::getInstance().setFilename(this->configFile_, filename);
257
258        // Parse bindings and create the ConfigValueContainers if necessary
259        clearBindings();
260        for (std::map<std::string, Button*>::const_iterator it = allButtons_.begin(); it != allButtons_.end(); ++it)
261            it->second->readConfigValue(this->configFile_);
262
263        COUT(3) << "KeyBinder: Loading key bindings done." << std::endl;
264    }
265
266    bool KeyBinder::setBinding(const std::string& binding, const std::string& name, bool bTemporary)
267    {
268        std::map<std::string, Button*>::iterator it = allButtons_.find(name);
269        if (it != allButtons_.end())
270        {
271            if (bTemporary)
272                it->second->configContainer_->tset(binding);
273            else
274                it->second->configContainer_->set(binding);
275            it->second->configContainer_->getValue(&(it->second->bindingString_), it->second);
276            return true;
277        }
278        else
279        {
280            COUT(2) << "Could not find key/button/axis with name '" << name << "'." << std::endl;
281            return false;
282        }
283    }
284
285    /**
286    @brief
287        Overwrites all bindings with ""
288    */
289    void KeyBinder::clearBindings()
290    {
291        for (std::map<std::string, Button*>::const_iterator it = allButtons_.begin(); it != allButtons_.end(); ++it)
292            it->second->clear();
293
294        for (unsigned int i = 0; i < paramCommandBuffer_.size(); i++)
295            delete paramCommandBuffer_[i];
296        paramCommandBuffer_.clear();
297    }
298
299    void KeyBinder::resetJoyStickAxes()
300    {
301        for (unsigned int iDev = 0; iDev < joySticks_.size(); ++iDev)
302        {
303            for (unsigned int i = 0; i < JoyStickAxisCode::numberOfAxes * 2; i++)
304            {
305                joyStickAxes_[iDev][i].absVal_ = 0.0f;
306                joyStickAxes_[iDev][i].relVal_ = 0.0f;
307            }
308        }
309    }
310
311    void KeyBinder::mouseUpdated(float dt)
312    {
313        if (bDeriveMouseInput_)
314        {
315            // only update when derivation dt has passed
316            if (deriveTime_ > derivePeriod_)
317            {
318                for (int i = 0; i < 2; i++)
319                {
320                    if (mouseRelative_[i] < 0)
321                    {
322                        mouseAxes_[2*i + 0].absVal_
323                            = -mouseRelative_[i] / deriveTime_ * 0.0005f * mouseSensitivityDerived_;
324                        mouseAxes_[2*i + 1].absVal_ = 0.0f;
325                    }
326                    else if (mouseRelative_[i] > 0)
327                    {
328                        mouseAxes_[2*i + 0].absVal_ = 0.0f;
329                        mouseAxes_[2*i + 1].absVal_
330                            =  mouseRelative_[i] / deriveTime_ * 0.0005f * mouseSensitivityDerived_;
331                    }
332                    else
333                    {
334                        mouseAxes_[2*i + 0].absVal_ = 0.0f;
335                        mouseAxes_[2*i + 1].absVal_ = 0.0f;
336                    }
337                    mouseRelative_[i] = 0;
338                    mouseAxes_[2*i + 0].hasChanged_ = true;
339                    mouseAxes_[2*i + 1].hasChanged_ = true;
340                }
341                deriveTime_ = 0.0f;
342            }
343            else
344                deriveTime_ += dt;
345        }
346
347        for (unsigned int i = 0; i < MouseAxisCode::numberOfAxes * 2; i++)
348        {
349            // Why dividing relative value by dt? The reason lies in the simple fact, that when you
350            // press a button that has relative movement, that value has to be multiplied by dt to be
351            // frame rate independent. This can easily (and only) be done in updateInput(float).
352            // Hence we need to divide by dt here for the mouse to compensate, because the relative
353            // move movements have nothing to do with dt.
354            if (dt != 0.0f)
355            {
356                // just ignore if dt == 0.0 because we have multiplied by 0.0 anyway..
357                mouseAxes_[i].relVal_ /= dt;
358            }
359
360            tickHalfAxis(mouseAxes_[i]);
361        }
362    }
363
364    void KeyBinder::joyStickUpdated(unsigned int joyStick, float dt)
365    {
366        for (unsigned int i = 0; i < JoyStickAxisCode::numberOfAxes * 2; i++)
367        {
368            tickHalfAxis(joyStickAxes_[joyStick][i]);
369        }
370    }
371
372    void KeyBinder::tickHalfAxis(HalfAxis& halfAxis)
373    {
374        // button mode
375        // TODO: optimize out all the half axes that don't act as a button at the moment
376        if (halfAxis.hasChanged_)
377        {
378            if (!halfAxis.pressed_ && halfAxis.absVal_ > halfAxis.buttonThreshold_)
379            {
380                // key pressed event
381                halfAxis.pressed_ = true;
382                if (halfAxis.nCommands_[KeybindMode::OnPress])
383                    halfAxis.execute(KeybindMode::OnPress);
384            }
385            else if (halfAxis.pressed_ && halfAxis.absVal_ < halfAxis.buttonThreshold_)
386            {
387                // key released event
388                halfAxis.pressed_ = false;
389                if (halfAxis.nCommands_[KeybindMode::OnRelease])
390                    halfAxis.execute(KeybindMode::OnRelease);
391            }
392            halfAxis.hasChanged_ = false;
393        }
394
395        if (halfAxis.pressed_)
396        {
397            // key held event
398            if (halfAxis.nCommands_[KeybindMode::OnHold])
399                halfAxis.execute(KeybindMode::OnHold);
400        }
401
402        // these are the actually useful axis bindings for analog input
403        if (!bFilterAnalogNoise_ || halfAxis.relVal_ > analogThreshold_ || halfAxis.absVal_ > analogThreshold_)
404        {
405            halfAxis.execute();
406        }
407    }
408
409    /**
410    @brief
411        Event handler for the mouseMoved Event.
412    @param e
413        Mouse state information
414    */
415    void KeyBinder::mouseMoved(IntVector2 abs_, IntVector2 rel_, IntVector2 clippingSize)
416    {
417        // y axis of mouse input is inverted
418        int rel[] = { rel_.x, -rel_.y };
419
420        if (bDeriveMouseInput_)
421        {
422            mouseRelative_[0] += rel[0];
423            mouseRelative_[1] += rel[1];
424        }
425        else
426        {
427            for (int i = 0; i < 2; i++)
428            {
429                if (rel[i]) // performance opt. for the case that rel[i] == 0
430                {
431                    // write absolute values
432                    mouseAxes_[2*i + 0].hasChanged_ = true;
433                    mouseAxes_[2*i + 1].hasChanged_ = true;
434                    mousePosition_[i] += rel[i];
435
436                    // clip absolute position
437                    if (mousePosition_[i] > mouseClippingSize_)
438                        mousePosition_[i] =  mouseClippingSize_;
439                    if (mousePosition_[i] < -mouseClippingSize_)
440                        mousePosition_[i] = -mouseClippingSize_;
441
442                    if (mousePosition_[i] < 0)
443                    {
444                        mouseAxes_[2*i + 0].absVal_ =  -mousePosition_[i]/(float)mouseClippingSize_ * mouseSensitivity_;
445                        mouseAxes_[2*i + 1].absVal_ =  0.0f;
446                    }
447                    else
448                    {
449                        mouseAxes_[2*i + 0].absVal_ =  0.0f;
450                        mouseAxes_[2*i + 1].absVal_ =   mousePosition_[i]/(float)mouseClippingSize_ * mouseSensitivity_;
451                    }
452                }
453            }
454        }
455
456        // relative
457        for (int i = 0; i < 2; i++)
458        {
459            if (rel[i] < 0)
460                mouseAxes_[0 + 2*i].relVal_ = -((float)rel[i])/(float)mouseClippingSize_ * mouseSensitivity_;
461            else
462                mouseAxes_[1 + 2*i].relVal_ =  ((float)rel[i])/(float)mouseClippingSize_ * mouseSensitivity_;
463        }
464    }
465
466    /**
467    @brief Event handler for the mouseScrolled Event.
468    @param e Mouse state information
469    */
470    void KeyBinder::mouseScrolled(int abs, int rel)
471    {
472        if (rel < 0)
473            for (int i = 0; i < -rel/mouseWheelStepSize_; i++)
474                mouseButtons_[8].execute(KeybindMode::OnPress, ((float)abs)/mouseWheelStepSize_);
475        else
476            for (int i = 0; i < rel/mouseWheelStepSize_; i++)
477                mouseButtons_[9].execute(KeybindMode::OnPress, ((float)abs)/mouseWheelStepSize_);
478    }
479
480    void KeyBinder::axisMoved(unsigned int device, unsigned int axisID, float value)
481    {
482        int i = axisID * 2;
483        JoyStickAxisVector& axis = joyStickAxes_[device];
484        if (value < 0)
485        {
486            axis[i].absVal_ = -value;
487            axis[i].relVal_ = -value;
488            axis[i].hasChanged_ = true;
489            if (axis[i + 1].absVal_ > 0.0f)
490            {
491                axis[i + 1].absVal_ = -0.0f;
492                axis[i + 1].relVal_ = -0.0f;
493                axis[i + 1].hasChanged_ = true;
494            }
495        }
496        else
497        {
498            axis[i + 1].absVal_ = value;
499            axis[i + 1].relVal_ = value;
500            axis[i + 1].hasChanged_ = true;
501            if (axis[i].absVal_ > 0.0f)
502            {
503                axis[i].absVal_ = -0.0f;
504                axis[i].relVal_ = -0.0f;
505                axis[i].hasChanged_ = true;
506            }
507        }
508    }
509}
Note: See TracBrowser for help on using the repository browser.