summaryrefslogtreecommitdiffstats
path: root/src/core/web_event_factory.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/web_event_factory.cpp')
-rw-r--r--src/core/web_event_factory.cpp262
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;