From 51a614a419e9745d275793e36b6aab295bf1e8e3 Mon Sep 17 00:00:00 2001 From: Lorn Potter Date: Sat, 30 Jun 2018 08:48:52 +1000 Subject: wasm: rename platform, refactor files and other things Task-number: QTBUG-69168 Change-Id: I78a3ad5e16fbb1046eedf2056583c022f7a27d5b Reviewed-by: Eskil Abrahamsen Blomfeldt --- configure.pri | 2 +- mkspecs/emscripten/qmake.conf | 74 --- mkspecs/emscripten/qplatformdefs.h | 181 ----- mkspecs/features/html5/html5.prf | 97 --- mkspecs/features/html5/qt.prf | 83 --- mkspecs/features/wasm/qt.prf | 83 +++ mkspecs/features/wasm/wasm.prf | 97 +++ mkspecs/wasm-emscripten/qmake.conf | 75 +++ mkspecs/wasm-emscripten/qplatformdefs.h | 181 +++++ src/corelib/codecs/qtextcodec_p.h | 2 +- src/corelib/global/qsystemdetection.h | 2 +- src/corelib/io/qfilesystemengine_unix.cpp | 8 +- src/corelib/io/qsettings.cpp | 4 +- src/corelib/kernel/qcoreapplication.cpp | 6 +- src/corelib/kernel/qeventdispatcher_unix.cpp | 2 +- src/corelib/kernel/qeventloop.cpp | 6 +- src/corelib/mimetypes/qmimedatabase.cpp | 2 +- src/corelib/plugin/qlibrary.h | 2 +- src/corelib/tools/tools.pri | 2 +- src/gui/configure.json | 6 +- src/gui/configure.pri | 2 +- src/gui/kernel/qguiapplication.cpp | 4 +- src/gui/kernel/qopenglcontext.cpp | 2 +- src/gui/opengl/qopenglframebufferobject.cpp | 8 +- src/network/access/access.pri | 6 +- src/network/access/qnetworkaccessmanager.cpp | 12 +- src/network/access/qnetworkaccessmanager.h | 4 +- src/network/access/qnetworkreplyemscriptenimpl.cpp | 669 ------------------- src/network/access/qnetworkreplyemscriptenimpl_p.h | 154 ----- src/network/access/qnetworkreplywasmimpl.cpp | 669 +++++++++++++++++++ src/network/access/qnetworkreplywasmimpl_p.h | 154 +++++ src/opengl/qglframebufferobject.cpp | 2 +- .../eglconvenience/qeglplatformcontext.cpp | 2 +- src/plugins/imageformats/gif/gif.pro | 2 +- src/plugins/imageformats/ico/ico.pro | 2 +- src/plugins/imageformats/jpeg/jpeg.pro | 2 +- .../platforminputcontexts.pro | 2 +- src/plugins/platforms/html5/fonts/DejaVuSans.ttf | Bin 493564 -> 0 bytes src/plugins/platforms/html5/fonts/Vera.ttf | Bin 65932 -> 0 bytes src/plugins/platforms/html5/fonts/html5fonts.qrc | 6 - src/plugins/platforms/html5/html5.json | 3 - src/plugins/platforms/html5/html5.pro | 59 -- src/plugins/platforms/html5/html5_shell.html | 61 -- src/plugins/platforms/html5/main.cpp | 54 -- src/plugins/platforms/html5/qhtml5backingstore.cpp | 165 ----- src/plugins/platforms/html5/qhtml5backingstore.h | 74 --- src/plugins/platforms/html5/qhtml5compositor.cpp | 728 --------------------- src/plugins/platforms/html5/qhtml5compositor.h | 171 ----- src/plugins/platforms/html5/qhtml5cursor.cpp | 131 ---- src/plugins/platforms/html5/qhtml5cursor.h | 43 -- .../platforms/html5/qhtml5eventdispatcher.cpp | 182 ------ .../platforms/html5/qhtml5eventdispatcher.h | 64 -- .../platforms/html5/qhtml5eventtranslator.cpp | 524 --------------- .../platforms/html5/qhtml5eventtranslator.h | 211 ------ src/plugins/platforms/html5/qhtml5fontdatabase.cpp | 88 --- src/plugins/platforms/html5/qhtml5fontdatabase.h | 49 -- src/plugins/platforms/html5/qhtml5integration.cpp | 249 ------- src/plugins/platforms/html5/qhtml5integration.h | 92 --- .../platforms/html5/qhtml5openglcontext.cpp | 147 ----- src/plugins/platforms/html5/qhtml5openglcontext.h | 63 -- src/plugins/platforms/html5/qhtml5screen.cpp | 143 ---- src/plugins/platforms/html5/qhtml5screen.h | 85 --- src/plugins/platforms/html5/qhtml5stylepixmaps_p.h | 183 ------ src/plugins/platforms/html5/qhtml5theme.cpp | 52 -- src/plugins/platforms/html5/qhtml5theme.h | 56 -- src/plugins/platforms/html5/qhtml5window.cpp | 406 ------------ src/plugins/platforms/html5/qhtml5window.h | 125 ---- src/plugins/platforms/html5/qtloader.js | 516 --------------- src/plugins/platforms/html5/qtlogo.svg | 67 -- src/plugins/platforms/platforms.pro | 2 +- src/plugins/platforms/wasm/fonts/DejaVuSans.ttf | Bin 0 -> 493564 bytes src/plugins/platforms/wasm/fonts/Vera.ttf | Bin 0 -> 65932 bytes src/plugins/platforms/wasm/fonts/wasmfonts.qrc | 6 + src/plugins/platforms/wasm/main.cpp | 54 ++ src/plugins/platforms/wasm/qtloader.js | 516 +++++++++++++++ src/plugins/platforms/wasm/qtlogo.svg | 67 ++ src/plugins/platforms/wasm/qwasmbackingstore.cpp | 165 +++++ src/plugins/platforms/wasm/qwasmbackingstore.h | 74 +++ src/plugins/platforms/wasm/qwasmcompositor.cpp | 728 +++++++++++++++++++++ src/plugins/platforms/wasm/qwasmcompositor.h | 171 +++++ src/plugins/platforms/wasm/qwasmcursor.cpp | 131 ++++ src/plugins/platforms/wasm/qwasmcursor.h | 43 ++ .../platforms/wasm/qwasmeventdispatcher.cpp | 182 ++++++ src/plugins/platforms/wasm/qwasmeventdispatcher.h | 64 ++ .../platforms/wasm/qwasmeventtranslator.cpp | 524 +++++++++++++++ src/plugins/platforms/wasm/qwasmeventtranslator.h | 211 ++++++ src/plugins/platforms/wasm/qwasmfontdatabase.cpp | 88 +++ src/plugins/platforms/wasm/qwasmfontdatabase.h | 49 ++ src/plugins/platforms/wasm/qwasmintegration.cpp | 249 +++++++ src/plugins/platforms/wasm/qwasmintegration.h | 92 +++ src/plugins/platforms/wasm/qwasmopenglcontext.cpp | 147 +++++ src/plugins/platforms/wasm/qwasmopenglcontext.h | 63 ++ src/plugins/platforms/wasm/qwasmscreen.cpp | 143 ++++ src/plugins/platforms/wasm/qwasmscreen.h | 85 +++ src/plugins/platforms/wasm/qwasmstylepixmaps_p.h | 183 ++++++ src/plugins/platforms/wasm/qwasmtheme.cpp | 52 ++ src/plugins/platforms/wasm/qwasmtheme.h | 56 ++ src/plugins/platforms/wasm/qwasmwindow.cpp | 406 ++++++++++++ src/plugins/platforms/wasm/qwasmwindow.h | 125 ++++ src/plugins/platforms/wasm/wasm.json | 3 + src/plugins/platforms/wasm/wasm.pro | 59 ++ src/plugins/platforms/wasm/wasm_shell.html | 61 ++ src/src.pro | 4 +- src/widgets/dialogs/qfiledialog.cpp | 2 +- src/widgets/kernel/qopenglwidget.cpp | 2 +- 105 files changed, 6108 insertions(+), 6107 deletions(-) delete mode 100644 mkspecs/emscripten/qmake.conf delete mode 100644 mkspecs/emscripten/qplatformdefs.h delete mode 100644 mkspecs/features/html5/html5.prf delete mode 100644 mkspecs/features/html5/qt.prf create mode 100644 mkspecs/features/wasm/qt.prf create mode 100644 mkspecs/features/wasm/wasm.prf create mode 100644 mkspecs/wasm-emscripten/qmake.conf create mode 100644 mkspecs/wasm-emscripten/qplatformdefs.h delete mode 100644 src/network/access/qnetworkreplyemscriptenimpl.cpp delete mode 100644 src/network/access/qnetworkreplyemscriptenimpl_p.h create mode 100644 src/network/access/qnetworkreplywasmimpl.cpp create mode 100644 src/network/access/qnetworkreplywasmimpl_p.h delete mode 100644 src/plugins/platforms/html5/fonts/DejaVuSans.ttf delete mode 100644 src/plugins/platforms/html5/fonts/Vera.ttf delete mode 100644 src/plugins/platforms/html5/fonts/html5fonts.qrc delete mode 100644 src/plugins/platforms/html5/html5.json delete mode 100644 src/plugins/platforms/html5/html5.pro delete mode 100644 src/plugins/platforms/html5/html5_shell.html delete mode 100644 src/plugins/platforms/html5/main.cpp delete mode 100644 src/plugins/platforms/html5/qhtml5backingstore.cpp delete mode 100644 src/plugins/platforms/html5/qhtml5backingstore.h delete mode 100644 src/plugins/platforms/html5/qhtml5compositor.cpp delete mode 100644 src/plugins/platforms/html5/qhtml5compositor.h delete mode 100644 src/plugins/platforms/html5/qhtml5cursor.cpp delete mode 100644 src/plugins/platforms/html5/qhtml5cursor.h delete mode 100644 src/plugins/platforms/html5/qhtml5eventdispatcher.cpp delete mode 100644 src/plugins/platforms/html5/qhtml5eventdispatcher.h delete mode 100644 src/plugins/platforms/html5/qhtml5eventtranslator.cpp delete mode 100644 src/plugins/platforms/html5/qhtml5eventtranslator.h delete mode 100644 src/plugins/platforms/html5/qhtml5fontdatabase.cpp delete mode 100644 src/plugins/platforms/html5/qhtml5fontdatabase.h delete mode 100644 src/plugins/platforms/html5/qhtml5integration.cpp delete mode 100644 src/plugins/platforms/html5/qhtml5integration.h delete mode 100644 src/plugins/platforms/html5/qhtml5openglcontext.cpp delete mode 100644 src/plugins/platforms/html5/qhtml5openglcontext.h delete mode 100644 src/plugins/platforms/html5/qhtml5screen.cpp delete mode 100644 src/plugins/platforms/html5/qhtml5screen.h delete mode 100644 src/plugins/platforms/html5/qhtml5stylepixmaps_p.h delete mode 100644 src/plugins/platforms/html5/qhtml5theme.cpp delete mode 100644 src/plugins/platforms/html5/qhtml5theme.h delete mode 100644 src/plugins/platforms/html5/qhtml5window.cpp delete mode 100644 src/plugins/platforms/html5/qhtml5window.h delete mode 100644 src/plugins/platforms/html5/qtloader.js delete mode 100644 src/plugins/platforms/html5/qtlogo.svg create mode 100644 src/plugins/platforms/wasm/fonts/DejaVuSans.ttf create mode 100644 src/plugins/platforms/wasm/fonts/Vera.ttf create mode 100644 src/plugins/platforms/wasm/fonts/wasmfonts.qrc create mode 100644 src/plugins/platforms/wasm/main.cpp create mode 100644 src/plugins/platforms/wasm/qtloader.js create mode 100644 src/plugins/platforms/wasm/qtlogo.svg create mode 100644 src/plugins/platforms/wasm/qwasmbackingstore.cpp create mode 100644 src/plugins/platforms/wasm/qwasmbackingstore.h create mode 100644 src/plugins/platforms/wasm/qwasmcompositor.cpp create mode 100644 src/plugins/platforms/wasm/qwasmcompositor.h create mode 100644 src/plugins/platforms/wasm/qwasmcursor.cpp create mode 100644 src/plugins/platforms/wasm/qwasmcursor.h create mode 100644 src/plugins/platforms/wasm/qwasmeventdispatcher.cpp create mode 100644 src/plugins/platforms/wasm/qwasmeventdispatcher.h create mode 100644 src/plugins/platforms/wasm/qwasmeventtranslator.cpp create mode 100644 src/plugins/platforms/wasm/qwasmeventtranslator.h create mode 100644 src/plugins/platforms/wasm/qwasmfontdatabase.cpp create mode 100644 src/plugins/platforms/wasm/qwasmfontdatabase.h create mode 100644 src/plugins/platforms/wasm/qwasmintegration.cpp create mode 100644 src/plugins/platforms/wasm/qwasmintegration.h create mode 100644 src/plugins/platforms/wasm/qwasmopenglcontext.cpp create mode 100644 src/plugins/platforms/wasm/qwasmopenglcontext.h create mode 100644 src/plugins/platforms/wasm/qwasmscreen.cpp create mode 100644 src/plugins/platforms/wasm/qwasmscreen.h create mode 100644 src/plugins/platforms/wasm/qwasmstylepixmaps_p.h create mode 100644 src/plugins/platforms/wasm/qwasmtheme.cpp create mode 100644 src/plugins/platforms/wasm/qwasmtheme.h create mode 100644 src/plugins/platforms/wasm/qwasmwindow.cpp create mode 100644 src/plugins/platforms/wasm/qwasmwindow.h create mode 100644 src/plugins/platforms/wasm/wasm.json create mode 100644 src/plugins/platforms/wasm/wasm.pro create mode 100644 src/plugins/platforms/wasm/wasm_shell.html diff --git a/configure.pri b/configure.pri index 0dcc335886..c00d368e6d 100644 --- a/configure.pri +++ b/configure.pri @@ -280,7 +280,7 @@ 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: html5:exists($$test_out_dir/arch.wasm): \ + 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/emscripten/qmake.conf b/mkspecs/emscripten/qmake.conf deleted file mode 100644 index ceb002895a..0000000000 --- a/mkspecs/emscripten/qmake.conf +++ /dev/null @@ -1,74 +0,0 @@ -# qmake configuration for building with emscripten -MAKEFILE_GENERATOR = UNIX -QMAKE_PLATFORM = html5 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_LFLAGS = --bind -s USE_ZLIB=1 -s USE_LIBPNG=1 -s USE_FREETYPE=1 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -EMCC_COMMON_LFLAGS += -s FULL_ES2=1 -s ALLOW_MEMORY_GROWTH=1 -s USE_WEBGL2=1 -s NO_EXIT_RUNTIME=0 - -# -s DEMANGLE_SUPPORT=1 -# -s USE_WEBGL2=1 -# -s DISABLE_EXCEPTION_CATCHING=1 -# -s TOTAL_STACK=15*1024*1024 - -EMCC_COMMON_LFLAGS_DEBUG = $$EMCC_COMMON_LFLAGS -s ASSERTIONS=2 -s DEMANGLE_SUPPORT=1 -s GL_DEBUG=1 - -# -s LIBRARY_DEBUG=1 -# -s SYSCALL_DEBUG=1 -# -s GL_DEBUG=1 -# -s FS_LOG=1 - -QMAKE_COMPILER += emscripten - -QMAKE_CC = emcc -QMAKE_CXX = em++ -s USE_LIBPNG=1 -s USE_FREETYPE=1 - -# QMAKE_CFLAGS_THREAD = -s USE_PTHREADS=1 -# QMAKE_CXXFLAGS_THREAD = $$QMAKE_CFLAGS_THREAD - -QMAKE_CFLAGS += $$EMCC_COMMON_LFLAGS -QMAKE_CXXFLAGS += $$EMCC_COMMON_LFLAGS - -# 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_CLEAN += *.html *.js *.wasm - -DEFINES += QT_NO_QFUTURE - -QTPLUGIN.platforms = html5 -load(qt_config) diff --git a/mkspecs/emscripten/qplatformdefs.h b/mkspecs/emscripten/qplatformdefs.h deleted file mode 100644 index c1a0d7b1a8..0000000000 --- a/mkspecs/emscripten/qplatformdefs.h +++ /dev/null @@ -1,181 +0,0 @@ -/**************************************************************************** -** -** 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 - -// We are hot - unistd.h should have turned on the specific APIs we requested - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#ifndef QT_NO_IPV6IFNAME -#include -#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/mkspecs/features/html5/html5.prf b/mkspecs/features/html5/html5.prf deleted file mode 100644 index 3e2b8399bb..0000000000 --- a/mkspecs/features/html5/html5.prf +++ /dev/null @@ -1,97 +0,0 @@ - -# DESTDIR will be emtpy if not set in the app .pro file; make sure it has a value -isEmpty(DESTDIR) : DESTDIR = $$OUT_PWD - -# Copies the given files to the destination directory -defineTest(copyToDestDir) { - files = $$1 - - for(FILE, files) { - # Replace slashes in paths with backslashes for Windows - win32:FILE ~= s,/,\\,g - win32:DDIR ~= s,/,\\,g - - QMAKE_POST_LINK += $$QMAKE_COPY $$quote($$FILE) $$quote($$DESTDIR) $$escape_expand(\\n\\t) - } - export(QMAKE_POST_LINK) -} - -# 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 - - !contains(CONFIG,config_test):!build_pass:cross_compile: { - QMAKE_CFLAGS += -s WASM=1 -s \"BINARYEN_METHOD=\'native-wasm\'\" -s \"BINARYEN_TRAP_MODE=\'clamp\'\" - QMAKE_CXXFLAGS += -s WASM=1 -s \"BINARYEN_METHOD=\'native-wasm\'\" -s \"BINARYEN_TRAP_MODE=\'clamp\'\" - QMAKE_LFLAGS += -s WASM=1 -s \"BINARYEN_METHOD=\'native-wasm\'\" -s \"BINARYEN_TRAP_MODE=\'clamp\'\" - } - - # Copy hosting html and javascript to the application build directory. - exists($$[QT_INSTALL_PLUGINS]/platforms/html5_shell.html) { - # dont pass this until it's installed somewhere - # otherwise makespec test fails during qt configure - HTML5_PLUGIN_PATH = $$[QT_INSTALL_PLUGINS]/platforms - } else { - ## internal build. not installed - HTML5_PLUGIN_PATH = $$PWD/../../../src/plugins/platforms/html5 - } - - # Copy/Generate main .html file (e.g. myapp.html) from the html5_shell.html by - # replacing the app name placeholder with the actual app name. - apphtml.target = $$DESTDIR/$$TARGET_HTML - apphtml.commands += sed -e s/APPNAME/$$TARGET_BASE/g $$quote($$HTML5_PLUGIN_PATH/html5_shell.html) > $$quote($$DESTDIR/$$TARGET_HTML) $$escape_expand(\\n\\t) - - # Copy qtloader.js - appjs.target = $$DESTDIR/qtloader.js - appjs.commands += $$QMAKE_COPY $$quote($$HTML5_PLUGIN_PATH/qtloader.js) $$quote($$DESTDIR) $$escape_expand(\\n\\t) - - # Copy qtlogo.svg - qtlogo.target = $$DESTDIR/qtlogo.svg - qtlogo.commands += $$QMAKE_COPY $$quote($$HTML5_PLUGIN_PATH/qtlogo.svg) $$quote($$DESTDIR) $$escape_expand(\\n\\t) - - QMAKE_EXTRA_TARGETS += apphtml appjs qtlogo - POST_TARGETDEPS += $$apphtml.target $$appjs.target $$qtlogo.target - - # Add manual target to make "make -B shellfiles" work. - shellfiles.target = shellfiles - shellfiles.depends = $$apphtml.target $$appjs.target $$qtlogo.target - 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 - - !build_pass:cross_compile: { - QMAKE_CFLAGS += -s WASM=1 -s \"BINARYEN_METHOD=\'native-wasm\'\" - QMAKE_LFLAGS += -s WASM=1 -s \"BINARYEN_METHOD=\'native-wasm\'\" - QMAKE_CXXFLAGS += -s WASM=1 -s \"BINARYEN_METHOD=\'native-wasm\'\" - } -} diff --git a/mkspecs/features/html5/qt.prf b/mkspecs/features/html5/qt.prf deleted file mode 100644 index 47d616ec90..0000000000 --- a/mkspecs/features/html5/qt.prf +++ /dev/null @@ -1,83 +0,0 @@ - -qt_depends = $$resolve_depends(QT, "QT.") -!watchos:equals(TEMPLATE, app):contains(qt_depends, gui(-private)?) { - LIBS *= -L$$[QT_INSTALL_PLUGINS/get]/platforms - - lib_name = html5 - 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) - -## taken from qt.prf -import_plugins { - manualplugs = $$QTPLUGIN # User may specify plugins. Mostly legacy. - autoplugs = # Auto-added plugins. - # First round: explicitly specified modules. - plugin_deps = $$all_qt_module_deps - for(ever) { - # Automatically link the default plugins for the linked Qt modules. - for (qtmod, plugin_deps) { - for (ptype, QT.$${qtmod}.plugin_types) { - nptype = $$replace(ptype, [-/], _) - isEmpty(QTPLUGIN.$$nptype) { - for (plug, QT_PLUGINS) { - equals(QT_PLUGIN.$${plug}.TYPE, $$ptype) { - for (dep, QT_PLUGIN.$${plug}.EXTENDS) { - !contains(all_qt_module_deps, $$dep) { - plug = - break() - } - } - autoplugs += $$plug - } - } - } else { - plug = $$eval(QTPLUGIN.$$nptype) - !equals(plug, -): \ - autoplugs += $$plug - } - } - } - QTPLUGIN = $$manualplugs $$autoplugs - QTPLUGIN = $$unique(QTPLUGIN) - - # Obtain the plugins' Qt dependencies ... - plugin_deps = - for (plug, QTPLUGIN): \ - plugin_deps += $$eval(QT_PLUGIN.$${plug}.DEPENDS) - plugin_deps = $$resolve_depends(plugin_deps, "QT.", ".depends" ".run_depends") - plugin_deps -= $$all_qt_module_deps - isEmpty(plugin_deps): \ - break() - # ... and start over if any new Qt modules appeared, - # as these may want to load plugins in turn. - all_qt_module_deps += $$plugin_deps - } - extraplugs = $$manualplugs - manualplugs -= $$autoplugs - extraplugs -= $$manualplugs - !isEmpty(extraplugs): \ - warning("Redundant entries in QTPLUGIN: $$extraplugs") - - !isEmpty(QTPLUGIN) { - IMPORT_FILE_CONT = \ - "// This file is autogenerated by qmake. It imports static plugin classes for" \ - "// static plugins specified using QTPLUGIN and QT_PLUGIN_CLASS. variables." \ - "$${LITERAL_HASH}include " - for (plug, QTPLUGIN) { - plug_class = $$eval(QT_PLUGIN.$${plug}.CLASS_NAME) - !isEmpty(plug_class): \ - IMPORT_FILE_CONT += "Q_IMPORT_PLUGIN($$plug_class)" - else: \ - warning("Plugin class name could not be determined for plugin '$$plug'.") - } - IMPORT_CPP = $$OUT_PWD/$$lower($$basename(TARGET))_plugin_import.cpp - write_file($$IMPORT_CPP, IMPORT_FILE_CONT)|error() - GENERATED_SOURCES += $$IMPORT_CPP - QMAKE_DISTCLEAN += $$IMPORT_CPP - } -} diff --git a/mkspecs/features/wasm/qt.prf b/mkspecs/features/wasm/qt.prf new file mode 100644 index 0000000000..2f6e3f8c34 --- /dev/null +++ b/mkspecs/features/wasm/qt.prf @@ -0,0 +1,83 @@ + +qt_depends = $$resolve_depends(QT, "QT.") +!watchos: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) + +## taken from qt.prf +import_plugins { + manualplugs = $$QTPLUGIN # User may specify plugins. Mostly legacy. + autoplugs = # Auto-added plugins. + # First round: explicitly specified modules. + plugin_deps = $$all_qt_module_deps + for(ever) { + # Automatically link the default plugins for the linked Qt modules. + for (qtmod, plugin_deps) { + for (ptype, QT.$${qtmod}.plugin_types) { + nptype = $$replace(ptype, [-/], _) + isEmpty(QTPLUGIN.$$nptype) { + for (plug, QT_PLUGINS) { + equals(QT_PLUGIN.$${plug}.TYPE, $$ptype) { + for (dep, QT_PLUGIN.$${plug}.EXTENDS) { + !contains(all_qt_module_deps, $$dep) { + plug = + break() + } + } + autoplugs += $$plug + } + } + } else { + plug = $$eval(QTPLUGIN.$$nptype) + !equals(plug, -): \ + autoplugs += $$plug + } + } + } + QTPLUGIN = $$manualplugs $$autoplugs + QTPLUGIN = $$unique(QTPLUGIN) + + # Obtain the plugins' Qt dependencies ... + plugin_deps = + for (plug, QTPLUGIN): \ + plugin_deps += $$eval(QT_PLUGIN.$${plug}.DEPENDS) + plugin_deps = $$resolve_depends(plugin_deps, "QT.", ".depends" ".run_depends") + plugin_deps -= $$all_qt_module_deps + isEmpty(plugin_deps): \ + break() + # ... and start over if any new Qt modules appeared, + # as these may want to load plugins in turn. + all_qt_module_deps += $$plugin_deps + } + extraplugs = $$manualplugs + manualplugs -= $$autoplugs + extraplugs -= $$manualplugs + !isEmpty(extraplugs): \ + warning("Redundant entries in QTPLUGIN: $$extraplugs") + + !isEmpty(QTPLUGIN) { + IMPORT_FILE_CONT = \ + "// This file is autogenerated by qmake. It imports static plugin classes for" \ + "// static plugins specified using QTPLUGIN and QT_PLUGIN_CLASS. variables." \ + "$${LITERAL_HASH}include " + for (plug, QTPLUGIN) { + plug_class = $$eval(QT_PLUGIN.$${plug}.CLASS_NAME) + !isEmpty(plug_class): \ + IMPORT_FILE_CONT += "Q_IMPORT_PLUGIN($$plug_class)" + else: \ + warning("Plugin class name could not be determined for plugin '$$plug'.") + } + IMPORT_CPP = $$OUT_PWD/$$lower($$basename(TARGET))_plugin_import.cpp + write_file($$IMPORT_CPP, IMPORT_FILE_CONT)|error() + GENERATED_SOURCES += $$IMPORT_CPP + QMAKE_DISTCLEAN += $$IMPORT_CPP + } +} diff --git a/mkspecs/features/wasm/wasm.prf b/mkspecs/features/wasm/wasm.prf new file mode 100644 index 0000000000..bea50f98f0 --- /dev/null +++ b/mkspecs/features/wasm/wasm.prf @@ -0,0 +1,97 @@ + +# DESTDIR will be emtpy if not set in the app .pro file; make sure it has a value +isEmpty(DESTDIR) : DESTDIR = $$OUT_PWD + +# Copies the given files to the destination directory +defineTest(copyToDestDir) { + files = $$1 + + for(FILE, files) { + # Replace slashes in paths with backslashes for Windows + win32:FILE ~= s,/,\\,g + win32:DDIR ~= s,/,\\,g + + QMAKE_POST_LINK += $$QMAKE_COPY $$quote($$FILE) $$quote($$DESTDIR) $$escape_expand(\\n\\t) + } + export(QMAKE_POST_LINK) +} + +# 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 + + !contains(CONFIG,config_test):!build_pass:cross_compile: { + QMAKE_CFLAGS += -s WASM=1 -s \"BINARYEN_METHOD=\'native-wasm\'\" -s \"BINARYEN_TRAP_MODE=\'clamp\'\" + QMAKE_CXXFLAGS += -s WASM=1 -s \"BINARYEN_METHOD=\'native-wasm\'\" -s \"BINARYEN_TRAP_MODE=\'clamp\'\" + QMAKE_LFLAGS += -s WASM=1 -s \"BINARYEN_METHOD=\'native-wasm\'\" -s \"BINARYEN_TRAP_MODE=\'clamp\'\" + } + + # Copy hosting html and javascript to the application build directory. + exists($$[QT_INSTALL_PLUGINS]/platforms/wasm_shell.html) { + # dont 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.target = $$DESTDIR/$$TARGET_HTML + apphtml.commands += sed -e s/APPNAME/$$TARGET_BASE/g $$quote($$WASM_PLUGIN_PATH/wasm_shell.html) > $$quote($$DESTDIR/$$TARGET_HTML) $$escape_expand(\\n\\t) + + # Copy qtloader.js + appjs.target = $$DESTDIR/qtloader.js + appjs.commands += $$QMAKE_COPY $$quote($$WASM_PLUGIN_PATH/qtloader.js) $$quote($$DESTDIR) $$escape_expand(\\n\\t) + + # Copy qtlogo.svg + qtlogo.target = $$DESTDIR/qtlogo.svg + qtlogo.commands += $$QMAKE_COPY $$quote($$WASM_PLUGIN_PATH/qtlogo.svg) $$quote($$DESTDIR) $$escape_expand(\\n\\t) + + QMAKE_EXTRA_TARGETS += apphtml appjs qtlogo + POST_TARGETDEPS += $$apphtml.target $$appjs.target $$qtlogo.target + + # Add manual target to make "make -B shellfiles" work. + shellfiles.target = shellfiles + shellfiles.depends = $$apphtml.target $$appjs.target $$qtlogo.target + 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 + + !build_pass:cross_compile: { + QMAKE_CFLAGS += -s WASM=1 -s \"BINARYEN_METHOD=\'native-wasm\'\" + QMAKE_LFLAGS += -s WASM=1 -s \"BINARYEN_METHOD=\'native-wasm\'\" + QMAKE_CXXFLAGS += -s WASM=1 -s \"BINARYEN_METHOD=\'native-wasm\'\" + } +} diff --git a/mkspecs/wasm-emscripten/qmake.conf b/mkspecs/wasm-emscripten/qmake.conf new file mode 100644 index 0000000000..bf46a063a1 --- /dev/null +++ b/mkspecs/wasm-emscripten/qmake.conf @@ -0,0 +1,75 @@ +# 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_LFLAGS = --bind -s USE_ZLIB=1 -s USE_LIBPNG=1 -s USE_FREETYPE=1 -s ERROR_ON_UNDEFINED_SYMBOLS=1 +EMCC_COMMON_LFLAGS += -s FULL_ES2=1 -s ALLOW_MEMORY_GROWTH=1 -s USE_WEBGL2=1 -s NO_EXIT_RUNTIME=0 + +# -s DEMANGLE_SUPPORT=1 +# -s USE_WEBGL2=1 +# -s DISABLE_EXCEPTION_CATCHING=1 +# -s TOTAL_STACK=15*1024*1024 + +EMCC_COMMON_LFLAGS_DEBUG = $$EMCC_COMMON_LFLAGS -s ASSERTIONS=2 -s DEMANGLE_SUPPORT=1 -s GL_DEBUG=1 + +# -s LIBRARY_DEBUG=1 +# -s SYSCALL_DEBUG=1 +# -s GL_DEBUG=1 +# -s FS_LOG=1 + +QMAKE_COMPILER += emscripten + +QMAKE_CC = emcc +QMAKE_CXX = em++ + +# QMAKE_CFLAGS_THREAD = -s USE_PTHREADS=1 +# QMAKE_CXXFLAGS_THREAD = $$QMAKE_CFLAGS_THREAD + +QMAKE_CFLAGS += $$EMCC_COMMON_LFLAGS +QMAKE_CXXFLAGS += $$EMCC_COMMON_LFLAGS + +# 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_CLEAN += *.html *.js *.wasm + +DEFINES += QT_NO_QFUTURE +QT_QPA_DEFAULT_PLATFORM = webassembly + +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 + +// We are hot - unistd.h should have turned on the specific APIs we requested + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef QT_NO_IPV6IFNAME +#include +#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/corelib/codecs/qtextcodec_p.h b/src/corelib/codecs/qtextcodec_p.h index 7635fce2af..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) || defined(Q_OS_HTML5) +#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/global/qsystemdetection.h b/src/corelib/global/qsystemdetection.h index 96dd9aa9bf..2600f7b661 100644 --- a/src/corelib/global/qsystemdetection.h +++ b/src/corelib/global/qsystemdetection.h @@ -138,7 +138,7 @@ #elif defined(__native_client__) # define Q_OS_NACL #elif defined(__EMSCRIPTEN__) -# define Q_OS_HTML5 +# 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 5561ecbb6d..71a7fc7d42 100644 --- a/src/corelib/io/qfilesystemengine_unix.cpp +++ b/src/corelib/io/qfilesystemengine_unix.cpp @@ -859,10 +859,10 @@ QString QFileSystemEngine::resolveUserName(uint userId) size_max = 1024; QVarLengthArray buf(size_max); #endif -#ifdef Q_OS_HTML5 +#ifdef Q_OS_WASM Q_UNUSED(userId) #endif -#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_HTML5) +#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM) struct passwd *pw = 0; #if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_VXWORKS) struct passwd entry; @@ -885,10 +885,10 @@ QString QFileSystemEngine::resolveGroupName(uint groupId) size_max = 1024; QVarLengthArray buf(size_max); #endif -#ifdef Q_OS_HTML5 +#ifdef Q_OS_WASM Q_UNUSED(groupId) #endif -#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_HTML5) +#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM) struct group *gr = 0; #if !defined(QT_NO_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 e3d78e7fd4..6d83b34a8b 100644 --- a/src/corelib/io/qsettings.cpp +++ b/src/corelib/io/qsettings.cpp @@ -77,7 +77,7 @@ # include #endif -#ifdef Q_OS_HTML5 +#ifdef Q_OS_WASM #include #endif @@ -1548,7 +1548,7 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile) perms |= QFile::ReadGroup | QFile::ReadOther; QFile(confFile->name).setPermissions(perms); } -#ifdef Q_OS_HTML5 +#ifdef Q_OS_WASM EM_ASM( Module.print("Start QSettings Emscripten current data to persistent Indexed Db"); FS.syncfs(false, function(err) { diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 7ddc6186a1..1ff9275935 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -116,7 +116,7 @@ # include #endif -#ifdef Q_OS_HTML5 +#ifdef Q_OS_WASM #include #endif @@ -503,7 +503,7 @@ QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv, uint QCoreApplicationPrivate::~QCoreApplicationPrivate() { -#ifdef Q_OS_HTML5 +#ifdef Q_OS_WASM EM_ASM( //unmount persistent directory as IDBFS FS.unmount('/home/web_user'); @@ -801,7 +801,7 @@ void QCoreApplicationPrivate::init() Q_ASSERT_X(!QCoreApplication::self, "QCoreApplication", "there should be only one application object"); QCoreApplication::self = q; -#ifdef Q_OS_HTML5 +#ifdef Q_OS_WASM EM_ASM( Module.print("mount persistent directory as IDBFS"); FS.mount(IDBFS, {}, '/home/web_user'); diff --git a/src/corelib/kernel/qeventdispatcher_unix.cpp b/src/corelib/kernel/qeventdispatcher_unix.cpp index 85846c3f6f..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) || defined(Q_OS_HTML5) +#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 e9f26e5070..502b075f98 100644 --- a/src/corelib/kernel/qeventloop.cpp +++ b/src/corelib/kernel/qeventloop.cpp @@ -49,7 +49,7 @@ #include #include -#ifdef Q_OS_HTML5 +#ifdef Q_OS_WASM #include #endif @@ -215,7 +215,7 @@ int QEventLoop::exec(ProcessEventsFlags flags) if (app && app->thread() == thread()) QCoreApplication::removePostedEvents(app, QEvent::Quit); -#ifdef Q_OS_HTML5 +#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 newer returns. @@ -285,7 +285,7 @@ void QEventLoop::exit(int returnCode) d->exit.storeRelease(true); d->threadData->eventDispatcher.load()->interrupt(); -#ifdef Q_OS_HTML5 +#ifdef Q_OS_WASM // QEventLoop::exec() never returns. We implement opproximate behavior here. if (d->threadData->loopLevel == 1) { emscripten_force_exit(returnCode); diff --git a/src/corelib/mimetypes/qmimedatabase.cpp b/src/corelib/mimetypes/qmimedatabase.cpp index d5f8b4a71b..f0a9486a7c 100644 --- a/src/corelib/mimetypes/qmimedatabase.cpp +++ b/src/corelib/mimetypes/qmimedatabase.cpp @@ -151,7 +151,7 @@ void QMimeDatabasePrivate::loadProviders() QVector QMimeDatabasePrivate::providers() { -#ifndef Q_OS_HTML5 //we dont have working mutex locks +#ifndef Q_OS_WASM //we dont have working mutex locks Q_ASSERT(!mutex.tryLock()); // caller should have locked mutex #endif if (m_providers.isEmpty()) { diff --git a/src/corelib/plugin/qlibrary.h b/src/corelib/plugin/qlibrary.h index fe60d36277..4337f338d8 100644 --- a/src/corelib/plugin/qlibrary.h +++ b/src/corelib/plugin/qlibrary.h @@ -42,7 +42,7 @@ #include -#ifndef Q_OS_HTML5 +#ifndef Q_OS_WASM QT_REQUIRE_CONFIG(library); #endif diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index 4cd38d67f9..4d6c969592 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -216,7 +216,7 @@ qtConfig(system-doubleconversion) { } # Note: libm should be present by default becaue this is C++ -unix:!macx-icc:!vxworks:!haiku:!integrity:!html5: 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 413305be4b..51e0389f14 100644 --- a/src/gui/configure.json +++ b/src/gui/configure.json @@ -1081,7 +1081,7 @@ "opengles3": { "label": "OpenGL ES 3.0", "condition": "features.opengles2 && !features.angle && tests.opengles3", - "disable": "config.html5", + "disable": "config.webassembly", "output": [ "publicFeature", { "type": "define", "name": "QT_OPENGL_ES_3" } @@ -1090,7 +1090,7 @@ "opengles31": { "label": "OpenGL ES 3.1", "condition": "features.opengles3 && tests.opengles31", - "disable": "config.html5", + "disable": "config.webassembly", "output": [ "publicFeature", { "type": "define", "name": "QT_OPENGL_ES_3_1" } @@ -1109,7 +1109,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 && !config.html5 && libs.opengl)" + || (!config.watchos && !config.win32 && !config.webassembly && libs.opengl)" }, "opengl-dynamic": { "label": "Dynamic OpenGL", diff --git a/src/gui/configure.pri b/src/gui/configure.pri index 6eb9d0fe07..be4378e4fd 100644 --- a/src/gui/configure.pri +++ b/src/gui/configure.pri @@ -55,7 +55,7 @@ defineTest(qtConfTest_qpaDefaultPlatform) { else: qnx: name = qnx else: integrity: name = integrityfb else: haiku: name = haiku - else: html5: name = html5 + 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 cc66827b41..09805369f0 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -114,7 +114,7 @@ # include #endif // Q_OS_WIN -#ifdef Q_OS_HTML5 +#ifdef Q_OS_WASM #include #endif @@ -1609,7 +1609,7 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate() qt_gl_set_global_share_context(0); } #endif -#ifdef Q_OS_HTML5 +#ifdef Q_OS_WASM EM_ASM( //unmount persistent directory as IDBFS FS.unmount('/home/web_user'); diff --git a/src/gui/kernel/qopenglcontext.cpp b/src/gui/kernel/qopenglcontext.cpp index deaace2db2..16224b6cd6 100644 --- a/src/gui/kernel/qopenglcontext.cpp +++ b/src/gui/kernel/qopenglcontext.cpp @@ -977,7 +977,7 @@ bool QOpenGLContext::makeCurrent(QSurface *surface) if (!surface->surfaceHandle()) return false; if (!surface->supportsOpenGL()) { -#ifndef Q_OS_HTML5 // ### work around the HTML5 platform plugin using QOpenGLContext with raster surfaces. +#ifndef Q_OS_WASM // ### work around the WASM platform plugin using QOpenGLContext with raster surfaces. qWarning() << "QOpenGLContext::makeCurrent() called with non-opengl surface" << surface; return false; #endif diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp index 20f1d06895..0831ca32a0 100644 --- a/src/gui/opengl/qopenglframebufferobject.cpp +++ b/src/gui/opengl/qopenglframebufferobject.cpp @@ -602,7 +602,7 @@ void QOpenGLFramebufferObjectPrivate::initDepthStencilAttachments(QOpenGLContext const int samples = requestedSamples; // free existing attachments -#ifdef Q_OS_HTML5 +#ifdef Q_OS_WASM if (depth_buffer_guard) { funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); depth_buffer_guard->free(); @@ -628,7 +628,7 @@ 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_HTML5 +#ifdef Q_OS_WASM // WebGL doesn't allow separately attach buffers to // STENCIL_ATTACHMENT and DEPTH_ATTACHMENT if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil) { @@ -747,12 +747,12 @@ void QOpenGLFramebufferObjectPrivate::initDepthStencilAttachments(QOpenGLContext stencil_buffer = 0; } } -#endif //Q_OS_HTML5 +#endif //Q_OS_WASM // The FBO might have become valid after removing the depth or stencil buffer. valid = checkFramebufferStatus(ctx); -#ifdef Q_OS_HTML5 +#ifdef Q_OS_WASM if (depth_buffer) { #else if (depth_buffer && stencil_buffer) { diff --git a/src/network/access/access.pri b/src/network/access/access.pri index caa81b9497..410bf18ad0 100644 --- a/src/network/access/access.pri +++ b/src/network/access/access.pri @@ -68,11 +68,11 @@ qtConfig(networkdiskcache) { mac: LIBS_PRIVATE += -framework Security -emscripten: { +wasm: { SOURCES += \ - access/qnetworkreplyemscriptenimpl.cpp + access/qnetworkreplywasmimpl.cpp HEADERS += \ - access/qnetworkreplyemscriptenimpl_p.h + access/qnetworkreplywasmimpl_p.h } include($$PWD/../../3rdparty/zlib_dependency.pri) diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 8b174d813f..08a17f8f77 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -82,9 +82,9 @@ #include #include #endif -#ifdef Q_OS_HTML5 +#ifdef Q_OS_WASM #include -#include "qnetworkreplyemscriptenimpl_p.h" +#include "qnetworkreplywasmimpl_p.h" #endif QT_BEGIN_NAMESPACE @@ -1347,12 +1347,12 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera bool isLocalFile = req.url().isLocalFile(); QString scheme = req.url().scheme(); -#ifdef Q_OS_HTML5 +#ifdef Q_OS_WASM if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) { - // return new QNetworkReplyEmscriptenImpl(this, req, op); + // return new QNetworkReplyWasmImpl(this, req, op); - QNetworkReplyEmscriptenImpl *reply = new QNetworkReplyEmscriptenImpl(this); - QNetworkReplyEmscriptenImplPrivate *priv = reply->d_func(); + QNetworkReplyWasmImpl *reply = new QNetworkReplyWasmImpl(this); + QNetworkReplyWasmImplPrivate *priv = reply->d_func(); priv->manager = this; priv->setup(op, req, outgoingData); return reply; diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h index 6082e73977..0dc2be3543 100644 --- a/src/network/access/qnetworkaccessmanager.h +++ b/src/network/access/qnetworkaccessmanager.h @@ -194,8 +194,8 @@ private: friend class QNetworkReplyHttpImpl; friend class QNetworkReplyHttpImplPrivate; friend class QNetworkReplyFileImpl; -#ifdef Q_OS_HTML5 - friend class QNetworkReplyEmscriptenImpl; +#ifdef Q_OS_WASM + friend class QNetworkReplyWasmImpl; #endif Q_DECLARE_PRIVATE(QNetworkAccessManager) Q_PRIVATE_SLOT(d_func(), void _q_replyFinished()) diff --git a/src/network/access/qnetworkreplyemscriptenimpl.cpp b/src/network/access/qnetworkreplyemscriptenimpl.cpp deleted file mode 100644 index e81c7a52a4..0000000000 --- a/src/network/access/qnetworkreplyemscriptenimpl.cpp +++ /dev/null @@ -1,669 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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 "qnetworkreplyemscriptenimpl_p.h" - -#include -#include "QtCore/qdatetime.h" -#include "qnetworkaccessmanager_p.h" -#include -#include -#include -#include "qnetworkfile_p.h" -#include "qnetworkrequest.h" -//#include "qnetworkaccessbackend_p.h" - -#include - -QT_BEGIN_NAMESPACE - -QNetworkReplyEmscriptenImplPrivate::QNetworkReplyEmscriptenImplPrivate() - : QNetworkReplyPrivate(), - managerPrivate(0), - downloadBufferReadPosition(0), - downloadBufferCurrentSize(0), - totalDownloadSize(0), - percentFinished(0) -{ -} - -QNetworkReplyEmscriptenImplPrivate::~QNetworkReplyEmscriptenImplPrivate() -{ -} - -QNetworkReplyEmscriptenImpl::~QNetworkReplyEmscriptenImpl() -{ -} - -QNetworkReplyEmscriptenImpl::QNetworkReplyEmscriptenImpl(QObject *parent) - : QNetworkReply(*new QNetworkReplyEmscriptenImplPrivate(), parent) -{ -} - -QByteArray QNetworkReplyEmscriptenImpl::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"; -// case QNetworkAccessManager::CustomOperation: -// return d->customVerb; - default: - break; - } - return QByteArray(); -} - -void QNetworkReplyEmscriptenImpl::close() -{ - QNetworkReply::close(); -} - -void QNetworkReplyEmscriptenImpl::abort() -{ - close(); -} - -qint64 QNetworkReplyEmscriptenImpl::bytesAvailable() const -{ - Q_D(const QNetworkReplyEmscriptenImpl); - - if (!d->isFinished) - return QNetworkReply::bytesAvailable(); - - return QNetworkReply::bytesAvailable() + d->downloadBufferCurrentSize - d->downloadBufferReadPosition; -} - -bool QNetworkReplyEmscriptenImpl::isSequential() const -{ - return true; -} - -qint64 QNetworkReplyEmscriptenImpl::size() const -{ - return QNetworkReply::size(); -} - -/*! - \internal -*/ -qint64 QNetworkReplyEmscriptenImpl::readData(char *data, qint64 maxlen) -{ - Q_D(QNetworkReplyEmscriptenImpl); - - qint64 howMuch = qMin(maxlen, (d->downloadBuffer.size()- d->downloadBufferReadPosition)); - memcpy(data, d->downloadBuffer.constData(), howMuch); - d->downloadBufferReadPosition += howMuch; - - return howMuch; -} - -void QNetworkReplyEmscriptenImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req, - QIODevice *data) -{ - Q_Q(QNetworkReplyEmscriptenImpl); - - outgoingData = data; - request = req; - url = request.url(); - operation = op; - - q->QIODevice::open(QIODevice::ReadOnly); - if (outgoingData) { - if (!outgoingData->isSequential()) { - // fixed size non-sequential (random-access) - // just start the operation - doSendRequest(); - return; - } else { - 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()) { - doSendRequest(); - return; - } else { - 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 QNetworkReplyEmscriptenImplPrivate::onLoadCallback(void *data, int statusCode, int statusReason, int readyState, int buffer, int bufferSize) -{ - QNetworkReplyEmscriptenImplPrivate *handler = reinterpret_cast(data); - - QString reasonStr = QString::fromUtf8((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((char *)buffer, bufferSize); - } - } - break; - }; - } - -void QNetworkReplyEmscriptenImplPrivate::onProgressCallback(void* data, int bytesWritten, int total, uint timestamp) -{ - Q_UNUSED(timestamp); - - QNetworkReplyEmscriptenImplPrivate *handler = reinterpret_cast(data); - handler->emitDataReadProgress(bytesWritten, total); -} - -void QNetworkReplyEmscriptenImplPrivate::onRequestErrorCallback(void* data, int statusCode, int statusReason) -{ - QString reasonStr = QString::fromUtf8((char *)statusReason); - - QNetworkReplyEmscriptenImplPrivate *handler = reinterpret_cast(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 QNetworkReplyEmscriptenImplPrivate::onResponseHeadersCallback(void* data, int headers) -{ - QNetworkReplyEmscriptenImplPrivate *handler = reinterpret_cast(data); - handler->headersReceived((char *)headers); -} - -void QNetworkReplyEmscriptenImplPrivate::doSendRequest() -{ - Q_Q(QNetworkReplyEmscriptenImpl); - 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 > &headers ,*/ -void QNetworkReplyEmscriptenImplPrivate::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 - - QList headersData = request.rawHeaderList(); - QStringList headersList; - for (int i = 0; i < headersData.size(); ++i) { - headersList << QString::fromUtf8(headersData.at(i) + ":" + request.rawHeader(headersData.at(i))); - } - - 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.UNSENT) console.log("UNSENT: Client has been created. open() not called yet."); //0 - if (this.readyState == this.OPENED) console.log("OPENED: open() has been called."); //1 - 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); - } - } - if (this.readyState == this.LOADING) console.log("LOADING: Downloading; responseText holds partial data.");//3 - if (this.readyState == this.DONE) { - console.log("DONE: The operation is complete.");//4 - - if (this.status < 300) { //success - if (this.status == 200 || this.status == 203) { - var datalength; - var byteArray = 0; - var buffer; - - if (this.responseType.length === 0 || this.responseType === 'document') { - console.log("xhr.responseText " + this.responseText); - 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.onload = function(e) { - if (xhr.status >= 300) { //error - handleError(xhr.status, xhr.statusText); - } - }; - - 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 QNetworkReplyEmscriptenImplPrivate::emitReplyError(QNetworkReply::NetworkError errorCode, const QString &errorString) -{ - Q_UNUSED(errorCode) - Q_Q(QNetworkReplyEmscriptenImpl); - - q->setError(errorCode, errorString); - emit q->error(errorCode); - - q->setFinished(true); - emit q->finished(); -} - -void QNetworkReplyEmscriptenImplPrivate::emitDataReadProgress(qint64 bytesReceived, qint64 bytesTotal) -{ - Q_Q(QNetworkReplyEmscriptenImpl); - - totalDownloadSize = bytesTotal; - - percentFinished = (bytesReceived / bytesTotal) * 100; - - emit q->downloadProgress(bytesReceived, totalDownloadSize); -} - -void QNetworkReplyEmscriptenImplPrivate::dataReceived(char *buffer, int bufferSize) -{ - Q_Q(QNetworkReplyEmscriptenImpl); - - 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 QNetworkReplyEmscriptenImplPrivate::headersReceived(char *buffer) -{ - Q_Q(QNetworkReplyEmscriptenImpl); - - 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(headerIndex), (QVariant)headersValue); - } - } - emit q->metaDataChanged(); -} - -void QNetworkReplyEmscriptenImplPrivate::_q_bufferOutgoingDataFinished() -{ - Q_Q(QNetworkReplyEmscriptenImpl); - - // 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 QNetworkReplyEmscriptenImplPrivate::_q_bufferOutgoingData() -{ - Q_Q(QNetworkReplyEmscriptenImpl); - - if (!outgoingDataBuffer) { - // first call, create our buffer - outgoingDataBuffer = QSharedPointer::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 QNetworkReplyEmscriptenImplPrivate::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 - -#include "moc_qnetworkreplyemscriptenimpl_p.cpp" - diff --git a/src/network/access/qnetworkreplyemscriptenimpl_p.h b/src/network/access/qnetworkreplyemscriptenimpl_p.h deleted file mode 100644 index 89df7771cc..0000000000 --- a/src/network/access/qnetworkreplyemscriptenimpl_p.h +++ /dev/null @@ -1,154 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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 QNETWORKREPLYEMSCRIPTENIMPL_H -#define QNETWORKREPLYEMSCRIPTENIMPL_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 -#include "qnetworkreply.h" -#include "qnetworkreply_p.h" -#include "qnetworkaccessmanager.h" -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -class QIODevice; -//class QNetworkAccessBackend; - -class QNetworkReplyEmscriptenImplPrivate; -class QNetworkReplyEmscriptenImpl: public QNetworkReply -{ - Q_OBJECT -public: - QNetworkReplyEmscriptenImpl(QObject *parent = 0/*QNetworkAccessManager *manager, const QNetworkRequest &req, const QNetworkAccessManager::Operation op, QIODevice *outgoingData*/); - ~QNetworkReplyEmscriptenImpl(); - virtual void abort() Q_DECL_OVERRIDE; - - // reimplemented from QNetworkReply - virtual void close() Q_DECL_OVERRIDE; - virtual qint64 bytesAvailable() const Q_DECL_OVERRIDE; - virtual bool isSequential () const Q_DECL_OVERRIDE; - qint64 size() const Q_DECL_OVERRIDE; - - virtual qint64 readData(char *data, qint64 maxlen) Q_DECL_OVERRIDE; - - void setup(QNetworkAccessManager::Operation op, const QNetworkRequest &request, - QIODevice *outgoingData); - - Q_DECLARE_PRIVATE(QNetworkReplyEmscriptenImpl) - - 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 QNetworkReplyEmscriptenImplPrivate: public QNetworkReplyPrivate -{ -public: - QNetworkReplyEmscriptenImplPrivate(); - ~QNetworkReplyEmscriptenImplPrivate(); - - 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 pendingDownloadData; - QSharedPointer pendingDownloadProgress; - // QNetworkAccessBackend *backend; - - qint64 bytesDownloaded; - qint64 bytesBuffered; - - qint64 downloadBufferReadPosition; - qint64 downloadBufferCurrentSize; - qint64 totalDownloadSize; - qint64 percentFinished; - QByteArray downloadBuffer; - - QIODevice *outgoingData; - QSharedPointer outgoingDataBuffer; - - static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url); - Q_DECLARE_PUBLIC(QNetworkReplyEmscriptenImpl) -}; - -QT_END_NAMESPACE - -//Q_DECLARE_METATYPE(QNetworkRequest::KnownHeaders) - -#endif // QNETWORKREPLYEMSCRIPTENIMPL_H diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp new file mode 100644 index 0000000000..18dcd3d4b3 --- /dev/null +++ b/src/network/access/qnetworkreplywasmimpl.cpp @@ -0,0 +1,669 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 +#include "QtCore/qdatetime.h" +#include "qnetworkaccessmanager_p.h" +#include +#include +#include +#include "qnetworkfile_p.h" +#include "qnetworkrequest.h" +//#include "qnetworkaccessbackend_p.h" + +#include + +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"; +// case QNetworkAccessManager::CustomOperation: +// return d->customVerb; + 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) { + if (!outgoingData->isSequential()) { + // fixed size non-sequential (random-access) + // just start the operation + doSendRequest(); + return; + } else { + 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()) { + doSendRequest(); + return; + } else { + 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(data); + + QString reasonStr = QString::fromUtf8((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((char *)buffer, bufferSize); + } + } + break; + }; + } + +void QNetworkReplyWasmImplPrivate::onProgressCallback(void* data, int bytesWritten, int total, uint timestamp) +{ + Q_UNUSED(timestamp); + + QNetworkReplyWasmImplPrivate *handler = reinterpret_cast(data); + handler->emitDataReadProgress(bytesWritten, total); +} + +void QNetworkReplyWasmImplPrivate::onRequestErrorCallback(void* data, int statusCode, int statusReason) +{ + QString reasonStr = QString::fromUtf8((char *)statusReason); + + QNetworkReplyWasmImplPrivate *handler = reinterpret_cast(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(data); + handler->headersReceived((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 > &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 + + QList headersData = request.rawHeaderList(); + QStringList headersList; + for (int i = 0; i < headersData.size(); ++i) { + headersList << QString::fromUtf8(headersData.at(i) + ":" + request.rawHeader(headersData.at(i))); + } + + 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.UNSENT) console.log("UNSENT: Client has been created. open() not called yet."); //0 + if (this.readyState == this.OPENED) console.log("OPENED: open() has been called."); //1 + 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); + } + } + if (this.readyState == this.LOADING) console.log("LOADING: Downloading; responseText holds partial data.");//3 + if (this.readyState == this.DONE) { + console.log("DONE: The operation is complete.");//4 + + if (this.status < 300) { //success + if (this.status == 200 || this.status == 203) { + var datalength; + var byteArray = 0; + var buffer; + + if (this.responseType.length === 0 || this.responseType === 'document') { + console.log("xhr.responseText " + this.responseText); + 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.onload = function(e) { + if (xhr.status >= 300) { //error + handleError(xhr.status, xhr.statusText); + } + }; + + 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(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::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 + +#include "moc_qnetworkreplywasmimpl_p.cpp" + diff --git a/src/network/access/qnetworkreplywasmimpl_p.h b/src/network/access/qnetworkreplywasmimpl_p.h new file mode 100644 index 0000000000..e683da61f3 --- /dev/null +++ b/src/network/access/qnetworkreplywasmimpl_p.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 +#include "qnetworkreply.h" +#include "qnetworkreply_p.h" +#include "qnetworkaccessmanager.h" +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QIODevice; +//class QNetworkAccessBackend; + +class QNetworkReplyWasmImplPrivate; +class QNetworkReplyWasmImpl: public QNetworkReply +{ + Q_OBJECT +public: + QNetworkReplyWasmImpl(QObject *parent = 0/*QNetworkAccessManager *manager, const QNetworkRequest &req, const QNetworkAccessManager::Operation op, QIODevice *outgoingData*/); + ~QNetworkReplyWasmImpl(); + virtual void abort() Q_DECL_OVERRIDE; + + // reimplemented from QNetworkReply + virtual void close() Q_DECL_OVERRIDE; + virtual qint64 bytesAvailable() const Q_DECL_OVERRIDE; + virtual bool isSequential () const Q_DECL_OVERRIDE; + qint64 size() const Q_DECL_OVERRIDE; + + virtual qint64 readData(char *data, qint64 maxlen) Q_DECL_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 pendingDownloadData; + QSharedPointer pendingDownloadProgress; + // QNetworkAccessBackend *backend; + + qint64 bytesDownloaded; + qint64 bytesBuffered; + + qint64 downloadBufferReadPosition; + qint64 downloadBufferCurrentSize; + qint64 totalDownloadSize; + qint64 percentFinished; + QByteArray downloadBuffer; + + QIODevice *outgoingData; + QSharedPointer 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/opengl/qglframebufferobject.cpp b/src/opengl/qglframebufferobject.cpp index 5bfc23a338..a7a998393c 100644 --- a/src/opengl/qglframebufferobject.cpp +++ b/src/opengl/qglframebufferobject.cpp @@ -570,7 +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_HTML5 +#ifndef Q_OS_WASM if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8, size.width(), size.height()); diff --git a/src/platformsupport/eglconvenience/qeglplatformcontext.cpp b/src/platformsupport/eglconvenience/qeglplatformcontext.cpp index 5131816213..4e86852bc8 100644 --- a/src/platformsupport/eglconvenience/qeglplatformcontext.cpp +++ b/src/platformsupport/eglconvenience/qeglplatformcontext.cpp @@ -277,7 +277,7 @@ EGLSurface QEGLPlatformContext::createTemporaryOffscreenSurface() EGL_LARGEST_PBUFFER, EGL_FALSE, EGL_NONE }; -#ifndef Q_OS_HTML5 +#ifndef Q_OS_WASM // eglCreateWindowSurface is not supported by emscripten // Cannot just pass m_eglConfig because it may not be suitable for pbuffers. Instead, diff --git a/src/plugins/imageformats/gif/gif.pro b/src/plugins/imageformats/gif/gif.pro index c6b8a231c9..2e519bb35e 100644 --- a/src/plugins/imageformats/gif/gif.pro +++ b/src/plugins/imageformats/gif/gif.pro @@ -9,4 +9,4 @@ PLUGIN_TYPE = imageformats PLUGIN_CLASS_NAME = QGifPlugin load(qt_plugin) -emscripten: CONFIG += static plugin +wasm: CONFIG += static plugin diff --git a/src/plugins/imageformats/ico/ico.pro b/src/plugins/imageformats/ico/ico.pro index f47470e751..6431b738c4 100644 --- a/src/plugins/imageformats/ico/ico.pro +++ b/src/plugins/imageformats/ico/ico.pro @@ -9,4 +9,4 @@ PLUGIN_TYPE = imageformats PLUGIN_CLASS_NAME = QICOPlugin load(qt_plugin) -emscripten: CONFIG += static plugin +wasm: CONFIG += static plugin diff --git a/src/plugins/imageformats/jpeg/jpeg.pro b/src/plugins/imageformats/jpeg/jpeg.pro index 37a5211625..6bdc4b0ce7 100644 --- a/src/plugins/imageformats/jpeg/jpeg.pro +++ b/src/plugins/imageformats/jpeg/jpeg.pro @@ -17,4 +17,4 @@ PLUGIN_TYPE = imageformats PLUGIN_CLASS_NAME = QJpegPlugin load(qt_plugin) -emscripten: CONFIG += static plugin +wasm: CONFIG += static plugin diff --git a/src/plugins/platforminputcontexts/platforminputcontexts.pro b/src/plugins/platforminputcontexts/platforminputcontexts.pro index d437d554e0..397490eae3 100644 --- a/src/plugins/platforminputcontexts/platforminputcontexts.pro +++ b/src/plugins/platforminputcontexts/platforminputcontexts.pro @@ -1,7 +1,7 @@ TEMPLATE = subdirs QT_FOR_CONFIG += gui-private -!emscripten:qtHaveModule(dbus) { +!wasm:qtHaveModule(dbus) { !mac:!win32:SUBDIRS += ibus } diff --git a/src/plugins/platforms/html5/fonts/DejaVuSans.ttf b/src/plugins/platforms/html5/fonts/DejaVuSans.ttf deleted file mode 100644 index 7e411a71be..0000000000 Binary files a/src/plugins/platforms/html5/fonts/DejaVuSans.ttf and /dev/null differ diff --git a/src/plugins/platforms/html5/fonts/Vera.ttf b/src/plugins/platforms/html5/fonts/Vera.ttf deleted file mode 100644 index 58cd6b5e61..0000000000 Binary files a/src/plugins/platforms/html5/fonts/Vera.ttf and /dev/null differ diff --git a/src/plugins/platforms/html5/fonts/html5fonts.qrc b/src/plugins/platforms/html5/fonts/html5fonts.qrc deleted file mode 100644 index d017417ee5..0000000000 --- a/src/plugins/platforms/html5/fonts/html5fonts.qrc +++ /dev/null @@ -1,6 +0,0 @@ - - - Vera.ttf - DejaVuSans.ttf - - diff --git a/src/plugins/platforms/html5/html5.json b/src/plugins/platforms/html5/html5.json deleted file mode 100644 index b5fbc59fd7..0000000000 --- a/src/plugins/platforms/html5/html5.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Keys": [ "html5" ] -} diff --git a/src/plugins/platforms/html5/html5.pro b/src/plugins/platforms/html5/html5.pro deleted file mode 100644 index 6072f1e1f8..0000000000 --- a/src/plugins/platforms/html5/html5.pro +++ /dev/null @@ -1,59 +0,0 @@ -TARGET = html5 -CONFIG += static plugin -QT += \ - core-private gui-private \ - eventdispatcher_support-private fontdatabase_support-private egl_support-private - -#DEFINES += QEGL_EXTRA_DEBUG - -# Avoid X11 header collision, use generic EGL native types -DEFINES += QT_EGL_NO_X11 - -SOURCES = main.cpp \ - qhtml5integration.cpp \ - qhtml5window.cpp \ - qhtml5screen.cpp \ - qhtml5fontdatabase.cpp \ - qhtml5eventtranslator.cpp \ - qhtml5eventdispatcher.cpp \ - qhtml5compositor.cpp \ - qhtml5cursor.cpp \ - qhtml5openglcontext.cpp \ - qhtml5theme.cpp - -HEADERS = qhtml5integration.h \ - qhtml5window.h \ - qhtml5screen.h \ - qhtml5fontdatabase.h \ - qhtml5eventtranslator.h \ - qhtml5eventdispatcher.h \ - qhtml5compositor.h \ - qhtml5stylepixmaps_p.h \ - qhtml5cursor.h \ - qhtml5openglcontext.h \ - qhtml5theme.h - -RESOURCES += fonts/html5fonts.qrc - -qtConfig(opengl) { - SOURCES += qhtml5backingstore.cpp - HEADERS += qhtml5backingstore.h -} -CONFIG += egl - -OTHER_FILES += \ - html5.json \ - html5_shell.html \ - qtloader.js - -shell_files.path = $$[QT_INSTALL_PLUGINS]/platforms -shell_files.files = html5_shell.html \ - qtloader.js \ - qtlogo.svg - -INSTALLS += shell_files - -PLUGIN_TYPE = platforms -PLUGIN_CLASS_NAME = QHtml5IntegrationPlugin -!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - -load(qt_plugin) diff --git a/src/plugins/platforms/html5/html5_shell.html b/src/plugins/platforms/html5/html5_shell.html deleted file mode 100644 index 67bfcdfbdc..0000000000 --- a/src/plugins/platforms/html5/html5_shell.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - APPNAME - - - -
-
- - Qt for WebAssembly: APPNAME -
- -
-
- - - - - - diff --git a/src/plugins/platforms/html5/main.cpp b/src/plugins/platforms/html5/main.cpp deleted file mode 100644 index 27aa30b371..0000000000 --- a/src/plugins/platforms/html5/main.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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 -#include "qhtml5integration.h" - -QT_BEGIN_NAMESPACE - -class QHtml5IntegrationPlugin : public QPlatformIntegrationPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "html5.json") -public: - QPlatformIntegration *create(const QString&, const QStringList&) override; -}; - -QPlatformIntegration* QHtml5IntegrationPlugin::create(const QString& system, const QStringList& paramList) -{ - Q_UNUSED(paramList); - if (!system.compare(QStringLiteral("html5"), Qt::CaseInsensitive)) - return new QHtml5Integration; - - return nullptr; -} - -QT_END_NAMESPACE - -#include "main.moc" diff --git a/src/plugins/platforms/html5/qhtml5backingstore.cpp b/src/plugins/platforms/html5/qhtml5backingstore.cpp deleted file mode 100644 index 7bddc1b66b..0000000000 --- a/src/plugins/platforms/html5/qhtml5backingstore.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/**************************************************************************** -** -** 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 "qhtml5backingstore.h" -#include "qhtml5window.h" -#include "qhtml5compositor.h" - -#include -#include -#include -#include -#include - -#include - -QT_BEGIN_NAMESPACE - -QHtml5BackingStore::QHtml5BackingStore(QHtml5Compositor *compositor, QWindow *window) - : QPlatformBackingStore(window) - , mCompositor(compositor) - , mTexture(new QOpenGLTexture(QOpenGLTexture::Target2D)) -{ - QHtml5Window *html5Window = static_cast(window->handle()); - if (html5Window) - html5Window->setBackingStore(this); -} - -QHtml5BackingStore::~QHtml5BackingStore() -{ -} - -QPaintDevice *QHtml5BackingStore::paintDevice() -{ - return &mImage; -} - -void QHtml5BackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) -{ - Q_UNUSED(window); - Q_UNUSED(region); - Q_UNUSED(offset); - - mDirty |= region; - mCompositor->requestRedraw(); -} - -void QHtml5BackingStore::updateTexture() -{ - if (mDirty.isNull()) - return; - - if (!mTexture->isCreated()) { - mTexture->setMinificationFilter(QOpenGLTexture::Nearest); - mTexture->setMagnificationFilter(QOpenGLTexture::Nearest); - mTexture->setWrapMode(QOpenGLTexture::ClampToEdge); - mTexture->setData(mImage, QOpenGLTexture::DontGenerateMipMaps); - mTexture->create(); - } - mTexture->bind(); - - QRegion fixed; - QRect imageRect = mImage.rect(); - - for (const QRect &rect : mDirty) { - - // Convert device-independent dirty region to device region - qreal dpr = mImage.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, - mImage.constScanLine(rect.y())); - } else { - glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, GL_UNSIGNED_BYTE, - mImage.copy(rect).constBits()); - } - } - /* End of code taken from QEGLPlatformBackingStore */ - - mDirty = QRegion(); -} - -void QHtml5BackingStore::beginPaint(const QRegion ®ion) -{ - mDirty |= region; - // Keep backing store device pixel ratio in sync with window - if (mImage.devicePixelRatio() != window()->devicePixelRatio()) - resize(backingStore()->size(), backingStore()->staticContents()); - - QPainter painter(&mImage); - painter.setCompositionMode(QPainter::CompositionMode_Source); - const QColor blank = Qt::transparent; - for (const QRect &rect : region) - painter.fillRect(rect, blank); -} - -void QHtml5BackingStore::resize(const QSize &size, const QRegion &staticContents) -{ - Q_UNUSED(staticContents) - - mImage = QImage(size * window()->devicePixelRatio(), QImage::Format_RGB32); - mImage.setDevicePixelRatio(window()->devicePixelRatio()); - - if (mTexture->isCreated()) - mTexture->destroy(); -} - -QImage QHtml5BackingStore::toImage() const -{ - // used by QPlatformBackingStore::composeAndFlush - return mImage; -} - -const QImage &QHtml5BackingStore::getImageRef() const -{ - return mImage; -} - -const QOpenGLTexture* QHtml5BackingStore::getUpdatedTexture() -{ - updateTexture(); - return mTexture.data(); -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/html5/qhtml5backingstore.h b/src/plugins/platforms/html5/qhtml5backingstore.h deleted file mode 100644 index d808d10254..0000000000 --- a/src/plugins/platforms/html5/qhtml5backingstore.h +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** -** -** 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 QHTML5BACKINGSTORE_H -#define QHTML5BACKINGSTORE_H - -#include -#include - -QT_BEGIN_NAMESPACE - -class QOpenGLTexture; -class QRegion; -class QHtml5Compositor; - -class QHtml5BackingStore : public QPlatformBackingStore -{ -public: - QHtml5BackingStore(QHtml5Compositor *compositor, QWindow *window); - ~QHtml5BackingStore(); - - QPaintDevice *paintDevice() override; - - void beginPaint(const QRegion &) override; - void flush(QWindow *window, const QRegion ®ion, 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: - QHtml5Compositor *mCompositor; - - QImage mImage; - - QScopedPointer mTexture; - - QRegion mDirty; - -}; - -QT_END_NAMESPACE - -#endif // QHTML5BACKINGSTORE_H diff --git a/src/plugins/platforms/html5/qhtml5compositor.cpp b/src/plugins/platforms/html5/qhtml5compositor.cpp deleted file mode 100644 index 2c30985832..0000000000 --- a/src/plugins/platforms/html5/qhtml5compositor.cpp +++ /dev/null @@ -1,728 +0,0 @@ -/**************************************************************************** -** -** 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 "qhtml5compositor.h" -#include "qhtml5window.h" -#include "qhtml5stylepixmaps_p.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -Q_GUI_EXPORT int qt_defaultDpiX(); - -QHtml5CompositedWindow::QHtml5CompositedWindow() - : window(0) - , parentWindow(nullptr) - , flushPending(false) - , visible(false) -{ -} - -QHtml5Compositor::QHtml5Compositor() - : m_frameBuffer(nullptr) - , mBlitter(new QOpenGLTextureBlitter) - , m_needComposit(false) - , m_inFlush(false) - , m_inResize(false) - , m_isEnabled(true) - , m_targetDevicePixelRatio(1) -{ -} - -QHtml5Compositor::~QHtml5Compositor() -{ - delete m_frameBuffer; -} - -void QHtml5Compositor::setEnabled(bool enabled) -{ - m_isEnabled = enabled; -} - -void QHtml5Compositor::addWindow(QHtml5Window *window, QHtml5Window *parentWindow) -{ - QHtml5CompositedWindow 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 QHtml5Compositor::removeWindow(QHtml5Window *window) -{ - QHtml5Window *platformWindow = m_compositedWindows[window].parentWindow; - - if (platformWindow) { - QHtml5Window *parentWindow = window; - m_compositedWindows[parentWindow].childWindows.removeAll(window); - } - - m_windowStack.removeAll(window); - m_compositedWindows.remove(window); - - notifyTopWindowChanged(window); -} - -void QHtml5Compositor::setScreen(QHtml5Screen *screen) -{ - mScreen = screen; -} - -void QHtml5Compositor::setVisible(QHtml5Window *window, bool visible) -{ - QHtml5CompositedWindow &compositedWindow = m_compositedWindows[window]; - if (compositedWindow.visible == visible) - return; - - compositedWindow.visible = visible; - compositedWindow.flushPending = true; - if (visible) - compositedWindow.damage = compositedWindow.window->geometry(); - else - globalDamage = compositedWindow.window->geometry(); // repaint previosly covered area. - - requestRedraw(); -} - -void QHtml5Compositor::raise(QHtml5Window *window) -{ - if (m_compositedWindows.size() <= 1) - return; - - QHtml5CompositedWindow &compositedWindow = m_compositedWindows[window]; - compositedWindow.damage = compositedWindow.window->geometry(); - m_windowStack.removeAll(window); - m_windowStack.append(window); - - notifyTopWindowChanged(window); -} - -void QHtml5Compositor::lower(QHtml5Window *window) -{ - if (m_compositedWindows.size() <= 1) - return; - - m_windowStack.removeAll(window); - m_windowStack.prepend(window); - QHtml5CompositedWindow &compositedWindow = m_compositedWindows[window]; - globalDamage = compositedWindow.window->geometry(); // repaint previosly covered area. - - notifyTopWindowChanged(window); -} - -void QHtml5Compositor::setParent(QHtml5Window *window, QHtml5Window *parent) -{ - m_compositedWindows[window].parentWindow = parent; - - requestRedraw(); -} - -void QHtml5Compositor::flush(QHtml5Window *window, const QRegion ®ion) -{ - QHtml5CompositedWindow &compositedWindow = m_compositedWindows[window]; - compositedWindow.flushPending = true; - compositedWindow.damage = region; - - requestRedraw(); -} - -int QHtml5Compositor::windowCount() const -{ - return m_windowStack.count(); -} - - -void QHtml5Compositor::redrawWindowContent() -{ - // Redraw window content by sending expose events. This redraw - // will cause a backing store flush, which will call requestRedraw() - // to composit. - for (QHtml5Window *platformWindow : m_windowStack) { - QWindow *window = platformWindow->window(); - QWindowSystemInterface::handleExposeEvent( - window, QRect(QPoint(0, 0), window->geometry().size())); - } -} - -void QHtml5Compositor::requestRedraw() -{ - if (m_needComposit) - return; - - m_needComposit = true; - QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); -} - -QWindow *QHtml5Compositor::windowAt(QPoint p, int padding) const -{ - int index = m_windowStack.count() - 1; - // qDebug() << "window at" << "point" << p << "window count" << index; - - while (index >= 0) { - const QHtml5CompositedWindow &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 *QHtml5Compositor::keyWindow() const -{ - return m_windowStack.at(m_windowStack.count() - 1)->window(); -} - -bool QHtml5Compositor::event(QEvent *ev) -{ - if (ev->type() == QEvent::UpdateRequest) { - if (m_isEnabled) - frame(); - return true; - } - - return QObject::event(ev); -} - -void QHtml5Compositor::blit(QOpenGLTextureBlitter *blitter, QHtml5Screen *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 QHtml5Compositor::drawWindowContent(QOpenGLTextureBlitter *blitter, QHtml5Screen *screen, QHtml5Window *window) -{ - QHtml5BackingStore* backingStore = window->backingStore(); - - QOpenGLTexture const* texture = backingStore->getUpdatedTexture(); - - blit(blitter, screen, texture, window->geometry()); -} - -QPalette QHtml5Compositor::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 QHtml5Compositor::titlebarRect(QHtml5TitleBarOptions tb, QHtml5Compositor::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); -} - -QHtml5Compositor::QHtml5TitleBarOptions QHtml5Compositor::makeTitleBarOptions(const QHtml5Window *window) -{ - int width = window->windowFrameGeometry().width(); - int border = window->borderWidth(); - - QHtml5TitleBarOptions 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 = QHtml5Compositor::makeWindowPalette(); - - if (window->window()->isActive()) { - titleBarOptions.palette.setCurrentColorGroup(QPalette::Active); - } else { - titleBarOptions.palette.setCurrentColorGroup(QPalette::Inactive); - } - - if (window->activeSubControl() != QHtml5Compositor::SC_None) { - titleBarOptions.subControls = window->activeSubControl(); - } - - if (!window->window()->title().isEmpty()) { - - titleBarOptions.titleBarOptionsString = window->window()->title(); - } - - return titleBarOptions; -} - -void QHtml5Compositor::drawWindowDecorations(QOpenGLTextureBlitter *blitter, QHtml5Screen *screen, QHtml5Window *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()); - - QHtml5TitleBarOptions titleBarOptions = makeTitleBarOptions(window); - - drawTitlebarWindow(titleBarOptions, &painter); - - QHtml5FrameOptions 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 QHtml5Compositor::drawFrameWindow(QHtml5FrameOptions 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(xpm)); - if (!QPixmapCache::find(tag, &result)) { - result = QPixmap(xpm); - QPixmapCache::insert(tag, result); - } - return result; -} - -void QHtml5Compositor::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 QHtml5Compositor::drawTitlebarWindow(QHtml5TitleBarOptions 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 QHtml5Compositor::drawShadePanel(QHtml5TitleBarOptions 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 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; idrawLines(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 QHtml5Compositor::drawWindow(QOpenGLTextureBlitter *blitter, QHtml5Screen *screen, QHtml5Window *window) -{ - if (window->window()->type() != Qt::Popup) - drawWindowDecorations(blitter, screen, window); - drawWindowContent(blitter, screen, window); -} - -void QHtml5Compositor::frame() -{ - if (!m_needComposit) - return; - - m_needComposit = false; - - if (m_windowStack.empty() || !mScreen) - return; - - QHtml5Window *someWindow = nullptr; - - foreach (QHtml5Window *window, m_windowStack) { - if (window->window()->surfaceClass() == QSurface::Window - && qt_window_private(static_cast(window->window()))->receivedExpose) { - someWindow = window; - break; - } - } - - if (!someWindow) - return; - - if (mContext.isNull()) { - mContext.reset(new QOpenGLContext()); - //mContext->setFormat(mScreen->format()); - mContext->setScreen(mScreen->screen()); - mContext->create(); - } - - mContext->makeCurrent(someWindow->window()); - - if (!mBlitter->isCreated()) - mBlitter->create(); - - qreal dpr = mScreen->devicePixelRatio(); - glViewport(0, 0, mScreen->geometry().width() * dpr, mScreen->geometry().height() *dpr); - - mContext->functions()->glClearColor(0.2, 0.2, 0.2, 1.0); - mContext->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - - mBlitter->bind(); - mBlitter->setRedBlueSwizzle(true); - - foreach (QHtml5Window *window, m_windowStack) { - QHtml5CompositedWindow &compositedWindow = m_compositedWindows[window]; - - if (!compositedWindow.visible) - continue; - - drawWindow(mBlitter.data(), mScreen, window); - } - - mBlitter->release(); - - if (someWindow && someWindow->window()->surfaceType() == QSurface::OpenGLSurface) - mContext->swapBuffers(someWindow->window()); -} - -void QHtml5Compositor::notifyTopWindowChanged(QHtml5Window* window) -{ - QWindow *modalWindow; - bool blocked = QGuiApplicationPrivate::instance()->isWindowBlocked(window->window(), &modalWindow); - - if (blocked) { - raise(static_cast(modalWindow->handle())); - return; - } - - requestRedraw(); - QWindowSystemInterface::handleWindowActivated(window->window()); -} diff --git a/src/plugins/platforms/html5/qhtml5compositor.h b/src/plugins/platforms/html5/qhtml5compositor.h deleted file mode 100644 index 8ce3f1948a..0000000000 --- a/src/plugins/platforms/html5/qhtml5compositor.h +++ /dev/null @@ -1,171 +0,0 @@ -/**************************************************************************** -** -** 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 QHTML5COMPOSITOR_H -#define QHTML5COMPOSITOR_H - -#include -#include - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QHtml5Window; -class QHtml5Screen; -class QOpenGLContext; -class QOpenGLTextureBlitter; - -class QHtml5CompositedWindow -{ -public: - QHtml5CompositedWindow(); - - QHtml5Window *window; - QHtml5Window *parentWindow; - QRegion damage; - bool flushPending; - bool visible; - QList childWindows; -}; - -class QHtml5Compositor : public QObject -{ - Q_OBJECT -public: - QHtml5Compositor(); - ~QHtml5Compositor(); - - enum QHtml5SubControl { - 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, QHtml5SubControl) - - enum QHtml5StateFlag { - State_None = 0x00000000, - State_Enabled = 0x00000001, - State_Raised = 0x00000002, - State_Sunken = 0x00000004 - }; - Q_DECLARE_FLAGS(StateFlags, QHtml5StateFlag) - - struct QHtml5TitleBarOptions { - QRect rect; - Qt::WindowFlags flags; - int state; - QPalette palette; - QString titleBarOptionsString; - QHtml5Compositor::SubControls subControls; - }; - - struct QHtml5FrameOptions { - QRect rect; - int lineWidth; - QPalette palette; - }; - - void setEnabled(bool enabled); - - void addWindow(QHtml5Window *window, QHtml5Window *parentWindow = nullptr); - void removeWindow(QHtml5Window *window); - void setScreen(QHtml5Screen *screen); - - void setVisible(QHtml5Window *window, bool visible); - void raise(QHtml5Window *window); - void lower(QHtml5Window *window); - void setParent(QHtml5Window *window, QHtml5Window *parent); - - void flush(QHtml5Window *surface, const QRegion ®ion); - - int windowCount() const; - - void redrawWindowContent(); - void requestRedraw(); - - QWindow *windowAt(QPoint p, int padding = 0) const; - QWindow *keyWindow() const; - - bool event(QEvent *event); - - static QHtml5TitleBarOptions makeTitleBarOptions(const QHtml5Window *window); - static QRect titlebarRect(QHtml5TitleBarOptions tb, QHtml5Compositor::SubControls subcontrol); - -private slots: - void frame(); - -private: - void createFrameBuffer(); - void flush2(const QRegion ®ion); - void flushCompletedCallback(int32_t); - void notifyTopWindowChanged(QHtml5Window* window); - void drawWindow(QOpenGLTextureBlitter *blitter, QHtml5Screen *screen, QHtml5Window *window); - void drawWindowContent(QOpenGLTextureBlitter *blitter, QHtml5Screen *screen, QHtml5Window *window); - void blit(QOpenGLTextureBlitter *blitter, QHtml5Screen *screen, const QOpenGLTexture *texture, QRect targetGeometry); - - void drawWindowDecorations(QOpenGLTextureBlitter *blitter, QHtml5Screen *screen, QHtml5Window *window); - void drwPanelButton(); -private: - QImage *m_frameBuffer; - QScopedPointer mContext; - QScopedPointer mBlitter; - QHtml5Screen *mScreen; - - QHash m_compositedWindows; - QList m_windowStack; - QRegion 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(QHtml5FrameOptions options, QPainter *painter); - void drawTitlebarWindow(QHtml5TitleBarOptions options, QPainter *painter); - void drawShadePanel(QHtml5TitleBarOptions options, QPainter *painter); - void drawItemPixmap(QPainter *painter, const QRect &rect, - int alignment, const QPixmap &pixmap) const; -}; -Q_DECLARE_OPERATORS_FOR_FLAGS(QHtml5Compositor::SubControls) - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/platforms/html5/qhtml5cursor.cpp b/src/plugins/platforms/html5/qhtml5cursor.cpp deleted file mode 100644 index 20c6633acd..0000000000 --- a/src/plugins/platforms/html5/qhtml5cursor.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/**************************************************************************** -** -** 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 "qhtml5cursor.h" - -#include - -#include - -void QHtml5Cursor::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() << "QHtml5Cursor: 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 QHtml5Cursor::cursorShapeToHtml(Qt::CursorShape shape) -{ - QByteArray cursorName; - - switch (shape) { - case Qt::ArrowCursor: - cursorName = "default"; - break; - case Qt::UpArrowCursor: - break; // ## missing? - 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/html5/qhtml5cursor.h b/src/plugins/platforms/html5/qhtml5cursor.h deleted file mode 100644 index fdf8abae0d..0000000000 --- a/src/plugins/platforms/html5/qhtml5cursor.h +++ /dev/null @@ -1,43 +0,0 @@ -/**************************************************************************** -** -** 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 QHTML5CURSOR_H -#define QHTML5CURSOR_H - -#include - -class QHtml5Cursor : public QPlatformCursor -{ -public: - void changeCursor(QCursor * windowCursor, QWindow * window) override; - - QByteArray cursorShapeToHtml(Qt::CursorShape shape); -}; - -#endif diff --git a/src/plugins/platforms/html5/qhtml5eventdispatcher.cpp b/src/plugins/platforms/html5/qhtml5eventdispatcher.cpp deleted file mode 100644 index af698032f2..0000000000 --- a/src/plugins/platforms/html5/qhtml5eventdispatcher.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/**************************************************************************** -** -** 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 "qhtml5eventdispatcher.h" - -#include -#include - -#include - -class QHtml5EventDispatcherPrivate : public QUnixEventDispatcherQPAPrivate -{ - -}; - -QHtml5EventDispatcher *g_htmlEventDispatcher; - -QHtml5EventDispatcher::QHtml5EventDispatcher(QObject *parent) - : QUnixEventDispatcherQPA(parent) -{ - - g_htmlEventDispatcher = this; -} - -QHtml5EventDispatcher::~QHtml5EventDispatcher() -{ - g_htmlEventDispatcher = nullptr; -} - -bool QHtml5EventDispatcher::registerRequestUpdateCallback(std::function callback) -{ - if (!g_htmlEventDispatcher || !g_htmlEventDispatcher->m_hasMainLoop) - return false; - - g_htmlEventDispatcher->m_requestUpdateCallbacks.append(callback); - emscripten_resume_main_loop(); - return true; -} - -void QHtml5EventDispatcher::maintainTimers() -{ - if (!g_htmlEventDispatcher || !g_htmlEventDispatcher->m_hasMainLoop) - return; - - g_htmlEventDispatcher->doMaintainTimers(); -} - -bool QHtml5EventDispatcher::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) { - QHtml5EventDispatcher *that = static_cast(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 QHtml5EventDispatcher::doMaintainTimers() -{ - Q_D(QHtml5EventDispatcher); - - // 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) { - QHtml5EventDispatcher *that = static_cast(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) { - QHtml5EventDispatcher *that = static_cast(eventDispatcher); - that->m_currentTargetTime = std::numeric_limits::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/html5/qhtml5eventdispatcher.h b/src/plugins/platforms/html5/qhtml5eventdispatcher.h deleted file mode 100644 index 95aacb7d2e..0000000000 --- a/src/plugins/platforms/html5/qhtml5eventdispatcher.h +++ /dev/null @@ -1,64 +0,0 @@ -/**************************************************************************** -** -** 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 QHTML5EVENTDISPATCHER_H -#define QHTML5EVENTDISPATCHER_H - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QHtml5EventDispatcherPrivate; - -class QHtml5EventDispatcher : public QUnixEventDispatcherQPA -{ - Q_DECLARE_PRIVATE(QHtml5EventDispatcher) -public: - explicit QHtml5EventDispatcher(QObject *parent = 0); - ~QHtml5EventDispatcher(); - - static bool registerRequestUpdateCallback(std::function 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::max(); - QVector> m_requestUpdateCallbacks; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/platforms/html5/qhtml5eventtranslator.cpp b/src/plugins/platforms/html5/qhtml5eventtranslator.cpp deleted file mode 100644 index 0bb5768ba6..0000000000 --- a/src/plugins/platforms/html5/qhtml5eventtranslator.cpp +++ /dev/null @@ -1,524 +0,0 @@ -/**************************************************************************** -** -** 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 "qhtml5eventtranslator.h" -#include "qhtml5eventdispatcher.h" -#include "qhtml5compositor.h" -#include "qhtml5integration.h" - -#include -#include -#include -#include -#include -#include - -#include - -#include - -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; - -QHtml5EventTranslator::QHtml5EventTranslator(QObject *parent) - : QObject(parent) - , draggedWindow(nullptr) - , pressedButtons(Qt::NoButton) - , resizeMode(QHtml5Window::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 -QFlags QHtml5EventTranslator::translatKeyModifier(const Event *event) -{ - QFlags 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 QHtml5EventTranslator::translateKeyboardEventModifier(const EmscriptenKeyboardEvent *keyEvent) -{ - QFlags keyModifier = translatKeyModifier(keyEvent); - if (keyEvent->location == DOM_KEY_LOCATION_NUMPAD) { - keyModifier |= Qt::KeypadModifier; - } - - return keyModifier; -} - -QFlags QHtml5EventTranslator::translateMouseEventModifier(const EmscriptenMouseEvent *mouseEvent) -{ - return translatKeyModifier(mouseEvent); -} - -int QHtml5EventTranslator::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( - 0, keyType, qtKey, translateKeyboardEventModifier(keyEvent), keyText); - QHtml5EventDispatcher::maintainTimers(); - return accepted ? 1 : 0; -} - -Qt::Key QHtml5EventTranslator::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: - 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(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 QHtml5EventTranslator::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 QHtml5EventTranslator::mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) -{ - QHtml5EventTranslator *translator = (QHtml5EventTranslator*)userData; - translator->processMouse(eventType,mouseEvent); - QHtml5EventDispatcher::maintainTimers(); - return 0; -} - -void resizeWindow(QWindow *window, QHtml5Window::ResizeMode mode, - QRect startRect, QPoint amount) -{ - if (mode == QHtml5Window::ResizeNone) - return; - - bool top = mode == QHtml5Window::ResizeTopLeft || - mode == QHtml5Window::ResizeTop || - mode == QHtml5Window::ResizeTopRight; - - bool bottom = mode == QHtml5Window::ResizeBottomLeft || - mode == QHtml5Window::ResizeBottom || - mode == QHtml5Window::ResizeBottomRight; - - bool left = mode == QHtml5Window::ResizeLeft || - mode == QHtml5Window::ResizeTopLeft || - mode == QHtml5Window::ResizeBottomLeft; - - bool right = mode == QHtml5Window::ResizeRight || - mode == QHtml5Window::ResizeTopRight || - mode == QHtml5Window::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 QHtml5EventTranslator::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 = QHtml5Integration::get()->compositor()->windowAt(point, 5); - QHtml5Window *htmlWindow = static_cast(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; - QHtml5Window *oldWindow = nullptr; - - if (mouseEvent->button == 0 && pressedWindow) { - oldWindow = static_cast(pressedWindow->handle()); - pressedWindow = nullptr; - } - - - if (mouseEvent->button == 0) { - draggedWindow = nullptr; - resizeMode = QHtml5Window::ResizeNone; - } - - if (oldWindow) - oldWindow->injectMouseReleased(localPoint, point, button, modifiers); - } - break; - case 8://move //drag event - { - buttonEventType = QEvent::MouseMove; - if (resizeMode == QHtml5Window::ResizeNone && draggedWindow) { - draggedWindow->setX(draggedWindow->x() + mouseEvent->movementX); - draggedWindow->setY(draggedWindow->y() + mouseEvent->movementY); - } - - if (resizeMode != QHtml5Window::ResizeNone) { - QPoint delta = QPoint(mouseEvent->canvasX, mouseEvent->canvasY) - resizePoint; - resizeWindow(draggedWindow, resizeMode, resizeStartRect, delta); - } - } - break; - default: - break; - }; - - if (window2 && !onFrame) { - QWindowSystemInterface::handleMouseEvent( - window2, timestamp, localPoint, point, pressedButtons, button, buttonEventType, modifiers); - } -} - -int QHtml5EventTranslator::focus_cb(int /*eventType*/, const EmscriptenFocusEvent */*focusEvent*/, void */*userData*/) -{ - return 0; -} - -int QHtml5EventTranslator::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 = QHtml5Integration::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 QHtml5EventTranslator::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) -{ - QList 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 = QHtml5Integration::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); - } - - QHtml5EventTranslator *html5EventTranslator = (QHtml5EventTranslator*)userData; - QFlags keyModifier = translatKeyModifier(touchEvent); - - if (eventType != EMSCRIPTEN_EVENT_TOUCHCANCEL) - QWindowSystemInterface::handleTouchEvent(window2, html5EventTranslator->getTimestamp(), html5EventTranslator->touchDevice, touchPointList, keyModifier); - else - QWindowSystemInterface::handleTouchCancelEvent(window2, html5EventTranslator->getTimestamp(), html5EventTranslator->touchDevice, keyModifier); - - QCoreApplication::processEvents(); - return 1; -} - -quint64 QHtml5EventTranslator::getTimestamp() -{ - return QDeadlineTimer::current().deadlineNSecs() / 1000; -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/html5/qhtml5eventtranslator.h b/src/plugins/platforms/html5/qhtml5eventtranslator.h deleted file mode 100644 index 9821b41630..0000000000 --- a/src/plugins/platforms/html5/qhtml5eventtranslator.h +++ /dev/null @@ -1,211 +0,0 @@ -/**************************************************************************** -** -** 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 QHTML5EVENTTRANSLATOR_H -#define QHTML5EVENTTRANSLATOR_H - -#include -#include -#include -#include -#include "qhtml5window.h" -#include - -QT_BEGIN_NAMESPACE - -class QWindow; - -class QHtml5EventTranslator : 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 QHtml5EventTranslator(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 - static QFlags translatKeyModifier(const Event *event); - static QFlags translateKeyboardEventModifier(const EmscriptenKeyboardEvent *keyEvent); - static QFlags 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; - - QHtml5Window::ResizeMode resizeMode; - QPoint resizePoint; - QRect resizeStartRect; - QTouchDevice* touchDevice; - quint64 getTimestamp(); -}; - -QT_END_NAMESPACE -#endif // QHTML5EVENTTRANSLATOR_H diff --git a/src/plugins/platforms/html5/qhtml5fontdatabase.cpp b/src/plugins/platforms/html5/qhtml5fontdatabase.cpp deleted file mode 100644 index 43d2c9eb53..0000000000 --- a/src/plugins/platforms/html5/qhtml5fontdatabase.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/**************************************************************************** -** -** 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 "qhtml5fontdatabase.h" - -#include -QT_BEGIN_NAMESPACE - -void QHtml5FontDatabase::populateFontDatabase() -{ - Q_INIT_RESOURCE(html5fonts); - - // 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 *QHtml5FontDatabase::fontEngine(const QFontDef &fontDef, void *handle) -{ - return QFreeTypeFontDatabase::fontEngine(fontDef, handle); -} - -QStringList QHtml5FontDatabase::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 QHtml5FontDatabase::addApplicationFont(const QByteArray &fontData, - const QString &fileName) -{ - return QFreeTypeFontDatabase::addApplicationFont(fontData, fileName); -} - -void QHtml5FontDatabase::releaseHandle(void *handle) -{ - QFreeTypeFontDatabase::releaseHandle(handle); -} - - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/html5/qhtml5fontdatabase.h b/src/plugins/platforms/html5/qhtml5fontdatabase.h deleted file mode 100644 index 53ce952663..0000000000 --- a/src/plugins/platforms/html5/qhtml5fontdatabase.h +++ /dev/null @@ -1,49 +0,0 @@ -/**************************************************************************** -** -** 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 QHTML5FONTDATABASE_H -#define QHTML5FONTDATABASE_H - -#include - -QT_BEGIN_NAMESPACE - -class QHtml5FontDatabase : 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/html5/qhtml5integration.cpp b/src/plugins/platforms/html5/qhtml5integration.cpp deleted file mode 100644 index f5945744f8..0000000000 --- a/src/plugins/platforms/html5/qhtml5integration.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/**************************************************************************** -** -** 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 "qhtml5integration.h" -#include "qhtml5eventtranslator.h" -#include "qhtml5eventdispatcher.h" -#include "qhtml5compositor.h" -#include "qhtml5openglcontext.h" -#include "qhtml5theme.h" - -#include "qhtml5window.h" -#ifndef QT_NO_OPENGL -# include "qhtml5backingstore.h" -#endif -#include "qhtml5fontdatabase.h" -#if defined(Q_OS_UNIX) -#include -#endif -#include -#include -#include -#include - -#include - -// this is where EGL headers are pulled in, make sure it is last -#include "qhtml5screen.h" - -using namespace emscripten; -QT_BEGIN_NAMESPACE - -void browserBeforeUnload() { - QHtml5Integration::QHtml5BrowserExit(); -} - -EMSCRIPTEN_BINDINGS(my_module) { - function("browserBeforeUnload", &browserBeforeUnload); -} - -static QHtml5Integration *globalHtml5Integration; -QHtml5Integration *QHtml5Integration::get() { return globalHtml5Integration; } - -void emscriptenOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) -{ - int emOutputFlags = (EM_LOG_CONSOLE | EM_LOG_DEMANGLE); - QByteArray localMsg = msg.toLocal8Bit(); - - switch (type) { - case QtDebugMsg: - emscripten_log(emOutputFlags, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); - break; - case QtInfoMsg: - emscripten_log(emOutputFlags, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); - break; - case QtWarningMsg: - emOutputFlags |= EM_LOG_WARN; - emscripten_log(emOutputFlags, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); - break; - case QtCriticalMsg: - emOutputFlags |= EM_LOG_ERROR; - emscripten_log(emOutputFlags, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); - break; - case QtFatalMsg: - emOutputFlags |= EM_LOG_ERROR; - emscripten_log(emOutputFlags, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); - abort(); - } -} - -QHtml5Integration::QHtml5Integration() - : mFontDb(nullptr), - mCompositor(new QHtml5Compositor), - mScreen(new QHtml5Screen(mCompositor)), - m_eventDispatcher(nullptr) -{ - qSetMessagePattern(QString("(%{function}:%{line}) - %{message}")); - qInstallMessageHandler(emscriptenOutput); - - globalHtml5Integration = this; - - updateQScreenAndCanvasRenderSize(); - screenAdded(mScreen); - emscripten_set_resize_callback(0, (void *)this, 1, uiEvent_cb); - - m_eventTranslator = new QHtml5EventTranslator(); - - EM_ASM(// exit app if browser closes - window.onbeforeunload = function () { - Module.browserBeforeUnload(); - }; - ); -} - -QHtml5Integration::~QHtml5Integration() -{ - delete mCompositor; - destroyScreen(mScreen); - delete mFontDb; - delete m_eventTranslator; -} - -void QHtml5Integration::QHtml5BrowserExit() -{ - QCoreApplication *app = QCoreApplication::instance(); - app->quit(); -} - -bool QHtml5Integration::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 *QHtml5Integration::createPlatformWindow(QWindow *window) const -{ - QHtml5Window *w = new QHtml5Window(window, mCompositor, m_backingStores.value(window)); - w->create(); - - return w; -} - -QPlatformBackingStore *QHtml5Integration::createPlatformBackingStore(QWindow *window) const -{ -#ifndef QT_NO_OPENGL - QHtml5BackingStore *backingStore = new QHtml5BackingStore(mCompositor, window); - m_backingStores.insert(window, backingStore); - return backingStore; -#else - return nullptr; -#endif -} - -#ifndef QT_NO_OPENGL -QPlatformOpenGLContext *QHtml5Integration::createPlatformOpenGLContext(QOpenGLContext *context) const -{ - return new QHtml5OpenGLContext(context->format()); -} -#endif - -QPlatformFontDatabase *QHtml5Integration::fontDatabase() const -{ - if (mFontDb == 0) - mFontDb = new QHtml5FontDatabase(); - - return mFontDb; -} - -QAbstractEventDispatcher *QHtml5Integration::createEventDispatcher() const -{ - return new QHtml5EventDispatcher(); -} - -QVariant QHtml5Integration::styleHint(QPlatformIntegration::StyleHint hint) const -{ - return QPlatformIntegration::styleHint(hint); -} - -QStringList QHtml5Integration::themeNames() const -{ - return QStringList() << QLatin1String("html5"); -} - -QPlatformTheme *QHtml5Integration::createPlatformTheme(const QString &name) const -{ - if (name == QLatin1String("html5")) - return new QHtml5Theme(); - return QPlatformIntegration::createPlatformTheme(name); -} - -int QHtml5Integration::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 QHtml5Integration::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); - - QHtml5Screen *screen = QHtml5Integration::get()->mScreen; - QSizeF canvasSize = cssSize * screen->devicePixelRatio(); - - set_canvas_size(canvasSize.width(), canvasSize.height()); - screen->setGeometry(QRect(QPoint(0, 0), cssSize.toSize())); - QHtml5Integration::get()->mCompositor->redrawWindowContent(); -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/html5/qhtml5integration.h b/src/plugins/platforms/html5/qhtml5integration.h deleted file mode 100644 index 727f9ee72b..0000000000 --- a/src/plugins/platforms/html5/qhtml5integration.h +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** 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 QHTML5INTEGRATION_H -#define QHTML5INTEGRATION_H - -#include "qhtml5window.h" - -#include -#include - -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -class QHtml5EventTranslator; -class QHtml5FontDatabase; -class QHtml5Window; -class QHtml5EventDispatcher; -class QHtml5Screen; -class QHtml5Compositor; -class QHtml5BackingStore; - -class QHtml5Integration : public QObject, public QPlatformIntegration -{ - Q_OBJECT -public: - QHtml5Integration(); - ~QHtml5Integration(); - - 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 QHtml5Integration *get(); - QHtml5Screen *screen() { return mScreen; } - QHtml5Compositor *compositor() { return mCompositor; } - QHtml5EventTranslator *eventTranslator() { return m_eventTranslator; } - - static void QHtml5BrowserExit(); - static void updateQScreenAndCanvasRenderSize(); - -private: - mutable QHtml5FontDatabase *mFontDb; - QHtml5Compositor *mCompositor; - mutable QHtml5Screen *mScreen; - mutable QHtml5EventTranslator *m_eventTranslator; - mutable QHtml5EventDispatcher *m_eventDispatcher; - static int uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData); - mutable QHash m_backingStores; -}; - -QT_END_NAMESPACE - -#endif // QHTML5INTEGRATION_H diff --git a/src/plugins/platforms/html5/qhtml5openglcontext.cpp b/src/plugins/platforms/html5/qhtml5openglcontext.cpp deleted file mode 100644 index 16fe73f935..0000000000 --- a/src/plugins/platforms/html5/qhtml5openglcontext.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/**************************************************************************** -** -** 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 "qhtml5openglcontext.h" - -#include - -QT_BEGIN_NAMESPACE - -QHtml5OpenGLContext::QHtml5OpenGLContext(const QSurfaceFormat &format) - : m_requestedFormat(format) -{ - m_requestedFormat.setRenderableType(QSurfaceFormat::OpenGLES); -} - -QHtml5OpenGLContext::~QHtml5OpenGLContext() -{ - if (m_context) - emscripten_webgl_destroy_context(m_context); -} - -void QHtml5OpenGLContext::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 QHtml5OpenGLContext - // instance. - reinterpret_cast(userData)->m_contextLost = true; - return true; - }; - bool capture = true; - emscripten_set_webglcontextlost_callback(canvasId, this, capture, callback); - } -} - -EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QHtml5OpenGLContext::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 QHtml5OpenGLContext::format() const -{ - return m_requestedFormat; -} - -GLuint QHtml5OpenGLContext::defaultFramebufferObject(QPlatformSurface *surface) const -{ - return QPlatformOpenGLContext::defaultFramebufferObject(surface); -} - -bool QHtml5OpenGLContext::makeCurrent(QPlatformSurface *surface) -{ - maybeRecreateEmscriptenContext(surface); - - return emscripten_webgl_make_context_current(m_context) == EMSCRIPTEN_RESULT_SUCCESS; -} - -void QHtml5OpenGLContext::swapBuffers(QPlatformSurface *surface) -{ - Q_UNUSED(surface); - // No swapbuffers on WebGl -} - -void QHtml5OpenGLContext::doneCurrent() -{ - // No doneCurrent on WebGl -} - -bool QHtml5OpenGLContext::isSharing() const -{ - return false; -} - -bool QHtml5OpenGLContext::isValid() const -{ - return (m_contextLost == false); -} - -QFunctionPointer QHtml5OpenGLContext::getProcAddress(const char *procName) -{ - return reinterpret_cast(eglGetProcAddress(procName)); -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/html5/qhtml5openglcontext.h b/src/plugins/platforms/html5/qhtml5openglcontext.h deleted file mode 100644 index 6503cf3ab9..0000000000 --- a/src/plugins/platforms/html5/qhtml5openglcontext.h +++ /dev/null @@ -1,63 +0,0 @@ -/**************************************************************************** -** -** 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 - -#include -#include - -QT_BEGIN_NAMESPACE - -class QHtml5OpenGLContext : public QPlatformOpenGLContext -{ -public: - QHtml5OpenGLContext(const QSurfaceFormat &format); - ~QHtml5OpenGLContext(); - - 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/html5/qhtml5screen.cpp b/src/plugins/platforms/html5/qhtml5screen.cpp deleted file mode 100644 index b0f64044a4..0000000000 --- a/src/plugins/platforms/html5/qhtml5screen.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/**************************************************************************** -** -** 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 "qhtml5screen.h" -#include "qhtml5window.h" -#include "qhtml5compositor.h" - -#include -#ifndef QT_NO_OPENGL -# include -#endif -#include -#include -#include -#include -#include - - -QT_BEGIN_NAMESPACE - -QHtml5Screen::QHtml5Screen(QHtml5Compositor *compositor) - : mCompositor(compositor) - , m_depth(32) - , m_format(QImage::Format_RGB32) -{ - mCompositor->setScreen(this); -} - -QHtml5Screen::~QHtml5Screen() -{ - -} - -QRect QHtml5Screen::geometry() const -{ - return m_geometry; -} - -int QHtml5Screen::depth() const -{ - return m_depth; -} - -QImage::Format QHtml5Screen::format() const -{ - return m_format; -} - -qreal QHtml5Screen::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 *QHtml5Screen::cursor() const -{ - return const_cast(&m_cursor); -} - -void QHtml5Screen::resizeMaximizedWindows() -{ - QList windows = QGuiApplication::allWindows(); - // 'screen()' still has the old geometry info while 'this' has the new geometry info - const QRect oldGeometry = screen()->geometry(); - const QRect oldAvailableGeometry = screen()->availableGeometry(); - - const QRect newGeometry = deviceIndependentGeometry(); - const QRect newAvailableGeometry = QHighDpi::fromNative(availableGeometry(), QHighDpiScaling::factor(this), newGeometry.topLeft()); - - // make sure maximized and fullscreen windows are updated - for (int i = 0; i < windows.size(); ++i) { - QWindow *w = windows.at(i); - - // Skip non-platform windows, e.g., offscreen windows. - if (!w->handle()) - continue; - - if (platformScreenForWindow(w) != this) - continue; - - if (w->windowState() & Qt::WindowMaximized || w->geometry() == oldAvailableGeometry) - w->setGeometry(newAvailableGeometry); - - else if (w->windowState() & Qt::WindowFullScreen || w->geometry() == oldGeometry) - w->setGeometry(newGeometry); - } -} - -QWindow *QHtml5Screen::topWindow() const -{ - return mCompositor->keyWindow(); -} - -QWindow *QHtml5Screen::topLevelAt(const QPoint & p) const -{ - return mCompositor->windowAt(p); -} - -void QHtml5Screen::invalidateSize() -{ - m_geometry = QRect(); -} - -void QHtml5Screen::setGeometry(const QRect &rect) -{ - m_geometry = rect; - QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry()); - resizeMaximizedWindows(); -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/html5/qhtml5screen.h b/src/plugins/platforms/html5/qhtml5screen.h deleted file mode 100644 index d3b81f234e..0000000000 --- a/src/plugins/platforms/html5/qhtml5screen.h +++ /dev/null @@ -1,85 +0,0 @@ -/**************************************************************************** -** -** 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 QHTML5SCREEN_H -#define QHTML5SCREEN_H - -#include "qhtml5cursor.h" - -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -class QPlatformOpenGLContext; -class QHtml5Window; -class QHtml5BackingStore; -class QHtml5Compositor; -class QOpenGLContext; - -class QHtml5Screen : public QObject, public QPlatformScreen -{ - Q_OBJECT -public: - - QHtml5Screen(QHtml5Compositor *compositor); - ~QHtml5Screen(); - - 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: - bool mUpdatePending; - -private: - QHtml5Compositor *mCompositor; - - QRect m_geometry = QRect(0, 0, 100, 100); - int m_depth; - QImage::Format m_format; - QHtml5Cursor m_cursor; -}; - -QT_END_NAMESPACE -#endif // QHTML5SCREEN_H diff --git a/src/plugins/platforms/html5/qhtml5stylepixmaps_p.h b/src/plugins/platforms/html5/qhtml5stylepixmaps_p.h deleted file mode 100644 index 809fb8e5af..0000000000 --- a/src/plugins/platforms/html5/qhtml5stylepixmaps_p.h +++ /dev/null @@ -1,183 +0,0 @@ -/**************************************************************************** -** -** 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 QHTML5STYLEPIXMAPS_P_H -#define QHTML5STYLEPIXMAPS_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 // QHTML5STYLEPIXMAPS_P_H diff --git a/src/plugins/platforms/html5/qhtml5theme.cpp b/src/plugins/platforms/html5/qhtml5theme.cpp deleted file mode 100644 index 31edcfb0cd..0000000000 --- a/src/plugins/platforms/html5/qhtml5theme.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/**************************************************************************** -** -** 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 "qhtml5theme.h" -#include - -QT_BEGIN_NAMESPACE - -QHtml5Theme::QHtml5Theme() -{ - -} - -QHtml5Theme::~QHtml5Theme() -{ - -} - -QVariant QHtml5Theme::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/html5/qhtml5theme.h b/src/plugins/platforms/html5/qhtml5theme.h deleted file mode 100644 index 7f3d58e171..0000000000 --- a/src/plugins/platforms/html5/qhtml5theme.h +++ /dev/null @@ -1,56 +0,0 @@ -/**************************************************************************** -** -** 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 QHTML5THEME_H -#define QHTML5THEME_H - -#include - -QT_BEGIN_NAMESPACE - -class QHtml5EventTranslator; -class QHtml5FontDatabase; -class QHtml5Window; -class QHtml5EventDispatcher; -class QHtml5Screen; -class QHtml5Compositor; -class QHtml5BackingStore; - -class QHtml5Theme : public QPlatformTheme -{ -public: - QHtml5Theme(); - ~QHtml5Theme(); - - QVariant themeHint(ThemeHint hint) const override; -}; - -QT_END_NAMESPACE - -#endif // QHTML5THEME_H diff --git a/src/plugins/platforms/html5/qhtml5window.cpp b/src/plugins/platforms/html5/qhtml5window.cpp deleted file mode 100644 index 7c4546a0e6..0000000000 --- a/src/plugins/platforms/html5/qhtml5window.cpp +++ /dev/null @@ -1,406 +0,0 @@ -/**************************************************************************** -** -** 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 -#include -#include -#include -#include - -#include "qhtml5window.h" -#include "qhtml5screen.h" -#include "qhtml5compositor.h" -#include "qhtml5eventdispatcher.h" - -#include - -#include - -Q_GUI_EXPORT int qt_defaultDpiX(); - -QT_BEGIN_NAMESPACE - -QHtml5Window::QHtml5Window(QWindow *w, QHtml5Compositor* compositor, QHtml5BackingStore *backingStore) - : QPlatformWindow(w), - mWindow(w), - mCompositor(compositor), - mBackingStore(backingStore) -{ - needsCompositor = w->surfaceType() != QSurface::OpenGLSurface; - static int serialNo = 0; - m_winid = ++serialNo; - qWarning("QHtml5Window %p: %p 0x%x\n", this, w, uint(m_winid)); - - mCompositor->addWindow(this); - - // Pure OpenGL windows draw directly using egl, disable the compositor. - mCompositor->setEnabled(w->surfaceType() != QSurface::OpenGLSurface); -} - -QHtml5Window::~QHtml5Window() -{ - mCompositor->removeWindow(this); -} - -void QHtml5Window::create() -{ - 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()); - hasTitle = window()->flags().testFlag(Qt::WindowTitleHint) && needsCompositor; - - if (window()->isTopLevel()) - setWindowIcon(window()->icon()); - mNormalGeometry = rect; -} - -QHtml5Screen *QHtml5Window::platformScreen() const -{ - return static_cast(window()->screen()->handle()); -} - -void QHtml5Window::setGeometry(const QRect &rect) -{ - QRect r = rect; - if (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 QHtml5Window::setVisible(bool visible) -{ - QRect newGeom; - - if (visible) { - bool convOk = false; - static bool envDisableForceFullScreen = qEnvironmentVariableIntValue("QT_QPA_HTML5_FORCE_FULLSCREEN", &convOk) == 0 && convOk; - - const bool forceFullScreen = /*!envDisableForceFullScreen && */!needsCompositor;//make gl apps fullscreen for now - - if (forceFullScreen || (mWindowState & Qt::WindowFullScreen)) - newGeom = platformScreen()->geometry(); - else if (mWindowState & Qt::WindowMaximized) - newGeom = platformScreen()->availableGeometry(); - } - QPlatformWindow::setVisible(visible); - - mCompositor->setVisible(this, visible); - - if (!newGeom.isEmpty()) - setGeometry(newGeom); // may or may not generate an expose - - QWindowSystemInterface::handleExposeEvent(window(), visible ? QRect(QPoint(), geometry().size()) : QRect()); - QWindowSystemInterface::flushWindowSystemEvents(); - invalidate(); -} - -QMargins QHtml5Window::frameMargins() const -{ - int border = hasTitle ? 4. * (qreal(qt_defaultDpiX()) / 96.0) : 0; - int titleBarHeight = hasTitle ? titleHeight() : 0; - - QMargins margins; - margins.setLeft(border); - margins.setRight(border); - margins.setTop(2*border + titleBarHeight); - margins.setBottom(border); - - return margins; -} - -void QHtml5Window::raise() -{ - mCompositor->raise(this); - QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), geometry().size())); - invalidate(); -} - -void QHtml5Window::lower() -{ - mCompositor->lower(this); - QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), geometry().size())); - invalidate(); -} - -WId QHtml5Window::winId() const -{ - return m_winid; -} - -void QHtml5Window::propagateSizeHints() -{ -// get rid of base class warning -} - -void QHtml5Window::injectMousePressed(const QPoint &local, const QPoint &global, - Qt::MouseButton button, Qt::KeyboardModifiers mods) -{ - Q_UNUSED(local); - Q_UNUSED(mods); - - if (!hasTitle || button != Qt::LeftButton) - return; - - if (maxButtonRect().contains(global)) - mActiveControl = QHtml5Compositor::SC_TitleBarMaxButton; - else if (minButtonRect().contains(global)) - mActiveControl = QHtml5Compositor::SC_TitleBarMinButton; - else if (closeButtonRect().contains(global)) - mActiveControl = QHtml5Compositor::SC_TitleBarCloseButton; - else if (normButtonRect().contains(global)) - mActiveControl = QHtml5Compositor::SC_TitleBarNormalButton; - - invalidate(); -} - -void QHtml5Window::injectMouseReleased(const QPoint &local, const QPoint &global, - Qt::MouseButton button, Qt::KeyboardModifiers mods) -{ - Q_UNUSED(local); - Q_UNUSED(mods); - - if (!hasTitle || button != Qt::LeftButton) - return; - - if (closeButtonRect().contains(global) && mActiveControl == QHtml5Compositor::SC_TitleBarCloseButton) - window()->close(); - - if (maxButtonRect().contains(global) && mActiveControl == QHtml5Compositor::SC_TitleBarMaxButton) { - window()->setWindowState(Qt::WindowMaximized); - platformScreen()->resizeMaximizedWindows(); - } - - if (normButtonRect().contains(global) && mActiveControl == QHtml5Compositor::SC_TitleBarNormalButton) { - window()->setWindowState(Qt::WindowNoState); - setGeometry(normalGeometry()); - } - - mActiveControl = QHtml5Compositor::SC_None; - - invalidate(); -} - -int QHtml5Window::titleHeight() const -{ - return 18. * (qreal(qt_defaultDpiX()) / 96.0);//dpiScaled(18.); -} - -int QHtml5Window::borderWidth() const -{ - return 4. * (qreal(qt_defaultDpiX()) / 96.0);// dpiScaled(4.); -} - -QRegion QHtml5Window::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 QHtml5Window::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 QHtml5Window::isPointOnTitle(QPoint point) const -{ - bool ok = titleGeometry().contains(point); - return ok; -} - -bool QHtml5Window::isPointOnResizeRegion(QPoint point) const -{ - return resizeRegion().contains(point); -} - -QHtml5Window::ResizeMode QHtml5Window::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 QHtml5Window *window, QHtml5Compositor::SubControls subControl) -{ - QHtml5Compositor::QHtml5TitleBarOptions options = QHtml5Compositor::makeTitleBarOptions(window); - - QRect r = QHtml5Compositor::titlebarRect(options, subControl); - r.translate(window->window()->frameGeometry().x(), window->window()->frameGeometry().y()); - - return r; -} - -QRect QHtml5Window::maxButtonRect() const -{ - return getSubControlRect(this, QHtml5Compositor::SC_TitleBarMaxButton); -} - -QRect QHtml5Window::minButtonRect() const -{ - return getSubControlRect(this, QHtml5Compositor::SC_TitleBarMinButton); -} - -QRect QHtml5Window::closeButtonRect() const -{ - return getSubControlRect(this, QHtml5Compositor::SC_TitleBarCloseButton); -} - -QRect QHtml5Window::normButtonRect() const -{ - return getSubControlRect(this, QHtml5Compositor::SC_TitleBarNormalButton); -} - -QRect QHtml5Window::sysMenuRect() const -{ - return getSubControlRect(this, QHtml5Compositor::SC_TitleBarSysMenu); -} - -QRegion QHtml5Window::titleControlRegion() const -{ - QRegion result; - result += closeButtonRect(); - result += minButtonRect(); - result += maxButtonRect(); - result += sysMenuRect(); - - return result; -} - -void QHtml5Window::invalidate() -{ - mCompositor->requestRedraw(); -} - -QHtml5Compositor::SubControls QHtml5Window::activeSubControl() const -{ - return mActiveControl; -} - -void QHtml5Window::setWindowState(Qt::WindowStates states) -{ - mWindowState = Qt::WindowNoState; - if (states & Qt::WindowMinimized) - mWindowState = Qt::WindowMinimized; - else if (states & Qt::WindowFullScreen) - mWindowState = Qt::WindowFullScreen; - else if (states & Qt::WindowMaximized) - mWindowState = Qt::WindowMaximized; -} - -QRect QHtml5Window::normalGeometry() const -{ - return mNormalGeometry; -} - -qreal QHtml5Window::devicePixelRatio() const -{ - return screen()->devicePixelRatio(); -} - -void QHtml5Window::requestUpdate() -{ - QPointer windowPointer(window()); - bool registered = QHtml5EventDispatcher::registerRequestUpdateCallback([=](){ - if (windowPointer.isNull()) - return; - - QWindowPrivate *wp = static_cast(QObjectPrivate::get(windowPointer)); - wp->deliverUpdateRequest(); - }); - - if (!registered) - QPlatformWindow::requestUpdate(); -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/html5/qhtml5window.h b/src/plugins/platforms/html5/qhtml5window.h deleted file mode 100644 index 709cb6c910..0000000000 --- a/src/plugins/platforms/html5/qhtml5window.h +++ /dev/null @@ -1,125 +0,0 @@ -/**************************************************************************** -** -** 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 QHTML5WINDOW_H -#define QHTML5WINDOW_H - -#include "qhtml5integration.h" -#include -#include -#include "qhtml5backingstore.h" -#include "qhtml5screen.h" -#include "qhtml5compositor.h" - -QT_BEGIN_NAMESPACE - -class QHtml5Compositor; - -class QHtml5Window : public QPlatformWindow -{ -public: - enum ResizeMode { - ResizeNone, - ResizeTopLeft, - ResizeTop, - ResizeTopRight, - ResizeRight, - ResizeBottomRight, - ResizeBottom, - ResizeBottomLeft, - ResizeLeft - }; - - QHtml5Window(QWindow *w, QHtml5Compositor *compositor, QHtml5BackingStore *backingStore); - ~QHtml5Window(); - - void create(); - - 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; - - QHtml5Screen *platformScreen() const; - void setBackingStore(QHtml5BackingStore *store) { mBackingStore = store; } - QHtml5BackingStore *backingStore() const { return mBackingStore; } - QWindow *window() const { return mWindow; } - - 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; - QHtml5Compositor::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 QHtml5Screen; - - QWindow* mWindow = nullptr; - QHtml5Compositor *mCompositor = nullptr; - QHtml5BackingStore *mBackingStore = nullptr; - QRect mNormalGeometry {0, 0, 0 ,0}; - QRect mOldGeometry; - Qt::WindowFlags mWindowFlags = Qt::Window; - Qt::WindowState mWindowState = Qt::WindowNoState; - QHtml5Compositor::SubControls mActiveControl = QHtml5Compositor::SC_None; - WId m_winid = 0; - bool hasTitle = false; - bool needsCompositor = false; -}; -QT_END_NAMESPACE -#endif // QHTML5WINDOW_H diff --git a/src/plugins/platforms/html5/qtloader.js b/src/plugins/platforms/html5/qtloader.js deleted file mode 100644 index 2b8ae40fd9..0000000000 --- a/src/plugins/platforms/html5/qtloader.js +++ /dev/null @@ -1,516 +0,0 @@ -/**************************************************************************** -** -** 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 : -// 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 : -// Restart attempts limit. The default is 10. -// stdoutEnabled : -// stderrEnabled : -// environment : -// 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 = '

${loadingState}...

'; - 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 = ` ${crashSymbols[symbolIndex]} ` - 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/html5/qtlogo.svg b/src/plugins/platforms/html5/qtlogo.svg deleted file mode 100644 index cb8989bb79..0000000000 --- a/src/plugins/platforms/html5/qtlogo.svg +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro index 17e617931b..806fb23812 100644 --- a/src/plugins/platforms/platforms.pro +++ b/src/plugins/platforms/platforms.pro @@ -46,7 +46,7 @@ haiku { SUBDIRS += haiku } -emscripten: SUBDIRS = html5 +wasm: SUBDIRS = wasm qtConfig(mirclient): SUBDIRS += mirclient diff --git a/src/plugins/platforms/wasm/fonts/DejaVuSans.ttf b/src/plugins/platforms/wasm/fonts/DejaVuSans.ttf new file mode 100644 index 0000000000..7e411a71be Binary files /dev/null and b/src/plugins/platforms/wasm/fonts/DejaVuSans.ttf differ diff --git a/src/plugins/platforms/wasm/fonts/Vera.ttf b/src/plugins/platforms/wasm/fonts/Vera.ttf new file mode 100644 index 0000000000..58cd6b5e61 Binary files /dev/null and b/src/plugins/platforms/wasm/fonts/Vera.ttf differ diff --git a/src/plugins/platforms/wasm/fonts/wasmfonts.qrc b/src/plugins/platforms/wasm/fonts/wasmfonts.qrc new file mode 100644 index 0000000000..d017417ee5 --- /dev/null +++ b/src/plugins/platforms/wasm/fonts/wasmfonts.qrc @@ -0,0 +1,6 @@ + + + Vera.ttf + DejaVuSans.ttf + + diff --git a/src/plugins/platforms/wasm/main.cpp b/src/plugins/platforms/wasm/main.cpp new file mode 100644 index 0000000000..1f629d9db8 --- /dev/null +++ b/src/plugins/platforms/wasm/main.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 +#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("webassembly"), 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..2b8ae40fd9 --- /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 : +// 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 : +// Restart attempts limit. The default is 10. +// stdoutEnabled : +// stderrEnabled : +// environment : +// 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 = '

${loadingState}...

'; + 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 = ` ${crashSymbols[symbolIndex]} ` + 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 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/plugins/platforms/wasm/qwasmbackingstore.cpp b/src/plugins/platforms/wasm/qwasmbackingstore.cpp new file mode 100644 index 0000000000..8b33f62ce0 --- /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 +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +QWasmBackingStore::QWasmBackingStore(QWasmCompositor *compositor, QWindow *window) + : QPlatformBackingStore(window) + , mCompositor(compositor) + , mTexture(new QOpenGLTexture(QOpenGLTexture::Target2D)) +{ + QWasmWindow *wasmWindow = static_cast(window->handle()); + if (wasmWindow) + wasmWindow->setBackingStore(this); +} + +QWasmBackingStore::~QWasmBackingStore() +{ +} + +QPaintDevice *QWasmBackingStore::paintDevice() +{ + return &mImage; +} + +void QWasmBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) +{ + Q_UNUSED(window); + Q_UNUSED(region); + Q_UNUSED(offset); + + mDirty |= region; + mCompositor->requestRedraw(); +} + +void QWasmBackingStore::updateTexture() +{ + if (mDirty.isNull()) + return; + + if (!mTexture->isCreated()) { + mTexture->setMinificationFilter(QOpenGLTexture::Nearest); + mTexture->setMagnificationFilter(QOpenGLTexture::Nearest); + mTexture->setWrapMode(QOpenGLTexture::ClampToEdge); + mTexture->setData(mImage, QOpenGLTexture::DontGenerateMipMaps); + mTexture->create(); + } + mTexture->bind(); + + QRegion fixed; + QRect imageRect = mImage.rect(); + + for (const QRect &rect : mDirty) { + + // Convert device-independent dirty region to device region + qreal dpr = mImage.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, + mImage.constScanLine(rect.y())); + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, GL_UNSIGNED_BYTE, + mImage.copy(rect).constBits()); + } + } + /* End of code taken from QEGLPlatformBackingStore */ + + mDirty = QRegion(); +} + +void QWasmBackingStore::beginPaint(const QRegion ®ion) +{ + mDirty |= region; + // Keep backing store device pixel ratio in sync with window + if (mImage.devicePixelRatio() != window()->devicePixelRatio()) + resize(backingStore()->size(), backingStore()->staticContents()); + + QPainter painter(&mImage); + 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) + + mImage = QImage(size * window()->devicePixelRatio(), QImage::Format_RGB32); + mImage.setDevicePixelRatio(window()->devicePixelRatio()); + + if (mTexture->isCreated()) + mTexture->destroy(); +} + +QImage QWasmBackingStore::toImage() const +{ + // used by QPlatformBackingStore::composeAndFlush + return mImage; +} + +const QImage &QWasmBackingStore::getImageRef() const +{ + return mImage; +} + +const QOpenGLTexture* QWasmBackingStore::getUpdatedTexture() +{ + updateTexture(); + return mTexture.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..9f8e503b88 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmbackingstore.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** 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 +#include + +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 ®ion, 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 *mCompositor; + + QImage mImage; + + QScopedPointer mTexture; + + QRegion mDirty; + +}; + +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..925201b1b9 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -0,0 +1,728 @@ +/**************************************************************************** +** +** 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 + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +Q_GUI_EXPORT int qt_defaultDpiX(); + +QWasmCompositedWindow::QWasmCompositedWindow() + : window(0) + , parentWindow(nullptr) + , flushPending(false) + , visible(false) +{ +} + +QWasmCompositor::QWasmCompositor() + : m_frameBuffer(nullptr) + , mBlitter(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) +{ + mScreen = 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 + 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]; + 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 ®ion) +{ + 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( + 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(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 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; idrawLines(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() || !mScreen) + return; + + QWasmWindow *someWindow = nullptr; + + foreach (QWasmWindow *window, m_windowStack) { + if (window->window()->surfaceClass() == QSurface::Window + && qt_window_private(static_cast(window->window()))->receivedExpose) { + someWindow = window; + break; + } + } + + if (!someWindow) + return; + + if (mContext.isNull()) { + mContext.reset(new QOpenGLContext()); + //mContext->setFormat(mScreen->format()); + mContext->setScreen(mScreen->screen()); + mContext->create(); + } + + mContext->makeCurrent(someWindow->window()); + + if (!mBlitter->isCreated()) + mBlitter->create(); + + qreal dpr = mScreen->devicePixelRatio(); + glViewport(0, 0, mScreen->geometry().width() * dpr, mScreen->geometry().height() *dpr); + + mContext->functions()->glClearColor(0.2, 0.2, 0.2, 1.0); + mContext->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + mBlitter->bind(); + mBlitter->setRedBlueSwizzle(true); + + foreach (QWasmWindow *window, m_windowStack) { + QWasmCompositedWindow &compositedWindow = m_compositedWindows[window]; + + if (!compositedWindow.visible) + continue; + + drawWindow(mBlitter.data(), mScreen, window); + } + + mBlitter->release(); + + if (someWindow && someWindow->window()->surfaceType() == QSurface::OpenGLSurface) + mContext->swapBuffers(someWindow->window()); +} + +void QWasmCompositor::notifyTopWindowChanged(QWasmWindow* window) +{ + QWindow *modalWindow; + bool blocked = QGuiApplicationPrivate::instance()->isWindowBlocked(window->window(), &modalWindow); + + if (blocked) { + raise(static_cast(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..06ddb88270 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmcompositor.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** 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 +#include + +#include +#include +#include +#include +#include + +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 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 ®ion); + + 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 flush2(const QRegion ®ion); + 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(); +private: + QImage *m_frameBuffer; + QScopedPointer mContext; + QScopedPointer mBlitter; + QWasmScreen *mScreen; + + QHash m_compositedWindows; + QList m_windowStack; + QRegion 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..2a4ca2067f --- /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 + +#include + +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; // ## missing? + 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..ef3ef80126 --- /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 + +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..a1c206e441 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** 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 +#include + +#include + +class QWasmEventDispatcherPrivate : public QUnixEventDispatcherQPAPrivate +{ + +}; + +QWasmEventDispatcher *g_htmlEventDispatcher; + +QWasmEventDispatcher::QWasmEventDispatcher(QObject *parent) + : QUnixEventDispatcherQPA(parent) +{ + + g_htmlEventDispatcher = this; +} + +QWasmEventDispatcher::~QWasmEventDispatcher() +{ + g_htmlEventDispatcher = nullptr; +} + +bool QWasmEventDispatcher::registerRequestUpdateCallback(std::function 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(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(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(eventDispatcher); + that->m_currentTargetTime = std::numeric_limits::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..aa562ebad2 --- /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 +#include +#include + +QT_BEGIN_NAMESPACE + +class QWasmEventDispatcherPrivate; + +class QWasmEventDispatcher : public QUnixEventDispatcherQPA +{ + Q_DECLARE_PRIVATE(QWasmEventDispatcher) +public: + explicit QWasmEventDispatcher(QObject *parent = 0); + ~QWasmEventDispatcher(); + + static bool registerRequestUpdateCallback(std::function 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::max(); + QVector> 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..67669b1482 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp @@ -0,0 +1,524 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include +#include +#include + +#include + +#include + +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 +QFlags QWasmEventTranslator::translatKeyModifier(const Event *event) +{ + QFlags 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 QWasmEventTranslator::translateKeyboardEventModifier(const EmscriptenKeyboardEvent *keyEvent) +{ + QFlags keyModifier = translatKeyModifier(keyEvent); + if (keyEvent->location == DOM_KEY_LOCATION_NUMPAD) { + keyModifier |= Qt::KeypadModifier; + } + + return keyModifier; +} + +QFlags 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( + 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: + 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(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(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(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( + 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 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 keyModifier = translatKeyModifier(touchEvent); + + if (eventType != EMSCRIPTEN_EVENT_TOUCHCANCEL) + QWindowSystemInterface::handleTouchEvent(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..a5e6b24836 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmeventtranslator.h @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include +#include "qwasmwindow.h" +#include + +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 + static QFlags translatKeyModifier(const Event *event); + static QFlags translateKeyboardEventModifier(const EmscriptenKeyboardEvent *keyEvent); + static QFlags 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..1b44675066 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmfontdatabase.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** 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 +QT_BEGIN_NAMESPACE + +void QWasmFontDatabase::populateFontDatabase() +{ + Q_INIT_RESOURCE(wasmfonts); + + // 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 + +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..196b2c6575 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** 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 +#endif +#include +#include +#include +#include + +#include + +// 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; } + +void emscriptenOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + int emOutputFlags = (EM_LOG_CONSOLE | EM_LOG_DEMANGLE); + QByteArray localMsg = msg.toLocal8Bit(); + + switch (type) { + case QtDebugMsg: + emscripten_log(emOutputFlags, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + break; + case QtInfoMsg: + emscripten_log(emOutputFlags, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + break; + case QtWarningMsg: + emOutputFlags |= EM_LOG_WARN; + emscripten_log(emOutputFlags, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + break; + case QtCriticalMsg: + emOutputFlags |= EM_LOG_ERROR; + emscripten_log(emOutputFlags, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + break; + case QtFatalMsg: + emOutputFlags |= EM_LOG_ERROR; + emscripten_log(emOutputFlags, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + abort(); + } +} + +QWasmIntegration::QWasmIntegration() + : mFontDb(nullptr), + mCompositor(new QWasmCompositor), + mScreen(new QWasmScreen(mCompositor)), + m_eventDispatcher(nullptr) +{ + qSetMessagePattern(QString("(%{function}:%{line}) - %{message}")); + qInstallMessageHandler(emscriptenOutput); + + globalHtml5Integration = this; + + updateQScreenAndCanvasRenderSize(); + screenAdded(mScreen); + 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 mCompositor; + destroyScreen(mScreen); + delete mFontDb; + 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 +{ + QWasmWindow *w = new QWasmWindow(window, mCompositor, m_backingStores.value(window)); + w->create(); + + return w; +} + +QPlatformBackingStore *QWasmIntegration::createPlatformBackingStore(QWindow *window) const +{ +#ifndef QT_NO_OPENGL + QWasmBackingStore *backingStore = new QWasmBackingStore(mCompositor, 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 (mFontDb == 0) + mFontDb = new QWasmFontDatabase(); + + return mFontDb; +} + +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()->mScreen; + QSizeF canvasSize = cssSize * screen->devicePixelRatio(); + + set_canvas_size(canvasSize.width(), canvasSize.height()); + screen->setGeometry(QRect(QPoint(0, 0), cssSize.toSize())); + QWasmIntegration::get()->mCompositor->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..a4b3ca939d --- /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 +#include + +#include + +#include +#include + +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 mScreen; } + QWasmCompositor *compositor() { return mCompositor; } + QWasmEventTranslator *eventTranslator() { return m_eventTranslator; } + + static void QWasmBrowserExit(); + static void updateQScreenAndCanvasRenderSize(); + +private: + mutable QWasmFontDatabase *mFontDb; + QWasmCompositor *mCompositor; + mutable QWasmScreen *mScreen; + mutable QWasmEventTranslator *m_eventTranslator; + mutable QWasmEventDispatcher *m_eventDispatcher; + static int uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData); + mutable QHash 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 + +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(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(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 + +#include +#include + +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..8667191033 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmscreen.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** 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 +#ifndef QT_NO_OPENGL +# include +#endif +#include +#include +#include +#include +#include + + +QT_BEGIN_NAMESPACE + +QWasmScreen::QWasmScreen(QWasmCompositor *compositor) + : mCompositor(compositor) + , m_depth(32) + , m_format(QImage::Format_RGB32) +{ + mCompositor->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(&m_cursor); +} + +void QWasmScreen::resizeMaximizedWindows() +{ + QList windows = QGuiApplication::allWindows(); + // 'screen()' still has the old geometry info while 'this' has the new geometry info + const QRect oldGeometry = screen()->geometry(); + const QRect oldAvailableGeometry = screen()->availableGeometry(); + + const QRect newGeometry = deviceIndependentGeometry(); + const QRect newAvailableGeometry = QHighDpi::fromNative(availableGeometry(), QHighDpiScaling::factor(this), newGeometry.topLeft()); + + // make sure maximized and fullscreen windows are updated + for (int i = 0; i < windows.size(); ++i) { + QWindow *w = windows.at(i); + + // Skip non-platform windows, e.g., offscreen windows. + if (!w->handle()) + continue; + + if (platformScreenForWindow(w) != this) + continue; + + if (w->windowState() & Qt::WindowMaximized || w->geometry() == oldAvailableGeometry) + w->setGeometry(newAvailableGeometry); + + else if (w->windowState() & Qt::WindowFullScreen || w->geometry() == oldGeometry) + w->setGeometry(newGeometry); + } +} + +QWindow *QWasmScreen::topWindow() const +{ + return mCompositor->keyWindow(); +} + +QWindow *QWasmScreen::topLevelAt(const QPoint & p) const +{ + return mCompositor->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..1777860c60 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmscreen.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** 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 + +#include +#include + +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: + bool mUpdatePending; + +private: + QWasmCompositor *mCompositor; + + 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..793c252adc --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmtheme.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** 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 + +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 + +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..9283c20d00 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmwindow.cpp @@ -0,0 +1,406 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include +#include + +#include "qwasmwindow.h" +#include "qwasmscreen.h" +#include "qwasmcompositor.h" +#include "qwasmeventdispatcher.h" + +#include + +#include + +Q_GUI_EXPORT int qt_defaultDpiX(); + +QT_BEGIN_NAMESPACE + +QWasmWindow::QWasmWindow(QWindow *w, QWasmCompositor* compositor, QWasmBackingStore *backingStore) + : QPlatformWindow(w), + mWindow(w), + mCompositor(compositor), + mBackingStore(backingStore) +{ + needsCompositor = w->surfaceType() != QSurface::OpenGLSurface; + static int serialNo = 0; + m_winid = ++serialNo; + qWarning("QWasmWindow %p: %p 0x%x\n", this, w, uint(m_winid)); + + mCompositor->addWindow(this); + + // Pure OpenGL windows draw directly using egl, disable the compositor. + mCompositor->setEnabled(w->surfaceType() != QSurface::OpenGLSurface); +} + +QWasmWindow::~QWasmWindow() +{ + mCompositor->removeWindow(this); +} + +void QWasmWindow::create() +{ + 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()); + hasTitle = window()->flags().testFlag(Qt::WindowTitleHint) && needsCompositor; + + if (window()->isTopLevel()) + setWindowIcon(window()->icon()); + mNormalGeometry = rect; +} + +QWasmScreen *QWasmWindow::platformScreen() const +{ + return static_cast(window()->screen()->handle()); +} + +void QWasmWindow::setGeometry(const QRect &rect) +{ + QRect r = rect; + if (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) { + bool convOk = false; + static bool envDisableForceFullScreen = qEnvironmentVariableIntValue("QT_QPA_WEBASSEMBLY_FORCE_FULLSCREEN", &convOk) == 0 && convOk; + + const bool forceFullScreen = /*!envDisableForceFullScreen && */!needsCompositor;//make gl apps fullscreen for now + + if (forceFullScreen || (mWindowState & Qt::WindowFullScreen)) + newGeom = platformScreen()->geometry(); + else if (mWindowState & Qt::WindowMaximized) + newGeom = platformScreen()->availableGeometry(); + } + QPlatformWindow::setVisible(visible); + + mCompositor->setVisible(this, visible); + + if (!newGeom.isEmpty()) + setGeometry(newGeom); // may or may not generate an expose + + QWindowSystemInterface::handleExposeEvent(window(), visible ? QRect(QPoint(), geometry().size()) : QRect()); + QWindowSystemInterface::flushWindowSystemEvents(); + invalidate(); +} + +QMargins QWasmWindow::frameMargins() const +{ + int border = hasTitle ? 4. * (qreal(qt_defaultDpiX()) / 96.0) : 0; + int titleBarHeight = hasTitle ? titleHeight() : 0; + + QMargins margins; + margins.setLeft(border); + margins.setRight(border); + margins.setTop(2*border + titleBarHeight); + margins.setBottom(border); + + return margins; +} + +void QWasmWindow::raise() +{ + mCompositor->raise(this); + QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), geometry().size())); + invalidate(); +} + +void QWasmWindow::lower() +{ + mCompositor->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 (!hasTitle || button != Qt::LeftButton) + return; + + if (maxButtonRect().contains(global)) + mActiveControl = QWasmCompositor::SC_TitleBarMaxButton; + else if (minButtonRect().contains(global)) + mActiveControl = QWasmCompositor::SC_TitleBarMinButton; + else if (closeButtonRect().contains(global)) + mActiveControl = QWasmCompositor::SC_TitleBarCloseButton; + else if (normButtonRect().contains(global)) + mActiveControl = 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 (!hasTitle || button != Qt::LeftButton) + return; + + if (closeButtonRect().contains(global) && mActiveControl == QWasmCompositor::SC_TitleBarCloseButton) + window()->close(); + + if (maxButtonRect().contains(global) && mActiveControl == QWasmCompositor::SC_TitleBarMaxButton) { + window()->setWindowState(Qt::WindowMaximized); + platformScreen()->resizeMaximizedWindows(); + } + + if (normButtonRect().contains(global) && mActiveControl == QWasmCompositor::SC_TitleBarNormalButton) { + window()->setWindowState(Qt::WindowNoState); + setGeometry(normalGeometry()); + } + + mActiveControl = 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() +{ + mCompositor->requestRedraw(); +} + +QWasmCompositor::SubControls QWasmWindow::activeSubControl() const +{ + return mActiveControl; +} + +void QWasmWindow::setWindowState(Qt::WindowStates states) +{ + mWindowState = Qt::WindowNoState; + if (states & Qt::WindowMinimized) + mWindowState = Qt::WindowMinimized; + else if (states & Qt::WindowFullScreen) + mWindowState = Qt::WindowFullScreen; + else if (states & Qt::WindowMaximized) + mWindowState = Qt::WindowMaximized; +} + +QRect QWasmWindow::normalGeometry() const +{ + return mNormalGeometry; +} + +qreal QWasmWindow::devicePixelRatio() const +{ + return screen()->devicePixelRatio(); +} + +void QWasmWindow::requestUpdate() +{ + QPointer windowPointer(window()); + bool registered = QWasmEventDispatcher::registerRequestUpdateCallback([=](){ + if (windowPointer.isNull()) + return; + + QWindowPrivate *wp = static_cast(QObjectPrivate::get(windowPointer)); + wp->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..80664ad4fc --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmwindow.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** 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 +#include +#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 create(); + + 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) { mBackingStore = store; } + QWasmBackingStore *backingStore() const { return mBackingStore; } + QWindow *window() const { return mWindow; } + + 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* mWindow = nullptr; + QWasmCompositor *mCompositor = nullptr; + QWasmBackingStore *mBackingStore = nullptr; + QRect mNormalGeometry {0, 0, 0 ,0}; + QRect mOldGeometry; + Qt::WindowFlags mWindowFlags = Qt::Window; + Qt::WindowState mWindowState = Qt::WindowNoState; + QWasmCompositor::SubControls mActiveControl = QWasmCompositor::SC_None; + WId m_winid = 0; + bool hasTitle = false; + bool 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..b84ff9edae --- /dev/null +++ b/src/plugins/platforms/wasm/wasm.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "webassembly" ] +} diff --git a/src/plugins/platforms/wasm/wasm.pro b/src/plugins/platforms/wasm/wasm.pro new file mode 100644 index 0000000000..e16e810ee6 --- /dev/null +++ b/src/plugins/platforms/wasm/wasm.pro @@ -0,0 +1,59 @@ +TARGET = wasm +CONFIG += static plugin +QT += \ + core-private gui-private \ + eventdispatcher_support-private fontdatabase_support-private egl_support-private + +#DEFINES += QEGL_EXTRA_DEBUG + +# 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 + +RESOURCES += fonts/wasmfonts.qrc + +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 @@ + + + + + + APPNAME + + + +

+
+ + Qt for WebAssembly: APPNAME +
+ +
+
+ + + + + + diff --git a/src/src.pro b/src/src.pro index 3390471a32..ca3f82e2e7 100644 --- a/src/src.pro +++ b/src/src.pro @@ -70,7 +70,7 @@ src_xml.subdir = $$PWD/xml src_xml.target = sub-xml src_xml.depends = src_corelib -!emscripten: { +!wasm: { src_dbus.subdir = $$PWD/dbus src_dbus.target = sub-dbus src_dbus.depends = src_corelib @@ -185,7 +185,7 @@ qtConfig(thread):qtConfig(dbus) { src_platformsupport.depends += src_dbus src_tools_qdbusxml2cpp src_plugins.depends += src_dbus src_tools_qdbusxml2cpp src_tools_qdbuscpp2xml } -!emscripten: qtConfig(concurrent): SUBDIRS += src_concurrent +!wasm: qtConfig(concurrent): SUBDIRS += src_concurrent qtConfig(gui) { qtConfig(harfbuzz):!qtConfig(system-harfbuzz) { SUBDIRS += src_3rdparty_harfbuzzng diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp index e59666ea43..4c7dc73cfb 100644 --- a/src/widgets/dialogs/qfiledialog.cpp +++ b/src/widgets/dialogs/qfiledialog.cpp @@ -1108,7 +1108,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) && !defined(Q_OS_HTML5) +# 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/kernel/qopenglwidget.cpp b/src/widgets/kernel/qopenglwidget.cpp index c1bf3cd4af..6f4224e734 100644 --- a/src/widgets/kernel/qopenglwidget.cpp +++ b/src/widgets/kernel/qopenglwidget.cpp @@ -906,7 +906,7 @@ 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_HTML5 +#ifdef Q_OS_WASM const int gl_depth_stencil_attachment = 0x821A; // GL_DEPTH_STENCIL_ATTACHMENT const GLenum attachments[] = { -- cgit v1.2.3