//  // // # # ### # # -= Nuclex Library =-  // // ## # # # ## ## KeyboardInputDevice.cpp - Keyboard input device  // // ### # # ###  // // # ### # ### Default implementation of an input device which uses the  // // # ## # # ## ## operating system's keyboard support routines  // // # # ### # # R1 (C)2002-2004 Markus Ewald -> License.txt  // //  // #include "Nuclex/Input/KeyboardInputDevice.h" #include "Nuclex/Application.h" #include "Nuclex/Kernel.h" #include "Nuclex/Input/InputServer.h" #include using namespace Nuclex; using namespace Nuclex::Input; namespace { //  // //  KeyboardTriggers  // //  // /// Scancode/Keyname translation /** Handles the translaten of scancodes to keynames and vice versa */ class KeyboardTriggers { public: /// Constructor KeyboardTriggers() { #ifdef NUCLEX_WIN32 // Win32: GetKeyNameText() for(size_t i = 0; i < 256; ++i) { wchar_t pszKeyName[256]; if(::GetKeyNameText((i << 16) | (1 << 25), pszKeyName, sizeof(pszKeyName))) { m_Scancodes[pszKeyName] = i; } } #else const wchar_t *ppszKeyNames[] = { NULL, L"Escape", L"1", L"2", L"3", L"4", L"5", L"6", L"7", L"8", L"9", L"0", L"Minus", L"Equals", L"Backspace", L"Tab", L"Q", L"W", L"E", L"R", L"T", L"Y", L"U", L"I", L"O", L"P", L"LeftBracket", L"RightBracket", L"Return", L"LeftControl", L"A", L"S", L"D", L"F", L"G", L"H", L"J", L"K", L"L", L"Semicolon", L"Apostrophe", L"Grave", L"LeftShift", L"Backslash", L"Z", L"X", L"C", L"V", L"B", L"N", L"M", L"Comma", L"Period", L"Slash", L"RightShift", L"Multiply", L"LeftMenu", L"Space", L"Capital", L"F1", L"F2", L"F3", L"F4", L"F5", L"F6", L"F7", L"F8", L"F9", L"F10", L"NumLock", L"Scroll", L"NumHome", L"Up", L"NumPageUp", L"Subtract", L"Left", L"NumCenter", L"Right", L"Add", L"NumEnd", L"Down", L"NumPageDown", L"NumInsert", L"Decimal", NULL, NULL, L"OEM102", L"F11", L"F12", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, L"F13", L"F14", L"F15", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, L"Kana", NULL, NULL, L"AbntC1", NULL, NULL, NULL, NULL, NULL, L"Convert", NULL, L"NoConvert", NULL, L"Yen", L"AbntC2", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, L"NumEquals", NULL, NULL, L"PrevTrack", L"At", L"Colon", L"Underline", L"Kanji", L"Stop", L"Ax", L"Unlabelled", NULL, L"NextTrack", NULL, NULL, L"NumEnter", L"RightControl", NULL, NULL, L"Mute", L"Calculator", L"Play", NULL, L"MediaStop", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, L"VolumeDown", NULL, L"VolumeUp", NULL, L"WebHome", L"NumComma", NULL, L"Divide", NULL, L"SysRequest", L"RightMenu", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, L"Pause", NULL, L"Home", L"NumUp", L"PageUp", NULL, L"NumLeft", NULL, L"NumRight", NULL, L"End", L"NumDown", L"PageDown", L"Insert", L"Delete", NULL, NULL, NULL, NULL, NULL, NULL, NULL, L"LeftWin", L"RightWin", L"AppMenu", L"Power", L"Sleep", NULL, NULL, NULL, L"Wake", NULL, L"WebSearch", L"WebFavorites", L"WebRefresh", L"WebStop", L"WebForward", L"WebBackward", L"MyComputer", L"Mail", L"MediaSelect", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; for(size_t i = 0; i < sizeof(ppszKeyNames) / sizeof(*ppszKeyNames); ++i) { if(ppszKeyNames[i]) { m_Scancodes[ppszKeyNames[i]] = i; } } #endif } /// Retrieves the scancode of a key unsigned char getScancode(const wstring &sTrigger) const { CharMap::const_iterator CharIt = m_Scancodes.find(sTrigger); if(CharIt == m_Scancodes.end()) { string::size_type Pos = sTrigger.find(L"Key 0x"); if((Pos != string::npos) && sTrigger.length() == 8) { return (toupper(sTrigger.at(6)) - 'A') * 16 + (toupper(sTrigger.at(7)) - 'A'); } else { throw InvalidArgumentException( "KeyboardTriggers::getScancode()", string("A key labelled '" + asciiFromUnicode(sTrigger) + "' does not exist") ); } } return CharIt->second; } /// Retrieves the name of a key wstring getKeyName(unsigned char cScancode) const { for( CharMap::const_iterator CharIt = m_Scancodes.begin(); CharIt != m_Scancodes.end(); ++CharIt ) { if(CharIt->second == cScancode) { return CharIt->first; } } return wstring(L"Key 0x") + wlexical_cast(static_cast(cScancode)); } private: typedef std::map CharMap; CharMap m_Scancodes; } g_KeyboardTriggers; //  // //  KeyboardTriggerEnumerator  // //  // /// Keyboard trigger enumerator /** A trigger enumerator which enumerates all keys available on the keyboard */ class KeyboardTriggerEnumerator : public InputDevice::TriggerEnumerator { public: /// Constructor KeyboardTriggerEnumerator() : m_nCurrentTrigger(0) {} /// Destructor virtual ~KeyboardTriggerEnumerator() {} // // TriggerEnumerator implementation // public: /// Cycle through triggers const InputDevice::Trigger *cycle() { if(m_nCurrentTrigger == 256) return NULL; m_Trigger.eKind = InputDevice::Trigger::K_BUTTON; m_Trigger.sName = KeyboardInputDevice::getKeyName( static_cast(m_nCurrentTrigger) ); ++m_nCurrentTrigger; return &m_Trigger; } private: InputDevice::Trigger m_Trigger; unsigned long m_nCurrentTrigger; }; } // namespace // ############################################################################################# // // # Nuclex::Input::KeyboardInputDevice::getScancode() # // // ############################################################################################# // /** Returns the scancode of the specified key @param sKeyName Name of the key whose scancode to return @return The key's scancode */ unsigned char KeyboardInputDevice::getScancode(const wstring &sKeyName) { return g_KeyboardTriggers.getScancode(sKeyName); } // ############################################################################################# // // # Nuclex::Input::KeyboardInputDevice::getKeyName() # // // ############################################################################################# // /** Returns the name of the specified key @param cScancode Scancode of the key whose name to return @return The key's name */ wstring KeyboardInputDevice::getKeyName(unsigned char cScancode) { return g_KeyboardTriggers.getKeyName(cScancode); } // ############################################################################################# // // # Nuclex::Input::KeyboardInputDevice::KeyboardInputDevice() # // // ############################################################################################# // /** Initializes an instance of KeyboardInputDevice @param TheKernel Kernel to which the device belongs */ KeyboardInputDevice::KeyboardInputDevice(Kernel &TheKernel) : m_Kernel(TheKernel), m_sName(L"Default Keyboard") { Application &App = TheKernel.getApplication(); App.OnKeyDown.connect(sigc::mem_fun(this, &KeyboardInputDevice::onKeyDown)); App.OnKeyUp.connect(sigc::mem_fun(this, &KeyboardInputDevice::onKeyUp)); App.OnChar.connect(sigc::mem_fun(this, &KeyboardInputDevice::onChar)); //TheKernel.OnHeartBeat.connect(SigC::slot(*this, KeyboardInputDevice::poll)); } // ############################################################################################# // // # Nuclex::Input::KeyboardInputDevice::~KeyboardInputDevice() # // // ############################################################################################# // /** Releases an instance of KeyboardInputDevice */ KeyboardInputDevice::~KeyboardInputDevice() {} // ############################################################################################# // // # Nuclex::Input::KeyboardInputDevice::getType() # // // ############################################################################################# // /** Returns the type of the input device. This is only intended for informational purposes @return The device's type */ InputDevice::Type KeyboardInputDevice::getType() const { return InputDevice::T_KEYBOARD; } // ############################################################################################# // // # Nuclex::Input::KeyboardInputDevice::getName() # // // ############################################################################################# // /** Returns a new enumerator over all triggers of the input device. A trigger is an abstract representation of an axis, a key or any other kind of button on an input device. @return A new enumerator over all triggers */ shared_ptr KeyboardInputDevice::enumTriggers() const { return shared_ptr( new KeyboardTriggerEnumerator() ); } // ############################################################################################# // // # Nuclex::Input::KeyboardInputDevice::onKeyDown() # // // ############################################################################################# // void KeyboardInputDevice::onKeyDown(unsigned char cKey) { m_Kernel.getInputServer()->generateInput(InputReceiver::Event( InputReceiver::Event::T_KEYDOWN, cKey )); } // ############################################################################################# // // # Nuclex::Input::KeyboardInputDevice::onKeyUp() # // // ############################################################################################# // void KeyboardInputDevice::onKeyUp(unsigned char cKey) { m_Kernel.getInputServer()->generateInput(InputReceiver::Event( InputReceiver::Event::T_KEYUP, cKey )); } // ############################################################################################# // // # Nuclex::Input::KeyboardInputDevice::onChar() # // // ############################################################################################# // void KeyboardInputDevice::onChar(wchar_t wChar) { m_Kernel.getInputServer()->generateInput(InputReceiver::Event( InputReceiver::Event::T_CHAR, wChar )); } // ############################################################################################# // // # Nuclex::Input::KeyboardInputDevice::poll() # // // ############################################################################################# // /** Should be called regularly to update the device's state */ void KeyboardInputDevice::poll() { for(size_t ControlIndex = 0; ControlIndex < m_Controls.size(); ++ControlIndex) m_Controls[ControlIndex].fValue = 0.0f; for(size_t BindingIndex = 0; BindingIndex < m_Bindings.size(); ++BindingIndex) { bool bDown = !!(::GetAsyncKeyState( ::MapVirtualKey(m_Bindings[BindingIndex].Scancode, 1) ) & 0x8000); switch(m_Bindings[BindingIndex].eInteraction) { case Trigger::I_DIRECT: { if(bDown) m_Controls[m_Bindings[BindingIndex].ControlIndex].fValue = 1.0f; break; } case Trigger::I_DOWN: { if(!m_Bindings[BindingIndex].bPreviousState && bDown) m_Controls[m_Bindings[BindingIndex].ControlIndex].fValue = 1.0f; m_Bindings[BindingIndex].bPreviousState = bDown; } case Trigger::I_UP: { if(m_Bindings[BindingIndex].bPreviousState && !bDown) m_Controls[m_Bindings[BindingIndex].ControlIndex].fValue = 1.0f; m_Bindings[BindingIndex].bPreviousState = bDown; } } } } // ############################################################################################# // // # Nuclex::Input::KeyboardInputDevice::bind() # // // ############################################################################################# // /** Adds a control to the input device @param sName Name of the control @param sTrigger Trigger to be captured */ void KeyboardInputDevice::bind(const wstring &sName, const wstring &sAction) { size_t Scancode = getScancode(Trigger::getTrigger(sAction)); Trigger::Interaction eInteraction = Trigger::getInteraction(sAction); size_t ControlIndex; for(ControlIndex = 0; ControlIndex < m_Controls.size(); ++ControlIndex) if(m_Controls[ControlIndex].sName == sName) break; if(ControlIndex == m_Controls.size()) { m_Controls.resize(ControlIndex + 1); m_Controls[ControlIndex].sName = sName; m_Controls[ControlIndex].fValue = 0.0f; } else { for(size_t BindingIndex = 0; BindingIndex < m_Bindings.size(); ++BindingIndex) if((m_Bindings[BindingIndex].ControlIndex == ControlIndex) && (m_Bindings[BindingIndex].Scancode == Scancode) && (m_Bindings[BindingIndex].eInteraction == eInteraction)) throw FailedException( "Nuclex::Input::KeyboardInputDevice::bind()", "A control with the same name and the same action has already been registered" ); } size_t BindingIndex = m_Bindings.size(); m_Bindings.resize(BindingIndex + 1); m_Bindings[BindingIndex].Scancode = Scancode; m_Bindings[BindingIndex].bPreviousState = false; m_Bindings[BindingIndex].eInteraction = eInteraction; m_Bindings[BindingIndex].ControlIndex = ControlIndex; } // ############################################################################################# // // # Nuclex::Input::KeyboardInputDevice::unbind() # // // ############################################################################################# // /** Removes a control from the input device @param sName Name of the control to remove @param sTrigger Trigger of the control to remove */ void KeyboardInputDevice::unbind(const wstring &sName, const wstring &sAction) { size_t Scancode = getScancode(Trigger::getTrigger(sAction)); Trigger::Interaction eInteraction = Trigger::getInteraction(sAction); size_t ControlIndex; for(ControlIndex = 0; ControlIndex < m_Controls.size(); ++ControlIndex) if(m_Controls[ControlIndex].sName == sName) break; if(ControlIndex == m_Controls.size()) throw FailedException( "Nuclex::Input::KeyboardInputDevice::unbind()", "A control with the specified name and action was not found" ); for(size_t BindingIndex = 0; BindingIndex < m_Bindings.size(); ++BindingIndex) { if((m_Bindings[BindingIndex].ControlIndex == ControlIndex) && (m_Bindings[BindingIndex].Scancode == Scancode) && (m_Bindings[BindingIndex].eInteraction == eInteraction)) { m_Bindings[BindingIndex] = m_Bindings[m_Bindings.size() - 1]; if(m_Bindings.size() > 0) m_Bindings.resize(m_Bindings.size() - 1); else m_Bindings.clear(); return; } } throw FailedException( "Nuclex::Input::KeyboardInputDevice::unbind()", "A control with the specified name and action was not found" ); } // ############################################################################################# // // # Nuclex::Input::KeyboardInputDevice::clearBindings() # // // ############################################################################################# // /** Removes all controls from the input device */ void KeyboardInputDevice::clearBindings() { m_Bindings.clear(); m_Controls.clear(); } // ############################################################################################# // // # Nuclex::Input::KeyboardInputDevice::getState() # // // ############################################################################################# // /** Returns the current state of a registered control @param sName Name of the control whose state to retrieve @return The control's current state */ real KeyboardInputDevice::getState(const wstring &sName) const { for(size_t ControlIndex = 0; ControlIndex < m_Controls.size(); ++ControlIndex) if(m_Controls[ControlIndex].sName == sName) return m_Controls[ControlIndex].fValue; return 0.0f; }