Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/menu/src/libraries/core/input/KeyBinder.cc @ 6051

Last change on this file since 6051 was 5929, checked in by rgrieder, 15 years ago

Merged core5 branch back to the trunk.
Key features include clean level unloading and an extended XML event system.

Two important notes:
Delete your keybindings.ini files! * or you will still get parser errors when loading the key bindings.
Delete build_dir/lib/modules/libgamestates.module! * or orxonox won't start.
Best thing to do is to delete the build folder ;)

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