Transfer the key handling logic to QWasmWindow

Also, use the embind approach as the rest of the events do, and
introduce a KeyEvent class which simplifies and streamlines event
support.

The event translator has been given a more specific function of
just handling the dead keys. Rest of the translation functionality
is coded directly in KeyEvent for more encapsulation.

Change-Id: I11b0262fc42fe920206ecc6de0d434b9d9ab9998
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
Mikolaj Boc 2023-01-24 10:18:34 +01:00
parent 62be4ab5be
commit d141d68949
17 changed files with 464 additions and 477 deletions

View File

@ -23,9 +23,9 @@ qt_internal_add_plugin(QWasmIntegrationPlugin
qwasmdom.cpp qwasmdom.h qwasmdom.cpp qwasmdom.h
qwasmevent.cpp qwasmevent.h qwasmevent.cpp qwasmevent.h
qwasmeventdispatcher.cpp qwasmeventdispatcher.h qwasmeventdispatcher.cpp qwasmeventdispatcher.h
qwasmeventtranslator.cpp qwasmeventtranslator.h
qwasmfontdatabase.cpp qwasmfontdatabase.h qwasmfontdatabase.cpp qwasmfontdatabase.h
qwasmintegration.cpp qwasmintegration.h qwasmintegration.cpp qwasmintegration.h
qwasmkeytranslator.cpp qwasmkeytranslator.h
qwasmoffscreensurface.cpp qwasmoffscreensurface.h qwasmoffscreensurface.cpp qwasmoffscreensurface.h
qwasmopenglcontext.cpp qwasmopenglcontext.h qwasmopenglcontext.cpp qwasmopenglcontext.h
qwasmplatform.cpp qwasmplatform.h qwasmplatform.cpp qwasmplatform.h

View File

@ -3,6 +3,7 @@
#include "qwasmclipboard.h" #include "qwasmclipboard.h"
#include "qwasmdom.h" #include "qwasmdom.h"
#include "qwasmevent.h"
#include "qwasmwindow.h" #include "qwasmwindow.h"
#include <private/qstdweb_p.h> #include <private/qstdweb_p.h>
@ -129,11 +130,9 @@ void QWasmClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode)
writeToClipboard(); writeToClipboard();
} }
QWasmClipboard::ProcessKeyboardResult QWasmClipboard::ProcessKeyboardResult QWasmClipboard::processKeyboard(const KeyEvent &event)
QWasmClipboard::processKeyboard(const QWasmEventTranslator::TranslatedEvent &event,
const QFlags<Qt::KeyboardModifier> &modifiers)
{ {
if (event.type != QEvent::KeyPress || !modifiers.testFlag(Qt::ControlModifier)) if (event.type != EventType::KeyDown || !event.modifiers.testFlag(Qt::ControlModifier))
return ProcessKeyboardResult::Ignored; return ProcessKeyboardResult::Ignored;
if (event.key != Qt::Key_C && event.key != Qt::Key_V && event.key != Qt::Key_X) if (event.key != Qt::Key_C && event.key != Qt::Key_V && event.key != Qt::Key_X)

View File

@ -12,10 +12,10 @@
#include <emscripten/bind.h> #include <emscripten/bind.h>
#include <emscripten/val.h> #include <emscripten/val.h>
#include "qwasmeventtranslator.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
struct KeyEvent;
class QWasmClipboard : public QObject, public QPlatformClipboard class QWasmClipboard : public QObject, public QPlatformClipboard
{ {
public: public:
@ -34,8 +34,7 @@ public:
bool supportsMode(QClipboard::Mode mode) const override; bool supportsMode(QClipboard::Mode mode) const override;
bool ownsMode(QClipboard::Mode mode) const override; bool ownsMode(QClipboard::Mode mode) const override;
ProcessKeyboardResult processKeyboard(const QWasmEventTranslator::TranslatedEvent &event, ProcessKeyboardResult processKeyboard(const KeyEvent &event);
const QFlags<Qt::KeyboardModifier> &modifiers);
static void installEventHandlers(const emscripten::val &target); static void installEventHandlers(const emscripten::val &target);
bool hasClipboardApi(); bool hasClipboardApi();

View File

@ -3,7 +3,6 @@
#include "qwasmcompositor.h" #include "qwasmcompositor.h"
#include "qwasmwindow.h" #include "qwasmwindow.h"
#include "qwasmeventtranslator.h"
#include "qwasmeventdispatcher.h" #include "qwasmeventdispatcher.h"
#include "qwasmclipboard.h" #include "qwasmclipboard.h"
#include "qwasmevent.h" #include "qwasmevent.h"
@ -30,9 +29,7 @@ using namespace emscripten;
Q_GUI_EXPORT int qt_defaultDpiX(); Q_GUI_EXPORT int qt_defaultDpiX();
QWasmCompositor::QWasmCompositor(QWasmScreen *screen) QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
: QObject(screen), : QObject(screen), m_windowStack(std::bind(&QWasmCompositor::onTopWindowChanged, this))
m_windowStack(std::bind(&QWasmCompositor::onTopWindowChanged, this)),
m_eventTranslator(std::make_unique<QWasmEventTranslator>())
{ {
m_touchDevice = std::make_unique<QPointingDevice>( m_touchDevice = std::make_unique<QPointingDevice>(
"touchscreen", 1, QInputDevice::DeviceType::TouchScreen, "touchscreen", 1, QInputDevice::DeviceType::TouchScreen,
@ -61,12 +58,11 @@ void QWasmCompositor::onScreenDeleting()
void QWasmCompositor::deregisterEventHandlers() void QWasmCompositor::deregisterEventHandlers()
{ {
QByteArray screenElementSelector = screen()->eventTargetId().toUtf8(); QByteArray screenElementSelector = screen()->eventTargetId().toUtf8();
emscripten_set_keydown_callback(screenElementSelector.constData(), 0, 0, NULL);
emscripten_set_keyup_callback(screenElementSelector.constData(), 0, 0, NULL);
emscripten_set_touchstart_callback(screenElementSelector.constData(), 0, 0, NULL); emscripten_set_touchstart_callback(screenElementSelector.constData(), 0, 0, NULL);
emscripten_set_touchend_callback(screenElementSelector.constData(), 0, 0, NULL); emscripten_set_touchend_callback(screenElementSelector.constData(), 0, 0, NULL);
emscripten_set_touchmove_callback(screenElementSelector.constData(), 0, 0, NULL); emscripten_set_touchmove_callback(screenElementSelector.constData(), 0, 0, NULL);
emscripten_set_touchcancel_callback(screenElementSelector.constData(), 0, 0, NULL); emscripten_set_touchcancel_callback(screenElementSelector.constData(), 0, 0, NULL);
} }
@ -82,11 +78,6 @@ void QWasmCompositor::initEventHandlers()
constexpr EM_BOOL UseCapture = 1; constexpr EM_BOOL UseCapture = 1;
const QByteArray screenElementSelector = screen()->eventTargetId().toUtf8(); const QByteArray screenElementSelector = screen()->eventTargetId().toUtf8();
emscripten_set_keydown_callback(screenElementSelector.constData(), (void *)this, UseCapture,
&keyboard_cb);
emscripten_set_keyup_callback(screenElementSelector.constData(), (void *)this, UseCapture,
&keyboard_cb);
emscripten_set_touchstart_callback(screenElementSelector.constData(), (void *)this, UseCapture, emscripten_set_touchstart_callback(screenElementSelector.constData(), (void *)this, UseCapture,
&touchCallback); &touchCallback);
emscripten_set_touchend_callback(screenElementSelector.constData(), (void *)this, UseCapture, emscripten_set_touchend_callback(screenElementSelector.constData(), (void *)this, UseCapture,
@ -283,46 +274,12 @@ QWasmScreen *QWasmCompositor::screen()
return static_cast<QWasmScreen *>(parent()); return static_cast<QWasmScreen *>(parent());
} }
int QWasmCompositor::keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
{
QWasmCompositor *wasmCompositor = reinterpret_cast<QWasmCompositor *>(userData);
return static_cast<int>(wasmCompositor->processKeyboard(eventType, keyEvent));
}
int QWasmCompositor::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) int QWasmCompositor::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
{ {
auto compositor = reinterpret_cast<QWasmCompositor*>(userData); auto compositor = reinterpret_cast<QWasmCompositor*>(userData);
return static_cast<int>(compositor->processTouch(eventType, touchEvent)); return static_cast<int>(compositor->processTouch(eventType, touchEvent));
} }
bool QWasmCompositor::processKeyboard(int eventType, const EmscriptenKeyboardEvent *emKeyEvent)
{
constexpr bool ProceedToNativeEvent = false;
Q_ASSERT(eventType == EMSCRIPTEN_EVENT_KEYDOWN || eventType == EMSCRIPTEN_EVENT_KEYUP);
auto translatedEvent = m_eventTranslator->translateKeyEvent(eventType, emKeyEvent);
const QFlags<Qt::KeyboardModifier> modifiers = KeyboardModifier::getForEvent(*emKeyEvent);
const auto clipboardResult = QWasmIntegration::get()->getWasmClipboard()->processKeyboard(
translatedEvent, modifiers);
using ProcessKeyboardResult = QWasmClipboard::ProcessKeyboardResult;
if (clipboardResult == ProcessKeyboardResult::NativeClipboardEventNeeded)
return ProceedToNativeEvent;
if (translatedEvent.text.isEmpty())
translatedEvent.text = QString(emKeyEvent->key);
if (translatedEvent.text.size() > 1)
translatedEvent.text.clear();
const auto result =
QWindowSystemInterface::handleKeyEvent(
0, translatedEvent.type, translatedEvent.key, modifiers, translatedEvent.text);
return clipboardResult == ProcessKeyboardResult::NativeClipboardEventAndCopiedDataNeeded
? ProceedToNativeEvent
: result;
}
bool QWasmCompositor::processTouch(int eventType, const EmscriptenTouchEvent *touchEvent) bool QWasmCompositor::processTouch(int eventType, const EmscriptenTouchEvent *touchEvent)
{ {
QList<QWindowSystemInterface::TouchPoint> touchPointList; QList<QWindowSystemInterface::TouchPoint> touchPointList;

View File

@ -26,7 +26,6 @@ class QWasmWindow;
class QWasmScreen; class QWasmScreen;
class QOpenGLContext; class QOpenGLContext;
class QOpenGLTexture; class QOpenGLTexture;
class QWasmEventTranslator;
class QWasmCompositor final : public QObject class QWasmCompositor final : public QObject
{ {
@ -69,11 +68,8 @@ private:
void deliverUpdateRequests(); void deliverUpdateRequests();
void deliverUpdateRequest(QWasmWindow *window, UpdateRequestDeliveryType updateType); void deliverUpdateRequest(QWasmWindow *window, UpdateRequestDeliveryType updateType);
static int keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData);
static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData); static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData);
bool processKeyboard(int eventType, const EmscriptenKeyboardEvent *keyEvent);
bool processTouch(int eventType, const EmscriptenTouchEvent *touchEvent); bool processTouch(int eventType, const EmscriptenTouchEvent *touchEvent);
void updateEnabledState(); void updateEnabledState();
@ -89,8 +85,6 @@ private:
std::unique_ptr<QPointingDevice> m_touchDevice; std::unique_ptr<QPointingDevice> m_touchDevice;
QMap<int, QPointF> m_pressedTouchIds; QMap<int, QPointF> m_pressedTouchIds;
std::unique_ptr<QWasmEventTranslator> m_eventTranslator;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -3,8 +3,39 @@
#include "qwasmevent.h" #include "qwasmevent.h"
#include "qwasmkeytranslator.h"
#include <QtCore/private/qmakearray_p.h>
#include <QtCore/private/qstringiterator_p.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
namespace {
constexpr std::string_view WebDeadKeyValue = "Dead";
bool isDeadKeyEvent(const char *key)
{
return qstrncmp(key, WebDeadKeyValue.data(), WebDeadKeyValue.size()) == 0;
}
Qt::Key webKeyToQtKey(const std::string &code, const std::string &key, bool isDeadKey)
{
if (isDeadKey) {
if (auto mapping = QWasmKeyTranslator::mapWebKeyTextToQtKey(code.c_str()))
return *mapping;
}
if (auto mapping = QWasmKeyTranslator::mapWebKeyTextToQtKey(key.c_str()))
return *mapping;
if (isDeadKey)
return Qt::Key_unknown;
// cast to unicode key
QString str = QString::fromUtf8(key.c_str()).toUpper();
QStringIterator i(str);
return static_cast<Qt::Key>(i.next(0));
}
} // namespace
namespace KeyboardModifier namespace KeyboardModifier
{ {
template <> template <>
@ -28,6 +59,51 @@ Event &Event::operator=(const Event &other) = default;
Event &Event::operator=(Event &&other) = default; Event &Event::operator=(Event &&other) = default;
KeyEvent::KeyEvent(EventType type, emscripten::val event) : Event(type, event["target"])
{
const auto code = event["code"].as<std::string>();
const auto webKey = event["key"].as<std::string>();
deadKey = isDeadKeyEvent(webKey.c_str());
key = webKeyToQtKey(code, webKey, deadKey);
modifiers = KeyboardModifier::getForEvent(event);
text = QString::fromUtf8(webKey);
if (text.size() > 1)
text.clear();
}
KeyEvent::~KeyEvent() = default;
KeyEvent::KeyEvent(const KeyEvent &other) = default;
KeyEvent::KeyEvent(KeyEvent &&other) = default;
KeyEvent &KeyEvent::operator=(const KeyEvent &other) = default;
KeyEvent &KeyEvent::operator=(KeyEvent &&other) = default;
std::optional<KeyEvent> KeyEvent::fromWebWithDeadKeyTranslation(emscripten::val event,
QWasmDeadKeySupport *deadKeySupport)
{
const auto eventType = ([&event]() -> std::optional<EventType> {
const auto eventTypeString = event["type"].as<std::string>();
if (eventTypeString == "keydown")
return EventType::KeyDown;
else if (eventTypeString == "keyup")
return EventType::KeyUp;
return std::nullopt;
})();
if (!eventType)
return std::nullopt;
auto result = KeyEvent(*eventType, event);
deadKeySupport->applyDeadKeyTranslations(&result);
return result;
}
MouseEvent::MouseEvent(EventType type, emscripten::val event) : Event(type, event["target"]) MouseEvent::MouseEvent(EventType type, emscripten::val event) : Event(type, event["target"])
{ {
mouseButton = MouseEvent::buttonFromWeb(event["button"].as<int>()); mouseButton = MouseEvent::buttonFromWeb(event["button"].as<int>());

View File

@ -17,8 +17,12 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QWasmDeadKeySupport;
enum class EventType { enum class EventType {
Drop, Drop,
KeyDown,
KeyUp,
PointerDown, PointerDown,
PointerMove, PointerMove,
PointerUp, PointerUp,
@ -113,26 +117,37 @@ QFlags<Qt::KeyboardModifier> getForEvent<EmscriptenKeyboardEvent>(
struct Event struct Event
{ {
EventType type;
emscripten::val target = emscripten::val::undefined();
Event(EventType type, emscripten::val target); Event(EventType type, emscripten::val target);
~Event(); ~Event();
Event(const Event &other); Event(const Event &other);
Event(Event &&other); Event(Event &&other);
Event &operator=(const Event &other); Event &operator=(const Event &other);
Event &operator=(Event &&other); Event &operator=(Event &&other);
EventType type;
emscripten::val target = emscripten::val::undefined();
};
struct KeyEvent : public Event
{
static std::optional<KeyEvent>
fromWebWithDeadKeyTranslation(emscripten::val webEvent, QWasmDeadKeySupport *deadKeySupport);
KeyEvent(EventType type, emscripten::val webEvent);
~KeyEvent();
KeyEvent(const KeyEvent &other);
KeyEvent(KeyEvent &&other);
KeyEvent &operator=(const KeyEvent &other);
KeyEvent &operator=(KeyEvent &&other);
Qt::Key key;
QFlags<Qt::KeyboardModifier> modifiers;
bool deadKey;
QString text;
}; };
struct MouseEvent : public Event struct MouseEvent : public Event
{ {
QPoint localPoint;
QPoint pointInPage;
QPoint pointInViewport;
Qt::MouseButton mouseButton;
Qt::MouseButtons mouseButtons;
QFlags<Qt::KeyboardModifier> modifiers;
MouseEvent(EventType type, emscripten::val webEvent); MouseEvent(EventType type, emscripten::val webEvent);
~MouseEvent(); ~MouseEvent();
MouseEvent(const MouseEvent &other); MouseEvent(const MouseEvent &other);
@ -174,6 +189,13 @@ struct MouseEvent : public Event
return QEvent::None; return QEvent::None;
} }
} }
QPoint localPoint;
QPoint pointInPage;
QPoint pointInViewport;
Qt::MouseButton mouseButton;
Qt::MouseButtons mouseButtons;
QFlags<Qt::KeyboardModifier> modifiers;
}; };
struct PointerEvent : public MouseEvent struct PointerEvent : public MouseEvent

View File

@ -1,339 +0,0 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qwasmeventtranslator.h"
#include "qwasmeventdispatcher.h"
#include "qwasmcompositor.h"
#include "qwasmintegration.h"
#include "qwasmclipboard.h"
#include "qwasmcursor.h"
#include <QtGui/qevent.h>
#include <qpa/qwindowsysteminterface.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qglobal.h>
#include <QtCore/qobject.h>
#include <QtCore/qdeadlinetimer.h>
#include <private/qmakearray_p.h>
#include <QtCore/qnamespace.h>
#include <QCursor>
#include <QtCore/private/qstringiterator_p.h>
#include <emscripten/bind.h>
#include <iostream>
QT_BEGIN_NAMESPACE
using namespace emscripten;
namespace {
constexpr std::string_view WebDeadKeyValue = "Dead";
struct Emkb2QtData
{
static constexpr char StringTerminator = '\0';
const char *em;
unsigned int qt;
constexpr bool operator<=(const Emkb2QtData &that) const noexcept
{
return !(strcmp(that) > 0);
}
bool operator<(const Emkb2QtData &that) const noexcept { return ::strcmp(em, that.em) < 0; }
constexpr bool operator==(const Emkb2QtData &that) const noexcept { return strcmp(that) == 0; }
constexpr int strcmp(const Emkb2QtData &that, const int i = 0) const
{
return em[i] == StringTerminator && that.em[i] == StringTerminator ? 0
: em[i] == StringTerminator ? -1
: that.em[i] == StringTerminator ? 1
: em[i] < that.em[i] ? -1
: em[i] > that.em[i] ? 1
: strcmp(that, i + 1);
}
};
template<unsigned int Qt, char ... EmChar>
struct Emkb2Qt
{
static constexpr const char storage[sizeof ... (EmChar) + 1] = {EmChar..., '\0'};
using Type = Emkb2QtData;
static constexpr Type data() noexcept { return Type{storage, Qt}; }
};
template<unsigned int Qt, char ... EmChar> constexpr char Emkb2Qt<Qt, EmChar...>::storage[];
static constexpr const auto WebToQtKeyCodeMappings = qMakeArray(
QSortedData<
Emkb2Qt< Qt::Key_Escape, 'E','s','c','a','p','e' >,
Emkb2Qt< Qt::Key_Tab, 'T','a','b' >,
Emkb2Qt< Qt::Key_Backspace, 'B','a','c','k','s','p','a','c','e' >,
Emkb2Qt< Qt::Key_Return, 'E','n','t','e','r' >,
Emkb2Qt< Qt::Key_Insert, 'I','n','s','e','r','t' >,
Emkb2Qt< Qt::Key_Delete, 'D','e','l','e','t','e' >,
Emkb2Qt< Qt::Key_Pause, 'P','a','u','s','e' >,
Emkb2Qt< Qt::Key_Pause, 'C','l','e','a','r' >,
Emkb2Qt< Qt::Key_Home, 'H','o','m','e' >,
Emkb2Qt< Qt::Key_End, 'E','n','d' >,
Emkb2Qt< Qt::Key_Left, 'A','r','r','o','w','L','e','f','t' >,
Emkb2Qt< Qt::Key_Up, 'A','r','r','o','w','U','p' >,
Emkb2Qt< Qt::Key_Right, 'A','r','r','o','w','R','i','g','h','t' >,
Emkb2Qt< Qt::Key_Down, 'A','r','r','o','w','D','o','w','n' >,
Emkb2Qt< Qt::Key_PageUp, 'P','a','g','e','U','p' >,
Emkb2Qt< Qt::Key_PageDown, 'P','a','g','e','D','o','w','n' >,
Emkb2Qt< Qt::Key_Shift, 'S','h','i','f','t' >,
Emkb2Qt< Qt::Key_Control, 'C','o','n','t','r','o','l' >,
Emkb2Qt< Qt::Key_Meta, 'M','e','t','a'>,
Emkb2Qt< Qt::Key_Meta, 'O','S'>,
Emkb2Qt< Qt::Key_Alt, 'A','l','t','L','e','f','t' >,
Emkb2Qt< Qt::Key_Alt, 'A','l','t' >,
Emkb2Qt< Qt::Key_CapsLock, 'C','a','p','s','L','o','c','k' >,
Emkb2Qt< Qt::Key_NumLock, 'N','u','m','L','o','c','k' >,
Emkb2Qt< Qt::Key_ScrollLock, 'S','c','r','o','l','l','L','o','c','k' >,
Emkb2Qt< Qt::Key_F1, 'F','1' >,
Emkb2Qt< Qt::Key_F2, 'F','2' >,
Emkb2Qt< Qt::Key_F3, 'F','3' >,
Emkb2Qt< Qt::Key_F4, 'F','4' >,
Emkb2Qt< Qt::Key_F5, 'F','5' >,
Emkb2Qt< Qt::Key_F6, 'F','6' >,
Emkb2Qt< Qt::Key_F7, 'F','7' >,
Emkb2Qt< Qt::Key_F8, 'F','8' >,
Emkb2Qt< Qt::Key_F9, 'F','9' >,
Emkb2Qt< Qt::Key_F10, 'F','1','0' >,
Emkb2Qt< Qt::Key_F11, 'F','1','1' >,
Emkb2Qt< Qt::Key_F12, 'F','1','2' >,
Emkb2Qt< Qt::Key_F13, 'F','1','3' >,
Emkb2Qt< Qt::Key_F14, 'F','1','4' >,
Emkb2Qt< Qt::Key_F15, 'F','1','5' >,
Emkb2Qt< Qt::Key_F16, 'F','1','6' >,
Emkb2Qt< Qt::Key_F17, 'F','1','7' >,
Emkb2Qt< Qt::Key_F18, 'F','1','8' >,
Emkb2Qt< Qt::Key_F19, 'F','1','9' >,
Emkb2Qt< Qt::Key_F20, 'F','2','0' >,
Emkb2Qt< Qt::Key_F21, 'F','2','1' >,
Emkb2Qt< Qt::Key_F22, 'F','2','2' >,
Emkb2Qt< Qt::Key_F23, 'F','2','3' >,
Emkb2Qt< Qt::Key_Paste, 'P','a','s','t','e' >,
Emkb2Qt< Qt::Key_AltGr, 'A','l','t','R','i','g','h','t' >,
Emkb2Qt< Qt::Key_Help, 'H','e','l','p' >,
Emkb2Qt< Qt::Key_yen, 'I','n','t','l','Y','e','n' >,
Emkb2Qt< Qt::Key_Menu, 'C','o','n','t','e','x','t','M','e','n','u' >
>::Data{}
);
static constexpr const auto WebToQtKeyCodeMappingsWithShift = qMakeArray(
QSortedData<
// shifted
Emkb2Qt< Qt::Key_Agrave, '\xc3','\x80' >,
Emkb2Qt< Qt::Key_Aacute, '\xc3','\x81' >,
Emkb2Qt< Qt::Key_Acircumflex, '\xc3','\x82' >,
Emkb2Qt< Qt::Key_Adiaeresis, '\xc3','\x84' >,
Emkb2Qt< Qt::Key_AE, '\xc3','\x86' >,
Emkb2Qt< Qt::Key_Atilde, '\xc3','\x83' >,
Emkb2Qt< Qt::Key_Aring, '\xc3','\x85' >,
Emkb2Qt< Qt::Key_Egrave, '\xc3','\x88' >,
Emkb2Qt< Qt::Key_Eacute, '\xc3','\x89' >,
Emkb2Qt< Qt::Key_Ecircumflex, '\xc3','\x8a' >,
Emkb2Qt< Qt::Key_Ediaeresis, '\xc3','\x8b' >,
Emkb2Qt< Qt::Key_Iacute, '\xc3','\x8d' >,
Emkb2Qt< Qt::Key_Icircumflex, '\xc3','\x8e' >,
Emkb2Qt< Qt::Key_Idiaeresis, '\xc3','\x8f' >,
Emkb2Qt< Qt::Key_Igrave, '\xc3','\x8c' >,
Emkb2Qt< Qt::Key_Ocircumflex, '\xc3','\x94' >,
Emkb2Qt< Qt::Key_Odiaeresis, '\xc3','\x96' >,
Emkb2Qt< Qt::Key_Ograve, '\xc3','\x92' >,
Emkb2Qt< Qt::Key_Oacute, '\xc3','\x93' >,
Emkb2Qt< Qt::Key_Ooblique, '\xc3','\x98' >,
Emkb2Qt< Qt::Key_Otilde, '\xc3','\x95' >,
Emkb2Qt< Qt::Key_Ucircumflex, '\xc3','\x9b' >,
Emkb2Qt< Qt::Key_Udiaeresis, '\xc3','\x9c' >,
Emkb2Qt< Qt::Key_Ugrave, '\xc3','\x99' >,
Emkb2Qt< Qt::Key_Uacute, '\xc3','\x9a' >,
Emkb2Qt< Qt::Key_Ntilde, '\xc3','\x91' >,
Emkb2Qt< Qt::Key_Ccedilla, '\xc3','\x87' >,
Emkb2Qt< Qt::Key_ydiaeresis, '\xc3','\x8f' >,
Emkb2Qt< Qt::Key_Yacute, '\xc3','\x9d' >
>::Data{}
);
std::optional<Qt::Key> findMappingByBisection(const char *toFind)
{
const Emkb2QtData searchKey{ toFind, 0 };
const auto it = std::lower_bound(WebToQtKeyCodeMappings.cbegin(), WebToQtKeyCodeMappings.cend(),
searchKey);
return it != WebToQtKeyCodeMappings.cend() && searchKey == *it ? static_cast<Qt::Key>(it->qt)
: std::optional<Qt::Key>();
}
bool isDeadKeyEvent(const EmscriptenKeyboardEvent *emKeyEvent)
{
return qstrncmp(emKeyEvent->key, WebDeadKeyValue.data(), WebDeadKeyValue.size()) == 0;
}
Qt::Key translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey)
{
const bool deadKeyEvent = isDeadKeyEvent(emscriptKey);
if (deadKeyEvent) {
if (auto mapping = findMappingByBisection(emscriptKey->code))
return *mapping;
}
if (auto mapping = findMappingByBisection(emscriptKey->key))
return *mapping;
if (deadKeyEvent)
return Qt::Key_unknown;
// cast to unicode key
QString str = QString::fromUtf8(emscriptKey->key).toUpper();
QStringIterator i(str);
return static_cast<Qt::Key>(i.next(0));
}
struct KeyMapping { Qt::Key from, to; };
constexpr KeyMapping tildeKeyTable[] = { // ~
{ Qt::Key_A, Qt::Key_Atilde },
{ Qt::Key_N, Qt::Key_Ntilde },
{ Qt::Key_O, Qt::Key_Otilde },
};
constexpr KeyMapping graveKeyTable[] = { // `
{ Qt::Key_A, Qt::Key_Agrave },
{ Qt::Key_E, Qt::Key_Egrave },
{ Qt::Key_I, Qt::Key_Igrave },
{ Qt::Key_O, Qt::Key_Ograve },
{ Qt::Key_U, Qt::Key_Ugrave },
};
constexpr KeyMapping acuteKeyTable[] = { // '
{ Qt::Key_A, Qt::Key_Aacute },
{ Qt::Key_E, Qt::Key_Eacute },
{ Qt::Key_I, Qt::Key_Iacute },
{ Qt::Key_O, Qt::Key_Oacute },
{ Qt::Key_U, Qt::Key_Uacute },
{ Qt::Key_Y, Qt::Key_Yacute },
};
constexpr KeyMapping diaeresisKeyTable[] = { // umlaut ¨
{ Qt::Key_A, Qt::Key_Adiaeresis },
{ Qt::Key_E, Qt::Key_Ediaeresis },
{ Qt::Key_I, Qt::Key_Idiaeresis },
{ Qt::Key_O, Qt::Key_Odiaeresis },
{ Qt::Key_U, Qt::Key_Udiaeresis },
{ Qt::Key_Y, Qt::Key_ydiaeresis },
};
constexpr KeyMapping circumflexKeyTable[] = { // ^
{ Qt::Key_A, Qt::Key_Acircumflex },
{ Qt::Key_E, Qt::Key_Ecircumflex },
{ Qt::Key_I, Qt::Key_Icircumflex },
{ Qt::Key_O, Qt::Key_Ocircumflex },
{ Qt::Key_U, Qt::Key_Ucircumflex },
};
static Qt::Key find_impl(const KeyMapping *first, const KeyMapping *last, Qt::Key key) noexcept
{
while (first != last) {
if (first->from == key)
return first->to;
++first;
}
return Qt::Key_unknown;
}
template <size_t N>
static Qt::Key find(const KeyMapping (&map)[N], Qt::Key key) noexcept
{
return find_impl(map, map + N, key);
}
Qt::Key translateBaseKeyUsingDeadKey(Qt::Key accentBaseKey, Qt::Key deadKey)
{
switch (deadKey) {
case Qt::Key_QuoteLeft: {
// ` macOS: Key_Dead_Grave
return platform() == Platform::MacOS ? find(graveKeyTable, accentBaseKey)
: find(diaeresisKeyTable, accentBaseKey);
}
case Qt::Key_O: // ´ Key_Dead_Grave
return find(graveKeyTable, accentBaseKey);
case Qt::Key_E: // ´ Key_Dead_Acute
return find(acuteKeyTable, accentBaseKey);
case Qt::Key_AsciiTilde:
case Qt::Key_N: // Key_Dead_Tilde
return find(tildeKeyTable, accentBaseKey);
case Qt::Key_U: // ¨ Key_Dead_Diaeresis
return find(diaeresisKeyTable, accentBaseKey);
case Qt::Key_I: // macOS Key_Dead_Circumflex
case Qt::Key_6: // linux
case Qt::Key_Apostrophe: // linux
return find(circumflexKeyTable, accentBaseKey);
default:
return Qt::Key_unknown;
};
}
template<class T>
std::optional<QString> findKeyTextByKeyId(const T &mappingArray, Qt::Key qtKey)
{
const auto it = std::find_if(mappingArray.cbegin(), mappingArray.cend(),
[qtKey](const Emkb2QtData &data) { return data.qt == qtKey; });
return it != mappingArray.cend() ? it->em : std::optional<QString>();
}
} // namespace
QWasmEventTranslator::QWasmEventTranslator() = default;
QWasmEventTranslator::~QWasmEventTranslator() = default;
QWasmEventTranslator::TranslatedEvent
QWasmEventTranslator::translateKeyEvent(int emEventType, const EmscriptenKeyboardEvent *keyEvent)
{
TranslatedEvent ret;
switch (emEventType) {
case EMSCRIPTEN_EVENT_KEYDOWN:
ret.type = QEvent::KeyPress;
break;
case EMSCRIPTEN_EVENT_KEYUP:
ret.type = QEvent::KeyRelease;
break;
default:
// Should not be reached - do not call with this event type.
Q_ASSERT(false);
break;
};
ret.key = translateEmscriptKey(keyEvent);
if (isDeadKeyEvent(keyEvent) || ret.key == Qt::Key_AltGr) {
if (keyEvent->shiftKey && ret.key == Qt::Key_QuoteLeft)
ret.key = Qt::Key_AsciiTilde;
m_emDeadKey = ret.key;
} else if (m_emDeadKey != Qt::Key_unknown
&& (m_keyModifiedByDeadKeyOnPress == Qt::Key_unknown
|| ret.key == m_keyModifiedByDeadKeyOnPress)) {
const Qt::Key baseKey = ret.key;
const Qt::Key translatedKey = translateBaseKeyUsingDeadKey(baseKey, m_emDeadKey);
if (translatedKey != Qt::Key_unknown)
ret.key = translatedKey;
if (auto text = keyEvent->shiftKey
? findKeyTextByKeyId(WebToQtKeyCodeMappingsWithShift, ret.key)
: findKeyTextByKeyId(WebToQtKeyCodeMappings, ret.key)) {
if (ret.type == QEvent::KeyPress) {
Q_ASSERT(m_keyModifiedByDeadKeyOnPress == Qt::Key_unknown);
m_keyModifiedByDeadKeyOnPress = baseKey;
} else {
Q_ASSERT(ret.type == QEvent::KeyRelease);
Q_ASSERT(m_keyModifiedByDeadKeyOnPress == baseKey);
m_keyModifiedByDeadKeyOnPress = Qt::Key_unknown;
m_emDeadKey = Qt::Key_unknown;
}
ret.text = *text;
return ret;
}
}
ret.text = QString::fromUtf8(keyEvent->key);
return ret;
}
QT_END_NAMESPACE

View File

@ -1,46 +0,0 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QWASMEVENTTRANSLATOR_H
#define QWASMEVENTTRANSLATOR_H
#include <QtCore/qobject.h>
#include <QtCore/qrect.h>
#include <QtCore/qpoint.h>
#include <emscripten/html5.h>
#include "qwasmwindow.h"
#include <QtGui/qinputdevice.h>
#include <QHash>
#include <QCursor>
#include "qwasmevent.h"
#include "qwasmplatform.h"
QT_BEGIN_NAMESPACE
class QWindow;
class QWasmEventTranslator : public QObject
{
Q_OBJECT
public:
struct TranslatedEvent
{
QEvent::Type type;
Qt::Key key;
QString text;
};
explicit QWasmEventTranslator();
~QWasmEventTranslator();
TranslatedEvent translateKeyEvent(int emEventType, const EmscriptenKeyboardEvent *keyEvent);
private:
static quint64 getTimestamp();
Qt::Key m_emDeadKey = Qt::Key_unknown;
Qt::Key m_keyModifiedByDeadKeyOnPress = Qt::Key_unknown;
};
QT_END_NAMESPACE
#endif // QWASMEVENTTRANSLATOR_H

View File

@ -5,9 +5,9 @@
#include "qwasminputcontext.h" #include "qwasminputcontext.h"
#include "qwasmintegration.h" #include "qwasmintegration.h"
#include "qwasmplatform.h"
#include <QRectF> #include <QRectF>
#include <qpa/qplatforminputcontext.h> #include <qpa/qplatforminputcontext.h>
#include "qwasmeventtranslator.h"
#include "qwasmscreen.h" #include "qwasmscreen.h"
#include <qguiapplication.h> #include <qguiapplication.h>
#include <qwindow.h> #include <qwindow.h>

View File

@ -2,7 +2,6 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qwasmintegration.h" #include "qwasmintegration.h"
#include "qwasmeventtranslator.h"
#include "qwasmeventdispatcher.h" #include "qwasmeventdispatcher.h"
#include "qwasmcompositor.h" #include "qwasmcompositor.h"
#include "qwasmopenglcontext.h" #include "qwasmopenglcontext.h"
@ -164,8 +163,10 @@ bool QWasmIntegration::hasCapability(QPlatformIntegration::Capability cap) const
QPlatformWindow *QWasmIntegration::createPlatformWindow(QWindow *window) const QPlatformWindow *QWasmIntegration::createPlatformWindow(QWindow *window) const
{ {
QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor(); auto *wasmScreen = QWasmScreen::get(window->screen());
return new QWasmWindow(window, compositor, m_backingStores.value(window)); QWasmCompositor *compositor = wasmScreen->compositor();
return new QWasmWindow(window, wasmScreen->deadKeySupport(), compositor,
m_backingStores.value(window));
} }
QPlatformBackingStore *QWasmIntegration::createPlatformBackingStore(QWindow *window) const QPlatformBackingStore *QWasmIntegration::createPlatformBackingStore(QWindow *window) const

View File

@ -0,0 +1,249 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qwasmkeytranslator.h"
#include "qwasmevent.h"
#include <QtCore/private/qmakearray_p.h>
#include <QtCore/qglobal.h>
#include <QtCore/qobject.h>
#include <algorithm>
QT_BEGIN_NAMESPACE
namespace {
struct WebKb2QtData
{
static constexpr char StringTerminator = '\0';
const char *web;
unsigned int qt;
constexpr bool operator<=(const WebKb2QtData &that) const noexcept
{
return !(strcmp(that) > 0);
}
bool operator<(const WebKb2QtData &that) const noexcept { return ::strcmp(web, that.web) < 0; }
constexpr bool operator==(const WebKb2QtData &that) const noexcept { return strcmp(that) == 0; }
constexpr int strcmp(const WebKb2QtData &that, const int i = 0) const
{
return web[i] == StringTerminator && that.web[i] == StringTerminator ? 0
: web[i] == StringTerminator ? -1
: that.web[i] == StringTerminator ? 1
: web[i] < that.web[i] ? -1
: web[i] > that.web[i] ? 1
: strcmp(that, i + 1);
}
};
template<unsigned int Qt, char... WebChar>
struct Web2Qt
{
static constexpr const char storage[sizeof...(WebChar) + 1] = { WebChar..., '\0' };
using Type = WebKb2QtData;
static constexpr Type data() noexcept { return Type{ storage, Qt }; }
};
template<unsigned int Qt, char... WebChar>
constexpr char Web2Qt<Qt, WebChar...>::storage[];
static constexpr const auto WebToQtKeyCodeMappings = qMakeArray(
QSortedData<Web2Qt<Qt::Key_Escape, 'E', 's', 'c', 'a', 'p', 'e'>,
Web2Qt<Qt::Key_Tab, 'T', 'a', 'b'>,
Web2Qt<Qt::Key_Backspace, 'B', 'a', 'c', 'k', 's', 'p', 'a', 'c', 'e'>,
Web2Qt<Qt::Key_Return, 'E', 'n', 't', 'e', 'r'>,
Web2Qt<Qt::Key_Insert, 'I', 'n', 's', 'e', 'r', 't'>,
Web2Qt<Qt::Key_Delete, 'D', 'e', 'l', 'e', 't', 'e'>,
Web2Qt<Qt::Key_Pause, 'P', 'a', 'u', 's', 'e'>,
Web2Qt<Qt::Key_Pause, 'C', 'l', 'e', 'a', 'r'>,
Web2Qt<Qt::Key_Home, 'H', 'o', 'm', 'e'>, Web2Qt<Qt::Key_End, 'E', 'n', 'd'>,
Web2Qt<Qt::Key_Left, 'A', 'r', 'r', 'o', 'w', 'L', 'e', 'f', 't'>,
Web2Qt<Qt::Key_Up, 'A', 'r', 'r', 'o', 'w', 'U', 'p'>,
Web2Qt<Qt::Key_Right, 'A', 'r', 'r', 'o', 'w', 'R', 'i', 'g', 'h', 't'>,
Web2Qt<Qt::Key_Down, 'A', 'r', 'r', 'o', 'w', 'D', 'o', 'w', 'n'>,
Web2Qt<Qt::Key_PageUp, 'P', 'a', 'g', 'e', 'U', 'p'>,
Web2Qt<Qt::Key_PageDown, 'P', 'a', 'g', 'e', 'D', 'o', 'w', 'n'>,
Web2Qt<Qt::Key_Shift, 'S', 'h', 'i', 'f', 't'>,
Web2Qt<Qt::Key_Control, 'C', 'o', 'n', 't', 'r', 'o', 'l'>,
Web2Qt<Qt::Key_Meta, 'M', 'e', 't', 'a'>, Web2Qt<Qt::Key_Meta, 'O', 'S'>,
Web2Qt<Qt::Key_Alt, 'A', 'l', 't', 'L', 'e', 'f', 't'>,
Web2Qt<Qt::Key_Alt, 'A', 'l', 't'>,
Web2Qt<Qt::Key_CapsLock, 'C', 'a', 'p', 's', 'L', 'o', 'c', 'k'>,
Web2Qt<Qt::Key_NumLock, 'N', 'u', 'm', 'L', 'o', 'c', 'k'>,
Web2Qt<Qt::Key_ScrollLock, 'S', 'c', 'r', 'o', 'l', 'l', 'L', 'o', 'c', 'k'>,
Web2Qt<Qt::Key_F1, 'F', '1'>, Web2Qt<Qt::Key_F2, 'F', '2'>,
Web2Qt<Qt::Key_F3, 'F', '3'>, Web2Qt<Qt::Key_F4, 'F', '4'>,
Web2Qt<Qt::Key_F5, 'F', '5'>, Web2Qt<Qt::Key_F6, 'F', '6'>,
Web2Qt<Qt::Key_F7, 'F', '7'>, Web2Qt<Qt::Key_F8, 'F', '8'>,
Web2Qt<Qt::Key_F9, 'F', '9'>, Web2Qt<Qt::Key_F10, 'F', '1', '0'>,
Web2Qt<Qt::Key_F11, 'F', '1', '1'>, Web2Qt<Qt::Key_F12, 'F', '1', '2'>,
Web2Qt<Qt::Key_F13, 'F', '1', '3'>, Web2Qt<Qt::Key_F14, 'F', '1', '4'>,
Web2Qt<Qt::Key_F15, 'F', '1', '5'>, Web2Qt<Qt::Key_F16, 'F', '1', '6'>,
Web2Qt<Qt::Key_F17, 'F', '1', '7'>, Web2Qt<Qt::Key_F18, 'F', '1', '8'>,
Web2Qt<Qt::Key_F19, 'F', '1', '9'>, Web2Qt<Qt::Key_F20, 'F', '2', '0'>,
Web2Qt<Qt::Key_F21, 'F', '2', '1'>, Web2Qt<Qt::Key_F22, 'F', '2', '2'>,
Web2Qt<Qt::Key_F23, 'F', '2', '3'>,
Web2Qt<Qt::Key_Paste, 'P', 'a', 's', 't', 'e'>,
Web2Qt<Qt::Key_AltGr, 'A', 'l', 't', 'R', 'i', 'g', 'h', 't'>,
Web2Qt<Qt::Key_Help, 'H', 'e', 'l', 'p'>,
Web2Qt<Qt::Key_yen, 'I', 'n', 't', 'l', 'Y', 'e', 'n'>,
Web2Qt<Qt::Key_Menu, 'C', 'o', 'n', 't', 'e', 'x', 't', 'M', 'e', 'n',
'u'>>::Data{});
static constexpr const auto WebToQtKeyCodeMappingsWithShift = qMakeArray(
QSortedData<
// shifted
Web2Qt<Qt::Key_Agrave, '\xc3', '\x80'>, Web2Qt<Qt::Key_Aacute, '\xc3', '\x81'>,
Web2Qt<Qt::Key_Acircumflex, '\xc3', '\x82'>,
Web2Qt<Qt::Key_Adiaeresis, '\xc3', '\x84'>, Web2Qt<Qt::Key_AE, '\xc3', '\x86'>,
Web2Qt<Qt::Key_Atilde, '\xc3', '\x83'>, Web2Qt<Qt::Key_Aring, '\xc3', '\x85'>,
Web2Qt<Qt::Key_Egrave, '\xc3', '\x88'>, Web2Qt<Qt::Key_Eacute, '\xc3', '\x89'>,
Web2Qt<Qt::Key_Ecircumflex, '\xc3', '\x8a'>,
Web2Qt<Qt::Key_Ediaeresis, '\xc3', '\x8b'>, Web2Qt<Qt::Key_Iacute, '\xc3', '\x8d'>,
Web2Qt<Qt::Key_Icircumflex, '\xc3', '\x8e'>,
Web2Qt<Qt::Key_Idiaeresis, '\xc3', '\x8f'>, Web2Qt<Qt::Key_Igrave, '\xc3', '\x8c'>,
Web2Qt<Qt::Key_Ocircumflex, '\xc3', '\x94'>,
Web2Qt<Qt::Key_Odiaeresis, '\xc3', '\x96'>, Web2Qt<Qt::Key_Ograve, '\xc3', '\x92'>,
Web2Qt<Qt::Key_Oacute, '\xc3', '\x93'>, Web2Qt<Qt::Key_Ooblique, '\xc3', '\x98'>,
Web2Qt<Qt::Key_Otilde, '\xc3', '\x95'>, Web2Qt<Qt::Key_Ucircumflex, '\xc3', '\x9b'>,
Web2Qt<Qt::Key_Udiaeresis, '\xc3', '\x9c'>, Web2Qt<Qt::Key_Ugrave, '\xc3', '\x99'>,
Web2Qt<Qt::Key_Uacute, '\xc3', '\x9a'>, Web2Qt<Qt::Key_Ntilde, '\xc3', '\x91'>,
Web2Qt<Qt::Key_Ccedilla, '\xc3', '\x87'>,
Web2Qt<Qt::Key_ydiaeresis, '\xc3', '\x8f'>,
Web2Qt<Qt::Key_Yacute, '\xc3', '\x9d'>>::Data{});
struct KeyMapping
{
Qt::Key from, to;
};
constexpr KeyMapping tildeKeyTable[] = {
// ~
{ Qt::Key_A, Qt::Key_Atilde },
{ Qt::Key_N, Qt::Key_Ntilde },
{ Qt::Key_O, Qt::Key_Otilde },
};
constexpr KeyMapping graveKeyTable[] = {
// `
{ Qt::Key_A, Qt::Key_Agrave }, { Qt::Key_E, Qt::Key_Egrave }, { Qt::Key_I, Qt::Key_Igrave },
{ Qt::Key_O, Qt::Key_Ograve }, { Qt::Key_U, Qt::Key_Ugrave },
};
constexpr KeyMapping acuteKeyTable[] = {
// '
{ Qt::Key_A, Qt::Key_Aacute }, { Qt::Key_E, Qt::Key_Eacute }, { Qt::Key_I, Qt::Key_Iacute },
{ Qt::Key_O, Qt::Key_Oacute }, { Qt::Key_U, Qt::Key_Uacute }, { Qt::Key_Y, Qt::Key_Yacute },
};
constexpr KeyMapping diaeresisKeyTable[] = {
// umlaut ¨
{ Qt::Key_A, Qt::Key_Adiaeresis }, { Qt::Key_E, Qt::Key_Ediaeresis },
{ Qt::Key_I, Qt::Key_Idiaeresis }, { Qt::Key_O, Qt::Key_Odiaeresis },
{ Qt::Key_U, Qt::Key_Udiaeresis }, { Qt::Key_Y, Qt::Key_ydiaeresis },
};
constexpr KeyMapping circumflexKeyTable[] = {
// ^
{ Qt::Key_A, Qt::Key_Acircumflex }, { Qt::Key_E, Qt::Key_Ecircumflex },
{ Qt::Key_I, Qt::Key_Icircumflex }, { Qt::Key_O, Qt::Key_Ocircumflex },
{ Qt::Key_U, Qt::Key_Ucircumflex },
};
static Qt::Key find_impl(const KeyMapping *first, const KeyMapping *last, Qt::Key key) noexcept
{
while (first != last) {
if (first->from == key)
return first->to;
++first;
}
return Qt::Key_unknown;
}
template<size_t N>
static Qt::Key find(const KeyMapping (&map)[N], Qt::Key key) noexcept
{
return find_impl(map, map + N, key);
}
Qt::Key translateBaseKeyUsingDeadKey(Qt::Key accentBaseKey, Qt::Key deadKey)
{
switch (deadKey) {
case Qt::Key_QuoteLeft: {
// ` macOS: Key_Dead_Grave
return platform() == Platform::MacOS ? find(graveKeyTable, accentBaseKey)
: find(diaeresisKeyTable, accentBaseKey);
}
case Qt::Key_O: // ´ Key_Dead_Grave
return find(graveKeyTable, accentBaseKey);
case Qt::Key_E: // ´ Key_Dead_Acute
return find(acuteKeyTable, accentBaseKey);
case Qt::Key_AsciiTilde:
case Qt::Key_N: // Key_Dead_Tilde
return find(tildeKeyTable, accentBaseKey);
case Qt::Key_U: // ¨ Key_Dead_Diaeresis
return find(diaeresisKeyTable, accentBaseKey);
case Qt::Key_I: // macOS Key_Dead_Circumflex
case Qt::Key_6: // linux
case Qt::Key_Apostrophe: // linux
return find(circumflexKeyTable, accentBaseKey);
default:
return Qt::Key_unknown;
};
}
template<class T>
std::optional<QString> findKeyTextByKeyId(const T &mappingArray, Qt::Key qtKey)
{
const auto it = std::find_if(mappingArray.cbegin(), mappingArray.cend(),
[qtKey](const WebKb2QtData &data) { return data.qt == qtKey; });
return it != mappingArray.cend() ? it->web : std::optional<QString>();
}
} // namespace
std::optional<Qt::Key> QWasmKeyTranslator::mapWebKeyTextToQtKey(const char *toFind)
{
const WebKb2QtData searchKey{ toFind, 0 };
const auto it = std::lower_bound(WebToQtKeyCodeMappings.cbegin(), WebToQtKeyCodeMappings.cend(),
searchKey);
return it != WebToQtKeyCodeMappings.cend() && searchKey == *it ? static_cast<Qt::Key>(it->qt)
: std::optional<Qt::Key>();
}
QWasmDeadKeySupport::QWasmDeadKeySupport() = default;
QWasmDeadKeySupport::~QWasmDeadKeySupport() = default;
void QWasmDeadKeySupport::applyDeadKeyTranslations(KeyEvent *event)
{
if (event->deadKey || event->key == Qt::Key_AltGr) {
if (event->modifiers.testFlag(Qt::ShiftModifier) && event->key == Qt::Key_QuoteLeft)
event->key = Qt::Key_AsciiTilde;
m_activeDeadKey = event->key;
} else if (m_activeDeadKey != Qt::Key_unknown
&& (m_keyModifiedByDeadKeyOnPress == Qt::Key_unknown
|| m_keyModifiedByDeadKeyOnPress == event->key)) {
const Qt::Key baseKey = event->key;
const Qt::Key translatedKey = translateBaseKeyUsingDeadKey(baseKey, m_activeDeadKey);
if (translatedKey != Qt::Key_unknown)
event->key = translatedKey;
if (auto foundText = event->modifiers.testFlag(Qt::ShiftModifier)
? findKeyTextByKeyId(WebToQtKeyCodeMappingsWithShift, event->key)
: findKeyTextByKeyId(WebToQtKeyCodeMappings, event->key)) {
if (event->type == EventType::KeyDown) {
Q_ASSERT(m_keyModifiedByDeadKeyOnPress == Qt::Key_unknown);
m_activeDeadKey = Qt::Key_unknown;
} else {
Q_ASSERT(event->type == EventType::KeyUp);
Q_ASSERT(m_keyModifiedByDeadKeyOnPress == baseKey);
m_activeDeadKey = Qt::Key_unknown;
m_keyModifiedByDeadKeyOnPress = Qt::Key_unknown;
}
event->text = foundText->size() == 1 ? *foundText : QString();
return;
}
}
}
QT_END_NAMESPACE

View File

@ -0,0 +1,34 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QWASMKEYTRANSLATOR_H
#define QWASMKEYTRANSLATOR_H
#include <QtCore/qnamespace.h>
#include <QtCore/qtypes.h>
#include <optional>
QT_BEGIN_NAMESPACE
struct KeyEvent;
namespace QWasmKeyTranslator {
std::optional<Qt::Key> mapWebKeyTextToQtKey(const char *toFind);
}
class QWasmDeadKeySupport
{
public:
explicit QWasmDeadKeySupport();
~QWasmDeadKeySupport();
void applyDeadKeyTranslations(KeyEvent *event);
private:
Qt::Key m_activeDeadKey = Qt::Key_unknown;
Qt::Key m_keyModifiedByDeadKeyOnPress = Qt::Key_unknown;
};
QT_END_NAMESPACE
#endif // QWASMKEYTRANSLATOR_H

View File

@ -2,11 +2,12 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qwasmscreen.h" #include "qwasmscreen.h"
#include "qwasmwindow.h"
#include "qwasmeventtranslator.h"
#include "qwasmcompositor.h" #include "qwasmcompositor.h"
#include "qwasmintegration.h"
#include "qwasmcssstyle.h" #include "qwasmcssstyle.h"
#include "qwasmintegration.h"
#include "qwasmkeytranslator.h"
#include "qwasmwindow.h"
#include <emscripten/bind.h> #include <emscripten/bind.h>
#include <emscripten/val.h> #include <emscripten/val.h>
@ -29,7 +30,7 @@ QWasmScreen::QWasmScreen(const emscripten::val &containerOrCanvas)
: m_container(containerOrCanvas), : m_container(containerOrCanvas),
m_shadowContainer(emscripten::val::undefined()), m_shadowContainer(emscripten::val::undefined()),
m_compositor(new QWasmCompositor(this)), m_compositor(new QWasmCompositor(this)),
m_eventTranslator(new QWasmEventTranslator()) m_deadKeySupport(std::make_unique<QWasmDeadKeySupport>())
{ {
auto document = m_container["ownerDocument"]; auto document = m_container["ownerDocument"];
// Each screen is represented by a div container. All of the windows exist therein as // Each screen is represented by a div container. All of the windows exist therein as
@ -111,11 +112,6 @@ QWasmCompositor *QWasmScreen::compositor()
return m_compositor.get(); return m_compositor.get();
} }
QWasmEventTranslator *QWasmScreen::eventTranslator()
{
return m_eventTranslator.get();
}
emscripten::val QWasmScreen::element() const emscripten::val QWasmScreen::element() const
{ {
return m_shadowContainer; return m_shadowContainer;

View File

@ -20,7 +20,7 @@ class QPlatformOpenGLContext;
class QWasmWindow; class QWasmWindow;
class QWasmBackingStore; class QWasmBackingStore;
class QWasmCompositor; class QWasmCompositor;
class QWasmEventTranslator; class QWasmDeadKeySupport;
class QOpenGLContext; class QOpenGLContext;
class QWasmScreen : public QObject, public QPlatformScreen class QWasmScreen : public QObject, public QPlatformScreen
@ -38,7 +38,7 @@ public:
QString outerScreenId() const; QString outerScreenId() const;
QWasmCompositor *compositor(); QWasmCompositor *compositor();
QWasmEventTranslator *eventTranslator(); QWasmDeadKeySupport *deadKeySupport() { return m_deadKeySupport.get(); }
QRect geometry() const override; QRect geometry() const override;
int depth() const override; int depth() const override;
@ -67,7 +67,7 @@ private:
emscripten::val m_container; emscripten::val m_container;
emscripten::val m_shadowContainer; emscripten::val m_shadowContainer;
std::unique_ptr<QWasmCompositor> m_compositor; std::unique_ptr<QWasmCompositor> m_compositor;
std::unique_ptr<QWasmEventTranslator> m_eventTranslator; std::unique_ptr<QWasmDeadKeySupport> m_deadKeySupport;
QRect m_geometry = QRect(0, 0, 100, 100); QRect m_geometry = QRect(0, 0, 100, 100);
int m_depth = 32; int m_depth = 32;
QImage::Format m_format = QImage::Format_RGB32; QImage::Format m_format = QImage::Format_RGB32;

View File

@ -11,6 +11,9 @@
#include "qwasmbase64iconstore.h" #include "qwasmbase64iconstore.h"
#include "qwasmdom.h" #include "qwasmdom.h"
#include "qwasmclipboard.h"
#include "qwasmintegration.h"
#include "qwasmkeytranslator.h"
#include "qwasmwindow.h" #include "qwasmwindow.h"
#include "qwasmwindowclientarea.h" #include "qwasmwindowclientarea.h"
#include "qwasmscreen.h" #include "qwasmscreen.h"
@ -31,11 +34,13 @@ QT_BEGIN_NAMESPACE
Q_GUI_EXPORT int qt_defaultDpiX(); Q_GUI_EXPORT int qt_defaultDpiX();
QWasmWindow::QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingStore *backingStore) QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport,
QWasmCompositor *compositor, QWasmBackingStore *backingStore)
: QPlatformWindow(w), : QPlatformWindow(w),
m_window(w), m_window(w),
m_compositor(compositor), m_compositor(compositor),
m_backingStore(backingStore), m_backingStore(backingStore),
m_deadKeySupport(deadKeySupport),
m_document(dom::document()), m_document(dom::document()),
m_qtWindow(m_document.call<emscripten::val>("createElement", emscripten::val("div"))), m_qtWindow(m_document.call<emscripten::val>("createElement", emscripten::val("div"))),
m_windowContents(m_document.call<emscripten::val>("createElement", emscripten::val("div"))), m_windowContents(m_document.call<emscripten::val>("createElement", emscripten::val("div"))),
@ -89,15 +94,15 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingSt
m_compositor->addWindow(this); m_compositor->addWindow(this);
const auto callback = std::function([this](emscripten::val event) { const auto pointerCallback = std::function([this](emscripten::val event) {
if (processPointer(*PointerEvent::fromWeb(event))) if (processPointer(*PointerEvent::fromWeb(event)))
event.call<void>("preventDefault"); event.call<void>("preventDefault");
}); });
m_pointerEnterCallback = m_pointerEnterCallback =
std::make_unique<qstdweb::EventCallback>(m_qtWindow, "pointerenter", callback); std::make_unique<qstdweb::EventCallback>(m_qtWindow, "pointerenter", pointerCallback);
m_pointerLeaveCallback = m_pointerLeaveCallback =
std::make_unique<qstdweb::EventCallback>(m_qtWindow, "pointerleave", callback); std::make_unique<qstdweb::EventCallback>(m_qtWindow, "pointerleave", pointerCallback);
m_dropCallback = std::make_unique<qstdweb::EventCallback>( m_dropCallback = std::make_unique<qstdweb::EventCallback>(
m_qtWindow, "drop", [this](emscripten::val event) { m_qtWindow, "drop", [this](emscripten::val event) {
@ -110,6 +115,15 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingSt
if (processWheel(*WheelEvent::fromWeb(event))) if (processWheel(*WheelEvent::fromWeb(event)))
event.call<void>("preventDefault"); event.call<void>("preventDefault");
}); });
const auto keyCallback = std::function([this](emscripten::val event) {
if (processKey(*KeyEvent::fromWebWithDeadKeyTranslation(event, m_deadKeySupport)))
event.call<void>("preventDefault");
});
m_keyDownCallback =
std::make_unique<qstdweb::EventCallback>(m_qtWindow, "keydown", keyCallback);
m_keyUpCallback = std::make_unique<qstdweb::EventCallback>(m_qtWindow, "keyup", keyCallback);
} }
QWasmWindow::~QWasmWindow() QWasmWindow::~QWasmWindow()
@ -433,6 +447,26 @@ void QWasmWindow::applyWindowState()
setGeometry(newGeom); setGeometry(newGeom);
} }
bool QWasmWindow::processKey(const KeyEvent &event)
{
constexpr bool ProceedToNativeEvent = false;
Q_ASSERT(event.type == EventType::KeyDown || event.type == EventType::KeyUp);
const auto clipboardResult =
QWasmIntegration::get()->getWasmClipboard()->processKeyboard(event);
using ProcessKeyboardResult = QWasmClipboard::ProcessKeyboardResult;
if (clipboardResult == ProcessKeyboardResult::NativeClipboardEventNeeded)
return ProceedToNativeEvent;
const auto result = QWindowSystemInterface::handleKeyEvent(
0, event.type == EventType::KeyDown ? QEvent::KeyPress : QEvent::KeyRelease, event.key,
event.modifiers, event.text);
return clipboardResult == ProcessKeyboardResult::NativeClipboardEventAndCopiedDataNeeded
? ProceedToNativeEvent
: result;
}
bool QWasmWindow::processPointer(const PointerEvent &event) bool QWasmWindow::processPointer(const PointerEvent &event)
{ {
if (event.pointerType != PointerType::Mouse) if (event.pointerType != PointerType::Mouse)
@ -548,6 +582,9 @@ void QWasmWindow::requestActivateWindow()
if (window()->isTopLevel()) if (window()->isTopLevel())
raise(); raise();
m_canvas.call<void>("focus");
QPlatformWindow::requestActivateWindow(); QPlatformWindow::requestActivateWindow();
} }

View File

@ -32,13 +32,16 @@ class EventCallback;
class ClientArea; class ClientArea;
struct DragEvent; struct DragEvent;
struct KeyEvent;
struct PointerEvent; struct PointerEvent;
class QWasmDeadKeySupport;
struct WheelEvent; struct WheelEvent;
class QWasmWindow final : public QPlatformWindow class QWasmWindow final : public QPlatformWindow
{ {
public: public:
QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingStore *backingStore); QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport, QWasmCompositor *compositor,
QWasmBackingStore *backingStore);
~QWasmWindow() final; ~QWasmWindow() final;
void destroy(); void destroy();
@ -95,6 +98,7 @@ private:
bool hasMaximizeButton() const; bool hasMaximizeButton() const;
void applyWindowState(); void applyWindowState();
bool processKey(const KeyEvent &event);
bool processPointer(const PointerEvent &event); bool processPointer(const PointerEvent &event);
bool processDrop(const DragEvent &event); bool processDrop(const DragEvent &event);
bool processWheel(const WheelEvent &event); bool processWheel(const WheelEvent &event);
@ -102,6 +106,7 @@ private:
QWindow *m_window = nullptr; QWindow *m_window = nullptr;
QWasmCompositor *m_compositor = nullptr; QWasmCompositor *m_compositor = nullptr;
QWasmBackingStore *m_backingStore = nullptr; QWasmBackingStore *m_backingStore = nullptr;
QWasmDeadKeySupport *m_deadKeySupport;
QRect m_normalGeometry {0, 0, 0 ,0}; QRect m_normalGeometry {0, 0, 0 ,0};
emscripten::val m_document; emscripten::val m_document;
@ -115,6 +120,9 @@ private:
std::unique_ptr<NonClientArea> m_nonClientArea; std::unique_ptr<NonClientArea> m_nonClientArea;
std::unique_ptr<ClientArea> m_clientArea; std::unique_ptr<ClientArea> m_clientArea;
std::unique_ptr<qstdweb::EventCallback> m_keyDownCallback;
std::unique_ptr<qstdweb::EventCallback> m_keyUpCallback;
std::unique_ptr<qstdweb::EventCallback> m_pointerLeaveCallback; std::unique_ptr<qstdweb::EventCallback> m_pointerLeaveCallback;
std::unique_ptr<qstdweb::EventCallback> m_pointerEnterCallback; std::unique_ptr<qstdweb::EventCallback> m_pointerEnterCallback;