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
qwasmevent.cpp qwasmevent.h
qwasmeventdispatcher.cpp qwasmeventdispatcher.h
qwasmeventtranslator.cpp qwasmeventtranslator.h
qwasmfontdatabase.cpp qwasmfontdatabase.h
qwasmintegration.cpp qwasmintegration.h
qwasmkeytranslator.cpp qwasmkeytranslator.h
qwasmoffscreensurface.cpp qwasmoffscreensurface.h
qwasmopenglcontext.cpp qwasmopenglcontext.h
qwasmplatform.cpp qwasmplatform.h

View File

@ -3,6 +3,7 @@
#include "qwasmclipboard.h"
#include "qwasmdom.h"
#include "qwasmevent.h"
#include "qwasmwindow.h"
#include <private/qstdweb_p.h>
@ -129,11 +130,9 @@ void QWasmClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode)
writeToClipboard();
}
QWasmClipboard::ProcessKeyboardResult
QWasmClipboard::processKeyboard(const QWasmEventTranslator::TranslatedEvent &event,
const QFlags<Qt::KeyboardModifier> &modifiers)
QWasmClipboard::ProcessKeyboardResult QWasmClipboard::processKeyboard(const KeyEvent &event)
{
if (event.type != QEvent::KeyPress || !modifiers.testFlag(Qt::ControlModifier))
if (event.type != EventType::KeyDown || !event.modifiers.testFlag(Qt::ControlModifier))
return ProcessKeyboardResult::Ignored;
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/val.h>
#include "qwasmeventtranslator.h"
QT_BEGIN_NAMESPACE
struct KeyEvent;
class QWasmClipboard : public QObject, public QPlatformClipboard
{
public:
@ -34,8 +34,7 @@ public:
bool supportsMode(QClipboard::Mode mode) const override;
bool ownsMode(QClipboard::Mode mode) const override;
ProcessKeyboardResult processKeyboard(const QWasmEventTranslator::TranslatedEvent &event,
const QFlags<Qt::KeyboardModifier> &modifiers);
ProcessKeyboardResult processKeyboard(const KeyEvent &event);
static void installEventHandlers(const emscripten::val &target);
bool hasClipboardApi();

View File

@ -3,7 +3,6 @@
#include "qwasmcompositor.h"
#include "qwasmwindow.h"
#include "qwasmeventtranslator.h"
#include "qwasmeventdispatcher.h"
#include "qwasmclipboard.h"
#include "qwasmevent.h"
@ -30,9 +29,7 @@ using namespace emscripten;
Q_GUI_EXPORT int qt_defaultDpiX();
QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
: QObject(screen),
m_windowStack(std::bind(&QWasmCompositor::onTopWindowChanged, this)),
m_eventTranslator(std::make_unique<QWasmEventTranslator>())
: QObject(screen), m_windowStack(std::bind(&QWasmCompositor::onTopWindowChanged, this))
{
m_touchDevice = std::make_unique<QPointingDevice>(
"touchscreen", 1, QInputDevice::DeviceType::TouchScreen,
@ -61,12 +58,11 @@ void QWasmCompositor::onScreenDeleting()
void QWasmCompositor::deregisterEventHandlers()
{
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_touchend_callback(screenElementSelector.constData(), 0, 0, NULL);
emscripten_set_touchmove_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;
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,
&touchCallback);
emscripten_set_touchend_callback(screenElementSelector.constData(), (void *)this, UseCapture,
@ -283,46 +274,12 @@ QWasmScreen *QWasmCompositor::screen()
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)
{
auto compositor = reinterpret_cast<QWasmCompositor*>(userData);
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)
{
QList<QWindowSystemInterface::TouchPoint> touchPointList;

View File

@ -26,7 +26,6 @@ class QWasmWindow;
class QWasmScreen;
class QOpenGLContext;
class QOpenGLTexture;
class QWasmEventTranslator;
class QWasmCompositor final : public QObject
{
@ -69,11 +68,8 @@ private:
void deliverUpdateRequests();
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);
bool processKeyboard(int eventType, const EmscriptenKeyboardEvent *keyEvent);
bool processTouch(int eventType, const EmscriptenTouchEvent *touchEvent);
void updateEnabledState();
@ -88,9 +84,7 @@ private:
std::unique_ptr<QPointingDevice> m_touchDevice;
QMap <int, QPointF> m_pressedTouchIds;
std::unique_ptr<QWasmEventTranslator> m_eventTranslator;
QMap<int, QPointF> m_pressedTouchIds;
};
QT_END_NAMESPACE

View File

@ -3,8 +3,39 @@
#include "qwasmevent.h"
#include "qwasmkeytranslator.h"
#include <QtCore/private/qmakearray_p.h>
#include <QtCore/private/qstringiterator_p.h>
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
{
template <>
@ -28,6 +59,51 @@ Event &Event::operator=(const 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"])
{
mouseButton = MouseEvent::buttonFromWeb(event["button"].as<int>());

View File

@ -17,8 +17,12 @@
QT_BEGIN_NAMESPACE
class QWasmDeadKeySupport;
enum class EventType {
Drop,
KeyDown,
KeyUp,
PointerDown,
PointerMove,
PointerUp,
@ -113,26 +117,37 @@ QFlags<Qt::KeyboardModifier> getForEvent<EmscriptenKeyboardEvent>(
struct Event
{
EventType type;
emscripten::val target = emscripten::val::undefined();
Event(EventType type, emscripten::val target);
~Event();
Event(const Event &other);
Event(Event &&other);
Event &operator=(const 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
{
QPoint localPoint;
QPoint pointInPage;
QPoint pointInViewport;
Qt::MouseButton mouseButton;
Qt::MouseButtons mouseButtons;
QFlags<Qt::KeyboardModifier> modifiers;
MouseEvent(EventType type, emscripten::val webEvent);
~MouseEvent();
MouseEvent(const MouseEvent &other);
@ -174,6 +189,13 @@ struct MouseEvent : public Event
return QEvent::None;
}
}
QPoint localPoint;
QPoint pointInPage;
QPoint pointInViewport;
Qt::MouseButton mouseButton;
Qt::MouseButtons mouseButtons;
QFlags<Qt::KeyboardModifier> modifiers;
};
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 "qwasmintegration.h"
#include "qwasmplatform.h"
#include <QRectF>
#include <qpa/qplatforminputcontext.h>
#include "qwasmeventtranslator.h"
#include "qwasmscreen.h"
#include <qguiapplication.h>
#include <qwindow.h>

View File

@ -2,7 +2,6 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qwasmintegration.h"
#include "qwasmeventtranslator.h"
#include "qwasmeventdispatcher.h"
#include "qwasmcompositor.h"
#include "qwasmopenglcontext.h"
@ -164,8 +163,10 @@ bool QWasmIntegration::hasCapability(QPlatformIntegration::Capability cap) const
QPlatformWindow *QWasmIntegration::createPlatformWindow(QWindow *window) const
{
QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor();
return new QWasmWindow(window, compositor, m_backingStores.value(window));
auto *wasmScreen = QWasmScreen::get(window->screen());
QWasmCompositor *compositor = wasmScreen->compositor();
return new QWasmWindow(window, wasmScreen->deadKeySupport(), compositor,
m_backingStores.value(window));
}
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
#include "qwasmscreen.h"
#include "qwasmwindow.h"
#include "qwasmeventtranslator.h"
#include "qwasmcompositor.h"
#include "qwasmintegration.h"
#include "qwasmcssstyle.h"
#include "qwasmintegration.h"
#include "qwasmkeytranslator.h"
#include "qwasmwindow.h"
#include <emscripten/bind.h>
#include <emscripten/val.h>
@ -29,7 +30,7 @@ QWasmScreen::QWasmScreen(const emscripten::val &containerOrCanvas)
: m_container(containerOrCanvas),
m_shadowContainer(emscripten::val::undefined()),
m_compositor(new QWasmCompositor(this)),
m_eventTranslator(new QWasmEventTranslator())
m_deadKeySupport(std::make_unique<QWasmDeadKeySupport>())
{
auto document = m_container["ownerDocument"];
// 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();
}
QWasmEventTranslator *QWasmScreen::eventTranslator()
{
return m_eventTranslator.get();
}
emscripten::val QWasmScreen::element() const
{
return m_shadowContainer;

View File

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

View File

@ -11,6 +11,9 @@
#include "qwasmbase64iconstore.h"
#include "qwasmdom.h"
#include "qwasmclipboard.h"
#include "qwasmintegration.h"
#include "qwasmkeytranslator.h"
#include "qwasmwindow.h"
#include "qwasmwindowclientarea.h"
#include "qwasmscreen.h"
@ -31,11 +34,13 @@ QT_BEGIN_NAMESPACE
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),
m_window(w),
m_compositor(compositor),
m_backingStore(backingStore),
m_deadKeySupport(deadKeySupport),
m_document(dom::document()),
m_qtWindow(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);
const auto callback = std::function([this](emscripten::val event) {
const auto pointerCallback = std::function([this](emscripten::val event) {
if (processPointer(*PointerEvent::fromWeb(event)))
event.call<void>("preventDefault");
});
m_pointerEnterCallback =
std::make_unique<qstdweb::EventCallback>(m_qtWindow, "pointerenter", callback);
std::make_unique<qstdweb::EventCallback>(m_qtWindow, "pointerenter", pointerCallback);
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_qtWindow, "drop", [this](emscripten::val event) {
@ -110,6 +115,15 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingSt
if (processWheel(*WheelEvent::fromWeb(event)))
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()
@ -433,6 +447,26 @@ void QWasmWindow::applyWindowState()
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)
{
if (event.pointerType != PointerType::Mouse)
@ -548,6 +582,9 @@ void QWasmWindow::requestActivateWindow()
if (window()->isTopLevel())
raise();
m_canvas.call<void>("focus");
QPlatformWindow::requestActivateWindow();
}

View File

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