summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.json5
-rw-r--r--configure.pri11
-rw-r--r--mkspecs/features/toolchain.prf4
-rw-r--r--mkspecs/features/wasm/qt.prf12
-rw-r--r--mkspecs/features/wasm/wasm.prf81
-rw-r--r--mkspecs/wasm-emscripten/qmake.conf90
-rw-r--r--mkspecs/wasm-emscripten/qplatformdefs.h181
-rw-r--r--src/3rdparty/double-conversion/include/double-conversion/utils.h2
-rw-r--r--src/3rdparty/wasm/DejaVuSans.ttfbin0 -> 493564 bytes
-rw-r--r--src/3rdparty/wasm/LICENSE15
-rw-r--r--src/3rdparty/wasm/Vera.ttfbin0 -> 65932 bytes
-rw-r--r--src/3rdparty/wasm/qt_attribution.json21
-rw-r--r--src/corelib/codecs/qtextcodec_p.h2
-rw-r--r--src/corelib/configure.json4
-rw-r--r--src/corelib/global/archdetect.cpp2
-rw-r--r--src/corelib/global/qcompilerdetection.h3
-rw-r--r--src/corelib/global/qlogging.cpp37
-rw-r--r--src/corelib/global/qprocessordetection.h6
-rw-r--r--src/corelib/global/qsystemdetection.h2
-rw-r--r--src/corelib/io/qfilesystemengine_unix.cpp4
-rw-r--r--src/corelib/io/qsettings.cpp11
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp22
-rw-r--r--src/corelib/kernel/qeventdispatcher_unix.cpp2
-rw-r--r--src/corelib/kernel/qeventloop.cpp24
-rw-r--r--src/corelib/mimetypes/qmimedatabase.cpp2
-rw-r--r--src/corelib/tools/qsimd.cpp17
-rw-r--r--src/corelib/tools/qunicodetables_p.h6
-rw-r--r--src/corelib/tools/tools.pri2
-rw-r--r--src/gui/configure.json10
-rw-r--r--src/gui/configure.pri1
-rw-r--r--src/gui/kernel/qguiapplication.cpp12
-rw-r--r--src/gui/kernel/qopenglcontext.cpp3
-rw-r--r--src/gui/opengl/qopenglframebufferobject.cpp45
-rw-r--r--src/network/access/access.pri7
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp13
-rw-r--r--src/network/access/qnetworkaccessmanager.h3
-rw-r--r--src/network/access/qnetworkreplywasmimpl.cpp640
-rw-r--r--src/network/access/qnetworkreplywasmimpl_p.h153
-rw-r--r--src/network/configure.json3
-rw-r--r--src/opengl/qglframebufferobject.cpp22
-rw-r--r--src/plugins/platforms/platforms.pro2
-rw-r--r--src/plugins/platforms/wasm/main.cpp54
-rw-r--r--src/plugins/platforms/wasm/qtloader.js516
-rw-r--r--src/plugins/platforms/wasm/qtlogo.svg67
-rw-r--r--src/plugins/platforms/wasm/qwasmbackingstore.cpp165
-rw-r--r--src/plugins/platforms/wasm/qwasmbackingstore.h70
-rw-r--r--src/plugins/platforms/wasm/qwasmcompositor.cpp721
-rw-r--r--src/plugins/platforms/wasm/qwasmcompositor.h169
-rw-r--r--src/plugins/platforms/wasm/qwasmcursor.cpp131
-rw-r--r--src/plugins/platforms/wasm/qwasmcursor.h43
-rw-r--r--src/plugins/platforms/wasm/qwasmeventdispatcher.cpp181
-rw-r--r--src/plugins/platforms/wasm/qwasmeventdispatcher.h64
-rw-r--r--src/plugins/platforms/wasm/qwasmeventtranslator.cpp522
-rw-r--r--src/plugins/platforms/wasm/qwasmeventtranslator.h210
-rw-r--r--src/plugins/platforms/wasm/qwasmfontdatabase.cpp86
-rw-r--r--src/plugins/platforms/wasm/qwasmfontdatabase.h49
-rw-r--r--src/plugins/platforms/wasm/qwasmintegration.cpp219
-rw-r--r--src/plugins/platforms/wasm/qwasmintegration.h92
-rw-r--r--src/plugins/platforms/wasm/qwasmopenglcontext.cpp147
-rw-r--r--src/plugins/platforms/wasm/qwasmopenglcontext.h63
-rw-r--r--src/plugins/platforms/wasm/qwasmscreen.cpp118
-rw-r--r--src/plugins/platforms/wasm/qwasmscreen.h82
-rw-r--r--src/plugins/platforms/wasm/qwasmstylepixmaps_p.h183
-rw-r--r--src/plugins/platforms/wasm/qwasmtheme.cpp50
-rw-r--r--src/plugins/platforms/wasm/qwasmtheme.h56
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.cpp398
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.h124
-rw-r--r--src/plugins/platforms/wasm/wasm.json3
-rw-r--r--src/plugins/platforms/wasm/wasm.pro65
-rw-r--r--src/plugins/platforms/wasm/wasm_shell.html61
-rw-r--r--src/widgets/dialogs/qfiledialog.cpp2
-rw-r--r--src/widgets/graphicsview/qgraphicsitem_p.h4
-rw-r--r--src/widgets/kernel/qopenglwidget.cpp10
-rw-r--r--util/unicode/main.cpp8
74 files changed, 6191 insertions, 24 deletions
diff --git a/configure.json b/configure.json
index 1f45f2ccb2..8c8118f39a 100644
--- a/configure.json
+++ b/configure.json
@@ -162,7 +162,8 @@
"sources": [
{ "libs": "-lzdll", "condition": "config.msvc" },
{ "libs": "-lzlib", "condition": "config.msvc" },
- { "libs": "-lz", "condition": "!config.msvc" }
+ { "libs": "-lz", "condition": "!config.msvc" },
+ { "libs": "-s USE_ZLIB=1", "condition": "config.wasm" }
]
},
"dbus": {
@@ -613,7 +614,7 @@
},
"use_gold_linker": {
"label": "Using gold linker",
- "condition": "!config.win32 && !config.integrity && tests.use_gold_linker",
+ "condition": "!config.win32 && !config.integrity && !config.wasm && tests.use_gold_linker",
"output": [ "privateConfig", "useGoldLinker" ]
},
"optimize_debug": {
diff --git a/configure.pri b/configure.pri
index 03eacd3c07..805b985fa6 100644
--- a/configure.pri
+++ b/configure.pri
@@ -68,7 +68,7 @@ defineReplace(qtConfFunc_crossCompile) {
}
defineReplace(qtConfFunc_licenseCheck) {
- exists($$QT_SOURCE_TREE/LICENSE.LGPL3)|exists($$QT_SOURCE_TREE/LICENSE.GPL2): \
+ exists($$QT_SOURCE_TREE/LICENSE.LGPL3)|exists($$QT_SOURCE_TREE/LICENSE.GPL2)|exists($$QT_SOURCE_TREE/LICENSE.GPL3): \
hasOpenSource = true
else: \
hasOpenSource = false
@@ -187,8 +187,13 @@ defineReplace(qtConfFunc_licenseCheck) {
theLicense = "GNU Lesser General Public License (LGPL) version 3"
showWhat = "Type 'L' to view the GNU Lesser General Public License version 3 (LGPLv3)."
gpl2Ok = false
+ gpl3Ok = false
winrt {
notTheLicense = "Note: GPL version 2 is not available on WinRT."
+ } else: wasm {
+ gpl3Ok = true
+ theLicense = "GNU General Public License (GPL) version 3"
+ showWhat = "Type 'G' to view the GNU General Public License version 3 (GPLv3)."
} else: $$qtConfEvaluate("features.android-style-assets") {
notTheLicense = "Note: GPL version 2 is not available due to using Android style assets."
} else {
@@ -230,6 +235,8 @@ defineReplace(qtConfFunc_licenseCheck) {
licenseFile = $$QT_SOURCE_TREE/LICENSE.LGPL3
} else: equals(commercial, no):equals(val, g):$$gpl2Ok {
licenseFile = $$QT_SOURCE_TREE/LICENSE.GPL2
+ } else: equals(commercial, no):equals(val, g):$$gpl3Ok {
+ licenseFile = $$QT_SOURCE_TREE/LICENSE.GPL3
} else {
next()
}
@@ -268,6 +275,8 @@ defineTest(qtConfTest_architecture) {
content = $$cat($$test_out_dir/arch.exe, blob)
else: android:exists($$test_out_dir/libarch.so): \
content = $$cat($$test_out_dir/libarch.so, blob)
+ else: wasm:exists($$test_out_dir/arch.wasm): \
+ content = $$cat($$test_out_dir/arch.wasm, blob)
else: \
error("$$eval($${1}.label) detection binary not found.")
diff --git a/mkspecs/features/toolchain.prf b/mkspecs/features/toolchain.prf
index bce3ef3954..4ecfb8d889 100644
--- a/mkspecs/features/toolchain.prf
+++ b/mkspecs/features/toolchain.prf
@@ -34,7 +34,9 @@ isEmpty($${target_prefix}.INCDIRS) {
#
# Get default include and library paths from compiler
#
- gcc {
+ wasm {
+ # wasm compiler does not work here, just use defaults
+ } else: gcc {
cmd_suffix = "<$$QMAKE_SYSTEM_NULL_DEVICE >$$QMAKE_SYSTEM_NULL_DEVICE"
equals(QMAKE_HOST.os, Windows): \
cmd_prefix = "set LC_ALL=C&"
diff --git a/mkspecs/features/wasm/qt.prf b/mkspecs/features/wasm/qt.prf
new file mode 100644
index 0000000000..9b9b58d3de
--- /dev/null
+++ b/mkspecs/features/wasm/qt.prf
@@ -0,0 +1,12 @@
+
+qt_depends = $$resolve_depends(QT, "QT.")
+equals(TEMPLATE, app):contains(qt_depends, gui(-private)?) {
+ LIBS *= -L$$[QT_INSTALL_PLUGINS/get]/platforms
+
+ lib_name = wasm
+ lib_path_and_base = $$[QT_INSTALL_PLUGINS/get]/platforms/lib$${lib_name}$$qtPlatformTargetSuffix()
+ LIBS += -l$${lib_name}$$qtPlatformTargetSuffix() $$fromfile($${lib_path_and_base}.prl, QMAKE_PRL_LIBS)
+}
+
+load(qt)
+
diff --git a/mkspecs/features/wasm/wasm.prf b/mkspecs/features/wasm/wasm.prf
new file mode 100644
index 0000000000..278a6719c7
--- /dev/null
+++ b/mkspecs/features/wasm/wasm.prf
@@ -0,0 +1,81 @@
+
+# DESTDIR will be empty if not set in the app .pro file; make sure it has a value
+isEmpty(DESTDIR): DESTDIR = $$OUT_PWD
+
+# Create js and wasm files for applications
+contains(TEMPLATE, .*app) {
+ TARGET_BASE = $${TARGET}
+ TARGET_HTML = $${TARGET}.html
+ TARGET_JS = $${TARGET}.js
+
+ # Make the emscripten compiler generate a js file
+ TARGET = $$TARGET_JS
+
+ QMAKE_INCDIR += $$(HOME)/.emscripten_ports/openssl/include
+
+ CONFIG += static
+ js_file.files = $$TARGET_JS
+ js_file.path = $$target.path
+ isEmpty(js_file.path): \
+ js_file.path += ./
+ INSTALLS += js_file
+
+ # Copy hosting html and javascript to the application build directory.
+ exists($$[QT_INSTALL_PLUGINS]/platforms/wasm_shell.html) {
+ # don't pass this until it's installed somewhere
+ # otherwise makespec test fails during qt configure
+ WASM_PLUGIN_PATH = $$[QT_INSTALL_PLUGINS]/platforms
+ } else {
+ ## internal build. not installed
+ WASM_PLUGIN_PATH = $$PWD/../../../src/plugins/platforms/wasm
+ }
+
+ # Copy/Generate main .html file (e.g. myapp.html) from the webassembly_shell.html by
+ # replacing the app name placeholder with the actual app name.
+ apphtml.name = application main html file
+ apphtml.output = $$DESTDIR/$$TARGET_HTML
+ apphtml.commands = sed -e s/APPNAME/$$TARGET_BASE/g $$WASM_PLUGIN_PATH/wasm_shell.html > $$DESTDIR/$$TARGET_HTML
+ apphtml.input = $$WASM_PLUGIN_PATH/wasm_shell.html
+ apphtml.depends = $$apphtml.input
+ QMAKE_EXTRA_COMPILERS += apphtml
+
+ appjs.name = application qtloader.js
+ appjs.output = $$DESTDIR/qtloader.js
+ appjs.commands = $$QMAKE_COPY $$WASM_PLUGIN_PATH/qtloader.js $$DESTDIR
+ appjs.input = $$WASM_PLUGIN_PATH/qtloader.js
+ appjs.depends = $$appjs.input
+ QMAKE_EXTRA_COMPILERS += appjs
+
+ appsvg.name = application qtlogo.svg
+ appsvg.output = $$DESTDIR/qtlogo.svg
+ appsvg.commands = $$QMAKE_COPY $$WASM_PLUGIN_PATH/qtlogo.svg $$DESTDIR
+ appsvg.input = $$WASM_PLUGIN_PATH/qtlogo.svg
+ appsvg.depends = $$appsvg.input
+ QMAKE_EXTRA_COMPILERS += appsvg
+
+ QMAKE_EXTRA_TARGETS += apphtml appjs appsvg
+ POST_TARGETDEPS += apphtml appjs appsvg
+
+ # Add manual target to make "make -B shellfiles" work.
+ shellfiles.target = shellfiles
+ shellfiles.depends = apphtml appjs appsvg
+ QMAKE_EXTRA_TARGETS += shellfiles
+
+ # emscripten ports are linked into the main module (this app), not the Qt
+ # libs which reference them
+ qt {
+ qt_depends = $$resolve_depends(QT, "QT.")
+ contains(qt_depends, core(-private)?): QMAKE_LFLAGS += \
+ $$QMAKE_LIBS_THREAD $$QMAKE_LIBS_ZLIB
+ contains(qt_depends, gui(-private)?): QMAKE_LFLAGS += \
+ $$QMAKE_LIBS_FREETYPE $$QMAKE_LIBS_LIBPNG
+ }
+}
+
+# Creates the stand-alone version of the library from bitcode
+!static:contains(TEMPLATE, .*lib): {
+ load(resolve_target)
+ QMAKE_POST_LINK += $$QMAKE_LINK_SHLIB $$QMAKE_RESOLVED_TARGET -o $${QMAKE_RESOLVED_TARGET}.js
+
+ QMAKE_INCDIR += $$(HOME)/.emscripten_ports/openssl/include
+}
diff --git a/mkspecs/wasm-emscripten/qmake.conf b/mkspecs/wasm-emscripten/qmake.conf
new file mode 100644
index 0000000000..c3b67310c8
--- /dev/null
+++ b/mkspecs/wasm-emscripten/qmake.conf
@@ -0,0 +1,90 @@
+# qmake configuration for building with emscripten
+MAKEFILE_GENERATOR = UNIX
+QMAKE_PLATFORM = wasm unix
+
+include(../common/gcc-base.conf)
+include(../common/clang.conf)
+
+EMTERP_FLAGS = \
+ -s EMTERPRETIFY=1 \
+ -s EMTERPRETIFY_ASYNC=1 \
+ -s \"EMTERPRETIFY_FILE=\'data.binary\'\" \
+ -s ASSERTIONS=1 \
+ --profiling-funcs
+
+EMCC_COMMON_CFLAGS = \
+ -s USE_LIBPNG=1 \
+ -s USE_FREETYPE=1 \
+ -s USE_ZLIB=1
+
+EMCC_COMMON_LFLAGS = \
+ -s WASM=1 \
+ -s FULL_ES2=1 \
+ -s ALLOW_MEMORY_GROWTH=1 \
+ -s USE_WEBGL2=1 \
+ -s NO_EXIT_RUNTIME=0 \
+ -s ERROR_ON_UNDEFINED_SYMBOLS=1 \
+ --bind \
+ -s \"BINARYEN_METHOD=\'native-wasm\'\" \
+ -s \"BINARYEN_TRAP_MODE=\'clamp\'\"
+
+EMCC_COMMON_LFLAGS_DEBUG = \
+ $$EMCC_COMMON_LFLAGS \
+ -s ASSERTIONS=2 \
+ -s DEMANGLE_SUPPORT=1 \
+ # -s LIBRARY_DEBUG=1 \ #print out library calls, verbose
+ # -s SYSCALL_DEBUG=1 \ #print out sys calls, verbose
+ # -s FS_LOG=1 \ #print out filesystem ops, verbose
+ # -s SOCKET_DEBUG \ #print out socket,network data transfer
+ -s GL_DEBUG=1
+
+# the -s arguments can also be used with release builds
+# but here in debug for clarity
+
+QMAKE_COMPILER += emscripten
+
+QMAKE_CC = emcc
+QMAKE_CXX = em++
+
+QMAKE_CFLAGS += $$EMCC_COMMON_CFLAGS
+QMAKE_CXXFLAGS += $$EMCC_COMMON_CFLAGS
+
+# Practical debugging setup:
+# "-g4" preserves function names for stack traces
+# "-Os" produces reasonably sized binaries
+QMAKE_CFLAGS_DEBUG -= -g
+QMAKE_CXXFLAGS_DEBUG -= -g
+QMAKE_CFLAGS_DEBUG += -Os -g4
+QMAKE_CXXFLAGS_DEBUG += -Os -g4
+QMAKE_LFLAGS_DEBUG += -Os -g4
+
+QMAKE_CXXFLAGS_RELEASE -= -O2
+QMAKE_CXXFLAGS_RELEASE += -O3
+QMAKE_CFLAGS_RELEASE -= -O2
+QMAKE_CFLAGS_RELEASE += -O3
+QMAKE_LFLAGS_RELEASE += -O3
+MAKE_CFLAGS_OPTIMIZE += -O3
+MAKE_CFLAGS_OPTIMIZE_FULL += -Oz
+
+QMAKE_LINK = $$QMAKE_CXX
+QMAKE_LINK_SHLIB = $$QMAKE_CXX
+QMAKE_LINK_C = $$QMAKE_CC
+QMAKE_LINK_C_SHLIB = $$QMAKE_CC
+
+QMAKE_LIBS_THREAD = $$QMAKE_CFLAGS_THREAD
+
+QMAKE_LFLAGS += $$EMCC_COMMON_LFLAGS
+QMAKE_LFLAGS_DEBUG += $$EMCC_COMMON_LFLAGS_DEBUG
+
+QMAKE_PREFIX_SHLIB = lib
+QMAKE_EXTENSION_SHLIB = so # llvm bitcode, linked to js in post_link
+QMAKE_PREFIX_STATICLIB = lib
+QMAKE_EXTENSION_STATICLIB = a # llvm bitcode
+
+QMAKE_AR = emar cqs
+QMAKE_DISTCLEAN += *.html *.js *.wasm
+
+QT_QPA_DEFAULT_PLATFORM = wasm
+
+QTPLUGIN.platforms = wasm
+load(qt_config)
diff --git a/mkspecs/wasm-emscripten/qplatformdefs.h b/mkspecs/wasm-emscripten/qplatformdefs.h
new file mode 100644
index 0000000000..c1a0d7b1a8
--- /dev/null
+++ b/mkspecs/wasm-emscripten/qplatformdefs.h
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the qmake spec of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPLATFORMDEFS_H
+#define QPLATFORMDEFS_H
+
+// Get Qt defines/settings
+
+#include "qglobal.h"
+
+// Set any POSIX/XOPEN defines at the top of this file to turn on specific APIs
+
+// 1) need to reset default environment if _BSD_SOURCE is defined
+// 2) need to specify POSIX thread interfaces explicitly in glibc 2.0
+// 3) it seems older glibc need this to include the X/Open stuff
+
+#include <unistd.h>
+
+// We are hot - unistd.h should have turned on the specific APIs we requested
+
+#include <features.h>
+#include <pthread.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <dlfcn.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/ipc.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#ifndef QT_NO_IPV6IFNAME
+#include <net/if.h>
+#endif
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#ifdef QT_LARGEFILE_SUPPORT
+#define QT_STATBUF struct stat64
+#define QT_STATBUF4TSTAT struct stat64
+#define QT_STAT ::stat64
+#define QT_FSTAT ::fstat64
+#define QT_LSTAT ::lstat64
+#define QT_OPEN ::open64
+#define QT_TRUNCATE ::truncate64
+#define QT_FTRUNCATE ::ftruncate64
+#define QT_LSEEK ::lseek64
+#else
+#define QT_STATBUF struct stat
+#define QT_STATBUF4TSTAT struct stat
+#define QT_STAT ::stat
+#define QT_FSTAT ::fstat
+#define QT_LSTAT ::lstat
+#define QT_OPEN ::open
+#define QT_TRUNCATE ::truncate
+#define QT_FTRUNCATE ::ftruncate
+#define QT_LSEEK ::lseek
+#endif
+
+#ifdef QT_LARGEFILE_SUPPORT
+#define QT_FOPEN ::fopen64
+#define QT_FSEEK ::fseeko64
+#define QT_FTELL ::ftello64
+#define QT_FGETPOS ::fgetpos64
+#define QT_FSETPOS ::fsetpos64
+#define QT_MMAP ::mmap64
+#define QT_FPOS_T fpos64_t
+#define QT_OFF_T off64_t
+#else
+#define QT_FOPEN ::fopen
+#define QT_FSEEK ::fseek
+#define QT_FTELL ::ftell
+#define QT_FGETPOS ::fgetpos
+#define QT_FSETPOS ::fsetpos
+#define QT_MMAP ::mmap
+#define QT_FPOS_T fpos_t
+#define QT_OFF_T long
+#endif
+
+#define QT_STAT_REG S_IFREG
+#define QT_STAT_DIR S_IFDIR
+#define QT_STAT_MASK S_IFMT
+#define QT_STAT_LNK S_IFLNK
+#define QT_SOCKET_CONNECT ::connect
+#define QT_SOCKET_BIND ::bind
+#define QT_FILENO fileno
+#define QT_CLOSE ::close
+#define QT_READ ::read
+#define QT_WRITE ::write
+#define QT_ACCESS ::access
+#define QT_GETCWD ::getcwd
+#define QT_CHDIR ::chdir
+#define QT_MKDIR ::mkdir
+#define QT_RMDIR ::rmdir
+#define QT_OPEN_LARGEFILE O_LARGEFILE
+#define QT_OPEN_RDONLY O_RDONLY
+#define QT_OPEN_WRONLY O_WRONLY
+#define QT_OPEN_RDWR O_RDWR
+#define QT_OPEN_CREAT O_CREAT
+#define QT_OPEN_TRUNC O_TRUNC
+#define QT_OPEN_APPEND O_APPEND
+#define QT_OPEN_EXCL O_EXCL
+
+// Directory iteration
+#define QT_DIR DIR
+
+#define QT_OPENDIR ::opendir
+#define QT_CLOSEDIR ::closedir
+
+#if defined(QT_LARGEFILE_SUPPORT) \
+ && defined(QT_USE_XOPEN_LFS_EXTENSIONS) \
+ && !defined(QT_NO_READDIR64)
+#define QT_DIRENT struct dirent64
+#define QT_READDIR ::readdir64
+#define QT_READDIR_R ::readdir64_r
+#else
+#define QT_DIRENT struct dirent
+#define QT_READDIR ::readdir
+#define QT_READDIR_R ::readdir_r
+#endif
+
+#define QT_SOCKET_CONNECT ::connect
+#define QT_SOCKET_BIND ::bind
+
+
+#define QT_SIGNAL_RETTYPE void
+#define QT_SIGNAL_ARGS int
+#define QT_SIGNAL_IGNORE SIG_IGN
+
+#define QT_SOCKLEN_T socklen_t
+
+#if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)
+#define QT_SNPRINTF ::snprintf
+#define QT_VSNPRINTF ::vsnprintf
+#endif
+
+#endif // QPLATFORMDEFS_H
diff --git a/src/3rdparty/double-conversion/include/double-conversion/utils.h b/src/3rdparty/double-conversion/include/double-conversion/utils.h
index ca7646d817..ff00855bea 100644
--- a/src/3rdparty/double-conversion/include/double-conversion/utils.h
+++ b/src/3rdparty/double-conversion/include/double-conversion/utils.h
@@ -77,7 +77,7 @@ inline void abort_noreturn() { abort(); }
defined(__SH4__) || defined(__alpha__) || \
defined(_MIPS_ARCH_MIPS32R2) || \
defined(__AARCH64EL__) || defined(__aarch64__) || \
- defined(__riscv)
+ defined(__riscv) || defined(__EMSCRIPTEN__)
#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1
#elif defined(__mc68000__)
#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS
diff --git a/src/3rdparty/wasm/DejaVuSans.ttf b/src/3rdparty/wasm/DejaVuSans.ttf
new file mode 100644
index 0000000000..7e411a71be
--- /dev/null
+++ b/src/3rdparty/wasm/DejaVuSans.ttf
Binary files differ
diff --git a/src/3rdparty/wasm/LICENSE b/src/3rdparty/wasm/LICENSE
new file mode 100644
index 0000000000..4f9e8544b7
--- /dev/null
+++ b/src/3rdparty/wasm/LICENSE
@@ -0,0 +1,15 @@
+Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license (“Fonts”) and associated documentation files (the “Font Software”), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions:
+
+The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces.
+
+The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words “Bitstream” or the word “Vera”.
+
+This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the “Bitstream Vera” names.
+
+The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself.
+
+THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
+
+Except as contained in this notice, the names of GNOME, the GNOME Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the GNOME Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.
diff --git a/src/3rdparty/wasm/Vera.ttf b/src/3rdparty/wasm/Vera.ttf
new file mode 100644
index 0000000000..58cd6b5e61
--- /dev/null
+++ b/src/3rdparty/wasm/Vera.ttf
Binary files differ
diff --git a/src/3rdparty/wasm/qt_attribution.json b/src/3rdparty/wasm/qt_attribution.json
new file mode 100644
index 0000000000..184e2968cd
--- /dev/null
+++ b/src/3rdparty/wasm/qt_attribution.json
@@ -0,0 +1,21 @@
+{
+ "Id": "vera_font",
+ "Name": "Vera",
+ "QDocModule": "qtcore",
+ "QtUsage": "Used for WebAssembly platform.",
+
+ "License": "Bitstream",
+ "LicenseFile": "LICENSE",
+ "Copyright": "Copyright (C) 2003 Bitstream,Inc"
+},
+{
+ "Id": "dejayvu",
+ "Name": "DejaVuSans",
+ "QDocModule": "qtcore",
+ "QtUsage": "Used for WebAssembly platform.",
+
+ "License": "Bitstream",
+ "LicenseFile": "LICENSE",
+ "Copyright": "Copyright (C) 2003 Bitstream,Inc"
+}
+
diff --git a/src/corelib/codecs/qtextcodec_p.h b/src/corelib/codecs/qtextcodec_p.h
index f3c2d090c9..be0cab93e6 100644
--- a/src/corelib/codecs/qtextcodec_p.h
+++ b/src/corelib/codecs/qtextcodec_p.h
@@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_TEXTCODEC
-#if defined(Q_OS_MAC) || defined(Q_OS_ANDROID) || defined(Q_OS_QNX)
+#if defined(Q_OS_MAC) || defined(Q_OS_ANDROID) || defined(Q_OS_QNX) || defined(Q_OS_WASM)
#define QT_LOCALE_IS_UTF8
#endif
diff --git a/src/corelib/configure.json b/src/corelib/configure.json
index 18fc22f18f..183eb3a13e 100644
--- a/src/corelib/configure.json
+++ b/src/corelib/configure.json
@@ -481,7 +481,7 @@
},
"eventfd": {
"label": "eventfd",
- "condition": "tests.eventfd",
+ "condition": "!config.wasm && tests.eventfd",
"output": [ "feature" ]
},
"futimens": {
@@ -592,7 +592,7 @@
"poll_ppoll": {
"label": "Native ppoll()",
"emitIf": "!config.win32",
- "condition": "tests.ppoll",
+ "condition": "!config.wasm && tests.ppoll",
"output": [ "privateFeature" ]
},
"poll_pollts": {
diff --git a/src/corelib/global/archdetect.cpp b/src/corelib/global/archdetect.cpp
index 6c1a026fa8..422df3ff90 100644
--- a/src/corelib/global/archdetect.cpp
+++ b/src/corelib/global/archdetect.cpp
@@ -51,6 +51,8 @@
# define ARCH_PROCESSOR "avr32"
#elif defined(Q_PROCESSOR_BLACKFIN)
# define ARCH_PROCESSOR "bfin"
+#elif defined(Q_PROCESSOR_WASM)
+# define ARCH_PROCESSOR "wasm"
#elif defined(Q_PROCESSOR_X86_32)
# define ARCH_PROCESSOR "i386"
#elif defined(Q_PROCESSOR_X86_64)
diff --git a/src/corelib/global/qcompilerdetection.h b/src/corelib/global/qcompilerdetection.h
index 2b85ea0815..345ab9e8ad 100644
--- a/src/corelib/global/qcompilerdetection.h
+++ b/src/corelib/global/qcompilerdetection.h
@@ -206,6 +206,9 @@
# define Q_DECL_NS_RETURNS_AUTORELEASED __attribute__((ns_returns_autoreleased))
# endif
# endif
+# ifdef __EMSCRIPTEN__
+# define Q_CC_EMSCRIPTEN
+# endif
# else
/* Plain GCC */
# if Q_CC_GNU >= 405
diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp
index a931b43220..2aabaa2ffb 100644
--- a/src/corelib/global/qlogging.cpp
+++ b/src/corelib/global/qlogging.cpp
@@ -94,6 +94,10 @@
# include "private/qcore_unix_p.h"
#endif
+#ifdef Q_OS_WASM
+#include <emscripten/emscripten.h>
+#endif
+
#if QT_CONFIG(regularexpression)
# ifdef __UCLIBC__
# if __UCLIBC_HAS_BACKTRACE__
@@ -1690,6 +1694,37 @@ static bool win_message_handler(QtMsgType type, const QMessageLogContext &contex
}
#endif
+#ifdef Q_OS_WASM
+static bool wasm_default_message_handler(QtMsgType type,
+ const QMessageLogContext &context,
+ const QString &message)
+{
+ if (shouldLogToStderr())
+ return false; // Leave logging up to stderr handler
+
+ QString formattedMessage = qFormatLogMessage(type, context, message);
+ int emOutputFlags = (EM_LOG_CONSOLE | EM_LOG_DEMANGLE);
+ QByteArray localMsg = message.toLocal8Bit();
+ switch (type) {
+ case QtDebugMsg:
+ break;
+ case QtInfoMsg:
+ break;
+ case QtWarningMsg:
+ emOutputFlags |= EM_LOG_WARN;
+ break;
+ case QtCriticalMsg:
+ emOutputFlags |= EM_LOG_ERROR;
+ break;
+ case QtFatalMsg:
+ emOutputFlags |= EM_LOG_ERROR;
+ }
+ emscripten_log(emOutputFlags, "%s\n", qPrintable(formattedMessage));
+
+ return true; // Prevent further output to stderr
+}
+#endif
+
#endif // Bootstrap check
// --------------------------------------------------------------------------
@@ -1735,6 +1770,8 @@ static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &con
# elif defined(QT_USE_APPLE_UNIFIED_LOGGING)
if (__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *))
handledStderr |= AppleUnifiedLogger::messageHandler(type, context, message);
+# elif defined Q_OS_WASM
+ handledStderr |= wasm_default_message_handler(type, context, message);
# endif
#endif
diff --git a/src/corelib/global/qprocessordetection.h b/src/corelib/global/qprocessordetection.h
index 9a3dfd776d..aaa27dff4a 100644
--- a/src/corelib/global/qprocessordetection.h
+++ b/src/corelib/global/qprocessordetection.h
@@ -320,6 +320,12 @@
# endif
# define Q_BYTE_ORDER Q_BIG_ENDIAN
+// -- Web Assembly --
+#elif defined(__EMSCRIPTEN__)
+# define Q_PROCESSOR_WASM
+# define Q_PROCESSOR_X86 6 // enables SIMD support
+# define Q_BYTE_ORDER Q_LITTLE_ENDIAN
+# define Q_PROCESSOR_WORDSIZE 8
#endif
/*
diff --git a/src/corelib/global/qsystemdetection.h b/src/corelib/global/qsystemdetection.h
index cacb95b674..aabe46f3c2 100644
--- a/src/corelib/global/qsystemdetection.h
+++ b/src/corelib/global/qsystemdetection.h
@@ -137,6 +137,8 @@
# define Q_OS_HPUX
#elif defined(__native_client__)
# define Q_OS_NACL
+#elif defined(__EMSCRIPTEN__)
+# define Q_OS_WASM
#elif defined(__linux__) || defined(__linux)
# define Q_OS_LINUX
#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp
index 835e6a6a44..eebf36416e 100644
--- a/src/corelib/io/qfilesystemengine_unix.cpp
+++ b/src/corelib/io/qfilesystemengine_unix.cpp
@@ -860,7 +860,7 @@ QString QFileSystemEngine::resolveUserName(uint userId)
QVarLengthArray<char, 1024> buf(size_max);
#endif
-#if !defined(Q_OS_INTEGRITY)
+#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM)
struct passwd *pw = 0;
#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_VXWORKS)
struct passwd entry;
@@ -884,7 +884,7 @@ QString QFileSystemEngine::resolveGroupName(uint groupId)
QVarLengthArray<char, 1024> buf(size_max);
#endif
-#if !defined(Q_OS_INTEGRITY)
+#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM)
struct group *gr = 0;
#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_VXWORKS) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID) && (__ANDROID_API__ >= 24))
size_max = sysconf(_SC_GETGR_R_SIZE_MAX);
diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp
index 1134c6bb85..391d2a49af 100644
--- a/src/corelib/io/qsettings.cpp
+++ b/src/corelib/io/qsettings.cpp
@@ -77,6 +77,10 @@
# include <ioLib.h>
#endif
+#ifdef Q_OS_WASM
+#include <emscripten.h>
+#endif
+
#include <algorithm>
#include <stdlib.h>
@@ -1544,6 +1548,13 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
perms |= QFile::ReadGroup | QFile::ReadOther;
QFile(confFile->name).setPermissions(perms);
}
+#ifdef Q_OS_WASM
+ EM_ASM(
+ // Sync sandbox filesystem to persistent database filesystem. See QTBUG-70002
+ FS.syncfs(false, function(err) {
+ });
+ );
+#endif
} else {
setStatus(QSettings::AccessError);
}
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index 0303916c11..1350a7aa94 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -118,6 +118,10 @@
# include <taskLib.h>
#endif
+#ifdef Q_OS_WASM
+#include <emscripten.h>
+#endif
+
#ifdef QT_BOOTSTRAPPED
#include <private/qtrace_p.h>
#else
@@ -486,6 +490,13 @@ QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv, uint
QCoreApplicationPrivate::~QCoreApplicationPrivate()
{
+#ifdef Q_OS_WASM
+ EM_ASM(
+ // unmount persistent directory as IDBFS
+ // see also QTBUG-70002
+ FS.unmount('/home/web_user');
+ );
+#endif
#ifndef QT_NO_QOBJECT
cleanupThreadData();
#endif
@@ -777,6 +788,17 @@ void QCoreApplicationPrivate::init()
Q_ASSERT_X(!QCoreApplication::self, "QCoreApplication", "there should be only one application object");
QCoreApplication::self = q;
+#ifdef Q_OS_WASM
+ EM_ASM(
+ // mount and sync persistent filesystem to sandbox
+ FS.mount(IDBFS, {}, '/home/web_user');
+ FS.syncfs(true, function(err) {
+ if (err)
+ Module.print(err);
+ });
+ );
+#endif
+
// Store app name/version (so they're still available after QCoreApplication is destroyed)
if (!coreappdata()->applicationNameSet)
coreappdata()->application = appName();
diff --git a/src/corelib/kernel/qeventdispatcher_unix.cpp b/src/corelib/kernel/qeventdispatcher_unix.cpp
index a28f2e3f0a..535f86fefe 100644
--- a/src/corelib/kernel/qeventdispatcher_unix.cpp
+++ b/src/corelib/kernel/qeventdispatcher_unix.cpp
@@ -130,7 +130,7 @@ static void initThreadPipeFD(int fd)
bool QThreadPipe::init()
{
-#if defined(Q_OS_NACL)
+#if defined(Q_OS_NACL) || defined(Q_OS_WASM)
// do nothing.
#elif defined(Q_OS_VXWORKS)
qsnprintf(name, sizeof(name), "/pipe/qt_%08x", int(taskIdSelf()));
diff --git a/src/corelib/kernel/qeventloop.cpp b/src/corelib/kernel/qeventloop.cpp
index f1d32b15d1..a6cc51621a 100644
--- a/src/corelib/kernel/qeventloop.cpp
+++ b/src/corelib/kernel/qeventloop.cpp
@@ -48,6 +48,10 @@
#include "qeventloop_p.h"
#include <private/qthread_p.h>
+#ifdef Q_OS_WASM
+#include <emscripten.h>
+#endif
+
QT_BEGIN_NAMESPACE
/*!
@@ -208,6 +212,15 @@ int QEventLoop::exec(ProcessEventsFlags flags)
if (app && app->thread() == thread())
QCoreApplication::removePostedEvents(app, QEvent::Quit);
+#ifdef Q_OS_WASM
+ // Partial support for nested event loops: Make the runtime throw a JavaSrcript
+ // exception, which returns control to the browser while preserving the C++ stack.
+ // Event processing then continues as normal. The sleep call below never returns.
+ // QTBUG-70185
+ if (d->threadData->loopLevel > 1)
+ emscripten_sleep(1);
+#endif
+
while (!d->exit.loadAcquire())
processEvents(flags | WaitForMoreEvents | EventLoopExec);
@@ -269,6 +282,17 @@ void QEventLoop::exit(int returnCode)
d->returnCode.store(returnCode);
d->exit.storeRelease(true);
d->threadData->eventDispatcher.load()->interrupt();
+
+#ifdef Q_OS_WASM
+ // QEventLoop::exec() never returns in emscripten. We implement approximate behavior here.
+ // QTBUG-70185
+ if (d->threadData->loopLevel == 1) {
+ emscripten_force_exit(returnCode);
+ } else {
+ d->inExec = false;
+ --d->threadData->loopLevel;
+ }
+#endif
}
/*!
diff --git a/src/corelib/mimetypes/qmimedatabase.cpp b/src/corelib/mimetypes/qmimedatabase.cpp
index a58698af53..10b5c8eafd 100644
--- a/src/corelib/mimetypes/qmimedatabase.cpp
+++ b/src/corelib/mimetypes/qmimedatabase.cpp
@@ -151,7 +151,9 @@ void QMimeDatabasePrivate::loadProviders()
QVector<QMimeProviderBase *> QMimeDatabasePrivate::providers()
{
+#ifndef Q_OS_WASM // stub implementation always returns true
Q_ASSERT(!mutex.tryLock()); // caller should have locked mutex
+#endif
if (m_providers.isEmpty()) {
loadProviders();
m_lastCheck.start();
diff --git a/src/corelib/tools/qsimd.cpp b/src/corelib/tools/qsimd.cpp
index 85efd3cded..07a8b022bc 100644
--- a/src/corelib/tools/qsimd.cpp
+++ b/src/corelib/tools/qsimd.cpp
@@ -190,7 +190,9 @@ static inline quint64 detectProcessorFeatures()
static int maxBasicCpuidSupported()
{
-#if defined(Q_CC_GNU)
+#if defined(Q_CC_EMSCRIPTEN)
+ return 6; // All features supported by Emscripten
+#elif defined(Q_CC_GNU)
qregisterint tmp1;
# if Q_PROCESSOR_X86 < 5
@@ -235,7 +237,7 @@ static int maxBasicCpuidSupported()
static void cpuidFeatures01(uint &ecx, uint &edx)
{
-#if defined(Q_CC_GNU)
+#if defined(Q_CC_GNU) && !defined(Q_CC_EMSCRIPTEN)
qregisterint tmp1;
asm ("xchg " PICreg", %2\n"
"cpuid\n"
@@ -252,6 +254,9 @@ static void cpuidFeatures01(uint &ecx, uint &edx)
__CPUID(1, info);
ecx = info[2];
edx = info[3];
+#else
+ Q_UNUSED(ecx);
+ Q_UNUSED(edx);
#endif
}
@@ -261,7 +266,7 @@ inline void __cpuidex(int info[4], int, __int64) { memset(info, 0, 4*sizeof(int)
static void cpuidFeatures07_00(uint &ebx, uint &ecx, uint &edx)
{
-#if defined(Q_CC_GNU)
+#if defined(Q_CC_GNU) && !defined(Q_CC_EMSCRIPTEN)
qregisteruint rbx; // in case it's 64-bit
qregisteruint rcx = 0;
qregisteruint rdx = 0;
@@ -294,7 +299,7 @@ inline quint64 _xgetbv(__int64) { return 0; }
#endif
static void xgetbv(uint in, uint &eax, uint &edx)
{
-#if defined(Q_CC_GNU) || defined(Q_CC_GHS)
+#if (defined(Q_CC_GNU) && !defined(Q_CC_EMSCRIPTEN)) || defined(Q_CC_GHS)
asm (".byte 0x0F, 0x01, 0xD0" // xgetbv instruction
: "=a" (eax), "=d" (edx)
: "c" (in));
@@ -302,6 +307,10 @@ static void xgetbv(uint in, uint &eax, uint &edx)
quint64 result = _xgetbv(in);
eax = result;
edx = result >> 32;
+#else
+ Q_UNUSED(in);
+ Q_UNUSED(eax);
+ Q_UNUSED(edx);
#endif
}
diff --git a/src/corelib/tools/qunicodetables_p.h b/src/corelib/tools/qunicodetables_p.h
index f3fb6ec1b0..3f2e91a9b2 100644
--- a/src/corelib/tools/qunicodetables_p.h
+++ b/src/corelib/tools/qunicodetables_p.h
@@ -72,6 +72,9 @@ struct Properties {
signed short mirrorDiff : 16;
ushort lowerCaseSpecial : 1;
signed short lowerCaseDiff : 15;
+#ifdef Q_OS_WASM
+ unsigned char : 0; //wasm 64 packing trick
+#endif
ushort upperCaseSpecial : 1;
signed short upperCaseDiff : 15;
ushort titleCaseSpecial : 1;
@@ -80,6 +83,9 @@ struct Properties {
signed short caseFoldDiff : 15;
ushort unicodeVersion : 8; /* 5 used */
ushort nfQuickCheck : 8;
+#ifdef Q_OS_WASM
+ unsigned char : 0; //wasm 64 packing trick
+#endif
ushort graphemeBreakClass : 5; /* 5 used */
ushort wordBreakClass : 5; /* 5 used */
ushort sentenceBreakClass : 8; /* 4 used */
diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri
index 9ec6ea4894..dc28e0e0a2 100644
--- a/src/corelib/tools/tools.pri
+++ b/src/corelib/tools/tools.pri
@@ -220,7 +220,7 @@ qtConfig(system-doubleconversion) {
}
# Note: libm should be present by default becaue this is C++
-unix:!macx-icc:!vxworks:!haiku:!integrity: LIBS_PRIVATE += -lm
+unix:!macx-icc:!vxworks:!haiku:!integrity:!wasm: LIBS_PRIVATE += -lm
TR_EXCLUDE += ../3rdparty/*
diff --git a/src/gui/configure.json b/src/gui/configure.json
index 26a5ae9a3d..4741ed345a 100644
--- a/src/gui/configure.json
+++ b/src/gui/configure.json
@@ -164,7 +164,8 @@
},
"sources": [
{ "type": "pkgConfig", "args": "freetype2" },
- { "type": "freetype", "libs": "-lfreetype" }
+ { "type": "freetype", "libs": "-lfreetype", "condition": "!config.wasm" },
+ { "type": "freetype", "libs": "-s USE_FREETYPE=1", "condition": "config.wasm" }
]
},
"fontconfig": {
@@ -308,7 +309,8 @@
{ "libs": "-llibpng16", "condition": "config.msvc" },
{ "libs": "-llibpng", "condition": "config.msvc" },
{ "libs": "-lpng16", "condition": "!config.msvc" },
- { "libs": "-lpng", "condition": "!config.msvc" }
+ { "libs": "-lpng", "condition": "!config.msvc" },
+ { "libs": "-s USE_LIBPNG=1", "condition": "config.wasm" }
],
"use": [
{ "lib": "zlib", "condition": "features.system-zlib" }
@@ -1111,7 +1113,7 @@
},
"opengles3": {
"label": "OpenGL ES 3.0",
- "condition": "features.opengles2 && !features.angle && tests.opengles3",
+ "condition": "features.opengles2 && !features.angle && tests.opengles3 && !config.wasm",
"output": [
"publicFeature",
{ "type": "define", "name": "QT_OPENGL_ES_3" }
@@ -1138,7 +1140,7 @@
"enable": "input.opengl == 'desktop'",
"disable": "input.opengl == 'es2' || input.opengl == 'dynamic' || input.opengl == 'no'",
"condition": "(config.win32 && !config.winrt && !features.opengles2 && (config.msvc || libs.opengl))
- || (!config.watchos && !config.win32 && libs.opengl)"
+ || (!config.watchos && !config.win32 && !config.wasm && libs.opengl)"
},
"opengl-dynamic": {
"label": "Dynamic OpenGL",
diff --git a/src/gui/configure.pri b/src/gui/configure.pri
index b40536e41d..2971fd136e 100644
--- a/src/gui/configure.pri
+++ b/src/gui/configure.pri
@@ -55,6 +55,7 @@ defineTest(qtConfTest_qpaDefaultPlatform) {
else: qnx: name = qnx
else: integrity: name = integrityfb
else: haiku: name = haiku
+ else: wasm: name = webassembly
else: name = xcb
$${1}.value = $$name
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index 93e8b6ee8f..bc1f83a9c8 100644
--- a/src/gui/kernel/qguiapplication.cpp
+++ b/src/gui/kernel/qguiapplication.cpp
@@ -114,6 +114,10 @@
# include <QtCore/QLibraryInfo>
#endif // Q_OS_WIN
+#ifdef Q_OS_WASM
+#include <emscripten.h>
+#endif
+
#include <qtgui_tracepoints_p.h>
#include <ctype.h>
@@ -1620,7 +1624,13 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate()
qt_gl_set_global_share_context(0);
}
#endif
-
+#ifdef Q_OS_WASM
+ EM_ASM(
+ // unmount persistent directory as IDBFS
+ // see QTBUG-70002
+ FS.unmount('/home/web_user');
+ );
+#endif
platform_integration->destroy();
delete platform_theme;
diff --git a/src/gui/kernel/qopenglcontext.cpp b/src/gui/kernel/qopenglcontext.cpp
index 7bf941b9a1..cd50294a6c 100644
--- a/src/gui/kernel/qopenglcontext.cpp
+++ b/src/gui/kernel/qopenglcontext.cpp
@@ -977,8 +977,11 @@ bool QOpenGLContext::makeCurrent(QSurface *surface)
if (!surface->surfaceHandle())
return false;
if (!surface->supportsOpenGL()) {
+#ifndef Q_OS_WASM // ### work around the WASM platform plugin using QOpenGLContext with raster surfaces.
+ // see QTBUG-70076
qWarning() << "QOpenGLContext::makeCurrent() called with non-opengl surface" << surface;
return false;
+#endif
}
QOpenGLContext *previous = QOpenGLContextPrivate::setCurrentContext(this);
diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp
index 6486a447ee..cae3d516c4 100644
--- a/src/gui/opengl/qopenglframebufferobject.cpp
+++ b/src/gui/opengl/qopenglframebufferobject.cpp
@@ -142,6 +142,14 @@ QT_BEGIN_NAMESPACE
#define GL_CONTEXT_LOST 0x0507
#endif
+#ifndef GL_DEPTH_STENCIL_ATTACHMENT
+#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
+#endif
+
+#ifndef GL_DEPTH_STENCIL
+#define GL_DEPTH_STENCIL 0x84F9
+#endif
+
/*!
@@ -619,7 +627,11 @@ void QOpenGLFramebufferObjectPrivate::initDepthStencilAttachments(QOpenGLContext
// free existing attachments
if (depth_buffer_guard) {
+#ifdef Q_OS_WASM
+ funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
+#else
funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
+#endif
depth_buffer_guard->free();
}
if (stencil_buffer_guard) {
@@ -637,7 +649,35 @@ void QOpenGLFramebufferObjectPrivate::initDepthStencilAttachments(QOpenGLContext
// In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a
// separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer
// might not be supported while separate buffers are, according to QTBUG-12861.
+#ifdef Q_OS_WASM
+ // WebGL doesn't allow separately attach buffers to
+ // STENCIL_ATTACHMENT and DEPTH_ATTACHMENT
+ // QTBUG-69913
+ if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil) {
+ funcs.glGenRenderbuffers(1, &depth_buffer);
+ funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
+ Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
+
+ GLenum storageFormat = GL_DEPTH_STENCIL;
+
+ if (samples != 0 ) {
+ funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
+ storageFormat, dsSize.width(), dsSize.height());
+ } else {
+ funcs.glRenderbufferStorage(GL_RENDERBUFFER, storageFormat,
+ dsSize.width(), dsSize.height());
+ }
+
+ funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, depth_buffer);
+ valid = checkFramebufferStatus(ctx);
+ if (!valid) {
+ funcs.glDeleteRenderbuffers(1, &depth_buffer);
+ depth_buffer = 0;
+ }
+ }
+#else
if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil
&& funcs.hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil))
{
@@ -729,11 +769,16 @@ void QOpenGLFramebufferObjectPrivate::initDepthStencilAttachments(QOpenGLContext
stencil_buffer = 0;
}
}
+#endif //Q_OS_WASM
// The FBO might have become valid after removing the depth or stencil buffer.
valid = checkFramebufferStatus(ctx);
+#ifdef Q_OS_WASM
+ if (depth_buffer) {
+#else
if (depth_buffer && stencil_buffer) {
+#endif
fbo_attachment = QOpenGLFramebufferObject::CombinedDepthStencil;
} else if (depth_buffer) {
fbo_attachment = QOpenGLFramebufferObject::Depth;
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
index a129beda15..b068f96283 100644
--- a/src/network/access/access.pri
+++ b/src/network/access/access.pri
@@ -68,6 +68,13 @@ qtConfig(networkdiskcache) {
mac: LIBS_PRIVATE += -framework Security
+wasm {
+ SOURCES += \
+ access/qnetworkreplywasmimpl.cpp
+ HEADERS += \
+ access/qnetworkreplywasmimpl_p.h
+}
+
include($$PWD/../../3rdparty/zlib_dependency.pri)
qtConfig(http) {
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 96e3f92db1..bec98a3f58 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -82,6 +82,9 @@
#include <SystemConfiguration/SystemConfiguration.h>
#include <Security/SecKeychain.h>
#endif
+#ifdef Q_OS_WASM
+#include "qnetworkreplywasmimpl_p.h"
+#endif
QT_BEGIN_NAMESPACE
@@ -1344,6 +1347,16 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
bool isLocalFile = req.url().isLocalFile();
QString scheme = req.url().scheme();
+#ifdef Q_OS_WASM
+ if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) {
+ QNetworkReplyWasmImpl *reply = new QNetworkReplyWasmImpl(this);
+ QNetworkReplyWasmImplPrivate *priv = reply->d_func();
+ priv->manager = this;
+ priv->setup(op, req, outgoingData);
+ return reply;
+ }
+#endif
+
// fast path for GET on file:// URLs
// The QNetworkAccessFileBackend will right now only be used for PUT
if (op == QNetworkAccessManager::GetOperation
diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h
index a0ce3eddcd..67b3a8b71b 100644
--- a/src/network/access/qnetworkaccessmanager.h
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -195,6 +195,9 @@ private:
friend class QNetworkReplyHttpImplPrivate;
friend class QNetworkReplyFileImpl;
+#ifdef Q_OS_WASM
+ friend class QNetworkReplyWasmImpl;
+#endif
Q_DECLARE_PRIVATE(QNetworkAccessManager)
Q_PRIVATE_SLOT(d_func(), void _q_replyFinished())
Q_PRIVATE_SLOT(d_func(), void _q_replyEncrypted())
diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp
new file mode 100644
index 0000000000..9c2ff8fb89
--- /dev/null
+++ b/src/network/access/qnetworkreplywasmimpl.cpp
@@ -0,0 +1,640 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkreplywasmimpl_p.h"
+#include "qnetworkrequest.h"
+
+#include <QtCore/qtimer.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qthread.h>
+
+#include <private/qnetworkaccessmanager_p.h>
+#include <private/qnetworkfile_p.h>
+
+#include <iostream>
+
+QT_BEGIN_NAMESPACE
+
+QNetworkReplyWasmImplPrivate::QNetworkReplyWasmImplPrivate()
+ : QNetworkReplyPrivate()
+ , managerPrivate(0)
+ , downloadBufferReadPosition(0)
+ , downloadBufferCurrentSize(0)
+ , totalDownloadSize(0)
+ , percentFinished(0)
+{
+}
+
+QNetworkReplyWasmImplPrivate::~QNetworkReplyWasmImplPrivate()
+{
+}
+
+QNetworkReplyWasmImpl::~QNetworkReplyWasmImpl()
+{
+}
+
+QNetworkReplyWasmImpl::QNetworkReplyWasmImpl(QObject *parent)
+ : QNetworkReply(*new QNetworkReplyWasmImplPrivate(), parent)
+{
+}
+
+QByteArray QNetworkReplyWasmImpl::methodName() const
+{
+ switch (operation()) {
+ case QNetworkAccessManager::HeadOperation:
+ return "HEAD";
+ case QNetworkAccessManager::GetOperation:
+ return "GET";
+ case QNetworkAccessManager::PutOperation:
+ return "PUT";
+ case QNetworkAccessManager::PostOperation:
+ return "POST";
+ case QNetworkAccessManager::DeleteOperation:
+ return "DELETE";
+ default:
+ break;
+ }
+ return QByteArray();
+}
+
+void QNetworkReplyWasmImpl::close()
+{
+ QNetworkReply::close();
+}
+
+void QNetworkReplyWasmImpl::abort()
+{
+ close();
+}
+
+qint64 QNetworkReplyWasmImpl::bytesAvailable() const
+{
+ Q_D(const QNetworkReplyWasmImpl);
+
+ if (!d->isFinished)
+ return QNetworkReply::bytesAvailable();
+
+ return QNetworkReply::bytesAvailable() + d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
+}
+
+bool QNetworkReplyWasmImpl::isSequential() const
+{
+ return true;
+}
+
+qint64 QNetworkReplyWasmImpl::size() const
+{
+ return QNetworkReply::size();
+}
+
+/*!
+ \internal
+*/
+qint64 QNetworkReplyWasmImpl::readData(char *data, qint64 maxlen)
+{
+ Q_D(QNetworkReplyWasmImpl);
+
+ qint64 howMuch = qMin(maxlen, (d->downloadBuffer.size() - d->downloadBufferReadPosition));
+ memcpy(data, d->downloadBuffer.constData(), howMuch);
+ d->downloadBufferReadPosition += howMuch;
+
+ return howMuch;
+}
+
+void QNetworkReplyWasmImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *data)
+{
+ Q_Q(QNetworkReplyWasmImpl);
+
+ outgoingData = data;
+ request = req;
+ url = request.url();
+ operation = op;
+
+ q->QIODevice::open(QIODevice::ReadOnly);
+ if (outgoingData && outgoingData->isSequential()) {
+ bool bufferingDisallowed =
+ request.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute, false).toBool();
+
+ if (bufferingDisallowed) {
+ // if a valid content-length header for the request was supplied, we can disable buffering
+ // if not, we will buffer anyway
+ if (!request.header(QNetworkRequest::ContentLengthHeader).isValid()) {
+ state = Buffering;
+ _q_bufferOutgoingData();
+ return;
+ }
+ } else {
+ // doSendRequest will be called when the buffering has finished.
+ state = Buffering;
+ _q_bufferOutgoingData();
+ return;
+ }
+ }
+ // No outgoing data (POST, ..)
+ doSendRequest();
+}
+
+void QNetworkReplyWasmImplPrivate::onLoadCallback(void *data, int statusCode, int statusReason, int readyState, int buffer, int bufferSize)
+{
+ QNetworkReplyWasmImplPrivate *handler = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(data);
+
+ const QString reasonStr = QString::fromUtf8(reinterpret_cast<char *>(statusReason));
+
+ switch (readyState) {
+ case 0://unsent
+ break;
+ case 1://opened
+ break;
+ case 2://headers received
+ break;
+ case 3://loading
+ break;
+ case 4: {//done
+ handler->q_func()->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
+ if (!reasonStr.isEmpty())
+ handler->q_func()->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reasonStr);
+
+ if (statusCode >= 400) {
+ if (!reasonStr.isEmpty())
+ handler->emitReplyError(handler->statusCodeFromHttp(statusCode, handler->request.url()), reasonStr);
+ } else {
+ handler->dataReceived(reinterpret_cast<char *>(buffer), bufferSize);
+ }
+ }
+ break;
+ };
+ }
+
+void QNetworkReplyWasmImplPrivate::onProgressCallback(void* data, int bytesWritten, int total, uint timestamp)
+{
+ Q_UNUSED(timestamp);
+
+ QNetworkReplyWasmImplPrivate *handler = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(data);
+ handler->emitDataReadProgress(bytesWritten, total);
+}
+
+void QNetworkReplyWasmImplPrivate::onRequestErrorCallback(void* data, int statusCode, int statusReason)
+{
+ QString reasonStr = QString::fromUtf8(reinterpret_cast<char *>(statusReason));
+
+ QNetworkReplyWasmImplPrivate *handler = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(data);
+
+ handler->q_func()->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
+ if (!reasonStr.isEmpty())
+ handler->q_func()->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reasonStr);
+
+ if (statusCode >= 400) {
+ if (!reasonStr.isEmpty())
+ handler->emitReplyError(handler->statusCodeFromHttp(statusCode, handler->request.url()), reasonStr);
+ }
+}
+
+void QNetworkReplyWasmImplPrivate::onResponseHeadersCallback(void* data, int headers)
+{
+ QNetworkReplyWasmImplPrivate *handler = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(data);
+ handler->headersReceived(reinterpret_cast<char *>(headers));
+}
+
+void QNetworkReplyWasmImplPrivate::doSendRequest()
+{
+ Q_Q(QNetworkReplyWasmImpl);
+ totalDownloadSize = 0;
+ jsRequest(QString::fromUtf8(q->methodName()), // GET POST
+ request.url().toString(),
+ (void *)&onLoadCallback,
+ (void *)&onProgressCallback,
+ (void *)&onRequestErrorCallback,
+ (void *)&onResponseHeadersCallback);
+}
+
+/* const QString &body, const QList<QPair<QByteArray, QByteArray> > &headers ,*/
+void QNetworkReplyWasmImplPrivate::jsRequest(const QString &verb, const QString &url,
+ void *loadCallback, void *progressCallback,
+ void *errorCallback, void *onResponseHeadersCallback)
+{
+ QString extraDataString;
+
+ QByteArray extraData;
+ if (outgoingData)
+ extraData = outgoingData->readAll();
+
+ if (extraData.size() > 0)
+ extraDataString.fromUtf8(extraData);
+
+ if (extraDataString.size() >= 0 && verb == QStringLiteral("POST") && extraDataString.startsWith(QStringLiteral("?")))
+ extraDataString.remove(QStringLiteral("?"));
+
+ // Probably a good idea to save any shared pointers as members in C++
+ // so the objects they point to survive as long as you need them
+
+ QStringList headersList;
+ for (auto header : request.rawHeaderList())
+ headersList << QString::fromUtf8(header + ":" + request.rawHeader(header));
+
+ EM_ASM_ARGS({
+ var verb = Pointer_stringify($0);
+ var url = Pointer_stringify($1);
+ var onLoadCallbackPointer = $2;
+ var onProgressCallbackPointer = $3;
+ var onErrorCallbackPointer = $4;
+ var onHeadersCallback = $5;
+ var handler = $8;
+
+ var dataToSend;
+ var extraRequestData = Pointer_stringify($6); // request parameters
+ var headersData = Pointer_stringify($7);
+
+ var xhr;
+ xhr = new XMLHttpRequest();
+ xhr.responseType = 'arraybuffer';
+
+ xhr.open(verb, url, true); //async
+
+ function handleError(xhrStatusCode, xhrStatusText) {
+ var errorPtr = allocate(intArrayFromString(xhrStatusText), 'i8', ALLOC_NORMAL);
+ Runtime.dynCall('viii', onErrorCallbackPointer, [handler, xhrStatusCode, errorPtr]);
+ _free(errorPtr);
+ }
+
+ if (headersData) {
+ var headers = headersData.split("&");
+ for (var i = 0; i < headers.length; i++) {
+ var header = headers[i].split(":")[0];
+ var value = headers[i].split(":")[1];
+
+ if (verb === 'POST' && value.toLowerCase().includes('json')) {
+ if (extraRequestData) {
+ xhr.responseType = 'json';
+ dataToSend = extraRequestData;
+ }
+ }
+ if (verb === 'POST' && value.toLowerCase().includes('form')) {
+ if (extraRequestData) {
+ var formData = new FormData();
+ var extra = extraRequestData.split("&");
+ for (var i = 0; i < extra.length; i++) {
+ formData.append(extra[i].split("=")[0],extra[i].split("=")[1]);
+ }
+ dataToSend = formData;
+ }
+ }
+ xhr.setRequestHeader(header, value);
+ }
+ }
+
+ xhr.onprogress = function(e) {
+ switch (xhr.status) {
+ case 200:
+ case 206:
+ case 300:
+ case 301:
+ case 302: {
+ var date = xhr.getResponseHeader('Last-Modified');
+ date = ((date != null) ? new Date(date).getTime() / 1000 : 0);
+ Runtime.dynCall('viiii', onProgressCallbackPointer, [handler, e.loaded, e.total, date]);
+ }
+ break;
+ }
+ };
+
+ xhr.onreadystatechange = function() {
+ if (this.readyState == this.HEADERS_RECEIVED) {
+ var responseStr = this.getAllResponseHeaders();
+ if (responseStr.length > 0) {
+ var ptr = allocate(intArrayFromString(responseStr), 'i8', ALLOC_NORMAL);
+ Runtime.dynCall('vii', onHeadersCallback, [handler, ptr]);
+ _free(ptr);
+ }
+ }
+ };
+
+ xhr.onload = function(e) {
+ if (xhr.status >= 300) { //error
+ handleError(xhr.status, xhr.statusText);
+ } else {
+ if (this.status == 200 || this.status == 203) {
+ var datalength;
+ var byteArray = 0;
+ var buffer;
+ if (this.responseType.length === 0 || this.responseType === 'document') {
+ byteArray = new Uint8Array(this.responseText);
+ } else if (this.responseType === 'json') {
+ var jsonResponse = JSON.stringify(this.response);
+ buffer = allocate(intArrayFromString(jsonResponse), 'i8', ALLOC_NORMAL);
+ datalength = jsonResponse.length;
+ } else if (this.responseType === 'arraybuffer') {
+ byteArray = new Uint8Array(xhr.response);
+ }
+ if (byteArray != 0 ) {
+ datalength = byteArray.length;
+ buffer = _malloc(datalength);
+ HEAPU8.set(byteArray, buffer);
+ }
+ var reasonPtr = allocate(intArrayFromString(this.statusText), 'i8', ALLOC_NORMAL);
+ Runtime.dynCall('viiiiii', onLoadCallbackPointer, [handler, this.status, reasonPtr, this.readyState, buffer, datalength]);
+ _free(buffer);
+ _free(reasonPtr);
+ }
+ }
+ };
+
+ xhr.onerror = function(e) {
+ handleError(xhr.status, xhr.statusText);
+ };
+ //TODO other operations, handle user/pass, handle binary data, data streaming
+ xhr.send(dataToSend);
+
+ }, verb.toLatin1().data(),
+ url.toLatin1().data(),
+ loadCallback,
+ progressCallback,
+ errorCallback,
+ onResponseHeadersCallback,
+ extraDataString.size() > 0 ? extraDataString.toLatin1().data() : extraData.data(),
+ headersList.join(QStringLiteral("&")).toLatin1().data(),
+ this
+ );
+}
+
+void QNetworkReplyWasmImplPrivate::emitReplyError(QNetworkReply::NetworkError errorCode, const QString &errorString)
+{
+ Q_UNUSED(errorCode)
+ Q_Q(QNetworkReplyWasmImpl);
+
+ q->setError(errorCode, errorString);
+ emit q->error(errorCode);
+
+ q->setFinished(true);
+ emit q->finished();
+}
+
+void QNetworkReplyWasmImplPrivate::emitDataReadProgress(qint64 bytesReceived, qint64 bytesTotal)
+{
+ Q_Q(QNetworkReplyWasmImpl);
+
+ totalDownloadSize = bytesTotal;
+
+ percentFinished = (bytesReceived / bytesTotal) * 100;
+
+ emit q->downloadProgress(bytesReceived, totalDownloadSize);
+}
+
+void QNetworkReplyWasmImplPrivate::dataReceived(char *buffer, int bufferSize)
+{
+ Q_Q(QNetworkReplyWasmImpl);
+
+ if (bufferSize > 0)
+ q->setReadBufferSize(bufferSize);
+
+ bytesDownloaded = bufferSize;
+
+ if (percentFinished != 100)
+ downloadBufferCurrentSize += bufferSize;
+ else
+ downloadBufferCurrentSize = bufferSize;
+
+ totalDownloadSize = downloadBufferCurrentSize;
+
+ downloadBuffer.append(buffer, bufferSize);
+
+ if (downloadBufferCurrentSize == totalDownloadSize) {
+ q->setFinished(true);
+ emit q->finished();
+ }
+}
+
+//taken from qnetworkrequest.cpp
+static int parseHeaderName(const QByteArray &headerName)
+{
+ if (headerName.isEmpty())
+ return -1;
+
+ switch (tolower(headerName.at(0))) {
+ case 'c':
+ if (qstricmp(headerName.constData(), "content-type") == 0)
+ return QNetworkRequest::ContentTypeHeader;
+ else if (qstricmp(headerName.constData(), "content-length") == 0)
+ return QNetworkRequest::ContentLengthHeader;
+ else if (qstricmp(headerName.constData(), "cookie") == 0)
+ return QNetworkRequest::CookieHeader;
+ break;
+
+ case 'l':
+ if (qstricmp(headerName.constData(), "location") == 0)
+ return QNetworkRequest::LocationHeader;
+ else if (qstricmp(headerName.constData(), "last-modified") == 0)
+ return QNetworkRequest::LastModifiedHeader;
+ break;
+
+ case 's':
+ if (qstricmp(headerName.constData(), "set-cookie") == 0)
+ return QNetworkRequest::SetCookieHeader;
+ else if (qstricmp(headerName.constData(), "server") == 0)
+ return QNetworkRequest::ServerHeader;
+ break;
+
+ case 'u':
+ if (qstricmp(headerName.constData(), "user-agent") == 0)
+ return QNetworkRequest::UserAgentHeader;
+ break;
+ }
+
+ return -1; // nothing found
+}
+
+
+void QNetworkReplyWasmImplPrivate::headersReceived(char *buffer)
+{
+ Q_Q(QNetworkReplyWasmImpl);
+
+ QString bufferString = QString::fromUtf8(buffer);
+ if (!bufferString.isEmpty()) {
+ QStringList headers = bufferString.split(QString::fromUtf8("\r\n"), QString::SkipEmptyParts);
+
+ for (int i = 0; i < headers.size(); i++) {
+ QString headerName = headers.at(i).split(QString::fromUtf8(": ")).at(0);
+ QString headersValue = headers.at(i).split(QString::fromUtf8(": ")).at(1);
+ if (headerName.isEmpty() || headersValue.isEmpty())
+ continue;
+
+ int headerIndex = parseHeaderName(headerName.toLocal8Bit());
+
+ if (headerIndex == -1)
+ q->setRawHeader(headerName.toLocal8Bit(), headersValue.toLocal8Bit());
+ else
+ q->setHeader(static_cast<QNetworkRequest::KnownHeaders>(headerIndex), (QVariant)headersValue);
+ }
+ }
+ emit q->metaDataChanged();
+}
+
+void QNetworkReplyWasmImplPrivate::_q_bufferOutgoingDataFinished()
+{
+ Q_Q(QNetworkReplyWasmImpl);
+
+ // make sure this is only called once, ever.
+ //_q_bufferOutgoingData may call it or the readChannelFinished emission
+ if (state != Buffering)
+ return;
+
+ // disconnect signals
+ QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
+ QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
+
+ // finally, start the request
+ doSendRequest();
+}
+
+void QNetworkReplyWasmImplPrivate::_q_bufferOutgoingData()
+{
+ Q_Q(QNetworkReplyWasmImpl);
+
+ if (!outgoingDataBuffer) {
+ // first call, create our buffer
+ outgoingDataBuffer = QSharedPointer<QRingBuffer>::create();
+
+ QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
+ QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
+ }
+
+ qint64 bytesBuffered = 0;
+ qint64 bytesToBuffer = 0;
+
+ // read data into our buffer
+ forever {
+ bytesToBuffer = outgoingData->bytesAvailable();
+ // unknown? just try 2 kB, this also ensures we always try to read the EOF
+ if (bytesToBuffer <= 0)
+ bytesToBuffer = 2*1024;
+
+ char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
+ bytesBuffered = outgoingData->read(dst, bytesToBuffer);
+
+ if (bytesBuffered == -1) {
+ // EOF has been reached.
+ outgoingDataBuffer->chop(bytesToBuffer);
+
+ _q_bufferOutgoingDataFinished();
+ break;
+ } else if (bytesBuffered == 0) {
+ // nothing read right now, just wait until we get called again
+ outgoingDataBuffer->chop(bytesToBuffer);
+
+ break;
+ } else {
+ // don't break, try to read() again
+ outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
+ }
+ }
+}
+
+//taken from qhttpthreaddelegate.cpp
+QNetworkReply::NetworkError QNetworkReplyWasmImplPrivate::statusCodeFromHttp(int httpStatusCode, const QUrl &url)
+{
+ QNetworkReply::NetworkError code;
+ // we've got an error
+ switch (httpStatusCode) {
+ case 400: // Bad Request
+ code = QNetworkReply::ProtocolInvalidOperationError;
+ break;
+
+ case 401: // Authorization required
+ code = QNetworkReply::AuthenticationRequiredError;
+ break;
+
+ case 403: // Access denied
+ code = QNetworkReply::ContentAccessDenied;
+ break;
+
+ case 404: // Not Found
+ code = QNetworkReply::ContentNotFoundError;
+ break;
+
+ case 405: // Method Not Allowed
+ code = QNetworkReply::ContentOperationNotPermittedError;
+ break;
+
+ case 407:
+ code = QNetworkReply::ProxyAuthenticationRequiredError;
+ break;
+
+ case 409: // Resource Conflict
+ code = QNetworkReply::ContentConflictError;
+ break;
+
+ case 410: // Content no longer available
+ code = QNetworkReply::ContentGoneError;
+ break;
+
+ case 418: // I'm a teapot
+ code = QNetworkReply::ProtocolInvalidOperationError;
+ break;
+
+ case 500: // Internal Server Error
+ code = QNetworkReply::InternalServerError;
+ break;
+
+ case 501: // Server does not support this functionality
+ code = QNetworkReply::OperationNotImplementedError;
+ break;
+
+ case 503: // Service unavailable
+ code = QNetworkReply::ServiceUnavailableError;
+ break;
+
+ default:
+ if (httpStatusCode > 500) {
+ // some kind of server error
+ code = QNetworkReply::UnknownServerError;
+ } else if (httpStatusCode >= 400) {
+ // content error we did not handle above
+ code = QNetworkReply::UnknownContentError;
+ } else {
+ qWarning("QNetworkAccess: got HTTP status code %d which is not expected from url: \"%s\"",
+ httpStatusCode, qPrintable(url.toString()));
+ code = QNetworkReply::ProtocolFailure;
+ }
+ };
+
+ return code;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkreplywasmimpl_p.h b/src/network/access/qnetworkreplywasmimpl_p.h
new file mode 100644
index 0000000000..a707390503
--- /dev/null
+++ b/src/network/access/qnetworkreplywasmimpl_p.h
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKREPLYWASMIMPL_H
+#define QNETWORKREPLYWASMIMPL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qnetworkreply.h"
+#include "qnetworkreply_p.h"
+#include "qnetworkaccessmanager.h"
+
+#include <QtCore/qfile.h>
+
+#include <private/qtnetworkglobal_p.h>
+#include <private/qabstractfileengine_p.h>
+
+#include <emscripten.h>
+#include <emscripten/html5.h>
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+
+class QNetworkReplyWasmImplPrivate;
+class QNetworkReplyWasmImpl: public QNetworkReply
+{
+ Q_OBJECT
+public:
+ QNetworkReplyWasmImpl(QObject *parent = nullptr);
+ ~QNetworkReplyWasmImpl();
+ virtual void abort() override;
+
+ // reimplemented from QNetworkReply
+ virtual void close() override;
+ virtual qint64 bytesAvailable() const override;
+ virtual bool isSequential () const override;
+ qint64 size() const override;
+
+ virtual qint64 readData(char *data, qint64 maxlen) override;
+
+ void setup(QNetworkAccessManager::Operation op, const QNetworkRequest &request,
+ QIODevice *outgoingData);
+
+ Q_DECLARE_PRIVATE(QNetworkReplyWasmImpl)
+
+ Q_PRIVATE_SLOT(d_func(), void emitReplyError(QNetworkReply::NetworkError errorCode, const QString &errorString))
+ Q_PRIVATE_SLOT(d_func(), void emitDataReadProgress(qint64 done, qint64 total))
+ Q_PRIVATE_SLOT(d_func(), void dataReceived(char *buffer, int bufferSize))
+
+private:
+ QByteArray methodName() const;
+
+};
+
+class QNetworkReplyWasmImplPrivate: public QNetworkReplyPrivate
+{
+public:
+ QNetworkReplyWasmImplPrivate();
+ ~QNetworkReplyWasmImplPrivate();
+
+ QNetworkAccessManagerPrivate *managerPrivate;
+ void doSendRequest();
+
+ void jsRequest(const QString &verb, const QString &url, void *, void *, void *, void *);
+
+ static void onLoadCallback(void *data, int statusCode, int statusReason, int readyState, int textBuffer, int size);
+ static void onProgressCallback(void *data, int done, int bytesTotal, uint timestamp);
+ static void onRequestErrorCallback(void *data, int statusCode, int statusReason);
+ static void onStateChangedCallback(int status);
+ static void onResponseHeadersCallback(void *data, int headers);
+
+ void emitReplyError(QNetworkReply::NetworkError errorCode, const QString &);
+ void emitDataReadProgress(qint64 done, qint64 total);
+ void dataReceived(char *buffer, int bufferSize);
+ void headersReceived(char *buffer);
+
+ void setup(QNetworkAccessManager::Operation op, const QNetworkRequest &request,
+ QIODevice *outgoingData);
+
+ State state;
+ void _q_bufferOutgoingData();
+ void _q_bufferOutgoingDataFinished();
+
+ QSharedPointer<QAtomicInt> pendingDownloadData;
+ QSharedPointer<QAtomicInt> pendingDownloadProgress;
+
+ qint64 bytesDownloaded;
+ qint64 bytesBuffered;
+
+ qint64 downloadBufferReadPosition;
+ qint64 downloadBufferCurrentSize;
+ qint64 totalDownloadSize;
+ qint64 percentFinished;
+ QByteArray downloadBuffer;
+
+ QIODevice *outgoingData;
+ QSharedPointer<QRingBuffer> outgoingDataBuffer;
+
+ static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url);
+ Q_DECLARE_PUBLIC(QNetworkReplyWasmImpl)
+};
+
+QT_END_NAMESPACE
+
+//Q_DECLARE_METATYPE(QNetworkRequest::KnownHeaders)
+
+#endif // QNETWORKREPLYWASMIMPL_H
diff --git a/src/network/configure.json b/src/network/configure.json
index 32fcfb499f..327131ba11 100644
--- a/src/network/configure.json
+++ b/src/network/configure.json
@@ -198,7 +198,7 @@
"label": "OpenSSL",
"enable": "input.openssl == 'yes' || input.openssl == 'linked' || input.openssl == 'runtime'",
"disable": "input.openssl == 'no' || input.ssl == 'no'",
- "autoDetect": "!config.winrt",
+ "autoDetect": "!config.winrt && !config.wasm",
"condition": "!features.securetransport && (features.openssl-linked || libs.openssl_headers)",
"output": [
"privateFeature",
@@ -288,6 +288,7 @@
"networkinterface": {
"label": "QNetworkInterface",
"purpose": "Supports enumerating a host's IP addresses and network interfaces.",
+ "condition": "!config.wasm",
"section": "Networking",
"output": [ "publicFeature", "feature" ]
},
diff --git a/src/opengl/qglframebufferobject.cpp b/src/opengl/qglframebufferobject.cpp
index 0b386ededc..0b2ddf97fe 100644
--- a/src/opengl/qglframebufferobject.cpp
+++ b/src/opengl/qglframebufferobject.cpp
@@ -103,6 +103,14 @@ extern QImage qt_gl_read_frame_buffer(const QSize&, bool, bool);
#define GL_DRAW_FRAMEBUFFER 0x8CA9
#endif
+#ifndef GL_DEPTH_STENCIL_ATTACHMENT
+#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
+#endif
+
+#ifndef GL_DEPTH_STENCIL
+#define GL_DEPTH_STENCIL 0x84F9
+#endif
+
/*!
\class QGLFramebufferObjectFormat
\inmodule QtOpenGL
@@ -562,6 +570,7 @@ void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz,
funcs.glGenRenderbuffers(1, &depth_buffer);
funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer));
+#ifndef Q_OS_WASM
if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
GL_DEPTH24_STENCIL8, size.width(), size.height());
@@ -574,6 +583,19 @@ void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz,
GL_RENDERBUFFER, depth_buffer);
funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, stencil_buffer);
+#else
+ // webgl does not allow separate depth and stencil attachments
+ if (samples != 0) {
+ funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
+ GL_DEPTH_STENCIL, size.width(), size.height());
+ } else {
+ funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL,
+ size.width(), size.height());
+ }
+ stencil_buffer = depth_buffer;
+ funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, depth_buffer);
+#endif
valid = checkFramebufferStatus();
if (!valid) {
diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro
index e61887618f..5bf2b77421 100644
--- a/src/plugins/platforms/platforms.pro
+++ b/src/plugins/platforms/platforms.pro
@@ -46,6 +46,8 @@ haiku {
SUBDIRS += haiku
}
+wasm: SUBDIRS = wasm
+
qtConfig(mirclient): SUBDIRS += mirclient
qtConfig(integrityfb): SUBDIRS += integrity
diff --git a/src/plugins/platforms/wasm/main.cpp b/src/plugins/platforms/wasm/main.cpp
new file mode 100644
index 0000000000..a4d23b4e0d
--- /dev/null
+++ b/src/plugins/platforms/wasm/main.cpp
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qpa/qplatformintegrationplugin.h>
+#include "qwasmintegration.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWasmIntegrationPlugin : public QPlatformIntegrationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "wasm.json")
+public:
+ QPlatformIntegration *create(const QString &, const QStringList &) override;
+};
+
+QPlatformIntegration *QWasmIntegrationPlugin::create(const QString& system, const QStringList& paramList)
+{
+ Q_UNUSED(paramList);
+ if (!system.compare(QStringLiteral("wasm"), Qt::CaseInsensitive))
+ return new QWasmIntegration;
+
+ return nullptr;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/platforms/wasm/qtloader.js b/src/plugins/platforms/wasm/qtloader.js
new file mode 100644
index 0000000000..37a5308034
--- /dev/null
+++ b/src/plugins/platforms/wasm/qtloader.js
@@ -0,0 +1,516 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// QtLoader provides javascript API for managing Qt application modules.
+//
+// QtLoader provides API on top of Emscripten which supports common lifecycle
+// tasks such as displaying placeholder content while the module downloads,
+// handing application exits, and checking for browser wasm support.
+//
+// There are two usage modes:
+// * Managed: QtLoader owns and manages the HTML display elements like
+// the loader and canvas.
+// * External: The embedding HTML page owns the display elements. QtLoader
+// provides event callbacks which the page reacts to.
+//
+// Managed mode usage:
+//
+// var config = {
+// containerElements : [$("container-id")];
+// }
+// var qtLoader = QtLoader(config);
+// qtLoader.loadEmscriptenModule("applicationName");
+//
+// External mode.usage:
+//
+// var config = {
+// showLoader: function() {
+// loader.style.display = 'block'
+// canvas.style.display = 'hidden'
+// },
+// showCanvas: function() {
+// loader.style.display = 'hidden'
+// canvas.style.display = 'block'
+// return canvas;
+// }
+// }
+// var qtLoader = QtLoader(config);
+// qtLoader.loadEmscriptenModule("applicationName");
+//
+// Config keys
+//
+// containerElements : [container-element, ...]
+// One or more HTML elements. QtLoader will display loader elements
+// on these while loading the applicaton, and replace the loader with a
+// canvas on load complete.
+// showLoader : function(status, containerElement)
+// Optional loading element constructor function. Implement to create
+// a custom loading screen. This function may be called multiple times,
+// while preparing the application binary. "status" is a string
+// containing the loading sub-status, and may be either "Downloading",
+// or "Compiling". The browser may be using streaming compilation, in
+// which case the wasm module is compiled during downloading and the
+// there is no separate compile step.
+// showCanvas : function(containerElement)
+// Optional canvas constructor function. Implement to create custom
+// canvas elements.
+// showExit : function(crashed, exitCode, containerElement)
+// Optional exited element constructor function.
+// showError : function(crashed, exitCode, containerElement)
+// Optional error element constructor function.
+//
+// path : <string>
+// Prefix path for wasm file, realative to the loading HMTL file.
+// restartMode : "DoNotRestart", "RestartOnExit", "RestartOnCrash"
+// Controls whether the application should be reloaded on exits. The default is "DoNotRestart"
+// restartType : "RestartModule", "ReloadPage"
+// restartLimit : <int>
+// Restart attempts limit. The default is 10.
+// stdoutEnabled : <bool>
+// stderrEnabled : <bool>
+// environment : <object>
+// key-value environment variable pairs.
+//
+// QtLoader object API
+//
+// webAssemblySupported : bool
+// webGLSupported : bool
+// canLoadQt : bool
+// Reports if WebAssembly and WebGL are supported. These are requirements for
+// running Qt applications.
+// loadEmscriptenModule(applicationName)
+// Loads the application from the given emscripten javascript module file and wasm file
+// status
+// One of "Created", "Loading", "Running", "Exited".
+// crashed
+// Set to true if there was an unclean exit.
+// exitCode
+// main()/emscripten_force_exit() return code. Valid on status change to
+// "Exited", iff crashed is false.
+// exitText
+// Abort/exit message.
+
+
+var Module = {}
+
+function QtLoader(config)
+{
+ function webAssemblySupported() {
+ return typeof WebAssembly !== "undefined"
+ }
+
+ function webGLSupported() {
+ // We expect that WebGL is supported if WebAssembly is; however
+ // the GPU may be blacklisted.
+ try {
+ var canvas = document.createElement("canvas");
+ return !!(window.WebGLRenderingContext && (canvas.getContext("webgl") || canvas.getContext("experimental-webgl")));
+ } catch (e) {
+ return false;
+ }
+ }
+
+ function canLoadQt() {
+ // The current Qt implementation requires WebAssembly (asm.js is not in use),
+ // and also WebGL (there is no raster fallback).
+ return webAssemblySupported() && webGLSupported();
+ }
+
+ function removeChildren(element) {
+ while (element.firstChild) element.removeChild(element.firstChild);
+ }
+
+ // Set default state handler functions if needed
+ if (config.containerElements !== undefined) {
+ config.showError = config.showError || function(errorText, container) {
+ removeChildren(container);
+ var errorTextElement = document.createElement("text");
+ errorTextElement.className = "QtError"
+ errorTextElement.innerHTML = errorText;
+ return errorTextElement;
+ }
+
+ config.showLoader = config.showLoader || function(loadingState, container) {
+ removeChildren(container);
+ var loadingText = document.createElement("text");
+ loadingText.className = "QtLoading"
+ loadingText.innerHTML = '<p><center> ${loadingState}...</center><p>';
+ return loadingText;
+ };
+
+ config.showCanvas = config.showCanvas || function(container) {
+ removeChildren(container);
+ var canvas = document.createElement("canvas");
+ canvas.className = "QtCanvas"
+ canvas.style = "height: 100%; width: 100%;"
+ return canvas;
+ }
+
+ config.showExit = config.showExit || function(crashed, exitCode, container) {
+ if (!crashed)
+ return undefined;
+
+ removeChildren(container);
+ var fontSize = 54;
+ var crashSymbols = ["\u{1F615}", "\u{1F614}", "\u{1F644}", "\u{1F928}", "\u{1F62C}",
+ "\u{1F915}", "\u{2639}", "\u{1F62E}", "\u{1F61E}", "\u{1F633}"];
+ var symbolIndex = Math.floor(Math.random() * crashSymbols.length);
+ var errorHtml = `<font size='${fontSize}'> ${crashSymbols[symbolIndex]} </font>`
+ var errorElement = document.createElement("text");
+ errorElement.className = "QtExit"
+ errorElement.innerHTML = errorHtml;
+ return errorElement;
+ }
+ }
+
+ config.restartMode = config.restartMode || "DoNotRestart";
+ config.restartLimit = config.restartLimit || 10;
+
+ if (config.stdoutEnabled === undefined) config.stdoutEnabled = true;
+ if (config.stderrEnabled === undefined) config.stderrEnabled = true;
+
+ // Make sure config.path is defined and ends with "/" if needed
+ if (config.path === undefined)
+ config.path = "";
+ if (config.path.length > 0 && !config.path.endsWith("/"))
+ config.path = config.path.concat("/");
+
+ if (config.environment === undefined)
+ config.environment = {};
+
+ var publicAPI = {};
+ publicAPI.webAssemblySupported = webAssemblySupported();
+ publicAPI.webGLSupported = webGLSupported();
+ publicAPI.canLoadQt = canLoadQt();
+ publicAPI.canLoadApplication = canLoadQt();
+ publicAPI.status = undefined;
+ publicAPI.loadEmscriptenModule = loadEmscriptenModule;
+
+ restartCount = 0;
+
+ function fetchResource(filePath) {
+ var fullPath = config.path + filePath;
+ return fetch(fullPath).then(function(response) {
+ if (!response.ok) {
+ self.error = response.status + " " + response.statusText + " " + response.url;
+ setStatus("Error");
+ return Promise.reject(self.error)
+ } else {
+ return response;
+ }
+ });
+ }
+
+ function fetchText(filePath) {
+ return fetchResource(filePath).then(function(response) {
+ return response.text();
+ });
+ }
+
+ function fetchThenCompileWasm(response) {
+ return response.arrayBuffer().then(function(data) {
+ self.loaderSubState = "Compiling";
+ setStatus("Loading") // trigger loaderSubState udpate
+ return WebAssembly.compile(data);
+ });
+ }
+
+ function fetchCompileWasm(filePath) {
+ return fetchResource(filePath).then(function(response) {
+ if (typeof WebAssembly.compileStreaming !== "undefined") {
+ self.loaderSubState = "Downloading/Compiling";
+ setStatus("Loading");
+ return WebAssembly.compileStreaming(response).catch(function(error) {
+ // compileStreaming may/will fail if the server does not set the correct
+ // mime type (application/wasm) for the wasm file. Fall back to fetch,
+ // then compile in this case.
+ return fetchThenCompileWasm(response);
+ });
+ } else {
+ // Fall back to fetch, then compile if compileStreaming is not supported
+ return fetchThenCompileWasm(response);
+ }
+ });
+ }
+
+ function loadEmscriptenModule(applicationName) {
+
+ // Loading in qtloader.js goes through four steps:
+ // 1) Check prerequisites
+ // 2) Download resources
+ // 3) Configure the emscripten Module object
+ // 4) Start the emcripten runtime, after which emscripten takes over
+
+ // Check for Wasm & WebGL support; set error and return before downloading resources if missing
+ if (!webAssemblySupported()) {
+ self.error = "Error: WebAssembly is not supported"
+ setStatus("Error");
+ return;
+ }
+ if (!webGLSupported()) {
+ self.error = "Error: WebGL is not supported"
+ setStatus("Error");
+ return;
+ }
+
+ // Continue waiting if loadEmscriptenModule() is called again
+ if (publicAPI.status == "Loading")
+ return;
+ self.loaderSubState = "Downloading";
+ setStatus("Loading");
+
+ // Fetch emscripten generated javascript runtime
+ var emscriptenModuleSource = undefined
+ var emscriptenModuleSourcePromise = fetchText(applicationName + ".js").then(function(source) {
+ emscriptenModuleSource = source
+ });
+
+ // Fetch and compile wasm module
+ var wasmModule = undefined;
+ var wasmModulePromise = fetchCompileWasm(applicationName + ".wasm").then(function (module) {
+ wasmModule = module;
+ });
+
+ // Wait for all resources ready
+ Promise.all([emscriptenModuleSourcePromise, wasmModulePromise]).then(function(){
+ completeLoadEmscriptenModule(applicationName, emscriptenModuleSource, wasmModule);
+ }).catch(function(error) {
+ self.error = error;
+ setStatus("Error");
+ });
+ }
+
+ function completeLoadEmscriptenModule(applicationName, emscriptenModuleSource, wasmModule) {
+
+ // The wasm binary has been compiled into a module during resource download,
+ // and is ready to be instantiated. Define the instantiateWasm callback which
+ // emscripten will call to create the instance.
+ Module.instantiateWasm = function(imports, successCallback) {
+ return WebAssembly.instantiate(wasmModule, imports).then(function(instance) {
+ successCallback(instance);
+ return instance;
+ }, function(error) {
+ self.error = error;
+ setStatus("Error");
+ });
+ };
+
+ Module.locateFile = Module.locateFile || function(filename) {
+ return config.path + filename;
+ };
+
+ // Attach status callbacks
+ Module.setStatus = Module.setStatus || function(text) {
+ // Currently the only usable status update from this function
+ // is "Running..."
+ if (text.startsWith("Running"))
+ setStatus("Running");
+ };
+ Module.monitorRunDependencies = Module.monitorRunDependencies || function(left) {
+ // console.log("monitorRunDependencies " + left)
+ };
+
+ // Attach standard out/err callbacks.
+ Module.print = Module.print || function(text) {
+ if (config.stdoutEnabled)
+ console.log(text)
+ };
+ Module.printErr = Module.printErr || function(text) {
+ // Filter out OpenGL getProcAddress warnings. Qt tries to resolve
+ // all possible function/extension names at startup which causes
+ // emscripten to spam the console log with warnings.
+ if (text.startsWith !== undefined && text.startsWith("bad name in getProcAddress:"))
+ return;
+
+ if (config.stderrEnabled)
+ console.log(text)
+ };
+
+ // Error handling: set status to "Exited", update crashed and
+ // exitCode according to exit type.
+ // Emscripten will typically call printErr with the error text
+ // as well. Note that emscripten may also throw exceptions from
+ // async callbacks. These should be handled in window.onerror by user code.
+ Module.onAbort = Module.onAbort || function(text) {
+ publicAPI.crashed = true;
+ publicAPI.exitText = text;
+ setStatus("Exited");
+ };
+ Module.quit = Module.quit || function(code, exception) {
+ if (exception.name == "ExitStatus") {
+ // Clean exit with code
+ publicAPI.exitText = undefined
+ publicAPI.exitCode = code;
+ } else {
+ publicAPI.exitText = exception.toString();
+ publicAPI.crashed = true;
+ }
+ setStatus("Exited");
+ };
+
+ // Set environment variables
+ Module.preRun = Module.preRun || []
+ Module.preRun.push(function() {
+ for (var [key, value] of Object.entries(config.environment)) {
+ Module.ENV[key.toUpperCase()] = value;
+ }
+ });
+
+ config.restart = function() {
+
+ // Restart by reloading the page. This will wipe all state which means
+ // reload loops can't be prevented.
+ if (config.restartType == "ReloadPage") {
+ location.reload();
+ }
+
+ // Restart by readling the emscripten app module.
+ ++self.restartCount;
+ if (self.restartCount > config.restartLimit) {
+ self.error = "Error: This application has crashed too many times and has been disabled. Reload the page to try again."
+ setStatus("Error");
+ return;
+ }
+ loadEmscriptenModule(applicationName);
+ };
+
+ publicAPI.exitCode = undefined;
+ publicAPI.exitText = undefined;
+ publicAPI.crashed = false;
+
+ // Finally evaluate the emscripten application script, which will
+ // reference the global Module object created above.
+ self.eval(emscriptenModuleSource); // ES5 indirect global scope eval
+ }
+
+ function setErrorContent() {
+ if (config.containerElements === undefined) {
+ if (config.showError !== undefined)
+ config.showError(self.error);
+ return;
+ }
+
+ for (container of config.containerElements) {
+ var errorElement = config.showError(self.error, container);
+ container.appendChild(errorElement);
+ }
+ }
+
+ function setLoaderContent() {
+ if (config.containerElements === undefined) {
+ if (config.showLoader !== undefined)
+ config.showLoader(self.loaderSubState);
+ return;
+ }
+
+ for (container of config.containerElements) {
+ var loaderElement = config.showLoader(self.loaderSubState, container);
+ container.appendChild(loaderElement);
+ }
+ }
+
+ function setCanvasContent() {
+ var firstCanvas;
+ if (config.containerElements === undefined) {
+ firstCanvas = config.showCanvas();
+ } else {
+ for (container of config.containerElements) {
+ var canvasElement = config.showCanvas(container);
+ container.appendChild(canvasElement);
+ }
+ firstCanvas = config.containerElements[0].firstChild;
+ }
+
+ if (Module.canvas === undefined) {
+ Module.canvas = firstCanvas;
+ }
+ }
+
+ function setExitContent() {
+
+ // publicAPI.crashed = true;
+
+ if (publicAPI.status != "Exited")
+ return;
+
+ if (config.containerElements === undefined) {
+ if (config.showExit !== undefined)
+ config.showExit(publicAPI.crashed, publicAPI.exitCode);
+ return;
+ }
+
+ if (!publicAPI.crashed)
+ return;
+
+ for (container of config.containerElements) {
+ var loaderElement = config.showExit(publicAPI.crashed, publicAPI.exitCode, container);
+ if (loaderElement !== undefined)
+ container.appendChild(loaderElement);
+ }
+ }
+
+ var committedStatus = undefined;
+ function handleStatusChange() {
+ if (publicAPI.status != "Loading" && committedStatus == publicAPI.status)
+ return;
+ committedStatus = publicAPI.status;
+
+ if (publicAPI.status == "Error") {
+ setErrorContent();
+ } else if (publicAPI.status == "Loading") {
+ setLoaderContent();
+ } else if (publicAPI.status == "Running") {
+ setCanvasContent();
+ } else if (publicAPI.status == "Exited") {
+ if (config.restartMode == "RestartOnExit" ||
+ config.restartMode == "RestartOnCrash" && publicAPI.crashed) {
+ committedStatus = undefined;
+ config.restart();
+ } else {
+ setExitContent();
+ }
+ }
+
+ // Send status change notification
+ if (config.statusChanged)
+ config.statusChanged(publicAPI.status);
+ }
+
+ function setStatus(status) {
+ if (status != "Loading" && publicAPI.status == status)
+ return;
+ publicAPI.status = status;
+
+ window.setTimeout(function() { handleStatusChange(); }, 0);
+ }
+
+ setStatus("Created");
+
+ return publicAPI;
+}
diff --git a/src/plugins/platforms/wasm/qtlogo.svg b/src/plugins/platforms/wasm/qtlogo.svg
new file mode 100644
index 0000000000..cb8989bb79
--- /dev/null
+++ b/src/plugins/platforms/wasm/qtlogo.svg
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="462pt"
+ height="339pt"
+ viewBox="0 0 462 339"
+ version="1.1"
+ id="svg2"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="TheQtCompany_logo_2.svg">
+ <metadata
+ id="metadata20">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs18" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1536"
+ inkscape:window-height="801"
+ id="namedview16"
+ showgrid="false"
+ inkscape:zoom="1.1138643"
+ inkscape:cx="270.58047"
+ inkscape:cy="174.65092"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
+ <path
+ fill="#41cd52"
+ d=" M 63.50 0.00 L 462.00 0.00 L 462.00 274.79 C 440.60 296.26 419.13 317.66 397.61 339.00 L 0.00 339.00 L 0.00 63.39 C 21.08 42.18 42.34 21.13 63.50 0.00 Z"
+ id="path6" />
+ <path
+ d=" M 122.37 71.33 C 137.50 61.32 156.21 58.79 174.00 58.95 C 190.94 59.16 208.72 62.13 222.76 72.24 C 232.96 79.41 239.59 90.48 244.01 101.93 C 251.16 120.73 253.26 141.03 253.50 161.01 C 253.53 181.13 252.62 201.69 245.96 220.86 C 241.50 233.90 233.01 245.48 221.81 253.52 C 229.87 266.58 238.09 279.54 246.15 292.60 C 236.02 297.27 225.92 301.97 215.78 306.62 C 207.15 292.38 198.56 278.11 189.90 263.89 C 178.19 265.81 166.21 265.66 154.44 264.36 C 140.34 262.67 125.97 258.37 115.09 248.88 C 106.73 241.64 101.48 231.51 97.89 221.21 C 92.01 203.79 90.43 185.25 90.16 166.97 C 90.02 147.21 91.28 127.14 97.24 108.18 C 101.85 93.92 109.48 79.69 122.37 71.33 Z"
+ id="path8"
+ fill="#ffffff" />
+ <path
+ d=" M 294.13 70.69 C 304.73 70.68 315.33 70.68 325.93 70.69 C 325.96 84.71 325.92 98.72 325.95 112.74 C 339.50 112.76 353.05 112.74 366.60 112.75 C 366.37 121.85 366.12 130.95 365.86 140.05 C 352.32 140.08 338.79 140.04 325.25 140.07 C 325.28 163.05 325.18 186.03 325.30 209.01 C 325.56 215.30 325.42 221.94 328.19 227.75 C 330.21 232.23 335.65 233.38 340.08 233.53 C 348.43 233.50 356.77 233.01 365.12 232.86 C 365.63 241.22 366.12 249.59 366.60 257.95 C 349.99 260.74 332.56 264.08 316.06 258.86 C 309.11 256.80 302.63 252.19 299.81 245.32 C 294.76 233.63 294.35 220.62 294.13 208.07 C 294.11 185.40 294.13 162.74 294.12 140.07 C 286.73 140.05 279.34 140.08 271.95 140.05 C 271.93 130.96 271.93 121.86 271.95 112.76 C 279.34 112.73 286.72 112.77 294.11 112.74 C 294.14 98.72 294.10 84.71 294.13 70.69 Z"
+ id="path10"
+ fill="#ffffff" />
+ <path
+ fill="#41cd52"
+ d=" M 160.51 87.70 C 170.80 86.36 181.60 86.72 191.34 90.61 C 199.23 93.73 205.93 99.84 209.47 107.58 C 214.90 119.31 216.98 132.26 218.03 145.05 C 219.17 162.07 219.01 179.25 216.66 196.17 C 215.01 206.24 212.66 216.85 205.84 224.79 C 198.92 232.76 188.25 236.18 178.01 236.98 C 167.21 237.77 155.82 236.98 146.07 231.87 C 140.38 228.84 135.55 224.09 132.73 218.27 C 129.31 211.30 127.43 203.69 126.11 196.07 C 122.13 171.91 121.17 146.91 126.61 122.89 C 128.85 113.83 132.11 104.53 138.73 97.70 C 144.49 91.85 152.51 88.83 160.51 87.70 Z"
+ id="path12" />
+</svg>
diff --git a/src/plugins/platforms/wasm/qwasmbackingstore.cpp b/src/plugins/platforms/wasm/qwasmbackingstore.cpp
new file mode 100644
index 0000000000..5b40c44807
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmbackingstore.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwasmbackingstore.h"
+#include "qwasmwindow.h"
+#include "qwasmcompositor.h"
+
+#include <QtGui/qopengltexture.h>
+#include <QtGui/qmatrix4x4.h>
+#include <QtGui/qpainter.h>
+#include <private/qguiapplication_p.h>
+#include <qpa/qplatformscreen.h>
+
+#include <QtGui/qbackingstore.h>
+
+QT_BEGIN_NAMESPACE
+
+QWasmBackingStore::QWasmBackingStore(QWasmCompositor *compositor, QWindow *window)
+ : QPlatformBackingStore(window)
+ , m_compositor(compositor)
+ , m_texture(new QOpenGLTexture(QOpenGLTexture::Target2D))
+{
+ QWasmWindow *wasmWindow = static_cast<QWasmWindow *>(window->handle());
+ if (wasmWindow)
+ wasmWindow->setBackingStore(this);
+}
+
+QWasmBackingStore::~QWasmBackingStore()
+{
+}
+
+QPaintDevice *QWasmBackingStore::paintDevice()
+{
+ return &m_image;
+}
+
+void QWasmBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)
+{
+ Q_UNUSED(window);
+ Q_UNUSED(region);
+ Q_UNUSED(offset);
+
+ m_dirty |= region;
+ m_compositor->requestRedraw();
+}
+
+void QWasmBackingStore::updateTexture()
+{
+ if (m_dirty.isNull())
+ return;
+
+ if (!m_texture->isCreated()) {
+ m_texture->setMinificationFilter(QOpenGLTexture::Nearest);
+ m_texture->setMagnificationFilter(QOpenGLTexture::Nearest);
+ m_texture->setWrapMode(QOpenGLTexture::ClampToEdge);
+ m_texture->setData(m_image, QOpenGLTexture::DontGenerateMipMaps);
+ m_texture->create();
+ }
+ m_texture->bind();
+
+ QRegion fixed;
+ QRect imageRect = m_image.rect();
+
+ for (const QRect &rect : m_dirty) {
+
+ // Convert device-independent dirty region to device region
+ qreal dpr = m_image.devicePixelRatio();
+ QRect deviceRect = QRect(rect.topLeft() * dpr, rect.size() * dpr);
+
+ // intersect with image rect to be sure
+ QRect r = imageRect & deviceRect;
+ // if the rect is wide enough it is cheaper to just extend it instead of doing an image copy
+ if (r.width() >= imageRect.width() / 2) {
+ r.setX(0);
+ r.setWidth(imageRect.width());
+ }
+
+ fixed |= r;
+ }
+
+ for (const QRect &rect : fixed) {
+ // if the sub-rect is full-width we can pass the image data directly to
+ // OpenGL instead of copying, since there is no gap between scanlines
+ if (rect.width() == imageRect.width()) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, rect.y(), rect.width(), rect.height(), GL_RGBA, GL_UNSIGNED_BYTE,
+ m_image.constScanLine(rect.y()));
+ } else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, GL_UNSIGNED_BYTE,
+ m_image.copy(rect).constBits());
+ }
+ }
+ /* End of code taken from QEGLPlatformBackingStore */
+
+ m_dirty = QRegion();
+}
+
+void QWasmBackingStore::beginPaint(const QRegion &region)
+{
+ m_dirty |= region;
+ // Keep backing store device pixel ratio in sync with window
+ if (m_image.devicePixelRatio() != window()->devicePixelRatio())
+ resize(backingStore()->size(), backingStore()->staticContents());
+
+ QPainter painter(&m_image);
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ const QColor blank = Qt::transparent;
+ for (const QRect &rect : region)
+ painter.fillRect(rect, blank);
+}
+
+void QWasmBackingStore::resize(const QSize &size, const QRegion &staticContents)
+{
+ Q_UNUSED(staticContents)
+
+ m_image = QImage(size * window()->devicePixelRatio(), QImage::Format_RGB32);
+ m_image.setDevicePixelRatio(window()->devicePixelRatio());
+
+ if (m_texture->isCreated())
+ m_texture->destroy();
+}
+
+QImage QWasmBackingStore::toImage() const
+{
+ // used by QPlatformBackingStore::composeAndFlush
+ return m_image;
+}
+
+const QImage &QWasmBackingStore::getImageRef() const
+{
+ return m_image;
+}
+
+const QOpenGLTexture *QWasmBackingStore::getUpdatedTexture()
+{
+ updateTexture();
+ return m_texture.data();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmbackingstore.h b/src/plugins/platforms/wasm/qwasmbackingstore.h
new file mode 100644
index 0000000000..6ffa262e3d
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmbackingstore.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWASMBACKINGSTORE_H
+#define QWASMBACKINGSTORE_H
+
+#include <qpa/qplatformbackingstore.h>
+#include <QtGui/qimage.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLTexture;
+class QRegion;
+class QWasmCompositor;
+
+class QWasmBackingStore : public QPlatformBackingStore
+{
+public:
+ QWasmBackingStore(QWasmCompositor *compositor, QWindow *window);
+ ~QWasmBackingStore();
+
+ QPaintDevice *paintDevice() override;
+
+ void beginPaint(const QRegion &) override;
+ void flush(QWindow *window, const QRegion &region, const QPoint &offset) override;
+ void resize(const QSize &size, const QRegion &staticContents) override;
+ QImage toImage() const override;
+ const QImage &getImageRef() const;
+
+ const QOpenGLTexture *getUpdatedTexture();
+
+protected:
+ void updateTexture();
+
+private:
+ QWasmCompositor *m_compositor;
+ QImage m_image;
+ QScopedPointer<QOpenGLTexture> m_texture;
+ QRegion m_dirty;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWASMBACKINGSTORE_H
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp
new file mode 100644
index 0000000000..f3ea013325
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp
@@ -0,0 +1,721 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwasmcompositor.h"
+#include "qwasmwindow.h"
+#include "qwasmstylepixmaps_p.h"
+
+#include <QtGui/qopengltexture.h>
+
+#include <QtGui/private/qwindow_p.h>
+#include <QtGui/qopenglcontext.h>
+#include <QtGui/qopenglfunctions.h>
+#include <QtGui/qopengltextureblitter.h>
+#include <QtGui/qpainter.h>
+#include <private/qpixmapcache_p.h>
+
+#include <private/qguiapplication_p.h>
+
+#include <qpa/qwindowsysteminterface.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtGui/qguiapplication.h>
+
+Q_GUI_EXPORT int qt_defaultDpiX();
+
+QWasmCompositedWindow::QWasmCompositedWindow()
+ : window(nullptr)
+ , parentWindow(nullptr)
+ , flushPending(false)
+ , visible(false)
+{
+}
+
+QWasmCompositor::QWasmCompositor()
+ : m_frameBuffer(nullptr)
+ , m_blitter(new QOpenGLTextureBlitter)
+ , m_needComposit(false)
+ , m_inFlush(false)
+ , m_inResize(false)
+ , m_isEnabled(true)
+ , m_targetDevicePixelRatio(1)
+{
+}
+
+QWasmCompositor::~QWasmCompositor()
+{
+ delete m_frameBuffer;
+}
+
+void QWasmCompositor::setEnabled(bool enabled)
+{
+ m_isEnabled = enabled;
+}
+
+void QWasmCompositor::addWindow(QWasmWindow *window, QWasmWindow *parentWindow)
+{
+ QWasmCompositedWindow compositedWindow;
+ compositedWindow.window = window;
+ compositedWindow.parentWindow = parentWindow;
+ m_compositedWindows.insert(window, compositedWindow);
+
+ if (parentWindow == 0)
+ m_windowStack.append(window);
+ else
+ m_compositedWindows[parentWindow].childWindows.append(window);
+
+ notifyTopWindowChanged(window);
+}
+
+void QWasmCompositor::removeWindow(QWasmWindow *window)
+{
+ QWasmWindow *platformWindow = m_compositedWindows[window].parentWindow;
+
+ if (platformWindow) {
+ QWasmWindow *parentWindow = window;
+ m_compositedWindows[parentWindow].childWindows.removeAll(window);
+ }
+
+ m_windowStack.removeAll(window);
+ m_compositedWindows.remove(window);
+
+ notifyTopWindowChanged(window);
+}
+
+void QWasmCompositor::setScreen(QWasmScreen *screen)
+{
+ m_screen = screen;
+}
+
+void QWasmCompositor::setVisible(QWasmWindow *window, bool visible)
+{
+ QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
+ if (compositedWindow.visible == visible)
+ return;
+
+ compositedWindow.visible = visible;
+ compositedWindow.flushPending = true;
+ if (visible)
+ compositedWindow.damage = compositedWindow.window->geometry();
+ else
+ m_globalDamage = compositedWindow.window->geometry(); // repaint previosly covered area.
+
+ requestRedraw();
+}
+
+void QWasmCompositor::raise(QWasmWindow *window)
+{
+ if (m_compositedWindows.size() <= 1)
+ return;
+
+ QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
+ compositedWindow.damage = compositedWindow.window->geometry();
+ m_windowStack.removeAll(window);
+ m_windowStack.append(window);
+
+ notifyTopWindowChanged(window);
+}
+
+void QWasmCompositor::lower(QWasmWindow *window)
+{
+ if (m_compositedWindows.size() <= 1)
+ return;
+
+ m_windowStack.removeAll(window);
+ m_windowStack.prepend(window);
+ QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
+ m_globalDamage = compositedWindow.window->geometry(); // repaint previosly covered area.
+
+ notifyTopWindowChanged(window);
+}
+
+void QWasmCompositor::setParent(QWasmWindow *window, QWasmWindow *parent)
+{
+ m_compositedWindows[window].parentWindow = parent;
+
+ requestRedraw();
+}
+
+void QWasmCompositor::flush(QWasmWindow *window, const QRegion &region)
+{
+ QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
+ compositedWindow.flushPending = true;
+ compositedWindow.damage = region;
+
+ requestRedraw();
+}
+
+int QWasmCompositor::windowCount() const
+{
+ return m_windowStack.count();
+}
+
+
+void QWasmCompositor::redrawWindowContent()
+{
+ // Redraw window content by sending expose events. This redraw
+ // will cause a backing store flush, which will call requestRedraw()
+ // to composit.
+ for (QWasmWindow *platformWindow : m_windowStack) {
+ QWindow *window = platformWindow->window();
+ QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(
+ window, QRect(QPoint(0, 0), window->geometry().size()));
+ }
+}
+
+void QWasmCompositor::requestRedraw()
+{
+ if (m_needComposit)
+ return;
+
+ m_needComposit = true;
+ QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
+}
+
+QWindow *QWasmCompositor::windowAt(QPoint p, int padding) const
+{
+ int index = m_windowStack.count() - 1;
+ // qDebug() << "window at" << "point" << p << "window count" << index;
+
+ while (index >= 0) {
+ const QWasmCompositedWindow &compositedWindow = m_compositedWindows[m_windowStack.at(index)];
+ //qDebug() << "windwAt testing" << compositedWindow.window <<
+
+ QRect geometry = compositedWindow.window->windowFrameGeometry()
+ .adjusted(-padding, -padding, padding, padding);
+
+ if (compositedWindow.visible && geometry.contains(p))
+ return m_windowStack.at(index)->window();
+ --index;
+ }
+
+ return 0;
+}
+
+QWindow *QWasmCompositor::keyWindow() const
+{
+ return m_windowStack.at(m_windowStack.count() - 1)->window();
+}
+
+bool QWasmCompositor::event(QEvent *ev)
+{
+ if (ev->type() == QEvent::UpdateRequest) {
+ if (m_isEnabled)
+ frame();
+ return true;
+ }
+
+ return QObject::event(ev);
+}
+
+void QWasmCompositor::blit(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, const QOpenGLTexture *texture, QRect targetGeometry)
+{
+ QMatrix4x4 m;
+ m.translate(-1.0f, -1.0f);
+
+ m.scale(2.0f / (float)screen->geometry().width(),
+ 2.0f / (float)screen->geometry().height());
+
+ m.translate((float)targetGeometry.width() / 2.0f,
+ (float)-targetGeometry.height() / 2.0f);
+
+ m.translate(targetGeometry.x(), screen->geometry().height() - targetGeometry.y());
+
+ m.scale(0.5f * (float)targetGeometry.width(),
+ 0.5f * (float)targetGeometry.height());
+
+ blitter->blit(texture->textureId(), m, QOpenGLTextureBlitter::OriginTopLeft);
+}
+
+void QWasmCompositor::drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window)
+{
+ QWasmBackingStore *backingStore = window->backingStore();
+
+ QOpenGLTexture const *texture = backingStore->getUpdatedTexture();
+
+ blit(blitter, screen, texture, window->geometry());
+}
+
+QPalette QWasmCompositor::makeWindowPalette()
+{
+ QPalette palette;
+ palette.setColor(QPalette::Active, QPalette::Highlight,
+ palette.color(QPalette::Active, QPalette::Highlight));
+ palette.setColor(QPalette::Active, QPalette::Base,
+ palette.color(QPalette::Active, QPalette::Highlight));
+ palette.setColor(QPalette::Inactive, QPalette::Highlight,
+ palette.color(QPalette::Inactive, QPalette::Dark));
+ palette.setColor(QPalette::Inactive, QPalette::Base,
+ palette.color(QPalette::Inactive, QPalette::Dark));
+ palette.setColor(QPalette::Inactive, QPalette::HighlightedText,
+ palette.color(QPalette::Inactive, QPalette::Window));
+
+ return palette;
+}
+
+QRect QWasmCompositor::titlebarRect(QWasmTitleBarOptions tb, QWasmCompositor::SubControls subcontrol)
+{
+ QRect ret;
+ const int controlMargin = 2;
+ const int controlHeight = tb.rect.height() - controlMargin *2;
+ const int delta = controlHeight + controlMargin;
+ int offset = 0;
+
+ bool isMinimized = tb.state & Qt::WindowMinimized;
+ bool isMaximized = tb.state & Qt::WindowMaximized;
+
+ ret = tb.rect;
+ switch (subcontrol) {
+ case SC_TitleBarLabel:
+ if (tb.flags & Qt::WindowSystemMenuHint)
+ ret.adjust(delta, 0, -delta, 0);
+ break;
+ case SC_TitleBarCloseButton:
+ if (tb.flags & Qt::WindowSystemMenuHint) {
+ ret.adjust(0, 0, -delta, 0);
+ offset += delta;
+ }
+ break;
+ case SC_TitleBarMaxButton:
+ if (!isMaximized && tb.flags & Qt::WindowMaximizeButtonHint) {
+ ret.adjust(0, 0, -delta*2, 0);
+ offset += (delta +delta);
+ }
+ break;
+ case SC_TitleBarNormalButton:
+ if (isMinimized && (tb.flags & Qt::WindowMinimizeButtonHint))
+ offset += delta;
+ else if (isMaximized && (tb.flags & Qt::WindowMaximizeButtonHint))
+ ret.adjust(0, 0, -delta*2, 0);
+ offset += (delta +delta);
+ break;
+ case SC_TitleBarSysMenu:
+ if (tb.flags & Qt::WindowSystemMenuHint) {
+ ret.setRect(tb.rect.left() + controlMargin, tb.rect.top() + controlMargin,
+ controlHeight, controlHeight);
+ }
+ break;
+ default:
+ break;
+ };
+
+ if (subcontrol != SC_TitleBarLabel && subcontrol != SC_TitleBarSysMenu) {
+ ret.setRect(tb.rect.right() - offset, tb.rect.top() + controlMargin,
+ controlHeight, controlHeight);
+ }
+
+ if (qApp->layoutDirection() == Qt::LeftToRight)
+ return ret;
+
+ QRect rect = ret;
+ rect.translate(2 * (tb.rect.right() - ret.right()) +
+ ret.width() - tb.rect.width(), 0);
+
+ return rect;
+}
+
+int dpiScaled(qreal value)
+{
+ return value * (qreal(qt_defaultDpiX()) / 96.0);
+}
+
+QWasmCompositor::QWasmTitleBarOptions QWasmCompositor::makeTitleBarOptions(const QWasmWindow *window)
+{
+ int width = window->windowFrameGeometry().width();
+ int border = window->borderWidth();
+
+ QWasmTitleBarOptions titleBarOptions;
+
+ titleBarOptions.rect = QRect(border, border, width - 2 * border, window->titleHeight());
+ titleBarOptions.flags = window->window()->flags();
+ titleBarOptions.state = window->window()->windowState();
+
+ bool isMaximized = titleBarOptions.state & Qt::WindowMaximized; // this gets reset when maximized
+
+ if (titleBarOptions.flags & (Qt::WindowTitleHint))
+ titleBarOptions.subControls |= SC_TitleBarLabel;
+ if (titleBarOptions.flags & Qt::WindowMaximizeButtonHint) {
+ if (isMaximized)
+ titleBarOptions.subControls |= SC_TitleBarNormalButton;
+ else
+ titleBarOptions.subControls |= SC_TitleBarMaxButton;
+ }
+ if (titleBarOptions.flags & Qt::WindowSystemMenuHint) {
+ titleBarOptions.subControls |= SC_TitleBarCloseButton;
+ titleBarOptions.subControls |= SC_TitleBarSysMenu;
+ }
+
+
+ titleBarOptions.palette = QWasmCompositor::makeWindowPalette();
+
+ if (window->window()->isActive())
+ titleBarOptions.palette.setCurrentColorGroup(QPalette::Active);
+ else
+ titleBarOptions.palette.setCurrentColorGroup(QPalette::Inactive);
+
+ if (window->activeSubControl() != QWasmCompositor::SC_None)
+ titleBarOptions.subControls = window->activeSubControl();
+
+ if (!window->window()->title().isEmpty())
+ titleBarOptions.titleBarOptionsString = window->window()->title();
+
+ return titleBarOptions;
+}
+
+void QWasmCompositor::drawWindowDecorations(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window)
+{
+ int width = window->windowFrameGeometry().width();
+ int height = window->windowFrameGeometry().height();
+ qreal dpr = window->devicePixelRatio();
+
+ QImage image(QSize(width * dpr, height * dpr), QImage::Format_RGB32);
+ image.setDevicePixelRatio(dpr);
+ QPainter painter(&image);
+ painter.fillRect(QRect(0, 0, width, height), painter.background());
+
+ QWasmTitleBarOptions titleBarOptions = makeTitleBarOptions(window);
+
+ drawTitlebarWindow(titleBarOptions, &painter);
+
+ QWasmFrameOptions frameOptions;
+ frameOptions.rect = QRect(0, 0, width, height);
+ frameOptions.lineWidth = dpiScaled(4.);
+
+ drawFrameWindow(frameOptions, &painter);
+
+ painter.end();
+
+ QOpenGLTexture texture(QOpenGLTexture::Target2D);
+ texture.setMinificationFilter(QOpenGLTexture::Nearest);
+ texture.setMagnificationFilter(QOpenGLTexture::Nearest);
+ texture.setWrapMode(QOpenGLTexture::ClampToEdge);
+ texture.setData(image, QOpenGLTexture::DontGenerateMipMaps);
+ texture.create();
+ texture.bind();
+
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image.width(), image.height(), GL_RGBA, GL_UNSIGNED_BYTE,
+ image.constScanLine(0));
+
+ blit(blitter, screen, &texture, QRect(window->windowFrameGeometry().topLeft(), QSize(width, height)));
+}
+
+void QWasmCompositor::drawFrameWindow(QWasmFrameOptions options, QPainter *painter)
+{
+ int x = options.rect.x();
+ int y = options.rect.y();
+ int w = options.rect.width();
+ int h = options.rect.height();
+ const QColor &c1 = options.palette.light().color();
+ const QColor &c2 = options.palette.shadow().color();
+ const QColor &c3 = options.palette.midlight().color();
+ const QColor &c4 = options.palette.dark().color();
+ const QBrush *fill = 0;
+
+ const qreal devicePixelRatio = painter->device()->devicePixelRatioF();
+ if (!qFuzzyCompare(devicePixelRatio, qreal(1))) {
+ const qreal inverseScale = qreal(1) / devicePixelRatio;
+ painter->scale(inverseScale, inverseScale);
+ x = qRound(devicePixelRatio * x);
+ y = qRound(devicePixelRatio * y);
+ w = qRound(devicePixelRatio * w);
+ h = qRound(devicePixelRatio * h);
+ }
+
+ QPen oldPen = painter->pen();
+ QPoint a[3] = { QPoint(x, y+h-2), QPoint(x, y), QPoint(x+w-2, y) };
+ painter->setPen(c1);
+ painter->drawPolyline(a, 3);
+ QPoint b[3] = { QPoint(x, y+h-1), QPoint(x+w-1, y+h-1), QPoint(x+w-1, y) };
+ painter->setPen(c2);
+ painter->drawPolyline(b, 3);
+ if (w > 4 && h > 4) {
+ QPoint c[3] = { QPoint(x+1, y+h-3), QPoint(x+1, y+1), QPoint(x+w-3, y+1) };
+ painter->setPen(c3);
+ painter->drawPolyline(c, 3);
+ QPoint d[3] = { QPoint(x+1, y+h-2), QPoint(x+w-2, y+h-2), QPoint(x+w-2, y+1) };
+ painter->setPen(c4);
+ painter->drawPolyline(d, 3);
+ if (fill)
+ painter->fillRect(QRect(x+2, y+2, w-4, h-4), *fill);
+ }
+ painter->setPen(oldPen);
+}
+
+//from commonstyle.cpp
+static QPixmap cachedPixmapFromXPM(const char * const *xpm)
+{
+ QPixmap result;
+ const QString tag = QString::asprintf("xpm:0x%p", static_cast<const void*>(xpm));
+ if (!QPixmapCache::find(tag, &result)) {
+ result = QPixmap(xpm);
+ QPixmapCache::insert(tag, result);
+ }
+ return result;
+}
+
+void QWasmCompositor::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment,
+ const QPixmap &pixmap) const
+{
+ qreal scale = pixmap.devicePixelRatio();
+ QSize size = pixmap.size() / scale;
+ int x = rect.x();
+ int y = rect.y();
+ int w = size.width();
+ int h = size.height();
+ if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter)
+ y += rect.size().height()/2 - h/2;
+ else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom)
+ y += rect.size().height() - h;
+ if ((alignment & Qt::AlignRight) == Qt::AlignRight)
+ x += rect.size().width() - w;
+ else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter)
+ x += rect.size().width()/2 - w/2;
+
+ QRect aligned = QRect(x, y, w, h);
+ QRect inter = aligned.intersected(rect);
+
+ painter->drawPixmap(inter.x(), inter.y(), pixmap, inter.x() - aligned.x(), inter.y() - aligned.y(), inter.width() * scale, inter.height() *scale);
+}
+
+
+void QWasmCompositor::drawTitlebarWindow(QWasmTitleBarOptions tb, QPainter *painter)
+{
+ QRect ir;
+ if (tb.subControls.testFlag(SC_TitleBarLabel)) {
+ QColor left = tb.palette.highlight().color();
+ QColor right = tb.palette.base().color();
+
+ QBrush fillBrush(left);
+ if (left != right) {
+ QPoint p1(tb.rect.x(), tb.rect.top() + tb.rect.height()/2);
+ QPoint p2(tb.rect.right(), tb.rect.top() + tb.rect.height()/2);
+ QLinearGradient lg(p1, p2);
+ lg.setColorAt(0, left);
+ lg.setColorAt(1, right);
+ fillBrush = lg;
+ }
+
+ painter->fillRect(tb.rect, fillBrush);
+ ir = titlebarRect(tb, SC_TitleBarLabel);
+ painter->setPen(tb.palette.highlightedText().color());
+ painter->drawText(ir.x() + 2, ir.y(), ir.width() - 2, ir.height(),
+ Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb.titleBarOptionsString);
+ } // SC_TitleBarLabel
+
+ bool down = false;
+ QPixmap pixmap;
+
+ if (tb.subControls.testFlag(SC_TitleBarCloseButton)
+ && tb.flags & Qt::WindowSystemMenuHint) {
+ ir = titlebarRect(tb, SC_TitleBarCloseButton);
+ down = tb.subControls & SC_TitleBarCloseButton && (tb.state & State_Sunken);
+ pixmap = cachedPixmapFromXPM(qt_close_xpm).scaled(QSize(10, 10));
+ drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
+ } //SC_TitleBarCloseButton
+
+ if (tb.subControls.testFlag(SC_TitleBarMaxButton)
+ && tb.flags & Qt::WindowMaximizeButtonHint
+ && !(tb.state & Qt::WindowMaximized)) {
+ ir = titlebarRect(tb, SC_TitleBarMaxButton);
+ down = tb.subControls & SC_TitleBarMaxButton && (tb.state & State_Sunken);
+ pixmap = cachedPixmapFromXPM(qt_maximize_xpm).scaled(QSize(10, 10));
+ drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
+ } //SC_TitleBarMaxButton
+
+ bool drawNormalButton = (tb.subControls & SC_TitleBarNormalButton)
+ && (((tb.flags & Qt::WindowMinimizeButtonHint)
+ && (tb.flags & Qt::WindowMinimized))
+ || ((tb.flags & Qt::WindowMaximizeButtonHint)
+ && (tb.flags & Qt::WindowMaximized)));
+
+ if (drawNormalButton) {
+ ir = titlebarRect(tb, SC_TitleBarNormalButton);
+ down = tb.subControls & SC_TitleBarNormalButton && (tb.state & State_Sunken);
+ pixmap = cachedPixmapFromXPM(qt_normalizeup_xpm).scaled( QSize(10, 10));
+
+ drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
+ } // SC_TitleBarNormalButton
+
+ if (tb.subControls & SC_TitleBarSysMenu && tb.flags & Qt::WindowSystemMenuHint) {
+ ir = titlebarRect(tb, SC_TitleBarSysMenu);
+ pixmap = cachedPixmapFromXPM(qt_menu_xpm).scaled(QSize(10, 10));
+ drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
+ }
+}
+
+void QWasmCompositor::drawShadePanel(QWasmTitleBarOptions options, QPainter *painter)
+{
+ int lineWidth = 1;
+ QPalette palette = options.palette;
+ const QBrush *fill = &options.palette.brush(QPalette::Button);
+
+ int x = options.rect.x();
+ int y = options.rect.y();
+ int w = options.rect.width();
+ int h = options.rect.height();
+
+ const qreal devicePixelRatio = painter->device()->devicePixelRatioF();
+ if (!qFuzzyCompare(devicePixelRatio, qreal(1))) {
+ const qreal inverseScale = qreal(1) / devicePixelRatio;
+ painter->scale(inverseScale, inverseScale);
+
+ x = qRound(devicePixelRatio * x);
+ y = qRound(devicePixelRatio * y);
+ w = qRound(devicePixelRatio * w);
+ h = qRound(devicePixelRatio * h);
+ lineWidth = qRound(devicePixelRatio * lineWidth);
+ }
+
+ QColor shade = palette.dark().color();
+ QColor light = palette.light().color();
+
+ if (fill) {
+ if (fill->color() == shade)
+ shade = palette.shadow().color();
+ if (fill->color() == light)
+ light = palette.midlight().color();
+ }
+ QPen oldPen = painter->pen();
+ QVector<QLineF> lines;
+ lines.reserve(2*lineWidth);
+
+ painter->setPen(light);
+ int x1, y1, x2, y2;
+ int i;
+ x1 = x;
+ y1 = y2 = y;
+ x2 = x + w - 2;
+ for (i = 0; i < lineWidth; i++) // top shadow
+ lines << QLineF(x1, y1++, x2--, y2++);
+
+ x2 = x1;
+ y1 = y + h - 2;
+ for (i = 0; i < lineWidth; i++) // left shado
+ lines << QLineF(x1++, y1, x2++, y2--);
+
+ painter->drawLines(lines);
+ lines.clear();
+ painter->setPen(shade);
+ x1 = x;
+ y1 = y2 = y+h-1;
+ x2 = x+w-1;
+ for (i=0; i<lineWidth; i++) { // bottom shadow
+ lines << QLineF(x1++, y1--, x2, y2--);
+ }
+ x1 = x2;
+ y1 = y;
+ y2 = y + h - lineWidth - 1;
+ for (i = 0; i < lineWidth; i++) // right shadow
+ lines << QLineF(x1--, y1++, x2--, y2);
+
+ painter->drawLines(lines);
+ if (fill) // fill with fill color
+ painter->fillRect(x+lineWidth, y+lineWidth, w-lineWidth*2, h-lineWidth*2, *fill);
+ painter->setPen(oldPen); // restore pen
+
+}
+
+void QWasmCompositor::drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window)
+{
+ if (window->window()->type() != Qt::Popup)
+ drawWindowDecorations(blitter, screen, window);
+ drawWindowContent(blitter, screen, window);
+}
+
+void QWasmCompositor::frame()
+{
+ if (!m_needComposit)
+ return;
+
+ m_needComposit = false;
+
+ if (m_windowStack.empty() || !m_screen)
+ return;
+
+ QWasmWindow *someWindow = nullptr;
+
+ foreach (QWasmWindow *window, m_windowStack) {
+ if (window->window()->surfaceClass() == QSurface::Window
+ && qt_window_private(static_cast<QWindow *>(window->window()))->receivedExpose) {
+ someWindow = window;
+ break;
+ }
+ }
+
+ if (!someWindow)
+ return;
+
+ if (m_context.isNull()) {
+ m_context.reset(new QOpenGLContext());
+ //mContext->setFormat(mScreen->format());
+ m_context->setScreen(m_screen->screen());
+ m_context->create();
+ }
+
+ m_context->makeCurrent(someWindow->window());
+
+ if (!m_blitter->isCreated())
+ m_blitter->create();
+
+ qreal dpr = m_screen->devicePixelRatio();
+ glViewport(0, 0, m_screen->geometry().width() * dpr, m_screen->geometry().height() * dpr);
+
+ m_context->functions()->glClearColor(0.2, 0.2, 0.2, 1.0);
+ m_context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ m_blitter->bind();
+ m_blitter->setRedBlueSwizzle(true);
+
+ foreach (QWasmWindow *window, m_windowStack) {
+ QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
+
+ if (!compositedWindow.visible)
+ continue;
+
+ drawWindow(m_blitter.data(), m_screen, window);
+ }
+
+ m_blitter->release();
+
+ if (someWindow && someWindow->window()->surfaceType() == QSurface::OpenGLSurface)
+ m_context->swapBuffers(someWindow->window());
+}
+
+void QWasmCompositor::notifyTopWindowChanged(QWasmWindow *window)
+{
+ QWindow *modalWindow;
+ bool blocked = QGuiApplicationPrivate::instance()->isWindowBlocked(window->window(), &modalWindow);
+
+ if (blocked) {
+ raise(static_cast<QWasmWindow*>(modalWindow->handle()));
+ return;
+ }
+
+ requestRedraw();
+ QWindowSystemInterface::handleWindowActivated(window->window());
+}
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h
new file mode 100644
index 0000000000..4e5ed46cec
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmcompositor.h
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWASMCOMPOSITOR_H
+#define QWASMCOMPOSITOR_H
+
+#include <QtGui/qregion.h>
+#include <qpa/qplatformwindow.h>
+
+#include <QtGui/qopengltextureblitter.h>
+#include <QtGui/qopengltexture.h>
+#include <QtGui/qpalette.h>
+#include <QtGui/qpainter.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWasmWindow;
+class QWasmScreen;
+class QOpenGLContext;
+class QOpenGLTextureBlitter;
+
+class QWasmCompositedWindow
+{
+public:
+ QWasmCompositedWindow();
+
+ QWasmWindow *window;
+ QWasmWindow *parentWindow;
+ QRegion damage;
+ bool flushPending;
+ bool visible;
+ QList<QWasmWindow *> childWindows;
+};
+
+class QWasmCompositor : public QObject
+{
+ Q_OBJECT
+public:
+ QWasmCompositor();
+ ~QWasmCompositor();
+
+ enum QWasmSubControl {
+ SC_None = 0x00000000,
+ SC_TitleBarSysMenu = 0x00000001,
+ SC_TitleBarMinButton = 0x00000002,
+ SC_TitleBarMaxButton = 0x00000004,
+ SC_TitleBarCloseButton = 0x00000008,
+ SC_TitleBarNormalButton = 0x00000010,
+ SC_TitleBarLabel = 0x00000100
+ };
+ Q_DECLARE_FLAGS(SubControls, QWasmSubControl)
+
+ enum QWasmStateFlag {
+ State_None = 0x00000000,
+ State_Enabled = 0x00000001,
+ State_Raised = 0x00000002,
+ State_Sunken = 0x00000004
+ };
+ Q_DECLARE_FLAGS(StateFlags, QWasmStateFlag)
+
+ struct QWasmTitleBarOptions {
+ QRect rect;
+ Qt::WindowFlags flags;
+ int state;
+ QPalette palette;
+ QString titleBarOptionsString;
+ QWasmCompositor::SubControls subControls;
+ };
+
+ struct QWasmFrameOptions {
+ QRect rect;
+ int lineWidth;
+ QPalette palette;
+ };
+
+ void setEnabled(bool enabled);
+
+ void addWindow(QWasmWindow *window, QWasmWindow *parentWindow = nullptr);
+ void removeWindow(QWasmWindow *window);
+ void setScreen(QWasmScreen *screen);
+
+ void setVisible(QWasmWindow *window, bool visible);
+ void raise(QWasmWindow *window);
+ void lower(QWasmWindow *window);
+ void setParent(QWasmWindow *window, QWasmWindow *parent);
+
+ void flush(QWasmWindow *surface, const QRegion &region);
+
+ int windowCount() const;
+
+ void redrawWindowContent();
+ void requestRedraw();
+
+ QWindow *windowAt(QPoint p, int padding = 0) const;
+ QWindow *keyWindow() const;
+
+ bool event(QEvent *event);
+
+ static QWasmTitleBarOptions makeTitleBarOptions(const QWasmWindow *window);
+ static QRect titlebarRect(QWasmTitleBarOptions tb, QWasmCompositor::SubControls subcontrol);
+
+private slots:
+ void frame();
+
+private:
+ void createFrameBuffer();
+ void flushCompletedCallback(int32_t);
+ void notifyTopWindowChanged(QWasmWindow *window);
+ void drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window);
+ void drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window);
+ void blit(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, const QOpenGLTexture *texture, QRect targetGeometry);
+
+ void drawWindowDecorations(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window);
+ void drwPanelButton();
+
+ QImage *m_frameBuffer;
+ QScopedPointer<QOpenGLContext> m_context;
+ QScopedPointer<QOpenGLTextureBlitter> m_blitter;
+ QWasmScreen *m_screen;
+
+ QHash<QWasmWindow *, QWasmCompositedWindow> m_compositedWindows;
+ QList<QWasmWindow *> m_windowStack;
+ QRegion m_globalDamage; // damage caused by expose, window close, etc.
+ bool m_needComposit;
+ bool m_inFlush;
+ bool m_inResize;
+ bool m_isEnabled;
+ QSize m_targetSize;
+ qreal m_targetDevicePixelRatio;
+
+ static QPalette makeWindowPalette();
+
+ void drawFrameWindow(QWasmFrameOptions options, QPainter *painter);
+ void drawTitlebarWindow(QWasmTitleBarOptions options, QPainter *painter);
+ void drawShadePanel(QWasmTitleBarOptions options, QPainter *painter);
+ void drawItemPixmap(QPainter *painter, const QRect &rect,
+ int alignment, const QPixmap &pixmap) const;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QWasmCompositor::SubControls)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/platforms/wasm/qwasmcursor.cpp b/src/plugins/platforms/wasm/qwasmcursor.cpp
new file mode 100644
index 0000000000..54804a55b3
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmcursor.cpp
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwasmcursor.h"
+
+#include <QtCore/qdebug.h>
+
+#include <emscripten/emscripten.h>
+
+void QWasmCursor::changeCursor(QCursor *windowCursor, QWindow *window)
+{
+ if (windowCursor == nullptr)
+ return;
+
+ // FIXME: The HTML5 plugin sets the cursor on the native canvas; when using multiple windows
+ // multiple cursors need to be managed taking mouse postion and stacking into account.
+ Q_UNUSED(window);
+
+ // Bitmap and custom cursors are not implemented (will fall back to "auto")
+ if (windowCursor->shape() == Qt::BitmapCursor || windowCursor->shape() >= Qt::CustomCursor)
+ qWarning() << "QWasmCursor: bitmap and custom cursors are not supported";
+
+ QByteArray htmlCursorName = cursorShapeToHtml(windowCursor->shape());
+
+ if (htmlCursorName.isEmpty())
+ htmlCursorName = "auto";
+
+ // Set cursor on the main canvas
+ EM_ASM_ARGS({
+ if (Module['canvas']) {
+ Module['canvas'].style['cursor'] = Pointer_stringify($0);
+ }
+ }, htmlCursorName.constData());
+}
+
+QByteArray QWasmCursor::cursorShapeToHtml(Qt::CursorShape shape)
+{
+ QByteArray cursorName;
+
+ switch (shape) {
+ case Qt::ArrowCursor:
+ cursorName = "default";
+ break;
+ case Qt::UpArrowCursor:
+ break;
+ case Qt::CrossCursor:
+ cursorName = "crosshair";
+ break;
+ case Qt::WaitCursor:
+ cursorName = "wait";
+ break;
+ case Qt::IBeamCursor:
+ cursorName = "text";
+ break;
+ case Qt::SizeVerCursor:
+ cursorName = "ns-resize";
+ break;
+ case Qt::SizeHorCursor:
+ cursorName = "ew-resize";
+ break;
+ case Qt::SizeBDiagCursor:
+ cursorName = "nesw-resize";
+ break;
+ case Qt::SizeFDiagCursor:
+ cursorName = "nwse-resize";
+ break;
+ case Qt::SizeAllCursor:
+ break; // no equivalent?
+ case Qt::BlankCursor:
+ cursorName = "none";
+ break;
+ case Qt::SplitVCursor:
+ cursorName = "row-resize";
+ break;
+ case Qt::SplitHCursor:
+ cursorName = "col-resize";
+ break;
+ case Qt::PointingHandCursor:
+ cursorName = "pointer";
+ break;
+ case Qt::ForbiddenCursor:
+ cursorName = "not-allowed";
+ break;
+ case Qt::WhatsThisCursor:
+ cursorName = "help";
+ break;
+ case Qt::BusyCursor:
+ cursorName = "wait";
+ break;
+ case Qt::OpenHandCursor:
+ break; // no equivalent?
+ case Qt::ClosedHandCursor:
+ break; // no equivalent?
+ case Qt::DragCopyCursor:
+ break; // no equivalent?
+ case Qt::DragMoveCursor:
+ break; // no equivalent?
+ case Qt::DragLinkCursor:
+ break; // no equivalent?
+ default:
+ break;
+ }
+
+ return cursorName;
+}
diff --git a/src/plugins/platforms/wasm/qwasmcursor.h b/src/plugins/platforms/wasm/qwasmcursor.h
new file mode 100644
index 0000000000..516e07aa31
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmcursor.h
@@ -0,0 +1,43 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWASMCURSOR_H
+#define QWASMCURSOR_H
+
+#include <qpa/qplatformcursor.h>
+
+class QWasmCursor : public QPlatformCursor
+{
+public:
+ void changeCursor(QCursor *windowCursor, QWindow *window) override;
+
+ QByteArray cursorShapeToHtml(Qt::CursorShape shape);
+};
+
+#endif
diff --git a/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp b/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
new file mode 100644
index 0000000000..41355d72ae
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwasmeventdispatcher.h"
+
+#include <QtCore/qcoreapplication.h>
+
+#include <emscripten.h>
+
+class QWasmEventDispatcherPrivate : public QEventDispatcherUNIXPrivate
+{
+
+};
+
+QWasmEventDispatcher *g_htmlEventDispatcher;
+
+QWasmEventDispatcher::QWasmEventDispatcher(QObject *parent)
+ : QUnixEventDispatcherQPA(parent)
+{
+
+ g_htmlEventDispatcher = this;
+}
+
+QWasmEventDispatcher::~QWasmEventDispatcher()
+{
+ g_htmlEventDispatcher = nullptr;
+}
+
+bool QWasmEventDispatcher::registerRequestUpdateCallback(std::function<void(void)> callback)
+{
+ if (!g_htmlEventDispatcher || !g_htmlEventDispatcher->m_hasMainLoop)
+ return false;
+
+ g_htmlEventDispatcher->m_requestUpdateCallbacks.append(callback);
+ emscripten_resume_main_loop();
+ return true;
+}
+
+void QWasmEventDispatcher::maintainTimers()
+{
+ if (!g_htmlEventDispatcher || !g_htmlEventDispatcher->m_hasMainLoop)
+ return;
+
+ g_htmlEventDispatcher->doMaintainTimers();
+}
+
+bool QWasmEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ // WaitForMoreEvents is not supported (except for in combination with EventLoopExec below),
+ // and we don't want the unix event dispatcher base class to attempt to wait either.
+ flags &= ~QEventLoop::WaitForMoreEvents;
+
+ // Handle normal processEvents.
+ if (!(flags & QEventLoop::EventLoopExec))
+ return QUnixEventDispatcherQPA::processEvents(flags);
+
+ // Handle processEvents from QEventLoop::exec():
+ //
+ // At this point the application has created its root objects on
+ // the stack and has called app.exec() which has called into this
+ // function via QEventLoop.
+ //
+ // The application now expects that exec() will not return until
+ // app exit time. However, the browser expects that we return
+ // control to it periodically, also after initial setup in main().
+
+ // EventLoopExec for nested event loops is not supported.
+ Q_ASSERT(!m_hasMainLoop);
+ m_hasMainLoop = true;
+
+ // Call emscripten_set_main_loop_arg() with a callback which processes
+ // events. Also set simulateInfiniteLoop to true which makes emscripten
+ // return control to the browser without unwinding the C++ stack.
+ auto callback = [](void *eventDispatcher) {
+ QWasmEventDispatcher *that = static_cast<QWasmEventDispatcher *>(eventDispatcher);
+
+ // Save and clear updateRequest callbacks so we can register new ones
+ auto requestUpdateCallbacksCopy = that->m_requestUpdateCallbacks;
+ that->m_requestUpdateCallbacks.clear();
+
+ // Repaint all windows
+ for (auto callback : qAsConst(requestUpdateCallbacksCopy))
+ callback();
+
+ // Pause main loop if no updates were requested. Updates will be
+ // restarted again by registerRequestUpdateCallback().
+ if (that->m_requestUpdateCallbacks.isEmpty())
+ emscripten_pause_main_loop();
+
+ that->doMaintainTimers();
+ };
+ int fps = 0; // update using requestAnimationFrame
+ int simulateInfiniteLoop = 1;
+ emscripten_set_main_loop_arg(callback, this, fps, simulateInfiniteLoop);
+
+ // Note: the above call never returns, not even at app exit
+ return false;
+}
+
+void QWasmEventDispatcher::doMaintainTimers()
+{
+ Q_D(QWasmEventDispatcher);
+
+ // This functon schedules native timers in order to wake up to
+ // process events and activate Qt timers. This is done using the
+ // emscripten_async_call() API which schedules a new timer.
+ // There is unfortunately no way to cancel or update a current
+ // native timer.
+
+ // Schedule a zero-timer to continue processing any pending events.
+ if (!m_hasZeroTimer && hasPendingEvents()) {
+ auto callback = [](void *eventDispatcher) {
+ QWasmEventDispatcher *that = static_cast<QWasmEventDispatcher *>(eventDispatcher);
+ that->m_hasZeroTimer = false;
+ that->QUnixEventDispatcherQPA::processEvents(QEventLoop::AllEvents);
+
+ // Processing events may have posted new events or created new timers
+ that->doMaintainTimers();
+ };
+
+ emscripten_async_call(callback, this, 0);
+ m_hasZeroTimer = true;
+ return;
+ }
+
+ auto timespecToNanosec = [](timespec ts) -> uint64_t { return ts.tv_sec * 1000 + ts.tv_nsec / (1000 * 1000); };
+
+ // Get current time and time-to-first-Qt-timer. This polls for system
+ // time, and we use this time as the current time for the duration of this call.
+ timespec toWait;
+ bool hasTimers = d->timerList.timerWait(toWait);
+ if (!hasTimers)
+ return; // no timer needed
+
+ uint64_t currentTime = timespecToNanosec(d->timerList.currentTime);
+ uint64_t toWaitDuration = timespecToNanosec(toWait);
+
+ // The currently scheduled timer target is stored in m_currentTargetTime.
+ // We can re-use it if the new target is equivalent or later.
+ uint64_t newTargetTime = currentTime + toWaitDuration;
+ if (newTargetTime >= m_currentTargetTime)
+ return; // existing timer is good
+
+ // Schedule a native timer with a callback which processes events (and timers)
+ auto callback = [](void *eventDispatcher) {
+ QWasmEventDispatcher *that = static_cast<QWasmEventDispatcher *>(eventDispatcher);
+ that->m_currentTargetTime = std::numeric_limits<uint64_t>::max();
+ that->QUnixEventDispatcherQPA::processEvents(QEventLoop::AllEvents);
+
+ // Processing events may have posted new events or created new timers
+ that->doMaintainTimers();
+ };
+ emscripten_async_call(callback, this, toWaitDuration);
+ m_currentTargetTime = newTargetTime;
+}
diff --git a/src/plugins/platforms/wasm/qwasmeventdispatcher.h b/src/plugins/platforms/wasm/qwasmeventdispatcher.h
new file mode 100644
index 0000000000..5300b3de73
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmeventdispatcher.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWASMEVENTDISPATCHER_H
+#define QWASMEVENTDISPATCHER_H
+
+#include <QtCore/qhash.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtEventDispatcherSupport/private/qunixeventdispatcher_qpa_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWasmEventDispatcherPrivate;
+
+class QWasmEventDispatcher : public QUnixEventDispatcherQPA
+{
+ Q_DECLARE_PRIVATE(QWasmEventDispatcher)
+public:
+ explicit QWasmEventDispatcher(QObject *parent = nullptr);
+ ~QWasmEventDispatcher();
+
+ static bool registerRequestUpdateCallback(std::function<void(void)> callback);
+ static void maintainTimers();
+
+protected:
+ bool processEvents(QEventLoop::ProcessEventsFlags flags) override;
+ void doMaintainTimers();
+
+private:
+ bool m_hasMainLoop = false;
+ bool m_hasZeroTimer = false;
+ uint64_t m_currentTargetTime = std::numeric_limits<uint64_t>::max();
+ QVector<std::function<void(void)>> m_requestUpdateCallbacks;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
new file mode 100644
index 0000000000..6545eda4e3
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
@@ -0,0 +1,522 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwasmeventtranslator.h"
+#include "qwasmeventdispatcher.h"
+#include "qwasmcompositor.h"
+#include "qwasmintegration.h"
+
+#include <QtGui/qevent.h>
+#include <qpa/qwindowsysteminterface.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qobject.h>
+
+#include <QtCore/qdeadlinetimer.h>
+
+#include <iostream>
+
+QT_BEGIN_NAMESPACE
+
+// macOS CTRL <-> META switching. We most likely want to enable
+// the existing switching code in QtGui, but for now do it here.
+static bool g_usePlatformMacCtrlMetaSwitching = false;
+
+QWasmEventTranslator::QWasmEventTranslator(QObject *parent)
+ : QObject(parent)
+ , draggedWindow(nullptr)
+ , pressedButtons(Qt::NoButton)
+ , resizeMode(QWasmWindow::ResizeNone)
+{
+ emscripten_set_keydown_callback(0, (void *)this, 1, &keyboard_cb);
+ emscripten_set_keyup_callback(0, (void *)this, 1, &keyboard_cb);
+
+ emscripten_set_mousedown_callback(0, (void *)this, 1, &mouse_cb);
+ emscripten_set_mouseup_callback(0, (void *)this, 1, &mouse_cb);
+ emscripten_set_mousemove_callback(0, (void *)this, 1, &mouse_cb);
+
+ emscripten_set_focus_callback(0, (void *)this, 1, &focus_cb);
+
+ emscripten_set_wheel_callback(0, (void *)this, 1, &wheel_cb);
+
+ touchDevice = new QTouchDevice;
+ touchDevice->setType(QTouchDevice::TouchScreen);
+ touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition);
+ QWindowSystemInterface::registerTouchDevice(touchDevice);
+
+ emscripten_set_touchstart_callback("#canvas", (void *)this, 1, &touchCallback);
+ emscripten_set_touchend_callback("#canvas", (void *)this, 1, &touchCallback);
+ emscripten_set_touchmove_callback("#canvas", (void *)this, 1, &touchCallback);
+ emscripten_set_touchcancel_callback("#canvas", (void *)this, 1, &touchCallback);
+
+ // The Platform Detect: expand coverage and move as needed
+ enum Platform {
+ GenericPlatform,
+ MacOSPlatform
+ };
+ Platform platform =
+ Platform(EM_ASM_INT("if (navigator.platform.includes(\"Mac\")) return 1; return 0;"));
+
+ g_usePlatformMacCtrlMetaSwitching = (platform == MacOSPlatform);
+}
+
+template <typename Event>
+QFlags<Qt::KeyboardModifier> QWasmEventTranslator::translatKeyModifier(const Event *event)
+{
+ QFlags<Qt::KeyboardModifier> keyModifier = Qt::NoModifier;
+ if (event->shiftKey)
+ keyModifier |= Qt::ShiftModifier;
+ if (event->ctrlKey) {
+ if (g_usePlatformMacCtrlMetaSwitching)
+ keyModifier |= Qt::MetaModifier;
+ else
+ keyModifier |= Qt::ControlModifier;
+ }
+ if (event->altKey)
+ keyModifier |= Qt::AltModifier;
+ if (event->metaKey) {
+ if (g_usePlatformMacCtrlMetaSwitching)
+ keyModifier |= Qt::ControlModifier;
+ else
+ keyModifier |= Qt::MetaModifier;
+ }
+ return keyModifier;
+}
+
+QFlags<Qt::KeyboardModifier> QWasmEventTranslator::translateKeyboardEventModifier(const EmscriptenKeyboardEvent *keyEvent)
+{
+ QFlags<Qt::KeyboardModifier> keyModifier = translatKeyModifier(keyEvent);
+ if (keyEvent->location == DOM_KEY_LOCATION_NUMPAD) {
+ keyModifier |= Qt::KeypadModifier;
+ }
+
+ return keyModifier;
+}
+
+QFlags<Qt::KeyboardModifier> QWasmEventTranslator::translateMouseEventModifier(const EmscriptenMouseEvent *mouseEvent)
+{
+ return translatKeyModifier(mouseEvent);
+}
+
+int QWasmEventTranslator::keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
+{
+ Q_UNUSED(userData)
+
+ bool alphanumeric;
+ Qt::Key qtKey = translateEmscriptKey(keyEvent, &alphanumeric);
+
+ QEvent::Type keyType = QEvent::None;
+ switch (eventType) {
+ case EMSCRIPTEN_EVENT_KEYPRESS:
+ case EMSCRIPTEN_EVENT_KEYDOWN: //down
+ keyType = QEvent::KeyPress;
+ break;
+ case EMSCRIPTEN_EVENT_KEYUP: //up
+ keyType = QEvent::KeyRelease;
+ break;
+ default:
+ break;
+ };
+
+ if (keyType == QEvent::None)
+ return 0;
+
+ QString keyText = alphanumeric ? QString(keyEvent->key) : QString();
+ bool accepted = QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
+ 0, keyType, qtKey, translateKeyboardEventModifier(keyEvent), keyText);
+ QWasmEventDispatcher::maintainTimers();
+ return accepted ? 1 : 0;
+}
+
+Qt::Key QWasmEventTranslator::translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey, bool *outAlphanumeric)
+{
+ Qt::Key qtKey;
+ if (outAlphanumeric)
+ *outAlphanumeric = false;
+
+ switch (emscriptKey->keyCode) {
+ case KeyMultiply: qtKey = Qt::Key_Asterisk; *outAlphanumeric = true; break;
+ case KeyAdd: qtKey = Qt::Key_Plus; *outAlphanumeric = true; break;
+ case KeyMinus: qtKey = Qt::Key_Minus; *outAlphanumeric = true; break;
+ case KeySubtract: qtKey = Qt::Key_Minus; *outAlphanumeric = true; break;
+ case KeyDecimal: qtKey = Qt::Key_Period; *outAlphanumeric = true; break;
+ case KeyDivide: qtKey = Qt::Key_Slash; *outAlphanumeric = true; break;
+ case KeyNumPad0: qtKey = Qt::Key_0; *outAlphanumeric = true; break;
+ case KeyNumPad1: qtKey = Qt::Key_1; *outAlphanumeric = true; break;
+ case KeyNumPad2: qtKey = Qt::Key_2; *outAlphanumeric = true; break;
+ case KeyNumPad3: qtKey = Qt::Key_3; *outAlphanumeric = true; break;
+ case KeyNumPad4: qtKey = Qt::Key_4; *outAlphanumeric = true; break;
+ case KeyNumPad5: qtKey = Qt::Key_5; *outAlphanumeric = true; break;
+ case KeyNumPad6: qtKey = Qt::Key_6; *outAlphanumeric = true; break;
+ case KeyNumPad7: qtKey = Qt::Key_7; *outAlphanumeric = true; break;
+ case KeyNumPad8: qtKey = Qt::Key_8; *outAlphanumeric = true; break;
+ case KeyNumPad9: qtKey = Qt::Key_9; *outAlphanumeric = true; break;
+ case KeyComma: qtKey = Qt::Key_Comma; *outAlphanumeric = true; break;
+ case KeyPeriod: qtKey = Qt::Key_Period; *outAlphanumeric = true; break;
+ case KeySlash: qtKey = Qt::Key_Slash; *outAlphanumeric = true; break;
+ case KeySemiColon: qtKey = Qt::Key_Semicolon; *outAlphanumeric = true; break;
+ case KeyEquals: qtKey = Qt::Key_Equal; *outAlphanumeric = true; break;
+ case KeyOpenBracket: qtKey = Qt::Key_BracketLeft; *outAlphanumeric = true; break;
+ case KeyCloseBracket: qtKey = Qt::Key_BracketRight; *outAlphanumeric = true; break;
+ case KeyBackSlash: qtKey = Qt::Key_Backslash; *outAlphanumeric = true; break;
+ case KeyMeta:
+ Q_FALLTHROUGH();
+ case KeyMetaRight:
+ qtKey = Qt::Key_Meta;
+ break;
+ case KeyTab: qtKey = Qt::Key_Tab; break;
+ case KeyClear: qtKey = Qt::Key_Clear; break;
+ case KeyBackSpace: qtKey = Qt::Key_Backspace; break;
+ case KeyEnter: qtKey = Qt::Key_Return; break;
+ case KeyShift: qtKey = Qt::Key_Shift; break;
+ case KeyControl: qtKey = Qt::Key_Control; break;
+ case KeyAlt: qtKey = Qt::Key_Alt; break;
+ case KeyCapsLock: qtKey = Qt::Key_CapsLock; break;
+ case KeyEscape: qtKey = Qt::Key_Escape; break;
+ case KeyPageUp: qtKey = Qt::Key_PageUp; break;
+ case KeyPageDown: qtKey = Qt::Key_PageDown; break;
+ case KeyEnd: qtKey = Qt::Key_End; break;
+ case KeyHome: qtKey = Qt::Key_Home; break;
+ case KeyLeft: qtKey = Qt::Key_Left; break;
+ case KeyUp: qtKey = Qt::Key_Up; break;
+ case KeyRight: qtKey = Qt::Key_Right; break;
+ case KeyDown: qtKey = Qt::Key_Down; break;
+ case KeyBrightnessDown: qtKey = Qt::Key_MonBrightnessDown; break;
+ case KeyBrightnessUp: qtKey = Qt::Key_MonBrightnessUp; break;
+ case KeyMediaTrackPrevious: qtKey = Qt::Key_MediaPrevious; break;
+ case KeyMediaPlayPause: qtKey = Qt::Key_MediaTogglePlayPause; break;
+ case KeyMediaTrackNext: qtKey = Qt::Key_MediaNext; break;
+ case KeyAudioVolumeMute: qtKey = Qt::Key_VolumeMute; break;
+ case KeyAudioVolumeDown: qtKey = Qt::Key_VolumeDown; break;
+ case KeyAudioVolumeUp: qtKey = Qt::Key_VolumeUp; break;
+ case KeyDelete: qtKey = Qt::Key_Delete; break;
+
+ case KeyF1: qtKey = Qt::Key_F1; break;
+ case KeyF2: qtKey = Qt::Key_F2; break;
+ case KeyF3: qtKey = Qt::Key_F3; break;
+ case KeyF4: qtKey = Qt::Key_F4; break;
+ case KeyF5: qtKey = Qt::Key_F5; break;
+ case KeyF6: qtKey = Qt::Key_F6; break;
+ case KeyF7: qtKey = Qt::Key_F7; break;
+ case KeyF8: qtKey = Qt::Key_F8; break;
+ case KeyF9: qtKey = Qt::Key_F9; break;
+ case KeyF10: qtKey = Qt::Key_F10; break;
+ case KeyF11: qtKey = Qt::Key_F11; break;
+ case KeyF12: qtKey = Qt::Key_F12; break;
+ case 124: qtKey = Qt::Key_F13; break;
+ case 125: qtKey = Qt::Key_F14; break;
+
+ case KeySpace:
+ default:
+ if (outAlphanumeric)
+ *outAlphanumeric = true;
+ qtKey = static_cast<Qt::Key>(emscriptKey->keyCode);
+ break;
+ }
+
+ // Handle Mac command key. Using event->keyCode as above is
+ // no reliable since the codes differ between browsers.
+ if (qstrncmp(emscriptKey->key, "Meta", 4) == 0) {
+ qtKey = Qt::Key_Meta;
+ *outAlphanumeric = false;
+ }
+
+ if (g_usePlatformMacCtrlMetaSwitching) {
+ if (qtKey == Qt::Key_Meta)
+ qtKey = Qt::Key_Control;
+ else if (qtKey == Qt::Key_Control)
+ qtKey = Qt::Key_Meta;
+ }
+
+ return qtKey;
+}
+
+Qt::MouseButton QWasmEventTranslator::translateMouseButton(unsigned short button)
+{
+ if (button == 0)
+ return Qt::LeftButton;
+ else if (button == 1)
+ return Qt::MiddleButton;
+ else if (button == 2)
+ return Qt::RightButton;
+
+ return Qt::NoButton;
+}
+
+int QWasmEventTranslator::mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
+{
+ QWasmEventTranslator *translator = (QWasmEventTranslator*)userData;
+ translator->processMouse(eventType,mouseEvent);
+ QWasmEventDispatcher::maintainTimers();
+ return 0;
+}
+
+void resizeWindow(QWindow *window, QWasmWindow::ResizeMode mode,
+ QRect startRect, QPoint amount)
+{
+ if (mode == QWasmWindow::ResizeNone)
+ return;
+
+ bool top = mode == QWasmWindow::ResizeTopLeft ||
+ mode == QWasmWindow::ResizeTop ||
+ mode == QWasmWindow::ResizeTopRight;
+
+ bool bottom = mode == QWasmWindow::ResizeBottomLeft ||
+ mode == QWasmWindow::ResizeBottom ||
+ mode == QWasmWindow::ResizeBottomRight;
+
+ bool left = mode == QWasmWindow::ResizeLeft ||
+ mode == QWasmWindow::ResizeTopLeft ||
+ mode == QWasmWindow::ResizeBottomLeft;
+
+ bool right = mode == QWasmWindow::ResizeRight ||
+ mode == QWasmWindow::ResizeTopRight ||
+ mode == QWasmWindow::ResizeBottomRight;
+
+ int x1 = startRect.left();
+ int y1 = startRect.top();
+ int x2 = startRect.right();
+ int y2 = startRect.bottom();
+
+ if (left)
+ x1 += amount.x();
+ if (top)
+ y1 += amount.y();
+ if (right)
+ x2 += amount.x();
+ if (bottom)
+ y2 += amount.y();
+
+ int w = x2-x1;
+ int h = y2-y1;
+
+ if (w < window->minimumWidth()) {
+ if (left)
+ x1 -= window->minimumWidth() - w;
+
+ w = window->minimumWidth();
+ }
+
+ if (h < window->minimumHeight()) {
+ if (top)
+ y1 -= window->minimumHeight() - h;
+
+ h = window->minimumHeight();
+ }
+
+ window->setGeometry(x1, y1, w, h);
+}
+
+void QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent)
+{
+ auto timestamp = mouseEvent->timestamp;
+ QPoint point(mouseEvent->canvasX, mouseEvent->canvasY);
+
+ QEvent::Type buttonEventType = QEvent::None;
+
+ Qt::MouseButton button = translateMouseButton(mouseEvent->button);
+ Qt::KeyboardModifiers modifiers = translateMouseEventModifier(mouseEvent);
+
+ QWindow *window2 = QWasmIntegration::get()->compositor()->windowAt(point, 5);
+ QWasmWindow *htmlWindow = static_cast<QWasmWindow*>(window2->handle());
+ bool onFrame = false;
+ if (window2 && !window2->geometry().contains(point))
+ onFrame = true;
+
+ QPoint localPoint(point.x() - window2->geometry().x(), point.y() - window2->geometry().y());
+
+ switch (eventType) {
+ case 5: //down
+ {
+ if (window2)
+ window2->raise();
+
+ pressedButtons.setFlag(button);
+
+ if (mouseEvent->button == 0) {
+ pressedWindow = window2;
+ buttonEventType = QEvent::MouseButtonPress;
+ if (htmlWindow && window2->flags().testFlag(Qt::WindowTitleHint) && htmlWindow->isPointOnTitle(point))
+ draggedWindow = window2;
+ else if (htmlWindow && htmlWindow->isPointOnResizeRegion(point)) {
+ draggedWindow = window2;
+ resizeMode = htmlWindow->resizeModeAtPoint(point);
+ resizePoint = point;
+ resizeStartRect = window2->geometry();
+ }
+ }
+
+ htmlWindow->injectMousePressed(localPoint, point, button, modifiers);
+ break;
+ }
+ case 6: //up
+ {
+ pressedButtons.setFlag(translateMouseButton(mouseEvent->button), false);
+ buttonEventType = QEvent::MouseButtonRelease;
+ QWasmWindow *oldWindow = nullptr;
+
+ if (mouseEvent->button == 0 && pressedWindow) {
+ oldWindow = static_cast<QWasmWindow*>(pressedWindow->handle());
+ pressedWindow = nullptr;
+ }
+
+
+ if (mouseEvent->button == 0) {
+ draggedWindow = nullptr;
+ resizeMode = QWasmWindow::ResizeNone;
+ }
+
+ if (oldWindow)
+ oldWindow->injectMouseReleased(localPoint, point, button, modifiers);
+ break;
+ }
+ case 8://move //drag event
+ {
+ buttonEventType = QEvent::MouseMove;
+ if (resizeMode == QWasmWindow::ResizeNone && draggedWindow) {
+ draggedWindow->setX(draggedWindow->x() + mouseEvent->movementX);
+ draggedWindow->setY(draggedWindow->y() + mouseEvent->movementY);
+ }
+
+ if (resizeMode != QWasmWindow::ResizeNone) {
+ QPoint delta = QPoint(mouseEvent->canvasX, mouseEvent->canvasY) - resizePoint;
+ resizeWindow(draggedWindow, resizeMode, resizeStartRect, delta);
+ }
+ break;
+ }
+ default:
+ break;
+ };
+
+ if (window2 && !onFrame) {
+ QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(
+ window2, timestamp, localPoint, point, pressedButtons, button, buttonEventType, modifiers);
+ }
+}
+
+int QWasmEventTranslator::focus_cb(int /*eventType*/, const EmscriptenFocusEvent */*focusEvent*/, void */*userData*/)
+{
+ return 0;
+}
+
+int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)
+{
+ Q_UNUSED(eventType)
+ Q_UNUSED(userData)
+
+ EmscriptenMouseEvent mouseEvent = wheelEvent->mouse;
+
+ int scrollFactor = 0;
+ switch (wheelEvent->deltaMode) {
+ case DOM_DELTA_PIXEL://chrome safari
+ scrollFactor = 1;
+ break;
+ case DOM_DELTA_LINE: //firefox
+ scrollFactor = 12;
+ break;
+ case DOM_DELTA_PAGE:
+ scrollFactor = 20;
+ break;
+ };
+
+ Qt::KeyboardModifiers modifiers = translateMouseEventModifier(&mouseEvent);
+ auto timestamp = mouseEvent.timestamp;
+ QPoint globalPoint(mouseEvent.canvasX, mouseEvent.canvasY);
+
+ QWindow *window2 = QWasmIntegration::get()->compositor()->windowAt(globalPoint, 5);
+
+ QPoint localPoint(globalPoint.x() - window2->geometry().x(), globalPoint.y() - window2->geometry().y());
+
+ QPoint pixelDelta;
+
+ if (wheelEvent->deltaY != 0) pixelDelta.setY(wheelEvent->deltaY * scrollFactor);
+ if (wheelEvent->deltaX != 0) pixelDelta.setX(wheelEvent->deltaX * scrollFactor);
+
+ QWindowSystemInterface::handleWheelEvent(window2, timestamp, localPoint, globalPoint, QPoint(), pixelDelta, modifiers);
+ return 1;
+}
+
+int QWasmEventTranslator::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
+{
+ QList<QWindowSystemInterface::TouchPoint> touchPointList;
+ touchPointList.reserve(touchEvent->numTouches);
+ QWindow *window2;
+
+ for (int i = 0; i < touchEvent->numTouches; i++) {
+
+ const EmscriptenTouchPoint *touches = &touchEvent->touches[i];
+
+ QPoint point(touches->canvasX, touches->canvasY);
+ window2 = QWasmIntegration::get()->compositor()->windowAt(point, 5);
+
+ QWindowSystemInterface::TouchPoint touchPoint;
+
+ auto cX = point.x();
+ auto cY = point.y();
+ touchPoint.area = QRect(0, 0, 8, 8);
+ touchPoint.area.moveCenter(QPointF(cX,cY)); // simulate area
+
+ touchPoint.id = touches->identifier;
+ touchPoint.normalPosition = QPointF(cX / window2->width(), cY / window2->height());
+
+ switch (eventType) {
+ case EMSCRIPTEN_EVENT_TOUCHSTART:
+ touchPoint.state = Qt::TouchPointPressed;
+ break;
+ case EMSCRIPTEN_EVENT_TOUCHEND:
+ touchPoint.state = Qt::TouchPointReleased;
+ break;
+ case EMSCRIPTEN_EVENT_TOUCHMOVE:
+ touchPoint.state = Qt::TouchPointMoved;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+
+ touchPointList.append(touchPoint);
+ }
+
+ QWasmEventTranslator *wasmEventTranslator = (QWasmEventTranslator*)userData;
+ QFlags<Qt::KeyboardModifier> keyModifier = translatKeyModifier(touchEvent);
+
+ if (eventType != EMSCRIPTEN_EVENT_TOUCHCANCEL)
+ QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(window2, wasmEventTranslator->getTimestamp(), wasmEventTranslator->touchDevice, touchPointList, keyModifier);
+ else
+ QWindowSystemInterface::handleTouchCancelEvent(window2, wasmEventTranslator->getTimestamp(), wasmEventTranslator->touchDevice, keyModifier);
+
+ QCoreApplication::processEvents();
+ return 1;
+}
+
+quint64 QWasmEventTranslator::getTimestamp()
+{
+ return QDeadlineTimer::current().deadlineNSecs() / 1000;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.h b/src/plugins/platforms/wasm/qwasmeventtranslator.h
new file mode 100644
index 0000000000..11430a57a2
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmeventtranslator.h
@@ -0,0 +1,210 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWASMEVENTTRANSLATOR_H
+#define QWASMEVENTTRANSLATOR_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qpoint.h>
+#include <emscripten/html5.h>
+#include "qwasmwindow.h"
+#include <QtGui/qtouchdevice.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindow;
+
+class QWasmEventTranslator : public QObject
+{
+ Q_OBJECT
+
+ enum KeyCode {
+ // numpad
+ KeyNumPad0 = 0x60,
+ KeyNumPad1 = 0x61,
+ KeyNumPad2 = 0x62,
+ KeyNumPad3 = 0x63,
+ KeyNumPad4 = 0x64,
+ KeyNumPad5 = 0x65,
+ KeyNumPad6 = 0x66,
+ KeyNumPad7 = 0x67,
+ KeyNumPad8 = 0x68,
+ KeyNumPad9 = 0x69,
+ KeyMultiply = 0x6A,
+ KeyAdd = 0x6B,
+ KeySeparator = 0x6C,
+ KeySubtract = 0x6D,
+ KeyDecimal = 0x6E,
+ KeyDivide = 0x6F,
+ KeyMeta = 0x5B,
+ KeyMetaRight = 0x5C,
+ ////////
+ KeyClear = 0x90,
+ KeyEnter = 0xD,
+ KeyBackSpace = 0x08,
+ KeyCancel = 0x03,
+ KeyTab = 0x09,
+ KeyShift = 0x10,
+ KeyControl = 0x11,
+ KeyAlt = 0x12,
+ KeyPause = 0x13,
+ KeyCapsLock = 0x14,
+ KeyEscape = 0x1B,
+ KeySpace = 0x20,
+ KeyPageUp = 0x21,
+ KeyPageDown = 0x22,
+ KeyEnd = 0x23,
+ KeyHome = 0x24,
+ KeyLeft = 0x25,
+ KeyUp = 0x26,
+ KeyRight = 0x27,
+ KeyDown = 0x28,
+ KeyComma = 0xBC,
+ KeyPeriod = 0xBE,
+ KeySlash = 0xBF,
+ KeyZero = 0x30,
+ KeyOne = 0x31,
+ KeyTwo = 0x32,
+ KeyThree = 0x33,
+ KeyFour = 0x34,
+ KeyFive = 0x35,
+ KeySix = 0x36,
+ KeySeven = 0x37,
+ KeyEight = 0x38,
+ KeyNine = 0x39,
+ KeyBrightnessDown = 0xD8,
+ KeyBrightnessUp = 0xD9,
+ KeyMediaTrackPrevious = 0xB1,
+ KeyMediaPlayPause = 0xB3,
+ KeyMediaTrackNext = 0xB0,
+ KeyAudioVolumeMute = 0xAD,
+ KeyAudioVolumeDown = 0xAE,
+ KeyAudioVolumeUp = 0xAF,
+ KeySemiColon = 0xBA,
+ KeyEquals = 0xBB,
+ KeyMinus = 0xBD,
+ KeyA = 0x41,
+ KeyB = 0x42,
+ KeyC = 0x43,
+ KeyD = 0x44,
+ KeyE = 0x45,
+ KeyF = 0x46,
+ KeyG = 0x47,
+ KeyH = 0x48,
+ KeyI = 0x49,
+ KeyJ = 0x4A,
+ KeyK = 0x4B,
+ KeyL = 0x4C,
+ KeyM = 0x4D,
+ KeyN = 0x4E,
+ KeyO = 0x4F,
+ KeyP = 0x50,
+ KeyQ = 0x51,
+ KeyR = 0x52,
+ KeyS = 0x53,
+ KeyT = 0x54,
+ KeyU = 0x55,
+ KeyV = 0x56,
+ KeyW = 0x57,
+ KeyX = 0x58,
+ KeyY = 0x59,
+ KeyZ = 0x5A,
+ KeyOpenBracket = 0xDB,
+ KeyBackSlash = 0xDC,
+ KeyCloseBracket = 0xDD,
+ KeyF1 = 0x70,
+ KeyF2 = 0x71,
+ KeyF3 = 0x72,
+ KeyF4 = 0x73,
+ KeyF5 = 0x74,
+ KeyF6 = 0x75,
+ KeyF7 = 0x76,
+ KeyF8 = 0x77,
+ KeyF9 = 0x78,
+ KeyF10 = 0x79,
+ KeyF11 = 0x7A,
+ KeyF12 = 0x7B,
+ KeyDelete = 0x2E,
+ KeyNumLock = 0x90,
+ KeyScrollLock = 0x91,
+ KeyPrintScreen = 0x9A,
+ KeyInsert = 0x9B,
+ KeyHelp = 0x9C,
+ KeyBackQuote = 0xC0,
+ KeyQuote = 0xDE,
+ KeyFinal = 0x18,
+ KeyConvert = 0x1C,
+ KeyNonConvert = 0x1D,
+ KeyAccept = 0x1E,
+ KeyModeChange = 0x1F,
+ KeyKana = 0x15,
+ KeyKanji = 0x19,
+ KeyUndefined = 0x0
+ };
+
+public:
+
+ explicit QWasmEventTranslator(QObject *parent = 0);
+
+ static int keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData);
+ static int mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
+ static int focus_cb(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData);
+ static int wheel_cb(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData);
+
+ static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData);
+
+ void processEvents();
+
+Q_SIGNALS:
+ void getWindowAt(const QPoint &point, QWindow **window);
+private:
+ static Qt::Key translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey, bool *outAlphanumretic);
+ template <typename Event>
+ static QFlags<Qt::KeyboardModifier> translatKeyModifier(const Event *event);
+ static QFlags<Qt::KeyboardModifier> translateKeyboardEventModifier(const EmscriptenKeyboardEvent *keyEvent);
+ static QFlags<Qt::KeyboardModifier> translateMouseEventModifier(const EmscriptenMouseEvent *mouseEvent);
+ static Qt::MouseButton translateMouseButton(unsigned short button);
+
+ void processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent);
+
+private:
+ QWindow *draggedWindow;
+ QWindow *pressedWindow;
+ Qt::MouseButtons pressedButtons;
+
+ QWasmWindow::ResizeMode resizeMode;
+ QPoint resizePoint;
+ QRect resizeStartRect;
+ QTouchDevice *touchDevice;
+ quint64 getTimestamp();
+};
+
+QT_END_NAMESPACE
+#endif // QWASMEVENTTRANSLATOR_H
diff --git a/src/plugins/platforms/wasm/qwasmfontdatabase.cpp b/src/plugins/platforms/wasm/qwasmfontdatabase.cpp
new file mode 100644
index 0000000000..0c72dfddc4
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmfontdatabase.cpp
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwasmfontdatabase.h"
+
+#include <QtCore/qfile.h>
+
+QT_BEGIN_NAMESPACE
+
+void QWasmFontDatabase::populateFontDatabase()
+{
+ // Load font file from resources. Currently
+ // all fonts needs to be bundled with the nexe
+ // as Qt resources.
+ QStringList fontFileNames = QStringList() << QStringLiteral(":/fonts/Vera.ttf")
+ << QStringLiteral(":/fonts/DejaVuSans.ttf");
+
+ foreach (const QString &fontFileName, fontFileNames) {
+ QFile theFont(fontFileName);
+ if (!theFont.open(QIODevice::ReadOnly))
+ break;
+
+ QFreeTypeFontDatabase::addTTFile(theFont.readAll(), fontFileName.toLatin1());
+ }
+}
+
+QFontEngine *QWasmFontDatabase::fontEngine(const QFontDef &fontDef, void *handle)
+{
+ return QFreeTypeFontDatabase::fontEngine(fontDef, handle);
+}
+
+QStringList QWasmFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style,
+ QFont::StyleHint styleHint,
+ QChar::Script script) const
+{
+ QStringList fallbacks
+ = QFreeTypeFontDatabase::fallbacksForFamily(family, style, styleHint, script);
+
+ // Add the vera.ttf font (loaded in populateFontDatabase above) as a falback font
+ // to all other fonts (except itself).
+ const QString veraFontFamily = QStringLiteral("Bitstream Vera Sans");
+ if (family != veraFontFamily)
+ fallbacks.append(veraFontFamily);
+
+ return fallbacks;
+}
+
+QStringList QWasmFontDatabase::addApplicationFont(const QByteArray &fontData,
+ const QString &fileName)
+{
+ return QFreeTypeFontDatabase::addApplicationFont(fontData, fileName);
+}
+
+void QWasmFontDatabase::releaseHandle(void *handle)
+{
+ QFreeTypeFontDatabase::releaseHandle(handle);
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmfontdatabase.h b/src/plugins/platforms/wasm/qwasmfontdatabase.h
new file mode 100644
index 0000000000..891f12859e
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmfontdatabase.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWASMFONTDATABASE_H
+#define QWASMFONTDATABASE_H
+
+#include <QtFontDatabaseSupport/private/qfreetypefontdatabase_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWasmFontDatabase : public QFreeTypeFontDatabase
+{
+public:
+ void populateFontDatabase() override;
+ QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
+ QStringList fallbacksForFamily(const QString &family, QFont::Style style,
+ QFont::StyleHint styleHint,
+ QChar::Script script) const override;
+ QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName) override;
+ void releaseHandle(void *handle) override;
+};
+QT_END_NAMESPACE
+#endif
diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp
new file mode 100644
index 0000000000..accc1fd2fe
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmintegration.cpp
@@ -0,0 +1,219 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwasmintegration.h"
+#include "qwasmeventtranslator.h"
+#include "qwasmeventdispatcher.h"
+#include "qwasmcompositor.h"
+#include "qwasmopenglcontext.h"
+#include "qwasmtheme.h"
+
+#include "qwasmwindow.h"
+#ifndef QT_NO_OPENGL
+# include "qwasmbackingstore.h"
+#endif
+#include "qwasmfontdatabase.h"
+#if defined(Q_OS_UNIX)
+#include <QtEventDispatcherSupport/private/qgenericunixeventdispatcher_p.h>
+#endif
+#include <qpa/qplatformwindow.h>
+#include <QtGui/qscreen.h>
+#include <qpa/qwindowsysteminterface.h>
+#include <QtCore/qcoreapplication.h>
+
+#include <emscripten/bind.h>
+
+// this is where EGL headers are pulled in, make sure it is last
+#include "qwasmscreen.h"
+
+using namespace emscripten;
+QT_BEGIN_NAMESPACE
+
+void browserBeforeUnload()
+{
+ QWasmIntegration::QWasmBrowserExit();
+}
+
+EMSCRIPTEN_BINDINGS(my_module)
+{
+ function("browserBeforeUnload", &browserBeforeUnload);
+}
+
+static QWasmIntegration *globalHtml5Integration;
+QWasmIntegration *QWasmIntegration::get() { return globalHtml5Integration; }
+
+QWasmIntegration::QWasmIntegration()
+ : m_fontDb(nullptr),
+ m_compositor(new QWasmCompositor),
+ m_screen(new QWasmScreen(m_compositor)),
+ m_eventDispatcher(nullptr)
+{
+
+ globalHtml5Integration = this;
+
+ updateQScreenAndCanvasRenderSize();
+ screenAdded(m_screen);
+ emscripten_set_resize_callback(0, (void *)this, 1, uiEvent_cb);
+
+ m_eventTranslator = new QWasmEventTranslator;
+
+ EM_ASM(// exit app if browser closes
+ window.onbeforeunload = function () {
+ Module.browserBeforeUnload();
+ };
+ );
+}
+
+QWasmIntegration::~QWasmIntegration()
+{
+ delete m_compositor;
+ destroyScreen(m_screen);
+ delete m_fontDb;
+ delete m_eventTranslator;
+}
+
+void QWasmIntegration::QWasmBrowserExit()
+{
+ QCoreApplication *app = QCoreApplication::instance();
+ app->quit();
+}
+
+bool QWasmIntegration::hasCapability(QPlatformIntegration::Capability cap) const
+{
+ switch (cap) {
+ case ThreadedPixmaps: return true;
+ case OpenGL: return true;
+ case ThreadedOpenGL: return true;
+ case RasterGLSurface: return true;
+ case MultipleWindows: return true;
+ case WindowManagement: return true;
+ default: return QPlatformIntegration::hasCapability(cap);
+ }
+}
+
+QPlatformWindow *QWasmIntegration::createPlatformWindow(QWindow *window) const
+{
+ return new QWasmWindow(window, m_compositor, m_backingStores.value(window));
+}
+
+QPlatformBackingStore *QWasmIntegration::createPlatformBackingStore(QWindow *window) const
+{
+#ifndef QT_NO_OPENGL
+ QWasmBackingStore *backingStore = new QWasmBackingStore(m_compositor, window);
+ m_backingStores.insert(window, backingStore);
+ return backingStore;
+#else
+ return nullptr;
+#endif
+}
+
+#ifndef QT_NO_OPENGL
+QPlatformOpenGLContext *QWasmIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
+{
+ return new QWasmOpenGLContext(context->format());
+}
+#endif
+
+QPlatformFontDatabase *QWasmIntegration::fontDatabase() const
+{
+ if (m_fontDb == nullptr)
+ m_fontDb = new QWasmFontDatabase;
+
+ return m_fontDb;
+}
+
+QAbstractEventDispatcher *QWasmIntegration::createEventDispatcher() const
+{
+ return new QWasmEventDispatcher;
+}
+
+QVariant QWasmIntegration::styleHint(QPlatformIntegration::StyleHint hint) const
+{
+ return QPlatformIntegration::styleHint(hint);
+}
+
+QStringList QWasmIntegration::themeNames() const
+{
+ return QStringList() << QLatin1String("webassembly");
+}
+
+QPlatformTheme *QWasmIntegration::createPlatformTheme(const QString &name) const
+{
+ if (name == QLatin1String("webassembly"))
+ return new QWasmTheme;
+ return QPlatformIntegration::createPlatformTheme(name);
+}
+
+int QWasmIntegration::uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData)
+{
+ Q_UNUSED(e)
+ Q_UNUSED(userData)
+
+ if (eventType == EMSCRIPTEN_EVENT_RESIZE) {
+ // This resize event is called when the HTML window is resized. Depending
+ // on the page layout the the canvas might also have been resized, so we
+ // update the Qt screen size (and canvas render size).
+ updateQScreenAndCanvasRenderSize();
+ }
+
+ return 0;
+}
+
+static void set_canvas_size(double width, double height)
+{
+ EM_ASM_({
+ var canvas = Module.canvas;
+ canvas.width = $0;
+ canvas.height = $1;
+ }, width, height);
+}
+
+void QWasmIntegration::updateQScreenAndCanvasRenderSize()
+{
+ // The HTML canvas has two sizes: the CSS size and the canvas render size.
+ // The CSS size is determined according to standard CSS rules, while the
+ // render size is set using the "width" and "height" attributes. The render
+ // size must be set manually and is not auto-updated on CSS size change.
+ // Setting the render size to a value larger than the CSS size enables high-dpi
+ // rendering.
+
+ double css_width;
+ double css_height;
+ emscripten_get_element_css_size(0, &css_width, &css_height);
+ QSizeF cssSize(css_width, css_height);
+
+ QWasmScreen *screen = QWasmIntegration::get()->m_screen;
+ QSizeF canvasSize = cssSize * screen->devicePixelRatio();
+
+ set_canvas_size(canvasSize.width(), canvasSize.height());
+ screen->setGeometry(QRect(QPoint(0, 0), cssSize.toSize()));
+ QWasmIntegration::get()->m_compositor->redrawWindowContent();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmintegration.h b/src/plugins/platforms/wasm/qwasmintegration.h
new file mode 100644
index 0000000000..ebc3d9d431
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmintegration.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWASMINTEGRATION_H
+#define QWASMINTEGRATION_H
+
+#include "qwasmwindow.h"
+
+#include <qpa/qplatformintegration.h>
+#include <qpa/qplatformscreen.h>
+
+#include <QtCore/qhash.h>
+
+#include <emscripten.h>
+#include <emscripten/html5.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWasmEventTranslator;
+class QWasmFontDatabase;
+class QWasmWindow;
+class QWasmEventDispatcher;
+class QWasmScreen;
+class QWasmCompositor;
+class QWasmBackingStore;
+
+class QWasmIntegration : public QObject, public QPlatformIntegration
+{
+ Q_OBJECT
+public:
+ QWasmIntegration();
+ ~QWasmIntegration();
+
+ bool hasCapability(QPlatformIntegration::Capability cap) const override;
+ QPlatformWindow *createPlatformWindow(QWindow *window) const override;
+ QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;
+#ifndef QT_NO_OPENGL
+ QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override;
+#endif
+ QPlatformFontDatabase *fontDatabase() const override;
+ QAbstractEventDispatcher *createEventDispatcher() const override;
+ QVariant styleHint(QPlatformIntegration::StyleHint hint) const override;
+ QStringList themeNames() const override;
+ QPlatformTheme *createPlatformTheme(const QString &name) const override;
+
+ static QWasmIntegration *get();
+ QWasmScreen *screen() { return m_screen; }
+ QWasmCompositor *compositor() { return m_compositor; }
+ QWasmEventTranslator *eventTranslator() { return m_eventTranslator; }
+
+ static void QWasmBrowserExit();
+ static void updateQScreenAndCanvasRenderSize();
+
+private:
+ mutable QWasmFontDatabase *m_fontDb;
+ QWasmCompositor *m_compositor;
+ mutable QWasmScreen *m_screen;
+ mutable QWasmEventTranslator *m_eventTranslator;
+ mutable QWasmEventDispatcher *m_eventDispatcher;
+ static int uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData);
+ mutable QHash<QWindow *, QWasmBackingStore *> m_backingStores;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWASMINTEGRATION_H
diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp
new file mode 100644
index 0000000000..73af3d1878
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwasmopenglcontext.h"
+
+#include <EGL/egl.h>
+
+QT_BEGIN_NAMESPACE
+
+QWasmOpenGLContext::QWasmOpenGLContext(const QSurfaceFormat &format)
+ : m_requestedFormat(format)
+{
+ m_requestedFormat.setRenderableType(QSurfaceFormat::OpenGLES);
+}
+
+QWasmOpenGLContext::~QWasmOpenGLContext()
+{
+ if (m_context)
+ emscripten_webgl_destroy_context(m_context);
+}
+
+void QWasmOpenGLContext::maybeRecreateEmscriptenContext(QPlatformSurface *surface)
+{
+ // Native emscripten contexts are tied to a single surface. Recreate
+ // the context if the surface is changed.
+ if (surface != m_surface) {
+ m_surface = surface;
+
+ // Destroy existing context
+ if (m_context)
+ emscripten_webgl_destroy_context(m_context);
+
+ // Create new context
+ const char *canvasId = 0; // (use default canvas) FIXME: get the actual canvas from the surface.
+ m_context = createEmscriptenContext(canvasId, m_requestedFormat);
+
+ // Register context-lost callback.
+ auto callback = [](int eventType, const void *reserved, void *userData) -> EM_BOOL
+ {
+ Q_UNUSED(eventType);
+ Q_UNUSED(reserved);
+ // The application may get contex-lost if e.g. moved to the background. Set
+ // m_contextLost which will make isValid() return false. Application code will
+ // then detect this and recrate the the context, resulting in a new QWasmOpenGLContext
+ // instance.
+ reinterpret_cast<QWasmOpenGLContext *>(userData)->m_contextLost = true;
+ return true;
+ };
+ bool capture = true;
+ emscripten_set_webglcontextlost_callback(canvasId, this, capture, callback);
+ }
+}
+
+EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(const char *canvasId, QSurfaceFormat format)
+{
+ EmscriptenWebGLContextAttributes attributes;
+ emscripten_webgl_init_context_attributes(&attributes); // Populate with default attributes
+
+ attributes.preferLowPowerToHighPerformance = false;
+ attributes.failIfMajorPerformanceCaveat = false;
+ attributes.antialias = true;
+ attributes.enableExtensionsByDefault = true;
+
+ if (format.majorVersion() == 3) {
+ attributes.majorVersion = 2;
+ }
+
+ // WebGL offers enable/disable control but not size control for these
+ attributes.alpha = format.alphaBufferSize() > 0;
+ attributes.depth = format.depthBufferSize() > 0;
+ attributes.stencil = format.stencilBufferSize() > 0;
+
+ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context(canvasId, &attributes);
+
+ return context;
+}
+
+QSurfaceFormat QWasmOpenGLContext::format() const
+{
+ return m_requestedFormat;
+}
+
+GLuint QWasmOpenGLContext::defaultFramebufferObject(QPlatformSurface *surface) const
+{
+ return QPlatformOpenGLContext::defaultFramebufferObject(surface);
+}
+
+bool QWasmOpenGLContext::makeCurrent(QPlatformSurface *surface)
+{
+ maybeRecreateEmscriptenContext(surface);
+
+ return emscripten_webgl_make_context_current(m_context) == EMSCRIPTEN_RESULT_SUCCESS;
+}
+
+void QWasmOpenGLContext::swapBuffers(QPlatformSurface *surface)
+{
+ Q_UNUSED(surface);
+ // No swapbuffers on WebGl
+}
+
+void QWasmOpenGLContext::doneCurrent()
+{
+ // No doneCurrent on WebGl
+}
+
+bool QWasmOpenGLContext::isSharing() const
+{
+ return false;
+}
+
+bool QWasmOpenGLContext::isValid() const
+{
+ return (m_contextLost == false);
+}
+
+QFunctionPointer QWasmOpenGLContext::getProcAddress(const char *procName)
+{
+ return reinterpret_cast<QFunctionPointer>(eglGetProcAddress(procName));
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.h b/src/plugins/platforms/wasm/qwasmopenglcontext.h
new file mode 100644
index 0000000000..9123100479
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmopenglcontext.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qpa/qplatformopenglcontext.h>
+
+#include <emscripten.h>
+#include <emscripten/html5.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWasmOpenGLContext : public QPlatformOpenGLContext
+{
+public:
+ QWasmOpenGLContext(const QSurfaceFormat &format);
+ ~QWasmOpenGLContext();
+
+ QSurfaceFormat format() const override;
+ void swapBuffers(QPlatformSurface *surface) override;
+ GLuint defaultFramebufferObject(QPlatformSurface *surface) const override;
+ bool makeCurrent(QPlatformSurface *surface) override;
+ void doneCurrent() override;
+ bool isSharing() const override;
+ bool isValid() const override;
+ QFunctionPointer getProcAddress(const char *procName) override;
+
+private:
+ void maybeRecreateEmscriptenContext(QPlatformSurface *surface);
+ static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE createEmscriptenContext(const char *canvasId, QSurfaceFormat format);
+
+ bool m_contextLost = false;
+ QSurfaceFormat m_requestedFormat;
+ QPlatformSurface *m_surface = nullptr;
+ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE m_context = 0;
+};
+
+QT_END_NAMESPACE
+
diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp
new file mode 100644
index 0000000000..93e9906ffc
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmscreen.cpp
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwasmscreen.h"
+#include "qwasmwindow.h"
+#include "qwasmcompositor.h"
+
+#include <QtEglSupport/private/qeglconvenience_p.h>
+#ifndef QT_NO_OPENGL
+# include <QtEglSupport/private/qeglplatformcontext_p.h>
+#endif
+#include <qpa/qwindowsysteminterface.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtGui/qguiapplication.h>
+#include <private/qhighdpiscaling_p.h>
+
+
+QT_BEGIN_NAMESPACE
+
+QWasmScreen::QWasmScreen(QWasmCompositor *compositor)
+ : m_compositor(compositor)
+ , m_depth(32)
+ , m_format(QImage::Format_RGB32)
+{
+ m_compositor->setScreen(this);
+}
+
+QWasmScreen::~QWasmScreen()
+{
+
+}
+
+QRect QWasmScreen::geometry() const
+{
+ return m_geometry;
+}
+
+int QWasmScreen::depth() const
+{
+ return m_depth;
+}
+
+QImage::Format QWasmScreen::format() const
+{
+ return m_format;
+}
+
+qreal QWasmScreen::devicePixelRatio() const
+{
+ // FIXME: The effective device pixel ratio may be different from the
+ // HTML window dpr if the OpenGL driver/GPU allocates a less than
+ // full resolution surface. Use emscripten_webgl_get_drawing_buffer_size()
+ // and compute the dpr instead.
+ double htmlWindowDpr = EM_ASM_DOUBLE({
+ return window.devicePixelRatio;
+ });
+ return qreal(htmlWindowDpr);
+}
+
+QPlatformCursor *QWasmScreen::cursor() const
+{
+ return const_cast<QWasmCursor *>(&m_cursor);
+}
+
+void QWasmScreen::resizeMaximizedWindows()
+{
+ QPlatformScreen::resizeMaximizedWindows();
+}
+
+QWindow *QWasmScreen::topWindow() const
+{
+ return m_compositor->keyWindow();
+}
+
+QWindow *QWasmScreen::topLevelAt(const QPoint &p) const
+{
+ return m_compositor->windowAt(p);
+}
+
+void QWasmScreen::invalidateSize()
+{
+ m_geometry = QRect();
+}
+
+void QWasmScreen::setGeometry(const QRect &rect)
+{
+ m_geometry = rect;
+ QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry());
+ resizeMaximizedWindows();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmscreen.h b/src/plugins/platforms/wasm/qwasmscreen.h
new file mode 100644
index 0000000000..3891db77bb
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmscreen.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWASMSCREEN_H
+#define QWASMSCREEN_H
+
+#include "qwasmcursor.h"
+
+#include <qpa/qplatformscreen.h>
+
+#include <QtCore/qscopedpointer.h>
+#include <QtCore/qtextstream.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPlatformOpenGLContext;
+class QWasmWindow;
+class QWasmBackingStore;
+class QWasmCompositor;
+class QOpenGLContext;
+
+class QWasmScreen : public QObject, public QPlatformScreen
+{
+ Q_OBJECT
+public:
+
+ QWasmScreen(QWasmCompositor *compositor);
+ ~QWasmScreen();
+
+ QRect geometry() const override;
+ int depth() const override;
+ QImage::Format format() const override;
+ qreal devicePixelRatio() const override;
+ QPlatformCursor *cursor() const override;
+
+ void resizeMaximizedWindows();
+ QWindow *topWindow() const;
+ QWindow *topLevelAt(const QPoint &p) const override;
+
+ void invalidateSize();
+
+public slots:
+ void setGeometry(const QRect &rect);
+protected:
+
+private:
+ QWasmCompositor *m_compositor;
+
+ QRect m_geometry = QRect(0, 0, 100, 100);
+ int m_depth;
+ QImage::Format m_format;
+ QWasmCursor m_cursor;
+};
+
+QT_END_NAMESPACE
+#endif // QWASMSCREEN_H
diff --git a/src/plugins/platforms/wasm/qwasmstylepixmaps_p.h b/src/plugins/platforms/wasm/qwasmstylepixmaps_p.h
new file mode 100644
index 0000000000..2b5860f42f
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmstylepixmaps_p.h
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWASMSTYLEPIXMAPS_P_H
+#define QWASMSTYLEPIXMAPS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+/* XPM */
+static const char * const qt_menu_xpm[] = {
+"16 16 72 1",
+" c None",
+". c #65AF36",
+"+ c #66B036",
+"@ c #77B94C",
+"# c #A7D28C",
+"$ c #BADBA4",
+"% c #A4D088",
+"& c #72B646",
+"* c #9ACB7A",
+"= c #7FBD56",
+"- c #85C05F",
+"; c #F4F9F0",
+"> c #FFFFFF",
+", c #E5F1DC",
+"' c #ECF5E7",
+") c #7ABA50",
+"! c #83BF5C",
+"~ c #AED595",
+"{ c #D7EACA",
+"] c #A9D28D",
+"^ c #BCDDA8",
+"/ c #C4E0B1",
+"( c #81BE59",
+"_ c #D0E7C2",
+": c #D4E9C6",
+"< c #6FB542",
+"[ c #6EB440",
+"} c #88C162",
+"| c #98CA78",
+"1 c #F4F9F1",
+"2 c #8FC56C",
+"3 c #F1F8EC",
+"4 c #E8F3E1",
+"5 c #D4E9C7",
+"6 c #74B748",
+"7 c #80BE59",
+"8 c #73B747",
+"9 c #6DB43F",
+"0 c #CBE4BA",
+"a c #80BD58",
+"b c #6DB33F",
+"c c #FEFFFE",
+"d c #68B138",
+"e c #F9FCF7",
+"f c #91C66F",
+"g c #E8F3E0",
+"h c #DCEDD0",
+"i c #91C66E",
+"j c #A3CF86",
+"k c #C9E3B8",
+"l c #B0D697",
+"m c #E3F0DA",
+"n c #95C873",
+"o c #E6F2DE",
+"p c #9ECD80",
+"q c #BEDEAA",
+"r c #C7E2B6",
+"s c #79BA4F",
+"t c #6EB441",
+"u c #BCDCA7",
+"v c #FAFCF8",
+"w c #F6FAF3",
+"x c #84BF5D",
+"y c #EDF6E7",
+"z c #FAFDF9",
+"A c #88C263",
+"B c #98CA77",
+"C c #CDE5BE",
+"D c #67B037",
+"E c #D9EBCD",
+"F c #6AB23C",
+"G c #77B94D",
+" .++++++++++++++",
+".+++++++++++++++",
+"+++@#$%&+++*=+++",
+"++-;>,>')+!>~+++",
+"++{>]+^>/(_>:~<+",
+"+[>>}+|>123>456+",
+"+7>>8+->>90>~+++",
+"+a>>b+a>c[0>~+++",
+"+de>=+f>g+0>~+++",
+"++h>i+j>k+0>~+++",
+"++l>mno>p+q>rst+",
+"++duv>wl++xy>zA+",
+"++++B>Cb++++&D++",
+"+++++0zE++++++++",
+"++++++FG+++++++.",
+"++++++++++++++. "};
+
+static const char * const qt_close_xpm[] = {
+"10 10 2 1",
+"# c #000000",
+". c None",
+"..........",
+".##....##.",
+"..##..##..",
+"...####...",
+"....##....",
+"...####...",
+"..##..##..",
+".##....##.",
+"..........",
+".........."};
+
+static const char * const qt_maximize_xpm[]={
+"10 10 2 1",
+"# c #000000",
+". c None",
+"#########.",
+"#########.",
+"#.......#.",
+"#.......#.",
+"#.......#.",
+"#.......#.",
+"#.......#.",
+"#.......#.",
+"#########.",
+".........."};
+
+
+static const char * const qt_normalizeup_xpm[] = {
+"10 10 2 1",
+"# c #000000",
+". c None",
+"...######.",
+"...######.",
+"...#....#.",
+".######.#.",
+".######.#.",
+".#....###.",
+".#....#...",
+".#....#...",
+".######...",
+".........."};
+
+
+#endif // QWASMSTYLEPIXMAPS_P_H
diff --git a/src/plugins/platforms/wasm/qwasmtheme.cpp b/src/plugins/platforms/wasm/qwasmtheme.cpp
new file mode 100644
index 0000000000..a7f2db3bd3
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmtheme.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwasmtheme.h"
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+QWasmTheme::QWasmTheme()
+{
+}
+
+QWasmTheme::~QWasmTheme()
+{
+}
+
+QVariant QWasmTheme::themeHint(ThemeHint hint) const
+{
+ if (hint == QPlatformTheme::StyleNames)
+ return QVariant(QStringList() << QLatin1String("fusion"));
+ return QPlatformTheme::themeHint(hint);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmtheme.h b/src/plugins/platforms/wasm/qwasmtheme.h
new file mode 100644
index 0000000000..e4cc06e049
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmtheme.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWASMTHEME_H
+#define QWASMTHEME_H
+
+#include <qpa/qplatformtheme.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWasmEventTranslator;
+class QWasmFontDatabase;
+class QWasmWindow;
+class QWasmEventDispatcher;
+class QWasmScreen;
+class QWasmCompositor;
+class QWasmBackingStore;
+
+class QWasmTheme : public QPlatformTheme
+{
+public:
+ QWasmTheme();
+ ~QWasmTheme();
+
+ QVariant themeHint(ThemeHint hint) const override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWASMTHEME_H
diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp
new file mode 100644
index 0000000000..0489813929
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmwindow.cpp
@@ -0,0 +1,398 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qpa/qwindowsysteminterface.h>
+#include <private/qguiapplication_p.h>
+#include <QtGui/private/qopenglcontext_p.h>
+#include <QtGui/private/qwindow_p.h>
+#include <QtGui/qopenglcontext.h>
+
+#include "qwasmwindow.h"
+#include "qwasmscreen.h"
+#include "qwasmcompositor.h"
+#include "qwasmeventdispatcher.h"
+
+#include <iostream>
+
+Q_GUI_EXPORT int qt_defaultDpiX();
+
+QT_BEGIN_NAMESPACE
+
+QWasmWindow::QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingStore *backingStore)
+ : QPlatformWindow(w),
+ m_window(w),
+ m_compositor(compositor),
+ m_backingStore(backingStore)
+{
+ m_needsCompositor = w->surfaceType() != QSurface::OpenGLSurface;
+ static int serialNo = 0;
+ m_winid = ++serialNo;
+ qWarning("QWasmWindow %p: %p 0x%x\n", this, w, uint(m_winid));
+
+ m_compositor->addWindow(this);
+
+ // Pure OpenGL windows draw directly using egl, disable the compositor.
+ m_compositor->setEnabled(w->surfaceType() != QSurface::OpenGLSurface);
+}
+
+QWasmWindow::~QWasmWindow()
+{
+ m_compositor->removeWindow(this);
+}
+
+void QWasmWindow::initialize()
+{
+ QRect rect = windowGeometry();
+
+ QPlatformWindow::setGeometry(rect);
+
+ const QSize minimumSize = windowMinimumSize();
+ if (rect.width() > 0 || rect.height() > 0) {
+ rect.setWidth(qBound(1, rect.width(), 2000));
+ rect.setHeight(qBound(1, rect.height(), 2000));
+ } else if (minimumSize.width() > 0 || minimumSize.height() > 0) {
+ rect.setSize(minimumSize);
+ }
+
+ setWindowState(window()->windowStates());
+ setWindowFlags(window()->flags());
+ setWindowTitle(window()->title());
+ m_hasTitle = window()->flags().testFlag(Qt::WindowTitleHint) && m_needsCompositor;
+
+ if (window()->isTopLevel())
+ setWindowIcon(window()->icon());
+ m_normalGeometry = rect;
+}
+
+QWasmScreen *QWasmWindow::platformScreen() const
+{
+ return static_cast<QWasmScreen *>(window()->screen()->handle());
+}
+
+void QWasmWindow::setGeometry(const QRect &rect)
+{
+ QRect r = rect;
+ if (m_needsCompositor) {
+ int yMin = window()->geometry().top() - window()->frameGeometry().top();
+
+ if (r.y() < yMin)
+ r.moveTop(yMin);
+ }
+ QWindowSystemInterface::handleGeometryChange(window(), r);
+ QPlatformWindow::setGeometry(r);
+
+ QWindowSystemInterface::flushWindowSystemEvents();
+ invalidate();
+}
+
+void QWasmWindow::setVisible(bool visible)
+{
+ QRect newGeom;
+
+ if (visible) {
+ const bool forceFullScreen = !m_needsCompositor;//make gl apps fullscreen for now
+
+ if (forceFullScreen || (m_windowState & Qt::WindowFullScreen))
+ newGeom = platformScreen()->geometry();
+ else if (m_windowState & Qt::WindowMaximized)
+ newGeom = platformScreen()->availableGeometry();
+ }
+ QPlatformWindow::setVisible(visible);
+
+ m_compositor->setVisible(this, visible);
+
+ if (!newGeom.isEmpty())
+ setGeometry(newGeom); // may or may not generate an expose
+
+ invalidate();
+}
+
+QMargins QWasmWindow::frameMargins() const
+{
+ int border = m_hasTitle ? 4. * (qreal(qt_defaultDpiX()) / 96.0) : 0;
+ int titleBarHeight = m_hasTitle ? titleHeight() : 0;
+
+ QMargins margins;
+ margins.setLeft(border);
+ margins.setRight(border);
+ margins.setTop(2*border + titleBarHeight);
+ margins.setBottom(border);
+
+ return margins;
+}
+
+void QWasmWindow::raise()
+{
+ m_compositor->raise(this);
+ QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), geometry().size()));
+ invalidate();
+}
+
+void QWasmWindow::lower()
+{
+ m_compositor->lower(this);
+ QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), geometry().size()));
+ invalidate();
+}
+
+WId QWasmWindow::winId() const
+{
+ return m_winid;
+}
+
+void QWasmWindow::propagateSizeHints()
+{
+// get rid of base class warning
+}
+
+void QWasmWindow::injectMousePressed(const QPoint &local, const QPoint &global,
+ Qt::MouseButton button, Qt::KeyboardModifiers mods)
+{
+ Q_UNUSED(local);
+ Q_UNUSED(mods);
+
+ if (!m_hasTitle || button != Qt::LeftButton)
+ return;
+
+ if (maxButtonRect().contains(global))
+ m_activeControl = QWasmCompositor::SC_TitleBarMaxButton;
+ else if (minButtonRect().contains(global))
+ m_activeControl = QWasmCompositor::SC_TitleBarMinButton;
+ else if (closeButtonRect().contains(global))
+ m_activeControl = QWasmCompositor::SC_TitleBarCloseButton;
+ else if (normButtonRect().contains(global))
+ m_activeControl = QWasmCompositor::SC_TitleBarNormalButton;
+
+ invalidate();
+}
+
+void QWasmWindow::injectMouseReleased(const QPoint &local, const QPoint &global,
+ Qt::MouseButton button, Qt::KeyboardModifiers mods)
+{
+ Q_UNUSED(local);
+ Q_UNUSED(mods);
+
+ if (!m_hasTitle || button != Qt::LeftButton)
+ return;
+
+ if (closeButtonRect().contains(global) && m_activeControl == QWasmCompositor::SC_TitleBarCloseButton)
+ window()->close();
+
+ if (maxButtonRect().contains(global) && m_activeControl == QWasmCompositor::SC_TitleBarMaxButton) {
+ window()->setWindowState(Qt::WindowMaximized);
+ platformScreen()->resizeMaximizedWindows();
+ }
+
+ if (normButtonRect().contains(global) && m_activeControl == QWasmCompositor::SC_TitleBarNormalButton) {
+ window()->setWindowState(Qt::WindowNoState);
+ setGeometry(normalGeometry());
+ }
+
+ m_activeControl = QWasmCompositor::SC_None;
+
+ invalidate();
+}
+
+int QWasmWindow::titleHeight() const
+{
+ return 18. * (qreal(qt_defaultDpiX()) / 96.0);//dpiScaled(18.);
+}
+
+int QWasmWindow::borderWidth() const
+{
+ return 4. * (qreal(qt_defaultDpiX()) / 96.0);// dpiScaled(4.);
+}
+
+QRegion QWasmWindow::titleGeometry() const
+{
+ int border = borderWidth();
+
+ QRegion result(window()->frameGeometry().x() + border,
+ window()->frameGeometry().y() + border,
+ window()->frameGeometry().width() - 2*border,
+ titleHeight());
+
+ result -= titleControlRegion();
+
+ return result;
+}
+
+QRegion QWasmWindow::resizeRegion() const
+{
+ int border = borderWidth();
+ QRegion result(window()->frameGeometry().adjusted(-border, -border, border, border));
+ result -= window()->frameGeometry().adjusted(border, border, -border, -border);
+
+ return result;
+}
+
+bool QWasmWindow::isPointOnTitle(QPoint point) const
+{
+ bool ok = titleGeometry().contains(point);
+ return ok;
+}
+
+bool QWasmWindow::isPointOnResizeRegion(QPoint point) const
+{
+ return resizeRegion().contains(point);
+}
+
+QWasmWindow::ResizeMode QWasmWindow::resizeModeAtPoint(QPoint point) const
+{
+ QPoint p1 = window()->frameGeometry().topLeft() - QPoint(5, 5);
+ QPoint p2 = window()->frameGeometry().bottomRight() + QPoint(5, 5);
+ int corner = 20;
+
+ QRect top(p1, QPoint(p2.x(), p1.y() + corner));
+ QRect middle(QPoint(p1.x(), p1.y() + corner), QPoint(p2.x(), p2.y() - corner));
+ QRect bottom(QPoint(p1.x(), p2.y() - corner), p2);
+
+ QRect left(p1, QPoint(p1.x() + corner, p2.y()));
+ QRect center(QPoint(p1.x() + corner, p1.y()), QPoint(p2.x() - corner, p2.y()));
+ QRect right(QPoint(p2.x() - corner, p1.y()), p2);
+
+ if (top.contains(point)) {
+ // Top
+ if (left.contains(point))
+ return ResizeTopLeft;
+ if (center.contains(point))
+ return ResizeTop;
+ if (right.contains(point))
+ return ResizeTopRight;
+ } else if (middle.contains(point)) {
+ // Middle
+ if (left.contains(point))
+ return ResizeLeft;
+ if (right.contains(point))
+ return ResizeRight;
+ } else if (bottom.contains(point)) {
+ // Bottom
+ if (left.contains(point))
+ return ResizeBottomLeft;
+ if (center.contains(point))
+ return ResizeBottom;
+ if (right.contains(point))
+ return ResizeBottomRight;
+ }
+
+ return ResizeNone;
+}
+
+QRect getSubControlRect(const QWasmWindow *window, QWasmCompositor::SubControls subControl)
+{
+ QWasmCompositor::QWasmTitleBarOptions options = QWasmCompositor::makeTitleBarOptions(window);
+
+ QRect r = QWasmCompositor::titlebarRect(options, subControl);
+ r.translate(window->window()->frameGeometry().x(), window->window()->frameGeometry().y());
+
+ return r;
+}
+
+QRect QWasmWindow::maxButtonRect() const
+{
+ return getSubControlRect(this, QWasmCompositor::SC_TitleBarMaxButton);
+}
+
+QRect QWasmWindow::minButtonRect() const
+{
+ return getSubControlRect(this, QWasmCompositor::SC_TitleBarMinButton);
+}
+
+QRect QWasmWindow::closeButtonRect() const
+{
+ return getSubControlRect(this, QWasmCompositor::SC_TitleBarCloseButton);
+}
+
+QRect QWasmWindow::normButtonRect() const
+{
+ return getSubControlRect(this, QWasmCompositor::SC_TitleBarNormalButton);
+}
+
+QRect QWasmWindow::sysMenuRect() const
+{
+ return getSubControlRect(this, QWasmCompositor::SC_TitleBarSysMenu);
+}
+
+QRegion QWasmWindow::titleControlRegion() const
+{
+ QRegion result;
+ result += closeButtonRect();
+ result += minButtonRect();
+ result += maxButtonRect();
+ result += sysMenuRect();
+
+ return result;
+}
+
+void QWasmWindow::invalidate()
+{
+ m_compositor->requestRedraw();
+}
+
+QWasmCompositor::SubControls QWasmWindow::activeSubControl() const
+{
+ return m_activeControl;
+}
+
+void QWasmWindow::setWindowState(Qt::WindowStates states)
+{
+ m_windowState = Qt::WindowNoState;
+ if (states & Qt::WindowMinimized)
+ m_windowState = Qt::WindowMinimized;
+ else if (states & Qt::WindowFullScreen)
+ m_windowState = Qt::WindowFullScreen;
+ else if (states & Qt::WindowMaximized)
+ m_windowState = Qt::WindowMaximized;
+}
+
+QRect QWasmWindow::normalGeometry() const
+{
+ return m_normalGeometry;
+}
+
+qreal QWasmWindow::devicePixelRatio() const
+{
+ return screen()->devicePixelRatio();
+}
+
+void QWasmWindow::requestUpdate()
+{
+ QPointer<QWindow> windowPointer(window());
+ bool registered = QWasmEventDispatcher::registerRequestUpdateCallback([=](){
+ if (windowPointer.isNull())
+ return;
+
+ deliverUpdateRequest();
+ });
+
+ if (!registered)
+ QPlatformWindow::requestUpdate();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmwindow.h b/src/plugins/platforms/wasm/qwasmwindow.h
new file mode 100644
index 0000000000..a0c463e796
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmwindow.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWASMWINDOW_H
+#define QWASMWINDOW_H
+
+#include "qwasmintegration.h"
+#include <qpa/qplatformwindow.h>
+#include <emscripten/html5.h>
+#include "qwasmbackingstore.h"
+#include "qwasmscreen.h"
+#include "qwasmcompositor.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWasmCompositor;
+
+class QWasmWindow : public QPlatformWindow
+{
+public:
+ enum ResizeMode {
+ ResizeNone,
+ ResizeTopLeft,
+ ResizeTop,
+ ResizeTopRight,
+ ResizeRight,
+ ResizeBottomRight,
+ ResizeBottom,
+ ResizeBottomLeft,
+ ResizeLeft
+ };
+
+ QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingStore *backingStore);
+ ~QWasmWindow();
+
+ void initialize() override;
+
+ void setGeometry(const QRect &) override;
+ void setVisible(bool visible) override;
+ QMargins frameMargins() const override;
+
+ WId winId() const override;
+
+ void propagateSizeHints() override;
+ void raise() override;
+ void lower() override;
+ QRect normalGeometry() const override;
+ qreal devicePixelRatio() const override;
+ void requestUpdate() override;
+
+ QWasmScreen *platformScreen() const;
+ void setBackingStore(QWasmBackingStore *store) { m_backingStore = store; }
+ QWasmBackingStore *backingStore() const { return m_backingStore; }
+ QWindow *window() const { return m_window; }
+
+ void injectMousePressed(const QPoint &local, const QPoint &global,
+ Qt::MouseButton button, Qt::KeyboardModifiers mods);
+ void injectMouseReleased(const QPoint &local, const QPoint &global,
+ Qt::MouseButton button, Qt::KeyboardModifiers mods);
+
+ int titleHeight() const;
+ int borderWidth() const;
+ QRegion titleGeometry() const;
+ QRegion resizeRegion() const;
+ bool isPointOnTitle(QPoint point) const;
+ bool isPointOnResizeRegion(QPoint point) const;
+ ResizeMode resizeModeAtPoint(QPoint point) const;
+ QRect maxButtonRect() const;
+ QRect minButtonRect() const;
+ QRect closeButtonRect() const;
+ QRect sysMenuRect() const;
+ QRect normButtonRect() const;
+ QRegion titleControlRegion() const;
+ QWasmCompositor::SubControls activeSubControl() const;
+
+ void setWindowState(Qt::WindowStates state) override;
+ bool setKeyboardGrabEnabled(bool) override { return false; }
+ bool setMouseGrabEnabled(bool) override { return false; }
+
+protected:
+ void invalidate();
+
+protected:
+ friend class QWasmScreen;
+
+ QWindow* m_window = nullptr;
+ QWasmCompositor *m_compositor = nullptr;
+ QWasmBackingStore *m_backingStore = nullptr;
+ QRect m_normalGeometry {0, 0, 0 ,0};
+
+ Qt::WindowState m_windowState = Qt::WindowNoState;
+ QWasmCompositor::SubControls m_activeControl = QWasmCompositor::SC_None;
+ WId m_winid = 0;
+ bool m_hasTitle = false;
+ bool m_needsCompositor = false;
+};
+QT_END_NAMESPACE
+#endif // QWASMWINDOW_H
diff --git a/src/plugins/platforms/wasm/wasm.json b/src/plugins/platforms/wasm/wasm.json
new file mode 100644
index 0000000000..6e700e06b9
--- /dev/null
+++ b/src/plugins/platforms/wasm/wasm.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "wasm" ]
+}
diff --git a/src/plugins/platforms/wasm/wasm.pro b/src/plugins/platforms/wasm/wasm.pro
new file mode 100644
index 0000000000..f1205702ef
--- /dev/null
+++ b/src/plugins/platforms/wasm/wasm.pro
@@ -0,0 +1,65 @@
+TARGET = wasm
+CONFIG += static plugin
+QT += \
+ core-private gui-private \
+ eventdispatcher_support-private fontdatabase_support-private egl_support-private
+
+# Avoid X11 header collision, use generic EGL native types
+DEFINES += QT_EGL_NO_X11
+
+SOURCES = \
+ main.cpp \
+ qwasmintegration.cpp \
+ qwasmwindow.cpp \
+ qwasmscreen.cpp \
+ qwasmfontdatabase.cpp \
+ qwasmeventtranslator.cpp \
+ qwasmeventdispatcher.cpp \
+ qwasmcompositor.cpp \
+ qwasmcursor.cpp \
+ qwasmopenglcontext.cpp \
+ qwasmtheme.cpp
+
+HEADERS = \
+ qwasmintegration.h \
+ qwasmwindow.h \
+ qwasmscreen.h \
+ qwasmfontdatabase.h \
+ qwasmeventtranslator.h \
+ qwasmeventdispatcher.h \
+ qwasmcompositor.h \
+ qwasmstylepixmaps_p.h \
+ qwasmcursor.h \
+ qwasmopenglcontext.h \
+ qwasmtheme.h
+
+wasmfonts.files = \
+ ../../../3rdparty/wasm/Vera.ttf \
+ ../../../3rdparty/wasm/DejaVuSans.ttf
+wasmfonts.prefix = /fonts
+wasmfonts.base = ../../../3rdparty/wasm
+RESOURCES += wasmfonts
+
+qtConfig(opengl) {
+ SOURCES += qwasmbackingstore.cpp
+ HEADERS += qwasmbackingstore.h
+}
+CONFIG += egl
+
+OTHER_FILES += \
+ wasm.json \
+ wasm_shell.html \
+ qtloader.js
+
+shell_files.path = $$[QT_INSTALL_PLUGINS]/platforms
+shell_files.files = \
+ wasm_shell.html \
+ qtloader.js \
+ qtlogo.svg
+
+INSTALLS += shell_files
+
+PLUGIN_TYPE = platforms
+PLUGIN_CLASS_NAME = QWasmIntegrationPlugin
+!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = -
+load(qt_plugin)
diff --git a/src/plugins/platforms/wasm/wasm_shell.html b/src/plugins/platforms/wasm/wasm_shell.html
new file mode 100644
index 0000000000..67bfcdfbdc
--- /dev/null
+++ b/src/plugins/platforms/wasm/wasm_shell.html
@@ -0,0 +1,61 @@
+<!doctype html>
+<html lang="en-us">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title>APPNAME</title>
+ <style>
+ html, body { padding: 0; margin : 0; overflow:hidden; height: 100% }
+ /* the canvas *must not* have any border or padding, or mouse coords will be wrong */
+ canvas { border: 0px none; background-color: white; height:100%; width:100%; }
+ </style>
+ </head>
+ <body onload="init()">
+ <figure style="overflow:visible;" id="spinner">
+ <center style="margin-top:1.5em; line-height:150%">
+ <img src="qtlogo.svg"; width=320; height=200; style="display:block"> </img>
+ <strong>Qt for WebAssembly: APPNAME</strong>
+ <div id="status"></div>
+ <noscript>JavaScript is disabled. Please enable JavaScript to use this application.</noscript>
+ </center>
+ </figure>
+ <canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>
+
+ <script type='text/javascript'>
+ function init() {
+ var spinner = document.getElementById('spinner');
+ var canvas = document.getElementById('canvas');
+ var status = document.getElementById('status')
+
+ var qtLoader = QtLoader({
+ showLoader: function(loaderStatus) {
+ spinner.style.display = 'block';
+ canvas.style.display = 'none';
+ status.innerHTML = loaderStatus + "...";
+ },
+ showError: function(errorText) {
+ status.innerHTML = errorText;
+ spinner.style.display = 'block';
+ canvas.style.display = 'none';
+ },
+ showExit: function() {
+ status.innerHTML = "Application exit";
+ if (qtLoader.exitCode !== undefined)
+ status.innerHTML += " with code " + qtLoader.exitCode;
+ if (qtLoader.exitText !== undefined)
+ status.innerHTML += " (" + qtLoader.exitText + ")";
+ spinner.style.display = 'block';
+ canvas.style.display = 'none';
+ },
+ showCanvas: function() {
+ spinner.style.display = 'none';
+ canvas.style.display = 'block';
+ return canvas;
+ },
+ });
+ qtLoader.loadEmscriptenModule("APPNAME");
+ }
+ </script>
+ <script type="text/javascript" src="qtloader.js"></script>
+ </body>
+</html>
diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp
index 9116bf61fe..6b037726a0 100644
--- a/src/widgets/dialogs/qfiledialog.cpp
+++ b/src/widgets/dialogs/qfiledialog.cpp
@@ -1113,7 +1113,7 @@ Q_AUTOTEST_EXPORT QString qt_tildeExpansion(const QString &path)
const QString homePath = QDir::homePath();
#else
const QByteArray userName = path.midRef(1, separatorPosition - 1).toLocal8Bit();
-# if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
+# if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_WASM)
passwd pw;
passwd *tmpPw;
char buf[200];
diff --git a/src/widgets/graphicsview/qgraphicsitem_p.h b/src/widgets/graphicsview/qgraphicsitem_p.h
index 9fc6c0794a..d586a22544 100644
--- a/src/widgets/graphicsview/qgraphicsitem_p.h
+++ b/src/widgets/graphicsview/qgraphicsitem_p.h
@@ -500,7 +500,9 @@ public:
quint32 filtersDescendantEvents : 1;
quint32 sceneTransformTranslateOnly : 1;
quint32 notifyBoundingRectChanged : 1;
-
+#ifdef Q_OS_WASM
+ unsigned char :0; //this aligns 64bit field for wasm see QTBUG-65259
+#endif
// New 32 bits
quint32 notifyInvalidated : 1;
quint32 mouseSetsFocus : 1;
diff --git a/src/widgets/kernel/qopenglwidget.cpp b/src/widgets/kernel/qopenglwidget.cpp
index 51e78ec765..ed0fe0ed91 100644
--- a/src/widgets/kernel/qopenglwidget.cpp
+++ b/src/widgets/kernel/qopenglwidget.cpp
@@ -906,9 +906,19 @@ void QOpenGLWidgetPrivate::invalidateFbo()
const int gl_color_attachment0 = 0x8CE0; // GL_COLOR_ATTACHMENT0
const int gl_depth_attachment = 0x8D00; // GL_DEPTH_ATTACHMENT
const int gl_stencil_attachment = 0x8D20; // GL_STENCIL_ATTACHMENT
+#ifdef Q_OS_WASM
+ // webgl does not allow separate depth and stencil attachments
+ // QTBUG-69913
+ const int gl_depth_stencil_attachment = 0x821A; // GL_DEPTH_STENCIL_ATTACHMENT
+
+ const GLenum attachments[] = {
+ gl_color_attachment0, gl_depth_attachment, gl_stencil_attachment, gl_depth_stencil_attachment
+ };
+#else
const GLenum attachments[] = {
gl_color_attachment0, gl_depth_attachment, gl_stencil_attachment
};
+#endif
f->glDiscardFramebufferEXT(GL_FRAMEBUFFER, sizeof attachments / sizeof *attachments, attachments);
} else {
f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
diff --git a/util/unicode/main.cpp b/util/unicode/main.cpp
index 0f3c28137d..0c3c0b2ee1 100644
--- a/util/unicode/main.cpp
+++ b/util/unicode/main.cpp
@@ -2473,6 +2473,10 @@ static QByteArray createPropertyInfo()
out += ", ";
out += QByteArray::number( p.lowerCaseDiff );
out += ", ";
+ out += "#ifdef Q_OS_WASM \n"
+// " unsigned char : 0; //wasm 64 packing trick QTBUG-65259\n"
+ out += "#endif \n"
+ out += ", ";
// " ushort upperCaseSpecial : 1;\n"
// " signed short upperCaseDiff : 15;\n"
out += QByteArray::number( p.upperCaseSpecial );
@@ -2497,6 +2501,10 @@ static QByteArray createPropertyInfo()
// " ushort nfQuickCheck : 8;\n"
out += QByteArray::number( p.nfQuickCheck );
out += ", ";
+ out += "#ifdef Q_OS_WASM \n"
+// " unsigned char : 0; //wasm 64 packing trick QTBUG-65259\n"
+ out += "#endif \n"
+ out += ", ";
// " ushort graphemeBreakClass : 5; /* 5 used */\n"
// " ushort wordBreakClass : 5; /* 5 used */\n"
// " ushort sentenceBreakClass : 8; /* 4 used */\n"