Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gamestate/src/libraries/core/input/KeyBinder.cc @ 6432

Last change on this file since 6432 was 6432, checked in by rgrieder, 14 years ago

Changed the way config values associated with general settings (ConfigFileType::Settings) are handled:

  • ConfigFileManager only handles config files listed in the ConfigFileType enum (normal enum again)
  • ConfigFileManager only takes care of ConfigFiles and returns a pointer to the right one, just two functions left. —> use like: ConfigFileManager::getInstance().getConfigFile(myType)→doSomething();
  • Moved all code (except for the argument completion functions) relating to ConfigFileType::Settings to a new class: SettingsConfigFile, which is a Singleton (it doesn't make sense to have multiple instances unless you start coding a lot more)
  • SettingsConfigFile handles config value containers according to their section and entry in the ini file, not according to class and variables names. (In most cases it will be class and variable names though)
  • SettingsConfigFile supports:
    • clear() (removes any file entries not associated to a config value container)
    • updateConfigValues() (does exactly that through the identifier)
    • config, tconfig and getConfig
    • commands listed above are exported to tolua, and tconfig, config and getConfig were given shortcuts in Lua (e.g. orxonox.config)
  • If you need to organise ConfigFiles yourself, just do it without the ConfigFileManager, like the KeyBinder does.
  • All getValue() functions have been split into getOrCreateValue() and getValue(), which is const
  • Removed obsolete config value management code in the Identifier (it still stores and destroys them and provides access to them)

All of that leads to one HUGE advantage:
"config OutputHandler softDebugLevelInGameConsole"
works now :D (any further implications are up to the reader…)
(it didn't work before because the actual config value container is in the InGameConsole singleton)

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