1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 or (at your option) any later version
20 ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 ** included in the packaging of this file. Please review the following
23 ** information to ensure the GNU General Public License requirements will
24 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 **
26 ** $QT_END_LICENSE$
27 **
28 ****************************************************************************/
29 
30 #include "qwasmeventtranslator.h"
31 #include "qwasmeventdispatcher.h"
32 #include "qwasmcompositor.h"
33 #include "qwasmintegration.h"
34 #include "qwasmclipboard.h"
35 #include "qwasmstring.h"
36 
37 #include <QtGui/qevent.h>
38 #include <qpa/qwindowsysteminterface.h>
39 #include <QtCore/qcoreapplication.h>
40 #include <QtCore/qglobal.h>
41 #include <QtCore/qobject.h>
42 
43 #include <QtCore/qdeadlinetimer.h>
44 #include <private/qmakearray_p.h>
45 #include <QtCore/qnamespace.h>
46 #include <QCursor>
47 
48 #include <emscripten/bind.h>
49 
50 #include <iostream>
51 
52 using namespace emscripten;
53 
54 QT_BEGIN_NAMESPACE
55 
56 typedef struct emkb2qt {
57     const char *em;
58     unsigned int qt;
59 
operator <=emkb2qt60     constexpr bool operator <=(const emkb2qt &that) const noexcept
61     {
62         return !(strcmp(that) > 0);
63     }
64 
operator <emkb2qt65     bool operator <(const emkb2qt &that) const noexcept
66     {
67          return ::strcmp(em, that.em) < 0;
68     }
strcmpemkb2qt69     constexpr int strcmp(const emkb2qt &that, const int i = 0) const
70      {
71          return em[i] == 0 && that.em[i] == 0 ? 0
72              : em[i] == 0 ? -1
73                  : that.em[i] == 0 ? 1
74                      : em[i] < that.em[i] ? -1
75                          : em[i] > that.em[i] ? 1
76                              : strcmp(that, i + 1);
77      }
78 } emkb2qt_t;
79 
80 template<unsigned int Qt, char ... EmChar>
81 struct Emkb2Qt
82 {
83     static constexpr const char storage[sizeof ... (EmChar) + 1] = {EmChar..., '\0'};
84     using Type = emkb2qt_t;
dataEmkb2Qt85     static constexpr Type data() noexcept { return Type{storage, Qt}; }
86 };
87 
88 template<unsigned int Qt, char ... EmChar> constexpr char Emkb2Qt<Qt, EmChar...>::storage[];
89 
90 static constexpr const auto KeyTbl = qMakeArray(
91     QSortedData<
92         Emkb2Qt< Qt::Key_Escape,        'E','s','c','a','p','e' >,
93         Emkb2Qt< Qt::Key_Tab,           'T','a','b' >,
94         Emkb2Qt< Qt::Key_Backspace,     'B','a','c','k','s','p','a','c','e' >,
95         Emkb2Qt< Qt::Key_Return,        'E','n','t','e','r' >,
96         Emkb2Qt< Qt::Key_Insert,        'I','n','s','e','r','t' >,
97         Emkb2Qt< Qt::Key_Delete,        'D','e','l','e','t','e' >,
98         Emkb2Qt< Qt::Key_Pause,         'P','a','u','s','e' >,
99         Emkb2Qt< Qt::Key_Pause,         'C','l','e','a','r' >,
100         Emkb2Qt< Qt::Key_Home,          'H','o','m','e' >,
101         Emkb2Qt< Qt::Key_End,           'E','n','d' >,
102         Emkb2Qt< Qt::Key_Left,          'A','r','r','o','w','L','e','f','t' >,
103         Emkb2Qt< Qt::Key_Up,            'A','r','r','o','w','U','p' >,
104         Emkb2Qt< Qt::Key_Right,         'A','r','r','o','w','R','i','g','h','t' >,
105         Emkb2Qt< Qt::Key_Down,          'A','r','r','o','w','D','o','w','n' >,
106         Emkb2Qt< Qt::Key_PageUp,        'P','a','g','e','U','p' >,
107         Emkb2Qt< Qt::Key_PageDown,      'P','a','g','e','D','o','w','n' >,
108         Emkb2Qt< Qt::Key_Shift,         'S','h','i','f','t' >,
109         Emkb2Qt< Qt::Key_Control,       'C','o','n','t','r','o','l' >,
110         Emkb2Qt< Qt::Key_Meta,          'O','S'>,
111         Emkb2Qt< Qt::Key_Alt,           'A','l','t','L','e','f','t' >,
112         Emkb2Qt< Qt::Key_Alt,           'A','l','t' >,
113         Emkb2Qt< Qt::Key_CapsLock,      'C','a','p','s','L','o','c','k' >,
114         Emkb2Qt< Qt::Key_NumLock,       'N','u','m','L','o','c','k' >,
115         Emkb2Qt< Qt::Key_ScrollLock,    'S','c','r','o','l','l','L','o','c','k' >,
116         Emkb2Qt< Qt::Key_F1,            'F','1' >,
117         Emkb2Qt< Qt::Key_F2,            'F','2' >,
118         Emkb2Qt< Qt::Key_F3,            'F','3' >,
119         Emkb2Qt< Qt::Key_F4,            'F','4' >,
120         Emkb2Qt< Qt::Key_F5,            'F','5' >,
121         Emkb2Qt< Qt::Key_F6,            'F','6' >,
122         Emkb2Qt< Qt::Key_F7,            'F','7' >,
123         Emkb2Qt< Qt::Key_F8,            'F','8' >,
124         Emkb2Qt< Qt::Key_F9,            'F','9' >,
125         Emkb2Qt< Qt::Key_F10,           'F','1','0' >,
126         Emkb2Qt< Qt::Key_F11,           'F','1','1' >,
127         Emkb2Qt< Qt::Key_F12,           'F','1','2' >,
128         Emkb2Qt< Qt::Key_F13,           'F','1','3' >,
129         Emkb2Qt< Qt::Key_F14,           'F','1','4' >,
130         Emkb2Qt< Qt::Key_F15,           'F','1','5' >,
131         Emkb2Qt< Qt::Key_F16,           'F','1','6' >,
132         Emkb2Qt< Qt::Key_F17,           'F','1','7' >,
133         Emkb2Qt< Qt::Key_F18,           'F','1','8' >,
134         Emkb2Qt< Qt::Key_F19,           'F','1','9' >,
135         Emkb2Qt< Qt::Key_F20,           'F','2','0' >,
136         Emkb2Qt< Qt::Key_F21,           'F','2','1' >,
137         Emkb2Qt< Qt::Key_F22,           'F','2','2' >,
138         Emkb2Qt< Qt::Key_F23,           'F','2','3' >,
139         Emkb2Qt< Qt::Key_Space,         ' ' >,
140         Emkb2Qt< Qt::Key_Comma,         ',' >,
141         Emkb2Qt< Qt::Key_Minus,         '-' >,
142         Emkb2Qt< Qt::Key_Period,        '.' >,
143         Emkb2Qt< Qt::Key_Slash,         '/' >,
144         Emkb2Qt< Qt::Key_0,             'D','i','g','i','t','0' >,
145         Emkb2Qt< Qt::Key_1,             'D','i','g','i','t','1' >,
146         Emkb2Qt< Qt::Key_2,             'D','i','g','i','t','2' >,
147         Emkb2Qt< Qt::Key_3,             'D','i','g','i','t','3' >,
148         Emkb2Qt< Qt::Key_4,             'D','i','g','i','t','4' >,
149         Emkb2Qt< Qt::Key_5,             'D','i','g','i','t','5' >,
150         Emkb2Qt< Qt::Key_6,             'D','i','g','i','t','6' >,
151         Emkb2Qt< Qt::Key_7,             'D','i','g','i','t','7' >,
152         Emkb2Qt< Qt::Key_8,             'D','i','g','i','t','8' >,
153         Emkb2Qt< Qt::Key_9,             'D','i','g','i','t','9' >,
154         Emkb2Qt< Qt::Key_Semicolon,     ';' >,
155         Emkb2Qt< Qt::Key_Equal,         '=' >,
156         Emkb2Qt< Qt::Key_A,             'K','e','y','A' >,
157         Emkb2Qt< Qt::Key_B,             'K','e','y','B' >,
158         Emkb2Qt< Qt::Key_C,             'K','e','y','C' >,
159         Emkb2Qt< Qt::Key_D,             'K','e','y','D' >,
160         Emkb2Qt< Qt::Key_E,             'K','e','y','E' >,
161         Emkb2Qt< Qt::Key_F,             'K','e','y','F' >,
162         Emkb2Qt< Qt::Key_G,             'K','e','y','G' >,
163         Emkb2Qt< Qt::Key_H,             'K','e','y','H' >,
164         Emkb2Qt< Qt::Key_I,             'K','e','y','I' >,
165         Emkb2Qt< Qt::Key_J,             'K','e','y','J' >,
166         Emkb2Qt< Qt::Key_K,             'K','e','y','K' >,
167         Emkb2Qt< Qt::Key_L,             'K','e','y','L' >,
168         Emkb2Qt< Qt::Key_M,             'K','e','y','M' >,
169         Emkb2Qt< Qt::Key_N,             'K','e','y','N' >,
170         Emkb2Qt< Qt::Key_O,             'K','e','y','O' >,
171         Emkb2Qt< Qt::Key_P,             'K','e','y','P' >,
172         Emkb2Qt< Qt::Key_Q,             'K','e','y','Q' >,
173         Emkb2Qt< Qt::Key_R,             'K','e','y','R' >,
174         Emkb2Qt< Qt::Key_S,             'K','e','y','S' >,
175         Emkb2Qt< Qt::Key_T,             'K','e','y','T' >,
176         Emkb2Qt< Qt::Key_U,             'K','e','y','U' >,
177         Emkb2Qt< Qt::Key_V,             'K','e','y','V' >,
178         Emkb2Qt< Qt::Key_W,             'K','e','y','W' >,
179         Emkb2Qt< Qt::Key_X,             'K','e','y','X' >,
180         Emkb2Qt< Qt::Key_Y,             'K','e','y','Y' >,
181         Emkb2Qt< Qt::Key_Z,             'K','e','y','Z' >,
182         Emkb2Qt< Qt::Key_BracketLeft,   '[' >,
183         Emkb2Qt< Qt::Key_Backslash,     '\\' >,
184         Emkb2Qt< Qt::Key_BracketRight,  ']' >,
185         Emkb2Qt< Qt::Key_Apostrophe,    '\'' >,
186         Emkb2Qt< Qt::Key_QuoteLeft,     'B','a','c','k','q','u','o','t','e' >,
187         Emkb2Qt< Qt::Key_multiply,      'N','u','m','p','a','d','M','u','l','t','i','p','l','y' >,
188         Emkb2Qt< Qt::Key_Minus,         'N','u','m','p','a','d','S','u','b','t','r','a','c','t' >,
189         Emkb2Qt< Qt::Key_Period,        'N','u','m','p','a','d','D','e','c','i','m','a','l' >,
190         Emkb2Qt< Qt::Key_Plus,          'N','u','m','p','a','d','A','d','d' >,
191         Emkb2Qt< Qt::Key_division,      'N','u','m','p','a','d','D','i','v','i','d','e' >,
192         Emkb2Qt< Qt::Key_Equal,         'N','u','m','p','a','d','E','q','u','a','l' >,
193         Emkb2Qt< Qt::Key_0,             'N','u','m','p','a','d','0' >,
194         Emkb2Qt< Qt::Key_1,             'N','u','m','p','a','d','1' >,
195         Emkb2Qt< Qt::Key_2,             'N','u','m','p','a','d','2' >,
196         Emkb2Qt< Qt::Key_3,             'N','u','m','p','a','d','3' >,
197         Emkb2Qt< Qt::Key_4,             'N','u','m','p','a','d','4' >,
198         Emkb2Qt< Qt::Key_5,             'N','u','m','p','a','d','5' >,
199         Emkb2Qt< Qt::Key_6,             'N','u','m','p','a','d','6' >,
200         Emkb2Qt< Qt::Key_7,             'N','u','m','p','a','d','7' >,
201         Emkb2Qt< Qt::Key_8,             'N','u','m','p','a','d','8' >,
202         Emkb2Qt< Qt::Key_9,             'N','u','m','p','a','d','9' >,
203         Emkb2Qt< Qt::Key_Comma,         'N','u','m','p','a','d','C','o','m','m','a' >,
204         Emkb2Qt< Qt::Key_Enter,         'N','u','m','p','a','d','E','n','t','e','r' >,
205         Emkb2Qt< Qt::Key_Paste,         'P','a','s','t','e' >,
206         Emkb2Qt< Qt::Key_AltGr,         'A','l','t','R','i','g','h','t' >,
207         Emkb2Qt< Qt::Key_Help,          'H','e','l','p' >,
208         Emkb2Qt< Qt::Key_Equal,         '=' >,
209         Emkb2Qt< Qt::Key_yen,           'I','n','t','l','Y','e','n' >,
210 
211         Emkb2Qt< Qt::Key_Exclam,        '\x21' >,
212         Emkb2Qt< Qt::Key_QuoteDbl,      '\x22' >,
213         Emkb2Qt< Qt::Key_NumberSign,    '\x23' >,
214         Emkb2Qt< Qt::Key_Dollar,        '\x24' >,
215         Emkb2Qt< Qt::Key_Percent,       '\x25' >,
216         Emkb2Qt< Qt::Key_Ampersand,     '\x26' >,
217         Emkb2Qt< Qt::Key_ParenLeft,     '\x28' >,
218         Emkb2Qt< Qt::Key_ParenRight,    '\x29' >,
219         Emkb2Qt< Qt::Key_Asterisk,      '\x2a' >,
220         Emkb2Qt< Qt::Key_Plus,          '\x2b' >,
221         Emkb2Qt< Qt::Key_Colon,         '\x3a' >,
222         Emkb2Qt< Qt::Key_Semicolon,     '\x3b' >,
223         Emkb2Qt< Qt::Key_Less,          '\x3c' >,
224         Emkb2Qt< Qt::Key_Equal,         '\x3d' >,
225         Emkb2Qt< Qt::Key_Greater,       '\x3e' >,
226         Emkb2Qt< Qt::Key_Question,      '\x3f' >,
227         Emkb2Qt< Qt::Key_At,            '\x40' >,
228         Emkb2Qt< Qt::Key_BracketLeft,   '\x5b' >,
229         Emkb2Qt< Qt::Key_Backslash,     '\x5c' >,
230         Emkb2Qt< Qt::Key_BracketRight,  '\x5d' >,
231         Emkb2Qt< Qt::Key_AsciiCircum,   '\x5e' >,
232         Emkb2Qt< Qt::Key_Underscore,    '\x5f' >,
233         Emkb2Qt< Qt::Key_QuoteLeft,     '\x60'>,
234         Emkb2Qt< Qt::Key_BraceLeft,     '\x7b'>,
235         Emkb2Qt< Qt::Key_Bar,           '\x7c'>,
236         Emkb2Qt< Qt::Key_BraceRight,    '\x7d'>,
237         Emkb2Qt< Qt::Key_AsciiTilde,    '\x7e'>,
238         Emkb2Qt< Qt::Key_Space,         '\x20' >,
239         Emkb2Qt< Qt::Key_Comma,         '\x2c' >,
240         Emkb2Qt< Qt::Key_Minus,         '\x2d' >,
241         Emkb2Qt< Qt::Key_Period,        '\x2e' >,
242         Emkb2Qt< Qt::Key_Slash,         '\x2f' >,
243         Emkb2Qt< Qt::Key_Apostrophe,    '\x27' >,
244         Emkb2Qt< Qt::Key_Menu,          'C','o','n','t','e','x','t','M','e','n','u' >,
245 
246         Emkb2Qt< Qt::Key_Agrave,        '\xc3','\xa0' >,
247         Emkb2Qt< Qt::Key_Aacute,        '\xc3','\xa1' >,
248         Emkb2Qt< Qt::Key_Acircumflex,   '\xc3','\xa2' >,
249         Emkb2Qt< Qt::Key_Adiaeresis,    '\xc3','\xa4' >,
250         Emkb2Qt< Qt::Key_AE,            '\xc3','\xa6' >,
251         Emkb2Qt< Qt::Key_Atilde,        '\xc3','\xa3' >,
252         Emkb2Qt< Qt::Key_Aring,         '\xc3','\xa5' >,
253         Emkb2Qt< Qt::Key_Ccedilla,      '\xc3','\xa7' >,
254         Emkb2Qt< Qt::Key_Egrave,        '\xc3','\xa8' >,
255         Emkb2Qt< Qt::Key_Eacute,        '\xc3','\xa9' >,
256         Emkb2Qt< Qt::Key_Ecircumflex,   '\xc3','\xaa' >,
257         Emkb2Qt< Qt::Key_Ediaeresis,    '\xc3','\xab' >,
258         Emkb2Qt< Qt::Key_Icircumflex,   '\xc3','\xae' >,
259         Emkb2Qt< Qt::Key_Idiaeresis,    '\xc3','\xaf' >,
260         Emkb2Qt< Qt::Key_Ocircumflex,   '\xc3','\xb4' >,
261         Emkb2Qt< Qt::Key_Odiaeresis,    '\xc3','\xb6' >,
262         Emkb2Qt< Qt::Key_Ograve,        '\xc3','\xb2' >,
263         Emkb2Qt< Qt::Key_Oacute,        '\xc3','\xb3' >,
264         Emkb2Qt< Qt::Key_Ooblique,      '\xc3','\xb8' >,
265         Emkb2Qt< Qt::Key_Otilde,        '\xc3','\xb5' >,
266         Emkb2Qt< Qt::Key_Ucircumflex,   '\xc3','\xbb' >,
267         Emkb2Qt< Qt::Key_Udiaeresis,    '\xc3','\xbc' >,
268         Emkb2Qt< Qt::Key_Ugrave,        '\xc3','\xb9' >,
269         Emkb2Qt< Qt::Key_Uacute,        '\xc3','\xba' >,
270         Emkb2Qt< Qt::Key_Ntilde,        '\xc3','\xb1' >,
271         Emkb2Qt< Qt::Key_ydiaeresis,    '\xc3','\xbf' >
272             >::Data{}
273         );
274 
275 static constexpr const auto DeadKeyShiftTbl = qMakeArray(
276     QSortedData<
277        // shifted
278         Emkb2Qt< Qt::Key_Agrave,        '\xc3','\x80' >,
279         Emkb2Qt< Qt::Key_Aacute,        '\xc3','\x81' >,
280         Emkb2Qt< Qt::Key_Acircumflex,   '\xc3','\x82' >,
281         Emkb2Qt< Qt::Key_Adiaeresis,    '\xc3','\x84' >,
282         Emkb2Qt< Qt::Key_AE,            '\xc3','\x86' >,
283         Emkb2Qt< Qt::Key_Atilde,        '\xc3','\x83' >,
284         Emkb2Qt< Qt::Key_Aring,         '\xc3','\x85' >,
285         Emkb2Qt< Qt::Key_Egrave,        '\xc3','\x88' >,
286         Emkb2Qt< Qt::Key_Eacute,        '\xc3','\x89' >,
287         Emkb2Qt< Qt::Key_Ecircumflex,   '\xc3','\x8a' >,
288         Emkb2Qt< Qt::Key_Ediaeresis,    '\xc3','\x8b' >,
289         Emkb2Qt< Qt::Key_Icircumflex,   '\xc3','\x8e' >,
290         Emkb2Qt< Qt::Key_Idiaeresis,    '\xc3','\x8f' >,
291         Emkb2Qt< Qt::Key_Ocircumflex,   '\xc3','\x94' >,
292         Emkb2Qt< Qt::Key_Odiaeresis,    '\xc3','\x96' >,
293         Emkb2Qt< Qt::Key_Ograve,        '\xc3','\x92' >,
294         Emkb2Qt< Qt::Key_Oacute,        '\xc3','\x93' >,
295         Emkb2Qt< Qt::Key_Ooblique,      '\xc3','\x98' >,
296         Emkb2Qt< Qt::Key_Otilde,        '\xc3','\x95' >,
297         Emkb2Qt< Qt::Key_Ucircumflex,   '\xc3','\x9b' >,
298         Emkb2Qt< Qt::Key_Udiaeresis,    '\xc3','\x9c' >,
299         Emkb2Qt< Qt::Key_Ugrave,        '\xc3','\x99' >,
300         Emkb2Qt< Qt::Key_Uacute,        '\xc3','\x9a' >,
301         Emkb2Qt< Qt::Key_Ntilde,        '\xc3','\x91' >,
302         Emkb2Qt< Qt::Key_Ccedilla,      '\xc3','\x87' >,
303         Emkb2Qt< Qt::Key_ydiaeresis,    '\xc3','\x8f' >
304     >::Data{}
305 );
306 
307 // macOS CTRL <-> META switching. We most likely want to enable
308 // the existing switching code in QtGui, but for now do it here.
309 static bool g_usePlatformMacSpecifics = false;
310 
311 bool g_useNaturalScrolling = true; // natural scrolling is default on linux/windows
312 
mouseWheelEvent(emscripten::val event)313 static void mouseWheelEvent(emscripten::val event) {
314 
315     emscripten::val wheelInterted = event["webkitDirectionInvertedFromDevice"];
316 
317     if (wheelInterted.as<bool>()) {
318         g_useNaturalScrolling = true;
319     }
320 }
321 
EMSCRIPTEN_BINDINGS(qtMouseModule)322 EMSCRIPTEN_BINDINGS(qtMouseModule) {
323     function("qtMouseWheelEvent", &mouseWheelEvent);
324 }
325 
QWasmEventTranslator(QWasmScreen * screen)326 QWasmEventTranslator::QWasmEventTranslator(QWasmScreen *screen)
327     : QObject(screen)
328     , draggedWindow(nullptr)
329     , lastWindow(nullptr)
330     , pressedButtons(Qt::NoButton)
331     , resizeMode(QWasmWindow::ResizeNone)
332 {
333 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
334     touchDevice = new QPointingDevice("touchscreen", 1, QInputDevice::DeviceType::TouchScreen,
335     QPointingDevice::PointerType::Finger,
336     QPointingDevice::Capability::Position | QPointingDevice::Capability::Area | QPointingDevice::Capability::NormalizedPosition,
337     10, 0);
338     QWindowSystemInterface::registerInputDevice(touchDevice);
339 #else
340     touchDevice = new QTouchDevice;
341     touchDevice->setType(QTouchDevice::TouchScreen);
342     touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition);
343     QWindowSystemInterface::registerTouchDevice(touchDevice);
344 #endif
345 
346     initEventHandlers();
347 }
348 
~QWasmEventTranslator()349 QWasmEventTranslator::~QWasmEventTranslator()
350 {
351     // deregister event handlers
352     QByteArray canvasSelector = "#" + screen()->canvasId().toUtf8();
353     emscripten_set_keydown_callback(canvasSelector.constData(), 0, 0, NULL);
354     emscripten_set_keyup_callback(canvasSelector.constData(),  0, 0, NULL);
355 
356     emscripten_set_mousedown_callback(canvasSelector.constData(), 0, 0, NULL);
357     emscripten_set_mouseup_callback(canvasSelector.constData(),  0, 0, NULL);
358     emscripten_set_mousemove_callback(canvasSelector.constData(),  0, 0, NULL);
359 
360     emscripten_set_focus_callback(canvasSelector.constData(),  0, 0, NULL);
361 
362     emscripten_set_wheel_callback(canvasSelector.constData(),  0, 0, NULL);
363 
364     emscripten_set_touchstart_callback(canvasSelector.constData(),  0, 0, NULL);
365     emscripten_set_touchend_callback(canvasSelector.constData(),  0, 0, NULL);
366     emscripten_set_touchmove_callback(canvasSelector.constData(),  0, 0, NULL);
367     emscripten_set_touchcancel_callback(canvasSelector.constData(),  0, 0, NULL);
368 }
369 
initEventHandlers()370 void QWasmEventTranslator::initEventHandlers()
371 {
372     QByteArray canvasSelector = "#" + screen()->canvasId().toUtf8();
373 
374     // The Platform Detect: expand coverage and move as needed
375     enum Platform {
376         GenericPlatform,
377         MacOSPlatform
378     };
379     Platform platform = Platform(emscripten::val::global("navigator")["platform"]
380             .call<bool>("includes", emscripten::val("Mac")));
381     g_usePlatformMacSpecifics = (platform == MacOSPlatform);
382 
383     if (platform == MacOSPlatform) {
384         g_useNaturalScrolling = false; // make this !default on macOS
385 
386         if (!emscripten::val::global("window")["safari"].isUndefined()) {
387             val canvas = screen()->canvas();
388             canvas.call<void>("addEventListener",
389                               val("wheel"),
390                               val::module_property("qtMouseWheelEvent"));
391         }
392     }
393 
394     emscripten_set_keydown_callback(canvasSelector.constData(), (void *)this, 1, &keyboard_cb);
395     emscripten_set_keyup_callback(canvasSelector.constData(), (void *)this, 1, &keyboard_cb);
396 
397     emscripten_set_mousedown_callback(canvasSelector.constData(), (void *)this, 1, &mouse_cb);
398     emscripten_set_mouseup_callback(canvasSelector.constData(), (void *)this, 1, &mouse_cb);
399     emscripten_set_mousemove_callback(canvasSelector.constData(), (void *)this, 1, &mouse_cb);
400 
401     emscripten_set_focus_callback(canvasSelector.constData(), (void *)this, 1, &focus_cb);
402 
403     emscripten_set_wheel_callback(canvasSelector.constData(), (void *)this, 1, &wheel_cb);
404 
405     emscripten_set_touchstart_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback);
406     emscripten_set_touchend_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback);
407     emscripten_set_touchmove_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback);
408     emscripten_set_touchcancel_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback);
409 }
410 
411 template <typename Event>
translatKeyModifier(const Event * event)412 QFlags<Qt::KeyboardModifier> QWasmEventTranslator::translatKeyModifier(const Event *event)
413 {
414     QFlags<Qt::KeyboardModifier> keyModifier = Qt::NoModifier;
415     if (event->shiftKey)
416         keyModifier |= Qt::ShiftModifier;
417     if (event->ctrlKey) {
418         if (g_usePlatformMacSpecifics)
419             keyModifier |= Qt::MetaModifier;
420         else
421             keyModifier |= Qt::ControlModifier;
422     }
423     if (event->altKey)
424         keyModifier |= Qt::AltModifier;
425     if (event->metaKey) {
426         if (g_usePlatformMacSpecifics)
427             keyModifier |= Qt::ControlModifier;
428         else
429             keyModifier |= Qt::MetaModifier;
430     }
431     return keyModifier;
432 }
433 
translateKeyboardEventModifier(const EmscriptenKeyboardEvent * keyEvent)434 QFlags<Qt::KeyboardModifier> QWasmEventTranslator::translateKeyboardEventModifier(const EmscriptenKeyboardEvent *keyEvent)
435 {
436     QFlags<Qt::KeyboardModifier> keyModifier = translatKeyModifier(keyEvent);
437     if (keyEvent->location == DOM_KEY_LOCATION_NUMPAD) {
438         keyModifier |= Qt::KeypadModifier;
439     }
440 
441     return keyModifier;
442 }
443 
translateMouseEventModifier(const EmscriptenMouseEvent * mouseEvent)444 QFlags<Qt::KeyboardModifier> QWasmEventTranslator::translateMouseEventModifier(const EmscriptenMouseEvent *mouseEvent)
445 {
446     return translatKeyModifier(mouseEvent);
447 }
448 
keyboard_cb(int eventType,const EmscriptenKeyboardEvent * keyEvent,void * userData)449 int QWasmEventTranslator::keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
450 {
451     QWasmEventTranslator *wasmTranslator = reinterpret_cast<QWasmEventTranslator *>(userData);
452     bool accepted = wasmTranslator->processKeyboard(eventType, keyEvent);
453 
454     return accepted ? 1 : 0;
455 }
456 
screen()457 QWasmScreen *QWasmEventTranslator::screen()
458 {
459     return static_cast<QWasmScreen *>(parent());
460 }
461 
translateEmscriptKey(const EmscriptenKeyboardEvent * emscriptKey)462 Qt::Key QWasmEventTranslator::translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey)
463 {
464     Qt::Key qtKey = Qt::Key_unknown;
465 
466     if (qstrncmp(emscriptKey->key, "Dead", 4) == 0 ) {
467         emkb2qt_t searchKey1{emscriptKey->code, 0};
468         for (auto it1 = KeyTbl.cbegin(); it1 != KeyTbl.end(); ++it1)
469             if (it1 != KeyTbl.end() && (qstrcmp(searchKey1.em, it1->em) == 0)) {
470                 qtKey = static_cast<Qt::Key>(it1->qt);
471             }
472 
473     } else if (qstrncmp(emscriptKey->code, "Key", 3) == 0 || qstrncmp(emscriptKey->code, "Numpad", 6) == 0 ||
474                  qstrncmp(emscriptKey->code, "Digit", 5) == 0) {
475         emkb2qt_t searchKey{emscriptKey->code, 0}; // search emcsripten code
476         auto it1 = std::lower_bound(KeyTbl.cbegin(), KeyTbl.cend(), searchKey);
477         if (it1 != KeyTbl.end() && !(searchKey < *it1)) {
478             qtKey = static_cast<Qt::Key>(it1->qt);
479         }
480     }
481 
482     if (qtKey == Qt::Key_unknown) {
483         emkb2qt_t searchKey{emscriptKey->key, 0}; // search unicode key
484         auto it1 = std::lower_bound(KeyTbl.cbegin(), KeyTbl.cend(), searchKey);
485         if (it1 != KeyTbl.end() && !(searchKey < *it1)) {
486             qtKey = static_cast<Qt::Key>(it1->qt);
487         }
488     }
489     if (qtKey == Qt::Key_unknown) {//try harder with shifted number keys
490         emkb2qt_t searchKey1{emscriptKey->key, 0};
491         for (auto it1 = KeyTbl.cbegin(); it1 != KeyTbl.end(); ++it1)
492             if (it1 != KeyTbl.end() && (qstrcmp(searchKey1.em, it1->em) == 0)) {
493                 qtKey = static_cast<Qt::Key>(it1->qt);
494             }
495     }
496 
497     return qtKey;
498 }
499 
translateMouseButton(unsigned short button)500 Qt::MouseButton QWasmEventTranslator::translateMouseButton(unsigned short button)
501 {
502     if (button == 0)
503         return Qt::LeftButton;
504     else if (button == 1)
505         return Qt::MiddleButton;
506     else if (button == 2)
507         return Qt::RightButton;
508 
509     return Qt::NoButton;
510 }
511 
mouse_cb(int eventType,const EmscriptenMouseEvent * mouseEvent,void * userData)512 int QWasmEventTranslator::mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
513 {
514     QWasmEventTranslator *translator = (QWasmEventTranslator*)userData;
515     translator->processMouse(eventType,mouseEvent);
516     QWasmEventDispatcher::maintainTimers();
517     return 0;
518 }
519 
resizeWindow(QWindow * window,QWasmWindow::ResizeMode mode,QRect startRect,QPoint amount)520 void resizeWindow(QWindow *window, QWasmWindow::ResizeMode mode,
521                   QRect startRect, QPoint amount)
522 {
523     if (mode == QWasmWindow::ResizeNone)
524         return;
525 
526     bool top = mode == QWasmWindow::ResizeTopLeft ||
527                mode == QWasmWindow::ResizeTop ||
528                mode == QWasmWindow::ResizeTopRight;
529 
530     bool bottom = mode == QWasmWindow::ResizeBottomLeft ||
531                   mode == QWasmWindow::ResizeBottom ||
532                   mode == QWasmWindow::ResizeBottomRight;
533 
534     bool left = mode == QWasmWindow::ResizeLeft ||
535                 mode == QWasmWindow::ResizeTopLeft ||
536                 mode == QWasmWindow::ResizeBottomLeft;
537 
538     bool right = mode == QWasmWindow::ResizeRight ||
539                  mode == QWasmWindow::ResizeTopRight ||
540                  mode == QWasmWindow::ResizeBottomRight;
541 
542     int x1 = startRect.left();
543     int y1 = startRect.top();
544     int x2 = startRect.right();
545     int y2 = startRect.bottom();
546 
547     if (left)
548         x1 += amount.x();
549     if (top)
550         y1 += amount.y();
551     if (right)
552         x2 += amount.x();
553     if (bottom)
554         y2 += amount.y();
555 
556     int w = x2-x1;
557     int h = y2-y1;
558 
559     if (w < window->minimumWidth()) {
560         if (left)
561             x1 -= window->minimumWidth() - w;
562 
563         w = window->minimumWidth();
564     }
565 
566     if (h < window->minimumHeight()) {
567         if (top)
568             y1 -= window->minimumHeight() - h;
569 
570         h = window->minimumHeight();
571     }
572 
573     window->setGeometry(x1, y1, w, h);
574 }
575 
processMouse(int eventType,const EmscriptenMouseEvent * mouseEvent)576 void QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent)
577 {
578     QPoint targetPoint(mouseEvent->targetX, mouseEvent->targetY);
579     QPoint globalPoint = screen()->geometry().topLeft() + targetPoint;
580 
581     QEvent::Type buttonEventType = QEvent::None;
582     Qt::MouseButton button = translateMouseButton(mouseEvent->button);
583     Qt::KeyboardModifiers modifiers = translateMouseEventModifier(mouseEvent);
584 
585     QWindow *window2 = nullptr;
586     if (resizeMode == QWasmWindow::ResizeNone)
587         window2 = screen()->compositor()->windowAt(globalPoint, 5);
588 
589     if (lastWindow && lastWindow->cursor() != Qt::ArrowCursor) {
590         lastWindow->setCursor(Qt::ArrowCursor);
591     }
592     if (window2 == nullptr) {
593         window2 = lastWindow;
594     } else {
595         lastWindow = window2;
596     }
597 
598     QPoint localPoint = window2->mapFromGlobal(globalPoint);
599     bool interior = window2->geometry().contains(globalPoint);
600 
601     QWasmWindow *htmlWindow = static_cast<QWasmWindow*>(window2->handle());
602     switch (eventType) {
603     case EMSCRIPTEN_EVENT_MOUSEDOWN:
604     {
605         if (window2)
606             window2->requestActivate();
607 
608         pressedButtons.setFlag(button);
609 
610         if (mouseEvent->button == 0) {
611             pressedWindow = window2;
612             buttonEventType = QEvent::MouseButtonPress;
613             if (!(htmlWindow->m_windowState & Qt::WindowFullScreen) && !(htmlWindow->m_windowState & Qt::WindowMaximized)) {
614                 if (htmlWindow && window2->flags().testFlag(Qt::WindowTitleHint) && htmlWindow->isPointOnTitle(globalPoint))
615                     draggedWindow = window2;
616                 else if (htmlWindow && htmlWindow->isPointOnResizeRegion(globalPoint)) {
617                     draggedWindow = window2;
618                     resizeMode = htmlWindow->resizeModeAtPoint(globalPoint);
619                     resizePoint = globalPoint;
620                     resizeStartRect = window2->geometry();
621                 }
622             }
623         }
624 
625         htmlWindow->injectMousePressed(localPoint, globalPoint, button, modifiers);
626         break;
627     }
628     case EMSCRIPTEN_EVENT_MOUSEUP:
629     {
630         pressedButtons.setFlag(translateMouseButton(mouseEvent->button), false);
631         buttonEventType = QEvent::MouseButtonRelease;
632         QWasmWindow *oldWindow = nullptr;
633 
634         if (mouseEvent->button == 0 && pressedWindow) {
635             oldWindow = static_cast<QWasmWindow*>(pressedWindow->handle());
636             pressedWindow = nullptr;
637         }
638 
639         if (mouseEvent->button == 0) {
640             draggedWindow = nullptr;
641             resizeMode = QWasmWindow::ResizeNone;
642         }
643 
644         if (oldWindow)
645             oldWindow->injectMouseReleased(localPoint, globalPoint, button, modifiers);
646         else
647             htmlWindow->injectMouseReleased(localPoint, globalPoint, button, modifiers);
648         break;
649     }
650     case EMSCRIPTEN_EVENT_MOUSEMOVE: // drag event
651     {
652         buttonEventType = QEvent::MouseMove;
653 
654         if (htmlWindow && htmlWindow->isPointOnResizeRegion(globalPoint))
655             window2->setCursor(cursorForMode(htmlWindow->resizeModeAtPoint(globalPoint)));
656 
657         if (!(htmlWindow->m_windowState & Qt::WindowFullScreen) && !(htmlWindow->m_windowState & Qt::WindowMaximized)) {
658             if (resizeMode == QWasmWindow::ResizeNone && draggedWindow) {
659                 draggedWindow->setX(draggedWindow->x() + mouseEvent->movementX);
660                 draggedWindow->setY(draggedWindow->y() + mouseEvent->movementY);
661             }
662 
663             if (resizeMode != QWasmWindow::ResizeNone && !(htmlWindow->m_windowState & Qt::WindowFullScreen)) {
664                 QPoint delta = QPoint(mouseEvent->targetX, mouseEvent->targetY) - resizePoint;
665                 resizeWindow(draggedWindow, resizeMode, resizeStartRect, delta);
666             }
667         }
668         break;
669     }
670     default: // MOUSELEAVE MOUSEENTER
671         break;
672     };
673     if (!window2 && buttonEventType == QEvent::MouseButtonRelease) {
674         window2 = lastWindow;
675         lastWindow = nullptr;
676         interior = true;
677     }
678     if (window2 && interior) {
679         QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(
680             window2, getTimestamp(), localPoint, globalPoint, pressedButtons, button, buttonEventType, modifiers);
681     }
682 }
683 
focus_cb(int,const EmscriptenFocusEvent *,void *)684 int QWasmEventTranslator::focus_cb(int /*eventType*/, const EmscriptenFocusEvent */*focusEvent*/, void */*userData*/)
685 {
686     return 0;
687 }
688 
wheel_cb(int eventType,const EmscriptenWheelEvent * wheelEvent,void * userData)689 int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)
690 {
691     Q_UNUSED(eventType)
692 
693     QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(userData);
694     EmscriptenMouseEvent mouseEvent = wheelEvent->mouse;
695 
696     int scrollFactor = 0;
697     switch (wheelEvent->deltaMode) {
698     case DOM_DELTA_PIXEL://chrome safari
699         scrollFactor = 1;
700         break;
701     case DOM_DELTA_LINE: //firefox
702         scrollFactor = 12;
703         break;
704     case DOM_DELTA_PAGE:
705         scrollFactor = 20;
706         break;
707     };
708 
709     if (g_useNaturalScrolling) //macOS platform has document oriented scrolling
710         scrollFactor = -scrollFactor;
711 
712     QWasmEventTranslator *translator = (QWasmEventTranslator*)userData;
713     Qt::KeyboardModifiers modifiers = translator->translateMouseEventModifier(&mouseEvent);
714     QPoint targetPoint(mouseEvent.targetX, mouseEvent.targetY);
715     QPoint globalPoint = eventTranslator->screen()->geometry().topLeft() + targetPoint;
716 
717     QWindow *window2 = eventTranslator->screen()->compositor()->windowAt(globalPoint, 5);
718     if (!window2)
719         return 0;
720     QPoint localPoint = window2->mapFromGlobal(globalPoint);
721 
722     QPoint pixelDelta;
723 
724     if (wheelEvent->deltaY != 0) pixelDelta.setY(wheelEvent->deltaY * scrollFactor);
725     if (wheelEvent->deltaX != 0) pixelDelta.setX(wheelEvent->deltaX * scrollFactor);
726 
727     QWindowSystemInterface::handleWheelEvent(window2, getTimestamp(), localPoint,
728                                              globalPoint, QPoint(), pixelDelta, modifiers);
729     QWasmEventDispatcher::maintainTimers();
730 
731     return 1;
732 }
733 
touchCallback(int eventType,const EmscriptenTouchEvent * touchEvent,void * userData)734 int QWasmEventTranslator::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
735 {
736     auto translator = reinterpret_cast<QWasmEventTranslator*>(userData);
737     return translator->handleTouch(eventType, touchEvent);
738 }
739 
handleTouch(int eventType,const EmscriptenTouchEvent * touchEvent)740 int QWasmEventTranslator::handleTouch(int eventType, const EmscriptenTouchEvent *touchEvent)
741 {
742     QList<QWindowSystemInterface::TouchPoint> touchPointList;
743     touchPointList.reserve(touchEvent->numTouches);
744     QWindow *window2;
745 
746     for (int i = 0; i < touchEvent->numTouches; i++) {
747 
748         const EmscriptenTouchPoint *touches = &touchEvent->touches[i];
749 
750         QPoint targetPoint(touches->targetX, touches->targetY);
751         QPoint globalPoint = screen()->geometry().topLeft() + targetPoint;
752 
753         window2 = this->screen()->compositor()->windowAt(globalPoint, 5);
754         if (window2 == nullptr)
755             continue;
756 
757         QWindowSystemInterface::TouchPoint touchPoint;
758 
759         touchPoint.area = QRect(0, 0, 8, 8);
760         touchPoint.id = touches->identifier;
761         touchPoint.pressure = 1.0;
762 
763         touchPoint.area.moveCenter(globalPoint);
764 
765         const auto tp = pressedTouchIds.constFind(touchPoint.id);
766         if (tp != pressedTouchIds.constEnd())
767             touchPoint.normalPosition = tp.value();
768 
769         QPointF localPoint = QPointF(window2->mapFromGlobal(globalPoint));
770         QPointF normalPosition(localPoint.x() / window2->width(),
771                                localPoint.y() / window2->height());
772 
773         const bool stationaryTouchPoint = (normalPosition == touchPoint.normalPosition);
774         touchPoint.normalPosition = normalPosition;
775 
776         switch (eventType) {
777         case EMSCRIPTEN_EVENT_TOUCHSTART:
778             if (tp != pressedTouchIds.constEnd()) {
779                 touchPoint.state = (stationaryTouchPoint
780                                     ? Qt::TouchPointStationary
781                                     : Qt::TouchPointMoved);
782             } else {
783                 touchPoint.state = Qt::TouchPointPressed;
784             }
785             pressedTouchIds.insert(touchPoint.id, touchPoint.normalPosition);
786 
787             break;
788         case EMSCRIPTEN_EVENT_TOUCHEND:
789             touchPoint.state = Qt::TouchPointReleased;
790             pressedTouchIds.remove(touchPoint.id);
791             break;
792         case EMSCRIPTEN_EVENT_TOUCHMOVE:
793             touchPoint.state = (stationaryTouchPoint
794                                 ? Qt::TouchPointStationary
795                                 : Qt::TouchPointMoved);
796 
797             pressedTouchIds.insert(touchPoint.id, touchPoint.normalPosition);
798             break;
799         default:
800             break;
801         }
802 
803         touchPointList.append(touchPoint);
804     }
805 
806     QFlags<Qt::KeyboardModifier> keyModifier = translatKeyModifier(touchEvent);
807 
808     QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(
809                 window2, getTimestamp(), touchDevice, touchPointList, keyModifier);
810 
811     if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL)
812         QWindowSystemInterface::handleTouchCancelEvent(window2, getTimestamp(), touchDevice, keyModifier);
813 
814     QWasmEventDispatcher::maintainTimers();
815     return 1;
816 }
817 
getTimestamp()818 quint64 QWasmEventTranslator::getTimestamp()
819 {
820     return QDeadlineTimer::current().deadlineNSecs() / 1000;
821 }
822 
823 struct KeyMapping { Qt::Key from, to; };
824 
825 constexpr KeyMapping tildeKeyTable[] = { // ~
826     { Qt::Key_A, Qt::Key_Atilde },
827     { Qt::Key_N, Qt::Key_Ntilde },
828     { Qt::Key_O, Qt::Key_Otilde },
829 };
830 constexpr KeyMapping graveKeyTable[] = { // `
831     { Qt::Key_A, Qt::Key_Agrave },
832     { Qt::Key_E, Qt::Key_Egrave },
833     { Qt::Key_I, Qt::Key_Igrave },
834     { Qt::Key_O, Qt::Key_Ograve },
835     { Qt::Key_U, Qt::Key_Ugrave },
836 };
837 constexpr KeyMapping acuteKeyTable[] = { // '
838     { Qt::Key_A, Qt::Key_Aacute },
839     { Qt::Key_E, Qt::Key_Eacute },
840     { Qt::Key_I, Qt::Key_Iacute },
841     { Qt::Key_O, Qt::Key_Oacute },
842     { Qt::Key_U, Qt::Key_Uacute },
843     { Qt::Key_Y, Qt::Key_Yacute },
844 };
845 constexpr KeyMapping diaeresisKeyTable[] = { // umlaut ¨
846     { Qt::Key_A, Qt::Key_Adiaeresis },
847     { Qt::Key_E, Qt::Key_Ediaeresis },
848     { Qt::Key_I, Qt::Key_Idiaeresis },
849     { Qt::Key_O, Qt::Key_Odiaeresis },
850     { Qt::Key_U, Qt::Key_Udiaeresis },
851     { Qt::Key_Y, Qt::Key_ydiaeresis },
852 };
853 constexpr KeyMapping circumflexKeyTable[] = { // ^
854     { Qt::Key_A, Qt::Key_Acircumflex },
855     { Qt::Key_E, Qt::Key_Ecircumflex },
856     { Qt::Key_I, Qt::Key_Icircumflex },
857     { Qt::Key_O, Qt::Key_Ocircumflex },
858     { Qt::Key_U, Qt::Key_Ucircumflex },
859 };
860 
find_impl(const KeyMapping * first,const KeyMapping * last,Qt::Key key)861 static Qt::Key find_impl(const KeyMapping *first, const KeyMapping *last, Qt::Key key) noexcept
862 {
863     while (first != last) {
864         if (first->from == key)
865             return first->to;
866         ++first;
867     }
868     return Qt::Key_unknown;
869 }
870 
871 template <size_t N>
find(const KeyMapping (& map)[N],Qt::Key key)872 static Qt::Key find(const KeyMapping (&map)[N], Qt::Key key) noexcept
873 {
874     return find_impl(map, map + N, key);
875 }
876 
translateDeadKey(Qt::Key deadKey,Qt::Key accentBaseKey)877 Qt::Key QWasmEventTranslator::translateDeadKey(Qt::Key deadKey, Qt::Key accentBaseKey)
878 {
879     Qt::Key wasmKey = Qt::Key_unknown;
880 
881     if (deadKey == Qt::Key_QuoteLeft ) {
882         if (g_usePlatformMacSpecifics) { // ` macOS: Key_Dead_Grave
883             wasmKey = find(graveKeyTable, accentBaseKey);
884         } else {
885             wasmKey = find(diaeresisKeyTable, accentBaseKey);
886         }
887         return wasmKey;
888     }
889 
890     switch (deadKey) {
891     //    case Qt::Key_QuoteLeft:
892     case Qt::Key_O: // ´ Key_Dead_Grave
893         wasmKey = find(graveKeyTable, accentBaseKey);
894         break;
895     case Qt::Key_E: // ´ Key_Dead_Acute
896         wasmKey = find(acuteKeyTable, accentBaseKey);
897         break;
898     case Qt::Key_AsciiTilde:
899     case Qt::Key_N:// Key_Dead_Tilde
900         wasmKey = find(tildeKeyTable, accentBaseKey);
901         break;
902     case Qt::Key_U:// ¨ Key_Dead_Diaeresis
903         wasmKey = find(diaeresisKeyTable, accentBaseKey);
904         break;
905     case Qt::Key_I:// macOS Key_Dead_Circumflex
906     case Qt::Key_6:// linux
907     case Qt::Key_Apostrophe:// linux
908         wasmKey = find(circumflexKeyTable, accentBaseKey);
909         break;
910     default:
911         break;
912 
913     };
914     return wasmKey;
915 }
916 
processKeyboard(int eventType,const EmscriptenKeyboardEvent * keyEvent)917 bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboardEvent *keyEvent)
918 {
919     Qt::Key qtKey = translateEmscriptKey(keyEvent);
920 
921     Qt::KeyboardModifiers modifiers = translateKeyboardEventModifier(keyEvent);
922 
923     QString keyText;
924     QEvent::Type keyType = QEvent::None;
925     switch (eventType) {
926     case EMSCRIPTEN_EVENT_KEYPRESS:
927     case EMSCRIPTEN_EVENT_KEYDOWN: // down
928         keyType = QEvent::KeyPress;
929 
930         if (m_emDeadKey != Qt::Key_unknown) {
931 
932             Qt::Key transformedKey = translateDeadKey(m_emDeadKey, qtKey);
933 
934             if (transformedKey != Qt::Key_unknown)
935                 qtKey = transformedKey;
936 
937             if (keyEvent->shiftKey == 0) {
938                 for (auto it = KeyTbl.cbegin(); it != KeyTbl.end(); ++it) {
939                     if (it != KeyTbl.end() && (qtKey == static_cast<Qt::Key>(it->qt))) {
940                         keyText = it->em;
941                         m_emDeadKey = Qt::Key_unknown;
942                         break;
943                     }
944                 }
945             } else {
946                 for (auto it = DeadKeyShiftTbl.cbegin(); it != DeadKeyShiftTbl.end(); ++it) {
947                     if (it != DeadKeyShiftTbl.end() && (qtKey == static_cast<Qt::Key>(it->qt))) {
948                         keyText = it->em;
949                         m_emDeadKey = Qt::Key_unknown;
950                         break;
951                     }
952                 }
953             }
954         }
955         if (qstrncmp(keyEvent->key, "Dead", 4) == 0 || qtKey == Qt::Key_AltGr) {
956             qtKey = translateEmscriptKey(keyEvent);
957             m_emStickyDeadKey = true;
958             if (keyEvent->shiftKey == 1 && qtKey == Qt::Key_QuoteLeft)
959                 qtKey = Qt::Key_AsciiTilde;
960             m_emDeadKey = qtKey;
961         }
962         break;
963     case EMSCRIPTEN_EVENT_KEYUP: // up
964         keyType = QEvent::KeyRelease;
965         if (m_emStickyDeadKey && qtKey != Qt::Key_Alt) {
966             m_emStickyDeadKey = false;
967         }
968         break;
969     default:
970         break;
971     };
972 
973     if (keyType == QEvent::None)
974         return 0;
975 
976     QFlags<Qt::KeyboardModifier> mods = translateKeyboardEventModifier(keyEvent);
977 
978     // Clipboard fallback path: cut/copy/paste are handled by clipboard event
979     // handlers if direct clipboard access is not available.
980     if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi && modifiers & Qt::ControlModifier &&
981         (qtKey == Qt::Key_X || qtKey == Qt::Key_C || qtKey == Qt::Key_V)) {
982             return 0;
983     }
984 
985     bool accepted = false;
986 
987     if (keyType == QEvent::KeyPress &&
988             mods.testFlag(Qt::ControlModifier)
989             && qtKey == Qt::Key_V) {
990         QWasmIntegration::get()->getWasmClipboard()->readTextFromClipboard();
991     } else {
992         if (keyText.isEmpty())
993             keyText = QString(keyEvent->key);
994         if (keyText.size() > 1)
995             keyText.clear();
996         accepted = QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
997                     0, keyType, qtKey, modifiers, keyText);
998     }
999     if (keyType == QEvent::KeyPress &&
1000             mods.testFlag(Qt::ControlModifier)
1001             && qtKey == Qt::Key_C) {
1002         QWasmIntegration::get()->getWasmClipboard()->writeTextToClipboard();
1003     }
1004 
1005     QWasmEventDispatcher::maintainTimers();
1006 
1007     return accepted;
1008 }
1009 
cursorForMode(QWasmWindow::ResizeMode m)1010 QCursor QWasmEventTranslator::cursorForMode(QWasmWindow::ResizeMode m)
1011 {
1012     switch (m) {
1013     case QWasmWindow::ResizeTopLeft:
1014     case QWasmWindow::ResizeBottomRight:
1015         return Qt::SizeFDiagCursor;
1016         break;
1017     case QWasmWindow::ResizeBottomLeft:
1018     case QWasmWindow::ResizeTopRight:
1019         return Qt::SizeBDiagCursor;
1020         break;
1021     case QWasmWindow::ResizeTop:
1022     case QWasmWindow::ResizeBottom:
1023         return Qt::SizeVerCursor;
1024         break;
1025     case QWasmWindow::ResizeLeft:
1026     case QWasmWindow::ResizeRight:
1027         return Qt::SizeHorCursor;
1028         break;
1029     }
1030     return Qt::ArrowCursor;
1031 }
1032 
1033 QT_END_NAMESPACE
1034