From 90311825830e34ba72ffcea10f45f3a5f9c6a0ba Mon Sep 17 00:00:00 2001 From: James Turner Date: Sat, 10 Jun 2017 15:49:32 +0100 Subject: [PATCH] GraphicsWindowQt5: improved keyboard + modifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes many kinds of input weirdness, caused by ‘stuck’ modifiers being set internally, and causing input bindings to be excluded. --- src/Viewer/GraphicsWindowQt5.cpp | 153 ++++++++++++++++++------------- src/Viewer/GraphicsWindowQt5.hxx | 4 +- 2 files changed, 91 insertions(+), 66 deletions(-) diff --git a/src/Viewer/GraphicsWindowQt5.cpp b/src/Viewer/GraphicsWindowQt5.cpp index 7f7a501ae..54f744bbe 100644 --- a/src/Viewer/GraphicsWindowQt5.cpp +++ b/src/Viewer/GraphicsWindowQt5.cpp @@ -42,7 +42,7 @@ public: QtKeyboardMap() { mKeyMap[Qt::Key_Escape ] = osgGA::GUIEventAdapter::KEY_Escape; - mKeyMap[Qt::Key_Delete ] = osgGA::GUIEventAdapter::KEY_Delete; + mKeyMap[Qt::Key_Delete ] = osgGA::GUIEventAdapter::KEY_Delete; mKeyMap[Qt::Key_Home ] = osgGA::GUIEventAdapter::KEY_Home; mKeyMap[Qt::Key_Enter ] = osgGA::GUIEventAdapter::KEY_KP_Enter; mKeyMap[Qt::Key_End ] = osgGA::GUIEventAdapter::KEY_End; @@ -57,10 +57,22 @@ public: mKeyMap[Qt::Key_Tab ] = osgGA::GUIEventAdapter::KEY_Tab; mKeyMap[Qt::Key_Space ] = osgGA::GUIEventAdapter::KEY_Space; mKeyMap[Qt::Key_Delete ] = osgGA::GUIEventAdapter::KEY_Delete; - mKeyMap[Qt::Key_Alt ] = osgGA::GUIEventAdapter::KEY_Alt_L; - mKeyMap[Qt::Key_Shift ] = osgGA::GUIEventAdapter::KEY_Shift_L; - mKeyMap[Qt::Key_Control ] = osgGA::GUIEventAdapter::KEY_Control_L; - mKeyMap[Qt::Key_Meta ] = osgGA::GUIEventAdapter::KEY_Meta_L; + mKeyMap[Qt::Key_Alt ] = osgGA::GUIEventAdapter::KEY_Alt_L; + mKeyMap[Qt::Key_Shift ] = osgGA::GUIEventAdapter::KEY_Shift_L; + +#if defined(Q_OS_MACOS) + // undo the Qt-mac remapping + mKeyMap[Qt::Key_Meta ] = osgGA::GUIEventAdapter::KEY_Control_L; + mKeyMap[Qt::Key_Control ] = osgGA::GUIEventAdapter::KEY_Meta_L; +#else + mKeyMap[Qt::Key_Control ] = osgGA::GUIEventAdapter::KEY_Control_L; + mKeyMap[Qt::Key_Meta ] = osgGA::GUIEventAdapter::KEY_Meta_L; +#endif + mKeyMap[Qt::Key_Super_L ] = osgGA::GUIEventAdapter::KEY_Super_L; + mKeyMap[Qt::Key_Super_R ] = osgGA::GUIEventAdapter::KEY_Super_R; + mKeyMap[Qt::Key_Hyper_L ] = osgGA::GUIEventAdapter::KEY_Hyper_L; + mKeyMap[Qt::Key_Hyper_R ] = osgGA::GUIEventAdapter::KEY_Hyper_R; + mKeyMap[Qt::Key_F1 ] = osgGA::GUIEventAdapter::KEY_F1; mKeyMap[Qt::Key_F2 ] = osgGA::GUIEventAdapter::KEY_F2; @@ -83,22 +95,7 @@ public: mKeyMap[Qt::Key_F19 ] = osgGA::GUIEventAdapter::KEY_F19; mKeyMap[Qt::Key_F20 ] = osgGA::GUIEventAdapter::KEY_F20; - mKeyMap[Qt::Key_hyphen ] = '-'; - mKeyMap[Qt::Key_Equal ] = '='; - - mKeyMap[Qt::Key_division ] = osgGA::GUIEventAdapter::KEY_KP_Divide; - mKeyMap[Qt::Key_multiply ] = osgGA::GUIEventAdapter::KEY_KP_Multiply; - mKeyMap[Qt::Key_Minus ] = '-'; - mKeyMap[Qt::Key_Plus ] = '+'; - //mKeyMap[Qt::Key_H ] = osgGA::GUIEventAdapter::KEY_KP_Home; - //mKeyMap[Qt::Key_ ] = osgGA::GUIEventAdapter::KEY_KP_Up; - //mKeyMap[92 ] = osgGA::GUIEventAdapter::KEY_KP_Page_Up; - //mKeyMap[86 ] = osgGA::GUIEventAdapter::KEY_KP_Left; - //mKeyMap[87 ] = osgGA::GUIEventAdapter::KEY_KP_Begin; - //mKeyMap[88 ] = osgGA::GUIEventAdapter::KEY_KP_Right; - //mKeyMap[83 ] = osgGA::GUIEventAdapter::KEY_KP_End; - //mKeyMap[84 ] = osgGA::GUIEventAdapter::KEY_KP_Down; - //mKeyMap[85 ] = osgGA::GUIEventAdapter::KEY_KP_Page_Down; + mKeyMap[Qt::Key_Insert ] = osgGA::GUIEventAdapter::KEY_KP_Insert; //mKeyMap[Qt::Key_Delete ] = osgGA::GUIEventAdapter::KEY_KP_Delete; } @@ -109,16 +106,32 @@ public: int remapKey(QKeyEvent* event) { + const QChar unicodePoint = event->text().isEmpty() ? QChar() : event->text().at(0); KeyMap::iterator itr = mKeyMap.find(event->key()); - if (itr == mKeyMap.end()) - { - return int(*(event->text().toLatin1().data())); + if (itr == mKeyMap.end()) { + if (unicodePoint.isNull()) { + // this happens for Ctrl modifiers on A-Z (at least). Keyboard.xml relies on the + // old ASCII mappings of these values, so we need to synthesise those here + // since Qt won't do it. + + const auto k = event->key(); + if ((k >= Qt::Key_A) && (k <= Qt::Key_Z)) { + return 1 + (k - Qt::Key_A); // offset into the ASCII control code range + } else { + qWarning() << Q_FUNC_INFO << "misssing mapping for key"; + } + } +#if 0 + qDebug() << "key" << event->key() << ", mods" << event->modifiers() << "Unicode:" << unicodePoint << ", ASCII:" + << unicodePoint.toLatin1() << "(" << (int)unicodePoint.toLatin1() << "), raw text:" << event->text(); +#endif + return int(unicodePoint.toLatin1()); } - else - return itr->second; + + return itr->second; } - private: +private: typedef std::map KeyMap; KeyMap mKeyMap; }; @@ -143,12 +156,12 @@ void GLWindow::onScreenChanged() #if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)) _devicePixelRatio = screen()->devicePixelRatio(); #endif - + if (_isPrimaryWindow) { // allow PUI and Canvas to be scaled fgSetDouble("/sim/rendering/gui-pixel-ratio", _devicePixelRatio); } - + syncGeometryWithOSG(); } @@ -156,10 +169,10 @@ void GLWindow::syncGeometryWithOSG() { const int w = width(); const int h = height(); - + int scaled_width = static_cast(w *_devicePixelRatio); int scaled_height = static_cast(h*_devicePixelRatio); - + if (_gw) { _gw->resized( x(), y(), scaled_width, scaled_height); _gw->getEventQueue()->windowResize( x(), y(), scaled_width, scaled_height ); @@ -189,7 +202,7 @@ bool GLWindow::event( QEvent* event ) if (event->type() == QEvent::WindowStateChange) { // keep full-screen state in sync const bool isFullscreen = (windowState() == Qt::WindowFullScreen); - + if (_isPrimaryWindow) { fgSetBool("/sim/startup/fullscreen", isFullscreen); } @@ -214,7 +227,7 @@ void GLWindow::processUpdateEvent() if (_gw->_viewer.lock(v)) { v->frame(); } - + // see discussion of QWindow::requestUpdate to see // why this is good behaviour if (_gw->_continousUpdate) { @@ -223,14 +236,29 @@ void GLWindow::processUpdateEvent() } -void GLWindow::setKeyboardModifiers( QInputEvent* event ) +static void setOSGModifier(int& modifiers, unsigned int bits, bool set) { - int modkey = event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier); - unsigned int mask = 0; - if ( modkey & Qt::ShiftModifier ) mask |= osgGA::GUIEventAdapter::MODKEY_SHIFT; - if ( modkey & Qt::ControlModifier ) mask |= osgGA::GUIEventAdapter::MODKEY_CTRL; - if ( modkey & Qt::AltModifier ) mask |= osgGA::GUIEventAdapter::MODKEY_ALT; - _gw->getEventQueue()->getCurrentEventState()->setModKeyMask( mask ); + if (set) { + modifiers |= bits; + } else { + modifiers &= ~bits; + } +} + +void GLWindow::setKeyboardModifiers(const Qt::KeyboardModifiers qtMods) +{ + auto es = _gw->getEventQueue()->getCurrentEventState(); + auto modifiers = es->getModKeyMask(); + +#if defined(Q_OS_MACOS) + setOSGModifier(modifiers, osgGA::GUIEventAdapter::MODKEY_CTRL, qtMods & Qt::MetaModifier); +#else + setOSGModifier(modifiers, osgGA::GUIEventAdapter::MODKEY_CTRL, qtMods & Qt::ControlModifier); +#endif + setOSGModifier(modifiers, osgGA::GUIEventAdapter::MODKEY_ALT, qtMods & Qt::AltModifier); + setOSGModifier(modifiers, osgGA::GUIEventAdapter::MODKEY_SHIFT, qtMods & Qt::ShiftModifier); + + _gw->getEventQueue()->getCurrentEventState()->setModKeyMask(modifiers); } void GLWindow::resizeEvent( QResizeEvent* event ) @@ -247,7 +275,7 @@ void GLWindow::moveEvent( QMoveEvent* event ) void GLWindow::keyPressEvent( QKeyEvent* event ) { - setKeyboardModifiers( event ); + setKeyboardModifiers( event->modifiers() ); int value = s_QtKeyboardMap.remapKey( event ); _gw->getEventQueue()->keyPress( value ); @@ -259,13 +287,10 @@ void GLWindow::keyPressEvent( QKeyEvent* event ) void GLWindow::keyReleaseEvent( QKeyEvent* event ) { - if( event->isAutoRepeat() ) - { + if (event->isAutoRepeat()) { event->ignore(); - } - else - { - setKeyboardModifiers( event ); + } else { + setKeyboardModifiers(event->modifiers() ); int value = s_QtKeyboardMap.remapKey( event ); _gw->getEventQueue()->keyRelease( value ); } @@ -287,7 +312,7 @@ void GLWindow::mousePressEvent( QMouseEvent* event ) case Qt::NoButton: button = 0; break; default: button = 0; break; } - setKeyboardModifiers( event ); + setKeyboardModifiers(event->modifiers()); _gw->getEventQueue()->mouseButtonPress( event->x()*_devicePixelRatio, event->y()*_devicePixelRatio, button ); } @@ -302,7 +327,7 @@ void GLWindow::mouseReleaseEvent( QMouseEvent* event ) case Qt::NoButton: button = 0; break; default: button = 0; break; } - setKeyboardModifiers( event ); + setKeyboardModifiers(event->modifiers()); _gw->getEventQueue()->mouseButtonRelease( event->x()*_devicePixelRatio, event->y()*_devicePixelRatio, button ); } @@ -317,19 +342,19 @@ void GLWindow::mouseDoubleClickEvent( QMouseEvent* event ) case Qt::NoButton: button = 0; break; default: button = 0; break; } - setKeyboardModifiers( event ); + setKeyboardModifiers(event->modifiers()); _gw->getEventQueue()->mouseDoubleButtonPress( event->x()*_devicePixelRatio, event->y()*_devicePixelRatio, button ); } void GLWindow::mouseMoveEvent( QMouseEvent* event ) { - setKeyboardModifiers( event ); + setKeyboardModifiers(event->modifiers()); _gw->getEventQueue()->mouseMotion( event->x()*_devicePixelRatio, event->y()*_devicePixelRatio ); } void GLWindow::wheelEvent( QWheelEvent* event ) { - setKeyboardModifiers( event ); + setKeyboardModifiers(event->modifiers()); _gw->getEventQueue()->mouseScroll( event->orientation() == Qt::Vertical ? (event->delta()>0 ? osgGA::GUIEventAdapter::SCROLL_UP : osgGA::GUIEventAdapter::SCROLL_DOWN) : @@ -354,17 +379,17 @@ bool GraphicsWindowQt5::init( Qt::WindowFlags f ) WindowData* windowData = _traits.get() ? dynamic_cast(_traits->inheritedWindowData.get()) : 0; assert(!_window); _ownsWidget = true; - + // WindowFlags Qt::WindowFlags flags = f | Qt::Window | Qt::CustomizeWindowHint; if ( _traits->windowDecoration ) { flags |= Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint; flags |= Qt::WindowFullscreenButtonHint; - + // TODO - check if this is desirable or not on Windows+Linux //flags |= Qt::MaximizeUsingFullscreenGeometryHint; } - + // create window _window.reset(new GLWindow); _window->setFlags(flags); @@ -372,7 +397,7 @@ bool GraphicsWindowQt5::init( Qt::WindowFlags f ) _window->setFormat(traits2qSurfaceFormat(_traits.get())); _window->create(); _window->setTitle( _traits->windowName.c_str() ); - + // to get OS-dependant default positioning of the window (which is desirable), // we must take care to only set the position if explicitly requested. // hence we set X & Y to these marker values by default. @@ -380,7 +405,7 @@ bool GraphicsWindowQt5::init( Qt::WindowFlags f ) if ((_traits->x != std::numeric_limits::max()) && (_traits->y != std::numeric_limits::max())) { _window->setPosition( _traits->x, _traits->y ); } - + QSize sz(_traits->width, _traits->height); if ( !_traits->supportsResize ) { _window->setMinimumSize( sz ); @@ -388,7 +413,7 @@ bool GraphicsWindowQt5::init( Qt::WindowFlags f ) } else { _window->resize( sz ); } - + if (windowData->createFullscreen) { // this doesn't seem to actually work, so we // check the flag again in realizeImplementation() @@ -399,7 +424,7 @@ bool GraphicsWindowQt5::init( Qt::WindowFlags f ) if (_window->_isPrimaryWindow) { fgSetDouble("/sim/rendering/gui-pixel-ratio", _window->_devicePixelRatio); } - + _window->setGraphicsWindow( this ); useCursor( _traits->useCursor ); @@ -619,12 +644,12 @@ bool GraphicsWindowQt5::realizeImplementation() } else { _window->show(); } - + #if (OPENSCENEGRAPH_MAJOR_VERSION == 3) && (OPENSCENEGRAPH_MINOR_VERSION >= 4) // make sure the event queue has the correct window rectangle size and input range getEventQueue()->syncWindowRectangleWithGraphicsContext(); #endif - + _realized = true; return true; } @@ -640,7 +665,7 @@ void GraphicsWindowQt5::closeImplementation() _window->close(); _window.reset(); } - + _context.reset(); _realized = false; } @@ -714,7 +739,7 @@ bool GraphicsWindowQt5::checkEvents() _sendResizeOnEventCheck = false; _window->syncGeometryWithOSG(); } - + // todo - only if not running inside QApplication::exec; can we check this? QCoreApplication::processEvents(QEventLoop::AllEvents); @@ -764,8 +789,6 @@ public: Qt5WindowingSystem() { - OSG_INFO << "QtWindowingSystemInterface()" << std::endl; - } ~Qt5WindowingSystem() diff --git a/src/Viewer/GraphicsWindowQt5.hxx b/src/Viewer/GraphicsWindowQt5.hxx index 180a9f90b..fe7408d67 100644 --- a/src/Viewer/GraphicsWindowQt5.hxx +++ b/src/Viewer/GraphicsWindowQt5.hxx @@ -62,7 +62,7 @@ public: inline bool getForwardKeyEvents() const { return _forwardKeyEvents; } virtual void setForwardKeyEvents( bool f ) { _forwardKeyEvents = f; } - void setKeyboardModifiers( QInputEvent* event ); + void setKeyboardModifiers(const Qt::KeyboardModifiers qtMods); virtual void keyPressEvent( QKeyEvent* event ); virtual void keyReleaseEvent( QKeyEvent* event ); @@ -87,6 +87,8 @@ private slots: protected: void syncGeometryWithOSG(); + void updateEventQueueModifiers(QKeyEvent* event); + friend class GraphicsWindowQt5; GraphicsWindowQt5* _gw = nullptr; // back-pointer