diff options
Diffstat (limited to 'src/core/web_event_factory.cpp')
-rw-r--r-- | src/core/web_event_factory.cpp | 262 |
1 files changed, 190 insertions, 72 deletions
diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp index 792c4c612..479fc38f8 100644 --- a/src/core/web_event_factory.cpp +++ b/src/core/web_event_factory.cpp @@ -65,10 +65,13 @@ */ #include "web_event_factory.h" -#include "third_party/WebKit/Source/platform/WindowsKeyboardCodes.h" +#include "third_party/blink/renderer/platform/windows_keyboard_codes.h" #include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/keycodes/dom/dom_key.h" #include "ui/events/keycodes/dom/keycode_converter.h" +#include "ui/events/keycodes/keyboard_code_conversion.h" + +#include <QtGui/private/qtgui-config_p.h> #include <QCoreApplication> #include <QElapsedTimer> @@ -83,11 +86,134 @@ using namespace blink; -static int windowsKeyCodeForKeyEvent(unsigned int keycode, bool isKeypad) +enum class KeyboardDriver { Unknown, Windows, Cocoa, Xkb, Evdev }; + +static KeyboardDriver keyboardDriverImpl() +{ + QString platformName = QGuiApplication::platformName(); + + if (platformName == QLatin1Literal("windows")) + return KeyboardDriver::Windows; + + if (platformName == QLatin1Literal("cocoa")) + return KeyboardDriver::Cocoa; + + if (platformName == QLatin1Literal("xcb") || platformName == QLatin1Literal("wayland")) + return KeyboardDriver::Xkb; + +#if QT_CONFIG(libinput) && QT_CONFIG(xkbcommon_evdev) + // Based on QEglFSIntegration::createInputHandlers and QLibInputKeyboard::processKey. + if (platformName == QLatin1Literal("eglfs") && !qEnvironmentVariableIntValue("QT_QPA_EGLFS_NO_LIBINPUT")) + return KeyboardDriver::Xkb; +#endif + +#if QT_CONFIG(evdev) + // Based on QEglFSIntegration::createInputHandlers. + if (platformName == QLatin1Literal("eglfs")) + return KeyboardDriver::Evdev; +#endif + + return KeyboardDriver::Unknown; +} + +static KeyboardDriver keyboardDriver() +{ + static KeyboardDriver cached = keyboardDriverImpl(); + return cached; +} + +// Qt swaps the Control and Meta keys on macOS (unless the attribute +// AA_MacDontSwapCtrlAndMeta is set). To preserve compatibility with Chromium we +// want to unswap them when forwarding events. The following two functions, +// qtKeyForKeyEvent and qtModifiersForEvent, should be used for accessing the +// key() and modifiers() properties to ensure that the unswapping is done +// consistently. +static int qtKeyForKeyEvent(const QKeyEvent *ev) +{ + int key = ev->key(); + if (keyboardDriver() == KeyboardDriver::Cocoa && !qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + if (key == Qt::Key_Control) + return Qt::Key_Meta; + if (key == Qt::Key_Meta) + return Qt::Key_Control; + } + return key; +} + +// See above +static Qt::KeyboardModifiers qtModifiersForEvent(const QInputEvent *ev) +{ + Qt::KeyboardModifiers modifiers = ev->modifiers(); + if (keyboardDriver() == KeyboardDriver::Cocoa && !qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + bool controlModifier = modifiers.testFlag(Qt::ControlModifier); + bool metaModifier = modifiers.testFlag(Qt::MetaModifier); + modifiers.setFlag(Qt::ControlModifier, metaModifier); + modifiers.setFlag(Qt::MetaModifier, controlModifier); + } + return modifiers; +} + +// QKeyEvent::text() has some limits that we need to work around. +// +// On Linux, the Control modifier transformation is applied [1]. For example, +// pressing Ctrl+@ generates the text "\u0000". We would like "@" instead. +// +// [1]: https://www.x.org/releases/current/doc/kbproto/xkbproto.html#Interpreting_the_Control_Modifier +// +// On macOS, if the Control modifier is used, then no text is generated at all. +// We need some text. +// +// The workaround is to use QKeyEvent::key() instead, when needed. Contrary to +// the documentation, QKeyEvent::key() is not at all limited to the values +// listed in the Qt::Key enum: it can actually contain any Unicode codepoint. +// The only drawback is that letters are always upper cased. +static QString qtTextForKeyEvent(const QKeyEvent *ev, int qtKey, Qt::KeyboardModifiers qtModifiers) +{ + QString text = ev->text(); + + if ((qtModifiers & Qt::ControlModifier) && keyboardDriver() == KeyboardDriver::Xkb) + text.clear(); + + if (!text.isEmpty() || qtKey >= Qt::Key_Escape) + return text; + + QChar ch(qtKey); + if (!(qtModifiers & Qt::ShiftModifier)) // No way to check for caps lock + ch = ch.toLower(); + + text.append(ch); + return text; +} + +// The 'native key code' in Chromium refers to +// +// - On Windows: the Windows OEM scancode. +// - On macOS: the NSEvent's keyCode. +// - On Linux: The XKB keycode. +static quint32 nativeKeyCodeForKeyEvent(const QKeyEvent *ev) +{ + // Ifdefs here should match <ui/events/keycodes/dom/keycode_converter.cc>, + // since that is where the native key code is eventually used. + // + // Note that Xkb key codes are only supported under Linux (no BSDs, + // Cygwin/X, etc). Also evdev key codes are *not* supported for the same + // reason. +#if defined(Q_OS_WINDOWS) + return keyboardDriver() == KeyboardDriver::Windows ? ev->nativeScanCode() : 0; +#elif defined(Q_OS_MACOS) + return keyboardDriver() == KeyboardDriver::Cocoa ? ev->nativeVirtualKey() : 0; +#elif defined(Q_OS_LINUX) + return keyboardDriver() == KeyboardDriver::Xkb ? ev->nativeScanCode() : 0; +#else + return 0; // 0 means unknown, KeyboardEvent.code will be empty string. +#endif +} + +static int windowsKeyCodeForQtKey(int qtKey, bool isKeypad) { // Determine wheter the event comes from the keypad if (isKeypad) { - switch (keycode) { + switch (qtKey) { case Qt::Key_0: return VK_NUMPAD0; // (60) Numeric keypad 0 key case Qt::Key_1: @@ -154,7 +280,7 @@ static int windowsKeyCodeForKeyEvent(unsigned int keycode, bool isKeypad) } } else { - switch (keycode) { + switch (qtKey) { case Qt::Key_Backspace: return VK_BACK; // (08) BACKSPACE key case Qt::Key_Backtab: @@ -647,12 +773,10 @@ static int windowsKeyCodeForKeyEvent(unsigned int keycode, bool isKeypad) * - ui::DomKey::VIDEO_MODE_NEXT * - ui::DomKey::WINK */ -static ui::DomKey getDomKeyFromQKeyEvent(QKeyEvent *ev) +static ui::DomKey domKeyForQtKey(int qtKey) { - if (!ev->text().isEmpty()) - return ui::DomKey::FromCharacter(ev->text().toUcs4().first()); - - switch (ev->key()) { + Q_ASSERT(qtKey >= Qt::Key_Escape); + switch (qtKey) { case Qt::Key_Backspace: return ui::DomKey::BACKSPACE; case Qt::Key_Tab: @@ -1022,20 +1146,17 @@ static ui::DomKey getDomKeyFromQKeyEvent(QKeyEvent *ev) } } -static inline double currentTimeForEvent(const QEvent *event) +static inline base::TimeTicks currentTimeForEvent(const QEvent *event) { Q_ASSERT(event); if (event->type() != QEvent::Leave) { const QInputEvent *inputEvent = static_cast<const QInputEvent *>(event); if (inputEvent->timestamp()) - return static_cast<double>(inputEvent->timestamp()) / 1000; + return base::TimeTicks::FromInternalValue(inputEvent->timestamp() * 1000); } - static QElapsedTimer timer; - if (!timer.isValid()) - timer.start(); - return static_cast<double>(timer.elapsed()) / 1000; + return base::TimeTicks::Now(); } template<class T> @@ -1085,17 +1206,10 @@ static inline WebInputEvent::Modifiers modifierForKeyCode(int key) return WebInputEvent::kShiftKey; case Qt::Key_Alt: return WebInputEvent::kAltKey; -#if defined(Q_OS_OSX) - case Qt::Key_Control: - return (!qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) ? WebInputEvent::kMetaKey : WebInputEvent::kControlKey; - case Qt::Key_Meta: - return (!qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) ? WebInputEvent::kControlKey : WebInputEvent::kMetaKey; -#else case Qt::Key_Control: return WebInputEvent::kControlKey; case Qt::Key_Meta: return WebInputEvent::kMetaKey; -#endif default: return static_cast<WebInputEvent::Modifiers>(0); } @@ -1104,22 +1218,11 @@ static inline WebInputEvent::Modifiers modifierForKeyCode(int key) static inline WebInputEvent::Modifiers modifiersForEvent(const QInputEvent* event) { unsigned result = 0; - Qt::KeyboardModifiers modifiers = event->modifiers(); -#if defined(Q_OS_OSX) - if (!qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { - if (modifiers & Qt::ControlModifier) - result |= WebInputEvent::kMetaKey; - if (modifiers & Qt::MetaModifier) - result |= WebInputEvent::kControlKey; - } else -#endif - { - if (modifiers & Qt::ControlModifier) - result |= WebInputEvent::kControlKey; - if (modifiers & Qt::MetaModifier) - result |= WebInputEvent::kMetaKey; - } - + Qt::KeyboardModifiers modifiers = qtModifiersForEvent(event); + if (modifiers & Qt::ControlModifier) + result |= WebInputEvent::kControlKey; + if (modifiers & Qt::MetaModifier) + result |= WebInputEvent::kMetaKey; if (modifiers & Qt::ShiftModifier) result |= WebInputEvent::kShiftKey; if (modifiers & Qt::AltModifier) @@ -1141,7 +1244,7 @@ static inline WebInputEvent::Modifiers modifiersForEvent(const QInputEvent* even const QKeyEvent *keyEvent = static_cast<const QKeyEvent*>(event); if (keyEvent->isAutoRepeat()) result |= WebInputEvent::kIsAutoRepeat; - result |= modifierForKeyCode(keyEvent->key()); + result |= modifierForKeyCode(qtKeyForKeyEvent(keyEvent)); } default: break; @@ -1222,7 +1325,7 @@ WebMouseEvent WebEventFactory::toWebMouseEvent(QMouseEvent *ev, double dpiScale) WebMouseEvent WebEventFactory::toWebMouseEvent(QHoverEvent *ev, double dpiScale) { WebMouseEvent webKitEvent; - webKitEvent.SetTimeStampSeconds(currentTimeForEvent(ev)); + webKitEvent.SetTimeStamp(currentTimeForEvent(ev)); webKitEvent.SetModifiers(modifiersForEvent(ev)); webKitEvent.SetType(webEventTypeForEvent(ev)); @@ -1260,7 +1363,7 @@ WebMouseEvent WebEventFactory::toWebMouseEvent(QEvent *ev) Q_ASSERT(ev->type() == QEvent::Leave || ev->type() == QEvent::HoverLeave); WebMouseEvent webKitEvent; - webKitEvent.SetTimeStampSeconds(currentTimeForEvent(ev)); + webKitEvent.SetTimeStamp(currentTimeForEvent(ev)); webKitEvent.SetType(WebInputEvent::kMouseLeave); return webKitEvent; } @@ -1269,16 +1372,16 @@ WebMouseEvent WebEventFactory::toWebMouseEvent(QEvent *ev) WebGestureEvent WebEventFactory::toWebGestureEvent(QNativeGestureEvent *ev, double dpiScale) { WebGestureEvent webKitEvent; - webKitEvent.SetTimeStampSeconds(currentTimeForEvent(ev)); + webKitEvent.SetTimeStamp(currentTimeForEvent(ev)); webKitEvent.SetModifiers(modifiersForEvent(ev)); - webKitEvent.x = static_cast<int>(ev->localPos().x() / dpiScale); - webKitEvent.y = static_cast<int>(ev->localPos().y() / dpiScale); + webKitEvent.SetPositionInWidget(WebFloatPoint(ev->localPos().x() / dpiScale, + ev->localPos().y() / dpiScale)); - webKitEvent.global_x = static_cast<int>(ev->screenPos().x() / dpiScale); - webKitEvent.global_y = static_cast<int>(ev->screenPos().y() / dpiScale); + webKitEvent.SetPositionInScreen(WebFloatPoint(ev->screenPos().x() / dpiScale, + ev->screenPos().y() / dpiScale)); - webKitEvent.source_device = blink::kWebGestureDeviceTouchpad; + webKitEvent.SetSourceDevice(blink::kWebGestureDeviceTouchpad); Qt::NativeGestureType gestureType = ev->gestureType(); switch (gestureType) { @@ -1319,13 +1422,14 @@ static void setBlinkWheelEventDelta(blink::WebMouseWheelEvent &webEvent) webEvent.delta_y = webEvent.wheel_ticks_y * wheelScrollLines * cDefaultQtScrollStep; } -blink::WebMouseWheelEvent::Phase toBlinkPhase(Qt::ScrollPhase phase) +blink::WebMouseWheelEvent::Phase toBlinkPhase(QWheelEvent *ev) { - switch (phase) { + switch (ev->phase()) { case Qt::NoScrollPhase: + case Qt::ScrollMomentum: return blink::WebMouseWheelEvent::kPhaseNone; case Qt::ScrollBegin: - return blink::WebMouseWheelEvent::kPhaseBegan; + return ev->angleDelta().isNull() ? blink::WebMouseWheelEvent::kPhaseMayBegin : blink::WebMouseWheelEvent::kPhaseBegan; case Qt::ScrollUpdate: return blink::WebMouseWheelEvent::kPhaseChanged; case Qt::ScrollEnd: @@ -1340,13 +1444,13 @@ blink::WebMouseWheelEvent WebEventFactory::toWebWheelEvent(QWheelEvent *ev, doub WebMouseWheelEvent webEvent; webEvent.SetType(webEventTypeForEvent(ev)); webEvent.SetModifiers(modifiersForEvent(ev)); - webEvent.SetTimeStampSeconds(currentTimeForEvent(ev)); + webEvent.SetTimeStamp(currentTimeForEvent(ev)); webEvent.SetPositionInWidget(ev->x() / dpiScale, ev->y() / dpiScale); webEvent.SetPositionInScreen(ev->globalX(), ev->globalY()); webEvent.wheel_ticks_x = static_cast<float>(ev->angleDelta().x()) / QWheelEvent::DefaultDeltasPerStep; webEvent.wheel_ticks_y = static_cast<float>(ev->angleDelta().y()) / QWheelEvent::DefaultDeltasPerStep; - webEvent.phase = toBlinkPhase(ev->phase()); + webEvent.phase = toBlinkPhase(ev); webEvent.has_precise_scrolling_deltas = true; setBlinkWheelEventDelta(webEvent); @@ -1359,10 +1463,10 @@ bool WebEventFactory::coalesceWebWheelEvent(blink::WebMouseWheelEvent &webEvent, return false; if (modifiersForEvent(ev) != webEvent.GetModifiers()) return false; - if (toBlinkPhase(ev->phase()) != webEvent.phase) + if (toBlinkPhase(ev) != webEvent.phase) return false; - webEvent.SetTimeStampSeconds(currentTimeForEvent(ev)); + webEvent.SetTimeStamp(currentTimeForEvent(ev)); webEvent.SetPositionInWidget(ev->x() / dpiScale, ev->y() / dpiScale); webEvent.SetPositionInScreen(ev->globalX(), ev->globalY()); @@ -1376,23 +1480,40 @@ bool WebEventFactory::coalesceWebWheelEvent(blink::WebMouseWheelEvent &webEvent, content::NativeWebKeyboardEvent WebEventFactory::toWebKeyboardEvent(QKeyEvent *ev) { content::NativeWebKeyboardEvent webKitEvent(reinterpret_cast<gfx::NativeEvent>(ev)); - webKitEvent.SetTimeStampSeconds(currentTimeForEvent(ev)); + webKitEvent.SetTimeStamp(currentTimeForEvent(ev)); webKitEvent.SetModifiers(modifiersForEvent(ev)); webKitEvent.SetType(webEventTypeForEvent(ev)); - webKitEvent.native_key_code = ev->nativeVirtualKey(); - webKitEvent.windows_key_code = windowsKeyCodeForKeyEvent(ev->key(), ev->modifiers() & Qt::KeypadModifier); - webKitEvent.dom_key = getDomKeyFromQKeyEvent(ev); - - ui::DomCode domCode = ui::DomCode::NONE; - int scanCode = ev->nativeScanCode(); - if (scanCode) - domCode = ui::KeycodeConverter::NativeKeycodeToDomCode(scanCode); - webKitEvent.dom_code = static_cast<int>(domCode); - - const ushort* text = ev->text().utf16(); - memcpy(&webKitEvent.text, text, std::min(sizeof(webKitEvent.text), size_t(ev->text().length() * 2))); - memcpy(&webKitEvent.unmodified_text, text, std::min(sizeof(webKitEvent.unmodified_text), size_t(ev->text().length() * 2))); + int qtKey = qtKeyForKeyEvent(ev); + Qt::KeyboardModifiers qtModifiers = qtModifiersForEvent(ev); + QString qtText = qtTextForKeyEvent(ev, qtKey, qtModifiers); + + webKitEvent.native_key_code = nativeKeyCodeForKeyEvent(ev); + webKitEvent.windows_key_code = windowsKeyCodeForQtKey(qtKey, qtModifiers & Qt::KeypadModifier); + + if (qtKey >= Qt::Key_Escape) + webKitEvent.dom_key = domKeyForQtKey(qtKey); + else if (!qtText.isEmpty()) + webKitEvent.dom_key = ui::DomKey::FromCharacter(qtText.toUcs4().first()); + + // The dom_code field should contain the USB keycode of the *physical* key + // that was pressed. Physical meaning independent of layout and modifiers. + // + // Since this information is not available from QKeyEvent in portable form, + // we try to compute it from the native key code. If there's no native key + // code available either, then we assume a US layout and convert it from + // windows_key_code. The result will be incorrect on non-US layouts. + if (webKitEvent.native_key_code) + webKitEvent.dom_code = static_cast<int>( + ui::KeycodeConverter::NativeKeycodeToDomCode(webKitEvent.native_key_code)); + else + webKitEvent.dom_code = static_cast<int>( + ui::UsLayoutKeyboardCodeToDomCode(static_cast<ui::KeyboardCode>(webKitEvent.windows_key_code))); + + const ushort* text = qtText.utf16(); + size_t textSize = std::min(sizeof(webKitEvent.text), size_t(qtText.length() * 2)); + memcpy(&webKitEvent.text, text, textSize); + memcpy(&webKitEvent.unmodified_text, text, textSize); if (webKitEvent.windows_key_code == VK_RETURN) { // This is the same behavior as GTK: @@ -1471,10 +1592,7 @@ bool WebEventFactory::getEditCommand(QKeyEvent *event, std::string *editCommand) } #ifdef Q_OS_MACOS - Qt::KeyboardModifier cmdKey = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta) ? - Qt::MetaModifier : - Qt::ControlModifier; - if ((event->modifiers() & ~Qt::ShiftModifier) == cmdKey) { + if ((qtModifiersForEvent(event) & ~Qt::ShiftModifier) == Qt::MetaModifier) { if (event->key() == Qt::Key_Backspace) { *editCommand = "DeleteToBeginningOfLine"; return true; |