From b1e95feb3bacaf6b970a436e54708923efbb8d76 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 9 Mar 2014 17:35:31 +0000 Subject: [PATCH] Add ButtonMap class / window.get_button_map() for querying keyboard layout + X11 implementation thereof --- panda/src/display/graphicsWindow.cxx | 23 +++- panda/src/display/graphicsWindow.h | 3 +- panda/src/putil/Sources.pp | 12 +- panda/src/putil/buttonMap.I | 141 +++++++++++++++++++++ panda/src/putil/buttonMap.cxx | 33 +++++ panda/src/putil/buttonMap.h | 78 ++++++++++++ panda/src/putil/buttonRegistry.cxx | 19 +++ panda/src/putil/buttonRegistry.h | 1 + panda/src/putil/config_util.cxx | 2 + panda/src/putil/p3putil_composite1.cxx | 1 + panda/src/x11display/x11GraphicsWindow.cxx | 37 +++++- panda/src/x11display/x11GraphicsWindow.h | 5 +- 12 files changed, 340 insertions(+), 15 deletions(-) create mode 100644 panda/src/putil/buttonMap.I create mode 100644 panda/src/putil/buttonMap.cxx create mode 100644 panda/src/putil/buttonMap.h diff --git a/panda/src/display/graphicsWindow.cxx b/panda/src/display/graphicsWindow.cxx index 99ec1e4eb3..36be7cb270 100644 --- a/panda/src/display/graphicsWindow.cxx +++ b/panda/src/display/graphicsWindow.cxx @@ -77,8 +77,8 @@ GraphicsWindow:: // Clean up python event handlers. #ifdef HAVE_PYTHON PythonWinProcClasses::iterator iter; - for (iter = _python_window_proc_classes.begin(); - iter != _python_window_proc_classes.end(); + for (iter = _python_window_proc_classes.begin(); + iter != _python_window_proc_classes.end(); ++iter) { delete *iter; } @@ -340,6 +340,17 @@ has_keyboard(int device) const { return result; } +//////////////////////////////////////////////////////////////////// +// Function: x11GraphicsWindow::get_keyboard_map +// Access: Published, Virtual +// Description: Returns a ButtonMap containing the association +// between raw buttons and virtual buttons. +//////////////////////////////////////////////////////////////////// +ButtonMap *GraphicsWindow:: +get_keyboard_map() const { + return NULL; +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsWindow::enable_pointer_events // Access: Published @@ -391,8 +402,10 @@ disable_pointer_mode(int device) { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindow::get_pointer // Access: Published -// Description: Returns the MouseData associated with the nth input -// device's pointer. +// Description: Returns the MouseData associated with the nth +// input device's pointer. This is deprecated; use +// get_pointer_device().get_pointer() instead, or for +// raw mice, use the InputDeviceManager interface. //////////////////////////////////////////////////////////////////// MouseData GraphicsWindow:: get_pointer(int device) const { @@ -409,7 +422,7 @@ get_pointer(int device) const { // Function: GraphicsWindow::move_pointer // Access: Published, Virtual // Description: Forces the pointer to the indicated position within -// the window, if possible. +// the window, if possible. // // Returns true if successful, false on failure. This // may fail if the mouse is not currently within the diff --git a/panda/src/display/graphicsWindow.h b/panda/src/display/graphicsWindow.h index 00ded5833f..2dfe1c3f60 100644 --- a/panda/src/display/graphicsWindow.h +++ b/panda/src/display/graphicsWindow.h @@ -29,6 +29,7 @@ #include "modifierButtons.h" #include "buttonEvent.h" #include "keyboardButton.h" +#include "buttonMap.h" #include "pnotify.h" #include "lightMutex.h" #include "lightReMutex.h" @@ -82,7 +83,7 @@ PUBLISHED: MAKE_SEQ(get_input_device_names, get_num_input_devices, get_input_device_name); bool has_pointer(int device) const; bool has_keyboard(int device) const; - + virtual ButtonMap *get_keyboard_map() const; void enable_pointer_events(int device); void disable_pointer_events(int device); diff --git a/panda/src/putil/Sources.pp b/panda/src/putil/Sources.pp index 6134d73f7c..a3046d68fe 100644 --- a/panda/src/putil/Sources.pp +++ b/panda/src/putil/Sources.pp @@ -21,8 +21,9 @@ bamWriter.I bamWriter.h \ bitArray.I bitArray.h \ bitMask.I bitMask.h \ - buttonHandle.I \ - buttonHandle.h buttonRegistry.I buttonRegistry.h \ + buttonHandle.I buttonHandle.h \ + buttonMap.I buttonMap.h \ + buttonRegistry.I buttonRegistry.h \ cachedTypedWritableReferenceCount.h cachedTypedWritableReferenceCount.I \ callbackData.h callbackData.I \ callbackObject.h callbackObject.I \ @@ -84,7 +85,7 @@ bamWriter.cxx \ bitArray.cxx \ bitMask.cxx \ - buttonHandle.cxx buttonRegistry.cxx \ + buttonHandle.cxx buttonMap.cxx buttonRegistry.cxx \ cachedTypedWritableReferenceCount.cxx \ callbackData.cxx \ callbackObject.cxx \ @@ -132,8 +133,9 @@ bamWriter.I bamWriter.h \ bitArray.I bitArray.h \ bitMask.I bitMask.h \ - buttonHandle.I buttonHandle.h buttonRegistry.I \ - buttonRegistry.h \ + buttonHandle.I buttonHandle.h \ + buttonMap.I buttonMap.h \ + buttonRegistry.I buttonRegistry.h \ cachedTypedWritableReferenceCount.h cachedTypedWritableReferenceCount.I \ callbackData.h callbackData.I \ callbackObject.h callbackObject.I \ diff --git a/panda/src/putil/buttonMap.I b/panda/src/putil/buttonMap.I new file mode 100644 index 0000000000..e51b3c5a0d --- /dev/null +++ b/panda/src/putil/buttonMap.I @@ -0,0 +1,141 @@ +// Filename: buttonMap.I +// Created by: rdb (09Mar14) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: ButtonMap::get_num_buttons +// Access: Published +// Description: Returns the number of buttons that this button +// mapping specifies. +//////////////////////////////////////////////////////////////////// +INLINE int ButtonMap:: +get_num_buttons() const { + return _buttons.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonMap::get_raw_button +// Access: Published +// Description: Returns the underlying raw button associated with +// the nth button. +//////////////////////////////////////////////////////////////////// +INLINE ButtonHandle ButtonMap:: +get_raw_button(int i) const { + return _buttons[i]->_raw; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonMap::get_mapped_button +// Access: Published +// Description: Returns the nth mapped button, meaning the button +// that the nth raw button is mapped to. +//////////////////////////////////////////////////////////////////// +INLINE ButtonHandle ButtonMap:: +get_mapped_button(int i) const { + return _buttons[i]->_mapped; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonMap::get_mapped_button_text +// Access: Published +// Description: Returns the text associated with the nth mapped +// button, meaning the button that the nth raw +// button is mapped to. +//////////////////////////////////////////////////////////////////// +INLINE const string &ButtonMap:: +get_mapped_button_text(int i) const { + return _buttons[i]->_text; +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonMap::get_mapped_button +// Access: Published +// Description: Returns the button that the given button is mapped +// to, or ButtonHandle::none() if this map does not +// specify a mapped button for the given raw button. +//////////////////////////////////////////////////////////////////// +INLINE ButtonHandle ButtonMap:: +get_mapped_button(ButtonHandle raw) const { + pmap::const_iterator it; + it = _button_map.find(raw.get_index()); + if (it == _button_map.end()) { + return ButtonHandle::none(); + } else { + return it->second._mapped; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtonMap::get_mapped_button +// Access: Published +// Description: Returns the button that the given button is mapped +// to, or ButtonHandle::none() if this map does not +// specify a mapped button for the given raw button. +//////////////////////////////////////////////////////////////////// +INLINE ButtonHandle ButtonMap:: +get_mapped_button(const string &raw_name) const { + ButtonHandle raw_button = ButtonRegistry::ptr()->find_button(raw_name); + if (raw_button == ButtonHandle::none()) { + return ButtonHandle::none(); + } else { + return get_mapped_button(raw_button); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtoMap::get_mapped_button_text +// Access: Published +// Description: If the button map specifies a special name for the +// button (eg. if the operating system or keyboard +// device has a localized name describing the key), +// returns it, or the empty string otherwise. +// +// Note that this is not the same as +// get_mapped_button().get_name(), which returns the +// name of the Panda event associated with the button. +//////////////////////////////////////////////////////////////////// +INLINE const string &ButtonMap:: +get_mapped_button_text(ButtonHandle raw) const { + pmap::const_iterator it; + it = _button_map.find(raw.get_index()); + if (it == _button_map.end()) { + static const string empty = ""; + return empty; + } else { + return it->second._text; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ButtoMap::get_mapped_button_text +// Access: Published +// Description: If the button map specifies a special name for the +// button (eg. if the operating system or keyboard +// device has a localized name describing the key), +// returns it, or the empty string otherwise. +// +// Note that this is not the same as +// get_mapped_button().get_name(), which returns the +// name of the Panda event associated with the button. +//////////////////////////////////////////////////////////////////// +INLINE const string &ButtonMap:: +get_mapped_button_text(const string &raw_name) const { + ButtonHandle raw_button = ButtonRegistry::ptr()->find_button(raw_name); + if (raw_button == ButtonHandle::none()) { + static const string empty = ""; + return empty; + } else { + return get_mapped_button_text(raw_button); + } +} diff --git a/panda/src/putil/buttonMap.cxx b/panda/src/putil/buttonMap.cxx new file mode 100644 index 0000000000..1452b4542c --- /dev/null +++ b/panda/src/putil/buttonMap.cxx @@ -0,0 +1,33 @@ +// Filename: buttonMap.cxx +// Created by: rdb (09Mar14) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "buttonMap.h" + +TypeHandle ButtonMap::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ButtonMap::map_button +// Access: Public +// Description: Registers a new button mapping. +//////////////////////////////////////////////////////////////////// +void ButtonMap:: +map_button(ButtonHandle raw_button, ButtonHandle button, const string &text) { + int index = raw_button.get_index(); + ButtonNode bnode; + bnode._raw = raw_button; + bnode._mapped = button; + bnode._text = text; + _button_map[index] = bnode; + _buttons.push_back(&_button_map[index]); +} diff --git a/panda/src/putil/buttonMap.h b/panda/src/putil/buttonMap.h new file mode 100644 index 0000000000..a6ca416071 --- /dev/null +++ b/panda/src/putil/buttonMap.h @@ -0,0 +1,78 @@ +// Filename: buttonMap.h +// Created by: rdb (07Mar14) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef BUTTONMAP_H +#define BUTTONMAP_H + +#include "pandabase.h" +#include "typedReferenceCount.h" +#include "buttonHandle.h" +#include "buttonRegistry.h" +#include "pmap.h" + +//////////////////////////////////////////////////////////////////// +// Class : ButtonMap +// Description : This class represents a map containing all of the +// buttons of a (keyboard) device, though it can also +// be used as a generic mapping between ButtonHandles. +// It maps an underlying 'raw' button to a 'virtual' +// button, which may optionally be associated with an +// appropriate platform-specific name for the button. +//////////////////////////////////////////////////////////////////// +class ButtonMap : public TypedReferenceCount { +PUBLISHED: + INLINE int get_num_buttons() const; + INLINE ButtonHandle get_raw_button(int i) const; + INLINE ButtonHandle get_mapped_button(int i) const; + INLINE const string &get_mapped_button_text(int i) const; + + INLINE ButtonHandle get_mapped_button(ButtonHandle raw) const; + INLINE ButtonHandle get_mapped_button(const string &raw_name) const; + INLINE const string &get_mapped_button_text(ButtonHandle raw) const; + INLINE const string &get_mapped_button_text(const string &raw_name) const; + +public: + void map_button(ButtonHandle raw_button, ButtonHandle button, const string &text = ""); + +private: + struct ButtonNode { + ButtonHandle _raw; + ButtonHandle _mapped; + string _text; + }; + + pmap _button_map; + pvector _buttons; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "ButtonMap", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "buttonMap.I" + +#endif diff --git a/panda/src/putil/buttonRegistry.cxx b/panda/src/putil/buttonRegistry.cxx index 44e6d410b1..ef94897dbd 100644 --- a/panda/src/putil/buttonRegistry.cxx +++ b/panda/src/putil/buttonRegistry.cxx @@ -131,6 +131,25 @@ get_button(const string &name) { return button; } +//////////////////////////////////////////////////////////////////// +// Function: ButtonRegistry::find_button +// Access: Published +// Description: Finds a ButtonHandle in the registry matching the +// indicated name. If there is no such ButtonHandle, +// returns ButtonHandle::none(). +//////////////////////////////////////////////////////////////////// +ButtonHandle ButtonRegistry:: +find_button(const string &name) { + NameRegistry::const_iterator ri; + ri = _name_registry.find(name); + + if (ri != _name_registry.end()) { + return (*ri).second->_handle; + } + + return ButtonHandle::none(); +} + //////////////////////////////////////////////////////////////////// // Function: ButtonRegistry::find_ascii_button // Access: Published diff --git a/panda/src/putil/buttonRegistry.h b/panda/src/putil/buttonRegistry.h index cb75e7a847..16f842a35f 100644 --- a/panda/src/putil/buttonRegistry.h +++ b/panda/src/putil/buttonRegistry.h @@ -48,6 +48,7 @@ public: PUBLISHED: ButtonHandle get_button(const string &name); + ButtonHandle find_button(const string &name); ButtonHandle find_ascii_button(char ascii_equivalent) const; void write(ostream &out) const; diff --git a/panda/src/putil/config_util.cxx b/panda/src/putil/config_util.cxx index 6795d69ea8..89b71a6b3f 100644 --- a/panda/src/putil/config_util.cxx +++ b/panda/src/putil/config_util.cxx @@ -21,6 +21,7 @@ #include "bitArray.h" #include "bitMask.h" #include "buttonHandle.h" +#include "buttonMap.h" #include "cachedTypedWritableReferenceCount.h" #include "callbackData.h" #include "callbackObject.h" @@ -186,6 +187,7 @@ init_libputil() { BitMask32::init_type(); BitMask64::init_type(); ButtonHandle::init_type(); + ButtonMap::init_type(); CPointerCallbackObject::init_type(); CachedTypedWritableReferenceCount::init_type(); CallbackData::init_type(); diff --git a/panda/src/putil/p3putil_composite1.cxx b/panda/src/putil/p3putil_composite1.cxx index f1f2f9d7ec..8c68adf9c1 100644 --- a/panda/src/putil/p3putil_composite1.cxx +++ b/panda/src/putil/p3putil_composite1.cxx @@ -10,6 +10,7 @@ #include "bitArray.cxx" #include "bitMask.cxx" #include "buttonHandle.cxx" +#include "buttonMap.cxx" #include "buttonRegistry.cxx" #include "cachedTypedWritableReferenceCount.cxx" #include "callbackData.cxx" diff --git a/panda/src/x11display/x11GraphicsWindow.cxx b/panda/src/x11display/x11GraphicsWindow.cxx index ec7c79a48b..68e60b8e83 100644 --- a/panda/src/x11display/x11GraphicsWindow.cxx +++ b/panda/src/x11display/x11GraphicsWindow.cxx @@ -19,6 +19,7 @@ #include "graphicsPipe.h" #include "keyboardButton.h" #include "mouseButton.h" +#include "buttonMap.h" #include "clockObject.h" #include "pStatTimer.h" #include "textEncoder.h" @@ -1545,8 +1546,10 @@ get_button(XKeyEvent &key_event, bool allow_shift) { // Called by get_button(), above. //////////////////////////////////////////////////////////////////// ButtonHandle x11GraphicsWindow:: -map_button(KeySym key) { +map_button(KeySym key) const { switch (key) { + case NoSymbol: + return ButtonHandle::none(); case XK_BackSpace: return KeyboardButton::backspace(); case XK_Tab: @@ -1864,7 +1867,7 @@ map_button(KeySym key) { // Description: Maps from a single X keycode to Panda's ButtonHandle. //////////////////////////////////////////////////////////////////// ButtonHandle x11GraphicsWindow:: -map_raw_button(KeyCode key) { +map_raw_button(KeyCode key) const { switch (key) { case 9: return KeyboardButton::escape(); case 10: return KeyboardButton::ascii_key('1'); @@ -2001,6 +2004,36 @@ get_mouse_button(XButtonEvent &button_event) { } } +//////////////////////////////////////////////////////////////////// +// Function: x11GraphicsWindow::get_keyboard_map +// Access: Private, Virtual +// Description: Returns a ButtonMap containing the association +// between raw buttons and virtual buttons. +//////////////////////////////////////////////////////////////////// +ButtonMap *x11GraphicsWindow:: +get_keyboard_map() const { + // NB. This could be improved by using the Xkb API. + //XkbDescPtr desc = XkbGetMap(_display, XkbAllMapComponentsMask, XkbUseCoreKbd); + ButtonMap *map = new ButtonMap; + + for (int k = 9; k <= 135; ++k) { + ButtonHandle raw_button = map_raw_button(k); + if (raw_button == ButtonHandle::none()) { + continue; + } + + KeySym sym = XKeycodeToKeysym(_display, k, 0); + ButtonHandle button = map_button(sym); + if (button == ButtonHandle::none()) { + continue; + } + + map->map_button(raw_button, button, name); + } + + return map; +} + //////////////////////////////////////////////////////////////////// // Function: x11GraphicsWindow::check_event // Access: Private, Static diff --git a/panda/src/x11display/x11GraphicsWindow.h b/panda/src/x11display/x11GraphicsWindow.h index e5f51d9cf3..e032cd7109 100644 --- a/panda/src/x11display/x11GraphicsWindow.h +++ b/panda/src/x11display/x11GraphicsWindow.h @@ -66,9 +66,10 @@ protected: void handle_keyrelease(XKeyEvent &event); ButtonHandle get_button(XKeyEvent &key_event, bool allow_shift); - ButtonHandle map_button(KeySym key); - ButtonHandle map_raw_button(KeyCode key); + ButtonHandle map_button(KeySym key) const; + ButtonHandle map_raw_button(KeyCode key) const; ButtonHandle get_mouse_button(XButtonEvent &button_event); + virtual ButtonMap *get_keyboard_map() const; static Bool check_event(X11_Display *display, XEvent *event, char *arg);