Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/consolecommands/src/libraries/core/input/KeyBinder.cc @ 6518

Last change on this file since 6518 was 6311, checked in by dafrick, 15 years ago

The KeyBindMenu now shows all Keybindings and allows for various manipulations.
For this the bookkeeping in KeyBinder has ben improved.
Also KeyEscape now can't be bound to other commands.

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