1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt WebGL module 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 "qwebglintegration.h"
31#include "qwebglintegration_p.h"
32
33#include "qwebglwindow.h"
34#include "qwebglcontext.h"
35#include "qwebglcontext.h"
36#include "qwebglhttpserver.h"
37#include "qwebglwebsocketserver.h"
38#include "qwebglplatformservices.h"
39
40#include <QtCore/qjsonarray.h>
41#include <QtCore/qjsondocument.h>
42#include <QtCore/qjsonobject.h>
43#include <QtCore/qloggingcategory.h>
44#include <QtCore/qtextstream.h>
45#include <QtCore/qtimer.h>
46#include <QtCore/qstring.h>
47#include <QtGui/qclipboard.h>
48#include <QtGui/qscreen.h>
49#include <QtGui/qwindow.h>
50#include <QtGui/qsurfaceformat.h>
51#include <QtGui/qopenglcontext.h>
52#include <QtGui/qoffscreensurface.h>
53#include <QtGui/qpa/qplatformwindow.h>
54#include <QtGui/private/qguiapplication_p.h>
55#include <QtGui/qpa/qwindowsysteminterface.h>
56#include <QtThemeSupport/private/qgenericunixthemes_p.h>
57#include <QtWebSockets/qwebsocket.h>
58
59#if defined(QT_QUICK_LIB)
60#include <QtQuick/qquickwindow.h>
61#endif
62
63QT_BEGIN_NAMESPACE
64
65Q_LOGGING_CATEGORY(lcWebGL, "qt.qpa.webgl")
66
67QWebGLIntegrationPrivate *QWebGLIntegrationPrivate::instance()
68{
69 auto platformIntegration = QGuiApplicationPrivate::instance()->platformIntegration();
70 return static_cast<QWebGLIntegration *>(platformIntegration)->d_ptr.data();
71}
72
73QWebGLIntegration::QWebGLIntegration(quint16 port, quint16 wssport) :
74 d_ptr(new QWebGLIntegrationPrivate)
75{
76 Q_D(QWebGLIntegration);
77 d->q_ptr = this;
78 d->httpPort = port;
79 d->wssPort = wssport;
80 d->touchDevice = new QTouchDevice;
81 d->touchDevice->setName("EmulatedTouchDevice");
82 d->touchDevice->setType(QTouchDevice::TouchScreen);
83 d->touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::Pressure |
84 QTouchDevice::MouseEmulation);
85 d->touchDevice->setMaximumTouchPoints(6);
86 QWindowSystemInterface::registerTouchDevice(device: d->touchDevice);
87
88 qCDebug(lcWebGL, "WebGL QPA Plugin created");
89 qRegisterMetaType<QWebSocket *>(typeName: "QWebSocket *");
90 qRegisterMetaType<QWebGLWebSocketServer::MessageType>(typeName: "QWebGLWebSocketServer::MessageType");
91}
92
93QWebGLIntegration::~QWebGLIntegration()
94{
95 Q_D(QWebGLIntegration);
96 QWindowSystemInterface::unregisterTouchDevice(device: d->touchDevice);
97}
98
99QWebGLIntegration *QWebGLIntegration::instance()
100{
101 return static_cast<QWebGLIntegration *>(qGuiApp->platformNativeInterface());
102}
103
104void QWebGLIntegration::initialize()
105{
106 Q_D(QWebGLIntegration);
107
108#if defined(QT_QUICK_LIB)
109 qputenv(varName: "QSG_RENDER_LOOP", value: "threaded"); // Force threaded QSG_RENDER_LOOP
110#endif
111
112 d->inputContext = QPlatformInputContextFactory::create();
113 d->screen = new QWebGLScreen;
114 QWindowSystemInterface::handleScreenAdded(screen: d->screen, isPrimary: true);
115
116 d->webSocketServer = new QWebGLWebSocketServer(d->wssPort);
117 d->httpServer = new QWebGLHttpServer(d->webSocketServer, this);
118 bool ok = d->httpServer->listen(address: QHostAddress::Any, port: d->httpPort);
119 if (!ok) {
120 qFatal(msg: "QWebGLIntegration::initialize: Failed to initialize: %s",
121 qPrintable(d->httpServer->errorString()));
122 }
123 d->webSocketServerThread = new QThread(this);
124 d->webSocketServerThread->setObjectName("WebSocketServer");
125 d->webSocketServer->moveToThread(thread: d->webSocketServerThread);
126 connect(sender: d->webSocketServerThread, signal: &QThread::finished,
127 receiver: d->webSocketServer, slot: &QObject::deleteLater);
128 QMetaObject::invokeMethod(obj: d->webSocketServer, member: "create", type: Qt::QueuedConnection);
129 QMutexLocker lock(d->webSocketServer->mutex());
130 d->webSocketServerThread->start();
131 d->webSocketServer->waitCondition()->wait(lockedMutex: d->webSocketServer->mutex());
132
133 qGuiApp->setQuitOnLastWindowClosed(false);
134}
135
136void QWebGLIntegration::destroy()
137{
138 Q_D(QWebGLIntegration);
139 const auto tlws = qGuiApp->topLevelWindows();
140 for (QWindow *w : tlws)
141 w->destroy();
142
143 QWindowSystemInterface::handleScreenRemoved(screen: d->screen);
144
145 d->screen = nullptr;
146
147 d->webSocketServerThread->quit();
148 d->webSocketServerThread->wait();
149 delete d->webSocketServerThread;
150}
151
152QAbstractEventDispatcher *QWebGLIntegration::createEventDispatcher() const
153{
154#ifdef Q_OS_WIN
155 return new QWindowsGuiEventDispatcher;
156#else
157 return createUnixEventDispatcher();
158#endif // Q_OS_WIN
159}
160
161QPlatformServices *QWebGLIntegration::services() const
162{
163 Q_D(const QWebGLIntegration);
164 return &d->services;
165}
166
167QPlatformInputContext *QWebGLIntegration::inputContext() const
168{
169 Q_D(const QWebGLIntegration);
170 return d->inputContext;
171}
172
173QPlatformFontDatabase *QWebGLIntegration::fontDatabase() const
174{
175 Q_D(const QWebGLIntegration);
176 return &d->fontDatabase;
177}
178
179QPlatformTheme *QWebGLIntegration::createPlatformTheme(const QString &name) const
180{
181#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
182 return QPlatformIntegration::createPlatformTheme(name);
183#else
184 return QGenericUnixTheme::createUnixTheme(name);
185#endif // Q_OS_WIN
186}
187
188QPlatformBackingStore *QWebGLIntegration::createPlatformBackingStore(QWindow *window) const
189{
190 Q_UNUSED(window);
191 qCCritical(lcWebGL, "WebGL QPA platform plugin: Raster surfaces are not supported");
192 return nullptr;
193}
194
195QPlatformWindow *QWebGLIntegration::createPlatformWindow(QWindow *window) const
196{
197 Q_D(const QWebGLIntegration);
198 qCDebug(lcWebGL, "Creating platform window for: %p", window);
199
200#if defined(QT_QUICK_LIB)
201 if (window->inherits(classname: "QQuickWindow")) {
202 auto quickWindow = (QQuickWindow *)window;
203 quickWindow->setPersistentSceneGraph(false);
204 quickWindow->setPersistentOpenGLContext(false);
205 }
206#endif
207
208 d->windows.append(t: window);
209 QObject::connect(sender: window, signal: &QWindow::destroyed, slot: [=] ()
210 {
211 d->windows.removeAll(t: window);
212 });
213
214 QWindowSystemInterface::flushWindowSystemEvents();
215
216 QWebGLWindow *platformWindow = nullptr;
217 QWebSocket *socket = nullptr;
218 auto winId = WId(-1);
219 {
220 QMutexLocker locker(&d->clients.mutex);
221
222 if (d->clients.list.isEmpty()) {
223 QMetaObject::invokeMethod(obj: window, member: "close", type: Qt::QueuedConnection);
224 return new QWebGLWindow(window);
225 }
226
227 auto client = &d->clients.list.first();
228 window->setScreen(client->platformScreen->screen());
229 client->platformWindows.append(t: new QWebGLWindow(window));
230 platformWindow = client->platformWindows.last();
231 socket = client->socket;
232 platformWindow->create();
233 platformWindow->requestActivateWindow();
234 winId = platformWindow->winId();
235 }
236
237 const QVariantMap values {
238 { "x", platformWindow->geometry().x() },
239 { "y", platformWindow->geometry().y() },
240 { "width", platformWindow->geometry().width() },
241 { "height", platformWindow->geometry().height() },
242 { "winId", winId },
243 { "title", qApp->applicationDisplayName() }
244 };
245 d->sendMessage(socket, type: QWebGLWebSocketServer::MessageType::CreateCanvas, values);
246
247 QObject::connect(sender: window, signal: &QWindow::windowTitleChanged, slot: [=](const QString &title)
248 {
249 const QVariantMap values{{ "title", title }, { "winId", winId }};
250 d->sendMessage(socket, type: QWebGLWebSocketServer::MessageType::ChangeTitle, values);
251 });
252 qCDebug(lcWebGL, "Created platform window %p for: %p", platformWindow, window);
253 return platformWindow;
254}
255
256QPlatformOffscreenSurface *QWebGLIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const
257{
258 qCDebug(lcWebGL, "New offscreen surface %p", surface);
259 return new QWebGLOffscreenSurface(surface);
260}
261
262QPlatformOpenGLContext *QWebGLIntegration::createPlatformOpenGLContext(QOpenGLContext *context)
263 const
264{
265 qCDebug(lcWebGL, "%p", context);
266 QVariant nativeHandle = context->nativeHandle();
267
268 const QSurfaceFormat adjustedFormat = context->format();
269 QWebGLContext *ctx = new QWebGLContext(adjustedFormat);
270 context->setNativeHandle(nativeHandle);
271 return ctx;
272}
273
274bool QWebGLIntegration::hasCapability(QPlatformIntegration::Capability cap) const
275{
276 switch (cap) {
277 case OpenGL:
278 case ThreadedOpenGL:
279 case ThreadedPixmaps:
280 return true;
281 default:
282 return false;
283 }
284}
285
286QPlatformNativeInterface *QWebGLIntegration::nativeInterface() const
287{
288 return const_cast<QWebGLIntegration *>(this);
289}
290
291void QWebGLIntegration::openUrl(const QUrl &url)
292{
293 Q_D(QWebGLIntegration);
294 qCDebug(lcWebGL, "%s", qPrintable(url.toString()));
295 QMutexLocker locker(&d->clients.mutex);
296 for (auto &clientData : d->clients.list) {
297 const QVariantMap values {
298 { "url", url }
299 };
300 d->sendMessage(socket: clientData.socket, type: QWebGLWebSocketServer::MessageType::OpenUrl, values);
301 }
302}
303
304QWebGLIntegrationPrivate::ClientData *QWebGLIntegrationPrivate::findClientData(
305 const QWebSocket *socket)
306{
307 QMutexLocker locker(&clients.mutex);
308 auto it = std::find_if(first: clients.list.begin(), last: clients.list.end(), pred: [=](const ClientData &data)
309 {
310 return data.socket == socket;
311 });
312
313 return it != clients.list.end() ? &*it : nullptr;
314}
315
316QWebGLIntegrationPrivate::ClientData *QWebGLIntegrationPrivate::findClientData(
317 const QPlatformSurface *surface)
318{
319 QMutexLocker locker(&clients.mutex);
320 auto it = std::find_if(first: clients.list.begin(), last: clients.list.end(), pred: [=](const ClientData &data)
321 {
322 if (!data.platformWindows.isEmpty() && data.platformWindows.last()->surface())
323 return surface == data.platformWindows.last()->surface()->surfaceHandle();
324 return false;
325 });
326 return it != clients.list.end() ? &*it : nullptr;
327}
328
329QWebGLWindow *QWebGLIntegrationPrivate::findWindow(const ClientData &clientData, WId winId)
330{
331 auto &windows = clientData.platformWindows;
332 auto it = std::find_if(first: windows.begin(), last: windows.end(), pred: [winId](QWebGLWindow *window)
333 {
334 return window->winId() == winId;
335 });
336 Q_ASSERT(it != windows.end());
337 return *it;
338}
339
340void QWebGLIntegrationPrivate::clientConnected(QWebSocket *socket,
341 const int width,
342 const int height,
343 const double physicalWidth,
344 const double physicalHeight)
345{
346 qCDebug(lcWebGL, "%p, Size: %dx%d. Physical Size: %fx%f",
347 socket, width, height, physicalWidth, physicalHeight);
348 QWebGLIntegrationPrivate::ClientData client;
349 client.socket = socket;
350 client.platformScreen = new QWebGLScreen(QSize(width, height),
351 QSizeF(physicalWidth, physicalHeight));
352 clients.mutex.lock();
353 clients.list.append(t: client);
354 clients.mutex.unlock();
355 QWindowSystemInterface::handleScreenAdded(screen: client.platformScreen, isPrimary: true);
356 connectNextClient();
357}
358
359void QWebGLIntegrationPrivate::clientDisconnected(QWebSocket *socket)
360{
361 qCDebug(lcWebGL, "%p", socket);
362 const auto predicate = [=](const QWebGLIntegrationPrivate::ClientData &item)
363 {
364 return socket == item.socket;
365 };
366
367 clients.mutex.lock();
368 auto it = std::find_if(first: clients.list.begin(), last: clients.list.end(), pred: predicate);
369 if (it != clients.list.end()) {
370 for (auto platformWindow : it->platformWindows) {
371 auto window = platformWindow->window();
372 QTimer::singleShot(interval: 0, receiver: window, slot: &QWindow::close);
373 }
374 clients.list.erase(it);
375 }
376 clients.mutex.unlock();
377 connectNextClient();
378}
379
380void QWebGLIntegrationPrivate::connectNextClient()
381{
382 static QMutex connecting;
383 if (connecting.tryLock()) {
384 QTimer::singleShot(interval: 1000, slot: [=]() {
385 clients.mutex.lock();
386 if (!clients.list.isEmpty()) {
387 const auto clientData = clients.list.first();
388 qCDebug(lcWebGL, "Connecting first client in the queue (%p)",
389 clientData.socket);
390 for (auto window : windows)
391 QMetaObject::invokeMethod(obj: window, member: "showFullScreen", type: Qt::QueuedConnection);
392 }
393 clients.mutex.unlock();
394 connecting.unlock();
395 });
396 };
397}
398
399void QWebGLIntegrationPrivate::sendMessage(QWebSocket *socket,
400 QWebGLWebSocketServer::MessageType type,
401 const QVariantMap &values) const
402{
403 const auto ok = QMetaObject::invokeMethod(obj: webSocketServer, member: "sendMessage",
404 Q_ARG(QWebSocket*, socket),
405 Q_ARG(QWebGLWebSocketServer::MessageType, type),
406 Q_ARG(QVariantMap, values));
407#if defined(QT_DEBUG)
408 Q_ASSERT(ok);
409#else
410 Q_UNUSED(ok);
411#endif
412}
413
414void QWebGLIntegrationPrivate::onTextMessageReceived(QWebSocket *socket, const QString &message)
415{
416 QJsonParseError parseError;
417 const auto document = QJsonDocument::fromJson(json: message.toUtf8(), error: &parseError);
418 Q_ASSERT(parseError.error == QJsonParseError::NoError);
419 Q_ASSERT(document.isObject());
420 const auto object = document.object();
421 Q_ASSERT(object.contains("type"));
422 const auto type = object[QStringLiteral("type")].toString();
423
424 auto integrationPrivate = QWebGLIntegrationPrivate::instance();
425 const auto clientData = integrationPrivate->findClientData(socket);
426
427 if (type == QStringLiteral("connect"))
428 clientConnected(socket, width: object["width"].toInt(), height: object["height"].toInt(),
429 physicalWidth: object["physicalWidth"].toDouble(), physicalHeight: object["physicalHeight"].toDouble());
430 else if (!clientData || clientData->platformWindows.isEmpty())
431 qCWarning(lcWebGL, "Message received before connect %s", qPrintable(message));
432 else if (type == QStringLiteral("default_context_parameters"))
433 handleDefaultContextParameters(clientData: *clientData, object);
434 else if (type == QStringLiteral("gl_response"))
435 handleGlResponse(object);
436 else if (type == QStringLiteral("mouse"))
437 handleMouse(clientData: *clientData, object);
438 else if (type == QStringLiteral("wheel"))
439 handleWheel(clientData: *clientData, object);
440 else if (type == QStringLiteral("touch"))
441 handleTouch(clientData: *clientData, object);
442 else if (type.startsWith(s: "key"))
443 handleKeyboard(clientData: *clientData, type, object);
444 else if (type == QStringLiteral("canvas_resize"))
445 handleCanvasResize(clientData: *clientData, object);
446}
447
448void QWebGLIntegrationPrivate::handleDefaultContextParameters(const ClientData &clientData,
449 const QJsonObject &object)
450{
451 const auto winId = object.value(key: "name").toInt(defaultValue: -1);
452 Q_ASSERT(winId != -1);
453 QWebGLWindow *platformWindow = findWindow(clientData, winId);
454 Q_ASSERT(platformWindow);
455 auto data = object.toVariantMap();
456 data.remove(akey: "name");
457 data.remove(akey: "type");
458 QMap<GLenum, QVariant> result;
459 for (auto it = data.cbegin(), end = data.cend(); it != end; ++it)
460 result.insert(akey: it.key().toInt(), avalue: *it);
461 platformWindow->setDefaults(result);
462}
463
464void QWebGLIntegrationPrivate::handleGlResponse(const QJsonObject &object)
465{
466 qCDebug(lcWebGL, ) << "gl_response message received" << object;
467 QMutexLocker locker(&waitMutex);
468 const auto id = object["id"];
469 const auto value = object["value"].toVariant();
470 Q_ASSERT(pendingResponses.contains(id.toInt()));
471 receivedResponses.insert(akey: id.toInt(), avalue: value);
472 pendingResponses.removeOne(t: id.toInt());
473 waitCondition.wakeAll();
474}
475
476void QWebGLIntegrationPrivate::handleCanvasResize(const ClientData &clientData,
477 const QJsonObject &object)
478{
479 qCDebug(lcWebGL, ) << "canvas_resize message received" << object;
480 const auto width = object["width"].toInt();
481 const auto height = object["height"].toInt();
482 const auto physicalWidth = object["physicalWidth"].toDouble();
483 const auto physicalHeight = object["physicalHeight"].toDouble();
484 clientData.platformScreen->setGeometry(width, height, physicalWidth, physicalHeight);
485}
486
487void QWebGLIntegrationPrivate::handleMouse(const ClientData &clientData, const QJsonObject &object)
488{
489 const auto winId = object.value(key: "name").toInt(defaultValue: -1);
490 Q_ASSERT(winId != -1);
491 QPointF localPos(object.value(key: "layerX").toDouble(),
492 object.value(key: "layerY").toDouble());
493 QPointF globalPos(object.value(key: "clientX").toDouble(),
494 object.value(key: "clientY").toDouble());
495 auto buttons = static_cast<Qt::MouseButtons>(object.value(key: "buttons").toInt());
496 auto time = object.value(key: "time").toString();
497 auto platformWindow = findWindow(clientData, winId);
498 QWindowSystemInterface::handleMouseEvent(window: platformWindow->window(),
499 timestamp: time.toULong(),
500 local: localPos,
501 global: globalPos,
502 state: Qt::MouseButtons(buttons),
503 button: Qt::NoButton,
504 type: QEvent::None,
505 mods: Qt::NoModifier,
506 source: Qt::MouseEventNotSynthesized);
507}
508
509void QWebGLIntegrationPrivate::handleWheel(const ClientData &clientData, const QJsonObject &object)
510{
511 const auto winId = object.value(key: "name").toInt(defaultValue: -1);
512 Q_ASSERT(winId != -1);
513 auto platformWindow = findWindow(clientData, winId);
514 auto time = object.value(key: "time").toDouble();
515 QPointF localPos(object.value(key: "layerX").toDouble(),
516 object.value(key: "layerY").toDouble());
517 QPointF globalPos(object.value(key: "clientX").toDouble(),
518 object.value(key: "clientY").toDouble());
519 const int deltaX = -object.value(key: "deltaX").toInt(defaultValue: 0);
520 const int deltaY = -object.value(key: "deltaY").toInt(defaultValue: 0);
521 auto orientation = deltaY != 0 ? Qt::Vertical : Qt::Horizontal;
522
523 QPoint point = (orientation == Qt::Vertical) ? QPoint(0, deltaY) : QPoint(deltaX, 0);
524 QWindowSystemInterface::handleWheelEvent(window: platformWindow->window(),
525 timestamp: time,
526 local: localPos,
527 global: globalPos,
528 pixelDelta: QPoint(),
529 angleDelta: point,
530 mods: Qt::NoModifier);
531}
532
533void QWebGLIntegrationPrivate::handleTouch(const ClientData &clientData, const QJsonObject &object)
534{
535 const auto winId = object.value(key: "name").toInt(defaultValue: -1);
536 Q_ASSERT(winId != -1);
537 auto window = findWindow(clientData, winId)->window();
538 const auto time = object.value(key: "time").toString();
539 const auto eventType = object.value(key: "event").toString();
540 if (eventType == QStringLiteral("touchcancel")) {
541 QWindowSystemInterface::handleTouchCancelEvent(window,
542 timestamp: time.toULong(),
543 device: touchDevice,
544 mods: Qt::NoModifier);
545 } else {
546 QList<QWindowSystemInterface::TouchPoint> points;
547 auto touchToPoint = [](const QJsonValue &touch) -> QWindowSystemInterface::TouchPoint {
548 QWindowSystemInterface::TouchPoint point; // support more than one
549 const auto pageX = touch.toObject().value(key: "pageX").toDouble();
550 const auto pageY = touch.toObject().value(key: "pageY").toDouble();
551 const auto radiusX = touch.toObject().value(key: "radiusX").toDouble();
552 const auto radiusY = touch.toObject().value(key: "radiusY").toDouble();
553 const auto clientX = touch.toObject().value(key: "clientX").toDouble();
554 const auto clientY = touch.toObject().value(key: "clientY").toDouble();
555 point.id = touch.toObject().value(key: "identifier").toInt(defaultValue: 0);
556 point.pressure = touch.toObject().value(key: "force").toDouble(defaultValue: 1.);
557 point.area.setX(pageX - radiusX);
558 point.area.setY(pageY - radiusY);
559 point.area.setWidth(radiusX * 2);
560 point.area.setHeight(radiusY * 2);
561 point.normalPosition.setX(touch.toObject().value(key: "normalPositionX").toDouble());
562 point.normalPosition.setY(touch.toObject().value(key: "normalPositionY").toDouble());
563 point.rawPositions = {{ clientX, clientY }};
564 return point;
565 };
566
567 for (const auto &touch : object.value(key: "changedTouches").toArray()) {
568 auto point = touchToPoint(touch);
569 if (eventType == QStringLiteral("touchstart")) {
570 point.state = Qt::TouchPointPressed;
571 } else if (eventType == QStringLiteral("touchend")) {
572 qCDebug(lcWebGL, ) << "end" << object;
573 point.state = Qt::TouchPointReleased;
574 } else {
575 Q_ASSERT(eventType == QStringLiteral("touchmove"));
576 point.state = Qt::TouchPointMoved;
577 }
578 points.append(t: point);
579 }
580
581 for (const auto &touch : object.value(key: "stationaryTouches").toArray()) {
582 auto point = touchToPoint(touch);
583 point.state = Qt::TouchPointStationary;
584 points.append(t: point);
585 }
586
587 QWindowSystemInterface::handleTouchEvent(window,
588 timestamp: time.toULong(),
589 device: touchDevice,
590 points,
591 mods: Qt::NoModifier);
592 }
593}
594
595void QWebGLIntegrationPrivate::handleKeyboard(const ClientData &clientData,
596 const QString &type,
597 const QJsonObject &object)
598{
599 const QHash<QString, Qt::Key> keyMap {
600 { "Alt", Qt::Key_Alt },
601 { "ArrowDown", Qt::Key_Down },
602 { "ArrowLeft", Qt::Key_Left },
603 { "ArrowRight", Qt::Key_Right },
604 { "ArrowUp", Qt::Key_Up },
605 { "Backspace", Qt::Key_Backspace },
606 { "Control", Qt::Key_Control },
607 { "Delete", Qt::Key_Delete },
608 { "End", Qt::Key_End },
609 { "Enter", Qt::Key_Enter },
610 { "F1", Qt::Key_F1 },
611 { "F2", Qt::Key_F2 },
612 { "F3", Qt::Key_F3 },
613 { "F4", Qt::Key_F4 },
614 { "F5", Qt::Key_F5 },
615 { "F6", Qt::Key_F6 },
616 { "F7", Qt::Key_F7 },
617 { "F8", Qt::Key_F8 },
618 { "F9", Qt::Key_F9 },
619 { "F10", Qt::Key_F10 },
620 { "F11", Qt::Key_F11 },
621 { "F12", Qt::Key_F12 },
622 { "Escape", Qt::Key_Escape },
623 { "Home", Qt::Key_Home },
624 { "Insert", Qt::Key_Insert },
625 { "Meta", Qt::Key_Meta },
626 { "PageDown", Qt::Key_PageDown },
627 { "PageUp", Qt::Key_PageUp },
628 { "Shift", Qt::Key_Shift },
629 { "Space", Qt::Key_Space },
630 { "AltGraph", Qt::Key_AltGr },
631 { "Tab", Qt::Key_Tab },
632 { "Unidentified", Qt::Key_F },
633 { "OS", Qt::Key_Super_L }
634 };
635 const auto timestamp = static_cast<ulong>(object.value(key: "time").toDouble(defaultValue: -1));
636 const auto keyName = object.value(key: "key").toString();
637 const auto specialKey = keyMap.find(akey: keyName);
638 QEvent::Type eventType;
639 if (type == QStringLiteral("keydown"))
640 eventType = QEvent::KeyPress;
641 else if (type == QStringLiteral("keyup"))
642 eventType = QEvent::KeyRelease;
643 else
644 return;
645 QString string(object.value(key: "key").toString());
646 int key = object.value(key: "which").toInt(defaultValue: 0);
647 if (specialKey != keyMap.end()) {
648 key = *specialKey;
649 string.clear();
650
651 // special case: match Qt's behavior on other platforms and differentiate:
652 // * "Enter": Qt::Key_Return
653 // * "NumpadEnter": Qt::Key_Enter
654 // TODO: consider whether "code" could be used rather than "keyName" above
655 if (key == Qt::Key_Enter && object.value(key: "code").toString() == QStringLiteral("Enter"))
656 key = Qt::Key_Return;
657 }
658
659 const auto window = clientData.platformWindows.last()->window();
660 QWindowSystemInterface::handleKeyEvent(window,
661 timestamp,
662 t: eventType,
663 k: key,
664 mods: convertKeyboardModifiers(object),
665 text: string);
666}
667
668Qt::KeyboardModifiers QWebGLIntegrationPrivate::convertKeyboardModifiers(const QJsonObject &object)
669{
670 Qt::KeyboardModifiers modifiers = Qt::NoModifier;
671 if (object.value(key: "ctrlKey").toBool())
672 modifiers |= Qt::ControlModifier;
673 if (object.value(key: "shiftKey").toBool())
674 modifiers |= Qt::ShiftModifier;
675 if (object.value(key: "altKey").toBool())
676 modifiers |= Qt::AltModifier;
677 if (object.value(key: "metaKey").toBool())
678 modifiers |= Qt::MetaModifier;
679 return modifiers;
680}
681
682QT_END_NAMESPACE
683

source code of qtwebglplugin/src/plugins/platforms/webgl/qwebglintegration.cpp