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