diff --git a/README.en.md b/README.en.md index c0f6da23808e50a82eb22080bba8318dbe843466..acb71f1e6f9f5ff62ed1fd2a57e30e79b688e81c 100644 --- a/README.en.md +++ b/README.en.md @@ -1,45 +1,39 @@ -# Contribution Note +# Contribution Instructions -Since the Qt source code contains multiple Qt sub-libraries, following the requirements of community management, Qt For OpenHarmony contributes in the form of code patches and independent adaptation modules. +Since the Qt source code includes multiple Qt sub-libraries, and in accordance with community management requirements, Qt For OpenHarmony contributes through code patches and independent adaptation modules. -## Code Patch Genearate +## Generating Code Patches -Use the "git diff" command to generate a code patch, and submit the patch to the corresponding Patch directory. +Use the “git diff” command to generate code patches and submit them to the corresponding Patch directory. -> Each sub-module uses the naming method of "submodulename.patch" to manage sub-module adaptation code patches. For example, execute "git diff v5.12.12 > qtbase.patch" in the qtbase sub-submodule directory to generate the corresponding qtbase sub-module code patch , after generating the code patch, follow the requirements of the OpenHarmony community and submit it to the main repository branch. +> Each sub-library manages adaptation code patches using the naming convention “submodulename.patch”. For example, executing “git diff v5.15.12 > qtbase.patch” in the qtbase sub-repository directory generates the corresponding qtbase sub-module code patch. After generating the code patch, follow the OpenHarmony community requirements to submit it to the main repository branch. -# Catalog Description +# Directory Description ``` OpenHarmony - Qt │ -└───patch - Source patch folder -|____v5.12.12 -│ │ qtbase.patch - qtbase submodule patch -| | qtsensors.patch - qtsensors submodule patch -| | qtdeclarative.patch qtdeclarative submodule patch -| | qtmultimedia.patch - qtmultimedia submodule patch -| | qtconnectivity.patch - qtconnectivity submodule patch -| | qtremoteobjects.patch - qtremoteobjects submodule patch -|____v5.15.11 -│ │ qtbase.patch - qtbase submodule patch -| | qtsensors.patch - qtsensors submodule patch -| | qtdeclarative.patch qtdeclarative submodule patch -| | qtmultimedia.patch - qtmultimedia submodule patch -| | qtconnectivity.patch - qtconnectivity submodule patch -| | qtremoteobjects.patch - qtremoteobjects submodule patch -└───windows_build.bat - windows cross compile script -└───linux_build.sh - Linux cross compile script +|__tests - Unit test code +└───patch - Source code patches +|____v5.15.12 +│ │ qt3d - qt3d sub-module code patch +│ │ qtbase - qtbase sub-module code patch +│ │ qtwebview - qtwebview sub-module code patch +│ │ qtsensors - qtsensors sub-module code patch +│ │ qtmultimedia - qtmultimedia sub-module code patch +│ │ qtdeclarative - qtdeclarative sub-module code patch +│ │ qtconnectivity - qtconnectivity sub-module code patch +│ │ qtquickcontrols - qtquickcontrols sub-module code patch └───LICENSE.FDL - GNU Free Documentation License └───LICENSE.GPLv2 - GNU GENERAL PUBLIC LICENSE Version 2 └───LICENSE.GPLv3 - GNU GENERAL PUBLIC LICENSE Version 3 └───LICENSE.LGPLv3 - GNU LESSER GENERAL PUBLIC LICENSE Version 3 └───LICENSE.LGPLv21 - GNU LESSER GENERAL PUBLIC LICENSE Version 2.1 └───LICENSE.QT-LICENSE-AGREEMENT - QT LICENSE AGREEMENT Agreement -└───README.md - Chinese readme -└───README.en.md - English readme +└───README.md - Chinese version readme +└───README.en.md - English version readme ``` # Using Qt For OpenHarmony -See the repository wiki for instructions:https://gitee.com/openharmony-sig/qt/wikis +Refer to the repository wiki instructions: https://gitee.com/openharmony-sig/qt/wikis diff --git a/README.md b/README.md index 187be8af48402854660db8bae4e2dea3a3c83cd5..fc3233d8a261e6827438be038ac89c17a5e94769 100755 --- a/README.md +++ b/README.md @@ -6,30 +6,24 @@ 使用"git diff"命令生成代码补丁,提交补丁到对应的Patch目录下。 -> 各子库使用"submodulename.patch"的命名方式对子库适配代码补丁进行管理,例如在qtbase子仓库目录下执行"git diff v5.12.12 > qtbase.patch"可生成对应的qtbase子模块代码补丁,生成代码补丁后,遵循OpenHarmony社区要求提交到主仓库分支。 +> 各子库使用"submodulename.patch"的命名方式对子库适配代码补丁进行管理,例如在qtbase子仓库目录下执行"git diff v5.15.12 > qtbase.patch"可生成对应的qtbase子模块代码补丁,生成代码补丁后,遵循OpenHarmony社区要求提交到主仓库分支。 # 目录说明 ``` OpenHarmony - Qt │ +|__tests - 单元测试代码 └───patch - 源码补丁 -|____v5.12.12 -│ │ qtbase.patch - qtbase子模块代码补丁 -| | qtsensors.patch - qtsensors子模块补丁 -| | qtdeclarative.patch qtdeclarative子模块补丁 -| | qtmultimedia.patch - qtmultimedia子模块补丁 -| | qtconnectivity.patch - qtconnectivity子模块补丁 -| | qtremoteobjects.patch - qtremoteobjects子模块代码补丁 -|____v5.15.11 -│ │ qtbase.patch - qtbase子模块代码补丁 -| | qtsensors.patch - qtsensors子模块补丁 -| | qtdeclarative.patch qtdeclarative子模块补丁 -| | qtmultimedia.patch - qtmultimedia子模块补丁 -| | qtconnectivity.patch - qtconnectivity子模块补丁 -| | qtremoteobjects.patch - qtremoteobjects子模块代码补丁 -└───windows_build.bat - windows环境下的交叉编译脚本 -└───linux_build.sh - Linux环境下的交叉编译脚本 +|____v5.15.12 +│ │ qt3d - qt3d子模块代码补丁 +│ │ qtbase - qtbase子模块代码补丁 +│ │ qtwebview - qtwebview子模块代码补丁 +│ │ qtsensors - qtsensors子模块代码补丁 +│ │ qtmultimedia - qtmultimedia子模块代码补丁 +│ │ qtdeclarative - qtdeclarative子模块代码补丁 +│ │ qtconnectivity - qtconnectivity子模块代码补丁 +│ │ qtquickcontrols - qtquickcontrols子模块代码补丁 └───LICENSE.FDL - GNU Free Documentation License └───LICENSE.GPLv2 - GNU GENERAL PUBLIC LICENSE Version 2 └───LICENSE.GPLv3 - GNU GENERAL PUBLIC LICENSE Version 3 @@ -43,3 +37,4 @@ OpenHarmony - Qt # Qt For OpenHarmony使用 参见仓库wiki说明:https://gitee.com/openharmony-sig/qt/wikis + diff --git a/patch/v5.12.12/qtbase.patch b/patch/v5.12.12/qtbase.patch deleted file mode 100644 index 565e39c774b6c22b20edcbf548c7ed1309255ab3..0000000000000000000000000000000000000000 --- a/patch/v5.12.12/qtbase.patch +++ /dev/null @@ -1,11477 +0,0 @@ -diff --git a/config.tests/arch/write_info.pri b/config.tests/arch/write_info.pri -index 3b55a63f49..e0518dd242 100644 ---- a/config.tests/arch/write_info.pri -+++ b/config.tests/arch/write_info.pri -@@ -3,7 +3,7 @@ targetinfofile ~= s/pro$/target.txt/ - - win32 { - ext = .exe --} else:android { -+} else:android|openharmony { - file_prefix = lib - ext = .so - } else:wasm { -diff --git a/mkspecs/common/oh-base-head.conf b/mkspecs/common/oh-base-head.conf -new file mode 100644 -index 0000000000..d7be212137 ---- /dev/null -+++ b/mkspecs/common/oh-base-head.conf -@@ -0,0 +1,24 @@ -+load(device_config) -+ -+SDK_ROOT = $$(OHOS_SDK_PATH) -+ -+isEmpty(NDK_TOOLCHAIN_PREFIX) { -+ equals(OHOS_ARCH, arm64-v8a): NDK_TOOLCHAIN_PREFIX = aarch64-linux-ohos -+ else: equals(OHOS_ARCH, armeabi-v7a): NDK_TOOLCHAIN_PREFIX = arm-linux-ohos -+ else: equals(OHOS_ARCH, x86_64): NDK_TOOLCHAIN_PREFIX = x86_64-linux-ohos -+ else: NDK_TOOLCHAIN_PREFIX = arm-linux-ohos -+} -+ -+QMAKE_CFLAGS = -D__MUSL__ -+QMAKE_CXXFLAGS = -D__MUSL__ -+QMAKE_CFLAGS += -DOPENHARMONY -+QMAKE_CXXFLAGS += -DOPENHARMONY -+ -+CROSS_COMPILE = $$shell_path($$SDK_ROOT/native/llvm/bin/) -+ -+QMAKE_PCH_OUTPUT_EXT = .gch -+ -+QMAKE_CFLAGS_PRECOMPILE = -x c-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT} -+QMAKE_CFLAGS_USE_PRECOMPILE = -include ${QMAKE_PCH_OUTPUT_BASE} -+QMAKE_CXXFLAGS_PRECOMPILE = -x c++-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT} -+QMAKE_CXXFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE -diff --git a/mkspecs/common/oh-base-tail.conf b/mkspecs/common/oh-base-tail.conf -new file mode 100644 -index 0000000000..5ee5cb4a57 ---- /dev/null -+++ b/mkspecs/common/oh-base-tail.conf -@@ -0,0 +1,76 @@ -+equals(OHOS_ARCH, armeabi-v7a): \ -+ QMAKE_CFLAGS += -march=armv7a -msoft-float -mfpu=vfp -fno-builtin-memmove -+else: equals(OHOS_ARCH, arm64-v8a): \ -+ QMAKE_CFLAGS += -march=armv8a -msoft-float -mfpu=vfp -fno-builtin-memmove -+else: equals(OHOS_ARCH, x86_64): \ -+ QMAKE_CFLAGS += -march=x86_64 -+else: \ -+ QMAKE_CFLAGS += -march=armv7a -msoft-float -mfpu=vfp -fno-builtin-memmove -+ -+ -+equals(OHOS_ARCH, armeabi-v7a) | equals(OHOS_ARCH, armeabi) { -+ CONFIG += optimize_size -+ QMAKE_CFLAGS_DEBUG = -g -marm -O0 -+} -+ -+QMAKE_CFLAGS_SHLIB = -fPIC -+QMAKE_CFLAGS_YACC = -Wno-unused -Wno-parentheses -+QMAKE_CFLAGS_THREAD = -D_REENTRANT -+QMAKE_CFLAGS_HIDESYMS = -fvisibility=hidden -+QMAKE_CFLAGS_NEON = -mfpu=neon -+ -+QMAKE_CFLAGS_GNUC99 = -std=gnu99 -+QMAKE_CFLAGS_GNUC11 = -std=gnu11 -+QMAKE_CXXFLAGS_CXX11 = -std=c++11 -+QMAKE_CXXFLAGS_CXX14 = -std=c++14 -+QMAKE_CXXFLAGS_CXX1Z = -std=c++1z -+QMAKE_CXXFLAGS_GNUCXX11 = -std=gnu++11 -+QMAKE_CXXFLAGS_GNUCXX14 = -std=gnu++14 -+QMAKE_CXXFLAGS_GNUCXX1Z = -std=gnu++1z -+ -+QMAKE_CXXFLAGS = $$QMAKE_CFLAGS -+QMAKE_CXXFLAGS_WARN_ON = $$QMAKE_CFLAGS_WARN_ON -+QMAKE_CXXFLAGS_WARN_OFF = $$QMAKE_CFLAGS_WARN_OFF -+QMAKE_CXXFLAGS_RELEASE += $$QMAKE_CFLAGS_RELEASE -+QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO += $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO -+QMAKE_CXXFLAGS_DEBUG += $$QMAKE_CFLAGS_DEBUG -+QMAKE_CXXFLAGS_SHLIB = $$QMAKE_CFLAGS_SHLIB -+QMAKE_CXXFLAGS_YACC = $$QMAKE_CFLAGS_YACC -+QMAKE_CXXFLAGS_THREAD = $$QMAKE_CFLAGS_THREAD -+QMAKE_CXXFLAGS_HIDESYMS = $$QMAKE_CFLAGS_HIDESYMS -fvisibility-inlines-hidden -+ -+# modifications to linux.conf -+QMAKE_AR = $${CROSS_COMPILE}llvm-ar cqs -+QMAKE_OBJCOPY = $${CROSS_COMPILE}llvm-objcopy -+QMAKE_NM = $${CROSS_COMPILE}llvm-nm -P -+ -+QMAKE_STRIP = -+ -+QMAKE_RANLIB = $${CROSS_COMPILE}llvm-ranlib -+ -+QMAKE_INCDIR_POST = $$shell_path($$SDK_ROOT/native/sysroot/usr/include/$${NDK_TOOLCHAIN_PREFIX}) -+QMAKE_LIBDIR_POST = $$shell_path($$SDK_ROOT/native/sysroot/usr/lib/$${NDK_TOOLCHAIN_PREFIX}) -+QMAKE_INCDIR_X11 = -+QMAKE_LIBDIR_X11 = -+QMAKE_INCDIR_OPENGL = -+QMAKE_LIBDIR_OPENGL = -+ -+QMAKE_LINK_SHLIB = $$QMAKE_LINK -+QMAKE_LFLAGS = --sysroot=$$shell_path($$SDK_ROOT/native/sysroot) -+QMAKE_LFLAGS_APP = -Wl,--no-undefined -Wl,-z,noexecstack -shared -+QMAKE_LFLAGS_SHLIB = -Wl,--no-undefined -Wl,-z,noexecstack -shared -+QMAKE_LFLAGS_PLUGIN = $$QMAKE_LFLAGS_SHLIB -+QMAKE_LFLAGS_NOUNDEF = -Wl,--no-undefined -+QMAKE_LFLAGS_RPATH = -Wl,-rpath= -+QMAKE_LFLAGS_RPATHLINK = -Wl,-rpath-link= -+ -+#QMAKE_LIBS_PRIVATE = -lc++ -lz -lm -ldl -lc -lhilog_ndk.z -+QMAKE_LIBS_X11 = -+QMAKE_LIBS_THREAD = -+QMAKE_LIBS_EGL = -lEGL -+QMAKE_LIBS_OPENGL = -+QMAKE_LIBS_OPENGL_ES2 = -lGLESv3 -+ -+!exists($$SDK_ROOT): error("You need to set the OHOS_SDK_PATH environment variable to point to your Harmony NDK.") -+ -+load(qt_config) -diff --git a/mkspecs/features/openharmony/openharmony.prf b/mkspecs/features/openharmony/openharmony.prf -new file mode 100644 -index 0000000000..14524b35ee ---- /dev/null -+++ b/mkspecs/features/openharmony/openharmony.prf -@@ -0,0 +1,4 @@ -+contains(TEMPLATE, ".*app") { -+ !contains(TARGET, ".so"): TARGET = lib$${TARGET}.so -+ QMAKE_LFLAGS += -Wl,-soname,$$shell_quote($$TARGET) -+} -diff --git a/mkspecs/features/qt_configure.prf b/mkspecs/features/qt_configure.prf -index 850ee98057..8c92306173 100644 ---- a/mkspecs/features/qt_configure.prf -+++ b/mkspecs/features/qt_configure.prf -@@ -777,6 +777,7 @@ defineTest(qtConfLibrary_inline) { - # this source type cannot fail. - defineTest(qtConfLibrary_makeSpec) { - spec = $$eval($${1}.spec) -+ - isEmpty(spec): \ - error("makeSpec source in library '$$eval($${1}.library)' does not specify 'spec'.") - -@@ -1233,6 +1234,7 @@ defineTest(qtConfPrepareCompileTestSource) { - defineTest(qtConfTest_compile) { - test = $$eval($${1}.test) - host = $$eval($${1}.host) -+ - isEmpty(host): host = false - - test_base_out_dir = $$OUT_PWD/$$basename(QMAKE_CONFIG_TESTS_DIR) -@@ -2135,8 +2137,7 @@ defineTest(qtConfOutput_publicFeature) { - name = "$$eval($${1}.name)" - isEmpty(name): \ - name = $$eval($${1}.feature) -- feature = $$replace(name, [-+.], _) -- -+ feature = $$replace(name, [-+.], _) - $${2} { - qtConfExtendVar("publicPro", "QT.$${currentModule}.enabled_features", $$name) - QT.$${currentModule}.enabled_features += $$name -diff --git a/mkspecs/features/qt_plugin.prf b/mkspecs/features/qt_plugin.prf -index 40528a65e2..3e563b8cc0 100644 ---- a/mkspecs/features/qt_plugin.prf -+++ b/mkspecs/features/qt_plugin.prf -@@ -91,7 +91,17 @@ CONFIG(static, static|shared)|prefix_build { - target.path = $$[QT_INSTALL_PLUGINS]/$$PLUGIN_TYPE - INSTALLS += target - --TARGET = $$qt5LibraryTarget($$TARGET) -+defineReplace(qt5OpenHarmonyPluginTarget) { -+ LIBRARY_NAME_PREFIX = $$2 -+ LIBRARY_NAME_PREFIX = $$replace(LIBRARY_NAME_PREFIX, "//", "/") -+ LIBRARY_NAME_PREFIX = $$replace(LIBRARY_NAME_PREFIX, "/", "_") -+ LIBRARY_NAME = $$LIBRARY_NAME_PREFIX$$qtLibraryTarget($$1) -+ unset(LIBRARY_NAME_PREFIX) -+ return($$LIBRARY_NAME) -+} -+ -+ -+TARGET = $$qt5OpenHarmonyPluginTarget($$TARGET, "plugins/$$PLUGIN_TYPE/") - - CONFIG += create_cmake - -diff --git a/mkspecs/oh-clang/qmake.conf b/mkspecs/oh-clang/qmake.conf -new file mode 100644 -index 0000000000..5bfd38b4f5 ---- /dev/null -+++ b/mkspecs/oh-clang/qmake.conf -@@ -0,0 +1,39 @@ -+# qmake configuration for building with oh-clang -+MAKEFILE_GENERATOR = UNIX -+QMAKE_PLATFORM = openharmony -+QMAKE_COMPILER = gcc clang llvm -+ -+CONFIG += unversioned_soname unversioned_libname plugin_with_soname $$QMAKE_PLATFORM -+ -+include(../common/linux.conf) -+include(../common/gcc-base-unix.conf) -+include(../common/clang.conf) -+include(../common/oh-base-head.conf) -+ -+NDK_LLVM_PATH = $$SDK_ROOT/native/llvm -+QMAKE_CC = $${CROSS_COMPILE}clang -+QMAKE_CXX = $${CROSS_COMPILE}clang++ -+ -+equals(OHOS_ARCH, armeabi-v7a): \ -+ QMAKE_CFLAGS += -target arm-linux-ohos -+else:equals(OHOS_ARCH,arm64-v8a): \ -+ QMAKE_CFLAGS += -target aarch64-linux-ohos -+else:equals(OHOS_ARCH,x86-64): \ -+ QMAKE_CFLAGS += -target x86_64-linux-ohos -+ -+#QMAKE_CFLAGS+=--gcc-toolchain=$$NDK_LLVM_PATH -+QMAKE_LINK += -Wl,--exclude-libs,$$shell_path($$SDK_ROOT/native/llvm/lib/$$NDK_TOOLCHAIN_PREFIX/libunwind.a) -+ -+QMAKE_CFLAGS += --sysroot=$$shell_path($$SDK_ROOT/native/sysroot) \ -+ -isystem=$$shell_path($$SDK_ROOT/native/sysroot) \ -+ -isystem=$$shell_path($$SDK_ROOT/native/sysroot/usr/include) \ -+ -isystem=$$shell_path($$SDK_ROOT/native/llvm/include/libcxx-ohos/include/c++/v1) -+ -+ -+QMAKE_CLFAGS += -g -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -fno-addrsig -Wa,--noexecstack -Wformat -Werror=format-security -+ -+QMAKE_LINK = $$QMAKE_CXX $$QMAKE_CFLAGS -+ -+QMAKE_CFLAGS_OPTIMIZE_SIZE = -Oz -+ -+include(../common/oh-base-tail.conf) -diff --git a/mkspecs/oh-clang/qplatformdefs.h b/mkspecs/oh-clang/qplatformdefs.h -new file mode 100644 -index 0000000000..f405c91ecb ---- /dev/null -+++ b/mkspecs/oh-clang/qplatformdefs.h -@@ -0,0 +1,177 @@ -+/**************************************************************************** -+** -+** Copyright (C) 2017 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 _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/global/qlogging.cpp b/src/corelib/global/qlogging.cpp -index 3c82097cfe..f85b2baa19 100644 ---- a/src/corelib/global/qlogging.cpp -+++ b/src/corelib/global/qlogging.cpp -@@ -1679,6 +1679,36 @@ static bool android_default_message_handler(QtMsgType type, - } - #endif //Q_OS_ANDROID - -+#ifdef Q_OS_OPENHARMONY -+#include -+#define APP_LOG_DOMAIN 0xf000 -+#define APP_LOG_TAG "QtForOpenHarmony" -+ -+static bool openharmony_default_message_handler(QtMsgType type, -+ const QMessageLogContext &context, -+ const QString &message) -+{ -+ if (shouldLogToStderr()) -+ return false; // Leave logging up to stderr handler -+ -+ QString formattedMessage = qFormatLogMessage(type, context, message); -+ -+ LogLevel priority = LOG_INFO; -+ switch (type) { -+ //LOG_DEBUG unable to print, temporarily use LOG_ INFO replace. -+ case QtDebugMsg: priority = LOG_INFO; break; -+ case QtInfoMsg: priority = LOG_INFO; break; -+ case QtWarningMsg: priority = LOG_WARN; break; -+ case QtCriticalMsg: priority = LOG_ERROR; break; -+ case QtFatalMsg: priority = LOG_FATAL; break; -+ }; -+ -+ OH_LOG_Print(LOG_APP, priority, APP_LOG_DOMAIN, APP_LOG_TAG, "%{public}s %{public}s\n", qPrintable(QCoreApplication::applicationName()), qPrintable(formattedMessage)); -+ -+ return true; // Prevent further output to stderr -+} -+#endif -+ - #ifdef Q_OS_WIN - static bool win_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) - { -@@ -1770,6 +1800,8 @@ static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &con - handledStderr |= AppleUnifiedLogger::messageHandler(type, context, message); - # elif defined Q_OS_WASM - handledStderr |= wasm_default_message_handler(type, context, message); -+#elif defined Q_OS_OPENHARMONY -+ handledStderr |= openharmony_default_message_handler(type, context, message); - # endif - #endif - -diff --git a/src/corelib/global/qsystemdetection.h b/src/corelib/global/qsystemdetection.h -index d87e181e5c..22227d672a 100644 ---- a/src/corelib/global/qsystemdetection.h -+++ b/src/corelib/global/qsystemdetection.h -@@ -111,6 +111,9 @@ - #elif defined(__ANDROID__) || defined(ANDROID) - # define Q_OS_ANDROID - # define Q_OS_LINUX -+#elif defined(__OHOS__) || defined(OPENHARMONY) -+# define Q_OS_OPENHARMONY -+# define Q_OS_LINUX - #elif defined(__CYGWIN__) - # define Q_OS_CYGWIN - #elif !defined(SAG_COM) && (!defined(WINAPI_FAMILY) || WINAPI_FAMILY==WINAPI_FAMILY_DESKTOP_APP) && (defined(WIN64) || defined(_WIN64) || defined(__WIN64__)) -diff --git a/src/corelib/io/qfileselector.cpp b/src/corelib/io/qfileselector.cpp -index 500b475d1d..8b09cfb04d 100644 ---- a/src/corelib/io/qfileselector.cpp -+++ b/src/corelib/io/qfileselector.cpp -@@ -199,6 +199,9 @@ static bool isLocalScheme(const QString &file) - bool local = file == QLatin1String("qrc"); - #ifdef Q_OS_ANDROID - local |= file == QLatin1String("assets"); -+#endif -+#ifdef Q_OS_OPENHARMONY -+ local |= file == QLatin1String("rawfile"); - #endif - return local; - } -@@ -223,6 +226,10 @@ QUrl QFileSelector::select(const QUrl &filePath) const - if (filePath.scheme() == QLatin1String("assets")) - scheme = QLatin1String("assets:"); - #endif -+#ifdef Q_OS_OPENHARMONY -+ if (filePath.scheme() == QLatin1String("rawfile")) -+ scheme = QLatin1String("rawfile:"); -+#endif - - QString equivalentPath = scheme + filePath.path(); - QString selectedPath = d->select(equivalentPath); -diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp -index 6d82981fd6..5896fcbdb5 100644 ---- a/src/corelib/io/qurl.cpp -+++ b/src/corelib/io/qurl.cpp -@@ -445,6 +445,11 @@ static inline QString webDavScheme() - return QStringLiteral("webdavs"); - } - -+static inline QString harmonyFileScheme() -+{ -+ return QStringLiteral("datashare"); -+} -+ - static inline QString webDavSslTag() - { - return QStringLiteral("@SSL"); -@@ -1021,6 +1026,9 @@ inline bool QUrlPrivate::setScheme(const QString &value, int len, bool doSetErro - if (scheme == fileScheme() - #ifdef Q_OS_WIN - || scheme == webDavScheme() -+#endif -+#ifdef Q_OS_OPENHARMONY -+ || scheme == harmonyFileScheme() - #endif - ) { - flags |= IsLocalFile; -diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri -index 3f7bf3cd47..f7c6fa9dc8 100644 ---- a/src/corelib/kernel/kernel.pri -+++ b/src/corelib/kernel/kernel.pri -@@ -212,4 +212,24 @@ android:!android-embedded { - kernel/qjni_p.h - } - -+openharmony { -+ SOURCES += kernel/qopenharmonyload.cpp \ -+ kernel/qopenharmonyjsobject.cpp \ -+ kernel/qopenharmonyjsobjectloader.cpp \ -+ kernel/qopenharmonyjsobjectpool.cpp \ -+ kernel/qopenharmonyjsenvironment.cpp \ -+ kernel/qopenharmonyjsfunction.cpp \ -+ kernel/qopenharmonyhelpers.cpp -+ -+ HEADERS += kernel/qopenharmonyjsobject.h \ -+ kernel/qopenharmonyjsobjectloader.h \ -+ kernel/qopenharmonyjsobjectpool.h \ -+ kernel/qopenharmonyjsenvironment.h \ -+ kernel/qopenharmonydefines.h \ -+ kernel/qopenharmonyjsfunction.h \ -+ kernel/qopenharmonyhelpers_p.h -+ -+ LIBS += -lace_napi.z -luv -lc++ -lz -lm -ldl -lc -lhilog_ndk.z -+} -+ - !darwin:!unix:!win32: SOURCES += kernel/qelapsedtimer_generic.cpp -diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp -index db6546028a..10fba6c9c8 100644 ---- a/src/corelib/kernel/qcoreapplication.cpp -+++ b/src/corelib/kernel/qcoreapplication.cpp -@@ -2337,7 +2337,7 @@ QString QCoreApplication::applicationFilePath() - } - #endif - #if defined( Q_OS_UNIX ) --# if defined(Q_OS_LINUX) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_EMBEDDED)) -+# if defined(Q_OS_LINUX) && !defined(Q_OS_OPENHARMONY) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_EMBEDDED)) - // Try looking for a /proc//exe symlink first which points to - // the absolute path of the executable - QFileInfo pfi(QString::fromLatin1("/proc/%1/exe").arg(getpid())); -diff --git a/src/corelib/kernel/qopenharmonydefines.h b/src/corelib/kernel/qopenharmonydefines.h -new file mode 100644 -index 0000000000..7480dc1c20 ---- /dev/null -+++ b/src/corelib/kernel/qopenharmonydefines.h -@@ -0,0 +1,107 @@ -+#ifndef QOPENHARMONYDEFINES_H -+#define QOPENHARMONYDEFINES_H -+#include -+ -+#define QPA_LOG_DOMAIN 0xff11 -+#define QPA_LOG_TAG "QtForOpenHarmony" -+#define LOGI(...) ((void)OH_LOG_Print(LOG_APP, LOG_INFO, QPA_LOG_DOMAIN, QPA_LOG_TAG, __VA_ARGS__)) -+#define LOGD(...) ((void)OH_LOG_Print(LOG_APP, LOG_DEBUG, QPA_LOG_DOMAIN, QPA_LOG_TAG, __VA_ARGS__)) -+#define LOGW(...) ((void)OH_LOG_Print(LOG_APP, LOG_WARN, QPA_LOG_DOMAIN, QPA_LOG_TAG, __VA_ARGS__)) -+#define LOGE(...) ((void)OH_LOG_Print(LOG_APP, LOG_ERROR, QPA_LOG_DOMAIN, QPA_LOG_TAG, __VA_ARGS__)) -+ -+#define NAPI_RETVAL_NOTHING -+ -+#define GET_AND_THROW_LAST_ERROR(env, status, function) \ -+ do { \ -+ const napi_extended_error_info* errorInfo = nullptr; \ -+ napi_get_last_error_info((env), &errorInfo); \ -+ bool isPending = false; \ -+ napi_is_exception_pending((env), &isPending); \ -+ if (!isPending && errorInfo != nullptr) { \ -+ const char* errorMessage = \ -+ errorInfo->error_message != nullptr ? errorInfo->error_message : function; \ -+ LOGE("call method %{public}s failed, the error code is %{public}d", errorMessage, status); \ -+ } \ -+ } while (0) -+ -+#define NAPI_ASSERT_BASE(env, assertion, message, retVal) \ -+ do { \ -+ if (!(assertion)) { \ -+ napi_throw_error((env), nullptr, "assertion (" #assertion ") failed: " message); \ -+ return retVal; \ -+ } \ -+ } while (0) -+ -+#define NAPI_ASSERT(env, assertion, message) NAPI_ASSERT_BASE(env, assertion, message, nullptr) -+ -+#define NAPI_ASSERT_RETURN_VOID(env, assertion, message) NAPI_ASSERT_BASE(env, assertion, message, NAPI_RETVAL_NOTHING) -+ -+#define NAPI_CALL_BASE(env, theCall, retVal) \ -+ do { \ -+ napi_status status = (theCall); \ -+ if (status != napi_ok) { \ -+ GET_AND_THROW_LAST_ERROR((env), status, #theCall); \ -+ return retVal; \ -+ } \ -+ } while (0) -+ -+#define NAPI_CALL_BASE_NO_THROW(env, theCall, retVal) \ -+ do { \ -+ if ((theCall) != napi_ok) { \ -+ return retVal; \ -+ } \ -+ } while (0) -+ -+#define NAPI_CALL(env, theCall) NAPI_CALL_BASE(env, theCall, nullptr) -+ -+#define NAPI_CALL_RETURN_VOID(env, theCall) NAPI_CALL_BASE(env, theCall, NAPI_RETVAL_NOTHING) -+ -+#define NAPI_CALL_RETURN_VOID_NO_THROW(env, theCall) NAPI_CALL_BASE_NO_THROW(env, theCall, NAPI_RETVAL_NOTHING) -+ -+#define DECLARE_NAPI_PROPERTY(name, val) \ -+ { \ -+ (name), nullptr, nullptr, nullptr, nullptr, val, napi_default, nullptr \ -+ } -+ -+#define DECLARE_NAPI_STATIC_PROPERTY(name, val) \ -+ { \ -+ (name), nullptr, nullptr, nullptr, nullptr, val, napi_static, nullptr \ -+ } -+ -+#define DECLARE_NAPI_FUNCTION(name, func) \ -+ { \ -+ (name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, nullptr \ -+ } -+ -+#define DECLARE_NAPI_FUNCTION_WITH_DATA(name, func, data) \ -+ { \ -+ (name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, data \ -+ } -+ -+#define DECLARE_NAPI_STATIC_FUNCTION(name, func) \ -+ { \ -+ (name), nullptr, (func), nullptr, nullptr, nullptr, napi_static, nullptr \ -+ } -+ -+#define DECLARE_NAPI_GETTER(name, getter) \ -+ { \ -+ (name), nullptr, nullptr, (getter), nullptr, nullptr, napi_default, nullptr \ -+ } -+ -+#define DECLARE_NAPI_SETTER(name, setter) \ -+ { \ -+ (name), nullptr, nullptr, nullptr, (setter), nullptr, napi_default, nullptr \ -+ } -+ -+#define DECLARE_NAPI_GETTER_SETTER(name, getter, setter) \ -+ { \ -+ (name), nullptr, nullptr, (getter), (setter), nullptr, napi_default, nullptr \ -+ } -+ -+#define DECLARE_NAPI_FUNCTION(name, func) \ -+ { \ -+ (name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, nullptr \ -+ } -+ -+ -+#endif // QOPENHARMONYDEFINES_H -diff --git a/src/corelib/kernel/qopenharmonyhelpers.cpp b/src/corelib/kernel/qopenharmonyhelpers.cpp -new file mode 100644 -index 0000000000..9d2664cae6 ---- /dev/null -+++ b/src/corelib/kernel/qopenharmonyhelpers.cpp -@@ -0,0 +1,132 @@ -+#include "qopenharmonyhelpers_p.h" -+#include "qopenharmonyjsenvironment.h" -+#include "qmutex.h" -+#include "qlist.h" -+#include "qsemaphore.h" -+#include "qsharedpointer.h" -+#include "qvector.h" -+#include "qthread.h" -+#include "qcoreapplication.h" -+#include -+ -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+namespace QtHarmonyPrivate { -+ ResumePauseListener::~ResumePauseListener() {} -+ void ResumePauseListener::handlePause() {} -+ void ResumePauseListener::handleResume() {} -+ -+ napi_value variant_to_napi_value(const QVariant &value) -+ { -+ napi_value ret = nullptr; -+ int t = value.userType(); -+ switch (t) { -+ case QVariant::Int: -+ ret = qJs::createInt32(value.toInt()); -+ break; -+ case QVariant::String: -+ ret = qJs::createString(value.toString()); -+ break; -+ case QVariant::StringList: -+ ret = qJs::createStringArray(value.toStringList()); -+ break; -+ case QVariant::Bool: -+ ret = qJs::createBool(value.toBool()); -+ break; -+ case QVariant::LongLong: -+ ret = qJs::createInt64(value.toLongLong()); -+ break; -+ case QVariant::ByteArray: -+ ret = qJs::createArrayBuffer(value.toByteArray()); -+ break; -+ case QVariant::Map: -+ ret = qJs::createObject(value.toMap()); -+ break; -+ default: -+ ret = qJs::createObjectFromUserType(value); -+ break; -+ } -+ return ret; -+ } -+ -+ QVariant napi_value_to_variant(napi_value value, int t) -+ { -+ QVariant ret; -+ switch (t) { -+ case QMetaType::Int: -+ ret = qJs::getInt32(value); -+ break; -+ case QMetaType::QString: -+ ret = qJs::getString(value); -+ break; -+ case QMetaType::QStringList: -+ ret = qJs::getStringList(value); -+ break; -+ case QMetaType::Bool: -+ ret = qJs::getBool(value); -+ break; -+ case QMetaType::LongLong: -+ ret = QVariant::fromValue(qJs::getInt64(value)); -+ break; -+ case QMetaType::QByteArray: -+ ret = QVariant::fromValue(qJs::getByteArray(value)); -+ break; -+ case QMetaType::QByteArrayList: -+ ret = QVariant::fromValue(qJs::getByteArrayList(value)); -+ break; -+ case QMetaType::QRect: -+ ret = qJs::getRect(value); -+ break; -+ default: -+ //Todo other type -+ break; -+ } -+ return ret; -+ } -+ -+ -+} -+ -+namespace { -+class ResumePauseListeners -+{ -+public: -+ QMutex mutex; -+ QList listeners; -+}; -+} -+ -+Q_GLOBAL_STATIC(ResumePauseListeners, g_resumePauseListeners) -+ -+void QtHarmonyPrivate::registerResumePauseListener(ResumePauseListener *listener) -+{ -+ QMutexLocker locker(&g_resumePauseListeners()->mutex); -+ g_resumePauseListeners()->listeners.append(listener); -+} -+ -+void QtHarmonyPrivate::unregisterResumePauseListener(ResumePauseListener *listener) -+{ -+ QMutexLocker locker(&g_resumePauseListeners()->mutex); -+ g_resumePauseListeners()->listeners.removeAll(listener); -+} -+ -+void QtHarmonyPrivate::handlePause() -+{ -+ QMutexLocker locker(&g_resumePauseListeners()->mutex); -+ const QList &listeners = g_resumePauseListeners()->listeners; -+ for (int i=0; ihandlePause(); -+} -+ -+void QtHarmonyPrivate::handleResume() -+{ -+ QMutexLocker locker(&g_resumePauseListeners()->mutex); -+ const QList &listeners = g_resumePauseListeners()->listeners; -+ for (int i=0; ihandleResume(); -+} -+ -+QT_END_NAMESPACE -diff --git a/src/corelib/kernel/qopenharmonyhelpers_p.h b/src/corelib/kernel/qopenharmonyhelpers_p.h -new file mode 100644 -index 0000000000..0c0bd12c64 ---- /dev/null -+++ b/src/corelib/kernel/qopenharmonyhelpers_p.h -@@ -0,0 +1,43 @@ -+#ifndef QOPENHARMONYHELPERS_H -+#define QOPENHARMONYHELPERS_H -+ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+ -+namespace QtHarmonyPrivate -+{ -+ class Q_CORE_EXPORT ResumePauseListener -+ { -+ public: -+ virtual ~ResumePauseListener(); -+ virtual void handlePause(); -+ virtual void handleResume(); -+ }; -+ -+ -+ enum class PermissionsResult { -+ Granted, -+ Denied -+ }; -+ -+ Q_CORE_EXPORT void handlePause(); -+ Q_CORE_EXPORT void handleResume(); -+ Q_CORE_EXPORT void registerResumePauseListener(ResumePauseListener *listener); -+ Q_CORE_EXPORT void unregisterResumePauseListener(ResumePauseListener *listener); -+ -+ Q_CORE_EXPORT napi_value variant_to_napi_value(const QVariant &value); -+ -+ Q_CORE_EXPORT QVariant napi_value_to_variant(napi_value value, int t); -+} -+ -+QT_END_NAMESPACE -+ -+#endif // QJNIHELPERS_H -diff --git a/src/corelib/kernel/qopenharmonyjsenvironment.cpp b/src/corelib/kernel/qopenharmonyjsenvironment.cpp -new file mode 100644 -index 0000000000..4d39fc5500 ---- /dev/null -+++ b/src/corelib/kernel/qopenharmonyjsenvironment.cpp -@@ -0,0 +1,427 @@ -+#include "qopenharmonyjsenvironment.h" -+#include "qopenharmonydefines.h" -+#include "qopenharmonyhelpers_p.h" -+#include "qopenharmonyjsfunction.h" -+ -+#include -+ -+static napi_threadsafe_function g_threadsafe_call_js_function = nullptr; -+napi_env QOpenHarmonyJsEnvironment::m_env = nullptr; -+bool QOpenHarmonyJsEnvironment::m_exit = false; -+static QHash g_creators; -+ -+static void cleanup_creators() -+{ -+ qDeleteAll(g_creators); -+ g_creators.clear(); -+} -+ -+static void callJsFunction(napi_env env, napi_value js_cb, void* context, void* data) { -+ Q_UNUSED(env); -+ Q_UNUSED(context); -+ QOpenHarmonyJsFunction *function = (QOpenHarmonyJsFunction*)data; -+ if (function == nullptr) -+ return; -+ -+ napi_value jsObject = function->attachedObject(); -+ napi_valuetype type; -+ napi_typeof(env, jsObject, &type); -+ if (type != napi_object) { -+ qWarning() << "get attached js object failed:" << function->objectName(); -+ function->cancel(); -+ return; -+ } -+ -+ napi_value objectFunction = qJs::function(jsObject, function->name()); -+ -+ function->call(jsObject, objectFunction); -+} -+ -+void QOpenHarmonyJsEnvironment::init(napi_env env) -+{ -+ m_env = env; -+ -+ napi_value name; -+ napi_create_string_utf8(env, "CallJsFunction", NAPI_AUTO_LENGTH, &name); -+ napi_create_threadsafe_function(env, nullptr, nullptr, name, 0, 1, nullptr, -+ nullptr, nullptr, callJsFunction, &g_threadsafe_call_js_function); -+ if (g_threadsafe_call_js_function == nullptr) { -+ LOGW("init call js function failed"); -+ } -+ qAddPostRoutine(cleanup_creators); -+} -+ -+void QOpenHarmonyJsEnvironment::quit() -+{ -+ m_exit = true; -+} -+ -+void QOpenHarmonyJsEnvironment::throwError(const QString &errorMessage) -+{ -+ QByteArray _data = errorMessage.toUtf8(); -+ napi_throw_type_error(m_env, NULL, _data.constData()); -+} -+ -+napi_value QOpenHarmonyJsEnvironment::createString(const QString &value) -+{ -+ QByteArray text = value.toUtf8(); -+ const char *textData = text.constData(); -+ return createString(textData); -+} -+ -+napi_value QOpenHarmonyJsEnvironment::createStringArray(const QStringList &strList) -+{ -+ napi_value result = nullptr; -+ if (napi_ok == napi_create_array(m_env, &result)) { -+ for (int k = 0; k < strList.count(); ++k) { -+ napi_set_element(m_env, result, k, createString(strList.at(k))); -+ } -+ } -+ return result; -+} -+ -+napi_value QOpenHarmonyJsEnvironment::createArrayBuffer(const QByteArray &buffer) -+{ -+ napi_value result = nullptr; -+ void *data = nullptr; -+ NAPI_CALL_BASE(m_env, napi_create_arraybuffer(m_env, buffer.length(), &data, &result), nullptr); -+ memcpy(data, buffer.data(), buffer.length()); -+ return result; -+} -+ -+napi_value QOpenHarmonyJsEnvironment::createObject(const QVariantMap &map) -+{ -+ napi_value result = nullptr; -+ QMapIterator i(map); -+ NAPI_CALL_BASE(m_env, napi_create_object(m_env, &result), nullptr); -+ while (i.hasNext()) { -+ i.next(); -+ napi_value name = createString(i.key()); -+ napi_value v = QtHarmonyPrivate::variant_to_napi_value(i.value()); -+ napi_set_property(m_env, result, name, v); -+ } -+ return result; -+} -+ -+napi_value QOpenHarmonyJsEnvironment::createObjectFromUserType(const QVariant &value) -+{ -+ if (!g_creators.contains(value.userType())) -+ return nullptr; -+ NapiValueCreator *c = g_creators.value(value.userType()); -+ return c->create(value); -+} -+ -+void QOpenHarmonyJsEnvironment::registerCreator(int type, NapiValueCreator *creator) -+{ -+ g_creators.insert(type, creator); -+} -+ -+napi_value QOpenHarmonyJsEnvironment::globalObject() { -+ napi_value _global; -+ NAPI_CALL(m_env, napi_get_global(m_env, &_global)); -+ return _global; -+} -+ -+napi_value QOpenHarmonyJsEnvironment::callFunction(napi_value object, napi_value function, int argc, const napi_value *argv) -+{ -+ napi_value return_value; -+ NAPI_CALL(m_env, napi_call_function(m_env, object, function, argc, argv, &return_value)); -+ return return_value; -+} -+ -+bool QOpenHarmonyJsEnvironment::callThreadSafeFunction(QOpenHarmonyJsFunction *func) -+{ -+ if (m_exit || g_threadsafe_call_js_function == nullptr) -+ return false; -+ napi_acquire_threadsafe_function(g_threadsafe_call_js_function); -+ return napi_call_threadsafe_function(g_threadsafe_call_js_function, func, napi_tsfn_blocking) == napi_ok; -+} -+ -+QString QOpenHarmonyJsEnvironment::getString(napi_value value) -+{ -+ size_t strLen = 0; -+ NAPI_CALL_BASE(m_env, napi_get_value_string_utf8(m_env, value, nullptr, -1, &strLen), QString()); -+ size_t bufLen = strLen + 1; -+ std::unique_ptr strBuffer = std::make_unique(bufLen); -+ NAPI_CALL_BASE(m_env, napi_get_value_string_utf8(m_env, value, strBuffer.get(), bufLen, &strLen), QString()); -+ QString str = QString::fromUtf8(strBuffer.get()); -+ return str; -+} -+ -+QList QOpenHarmonyJsEnvironment::getNumbers(napi_value value) -+{ -+ bool result = false; -+ NAPI_CALL_BASE(m_env, napi_is_array(m_env, value, &result), QList()); -+ if (!result) -+ return QList(); -+ uint32_t length = 0; -+ NAPI_CALL_BASE(m_env, napi_get_array_length(m_env, value, &length), QList()); -+ QList ret; -+ for (uint32_t i = 0; i < length; ++i) { -+ napi_value temp = nullptr; -+ napi_get_element(m_env, value, i, &temp); -+ if (temp == nullptr) -+ continue; -+ ret << getInt32(temp); -+ } -+ return ret; -+} -+ -+QList QOpenHarmonyJsEnvironment::getRealNumbers(napi_value value) -+{ -+ bool result = false; -+ NAPI_CALL_BASE(m_env, napi_is_array(m_env, value, &result), QList()); -+ if (!result) -+ return QList(); -+ uint32_t length = 0; -+ NAPI_CALL_BASE(m_env, napi_get_array_length(m_env, value, &length), QList()); -+ QList ret; -+ for (uint32_t i = 0; i < length; ++i) { -+ napi_value temp = nullptr; -+ napi_get_element(m_env, value, i, &temp); -+ if (temp == nullptr) -+ continue; -+ ret << getDouble(temp); -+ } -+ return ret; -+} -+ -+QStringList QOpenHarmonyJsEnvironment::getStringList(napi_value value) -+{ -+ bool result = false; -+ NAPI_CALL_BASE(m_env, napi_is_array(m_env, value, &result), QStringList()); -+ if (!result) -+ return QStringList(); -+ uint32_t length = 0; -+ NAPI_CALL_BASE(m_env, napi_get_array_length(m_env, value, &length), QStringList()); -+ QStringList ret; -+ for (uint32_t i = 0; i < length; ++i) { -+ napi_value temp = nullptr; -+ napi_get_element(m_env, value, i, &temp); -+ if (temp == nullptr) -+ continue; -+ ret << getString(temp); -+ } -+ return ret; -+} -+ -+QByteArray QOpenHarmonyJsEnvironment::getByteArray(napi_value value) -+{ -+ bool isArraryBuffer = false; -+ NAPI_CALL_BASE(m_env, napi_is_arraybuffer(m_env, value, &isArraryBuffer), QByteArray()); -+ if (!isArraryBuffer) { -+ return QByteArray(); -+ } -+ void *data = nullptr; -+ size_t lenght = 0; -+ NAPI_CALL_BASE(m_env, napi_get_arraybuffer_info(m_env, value, &data, &lenght), QByteArray()); -+ QByteArray result = QByteArray((char*)(data), lenght); -+ return result; -+} -+ -+QByteArrayList QOpenHarmonyJsEnvironment::getByteArrayList(napi_value value) -+{ -+ bool result = false; -+ NAPI_CALL_BASE(m_env, napi_is_array(m_env, value, &result), QByteArrayList()); -+ if (!result) -+ return QByteArrayList(); -+ uint32_t length = 0; -+ NAPI_CALL_BASE(m_env, napi_get_array_length(m_env, value, &length), QByteArrayList()); -+ QByteArrayList ret; -+ for (uint32_t i = 0; i < length; ++i) { -+ napi_value temp = nullptr; -+ napi_get_element(m_env, value, i, &temp); -+ if (temp == nullptr) -+ continue; -+ ret << getByteArray(temp); -+ } -+ return ret; -+} -+ -+ -+//QVariantMap QOpenHarmonyJsEnvironment::getMap(napi_value value) -+//{ -+// napi_value names; -+// NAPI_CALL_BASE(m_env, napi_get_property_names(m_env, value, &names), QVariantMap()); -+// QByteArrayList nameList = getByteArrayList(names); -+// if (nameList.isEmpty()) -+// return QVariantMap(); -+// QVariantMap ret; -+// for (int i = 0; i < nameList.count(); ++i) { -+// napi_value result = nullptr; -+// QByteArray name = nameList.at(i); -+// napi_get_named_property(m_env, value, name.constData(), &result); -+//// napi_valuetype valueType; -+//// napi_typeof(m_env, value, &valueType); -+//// if (valueType == napi_number) -+//// return 0; -+// } -+// return ret; -+//} -+ -+bool QOpenHarmonyJsEnvironment::getBool(napi_value value) -+{ -+ bool result; -+ NAPI_CALL_BASE(m_env, napi_get_value_bool(m_env, value, &result), false); -+ return result; -+} -+ -+napi_value QOpenHarmonyJsEnvironment::objectPropertyValue(napi_value object, const QString &name) -+{ -+ napi_value result = nullptr; -+ QByteArray data = name.toUtf8(); -+ NAPI_CALL(m_env, napi_get_named_property(m_env, object, data.constData(), &result)); -+ return result; -+} -+ -+napi_value QOpenHarmonyJsEnvironment::createString(const char *value) -+{ -+ napi_value result; -+ NAPI_CALL(m_env, napi_create_string_utf8(m_env, value, NAPI_AUTO_LENGTH, &result)); -+ return result; -+} -+ -+napi_value QOpenHarmonyJsEnvironment::createBool(bool value) -+{ -+ napi_value result; -+ NAPI_CALL(m_env, napi_get_boolean(m_env, value, &result)); -+ return result; -+} -+ -+napi_value QOpenHarmonyJsEnvironment::createInt32(int value) -+{ -+ napi_value result; -+ NAPI_CALL(m_env, napi_create_int32(m_env, value, &result)); -+ return result; -+} -+ -+napi_value QOpenHarmonyJsEnvironment::createInt64(qlonglong value) -+{ -+ napi_value result; -+ NAPI_CALL(m_env, napi_create_int64(m_env, value, &result)); -+ return result; -+} -+ -+int64_t QOpenHarmonyJsEnvironment::getInt64(napi_value value) -+{ -+ int64_t result; -+ NAPI_CALL_BASE(m_env, napi_get_value_int64(m_env, value, &result), 0); -+ return result; -+} -+ -+int32_t QOpenHarmonyJsEnvironment::getInt32(napi_value value) -+{ -+ int32_t result; -+ NAPI_CALL_BASE(m_env, napi_get_value_int32(m_env, value, &result), 0); -+ return result; -+} -+ -+double QOpenHarmonyJsEnvironment::getDouble(napi_value value) -+{ -+// napi_valuetype valueType; -+// napi_typeof(m_env, value, &valueType); -+// if (valueType != napi_number) -+// return 0; -+ double result; -+ NAPI_CALL_BASE(m_env, napi_get_value_double(m_env, value, &result), 0); -+ return result; -+} -+ -+template<> -+void QOpenHarmonyJsEnvironment::getObjectPropertyValue(napi_value object, const QString &propertyName) -+{ -+ -+} -+ -+template<> -+QByteArray QOpenHarmonyJsEnvironment::getObjectPropertyValue(napi_value object, const QString &propertyName) -+{ -+ napi_value value = objectPropertyValue(object, propertyName); -+ return getByteArray(value); -+} -+ -+template<> -+QString QOpenHarmonyJsEnvironment::getObjectPropertyValue(napi_value object, const QString &propertyName) -+{ -+ napi_value value = objectPropertyValue(object, propertyName); -+ return getString(value); -+} -+ -+template<> -+bool QOpenHarmonyJsEnvironment::getObjectPropertyValue(napi_value object, const QString &propertyName) -+{ -+ napi_value value = objectPropertyValue(object, propertyName); -+ return getBool(value); -+} -+ -+template<> -+int64_t QOpenHarmonyJsEnvironment::getObjectPropertyValue(napi_value object, const QString &propertyName) -+{ -+ napi_value value = objectPropertyValue(object, propertyName); -+ return getInt64(value); -+} -+ -+template<> -+int32_t QOpenHarmonyJsEnvironment::getObjectPropertyValue(napi_value object, const QString &propertyName) -+{ -+ napi_value value = objectPropertyValue(object, propertyName); -+ return getInt32(value); -+} -+ -+template<> -+double QOpenHarmonyJsEnvironment::getObjectPropertyValue(napi_value object, const QString &propertyName) -+{ -+ napi_value value = objectPropertyValue(object, propertyName); -+ return getDouble(value); -+} -+ -+template<> -+void *QOpenHarmonyJsEnvironment::getValue(napi_value value) -+{ -+ return value; -+} -+ -+template<> -+QByteArray QOpenHarmonyJsEnvironment::getValue(napi_value value) -+{ -+ return getByteArray(value); -+} -+ -+template<> -+QString QOpenHarmonyJsEnvironment::getValue(napi_value value) -+{ -+ return getString(value); -+} -+ -+template<> -+bool QOpenHarmonyJsEnvironment::getValue(napi_value value) -+{ -+ return getBool(value); -+} -+ -+template<> -+int64_t QOpenHarmonyJsEnvironment::getValue(napi_value value) -+{ -+ return getInt64(value); -+} -+ -+template<> -+int32_t QOpenHarmonyJsEnvironment::getValue(napi_value value) -+{ -+ return getInt32(value); -+} -+ -+template<> -+double QOpenHarmonyJsEnvironment::getValue(napi_value value) -+{ -+ return getDouble(value); -+} -+ -+QRect QOpenHarmonyJsEnvironment::getRect(napi_value value) -+{ -+ int64_t left = getInt64(objectPropertyValue(value, "left")); -+ int64_t top = getInt64(objectPropertyValue(value, "top")); -+ int64_t width = getInt64(objectPropertyValue(value, "width")); -+ int64_t height = getInt64(objectPropertyValue(value, "height")); -+ return QRect(left, top, width, height); -+} -diff --git a/src/corelib/kernel/qopenharmonyjsenvironment.h b/src/corelib/kernel/qopenharmonyjsenvironment.h -new file mode 100644 -index 0000000000..0718f7ee9a ---- /dev/null -+++ b/src/corelib/kernel/qopenharmonyjsenvironment.h -@@ -0,0 +1,156 @@ -+#ifndef QOPENHARMONYJSENVIRONMENT_H -+#define QOPENHARMONYJSENVIRONMENT_H -+ -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+#define qJs QOpenHarmonyJsEnvironment -+class QOpenHarmonyJsFunction; -+ -+struct NapiValueCreator -+{ -+ virtual napi_value create(const QVariant &value) = 0; -+ virtual ~NapiValueCreator() {} -+}; -+ -+template -+struct UserTypeNapiValueCreator : NapiValueCreator -+{ -+ UserTypeNapiValueCreator(std::function &f) : _f(f) {} -+ virtual napi_value create(const QVariant &value) -+ { -+ if (_f && value.canConvert()) { -+ return _f(value.value()); -+ } -+ return nullptr; -+ } -+ std::function _f; -+}; -+ -+class Q_CORE_EXPORT QOpenHarmonyJsEnvironment -+{ -+public: -+ QOpenHarmonyJsEnvironment(); -+ ~QOpenHarmonyJsEnvironment(); -+ -+ static void init(napi_env env); -+ -+ static void quit(); -+ -+ static void throwError(const QString &errorMessage); -+ -+ static napi_env env() { return m_env; } -+ -+ static napi_value createString(const char *value); -+ -+ static napi_value createString(const QString &value); -+ -+ static napi_value createBool(bool value); -+ -+ static napi_value createInt32(int value); -+ -+ static napi_value createInt64(qlonglong value); -+ -+ static napi_value createStringArray(const QStringList &strList); -+ -+ static napi_value createArrayBuffer(const QByteArray &buffer); -+ -+ static napi_value createObject(const QVariantMap &map); -+ -+ static napi_value createObjectFromUserType(const QVariant &value); -+ -+ template -+ static void registerCreator(std::function &f) -+ { -+ int id = qMetaTypeId(); -+ NapiValueCreator *creator = new UserTypeNapiValueCreator(f); -+ registerCreator(id, creator); -+ } -+ -+ static void registerCreator(int type, NapiValueCreator *creator); -+ -+ static napi_value globalObject(); -+ -+ static napi_value globalThis() -+ { -+ return objectPropertyValue(globalObject(), QLatin1String("globalThis")); -+ } -+ -+ static napi_value jsObject(const QString &name) -+ { -+ return objectPropertyValue(globalThis(), name); -+ } -+ -+ static napi_value function(napi_value jsObject, const QString &functionName) -+ { -+ return objectPropertyValue(jsObject, functionName); -+ } -+ -+ template -+ static T getObjectPropertyValue(napi_value object, const QString &propertyName); -+ -+ template -+ static T getValue(napi_value value); -+ -+ static napi_value callFunction(napi_value object, napi_value function, int argc = 0, const napi_value *argv = nullptr); -+ -+ static bool callThreadSafeFunction(QOpenHarmonyJsFunction *func); -+ -+ static QString getString(napi_value value); -+ -+ static QList getNumbers(napi_value value); -+ static QList getRealNumbers(napi_value value); -+ -+ static QStringList getStringList(napi_value value); -+ -+ static QByteArray getByteArray(napi_value value); -+ -+ static QByteArrayList getByteArrayList(napi_value value); -+ -+ static bool getBool(napi_value value); -+ -+ static int64_t getInt64(napi_value value); -+ -+ static int32_t getInt32(napi_value value); -+ -+ static double getDouble(napi_value value); -+ -+ static QRect getRect(napi_value value); -+ -+ static napi_value objectPropertyValue(napi_value object, const QString &propertyName); -+ -+ static napi_value initJsObjectLoader(napi_env env, napi_callback_info info); -+ static napi_value initRemoveObject(napi_env env, napi_callback_info info); -+private: -+ static napi_env m_env; -+ static bool m_exit; -+}; -+ -+template<> -+inline QList QOpenHarmonyJsEnvironment::getValue>(napi_value value) -+{ -+ return getNumbers(value); -+} -+ -+template<> -+inline QList QOpenHarmonyJsEnvironment::getValue(napi_value value) -+{ -+ return getRealNumbers(value); -+} -+ -+template<> -+inline QByteArrayList QOpenHarmonyJsEnvironment::getValue(napi_value value) -+{ -+ return getByteArrayList(value); -+} -+ -+template<> -+inline QStringList QOpenHarmonyJsEnvironment::getValue(napi_value value) -+{ -+ return getStringList(value); -+} -+ -+QT_END_NAMESPACE -+#endif // QOPENHARMONYJSENVIRONMENT_H -diff --git a/src/corelib/kernel/qopenharmonyjsfunction.cpp b/src/corelib/kernel/qopenharmonyjsfunction.cpp -new file mode 100644 -index 0000000000..dbe216de8f ---- /dev/null -+++ b/src/corelib/kernel/qopenharmonyjsfunction.cpp -@@ -0,0 +1,207 @@ -+#include "qopenharmonyjsfunction.h" -+#include "qopenharmonyjsenvironment.h" -+#include "qopenharmonydefines.h" -+#include "qopenharmonyhelpers_p.h" -+#include "qopenharmonyjsobject.h" -+ -+#include -+#include -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyJsFunctionPrivate -+{ -+public: -+ QOpenHarmonyJsFunctionPrivate(); -+ void init(napi_value function, const QString &functionName); -+ QString jsObjectName() const; -+ const QVariantList &args() const; -+ static napi_value then(napi_env, napi_callback_info info); -+ -+ void setResult(napi_value result); -+ void wait(int timeout = 5000); -+ -+ bool hasResult() const; -+ -+ QOpenHarmonyJsFunction *q_ptr; -+ QString m_name; -+ QVariantList m_args; -+ QOpenHarmonyJsObject *m_jsObject; -+ QVariant m_result; -+ int m_resultType; -+ napi_ref m_objectRef; -+ QBasicAtomicInt m_bResultReceived; // bool -+}; -+ -+QOpenHarmonyJsFunctionPrivate::QOpenHarmonyJsFunctionPrivate() -+ : m_resultType(QVariant::Invalid) -+ , m_objectRef(nullptr) -+{ -+ m_bResultReceived.storeRelease(false); -+} -+ -+ -+const QVariantList &QOpenHarmonyJsFunctionPrivate::args() const -+{ -+ return m_args; -+} -+ -+QString QOpenHarmonyJsFunctionPrivate::jsObjectName() const -+{ -+ return m_jsObject->objectName(); -+} -+ -+void QOpenHarmonyJsFunctionPrivate::wait(int timeout) -+{ -+ QElapsedTimer start; -+ start.start(); -+ while (!m_bResultReceived.loadAcquire()) { -+ if (start.elapsed() > timeout) -+ break; -+ } -+ if (!m_bResultReceived.loadAcquire()) { -+ LOGW("Wait js method %{public}s result failded", qPrintable(m_name)); -+ } -+} -+ -+bool QOpenHarmonyJsFunctionPrivate::hasResult() const -+{ -+ return m_resultType != QVariant::Invalid && m_resultType != QMetaType::Void; -+} -+ -+napi_value QOpenHarmonyJsFunctionPrivate::then(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 1; -+ napi_value argv[1] = {nullptr}; -+ void *data = nullptr; -+ napi_get_cb_info(env, info, &argc, argv, nullptr, &data); -+ QOpenHarmonyJsFunctionPrivate *function = reinterpret_cast(data); -+ if (function != nullptr) -+ function->setResult(argv[0]); -+ return argv[0]; -+} -+ -+void QOpenHarmonyJsFunctionPrivate::setResult(napi_value result) -+{ -+ if (m_resultType == QOpenHarmonyJsFunction::JsRefType) { -+ LOGI("Get js method %1 result object ref from callback: %{public}s", qPrintable(m_name)); -+ napi_create_reference(qJs::env(), result, 1, &m_objectRef); -+ m_bResultReceived.storeRelease(true); -+ return; -+ } -+ m_result = QtHarmonyPrivate::napi_value_to_variant(result, m_resultType); -+ if (m_result.type() == QVariant::ByteArray) { -+ LOGI("Get js method %{public}s result from callback QByteArrary length: %{public}d", qPrintable(m_name), m_result.toByteArray().length()); -+ } else { -+ LOGI("Get js method %{public}s result from callback: %{public}s", qPrintable(m_name), qPrintable(m_result.toString())); -+ } -+ m_bResultReceived.storeRelease(true); -+} -+ -+QOpenHarmonyJsFunction::QOpenHarmonyJsFunction(const QOpenHarmonyJsObject *jsObject, const QString &functionName) -+{ -+ d_ptr.reset(new QOpenHarmonyJsFunctionPrivate); -+ d_ptr->q_ptr = this; -+ d_ptr->m_jsObject = const_cast(jsObject); -+ d_ptr->m_name = functionName; -+} -+ -+void QOpenHarmonyJsFunction::call(napi_value jsObject, napi_value function) -+{ -+ Q_D(QOpenHarmonyJsFunction); -+ const QVariantList &args = d->args(); -+ -+ napi_value result = nullptr; -+ if (args.isEmpty()) { -+ result = qJs::callFunction(jsObject, function); -+ } -+ else { -+ size_t argc = static_cast(args.count()); -+ QScopedArrayPointer argv(new napi_value[argc]); -+ for (int i = 0; i < args.count(); ++i) { -+ argv[i] = nullptr; -+ argv[i] = QtHarmonyPrivate::variant_to_napi_value(args.at(i)); -+ } -+ result = qJs::callFunction(jsObject, function, argc, argv.get()); -+ } -+ if (!d->hasResult()) -+ return; -+ bool is_promise = false; -+ napi_is_promise(qJs::env(), result, &is_promise); -+ napi_valuetype type; -+ napi_typeof(qJs::env(), result, &type); -+ LOGI("the result type is %{public}d the result is promise: %{public}d", type, is_promise); -+ if (is_promise) { -+ napi_value then = qJs::objectPropertyValue(result, "then"); -+ napi_value func = nullptr; -+ napi_create_function(qJs::env(), "thenResult", 1, QOpenHarmonyJsFunctionPrivate::then, d, &func); -+ napi_value _argv[1] = {func}; -+ //trigger then method -+ result = qJs::callFunction(result, then, 1, _argv); -+ } else { -+ d->setResult(result); -+ } -+} -+ -+QOpenHarmonyJsFunction::~QOpenHarmonyJsFunction() -+{ -+ -+} -+ -+void QOpenHarmonyJsFunction::call(const QVariantList &args) -+{ -+ d_ptr->m_bResultReceived.storeRelease(false); -+ d_ptr->m_args = args; -+ LOGI("call js function %{public}s", qPrintable(d_ptr->m_name)); -+ if (!qJs::callThreadSafeFunction(this)) -+ return; -+ if (!d_ptr->hasResult()) { -+ // 没有返回值的不等待 -+ return; -+ } -+ d_ptr->wait(); -+} -+ -+QVariant QOpenHarmonyJsFunction::jsResult() const -+{ -+ return d_ptr->m_result; -+} -+ -+napi_ref QOpenHarmonyJsFunction::refResult() const -+{ -+ Q_D(const QOpenHarmonyJsFunction); -+ return d->m_objectRef; -+} -+ -+napi_value QOpenHarmonyJsFunction::attachedObject() const { -+ Q_D(const QOpenHarmonyJsFunction); -+ return d->m_jsObject->jsObject(); -+} -+ -+QString QOpenHarmonyJsFunction::name() const -+{ -+ Q_D(const QOpenHarmonyJsFunction); -+ return d->m_name; -+} -+ -+QString QOpenHarmonyJsFunction::objectName() -+{ -+ Q_D(const QOpenHarmonyJsFunction); -+ return d->jsObjectName(); -+} -+ -+void QOpenHarmonyJsFunction::cancel() -+{ -+ Q_D(QOpenHarmonyJsFunction); -+ d->m_bResultReceived.storeRelease(true); -+} -+ -+void QOpenHarmonyJsFunction::setResultType(int t) -+{ -+ d_ptr->m_resultType = t; -+} -+ -+QT_END_NAMESPACE -diff --git a/src/corelib/kernel/qopenharmonyjsfunction.h b/src/corelib/kernel/qopenharmonyjsfunction.h -new file mode 100644 -index 0000000000..17fb051a7b ---- /dev/null -+++ b/src/corelib/kernel/qopenharmonyjsfunction.h -@@ -0,0 +1,94 @@ -+#ifndef QOPENHARMONYJSFUNCTION_H -+#define QOPENHARMONYJSFUNCTION_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+QT_BEGIN_NAMESPACE -+class QOpenHarmonyJsObject; -+class QOpenHarmonyJsFunctionPrivate; -+ -+class Q_CORE_EXPORT QOpenHarmonyJsFunction -+{ -+ Q_DECLARE_PRIVATE(QOpenHarmonyJsFunction) -+public: -+ enum { JsRefType = QMetaType::User + 1000 }; -+ ~QOpenHarmonyJsFunction(); -+ -+ template -+ void callReturnRef(Args &&...args) { -+ QVariantList params; -+ setResultType(JsRefType); -+ call(packParams(params, std::forward(args)...)); -+ } -+ -+ template -+ void call(Args&&... args) -+ { -+ QVariantList params; -+ setResultType(qMetaTypeId()); -+ call(packParams(params, std::forward(args)...)); -+ } -+ -+ template -+ void callWithoutReturn(Args&&... args) -+ { -+ QVariantList params; -+ call(packParams(params, std::forward(args)...)); -+ } -+ -+ void call(const QVariantList &args); -+ -+ template -+ RETURN_TYPE result() const; -+ -+ QVariant jsResult() const; -+ -+ napi_ref refResult() const; -+ -+ napi_value attachedObject() const; -+ -+ QString name() const; -+ -+ QString objectName(); -+ -+ void cancel(); -+ -+ void call(napi_value jsObject, napi_value function); -+private: -+ template -+ const QVariantList &packParams(QVariantList& paramList, Arg0&& arg, Args&&... args) -+ { -+ QVariant v; -+ v.setValue(std::forward(arg)); -+ paramList.push_back(v); -+ packParams(paramList, std::forward(args)...); -+ return paramList; -+ } -+ -+ const QVariantList& packParams(QVariantList& paramList) -+ { -+ return paramList; -+ } -+ -+ void setResultType(int t); -+ -+ friend class QOpenHarmonyJsObject; -+ QOpenHarmonyJsFunction(const QOpenHarmonyJsObject *jsObject, const QString &functionName); -+ -+ QScopedPointer d_ptr; -+}; -+ -+template -+RET QOpenHarmonyJsFunction::result() const -+{ -+ return jsResult().value(); -+} -+ -+QT_END_NAMESPACE -+#endif // QOPENHARMONYJSFUNCTION_H -diff --git a/src/corelib/kernel/qopenharmonyjsobject.cpp b/src/corelib/kernel/qopenharmonyjsobject.cpp -new file mode 100644 -index 0000000000..1680e85b69 ---- /dev/null -+++ b/src/corelib/kernel/qopenharmonyjsobject.cpp -@@ -0,0 +1,99 @@ -+#include "qopenharmonyjsobject.h" -+#include "qopenharmonydefines.h" -+#include "qopenharmonyjsfunction.h" -+#include "qopenharmonyjsobjectpool.h" -+#include "qopenharmonyhelpers_p.h" -+#include "qopenharmonyjsenvironment.h" -+ -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyJsObjectPrivate -+{ -+ QOpenHarmonyJsObject *q_ptr; -+ Q_DECLARE_PUBLIC(QOpenHarmonyJsObject) -+public: -+ QOpenHarmonyJsObjectPrivate(const QString &objectType, const QString &objectName); -+ virtual ~QOpenHarmonyJsObjectPrivate(); -+ QOpenHarmonyJsFunction *createJsFunction(const QString &jsFunction); -+ -+ napi_ref m_jsObject = nullptr; -+ QString m_objectType; -+ QString m_objectName; -+ -+ QHash m_functions; -+}; -+ -+QOpenHarmonyJsObject::QOpenHarmonyJsObject(const QString &objectType, const QString &objectName, napi_value jsObject) -+ : d_ptr(new QOpenHarmonyJsObjectPrivate(objectType, objectName)) -+{ -+ d_ptr->q_ptr = this; -+ // 必须使用ref, 否则会崩溃 -+ napi_create_reference(qJs::env(), jsObject, 1, &d_ptr->m_jsObject); -+} -+ -+QOpenHarmonyJsObject::~QOpenHarmonyJsObject() -+{ -+ napi_delete_reference(qJs::env(), d_ptr->m_jsObject); -+} -+ -+bool QOpenHarmonyJsObject::isValid() const -+{ -+ Q_D(const QOpenHarmonyJsObject); -+ return d->m_jsObject != nullptr; -+} -+ -+QOpenHarmonyJsFunction *QOpenHarmonyJsObject::getJsFunction(const QString &name) const -+{ -+ Q_D(const QOpenHarmonyJsObject); -+ if (d->m_functions.contains(name)) { -+ return d->m_functions.value(name); -+ } -+ -+ QOpenHarmonyJsFunction *function = new QOpenHarmonyJsFunction(this, name); -+ const_cast(d)->m_functions.insert(name, function); -+ return function; -+} -+ -+QString QOpenHarmonyJsObject::uniqueName() const -+{ -+ return objectType() + "_" + objectName(); -+} -+ -+QString QOpenHarmonyJsObject::objectType() const -+{ -+ Q_D(const QOpenHarmonyJsObject); -+ return d->m_objectType; -+} -+ -+QString QOpenHarmonyJsObject::objectName() const -+{ -+ Q_D(const QOpenHarmonyJsObject); -+ return d->m_objectName; -+} -+ -+napi_value QOpenHarmonyJsObject::jsObject() const -+{ -+ Q_D(const QOpenHarmonyJsObject); -+ napi_value jsObject; -+ napi_get_reference_value(qJs::env(), d->m_jsObject, &jsObject); -+ return jsObject; -+} -+ -+ -+QOpenHarmonyJsObjectPrivate::QOpenHarmonyJsObjectPrivate(const QString &objectType, const QString &objectName) -+ : m_objectType(objectType) -+ , m_objectName(objectName) -+{ -+ -+} -+ -+QOpenHarmonyJsObjectPrivate::~QOpenHarmonyJsObjectPrivate() -+{ -+ qDeleteAll(m_functions); -+ m_functions.clear(); -+} -+ -+QT_END_NAMESPACE -+ -diff --git a/src/corelib/kernel/qopenharmonyjsobject.h b/src/corelib/kernel/qopenharmonyjsobject.h -new file mode 100644 -index 0000000000..6540813735 ---- /dev/null -+++ b/src/corelib/kernel/qopenharmonyjsobject.h -@@ -0,0 +1,79 @@ -+#ifndef QOPENHARMONYJSOBJECT_H -+#define QOPENHARMONYJSOBJECT_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyJsObjectPrivate; -+ -+class Q_CORE_EXPORT QOpenHarmonyJsObject -+{ -+ friend class QOpenHarmonyJsObjectPool; -+ friend class QOpenHarmonyJsFunction; -+ friend class QSharedPointer; -+ Q_DECLARE_PRIVATE(QOpenHarmonyJsObject) -+public: -+ virtual ~QOpenHarmonyJsObject(); -+ -+ bool isValid() const; -+ -+ template -+ napi_ref callReturnRef(const QString &jsFunction, Args &&...args) -+ { -+ QOpenHarmonyJsFunction *f = getJsFunction(jsFunction); -+ if (f == nullptr) -+ return nullptr; -+ -+ f->callReturnRef(std::forward(args)...); -+ return f->refResult(); -+ } -+ -+ template -+ RET call(const QString &jsFunction, Args&&... args); -+ -+ template -+ void callWithoutReturn(const QString &jsFunction, Args&&... args) -+ { -+ QOpenHarmonyJsFunction *f = getJsFunction(jsFunction); -+ if (f == nullptr) -+ return; -+ f->callWithoutReturn(std::forward(args)...); -+ } -+ -+ QOpenHarmonyJsFunction *getJsFunction(const QString &name) const; -+ -+ QString objectName() const; -+ QString objectType() const; -+ -+ QString uniqueName() const; -+ -+private: -+ Q_DISABLE_COPY(QOpenHarmonyJsObject) -+ QOpenHarmonyJsObject(const QString &objectType, const QString &objectName, napi_value jsObject); -+ -+ napi_value jsObject() const; -+ -+ QScopedPointer d_ptr; -+}; -+ -+template -+RET QOpenHarmonyJsObject::call(const QString &jsFunction, Args&&... args) -+{ -+ QOpenHarmonyJsFunction *f = getJsFunction(jsFunction); -+ if (f == nullptr) -+ return RET(); -+ -+ f->call(std::forward(args)...); -+ return f->result(); -+} -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYEGLCORE_H -diff --git a/src/corelib/kernel/qopenharmonyjsobjectloader.cpp b/src/corelib/kernel/qopenharmonyjsobjectloader.cpp -new file mode 100644 -index 0000000000..ef698869ab ---- /dev/null -+++ b/src/corelib/kernel/qopenharmonyjsobjectloader.cpp -@@ -0,0 +1,170 @@ -+#include "qopenharmonyjsobjectloader.h" -+#include "qopenharmonyjsobject.h" -+#include "qopenharmonyjsobjectpool.h" -+#include "qopenharmonyhelpers_p.h" -+#include "qopenharmonydefines.h" -+ -+#include -+#include -+#include -+#include -+ -+Q_GLOBAL_STATIC(QOpenHarmonyJsObjectLoader, loader) -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyJsObjectLoaderPrivate -+{ -+ Q_DECLARE_PUBLIC(QOpenHarmonyJsObjectLoader) -+public: -+ QOpenHarmonyJsObjectLoader *q_ptr; -+ QBasicAtomicInt m_bObjectCreated; // bool -+ -+ void wait(const QString &objectType, const QString &objectName, int timeout); -+ void objectCreated(); -+}; -+ -+static napi_threadsafe_function g_threadsafe_new_object_function = nullptr; -+static napi_threadsafe_function g_threadsafe_remove_object_function = nullptr; -+ -+struct ObjectParams { -+ QOpenHarmonyJsObjectLoaderPrivate *loader; -+ QVariantList constructArgs; -+ QString type; -+ QString name; -+}; -+ -+static void callNewObject(napi_env env, napi_value js_cb, void* context, void* data) { -+ Q_UNUSED(env); -+ Q_UNUSED(context); -+ -+ ObjectParams *p = (ObjectParams*)data; -+ size_t argc = 1 + 1 + static_cast(p->constructArgs.count()); -+ QScopedArrayPointer argv(new napi_value[argc]); -+ argv[0] = qJs::createString(p->type); -+ argv[1] = qJs::createString(p->name); -+ if (!p->constructArgs.isEmpty()) { -+ for (int i = 0; i < p->constructArgs.count(); ++i) { -+ argv[i + 2] = nullptr; -+ argv[i + 2] = QtHarmonyPrivate::variant_to_napi_value(p->constructArgs.at(i)); -+ } -+ } -+ napi_value undefined = nullptr; -+ napi_get_undefined(qJs::env(), &undefined); -+ napi_value object = qJs::callFunction(undefined, js_cb, argc, argv.get()); -+ qJsObjectPool->create(p->type, p->name, object); -+ p->loader->objectCreated(); -+ delete p; -+} -+ -+static void callRemoveObject(napi_env env, napi_value js_cb, void* context, void* data) { -+ Q_UNUSED(env); -+ Q_UNUSED(context); -+ const char *objectName = (const char *)(data); -+ -+ napi_value undefined = nullptr; -+ napi_get_undefined(qJs::env(), &undefined); -+ napi_value argv[1] = {qJs::createString(objectName)}; -+ qJs::callFunction(undefined, js_cb, 1, argv); -+} -+ -+ -+napi_value QOpenHarmonyJsObjectLoader::initJsObjectLoader(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 2; -+ napi_value args[2]; -+ napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); -+ -+ if (argc != 2) { -+ return qJs::createBool(false); -+ } -+ -+ napi_value newObject = args[0]; -+ napi_value removeObject = args[1]; -+ -+ napi_value name_newObject; -+ napi_create_string_utf8(env, "JsObjectLoader", NAPI_AUTO_LENGTH, &name_newObject); -+ napi_create_threadsafe_function(env, newObject, nullptr, name_newObject, 0, 1, nullptr, -+ nullptr, nullptr, callNewObject, &g_threadsafe_new_object_function); -+ -+ napi_value name_removeObject; -+ napi_create_string_utf8(env, "RemoveObject", NAPI_AUTO_LENGTH, &name_removeObject); -+ napi_create_threadsafe_function(env, removeObject, nullptr, name_removeObject, 0, 1, nullptr, -+ nullptr, nullptr, callRemoveObject, &g_threadsafe_remove_object_function); -+ -+ if (g_threadsafe_new_object_function == nullptr || g_threadsafe_remove_object_function == nullptr) -+ return qJs::createBool(false); -+ return qJs::createBool(true); -+} -+ -+QOpenHarmonyJsObjectLoader::QOpenHarmonyJsObjectLoader() -+{ -+ d_ptr.reset(new QOpenHarmonyJsObjectLoaderPrivate); -+ d_ptr->q_ptr = this; -+} -+ -+QOpenHarmonyJsObjectLoader::~QOpenHarmonyJsObjectLoader() -+{ -+ -+} -+ -+QOpenHarmonyJsObjectLoader *QOpenHarmonyJsObjectLoader::instance() -+{ -+ return loader(); -+} -+ -+QSharedPointer QOpenHarmonyJsObjectLoader::create(const QString &objectType, const QString &objectName, const QVariantList &constructArgs) -+{ -+ LOGI("request js object: %{public}s %{public}s", qPrintable(objectType), qPrintable(objectName)); -+ if (objectType.isEmpty()) -+ return QSharedPointer(); -+ -+ QString _objectName = objectName; -+ if (_objectName.isEmpty()) -+ _objectName = objectType; -+ QSharedPointer object = qJsObjectPool->find(objectType, _objectName); -+ if (object.isNull() && g_threadsafe_new_object_function != nullptr) { -+ d_ptr->m_bObjectCreated.storeRelease(false); -+ ObjectParams *p = new ObjectParams; -+ p->constructArgs = constructArgs; -+ p->name = _objectName; -+ p->type = objectType; -+ p->loader = d_ptr.data(); -+ napi_acquire_threadsafe_function(g_threadsafe_new_object_function); -+ napi_call_threadsafe_function(g_threadsafe_new_object_function, p, napi_tsfn_blocking); -+ d_ptr->wait(objectType, _objectName, 3000); -+ } -+ return qJsObjectPool->find(objectType, _objectName); -+} -+ -+void QOpenHarmonyJsObjectLoader::remove(const QString &name) -+{ -+ if (g_threadsafe_remove_object_function != nullptr) { -+ napi_acquire_threadsafe_function(g_threadsafe_remove_object_function); -+ QByteArray dataArray = name.toLatin1(); -+ char *_data = dataArray.data(); -+ napi_call_threadsafe_function(g_threadsafe_remove_object_function, (void *)_data, napi_tsfn_blocking); -+ } -+ qJsObjectPool->remove(name); -+} -+ -+void QOpenHarmonyJsObjectLoaderPrivate::wait(const QString &objectType, const QString &objectName, int timeout) -+{ -+ QElapsedTimer start; -+ start.start(); -+ while (!m_bObjectCreated.loadAcquire()) { -+ if (start.elapsed() > timeout) -+ break; -+ } -+ if (!m_bObjectCreated.loadAcquire()) { -+ LOGW("create type of %{public}s js object %{public}s result failded", qPrintable(objectType), qPrintable(objectName)); -+ } -+} -+ -+void QOpenHarmonyJsObjectLoaderPrivate::objectCreated() -+{ -+ m_bObjectCreated.storeRelease(true); -+} -+ -+QT_END_NAMESPACE -+ -diff --git a/src/corelib/kernel/qopenharmonyjsobjectloader.h b/src/corelib/kernel/qopenharmonyjsobjectloader.h -new file mode 100644 -index 0000000000..6ef2855374 ---- /dev/null -+++ b/src/corelib/kernel/qopenharmonyjsobjectloader.h -@@ -0,0 +1,36 @@ -+#ifndef QOPENHARMONYJSOBJECTLOADER_H -+#define QOPENHARMONYJSOBJECTLOADER_H -+ -+#include -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+#define qJsObjectLoader QOpenHarmonyJsObjectLoader::instance() -+ -+class QOpenHarmonyJsObject; -+class QOpenHarmonyJsObjectLoaderPrivate; -+ -+class Q_CORE_EXPORT QOpenHarmonyJsObjectLoader -+{ -+ Q_DECLARE_PRIVATE(QOpenHarmonyJsObjectLoader) -+public: -+ QOpenHarmonyJsObjectLoader(); -+ virtual ~QOpenHarmonyJsObjectLoader(); -+ -+ static QOpenHarmonyJsObjectLoader *instance(); -+ -+ QSharedPointer create(const QString &objectType, const QString &objectName = QString(), const QVariantList &constructArgs = QVariantList()); -+ -+ void remove(const QString &name); -+ -+ static napi_value initJsObjectLoader(napi_env env, napi_callback_info info); -+private: -+ QScopedPointer d_ptr; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYEGLCORE_H -diff --git a/src/corelib/kernel/qopenharmonyjsobjectpool.cpp b/src/corelib/kernel/qopenharmonyjsobjectpool.cpp -new file mode 100644 -index 0000000000..5812add919 ---- /dev/null -+++ b/src/corelib/kernel/qopenharmonyjsobjectpool.cpp -@@ -0,0 +1,72 @@ -+#include "qopenharmonyjsobjectpool.h" -+#include "qopenharmonydefines.h" -+#include "qopenharmonyjsfunction.h" -+#include "qopenharmonyjsobject.h" -+ -+ -+Q_GLOBAL_STATIC(QOpenHarmonyJsObjectPool, pool) -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyJsObjectPoolPrivate -+{ -+ Q_DECLARE_PUBLIC(QOpenHarmonyJsObjectPool) -+ -+ QOpenHarmonyJsObjectPool *q_ptr; -+ -+ QHash> m_jsObjects; -+ -+ QSharedPointer add(const QString &objectType, const QString &objectName, napi_value jsObject); -+ QString uniqueName(const QString &objectType, const QString &objectName) const; -+}; -+ -+QOpenHarmonyJsObjectPool::QOpenHarmonyJsObjectPool() -+ : d_ptr(new QOpenHarmonyJsObjectPoolPrivate) -+{ -+ d_ptr->q_ptr = this; -+} -+ -+QOpenHarmonyJsObjectPool::~QOpenHarmonyJsObjectPool() -+{ -+ -+} -+ -+QOpenHarmonyJsObjectPool *QOpenHarmonyJsObjectPool::instance() -+{ -+ return pool(); -+} -+ -+QSharedPointer QOpenHarmonyJsObjectPoolPrivate::add(const QString &objectType, const QString &objectName, napi_value jsObject) -+{ -+ QSharedPointer object = QSharedPointer::create(objectType, objectName, jsObject); -+ m_jsObjects.insert(object->uniqueName(), object); -+ return object; -+} -+ -+QString QOpenHarmonyJsObjectPoolPrivate::uniqueName(const QString &objectType, const QString &objectName) const -+{ -+ return objectType + "_" + objectName; -+} -+ -+bool QOpenHarmonyJsObjectPool::remove(const QString &name) -+{ -+ Q_D(QOpenHarmonyJsObjectPool); -+ if (d->m_jsObjects.contains(name)) { -+ QSharedPointer object = d->m_jsObjects.take(name); -+ } -+ return true; -+} -+ -+QSharedPointer QOpenHarmonyJsObjectPool::find(const QString &objectType, const QString &objectName) -+{ -+ Q_D(QOpenHarmonyJsObjectPool); -+ return d->m_jsObjects.value(d->uniqueName(objectType, objectName)); -+} -+ -+void QOpenHarmonyJsObjectPool::create(const QString &objectType, const QString &objectName, napi_value jsObject) -+{ -+ Q_D(QOpenHarmonyJsObjectPool); -+ d->add(objectType, objectName, jsObject); -+} -+ -+QT_END_NAMESPACE -diff --git a/src/corelib/kernel/qopenharmonyjsobjectpool.h b/src/corelib/kernel/qopenharmonyjsobjectpool.h -new file mode 100644 -index 0000000000..460a17ecf1 ---- /dev/null -+++ b/src/corelib/kernel/qopenharmonyjsobjectpool.h -@@ -0,0 +1,39 @@ -+#ifndef QOPENHARMONYJSOBJECTPOOL_H -+#define QOPENHARMONYJSOBJECTPOOL_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyJsObject; -+ -+class QOpenHarmonyJsObjectPoolPrivate; -+ -+#define qJsObjectPool QOpenHarmonyJsObjectPool::instance() -+ -+class QOpenHarmonyJsObjectPool -+{ -+ Q_DECLARE_PRIVATE(QOpenHarmonyJsObjectPool) -+public: -+ QOpenHarmonyJsObjectPool(); -+ virtual ~QOpenHarmonyJsObjectPool(); -+ -+ static QOpenHarmonyJsObjectPool *instance(); -+ -+ bool remove(const QString &name); -+ -+ QSharedPointer find(const QString &objectType, const QString &objectName = QString()); -+ -+ void create(const QString &objectType, const QString &objectName, napi_value jsObject); -+private: -+ QScopedPointer d_ptr; -+}; -+ -+#endif // QOPENHARMONYEGLCORE_H -diff --git a/src/corelib/kernel/qopenharmonyload.cpp b/src/corelib/kernel/qopenharmonyload.cpp -new file mode 100644 -index 0000000000..e980f4cf8d ---- /dev/null -+++ b/src/corelib/kernel/qopenharmonyload.cpp -@@ -0,0 +1,48 @@ -+#include -+#include -+ -+#include "qopenharmonyjsenvironment.h" -+#include "qopenharmonyjsobjectloader.h" -+#include "qopenharmonydefines.h" -+ -+/* -+ * function for module exports -+ */ -+EXTERN_C_START -+static napi_value Init(napi_env env, napi_value exports) -+{ -+ static bool initialized = false; -+ if (initialized) -+ return exports; -+ -+ napi_property_descriptor desc[] ={ -+ DECLARE_NAPI_FUNCTION("initJsObjectLoader", QOpenHarmonyJsObjectLoader::initJsObjectLoader), -+ }; -+ NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); -+ -+ initialized = true; -+ LOGI("init in qt core"); -+ qJs::init(env); -+ return exports; -+} -+EXTERN_C_END -+ -+/* -+ * Napi Module define -+ */ -+static napi_module openharmonyQtCoreModule = { -+ .nm_version = 1, -+ .nm_flags = 0, -+ .nm_filename = nullptr, -+ .nm_register_func = Init, -+ .nm_modname = "Qt5Core", -+ .nm_priv = ((void*)0), -+ .reserved = { 0 }, -+}; -+/* -+ * Module register function -+ */ -+extern "C" __attribute__((constructor)) void RegisterModule(void) -+{ -+ napi_module_register(&openharmonyQtCoreModule); -+} -diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp -index 35c64180d4..396ee5882c 100644 ---- a/src/corelib/plugin/qfactoryloader.cpp -+++ b/src/corelib/plugin/qfactoryloader.cpp -@@ -193,7 +193,11 @@ void QFactoryLoader::update() - continue; - d->loadedPaths << pluginDir; - -+#ifdef Q_OS_OPENHARMONY -+ QString path = pluginDir; -+#else - QString path = pluginDir + d->suffix; -+#endif - - if (qt_debug_component()) - qDebug() << "QFactoryLoader::QFactoryLoader() checking directory path" << path << "..."; -@@ -204,6 +208,8 @@ void QFactoryLoader::update() - QStringList plugins = QDir(path).entryList( - #ifdef Q_OS_WIN - QStringList(QStringLiteral("*.dll")), -+#elif defined(Q_OS_OPENHARMONY) -+ QStringList(QString("libplugins_%1_*.so").arg(d->suffix)), - #endif - QDir::Files); - QLibraryPrivate *library = 0; -@@ -339,6 +345,10 @@ QFactoryLoader::QFactoryLoader(const char *iid, - #if QT_CONFIG(library) - d->cs = cs; - d->suffix = suffix; -+# ifdef Q_OS_OPENHARMONY -+ if (!d->suffix.isEmpty() && d->suffix.at(0) == QLatin1Char('/')) -+ d->suffix.remove(0, 1); -+# endif - - QMutexLocker locker(qt_factoryloader_mutex()); - update(); -diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp -index 695d45d8e7..b10617fdaa 100644 ---- a/src/corelib/thread/qthread_unix.cpp -+++ b/src/corelib/thread/qthread_unix.cpp -@@ -312,7 +312,7 @@ static void setCurrentThreadName(const char *name) - - void *QThreadPrivate::start(void *arg) - { --#if !defined(Q_OS_ANDROID) -+#if !defined(Q_OS_ANDROID) && !defined(Q_OS_OPENHARMONY) - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); - #endif - pthread_cleanup_push(QThreadPrivate::finish, arg); -@@ -354,7 +354,7 @@ void *QThreadPrivate::start(void *arg) - #endif - - emit thr->started(QThread::QPrivateSignal()); --#if !defined(Q_OS_ANDROID) -+#if !defined(Q_OS_ANDROID) && !defined(Q_OS_OPENHARMONY) - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); - pthread_testcancel(); - #endif -@@ -751,7 +751,7 @@ void QThread::start(Priority priority) - - void QThread::terminate() - { --#if !defined(Q_OS_ANDROID) -+#if !defined(Q_OS_ANDROID) && !defined(Q_OS_OPENHARMONY) - Q_D(QThread); - QMutexLocker locker(&d->mutex); - -@@ -793,7 +793,7 @@ void QThread::setTerminationEnabled(bool enabled) - "Current thread was not started with QThread."); - - Q_UNUSED(thr) --#if defined(Q_OS_ANDROID) -+#if defined(Q_OS_ANDROID) || defined(Q_OS_OPENHARMONY) - Q_UNUSED(enabled); - #else - pthread_setcancelstate(enabled ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE, NULL); -diff --git a/src/gui/configure.json b/src/gui/configure.json -index 6dd6f5b16c..65d59f07be 100644 ---- a/src/gui/configure.json -+++ b/src/gui/configure.json -@@ -1313,7 +1313,7 @@ - "label": "OpenGL ES 2.0", - "enable": "input.opengl == 'es2'", - "disable": "input.opengl == 'desktop' || input.opengl == 'dynamic' || input.opengl == 'no'", -- "condition": "config.win32 || (!config.watchos && !features.opengl-desktop && libs.opengl_es2)", -+ "condition": "config.openharmony || config.win32 || (!config.watchos && !features.opengl-desktop && libs.opengl_es2)", - "output": [ - "publicFeature", - "publicQtConfig", -@@ -1323,7 +1323,7 @@ - }, - "opengles3": { - "label": "OpenGL ES 3.0", -- "condition": "features.opengles2 && !features.angle && tests.opengles3 && !config.wasm", -+ "condition": "features.opengles2 && !features.angle && !config.wasm", - "output": [ - "publicFeature", - { "type": "define", "name": "QT_OPENGL_ES_3" } -diff --git a/src/gui/configure.pri b/src/gui/configure.pri -index 1b95449a10..b409140a2d 100644 ---- a/src/gui/configure.pri -+++ b/src/gui/configure.pri -@@ -66,6 +66,7 @@ defineTest(qtConfTest_qpaDefaultPlatform) { - else: integrity: name = integrityfb - else: haiku: name = haiku - else: wasm: name = wasm -+ else: openharmony: name = openharmony - else: name = xcb - - $${1}.value = $$name -diff --git a/src/gui/gui.pro b/src/gui/gui.pro -index 06c9cd3939..d45b4d8b8c 100644 ---- a/src/gui/gui.pro -+++ b/src/gui/gui.pro -@@ -70,7 +70,12 @@ qtConfig(angle) { - CMAKE_QT_OPENGL_IMPLEMENTATION = GLESv2 - } else { - qtConfig(egl) { -- CMAKE_EGL_LIBS = $$cmakeProcessLibs($$QMAKE_LIBS_EGL) -+ #FIXME wanghao changed QMAKE_LIBS_EGL is absolute path -+ openharmony { -+ CMAKE_EGL_LIBS = EGL -+ } else { -+ CMAKE_EGL_LIBS = $$cmakeProcessLibs($$QMAKE_LIBS_EGL) -+ } - !isEmpty(QMAKE_LIBDIR_EGL): CMAKE_EGL_LIBDIR += $$cmakeTargetPath($$QMAKE_LIBDIR_EGL) - } - -@@ -78,6 +83,7 @@ qtConfig(angle) { - !isEmpty(QMAKE_INCDIR_OPENGL_ES2): CMAKE_GL_INCDIRS = $$cmakeTargetPaths($$QMAKE_INCDIR_OPENGL_ES2) - CMAKE_OPENGL_INCDIRS = $$cmakePortablePaths($$QMAKE_INCDIR_OPENGL_ES2) - CMAKE_OPENGL_LIBS = $$cmakeProcessLibs($$QMAKE_LIBS_OPENGL_ES2) -+ openharmony:CMAKE_OPENGL_LIBS=GLESv3 #FIXME wanghao changed CMAKE_OPENGL_LIBS is absolute path - !isEmpty(QMAKE_LIBDIR_OPENGL_ES2): CMAKE_OPENGL_LIBDIR = $$cmakePortablePaths($$QMAKE_LIBDIR_OPENGL_ES2) - CMAKE_GL_HEADER_NAME = GLES2/gl2.h - CMAKE_QT_OPENGL_IMPLEMENTATION = GLESv2 -diff --git a/src/gui/kernel/qscreen.cpp b/src/gui/kernel/qscreen.cpp -index f208eb02be..9033d0730c 100644 ---- a/src/gui/kernel/qscreen.cpp -+++ b/src/gui/kernel/qscreen.cpp -@@ -392,7 +392,11 @@ QRect QScreen::geometry() const - QRect QScreen::availableGeometry() const - { - Q_D(const QScreen); -+#ifdef Q_OS_OPENHARMONY -+ return d->platformScreen->availableGeometry(); -+#else - return d->availableGeometry; -+#endif - } - - /*! -diff --git a/src/gui/kernel/qsimpledrag.cpp b/src/gui/kernel/qsimpledrag.cpp -index bd409c124f..29bb587b12 100644 ---- a/src/gui/kernel/qsimpledrag.cpp -+++ b/src/gui/kernel/qsimpledrag.cpp -@@ -248,7 +248,7 @@ void QBasicDrag::recreateShapedPixmapWindow(QScreen *screen, const QPoint &pos) - - m_drag_icon_window->setUseCompositing(m_useCompositing); - m_drag_icon_window->setPixmap(m_drag->pixmap()); -- m_drag_icon_window->setHotspot(m_drag->hotSpot()); -+ m_drag_icon_window->setHotspot(m_drag->hotSpot()); - m_drag_icon_window->updateGeometry(pos); - m_drag_icon_window->setVisible(true); - } -diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp -index 60353cb03e..b8b7290cbf 100644 ---- a/src/network/access/qnetworkaccessfilebackend.cpp -+++ b/src/network/access/qnetworkaccessfilebackend.cpp -@@ -57,6 +57,9 @@ QStringList QNetworkAccessFileBackendFactory::supportedSchemes() const - << QStringLiteral("qrc"); - #if defined(Q_OS_ANDROID) - schemes << QStringLiteral("assets"); -+#endif -+#if defined(Q_OS_OPENHARMONY) -+ schemes << QStringLiteral("rawfile"); - #endif - return schemes; - } -@@ -80,6 +83,9 @@ QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op, - if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0 - #if defined(Q_OS_ANDROID) - || url.scheme().compare(QLatin1String("assets"), Qt::CaseInsensitive) == 0 -+#endif -+#if defined(Q_OS_OPENHARMONY) -+ || url.scheme().compare(QLatin1String("rawfile"), Qt::CaseInsensitive) == 0 - #endif - || url.isLocalFile()) { - return new QNetworkAccessFileBackend; -@@ -136,6 +142,11 @@ void QNetworkAccessFileBackend::open() - if (url.scheme() == QLatin1String("assets")) - fileName = QLatin1String("assets:") + url.path(); - else -+#endif -+#if defined(Q_OS_OPENHARMONY) -+ if (url.scheme() == QLatin1String("rawfile")) -+ fileName = QLatin1String("rawfile:") + url.path(); -+ else - #endif - fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery); - } -diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp -index 31b1dbf2a5..c42d41d696 100644 ---- a/src/network/access/qnetworkaccessmanager.cpp -+++ b/src/network/access/qnetworkaccessmanager.cpp -@@ -1378,6 +1378,9 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera - if (isLocalFile - #ifdef Q_OS_ANDROID - || scheme == QLatin1String("assets") -+#endif -+#ifdef Q_OS_OPENHARMONY -+ || scheme == QLatin1String("rawfile") - #endif - || scheme == QLatin1String("qrc")) { - return new QNetworkReplyFileImpl(this, req, op); -diff --git a/src/network/access/qnetworkreplyfileimpl.cpp b/src/network/access/qnetworkreplyfileimpl.cpp -index ef319ebf0d..7f531f5b47 100644 ---- a/src/network/access/qnetworkreplyfileimpl.cpp -+++ b/src/network/access/qnetworkreplyfileimpl.cpp -@@ -109,6 +109,11 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con - if (scheme == QLatin1String("assets")) - fileName = QLatin1String("assets:") + url.path(); - else -+#endif -+#if defined(Q_OS_OPENHARMONY) -+ if (scheme == QLatin1String("rawfile")) -+ fileName = QLatin1String("rawfile:") + url.path(); -+ else - #endif - fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery); - } -diff --git a/src/network/socket/qabstractsocketengine.cpp b/src/network/socket/qabstractsocketengine.cpp -index 3fffff6d5a..f461b555dc 100644 ---- a/src/network/socket/qabstractsocketengine.cpp -+++ b/src/network/socket/qabstractsocketengine.cpp -@@ -117,8 +117,7 @@ QAbstractSocketEngine *QAbstractSocketEngine::createSocketEngine(QAbstractSocket - // only NoProxy can have reached here - if (proxy.type() != QNetworkProxy::NoProxy) - return 0; --#endif -- -+#endif - return new QNativeSocketEngine(parent); - } - -diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp -index 8947a7ee8a..11ad26f306 100644 ---- a/src/network/socket/qnativesocketengine.cpp -+++ b/src/network/socket/qnativesocketengine.cpp -@@ -440,7 +440,7 @@ QNativeSocketEngine::~QNativeSocketEngine() - broadcast enabled. - */ - bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol protocol) --{ -+{ - Q_D(QNativeSocketEngine); - if (isValid()) - close(); -@@ -691,15 +691,20 @@ bool QNativeSocketEngine::bind(const QHostAddress &address, quint16 port) - */ - bool QNativeSocketEngine::listen() - { -+ qWarning() << "<-----------QNativeSocketEngine::listen()---000"; - Q_D(QNativeSocketEngine); -+ qWarning() << "<-----------QNativeSocketEngine::listen()---111"; - Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::listen(), false); -+ qWarning() << "<-----------QNativeSocketEngine::listen()---222"; - Q_CHECK_STATE(QNativeSocketEngine::listen(), QAbstractSocket::BoundState, false); - #ifndef QT_NO_SCTP - Q_CHECK_TYPES(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket, - QAbstractSocket::SctpSocket, false); - #else -+ qWarning() << "<-----------QNativeSocketEngine::listen()---333"; - Q_CHECK_TYPE(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket, false); - #endif -+ qWarning() << "<-----------QNativeSocketEngine::listen()---444"; - - // We're using a backlog of 50. Most modern kernels support TCP - // syncookies by default, and if they do, the backlog is ignored. -diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp -index e7e4d64c32..7a6dfe44bc 100644 ---- a/src/network/socket/qsocks5socketengine.cpp -+++ b/src/network/socket/qsocks5socketengine.cpp -@@ -1906,13 +1906,13 @@ QAbstractSocketEngine * - QSocks5SocketEngineHandler::createSocketEngine(QAbstractSocket::SocketType socketType, - const QNetworkProxy &proxy, QObject *parent) - { -- Q_UNUSED(socketType); -- -+ Q_UNUSED(socketType); - // proxy type must have been resolved by now - if (proxy.type() != QNetworkProxy::Socks5Proxy) { - QSOCKS5_DEBUG << "not proxying"; - return 0; - } -+ - QScopedPointer engine(new QSocks5SocketEngine(parent)); - engine->setProxy(proxy); - return engine.take(); -diff --git a/src/network/socket/qtcpserver.cpp b/src/network/socket/qtcpserver.cpp -index eddf789921..f0f3a62935 100644 ---- a/src/network/socket/qtcpserver.cpp -+++ b/src/network/socket/qtcpserver.cpp -@@ -305,11 +305,10 @@ bool QTcpServer::listen(const QHostAddress &address, quint16 port) - static const QNetworkProxy &proxy = *(QNetworkProxy *)0; - #else - QNetworkProxy proxy = d->resolveProxy(addr, port); --#endif -- -+#endif - delete d->socketEngine; - d->socketEngine = QAbstractSocketEngine::createSocketEngine(d->socketType, proxy, this); -- if (!d->socketEngine) { -+ if (!d->socketEngine) { - d->serverSocketError = QAbstractSocket::UnsupportedSocketOperationError; - d->serverSocketErrorString = tr("Operation on socket is not supported"); - return false; -@@ -318,18 +317,18 @@ bool QTcpServer::listen(const QHostAddress &address, quint16 port) - //copy network session down to the socket engine (if it has been set) - d->socketEngine->setProperty("_q_networksession", property("_q_networksession")); - #endif -- if (!d->socketEngine->initialize(d->socketType, proto)) { -+ if (!d->socketEngine->initialize(d->socketType, proto)) { - d->serverSocketError = d->socketEngine->error(); - d->serverSocketErrorString = d->socketEngine->errorString(); - return false; - } -+ - proto = d->socketEngine->protocol(); - if (addr.protocol() == QAbstractSocket::AnyIPProtocol && proto == QAbstractSocket::IPv4Protocol) - addr = QHostAddress::AnyIPv4; - - d->configureCreatedSocket(); -- -- if (!d->socketEngine->bind(addr, port)) { -+ if (!d->socketEngine->bind(addr, port)) { - d->serverSocketError = d->socketEngine->error(); - d->serverSocketErrorString = d->socketEngine->errorString(); - return false; -diff --git a/src/openharmony/entryability/EntryAbility.ts b/src/openharmony/entryability/EntryAbility.ts -new file mode 100644 -index 0000000000..6a4626655c ---- /dev/null -+++ b/src/openharmony/entryability/EntryAbility.ts -@@ -0,0 +1,44 @@ -+import hilog from '@ohos.hilog'; -+import UIAbility from '@ohos.app.ability.UIAbility'; -+import Window from '@ohos.window' -+import JsApplication from '../native/QtCore/JsApplication' -+import JsDataStore from '../native/QtCore/JsDataStore' -+import JSLogger from '../native/QtCore/JsLogger' -+ -+export default class EntryAbility extends UIAbility { -+ -+ onCreate(want, launchParam) { -+ JsDataStore.setContext(this.context); -+ JsDataStore.setElementName({ -+ bundleName: want.bundleName, -+ abilityName: want.abilityName, -+ moduleName: want.moduleName -+ }); -+ } -+ -+ onDestroy() { -+ JSLogger.info('%{public}s', 'Ability onDestroy'); -+ JsApplication.quit(); -+ } -+ -+ onWindowStageCreate(windowStage: Window.WindowStage) { -+ // Main window is created, set main page for this ability -+ JSLogger.info('%{public}s', 'Ability onWindowStageCreate'); -+ JsApplication.run(windowStage); -+ } -+ -+ onWindowStageDestroy() { -+ // Main window is destroyed, release UI related resources -+ JSLogger.info('%{public}s', 'Ability onWindowStageDestroy'); -+ } -+ -+ onForeground() { -+ // Ability has brought to foreground -+ JSLogger.info('%{public}s', 'Ability onForeground'); -+ } -+ -+ onBackground() { -+ // Ability has back to background -+ JSLogger.info('%{public}s', 'Ability onBackground'); -+ } -+}; -diff --git a/src/openharmony/native/QtCore/JsApplication.ts b/src/openharmony/native/QtCore/JsApplication.ts -new file mode 100644 -index 0000000000..0e5e739335 ---- /dev/null -+++ b/src/openharmony/native/QtCore/JsApplication.ts -@@ -0,0 +1,184 @@ -+import font from '@ohos.font'; -+import fs from '@ohos.file.fs'; -+import window from '@ohos.window'; -+import JsLogger from './JsLogger'; -+import display from '@ohos.display'; -+import { JsWindow } from './JsWindow'; -+import { QtQPA } from './JsQtPlatform'; -+import JsDataStore from './JsDataStore'; -+import { Font, UIContext } from '@ohos.arkui.UIContext'; -+import { JsWindowManager } from './JsWindowManager'; -+import resourceManager from '@ohos.resourceManager'; -+ -+class JsApplication { -+ private mainWindow: window.Window = null; -+ private mainWindowName: string = "opemharmony_qt_mainwindow"; -+ private qpa: any = QtQPA; -+ private timerId: number | undefined; -+ -+ getMainWindow(): window.Window { -+ return this.mainWindow; -+ } -+ -+ getMainWindowName(): string { -+ return this.mainWindowName; -+ } -+ -+ addWindow(name: string, window: JsWindow): void { -+ let obj = JsDataStore.getJsObjectLoader().createObject("JsWindowManager", "JsWindowManager"); -+ (obj as JsWindowManager).addWindow(name, window); -+ } -+ -+ updateQtScreen(size: window.Size): void { -+ let d = display.getDefaultDisplaySync(); -+ let titleHeight: number = JsDataStore.windowTitleHeight(); -+ let borderWidth: number = JsDataStore.windowBorderWidth(); -+ JsDataStore.getQtNativeModule("QPA").setDisplayMetrics(size.width - borderWidth - borderWidth, size.height - titleHeight - borderWidth, d.densityDPI, d.scaledDensity, d.xDPI, d.yDPI, -+ 0, 0, size.width - borderWidth - borderWidth, size.height - titleHeight - borderWidth); -+ } -+ -+ async run(windowStage: window.WindowStage) { -+ let qtMajorVersion: number = this.qpa.qtMajorVersion(); -+ let QtCoreModule: any = null; -+ if (qtMajorVersion == 5) -+ QtCoreModule = await import("libQt5Core.so"); -+ else if (qtMajorVersion == 6) -+ QtCoreModule = await import("libQt6Core.so"); -+ if (QtCoreModule == null) { -+ JsLogger.fatal("Cannot load QtCore module"); -+ return; -+ } -+ -+ JsDataStore.setQtMajorVersion(qtMajorVersion); -+ let QtCore = QtCoreModule.default; -+ QtCore.initJsObjectLoader((type: string, name: string, ...args: any[]) => { -+ if (JsDataStore.getJsObjectLoader().hasJsObject(name)) -+ return JsDataStore.getJsObjectLoader().getJsObject(name) -+ return JsDataStore.getJsObjectLoader().createObject(type, name, ...args); -+ }, (name: string) => { -+ return JsDataStore.getJsObjectLoader().deleteJsObject(name); -+ }); -+ JsDataStore.addQtNativeModule("QPA", this.qpa); -+ JsDataStore.addQtNativeModule("QtCore", QtCore); -+ this.mainWindow = windowStage.getMainWindowSync(); -+ let window = new JsWindow(this.mainWindowName, this.mainWindow); -+ let localStore = new LocalStorage({ "name": this.mainWindowName }); -+ await windowStage.loadContent('pages/Index', localStore); -+ this.addWindow(this.mainWindowName, window); -+ if (this.qpa != null) { -+ let p = this.mainWindow.getWindowProperties(); -+ this.updateQtScreen({ width: p.windowRect.width, height: p.windowRect.height }); -+ } -+ this.mainWindow.on("windowSizeChange", this.updateQtScreen); -+ windowStage.on("windowStageEvent", (state) => { -+ if (this.qpa != null) -+ this.qpa.updateApplicationState(state); -+ JsLogger.info("window stage changed %{public}d", state); -+ }); -+ -+ /* NOTE 获取字体信息 */ -+ let mainClass = await windowStage.getMainWindow(); -+ let font: Font = mainClass.getUIContext().getFont(); -+ let fontList: Array = new Array(); -+ for (let f of font.getSystemFontList()) { -+ let fontInfo: font.FontInfo = font.getFontByName(f); -+ fontList.push(fontInfo.path); -+ } -+ this.qpa.setFontInfos(fontList); -+ JsDataStore.setWindowStage(windowStage); -+ -+ await this.extractFilesToCache(); -+ this.loadQtApplication(); -+ } -+ -+ quit() { -+ this.qpa.quitQtApplication(); -+ } -+ -+ loadQtApplication() { -+ this.qpa.setResourceManager(JsDataStore.getResourceManager()); -+ let result : boolean = this.qpa.startQtApplication(JsDataStore.getApplicationDirs(), "libentry.so"); -+ JsLogger.info("load qt application result: %{public}s", result); -+ -+ //周期检测qt应用是否执行完成(1000ms) -+ this.timerId = setInterval(() => { -+ JsLogger.info("check qt app is finish?"); -+ let res = this.qpa.waitForQtAppExit(); -+ if(res == 0) -+ { -+ JsLogger.info("qt application finished"); -+ //鸿蒙程序调用qt程序退出函数 -+ this.quit(); -+ JsDataStore.getContext().getApplicationContext().killAllProcesses(); -+ } -+ }, 1000); -+ } -+ -+ private saveFileToCache(file: resourceManager.RawFileDescriptor, des: string) { -+ let paths = des.split("/").slice(0, -1); -+ let dirs = JsDataStore.getApplicationDirs(); -+ let temp = dirs.cacheDir + "/"; -+ for (let i = 0; i < paths.length; ++i) { -+ temp = temp + paths[i]; -+ let result = fs.accessSync(temp); -+ if (!result) { -+ fs.mkdirSync(temp); -+ } -+ temp = temp + "/"; -+ } -+ -+ let fileName = dirs.cacheDir + "/" + des; -+ // 创建缓存文件(当前是覆盖式创建) -+ let cacheFile = fs.openSync(fileName, -+ fs.OpenMode.WRITE_ONLY | fs.OpenMode.CREATE | fs.OpenMode.TRUNC) -+ let buffer = new ArrayBuffer(4096); -+ let currentOffset = file.offset; -+ let lengthNeedToReed = file.length; -+ let readOption = { -+ offset: currentOffset, -+ length: lengthNeedToReed > buffer.byteLength ? 4096 : lengthNeedToReed -+ } -+ while (true) { -+ // 读取buffer容量的内容 -+ let readLength = fs.readSync(file.fd, buffer, readOption); -+ // 写入buffer容量的内容 -+ fs.writeSync(cacheFile.fd, buffer, { -+ length: readLength -+ }) -+ // 判断后续内容 修改读文件的参数 -+ if (readLength < 4096) { -+ break; -+ } -+ lengthNeedToReed -= readLength; -+ readOption.offset += readLength; -+ readOption.length = lengthNeedToReed > buffer.byteLength ? 4096 : lengthNeedToReed; -+ } -+ fs.close(cacheFile); -+ } -+ -+ async extractFile(src: string) { -+ try { -+ let R = JsDataStore.getResourceManager(); -+ let file: resourceManager.RawFileDescriptor = await R.getRawFd(src); -+ this.saveFileToCache(file, src); -+ } catch (err) { -+ JsLogger.error("extract file failed: %{public}s %{public}s", JSON.stringify(err), src); -+ } -+ } -+ -+ async extractFilesToCache() { -+ try { -+ let R = JsDataStore.getResourceManager(); -+ let rawContent: Uint8Array = await R.getRawFileContent("qt.json"); -+ let str: string = String.fromCharCode.apply(null, rawContent) -+ let files = JSON.parse(str); -+ for (var i = 0; i < files.files.length; ++i) { -+ await this.extractFile(files.files[i]); -+ } -+ } catch (err) { -+ JsLogger.error("read file qt.json failed: %{public}s", JSON.stringify(err)); -+ } -+ } -+} -+ -+export default new JsApplication; -diff --git a/src/openharmony/native/QtCore/JsDataStore.ts b/src/openharmony/native/QtCore/JsDataStore.ts -new file mode 100644 -index 0000000000..be9a25b667 ---- /dev/null -+++ b/src/openharmony/native/QtCore/JsDataStore.ts -@@ -0,0 +1,136 @@ -+import window from '@ohos.window'; -+import HashMap from '@ohos.util.HashMap'; -+import { JsQtModule } from './JsQtModule' -+import deviceInfo from '@ohos.deviceInfo'; -+import common from '@ohos.app.ability.common'; -+import QtJsObjectLoader from './JsObjectLoader'; -+import resourceManager from '@ohos.resourceManager'; -+import bundleManager from '@ohos.bundle.bundleManager'; -+ -+interface ApplicationDirs { -+ tempDir: string, -+ filesDir: string, -+ cacheDir: string, -+ databaseDir: string, -+ bundleCodeDir: string, -+ preferencesDir: string, -+ distributedFilesDir: string, -+ qmlDir: string -+} -+ -+class JsDataStore { -+ private windowName: string; -+ private context: common.UIAbilityContext = null; -+ private windowStage: window.WindowStage; -+ private elementName: bundleManager.ElementName = null; -+ private jsModules: JsQtModule[] = []; -+ private ApplicationDirs: ApplicationDirs; -+ private qtMajorVersion: number = 0; -+ private qtNativeModules: HashMap = new HashMap(); -+ private jsObjectLoader: QtJsObjectLoader = new QtJsObjectLoader; -+ -+ constructor() { -+ } -+ -+ setContext(context: common.UIAbilityContext) { -+ this.context = context; -+ let appContext = this.context.getApplicationContext(); -+ let dirs : ApplicationDirs = { -+ tempDir: appContext.tempDir, -+ filesDir: appContext.filesDir, -+ cacheDir: appContext.cacheDir, -+ databaseDir: appContext.databaseDir, -+ bundleCodeDir: appContext.bundleCodeDir, -+ preferencesDir: appContext.preferencesDir, -+ distributedFilesDir: appContext.distributedFilesDir, -+ qmlDir: appContext.cacheDir + "/Qt/qml" -+ }; -+ this.ApplicationDirs = dirs; -+ } -+ -+ getContext(): common.UIAbilityContext { -+ return this.context; -+ } -+ -+ getApplicationDirs() : ApplicationDirs { -+ return this.ApplicationDirs; -+ } -+ -+ setElementName(en: bundleManager.ElementName) { -+ this.elementName = en; -+ } -+ -+ getElementName(): bundleManager.ElementName { -+ return this.elementName; -+ } -+ -+ -+ setWindowStage(windowStage: window.WindowStage) { -+ this.windowStage = windowStage; -+ } -+ -+ getWindowStage(): window.WindowStage { -+ return this.windowStage; -+ } -+ -+ getResourceManager() : resourceManager.ResourceManager { -+ return this.context.resourceManager; -+ } -+ -+ addJsModule(module: JsQtModule) { -+ this.jsModules.push(module); -+ } -+ -+ getJsModules(): JsQtModule[] { -+ return this.jsModules; -+ } -+ -+ getJsObjectLoader(): QtJsObjectLoader { -+ return this.jsObjectLoader; -+ } -+ -+ getQtMajorVersion(): number { -+ return this.qtMajorVersion; -+ } -+ -+ setQtMajorVersion(version: number) { -+ this.qtMajorVersion = version; -+ } -+ -+ addQtNativeModule(name: string, module: any) { -+ this.qtNativeModules.set(name, module); -+ } -+ -+ getQtNativeModule(name: string): any { -+ return this.qtNativeModules.get(name); -+ } -+ -+ deviceType(): string { -+ // 现目前在pc、pad上模拟成手机端 -+ return 'default'; -+ // return deviceInfo.deviceType; -+ } -+ -+ windowTitleHeight(): number { -+ // Todo read from system -+ return deviceInfo.deviceType == "default" ? 0 : 93; -+ } -+ -+ windowBorderWidth(): number { -+ // Todo read from system -+ return deviceInfo.deviceType == "default" ? 0 : 4; -+ } -+ -+ statusBarHeight(): number { -+ // Todo read from system -+ return deviceInfo.deviceType == "default" ? 0 : 73; -+ } -+ -+ navigationBarHeight(): number { -+ // Todo read from system -+ return deviceInfo.deviceType == "default" ? 0 : 100; -+ } -+ -+} -+ -+export default new JsDataStore; -\ No newline at end of file -diff --git a/src/openharmony/native/QtCore/JsDialog.ts b/src/openharmony/native/QtCore/JsDialog.ts -new file mode 100644 -index 0000000000..5994b55e8b ---- /dev/null -+++ b/src/openharmony/native/QtCore/JsDialog.ts -@@ -0,0 +1,183 @@ -+import promptAction from '@ohos.promptAction' -+import picker from '@ohos.file.picker'; -+import JsDataStore from './JsDataStore' -+import uri from '@ohos.uri'; -+import fs from '@ohos.file.fs'; -+import JsLogger from './JsLogger' -+ -+export class JsDialog { -+ -+ constructor() { -+ } -+ -+ messageBox(handler : number, title : string, text : string, buttons : Array): boolean { -+ var opt: promptAction.ShowDialogOptions = { -+ title: title, -+ message: text, -+ buttons: [{ -+ text: "OK", -+ color: "#000000" -+ }] -+ }; -+ if (buttons != null && buttons.length > 0) { -+ var first : promptAction.Button = { -+ text: buttons[0], -+ color: '#000000', -+ } -+ opt.buttons[0] = first; -+ for (var i = 1; i < buttons.length; i++) { -+ var button : promptAction.Button = { -+ text: buttons[i], -+ color: '#000000', -+ } -+ opt.buttons.push(button) -+ } -+ } -+ -+ promptAction.showDialog(opt, (err, data) => { -+ if (err) { -+ JsLogger.error("show dialog error: %{public}s", JSON.stringify(err)); -+ } -+ let index = err ? -1 : data.index; -+ JsDataStore.getQtNativeModule("QPA").dialogResult(handler, index); -+ }); -+ return true; -+ } -+ -+ isVideo(filter: string): boolean { -+ return filter.includes("mp4") || filter.includes("MPEG") -+ || filter.includes("MPG") || filter.includes("DAT") -+ || filter.includes("MOV") || filter.includes("FLV"); -+ } -+ -+ isAudio(filter: string): boolean { -+ return filter.includes("mp3") || filter.includes("wma") || filter.includes("ogg") || filter.includes("flac") -+ || filter.includes("wv"); -+ } -+ -+ isImage(filter: string): boolean { -+ return filter.includes("png") || filter.includes("jpeg") -+ || filter.includes("jpg") || filter.includes("bmp") -+ } -+ -+ async openFileDialog(handler: number, filter: string) : Promise { -+ // let isImage = this.isImage(filter); -+ // let isVideo = this.isVideo(filter); -+ // let isAudio = this.isAudio(filter); -+ // if (isImage || isVideo) { -+ // const photoSelectOptions = new picker.PhotoSelectOptions(); -+ // if (isImage) -+ // photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; -+ // if (isVideo) -+ // photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.VIDEO_TYPE; -+ // if (isVideo && isImage) -+ // photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE; -+ // photoSelectOptions.maxSelectNumber = 5; -+ // const photoViewPicker = new picker.PhotoViewPicker(); -+ // photoViewPicker.select(photoSelectOptions).then((photoSelectResult) => { -+ // JsLogger.info('photoViewPicker.select to file succeed and uri is: %{public}', uri); -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, photoSelectResult.photoUris); -+ // }).catch((err) => { -+ // JsLogger.error(`Invoke photoViewPicker.select failed, code is %{public}d, message is %{public}s`, err.code, err.message); -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); -+ // }) -+ // } else if (isAudio) { -+ // const audioSelectOptions = new picker.AudioSelectOptions(); -+ // const audioViewPicker = new picker.AudioViewPicker(); -+ // audioViewPicker.select(audioSelectOptions).then(audioSelectResult => { -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, audioSelectResult); -+ // JsLogger.info('audioViewPicker.select to file succeed and uri is: %{public}s', JSON.stringify(audioSelectResult)); -+ // }).catch((err) => { -+ // JsLogger.error(`Invoke audioViewPicker.select failed, code is %{public}d, message is %{public}s`, err.code, err.message); -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); -+ // }) -+ // } else { -+ // const documentSelectOptions = new picker.DocumentSelectOptions(); -+ // const documentViewPicker = new picker.DocumentViewPicker(); -+ // documentViewPicker.select(documentSelectOptions).then((documentResult) => { -+ // JsLogger.info('documentViewPicker.select to file succeed and uri is: %{public}s', JSON.stringify(documentResult)); -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, documentResult); -+ // }).catch((error) => { -+ // JsLogger.error(`Invoke documentViewPicker.select failed, code is %{public}d, message is %{public}s`, error.code, error.message); -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); -+ // }); -+ // } -+ -+ let config = { -+ action: 'ohos.want.action.OPEN_FILE', -+ parameters: { -+ startMode: 'choose', -+ } -+ } -+ -+ let context = JsDataStore.getContext(); -+ -+ context.startAbilityForResult(config).then((result) => { -+ // 获取到文档文件的uri -+ let select_item_list = result.want.parameters.select_item_list; -+ JsLogger.info("select file: %{public}s", select_item_list.toString()); -+ // 有的系统是file://xxx/xxx/xx/xx -+ JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, [select_item_list.toString()]); -+ }).catch((error) => { -+ JsLogger.error("open file dialog result %{public}s", JSON.stringify(error)); -+ JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); -+ }); -+ return true; -+ } -+ -+ async saveFileDialog(handler : number, fileName: string) : Promise { -+ // let isImage = this.isImage(fileName); -+ // let isVideo = this.isVideo(fileName); -+ // let isAudio = this.isAudio(fileName); -+ // if (isImage || isVideo) { -+ // const photoSaveOptions = new picker.PhotoSaveOptions(); -+ // photoSaveOptions.newFileNames = [fileName]; -+ // const photoViewPicker = new picker.PhotoViewPicker(); -+ // photoViewPicker.save(photoSaveOptions).then((photoResult) => { -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, photoResult); -+ // }).catch((error) => { -+ // JsLogger.error(`Invoke documentViewPicker.select failed, code is %{public}d, message is %{public}s`, error.code, error.message); -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); -+ // }); -+ // } else if (isAudio) { -+ // const audioSaveOptions = new picker.AudioSaveOptions(); -+ // audioSaveOptions.newFileNames = [fileName]; -+ // const audioViewPicker = new picker.AudioViewPicker(); -+ // audioViewPicker.save(audioSaveOptions).then((audioResult) => { -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, audioResult); -+ // }).catch((error) => { -+ // JsLogger.error(`Invoke documentViewPicker.select failed, code is %{public}d, message is %{public}s`, error.code, error.message); -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); -+ // }); -+ // } else { -+ // const documentSaveOptions = new picker.DocumentSaveOptions(); -+ // documentSaveOptions.newFileNames = [fileName]; -+ // const documentViewPicker = new picker.DocumentViewPicker(); -+ // documentViewPicker.save(documentSaveOptions).then((documentResult) => { -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, documentResult); -+ // }).catch((error) => { -+ // JsLogger.error(`Invoke documentViewPicker.select failed, code is %{public}d, message is %{public}s`, error.code, error.message); -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); -+ // }); -+ // } -+ // return true; -+ let config = { -+ action: 'ohos.want.action.CREATE_FILE', -+ parameters: { -+ startMode: 'save', -+ key_pick_file_name: [fileName], -+ saveFile: fileName, -+ } -+ } -+ -+ try { -+ let context = JsDataStore.getContext() -+ let result = await context.startAbilityForResult(config); -+ JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, [result.want.parameters.pick_path_return.toString()]); -+ } catch (error) { -+ JsLogger.info("startAbilityForResult Promise.Reject is called, error.code = %{public}s", error.code) -+ JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); -+ } -+ return true; -+ } -+} -diff --git a/src/openharmony/native/QtCore/JsFile.ts b/src/openharmony/native/QtCore/JsFile.ts -new file mode 100644 -index 0000000000..a840215671 ---- /dev/null -+++ b/src/openharmony/native/QtCore/JsFile.ts -@@ -0,0 +1,91 @@ -+import fs from '@ohos.file.fs'; -+import deviceInfo from '@ohos.deviceInfo' -+import JSLogger from './JsLogger' -+ -+export class JsFile { -+ -+ private uri : string = ''; -+ private file : fs.File= null; -+ private valid : boolean = false; -+ private offset : number = 0; -+ -+ constructor(uriString: string) { -+ let _uri = uriString; -+ const versionRegex = /(\d+\.\d+\.\d+\.\d+)/; -+ const match = deviceInfo.displayVersion.match(versionRegex); -+ let version = ''; -+ if (match) { -+ version = match[0] -+ } -+ if (version != '' && version.localeCompare("4.0.7.5") == -1) -+ _uri = _uri.replace("datashare://", "file:") -+ this.uri = _uri; -+ } -+ -+ open(mode: number): boolean { -+ try { -+ JSLogger.info("open file: %{public}s %{public}d", this.uri, mode) -+ this.file = fs.openSync(this.uri, mode); -+ this.valid = true; -+ } catch (error) { -+ JSLogger.error("file open: %{public}s", JSON.stringify(error)); -+ this.valid = false; -+ } finally { -+ return this.valid; -+ } -+ } -+ -+ write(buffer : ArrayBuffer): number { -+ try { -+ return fs.writeSync(this.file.fd, buffer); -+ } catch (error) { -+ JSLogger.error("file write: %{public}s", JSON.stringify(error)); -+ return -1; -+ } -+ } -+ -+ seek(offset: number): boolean { -+ this.offset = offset; -+ return true; -+ } -+ -+ pos(): number { -+ return this.offset; -+ } -+ -+ size(): number { -+ return fs.statSync(this.file.fd).size; -+ } -+ -+ flush(): boolean { -+ return true; -+ } -+ -+ read(length: number): ArrayBuffer { -+ let buf = new ArrayBuffer(length); -+ try { -+ let options = { "offset": this.offset, "length": length } -+ let result = fs.readSync(this.file.fd, buf, options); -+ this.offset = this.offset + result; -+ return buf; -+ } catch (error) { -+ JSLogger.error("file read error: %{public}s", JSON.stringify(error)); -+ return null; -+ } -+ } -+ -+ close(): boolean { -+ if (this.valid) { -+ try { -+ fs.closeSync(this.file); -+ } catch (error) { -+ JSLogger.error("file close %{public}s", JSON.stringify(error)); -+ } finally { -+ this.valid = false; -+ return true; -+ } -+ } else { -+ return true; -+ } -+ } -+} -diff --git a/src/openharmony/native/QtCore/JsInputMethod.ts b/src/openharmony/native/QtCore/JsInputMethod.ts -new file mode 100644 -index 0000000000..0984b1c765 ---- /dev/null -+++ b/src/openharmony/native/QtCore/JsInputMethod.ts -@@ -0,0 +1,286 @@ -+import JsDataStore from './JsDataStore' -+import { BusinessError } from '@ohos.base'; -+import inputMethod from '@ohos.inputMethod'; -+import inputMethodEngine from '@ohos.inputMethodEngine'; -+ -+export class JsInputMethod { -+ private handler: number = 0; -+ private visable: boolean = false; /* ¼뷨visible״̬ */ -+ private attached: boolean = false; -+ private inputMethodController: inputMethod.InputMethodController = inputMethod.getController(); -+ -+ public constructor() { -+ } -+ -+ private subscribe() { -+ try { -+ this.inputMethodController.on('insertText', (text: string) => { -+ console.info('Succeeded in getting callback1 data: ' + JSON.stringify(text)); -+ JsDataStore.getQtNativeModule("QPA").insertText(this.handler, text); -+ }); -+ } catch (err) { -+ console.error(`Failed to subscribe insertText: ${JSON.stringify(err)}`); -+ } -+ -+ try { -+ this.inputMethodController.on('sendFunctionKey', (functionKey: inputMethod.FunctionKey) => { -+ console.warn(`Succeeded in subscribing sendFunctionKey, functionKey.enterKeyType: ${functionKey.enterKeyType}`); -+ }); -+ } catch (err) { -+ console.error(`Failed to subscribe sendFunctionKey: ${JSON.stringify(err)}`); -+ } -+ -+ try { -+ this.inputMethodController.on('sendKeyboardStatus', (keyboardStatus: inputMethod.KeyboardStatus) => { -+ console.info(`Succeeded in subscribing sendKeyboardStatus, keyboardStatus: ${keyboardStatus}`); -+ switch (keyboardStatus) { -+ case inputMethod.KeyboardStatus.HIDE: -+ this.visable = false; -+ this.detach(); -+ break; -+ case inputMethod.KeyboardStatus.SHOW: -+ this.visable = true; -+ break; -+ default: -+ break; -+ } -+ }); -+ } catch (err) { -+ console.error(`Failed to subscribe sendKeyboardStatus: ${JSON.stringify(err)}`); -+ } -+ -+ try { -+ this.inputMethodController.on('deleteLeft', (length: number) => { -+ console.log(`Succeeded in subscribing deleteLeft, length: ${length}`); -+ JsDataStore.getQtNativeModule("QPA").deleteLeft(this.handler, length); -+ }); -+ } catch (err) { -+ console.error(`Failed to subscribe deleteLeft: ${JSON.stringify(err)}`); -+ } -+ -+ try { -+ this.inputMethodController.on('deleteRight', (length: number) => { -+ console.log(`Succeeded in subscribing deleteRight, length: ${length}`); -+ JsDataStore.getQtNativeModule("QPA").deleteRight(this.handler, length); -+ }); -+ } catch (err) { -+ console.error(`Failed to subscribe deleteRight: ${JSON.stringify(err)}`); -+ } -+ -+ try { -+ this.inputMethodController.on('moveCursor', (direction: inputMethod.Direction) => { -+ console.log(`Succeeded in subscribing moveCursor, direction: ${direction}`); -+ JsDataStore.getQtNativeModule("QPA").moveCursor(this.handler, direction); -+ }); -+ } catch (err) { -+ console.error(`Failed to subscribe moveCursor: ${JSON.stringify(err)}`); -+ } -+ -+ try { -+ this.inputMethodController.on('handleExtendAction', (action: inputMethod.ExtendAction) => { -+ console.log(`Succeeded in subscribing handleExtendAction, action: ${action}`); -+ }); -+ } catch (err) { -+ console.error(`Failed to subscribe handleExtendAction: ${JSON.stringify(err)}`); -+ } -+ -+ try { -+ this.inputMethodController.on('selectByRange', (range: inputMethod.Range) => { -+ console.log(`Succeeded in subscribing selectByRange: start: ${range.start} , end: ${range.end}`); -+ }); -+ } catch (err) { -+ console.error(`Failed to subscribe selectByRange: ${JSON.stringify(err)}`); -+ } -+ -+ try { -+ this.inputMethodController.on('selectByMovement', (movement: inputMethod.Movement) => { -+ console.log('Succeeded in subscribing selectByMovement: direction: ' + movement.direction); -+ }); -+ } catch (err) { -+ console.error(`Failed to subscribe selectByMovement: ${JSON.stringify(err)}`); -+ } -+ -+ try { -+ this.inputMethodController.on('getLeftTextOfCursor', (length: number) => { -+ console.info(`Succeeded in subscribing getLeftTextOfCursor, length: ${length}`); -+ let text: string = ""; -+ return text; -+ }); -+ } catch (err) { -+ console.error(`Failed to unsubscribing getLeftTextOfCursor. err: ${JSON.stringify(err)}`); -+ } -+ -+ try { -+ this.inputMethodController.on('getRightTextOfCursor', (length: number) => { -+ console.info(`Succeeded in subscribing getRightTextOfCursor, length: ${length}`); -+ let text: string = ""; -+ return text; -+ }); -+ } catch (err) { -+ console.error(`Failed to subscribe getRightTextOfCursor. err: ${JSON.stringify(err)}`); -+ } -+ -+ try { -+ this.inputMethodController.on('getTextIndexAtCursor', () => { -+ console.info(`Succeeded in subscribing getTextIndexAtCursor.`); -+ let index: number = 0; -+ return index; -+ }); -+ } catch (err) { -+ console.error(`Failed to subscribe getTextIndexAtCursor. err: ${JSON.stringify(err)}`); -+ } -+ } -+ -+ private unsubscribe() { -+ this.inputMethodController.off('moveCursor'); -+ this.inputMethodController.off('insertText'); -+ this.inputMethodController.off('deleteLeft'); -+ this.inputMethodController.off('deleteRight'); -+ this.inputMethodController.off('selectByRange'); -+ this.inputMethodController.off('sendFunctionKey'); -+ this.inputMethodController.off('selectByMovement'); -+ this.inputMethodController.off('sendKeyboardStatus'); -+ this.inputMethodController.off('handleExtendAction'); -+ this.inputMethodController.off('getLeftTextOfCursor'); -+ this.inputMethodController.off('getRightTextOfCursor'); -+ this.inputMethodController.off('getTextIndexAtCursor'); -+ } -+ -+ isInputPanelVisible(): boolean { -+ return this.visable; -+ } -+ -+ isAttached() { -+ return this.attached; -+ } -+ -+ async attach(handler: number, inputType: number, enterType: number): Promise { -+ if (this.attached) { -+ return true; -+ } -+ -+ if (0 == handler) { -+ console.error("handle number is error"); -+ return false; -+ } -+ -+ this.handler = handler; -+ try { -+ let textConfig: inputMethod.TextConfig = { -+ inputAttribute: { -+ textInputType: inputType, -+ enterKeyType: enterType -+ } -+ }; -+ await this.inputMethodController.attach(true, textConfig).then(() => { -+ this.subscribe(); -+ this.attached = true; -+ console.log('Succeeded in attaching inputMethod.'); -+ }).catch((err) => { -+ this.attached = false; -+ console.error(`Failed to attach: ${JSON.stringify(err)}`); -+ }) -+ } catch (err) { -+ this.attached = false; -+ console.error(`Failed to attach: ${JSON.stringify(err)}`); -+ } -+ return this.attached; -+ } -+ -+ async detach(): Promise { -+ this.unsubscribe(); -+ await this.inputMethodController.detach().then(() => { -+ this.attached = false; -+ console.log('Succeeded in detaching inputMethod.'); -+ }).catch((err) => { -+ this.attached = true; -+ console.error(`Failed to detach: ${JSON.stringify(err)}`); -+ }); -+ return !this.attached; -+ } -+ -+ showTextInput() { -+ this.inputMethodController.showTextInput().then(() => { -+ console.log('Succeeded in showing text input.'); -+ }).catch((err) => { -+ console.error(`Failed to showTextInput: ${JSON.stringify(err)}`); -+ }); -+ } -+ -+ hideTextInput() { -+ this.inputMethodController.hideTextInput().then(() => { -+ console.log('Succeeded in hiding inputMethod.'); -+ }).catch((err) => { -+ console.error(`Failed to hideTextInput: ${JSON.stringify(err)}`); -+ }); -+ } -+ -+ setCallingWindow(windowId: number) { -+ try { -+ this.inputMethodController.setCallingWindow(windowId).then(() => { -+ console.log('Succeeded in setting callingWindow.'); -+ }).catch((err) => { -+ console.error(`Failed to setCallingWindow: ${JSON.stringify(err)}`); -+ }) -+ } catch (err) { -+ console.error(`Failed to setCallingWindow: ${JSON.stringify(err)}`); -+ } -+ } -+ -+ updateCursor(left: number, top: number, width: number, height: number) { -+ try { -+ let cursorInfo: inputMethod.CursorInfo = { left: left, top: top, width: width, height: height }; -+ this.inputMethodController.updateCursor(cursorInfo).then(() => { -+ console.log('Succeeded in updating cursorInfo.'); -+ }).catch((err) => { -+ console.error(`Failed to updateCursor: ${JSON.stringify(err)}`); -+ }) -+ } catch (err) { -+ console.error(`Failed to updateCursor: ${JSON.stringify(err)}`); -+ } -+ } -+ -+ changeSelection(text: string, start: number, end: number) { -+ try { -+ this.inputMethodController.changeSelection(text, start, end).then(() => { -+ console.log('Succeeded in changing selection.'); -+ }).catch((err) => { -+ console.error(`Failed to changeSelection: ${JSON.stringify(err)}`); -+ }) -+ } catch (err) { -+ console.error(`Failed to changeSelection: ${JSON.stringify(err)}`); -+ } -+ } -+ -+ stopInputSession() { -+ try { -+ this.inputMethodController.stopInputSession().then((result: boolean) => { -+ if (result) { -+ console.log('Succeeded in stopping inputSession.'); -+ } else { -+ console.error('Failed to stopInputSession.'); -+ } -+ }).catch((err) => { -+ console.error(`Failed to stopInputSession: ${JSON.stringify(err)}`); -+ }) -+ } catch (err) { -+ console.error(`Failed to stopInputSession: ${JSON.stringify(err)}`); -+ } -+ } -+ -+ showSoftKeyboard() { -+ this.inputMethodController.showSoftKeyboard().then(() => { -+ console.log('Succeeded in showing softKeyboard.'); -+ }).catch((err: BusinessError) => { -+ console.error(`Failed to show softKeyboard: ${JSON.stringify(err)}`); -+ }); -+ } -+ -+ hideSoftKeyboard() { -+ this.inputMethodController.hideSoftKeyboard().then(() => { -+ console.log('Succeeded in hiding softKeyboard.'); -+ }).catch((err: BusinessError) => { -+ console.error(`Failed to hide softKeyboard: ${JSON.stringify(err)}`); -+ }); -+ } -+} -\ No newline at end of file -diff --git a/src/openharmony/native/QtCore/JsLogger.ts b/src/openharmony/native/QtCore/JsLogger.ts -new file mode 100644 -index 0000000000..328a60c965 ---- /dev/null -+++ b/src/openharmony/native/QtCore/JsLogger.ts -@@ -0,0 +1,38 @@ -+import hilog from '@ohos.hilog'; -+ -+export class JsLogger { -+ private domain: number; -+ private tag: string; -+ -+ constructor(tag: string) { -+ this.tag = tag; -+ this.domain = 0xFF00; -+ } -+ -+ debug(format: string, ...args: any[]): void { -+ hilog.debug(this.domain, this.tag, format, args); -+ } -+ -+ info(format: string, ...args: any[]): void { -+ hilog.info(this.domain, this.tag, format, args); -+ } -+ -+ warn(format: string, ...args: any[]): void { -+ hilog.warn(this.domain, this.tag, format, args); -+ } -+ -+ error(format: string, ...args: any[]): void { -+ hilog.error(this.domain, this.tag, format, args); -+ } -+ -+ fatal(format: string, ...args: any[]): void { -+ hilog.fatal(this.domain, this.tag, format, args); -+ } -+ -+ isLoggable(level: number): void { -+ hilog.isLoggable(this.domain, this.tag, level); -+ } -+} -+ -+export default new JsLogger('QtForHarmony'); -+ -diff --git a/src/openharmony/native/QtCore/JsObjectLoader.ts b/src/openharmony/native/QtCore/JsObjectLoader.ts -new file mode 100644 -index 0000000000..115fa04ef6 ---- /dev/null -+++ b/src/openharmony/native/QtCore/JsObjectLoader.ts -@@ -0,0 +1,52 @@ -+import JsDataStore from './JsDataStore' -+import { JsQtModule } from './JsQtModule' -+import HashMap from '@ohos.util.HashMap'; -+ -+export default class QtJsObjectLoader { -+ -+ jsObjects: HashMap = new HashMap(); -+ -+ constructor() { -+ -+ } -+ -+ createObject(type: string, name: string, ...args: any[]): Object { -+ let modules = JsDataStore.getJsModules(); -+ let module: JsQtModule = null; -+ for (let i = 0; i < modules.length; ++i) { -+ if (modules[i].hasJsObjectType(type)) { -+ module = modules[i]; -+ break; -+ } -+ } -+ -+ if (module == null) { -+ return null; -+ } -+ -+ let obj = module.createJsObject(type, name, ...args); -+ this.addJsObject(name, obj); -+ return obj; -+ } -+ -+ hasJsObject(name: string) : boolean { -+ return this.jsObjects.hasKey(name); -+ } -+ -+ addJsObject(name: string, obj: Object) : void { -+ this.jsObjects.set(name, obj); -+ } -+ -+ getJsObject(name: string): Object { -+ if (this.jsObjects.hasKey(name)) -+ return this.jsObjects.get(name); -+ return undefined; -+ } -+ -+ deleteJsObject(name: string): boolean { -+ if (!this.jsObjects.hasKey(name)) -+ return false; -+ this.jsObjects.remove(name); -+ return true; -+ } -+} -\ No newline at end of file -diff --git a/src/openharmony/native/QtCore/JsPasteBoard.ts b/src/openharmony/native/QtCore/JsPasteBoard.ts -new file mode 100644 -index 0000000000..6d1dd690fd ---- /dev/null -+++ b/src/openharmony/native/QtCore/JsPasteBoard.ts -@@ -0,0 +1,34 @@ -+import pasteboard from '@ohos.pasteboard'; -+import JsDataStore from './JsDataStore' -+import JsLogger from './JsLogger' -+ -+export class JsPasteBoard { -+ -+ constructor() { -+ let systemPasteboard = pasteboard.getSystemPasteboard(); -+ systemPasteboard.on('update', () => { -+ JsDataStore.getQtNativeModule("QPA").pasteChanged(); -+ }); -+ } -+ -+ async hasClipboardText() : Promise { -+ let systemPasteboard = pasteboard.getSystemPasteboard(); -+ let result = await systemPasteboard.hasData() -+ return result; -+ } -+ -+ async clipboardText() : Promise { -+ let systemPasteboard = pasteboard.getSystemPasteboard(); -+ let pastedata = await systemPasteboard.getData(); -+ let result = pastedata.getPrimaryText(); -+ return result; -+ } -+ -+ setClipboardText(text: string) : boolean { -+ JsLogger.info('set clipboard text %{public}s', text); -+ var pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text); -+ let systemPasteboard = pasteboard.getSystemPasteboard(); -+ systemPasteboard.setData(pasteData); -+ return true; -+ } -+} -diff --git a/src/openharmony/native/QtCore/JsQtModule.ts b/src/openharmony/native/QtCore/JsQtModule.ts -new file mode 100644 -index 0000000000..0a2a0f6b00 ---- /dev/null -+++ b/src/openharmony/native/QtCore/JsQtModule.ts -@@ -0,0 +1,32 @@ -+import JsDataStore from './JsDataStore' -+ -+export class JsQtModule { -+ -+ moduleJsObjects : Object = null; -+ -+ constructor() { -+ JsDataStore.addJsModule(this); -+ } -+ -+ hasJsObjectType(type: string): boolean { -+ if (this.moduleJsObjects == null) -+ return false; -+ const constructor = this.moduleJsObjects[type]; -+ return constructor != null; -+ } -+ -+ createJsObjectImpl(type: string, ...args: any[]): Object { -+ if (this.moduleJsObjects == null) -+ return null; -+ const constructor = this.moduleJsObjects[type]; -+ if (constructor) { -+ return new constructor(...args); -+ } -+ return null; -+ } -+ -+ createJsObject(type: string, name: string, ...args: any[]): Object { -+ let obj = this.createJsObjectImpl(type, ...args); -+ return obj; -+ } -+} -\ No newline at end of file -diff --git a/src/openharmony/native/QtCore/JsQtPlatform.ts b/src/openharmony/native/QtCore/JsQtPlatform.ts -new file mode 100644 -index 0000000000..c3396b5886 ---- /dev/null -+++ b/src/openharmony/native/QtCore/JsQtPlatform.ts -@@ -0,0 +1,23 @@ -+import { JsQtModule } from './JsQtModule' -+import { JsDialog } from './JsDialog' -+import { JsPasteBoard } from './JsPasteBoard' -+import { JsInputMethod } from './JsInputMethod' -+import { JsWindowManager } from './JsWindowManager' -+import { JsFile } from './JsFile' -+import qpa from 'libplugins_platforms_qopenharmony.so' -+ -+class JsQtPlatform extends JsQtModule { -+ -+ public constructor() { -+ super(); -+ this.moduleJsObjects = { -+ JsDialog, -+ JsPasteBoard, -+ JsInputMethod, -+ JsWindowManager, -+ JsFile -+ }; -+ } -+} -+export default new JsQtPlatform; -+export const QtQPA : Object = qpa -diff --git a/src/openharmony/native/QtCore/JsWindow.ts b/src/openharmony/native/QtCore/JsWindow.ts -new file mode 100644 -index 0000000000..62861e0292 ---- /dev/null -+++ b/src/openharmony/native/QtCore/JsWindow.ts -@@ -0,0 +1,81 @@ -+import window from '@ohos.window' -+import JsLogger from './JsLogger' -+import display from '@ohos.display'; -+import JsDataStore from './JsDataStore' -+ -+export class JsWindow { -+ -+ private name: string; -+ private window: window.Window = undefined; -+ private rect: window.Rect; -+ -+ constructor(name: string, window: window.Window) { -+ this.name = name; -+ this.window = window; -+ this.window?.on("windowSizeChange", (size)=>{ -+ JsLogger.info("%{public}s window size changed %{public}d %{public}d", name, size.width, size.height); -+ // JsDataStore.getQtNativeModule("QPA").handleGeometryChange(name, 0, 0, size.width, size.height); -+ }); -+ } -+ -+ async setGeometry(x: number, y: number, w: number, h: number): Promise { -+ JsLogger.info('set window geometry: %{public}s %{public}d %{public}d %{public}d %{public}d', x, y, w, h) -+ try { -+ if (this.window != undefined) { -+ let visible = this.window.isWindowShowing(); -+ if (visible) { -+ await this.window.moveWindowTo(x, y); -+ await this.window.resize(w, h); -+ } -+ this.rect = {'width': w, 'height': h, 'left': x, 'top': y} -+ } -+ return true; -+ } catch (exception) { -+ JsLogger.error('Failed to call setGeometry for the Window %{public}s. Cause: %{public}s', this.name, JSON.stringify(exception)); -+ return false; -+ } -+ } -+ -+ async destroyWindow() : Promise { -+ try { -+ await this.window.destroyWindow(); -+ return true; -+ } catch (exception) { -+ JsLogger.error('Failed to call destroyWindow for the Window %{public}s. Cause: %{public}s', this.name, JSON.stringify(exception)); -+ return false; -+ } -+ } -+ -+ async setVisible(visible: boolean): Promise { -+ JsLogger.error("set window visible: %{public}s %{public}s", this.name, visible); -+ try { -+ if (this.window != undefined) { -+ if (visible) { -+ await this.window.showWindow(); -+ await this.window.moveWindowTo(this.rect.left, this.rect.top); -+ await this.window.resize(this.rect.width, this.rect.height); -+ } else { -+ this.hideWindow(); -+ } -+ } -+ return true; -+ } catch (exception) { -+ JsLogger.error('Failed to call showWindow for the Window. Cause: %{public}s', JSON.stringify(exception)); -+ return false; -+ } -+ } -+ -+ async hideWindow() : Promise { -+ let d = display.getDefaultDisplaySync(); -+ // 由于鸿蒙没有对外提供hide接口,把窗口移到屏幕之外去 -+ await this.window.moveWindowTo(d.width + 50, d.height + 50); -+ } -+ -+ async load() : Promise { -+ let store = new LocalStorage({"name": this.name}); -+ await this.window.loadContent('pages/Index', store); -+ // 如果不显示窗口,xcompoent不会创建,导致c++端等待失败。 -+ await this.setVisible(true); -+ this.hideWindow(); -+ } -+} -diff --git a/src/openharmony/native/QtCore/JsWindowManager.ts b/src/openharmony/native/QtCore/JsWindowManager.ts -new file mode 100644 -index 0000000000..86263ca195 ---- /dev/null -+++ b/src/openharmony/native/QtCore/JsWindowManager.ts -@@ -0,0 +1,46 @@ -+import window from '@ohos.window' -+import HashMap from '@ohos.util.HashMap' -+import JsDataStore from './JsDataStore' -+import JsLogger from './JsLogger' -+import { JsWindow } from './JsWindow' -+ -+export class JsWindowManager { -+ private windows: HashMap = new HashMap; -+ -+ constructor() { -+ -+ } -+ -+ -+ async addWindow(name: string, window: JsWindow) : Promise { -+ if (this.windows.hasKey(name)) -+ return; -+ this.windows.set(name, window) -+ JsDataStore.getJsObjectLoader().addJsObject(name, window); -+ } -+ -+ async createWindow(name: string): Promise { -+ try { -+ if (this.windows.hasKey(name)) -+ return false; -+ let windowStage = JsDataStore.getWindowStage(); -+ let windowClass = await windowStage.createSubWindow(name); -+ let window = new JsWindow(name, windowClass); -+ await window.load(); -+ this.addWindow(name, window); -+ return true; -+ } catch (e) { -+ JsLogger.error("create window failed %{public}s", JSON.stringify(e)); -+ return false; -+ } -+ } -+ -+ async destoryWindow(name: string): Promise { -+ if (!this.windows.hasKey(name)) -+ return false; -+ let window = this.windows.get(name); -+ let result =await window.destroyWindow(); -+ JsDataStore.getJsObjectLoader().deleteJsObject(name); -+ return result; -+ } -+} -diff --git a/src/openharmony/openharmony.pro b/src/openharmony/openharmony.pro -new file mode 100644 -index 0000000000..1c9156fde0 ---- /dev/null -+++ b/src/openharmony/openharmony.pro -@@ -0,0 +1,12 @@ -+TEMPLATE = aux -+CONFIG -= qt -+ -+templates.files += $$files($$PWD/entryability/*.ts, true) -+templates.files += $$files($$PWD/native/*.ts, true) -+templates.files += $$files($$PWD/pages/*.ets, true) -+templates.path = $$[QT_INSTALL_PREFIX]/openharmony/qtbase -+templates.base = $$PWD -+ -+INSTALLS += templates -+ -+OTHER_FILES += $$templates.files -diff --git a/src/openharmony/pages/Index.ets b/src/openharmony/pages/Index.ets -new file mode 100644 -index 0000000000..c877cc24ef ---- /dev/null -+++ b/src/openharmony/pages/Index.ets -@@ -0,0 +1,14 @@ -+@Entry -+@Component -+struct Index { -+ private store: LocalStorage = LocalStorage.GetShared(); -+ private idName = this.store.get("name"); -+ -+ build() { -+ Row() { -+ XComponent({id: this.idName, type: 'surface', libraryname: 'plugins_platforms_qopenharmony' }).width("100%").height("100%") -+ .focusable(true) -+ .focusOnTouch(true) -+ } -+ } -+} -diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp -index ad134a825f..dcbefde499 100644 ---- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp -+++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp -@@ -149,10 +149,14 @@ void QEvdevKeyboardHandler::switchLed(int led, bool state) - qCDebug(qLcEvdevKey) << "switchLed" << led << state; - - struct ::input_event led_ie; -+#if __BITS_PER_LONG != 32 || !defined(__USE_TIME_BITS64) - ::gettimeofday(&led_ie.time, 0); -- led_ie.type = EV_LED; -- led_ie.code = led; -- led_ie.value = state; -+#else -+ struct timeval tv; -+ ::gettimeofday(&tv, 0); -+ led_ie.__sec = tv.tv_sec; -+ led_ie.__usec = tv.tv_usec; -+#endif - - qt_safe_write(m_fd.get(), &led_ie, sizeof(led_ie)); - } -diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp -index 70271c7fd6..1634094364 100644 ---- a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp -+++ b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp -@@ -568,7 +568,11 @@ void QEvdevTouchScreenData::processInputEvent(input_event *data) - - // update timestamps - m_lastTimeStamp = m_timeStamp; -+#if __BITS_PER_LONG != 32 || !defined(__USE_TIME_BITS64) - m_timeStamp = data->time.tv_sec + data->time.tv_usec / 1000000.0; -+#else -+ m_timeStamp = data->__sec + data->__usec / 1000000.0 ; -+#endif - - m_lastTouchPoints = m_touchPoints; - m_touchPoints.clear(); -diff --git a/src/plugins/platforms/openharmony/openharmony.json b/src/plugins/platforms/openharmony/openharmony.json -new file mode 100644 -index 0000000000..886625295c ---- /dev/null -+++ b/src/plugins/platforms/openharmony/openharmony.json -@@ -0,0 +1,3 @@ -+{ -+ "Keys": [ "openharmony" ] -+} -diff --git a/src/plugins/platforms/openharmony/openharmony.pro b/src/plugins/platforms/openharmony/openharmony.pro -new file mode 100644 -index 0000000000..2f236e120f ---- /dev/null -+++ b/src/plugins/platforms/openharmony/openharmony.pro -@@ -0,0 +1,71 @@ -+TARGET = qopenharmony -+ -+LIBS += -lEGL -lace_napi.z -lace_ndk.z -lrawfile.z -+ -+QT += \ -+ core-private gui-private egl_support-private \ -+ eventdispatcher_support-private fontdatabase_support-private -+ -+qtConfig(vulkan){ -+ QT += vulkan_support-private -+ -+ HEADERS += $$PWD/qopenharmonyplatformvulkaninstance.h -+ -+ SOURCES += $$PWD/qopenharmonyplatformvulkaninstance.cpp -+} -+ -+OTHER_FILES += $$PWD/openharmony.json -+ -+HEADERS += $$PWD/qopenharmonyplatformintegration.h \ -+ $$PWD/qopenharmonyplatformwindow.h \ -+ $$PWD/qopenharmonyplatformopenglwindow.h \ -+ $$PWD/qopenharmonyplatformforeignwindow.h \ -+ $$PWD/qopenharmonyplatformscreen.h \ -+ $$PWD/qopenharmonyplatformbackingstore.h \ -+ $$PWD/qopenharmonylog.h \ -+ $$PWD/qopenharmonyxcomponent.h \ -+ $$PWD/qopenharmonyjswindowmanager.h \ -+ $$PWD/qopenharmonyplatformopenglcontext.h \ -+ $$PWD/qopenharmonyeventdispatcher.h \ -+ $$PWD/qopenharmonyplatformoffscreensurface.h \ -+ $$PWD/qopenharmonymain.h \ -+ $$PWD/qopenharmonyeglcore.h \ -+ $$PWD/qopenharmonyplatformfontdatabase.h \ -+ $$PWD/qopenharmonyplatforminputcontext.h \ -+ $$PWD/qopenharmonyplatformtheme.h \ -+ $$PWD/qopenharmonyplatformdialoghelpers.h \ -+ $$PWD/qopenharmonydrag.h \ -+ $$PWD/qopenharmonyfileenginehandler.h \ -+ $$PWD/qopenharmonyjswindow.h -+ -+SOURCES += $$PWD/qopenharmonyplatformplugin.cpp \ -+ $$PWD/qopenharmonyplatformwindow.cpp \ -+ $$PWD/qopenharmonyplatformintegration.cpp \ -+ $$PWD/qopenharmonyplatformopenglwindow.cpp \ -+ $$PWD/qopenharmonyplatformforeignwindow.cpp \ -+ $$PWD/qopenharmonyplatformscreen.cpp \ -+ $$PWD/qopenharmonyplatformbackingstore.cpp \ -+ $$PWD/qopenharmonyplatformopenglcontext.cpp \ -+ $$PWD/qopenharmonyplatformoffscreensurface.cpp \ -+ $$PWD/qopenharmonymain.cpp \ -+ $$PWD/qopenharmonyxcomponent.cpp \ -+ $$PWD/qopenharmonyjswindowmanager.cpp \ -+ $$PWD/qopenharmonyeventdispatcher.cpp \ -+ $$PWD/qopenharmonyeglcore.cpp \ -+ $$PWD/qopenharmonyplatformfontdatabase.cpp \ -+ $$PWD/qopenharmonyplatforminputcontext.cpp \ -+ $$PWD/qopenharmonyplatformtheme.cpp \ -+ $$PWD/qopenharmonyplatformdialoghelpers.cpp \ -+ $$PWD/qopenharmonyplatformclipboard.cpp \ -+ $$PWD/qopenharmonydrag.cpp \ -+ $$PWD/qopenharmonyfileenginehandler.cpp \ -+ $$PWD/qopenharmonyjswindow.cpp -+ -+ -+PLUGIN_TYPE = platforms -+ -+PLUGIN_CLASS_NAME = QOpenHarmonyPlatformIntegrationPlugin -+!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - -+ -+load(qt_plugin) -+ -diff --git a/src/plugins/platforms/openharmony/qopenharmonydrag.cpp b/src/plugins/platforms/openharmony/qopenharmonydrag.cpp -new file mode 100644 -index 0000000000..1f088afc20 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonydrag.cpp -@@ -0,0 +1,138 @@ -+#include -+#include -+ -+#include "qguiapplication.h" -+#include "qopenharmonydrag.h" -+#include "qpa/qplatformscreen.h" -+#include "qpa/qplatformwindow.h" -+#include -+#include "qpa/qwindowsysteminterface.h" -+#include "private/qguiapplication_p.h" -+ -+QT_BEGIN_NAMESPACE -+ -+Q_LOGGING_CATEGORY(ohDnd, "qt.openharmony.dnd") -+ -+static QWindow* topLevelAt(const QPoint &pos) -+{ -+ QWindowList list = QGuiApplication::topLevelWindows(); -+ for (int i = list.count()-1; i >= 0; --i) { -+ QWindow *w = list.at(i); -+ if (w->isVisible() && w->handle() && w->geometry().contains(pos)/* && !qobject_cast(w)*/) -+ return w; -+ } -+ return 0; -+} -+ -+QOpenHarmonyDrag::QOpenHarmonyDrag() = default; -+QOpenHarmonyDrag::~QOpenHarmonyDrag() -+{ -+ -+} -+ -+void QOpenHarmonyDrag::startDrag() -+{ -+ qInfo() << "<----------------------:::QOpenHarmonyDrag::startDrag()"; -+ setExecutedDropAction(Qt::IgnoreAction); -+ -+ QBasicDrag::startDrag(); -+ // Here we can be fairly sure that QGuiApplication::mouseButtons/keyboardModifiers() will -+ // contain sensible values as startDrag() normally is called from mouse event handlers -+ // by QDrag::exec(). A better API would be if we could pass something like "input device -+ // pointer" to QDrag::exec(). My guess is that something like that might be required for -+ // QTBUG-52430. -+ m_sourceWindow = topLevelAt(QCursor::pos()); -+ m_windowUnderCursor = m_sourceWindow; -+ if (m_sourceWindow) { -+ auto nativePixelPos = QHighDpi::toNativePixels(QCursor::pos(), m_sourceWindow); -+ move(nativePixelPos, QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers()); -+ } else { -+ setCanDrop(false); -+ updateCursor(Qt::IgnoreAction); -+ } -+ -+ qCDebug(ohDnd) << "drag began from" << m_sourceWindow << "cursor pos" << QCursor::pos() << "can drop?" << canDrop(); -+ qInfo() << "<----------------------:::QOpenHarmonyDrag::startDrag()<====================="; -+} -+ -+static void sendDragLeave(QWindow *window) -+{ -+ QWindowSystemInterface::handleDrag(window, nullptr, QPoint(), Qt::IgnoreAction, 0, 0); -+} -+ -+void QOpenHarmonyDrag::cancel() -+{ -+ QBasicDrag::cancel(); -+ if (drag() && m_sourceWindow) { -+ sendDragLeave(m_sourceWindow); -+ m_sourceWindow = nullptr; -+ } -+} -+ -+static inline QPoint fromNativeGlobalPixels(const QPoint &point) -+{ -+#ifndef QT_NO_HIGHDPISCALING -+ QPoint res = point; -+ if (QHighDpiScaling::isActive()) { -+ for (const QScreen *s : qAsConst(QGuiApplicationPrivate::screen_list)) { -+ if (s->handle()->geometry().contains(point)) { -+ res = QHighDpi::fromNativePixels(point, s); -+ break; -+ } -+ } -+ } -+ return res; -+#else -+ return point; -+#endif -+} -+ -+void QOpenHarmonyDrag::move(const QPoint &nativeGlobalPos, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) -+{ -+ QPoint globalPos = fromNativeGlobalPixels(nativeGlobalPos); -+ moveShapedPixmapWindow(globalPos); -+ QWindow *window = topLevelAt(globalPos); -+ -+ if (!window || window != m_windowUnderCursor) { -+ if (m_windowUnderCursor) -+ sendDragLeave(m_windowUnderCursor); -+ m_windowUnderCursor = window; -+ if (!window) { -+ // QSimpleDrag supports only in-process dnd, we can't drop anywhere else. -+ setCanDrop(false); -+ updateCursor(Qt::IgnoreAction); -+ return; -+ } -+ } -+ -+ const QPoint pos = nativeGlobalPos - window->handle()->geometry().topLeft(); -+ const QPlatformDragQtResponse qt_response = QWindowSystemInterface::handleDrag( -+ window, drag()->mimeData(), pos, drag()->supportedActions(), -+ buttons, modifiers); -+ -+ setCanDrop(qt_response.isAccepted()); -+ updateCursor(qt_response.acceptedAction()); -+} -+ -+void QOpenHarmonyDrag::drop(const QPoint &nativeGlobalPos, Qt::MouseButtons buttons, -+ Qt::KeyboardModifiers modifiers) -+{ -+ QPoint globalPos = fromNativeGlobalPixels(nativeGlobalPos); -+ -+ QBasicDrag::drop(nativeGlobalPos, buttons, modifiers); -+ QWindow *window = topLevelAt(globalPos); -+ if (!window) -+ return; -+ -+ const QPoint pos = nativeGlobalPos - window->handle()->geometry().topLeft(); -+ const QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop( -+ window, drag()->mimeData(), pos, drag()->supportedActions(), -+ buttons, modifiers); -+ if (response.isAccepted()) { -+ setExecutedDropAction(response.acceptedAction()); -+ } else { -+ setExecutedDropAction(Qt::IgnoreAction); -+ } -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/platforms/openharmony/qopenharmonydrag.h b/src/plugins/platforms/openharmony/qopenharmonydrag.h -new file mode 100644 -index 0000000000..f29ee6fccb ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonydrag.h -@@ -0,0 +1,25 @@ -+#ifndef QOPENHARMONYDRAG_H -+#define QOPENHARMONYDRAG_H -+ -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyDrag : public QBasicDrag -+{ -+public: -+ QOpenHarmonyDrag(); -+ virtual ~QOpenHarmonyDrag(); -+ -+protected: -+ virtual void startDrag() override; -+ virtual void cancel() override; -+ virtual void move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override; -+ virtual void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYDRAG_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyeglcore.cpp b/src/plugins/platforms/openharmony/qopenharmonyeglcore.cpp -new file mode 100644 -index 0000000000..62afca9740 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyeglcore.cpp -@@ -0,0 +1,335 @@ -+#include "qopenharmonyeglcore.h" -+#include -+#include -+#include -+#include "qopenharmonylog.h" -+#include -+ -+EGLDisplay QOpenHarmonyEGLCore::m_eglDisplay = EGL_NO_DISPLAY; -+EGLConfig QOpenHarmonyEGLCore::m_eglConfig = nullptr; -+ -+ -+static char vertextShader[] = -+ "#version 300 es\n" -+ "layout (location = 0) in vec4 a_position;\n" -+ "layout (location = 1) in vec2 a_textCoord;\n" -+ "out vec2 v_texCoord;\n" -+ "void main() { \n" -+ " gl_Position = a_position;\n" -+ " v_texCoord = a_textCoord;\n" -+ "}\n"; -+ -+static char fragmentShader[] = -+ "#version 300 es\n" -+ "precision mediump float;\n" -+ "in vec2 v_texCoord;\n" -+ "layout(location = 0) out vec4 outColor;\n" -+ "uniform sampler2D s_TextureMap;\n" -+ "void main() {\n" -+ " outColor = texture(s_TextureMap,v_texCoord);\n" -+ "}\n"; -+ -+QOpenHarmonyEGLCore::QOpenHarmonyEGLCore() -+{ -+ -+} -+ -+QOpenHarmonyEGLCore::~QOpenHarmonyEGLCore() -+{ -+ if (m_eglSurface != EGL_NO_SURFACE) { -+ eglDestroySurface(m_eglDisplay, m_eglSurface); -+ m_eglSurface = EGL_NO_SURFACE; -+ } -+} -+ -+void QOpenHarmonyEGLCore::init() -+{ -+ m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); -+ if (Q_UNLIKELY(m_eglDisplay == EGL_NO_DISPLAY)) { -+ LOGE("Could not open egl display"); -+ return; -+ } -+ -+ EGLint major, minor; -+ if (Q_UNLIKELY(!eglInitialize(m_eglDisplay, &major, &minor))) { -+ m_eglDisplay = EGL_NO_DISPLAY; -+ LOGE("Could not initialize egl display"); -+ } -+ -+ if (Q_UNLIKELY(!eglBindAPI(EGL_OPENGL_ES_API))) { -+ LOGE("Could not bind GL_ES API"); -+ return; -+ } -+ -+ m_eglConfig = initConfig(); -+ if (m_eglConfig == nullptr) { -+ LOGE("init config error"); -+ return; -+ } -+} -+ -+bool QOpenHarmonyEGLCore::valid() const -+{ -+ return m_valid; -+} -+ -+void QOpenHarmonyEGLCore::createSurface(EGLNativeWindowType window, int w, int h) -+{ -+ if (m_eglSurface != EGL_NO_SURFACE) { -+ eglDestroySurface(m_eglDisplay, m_eglSurface); -+ m_eglSurface = EGL_NO_SURFACE; -+ } -+ LOGI("QOpenHarmonyEGLCore::init window = %{public}p, w = %{public}d, h = %{public}d.", window, w, h); -+ m_width = w; -+ m_height = h; -+ m_eglWindow = window; -+ -+ if (m_eglWindow) { -+ m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, m_eglWindow, NULL); -+ if (m_eglSurface == nullptr) { -+ LOGE("EGLCore::eglCreateWindowSurface eglSurface is null, error code %{public}x", eglGetError()); -+ return; -+ } -+ } -+ m_valid = true; -+} -+ -+void QOpenHarmonyEGLCore::updateSurfaceSize(int w, int h) -+{ -+ m_width = w; -+ m_height = h; -+ if (m_image.isNull()) { -+ QImage::Format format = QImage::Format_RGBA8888_Premultiplied; -+ m_image = QImage(w, h, format); -+ m_image.fill(Qt::white); -+ } else { -+ m_image = m_image.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); -+ } -+// drawImage(m_image); -+} -+ -+EGLConfig QOpenHarmonyEGLCore::initConfig(int version) { -+ int attribList[] = { -+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT, -+ EGL_RED_SIZE, 8, -+ EGL_GREEN_SIZE, 8, -+ EGL_BLUE_SIZE, 8, -+ EGL_ALPHA_SIZE, 8, -+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, -+ EGL_NONE -+ }; -+ EGLConfig configs = NULL; -+ int configsNum; -+ eglChooseConfig(m_eglDisplay, attribList, &configs, 1, &configsNum); -+ return configs; -+} -+ -+void QOpenHarmonyEGLCore::createTexture() -+{ -+ glActiveTexture(GL_TEXTURE0); -+ glGenTextures(1, &m_TextureId); -+ glBindTexture(GL_TEXTURE_2D, m_TextureId); -+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); -+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); -+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); -+} -+ -+EGLSurface QOpenHarmonyEGLCore::eglSurface() const -+{ -+ return m_eglSurface; -+} -+ -+QImage QOpenHarmonyEGLCore::image() const -+{ -+ if (m_image.size() != QSize(m_width, m_height)) -+ m_image = m_image.scaled(m_width, m_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); -+ return m_image; -+} -+ -+void QOpenHarmonyEGLCore::initCore() -+{ -+ int attrib3_list[] = { -+ EGL_CONTEXT_CLIENT_VERSION, 2, -+ EGL_NONE -+ }; -+ -+ m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, attrib3_list); -+ -+ if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)) { -+ LOGE("EGLCore::eglMakeCurrent error = %{public}d", eglGetError()); -+ } -+ m_program = createProgram(vertextShader, fragmentShader); -+ if (!m_program) { -+ LOGE("Could not create createProgram"); -+ return; -+ } else { -+ m_samplerLoc = glGetUniformLocation(m_program, "s_TextureMap"); -+ } -+ -+ createTexture(); -+ m_inited = true; -+} -+ -+void QOpenHarmonyEGLCore::drawImage(const QImage &image) -+{ -+ if (!m_valid) -+ return; -+ if (!m_inited) -+ initCore(); -+ -+ if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)) { -+ LOGE("EGLCore::eglMakeCurrent error = %{public}d", eglGetError()); -+ } -+ glViewport(0, 0, m_width, m_height); -+// glClearColor(1.0, 1.0, 1.0, 1.0); -+// glClear(GL_COLOR_BUFFER_BIT); -+ -+ glActiveTexture(GL_TEXTURE0); -+ glBindTexture(GL_TEXTURE_2D, m_TextureId); -+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.bits()); -+ glBindTexture(GL_TEXTURE_2D, GL_NONE); -+ -+ static GLfloat verticesCoords[] = { -+ -1.0f, 1.0f, 0.0f, // Position 0 -+ -1.0f, -1.0f, 0.0f, // Position 1 -+ 1.0f, -1.0f, 0.0f, // Position 2 -+ 1.0f, 1.0f, 0.0f, // Position 3 -+ }; -+ // 4 texture coord -+ static GLfloat textureCoords[] = { -+ 0.0f, 0.0f, // TexCoord 0 -+ 0.0f, 1.0f, // TexCoord 1 -+ 1.0f, 1.0f, // TexCoord 2 -+ 1.0f, 0.0f // TexCoord 3 -+ }; -+ -+ // render container -+ glUseProgram(m_program); -+ // Load the vertex position -+ glVertexAttribPointer (0, 3, GL_FLOAT, -+ GL_FALSE, 3 * sizeof (GLfloat), verticesCoords); -+ // Load the texture coordinate -+ glVertexAttribPointer (1, 2, GL_FLOAT, -+ GL_FALSE, 2 * sizeof (GLfloat), textureCoords); -+ -+ glEnableVertexAttribArray (0); -+ glEnableVertexAttribArray (1); -+ -+ // Bind the RGBA map -+ glActiveTexture(GL_TEXTURE0); -+ glBindTexture(GL_TEXTURE_2D, m_TextureId); -+ -+ // Set the RGBA map sampler to texture unit to 0 -+ glUniform1i(m_samplerLoc, 0); -+ static GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; -+ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); -+ update(); -+ m_image = image; -+} -+ -+void QOpenHarmonyEGLCore::update() -+{ -+ eglSwapBuffers(m_eglDisplay, m_eglSurface); -+} -+ -+GLuint QOpenHarmonyEGLCore::loadShader(GLenum type, const char *shaderSrc) -+{ -+ GLuint shader; -+ GLint compiled; -+ -+ shader = glCreateShader(type); -+ if (shader == 0) { -+ LOGE("LoadShader shader error"); -+ return 0; -+ } -+ -+ glShaderSource(shader, 1, &shaderSrc, nullptr); -+ glCompileShader(shader); -+ -+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); -+ -+ if (!compiled) { -+ GLint infoLen = 0; -+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); -+ -+ if (infoLen > 1) { -+ char *infoLog = (char*)malloc(sizeof(char) * infoLen); -+ glGetShaderInfoLog(shader, infoLen, nullptr, infoLog); -+ LOGE("Error compiling shader:\n%s\n",infoLog); -+ free(infoLog); -+ } -+ glDeleteShader(shader); -+ return 0; -+ } -+ return shader; -+} -+ -+GLuint QOpenHarmonyEGLCore::createProgram(const char *vertexShader, const char *fragShader) -+{ -+ GLuint vertex; -+ GLuint fragment; -+ GLuint program; -+ GLint linked; -+ -+ vertex = loadShader(GL_VERTEX_SHADER, vertexShader); -+ if (vertex == 0) { -+ LOGE("createProgram vertex error"); -+ return 0; -+ } -+ -+ fragment = loadShader(GL_FRAGMENT_SHADER, fragShader); -+ if (fragment == 0) { -+ LOGE("createProgram fragment error"); -+ glDeleteShader(vertex); -+ return 0; -+ } -+ -+ program = glCreateProgram(); -+ if (program == 0) { -+ LOGE("createProgram program error"); -+ glDeleteShader(vertex); -+ glDeleteShader(fragment); -+ return 0; -+ } -+ -+ glAttachShader(program, vertex); -+ glAttachShader(program, fragment); -+ glLinkProgram(program); -+ glGetProgramiv(program, GL_LINK_STATUS, &linked); -+ if (!linked) { -+ LOGE("createProgram linked error"); -+ GLint infoLen = 0; -+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen); -+ if (infoLen > 1) { -+ char *infoLog = (char *)malloc(sizeof(char) * infoLen); -+ glGetProgramInfoLog(program, infoLen, nullptr, infoLog); -+ LOGE("Error linking program:\n%s\n",infoLog); -+ free(infoLog); -+ } -+ glDeleteShader(vertex); -+ glDeleteShader(fragment); -+ glDeleteProgram(program); -+ return 0; -+ } -+ glDeleteShader(vertex); -+ glDeleteShader(fragment); -+ -+ return program; -+} -+ -+EGLConfig QOpenHarmonyEGLCore::eglConfig() -+{ -+ return m_eglConfig; -+} -+ -+void QOpenHarmonyEGLCore::clear() -+{ -+ if (m_eglDisplay != EGL_NO_DISPLAY) -+ eglTerminate(m_eglDisplay); -+} -+ -+EGLDisplay QOpenHarmonyEGLCore::eglDisplay() -+{ -+ return m_eglDisplay; -+} -diff --git a/src/plugins/platforms/openharmony/qopenharmonyeglcore.h b/src/plugins/platforms/openharmony/qopenharmonyeglcore.h -new file mode 100644 -index 0000000000..68c17e05e4 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyeglcore.h -@@ -0,0 +1,57 @@ -+#ifndef QOPENHARMONYEGLCORE_H -+#define QOPENHARMONYEGLCORE_H -+ -+#include -+#include -+#include -+#include -+ -+class QOpenHarmonyEGLCore -+{ -+public: -+ QOpenHarmonyEGLCore(); -+ ~QOpenHarmonyEGLCore(); -+ -+ static void init(); -+ -+ bool valid() const; -+ -+ void createSurface(EGLNativeWindowType window, int w, int h); -+ void updateSurfaceSize(int w, int h); -+ void drawImage(const QImage &image); -+ -+public: -+ static EGLDisplay eglDisplay(); -+ -+ static EGLConfig eglConfig(); -+ -+ static void clear(); -+ -+ EGLSurface eglSurface() const; -+ -+ QImage image() const; -+ -+private: -+ void initCore(); -+ static EGLConfig initConfig(int version = 3); -+ void update(); -+ GLuint loadShader(GLenum type, const char *shaderSrc); -+ GLuint createProgram(const char *vertexShader, const char *fragShader); -+ void createTexture(); -+ -+ EGLNativeWindowType m_eglWindow = 0; -+ static EGLDisplay m_eglDisplay; -+ static EGLConfig m_eglConfig; -+ EGLContext m_eglContext = EGL_NO_CONTEXT; -+ EGLSurface m_eglSurface = nullptr; -+ GLuint m_program; -+ GLuint m_TextureId; -+ GLint m_samplerLoc; -+ int m_width; -+ int m_height; -+ bool m_valid = false; -+ bool m_inited = false; -+ mutable QImage m_image; -+}; -+ -+#endif // QOPENHARMONYEGLCORE_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyeventdispatcher.cpp b/src/plugins/platforms/openharmony/qopenharmonyeventdispatcher.cpp -new file mode 100644 -index 0000000000..d86340340b ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyeventdispatcher.cpp -@@ -0,0 +1,127 @@ -+#include "qopenharmonyeventdispatcher.h" -+ -+QOpenHarmonyEventDispatcher::QOpenHarmonyEventDispatcher(QObject *parent) : -+ QUnixEventDispatcherQPA(parent) -+{ -+ QOpenHarmonyEventDispatcherStopper::instance()->addEventDispatcher(this); -+} -+ -+QOpenHarmonyEventDispatcher::~QOpenHarmonyEventDispatcher() -+{ -+ QOpenHarmonyEventDispatcherStopper::instance()->removeEventDispatcher(this); -+} -+ -+enum States {Running = 0, StopRequest = 1, Stopping = 2}; -+ -+void QOpenHarmonyEventDispatcher::start() -+{ -+ int prevState = m_stopRequest.fetchAndStoreAcquire(Running); -+ if (prevState == Stopping) { -+ m_semaphore.release(); -+ wakeUp(); -+ } else if (prevState == Running) { -+ qWarning("Error: start without corresponding stop"); -+ } -+ //else if prevState == StopRequest, no action needed -+} -+ -+void QOpenHarmonyEventDispatcher::stop() -+{ -+ if (m_stopRequest.testAndSetAcquire(Running, StopRequest)) -+ wakeUp(); -+ else -+ qWarning("Error: start/stop out of sync"); -+} -+ -+void QOpenHarmonyEventDispatcher::goingToStop(bool stop) -+{ -+ m_goingToStop.store(stop ? 1 : 0); -+ if (!stop) -+ wakeUp(); -+} -+ -+class OpenHarmonyDeadlockProtector -+{ -+public: -+ OpenHarmonyDeadlockProtector() -+ : m_acquired(0) -+ { -+ } -+ -+ ~OpenHarmonyDeadlockProtector() { -+ if (m_acquired) -+ s_blocked.storeRelease(0); -+ } -+ -+ bool acquire() { -+ m_acquired = s_blocked.testAndSetAcquire(0, 1); -+ return m_acquired; -+ } -+ -+private: -+ static QAtomicInt s_blocked; -+ int m_acquired; -+}; -+ -+QAtomicInt OpenHarmonyDeadlockProtector::s_blocked(0); -+ -+bool QOpenHarmonyEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) -+{ -+ if (m_goingToStop.load()) -+ flags |= QEventLoop::ExcludeSocketNotifiers | QEventLoop::X11ExcludeTimers; -+ -+ { -+ OpenHarmonyDeadlockProtector protector; -+ if (protector.acquire() && m_stopRequest.testAndSetAcquire(StopRequest, Stopping)) { -+ m_semaphore.acquire(); -+ wakeUp(); -+ } -+ } -+ -+ return QUnixEventDispatcherQPA::processEvents(flags); -+} -+ -+QOpenHarmonyEventDispatcherStopper *QOpenHarmonyEventDispatcherStopper::instance() -+{ -+ static QOpenHarmonyEventDispatcherStopper openharmonyEventDispatcherStopper; -+ return &openharmonyEventDispatcherStopper; -+} -+ -+void QOpenHarmonyEventDispatcherStopper::startAll() -+{ -+ QMutexLocker lock(&m_mutex); -+ if (!m_started.testAndSetOrdered(0, 1)) -+ return; -+ -+ for (QOpenHarmonyEventDispatcher *d : qAsConst(m_dispatchers)) -+ d->start(); -+} -+ -+void QOpenHarmonyEventDispatcherStopper::stopAll() -+{ -+ QMutexLocker lock(&m_mutex); -+ if (!m_started.testAndSetOrdered(1, 0)) -+ return; -+ -+ for (QOpenHarmonyEventDispatcher *d : qAsConst(m_dispatchers)) -+ d->stop(); -+} -+ -+void QOpenHarmonyEventDispatcherStopper::addEventDispatcher(QOpenHarmonyEventDispatcher *dispatcher) -+{ -+ QMutexLocker lock(&m_mutex); -+ m_dispatchers.push_back(dispatcher); -+} -+ -+void QOpenHarmonyEventDispatcherStopper::removeEventDispatcher(QOpenHarmonyEventDispatcher *dispatcher) -+{ -+ QMutexLocker lock(&m_mutex); -+ m_dispatchers.erase(std::find(m_dispatchers.begin(), m_dispatchers.end(), dispatcher)); -+} -+ -+void QOpenHarmonyEventDispatcherStopper::goingToStop(bool stop) -+{ -+ QMutexLocker lock(&m_mutex); -+ for (QOpenHarmonyEventDispatcher *d : qAsConst(m_dispatchers)) -+ d->goingToStop(stop); -+} -diff --git a/src/plugins/platforms/openharmony/qopenharmonyeventdispatcher.h b/src/plugins/platforms/openharmony/qopenharmonyeventdispatcher.h -new file mode 100644 -index 0000000000..51fe72e96e ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyeventdispatcher.h -@@ -0,0 +1,46 @@ -+#ifndef QOPENHARMONYEVENTDISPATCHER_H -+#define QOPENHARMONYEVENTDISPATCHER_H -+ -+#include -+#include -+#include -+ -+class QOpenHarmonyEventDispatcher : public QUnixEventDispatcherQPA -+{ -+ Q_OBJECT -+public: -+ explicit QOpenHarmonyEventDispatcher(QObject *parent = 0); -+ ~QOpenHarmonyEventDispatcher(); -+ void start(); -+ void stop(); -+ -+ void goingToStop(bool stop); -+ -+protected: -+ bool processEvents(QEventLoop::ProcessEventsFlags flags) override; -+ -+private: -+ QAtomicInt m_stopRequest; -+ QAtomicInt m_goingToStop; -+ QSemaphore m_semaphore; -+}; -+ -+class QOpenHarmonyEventDispatcherStopper -+{ -+public: -+ static QOpenHarmonyEventDispatcherStopper *instance(); -+ static bool stopped() {return !instance()->m_started.load(); } -+ void startAll(); -+ void stopAll(); -+ void addEventDispatcher(QOpenHarmonyEventDispatcher *dispatcher); -+ void removeEventDispatcher(QOpenHarmonyEventDispatcher *dispatcher); -+ void goingToStop(bool stop); -+ -+private: -+ QMutex m_mutex; -+ QAtomicInt m_started = 1; -+ QVector m_dispatchers; -+}; -+ -+ -+#endif // QOPENHARMONYEVENTDISPATCHER_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyfileenginehandler.cpp b/src/plugins/platforms/openharmony/qopenharmonyfileenginehandler.cpp -new file mode 100644 -index 0000000000..a2a7dec9d4 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyfileenginehandler.cpp -@@ -0,0 +1,466 @@ -+#include "qopenharmonyfileenginehandler.h" -+#include "qopenharmonymain.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+typedef QVector FilesList; -+ -+struct OpenHarmonyRawDir -+{ -+ OpenHarmonyRawDir(RawDir* rd) -+ { -+ if (rd) { -+ const char *fileName; -+ int count = OH_ResourceManager_GetRawFileCount(rd); -+ for (int i = 0; i < count; ++i) { -+ const char *fileName = OH_ResourceManager_GetRawFileName(rd, i); -+ m_items.push_back(QString::fromUtf8(fileName)); -+ } -+ OH_ResourceManager_CloseRawDir(rd); -+ } -+ } -+ FilesList m_items; -+}; -+ -+ -+class OpenHarmonyAbstractFileEngineIterator: public QAbstractFileEngineIterator -+{ -+public: -+ OpenHarmonyAbstractFileEngineIterator(QDir::Filters filters, -+ const QStringList &nameFilters, -+ QSharedPointer rd, -+ const QString &path) -+ : QAbstractFileEngineIterator(filters, nameFilters) -+ { -+ m_items = rd->m_items; -+ m_index = -1; -+ m_path = path; -+ } -+ -+ QFileInfo currentFileInfo() const override -+ { -+ return QFileInfo(currentFilePath()); -+ } -+ -+ QString currentFileName() const override -+ { -+ if (m_index < 0 || m_index >= m_items.size()) -+ return QString(); -+ QString fileName = m_items[m_index]; -+ if (fileName.endsWith(QLatin1Char('/'))) -+ fileName.chop(1); -+ return fileName; -+ } -+ -+ virtual QString currentFilePath() const -+ { -+ return m_path + currentFileName(); -+ } -+ -+ bool hasNext() const override -+ { -+ return m_items.size() && (m_index < m_items.size() - 1); -+ } -+ -+ QString next() override -+ { -+ if (!hasNext()) -+ return QString(); -+ m_index++; -+ return currentFileName(); -+ } -+ -+private: -+ QString m_path; -+ FilesList m_items; -+ int m_index; -+}; -+ -+ -+class QOpenHarmonyRawfileEngine: public QAbstractFileEngine -+{ -+public: -+ explicit QOpenHarmonyRawfileEngine(RawFile *file, const QString &fileName) -+ : m_file(file) -+ , m_fileName(fileName) -+ { -+ -+ } -+ -+ explicit QOpenHarmonyRawfileEngine(QSharedPointer ohrd, const QString &fileName) -+ { -+ m_file = 0; -+ m_dir = ohrd; -+ m_fileName = fileName; -+ if (!m_fileName.endsWith(QLatin1Char('/'))) -+ m_fileName += QLatin1Char('/'); -+ } -+ -+ bool open(QIODevice::OpenMode openMode) override -+ { -+ return m_file != 0 && (openMode & QIODevice::WriteOnly) == 0; -+ } -+ -+ bool close() override -+ { -+ if (m_file) { -+ OH_ResourceManager_CloseRawFile(m_file); -+ m_file = 0; -+ return true; -+ } -+ return false; -+ } -+ -+ qint64 size() const override -+ { -+ if (m_file) -+ return OH_ResourceManager_GetRawFileSize(m_file); -+ return -1; -+ } -+ -+ qint64 pos() const override -+ { -+ if (m_file) -+ return OH_ResourceManager_GetRawFileOffset(m_file); -+ return -1; -+ } -+ -+ bool seek(qint64 pos) override -+ { -+ if (m_file) -+ return pos == OH_ResourceManager_SeekRawFile(m_file, pos, 0); -+ return false; -+ } -+ -+ qint64 read(char *data, qint64 maxlen) override -+ { -+ if (m_file) -+ return OH_ResourceManager_ReadRawFile(m_file, data, maxlen); -+ return -1; -+ } -+ -+ bool isSequential() const override -+ { -+ return false; -+ } -+ -+ bool caseSensitive() const override -+ { -+ return true; -+ } -+ -+ bool isRelativePath() const override -+ { -+ return false; -+ } -+ -+ FileFlags fileFlags(FileFlags type = FileInfoAll) const override -+ { -+ FileFlags flags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag); -+ if (m_file) -+ flags |= FileType; -+ if (!m_dir.isNull()) -+ flags |= DirectoryType; -+ -+ return type & flags; -+ } -+ -+ QString fileName(FileName file = DefaultName) const override -+ { -+ int pos; -+ switch (file) { -+ case DefaultName: -+ case AbsoluteName: -+ case CanonicalName: -+ return m_fileName; -+ case BaseName: -+ if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1) -+ return m_fileName.mid(pos); -+ else -+ return m_fileName; -+ case PathName: -+ case AbsolutePathName: -+ case CanonicalPathName: -+ if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1) -+ return m_fileName.left(pos); -+ else -+ return m_fileName; -+ default: -+ return QString(); -+ } -+ } -+ -+ void setFileName(const QString &file) override -+ { -+ if (file == m_fileName) -+ return; -+ -+ m_fileName = file; -+ close(); -+ } -+ -+ Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override -+ { -+ if (!m_dir.isNull()) -+ return new OpenHarmonyAbstractFileEngineIterator(filters, filterNames, m_dir, m_fileName); -+ return 0; -+ } -+private: -+ RawFile *m_file; -+ QString m_fileName; -+ QSharedPointer m_dir; -+}; -+ -+ -+class QOpenHarmonyFileEngine: public QAbstractFileEngine -+{ -+public: -+ explicit QOpenHarmonyFileEngine(const QString &fileName) -+ { -+ m_fileName = fileName; -+ static int index = 0; -+ m_fileHandler = QString("fileHandler%1").arg(index++); -+ m_jsFile = qJsObjectLoader->create("JsFile", m_fileHandler, QVariantList() << m_fileName); -+ } -+ -+ ~QOpenHarmonyFileEngine() -+ { -+ close(); -+ if (!m_jsFile.isNull()) { -+ qJsObjectLoader->remove(m_jsFile->uniqueName()); -+ } -+ } -+ -+ bool checkJsFile() const -+ { -+ if (m_jsFile.isNull() || !m_jsFile->isValid()) { -+ qWarning() << "js file object is invalid"; -+ return false; -+ } -+ return true; -+ } -+ -+ bool open(QIODevice::OpenMode openMode) override -+ { -+ if (!checkJsFile()) -+ return false; -+ -+// const READ_ONLY = 0o0; // Read only Permission -+// const WRITE_ONLY = 0o1; // Write only Permission -+// const READ_WRITE = 0o2; // Write and Read Permission -+// const CREATE = 0o100; // If not exist, create file -+// const TRUNC = 0o1000; // File truncate len 0 -+// const APPEND = 0o2000; // File append write -+// const NONBLOCK = 0o4000; // File open in nonblocking mode -+// const DIR = 0o200000; // File is Dir -+// const NOFOLLOW = 0o400000; // File is not symbolic link -+// const SYNC = 0o4010000; // SYNC IO -+ -+ int mode = 0; -+ if (openMode & QIODevice::ReadOnly) { -+ -+ } -+ if (openMode & QIODevice::WriteOnly) { -+ mode |= 1; -+ } -+ if (openMode & QIODevice::Append) { -+ mode |= 02000; -+ } -+ -+ if (openMode & QIODevice::Truncate) { -+ mode |= 01000; -+ } -+ -+ return m_jsFile->call("open", mode); -+ } -+ -+ bool flush() -+ { -+ if (!checkJsFile()) -+ return false; -+ return m_jsFile->call("flush"); -+ } -+ -+ bool close() override -+ { -+ if (!checkJsFile()) -+ return false; -+ return m_jsFile->call("close"); -+ } -+ -+ qint64 size() const override -+ { -+ if (!checkJsFile()) -+ return -1; -+ return m_jsFile->call("size"); -+ } -+ -+ qint64 pos() const override -+ { -+ if (!checkJsFile()) -+ return -1; -+ return m_jsFile->call("pos"); -+ } -+ -+ bool seek(qint64 pos) override -+ { -+ if (!checkJsFile()) -+ return false; -+ return m_jsFile->call("seek", pos); -+ } -+ -+ qint64 read(char *data, qint64 maxlen) override -+ { -+ if (!checkJsFile()) -+ return -1; -+ QByteArray result = m_jsFile->call("read", maxlen); -+ qint64 l = result.length(); -+ memcpy(data, result.constData(), qMin(l, maxlen)); -+ return l; -+ } -+ -+ bool isSequential() const override -+ { -+ return false; -+ } -+ -+ bool caseSensitive() const override -+ { -+ return true; -+ } -+ -+ bool isRelativePath() const override -+ { -+ return false; -+ } -+ -+ FileFlags fileFlags(FileFlags type = FileInfoAll) const override -+ { -+ FileFlags flags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag); -+ if (m_jsFile->isValid()) { -+ flags |= FileType; -+ } -+ -+ return type & flags; -+ } -+ -+ QString fileName(FileName file = DefaultName) const override -+ { -+ int pos; -+ switch (file) { -+ case DefaultName: -+ case AbsoluteName: -+ case CanonicalName: -+ return m_fileName; -+ case BaseName: -+ if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1) -+ return m_fileName.mid(pos); -+ else -+ return m_fileName; -+ case PathName: -+ case AbsolutePathName: -+ case CanonicalPathName: -+ if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1) -+ return m_fileName.left(pos); -+ else -+ return m_fileName; -+ default: -+ return QString(); -+ } -+ } -+ -+ void setFileName(const QString &file) override -+ { -+ if (file == m_fileName) -+ return; -+ -+ m_fileName = file; -+ -+ -+ close(); -+ } -+ -+ qint64 write(const char *data, qint64 len) override -+ { -+ if (!checkJsFile()) -+ return -1; -+ QByteArray dataArrary = QByteArray(data, len); -+ qint64 result = m_jsFile->call("write", dataArrary); -+ return result; -+ } -+ -+private: -+ QString m_fileName; -+ QString m_fileHandler; -+ QSharedPointer m_jsFile; -+}; -+ -+ -+QOpenHarmonyFileEngineHandler::QOpenHarmonyFileEngineHandler() -+ : m_rawDirCache(5) -+ , m_hasPrepopulatedCache(false) -+ , m_hasTriedPrepopulatingCache(false) -+{ -+ -+} -+ -+QAbstractFileEngine *QOpenHarmonyFileEngineHandler::create(const QString &fileName) const -+{ -+ if (fileName.isEmpty()) -+ return 0; -+ -+ static QLatin1String datasharePrefix("datashare:"); -+ static QLatin1String rawfilePrefix("rawfile:/"); -+ -+ if (!fileName.startsWith(datasharePrefix) && !fileName.startsWith(rawfilePrefix)) -+ return 0; -+ -+ if (fileName.startsWith(datasharePrefix)) -+ return new QOpenHarmonyFileEngine(fileName); -+ -+ if (fileName.startsWith(rawfilePrefix)) { -+ QString _fileName = fileName; -+ _fileName.replace("rawfile:/", ""); -+ QByteArray byteArray = _fileName.toLatin1(); -+ RawFile *file = OH_ResourceManager_OpenRawFile(QtOpenHarmony::resourceManager(), byteArray.constData()); -+ if (file != nullptr) { -+ return new QOpenHarmonyRawfileEngine(file, fileName); -+ } -+ return 0; -+ -+ //Todo OH_ResourceManager_OpenRawDir传入不存在的目录返回值不为空,无法判断 -+ -+// m_rawfileCacheMutext.lock(); -+// QSharedPointer *ohrd = m_rawDirCache.object(_fileName.toLatin1()); -+// m_rawfileCacheMutext.unlock(); -+ -+// if (!ohrd) { -+// if (!m_hasPrepopulatedCache) { -+// RawDir *rd = OH_ResourceManager_OpenRawDir(QtOpenHarmony::resourceManager(), byteArray.constData()); -+// if (rd) { -+// ohrd = new QSharedPointer(new OpenHarmonyRawDir(rd)); -+// m_rawfileCacheMutext.lock(); -+// m_rawDirCache.insert(_fileName.toLatin1(), ohrd); -+// m_rawfileCacheMutext.unlock(); -+// return 0; -+// return new QOpenHarmonyRawfileEngine(*ohrd, fileName); -+// } -+// return 0; -+// } -+// } else { -+// return new QOpenHarmonyRawfileEngine(*ohrd, fileName); -+// } -+ } -+ return 0; -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/platforms/openharmony/qopenharmonyfileenginehandler.h b/src/plugins/platforms/openharmony/qopenharmonyfileenginehandler.h -new file mode 100644 -index 0000000000..b81d93de63 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyfileenginehandler.h -@@ -0,0 +1,28 @@ -+#ifndef QOPENHARMONYFILEENGINEHANDLER_H -+#define QOPENHARMONYFILEENGINEHANDLER_H -+ -+#include -+#include -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+struct OpenHarmonyRawDir; -+ -+class Q_CORE_EXPORT QOpenHarmonyFileEngineHandler : public QAbstractFileEngineHandler -+{ -+public: -+ QOpenHarmonyFileEngineHandler(); -+ virtual QAbstractFileEngine *create(const QString &fileName) const override; -+private: -+ -+ mutable QCache> m_rawDirCache; -+ mutable QMutex m_rawfileCacheMutext; -+ mutable bool m_hasPrepopulatedCache; -+ mutable bool m_hasTriedPrepopulatingCache; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYFILEENGINEHANDLER_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyjswindow.cpp b/src/plugins/platforms/openharmony/qopenharmonyjswindow.cpp -new file mode 100644 -index 0000000000..425c4316ee ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyjswindow.cpp -@@ -0,0 +1,132 @@ -+#include "qopenharmonyjswindow.h" -+#include "qopenharmonyplatformwindow.h" -+#include "qopenharmonyxcomponent.h" -+#include -+#include "qopenharmonymain.h" -+#include "qopenharmonylog.h" -+ -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+QOpenHarmonyJsWindow::QOpenHarmonyJsWindow(const QString &name) -+ : m_window(nullptr) -+ , m_component(nullptr) -+ , m_name(name) -+{ -+ -+} -+ -+void QOpenHarmonyJsWindow::setQtWindow(QOpenHarmonyPlatformWindow *window) -+{ -+ if (window == nullptr) -+ return; -+ m_window = window; -+} -+ -+QString QOpenHarmonyJsWindow::name() const -+{ -+ return m_name; -+} -+ -+QRect QOpenHarmonyJsWindow::geometry() const -+{ -+ return m_geometry; -+} -+ -+void QOpenHarmonyJsWindow::setComponent(QOpenHarmonyXComponent *component) -+{ -+ if (component == nullptr) -+ return; -+ m_component = component; -+} -+ -+void QOpenHarmonyJsWindow::setNativeWindow(EGLNativeWindowType nativeWindow) -+{ -+ m_nativeWindow = nativeWindow; -+ updateSurfaceSize(); -+} -+ -+EGLNativeWindowType QOpenHarmonyJsWindow::nativeWindow() const -+{ -+ return m_nativeWindow; -+} -+ -+void QOpenHarmonyJsWindow::updateSurfaceSize() -+{ -+ uint64_t w; -+ uint64_t h; -+ int32_t ret = OH_NativeXComponent_GetXComponentSize(m_component->nativeComponent(), (void *)m_nativeWindow, &w, &h); -+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { -+ LOGE("Get surface size failed"); -+ return; -+ } -+ -+ LOGI("Get surface size %{public}llu, %{public}llu", w, h); -+ m_component->updateSize(w, h); -+} -+ -+OH_NativeXComponent *QOpenHarmonyJsWindow::component() const -+{ -+ if (m_component == nullptr) -+ return nullptr; -+ return m_component->nativeComponent(); -+} -+ -+EGLSurface QOpenHarmonyJsWindow::eglSurface() -+{ -+ if (m_component == nullptr) -+ return nullptr; -+ return m_component->eglSurface(); -+} -+ -+QImage QOpenHarmonyJsWindow::image() const -+{ -+ if (m_component == nullptr) -+ return QImage(); -+ return m_component->image(); -+} -+ -+void QOpenHarmonyJsWindow::paint(const QImage &image) -+{ -+ if (m_component == nullptr) -+ return; -+ return m_component->paint(image); -+} -+ -+QMargins QOpenHarmonyJsWindow::frameMargins() const -+{ -+ return QtOpenHarmony::frameMargins(); -+} -+ -+void QOpenHarmonyJsWindow::setGeometry(const QRect &rect) -+{ -+ if (m_geometry == rect) -+ return; -+ m_geometry = rect; -+ if (m_jsWindow.isNull()) -+ m_jsWindow = qJsObjectLoader->create("JsWindow", m_name); -+ m_jsWindow->call("setGeometry", rect.left(), rect.top(), rect.width(), rect.height()); -+} -+ -+void QOpenHarmonyJsWindow::handleGeometryChange(const QRect &rect) -+{ -+ if (m_geometry == rect) -+ return; -+ m_geometry = rect; -+ if (m_window != nullptr) { -+ m_window->setGeometry(rect); -+ } -+} -+ -+void QOpenHarmonyJsWindow::setVisible(bool visible) -+{ -+ if (m_jsWindow.isNull()) -+ m_jsWindow = qJsObjectLoader->create("JsWindow", m_name); -+ m_jsWindow->call("setVisible", visible); -+} -+ -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/platforms/openharmony/qopenharmonyjswindow.h b/src/plugins/platforms/openharmony/qopenharmonyjswindow.h -new file mode 100644 -index 0000000000..fca5cd99c3 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyjswindow.h -@@ -0,0 +1,64 @@ -+#ifndef QOPENHARMONYJSWINDOW_H -+#define QOPENHARMONYJSWINDOW_H -+ -+struct OH_NativeXComponent; -+#include -+#include -+#include -+#include -+#include -+ -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyJsObject; -+class QOpenHarmonyXComponent; -+class QOpenHarmonyPlatformWindow; -+ -+class QOpenHarmonyJsWindow -+{ -+public: -+ explicit QOpenHarmonyJsWindow(const QString &name); -+ -+ void setQtWindow(QOpenHarmonyPlatformWindow *window); -+ -+ QString name() const; -+ -+ void setVisible(bool visible); -+ -+ void setGeometry(const QRect &rect); -+ -+ void handleGeometryChange(const QRect &rect); -+ -+ QRect geometry() const; -+ -+ void setComponent(QOpenHarmonyXComponent *component); -+ -+ void setNativeWindow(EGLNativeWindowType nativeWindow); -+ -+ EGLNativeWindowType nativeWindow() const; -+ -+ void updateSurfaceSize(); -+ -+ -+ OH_NativeXComponent *component() const; -+ -+ EGLSurface eglSurface(); -+ -+ QImage image() const; -+ -+ void paint(const QImage &image); -+ -+ QMargins frameMargins() const; -+ -+private: -+ QOpenHarmonyPlatformWindow *m_window; -+ QSharedPointer m_jsWindow; -+ QOpenHarmonyXComponent *m_component; -+ QString m_name; -+ QRect m_geometry; -+ EGLNativeWindowType m_nativeWindow = 0; -+}; -+ -+QT_END_NAMESPACE -+#endif // QOPENHARMONYJSWINDOW_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyjswindowmanager.cpp b/src/plugins/platforms/openharmony/qopenharmonyjswindowmanager.cpp -new file mode 100644 -index 0000000000..bcbb1f55fb ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyjswindowmanager.cpp -@@ -0,0 +1,195 @@ -+#include "qopenharmonyjswindowmanager.h" -+#include "qopenharmonyxcomponent.h" -+#include "qopenharmonyjsenvironment.h" -+#include "qopenharmonyjsobjectloader.h" -+#include "qopenharmonylog.h" -+#include "qopenharmonymain.h" -+#include "qopenharmonyjswindow.h" -+ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+Q_GLOBAL_STATIC(QOpenHarmonyJsWindowManager, manager) -+ -+QOpenHarmonyJsWindowManager::QOpenHarmonyJsWindowManager() -+ : m_mainwindow(nullptr) -+{ -+ -+} -+ -+QOpenHarmonyJsWindowManager::~QOpenHarmonyJsWindowManager() -+{ -+ qDeleteAll(m_windows); -+} -+ -+static QString windowName() -+{ -+ static int index = 1; -+ QString name = QString("openharmony_window_%1").arg(index++); -+ return name; -+} -+ -+QOpenHarmonyJsWindow *QOpenHarmonyJsWindowManager::createWindow(QOpenHarmonyPlatformWindow *platformWindow) -+{ -+ QString name = QtOpenHarmony::isPhone() ? "opemharmony_qt_mainwindow" : windowName(); -+ QOpenHarmonyJsWindow *jsWindow = window(name); -+ if (jsWindow != nullptr) { -+ jsWindow->setQtWindow(platformWindow); -+ return jsWindow; -+ } -+ m_jsWindowCreated.storeRelease(false); -+ qDebug() << "create open harmony platfrom window for" << name; -+ checkJsWindowManager(); -+ bool result = m_jsWindowManager->call("createWindow", name); -+ if (!result) { -+ qWarning() << "create open harmony platfrom window failed"; -+ return nullptr; -+ } -+ if (wait()) { -+ qDebug() << "create open harmony platfrom window succeeded"; -+ } -+ else { -+ qWarning() << "create open harmony platfrom window failed"; -+ return nullptr; -+ } -+ -+ jsWindow = window(name); -+ if (jsWindow != nullptr) { -+ jsWindow->setQtWindow(platformWindow); -+ return jsWindow; -+ } -+ return jsWindow; -+} -+ -+void QOpenHarmonyJsWindowManager::destoryWindow(QOpenHarmonyJsWindow *window) -+{ -+ if (window == nullptr || !m_windows.contains(window)) -+ return; -+ m_windows.removeOne(window); -+ checkJsWindowManager(); -+ bool result = m_jsWindowManager->call("destoryWindow", window->name()); -+ if (!result) { -+ qWarning() << "destory open harmony platfrom window failed"; -+ } -+ delete window; -+} -+ -+ -+QOpenHarmonyJsWindowManager *QOpenHarmonyJsWindowManager::instance() -+{ -+ return manager(); -+} -+ -+QOpenHarmonyJsWindow *QOpenHarmonyJsWindowManager::window(OH_NativeXComponent *component) const -+{ -+ return find([component](QOpenHarmonyJsWindow *w){ -+ return w->component() == component; -+ }); -+} -+ -+QOpenHarmonyJsWindow *QOpenHarmonyJsWindowManager::window(const QString &name) const -+{ -+ return find([name](QOpenHarmonyJsWindow *w){ -+ return w->name() == name; -+ }); -+} -+ -+void QOpenHarmonyJsWindowManager::setWindowXComponent(OH_NativeXComponent *component) -+{ -+ QOpenHarmonyXComponent *xc = new QOpenHarmonyXComponent(component); -+ QString name = xc->name(); -+ QOpenHarmonyJsWindow *jsWindow = new QOpenHarmonyJsWindow(name); -+ xc->setWindow(jsWindow); -+ jsWindow->setComponent(xc); -+ m_windows << jsWindow; -+} -+ -+void QOpenHarmonyJsWindowManager::checkJsWindowManager() -+{ -+ if (m_jsWindowManager.isNull()) -+ m_jsWindowManager = qJsObjectLoader->create("JsWindowManager"); -+} -+ -+QOpenHarmonyJsWindow *QOpenHarmonyJsWindowManager::find(std::function function) const -+{ -+ auto it = std::find_if(m_windows.constBegin(), m_windows.constEnd(), function); -+ return it == m_windows.constEnd() ? nullptr : *it; -+} -+ -+bool QOpenHarmonyJsWindowManager::wait(int timeout) -+{ -+ QElapsedTimer start; -+ start.start(); -+ while (!m_jsWindowCreated.loadAcquire()) { -+ QThread::sleep(0); -+ if (start.elapsed() > timeout) -+ return false; -+ } -+ return true; -+} -+ -+static napi_value handleJsGeometryChange(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 5; -+ napi_value args[5]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 5) { -+ qJs::throwError("Wrong number of arguments"); -+ return nullptr; -+ } -+ -+ QString name = qJs::getString(args[0]); -+ int left = qJs::getInt32(args[1]); -+ int top = qJs::getInt32(args[2]); -+ int w = qJs::getInt32(args[3]); -+ int h = qJs::getInt32(args[4]); -+ qJsWindowManager->handleGeometryChange(name, QRect(left, top, w, h)); -+ return nullptr; -+} -+ -+void QOpenHarmonyJsWindowManager::init(napi_env env, napi_value exports) -+{ -+ static bool inited = false; -+ if (!inited) { -+ inited = true; -+ napi_property_descriptor desc[] ={ -+ DECLARE_NAPI_FUNCTION("handleGeometryChange", handleJsGeometryChange), -+ }; -+ napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); -+ } -+ napi_value exportInstance = nullptr; -+ OH_NativeXComponent *nativeXComponent = nullptr; -+ int32_t ret; -+ char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { }; -+ uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; -+ -+ NAPI_CALL_RETURN_VOID_NO_THROW(env, napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance)); -+ NAPI_CALL_RETURN_VOID_NO_THROW(env, napi_unwrap(env, exportInstance, reinterpret_cast(&nativeXComponent))); -+ -+ qJsWindowManager->setWindowXComponent(nativeXComponent); -+} -+ -+void QOpenHarmonyJsWindowManager::handleGeometryChange(const QString &name, const QRect &rect) -+{ -+ if (QtOpenHarmony::isPhone()) { -+ QtOpenHarmony::updateDisplayMetrics(rect.width(), rect.height()); -+ } else { -+ QOpenHarmonyJsWindow *w = window(name); -+ if (w == nullptr) -+ return; -+ w->handleGeometryChange(rect); -+ } -+} -+ -+void QOpenHarmonyJsWindowManager::windowCreated() -+{ -+ m_jsWindowCreated.storeRelease(true); -+} -diff --git a/src/plugins/platforms/openharmony/qopenharmonyjswindowmanager.h b/src/plugins/platforms/openharmony/qopenharmonyjswindowmanager.h -new file mode 100644 -index 0000000000..93fd13de04 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyjswindowmanager.h -@@ -0,0 +1,53 @@ -+#ifndef QOPENHARMONYJSWINDOWMANAGER_H -+#define QOPENHARMONYJSWINDOWMANAGER_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+class QOpenHarmonyPlatformWindow; -+class QOpenHarmonyJsWindow; -+class QOpenHarmonyJsObject; -+struct OH_NativeXComponent; -+class QOpenHarmonyXComponent; -+ -+#define qJsWindowManager QOpenHarmonyJsWindowManager::instance() -+ -+class QOpenHarmonyJsWindowManager -+{ -+public: -+ QOpenHarmonyJsWindowManager(); -+ virtual ~QOpenHarmonyJsWindowManager(); -+ -+ static QOpenHarmonyJsWindowManager *instance(); -+ -+ QOpenHarmonyJsWindow *createWindow(QOpenHarmonyPlatformWindow *platformWindow = nullptr); -+ -+ void destoryWindow(QOpenHarmonyJsWindow *window); -+ -+ QOpenHarmonyJsWindow *window(OH_NativeXComponent *component) const; -+ -+ QOpenHarmonyJsWindow *window(const QString &name) const; -+ -+ static void init(napi_env env, napi_value exports); -+ -+ void handleGeometryChange(const QString &name, const QRect &rect); -+ -+ void windowCreated(); -+private slots: -+ void setWindowXComponent(OH_NativeXComponent *component); -+ void checkJsWindowManager(); -+private: -+ QOpenHarmonyJsWindow *find(std::function function) const; -+ bool wait(int timeout = 2000); -+private: -+ QList m_windows; -+ QSharedPointer m_jsWindowManager; -+ QBasicAtomicInt m_jsWindowCreated; -+ QOpenHarmonyJsWindow *m_mainwindow; -+}; -+ -+#endif // QOPENHARMONYJSWINDOWMANAGER_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonylog.h b/src/plugins/platforms/openharmony/qopenharmonylog.h -new file mode 100644 -index 0000000000..1a88d88b24 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonylog.h -@@ -0,0 +1,106 @@ -+#ifndef QOPENHARMONYDEFINES_H -+#define QOPENHARMONYDEFINES_H -+#include -+ -+#define NAPI_RETVAL_NOTHING -+ -+#define GET_AND_THROW_LAST_ERROR(env) \ -+ do { \ -+ const napi_extended_error_info* errorInfo = nullptr; \ -+ napi_get_last_error_info((env), &errorInfo); \ -+ bool isPending = false; \ -+ napi_is_exception_pending((env), &isPending); \ -+ if (!isPending && errorInfo != nullptr) { \ -+ const char* errorMessage = \ -+ errorInfo->error_message != nullptr ? errorInfo->error_message : "empty error message"; \ -+ napi_throw_error((env), nullptr, errorMessage); \ -+ } \ -+ } while (0) -+ -+#define NAPI_ASSERT_BASE(env, assertion, message, retVal) \ -+ do { \ -+ if (!(assertion)) { \ -+ napi_throw_error((env), nullptr, "assertion (" #assertion ") failed: " message); \ -+ return retVal; \ -+ } \ -+ } while (0) -+ -+#define NAPI_ASSERT(env, assertion, message) NAPI_ASSERT_BASE(env, assertion, message, nullptr) -+ -+#define NAPI_ASSERT_RETURN_VOID(env, assertion, message) NAPI_ASSERT_BASE(env, assertion, message, NAPI_RETVAL_NOTHING) -+ -+#define NAPI_CALL_BASE(env, theCall, retVal) \ -+ do { \ -+ if ((theCall) != napi_ok) { \ -+ GET_AND_THROW_LAST_ERROR((env)); \ -+ return retVal; \ -+ } \ -+ } while (0) -+ -+#define NAPI_CALL_BASE_NO_THROW(env, theCall, retVal) \ -+ do { \ -+ if ((theCall) != napi_ok) { \ -+ return retVal; \ -+ } \ -+ } while (0) -+ -+#define NAPI_CALL(env, theCall) NAPI_CALL_BASE(env, theCall, nullptr) -+ -+#define NAPI_CALL_RETURN_VOID(env, theCall) NAPI_CALL_BASE(env, theCall, NAPI_RETVAL_NOTHING) -+ -+#define NAPI_CALL_RETURN_VOID_NO_THROW(env, theCall) NAPI_CALL_BASE_NO_THROW(env, theCall, NAPI_RETVAL_NOTHING) -+ -+#define DECLARE_NAPI_PROPERTY(name, val) \ -+ { \ -+ (name), nullptr, nullptr, nullptr, nullptr, val, napi_default, nullptr \ -+ } -+ -+#define DECLARE_NAPI_STATIC_PROPERTY(name, val) \ -+ { \ -+ (name), nullptr, nullptr, nullptr, nullptr, val, napi_static, nullptr \ -+ } -+ -+#define DECLARE_NAPI_FUNCTION(name, func) \ -+ { \ -+ (name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, nullptr \ -+ } -+ -+#define DECLARE_NAPI_FUNCTION_WITH_DATA(name, func, data) \ -+ { \ -+ (name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, data \ -+ } -+ -+#define DECLARE_NAPI_STATIC_FUNCTION(name, func) \ -+ { \ -+ (name), nullptr, (func), nullptr, nullptr, nullptr, napi_static, nullptr \ -+ } -+ -+#define DECLARE_NAPI_GETTER(name, getter) \ -+ { \ -+ (name), nullptr, nullptr, (getter), nullptr, nullptr, napi_default, nullptr \ -+ } -+ -+#define DECLARE_NAPI_SETTER(name, setter) \ -+ { \ -+ (name), nullptr, nullptr, nullptr, (setter), nullptr, napi_default, nullptr \ -+ } -+ -+#define DECLARE_NAPI_GETTER_SETTER(name, getter, setter) \ -+ { \ -+ (name), nullptr, nullptr, (getter), (setter), nullptr, napi_default, nullptr \ -+ } -+ -+#define DECLARE_NAPI_FUNCTION(name, func) \ -+ { \ -+ (name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, nullptr \ -+ } -+ -+ -+#define QPA_LOG_DOMAIN 0xff00 -+#define QPA_LOG_TAG "QpaForOpenHarmony" -+#define LOGI(...) ((void)OH_LOG_Print(LOG_APP, LOG_INFO, QPA_LOG_DOMAIN, QPA_LOG_TAG, __VA_ARGS__)) -+#define LOGD(...) ((void)OH_LOG_Print(LOG_APP, LOG_DEBUG, QPA_LOG_DOMAIN, QPA_LOG_TAG, __VA_ARGS__)) -+#define LOGW(...) ((void)OH_LOG_Print(LOG_APP, LOG_WARN, QPA_LOG_DOMAIN, QPA_LOG_TAG, __VA_ARGS__)) -+#define LOGE(...) ((void)OH_LOG_Print(LOG_APP, LOG_ERROR, QPA_LOG_DOMAIN, QPA_LOG_TAG, __VA_ARGS__)) -+ -+#endif // QOPENHARMONYDEFINES_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonymain.cpp b/src/plugins/platforms/openharmony/qopenharmonymain.cpp -new file mode 100644 -index 0000000000..ca8eefffcc ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonymain.cpp -@@ -0,0 +1,451 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "qopenharmonylog.h" -+#include "qopenharmonymain.h" -+#include "qopenharmonyfileenginehandler.h" -+#include "qopenharmonyjswindowmanager.h" -+#include "qopenharmonyplatformintegration.h" -+#include "qopenharmonyplatformdialoghelpers.h" -+#include "qopenharmonyplatformclipboard.h" -+#include "qopenharmonyplatforminputcontext.h" -+#include "qopenharmonyeventdispatcher.h" -+#include "qopenharmonyplatformwindow.h" -+#include "qopenharmonyplatformfontdatabase.h" -+ -+extern "C" typedef int (*Main)(int, char **); //use the standard main method to start the application -+static Main m_main = nullptr; -+static void *m_mainLibraryHnd = nullptr; -+pthread_t m_qtAppThread = 0; -+static sem_t m_exitSemaphore, m_terminateSemaphore; -+static double m_scaledDensity = 0; -+static double m_density = 1.5; -+static int m_screenWidthPixels = 0; -+static int m_screenHeightPixels = 0; -+static int m_desktopWidthPixels = 0; -+static int m_desktopHeightPixels = 0; -+static int m_avoidLeftWidth = 0; -+static int m_avoidTopHeight = 0; -+static NativeResourceManager *m_resourceManager = nullptr; -+static QOpenHarmonyPlatformIntegration *m_platformIntegration = nullptr; -+static QOpenHarmonyFileEngineHandler *m_openHarmonyFileEngineHandler = nullptr; -+static QString m_deviceType = "default"; -+static QList m_applicationParams; -+ -+static void *startMainMethod(void *arg) -+{ -+ QVarLengthArray params(m_applicationParams.size()); -+ -+ for (int i = 0; i < m_applicationParams.size(); i++) -+ params[i] = static_cast(m_applicationParams[i].constData()); -+ -+ int ret = m_main(m_applicationParams.length(), const_cast(params.data())); -+ -+ if (m_mainLibraryHnd) { -+ int res = dlclose(m_mainLibraryHnd); -+ if (res < 0) -+ LOGE("dlclose failed: %{public}s", dlerror()); -+ } -+ -+ sem_post(&m_terminateSemaphore); -+ sem_wait(&m_exitSemaphore); -+ sem_destroy(&m_exitSemaphore); -+#if 1 -+ if (0 != ret) -+ pthread_exit((void*)-1); -+ else -+ pthread_exit((void*)0); -+#endif -+ return nullptr; -+} -+ -+static napi_value startQtApplication(napi_env env, napi_callback_info info) -+{ -+ napi_status status; -+ size_t argc = 3; -+ napi_value args[3]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc < 2) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments"); -+ return qJs::createBool(false); -+ } -+ -+ QString bundleStr = qJs::getObjectPropertyValue(args[0], "bundleCodeDir"); -+ QByteArray bundle = bundleStr.toLatin1(); -+ if (bundle.isEmpty()) { -+ LOGE("args is vaild"); -+ return qJs::createBool(false); -+ } -+ -+#if defined(Q_PROCESSOR_ARM_64) -+ QByteArray libDir = bundle + "/libs/arm64"; -+#elif defined(Q_PROCESSOR_ARM_32) -+ QByteArray libDir = bundle + "/libs/arm"; -+#endif -+ -+ QByteArrayList qmls = {bundle + "/entry/resources/rawfile"}; -+ QString cacheQmlStr = qJs::getObjectPropertyValue(args[0], "qmlDir"); -+ if (!cacheQmlStr.isEmpty()) -+ qmls << cacheQmlStr.toLatin1(); -+ -+ QString cacheDir = qJs::getObjectPropertyValue(args[0], "cacheDir"); -+ if (::setenv("QT_HARMONY_CACHE_DIR", cacheDir.toLatin1().constData(), 1) != 0) { -+ LOGE("Can't set environment for QT_HARMONY_CACHE_DIR"); -+ } -+ -+ if (::setenv("QT_QPA_PLATFORM_PLUGIN_PATH", libDir.constData(), 1) != 0) { -+ LOGE("Can't set environment for QT_QPA_PLATFORM_PLUGIN_PATH"); -+ } -+ if (::setenv("QML_DISABLE_DISK_CACHE", "1", 1) != 0) { -+ LOGE("Can't set environment for QML_DISABLE_DISK_CACHE"); -+ } -+ if (::setenv("QT_PLUGIN_PATH", libDir.constData(), 1) != 0) { -+ LOGE("Can't set environment for QT_PLUGIN_PATH"); -+ } -+ -+ QByteArrayList boundPath = { libDir }; -+ QByteArray boundImport = boundPath.join(":"); -+ qWarning() << "harmony boundImport import path:" << boundImport; -+ if (::setenv("QT_HARMONY_BUNDLED_LIBS_PATH", boundImport.constData(), 1) != 0) { -+ LOGE("Can't set environment for QT_HARMONY_BUNDLED_LIBS_PATH"); -+ } -+ -+ QByteArray qmlImportPath = qmls.join(":"); -+ qWarning() << "qml import path:" << qmlImportPath; -+ if (::setenv("QML2_IMPORT_PATH", qmlImportPath.constData(), 1) != 0) { -+ LOGE("Can't set environment for QML2_IMPORT_PATH"); -+ } -+#if 0 -+ if (::setenv("QT_DEBUG_PLUGINS", "1", 1) != 0) { -+ LOGE("Can't set environment for QT_DEBUG_PLUGINS"); -+ } -+#endif -+ m_mainLibraryHnd = nullptr; -+ -+ QByteArray params = qJs::getString(args[1]).toLatin1(); -+ m_applicationParams = params.split('\t'); -+ if (m_applicationParams.empty()) { -+ LOGE("no qt application specified"); -+ return qJs::createBool(false); -+ } -+ QByteArray fileName = m_applicationParams.first(); -+ LOGI("load qt application %{public}s", fileName.constData()); -+ -+ //look for main() -+ // Obtain a handle to the main library (the library that contains the main() function). -+ m_mainLibraryHnd = dlopen(fileName.constData(), 0); -+ if (Q_UNLIKELY(!m_mainLibraryHnd)) { -+ QString error = QString("dlopen failed: %1").arg(dlerror()); -+ LOGI("load qt application %{public}s failed%{public}s", fileName.constData(), dlerror()); -+ napi_throw_type_error(env, NULL, error.toUtf8().constData()); -+ return nullptr; -+ } -+ m_main = (Main)dlsym(m_mainLibraryHnd, "main"); -+ if (Q_UNLIKELY(!m_main)) { -+ QString error = QString("dlsym failed: %1, Could not find main method.").arg(dlerror()); -+ qJs::throwError(error.toUtf8().constData()); -+ return qJs::createBool(false); -+ } -+ if (sem_init(&m_exitSemaphore, 0, 0) == -1) -+ return qJs::createBool(false); -+ -+ if (sem_init(&m_terminateSemaphore, 0, 0) == -1) -+ return qJs::createBool(false); -+ -+ -+ m_openHarmonyFileEngineHandler = new QOpenHarmonyFileEngineHandler; -+ m_applicationParams[0] = libDir + "/" + fileName; -+ pthread_create(&m_qtAppThread, nullptr, startMainMethod, nullptr); -+ return qJs::createBool(true); -+} -+ -+static napi_value setResourceManager(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 1; -+ napi_value args[1]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ if (argc != 1) { -+ qJs::throwError("Wrong number of arguments"); -+ return nullptr; -+ } -+ m_resourceManager = OH_ResourceManager_InitNativeResourceManager(env, args[0]); -+ return nullptr; -+} -+ -+static napi_value setDeviceType(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 1; -+ napi_value args[1]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ if (argc != 1) { -+ qJs::throwError("Wrong number of arguments"); -+ return nullptr; -+ } -+ m_deviceType = qJs::getString(args[0]); -+ return nullptr; -+} -+ -+static napi_value setDisplayMetrics(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 10; -+ napi_value args[10]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 10) { -+ qJs::throwError("Wrong number of arguments"); -+ return nullptr; -+ } -+ -+ double value[10] = {0}; -+ for (size_t i = 0; i < argc; ++i) { -+ value[i] = qJs::getDouble(args[i]); -+ } -+ -+ m_screenWidthPixels = static_cast(value[0]); -+ m_screenHeightPixels = static_cast(value[1]); -+ -+ m_density = value[2]; -+ m_scaledDensity = value[3]; -+ -+ // openharmony old api, xdpi and ydpi is 0 -+ double xdpi = value[4]; -+ double ydpi = value[5]; -+ -+ m_avoidLeftWidth = static_cast(value[6]); -+ m_avoidTopHeight = static_cast(value[7]); -+ -+ m_desktopWidthPixels = static_cast(value[8]); -+ m_desktopHeightPixels = static_cast(value[9]); -+ -+ m_screenWidthPixels = qMax(m_screenWidthPixels, m_desktopWidthPixels); -+ m_screenHeightPixels = qMax(m_screenHeightPixels, m_desktopHeightPixels); -+ -+ if (m_platformIntegration == nullptr) { -+ QOpenHarmonyPlatformIntegration::setDefaultDisplayMetrics(m_avoidLeftWidth, m_avoidTopHeight, m_desktopWidthPixels, -+ m_desktopHeightPixels, -+ qRound(double(m_screenWidthPixels) / xdpi * 25.4), -+ qRound(double(m_screenHeightPixels) / ydpi * 25.4), -+ m_screenWidthPixels, -+ m_screenHeightPixels); -+ } else { -+ m_platformIntegration->setDisplayMetrics(qRound(double(m_screenWidthPixels) / xdpi * 25.4), -+ qRound(double(m_screenHeightPixels) / ydpi * 25.4)); -+ m_platformIntegration->setScreenSize(m_screenWidthPixels, m_screenHeightPixels); -+ m_platformIntegration->setDesktopSize(m_avoidLeftWidth, m_avoidTopHeight, m_desktopWidthPixels, m_desktopHeightPixels); -+ } -+ return nullptr; -+} -+ -+static napi_value updateApplicationState(napi_env env, napi_callback_info info) -+{ -+ if (!m_main || !m_platformIntegration) { -+ return nullptr; -+ } -+ -+ size_t argc = 1; -+ napi_value args[1]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 1) { -+ qJs::throwError("Wrong number of arguments in updateApplicationState"); -+ return nullptr; -+ } -+ -+ Qt::ApplicationState qtState = Qt::ApplicationSuspended; -+ -+// Openharmony doc -+// SHOWN = 1, -+// ACTIVE, -+// INACTIVE, -+// HIDDEN -+ -+ int32_t state = qJs::getInt32(args[0]); -+ switch (state) { -+ case 1: -+ qtState = Qt::ApplicationActive; -+ break; -+ case 2: -+ qtState = Qt::ApplicationActive; -+ break; -+ case 3: -+ qtState = Qt::ApplicationInactive; -+ break; -+ case 4: -+ qtState = Qt::ApplicationSuspended; -+ } -+ -+ if (qtState == Qt::ApplicationActive) -+ QtHarmonyPrivate::handleResume(); -+ else if (state == Qt::ApplicationInactive) -+ QtHarmonyPrivate::handlePause(); -+ -+ if (qtState <= Qt::ApplicationInactive) { -+ // NOTE: sometimes we will receive two consecutive suspended notifications, -+ // In the second suspended notification, QWindowSystemInterface::flushWindowSystemEvents() -+ // will deadlock since the dispatcher has been stopped in the first suspended notification. -+ // To avoid the deadlock we simply return if we found the event dispatcher has been stopped. -+ if (QOpenHarmonyEventDispatcherStopper::instance()->stopped()) -+ return nullptr; -+ -+ // Don't send timers and sockets events anymore if we are going to hide all windows -+ QOpenHarmonyEventDispatcherStopper::instance()->goingToStop(true); -+ QWindowSystemInterface::handleApplicationStateChanged(qtState); -+ if (state == Qt::ApplicationSuspended) -+ QOpenHarmonyEventDispatcherStopper::instance()->stopAll(); -+ } else { -+ QOpenHarmonyEventDispatcherStopper::instance()->startAll(); -+ QWindowSystemInterface::handleApplicationStateChanged(qtState); -+ QOpenHarmonyEventDispatcherStopper::instance()->goingToStop(false); -+ } -+ return nullptr; -+} -+ -+static napi_value quitQtApplication(napi_env env, napi_callback_info info) -+{ -+ if (m_openHarmonyFileEngineHandler != nullptr) { -+ delete m_openHarmonyFileEngineHandler; -+ m_openHarmonyFileEngineHandler = nullptr; -+ } -+ LOGI("quit qt application"); -+ OH_ResourceManager_ReleaseNativeResourceManager(m_resourceManager); -+ qJs::quit(); -+ //qApp->quit(); -+ return nullptr; -+} -+ -+static napi_value qtMajorVersion(napi_env env, napi_callback_info info) -+{ -+ return qJs::createInt32(QT_VERSION_MAJOR); -+} -+ -+static napi_value waitForQtAppExit(napi_env env, napi_callback_info info) -+{ -+ // sem_wait(&m_terminateSemaphore); -+ if (sem_trywait(&m_terminateSemaphore) == 0) { -+ return qJs::createInt32(0); -+ } -+ else -+ { -+ return qJs::createInt32(1); -+ } -+} -+ -+/* -+ * function for module exports -+ */ -+EXTERN_C_START -+static napi_value Init(napi_env env, napi_value exports) -+{ -+ static bool inited = false; -+ QOpenHarmonyJsWindowManager::init(env, exports); -+ if (!inited) { -+ napi_property_descriptor desc[] ={ -+ DECLARE_NAPI_FUNCTION("startQtApplication", startQtApplication), -+ DECLARE_NAPI_FUNCTION("setDisplayMetrics", setDisplayMetrics), -+ DECLARE_NAPI_FUNCTION("updateApplicationState", updateApplicationState), -+ DECLARE_NAPI_FUNCTION("setResourceManager", setResourceManager), -+ DECLARE_NAPI_FUNCTION("quitQtApplication", quitQtApplication), -+ DECLARE_NAPI_FUNCTION("qtMajorVersion", qtMajorVersion), -+ DECLARE_NAPI_FUNCTION("setDeviceType", setDeviceType), -+ DECLARE_NAPI_FUNCTION("waitForQtAppExit", waitForQtAppExit), -+ }; -+ NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); -+ QOpenHarmonyPlatformInputContext::init(env, exports); -+ QOpenHarmonyPlatformFontDatabase::init(env, exports); -+ QOpenHarmonyPlatformMessageDialogHelper::init(env, exports); -+ QOpenHarmonyPlatformFileDialogHelper::init(env, exports); -+ -+#ifndef QT_NO_CLIPBOARD -+ QOpenHarmonyPlatformClipboard::init(env, exports); -+#endif -+ qJs::init(env); -+ inited = true; -+ } -+ return exports; -+} -+EXTERN_C_END -+ -+/* -+ * Napi Module define -+ */ -+static napi_module openharmonyPluginModule = { -+ .nm_version = 1, -+ .nm_flags = 0, -+ .nm_filename = nullptr, -+ .nm_register_func = Init, -+ .nm_modname = "plugins_platforms_qopenharmony", -+ .nm_priv = ((void*)0), -+ .reserved = { 0 }, -+}; -+/* -+ * Module register function -+ */ -+extern "C" __attribute__((constructor)) void RegisterModule(void) -+{ -+ napi_module_register(&openharmonyPluginModule); -+} -+ -+double QtOpenHarmony::scaledDensity() -+{ -+ return m_scaledDensity; -+} -+ -+double QtOpenHarmony::pixelDensity() -+{ -+ return m_density; -+} -+ -+void QtOpenHarmony::setOpenHarmonyPlatformIntegration(QOpenHarmonyPlatformIntegration *integration) -+{ -+ m_platformIntegration = integration; -+} -+ -+void QtOpenHarmony::updateDisplayMetrics(int w, int h) -+{ -+ m_screenWidthPixels = w; -+ m_screenHeightPixels = h; -+ if (m_platformIntegration != nullptr) -+ m_platformIntegration->setScreenSize(w, h); -+} -+ -+NativeResourceManager *QtOpenHarmony::resourceManager() -+{ -+ return m_resourceManager; -+} -+ -+bool QtOpenHarmony::isPhone() -+{ -+ return m_deviceType == "default"; -+} -+ -+QOpenHarmonyPlatformIntegration *QtOpenHarmony::openharmonyPlatformIntegration() -+{ -+ return m_platformIntegration; -+} -+ -+QMargins QtOpenHarmony::frameMargins() -+{ -+ if (isPhone()) -+ return QMargins(); -+ // Todo read from js -+ return QMargins(7, 92, 6, 6); -+} -diff --git a/src/plugins/platforms/openharmony/qopenharmonymain.h b/src/plugins/platforms/openharmony/qopenharmonymain.h -new file mode 100644 -index 0000000000..c6209bb941 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonymain.h -@@ -0,0 +1,26 @@ -+#ifndef QOPENHARMONYMAIN_H -+#define QOPENHARMONYMAIN_H -+ -+#include -+#include -+ -+class QOpenHarmonyPlatformIntegration; -+struct NativeResourceManager; -+ -+namespace QtOpenHarmony { -+ -+void setOpenHarmonyPlatformIntegration(QOpenHarmonyPlatformIntegration *integration); -+ -+double scaledDensity(); -+ -+double pixelDensity(); -+ -+QMargins frameMargins(); -+ -+void updateDisplayMetrics(int w, int h); -+bool isPhone(); -+NativeResourceManager *resourceManager(); -+QOpenHarmonyPlatformIntegration *openharmonyPlatformIntegration(); -+} -+ -+#endif //QOPENHARMONYMAIN_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformbackingstore.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformbackingstore.cpp -new file mode 100644 -index 0000000000..a6a9c436fe ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformbackingstore.cpp -@@ -0,0 +1,48 @@ -+#include "qopenharmonyplatformbackingstore.h" -+#include "qopenharmonyplatformscreen.h" -+#include "qopenharmonyplatformwindow.h" -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+QOpenHarmonyPlatformBackingStore::QOpenHarmonyPlatformBackingStore(QWindow *window) -+ : QPlatformBackingStore(window) -+{ -+ if (window->handle()) -+ setBackingStore(window); -+} -+ -+QPaintDevice *QOpenHarmonyPlatformBackingStore::paintDevice() -+{ -+ return &m_image; -+} -+ -+void QOpenHarmonyPlatformBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) -+{ -+ Q_UNUSED(offset); -+ -+ if (!m_backingStoreSet) -+ setBackingStore(window); -+ -+ (static_cast(window->handle()))->repaint(region); -+} -+ -+void QOpenHarmonyPlatformBackingStore::resize(const QSize &size, const QRegion &staticContents) -+{ -+ Q_UNUSED(staticContents); -+ -+ if (m_image.size() != size) -+ m_image = QImage(size, window()->screen()->handle()->format()); -+} -+ -+void QOpenHarmonyPlatformBackingStore::setBackingStore(QWindow *window) -+{ -+ if (window->surfaceType() == QSurface::RasterSurface || window->surfaceType() == QSurface::RasterGLSurface) { -+ (static_cast(window->handle()))->setBackingStore(this); -+ m_backingStoreSet = true; -+ } else { -+ qWarning("QOpenHarmonyPlatformBackingStore does not support OpenGL-only windows."); -+ } -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformbackingstore.h b/src/plugins/platforms/openharmony/qopenharmonyplatformbackingstore.h -new file mode 100644 -index 0000000000..db75831b39 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformbackingstore.h -@@ -0,0 +1,25 @@ -+#ifndef QOPENHARMONYPLATFORMBACKINGSTORE_H -+#define QOPENHARMONYPLATFORMBACKINGSTORE_H -+ -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyPlatformBackingStore : public QPlatformBackingStore -+{ -+public: -+ explicit QOpenHarmonyPlatformBackingStore(QWindow *window); -+ QPaintDevice *paintDevice() 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 { return m_image; } -+ void setBackingStore(QWindow *window); -+protected: -+ QImage m_image; -+ bool m_backingStoreSet = false; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYPLATFORMBACKINGSTORE_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformclipboard.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformclipboard.cpp -new file mode 100644 -index 0000000000..6d4531134a ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformclipboard.cpp -@@ -0,0 +1,86 @@ -+#include "qopenharmonyplatformclipboard.h" -+#include "qopenharmonymain.h" -+#include "qopenharmonyjsobjectloader.h" -+ -+#include -+#include -+#include -+ -+#include -+#include -+ -+#ifndef QT_NO_CLIPBOARD -+ -+#define qClipBoard QOpenHarmonyPlatformClipboard::instance() -+ -+QOpenHarmonyPlatformClipboard *QOpenHarmonyPlatformClipboard::m_self = nullptr; -+ -+static napi_value pasteChanged(napi_env env, napi_callback_info info) -+{ -+ Q_UNUSED(env) -+ Q_UNUSED(info) -+ qClipBoard->emitChanged(QClipboard::Clipboard); -+ return nullptr; -+} -+ -+ -+napi_value QOpenHarmonyPlatformClipboard::init(napi_env env, napi_value exports) -+{ -+ napi_property_descriptor desc[] ={ -+ DECLARE_NAPI_FUNCTION("pasteChanged", pasteChanged), -+ }; -+ NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); -+ -+ return nullptr; -+} -+ -+QOpenHarmonyPlatformClipboard::QOpenHarmonyPlatformClipboard() -+{ -+ m_self = this; -+ m_jsClipboard = qJsObjectLoader->create("JsPasteBoard"); -+} -+ -+QOpenHarmonyPlatformClipboard::~QOpenHarmonyPlatformClipboard() -+{ -+ m_self = nullptr; -+} -+ -+QOpenHarmonyPlatformClipboard *QOpenHarmonyPlatformClipboard::instance() -+{ -+ return m_self; -+} -+ -+QMimeData *QOpenHarmonyPlatformClipboard::mimeData(QClipboard::Mode mode) -+{ -+ Q_UNUSED(mode); -+ Q_ASSERT(supportsMode(mode)); -+ m_mimeData.setText(hasClipboardText() ? clipboardText() : QString()); -+ return &m_mimeData; -+} -+ -+void QOpenHarmonyPlatformClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) -+{ -+ if (supportsMode(mode)) { -+ QString text = (data != 0 && data->hasText() ? data->text() : QString()); -+ m_jsClipboard->callWithoutReturn("setClipboardText", text);; -+ } -+ if (data != 0) -+ data->deleteLater(); -+} -+ -+bool QOpenHarmonyPlatformClipboard::supportsMode(QClipboard::Mode mode) const -+{ -+ return QClipboard::Clipboard == mode; -+} -+ -+bool QOpenHarmonyPlatformClipboard::hasClipboardText() -+{ -+ return m_jsClipboard->call("hasClipboardText"); -+} -+ -+QString QOpenHarmonyPlatformClipboard::clipboardText() -+{ -+ return m_jsClipboard->call("clipboardText"); -+} -+ -+#endif // QT_NO_CLIPBOARD -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformclipboard.h b/src/plugins/platforms/openharmony/qopenharmonyplatformclipboard.h -new file mode 100644 -index 0000000000..40b3576be4 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformclipboard.h -@@ -0,0 +1,36 @@ -+#ifndef QOPENHARMONYPLATFORMCLIPBOARD_H -+#define QOPENHARMONYPLATFORMCLIPBOARD_H -+ -+#include -+#include -+#include -+#include -+ -+#ifndef QT_NO_CLIPBOARD -+class QOpenHarmonyJsObject; -+ -+class QOpenHarmonyPlatformClipboard: public QPlatformClipboard -+{ -+public: -+ QOpenHarmonyPlatformClipboard(); -+ ~QOpenHarmonyPlatformClipboard(); -+ static QOpenHarmonyPlatformClipboard *instance(); -+ -+ QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard) override; -+ void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard) override; -+ bool supportsMode(QClipboard::Mode mode) const override; -+ -+ bool hasClipboardText(); -+ -+ QString clipboardText(); -+ -+ static napi_value init(napi_env env, napi_value exports); -+private: -+ QMimeData m_mimeData; -+ static QOpenHarmonyPlatformClipboard *m_self; -+ QSharedPointer m_jsClipboard; -+}; -+ -+#endif // QT_NO_CLIPBOARD -+ -+#endif // QOPENHARMONYPLATFORMCLIPBOARD_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformdialoghelpers.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformdialoghelpers.cpp -new file mode 100644 -index 0000000000..dbd3897b53 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformdialoghelpers.cpp -@@ -0,0 +1,298 @@ -+#include "qopenharmonyplatformdialoghelpers.h" -+#include "qopenharmonymain.h" -+#include "qopenharmonyjsobjectloader.h" -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+void QWaiter::wait(int timeout) -+{ -+ if (timeout != -1) -+ QTimer::singleShot(timeout, &m_loop, &QEventLoop::quit); -+ m_loop.exec(); -+} -+ -+void QWaiter::cancelWait() -+{ -+ if (m_loop.isRunning()) -+ m_loop.exit(); -+} -+ -+QOpenHarmonyPlatformMessageDialogHelper::QOpenHarmonyPlatformMessageDialogHelper() -+ : m_buttonId(-1) -+ , m_shown(false) -+{ -+ m_jsDialog = qJsObjectLoader->create("JsDialog"); -+} -+ -+QOpenHarmonyPlatformMessageDialogHelper::~QOpenHarmonyPlatformMessageDialogHelper() -+{ -+ -+} -+ -+void QOpenHarmonyPlatformMessageDialogHelper::exec() -+{ -+ if (!m_shown) -+ show(Qt::Dialog, Qt::ApplicationModal, 0); -+ wait(-1); -+} -+ -+static QString htmlText(QString text) -+{ -+ if (Qt::mightBeRichText(text)) -+ return text; -+ text.remove(QLatin1Char('\r')); -+ return text.toHtmlEscaped().replace(QLatin1Char('\n'), QLatin1String("
")); -+} -+ -+bool QOpenHarmonyPlatformMessageDialogHelper::show(Qt::WindowFlags windowFlags -+ , Qt::WindowModality windowModality -+ , QWindow *parent) -+{ -+ Q_UNUSED(windowFlags) -+ Q_UNUSED(windowModality) -+ Q_UNUSED(parent) -+ if (m_jsDialog.isNull()) -+ return false; -+ QSharedPointer opt = options(); -+ if (!opt.data()) -+ return false; -+ -+ m_buttons.clear(); -+ m_stanardButton.clear(); -+ const int * currentLayout = buttonLayout(Qt::Horizontal); -+ while (*currentLayout != QPlatformDialogHelper::EOL) { -+ int role = (*currentLayout & ~QPlatformDialogHelper::Reverse); -+ addButtons(opt, static_cast(role)); -+ ++currentLayout; -+ } -+ -+ QString text = htmlText(opt->text()); -+ QString str = htmlText(opt->informativeText()); -+ if (!str.isEmpty()) -+ text += str; -+ str = htmlText(opt->detailedText()); -+ if (!str.isEmpty()) -+ text += str; -+ -+ m_jsDialog->callWithoutReturn("messageBox", QVariant(qlonglong(static_cast(this))), htmlText(opt->windowTitle()), text, m_buttons); -+ m_shown = true; -+ return true; -+} -+ -+void QOpenHarmonyPlatformMessageDialogHelper::addButtons(QSharedPointer opt, ButtonRole role) -+{ -+ for (const QMessageDialogOptions::CustomButton &b : opt->customButtons()) { -+ if (b.role == role) { -+ QString label = b.label; -+ label.remove(QChar('&')); -+ } -+ } -+ -+ for (int i = QPlatformDialogHelper::FirstButton; i < QPlatformDialogHelper::LastButton; i<<=1) { -+ StandardButton b = static_cast(i); -+ if (buttonRole(b) == role && (opt->standardButtons() & i)) { -+ const QString text = QGuiApplicationPrivate::platformTheme()->standardButtonText(b); -+ m_buttons << text; -+ m_stanardButton << b; -+ } -+ } -+} -+ -+void QOpenHarmonyPlatformMessageDialogHelper::hide() -+{ -+ m_shown = false; -+} -+ -+void QOpenHarmonyPlatformMessageDialogHelper::setDialogResult(int buttonID) -+{ -+ m_buttonId = buttonID; -+ cancelWait(); -+ if (m_buttonId < 0) { -+ emit reject(); -+ return; -+ } -+ -+// QPlatformDialogHelper::StandardButton standardButton = static_cast(buttonID); -+ QPlatformDialogHelper::StandardButton standardButton = m_stanardButton.at(buttonID); -+ QPlatformDialogHelper::ButtonRole role = QPlatformDialogHelper::buttonRole(standardButton); -+ if (buttonID > QPlatformDialogHelper::LastButton) { -+ const QMessageDialogOptions::CustomButton *custom = options()->customButton(buttonID); -+ Q_ASSERT(custom); -+ role = custom->role; -+ } -+ emit clicked(standardButton, role); -+} -+ -+static napi_value dialogResult(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 2; -+ napi_value args[2]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 2) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments"); -+ return nullptr; -+ } -+ -+ int64_t value0 = qJs::getInt64(args[0]); -+ int32_t value1 = qJs::getInt32(args[1]); -+ -+ -+ QObject *object = reinterpret_cast(value0); -+ QMetaObject::invokeMethod(object, "setDialogResult", Qt::QueuedConnection, Q_ARG(int, value1)); -+ return nullptr; -+} -+ -+napi_value QOpenHarmonyPlatformMessageDialogHelper::init(napi_env env, napi_value exports) -+{ -+ napi_property_descriptor desc[] ={ -+ DECLARE_NAPI_FUNCTION("dialogResult", dialogResult), -+ }; -+ NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); -+ return nullptr; -+} -+ -+static napi_value selectedFilesResult(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 2; -+ napi_value args[2]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ if (argc != 2) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments"); -+ return nullptr; -+ } -+ int64_t value0 = qJs::getInt64(args[0]); -+ QStringList value1 = qJs::getStringList(args[1]); -+ -+ -+ QObject *object = reinterpret_cast(value0); -+ QMetaObject::invokeMethod(object, "setFileResult", Qt::QueuedConnection, Q_ARG(QStringList, value1)); -+ -+ return nullptr; -+} -+ -+napi_value QOpenHarmonyPlatformFileDialogHelper::init(napi_env env, napi_value exports) -+{ -+ napi_property_descriptor desc[] ={ -+ DECLARE_NAPI_FUNCTION("selectedFilesResult", selectedFilesResult), -+ }; -+ NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); -+ return nullptr; -+} -+ -+QOpenHarmonyPlatformFileDialogHelper::QOpenHarmonyPlatformFileDialogHelper() -+ : m_shown(false) -+{ -+ m_jsDialog = qJsObjectLoader->create("JsDialog"); -+} -+ -+QOpenHarmonyPlatformFileDialogHelper::~QOpenHarmonyPlatformFileDialogHelper() -+{ -+ -+} -+ -+bool QOpenHarmonyPlatformFileDialogHelper::defaultNameFilterDisables() const -+{ -+ return true; -+} -+ -+void QOpenHarmonyPlatformFileDialogHelper::setDirectory(const QUrl &directory) -+{ -+ m_dir = directory; -+} -+ -+QUrl QOpenHarmonyPlatformFileDialogHelper::directory() const -+{ -+ return m_dir; -+} -+ -+void QOpenHarmonyPlatformFileDialogHelper::selectFile(const QUrl &filename) -+{ -+ m_saveFile = filename.toLocalFile(); -+ if (m_saveFile.contains("/")) { -+ int index = m_saveFile.lastIndexOf("/"); -+ m_saveFile = m_saveFile.right(m_saveFile.length() - index - 1); -+ } -+} -+ -+QList QOpenHarmonyPlatformFileDialogHelper::selectedFiles() const -+{ -+ QList result; -+ for (int i = 0; i < m_files.count(); ++i) { -+ result << QUrl(m_files.at(i)); -+ } -+ QSharedPointer opt = options(); -+ if (opt.data()) { -+ bool save = (opt->acceptMode() == QFileDialogOptions::AcceptSave); -+ if (save) { -+ // 等待js端把文件创建出来,100可调整 -+ QThread::msleep(100); -+ } -+ } -+ return result; -+} -+ -+void QOpenHarmonyPlatformFileDialogHelper::setFilter() -+{ -+ -+} -+ -+void QOpenHarmonyPlatformFileDialogHelper::selectNameFilter(const QString &filter) -+{ -+ m_filter = filter; -+} -+ -+QString QOpenHarmonyPlatformFileDialogHelper::selectedNameFilter() const -+{ -+ return m_filter; -+} -+ -+void QOpenHarmonyPlatformFileDialogHelper::exec() -+{ -+ if (!m_shown) -+ show(Qt::Dialog, Qt::ApplicationModal, nullptr); -+ wait(-1); -+} -+ -+bool QOpenHarmonyPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) -+{ -+ Q_UNUSED(windowModality) -+ if (m_jsDialog.isNull()) -+ return false; -+ QSharedPointer opt = options(); -+ if (!opt.data()) -+ return false; -+ bool open = (opt->acceptMode() == QFileDialogOptions::AcceptOpen); -+ if (open) -+ m_jsDialog->callWithoutReturn("openFileDialog", QVariant(qlonglong(static_cast(this))), m_filter); -+ else -+ m_jsDialog->callWithoutReturn("saveFileDialog", QVariant(qlonglong(static_cast(this))), m_saveFile); -+ m_shown = true; -+ return true; -+} -+ -+void QOpenHarmonyPlatformFileDialogHelper::hide() -+{ -+ m_shown = false; -+} -+ -+void QOpenHarmonyPlatformFileDialogHelper::setFileResult(const QStringList &file) -+{ -+ m_files.clear(); -+ if (!file.isEmpty()) { -+ m_files << file; -+ } -+ cancelWait(); -+ if (m_files.isEmpty()) -+ emit reject(); -+ else -+ emit accept(); -+} -+ -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformdialoghelpers.h b/src/plugins/platforms/openharmony/qopenharmonyplatformdialoghelpers.h -new file mode 100644 -index 0000000000..39762fdc90 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformdialoghelpers.h -@@ -0,0 +1,87 @@ -+#ifndef QOPENHARMONYPLATFORMDIALOGHELPERS_H -+#define QOPENHARMONYPLATFORMDIALOGHELPERS_H -+ -+#include -+#include -+ -+#include -+#include -+#include -+ -+class QOpenHarmonyJsObject; -+ -+class QWaiter -+{ -+public: -+ QWaiter() {} -+ -+ void wait(int timeout = 1000); -+ void cancelWait(); -+private: -+ QEventLoop m_loop; -+}; -+ -+class QOpenHarmonyPlatformMessageDialogHelper: public QPlatformMessageDialogHelper, public QWaiter -+{ -+ Q_OBJECT -+public: -+ QOpenHarmonyPlatformMessageDialogHelper(); -+ ~QOpenHarmonyPlatformMessageDialogHelper(); -+ void exec() override; -+ bool show(Qt::WindowFlags windowFlags, -+ Qt::WindowModality windowModality, -+ QWindow *parent) override; -+ void hide() override; -+ -+ static napi_value init(napi_env env, napi_value exports); -+ -+public slots: -+ void setDialogResult(int buttonID); -+ -+private: -+ void addButtons(QSharedPointer opt, ButtonRole role); -+ -+private: -+ int m_buttonId; -+ bool m_shown; -+ QStringList m_buttons; -+ QList m_stanardButton; -+ QSharedPointer m_jsDialog; -+}; -+ -+class QOpenHarmonyPlatformFileDialogHelper: public QPlatformFileDialogHelper, public QWaiter -+{ -+ Q_OBJECT -+public: -+ QOpenHarmonyPlatformFileDialogHelper(); -+ ~QOpenHarmonyPlatformFileDialogHelper(); -+ -+ virtual bool defaultNameFilterDisables() const override; -+ virtual void setDirectory(const QUrl &directory) override; -+ virtual QUrl directory() const override; -+ virtual void selectFile(const QUrl &filename) override; -+ virtual QList selectedFiles() const override; -+ virtual void setFilter() override; -+ virtual void selectNameFilter(const QString &filter) override; -+ virtual QString selectedNameFilter() const override; -+ -+ virtual void exec() override; -+ virtual bool show(Qt::WindowFlags windowFlags, -+ Qt::WindowModality windowModality, -+ QWindow *parent) override; -+ virtual void hide() override; -+ -+public slots: -+ void setFileResult(const QStringList &file); -+ -+ static napi_value init(napi_env env, napi_value exports); -+private: -+ bool m_shown; -+ QStringList m_files; -+ QSharedPointer m_jsDialog; -+ QString m_saveFile; -+ QUrl m_dir; -+ QString m_filter; -+}; -+ -+#endif // QOPENHARMONYPLATFORMDIALOGHELPERS_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformfontdatabase.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformfontdatabase.cpp -new file mode 100644 -index 0000000000..dc5b6c998f ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformfontdatabase.cpp -@@ -0,0 +1,83 @@ -+#include -+#include -+#include -+#include -+ -+#include "qopenharmonyplatformfontdatabase.h" -+ -+static QStringList gs_fonts; -+ -+/* 通过鸿蒙ts接口查询系统提供的字体路径 -+ * 然后传入C++端使用 -+ */ -+static napi_value setFontInfos(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 1; -+ napi_value args[1]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ if (argc != 1) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments"); -+ return nullptr; -+ } -+ -+ if (gs_fonts.isEmpty()) -+ gs_fonts = qJs::getStringList(args[0]); -+ -+ return Q_NULLPTR; -+} -+ -+QT_BEGIN_NAMESPACE -+ -+QString QOpenHarmonyPlatformFontDatabase::fontDir() const -+{ -+ /* FIMXE 鸿蒙权限收紧,读取该目录会提示读取失败 */ -+ return QLatin1String("/system/fonts"); -+} -+ -+void QOpenHarmonyPlatformFontDatabase::populateFontDatabase() -+{ -+ /* FIXME -+ * 使用鸿蒙ts端查询到的字体路径 -+ * 否则,扫描fontDir路径中的字体文件 -+ * (该操作因为鸿蒙系统权限收紧会提示读取目录失败的警告) -+ */ -+ if (!gs_fonts.isEmpty()) { -+ for (const QString &f : qAsConst(gs_fonts)) { -+ const QByteArray file = QFile::encodeName(f); -+ QFreeTypeFontDatabase::addTTFile(QByteArray(), file); -+ } -+ return; -+ } -+ -+ QString fontpath = fontDir(); -+ QDir dir(fontpath); -+ -+ if (Q_UNLIKELY(!dir.exists())) { -+ qWarning("QFontDatabase: Cannot find font directory %s - is Qt installed correctly?", -+ qPrintable(fontpath)); -+ //Todo Qt使用_stat函数获取状态信息时返回-1,权限被拒绝.但是使用entryInfoList能够列出目录下的字体 -+ } -+ -+ QStringList nameFilters; -+ nameFilters << QLatin1String("*.ttf") -+ << QLatin1String("*.otf") -+ << QLatin1String("*.ttc"); -+ -+ const auto entries = dir.entryInfoList(nameFilters, QDir::Files); -+ for (const QFileInfo &fi : entries) { -+ const QByteArray file = QFile::encodeName(fi.absoluteFilePath()); -+ QFreeTypeFontDatabase::addTTFile(QByteArray(), file); -+ } -+} -+ -+napi_value QOpenHarmonyPlatformFontDatabase::init(napi_env env, napi_value exports) -+{ -+ napi_property_descriptor desc[] ={ -+ DECLARE_NAPI_FUNCTION("setFontInfos", setFontInfos), -+ }; -+ NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); -+ return nullptr; -+} -+ -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformfontdatabase.h b/src/plugins/platforms/openharmony/qopenharmonyplatformfontdatabase.h -new file mode 100644 -index 0000000000..bfad0a18a1 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformfontdatabase.h -@@ -0,0 +1,19 @@ -+#ifndef QOPENHARMONYPLATFORMFONTDATABASE_H -+#define QOPENHARMONYPLATFORMFONTDATABASE_H -+ -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyPlatformFontDatabase: public QFreeTypeFontDatabase -+{ -+public: -+ QString fontDir() const override; -+ void populateFontDatabase() override; -+ static napi_value init(napi_env env, napi_value exports); -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYPLATFORMFONTDATABASE_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformforeignwindow.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformforeignwindow.cpp -new file mode 100644 -index 0000000000..38c5c0ae04 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformforeignwindow.cpp -@@ -0,0 +1,54 @@ -+#include "qopenharmonyplatformforeignwindow.h" -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+QOpenHarmonyPlatformForeignWindow::QOpenHarmonyPlatformForeignWindow(QWindow *window, WId nativeHandle) -+ : QOpenHarmonyPlatformWindow(window), -+ m_surfaceId(-1) -+{ -+ -+} -+ -+QOpenHarmonyPlatformForeignWindow::~QOpenHarmonyPlatformForeignWindow() -+{ -+ -+} -+ -+void QOpenHarmonyPlatformForeignWindow::lower() -+{ -+ if (m_surfaceId == -1) -+ return; -+ -+ QOpenHarmonyPlatformWindow::lower(); -+} -+ -+void QOpenHarmonyPlatformForeignWindow::raise() -+{ -+ if (m_surfaceId == -1) -+ return; -+ -+ QOpenHarmonyPlatformWindow::raise(); -+} -+ -+void QOpenHarmonyPlatformForeignWindow::setGeometry(const QRect &rect) -+{ -+ QOpenHarmonyPlatformWindow::setGeometry(rect); -+} -+ -+void QOpenHarmonyPlatformForeignWindow::setVisible(bool visible) -+{ -+} -+ -+void QOpenHarmonyPlatformForeignWindow::applicationStateChanged(Qt::ApplicationState state) -+{ -+ QOpenHarmonyPlatformWindow::applicationStateChanged(state); -+} -+ -+void QOpenHarmonyPlatformForeignWindow::setParent(const QPlatformWindow *window) -+{ -+ Q_UNUSED(window); -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformforeignwindow.h b/src/plugins/platforms/openharmony/qopenharmonyplatformforeignwindow.h -new file mode 100644 -index 0000000000..e792d1a019 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformforeignwindow.h -@@ -0,0 +1,27 @@ -+#ifndef QOPENHARMONYPLATFORMFOREIGNWINDOW_H -+#define QOPENHARMONYPLATFORMFOREIGNWINDOW_H -+ -+#include "qopenharmonyplatformwindow.h" -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyPlatformForeignWindow : public QOpenHarmonyPlatformWindow -+{ -+public: -+ explicit QOpenHarmonyPlatformForeignWindow(QWindow *window, WId nativeHandle); -+ ~QOpenHarmonyPlatformForeignWindow() override; -+ void lower() override; -+ void raise() override; -+ void setGeometry(const QRect &rect) override; -+ void setVisible(bool visible) override; -+ void applicationStateChanged(Qt::ApplicationState state) override; -+ void setParent(const QPlatformWindow *window) override; -+ bool isForeignWindow() const override { return true; } -+ -+private: -+ int m_surfaceId; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYPLATFORMFOREIGNWINDOW_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatforminputcontext.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatforminputcontext.cpp -new file mode 100644 -index 0000000000..d00836a94c ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatforminputcontext.cpp -@@ -0,0 +1,1175 @@ -+#include "qopenharmonymain.h" -+#include "qopenharmonyjsobjectloader.h" -+#include "qopenharmonyplatformscreen.h" -+#include "qopenharmonyplatformintegration.h" -+#include "qopenharmonyplatforminputcontext.h" -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+namespace { -+ -+class BatchEditLock -+{ -+public: -+ -+ explicit BatchEditLock(QOpenHarmonyPlatformInputContext *context) -+ : m_context(context) -+ { -+ m_context->beginBatchEdit(); -+ } -+ -+ ~BatchEditLock() -+ { -+ m_context->endBatchEdit(); -+ } -+ -+ BatchEditLock(const BatchEditLock &) = delete; -+ BatchEditLock &operator=(const BatchEditLock &) = delete; -+ -+private: -+ -+ QOpenHarmonyPlatformInputContext *m_context; -+}; -+ -+} // namespace anonymous -+ -+/*! -+ * \brief Qt-鸿蒙平台下的输入类型枚举值映射 -+ * \param hints Qt下的枚举值 -+ * \return 返回Qt-鸿蒙的输入类型映射值 -+ */ -+static int harmonyTextInputType(Qt::InputMethodHints hints) -+{ -+ /* FIXME Qt支持位运算,鸿蒙文本类型不支持位运算的枚举值 */ -+ if(hints.testFlag(Qt::ImhMultiLine)) /* 多行文本类型 */ -+ return 1; -+ if (hints.testFlag(Qt::ImhPreferNumbers) || -+ hints.testFlag(Qt::ImhDigitsOnly)) /* 数字类型 */ -+ return 2; -+ if (hints.testFlag(Qt::ImhDate) || -+ hints.testFlag(Qt::ImhTime)) /* 日期类型 */ -+ return 4; -+ if (hints.testFlag(Qt::ImhHiddenText)) /* 密码类型 */ -+ return 7; -+ if ((hints.testFlag(Qt::ImhDigitsOnly) | -+ hints.testFlag(Qt::ImhFormattedNumbersOnly)) && -+ hints.testFlag(Qt::ImhHiddenText)) /* 数字密码类型 */ -+ return 8; -+ if (hints.testFlag(Qt::ImhDialableCharactersOnly)) /* 电话号码类型 */ -+ return 3; -+ if (hints.testFlag(Qt::ImhEmailCharactersOnly)) /* 邮箱地址类型 */ -+ return 5; -+ if (hints.testFlag(Qt::ImhUrlCharactersOnly)) /* 链接类型 */ -+ return 6; -+ -+ return 0; /* 默认文本类型 */ -+} -+/*! -+ * \brief Qt-鸿蒙平台下的EnterKey功能值映射 -+ * \param type Qt下的EnterKey功能枚举值 -+ * \return 返回Qt-鸿蒙的EnterKey功能映射值 -+ */ -+static int harmonyEnterKeyType(Qt::EnterKeyType type) -+{ -+ switch (type) { -+ case Qt::EnterKeyDone: -+ case Qt::EnterKeyDefault: -+ return 6; -+ case Qt::EnterKeyReturn: -+ case Qt::EnterKeyPrevious: -+ return 7; -+ case Qt::EnterKeyGo: return 2; -+ case Qt::EnterKeySend: return 4; -+ case Qt::EnterKeySearch: return 3; -+ case Qt::EnterKeyNext:return 5; -+ default: break; -+ } -+ return 0; -+} -+ -+static QOpenHarmonyPlatformInputContext *m_openHarmonyInputContext = 0; -+ -+static napi_value insertText(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 2; -+ napi_value args[2]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ if (argc != 2) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments"); -+ return nullptr; -+ } -+ -+ int64_t value0 = qJs::getInt64(args[0]); -+ QString value1 = qJs::getString(args[1]); -+ QObject *object = reinterpret_cast(value0); -+ -+ QMetaObject::invokeMethod(object, "commitText", Qt::QueuedConnection, Q_ARG(QString, value1)); -+ return Q_NULLPTR; -+} -+ -+static napi_value deleteLeft(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 2; -+ napi_value args[2]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ if (argc != 2) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments"); -+ return nullptr; -+ } -+ -+ int64_t value0 = qJs::getInt64(args[0]); -+ int32_t value1 = qJs::getInt32(args[1]); -+ QObject *object = reinterpret_cast(value0); -+ -+ QMetaObject::invokeMethod(object, "deleteSurroundingText", Qt::QueuedConnection, -+ Q_ARG(int, int(value1)), Q_ARG(int, 0)); -+ return Q_NULLPTR; -+} -+ -+static napi_value deleteRight(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 2; -+ napi_value args[2]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ if (argc != 2) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments"); -+ return nullptr; -+ } -+ -+ int64_t value0 = qJs::getInt64(args[0]); -+ int32_t value1 = qJs::getInt32(args[1]); -+ QObject *object = reinterpret_cast(value0); -+ -+ QMetaObject::invokeMethod(object, "deleteSurroundingText", Qt::QueuedConnection, -+ Q_ARG(int, 0), Q_ARG(int, int(value1))); -+ -+ return Q_NULLPTR; -+} -+static napi_value moveCursor(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 2; -+ napi_value args[2]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ if (argc != 2) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments"); -+ return nullptr; -+ } -+ -+ int64_t value0 = qJs::getInt64(args[0]); -+ int32_t value1 = qJs::getInt32(args[1]); -+ QObject *object = reinterpret_cast(value0); -+ -+ QMetaObject::invokeMethod(object, "commitMoveCursor", Qt::QueuedConnection, -+ Q_ARG(int, int(value1))); -+ -+ return Q_NULLPTR; -+} -+ -+ -+// cursor position getter that also works with editors that have not been updated to the new API -+static inline int getAbsoluteCursorPosition(const QSharedPointer &query) -+{ -+ QVariant absolutePos = query->value(Qt::ImAbsolutePosition); -+ return absolutePos.isValid() ? absolutePos.toInt() : query->value(Qt::ImCursorPosition).toInt(); -+} -+ -+// position of the start of the current block -+static inline int getBlockPosition(const QSharedPointer &query) -+{ -+ QVariant absolutePos = query->value(Qt::ImAbsolutePosition); -+ return absolutePos.isValid() ? absolutePos.toInt() - query->value(Qt::ImCursorPosition).toInt() : 0; -+} -+ -+napi_value QOpenHarmonyPlatformInputContext::init(napi_env env, napi_value exports) -+{ -+ napi_property_descriptor desc[] ={ -+ DECLARE_NAPI_FUNCTION("insertText", insertText), -+ DECLARE_NAPI_FUNCTION("moveCursor", moveCursor), -+ DECLARE_NAPI_FUNCTION("deleteLeft", deleteLeft), -+ DECLARE_NAPI_FUNCTION("deleteRight", deleteRight), -+ }; -+ NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); -+ return nullptr; -+} -+ -+void QOpenHarmonyPlatformInputContext::dispatchKeyEvent(OH_NativeXComponent_KeyCode code, -+ OH_NativeXComponent_KeyAction action, -+ OH_NativeXComponent_EventSourceType stype, -+ int64_t deviceId, -+ int64_t timeStamp) -+{ -+ Q_UNUSED(deviceId);Q_UNUSED(timeStamp); -+ static QHash sKeyType{ -+ { OH_NATIVEXCOMPONENT_KEY_ACTION_DOWN, QEvent::KeyPress }, -+ { OH_NATIVEXCOMPONENT_KEY_ACTION_UP, QEvent::KeyRelease }, -+ }; -+ -+ -+ static QHash sKeyMap{ -+ { KEY_HOME, Qt::Key_Home }, -+ { KEY_BACK, Qt::Key_Back }, -+ { KEY_MEDIA_PLAY_PAUSE, Qt::Key_MediaTogglePlayPause }, -+ { KEY_MEDIA_STOP, Qt::Key_MediaStop }, -+ { KEY_MEDIA_NEXT, Qt::Key_MediaNext }, -+ { KEY_MEDIA_PREVIOUS, Qt::Key_MediaPrevious }, -+ { KEY_MEDIA_REWIND, Qt::Key_AudioRewind }, -+ { KEY_MEDIA_FAST_FORWARD, Qt::Key_AudioForward }, -+ { KEY_VOLUME_UP, Qt::Key_VolumeUp }, -+ { KEY_VOLUME_DOWN, Qt::Key_VolumeDown }, -+ { KEY_POWER, Qt::Key_PowerDown }, -+ { KEY_CAMERA, Qt::Key_Camera }, -+ { KEY_VOLUME_MUTE, Qt::Key_VolumeMute }, -+ { KEY_MUTE, Qt::Key_MicMute }, -+ { KEY_BRIGHTNESS_UP, Qt::Key_MonBrightnessUp }, -+ { KEY_BRIGHTNESS_DOWN, Qt::Key_MonBrightnessDown }, -+ { KEY_0, Qt::Key_0 }, -+ { KEY_1, Qt::Key_1 }, -+ { KEY_2, Qt::Key_2 }, -+ { KEY_3, Qt::Key_3 }, -+ { KEY_4, Qt::Key_4 }, -+ { KEY_5, Qt::Key_5 }, -+ { KEY_6, Qt::Key_6 }, -+ { KEY_7, Qt::Key_7 }, -+ { KEY_8, Qt::Key_8 }, -+ { KEY_9, Qt::Key_9 }, -+ { KEY_STAR, Qt::Key_Asterisk }, -+ { KEY_POUND, Qt::Key_NumberSign }, -+ { KEY_DPAD_UP, Qt::Key_Up }, -+ { KEY_DPAD_DOWN, Qt::Key_Down }, -+ { KEY_DPAD_LEFT, Qt::Key_Left }, -+ { KEY_DPAD_RIGHT, Qt::Key_Right }, -+ //{ KEY_DPAD_CENTER, Qt::Key_ }, -+ { KEY_A, Qt::Key_A }, -+ { KEY_B, Qt::Key_B }, -+ { KEY_C, Qt::Key_C }, -+ { KEY_D, Qt::Key_D }, -+ { KEY_E, Qt::Key_E }, -+ { KEY_F, Qt::Key_F }, -+ { KEY_G, Qt::Key_G }, -+ { KEY_H, Qt::Key_H }, -+ { KEY_I, Qt::Key_I }, -+ { KEY_J, Qt::Key_J }, -+ { KEY_K, Qt::Key_K }, -+ { KEY_L, Qt::Key_L }, -+ { KEY_M, Qt::Key_M }, -+ { KEY_N, Qt::Key_N }, -+ { KEY_O, Qt::Key_O }, -+ { KEY_P, Qt::Key_P }, -+ { KEY_Q, Qt::Key_Q }, -+ { KEY_R, Qt::Key_R }, -+ { KEY_S, Qt::Key_S }, -+ { KEY_T, Qt::Key_T }, -+ { KEY_U, Qt::Key_U }, -+ { KEY_V, Qt::Key_V }, -+ { KEY_W, Qt::Key_W }, -+ { KEY_X, Qt::Key_X }, -+ { KEY_Y, Qt::Key_Y }, -+ { KEY_Z, Qt::Key_Z }, -+ { KEY_COMMA, Qt::Key_Comma }, -+ { KEY_PERIOD, Qt::Key_Period }, -+ { KEY_ALT_LEFT, Qt::Key_Alt }, -+ { KEY_ALT_RIGHT, Qt::Key_Alt }, -+ { KEY_SHIFT_LEFT, Qt::Key_Shift }, -+ { KEY_SHIFT_RIGHT, Qt::Key_Shift }, -+ { KEY_TAB, Qt::Key_Tab }, -+ { KEY_SPACE, Qt::Key_Space }, -+ //{ KEY_SYM, Qt::KEY_}, -+ { KEY_EXPLORER, Qt::Key_Explorer }, -+ //{ KEY_ENVELOPE, Qt::Key_ }, -+ { KEY_ENTER, Qt::Key_Enter }, -+ { KEY_DEL, Qt::Key_Back }, -+ { KEY_GRAVE, Qt::Key_Apostrophe }, -+ { KEY_MINUS, Qt::Key_Minus }, -+ { KEY_EQUALS, Qt::Key_Equal }, -+ { KEY_LEFT_BRACKET, Qt::Key_BracketLeft }, -+ { KEY_RIGHT_BRACKET, Qt::Key_BracketRight }, -+ { KEY_BACKSLASH, Qt::Key_Backslash }, -+ { KEY_SEMICOLON, Qt::Key_Semicolon }, -+ { KEY_APOSTROPHE, Qt::Key_Apostrophe }, -+ { KEY_SLASH, Qt::Key_Slash }, -+ { KEY_AT, Qt::Key_At }, -+ { KEY_PLUS, Qt::Key_Plus }, -+ { KEY_MENU, Qt::Key_Menu }, -+ { KEY_PAGE_UP, Qt::Key_PageUp }, -+ { KEY_PAGE_DOWN, Qt::Key_PageDown }, -+ { KEY_ESCAPE, Qt::Key_Escape }, -+ { KEY_FORWARD_DEL, Qt::Key_Delete }, -+ { KEY_CTRL_LEFT, Qt::Key_Control }, -+ { KEY_CTRL_RIGHT, Qt::Key_Control }, -+ { KEY_CAPS_LOCK, Qt::Key_CapsLock }, -+ { KEY_SCROLL_LOCK, Qt::Key_ScrollLock }, -+ { KEY_META_LEFT, Qt::Key_Meta }, -+ { KEY_META_RIGHT, Qt::Key_Meta }, -+ //{ KEY_FUNCTION, Qt::Key_ }, -+ { KEY_SYSRQ, Qt::Key_SysReq }, -+ { KEY_BREAK, Qt::Key_Pause }, -+ { KEY_MOVE_HOME, Qt::Key_Home }, -+ { KEY_MOVE_END, Qt::Key_End }, -+ { KEY_INSERT, Qt::Key_Insert }, -+ { KEY_FORWARD, Qt::Key_Forward }, -+ { KEY_MEDIA_PLAY, Qt::Key_MediaPlay }, -+ { KEY_MEDIA_PAUSE, Qt::Key_MediaPause }, -+ //{ KEY_MEDIA_CLOSE, Qt::Key_Media }, -+ //{ KEY_MEDIA_EJECT, Qt::Key_Eject }, -+ { KEY_MEDIA_RECORD, Qt::Key_MediaRecord }, -+ { KEY_F1, Qt::Key_F1 }, -+ { KEY_F2, Qt::Key_F2 }, -+ { KEY_F3, Qt::Key_F3 }, -+ { KEY_F4, Qt::Key_F4 }, -+ { KEY_F5, Qt::Key_F5 }, -+ { KEY_F6, Qt::Key_F6 }, -+ { KEY_F7, Qt::Key_F7 }, -+ { KEY_F8, Qt::Key_F8 }, -+ { KEY_F9, Qt::Key_F9 }, -+ { KEY_F10, Qt::Key_F10 }, -+ { KEY_F11, Qt::Key_F11 }, -+ { KEY_F12, Qt::Key_F12 }, -+ { KEY_NUM_LOCK, Qt::Key_NumLock }, -+ { KEY_NUMPAD_0, Qt::Key_0 }, -+ { KEY_NUMPAD_1, Qt::Key_1 }, -+ { KEY_NUMPAD_2, Qt::Key_2 }, -+ { KEY_NUMPAD_3, Qt::Key_3 }, -+ { KEY_NUMPAD_4, Qt::Key_4 }, -+ { KEY_NUMPAD_5, Qt::Key_5 }, -+ { KEY_NUMPAD_6, Qt::Key_6 }, -+ { KEY_NUMPAD_7, Qt::Key_7 }, -+ { KEY_NUMPAD_8, Qt::Key_8 }, -+ { KEY_NUMPAD_9, Qt::Key_9 }, -+ { KEY_NUMPAD_DIVIDE, Qt::Key_Slash }, -+ { KEY_NUMPAD_MULTIPLY, Qt::Key_Asterisk }, -+ { KEY_NUMPAD_SUBTRACT, Qt::Key_Minus }, -+ { KEY_NUMPAD_ADD, Qt::Key_Plus }, -+ { KEY_NUMPAD_DOT, Qt::Key_Period }, -+ { KEY_NUMPAD_COMMA, Qt::Key_Comma }, -+ { KEY_NUMPAD_ENTER, Qt::Key_Enter }, -+ { KEY_NUMPAD_EQUALS, Qt::Key_Equal }, -+ { KEY_NUMPAD_LEFT_PAREN, Qt::Key_ParenLeft }, -+ { KEY_NUMPAD_RIGHT_PAREN, Qt::Key_ParenRight }, -+ //{ KEY_VIRTUAL_MULTITASK, Qt::Key_ }, -+ { KEY_SLEEP, Qt::Key_Sleep }, -+ { KEY_ZENKAKU_HANKAKU, Qt::Key_Zenkaku_Hankaku }, -+ //{ KEY_102ND, Qt::Key_ }, -+ //{ KEY_RO, Qt::Key_ }, -+ { KEY_KATAKANA, Qt::Key_Katakana }, -+ { KEY_HIRAGANA, Qt::Key_Hiragana }, -+ { KEY_HENKAN, Qt::Key_Henkan }, -+ { KEY_KATAKANA_HIRAGANA, Qt::Key_Hiragana_Katakana }, -+ { KEY_MUHENKAN, Qt::Key_Muhenkan }, -+ { KEY_LINEFEED, Qt::Key_Return }, -+ //{ KEY_MACRO, Qt::Key_macron }, -+ //{ KEY_NUMPAD_PLUSMINUS, Qt::Key_ }, -+ //{ KEY_SCALE, Qt::Key_ }, -+ { KEY_HANGUEL, Qt::Key_Hangul }, -+ { KEY_HANJA, Qt::Key_Hangul_Hanja }, -+ { KEY_YEN, Qt::Key_yen }, -+ { KEY_STOP, Qt::Key_Stop }, -+ //{ KEY_AGAIN, Qt::Key_ }, -+ //{ KEY_PROPS, Qt::Key_ }, -+ { KEY_UNDO, Qt::Key_Undo }, -+ { KEY_COPY, Qt::Key_Copy }, -+ { KEY_OPEN, Qt::Key_Open }, -+ { KEY_PASTE, Qt::Key_Paste }, -+ { KEY_FIND, Qt::Key_Find }, -+ { KEY_CUT, Qt::Key_Cut }, -+ { KEY_HELP, Qt::Key_Help }, -+ //{ KEY_CALC, Qt::Key_ }, -+ //{ KEY_FILE, Qt::Key_ }, -+ //{ KEY_BOOKMARKS, Qt::Key_Book }, -+ //{ KEY_NEXT, Qt::Key_ }, -+ //{ KEY_PLAYPAUSE, Qt::Key_MediaTogglePlayPause } -+ //{ KEY_PREVIOUS, Qt::Key_ }, -+ //{ KEY_STOPCD, Qt::Key_ }, -+ //{ KEY_CONFIG, Qt::Key_ }, -+ { KEY_REFRESH, Qt::Key_Refresh }, -+ { KEY_EXIT, Qt::Key_Exit }, -+ //{ KEY_EDIT, Qt::Key_ }, -+ //{ KEY_SCROLLUP, Qt::Key_ }, -+ //{ KEY_SCROLLDOWN, Qt::Key_ }, -+ { KEY_NEW, Qt::Key_New }, -+ { KEY_REDO, Qt::Key_Redo }, -+ { KEY_CLOSE, Qt::Key_Close }, -+ { KEY_PLAY, Qt::Key_Play }, -+ //{ KEY_BASSBOOST, Qt::Key_ }, -+ { KEY_PRINT, Qt::Key_Print }, -+ //{ KEY_CHAT, Qt::Key_ }, -+ { KEY_FINANCE, Qt::Key_Finance }, -+ { KEY_CANCEL, Qt::Key_Cancel }, -+ //{ KEY_KBDILLUM_TOGGLE, Qt::Key_ }, -+ { KEY_KBDILLUM_DOWN, Qt::Key_KeyboardBrightnessDown }, -+ { KEY_KBDILLUM_UP, Qt::Key_KeyboardBrightnessUp }, -+ { KEY_SEND, Qt::Key_Send }, -+ { KEY_REPLY, Qt::Key_Reply }, -+ { KEY_FORWARDMAIL, Qt::Key_Forward }, -+ { KEY_SAVE, Qt::Key_Save }, -+ { KEY_DOCUMENTS, Qt::Key_Documents }, -+ //{ KEY_VIDEO_NEXT, Qt::Key_ }, -+ //{ KEY_VIDEO_PREV, Qt::Key_ }, -+ //{ KEY_BRIGHTNESS_CYCLE, Qt::Key_ }, -+ //{ KEY_BRIGHTNESS_ZERO, Qt::Key_ }, -+ //{ KEY_DISPLAY_OFF, Qt::Key_ }, -+ //{ KEY_BTN_MISC, Qt::Key_ }, -+ { KEY_GOTO, Qt::Key_Go }, -+ { KEY_INFO, Qt::Key_Info }, -+ //{ KEY_PROGRAM, Qt::Key_ }, -+ //{ KEY_PVR, Qt::Key_ }, -+ { KEY_SUBTITLE, Qt::Key_Subtitle }, -+ //{ KEY_FULL_SCREEN, Qt::Key_ }, -+ //{ KEY_KEYBOARD, Qt::Key_ }, -+ //{ KEY_ASPECT_RATIO, Qt::Key_ }, -+ //{ KEY_PC, Qt::Key_ }, -+ //{ KEY_TV, Qt::Key_ }, -+ //{ KEY_TV2, Qt::Key_ }, -+ //{ KEY_VCR, Qt::Key_ }, -+ //{ KEY_VCR2, Qt::Key_ }, -+ //{ KEY_SAT, Qt::Key_ }, -+ { KEY_CD, Qt::Key_CD }, -+ //{ KEY_TAPE, Qt::Key_ }, -+ //{ KEY_TUNER, Qt::Key_ }, -+ { KEY_PLAYER, Qt::Key_Play }, -+ //{ KEY_DVD, Qt::Key_ }, -+ //{ KEY_AUDIO, Qt::Key_ }, -+ { KEY_VIDEO, Qt::Key_Video }, -+ { KEY_MEMO, Qt::Key_Memo }, -+ { KEY_CALENDAR, Qt::Key_Calendar }, -+ { KEY_RED, Qt::Key_Red }, -+ { KEY_GREEN, Qt::Key_Green }, -+ { KEY_YELLOW, Qt::Key_Yellow }, -+ { KEY_BLUE, Qt::Key_Blue }, -+ { KEY_CHANNELUP, Qt::Key_ChannelUp }, -+ { KEY_CHANNELDOWN, Qt::Key_ChannelDown }, -+ //{ KEY_LAST, Qt::Key_ }, -+ //{ KEY_RESTART, Qt::Key_ }, -+ //{ KEY_SLOW, Qt::Key_ }, -+ //{ KEY_SHUFFLE, Qt::Key_ }, -+ //{ KEY_VIDEOPHONE, Qt::Key_ }, -+ { KEY_GAMES, Qt::Key_Game }, -+ { KEY_ZOOMIN, Qt::Key_ZoomIn }, -+ { KEY_ZOOMOUT, Qt::Key_ZoomOut }, -+ //{ KEY_ZOOMRESET, Qt::Key_ }, -+ //{ KEY_WORDPROCESSOR, Qt::Key_ }, -+ //{ KEY_EDITOR, Qt::Key_ }, -+ //{ KEY_SPREADSHEET, Qt::Key_ }, -+ //{ KEY_GRAPHICSEDITOR, Qt::Key_ }, -+ //{ KEY_PRESENTATION, Qt::Key_ }, -+ //{ KEY_DATABASE, Qt::Key_ }, -+ { KEY_NEWS, Qt::Key_News }, -+ //{ KEY_VOICEMAIL, Qt::Key_ }, -+ //{ KEY_ADDRESSBOOK, Qt::Key_ }, -+ { KEY_MESSENGER, Qt::Key_Messenger }, -+ //{ KEY_BRIGHTNESS_TOGGLE, Qt::Key_BrightnessAdjust } -+ //{ KEY_SPELLCHECK, Qt::Key_Spell } -+ //{ KEY_COFFEE, Qt::Key_ }, -+ //{ KEY_MEDIA_REPEAT, Qt::Key_ }, -+ //{ KEY_IMAGES, Qt::Key_ }, -+ //{ KEY_BUTTONCONFIG, Qt::Key_ }, -+ //{ KEY_TASKMANAGER, Qt::Key_TaskPane } -+ //{ KEY_JOURNAL, Qt::Key_ }, -+ //{ KEY_CONTROLPANEL, Qt::Key_ }, -+ //{ KEY_APPSELECT, Qt::Key_ }, -+ { KEY_SCREENSAVER, Qt::Key_ScreenSaver }, -+ //{ KEY_ASSISTANT, Qt::Key_ }, -+ //{ KEY_KBD_LAYOUT_NEXT, Qt::Key_ }, -+ //{ KEY_BRIGHTNESS_MIN, Qt::Key_ }, -+ //{ KEY_BRIGHTNESS_MAX, Qt::Key_ }, -+ //{ KEY_KBDINPUTASSIST_PREV, Qt::Key_ }, -+ //{ KEY_KBDINPUTASSIST_NEXT, Qt::Key_ }, -+ //{ KEY_KBDINPUTASSIST_PREVGROUP, Qt::Key_ }, -+ //{ KEY_KBDINPUTASSIST_NEXTGROUP, Qt::Key_ }, -+ //{ KEY_KBDINPUTASSIST_ACCEPT, Qt::Key_ }, -+ //{ KEY_KBDINPUTASSIST_CANCEL, Qt::Key_ }, -+ //{ KEY_FRONT, Qt::Key_ } -+ //{ KEY_SETUP, Qt::Key_Settings }, -+ { KEY_WAKEUP, Qt::Key_WakeUp }, -+ //{ KEY_SENDFILE, Qt::Key_ }, -+ //{ KEY_DELETEFILE, Qt::Key_ }, -+ { KEY_XFER, Qt::Key_Xfer }, -+ //{ KEY_PROG1, Qt::Key_ }, -+ //{ KEY_PROG2, Qt::Key_ }, -+ //{ KEY_MSDOS, Qt::Key_ }, -+ //{ KEY_SCREENLOCK, Qt::Key_ }, -+ //{ KEY_DIRECTION_ROTATE_DISPLAY, Qt::Key }, -+ //{ KEY_CYCLEWINDOWS, Qt::Key_ }, -+ //{ KEY_COMPUTER, Qt::Key }, -+ { KEY_EJECTCLOSECD, Qt::Key_Eject }, -+ //{ KEY_ISO, Qt::Key_ }, -+ //{ KEY_MOVE, Qt::Key_ }, -+ { KEY_F13, Qt::Key_F13 }, -+ { KEY_F14, Qt::Key_F14 }, -+ { KEY_F15, Qt::Key_F15 }, -+ { KEY_F16, Qt::Key_F16 }, -+ { KEY_F17, Qt::Key_F17 }, -+ { KEY_F18, Qt::Key_F18 }, -+ { KEY_F19, Qt::Key_F19 }, -+ { KEY_F20, Qt::Key_F20 }, -+ { KEY_F21, Qt::Key_F21 }, -+ { KEY_F22, Qt::Key_F22 }, -+ { KEY_F23, Qt::Key_F23 }, -+ { KEY_F24, Qt::Key_F24 }, -+ //{ KEY_PROG3, Qt::Key_ }, -+ //{ KEY_PROG4, Qt::Key_ }, -+ //{ KEY_DASHBOARD, Qt::Key_ }, -+ { KEY_SUSPEND, Qt::Key_Suspend }, -+ //{ KEY_HP, Qt::Key_ }, -+ //{ KEY_SOUND, Qt::Key_ }, -+ { KEY_QUESTION, Qt::Key_Question }, -+ //{ KEY_CONNECT, Qt::Key_ }, -+ //{ KEY_SPORT, Qt::Key_ }, -+ { KEY_SHOP, Qt::Key_Shop }, -+ //{ KEY_ALTERASE, Qt::Key_ }, -+ //{ KEY_SWITCHVIDEOMODE, Qt::Key_Mode_switch } -+ { KEY_BATTERY, Qt::Key_Battery }, -+ { KEY_BLUETOOTH, Qt::Key_Bluetooth }, -+ { KEY_WLAN, Qt::Key_WLAN }, -+ { KEY_UWB, Qt::Key_UWB }, -+ //{ KEY_WWAN_WIMAX, Qt::Key_ }, -+ //{ KEY_RFKILL, Qt::Key_ }, -+ //{ KEY_CHANNEL, Qt::Key_ }, -+ { KEY_BTN_0, Qt::Key_0 }, -+ { KEY_BTN_1, Qt::Key_1 }, -+ { KEY_BTN_2, Qt::Key_2 }, -+ { KEY_BTN_3, Qt::Key_3 }, -+ { KEY_BTN_4, Qt::Key_4 }, -+ { KEY_BTN_5, Qt::Key_5 }, -+ { KEY_BTN_6, Qt::Key_6 }, -+ { KEY_BTN_7, Qt::Key_7 }, -+ { KEY_BTN_8, Qt::Key_8 }, -+ { KEY_BTN_9, Qt::Key_9 } -+ }; -+ -+ static QHash sKeyModifers{ -+ { KEY_SHIFT_LEFT, Qt::ShiftModifier }, -+ { KEY_SHIFT_RIGHT, Qt::ShiftModifier }, -+ { KEY_CTRL_LEFT, Qt::ControlModifier }, -+ { KEY_CTRL_RIGHT, Qt::ControlModifier }, -+ { KEY_ALT_LEFT, Qt::AltModifier }, -+ { KEY_ALT_RIGHT, Qt::AltModifier }, -+ { KEY_META_LEFT, Qt::MetaModifier }, -+ { KEY_META_RIGHT, Qt::MetaModifier }, -+ { KEY_NUM_LOCK, Qt::KeypadModifier }, -+ { KEY_NUMPAD_0, Qt::KeypadModifier }, -+ { KEY_NUMPAD_1, Qt::KeypadModifier }, -+ { KEY_NUMPAD_2, Qt::KeypadModifier }, -+ { KEY_NUMPAD_3, Qt::KeypadModifier }, -+ { KEY_NUMPAD_4, Qt::KeypadModifier }, -+ { KEY_NUMPAD_5, Qt::KeypadModifier }, -+ { KEY_NUMPAD_6, Qt::KeypadModifier }, -+ { KEY_NUMPAD_7, Qt::KeypadModifier }, -+ { KEY_NUMPAD_8, Qt::KeypadModifier }, -+ { KEY_NUMPAD_9, Qt::KeypadModifier }, -+ { KEY_NUMPAD_DIVIDE, Qt::KeypadModifier }, -+ { KEY_NUMPAD_MULTIPLY, Qt::KeypadModifier }, -+ { KEY_NUMPAD_SUBTRACT, Qt::KeypadModifier }, -+ { KEY_NUMPAD_ADD, Qt::KeypadModifier }, -+ { KEY_NUMPAD_DOT, Qt::KeypadModifier }, -+ { KEY_NUMPAD_COMMA, Qt::KeypadModifier }, -+ { KEY_NUMPAD_ENTER, Qt::KeypadModifier }, -+ { KEY_NUMPAD_EQUALS, Qt::KeypadModifier }, -+ { KEY_NUMPAD_LEFT_PAREN, Qt::KeypadModifier }, -+ { KEY_NUMPAD_RIGHT_PAREN, Qt::KeypadModifier}, -+ }; -+ -+ /* 有修饰符按下时记录按下的修饰符 -+ * 松开时移除修饰符记录 -+ */ -+ -+ Qt::KeyboardModifiers modifiers(Qt::NoModifier); -+ QEvent::Type t = sKeyType.value(action, QEvent::None); -+ Qt::KeyboardModifier m = sKeyModifers.value(code, Qt::NoModifier); -+ if (Qt::NoModifier != m && QEvent::KeyPress == t){ -+ modifiers |= m; -+ m_mpd.insert(m); -+ } -+ else if (Qt::NoModifier != m && QEvent::KeyRelease == t) { -+ m_mpd.remove(m); -+ } -+ -+ Qt::Key k = sKeyMap.value(code, Qt::Key_unknown); -+ -+ int nativeModifier = KEY_UNKNOWN; -+ for (Qt::KeyboardModifier mf : m_mpd) -+ nativeModifier |= int(mf); -+ -+ QKeyEvent *kEvent = new QKeyEvent(t, k, modifiers, code, code, nativeModifier); -+ /* NOTE 因为鸿蒙回调在其他线程,这里事件处理只能通过线程队列发送 */ -+ QGuiApplication::postEvent(m_focusObject, kEvent); -+ -+ /* 快捷点组合判断 */ -+ if (m_mpd.contains(Qt::ControlModifier) && KEY_A == code) -+ QMetaObject::invokeMethod(this, "sendShortcut", Qt::QueuedConnection, -+ Q_ARG(int, QKeySequence::SelectAll)); -+ else if (m_mpd.contains(Qt::ControlModifier) && KEY_C == code) -+ QMetaObject::invokeMethod(this, "sendShortcut", Qt::QueuedConnection, -+ Q_ARG(int, QKeySequence::Copy)); -+ else if (m_mpd.contains(Qt::ControlModifier) && KEY_V == code) -+ QMetaObject::invokeMethod(this, "sendShortcut", Qt::QueuedConnection, -+ Q_ARG(int, QKeySequence::Paste)); -+} -+ -+void QOpenHarmonyPlatformInputContext::sendShortcut(int key) -+{ -+ QKeySequence::StandardKey skey = QKeySequence::StandardKey(key); -+ QKeySequence sequence(skey); -+ for (int i = 0; i < sequence.count(); ++i) { -+ const int keys = sequence[i]; -+ Qt::Key key = Qt::Key(keys & ~Qt::KeyboardModifierMask); -+ Qt::KeyboardModifiers mod = Qt::KeyboardModifiers(keys & Qt::KeyboardModifierMask); -+ -+ QKeyEvent pressEvent(QEvent::KeyPress, key, mod); -+ QKeyEvent releaseEvent(QEvent::KeyRelease, key, mod); -+ -+ QGuiApplication::sendEvent(m_focusObject, &pressEvent); -+ QGuiApplication::sendEvent(m_focusObject, &releaseEvent); -+ } -+} -+ -+void QOpenHarmonyPlatformInputContext::updateCursorPosition() -+{ -+ // qWarning() << "<--------------::::" << Q_FUNC_INFO; -+} -+ -+QOpenHarmonyPlatformInputContext::QOpenHarmonyPlatformInputContext() -+ : QPlatformInputContext() -+ , m_composingTextStart(-1) -+ , m_composingCursor(-1) -+ , m_batchEditNestingLevel(0) -+ , m_focusObject(Q_NULLPTR) -+{ -+ m_jsInputCtl = qJsObjectLoader->create("JsInputMethod"); -+ m_openHarmonyInputContext = this; -+} -+ -+QOpenHarmonyPlatformInputContext::~QOpenHarmonyPlatformInputContext() -+{ -+ m_openHarmonyInputContext = Q_NULLPTR; -+} -+/*! -+ * \brief 检查输入上下文是否具有指定的功能 -+ * \param capability 枚举值,用于指定功能类型 -+ * \return 返回布尔值,如果具有指定功能,则返回 true,否则返回 false -+ */ -+bool QOpenHarmonyPlatformInputContext::hasCapability(Capability capability) const -+{ -+ switch (capability) { -+ case HiddenTextCapability: -+ return false; -+ default:break; -+ } -+ return true; -+} -+/*! -+ * \brief 重置输入法状态,将输入法状态恢复到默认状态 -+ */ -+void QOpenHarmonyPlatformInputContext::reset() -+{ -+ // qWarning() << "<-----------::" << Q_FUNC_INFO; -+ focusObjectStopComposing(); -+ m_batchEditNestingLevel = 0; -+ -+ if (m_jsInputCtl->call("isAttached")) -+ m_jsInputCtl->call("detach"); -+ -+ clearInput(); -+ emitInputPanelVisibleChanged(); -+} -+/*! -+ * \brief 提交当前输入法状态,将当前输入法状态应用到文本编辑器中 -+ */ -+void QOpenHarmonyPlatformInputContext::commit() -+{ -+ // qWarning() << "<-----------::" << Q_FUNC_INFO; -+ if(m_jsInputCtl->call("isAttached")) -+ m_jsInputCtl->call("detach"); -+} -+/*! -+ * \brief 编辑器更新通知。由QInputMethod::update()调用 -+ * 函数的作用是更新输入上下文的状态,以便与应用程序的输入方法通信 -+ * 并响应输入法相关的查询,一般流程如下: -+ * 1.当输入法状态发生变化,或者焦点对象发生变化时 -+ * QGuiApplication 会调用当前活动的 QPlatformInputContext 对象的 update 函数 -+ * -+ * 2.在 update 函数内部,QPlatformInputContext 可以查询焦点对象的相关信息, -+ * 例如文本内容、光标位置等,然后将这些信息传递给输入法对象 -+ * -+ * 3.输入法对象根据接收到的信息更新其状态,并相应地调整显示内容或行为 -+ * 4.用户与输入法进行交互,输入文本或者调整输入法的设置 -+ * 5.当用户输入完成或者输入法状态发生变化时,输入法对象可能会触发输入法事件, -+ * 通知 QPlatformInputContext 对象更新状态 -+ */ -+void QOpenHarmonyPlatformInputContext::update(Qt::InputMethodQueries queries) -+{ -+ // qWarning() << "<-----------::" << Q_FUNC_INFO; -+ QSharedPointer query = focusObjectInputMethodQuery(queries); -+ if (query.isNull()) -+ return; -+ -+#warning TODO extract the needed data from query -+} -+ -+/*! -+ * \brief 调用输入法的动作,以执行指定的操作,如向前移动光标、向后删除等 -+ * \param act 动作类型 -+ * \param cursorPosition 光标位置 -+ */ -+void QOpenHarmonyPlatformInputContext::invokeAction(QInputMethod::Action act, int cursorPosition) -+{ -+ // qWarning() << "<-----------::" << Q_FUNC_INFO; -+#warning TODO Handle -+ Q_UNUSED(act); -+ Q_UNUSED(cursorPosition); -+} -+/*! -+ * \brief 过滤输入事件,以便输入上下文可以处理输入事件并相应地调整输入法状态 -+ * \param event 输入事件 -+ * \return 返回布尔值 -+ */ -+bool QOpenHarmonyPlatformInputContext::filterEvent(const QEvent *event) -+{ -+ return QPlatformInputContext::filterEvent(event); -+} -+/*! -+ * \brief 获取键盘区域的矩形范围,以便应用程序可以根据键盘的显示状态调整布局 -+ * \return 返回键盘区域矩形大小 -+ */ -+QRectF QOpenHarmonyPlatformInputContext::keyboardRect() const -+{ -+ //TODO acquire softkeyborad rect -+ return QRectF(); -+} -+ -+/*! -+ * \brief 返回布尔值,指示输入面板是否处于动画状态 -+ * \return 返回布尔值 -+ */ -+bool QOpenHarmonyPlatformInputContext::isAnimating() const -+{ -+ return false; -+} -+/*! -+ * \brief 显示输入面板 -+ */ -+void QOpenHarmonyPlatformInputContext::showInputPanel() -+{ -+ // qWarning() << "<-----------::" << Q_FUNC_INFO; -+ QSharedPointer query = focusObjectInputMethodQuery(); -+ if (query.isNull()) -+ return; -+ -+ disconnect(m_updateCursorPosConnection); -+ if (qGuiApp->focusObject()->metaObject()->indexOfSignal("cursorPositionChanged(int,int)") >= 0) // QLineEdit breaks the pattern -+ m_updateCursorPosConnection = connect(qGuiApp->focusObject(), SIGNAL(cursorPositionChanged(int,int)), this, SLOT(updateCursorPosition())); -+ else -+ m_updateCursorPosConnection = connect(qGuiApp->focusObject(), SIGNAL(cursorPositionChanged()), this, SLOT(updateCursorPosition())); -+ -+ Qt::InputMethodHints hints = query->value(Qt::ImHints).value(); -+ Qt::EnterKeyType enterType = query->value(Qt::ImEnterKeyType).value(); -+ -+ bool isAttached = m_jsInputCtl->call("isAttached"); -+ if (!isAttached) -+ m_jsInputCtl->call("attach", QVariant(qlonglong(static_cast(this))), -+ harmonyTextInputType(hints), -+ harmonyEnterKeyType(enterType)); -+ -+ emitInputPanelVisibleChanged(); -+} -+/*! -+ * \brief 隐藏输入面板 -+ */ -+void QOpenHarmonyPlatformInputContext::hideInputPanel() -+{ -+ // qWarning() << "<-----------::" << Q_FUNC_INFO; -+#if 0 -+ if(m_jsInputCtl->call("isAttached")) -+ m_jsInputCtl->call("detach"); -+#endif -+ clearInput(); -+ emitInputPanelVisibleChanged(); -+} -+/*! -+ * \brief 返回布尔值,指示输入面板的可见性状态 -+ * \return 返回布尔值 -+ */ -+bool QOpenHarmonyPlatformInputContext::isInputPanelVisible() const -+{ -+ bool visible = m_jsInputCtl->call("isInputPanelVisible"); -+ return visible; -+} -+/*! -+ * \brief 设置获得焦点的对象 -+ * \param object 对象指针 -+ */ -+void QOpenHarmonyPlatformInputContext::setFocusObject(QObject *object) -+{ -+ if (object != m_focusObject) { -+ m_focusObject = object; -+ reset(); -+ } -+ QPlatformInputContext::setFocusObject(object); -+} -+ -+bool QOpenHarmonyPlatformInputContext::beginBatchEdit() -+{ -+ ++m_batchEditNestingLevel; -+ return true; -+} -+ -+bool QOpenHarmonyPlatformInputContext::endBatchEdit() -+{ -+ if (--m_batchEditNestingLevel == 0) { //ending batch edit mode -+ //focusObjectStartComposing(); -+ updateCursorPosition(); -+ } -+ return true; -+} -+ -+QOpenHarmonyPlatformInputContext *QOpenHarmonyPlatformInputContext::openHarmonyInputContext() -+{ -+ return m_openHarmonyInputContext; -+} -+ -+void QOpenHarmonyPlatformInputContext::setPlatformIntegration(QOpenHarmonyPlatformIntegration *integration) -+{ -+ m_integration = integration; -+} -+ -+void QOpenHarmonyPlatformInputContext::clear() -+{ -+ m_integration = nullptr; -+ m_ignoreMouseEvents = false; -+} -+ -+void QOpenHarmonyPlatformInputContext::touchBegin() -+{ -+ m_touchPoints.clear(); -+} -+ -+void QOpenHarmonyPlatformInputContext::touchAdd(int id, int action, float force, float x, float y) -+{ -+ if (m_integration == nullptr) -+ return; -+ -+ Qt::TouchPointState state = Qt::TouchPointStationary; -+ switch (action) { -+ case 0: -+ state = Qt::TouchPointPressed; -+ break; -+ case 1: -+ state = Qt::TouchPointReleased; -+ break; -+ case 2: -+ state = Qt::TouchPointMoved; -+ break; -+ case 3: -+ // ohos touch cancel -+ // state = Qt::TouchPointStationary; -+ break; -+ default: -+ break; -+ } -+ -+ QRect rc = m_integration->screen()->availableGeometry(); -+ -+ const double dw = static_cast(rc.width()); -+ const double dh = static_cast(rc.height()); -+ QWindowSystemInterface::TouchPoint touchPoint; -+ touchPoint.id = id; -+ touchPoint.pressure = static_cast(force); -+ // touchPoint.rotation = qRadiansToDegrees(rotation); -+ touchPoint.rotation = 0; -+ touchPoint.normalPosition = QPointF(double(x / dw), double(y / dh)); -+ touchPoint.state = state; -+ -+ float minor = 50; -+ float major = 50; -+ touchPoint.area = QRectF(x - minor, -+ y - major, -+ double(minor * 2), -+ double(major * 2)); -+ m_touchPoints.push_back(touchPoint); -+ -+ if (state == Qt::TouchPointPressed) { -+ touchDown(x, y); -+ } -+} -+ -+void QOpenHarmonyPlatformInputContext::touchEnd(int id) -+{ -+ Q_UNUSED(id) -+ if ((m_integration == nullptr) || m_touchPoints.isEmpty()) -+ return; -+ -+ QTouchDevice *touchDevice = m_integration->touchDevice(); -+ if (touchDevice == nullptr) { -+ touchDevice = new QTouchDevice; -+ touchDevice->setType(QTouchDevice::TouchScreen); -+ touchDevice->setCapabilities(QTouchDevice::Position -+ | QTouchDevice::Area -+ | QTouchDevice::Pressure -+ | QTouchDevice::NormalizedPosition); -+ QWindowSystemInterface::registerTouchDevice(touchDevice); -+ m_integration->setTouchDevice(touchDevice); -+ } -+ -+ QWindow *window = m_integration->screen()->topLevelAt(m_touchPoints.at(0).area.center().toPoint()); -+ QWindowSystemInterface::handleTouchEvent(window, touchDevice, m_touchPoints); -+} -+ -+void QOpenHarmonyPlatformInputContext::setComposingText(const QString &text) -+{ -+ m_composingText = text; -+} -+ -+void QOpenHarmonyPlatformInputContext::mousePress(float x, float y) -+{ -+ if ((m_integration == nullptr) || m_ignoreMouseEvents) -+ return; -+ -+ QPoint globalPos(x,y); -+ QWindow *tlw = m_integration->screen()->topLevelAt(globalPos); -+ // m_mouseGrabber = tlw; -+ QPoint localPos = tlw ? (globalPos - tlw->position()) : globalPos; -+ QWindowSystemInterface::handleMouseEvent(tlw, localPos, globalPos, -+ Qt::MouseButtons(Qt::LeftButton), -+ Qt::LeftButton, QEvent::MouseButtonPress); -+} -+ -+void QOpenHarmonyPlatformInputContext::mouseRelease(float x, float y) -+{ -+ if ((m_integration == nullptr) || m_ignoreMouseEvents) -+ return; -+ -+ QPoint globalPos(x,y); -+ QWindow *tlw = m_integration->screen()->topLevelAt(globalPos); -+ // m_mouseGrabber = tlw; -+ QPoint localPos = tlw ? (globalPos - tlw->position()) : globalPos; -+ QWindowSystemInterface::handleMouseEvent(tlw, localPos, globalPos, -+ Qt::MouseButtons(Qt::LeftButton), -+ Qt::LeftButton, QEvent::MouseButtonPress); -+} -+ -+void QOpenHarmonyPlatformInputContext::touchDown(float x, float y) -+{ -+ -+} -+ -+void QOpenHarmonyPlatformInputContext::clearInput() -+{ -+ m_composingText.clear(); -+ m_composingTextStart = -1; -+ m_composingCursor = -1; -+ // m_extractedText.clear(); -+} -+ -+void QOpenHarmonyPlatformInputContext::commitText(QString text) -+{ -+ QSharedPointer query = focusObjectInputMethodQuery(); -+ if (query.isNull()) -+ return; -+ -+ m_composingText = text; -+ -+ QInputMethodEvent event; -+ event.setCommitString(text); -+ QGuiApplication::sendEvent(m_focusObject, &event); -+} -+ -+void QOpenHarmonyPlatformInputContext::commitMoveCursor(int direction) -+{ -+ QSharedPointer query = focusObjectInputMethodQuery(); -+ if (query.isNull()) -+ return; -+ -+ QInputMethodEvent event; -+ const int absolutecursorPos = getAbsoluteCursorPosition(query); -+ /* 1-up 2-down -+ * 3-left 4-right -+ */ -+ switch(direction) -+ { -+ case 1: -+ break; -+ case 2: -+ break; -+ case 3: -+ event = QInputMethodEvent({}, { { QInputMethodEvent::Selection, qMax(absolutecursorPos - 1, 0), 0 } }); -+ break; -+ case 4: -+ event = QInputMethodEvent ({}, { { QInputMethodEvent::Selection, absolutecursorPos + 1, 0 } }); -+ break; -+ default:break; -+ } -+ QGuiApplication::sendEvent(m_focusObject, &event); -+} -+ -+void QOpenHarmonyPlatformInputContext::showInputPanelLater(Qt::ApplicationState state) -+{ -+ if (state != Qt::ApplicationActive) -+ return; -+ disconnect(qGuiApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(showInputPanelLater(Qt::ApplicationState))); -+ showInputPanel(); -+} -+ -+void QOpenHarmonyPlatformInputContext::deleteSurroundingText(int leftLength, int rightLength) -+{ -+ QSharedPointer query = focusObjectInputMethodQuery(); -+ if (query.isNull()) -+ return; -+ -+ const int absolutecursorPos = getAbsoluteCursorPosition(query); -+ const QString &selectText = query->value(Qt::ImCurrentSelection).toString(); -+ const int selectLength = selectText.length(); -+ int beginPos = absolutecursorPos; -+ -+ QInputMethodEvent event; -+ if (!selectText.isEmpty()) { -+ beginPos = qMax(absolutecursorPos - selectLength, 0); -+ event = QInputMethodEvent ({}, { { QInputMethodEvent::Selection, beginPos, selectLength } }); -+ QGuiApplication::sendEvent(m_focusObject, &event); -+ -+ event.setCommitString({}, beginPos, selectLength); -+ QGuiApplication::sendEvent(m_focusObject, &event); -+ } -+ -+ if (leftLength > 0) { -+ beginPos = qMax(absolutecursorPos - leftLength, 0); -+ event = QInputMethodEvent ({}, { { QInputMethodEvent::Selection, beginPos, leftLength} }); -+ QGuiApplication::sendEvent(m_focusObject, &event); -+ -+ event.setCommitString({}, beginPos, leftLength); -+ QGuiApplication::sendEvent(m_focusObject, &event); -+ } -+ -+ if (rightLength > 0) { -+ beginPos = absolutecursorPos <= 0 ? -1 : absolutecursorPos; -+ event = QInputMethodEvent ({}, { { QInputMethodEvent::Selection, beginPos, rightLength }}); -+ QGuiApplication::sendEvent(m_focusObject, &event); -+ -+ event.setCommitString({}, beginPos, rightLength); -+ QGuiApplication::sendEvent(m_focusObject, &event); -+ } -+ -+ event = QInputMethodEvent({}, { { QInputMethodEvent::Selection, beginPos, 0 } }); -+ QGuiApplication::sendEvent(m_focusObject, &event); -+} -+ -+void QOpenHarmonyPlatformInputContext::sendInputMethodEvent(QInputMethodEvent *event) -+{ -+ if (!qGuiApp) -+ return; -+ -+ QObject *focusObject = qGuiApp->focusObject(); -+ if (!focusObject) -+ return; -+ -+ QCoreApplication::sendEvent(focusObject, event); -+} -+ -+QSharedPointer QOpenHarmonyPlatformInputContext::focusObjectInputMethodQuery(Qt::InputMethodQueries queries) -+{ -+ if (!qGuiApp) -+ return {}; -+ -+ QObject *focusObject = qGuiApp->focusObject(); -+ if (!focusObject) -+ return {}; -+ -+ QInputMethodQueryEvent *ret = new QInputMethodQueryEvent(queries); -+ QCoreApplication::sendEvent(focusObject, ret); -+ return QSharedPointer(ret); -+} -+ -+bool QOpenHarmonyPlatformInputContext::focusObjectIsComposing() const -+{ -+ return m_composingCursor != -1; -+} -+ -+void QOpenHarmonyPlatformInputContext::focusObjectStartComposing() -+{ -+ if (focusObjectIsComposing() || m_composingText.isEmpty()) -+ return; -+ -+ // Composing strings containing newline characters are rare and may cause problems -+ if (m_composingText.contains(QLatin1Char('\n'))) -+ return; -+ -+ QSharedPointer query = focusObjectInputMethodQuery(); -+ if (!query) -+ return; -+ -+ if (query->value(Qt::ImCursorPosition).toInt() != query->value(Qt::ImAnchorPosition).toInt()) -+ return; -+ -+ const int absoluteCursorPos = getAbsoluteCursorPosition(query); -+ if (absoluteCursorPos < m_composingTextStart -+ || absoluteCursorPos > m_composingTextStart + m_composingText.length()) -+ return; -+ -+ m_composingCursor = absoluteCursorPos; -+ -+ QTextCharFormat underlined; -+ underlined.setFontUnderline(true); -+ -+ QInputMethodEvent event(m_composingText, { -+ { QInputMethodEvent::Cursor, absoluteCursorPos - m_composingTextStart, 1 }, -+ { QInputMethodEvent::TextFormat, 0, m_composingText.length(), underlined } -+ }); -+ -+ event.setCommitString({}, m_composingTextStart - absoluteCursorPos, m_composingText.length()); -+ -+ QGuiApplication::sendEvent(m_focusObject, &event); -+} -+ -+bool QOpenHarmonyPlatformInputContext::focusObjectStopComposing() -+{ -+ if (!focusObjectIsComposing()) -+ return true; // not composing -+ -+ QSharedPointer query = focusObjectInputMethodQuery(); -+ if (query.isNull()) -+ return false; -+ -+ const int blockPos = getBlockPosition(query); -+ const int localCursorPos = m_composingCursor - blockPos; -+ -+ m_composingCursor = -1; -+ -+ { -+ // commit the composing test -+ QList attributes; -+ QInputMethodEvent event(QString(), attributes); -+ event.setCommitString(m_composingText); -+ sendInputMethodEvent(&event); -+ } -+ { -+ // Moving Qt's cursor to where the preedit cursor used to be -+ QList attributes; -+ attributes.append( -+ QInputMethodEvent::Attribute(QInputMethodEvent::Selection, localCursorPos, 0)); -+ QInputMethodEvent event(QString(), attributes); -+ sendInputMethodEvent(&event); -+ } -+ -+ return true; -+} -+ -+ -+QT_END_NAMESPACE -+ -+ -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatforminputcontext.h b/src/plugins/platforms/openharmony/qopenharmonyplatforminputcontext.h -new file mode 100644 -index 0000000000..62fb0fadfc ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatforminputcontext.h -@@ -0,0 +1,95 @@ -+#ifndef QOPENHARMONYPLATFORMINPUTCONTEXT_H -+#define QOPENHARMONYPLATFORMINPUTCONTEXT_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+class QOpenHarmonyJsObject; -+class QOpenHarmonyPlatformIntegration; -+ -+QT_BEGIN_NAMESPACE -+class QOpenHarmonyPlatformInputContext: public QPlatformInputContext -+{ -+ Q_OBJECT -+ -+public: -+ QOpenHarmonyPlatformInputContext(); -+ ~QOpenHarmonyPlatformInputContext() override; -+ -+ /* 判断输入上下文是否有效 */ -+ bool isValid() const override { return true; } -+ bool hasCapability(Capability capability) const override; -+ -+ void reset() override; -+ void commit() override; -+ void update(Qt::InputMethodQueries queries) override; -+ void invokeAction(QInputMethod::Action act, int cursorPosition) override; -+ -+ bool filterEvent(const QEvent *event) override; -+ QRectF keyboardRect() const override; -+ -+ bool isAnimating() const override; -+ -+ void showInputPanel() override; -+ void hideInputPanel() override; -+ bool isInputPanelVisible() const override; -+ -+ void setFocusObject(QObject *object) override; -+ -+ bool beginBatchEdit(); -+ bool endBatchEdit(); -+ -+ void clear(); -+ void touchBegin(); -+ void touchEnd(int id); -+ void setComposingText(const QString &text); -+ void touchAdd(int id, int action, float force, float x, float y); -+ static QOpenHarmonyPlatformInputContext * openHarmonyInputContext(); -+ void setPlatformIntegration(QOpenHarmonyPlatformIntegration *integration); -+ static napi_value init(napi_env env, napi_value exports); -+ void dispatchKeyEvent(OH_NativeXComponent_KeyCode code, -+ OH_NativeXComponent_KeyAction action, -+ OH_NativeXComponent_EventSourceType stype, int64_t deviceId, int64_t timeStamp); -+ Q_INVOKABLE void sendShortcut(int key); -+ -+public Q_SLOTS: -+ void updateCursorPosition(); -+ void mousePress(float x, float y); -+ void mouseRelease(float x, float y); -+ void touchDown(float x, float y); -+ -+private Q_SLOTS: -+ void clearInput(); -+ void commitText(QString text); -+ void commitMoveCursor(int direction); -+ void showInputPanelLater(Qt::ApplicationState state); -+ void deleteSurroundingText(int leftLength, int rightLength); -+ -+private: -+ bool focusObjectIsComposing() const; -+ void focusObjectStartComposing(); -+ bool focusObjectStopComposing(); -+ void sendInputMethodEvent(QInputMethodEvent *event); -+ QSharedPointer focusObjectInputMethodQuery(Qt::InputMethodQueries queries = Qt::ImQueryAll); -+ -+private: -+ QString m_composingText; -+ int m_composingTextStart; -+ int m_composingCursor; -+ QObject *m_focusObject; -+ int m_batchEditNestingLevel; -+ QSet m_mpd; /* 记录按下的修饰键 */ -+ bool m_ignoreMouseEvents = false; -+ QMetaObject::Connection m_updateCursorPosConnection; -+ QOpenHarmonyPlatformIntegration *m_integration = nullptr; -+ QList m_touchPoints; -+ QSharedPointer m_jsInputCtl; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYPLATFORMINPUTCONTEXT_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformintegration.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformintegration.cpp -new file mode 100644 -index 0000000000..c397f8fe4f ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformintegration.cpp -@@ -0,0 +1,296 @@ -+#include "qopenharmonyplatformintegration.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include "qabstracteventdispatcher.h" -+#include "qopenharmonyeventdispatcher.h" -+#include "qopenharmonyplatformbackingstore.h" -+#include "qopenharmonyplatformforeignwindow.h" -+#include "qopenharmonyplatformopenglcontext.h" -+#include "qopenharmonyplatformopenglwindow.h" -+#include "qopenharmonyplatformscreen.h" -+#include "qopenharmonyplatformfontdatabase.h" -+#include "qopenharmonyplatformoffscreensurface.h" -+#include "qopenharmonyplatforminputcontext.h" -+#include "qopenharmonyplatformtheme.h" -+#include "qopenharmonyplatformclipboard.h" -+#include "qopenharmonyeglcore.h" -+#if QT_CONFIG(vulkan) -+#include "qopenharmonyplatformvulkaninstance.h" -+#endif -+#include "qopenharmonydefines.h" -+#include "qopenharmonyeglcore.h" -+#include "qopenharmonymain.h" -+ -+#include -+#include -+ -+ -+QT_BEGIN_NAMESPACE -+ -+int QOpenHarmonyPlatformIntegration::m_defaultGeometryWidth = 720; -+int QOpenHarmonyPlatformIntegration::m_defaultGeometryHeight = 1280; -+int QOpenHarmonyPlatformIntegration::m_defaultScreenWidth = 720; -+int QOpenHarmonyPlatformIntegration::m_defaultScreenHeight = 1280; -+int QOpenHarmonyPlatformIntegration::m_defaultPhysicalSizeWidth = 68; -+int QOpenHarmonyPlatformIntegration::m_defaultPhysicalSizeHeight = 121; -+int QOpenHarmonyPlatformIntegration::m_defaultLeft = 0; -+int QOpenHarmonyPlatformIntegration::m_defaultTop = 72; -+ -+Qt::ScreenOrientation QOpenHarmonyPlatformIntegration::m_orientation = Qt::PrimaryOrientation; -+Qt::ScreenOrientation QOpenHarmonyPlatformIntegration::m_nativeOrientation = Qt::PrimaryOrientation; -+ -+bool QOpenHarmonyPlatformIntegration::m_showPasswordEnabled = false; -+ -+ -+QOpenHarmonyPlatformIntegration::QOpenHarmonyPlatformIntegration(const QStringList ¶mList) -+{ -+ Q_UNUSED(paramList); -+ -+ QOpenHarmonyEGLCore::init(); -+ m_eglDisplay = QOpenHarmonyEGLCore::eglDisplay(); -+ m_eglConfig = QOpenHarmonyEGLCore::eglConfig(); -+ -+ m_harmonyFontDatabase.reset(new QOpenHarmonyPlatformFontDatabase); -+ -+ m_primaryScreen.reset(new QOpenHarmonyPlatformScreen); -+ m_primaryScreen->setPhysicalSize(QSize(m_defaultPhysicalSizeWidth, m_defaultPhysicalSizeHeight)); -+ m_primaryScreen->setSize(QSize(m_defaultScreenWidth, m_defaultScreenHeight)); -+ m_primaryScreen->setAvailableGeometry(QRect(m_defaultLeft, m_defaultTop, m_defaultGeometryWidth, m_defaultGeometryHeight)); -+ QWindowSystemInterface::handleScreenAdded(m_primaryScreen.data()); -+ -+ m_mainThread = QThread::currentThread(); -+ -+#ifndef QT_NO_CLIPBOARD -+ m_platformClipboard.reset(new QOpenHarmonyPlatformClipboard); -+#endif -+ -+ m_platformNativeInterface.reset(new QOpenHarmonyPlatformNativeInterface()); -+ QCoreApplication::postEvent(m_platformNativeInterface.data(), new QEvent(QEvent::User)); -+} -+ -+ -+bool QOpenHarmonyPlatformIntegration::hasCapability(Capability cap) const -+{ -+ return true; -+} -+ -+QPlatformBackingStore *QOpenHarmonyPlatformIntegration::createPlatformBackingStore(QWindow *window) const -+{ -+ return new QOpenHarmonyPlatformBackingStore(window); -+} -+ -+#ifndef QT_NO_OPENGL -+QPlatformOpenGLContext *QOpenHarmonyPlatformIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const -+{ -+ QSurfaceFormat format(context->format()); -+ format.setAlphaBufferSize(8); -+ format.setRedBufferSize(8); -+ format.setGreenBufferSize(8); -+ format.setBlueBufferSize(8); -+ format.setDepthBufferSize(8); -+ format.setStencilBufferSize(8); -+ EGLConfig confg = QOpenHarmonyEGLCore::eglConfig(); -+ auto ctx = new QOpenHarmonyPlatformOpenGLContext(format, context->shareHandle(), m_eglDisplay, &confg, context->nativeHandle()); -+ context->setNativeHandle(QVariant::fromValue(QEGLNativeContext(ctx->eglContext(), m_eglDisplay))); -+ return ctx; -+} -+#endif -+ -+QPlatformOffscreenSurface *QOpenHarmonyPlatformIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const -+{ -+ QSurfaceFormat format(surface->requestedFormat()); -+ format.setAlphaBufferSize(8); -+ format.setRedBufferSize(8); -+ format.setGreenBufferSize(8); -+ format.setBlueBufferSize(8); -+ -+ if (surface->nativeHandle()) { -+ return new QOpenHarmonyPlatformOffscreenSurface(m_eglDisplay, format, surface); -+ } -+ -+ return new QEGLPbuffer(m_eglDisplay, format, surface); -+} -+ -+QPlatformWindow *QOpenHarmonyPlatformIntegration::createPlatformWindow(QWindow *window) const -+{ -+ return new QOpenHarmonyPlatformOpenGLWindow(window, m_eglDisplay); -+} -+ -+QPlatformWindow *QOpenHarmonyPlatformIntegration::createForeignWindow(QWindow *window, WId nativeHandle) const -+{ -+ return new QOpenHarmonyPlatformForeignWindow(window, nativeHandle); -+} -+ -+QAbstractEventDispatcher *QOpenHarmonyPlatformIntegration::createEventDispatcher() const -+{ -+ return new QOpenHarmonyEventDispatcher; -+} -+ -+QOpenHarmonyPlatformIntegration::~QOpenHarmonyPlatformIntegration() -+{ -+ QOpenHarmonyEGLCore::clear(); -+ QOpenHarmonyPlatformInputContext *context = qobject_cast(m_platformInputContext.data()); -+ if (context != nullptr) -+ context->clear(); -+ QtOpenHarmony::setOpenHarmonyPlatformIntegration(nullptr); -+} -+ -+void QOpenHarmonyPlatformIntegration::initialize() -+{ -+ const QString icStr = QPlatformInputContextFactory::requested(); -+ if (icStr.isNull()) { -+ QOpenHarmonyPlatformInputContext *context = new QOpenHarmonyPlatformInputContext; -+ context->setPlatformIntegration(this); -+ m_platformInputContext.reset(context); -+ } -+ else -+ m_platformInputContext.reset(QPlatformInputContextFactory::create(icStr)); -+} -+ -+QPlatformInputContext *QOpenHarmonyPlatformIntegration::inputContext() const -+{ -+ return m_platformInputContext.data(); -+} -+ -+QPlatformNativeInterface *QOpenHarmonyPlatformIntegration::nativeInterface() const -+{ -+ return m_platformNativeInterface.data(); -+} -+ -+QVariant QOpenHarmonyPlatformIntegration::styleHint(StyleHint hint) const -+{ -+ switch (hint) { -+ case PasswordMaskDelay: -+ return m_showPasswordEnabled ? 1500 : 0; -+ case ShowIsMaximized: -+ return QtOpenHarmony::isPhone(); -+ default: -+ return QPlatformIntegration::styleHint(hint); -+ } -+} -+ -+Qt::WindowState QOpenHarmonyPlatformIntegration::defaultWindowState(Qt::WindowFlags flags) const -+{ -+ // Don't maximize dialogs on OpenHarmony -+ if (flags & Qt::Dialog & ~Qt::Window) { -+ return Qt::WindowNoState; -+ } -+ return QPlatformIntegration::defaultWindowState(flags); -+} -+/* NOTE 改主题名称和加载的样式插件相关联 */ -+static const QLatin1String openharmonyThemeName("openharmony"); -+QStringList QOpenHarmonyPlatformIntegration::themeNames() const -+{ -+ return QStringList{ "harmonyos", QString(openharmonyThemeName) }; -+} -+ -+QPlatformTheme *QOpenHarmonyPlatformIntegration::createPlatformTheme(const QString &name) const -+{ -+ return new QOpenHarmonyPlatformTheme; -+} -+ -+void QOpenHarmonyPlatformIntegration::setDefaultDisplayMetrics(int left, int top, int gw, int gh, int sw, int sh, int screenWidth, int screenHeight) -+{ -+ m_defaultLeft = left; -+ m_defaultTop = top; -+ m_defaultGeometryWidth = gw; -+ m_defaultGeometryHeight = gh; -+ m_defaultPhysicalSizeWidth = sw; -+ m_defaultPhysicalSizeHeight = sh; -+ m_defaultScreenWidth = screenWidth; -+ m_defaultScreenHeight = screenHeight; -+} -+ -+void QOpenHarmonyPlatformIntegration::setDefaultDesktopSize(int gw, int gh) -+{ -+ m_defaultScreenWidth = gw; -+ m_defaultScreenHeight = gh; -+} -+ -+void QOpenHarmonyPlatformIntegration::setDefaultGeometrySize(int gw, int gh) -+{ -+ m_defaultGeometryWidth = gw; -+ m_defaultGeometryHeight = gh; -+} -+ -+void QOpenHarmonyPlatformIntegration::setScreenOrientation(Qt::ScreenOrientation currentOrientation, -+ Qt::ScreenOrientation nativeOrientation) -+{ -+ m_orientation = currentOrientation; -+ m_nativeOrientation = nativeOrientation; -+} -+ -+void QOpenHarmonyPlatformIntegration::flushPendingUpdates() -+{ -+ m_primaryScreen->setPhysicalSize(QSize(m_defaultPhysicalSizeWidth, -+ m_defaultPhysicalSizeHeight)); -+ m_primaryScreen->setSize(QSize(m_defaultScreenWidth, m_defaultScreenHeight)); -+ m_primaryScreen->setAvailableGeometry(QRect(m_defaultLeft, m_defaultTop, m_defaultGeometryWidth, m_defaultGeometryHeight)); -+} -+ -+void QOpenHarmonyPlatformIntegration::setDesktopSize(int left, int top, int width, int height) -+{ -+ if (m_primaryScreen.isNull()) -+ return; -+ m_primaryScreen->setAvailableGeometry(QRect(left, top, width, height)); -+} -+ -+void QOpenHarmonyPlatformIntegration::setDisplayMetrics(int width, int height) -+{ -+ if (m_primaryScreen.isNull()) -+ return; -+ QMetaObject::invokeMethod(m_primaryScreen.data(), "setPhysicalSize", Qt::AutoConnection, Q_ARG(QSize, QSize(width, height))); -+} -+ -+void QOpenHarmonyPlatformIntegration::setScreenSize(int width, int height) -+{ -+ if (m_primaryScreen.isNull()) -+ return; -+ QMetaObject::invokeMethod(m_primaryScreen.data(), "setSize", Qt::AutoConnection, Q_ARG(QSize, QSize(width, height))); -+} -+ -+QPlatformFontDatabase *QOpenHarmonyPlatformIntegration::fontDatabase() const -+{ -+ return m_harmonyFontDatabase.data(); -+} -+ -+#ifndef QT_NO_CLIPBOARD -+QPlatformClipboard *QOpenHarmonyPlatformIntegration::clipboard() const -+{ -+ return m_platformClipboard.data(); -+} -+#endif -+ -+#if QT_CONFIG(vulkan) -+ -+QPlatformVulkanInstance *QOpenHarmonyPlatformIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const -+{ -+ return new QOpenHarmonyPlatformVulkanInstance(instance); -+} -+ -+#endif // QT_CONFIG(vulkan) -+ -+void QOpenHarmonyPlatformNativeInterface::customEvent(QEvent *event) -+{ -+ if (event->type() != QEvent::User) -+ return; -+ -+ QOpenHarmonyPlatformIntegration *api = static_cast(QGuiApplicationPrivate::platformIntegration()); -+ QtOpenHarmony::setOpenHarmonyPlatformIntegration(api); -+ api->flushPendingUpdates(); -+} -+ -+ -+QT_END_NAMESPACE -+ -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformintegration.h b/src/plugins/platforms/openharmony/qopenharmonyplatformintegration.h -new file mode 100644 -index 0000000000..a1706af0ea ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformintegration.h -@@ -0,0 +1,133 @@ -+#ifndef QOPENHARMONYPLATFORMINTERATION_H -+#define QOPENHARMONYPLATFORMINTERATION_H -+ -+#include -+ -+#include -+#include -+#include -+ -+#include -+ -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QTouchDevice; -+class QDesktopWidget; -+class QOpenHarmonyDrag; -+class QPlatformAccessibility; -+class QOpenHarmonyPlatformScreen; -+class QOpenHarmonyPlatformFontDatabase; -+class QOpenHarmonyPlatformInputContext; -+ -+ -+class QOpenHarmonyPlatformNativeInterface: public QPlatformNativeInterface -+{ -+protected: -+ void customEvent(QEvent *event) override; -+}; -+ -+class QOpenHarmonyPlatformIntegration : public QPlatformIntegration -+{ -+ friend class QOpenHarmonyPlatformScreen; -+ -+public: -+ QOpenHarmonyPlatformIntegration(const QStringList ¶mList); -+ ~QOpenHarmonyPlatformIntegration() override; -+ -+ void initialize() override; -+ -+ bool hasCapability(QPlatformIntegration::Capability cap) const override; -+ -+ QPlatformWindow *createPlatformWindow(QWindow *window) const override; -+ QPlatformWindow *createForeignWindow(QWindow *window, WId nativeHandle) const override; -+ QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override; -+#ifndef QT_NO_OPENGL -+ QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override; -+#endif -+ QAbstractEventDispatcher *createEventDispatcher() const override; -+ QOpenHarmonyPlatformScreen *screen() { return m_primaryScreen.data(); } -+ QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override; -+ -+ virtual void setDesktopSize(int left, int top, int width, int height); -+ virtual void setDisplayMetrics(int width, int height); -+ void setScreenSize(int width, int height); -+ bool isVirtualDesktop() { return true; } -+ -+ QPlatformFontDatabase *fontDatabase() const override; -+ -+#ifndef QT_NO_CLIPBOARD -+ QPlatformClipboard *clipboard() const override; -+#endif -+ -+ QPlatformInputContext *inputContext() const override; -+ QPlatformNativeInterface *nativeInterface() const override; -+// QPlatformServices *services() const override; -+ -+//#ifndef QT_NO_ACCESSIBILITY -+// virtual QPlatformAccessibility *accessibility() const override; -+//#endif -+ -+ QVariant styleHint(StyleHint hint) const override; -+ Qt::WindowState defaultWindowState(Qt::WindowFlags flags) const override; -+ -+ QStringList themeNames() const override; -+ QPlatformTheme *createPlatformTheme(const QString &name) const override; -+ -+ static void setDefaultDisplayMetrics(int left, int top, int gw, int gh, int sw, int sh, int width, int height); -+ static void setDefaultDesktopSize(int gw, int gh); -+ static void setDefaultGeometrySize(int gw, int gh); -+ static void setScreenOrientation(Qt::ScreenOrientation currentOrientation, -+ Qt::ScreenOrientation nativeOrientation); -+ -+ static QSize defaultDesktopSize() -+ { -+ return QSize(m_defaultGeometryWidth, m_defaultGeometryHeight); -+ } -+ -+ QTouchDevice *touchDevice() const { return m_touchDevice; } -+ void setTouchDevice(QTouchDevice *touchDevice) { m_touchDevice = touchDevice; } -+ -+ void flushPendingUpdates(); -+ -+#if QT_CONFIG(vulkan) -+ QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const; -+#endif // QT_CONFIG(vulkan) -+private: -+ EGLDisplay m_eglDisplay; -+ EGLConfig m_eglConfig; -+ QTouchDevice *m_touchDevice = nullptr; -+ -+ QScopedPointer m_primaryScreen; -+ -+ QThread *m_mainThread; -+ -+ static int m_defaultGeometryWidth; -+ static int m_defaultGeometryHeight; -+ static int m_defaultPhysicalSizeWidth; -+ static int m_defaultPhysicalSizeHeight; -+ static int m_defaultScreenWidth; -+ static int m_defaultScreenHeight; -+ static int m_defaultLeft; -+ static int m_defaultTop; -+ -+ static Qt::ScreenOrientation m_orientation; -+ static Qt::ScreenOrientation m_nativeOrientation; -+ static bool m_showPasswordEnabled; -+ -+ QScopedPointer m_harmonyFontDatabase; -+ -+ QScopedPointer m_platformInputContext; -+ -+#ifndef QT_NO_CLIPBOARD -+ QScopedPointer m_platformClipboard; -+#endif -+ -+ QScopedPointer m_platformNativeInterface; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformoffscreensurface.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformoffscreensurface.cpp -new file mode 100644 -index 0000000000..def46c762e ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformoffscreensurface.cpp -@@ -0,0 +1,32 @@ -+#include "qopenharmonyplatformoffscreensurface.h" -+ -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+QOpenHarmonyPlatformOffscreenSurface::QOpenHarmonyPlatformOffscreenSurface(EGLDisplay display, const QSurfaceFormat &format, QOffscreenSurface *offscreenSurface) -+ : QPlatformOffscreenSurface(offscreenSurface) -+ , m_format(format) -+ , m_display(display) -+ , m_surface(EGL_NO_SURFACE) -+{ -+ // Get native handle -+ EGLNativeWindowType surfaceTexture = (EGLNativeWindowType)offscreenSurface->nativeHandle(); -+ -+ EGLConfig config = q_configFromGLFormat(m_display, m_format, false); -+ if (config) { -+ const EGLint attributes[] = { -+ EGL_NONE -+ }; -+ m_surface = eglCreateWindowSurface(m_display, config, surfaceTexture, attributes); -+ } -+} -+ -+QOpenHarmonyPlatformOffscreenSurface::~QOpenHarmonyPlatformOffscreenSurface() -+{ -+ eglDestroySurface(m_display, m_surface); -+} -+ -+QT_END_NAMESPACE -+ -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformoffscreensurface.h b/src/plugins/platforms/openharmony/qopenharmonyplatformoffscreensurface.h -new file mode 100644 -index 0000000000..0b4009d140 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformoffscreensurface.h -@@ -0,0 +1,28 @@ -+#ifndef QOPENHARMONYPLATFORMOFFSCREENSURFACETEXTURE_H -+#define QOPENHARMONYPLATFORMOFFSCREENSURFACETEXTURE_H -+ -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+class QOffscreenSurface; -+class QOpenHarmonyPlatformOffscreenSurface : public QPlatformOffscreenSurface -+{ -+public: -+ QOpenHarmonyPlatformOffscreenSurface(EGLDisplay display, const QSurfaceFormat &format, -+ QOffscreenSurface *offscreenSurface); -+ ~QOpenHarmonyPlatformOffscreenSurface(); -+ -+ QSurfaceFormat format() const override { return m_format; } -+ bool isValid() const override { return m_surface != EGL_NO_SURFACE; } -+ -+ EGLSurface surface() const { return m_surface; } -+private: -+ QSurfaceFormat m_format; -+ EGLDisplay m_display; -+ EGLSurface m_surface; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYPLATFORMOFFSCREENSURFACETEXTURE_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformopenglcontext.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformopenglcontext.cpp -new file mode 100644 -index 0000000000..fdb3c5cfbb ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformopenglcontext.cpp -@@ -0,0 +1,49 @@ -+#include "qopenharmonyplatformopenglcontext.h" -+#include "qopenharmonyplatformopenglwindow.h" -+#include "qopenharmonyplatformintegration.h" -+#include "qopenharmonydefines.h" -+ -+#include -+ -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+QOpenHarmonyPlatformOpenGLContext::QOpenHarmonyPlatformOpenGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display, EGLConfig *config, -+ const QVariant &nativeHandle) -+ :QEGLPlatformContext(format, share, display, config, nativeHandle) -+{ -+} -+ -+//void QOpenHarmonyPlatformOpenGLContext::swapBuffers(QPlatformSurface *surface) -+//{ -+// if (surface->surface()->surfaceClass() == QSurface::Window && -+// static_cast(surface)->checkNativeSurface(eglConfig())) { -+// QEGLPlatformContext::makeCurrent(surface); -+// } -+ -+// QEGLPlatformContext::swapBuffers(surface); -+//} -+ -+//bool QOpenHarmonyPlatformOpenGLContext::makeCurrent(QPlatformSurface *surface) -+//{ -+// return QEGLPlatformContext::makeCurrent(surface); -+//} -+ -+EGLSurface QOpenHarmonyPlatformOpenGLContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface) -+{ -+ if (surface->surface()->surfaceClass() == QSurface::Window) { -+ return static_cast(surface)->eglSurface(eglConfig()); -+ } else { -+ auto platformOffscreenSurface = static_cast(surface); -+ if (platformOffscreenSurface->offscreenSurface()->nativeHandle()) -+ return 0; -+// return static_cast(surface)->surface(); -+ else -+ return static_cast(surface)->pbuffer(); -+ } -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformopenglcontext.h b/src/plugins/platforms/openharmony/qopenharmonyplatformopenglcontext.h -new file mode 100644 -index 0000000000..c885aa5fec ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformopenglcontext.h -@@ -0,0 +1,22 @@ -+#ifndef QOPENHARMONYPLATFORMOPENGLCONTEXT_H -+#define QOPENHARMONYPLATFORMOPENGLCONTEXT_H -+ -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyPlatformOpenGLContext : public QEGLPlatformContext -+{ -+public: -+ QOpenHarmonyPlatformOpenGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display, EGLConfig *config, const QVariant &nativeHandle); -+// void swapBuffers(QPlatformSurface *surface) override; -+// bool makeCurrent(QPlatformSurface *surface) override; -+ -+private: -+ EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface) override; -+ -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYPLATFORMOPENGLCONTEXT_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformopenglwindow.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformopenglwindow.cpp -new file mode 100644 -index 0000000000..8e80ab7de7 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformopenglwindow.cpp -@@ -0,0 +1,220 @@ -+#include "qopenharmonyplatformopenglwindow.h" -+#include "qopenharmonyxcomponent.h" -+#include "qopenharmonyplatformscreen.h" -+#include "qopenharmonydefines.h" -+#include "qopenharmonyeglcore.h" -+#include "qopenharmonyjsobject.h" -+#include "qopenharmonyplatformbackingstore.h" -+#include "qopenharmonyjswindowmanager.h" -+#include "qopenharmonyjswindow.h" -+#include "qopenharmonymain.h" -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#include -+QT_BEGIN_NAMESPACE -+ -+ -+QOpenHarmonyPlatformOpenGLWindow::QOpenHarmonyPlatformOpenGLWindow(QWindow *window, EGLDisplay display) -+ :QOpenHarmonyPlatformWindow(window), m_eglDisplay(display), m_jsWindow(nullptr) -+{ -+ -+} -+ -+QOpenHarmonyPlatformOpenGLWindow::~QOpenHarmonyPlatformOpenGLWindow() -+{ -+ if (QtOpenHarmony::isPhone()) -+ return; -+ clearEgl(); -+ if (m_jsWindow != nullptr) -+ qJsWindowManager->destoryWindow(m_jsWindow); -+} -+ -+void QOpenHarmonyPlatformOpenGLWindow::repaint(const QRegion ®ion) -+{ -+ // This is only for real raster top-level windows. Stop in all other cases. -+ if ((window()->surfaceType() == QSurface::RasterGLSurface && qt_window_private(window())->compositing) -+ || window()->surfaceType() == QSurface::OpenGLSurface -+ || QOpenHarmonyPlatformWindow::parent()) -+ return; -+ -+ if (QtOpenHarmony::isPhone()) { -+ // phone draw all windows in a xcomponent -+ QRect dirtyClient = region.boundingRect(); -+ QRect currentGeometry = geometry(); -+ QRect dirtyRegion(currentGeometry.left() + dirtyClient.left(), -+ currentGeometry.top() + dirtyClient.top(), -+ dirtyClient.width(), -+ dirtyClient.height()); -+ QRect mOldGeometryLocal = m_oldGeometry; -+ m_oldGeometry = currentGeometry; -+ // If this is a move, redraw the previous location -+ if (mOldGeometryLocal != currentGeometry) -+ platformScreen()->setDirty(mOldGeometryLocal); -+ platformScreen()->setDirty(dirtyRegion); -+ } else { -+ // pad pc draw window in a independent xcomponent -+ if (m_jsWindow == nullptr) { -+ createJsWindow(); -+ } -+ if (m_jsWindow == nullptr) -+ return; -+ QOpenHarmonyPlatformBackingStore *backingStore = this->backingStore(); -+ if (backingStore) { -+ QImage image = m_jsWindow->image(); -+ QPainter compositePainter(&image); -+ compositePainter.setCompositionMode(QPainter::CompositionMode_Source); -+ QImage backingStoreImage = backingStore->toImage(); -+ compositePainter.drawImage(QPoint(0, 0), backingStoreImage, backingStoreImage.rect()); -+#ifdef USE_MASK -+ compositePainter.save(); -+ compositePainter.setBrush(QBrush(Qt::red)); -+ QString ct("managed by isoftstone"); -+ QStaticText sct(ct); -+ QFontMetrics fm(compositePainter.font()); -+ int height = fm.height(); -+ int width = fm.horizontalAdvance(ct); -+ QPoint tp = image.rect().bottomRight(); -+ tp.rx() -= (width + 2); -+ tp.ry() -= (height + 2); -+ compositePainter.drawStaticText(tp, sct); -+ compositePainter.restore(); -+#endif -+ -+ m_jsWindow->paint(image); -+ } -+ } -+} -+ -+QMargins QOpenHarmonyPlatformOpenGLWindow::frameMargins() const -+{ -+ return QtOpenHarmony::frameMargins(); -+} -+ -+void QOpenHarmonyPlatformOpenGLWindow::setGeometry(const QRect &rect) -+{ -+ // 在手机上,所有窗口绘制在一个Xcomponent, -+ if (!QtOpenHarmony::isPhone()) { -+ if (m_jsWindow == nullptr) -+ createJsWindow(); -+ if (m_jsWindow == nullptr) -+ return; -+ // 设置jswindow的尺寸 -+ m_jsWindow->setGeometry(rect); -+ } -+ if (rect == geometry()) -+ return; -+ -+ m_oldGeometry = geometry(); -+ -+ QOpenHarmonyPlatformWindow::setGeometry(rect); -+ QRect availableGeometry = screen()->availableGeometry(); -+ if (m_oldGeometry.width() == 0 -+ && m_oldGeometry.height() == 0 -+ && rect.width() > 0 -+ && rect.height() > 0 -+ && availableGeometry.width() > 0 -+ && availableGeometry.height() > 0) { -+ QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size())); -+ } -+ -+ if (rect.topLeft() != m_oldGeometry.topLeft()) -+ repaint(QRegion(rect)); -+} -+ -+EGLSurface QOpenHarmonyPlatformOpenGLWindow::surface() -+{ -+ if (m_jsWindow == nullptr) { -+ createJsWindow(); -+ } -+ -+ return m_jsWindow == nullptr ? EGL_NO_SURFACE : m_jsWindow->eglSurface(); -+} -+ -+EGLSurface QOpenHarmonyPlatformOpenGLWindow::eglSurface(EGLConfig config) -+{ -+ if (QGuiApplication::applicationState() == Qt::ApplicationSuspended) -+ return m_eglSurface; -+ -+ if (m_eglSurface == EGL_NO_SURFACE) { -+ checkNativeSurface(config); -+ } -+ return m_eglSurface; -+} -+ -+bool QOpenHarmonyPlatformOpenGLWindow::checkNativeSurface(EGLConfig config) -+{ -+ createEgl(config); -+ // we've create another surface, the window should be repainted -+ QRect availableGeometry = screen()->availableGeometry(); -+ if (geometry().width() > 0 && geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0) -+ QWindowSystemInterface::handleExposeEvent(window(), QRegion(QRect(QPoint(), geometry().size()))); -+ return true; -+} -+ -+void QOpenHarmonyPlatformOpenGLWindow::applicationStateChanged(Qt::ApplicationState state) -+{ -+ QOpenHarmonyPlatformWindow::applicationStateChanged(state); -+ if (state <= Qt::ApplicationHidden) { -+ clearEgl(); -+ } -+} -+ -+void QOpenHarmonyPlatformOpenGLWindow::createEgl(EGLConfig config) -+{ -+// clearEgl(); -+ EGLint winAttribs[] = {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, EGL_NONE}; -+ m_eglSurface = surface(); -+ m_format = q_glFormatFromConfig(m_eglDisplay, config, window()->requestedFormat()); -+ if (Q_UNLIKELY(m_eglSurface == EGL_NO_SURFACE)) { -+ EGLint error = eglGetError(); -+ eglTerminate(m_eglDisplay); -+ qWarning("EGL Error : Could not create the egl surface: error = 0x%x\n", error); -+ } -+} -+ -+QSurfaceFormat QOpenHarmonyPlatformOpenGLWindow::format() const -+{ -+ if (m_eglSurface == 0) -+ return window()->requestedFormat(); -+ else -+ return m_format; -+} -+ -+void QOpenHarmonyPlatformOpenGLWindow::clearEgl() -+{ -+ if (m_eglSurface != EGL_NO_SURFACE) { -+ eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); -+ eglDestroySurface(m_eglDisplay, m_eglSurface); -+ m_eglSurface = EGL_NO_SURFACE; -+ } -+} -+ -+void QOpenHarmonyPlatformOpenGLWindow::createJsWindow() const -+{ -+ m_jsWindow = qJsWindowManager->createWindow(const_cast(this)); -+} -+ -+EGLDisplay QOpenHarmonyPlatformOpenGLWindow::eglDisplay() const -+{ -+ return m_eglDisplay; -+} -+ -+void QOpenHarmonyPlatformOpenGLWindow::setVisible(bool visible) -+{ -+ QOpenHarmonyPlatformWindow::setVisible(visible); -+ if (!QtOpenHarmony::isPhone()) { -+ if (m_jsWindow == nullptr) -+ createJsWindow(); -+ m_jsWindow->setVisible(visible); -+ } -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformopenglwindow.h b/src/plugins/platforms/openharmony/qopenharmonyplatformopenglwindow.h -new file mode 100644 -index 0000000000..785ede93ef ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformopenglwindow.h -@@ -0,0 +1,49 @@ -+#ifndef QOPENHARMONYPLATFORMOPENGLWINDOW_H -+#define QOPENHARMONYPLATFORMOPENGLWINDOW_H -+ -+#include -+#include "qopenharmonyplatformwindow.h" -+ -+QT_BEGIN_NAMESPACE -+class QOpenHarmonyJsObject; -+class QOpenHarmonyJsWindow; -+ -+class QOpenHarmonyPlatformOpenGLWindow : public QOpenHarmonyPlatformWindow -+{ -+public: -+ explicit QOpenHarmonyPlatformOpenGLWindow(QWindow *window, EGLDisplay display); -+ ~QOpenHarmonyPlatformOpenGLWindow() override; -+ -+ void setGeometry(const QRect &rect) override; -+ -+ EGLSurface eglSurface(EGLConfig config); -+ QSurfaceFormat format() const override; -+ -+ bool checkNativeSurface(EGLConfig config); -+ -+ void applicationStateChanged(Qt::ApplicationState) override; -+ -+ void repaint(const QRegion ®ion) override; -+ -+ virtual QMargins frameMargins() const; -+ -+ EGLDisplay eglDisplay() const; -+ -+ void setVisible(bool visible); -+ -+protected: -+ EGLSurface surface(); -+ void createEgl(EGLConfig config); -+ void clearEgl(); -+ void createJsWindow() const; -+ -+private: -+ EGLDisplay m_eglDisplay = EGL_NO_DISPLAY; -+ EGLSurface m_eglSurface = EGL_NO_SURFACE; -+ QSurfaceFormat m_format; -+ QRect m_oldGeometry; -+ mutable QOpenHarmonyJsWindow *m_jsWindow = nullptr; -+}; -+ -+QT_END_NAMESPACE -+#endif // QOPENHARMONYPLATFORMOPENGLWINDOW_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformplugin.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformplugin.cpp -new file mode 100644 -index 0000000000..c4cb6257c3 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformplugin.cpp -@@ -0,0 +1,26 @@ -+#include -+#include "qopenharmonyplatformintegration.h" -+#include "qopenharmonydefines.h" -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyPlatformIntegrationPlugin: public QPlatformIntegrationPlugin -+{ -+ Q_OBJECT -+ Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "openharmony.json") -+public: -+ QPlatformIntegration *create(const QString &key, const QStringList ¶mList) override; -+}; -+ -+ -+QPlatformIntegration *QOpenHarmonyPlatformIntegrationPlugin::create(const QString &key, const QStringList ¶mList) -+{ -+ Q_UNUSED(paramList); -+ if (!key.compare(QLatin1String("openharmony"), Qt::CaseInsensitive)) -+ return new QOpenHarmonyPlatformIntegration(paramList); -+ return 0; -+} -+ -+QT_END_NAMESPACE -+#include "qopenharmonyplatformplugin.moc" -+ -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformscreen.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformscreen.cpp -new file mode 100644 -index 0000000000..10a9b29cf2 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformscreen.cpp -@@ -0,0 +1,329 @@ -+#include -+#include -+ -+#ifdef USE_MASK -+#include -+#endif -+ -+#include -+#include -+ -+#include "qopenharmonymain.h" -+#include "qopenharmonyplatformscreen.h" -+#include "qopenharmonyplatformbackingstore.h" -+#include "qopenharmonyplatformintegration.h" -+#include "qopenharmonyplatformwindow.h" -+#include "qopenharmonyplatformopenglwindow.h" -+#include "qopenharmonydefines.h" -+#include "qopenharmonyjswindow.h" -+#include "qopenharmonyjswindowmanager.h" -+ -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+ -+QOpenHarmonyPlatformScreen::QOpenHarmonyPlatformScreen() -+ : QObject(), QPlatformScreen(), m_stopDraw(false) -+{ -+ m_availableGeometry = QRect(QOpenHarmonyPlatformIntegration::m_defaultLeft, QOpenHarmonyPlatformIntegration::m_defaultTop, QOpenHarmonyPlatformIntegration::m_defaultGeometryWidth, QOpenHarmonyPlatformIntegration::m_defaultGeometryHeight); -+ m_size = QSize(QOpenHarmonyPlatformIntegration::m_defaultScreenWidth, QOpenHarmonyPlatformIntegration::m_defaultScreenHeight); -+ -+ m_format = QImage::Format_ARGB32_Premultiplied; -+ m_depth = 32; -+ m_physicalSize.setHeight(QOpenHarmonyPlatformIntegration::m_defaultPhysicalSizeHeight); -+ m_physicalSize.setWidth(QOpenHarmonyPlatformIntegration::m_defaultPhysicalSizeWidth); -+ connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QOpenHarmonyPlatformScreen::applicationStateChanged); -+} -+ -+QOpenHarmonyPlatformScreen::~QOpenHarmonyPlatformScreen() -+{ -+ -+} -+ -+QWindow *QOpenHarmonyPlatformScreen::topWindow() const -+{ -+ for (QOpenHarmonyPlatformWindow *w : m_windowStack) { -+ if (w->window()->type() == Qt::Window || -+ w->window()->type() == Qt::Popup || -+ w->window()->type() == Qt::Dialog) { -+ return w->window(); -+ } -+ } -+ return nullptr; -+} -+ -+QWindow *QOpenHarmonyPlatformScreen::topLevelAt(const QPoint &p) const -+{ -+ for (QOpenHarmonyPlatformWindow *w : m_windowStack) { -+ if (w->geometry().contains(p, false) && w->window()->isVisible()) -+ return w->window(); -+ } -+ return nullptr; -+} -+ -+bool QOpenHarmonyPlatformScreen::event(QEvent *event) -+{ -+ if (event->type() == QEvent::UpdateRequest) { -+ doRedraw(); -+ m_updatePending = false; -+ return true; -+ } -+ return QObject::event(event); -+} -+ -+void QOpenHarmonyPlatformScreen::addWindow(QOpenHarmonyPlatformWindow *window) -+{ -+ if (window->parent() && window->isRaster()) -+ return; -+ -+ Q_ASSERT(!m_windowStack.contains(window)); -+ m_windowStack.prepend(window); -+ if (window->isRaster() && QtOpenHarmony::isPhone()) { -+ setDirty(window->geometry()); -+ } -+ -+ QWindow *w = topWindow(); -+ QWindowSystemInterface::handleWindowActivated(w); -+ topWindowChanged(w); -+} -+void QOpenHarmonyPlatformScreen::removeWindow(QOpenHarmonyPlatformWindow *window) -+{ -+ if (window->parent() && window->isRaster()) -+ return; -+ -+ -+ Q_ASSERT(m_windowStack.contains(window)); -+ m_windowStack.removeOne(window); -+ Q_ASSERT(!m_windowStack.contains(window)); -+ -+ if (window->isRaster() && QtOpenHarmony::isPhone()) { -+ setDirty(window->geometry()); -+ } -+ -+ QWindow *w = topWindow(); -+ QWindowSystemInterface::handleWindowActivated(w); -+ topWindowChanged(w); -+} -+ -+void QOpenHarmonyPlatformScreen::raise(QOpenHarmonyPlatformWindow *window) -+{ -+ if (window->parent() && window->isRaster()) -+ return; -+ -+ int index = m_windowStack.indexOf(window); -+ if (index <= 0) -+ return; -+ m_windowStack.move(index, 0); -+ if (window->isRaster() && QtOpenHarmony::isPhone()) { -+ setDirty(window->geometry()); -+ } -+ QWindow *w = topWindow(); -+ QWindowSystemInterface::handleWindowActivated(w); -+ topWindowChanged(w); -+} -+ -+void QOpenHarmonyPlatformScreen::lower(QOpenHarmonyPlatformWindow *window) -+{ -+ if (window->parent() && window->isRaster()) -+ return; -+ -+ int index = m_windowStack.indexOf(window); -+ if (index == -1 || index == (m_windowStack.size() - 1)) -+ return; -+ m_windowStack.move(index, m_windowStack.size() - 1); -+ if (window->isRaster() && QtOpenHarmony::isPhone()) { -+ setDirty(window->geometry()); -+ } -+ QWindow *w = topWindow(); -+ QWindowSystemInterface::handleWindowActivated(w); -+ topWindowChanged(w); -+} -+ -+void QOpenHarmonyPlatformScreen::scheduleUpdate() -+{ -+ if (!m_updatePending) { -+ m_updatePending = true; -+ QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); -+ } -+} -+ -+void QOpenHarmonyPlatformScreen::setDirty(const QRect &rect) -+{ -+ QRect intersection = rect.intersected(m_availableGeometry); -+ m_dirtyRect |= intersection; -+ scheduleUpdate(); -+} -+ -+void QOpenHarmonyPlatformScreen::setPhysicalSize(const QSize &size) -+{ -+ m_physicalSize = size; -+} -+ -+void QOpenHarmonyPlatformScreen::setSize(const QSize &size) -+{ -+ m_size = size; -+ QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry()); -+} -+ -+void QOpenHarmonyPlatformScreen::setAvailableGeometry(const QRect &rect) -+{ -+ if (m_availableGeometry == rect) -+ return; -+ -+ QRect oldGeometry = m_availableGeometry; -+ -+ m_availableGeometry = rect; -+ -+ QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry()); -+ resizeMaximizedWindows(); -+ -+ if (oldGeometry.width() == 0 && oldGeometry.height() == 0 && rect.width() > 0 && rect.height() > 0) { -+ QList windows = QGuiApplication::allWindows(); -+ for (int i = 0; i < windows.size(); ++i) { -+ QWindow *w = windows.at(i); -+ if (w->handle()) { -+ QRect geometry = w->handle()->geometry(); -+ if (geometry.width() > 0 && geometry.height() > 0) -+ QWindowSystemInterface::handleExposeEvent(w, QRect(QPoint(0, 0), geometry.size())); -+ } -+ } -+ } -+} -+ -+void QOpenHarmonyPlatformScreen::applicationStateChanged(Qt::ApplicationState state) -+{ -+ for (QOpenHarmonyPlatformWindow *w : qAsConst(m_windowStack)) -+ w->applicationStateChanged(state); -+ -+// if (state <= Qt::ApplicationHidden) { -+// releaseSurface(); -+// } -+ m_stopDraw = (state <= Qt::ApplicationHidden); -+} -+ -+void QOpenHarmonyPlatformScreen::topWindowChanged(QWindow *w) -+{ -+ if (w != 0) { -+ QOpenHarmonyPlatformWindow *platformWindow = static_cast(w->handle()); -+ if (platformWindow != 0) -+ platformWindow->updateStatusBarVisibility(); -+ } -+} -+ -+void QOpenHarmonyPlatformScreen::doRedraw() -+{ -+ if (m_dirtyRect.isEmpty() || m_stopDraw) -+ return; -+ -+ // Stop if there are no visible raster windows. If we only have RasterGLSurface -+ // windows that have renderToTexture children (i.e. they need the OpenGL path) then -+ // we do not need an overlay surface. -+ bool hasVisibleRasterWindows = false; -+ for (QOpenHarmonyPlatformWindow *window : qAsConst(m_windowStack)) { -+ if (window->window()->isVisible() && window->isRaster() && !qt_window_private(window->window())->compositing) { -+ hasVisibleRasterWindows = true; -+ break; -+ } -+ } -+ if (!hasVisibleRasterWindows) { -+ qDebug() << "no visible raster windows"; -+ return; -+ } -+ -+ if (m_window == nullptr || m_windowStack.isEmpty()) { -+ m_window = qJsWindowManager->createWindow(m_windowStack.first()); -+ } -+ if (m_window == nullptr) { -+ return; -+ } -+ -+ QImage screenImage = m_window->image(); -+ QPainter compositePainter(&screenImage); -+ compositePainter.setCompositionMode(QPainter::CompositionMode_Source); -+ -+ QRegion visibleRegion(m_dirtyRect); -+ for (QOpenHarmonyPlatformWindow *window : qAsConst(m_windowStack)) { -+ if (!window->window()->isVisible() -+ || qt_window_private(window->window())->compositing -+ || !window->isRaster()) -+ continue; -+ -+ for (const QRect &rect : std::vector(visibleRegion.begin(), visibleRegion.end())) { -+ QRect targetRect = window->geometry(); -+ targetRect &= rect; -+ -+ if (targetRect.isNull()) -+ continue; -+ -+ visibleRegion -= targetRect; -+ QRect windowRect = targetRect.translated(-window->geometry().topLeft()); -+ QOpenHarmonyPlatformOpenGLWindow *openglWindow = static_cast(window); -+ if (openglWindow != nullptr) { -+ QOpenHarmonyPlatformBackingStore *backingStore = openglWindow->backingStore(); -+ if (backingStore) { -+ compositePainter.drawImage(targetRect.topLeft(), backingStore->toImage(), windowRect); -+ } -+ } -+ } -+ } -+ -+ for (const QRect &rect : visibleRegion) -+ compositePainter.fillRect(rect, QColor(Qt::transparent)); -+ -+#ifdef USE_MASK -+ compositePainter.save(); -+ compositePainter.setBrush(QBrush(Qt::red)); -+ QString ct("managed by isoftstone"); -+ QStaticText sct(ct); -+ QFontMetrics fm(compositePainter.font()); -+ int height = fm.height(); -+ int width = fm.horizontalAdvance(ct); -+ QPoint tp = screenImage.rect().bottomRight(); -+ tp.rx() -= (width + 2); -+ tp.ry() -= (height + 2); -+ compositePainter.drawStaticText(tp, sct); -+ compositePainter.restore(); -+#endif -+ -+ m_window->paint(screenImage); -+ m_dirtyRect = QRect(); -+} -+ -+QDpi QOpenHarmonyPlatformScreen::logicalDpi() const -+{ -+ qreal lDpi = QtOpenHarmony::scaledDensity() * 72; -+ return QDpi(lDpi, lDpi); -+} -+ -+qreal QOpenHarmonyPlatformScreen::pixelDensity() const -+{ -+ return QtOpenHarmony::pixelDensity(); -+} -+ -+Qt::ScreenOrientation QOpenHarmonyPlatformScreen::orientation() const -+{ -+ return QOpenHarmonyPlatformIntegration::m_orientation; -+} -+ -+Qt::ScreenOrientation QOpenHarmonyPlatformScreen::nativeOrientation() const -+{ -+ return QOpenHarmonyPlatformIntegration::m_nativeOrientation; -+} -+ -+void QOpenHarmonyPlatformScreen::releaseSurface() -+{ -+// if (m_component != nullptr) { -+// QOpenHarmonyXComponentManager::instance()->remove(m_component); -+// m_component = nullptr; -+// } -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformscreen.h b/src/plugins/platforms/openharmony/qopenharmonyplatformscreen.h -new file mode 100644 -index 0000000000..20bec988ab ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformscreen.h -@@ -0,0 +1,80 @@ -+#ifndef QOPENHARMONYPLATFORMSCREEN_H -+#define QOPENHARMONYPLATFORMSCREEN_H -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyPlatformWindow; -+class QOpenHarmonyJsWindow; -+ -+class QOpenHarmonyPlatformScreen: public QObject, public QPlatformScreen -+{ -+ Q_OBJECT -+public: -+ QOpenHarmonyPlatformScreen(); -+ ~QOpenHarmonyPlatformScreen(); -+ -+ QRect geometry() const override { return QRect(QPoint(), m_size); } -+ QRect availableGeometry() const override { return m_availableGeometry; } -+ int depth() const override { return m_depth; } -+ QImage::Format format() const override { return m_format; } -+ QSizeF physicalSize() const override { return m_physicalSize; } -+ -+ inline QWindow *topWindow() const; -+ QWindow *topLevelAt(const QPoint & p) const override; -+ -+ // compositor api -+ void addWindow(QOpenHarmonyPlatformWindow *window); -+ void removeWindow(QOpenHarmonyPlatformWindow *window); -+ void raise(QOpenHarmonyPlatformWindow *window); -+ void lower(QOpenHarmonyPlatformWindow *window); -+ -+ void scheduleUpdate(); -+ void topWindowChanged(QWindow *w); -+ -+public slots: -+ void setDirty(const QRect &rect); -+ void setPhysicalSize(const QSize &size); -+ void setAvailableGeometry(const QRect &rect); -+ void setSize(const QSize &size); -+ -+protected: -+ bool event(QEvent *event) override; -+ -+ typedef QList WindowStackType; -+ WindowStackType m_windowStack; -+ QRect m_dirtyRect; -+ bool m_updatePending = false; -+ -+ QRect m_availableGeometry; -+ int m_depth; -+ QImage::Format m_format; -+ QSizeF m_physicalSize; -+ -+private: -+ QDpi logicalDpi() const override; -+ qreal pixelDensity() const override; -+ Qt::ScreenOrientation orientation() const override; -+ Qt::ScreenOrientation nativeOrientation() const override; -+ void releaseSurface(); -+ void applicationStateChanged(Qt::ApplicationState); -+ -+private slots: -+ void doRedraw(); -+ -+private: -+ QOpenHarmonyJsWindow *m_window = nullptr; -+ QWaitCondition m_surfaceWaitCondition; -+ QSize m_size; -+ bool m_stopDraw; -+}; -+ -+QT_END_NAMESPACE -+#endif -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformtheme.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformtheme.cpp -new file mode 100644 -index 0000000000..84b3ec2649 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformtheme.cpp -@@ -0,0 +1,119 @@ -+#include -+#include -+#include -+#include "qopenharmonyplatformtheme.h" -+#include "qopenharmonyplatformdialoghelpers.h" -+ -+#include -+#include -+ -+static QPalette qt_harmonyPalette() -+{ -+#if 1 -+ QColor base("#FFFFFF"); -+ QColor text("#182431"); -+ QColor window("#FFFFFF"); -+ QColor button("#0C182431"); -+ QColor windowText("#182431"); -+ QColor brightText("#FFFFFF"); -+ QColor dark = button.darker(); -+ QColor light = button.lighter(); -+ QColor mid = button.lighter(130); -+ -+ QPalette harmonyPalette(windowText, button, light, dark, mid, text, brightText, base, window); -+ -+ return harmonyPalette; -+#endif -+ -+#if 0 -+ QColor backGround(239, 239, 239); -+ QColor light = backGround.lighter(150); -+ QColor mid(backGround.darker(130)); -+ QColor midLight = mid.lighter(110); -+ QColor base = Qt::white; -+ QColor disabledBase(backGround); -+ QColor dark = backGround.darker(150); -+ QColor darkDisabled = QColor(209, 209, 209).darker(110); -+ QColor text = Qt::black; -+ QColor hightlightedText = Qt::white; -+ QColor disabledText = QColor(190, 190, 190); -+ QColor button = backGround; -+ QColor shadow = dark.darker(135); -+ QColor disabledShadow = shadow.lighter(150); -+ -+ QPalette fusionPalette(Qt::black,backGround,light,dark,mid,text,base); -+ fusionPalette.setBrush(QPalette::Midlight, midLight); -+ fusionPalette.setBrush(QPalette::Button, button); -+ fusionPalette.setBrush(QPalette::Shadow, shadow); -+ fusionPalette.setBrush(QPalette::HighlightedText, hightlightedText); -+ -+ fusionPalette.setBrush(QPalette::Disabled, QPalette::Text, disabledText); -+ fusionPalette.setBrush(QPalette::Disabled, QPalette::WindowText, disabledText); -+ fusionPalette.setBrush(QPalette::Disabled, QPalette::ButtonText, disabledText); -+ fusionPalette.setBrush(QPalette::Disabled, QPalette::Base, disabledBase); -+ fusionPalette.setBrush(QPalette::Disabled, QPalette::Dark, darkDisabled); -+ fusionPalette.setBrush(QPalette::Disabled, QPalette::Shadow, disabledShadow); -+ -+ fusionPalette.setBrush(QPalette::Active, QPalette::Highlight, QColor(48, 140, 198)); -+ fusionPalette.setBrush(QPalette::Inactive, QPalette::Highlight, QColor(48, 140, 198)); -+ fusionPalette.setBrush(QPalette::Disabled, QPalette::Highlight, QColor(145, 145, 145)); -+ return fusionPalette; -+#endif -+} -+ -+QOpenHarmonyPlatformTheme::QOpenHarmonyPlatformTheme() -+{ -+ QGuiApplication::setPalette(qt_harmonyPalette()); -+} -+ -+QString QOpenHarmonyPlatformTheme::standardButtonText(int button) const -+{ -+ switch (button) { -+ case QPlatformDialogHelper::Yes: -+ return QCoreApplication::translate("QOpenHarmonyPlatformTheme", "Yes"); -+ case QPlatformDialogHelper::YesToAll: -+ return QCoreApplication::translate("QOpenHarmonyPlatformTheme", "Yes to All"); -+ case QPlatformDialogHelper::No: -+ return QCoreApplication::translate("QOpenHarmonyPlatformTheme", "No"); -+ case QPlatformDialogHelper::NoToAll: -+ return QCoreApplication::translate("QOpenHarmonyPlatformTheme", "No to All"); -+ } -+ return QPlatformTheme::standardButtonText(button); -+} -+ -+bool QOpenHarmonyPlatformTheme::usePlatformNativeDialog(QPlatformTheme::DialogType type) const -+{ -+ Q_UNUSED(type) -+ return true; -+} -+ -+const QPalette *QOpenHarmonyPlatformTheme::palette(Palette type) const -+{ -+ if (type == QPlatformTheme::SystemPalette) { -+ QPalette *p = new QPalette(qt_harmonyPalette()); -+ return p; -+ } -+ return nullptr; -+} -+ -+QPlatformDialogHelper *QOpenHarmonyPlatformTheme::createPlatformDialogHelper(QPlatformTheme::DialogType type) const -+{ -+ switch (type) { -+ case MessageDialog: -+ return new QOpenHarmonyPlatformMessageDialogHelper; -+ case FileDialog: -+ return new QOpenHarmonyPlatformFileDialogHelper; -+ default: -+ return 0; -+ } -+} -+ -+QVariant QOpenHarmonyPlatformTheme::themeHint(ThemeHint hint) const -+{ -+ switch(hint) { -+ case ThemeHint::StyleNames: -+ return QVariant(QStringList{ "harmonyos", "openharmony" }); /* FIXME 暂时认为OpenHarmony HarmonyOS是两种不同的主题样式 */ -+ default: -+ return QPlatformTheme::themeHint(hint); -+ } -+} -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformtheme.h b/src/plugins/platforms/openharmony/qopenharmonyplatformtheme.h -new file mode 100644 -index 0000000000..34ee717e33 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformtheme.h -@@ -0,0 +1,18 @@ -+#ifndef QOPENHARMONYPLATFORMTHEME_H -+#define QOPENHARMONYPLATFORMTHEME_H -+ -+#include -+#include -+ -+class QOpenHarmonyPlatformTheme: public QPlatformTheme -+{ -+public: -+ QOpenHarmonyPlatformTheme(); -+ QVariant themeHint(ThemeHint hint) const override; -+ virtual QString standardButtonText(int button) const override; -+ bool usePlatformNativeDialog(DialogType type) const override; -+ const QPalette *palette(Palette type = SystemPalette) const override; -+ QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override; -+}; -+ -+#endif // QOPENHARMONYPLATFORMTHEME_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformvulkaninstance.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformvulkaninstance.cpp -new file mode 100644 -index 0000000000..92e75915d3 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformvulkaninstance.cpp -@@ -0,0 +1,27 @@ -+#include "qopenharmonyplatformvulkaninstance.h" -+ -+QT_BEGIN_NAMESPACE -+ -+QOpenHarmonyPlatformVulkanInstance::QOpenHarmonyPlatformVulkanInstance(QVulkanInstance *instance) -+ : m_instance(instance) -+{ -+ m_lib.setFileName(QStringLiteral("vulkan")); -+ -+ if (!m_lib.load()) { -+ qWarning("Failed to load %s", qPrintable(m_lib.fileName())); -+ return; -+ } -+ -+ init(&m_lib); -+} -+ -+void QOpenHarmonyPlatformVulkanInstance::createOrAdoptInstance() -+{ -+ initInstance(m_instance, QByteArrayList() << QByteArrayLiteral("VK_KHR_android_surface")); -+} -+ -+QOpenHarmonyPlatformVulkanInstance::~QOpenHarmonyPlatformVulkanInstance() -+{ -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformvulkaninstance.h b/src/plugins/platforms/openharmony/qopenharmonyplatformvulkaninstance.h -new file mode 100644 -index 0000000000..27803ba6a1 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformvulkaninstance.h -@@ -0,0 +1,24 @@ -+#ifndef QOPENHARMONYPLATFORMVULKANINSTANCE_H -+#define QOPENHARMONYPLATFORMVULKANINSTANCE_H -+ -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyPlatformVulkanInstance : public QBasicPlatformVulkanInstance -+{ -+public: -+ QOpenHarmonyPlatformVulkanInstance(QVulkanInstance *instance); -+ ~QOpenHarmonyPlatformVulkanInstance(); -+ -+ void createOrAdoptInstance() override; -+ -+private: -+ QVulkanInstance *m_instance; -+ QLibrary m_lib; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYPLATFORMVULKANINSTANCE_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformwindow.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformwindow.cpp -new file mode 100644 -index 0000000000..a73f38fba3 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformwindow.cpp -@@ -0,0 +1,145 @@ -+#include "qopenharmonyplatformwindow.h" -+#include "qopenharmonyplatformscreen.h" -+ -+#include -+#include -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+enum { -+ defaultWindowWidth = 160, -+ defaultWindowHeight = 160 -+}; -+ -+QOpenHarmonyPlatformWindow::QOpenHarmonyPlatformWindow(QWindow *window) -+ : QPlatformWindow(window) -+{ -+ m_windowFlags = window->flags(); -+ m_windowState = Qt::WindowNoState; -+ static QAtomicInt winIdGenerator(1); -+ m_windowId = winIdGenerator.fetchAndAddRelaxed(1); -+ setWindowState(window->windowStates()); -+ m_rect = initialGeometry(window, -+ QRect(0, 0, 400, 300), defaultWindowWidth, defaultWindowHeight); -+} -+ -+void QOpenHarmonyPlatformWindow::lower() -+{ -+ platformScreen()->lower(this); -+} -+ -+void QOpenHarmonyPlatformWindow::raise() -+{ -+ updateStatusBarVisibility(); -+ platformScreen()->raise(this); -+} -+ -+void QOpenHarmonyPlatformWindow::setGeometry(const QRect &rect) -+{ -+ QWindowSystemInterface::handleGeometryChange(window(), rect); -+ QPlatformWindow::setGeometry(rect); -+ m_rect = rect; -+} -+ -+QRect QOpenHarmonyPlatformWindow::geometry() const -+{ -+ return m_rect; -+} -+ -+void QOpenHarmonyPlatformWindow::setVisible(bool visible) -+{ -+ if (visible) { -+ updateStatusBarVisibility(); -+ if (m_windowState & Qt::WindowFullScreen) -+ setGeometry(platformScreen()->geometry()); -+ else if (m_windowState & Qt::WindowMaximized) -+ setGeometry(platformScreen()->availableGeometry()); -+ else if (m_windowState == Qt::WindowNoState) { -+ QRect rect = initialGeometry(window(), -+ window()->geometry(), defaultWindowWidth, defaultWindowHeight); -+ setGeometry(rect); -+ } -+ platformScreen()->addWindow(this); -+ } else { -+ platformScreen()->removeWindow(this); -+ } -+ -+ QRect availableGeometry = screen()->availableGeometry(); -+ if (geometry().width() > 0 && geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0) -+ QPlatformWindow::setVisible(visible); -+} -+ -+void QOpenHarmonyPlatformWindow::setWindowState(Qt::WindowStates state) -+{ -+ if (m_windowState == state) -+ return; -+ -+ QPlatformWindow::setWindowState(state); -+ m_windowState = state; -+ -+ if (window()->isVisible()) -+ updateStatusBarVisibility(); -+} -+ -+void QOpenHarmonyPlatformWindow::setWindowFlags(Qt::WindowFlags flags) -+{ -+ if (m_windowFlags == flags) -+ return; -+ -+ m_windowFlags = flags; -+} -+ -+Qt::WindowFlags QOpenHarmonyPlatformWindow::windowFlags() const -+{ -+ return m_windowFlags; -+} -+ -+void QOpenHarmonyPlatformWindow::setParent(const QPlatformWindow *window) -+{ -+ Q_UNUSED(window); -+} -+ -+QOpenHarmonyPlatformScreen *QOpenHarmonyPlatformWindow::platformScreen() const -+{ -+ return static_cast(window()->screen()->handle()); -+} -+ -+void QOpenHarmonyPlatformWindow::propagateSizeHints() -+{ -+ //shut up warning from default implementation -+} -+ -+void QOpenHarmonyPlatformWindow::requestActivateWindow() -+{ -+ platformScreen()->topWindowChanged(window()); -+} -+ -+void QOpenHarmonyPlatformWindow::updateStatusBarVisibility() -+{ -+ Qt::WindowFlags flags = window()->flags(); -+ bool isNonRegularWindow = flags & (Qt::Popup | Qt::Dialog | Qt::Sheet) & ~Qt::Window; -+ if (!isNonRegularWindow) { -+ } -+} -+ -+bool QOpenHarmonyPlatformWindow::isExposed() const -+{ -+ return qApp->applicationState() > Qt::ApplicationHidden -+ && window()->isVisible() -+ && !window()->geometry().isEmpty(); -+} -+ -+void QOpenHarmonyPlatformWindow::applicationStateChanged(Qt::ApplicationState) -+{ -+ QRegion region; -+ if (isExposed()) -+ region = QRect(QPoint(), geometry().size()); -+ -+ QWindowSystemInterface::handleExposeEvent(window(), region); -+ QWindowSystemInterface::flushWindowSystemEvents(); -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformwindow.h b/src/plugins/platforms/openharmony/qopenharmonyplatformwindow.h -new file mode 100644 -index 0000000000..02a723f2c0 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyplatformwindow.h -@@ -0,0 +1,64 @@ -+#ifndef QOPENHARMONYPLATFORMWINDOW_H -+#define QOPENHARMONYPLATFORMWINDOW_H -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyPlatformScreen; -+class QOpenHarmonyPlatformBackingStore; -+ -+class QOpenHarmonyPlatformWindow: public QPlatformWindow -+{ -+public: -+ explicit QOpenHarmonyPlatformWindow(QWindow *window); -+ -+ void lower() override; -+ void raise() override; -+ -+ void setVisible(bool visible) override; -+ -+ void setWindowState(Qt::WindowStates state) override; -+ void setWindowFlags(Qt::WindowFlags flags) override; -+ Qt::WindowFlags windowFlags() const; -+ void setParent(const QPlatformWindow *window) override; -+ WId winId() const override { return m_windowId; } -+ -+ QOpenHarmonyPlatformScreen *platformScreen() const; -+ -+ void propagateSizeHints() override; -+ void requestActivateWindow() override; -+ void updateStatusBarVisibility(); -+ inline bool isRaster() const { -+ if (isForeignWindow()) -+ return false; -+ -+ return window()->surfaceType() == QSurface::RasterSurface -+ || window()->surfaceType() == QSurface::RasterGLSurface; -+ } -+ bool isExposed() const override; -+ -+ virtual void applicationStateChanged(Qt::ApplicationState); -+ -+ void setBackingStore(QOpenHarmonyPlatformBackingStore *store) { m_backingStore = store; } -+ QOpenHarmonyPlatformBackingStore *backingStore() const { return m_backingStore; } -+ -+ virtual void repaint(const QRegion &) { } -+ -+ void setGeometry(const QRect &rect) override; -+ -+ QRect geometry() const; -+ -+protected: -+ Qt::WindowFlags m_windowFlags; -+ Qt::WindowStates m_windowState; -+ -+ WId m_windowId; -+ QRect m_rect; -+ -+ QOpenHarmonyPlatformBackingStore *m_backingStore = nullptr; -+}; -+ -+QT_END_NAMESPACE -+#endif // QOPENHARMONYPLATFORMWINDOW_H -diff --git a/src/plugins/platforms/openharmony/qopenharmonyxcomponent.cpp b/src/plugins/platforms/openharmony/qopenharmonyxcomponent.cpp -new file mode 100644 -index 0000000000..0d43504bb1 ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyxcomponent.cpp -@@ -0,0 +1,257 @@ -+#include -+#include -+ -+#include "qopenharmonyxcomponent.h" -+#include "qopenharmonyplatforminputcontext.h" -+#include "qopenharmonyeglcore.h" -+#include "qopenharmonyjswindowmanager.h" -+#include "qopenharmonyjswindow.h" -+#include "qopenharmonyplatformwindow.h" -+#include "qopenharmonylog.h" -+#include "qopenharmonymain.h" -+ -+QOpenHarmonyXComponent::QOpenHarmonyXComponent(OH_NativeXComponent *component) -+ : m_nativeComponent(component) -+ , m_window(nullptr) -+{ -+ m_egl.reset(new QOpenHarmonyEGLCore); -+ initXComponent(component); -+} -+ -+QOpenHarmonyXComponent::~QOpenHarmonyXComponent() -+{ -+ delete m_touchEventCallback; -+ delete m_mouseEventCallback; -+} -+ -+void QOpenHarmonyXComponent::setWindow(QOpenHarmonyJsWindow *jsWindow) -+{ -+ m_window = jsWindow; -+} -+ -+void QOpenHarmonyXComponent::paint(const QImage &image) -+{ -+ if (!m_surfaceCreated) -+ m_surfaceCreated = createEglSurface(); -+ if (m_surfaceCreated) -+ m_egl->drawImage(image); -+} -+ -+QString QOpenHarmonyXComponent::name() const -+{ -+ return m_name; -+} -+ -+void QOpenHarmonyXComponent::onSurfaceCreated(OH_NativeXComponent *component, void *window) -+{ -+ QOpenHarmonyJsWindow *jsWindow = qJsWindowManager->window(component); -+ if (jsWindow == nullptr) -+ return; -+ EGLNativeWindowType nativeWindow = reinterpret_cast(window); -+ jsWindow->setNativeWindow(nativeWindow); -+ qJsWindowManager->windowCreated(); -+} -+ -+void QOpenHarmonyXComponent::onSurfaceChanged(OH_NativeXComponent *component, void *window) -+{ -+ Q_UNUSED(window) -+ QOpenHarmonyJsWindow *w = qJsWindowManager->window(component); -+ if (w == nullptr) -+ return; -+ w->updateSurfaceSize(); -+} -+ -+void QOpenHarmonyXComponent::onSurfaceDestroyed(OH_NativeXComponent *component, void *window) -+{ -+ // QOpenHarmonyEGLCore::instance()->onSurfaceDestroyed(m_nativeWindow); -+} -+ -+void QOpenHarmonyXComponent::dispatchKeyEvent(OH_NativeXComponent *component, void *window) -+{ -+ Q_UNUSED(window); -+ QOpenHarmonyPlatformInputContext *context = QOpenHarmonyPlatformInputContext::openHarmonyInputContext(); -+ if (Q_NULLPTR == context) -+ return; -+ -+ OH_NativeXComponent_KeyEvent *keyEvent = Q_NULLPTR; -+ /* 获取由XComponent触发的按键事件 */ -+ if (OH_NativeXComponent_GetKeyEvent(component, &keyEvent) >= 0) { -+ OH_NativeXComponent_KeyAction action; -+ OH_NativeXComponent_GetKeyEventAction(keyEvent, &action); /* 获取按键事件的动作 */ -+ -+ OH_NativeXComponent_KeyCode code; -+ OH_NativeXComponent_GetKeyEventCode(keyEvent, &code); /* 获取按键事件的键码值 */ -+ -+ OH_NativeXComponent_EventSourceType sourceType; -+ OH_NativeXComponent_GetKeyEventSourceType(keyEvent, &sourceType); /* 获取按键事件的输入源类型 */ -+ -+ int64_t deviceId; -+ OH_NativeXComponent_GetKeyEventDeviceId(keyEvent, &deviceId); /* 获取按键事件的设备ID */ -+ -+ int64_t timeStamp; -+ OH_NativeXComponent_GetKeyEventTimestamp(keyEvent, &timeStamp); /* 获取按键事件的时间戳 */ -+ -+#if 0 -+ qWarning() << "dispatchKeyEvent:::::" << action << code << sourceType << deviceId << timeStamp; -+ QOpenHarmonyPlatformInputContext *context = QOpenHarmonyPlatformInputContext::openHarmonyInputContext(); -+ if (Q_NULLPTR == context) -+ return; -+#endif -+ context->dispatchKeyEvent(code, action, sourceType, deviceId, timeStamp); -+ -+ //nativeModifier = Qt::NoModifier; -+ //QWindowSystemInterface::handleKeyEvent(Q_NULLPTR, ulong(timeStamp), t, k, modifiers); -+ -+ } else { -+ qWarning() << "Get Key Event Error"; -+ } -+} -+ -+void QOpenHarmonyXComponent::dispatchTouchEvent(OH_NativeXComponent *component, void *window) -+{ -+ QOpenHarmonyJsWindow *w = qJsWindowManager->window(component); -+ if (w == nullptr) -+ return; -+ OH_NativeXComponent_TouchEvent touchEvent; -+ int32_t ret = OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent); -+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) -+ return; -+ QOpenHarmonyPlatformInputContext *context = QOpenHarmonyPlatformInputContext::openHarmonyInputContext(); -+ context->touchBegin(); -+ QMargins margins = w->frameMargins(); -+ QRect g = w->geometry(); -+ -+ int left = g.left() + margins.left(); -+ int top = g.top() + margins.top(); -+ -+ for (int i = 0; i < touchEvent.numPoints; i++) { -+ OH_NativeXComponent_TouchPoint &point = touchEvent.touchPoints[i]; -+ float x = point.x + left; -+ float y = point.y + top; -+ context->touchAdd(touchEvent.deviceId, touchEvent.type, point.force, x, y); -+ } -+ context->touchEnd(0); -+} -+ -+void QOpenHarmonyXComponent::dispatchMouseEvent(OH_NativeXComponent *component, void *window) -+{ -+ QOpenHarmonyJsWindow *w = qJsWindowManager->window(component); -+ if (w == nullptr) -+ return; -+ OH_NativeXComponent_MouseEvent mouseEvent; -+ int32_t ret = OH_NativeXComponent_GetMouseEvent(component, window, &mouseEvent); -+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) -+ return; -+ QMargins margins = w->frameMargins(); -+ QRect g = w->geometry(); -+ -+ int left = g.left() + margins.left(); -+ int top = g.top() + margins.top(); -+ -+ float x = mouseEvent.x + left; -+ float y = mouseEvent.y + top; -+ OH_NativeXComponent_MouseEventAction action = mouseEvent.action; -+ switch (action) { -+ case OH_NATIVEXCOMPONENT_MOUSE_RELEASE: -+ QOpenHarmonyPlatformInputContext::openHarmonyInputContext()->mouseRelease(x, y); -+ break; -+ case OH_NATIVEXCOMPONENT_MOUSE_PRESS: -+ QOpenHarmonyPlatformInputContext::openHarmonyInputContext()->mousePress(x, y); -+ break; -+ case OH_NATIVEXCOMPONENT_MOUSE_MOVE: -+ break; -+ } -+} -+ -+void QOpenHarmonyXComponent::dispatchHoverEvent(OH_NativeXComponent *component, bool isHover) -+{ -+ -+} -+ -+void QOpenHarmonyXComponent::initXComponent(OH_NativeXComponent *component) -+{ -+ if (m_nativeComponent != nullptr) { -+ m_touchEventCallback = new OH_NativeXComponent_Callback; -+ m_touchEventCallback->OnSurfaceCreated = &QOpenHarmonyXComponent::onSurfaceCreated; -+ m_touchEventCallback->OnSurfaceChanged = &QOpenHarmonyXComponent::onSurfaceChanged; -+ m_touchEventCallback->OnSurfaceDestroyed = &QOpenHarmonyXComponent::onSurfaceDestroyed; -+ m_touchEventCallback->DispatchTouchEvent = &QOpenHarmonyXComponent::dispatchTouchEvent; -+ int32_t ret = OH_NativeXComponent_RegisterCallback(component, m_touchEventCallback); -+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { -+ LOGI("set surface touch callback failed"); -+ } -+ -+ m_mouseEventCallback = new OH_NativeXComponent_MouseEvent_Callback; -+ m_mouseEventCallback->DispatchMouseEvent = &QOpenHarmonyXComponent::dispatchMouseEvent; -+ m_mouseEventCallback->DispatchHoverEvent = &QOpenHarmonyXComponent::dispatchHoverEvent; -+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { -+ LOGI("set surface mouse callback failed"); -+ } -+ -+ ret = OH_NativeXComponent_RegisterKeyEventCallback(component, &QOpenHarmonyXComponent::dispatchKeyEvent); -+ qWarning() << "OH_NativeXComponent_RegisterKeyEventCallback ret:" << ret; -+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) -+ LOGI("set surface keybord callback failed"); -+ -+ char id[OH_XCOMPONENT_ID_LEN_MAX + 1] = { }; -+ uint64_t id_length = OH_XCOMPONENT_ID_LEN_MAX + 1; -+ -+ ret = OH_NativeXComponent_GetXComponentId(component, id, &id_length); -+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { -+ return; -+ } -+ LOGI("get xcomponent succeded %{public}s", id); -+ m_name = QString::fromUtf8(id); -+ } -+} -+ -+QImage QOpenHarmonyXComponent::image() const -+{ -+ QImage image = m_egl->image(); -+ if (image.isNull()) { -+ QImage::Format format = QImage::Format_RGBA8888_Premultiplied; -+ image = QImage(m_size.width(), m_size.height(), format); -+ image.fill(Qt::white); -+ return image; -+ } -+ return image; -+} -+ -+OH_NativeXComponent *QOpenHarmonyXComponent::nativeComponent() const -+{ -+ return m_nativeComponent; -+} -+ -+bool QOpenHarmonyXComponent::createEglSurface() -+{ -+ if (m_window->nativeWindow() == 0) -+ return false; -+ m_egl->createSurface(m_window->nativeWindow(), m_size.width(), m_size.height()); -+ return true; -+} -+ -+void QOpenHarmonyXComponent::updateEglSurface() -+{ -+ m_egl->updateSurfaceSize(m_size.width(), m_size.height()); -+} -+ -+void QOpenHarmonyXComponent::updateSize(int w, int h) -+{ -+ m_size.setWidth(w); -+ m_size.setHeight(h); -+ updateEglSurface(); -+ -+} -+ -+EGLSurface QOpenHarmonyXComponent::eglSurface() -+{ -+ if (!m_surfaceCreated) -+ m_surfaceCreated = createEglSurface(); -+ return m_egl->eglSurface(); -+} -+ -+void QOpenHarmonyXComponent::setNativeXComponent(OH_NativeXComponent *component) -+{ -+ m_nativeComponent = component; -+ initXComponent(component); -+} -diff --git a/src/plugins/platforms/openharmony/qopenharmonyxcomponent.h b/src/plugins/platforms/openharmony/qopenharmonyxcomponent.h -new file mode 100644 -index 0000000000..f7edc8475e ---- /dev/null -+++ b/src/plugins/platforms/openharmony/qopenharmonyxcomponent.h -@@ -0,0 +1,67 @@ -+#ifndef QOPENHARMONYXCOMPONENT_H -+#define QOPENHARMONYXCOMPONENT_H -+ -+#include -+#include -+#include -+#include -+#include -+ -+class QOpenHarmonyEGLCore; -+struct OH_NativeXComponent; -+class QOpenHarmonyJsWindow; -+struct OH_NativeXComponent_Callback; -+struct OH_NativeXComponent_MouseEvent_Callback; -+ -+class QOpenHarmonyXComponent -+{ -+public: -+ QOpenHarmonyXComponent(OH_NativeXComponent *component = nullptr); -+ virtual ~QOpenHarmonyXComponent(); -+ -+ void setWindow(QOpenHarmonyJsWindow *jsWindow); -+ -+ void paint(const QImage &image); -+ -+ QString name() const; -+ -+ EGLSurface eglSurface(); -+ -+ void setNativeXComponent(OH_NativeXComponent *component); -+ OH_NativeXComponent *nativeComponent() const; -+ -+ QImage image() const; -+ -+ void updateSize(int w, int h); -+private: -+ bool createEglSurface(); -+ -+ void updateEglSurface(); -+ -+ static void onSurfaceCreated(OH_NativeXComponent* component, void* window); -+ -+ static void onSurfaceChanged(OH_NativeXComponent* component, void* window); -+ -+ static void onSurfaceDestroyed(OH_NativeXComponent* component, void* window); -+ -+ static void dispatchKeyEvent(OH_NativeXComponent *component, void *window); -+ -+ static void dispatchTouchEvent(OH_NativeXComponent* component, void* window); -+ -+ static void dispatchMouseEvent(OH_NativeXComponent* component, void* window); -+ -+ static void dispatchHoverEvent(OH_NativeXComponent* component, bool isHover); -+ -+private: -+ void initXComponent(OH_NativeXComponent *component); -+private: -+ OH_NativeXComponent *m_nativeComponent = nullptr; -+ OH_NativeXComponent_Callback *m_touchEventCallback = nullptr; -+ OH_NativeXComponent_MouseEvent_Callback *m_mouseEventCallback = nullptr; -+ QScopedPointer m_egl; -+ QOpenHarmonyJsWindow *m_window = nullptr; -+ QSize m_size; -+ bool m_surfaceCreated = false; -+ QString m_name; -+}; -+#endif // QOPENHARMONYXCOMPONENT_H -diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro -index acc55adf6f..288df4ce01 100644 ---- a/src/plugins/platforms/platforms.pro -+++ b/src/plugins/platforms/platforms.pro -@@ -3,9 +3,11 @@ QT_FOR_CONFIG += gui-private - - android:!android-embedded: SUBDIRS += android - --!android: SUBDIRS += minimal -+openharmony: SUBDIRS += openharmony - --!android:qtConfig(freetype): SUBDIRS += offscreen -+!android:!openharmony: SUBDIRS += minimal -+ -+!android:!openharmony:qtConfig(freetype): SUBDIRS += offscreen - - qtConfig(xcb) { - SUBDIRS += xcb -diff --git a/src/src.pro b/src/src.pro -index 1c76a2e46f..18d8903d9b 100644 ---- a/src/src.pro -+++ b/src/src.pro -@@ -147,6 +147,8 @@ src_plugins.target = sub-plugins - - src_android.subdir = $$PWD/android - -+src_openharmony.subdir = $$PWD/openharmony -+ - # this order is important - !qtConfig(system-zlib)|cross_compile { - SUBDIRS += src_qtzlib -@@ -238,6 +240,8 @@ nacl: SUBDIRS -= src_network src_testlib - - android:!android-embedded: SUBDIRS += src_android src_3rdparty_gradle - -+openharmony: SUBDIRS += src_openharmony -+ - TR_EXCLUDE = \ - src_tools_bootstrap src_tools_moc src_tools_rcc src_tools_uic src_tools_qlalr \ - src_tools_bootstrap_dbus src_tools_qdbusxml2cpp src_tools_qdbuscpp2xml \ -diff --git a/src/testlib/qplaintestlogger.cpp b/src/testlib/qplaintestlogger.cpp -index ed53dcdde8..35097057e7 100644 ---- a/src/testlib/qplaintestlogger.cpp -+++ b/src/testlib/qplaintestlogger.cpp -@@ -66,6 +66,10 @@ - # include - #endif - -+#ifdef Q_OS_OPENHARMONY -+#include -+#endif -+ - #ifdef Q_OS_WIN - # include - #endif -@@ -221,6 +225,9 @@ void QPlainTestLogger::outputMessage(const char *str) - } - #elif defined(Q_OS_ANDROID) - __android_log_write(ANDROID_LOG_INFO, "QTestLib", str); -+#elif defined(Q_OS_OPENHARMONY) -+ /* wanghao TODO */ -+ qInfo() << str; - #endif - outputString(str); - } -diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp -index ff5dbaa44a..2401af425e 100644 ---- a/src/testlib/qtestlog.cpp -+++ b/src/testlib/qtestlog.cpp -@@ -417,7 +417,9 @@ void QTestLog::startLogging() - elapsedFunctionTime.start(); - FOREACH_TEST_LOGGER - logger->startLogging(); -+#ifndef Q_OS_OPENHARMONY - QTest::oldMessageHandler = qInstallMessageHandler(QTest::messageHandler); -+#endif - } - - void QTestLog::stopLogging() -diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp -index eb3479b3e0..39cd4c95c8 100644 ---- a/src/widgets/dialogs/qfiledialog.cpp -+++ b/src/widgets/dialogs/qfiledialog.cpp -@@ -2169,7 +2169,15 @@ QString QFileDialog::getOpenFileName(QWidget *parent, - { - const QStringList schemes = QStringList(QStringLiteral("file")); - const QUrl selectedUrl = getOpenFileUrl(parent, caption, QUrl::fromLocalFile(dir), filter, selectedFilter, options, schemes); -+#ifdef Q_OS_OPENHARMONY -+ QString fileName = selectedUrl.toLocalFile(); -+ if (!fileName.isEmpty()) { -+ fileName.prepend("datashare://"); -+ } -+ return fileName; -+#else - return selectedUrl.toLocalFile(); -+#endif - } - - /*! -@@ -2400,7 +2408,15 @@ QString QFileDialog::getSaveFileName(QWidget *parent, - { - const QStringList schemes = QStringList(QStringLiteral("file")); - const QUrl selectedUrl = getSaveFileUrl(parent, caption, QUrl::fromLocalFile(dir), filter, selectedFilter, options, schemes); -+#ifdef Q_OS_OPENHARMONY -+ QString fileName = selectedUrl.toLocalFile(); -+ if (!fileName.isEmpty()) { -+ fileName.prepend("datashare://"); -+ } -+ return fileName; -+#else - return selectedUrl.toLocalFile(); -+#endif - } - - /*! -diff --git a/src/widgets/widgets/qtextbrowser.cpp b/src/widgets/widgets/qtextbrowser.cpp -index 09541bb53b..a8dda888a4 100644 ---- a/src/widgets/widgets/qtextbrowser.cpp -+++ b/src/widgets/widgets/qtextbrowser.cpp -@@ -161,6 +161,11 @@ QString QTextBrowserPrivate::findFile(const QUrl &name) const - if (name.scheme() == QLatin1String("assets")) - fileName = QLatin1String("assets:") + name.path(); - else -+#endif -+#if defined(Q_OS_OPENHARMONY) -+ if (name.scheme() == QLatin1String("rawfile")) -+ fileName = QLatin1String("rawfile:") + name.path(); -+ else - #endif - fileName = name.toLocalFile(); - } -@@ -231,6 +236,9 @@ void QTextBrowserPrivate::_q_activateAnchor(const QString &href) - url.scheme() == QLatin1String("file") - #if defined(Q_OS_ANDROID) - || url.scheme() == QLatin1String("assets") -+#endif -+#if defined(Q_OS_OPENHARMONY) -+ || url.scheme() == QLatin1String("rawfile") - #endif - || url.scheme() == QLatin1String("qrc"); - if ((openExternalLinks && !isFileScheme && !url.isRelative()) -diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp -index e6fac74ccc..ca6e8f3c45 100644 ---- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp -+++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp -@@ -365,7 +365,7 @@ protected: - const QByteArray name = "Bar" + QByteArray::number(i) + postFix; - const char *nm = name.constData(); - int tp = qRegisterMetaType(nm); --#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) -+#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_OPENHARMONY) - pthread_yield(); - #endif - QMetaType info(tp); -diff --git a/tests/auto/corelib/plugin/qpluginloader/almostplugin/almostplugin.cpp b/tests/auto/corelib/plugin/qpluginloader/almostplugin/almostplugin.cpp -index 75806dd285..b4904109ec 100644 ---- a/tests/auto/corelib/plugin/qpluginloader/almostplugin/almostplugin.cpp -+++ b/tests/auto/corelib/plugin/qpluginloader/almostplugin/almostplugin.cpp -@@ -1,36 +1,41 @@ --/**************************************************************************** --** --** Copyright (C) 2016 The Qt Company Ltd. --** Contact: https://www.qt.io/licensing/ --** --** This file is part of the test suite of the Qt Toolkit. --** --** $QT_BEGIN_LICENSE:GPL-EXCEPT$ --** 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 as published by the Free Software --** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT --** 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 "almostplugin.h" --#include -- --QString AlmostPlugin::pluginName() const --{ -- unresolvedSymbol(); -- return QLatin1String("Plugin ok"); --} -+/**************************************************************************** -+** -+** Copyright (C) 2016 The Qt Company Ltd. -+** Contact: https://www.qt.io/licensing/ -+** -+** This file is part of the test suite of the Qt Toolkit. -+** -+** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -+** 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 as published by the Free Software -+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -+** 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 "almostplugin.h" -+#include -+ -+QString AlmostPlugin::pluginName() const -+{ -+ unresolvedSymbol(); -+ return QLatin1String("Plugin ok"); -+} -+ -+void AlmostPlugin::unresolvedSymbol() const -+{ -+ -+} -\ No newline at end of file -diff --git a/tests/auto/corelib/serialization/serialization.pro b/tests/auto/corelib/serialization/serialization.pro -index 9638178cdc..f4a5a3c5b1 100644 ---- a/tests/auto/corelib/serialization/serialization.pro -+++ b/tests/auto/corelib/serialization/serialization.pro -@@ -11,7 +11,8 @@ SUBDIRS = \ - qxmlstream - - !qtHaveModule(gui): SUBDIRS -= \ -- qdatastream -+ qdatastream \ -+ qdatastream_core_pixmap - - !qtHaveModule(network): SUBDIRS -= \ - qtextstream -diff --git a/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp -index 42347d6788..36a4c29d0a 100644 ---- a/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp -+++ b/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp -@@ -656,6 +656,7 @@ void tst_QDateTime::setMSecsSinceEpoch_data() - - void tst_QDateTime::setMSecsSinceEpoch() - { -+#if QT_CONFIG(timezone) - QFETCH(qint64, msecs); - QFETCH(QDateTime, utc); - QFETCH(QDateTime, cet); -@@ -729,6 +730,7 @@ void tst_QDateTime::setMSecsSinceEpoch() - - QDateTime reference(QDate(1970, 1, 1), QTime(), Qt::UTC); - QCOMPARE(dt, reference.addMSecs(msecs)); -+#endif //timezone - } - - void tst_QDateTime::fromMSecsSinceEpoch_data() -@@ -914,6 +916,7 @@ void tst_QDateTime::toString_textDate() - - void tst_QDateTime::toString_textDate_extra() - { -+#if QT_CONFIG(timezone) - QLatin1String GMT("GMT"); - QDateTime dt = QDateTime::fromMSecsSinceEpoch(0, Qt::LocalTime); - QVERIFY(!dt.toString().endsWith(GMT)); -@@ -923,7 +926,6 @@ void tst_QDateTime::toString_textDate_extra() - QVERIFY(dt.toString() != QLatin1String("Thu Jan 1 00:00:00 1970")); - else - QCOMPARE(dt.toString(), QLatin1String("Thu Jan 1 00:00:00 1970")); --#if QT_CONFIG(timezone) - # if defined Q_OS_UNIX && !defined Q_OS_DARWIN && !defined Q_OS_ANDROID - # define CORRECT_ZONE_ABBREV - # endif // QTBUG-57320, QTBUG-57298, QTBUG-68833 -@@ -954,9 +956,9 @@ void tst_QDateTime::toString_textDate_extra() - } else { - qDebug("Missed zone test: no Europe/Berlin zone available"); - } --#endif // timezone - dt = QDateTime::fromMSecsSinceEpoch(0, Qt::UTC); - QVERIFY(dt.toString().endsWith(GMT)); -+#endif // timezone - } - - void tst_QDateTime::toString_rfcDate_data() -@@ -2519,6 +2521,7 @@ void tst_QDateTime::fromStringToStringLocale() - - void tst_QDateTime::offsetFromUtc() - { -+#if QT_CONFIG(timezone) - /* Check default value. */ - QCOMPARE(QDateTime().offsetFromUtc(), 0); - -@@ -2550,6 +2553,7 @@ void tst_QDateTime::offsetFromUtc() - - QDateTime dt6(QDate(2013, 6, 1), QTime(0, 0, 0), QTimeZone("Pacific/Auckland")); - QCOMPARE(dt6.offsetFromUtc(), 43200); -+#endif //timezone - } - - void tst_QDateTime::setOffsetFromUtc() -@@ -2705,6 +2709,7 @@ void tst_QDateTime::zoneAtTime_data() - - void tst_QDateTime::zoneAtTime() - { -+#if QT_CONFIG(timezone) - QFETCH(QByteArray, ianaID); - QFETCH(QDate, date); - QFETCH(int, offset); -@@ -2717,6 +2722,7 @@ void tst_QDateTime::zoneAtTime() - QCOMPARE(zone.standardTimeOffset(QDateTime(date, noon, zone)), offset); - else // zone.offsetFromUtc *does* include DST, even before epoch - QCOMPARE(zone.offsetFromUtc(QDateTime(date, noon, zone)), offset); -+#endif //timezone - } - - void tst_QDateTime::timeZoneAbbreviation() -@@ -2758,6 +2764,7 @@ void tst_QDateTime::timeZoneAbbreviation() - const QString cest(QStringLiteral("CEST")); - #endif - -+#if QT_CONFIG(timezone) - QDateTime dt5(QDate(2013, 1, 1), QTime(0, 0, 0), QTimeZone("Europe/Berlin")); - #ifdef Q_OS_WIN - QEXPECT_FAIL("", "Windows only reports long names (QTBUG-32759)", Continue); -@@ -2768,6 +2775,7 @@ void tst_QDateTime::timeZoneAbbreviation() - QEXPECT_FAIL("", "Windows only reports long names (QTBUG-32759)", Continue); - #endif - QCOMPARE(dt6.timeZoneAbbreviation(), cest); -+#endif /timezone - } - - void tst_QDateTime::getDate() -@@ -3239,6 +3247,7 @@ void tst_QDateTime::daylightTransitions() const - - void tst_QDateTime::timeZones() const - { -+#if QT_CONFIG(timezone) - QTimeZone invalidTz = QTimeZone("Vulcan/ShiKahr"); - QCOMPARE(invalidTz.isValid(), false); - QDateTime invalidDateTime = QDateTime(QDate(2000, 1, 1), QTime(0, 0, 0), invalidTz); -@@ -3431,6 +3440,7 @@ void tst_QDateTime::timeZones() const - QDateTime future(QDate(2015, 1, 1), QTime(0, 0, 0), sgt); - QVERIFY(future.isValid()); - QCOMPARE(future.offsetFromUtc(), 28800); -+#endif //timezone - } - - #if defined(Q_OS_UNIX) -@@ -3452,6 +3462,7 @@ static void setTimeZone(const QByteArray &tz) - - void tst_QDateTime::systemTimeZoneChange() const - { -+#if QT_CONFIG(timezone) - struct ResetTZ { - QByteArray original; - ResetTZ() : original(qgetenv("TZ")) {} -@@ -3480,11 +3491,13 @@ void tst_QDateTime::systemTimeZoneChange() const - QCOMPARE(utcDate.toMSecsSinceEpoch(), utcMsecs); - QCOMPARE(tzDate, QDateTime(QDate(2012, 6, 1), QTime(2, 15, 30), QTimeZone("Australia/Brisbane"))); - QCOMPARE(tzDate.toMSecsSinceEpoch(), tzMsecs); -+#endif //timezone - } - #endif - - void tst_QDateTime::invalid() const - { -+#if QT_CONFIG(timezone) - QDateTime invalidDate = QDateTime(QDate(0, 0, 0), QTime(-1, -1, -1)); - QCOMPARE(invalidDate.isValid(), false); - QCOMPARE(invalidDate.timeSpec(), Qt::LocalTime); -@@ -3500,6 +3513,7 @@ void tst_QDateTime::invalid() const - QDateTime tzDate = invalidDate.toTimeZone(QTimeZone("Europe/Oslo")); - QCOMPARE(tzDate.isValid(), false); - QCOMPARE(tzDate.timeSpec(), Qt::TimeZone); -+#endif //timezone - } - - void tst_QDateTime::macTypes() -diff --git a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp -index 230ae4d8aa..33ebc80f00 100644 ---- a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp -+++ b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp -@@ -46,7 +46,7 @@ - #include - #include - --#if defined(Q_OS_LINUX) && !defined(__UCLIBC__) -+#if defined(Q_OS_LINUX) && !defined(__UCLIBC__) && !defined(Q_OS_OPENHARMONY) - # define QT_USE_FENV - #endif - -@@ -1726,6 +1726,7 @@ void tst_QLocale::formatDateTime() - - void tst_QLocale::formatTimeZone() - { -+#if QT_CONFIG(timezone) - QLocale enUS("en_US"); - - QDateTime dt1(QDate(2013, 1, 1), QTime(1, 0, 0), Qt::OffsetFromUTC, 60 * 60); -@@ -1786,6 +1787,7 @@ void tst_QLocale::formatTimeZone() - // Time on its own will always be current local time zone - QCOMPARE(enUS.toString(QTime(1, 2, 3), "t"), - QDateTime::currentDateTime().timeZoneAbbreviation()); -+#endif //timezone - } - - void tst_QLocale::toDateTime_data() -diff --git a/tests/auto/corelib/tools/tools.pro b/tests/auto/corelib/tools/tools.pro -index f28cf21b8b..1c12619544 100644 ---- a/tests/auto/corelib/tools/tools.pro -+++ b/tests/auto/corelib/tools/tools.pro -@@ -1,71 +1,71 @@ --TEMPLATE=subdirs --SUBDIRS=\ -- collections \ -- containerapisymmetry \ -- qalgorithms \ -- qarraydata \ -- qarraydata_strictiterators \ -- qbitarray \ -- qbytearray \ -- qbytearraylist \ -- qbytearraymatcher \ -- qbytedatabuffer \ -- qcache \ -- qchar \ -- qcollator \ -- qcommandlineparser \ -- qcontiguouscache \ -- qcryptographichash \ -- qdate \ -- qdatetime \ -- qeasingcurve \ -- qexplicitlyshareddatapointer \ -- qfreelist \ -- qhash \ -- qhash_strictiterators \ -- qhashfunctions \ -- qlatin1string \ -- qline \ -- qlinkedlist \ -- qlist \ -- qlist_strictiterators \ -- qlocale \ -- qmakearray \ -- qmap \ -- qmap_strictiterators \ -- qmargins \ -- qmessageauthenticationcode \ -- qpair \ -- qpoint \ -- qpointf \ -- qqueue \ -- qrect \ -- qregexp \ -- qregularexpression \ -- qringbuffer \ -- qscopedpointer \ -- qscopedvaluerollback \ -- qset \ -- qsharedpointer \ -- qsize \ -- qsizef \ -- qstl \ -- qstring \ -- qstring_no_cast_from_bytearray \ -- qstringapisymmetry \ -- qstringbuilder \ -- qstringiterator \ -- qstringlist \ -- qstringmatcher \ -- qstringref \ -- qstringview \ -- qtextboundaryfinder \ -- qtime \ -- qtimezone \ -- qtimeline \ -- qvarlengtharray \ -- qvector \ -- qvector_strictiterators \ -- qversionnumber -- --darwin: SUBDIRS += qmacautoreleasepool -+TEMPLATE=subdirs -+SUBDIRS=\ -+ collections \ -+ containerapisymmetry \ -+ qalgorithms \ -+ qarraydata \ -+ qarraydata_strictiterators \ -+ qbitarray \ -+ qbytearray \ -+ qbytearraylist \ -+ qbytearraymatcher \ -+ qbytedatabuffer \ -+ qcache \ -+ qchar \ -+ qcollator \ -+ qcommandlineparser \ -+ qcontiguouscache \ -+ qcryptographichash \ -+ qdate \ -+ qdatetime \ -+ qeasingcurve \ -+ qexplicitlyshareddatapointer \ -+ qfreelist \ -+ qhash \ -+ qhash_strictiterators \ -+ qhashfunctions \ -+ qlatin1string \ -+ qline \ -+ qlinkedlist \ -+ qlist \ -+ qlist_strictiterators \ -+ qlocale \ -+ qmakearray \ -+ qmap \ -+ qmap_strictiterators \ -+ qmargins \ -+ qmessageauthenticationcode \ -+ qpair \ -+ qpoint \ -+ qpointf \ -+ qqueue \ -+ qrect \ -+ qregexp \ -+ qregularexpression \ -+ qringbuffer \ -+ qscopedpointer \ -+ qscopedvaluerollback \ -+ qset \ -+ qsharedpointer \ -+ qsize \ -+ qsizef \ -+ qstl \ -+ qstring \ -+ qstring_no_cast_from_bytearray \ -+ qstringapisymmetry \ -+ qstringbuilder \ -+ qstringiterator \ -+ qstringlist \ -+ qstringmatcher \ -+ qstringref \ -+ qstringview \ -+ qtextboundaryfinder \ -+ qtime \ -+ qtimezone \ -+ qtimeline \ -+ qvarlengtharray \ -+ qvector \ -+ qvector_strictiterators \ -+ qversionnumber -+ -+darwin: SUBDIRS += qmacautoreleasepool -\ No newline at end of file -diff --git a/tests/auto/network/access/access.pro b/tests/auto/network/access/access.pro -index b140b5e9f2..a0abd994b0 100644 ---- a/tests/auto/network/access/access.pro -+++ b/tests/auto/network/access/access.pro -@@ -1,25 +1,29 @@ --TEMPLATE=subdirs --SUBDIRS=\ -- qnetworkdiskcache \ -- qnetworkcookiejar \ -- qnetworkaccessmanager \ -- qnetworkcookie \ -- qnetworkrequest \ -- qhttpnetworkconnection \ -- qnetworkreply \ -- spdy \ -- qnetworkcachemetadata \ -- qftp \ -- qhttpnetworkreply \ -- qabstractnetworkcache \ -- hpack \ -- http2 \ -- hsts -- --!qtConfig(private_tests): SUBDIRS -= \ -- qhttpnetworkconnection \ -- qhttpnetworkreply \ -- qftp \ -- hpack \ -- http2 \ -- hsts -+TEMPLATE=subdirs -+SUBDIRS=\ -+ qnetworkdiskcache \ -+ qnetworkcookiejar \ -+ qnetworkaccessmanager \ -+ qnetworkcookie \ -+ qnetworkrequest \ -+ qhttpnetworkconnection \ -+ qnetworkreply \ -+ spdy \ -+ qnetworkcachemetadata \ -+ qftp \ -+ qhttpnetworkreply \ -+ qabstractnetworkcache \ -+ hpack \ -+ http2 \ -+ hsts -+ -+!qtConfig(private_tests): SUBDIRS -= \ -+ qhttpnetworkconnection \ -+ qhttpnetworkreply \ -+ qftp \ -+ hpack \ -+ http2 \ -+ hsts -+ -+#TODO support http -+SUBDIRS -= qnetworkreply \ -+ spdy -\ No newline at end of file -diff --git a/tests/auto/network/kernel/kernel.pro b/tests/auto/network/kernel/kernel.pro -index 42df80dfa1..c3ef0fa6c7 100644 ---- a/tests/auto/network/kernel/kernel.pro -+++ b/tests/auto/network/kernel/kernel.pro -@@ -1,24 +1,23 @@ --TEMPLATE=subdirs --SUBDIRS=\ -- qdnslookup \ -- qdnslookup_appless \ -- qhostinfo \ -- qnetworkproxyfactory \ -- qauthenticator \ -- qnetworkproxy \ -- qnetworkinterface \ -- qnetworkdatagram \ -- qnetworkaddressentry \ -- qhostaddress \ -- --winrt: SUBDIRS -= \ -- qnetworkproxy \ -- qnetworkproxyfactory \ -- --osx: SUBDIRS -= \ # QTBUG-41847 -- qhostinfo \ -- --!qtConfig(private_tests): SUBDIRS -= \ -- qauthenticator \ -- qhostinfo \ -- -+TEMPLATE=subdirs -+SUBDIRS=\ -+ qdnslookup \ -+ qdnslookup_appless \ -+ qhostinfo \ -+ qnetworkproxyfactory \ -+ qauthenticator \ -+ qnetworkproxy \ -+ qnetworkinterface \ -+ qnetworkdatagram \ -+ qnetworkaddressentry \ -+ qhostaddress \ -+ -+winrt: SUBDIRS -= \ -+ qnetworkproxy \ -+ qnetworkproxyfactory \ -+ -+osx: SUBDIRS -= \ # QTBUG-41847 -+ qhostinfo \ -+ -+!qtConfig(private_tests): SUBDIRS -= \ -+ qauthenticator \ -+ qhostinfo \ -\ No newline at end of file -diff --git a/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp b/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp -index 224e4d61a9..12b0fa4f4c 100644 ---- a/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp -+++ b/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp -@@ -43,7 +43,7 @@ - # endif - #endif - --#ifdef Q_OS_ANDROID -+#if defined(Q_OS_ANDROID) || defined(Q_OS_OPENHARMONY) - # include - #endif - -@@ -382,7 +382,7 @@ void tst_QHostAddress::assignment() - QCOMPARE(address, QHostAddress("::1")); - - // WinRT does not support sockaddr_in --#ifndef Q_OS_WINRT -+#if !defined(Q_OS_WINRT) - QHostAddress addr("4.2.2.1"); - sockaddr_in sockAddr; - sockAddr.sin_family = AF_INET; -diff --git a/tests/auto/other/toolsupport/tst_toolsupport.cpp b/tests/auto/other/toolsupport/tst_toolsupport.cpp -index f31a755f9e..1cb8f94e55 100644 ---- a/tests/auto/other/toolsupport/tst_toolsupport.cpp -+++ b/tests/auto/other/toolsupport/tst_toolsupport.cpp -@@ -140,8 +140,10 @@ void tst_toolsupport::offsets_data() - << pmm_to_offsetof(&QDateTimePrivate::m_status) << 8 << 8; - QTest::newRow("QDateTimePrivate::m_offsetFromUtc") - << pmm_to_offsetof(&QDateTimePrivate::m_offsetFromUtc) << 12 << 12; -+#if QT_CONFIG(timezone) - QTest::newRow("QDateTimePrivate::m_timeZone") - << pmm_to_offsetof(&QDateTimePrivate::m_timeZone) << 20 << 24; -+#endif //timezone - } - #endif // RUN_MEMBER_OFFSET_TEST - } -diff --git a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp -index 710f26b72d..d77e95db9e 100644 ---- a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp -+++ b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp -@@ -4124,6 +4124,7 @@ void tst_QSqlQuery::QTBUG_2192() - - void tst_QSqlQuery::QTBUG_36211() - { -+#if QT_CONFIG(timezone) - QFETCH( QString, dbName ); - QSqlDatabase db = QSqlDatabase::database( dbName ); - CHECK_DATABASE( db ); -@@ -4159,6 +4160,7 @@ void tst_QSqlQuery::QTBUG_36211() - } - } - } -+#endif //timezone - } - - void tst_QSqlQuery::QTBUG_53969() -@@ -4632,6 +4634,7 @@ void tst_QSqlQuery::QTBUG_57138() - - void tst_QSqlQuery::dateTime_data() - { -+#if QT_CONFIG(timezone) - QTest::addColumn("dbName"); - QTest::addColumn("tableName"); - QTest::addColumn("createTableString"); -@@ -4680,6 +4683,7 @@ void tst_QSqlQuery::dateTime_data() - << dbName << tableNameDate << QStringLiteral(" (dt DATE)") - << dateTimes << expectedDateTimes; - } -+#endif //timezone - } - - void tst_QSqlQuery::dateTime() diff --git a/patch/v5.12.12/qtconnectivity.patch b/patch/v5.12.12/qtconnectivity.patch deleted file mode 100644 index ecdefb517dc5f268d9ef2b557201c3bfe87368fc..0000000000000000000000000000000000000000 --- a/patch/v5.12.12/qtconnectivity.patch +++ /dev/null @@ -1,8613 +0,0 @@ -diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro -index 0b253942..7525f76b 100644 ---- a/src/bluetooth/bluetooth.pro -+++ b/src/bluetooth/bluetooth.pro -@@ -155,6 +155,21 @@ qtConfig(bluez) { - - PRIVATE_HEADERS += qlowenergycontroller_android_p.h \ - qbluetoothsocket_android_p.h -+} else:openharmony { -+ include(openharmony/openharmony.pri) -+ DEFINES += QT_OPENHARMONY_BLUETOOTH -+ -+ SOURCES += \ -+ qbluetoothserver_ohos.cpp \ -+ qbluetoothsocket_ohos.cpp \ -+ qbluetoothlocaldevice_ohos.cpp \ -+ qbluetoothserviceinfo_ohos.cpp \ -+ qlowenergycontroller_ohos.cpp \ -+ qbluetoothdevicediscoveryagent_ohos.cpp \ -+ qbluetoothservicediscoveryagent_ohos.cpp -+ -+ PRIVATE_HEADERS += qbluetoothsocket_ohos_p.h \ -+ qlowenergycontroller_ohos_p.h - } else:osx { - QT_PRIVATE = concurrent - DEFINES += QT_OSX_BLUETOOTH -diff --git a/src/bluetooth/openharmony/bluetoothdatareceiver.cpp b/src/bluetooth/openharmony/bluetoothdatareceiver.cpp -new file mode 100644 -index 00000000..36a12131 ---- /dev/null -+++ b/src/bluetooth/openharmony/bluetoothdatareceiver.cpp -@@ -0,0 +1,89 @@ -+#include -+ -+#include "openharmony/bluetoothdatareceiver_p.h" -+#include "qbluetoothsocket_ohos_p.h" -+#include "qopenharmonydefines.h" -+#include "qopenharmonyjsenvironment.h" -+ -+QT_BEGIN_NAMESPACE -+ -+Q_DECLARE_LOGGING_CATEGORY(QT_BT_OPENHARMONY) -+ -+static napi_value socketDataAvailable(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 2; -+ napi_value args[2]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 2) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments in discoveryResult"); -+ return nullptr; -+ } -+ -+ int64_t value0 = qJs::getInt64(args[0]); -+ QByteArray value1 = qJs::getByteArray(args[1]); -+ -+ -+ BluetoothDataReceiver *receiver = reinterpret_cast(value0); -+ if (receiver != nullptr) { -+ receiver->jsReadyRead(value1); -+ } -+ -+ return nullptr; -+} -+ -+napi_value BluetoothDataReceiver::init(napi_env env, napi_value exports) -+{ -+ napi_property_descriptor desc[] ={ -+ DECLARE_NAPI_FUNCTION("socketDataAvailable", socketDataAvailable), -+ }; -+ NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); -+ return nullptr; -+} -+ -+ -+BluetoothDataReceiver::BluetoothDataReceiver(QBluetoothSocketPrivateOpenHarmony *socket) -+ : QObject(), m_socket_p(socket) -+{ -+} -+ -+bool BluetoothDataReceiver::start() -+{ -+ QMutexLocker lock(&m_mutex); -+ -+ return m_socket_p->start(); -+} -+ -+qint64 BluetoothDataReceiver::bytesAvailable() const -+{ -+ QMutexLocker locker(&m_mutex); -+ return m_socket_p->buffer.size(); -+} -+ -+bool BluetoothDataReceiver::canReadLine() const -+{ -+ QMutexLocker locker(&m_mutex); -+ return m_socket_p->buffer.canReadLine(); -+} -+ -+qint64 BluetoothDataReceiver::readData(char *data, qint64 maxSize) -+{ -+ QMutexLocker locker(&m_mutex); -+ -+ if (!m_socket_p->buffer.isEmpty()) -+ return m_socket_p->buffer.read(data, maxSize); -+ -+ return 0; -+} -+ -+//inside the js thread -+void BluetoothDataReceiver::jsReadyRead(const QByteArray &data) -+{ -+ QMutexLocker lock(&m_mutex); -+ int bufferLength = data.length(); -+ char *writePtr = m_socket_p->buffer.reserve(bufferLength); -+ memcpy(writePtr, data.constData(), bufferLength); -+ emit dataAvailable(); -+} -+ -+QT_END_NAMESPACE -diff --git a/src/bluetooth/openharmony/bluetoothdatareceiver_p.h b/src/bluetooth/openharmony/bluetoothdatareceiver_p.h -new file mode 100644 -index 00000000..5a2d24cf ---- /dev/null -+++ b/src/bluetooth/openharmony/bluetoothdatareceiver_p.h -@@ -0,0 +1,36 @@ -+#ifndef BLUETOOTHDATARECEIVER_P_H -+#define BLUETOOTHDATARECEIVER_P_H -+ -+ -+#include -+#include -+#include -+QT_BEGIN_NAMESPACE -+ -+class QBluetoothSocketPrivateOpenHarmony; -+ -+class BluetoothDataReceiver : public QObject -+{ -+ Q_OBJECT -+public: -+ explicit BluetoothDataReceiver(QBluetoothSocketPrivateOpenHarmony *socket_p); -+ -+ qint64 bytesAvailable() const; -+ bool canReadLine() const; -+ bool start(); -+ -+ qint64 readData(char *data, qint64 maxSize); -+ void jsReadyRead(const QByteArray &buffer); -+ -+ static napi_value init(napi_env env, napi_value exports); -+signals: -+ void dataAvailable(); -+ -+private: -+ QBluetoothSocketPrivateOpenHarmony *m_socket_p; -+ mutable QMutex m_mutex; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // BLUETOOTHDATARECEIVER_P_H -diff --git a/src/bluetooth/openharmony/lowenergynotificationhub.cpp b/src/bluetooth/openharmony/lowenergynotificationhub.cpp -new file mode 100644 -index 00000000..e757d324 ---- /dev/null -+++ b/src/bluetooth/openharmony/lowenergynotificationhub.cpp -@@ -0,0 +1,367 @@ -+#include "lowenergynotificationhub_p.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+typedef QHash HubMapType; -+Q_GLOBAL_STATIC(HubMapType, hubMap) -+ -+static QReadWriteLock lock; -+ -+Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID) -+ -+LowEnergyNotificationHub::LowEnergyNotificationHub(const QBluetoothAddress &remote, -+ bool isPeripheral, QObject *parent) -+ : QObject(parent), jsToCtoken(0) -+{ -+ QSharedPointer jsBluetoothManager = qJsObjectLoader->create("JsBluetoothManager"); -+ if (jsBluetoothManager.isNull() || !jsBluetoothManager->isValid()) -+ return; -+ -+ static int index = 0; -+ QString name = QString("QtLowEnergyNotificationHub%1").arg(index++); -+ bool result = false; -+ if (isPeripheral) { -+ result = jsBluetoothManager->call("createBluetoothGattServer", name); -+ } else { -+ result = jsBluetoothManager->call("createBluetoothLE", name, remote.toString()); -+ } -+ if (result) { -+ m_jsBluetoothLe = qJsObjectLoader->create(name); -+ if (m_jsBluetoothLe.isNull() || !m_jsBluetoothLe->isValid()) -+ return; -+ } -+ -+ lock.lockForWrite(); -+ -+ while (true) { -+ jsToCtoken = QRandomGenerator::global()->generate(); -+ if (!hubMap()->contains(jsToCtoken)) -+ break; -+ } -+ -+ hubMap()->insert(jsToCtoken, this); -+ m_jsBluetoothLe->callWithoutReturn("setQtObject", jsToCtoken); -+ lock.unlock(); -+} -+ -+LowEnergyNotificationHub::~LowEnergyNotificationHub() -+{ -+ lock.lockForWrite(); -+ hubMap()->remove(jsToCtoken); -+ lock.unlock(); -+} -+ -+// runs in Js thread -+static napi_value lowEnergy_connectionChanged(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 3; -+ napi_value args[3]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 3) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments in lowEnergy_connectionChange"); -+ return nullptr; -+ } -+ -+ int qtObject = qJs::getInt32(args[0]); -+ int errorCode = qJs::getInt32(args[1]); -+ int newState = qJs::getInt32(args[2]); -+ -+ lock.lockForRead(); -+ LowEnergyNotificationHub *hub = hubMap()->value(qtObject); -+ lock.unlock(); -+ if (!hub) -+ return nullptr; -+ -+ QMetaObject::invokeMethod(hub, "connectionUpdated", Qt::QueuedConnection, -+ Q_ARG(QLowEnergyController::ControllerState, -+ (QLowEnergyController::ControllerState)newState), -+ Q_ARG(QLowEnergyController::Error, -+ (QLowEnergyController::Error)errorCode)); -+ return nullptr; -+} -+ -+static napi_value lowEnergy_servicesDiscovered(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 3; -+ napi_value args[3]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 3) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments in lowEnergy_connectionChange"); -+ return nullptr; -+ } -+ -+ int qtObject = qJs::getInt32(args[0]); -+ QString uuids = qJs::getString(args[1]); -+ int errorCode = qJs::getInt32(args[2]); -+ -+ lock.lockForRead(); -+ LowEnergyNotificationHub *hub = hubMap()->value(qtObject); -+ lock.unlock(); -+ if (!hub) -+ return nullptr; -+ -+ QMetaObject::invokeMethod(hub, "servicesDiscovered", Qt::QueuedConnection, -+ Q_ARG(QLowEnergyController::Error, -+ (QLowEnergyController::Error)errorCode), -+ Q_ARG(QString, uuids)); -+ return nullptr; -+} -+ -+static napi_value lowEnergy_characteristicRead(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 6; -+ napi_value args[6]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 6) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments in lowEnergy_characteristicRead"); -+ return nullptr; -+ } -+ -+ int qtObject = qJs::getInt32(args[0]); -+ QBluetoothUuid serviceUuid = QBluetoothUuid(qJs::getString(args[1])); -+ int handle = qJs::getInt32(args[2]); -+ QBluetoothUuid charUuid = QBluetoothUuid(qJs::getString(args[3])); -+ int properties = qJs::getInt32(args[4]); -+ QByteArray payload = qJs::getByteArray(args[5]); -+ lock.lockForRead(); -+ LowEnergyNotificationHub *hub = hubMap()->value(qtObject); -+ lock.unlock(); -+ if (!hub) -+ return nullptr; -+ -+ QMetaObject::invokeMethod(hub, "characteristicRead", Qt::QueuedConnection, -+ Q_ARG(QBluetoothUuid, serviceUuid), -+ Q_ARG(int, handle), -+ Q_ARG(QBluetoothUuid, charUuid), -+ Q_ARG(int, properties), -+ Q_ARG(QByteArray, payload)); -+ return nullptr; -+ -+} -+ -+static napi_value lowEnergy_descriptorRead(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 6; -+ napi_value args[6]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 6) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments in lowEnergy_descriptorRead"); -+ return nullptr; -+ } -+ -+ int qtObject = qJs::getInt32(args[0]); -+ QBluetoothUuid serviceUuid = QBluetoothUuid(qJs::getString(args[1])); -+ QBluetoothUuid charUuid = QBluetoothUuid(qJs::getString(args[2])); -+ QBluetoothUuid descUuid = QBluetoothUuid(qJs::getString(args[3])); -+ int handle = qJs::getInt32(args[4]); -+ QByteArray payload = qJs::getByteArray(args[5]); -+ -+ lock.lockForRead(); -+ LowEnergyNotificationHub *hub = hubMap()->value(qtObject); -+ lock.unlock(); -+ if (!hub) -+ return nullptr; -+ -+ -+ QMetaObject::invokeMethod(hub, "descriptorRead", Qt::QueuedConnection, -+ Q_ARG(QBluetoothUuid, serviceUuid), -+ Q_ARG(QBluetoothUuid, charUuid), -+ Q_ARG(int, handle), -+ Q_ARG(QBluetoothUuid, descUuid), -+ Q_ARG(QByteArray, payload)); -+} -+ -+static napi_value lowEnergy_serverDescriptorWritten(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 5; -+ napi_value args[5]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 5) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments in lowEnergy_serverDescriptorWritten"); -+ return nullptr; -+ } -+ -+ int qtObject = qJs::getInt32(args[0]); -+ QBluetoothUuid serviceUuid = QBluetoothUuid(qJs::getString(args[1])); -+ QBluetoothUuid charUuid = QBluetoothUuid(qJs::getString(args[2])); -+ QBluetoothUuid descUuid = QBluetoothUuid(qJs::getString(args[3])); -+ QByteArray payload = qJs::getByteArray(args[4]); -+ lock.lockForRead(); -+ LowEnergyNotificationHub *hub = hubMap()->value(qtObject); -+ lock.unlock(); -+ if (!hub) -+ return nullptr; -+ -+ QMetaObject::invokeMethod(hub, "serverDescriptorWritten", Qt::QueuedConnection, -+ Q_ARG(QBluetoothUuid, serviceUuid), -+ Q_ARG(QBluetoothUuid, charUuid), -+ Q_ARG(QBluetoothUuid, descUuid), -+ Q_ARG(QByteArray, payload)); -+} -+ -+static napi_value characteristicChanged(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 3; -+ napi_value args[3]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 3) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments in characteristicChanged"); -+ return nullptr; -+ } -+ -+ int qtObject = qJs::getInt32(args[0]); -+ -+ lock.lockForRead(); -+ LowEnergyNotificationHub *hub = hubMap()->value(qtObject); -+ lock.unlock(); -+ if (!hub) -+ return nullptr; -+ -+ int charHandle = qJs::getInt32(args[1]); -+ QByteArray payload = qJs::getByteArray(args[2]); -+ -+ -+ QMetaObject::invokeMethod(hub, "characteristicChanged", Qt::QueuedConnection, -+ Q_ARG(int, charHandle), Q_ARG(QByteArray, payload)); -+} -+ -+static napi_value lowEnergy_serverCharacteristicChanged(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 4; -+ napi_value args[4]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 4) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments in characteristicChanged"); -+ return nullptr; -+ } -+ -+ int qtObject = qJs::getInt32(args[0]); -+ lock.lockForRead(); -+ LowEnergyNotificationHub *hub = hubMap()->value(qtObject); -+ lock.unlock(); -+ if (!hub) -+ return nullptr; -+ -+ QBluetoothUuid serviceUuid = QBluetoothUuid(qJs::getString(args[1])); -+ QBluetoothUuid charUuid = QBluetoothUuid(qJs::getString(args[2])); -+ QByteArray payload = qJs::getByteArray(args[3]); -+ -+ QMetaObject::invokeMethod(hub, "serverCharacteristicChanged", Qt::QueuedConnection, -+ Q_ARG(QBluetoothUuid, serviceUuid), -+ Q_ARG(QBluetoothUuid, charUuid), -+ Q_ARG(QByteArray, payload)); -+} -+ -+static napi_value lowEnergy_serviceDetailDiscoveryFinished(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 4; -+ napi_value args[4]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 4) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments in characteristicChanged"); -+ return nullptr; -+ } -+ -+ int qtObject = qJs::getInt32(args[0]); -+ lock.lockForRead(); -+ LowEnergyNotificationHub *hub = hubMap()->value(qtObject); -+ lock.unlock(); -+ if (!hub) -+ return nullptr; -+ -+ QString serviceUuid = qJs::getString(args[1]); -+ int startHandle = qJs::getInt32(args[2]); -+ int endHandle = qJs::getInt32(args[3]); -+ -+ QMetaObject::invokeMethod(hub, "serviceDetailsDiscoveryFinished", -+ Qt::QueuedConnection, -+ Q_ARG(QString, serviceUuid), -+ Q_ARG(int, startHandle), -+ Q_ARG(int, endHandle)); -+} -+ -+static napi_value lowEnergy_serviceError(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 3; -+ napi_value args[3]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 3) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments in lowEnergy_serviceError"); -+ return nullptr; -+ } -+ -+ int qtObject = qJs::getInt32(args[0]); -+ lock.lockForRead(); -+ LowEnergyNotificationHub *hub = hubMap()->value(qtObject); -+ lock.unlock(); -+ if (!hub) -+ return nullptr; -+ -+ int attributeHandle = qJs::getInt32(args[1]); -+ int errorCode = qJs::getInt32(args[2]); -+ -+ QMetaObject::invokeMethod(hub, "serviceError", Qt::QueuedConnection, -+ Q_ARG(int, attributeHandle), -+ Q_ARG(QLowEnergyService::ServiceError, -+ (QLowEnergyService::ServiceError)errorCode)); -+} -+ -+ -+static napi_value lowEnergy_advertisementError(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 2; -+ napi_value args[2]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 2) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments in lowEnergy_advertisementError"); -+ return nullptr; -+ } -+ -+ int qtObject = qJs::getInt32(args[0]); -+ lock.lockForRead(); -+ LowEnergyNotificationHub *hub = hubMap()->value(qtObject); -+ lock.unlock(); -+ if (!hub) -+ return nullptr; -+ -+ QString errorString = qJs::getString(args[1]); -+ -+ QMetaObject::invokeMethod(hub, "advertisementError", Qt::QueuedConnection, -+ Q_ARG(QString, errorString)); -+} -+ -+napi_value LowEnergyNotificationHub::init(napi_env env, napi_value exports) -+{ -+ napi_property_descriptor desc[] ={ -+ DECLARE_NAPI_FUNCTION("connectionChanged", lowEnergy_connectionChanged), -+ DECLARE_NAPI_FUNCTION("servicesDiscovered", lowEnergy_servicesDiscovered), -+ DECLARE_NAPI_FUNCTION("characteristicRead", lowEnergy_characteristicRead), -+ DECLARE_NAPI_FUNCTION("descriptorRead", lowEnergy_descriptorRead), -+ DECLARE_NAPI_FUNCTION("serverCharacteristicChanged", lowEnergy_serverCharacteristicChanged), -+ DECLARE_NAPI_FUNCTION("serverDescriptorWritten", lowEnergy_serverDescriptorWritten), -+ DECLARE_NAPI_FUNCTION("advertisementError", lowEnergy_advertisementError), -+ DECLARE_NAPI_FUNCTION("serviceDetailDiscoveryFinished", lowEnergy_serviceDetailDiscoveryFinished), -+ DECLARE_NAPI_FUNCTION("serviceError", lowEnergy_serviceError) -+ }; -+ NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); -+ return nullptr; -+} -+ -+QT_END_NAMESPACE -diff --git a/src/bluetooth/openharmony/lowenergynotificationhub_p.h b/src/bluetooth/openharmony/lowenergynotificationhub_p.h -new file mode 100644 -index 00000000..ad2f5db1 ---- /dev/null -+++ b/src/bluetooth/openharmony/lowenergynotificationhub_p.h -@@ -0,0 +1,62 @@ -+#ifndef LOWENERGYNOTIFICATIONHUB_H -+#define LOWENERGYNOTIFICATIONHUB_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+QT_BEGIN_NAMESPACE -+class QOpenHarmonyJsObject; -+class LowEnergyNotificationHub : public QObject -+{ -+ Q_OBJECT -+public: -+ explicit LowEnergyNotificationHub(const QBluetoothAddress &remote, bool isPeripheral, -+ QObject *parent = nullptr); -+ ~LowEnergyNotificationHub(); -+ -+ QSharedPointer jsObject() const -+ { -+ return m_jsBluetoothLe; -+ } -+ -+ static napi_value init(napi_env env, napi_value exports); -+ -+signals: -+ void connectionUpdated(QLowEnergyController::ControllerState newState, -+ QLowEnergyController::Error errorCode); -+ void servicesDiscovered(QLowEnergyController::Error errorCode, const QString &uuids); -+ void serviceDetailsDiscoveryFinished(const QString& serviceUuid, -+ int startHandle, int endHandle); -+ void characteristicRead(const QBluetoothUuid &serviceUuid, -+ int handle, const QBluetoothUuid &charUuid, -+ int properties, const QByteArray &data); -+ void descriptorRead(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid, -+ int handle, const QBluetoothUuid &descUuid, const QByteArray &data); -+ void characteristicWritten(int charHandle, const QByteArray &data, -+ QLowEnergyService::ServiceError errorCode); -+ void descriptorWritten(int descHandle, const QByteArray &data, -+ QLowEnergyService::ServiceError errorCode); -+ void serverDescriptorWritten(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &characteristicUuid, const QBluetoothUuid &descriptorUuid, const QByteArray &newValue); -+ void characteristicChanged(int charHandle, const QByteArray &data); -+ void serverCharacteristicChanged(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid, const QByteArray &newValue); -+ void serviceError(int attributeHandle, QLowEnergyService::ServiceError errorCode); -+ void advertisementError(const QString &errorString); -+ -+public slots: -+private: -+ QSharedPointer m_jsBluetoothLe; -+ int jsToCtoken; -+ -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // LOWENERGYNOTIFICATIONHUB_H -+ -diff --git a/src/bluetooth/openharmony/openharmony.pri b/src/bluetooth/openharmony/openharmony.pri -new file mode 100644 -index 00000000..fbc1329e ---- /dev/null -+++ b/src/bluetooth/openharmony/openharmony.pri -@@ -0,0 +1,20 @@ -+PRIVATE_HEADERS += \ -+ $$PWD/openharmonyload_p.h \ -+ $$PWD/bluetoothdatareceiver_p.h \ -+ $$PWD/serveracceptancethread_p.h \ -+ $$PWD/openharmonybroadcastreceiver_p.h \ -+ $$PWD/openharmonyservicediscoveryreceiver_p.h \ -+ $$PWD/openharmonybluetoothdiscoveryreceiver_p.h \ -+ $$PWD/openharmonylocaldevicebroadcastreceiver_p.h \ -+ $$PWD/lowenergynotificationhub_p.h -+ -+SOURCES += \ -+ $$PWD/openharmonyload.cpp \ -+ $$PWD/bluetoothdatareceiver.cpp \ -+ $$PWD/serveracceptancethread.cpp \ -+ $$PWD/openharmonybroadcastreceiver.cpp \ -+ $$PWD/openharmonyservicediscoveryreceiver.cpp \ -+ $$PWD/openharmonybluetoothdiscoveryreceiver.cpp \ -+ $$PWD/openharmonylocaldevicebroadcastreceiver.cpp \ -+ $$PWD/lowenergynotificationhub.cpp -+ -diff --git a/src/bluetooth/openharmony/openharmonybluetoothdiscoveryreceiver.cpp b/src/bluetooth/openharmony/openharmonybluetoothdiscoveryreceiver.cpp -new file mode 100644 -index 00000000..fb2801fb ---- /dev/null -+++ b/src/bluetooth/openharmony/openharmonybluetoothdiscoveryreceiver.cpp -@@ -0,0 +1,88 @@ -+#include "openharmonybluetoothdiscoveryreceiver_p.h" -+#include "qopenharmonydefines.h" -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+static OpenHarmonyBluetoothDiscoveryReceiver *receiver = nullptr; -+ -+static napi_value discoveryResult(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 1; -+ napi_value args[1]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 1) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments in discoveryResult"); -+ return nullptr; -+ } -+ -+ QString target = qJs::getString(args[0]); -+ -+ QMetaObject::invokeMethod(receiver, "received", Qt::QueuedConnection, Q_ARG(QString, target)); -+ return nullptr; -+} -+ -+OpenHarmonyBluetoothDiscoveryReceiver::OpenHarmonyBluetoothDiscoveryReceiver() -+{ -+ receiver = this; -+ m_thread = new ParseThread; -+} -+ -+OpenHarmonyBluetoothDiscoveryReceiver::~OpenHarmonyBluetoothDiscoveryReceiver() -+{ -+ receiver = nullptr; -+} -+ -+napi_value OpenHarmonyBluetoothDiscoveryReceiver::init(napi_env env, napi_value exports) -+{ -+ napi_property_descriptor desc[] ={ -+ DECLARE_NAPI_FUNCTION("discoveryResult", discoveryResult), -+ }; -+ NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); -+ return nullptr; -+} -+ -+void OpenHarmonyBluetoothDiscoveryReceiver::received(const QString &target) -+{ -+ { -+ QMutexLocker locker(&m_mutex); -+ m_targets << target; -+ } -+ if (!m_thread->isRunning()) -+ m_thread->start(); -+} -+ -+void ParseThread::run() -+{ -+ while (true) { -+ QString target; -+ { -+ QMutexLocker locker(&receiver->m_mutex); -+ if (!receiver->m_targets.isEmpty()) -+ target = receiver->m_targets.takeFirst(); -+ } -+ if (!target.isEmpty()) { -+ QSharedPointer jsRemote = qJsObjectLoader->create(target); -+ if (!jsRemote->isValid()) -+ continue; -+ int classOfDevice = jsRemote->call("getClass"); -+ QString name = jsRemote->call("getName"); -+ bool isBle = jsRemote->call("isBLE"); -+ QBluetoothDeviceInfo info(QBluetoothAddress(target), name, classOfDevice); -+ emit receiver->deviceDiscovered(info, isBle); -+ } else { -+ QThread::sleep(1); -+ static int index = 0; -+ index++; -+ if (index > 10) { -+ index = 0; -+ break; -+ } -+ } -+ } -+} -diff --git a/src/bluetooth/openharmony/openharmonybluetoothdiscoveryreceiver_p.h b/src/bluetooth/openharmony/openharmonybluetoothdiscoveryreceiver_p.h -new file mode 100644 -index 00000000..66d2155d ---- /dev/null -+++ b/src/bluetooth/openharmony/openharmonybluetoothdiscoveryreceiver_p.h -@@ -0,0 +1,46 @@ -+#ifndef OPENHARMONYBLUETOOTHDISCOVERYRECEIVER_P_H -+#define OPENHARMONYBLUETOOTHDISCOVERYRECEIVER_P_H -+ -+#include -+#include -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+class QOpenHarmonyJsObject; -+class QBluetoothDeviceInfo; -+ -+class ParseThread : public QThread -+{ -+ Q_OBJECT -+ virtual void run(); -+}; -+ -+ -+class OpenHarmonyBluetoothDiscoveryReceiver : public QObject -+{ -+ Q_OBJECT -+ friend class ParseThread; -+public: -+ OpenHarmonyBluetoothDiscoveryReceiver(); -+ ~OpenHarmonyBluetoothDiscoveryReceiver(); -+ -+ static napi_value init(napi_env env, napi_value exports); -+ -+private slots: -+ void received(const QString &target); -+ -+signals: -+ void deviceDiscovered(const QBluetoothDeviceInfo &info, bool isLeScanResult); -+ void finished(); -+ -+private: -+ QSharedPointer m_jsBluetoothManager; -+ QStringList m_targets; -+ QMutex m_mutex; -+ ParseThread *m_thread; -+}; -+ -+QT_END_NAMESPACE -+#endif // OPENHARMONYBLUETOOTHDISCOVERYRECEIVER_P_H -diff --git a/src/bluetooth/openharmony/openharmonybroadcastreceiver.cpp b/src/bluetooth/openharmony/openharmonybroadcastreceiver.cpp -new file mode 100644 -index 00000000..91767a07 ---- /dev/null -+++ b/src/bluetooth/openharmony/openharmonybroadcastreceiver.cpp -@@ -0,0 +1,51 @@ -+#include -+#include -+#include -+#include -+#include -+ -+#include "qopenharmonydefines.h" -+#include "openharmonybroadcastreceiver_p.h" -+ -+static napi_value servicesReceiver(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 1; -+ napi_value args[1]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 1) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments in discoveryResult"); -+ return nullptr; -+ } -+ -+ int64_t value0 = qJs::getInt64(args[0]); -+ QByteArray value1 = qJs::getByteArray(args[1]); -+ -+ QJsonParseError err; -+ QJsonDocument doc = QJsonDocument::fromJson(value1, &err); -+ if (QJsonParseError::NoError != err.error) -+ return nullptr; -+ -+ -+ OPenHarmonyBroadcastReceiver *jsThread = reinterpret_cast(value0); -+ if (jsThread != nullptr) { -+ QMetaObject::invokeMethod(jsThread, "onReceive", Qt::QueuedConnection, Q_ARG(QJsonObject, doc.object())); -+ } -+ -+ return nullptr; -+} -+ -+OPenHarmonyBroadcastReceiver::OPenHarmonyBroadcastReceiver(QObject *parent) -+ : QObject{parent} -+{ -+ -+} -+ -+napi_value OPenHarmonyBroadcastReceiver::init(napi_env env, napi_value exports) -+{ -+ napi_property_descriptor desc[] ={ -+ DECLARE_NAPI_FUNCTION("servicesReceiver", servicesReceiver), -+ }; -+ NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); -+ return nullptr; -+} -diff --git a/src/bluetooth/openharmony/openharmonybroadcastreceiver_p.h b/src/bluetooth/openharmony/openharmonybroadcastreceiver_p.h -new file mode 100644 -index 00000000..29377e33 ---- /dev/null -+++ b/src/bluetooth/openharmony/openharmonybroadcastreceiver_p.h -@@ -0,0 +1,22 @@ -+ -+#ifndef OPENHARMONYBROADCASTRECEIVER_P_H -+#define OPENHARMONYBROADCASTRECEIVER_P_H -+ -+ -+#include -+#include -+#include -+ -+class OPenHarmonyBroadcastReceiver : public QObject -+{ -+ Q_OBJECT -+public: -+ explicit OPenHarmonyBroadcastReceiver(QObject *parent = nullptr); -+ static napi_value init(napi_env env, napi_value exports); -+ -+protected: -+ virtual void onReceive(const QJsonObject &json) = 0; -+ virtual void onReceiveLeScan(const QJsonObject &json) = 0; -+}; -+ -+#endif // OPENHARMONYBROADCASTRECEIVER_P_H -diff --git a/src/bluetooth/openharmony/openharmonyload.cpp b/src/bluetooth/openharmony/openharmonyload.cpp -new file mode 100644 -index 00000000..506aed6f ---- /dev/null -+++ b/src/bluetooth/openharmony/openharmonyload.cpp -@@ -0,0 +1,47 @@ -+#include -+#include "qopenharmonydefines.h" -+#include "bluetoothdatareceiver_p.h" -+#include "serveracceptancethread_p.h" -+#include "openharmonybroadcastreceiver_p.h" -+#include "openharmonybluetoothdiscoveryreceiver_p.h" -+#include "lowenergynotificationhub_p.h" -+/* -+ * function for module exports -+ */ -+EXTERN_C_START -+static napi_value Init(napi_env env, napi_value exports) -+{ -+ static bool inited = false; -+ if (!inited) { -+ -+ BluetoothDataReceiver::init(env, exports); -+ ServerAcceptanceThread::init(env, exports); -+ LowEnergyNotificationHub::init(env, exports); -+ OPenHarmonyBroadcastReceiver::init(env, exports); -+ OpenHarmonyBluetoothDiscoveryReceiver::init(env, exports); -+ LOGI("init bluetooth module"); -+ inited = true; -+ } -+ return exports; -+} -+EXTERN_C_END -+ -+/* -+ * Napi Module define -+ */ -+static napi_module openharmonyQtBluetoothModule = { -+ .nm_version = 1, -+ .nm_flags = 0, -+ .nm_filename = nullptr, -+ .nm_register_func = Init, -+ .nm_modname = "openharmony_qt_bluetooth", -+ .nm_priv = ((void*)0), -+ .reserved = { 0 }, -+}; -+/* -+ * Module register function -+ */ -+extern "C" __attribute__((constructor)) void RegisterModule(void) -+{ -+ napi_module_register(&openharmonyQtBluetoothModule); -+} -diff --git a/src/bluetooth/openharmony/openharmonyload_p.h b/src/bluetooth/openharmony/openharmonyload_p.h -new file mode 100644 -index 00000000..ba9bbb47 ---- /dev/null -+++ b/src/bluetooth/openharmony/openharmonyload_p.h -@@ -0,0 +1,5 @@ -+#ifndef OPENHARMONYLOAD_P_H -+#define OPENHARMONYLOAD_P_H -+ -+ -+#endif // OPENHARMONYLOAD_P_H -diff --git a/src/bluetooth/openharmony/openharmonylocaldevicebroadcastreceiver.cpp b/src/bluetooth/openharmony/openharmonylocaldevicebroadcastreceiver.cpp -new file mode 100644 -index 00000000..952e2be8 ---- /dev/null -+++ b/src/bluetooth/openharmony/openharmonylocaldevicebroadcastreceiver.cpp -@@ -0,0 +1,45 @@ -+#include -+#include "openharmonylocaldevicebroadcastreceiver_p.h" -+ -+Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID) -+ -+/* 扫描模式 -+ * -+ * SCAN_MODE_NONE 0 没有扫描模式 -+ * SCAN_MODE_CONNECTABLE 1 可连接扫描模式 -+ * SCAN_MODE_GENERAL_DISCOVERABLE 2 general发现模式 -+ * SCAN_MODE_LIMITED_DISCOVERABLE 3 limited发现模式 -+ * SCAN_MODE_CONNECTABLE_GENERAL_DISCOVERABLE 4 可连接general发现模式 -+ * SCAN_MODE_CONNECTABLE_LIMITED_DISCOVERABLE 5 可连接limited发现模式 -+ */ -+ -+/* 配对状态 -+ * -+ * BOND_STATE_INVALID 0 无效的配对 -+ * BOND_STATE_BONDING 1 正在配对 -+ * BOND_STATE_BONDED 2 已配对 -+ */ -+ -+OPenHarmonyLocalDeviceBroadcastReceiver::OPenHarmonyLocalDeviceBroadcastReceiver(QObject *parent) -+ : OPenHarmonyBroadcastReceiver{parent} -+{ -+ for (int i = 0; i < 6; ++i) { -+ hostModePreset[i] = i; -+ if (i <= 2) -+ bondingModePreset[i] = i; -+ } -+} -+ -+void OPenHarmonyLocalDeviceBroadcastReceiver::onReceive(const QJsonObject &json) -+{ -+ qCDebug(QT_BT_ANDROID) << QStringLiteral("LocalDeviceBroadcastReceiver::onReceive()"); -+ -+ -+} -+ -+bool OPenHarmonyLocalDeviceBroadcastReceiver::pairingConfirmation(bool accept) -+{ -+ Q_UNUSED(accept); -+ return false; -+} -+ -diff --git a/src/bluetooth/openharmony/openharmonylocaldevicebroadcastreceiver_p.h b/src/bluetooth/openharmony/openharmonylocaldevicebroadcastreceiver_p.h -new file mode 100644 -index 00000000..aaae3db7 ---- /dev/null -+++ b/src/bluetooth/openharmony/openharmonylocaldevicebroadcastreceiver_p.h -@@ -0,0 +1,34 @@ -+#ifndef OPENHARMONYLOCALDEVICEBROADCASTRECEIVER_H -+#define OPENHARMONYLOCALDEVICEBROADCASTRECEIVER_H -+ -+#include -+#include -+ -+#include "openharmonybroadcastreceiver_p.h" -+ -+class OPenHarmonyLocalDeviceBroadcastReceiver : public OPenHarmonyBroadcastReceiver -+{ -+ Q_OBJECT -+ -+public: -+ explicit OPenHarmonyLocalDeviceBroadcastReceiver(QObject *parent = nullptr); -+ bool pairingConfirmation(bool accept); -+ -+public Q_SLOTS: -+ void onReceive(const QJsonObject &json) override; -+ void onReceiveLeScan(const QJsonObject &json) override {} -+ -+signals: -+ void hostModeStateChanged(QBluetoothLocalDevice::HostMode state); -+ void pairingStateChanged(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing); -+ void connectDeviceChanges(const QBluetoothAddress &address, bool isConnectEvent); -+ void pairingDisplayConfirmation(const QBluetoothAddress &address, const QString& pin); -+ void pairingDisplayPinCode(const QBluetoothAddress &address, const QString& pin); -+ -+private: -+ int previousScanMode; -+ int bondingModePreset[3]; -+ int hostModePreset[6]; -+}; -+ -+#endif // OPENHARMONYLOCALDEVICEBROADCASTRECEIVER_H -diff --git a/src/bluetooth/openharmony/openharmonyservicediscoveryreceiver.cpp b/src/bluetooth/openharmony/openharmonyservicediscoveryreceiver.cpp -new file mode 100644 -index 00000000..d9cea954 ---- /dev/null -+++ b/src/bluetooth/openharmony/openharmonyservicediscoveryreceiver.cpp -@@ -0,0 +1,20 @@ -+#include -+#include "openharmonyservicediscoveryreceiver_p.h" -+ -+OPenHarmonyServiceDiscoveryReceiver::OPenHarmonyServiceDiscoveryReceiver(QObject *parent) -+ : OPenHarmonyBroadcastReceiver(parent) -+{ -+ -+} -+ -+void OPenHarmonyServiceDiscoveryReceiver::onReceive(const QJsonObject &json) -+{ -+ qWarning() << "<<<<<<<<--------::" << json; -+} -+ -+QList OPenHarmonyServiceDiscoveryReceiver::convertParcelableArray(const QJsonObject &json) -+{ -+ QList result; -+ return result; -+} -+ -diff --git a/src/bluetooth/openharmony/openharmonyservicediscoveryreceiver_p.h b/src/bluetooth/openharmony/openharmonyservicediscoveryreceiver_p.h -new file mode 100644 -index 00000000..5ddd0e36 ---- /dev/null -+++ b/src/bluetooth/openharmony/openharmonyservicediscoveryreceiver_p.h -@@ -0,0 +1,27 @@ -+ -+#ifndef OPENHARMONYSERVICEDISCOVERYRECEIVER_H -+#define OPENHARMONYSERVICEDISCOVERYRECEIVER_H -+ -+ -+#include -+#include -+#include -+ -+#include "openharmonybroadcastreceiver_p.h" -+ -+class OPenHarmonyServiceDiscoveryReceiver : public OPenHarmonyBroadcastReceiver -+{ -+ Q_OBJECT -+public: -+ OPenHarmonyServiceDiscoveryReceiver(QObject* parent = nullptr); -+ static QList convertParcelableArray(const QJsonObject &json); -+ -+public Q_SLOTS: -+ void onReceive(const QJsonObject &json) override; -+ void onReceiveLeScan(const QJsonObject &json) override {} -+ -+signals: -+ void uuidFetchFinished(const QBluetoothAddress &addr, const QList &serviceUuid); -+}; -+ -+#endif // OPENHARMONYSERVICEDISCOVERYRECEIVER_H -diff --git a/src/bluetooth/openharmony/serveracceptancethread.cpp b/src/bluetooth/openharmony/serveracceptancethread.cpp -new file mode 100644 -index 00000000..d9e11afe ---- /dev/null -+++ b/src/bluetooth/openharmony/serveracceptancethread.cpp -@@ -0,0 +1,201 @@ -+#include -+#include -+#include "QtCore/qopenharmonydefines.h" -+#include "QtCore/qopenharmonyjsenvironment.h" -+#include "QtCore/qopenharmonyjsobjectloader.h" -+#include "openharmony/serveracceptancethread_p.h" -+ -+Q_DECLARE_LOGGING_CATEGORY(QT_BT_OPENHARMONY) -+ -+static napi_value occurError(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 2; -+ napi_value args[2]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 2) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments in discoveryResult"); -+ return nullptr; -+ } -+ -+ int64_t value0 = qJs::getInt64(args[0]); -+ int32_t value1 = qJs::getInt32(args[1]); -+ -+ -+ ServerAcceptanceThread *jsThread = reinterpret_cast(value0); -+ if (jsThread != nullptr) { -+ QMetaObject::invokeMethod(jsThread, "jsThreadErrorOccurred", -+ Qt::QueuedConnection, Q_ARG(int, int(value1))); -+ } -+ -+ return nullptr; -+} -+ -+static napi_value acceptClientSocket(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 2; -+ napi_value args[2]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 2) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments in discoveryResult"); -+ return nullptr; -+ } -+ -+ int64_t value0 = qJs::getInt64(args[0]); -+ int32_t value1 = qJs::getInt64(args[1]); -+ -+ -+ ServerAcceptanceThread *jsThread = reinterpret_cast(value0); -+ if (jsThread != nullptr) { -+ QMetaObject::invokeMethod(jsThread, "jsNewSocket", -+ Qt::QueuedConnection, Q_ARG(int32_t, value1)); -+ } -+ -+ return nullptr; -+} -+ -+ServerAcceptanceThread::ServerAcceptanceThread(QObject *parent) : -+ QObject(parent), maxPendingConnections(1) -+{ -+ qRegisterMetaType(); -+ m_btSrv = qJsObjectLoader->create("JsBluetoothServer"); -+} -+ -+ServerAcceptanceThread::~ServerAcceptanceThread() -+{ -+ Q_ASSERT(!isRunning()); -+ QMutexLocker lock(&m_mutex); -+ shutdownPendingConnections(); -+ -+ QString tmpName = m_serviceName + m_uuid.toString(); -+ bool result = m_btSrv->call("destroyServer", tmpName); -+ if (!result) { -+ jsThreadErrorOccurred(1); -+ return; -+ } -+} -+ -+void ServerAcceptanceThread::setServiceDetails(const QBluetoothUuid &uuid, -+ const QString &serviceName, -+ QBluetooth::SecurityFlags securityFlags) -+{ -+ QMutexLocker lock(&m_mutex); -+ m_uuid = uuid; -+ m_serviceName = serviceName; -+ secFlags = securityFlags; -+} -+ -+napi_value ServerAcceptanceThread::init(napi_env env, napi_value exports) -+{ -+ napi_property_descriptor desc[] ={ -+ DECLARE_NAPI_FUNCTION("occurError", occurError), -+ DECLARE_NAPI_FUNCTION("acceptClientSocket", acceptClientSocket), -+ }; -+ NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); -+ return nullptr; -+} -+ -+bool ServerAcceptanceThread::hasPendingConnections() const -+{ -+ QMutexLocker lock(&m_mutex); -+ return (pendingSockets.count() > 0); -+} -+ -+int ServerAcceptanceThread::nextPendingConnection() -+{ -+ QMutexLocker lock(&m_mutex); -+ if (pendingSockets.isEmpty()) -+ return -1; -+ else -+ return pendingSockets.takeFirst(); -+} -+ -+void ServerAcceptanceThread::setMaxPendingConnections(int maximumCount) -+{ -+ QMutexLocker lock(&m_mutex); -+ maxPendingConnections = maximumCount; -+} -+ -+void ServerAcceptanceThread::run() -+{ -+ QMutexLocker lock(&m_mutex); -+ -+ if (!validSetup()) { -+ qCWarning(QT_BT_OPENHARMONY) << "Invalid Server Socket setup"; -+ return; -+ } -+ -+ if (isRunning()) { -+ stop(); -+ shutdownPendingConnections(); -+ } -+ -+ QString tmpName = m_serviceName + m_uuid.toString(); -+ bool result = m_btSrv->call("createServer", tmpName, QVariant(qlonglong(static_cast(this)))); -+ if (!result) { -+ jsThreadErrorOccurred(1); -+ return; -+ } -+ -+ jsThread = qJsObjectLoader->create(tmpName); -+ if (!jsThread->isValid()) -+ return; -+ -+ QString tempUuid = m_uuid.toString(); -+ tempUuid.chop(1); //remove trailing '}' -+ tempUuid.remove(0,1); //remove first '{' -+ -+ bool isSecure = !(secFlags == QBluetooth::NoSecurity); -+ jsThread->callWithoutReturn("listen", tempUuid, m_serviceName, isSecure); -+} -+ -+void ServerAcceptanceThread::stop() -+{ -+ if (jsThread->isValid()) { -+ qCDebug(QT_BT_OPENHARMONY) << "Closing server socket"; -+ jsThread->callWithoutReturn("close"); -+ } -+} -+ -+bool ServerAcceptanceThread::isRunning() -+{ -+ if (!jsThread.isNull() && jsThread->isValid()) -+ return jsThread->call("isAlive"); -+ -+ return false; -+} -+ -+void ServerAcceptanceThread::jsThreadErrorOccurred(int errorCode) -+{ -+ qCDebug(QT_BT_OPENHARMONY) << "jsThread error:" << errorCode; -+ emit error(QBluetoothServer::InputOutputError); -+} -+ -+void ServerAcceptanceThread::jsNewSocket(int32_t s) -+{ -+ QMutexLocker lock(&m_mutex); -+ if (-1 == s) -+ return; -+ -+ if (pendingSockets.count() < maxPendingConnections) { -+ qCDebug(QT_BT_OPENHARMONY) << "New incoming js socket detected"; -+ pendingSockets.append(s); -+ emit newConnection(); -+ } else { -+ qCWarning(QT_BT_OPENHARMONY) << "Refusing connection due to limited pending socket queue"; -+ } -+} -+ -+bool ServerAcceptanceThread::validSetup() const -+{ -+ return (!m_uuid.isNull() && !m_serviceName.isEmpty()); -+} -+ -+void ServerAcceptanceThread::shutdownPendingConnections() -+{ -+ while (!pendingSockets.isEmpty()) { -+ int socket = pendingSockets.takeFirst(); -+ jsThread->callWithoutReturn("closeClient", socket); -+ } -+} -diff --git a/src/bluetooth/openharmony/serveracceptancethread_p.h b/src/bluetooth/openharmony/serveracceptancethread_p.h -new file mode 100644 -index 00000000..ae54df82 ---- /dev/null -+++ b/src/bluetooth/openharmony/serveracceptancethread_p.h -@@ -0,0 +1,52 @@ -+#ifndef SERVERACCEPTANCETHREAD_P_H -+#define SERVERACCEPTANCETHREAD_P_H -+ -+#include -+#include -+#include -+#include -+ -+#include "qbluetooth.h" -+#include -+ -+class ServerAcceptanceThread : public QObject -+{ -+ Q_OBJECT -+public: -+ explicit ServerAcceptanceThread(QObject *parent = nullptr); -+ ~ServerAcceptanceThread(); -+ void setServiceDetails(const QBluetoothUuid &uuid, const QString &serviceName, -+ QBluetooth::SecurityFlags securityFlags); -+ -+ static napi_value init(napi_env env, napi_value exports); -+ -+ int nextPendingConnection(); -+ bool hasPendingConnections() const; -+ void setMaxPendingConnections(int maximumCount); -+ -+ Q_INVOKABLE void jsThreadErrorOccurred(int errorCode); -+ Q_INVOKABLE void jsNewSocket(int32_t socket); -+ -+ void run(); -+ void stop(); -+ bool isRunning(); -+ -+signals: -+ void newConnection(); -+ void error(QBluetoothServer::Error); -+ -+private: -+ bool validSetup() const; -+ void shutdownPendingConnections(); -+ -+ QList pendingSockets; -+ mutable QMutex m_mutex; -+ QString m_serviceName; -+ QBluetoothUuid m_uuid; -+ int maxPendingConnections; -+ QBluetooth::SecurityFlags secFlags; -+ QSharedPointer m_btSrv; -+ QSharedPointer jsThread; -+ -+}; -+#endif // SERVERACCEPTANCETHREAD_P_H -diff --git a/src/bluetooth/qbluetooth.cpp b/src/bluetooth/qbluetooth.cpp -index 6eab11fb..49d27923 100644 ---- a/src/bluetooth/qbluetooth.cpp -+++ b/src/bluetooth/qbluetooth.cpp -@@ -102,5 +102,6 @@ Q_LOGGING_CATEGORY(QT_BT, "qt.bluetooth") - Q_LOGGING_CATEGORY(QT_BT_ANDROID, "qt.bluetooth.android") - Q_LOGGING_CATEGORY(QT_BT_BLUEZ, "qt.bluetooth.bluez") - Q_LOGGING_CATEGORY(QT_BT_WINRT, "qt.bluetooth.winrt") -+Q_LOGGING_CATEGORY(QT_BT_OPENHARMONY, "qt.bluetooth.openharmony") - - QT_END_NAMESPACE -diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_ohos.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_ohos.cpp -new file mode 100644 -index 00000000..6cf0e437 ---- /dev/null -+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_ohos.cpp -@@ -0,0 +1,276 @@ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include "qbluetoothdevicediscoveryagent_p.h" -+#include "openharmony/openharmonybluetoothdiscoveryreceiver_p.h" -+ -+QT_BEGIN_NAMESPACE -+ -+Q_DECLARE_LOGGING_CATEGORY(QT_BT_OPENHARMONY) -+ -+enum { -+ NoScanActive = 0, -+ SDPScanActive = 1, -+ BtleScanActive = 2 -+}; -+ -+enum { -+ STATE_OFF = 0, /* 表示蓝牙已关闭 */ -+ STATE_TURNING_ON, /* 表示蓝牙正在打开 */ -+ STATE_ON, /* 表示蓝牙已打开 */ -+ STATE_TURNING_OFF, /* 表示蓝牙正在关闭 */ -+ STATE_BLE_TURNING_ON, /* 表示蓝牙正在打开LE-only模式 */ -+ STATE_BLE_ON, /* 表示蓝牙正处于LE-only模式 */ -+ STATE_BLE_TURNING_OFF /* 表示蓝牙正在关闭LE-only模式 */ -+}; -+ -+QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( -+ const QBluetoothAddress &deviceAdapter, QBluetoothDeviceDiscoveryAgent *parent) : -+ inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry), -+ lastError(QBluetoothDeviceDiscoveryAgent::NoError), -+ receiver(0), -+ m_adapterAddress(deviceAdapter), -+ m_active(NoScanActive), -+ leScanTimeout(Q_NULLPTR), -+ pendingCancel(false), -+ pendingStart(false), -+ lowEnergySearchTimeout(25000), -+ q_ptr(parent) -+{ -+ m_jsBluetoothManager = qJsObjectLoader->create("JsBluetoothManager"); -+} -+ -+QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() -+{ -+ if (receiver) { -+ delete receiver; -+ } -+} -+ -+bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const -+{ -+ if (pendingStart) -+ return true; -+ if (pendingCancel) -+ return false; -+ return m_active != NoScanActive; -+} -+ -+QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods() -+{ -+ return (LowEnergyMethod | ClassicMethod); -+} -+ -+void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods) -+{ -+ requestedMethods = methods; -+ -+ if (pendingCancel) { -+ pendingStart = true; -+ return; -+ } -+ -+ Q_Q(QBluetoothDeviceDiscoveryAgent); -+ if (!m_jsBluetoothManager->call("isSupport")) { -+ qCWarning(QT_BT_OPENHARMONY) << "Device does not support Bluetooth"; -+ lastError = QBluetoothDeviceDiscoveryAgent::InputOutputError; -+ errorString = QBluetoothDeviceDiscoveryAgent::tr("Device does not support Bluetooth"); -+ emit q->error(lastError); -+ return; -+ } -+ -+ if (!m_adapterAddress.isNull() && m_jsBluetoothManager->call("getAddress") != m_adapterAddress.toString()) { -+ qCWarning(QT_BT_OPENHARMONY) << "Incorrect local adapter passed."; -+ lastError = QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError; -+ errorString = QBluetoothDeviceDiscoveryAgent::tr("Passed address is not a local device."); -+ emit q->error(lastError); -+ return; -+ } -+ -+ int state = m_jsBluetoothManager->call("getState"); -+ if (STATE_ON != state) { -+ lastError = QBluetoothDeviceDiscoveryAgent::PoweredOffError; -+ errorString = QBluetoothDeviceDiscoveryAgent::tr("Device is powered off"); -+ emit q->error(lastError); -+ return; -+ } -+ -+ if (!receiver) { -+ receiver = new OpenHarmonyBluetoothDiscoveryReceiver(); -+ qRegisterMetaType(); -+ QObject::connect(receiver, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo,bool)), -+ this, SLOT(processDiscoveredDevices(QBluetoothDeviceInfo,bool))); -+ QObject::connect(receiver, SIGNAL(finished()), this, SLOT(processSdpDiscoveryFinished())); -+ } -+ -+ discoveredDevices.clear(); -+ // by arbitrary definition we run classic search first -+ if (requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod) { -+ const bool success = m_jsBluetoothManager->call("startBluetoothDiscovery"); -+ if (!success) { -+ qCDebug(QT_BT_OPENHARMONY) << "Classic Discovery cannot be started"; -+ if (requestedMethods == QBluetoothDeviceDiscoveryAgent::ClassicMethod) { -+ //only classic discovery requested -> error out -+ lastError = QBluetoothDeviceDiscoveryAgent::InputOutputError; -+ errorString = QBluetoothDeviceDiscoveryAgent::tr("Classic Discovery cannot be started"); -+ emit q->error(lastError); -+ return; -+ } // else fall through to LE discovery -+ } else { -+ m_active = SDPScanActive; -+ qCDebug(QT_BT_OPENHARMONY) -+ << "QBluetoothDeviceDiscoveryAgentPrivate::start() - Classic search successfully started."; -+ return; -+ } -+ } -+ -+ if (requestedMethods & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod) { -+ // LE search only requested or classic discovery failed but lets try LE scan anyway -+ Q_ASSERT(requestedMethods & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); -+ -+ startLowEnergyScan(); -+ } -+} -+ -+void QBluetoothDeviceDiscoveryAgentPrivate::processSdpDiscoveryFinished() -+{ -+ Q_Q(QBluetoothDeviceDiscoveryAgent); -+ emit q->finished(); -+} -+ -+void QBluetoothDeviceDiscoveryAgentPrivate::processDiscoveredDevices(const QBluetoothDeviceInfo &info, bool isLeResult) -+{ -+ // If we have two active agents both receive the same signal. -+ // If this one is not active ignore the device information -+ if (m_active != SDPScanActive && !isLeResult) -+ return; -+ if (m_active != BtleScanActive && isLeResult) -+ return; -+ -+ Q_Q(QBluetoothDeviceDiscoveryAgent); -+ -+ // OpenHarmony Classic scan and LE scan can find the same device under different names -+ // The classic name finds the SDP based device name, the LE scan finds the name in -+ // the advertisement package. -+ // If address is same but name different then we keep both entries. -+ -+ for (int i = 0; i < discoveredDevices.size(); i++) { -+ if (discoveredDevices[i].address() == info.address()) { -+ QBluetoothDeviceInfo::Fields updatedFields = QBluetoothDeviceInfo::Field::None; -+ if (discoveredDevices[i].rssi() != info.rssi()) { -+ qCDebug(QT_BT_OPENHARMONY()) << "Updating RSSI for" << info.address() -+ << info.rssi(); -+ discoveredDevices[i].setRssi(info.rssi()); -+ updatedFields.setFlag(QBluetoothDeviceInfo::Field::RSSI); -+ } -+ if (discoveredDevices[i].manufacturerData() != info.manufacturerData()) { -+ qCDebug(QT_BT_OPENHARMONY) << "Updating manufacturer data for" << info.address(); -+ const QVector keys = info.manufacturerIds(); -+ for (auto key: keys) -+ discoveredDevices[i].setManufacturerData(key, info.manufacturerData(key)); -+ updatedFields.setFlag(QBluetoothDeviceInfo::Field::ManufacturerData); -+ } -+ -+ if (lowEnergySearchTimeout > 0) { -+ if (discoveredDevices[i] != info) { -+ if (discoveredDevices.at(i).name() == info.name()) { -+ qCDebug(QT_BT_OPENHARMONY) << "Almost Duplicate " << info.address() -+ << info.name() << "- replacing in place"; -+ discoveredDevices.replace(i, info); -+ emit q->deviceDiscovered(info); -+ } -+ } else { -+ if (!updatedFields.testFlag(QBluetoothDeviceInfo::Field::None)) -+ emit q->deviceUpdated(discoveredDevices[i], updatedFields); -+ } -+ -+ return; -+ } -+ -+ discoveredDevices.replace(i, info); -+ emit q->deviceDiscovered(info); -+ -+ if (!updatedFields.testFlag(QBluetoothDeviceInfo::Field::None)) -+ emit q->deviceUpdated(discoveredDevices[i], updatedFields); -+ -+ return; -+ } -+ } -+ -+ discoveredDevices.append(info); -+ qCDebug(QT_BT_OPENHARMONY) << "Device found: " << info.name() << info.address().toString() -+ << "isLeScanResult:" << isLeResult; -+ emit q->deviceDiscovered(info); -+} -+ -+void QBluetoothDeviceDiscoveryAgentPrivate::stop() -+{ -+ Q_Q(QBluetoothDeviceDiscoveryAgent); -+ -+ if (m_active == NoScanActive) -+ return; -+ -+ if (m_active == SDPScanActive) { -+ if (pendingCancel) -+ return; -+ -+ pendingCancel = true; -+ pendingStart = false; -+ bool success = m_jsBluetoothManager->call("stopBluetoothDiscovery"); -+ if (!success) { -+ lastError = QBluetoothDeviceDiscoveryAgent::InputOutputError; -+ errorString = QBluetoothDeviceDiscoveryAgent::tr("Discovery cannot be stopped"); -+ emit q->error(lastError); -+ return; -+ } -+ } else if (m_active == BtleScanActive) { -+ stopLowEnergyScan(); -+ } -+} -+void QBluetoothDeviceDiscoveryAgentPrivate::startLowEnergyScan() -+{ -+ Q_Q(QBluetoothDeviceDiscoveryAgent); -+ -+ m_active = BtleScanActive; -+ -+ -+ m_jsBluetoothManager->callWithoutReturn("startBLEScan"); -+ -+ // wait interval and sum up what was found -+ if (!leScanTimeout) { -+ leScanTimeout = new QTimer(this); -+ leScanTimeout->setSingleShot(true); -+ connect(leScanTimeout, &QTimer::timeout, -+ this, &QBluetoothDeviceDiscoveryAgentPrivate::stopLowEnergyScan); -+ } -+ -+ if (lowEnergySearchTimeout > 0) { // otherwise no timeout and stop() required -+ leScanTimeout->setInterval(lowEnergySearchTimeout); -+ leScanTimeout->start(); -+ } -+ -+ qCDebug(QT_BT_OPENHARMONY) -+ << "QBluetoothDeviceDiscoveryAgentPrivate::start() - Low Energy search successfully started."; -+} -+ -+void QBluetoothDeviceDiscoveryAgentPrivate::stopLowEnergyScan() -+{ -+ m_jsBluetoothManager->callWithoutReturn("stopBLEScan"); -+ -+ m_active = NoScanActive; -+ -+ Q_Q(QBluetoothDeviceDiscoveryAgent); -+ if (leScanTimeout->isActive()) { -+ // still active if this function was called from stop() -+ leScanTimeout->stop(); -+ emit q->canceled(); -+ } else { -+ // timeout -> regular stop -+ emit q->finished(); -+ } -+} -+QT_END_NAMESPACE -diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h -index ce31392f..4a5d2ff3 100644 ---- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h -+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h -@@ -83,6 +83,13 @@ QT_END_NAMESPACE - #include - #endif - -+#ifdef QT_OPENHARMONY_BLUETOOTH -+#include -+#include -+class QOpenHarmonyJsObject; -+class OpenHarmonyBluetoothDiscoveryReceiver; -+#endif -+ - QT_BEGIN_NAMESPACE - - #ifdef QT_WINRT_BLUETOOTH -@@ -90,7 +97,7 @@ class QWinRTBluetoothDeviceDiscoveryWorker; - #endif - - class QBluetoothDeviceDiscoveryAgentPrivate --#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) -+#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) || defined(QT_OPENHARMONY_BLUETOOTH) - : public QObject - { - Q_OBJECT -@@ -164,6 +171,24 @@ private: - QTimer extendedDiscoveryTimer; - #endif - -+#ifdef QT_OPENHARMONY_BLUETOOTH -+private slots: -+ void processSdpDiscoveryFinished(); -+ void processDiscoveredDevices(const QBluetoothDeviceInfo &info, bool isLeResult); -+ void stopLowEnergyScan(); -+ -+private: -+ void startLowEnergyScan(); -+ -+private: -+ OpenHarmonyBluetoothDiscoveryReceiver *receiver; -+ QBluetoothAddress m_adapterAddress; -+ short m_active; -+ QTimer *leScanTimeout; -+ bool pendingCancel, pendingStart; -+ QSharedPointer m_jsBluetoothManager; -+#endif -+ - #ifdef QT_WINRT_BLUETOOTH - private slots: - void registerDevice(const QBluetoothDeviceInfo &info); -diff --git a/src/bluetooth/qbluetoothlocaldevice.h b/src/bluetooth/qbluetoothlocaldevice.h -index 9f6d1e1b..59bdd36b 100644 ---- a/src/bluetooth/qbluetoothlocaldevice.h -+++ b/src/bluetooth/qbluetoothlocaldevice.h -@@ -69,6 +69,11 @@ public: - HostConnectable, - HostDiscoverable, - HostDiscoverableLimitedInquiry -+#ifdef Q_OS_OPENHARMONY -+ , -+ HostConnectableGeneralDiscoverable, -+ HostConnectableLimitedDiscoverable -+#endif - }; - Q_ENUM(HostMode) - -diff --git a/src/bluetooth/qbluetoothlocaldevice_ohos.cpp b/src/bluetooth/qbluetoothlocaldevice_ohos.cpp -new file mode 100644 -index 00000000..d3d3869d ---- /dev/null -+++ b/src/bluetooth/qbluetoothlocaldevice_ohos.cpp -@@ -0,0 +1,332 @@ -+#include -+#include -+#include -+ -+#include "QtCore/qopenharmonyjsobjectloader.h" -+#include "qbluetoothlocaldevice_p.h" -+//#include "openharmony/localdevicebroadcastreceiver_p.h" -+ -+QT_BEGIN_NAMESPACE -+ -+Q_DECLARE_LOGGING_CATEGORY(QT_BT_OPENHARMONY) -+ -+QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate( -+ QBluetoothLocalDevice *q, const QBluetoothAddress &address) : -+ q_ptr(q) -+{ -+ -+ initialize(address); -+ -+// receiver = new LocalDeviceBroadcastReceiver(q_ptr); -+// connect(receiver, &LocalDeviceBroadcastReceiver::hostModeStateChanged, -+// this, &QBluetoothLocalDevicePrivate::processHostModeChange); -+// connect(receiver, &LocalDeviceBroadcastReceiver::pairingStateChanged, -+// this, &QBluetoothLocalDevicePrivate::processPairingStateChanged); -+// connect(receiver, &LocalDeviceBroadcastReceiver::connectDeviceChanges, -+// this, &QBluetoothLocalDevicePrivate::processConnectDeviceChanges); -+// connect(receiver, &LocalDeviceBroadcastReceiver::pairingDisplayConfirmation, -+// this, &QBluetoothLocalDevicePrivate::processDisplayConfirmation); -+} -+ -+QBluetoothLocalDevicePrivate::~QBluetoothLocalDevicePrivate() -+{ -+ -+} -+ -+void QBluetoothLocalDevicePrivate::initialize(const QBluetoothAddress &address) -+{ -+ m_jsBluetoothManager = qJsObjectLoader->create("JsBluetoothManager"); -+ if (!m_jsBluetoothManager->call("isSupport")) { -+ qCWarning(QT_BT_OPENHARMONY) << "Device does not support Bluetooth"; -+ return; -+ } -+ -+ if (!address.isNull()) { -+ const QString localAddress -+ = m_jsBluetoothManager->call("getAddress"); -+ if (localAddress != address.toString()) { -+ // passed address not local one -> invalid -+ m_jsBluetoothManager.reset(); -+ } -+ } -+} -+ -+bool QBluetoothLocalDevicePrivate::isValid() const -+{ -+ return m_jsBluetoothManager.isNull() ? false : m_jsBluetoothManager->isValid(); -+} -+ -+void QBluetoothLocalDevicePrivate::processHostModeChange(QBluetoothLocalDevice::HostMode newMode) -+{ -+ if (!pendingHostModeTransition) { -+ // if not in transition -> pass data on -+ emit q_ptr->hostModeStateChanged(newMode); -+ return; -+ } -+ -+ if (isValid() && newMode == QBluetoothLocalDevice::HostPoweredOff) { -+ bool success = m_jsBluetoothManager->call("enable"); -+ if (!success) -+ emit q_ptr->error(QBluetoothLocalDevice::UnknownError); -+ } -+ -+ pendingHostModeTransition = false; -+} -+ -+// Return -1 if address is not part of a pending pairing request -+// Otherwise it returns the index of address in pendingPairings -+int QBluetoothLocalDevicePrivate::pendingPairing(const QBluetoothAddress &address) -+{ -+ for (int i = 0; i < pendingPairings.count(); i++) { -+ if (pendingPairings.at(i).first == address) -+ return i; -+ } -+ -+ return -1; -+} -+ -+void QBluetoothLocalDevicePrivate::processPairingStateChanged( -+ const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing) -+{ -+ int index = pendingPairing(address); -+ -+ if (index < 0) -+ return; // ignore unrelated pairing signals -+ -+ QPair entry = pendingPairings.takeAt(index); -+ if ((entry.second && pairing == QBluetoothLocalDevice::Paired) -+ || (!entry.second && pairing == QBluetoothLocalDevice::Unpaired)) { -+ emit q_ptr->pairingFinished(address, pairing); -+ } else { -+ emit q_ptr->error(QBluetoothLocalDevice::PairingError); -+ } -+} -+ -+void QBluetoothLocalDevicePrivate::processConnectDeviceChanges(const QBluetoothAddress &address, -+ bool isConnectEvent) -+{ -+ int index = -1; -+ for (int i = 0; i < connectedDevices.count(); i++) { -+ if (connectedDevices.at(i) == address) { -+ index = i; -+ break; -+ } -+ } -+ -+ if (isConnectEvent) { // connect event -+ if (index >= 0) -+ return; -+ connectedDevices.append(address); -+ emit q_ptr->deviceConnected(address); -+ } else { // disconnect event -+ connectedDevices.removeAll(address); -+ emit q_ptr->deviceDisconnected(address); -+ } -+} -+ -+void QBluetoothLocalDevicePrivate::processDisplayConfirmation(const QBluetoothAddress &address, -+ const QString &pin) -+{ -+ // only send pairing notification for pairing requests issued by -+ // this QBluetoothLocalDevice instance -+ if (pendingPairing(address) == -1) -+ return; -+ -+ emit q_ptr->pairingDisplayConfirmation(address, pin); -+ emit q_ptr->pairingDisplayPinCode(address, pin); -+} -+ -+QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) : -+ QObject(parent), -+ d_ptr(new QBluetoothLocalDevicePrivate(this, QBluetoothAddress())) -+{ -+} -+ -+QBluetoothLocalDevice::QBluetoothLocalDevice(const QBluetoothAddress &address, QObject *parent) : -+ QObject(parent), -+ d_ptr(new QBluetoothLocalDevicePrivate(this, address)) -+{ -+} -+ -+QString QBluetoothLocalDevice::name() const -+{ -+ if (d_ptr->isValid()) -+ return d_ptr->m_jsBluetoothManager->call("getName"); -+ -+ return QString(); -+} -+ -+QBluetoothAddress QBluetoothLocalDevice::address() const -+{ -+ QString result; -+ if (d_ptr->isValid()) { -+ result = d_ptr->m_jsBluetoothManager->call("getAddress"); -+ } -+ -+ QBluetoothAddress address(result); -+ return address; -+} -+ -+void QBluetoothLocalDevice::powerOn() -+{ -+ if (hostMode() != HostPoweredOff) -+ return; -+ -+ if (d_ptr->isValid()) { -+ bool ret = d_ptr->m_jsBluetoothManager->call("enable"); -+ if (!ret) -+ emit error(QBluetoothLocalDevice::UnknownError); -+ } -+} -+ -+void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode requestedMode) -+{ -+ QBluetoothLocalDevice::HostMode mode = requestedMode; -+ if (requestedMode == HostDiscoverableLimitedInquiry) -+ mode = HostDiscoverable; -+ -+ if (mode == hostMode()) -+ return; -+ -+ if (mode == QBluetoothLocalDevice::HostPoweredOff) { -+ bool success = false; -+ if (d_ptr->isValid()) -+ success = d_ptr->m_jsBluetoothManager->call("disable"); -+ -+ if (!success) -+ emit error(QBluetoothLocalDevice::UnknownError); -+ } else if (mode == QBluetoothLocalDevice::HostConnectable) { -+ if (hostMode() == QBluetoothLocalDevice::HostDiscoverable) { -+ // cannot directly go from Discoverable to Connectable -+ // we need to go to disabled mode and enable once disabling came through -+ -+ setHostMode(QBluetoothLocalDevice::HostPoweredOff); -+ d_ptr->pendingHostModeTransition = true; -+ } else { -+ d_ptr->m_jsBluetoothManager->callWithoutReturn("setScanMode", 1); -+ } -+ } else if (mode == QBluetoothLocalDevice::HostDiscoverable) { -+ d_ptr->m_jsBluetoothManager->callWithoutReturn("setScanMode", 2); -+ } else if (mode == QBluetoothLocalDevice::HostDiscoverableLimitedInquiry) { -+ d_ptr->m_jsBluetoothManager->callWithoutReturn("setScanMode", 3); -+ } -+} -+ -+QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const -+{ -+ if (d_ptr->isValid()) { -+ int scanMode = d_ptr->m_jsBluetoothManager->call("scanMode"); -+ -+ switch (scanMode) { -+ case 0: // SCAN_MODE_NONE -+ return HostPoweredOff; -+ case 1: // SCAN_MODE_CONNECTABLE -+ return HostConnectable; -+ case 2: // SCAN_MODE_GENERAL_DISCOVERABLE -+ return HostDiscoverable; -+ case 3: // SCAN_MODE_LIMITED_DISCOVERABLE -+ return HostDiscoverableLimitedInquiry; -+ case 4: -+ return HostConnectableGeneralDiscoverable; -+ case 5: -+ return HostConnectableLimitedDiscoverable; -+ default: -+ break; -+ } -+ } -+ -+ return HostPoweredOff; -+} -+ -+QList QBluetoothLocalDevice::allDevices() -+{ -+ QList localDevices; -+ -+ QSharedPointer obj = qJsObjectLoader->create("JsBluetoothManager"); -+ -+ if (!obj.isNull() && obj->isValid()) { -+ QBluetoothHostInfo info; -+ info.setName(obj->call("getLocalName")); -+ info.setAddress(QBluetoothAddress(obj->call("getAddress"))); -+ localDevices.append(info); -+ } -+ return localDevices; -+} -+ -+void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pairing pairing) -+{ -+ if (address.isNull()) { -+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -+ Q_ARG(QBluetoothLocalDevice::Error, -+ QBluetoothLocalDevice::PairingError)); -+ return; -+ } -+ -+ const Pairing previousPairing = pairingStatus(address); -+ Pairing newPairing = pairing; -+ if (pairing == AuthorizedPaired) -+ newPairing = Paired; -+ -+ if (previousPairing == newPairing) { -+ QMetaObject::invokeMethod(this, "pairingFinished", Qt::QueuedConnection, -+ Q_ARG(QBluetoothAddress, address), -+ Q_ARG(QBluetoothLocalDevice::Pairing, pairing)); -+ return; -+ } -+ -+ if (isValid()) { -+ bool success = d_ptr->m_jsBluetoothManager->call("pair", address.toString(), newPairing == Paired); -+ if (!success) { -+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, -+ Q_ARG(QBluetoothLocalDevice::Error, -+ QBluetoothLocalDevice::PairingError)); -+ } -+ } else { -+ d_ptr->pendingPairings.append(qMakePair(address, newPairing == Paired ? true : false)); -+ } -+} -+ -+QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus( -+ const QBluetoothAddress &address) const -+{ -+ if (!isValid()) -+ return Unpaired; -+ -+ -+ int bondState = d_ptr->m_jsBluetoothManager->call("getBondState", address.toString()); -+ switch (bondState) { -+ case 2: // BOND_STATE_BONDED -+ return Paired; -+ default: -+ break; -+ } -+ -+ return Unpaired; -+} -+ -+void QBluetoothLocalDevice::pairingConfirmation(bool confirmation) -+{ -+// bool success = d_ptr->receiver->pairingConfirmation(confirmation); -+// if (!success) -+// emit error(PairingError); -+} -+ -+QList QBluetoothLocalDevice::connectedDevices() const -+{ -+ if (!isValid()) -+ return QList(); -+ -+ QList knownAddresses; -+ -+ QStringList devices = d_ptr->m_jsBluetoothManager->call("pairedDevices"); -+ int size = devices.size(); -+ for (int i = 0; i < size; i++) { -+ QBluetoothAddress address(devices.at(i)); -+ if (!address.isNull() && !knownAddresses.contains(address)) -+ knownAddresses.append(address); -+ } -+ -+ return knownAddresses; -+} -+ -+QT_END_NAMESPACE -diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h -index 1f99f27e..b30c6004 100644 ---- a/src/bluetooth/qbluetoothlocaldevice_p.h -+++ b/src/bluetooth/qbluetoothlocaldevice_p.h -@@ -84,6 +84,10 @@ QT_END_NAMESPACE - #include - #endif - -+#ifdef QT_OPENHARMONY_BLUETOOTH -+#include "QtCore/qopenharmonyjsobject.h" -+#endif -+ - QT_BEGIN_NAMESPACE - - extern void registerQBluetoothLocalDeviceMetaType(); -@@ -97,7 +101,7 @@ class QBluetoothLocalDevicePrivate : public QObject - Q_OBJECT - public: - QBluetoothLocalDevicePrivate( -- QBluetoothLocalDevice *q, const QBluetoothAddress &address = QBluetoothAddress()); -+ QBluetoothLocalDevice *q, const QBluetoothAddress &address = QBluetoothAddress()); - ~QBluetoothLocalDevicePrivate(); - - QAndroidJniObject *adapter(); -@@ -207,6 +211,43 @@ private: - void initializeAdapter(); - void initializeAdapterBluez5(); - }; -+#elif QT_OPENHARMONY_BLUETOOTH -+class QOpenHarmonyJsObject; -+class LocalDeviceBroadcastReceiver; -+class QBluetoothLocalDevicePrivate : public QObject -+{ -+ Q_OBJECT -+ Q_DECLARE_PUBLIC(QBluetoothLocalDevice) -+ -+public: -+ QBluetoothLocalDevicePrivate( -+ QBluetoothLocalDevice *q, const QBluetoothAddress &address = QBluetoothAddress()); -+ ~QBluetoothLocalDevicePrivate(); -+ -+ void initialize(const QBluetoothAddress &address); -+ static bool startDiscovery(); -+ static bool cancelDiscovery(); -+ static bool isDiscovering(); -+ bool isValid() const; -+ -+private slots: -+ void processHostModeChange(QBluetoothLocalDevice::HostMode newMode); -+ void processPairingStateChanged(const QBluetoothAddress &address, -+ QBluetoothLocalDevice::Pairing pairing); -+ void processConnectDeviceChanges(const QBluetoothAddress &address, bool isConnectEvent); -+ void processDisplayConfirmation(const QBluetoothAddress &address, const QString &pin); -+ -+public: -+ LocalDeviceBroadcastReceiver *receiver; -+ bool pendingHostModeTransition = false; -+ QList > pendingPairings; -+ -+ QList connectedDevices; -+private: -+ int pendingPairing(const QBluetoothAddress &address); -+ QBluetoothLocalDevice *q_ptr; -+ QSharedPointer m_jsBluetoothManager; -+}; - #elif !defined(QT_OSX_BLUETOOTH) // winrt and dummy backend - class QBluetoothLocalDevicePrivate : public QObject - { -diff --git a/src/bluetooth/qbluetoothserver_ohos.cpp b/src/bluetooth/qbluetoothserver_ohos.cpp -new file mode 100644 -index 00000000..90b98398 ---- /dev/null -+++ b/src/bluetooth/qbluetoothserver_ohos.cpp -@@ -0,0 +1,237 @@ -+#include -+#include -+ -+#include "qbluetoothserver.h" -+#include "qbluetoothserver_p.h" -+#include "qbluetoothsocket.h" -+#include "qbluetoothsocket_ohos_p.h" -+#include "qbluetoothlocaldevice.h" -+#include "QtCore/qopenharmonyjsobject.h" -+#include "QtCore/qopenharmonyjsobjectloader.h" -+#include "openharmony/serveracceptancethread_p.h" -+ -+QT_BEGIN_NAMESPACE -+ -+Q_DECLARE_LOGGING_CATEGORY(QT_BT_OPENHARMONY) -+ -+QHash __fakeServerPorts; -+QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType) -+ : socket(0),maxPendingConnections(1), securityFlags(QBluetooth::NoSecurity), serverType(sType), -+ m_lastError(QBluetoothServer::NoError) -+{ -+ thread = new ServerAcceptanceThread(); -+ thread->setMaxPendingConnections(maxPendingConnections); -+} -+ -+QBluetoothServerPrivate::~QBluetoothServerPrivate() -+{ -+ Q_Q(QBluetoothServer); -+ if (isListening()) -+ q->close(); -+ -+ __fakeServerPorts.remove(this); -+ -+ thread->deleteLater(); -+ thread = nullptr; -+} -+ -+bool QBluetoothServerPrivate::initiateActiveListening( -+ const QBluetoothUuid& uuid, const QString &serviceName) -+{ -+ qCDebug(QT_BT_OPENHARMONY) << "Initiate active listening" << uuid.toString() << serviceName; -+ -+ if (uuid.isNull() || serviceName.isEmpty()) -+ return false; -+ -+ //no change of SP profile details -> do nothing -+ if (uuid == m_uuid && serviceName == m_serviceName && thread->isRunning()) -+ return true; -+ -+ m_uuid = uuid; -+ m_serviceName = serviceName; -+ thread->setServiceDetails(m_uuid, m_serviceName, securityFlags); -+ -+ thread->run(); -+ if (!thread->isRunning()) -+ return false; -+ -+ return true; -+} -+ -+bool QBluetoothServerPrivate::deactivateActiveListening() -+{ -+ if (isListening()) { -+ //suppress last error signal due to intended closure -+ thread->disconnect(); -+ thread->stop(); -+ } -+ return true; -+} -+ -+bool QBluetoothServerPrivate::isListening() const -+{ -+ return __fakeServerPorts.contains(const_cast(this)); -+} -+ -+void QBluetoothServer::close() -+{ -+ Q_D(QBluetoothServer); -+ -+ __fakeServerPorts.remove(d); -+ if (d->thread->isRunning()) { -+ //suppress last error signal due to intended closure -+ d->thread->disconnect(); -+ d->thread->stop(); -+ } -+} -+ -+bool QBluetoothServer::listen(const QBluetoothAddress &localAdapter, quint16 port) -+{ -+ Q_D(QBluetoothServer); -+ if (serverType() != QBluetoothServiceInfo::RfcommProtocol) { -+ d->m_lastError = UnsupportedProtocolError; -+ emit error(d->m_lastError); -+ return false; -+ } -+ -+ const QList localDevices = QBluetoothLocalDevice::allDevices(); -+ if (!localDevices.count()) { -+ qCWarning(QT_BT_OPENHARMONY) << "Device does not support Bluetooth"; -+ d->m_lastError = QBluetoothServer::UnknownError; -+ emit error(d->m_lastError); -+ return false; //no Bluetooth device -+ } -+ -+ if (!localAdapter.isNull()) { -+ bool found = false; -+ for (const QBluetoothHostInfo &hostInfo : localDevices) { -+ if (hostInfo.address() == localAdapter) { -+ found = true; -+ break; -+ } -+ } -+ -+ if (!found) { -+ qCWarning(QT_BT_OPENHARMONY) << localAdapter.toString() << "is not a valid local Bt adapter"; -+ return false; -+ } -+ } -+ -+ if (d->isListening()) -+ return false; -+ -+ //check Bluetooth is available and online -+ static QSharedPointer btMgr = qJsObjectLoader->create("JsBluetoothManager"); -+ if (!btMgr->isValid()) { -+ qCWarning(QT_BT_OPENHARMONY) << "Device does not support Bluetooth"; -+ d->m_lastError = QBluetoothServer::UnknownError; -+ emit error(d->m_lastError); -+ return false; -+ } -+ -+ const int state = btMgr->call("getState"); -+ static QVector offState{ 0, 3, 6 }; -+ -+ if (offState.contains(state)) { //BluetoothState -+ d->m_lastError = QBluetoothServer::PoweredOffError; -+ emit error(d->m_lastError); -+ qCWarning(QT_BT_OPENHARMONY) << "Bluetooth device is powered off"; -+ return false; -+ } -+ -+ //We can not register an actual Rfcomm port, because the platform does not allow it -+ //but we need a way to associate a server with a service -+ if (port == 0) { //Try to assign a non taken port id -+ for (int i=1; ; i++){ -+ if (__fakeServerPorts.key(i) == 0) { -+ port = i; -+ break; -+ } -+ } -+ } -+ -+ if (__fakeServerPorts.key(port) == 0) { -+ __fakeServerPorts[d] = port; -+ -+ qCDebug(QT_BT_OPENHARMONY) << "Port" << port << "registered"; -+ } else { -+ qCWarning(QT_BT_OPENHARMONY) << "server with port" << port << "already registered or port invalid"; -+ d->m_lastError = ServiceAlreadyRegisteredError; -+ emit error(d->m_lastError); -+ return false; -+ } -+ -+ connect(d->thread, SIGNAL(newConnection()), -+ this, SIGNAL(newConnection())); -+ connect(d->thread, SIGNAL(error(QBluetoothServer::Error)), -+ this, SIGNAL(error(QBluetoothServer::Error)), Qt::QueuedConnection); -+ -+ return true; -+} -+ -+void QBluetoothServer::setMaxPendingConnections(int numConnections) -+{ -+ Q_D(QBluetoothServer); -+ d->maxPendingConnections = numConnections; -+ d->thread->setMaxPendingConnections(numConnections); -+} -+ -+QBluetoothAddress QBluetoothServer::serverAddress() const -+{ -+ //only supports one local adapter -+ QList hosts = QBluetoothLocalDevice::allDevices(); -+ Q_ASSERT(hosts.count() <= 1); -+ -+ if (hosts.isEmpty()) -+ return QBluetoothAddress(); -+ else -+ return hosts.at(0).address(); -+} -+ -+quint16 QBluetoothServer::serverPort() const -+{ -+ //We return the fake port -+ Q_D(const QBluetoothServer); -+ return __fakeServerPorts.value((QBluetoothServerPrivate*)d, 0); -+} -+ -+bool QBluetoothServer::hasPendingConnections() const -+{ -+ Q_D(const QBluetoothServer); -+ -+ return d->thread->hasPendingConnections(); -+} -+ -+QBluetoothSocket *QBluetoothServer::nextPendingConnection() -+{ -+ Q_D(const QBluetoothServer); -+ -+ int socket = d->thread->nextPendingConnection(); -+ if (-1 == socket) -+ return 0; -+ -+ QBluetoothSocket *newSocket = new QBluetoothSocket(); -+ -+ bool success = newSocket->d_ptr->setSocketDescriptor(socket, d->serverType); -+ if (!success) { -+ delete newSocket; -+ newSocket = nullptr; -+ } -+ -+ return newSocket; -+} -+ -+void QBluetoothServer::setSecurityFlags(QBluetooth::SecurityFlags security) -+{ -+ Q_D(QBluetoothServer); -+ d->securityFlags = security; -+} -+ -+QBluetooth::SecurityFlags QBluetoothServer::securityFlags() const -+{ -+ Q_D(const QBluetoothServer); -+ return d->securityFlags; -+} -+ -+QT_END_NAMESPACE -+ -diff --git a/src/bluetooth/qbluetoothserver_p.h b/src/bluetooth/qbluetoothserver_p.h -index d78eee5f..f8d67cb5 100644 ---- a/src/bluetooth/qbluetoothserver_p.h -+++ b/src/bluetooth/qbluetoothserver_p.h -@@ -69,6 +69,10 @@ QT_FORWARD_DECLARE_CLASS(QSocketNotifier) - class ServerAcceptanceThread; - #endif - -+#ifdef QT_OPENHARMONY_BLUETOOTH -+class ServerAcceptanceThread; -+#endif -+ - #ifdef QT_WINRT_BLUETOOTH - #include - -@@ -124,6 +128,14 @@ public: - bool isListening() const; - bool initiateActiveListening(const QBluetoothUuid& uuid, const QString &serviceName); - bool deactivateActiveListening(); -+#elif defined(QT_OPENHARMONY_BLUETOOTH) -+ ServerAcceptanceThread *thread; -+ QString m_serviceName; -+ QBluetoothUuid m_uuid; -+public: -+ bool isListening() const; -+ bool initiateActiveListening(const QBluetoothUuid& uuid, const QString &serviceName); -+ bool deactivateActiveListening(); - #elif defined(QT_WINRT_BLUETOOTH) - EventRegistrationToken connectionToken {-1}; - -diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.cpp b/src/bluetooth/qbluetoothservicediscoveryagent.cpp -index 53ce98e5..d9c9a9b8 100644 ---- a/src/bluetooth/qbluetoothservicediscoveryagent.cpp -+++ b/src/bluetooth/qbluetoothservicediscoveryagent.cpp -@@ -317,8 +317,7 @@ QBluetoothAddress QBluetoothServiceDiscoveryAgent::remoteAddress() const - */ - void QBluetoothServiceDiscoveryAgent::start(DiscoveryMode mode) - { -- Q_D(QBluetoothServiceDiscoveryAgent); -- -+ Q_D(QBluetoothServiceDiscoveryAgent); - if (d->discoveryState() == QBluetoothServiceDiscoveryAgentPrivate::Inactive - && d->error != InvalidBluetoothAdapterError) { - #if QT_CONFIG(bluez) -@@ -326,14 +325,14 @@ void QBluetoothServiceDiscoveryAgent::start(DiscoveryMode mode) - // on Bluez5 - d->foundHostAdapterPath.clear(); - #endif -- d->setDiscoveryMode(mode); -- if (d->deviceAddress.isNull()) { -+ d->setDiscoveryMode(mode); -+ if (d->deviceAddress.isNull()) { - d->startDeviceDiscovery(); - } else { - d->discoveredDevices << QBluetoothDeviceInfo(d->deviceAddress, QString(), 0); -- d->startServiceDiscovery(); -+ d->startServiceDiscovery(); - } -- } -+ } - } - - /*! -@@ -425,7 +424,7 @@ QString QBluetoothServiceDiscoveryAgent::errorString() const - This signal is triggered when the service discovery was canceled via a call to \l stop(). - */ - -- -+#include - /*! - Starts device discovery. - */ -@@ -443,6 +442,10 @@ void QBluetoothServiceDiscoveryAgentPrivate::startDeviceDiscovery() - q, [this](){ - this->_q_deviceDiscoveryFinished(); - }); -+ /* FIXME wanghao ohos蓝牙模块未提供扫描结束接口 */ -+ QTimer::singleShot(2000, [this]{ -+ this->_q_deviceDiscoveryFinished(); -+ }); - QObject::connect(deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, - q, [this](const QBluetoothDeviceInfo &info){ - this->_q_deviceDiscovered(info); -@@ -455,8 +458,11 @@ void QBluetoothServiceDiscoveryAgentPrivate::startDeviceDiscovery() - } - - setDiscoveryState(DeviceDiscovery); -- -- deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::ClassicMethod); -+#ifdef QT_OPENHARMONY_BLUETOOTH -+ //deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::ClassicMethod); -+#else -+ deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::ClassicMethod); -+#endif - } - - /*! -@@ -495,8 +501,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscoveryFinished() - } - - delete deviceDiscoveryAgent; -- deviceDiscoveryAgent = nullptr; -- -+ deviceDiscoveryAgent = nullptr; - startServiceDiscovery(); - } - -@@ -534,14 +539,12 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscoveryError(QBluetoothD - */ - void QBluetoothServiceDiscoveryAgentPrivate::startServiceDiscovery() - { -- Q_Q(QBluetoothServiceDiscoveryAgent); -- -+ Q_Q(QBluetoothServiceDiscoveryAgent); - if (discoveredDevices.isEmpty()) { - setDiscoveryState(Inactive); - emit q->finished(); - return; - } -- - setDiscoveryState(ServiceDiscovery); - start(discoveredDevices.at(0).address()); - } -diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_ohos.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_ohos.cpp -new file mode 100644 -index 00000000..ae6c7cfc ---- /dev/null -+++ b/src/bluetooth/qbluetoothservicediscoveryagent_ohos.cpp -@@ -0,0 +1,445 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "qbluetoothsocket_ohos_p.h" -+#include "QtCore/qopenharmonyjsobject.h" -+#include "QtCore/qopenharmonyjsobjectloader.h" -+#include "qbluetoothservicediscoveryagent_p.h" -+#include "openharmony/openharmonyservicediscoveryreceiver_p.h" -+#include "openharmony/openharmonylocaldevicebroadcastreceiver_p.h" -+ -+QT_BEGIN_NAMESPACE -+ -+Q_DECLARE_LOGGING_CATEGORY(QT_BT_OPENHARMONY) -+static int gi_count = 0; -+ -+QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate( -+ QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &deviceAdapter) -+ : error(QBluetoothServiceDiscoveryAgent::NoError), -+ m_deviceAdapterAddress(deviceAdapter), -+ state(Inactive), -+ mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery), -+ singleDevice(false), -+ q_ptr(qp), -+ receiver(nullptr), -+ m_jsBtMgr(nullptr), -+ m_jsAdapter(nullptr) -+ -+{ -+ // If a specific adapter address is requested we need to check it matches -+ // the current local adapter. If it does not match we emit -+ // InvalidBluetoothAdapterError when calling start() -+ -+ bool createAdapter = true; -+ if (!deviceAdapter.isNull()) { -+ const QList devices = QBluetoothLocalDevice::allDevices(); -+ if (devices.isEmpty()) { -+ createAdapter = false; -+ } else { -+ auto match = [deviceAdapter](const QBluetoothHostInfo& info) { -+ return info.address() == deviceAdapter; -+ }; -+ -+ auto result = std::find_if(devices.begin(), devices.end(), match); -+ if (result == devices.end()) -+ createAdapter = false; -+ } -+ } -+ -+ /* -+ We assume that the current local adapter has been passed. -+ The logic below must change once there is more than one adapter. -+ */ -+ if (createAdapter) -+ m_jsBtMgr = qJsObjectLoader->create("JsBluetoothManager"); -+ -+ if (!m_jsBtMgr->isValid()) -+ qCWarning(QT_BT_OPENHARMONY) << "Platform does not support Bluetooth"; -+ -+ -+ qRegisterMetaType >(); -+} -+ -+QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate() -+{ -+ if (receiver) { -+ delete receiver; -+ } -+ -+ if (localDeviceReceiver) { -+ delete localDeviceReceiver; -+ } -+} -+ -+void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &address) -+{ -+ Q_Q(QBluetoothServiceDiscoveryAgent); -+ qWarning() << "<<<<<<<<------000" << address.toString(); -+ -+ if (!m_jsBtMgr->isValid()) { -+ if (m_deviceAdapterAddress.isNull()) { -+ error = QBluetoothServiceDiscoveryAgent::UnknownError; -+ errorString = QBluetoothServiceDiscoveryAgent::tr("Platform does not support Bluetooth"); -+ } else { -+ // specific adapter was requested which does not match the locally -+ // existing adapter -+ error = QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError; -+ errorString = QBluetoothServiceDiscoveryAgent::tr("Invalid Bluetooth adapter address"); -+ } -+ -+ //abort any outstanding discoveries -+ discoveredDevices.clear(); -+ emit q->error(error); -+ _q_serviceDiscoveryFinished(); -+ -+ return; -+ } -+// qWarning() << "<<<<<<<<------111" << address.toString(); -+// QByteArray remoteDev = m_jsBtMgr->call("getRemoteDeviceName", address.toString()); -+// qWarning() << "<<<<<<<<------111-2" << remoteDev; -+// if (remoteDev.isEmpty()) { -+// //if it was only device then its error -> otherwise go to next device -+// if (singleDevice) { -+// error = QBluetoothServiceDiscoveryAgent::InputOutputError; -+// errorString = QBluetoothServiceDiscoveryAgent::tr("Cannot create Android BluetoothDevice"); -+ -+// qCWarning(QT_BT_OPENHARMONY) << "Cannot start SDP for" << discoveredDevices.at(0).name() -+// << "(" << address.toString() << ")"; -+// emit q->error(error); -+// } -+// _q_serviceDiscoveryFinished(); -+// return; -+// } -+ qWarning() << "<<<<<<<<------222" << address.toString(); -+ if (mode == QBluetoothServiceDiscoveryAgent::MinimalDiscovery) { -+ qCDebug(QT_BT_OPENHARMONY) << "Minimal discovery on (" << discoveredDevices.at(0).name() -+ << ")" << address.toString(); -+ -+ /* TODO */ -+ error = QBluetoothServiceDiscoveryAgent::UnknownError; -+ errorString = QBluetoothServiceDiscoveryAgent::tr("Platform does not support Bluetooth"); -+ -+ discoveredDevices.clear(); -+ emit q->error(error); -+ _q_serviceDiscoveryFinished(); -+ return; -+ } else { -+ qWarning() << "<<<<<<<<------333" << address.toString(); -+// qCDebug(QT_BT_OPENHARMONY) << "Full discovery on (" << discoveredDevices.at(0).name() -+// << ")" << address.toString(); -+ -+ qWarning() << "<<<<<<<<------333--222" << address.toString(); -+ //Full discovery uses BluetoothDevice.fetchUuidsWithSdp() -+ if (!receiver) { -+ receiver = new OPenHarmonyServiceDiscoveryReceiver(); -+ QString name = QString("QBluetoothServiceDiscoveryAgent_%1").arg(++gi_count); -+ qWarning() << "<<<<<<<<------444" << address.toString(); -+ m_jsBtMgr->call("createServiceAdapter", name, QVariant(qlonglong(static_cast(receiver)))); -+ qWarning() << "<<<<<<<<------555" << address.toString(); -+ m_jsAdapter = qJsObjectLoader->create(name); -+ QObject::connect(receiver, &OPenHarmonyServiceDiscoveryReceiver::uuidFetchFinished, -+ q, [this](const QBluetoothAddress &address, const QList& uuids) { -+ qWarning() << "<<<<<<<<------_q_processFetchedUuids" << address; -+ this->_q_processFetchedUuids(address, uuids); -+ }); -+ } -+ -+ if (!localDeviceReceiver) { -+ localDeviceReceiver = new OPenHarmonyLocalDeviceBroadcastReceiver(); -+ QObject::connect(localDeviceReceiver, &OPenHarmonyLocalDeviceBroadcastReceiver::hostModeStateChanged, -+ q, [this](QBluetoothLocalDevice::HostMode state){ -+ this->_q_hostModeStateChanged(state); -+ }); -+ } -+ if (!m_jsAdapter->isValid()) -+ return; -+ -+ qWarning() << "<<<<<<<<------666" << address.toString(); -+ bool ok = m_jsAdapter->call("createAdapter", address.toString()); -+ qWarning() << "<<<<<<<<------777" << address.toString() << ok; -+ if (!ok) { -+ receiver->deleteLater(); -+ receiver = nullptr; -+ qCWarning(QT_BT_OPENHARMONY) << "Cannot start dynamic fetch."; -+ _q_serviceDiscoveryFinished(); -+ } -+ qWarning() << "<===============LLLLL---"; -+ m_jsAdapter->callWithoutReturn("fetchService"); -+ qWarning() << "<===============LLLLL---222"; -+ } -+} -+ -+void QBluetoothServiceDiscoveryAgentPrivate::stop() -+{ -+ sdpCache.clear(); -+ discoveredDevices.clear(); -+ -+ //kill receiver to limit load of signals -+ //receiver->unregisterReceiver(); -+ receiver->deleteLater(); -+ receiver = nullptr; -+ -+ Q_Q(QBluetoothServiceDiscoveryAgent); -+ emit q->canceled(); -+} -+ -+void QBluetoothServiceDiscoveryAgentPrivate::_q_processFetchedUuids( -+ const QBluetoothAddress &address, const QList &uuids) -+{ -+ //don't leave more data through if we are not interested anymore -+ if (discoveredDevices.count() == 0) -+ return; -+ -+ //could not find any service for the current address/device -> go to next one -+ if (address.isNull() || uuids.isEmpty()) { -+ if (discoveredDevices.count() == 1) { -+ Q_Q(QBluetoothServiceDiscoveryAgent); -+ QTimer::singleShot(4000, q, [this]() { -+ this->_q_fetchUuidsTimeout(); -+ }); -+ } -+ _q_serviceDiscoveryFinished(); -+ return; -+ } -+ -+ if (QT_BT_OPENHARMONY().isDebugEnabled()) { -+ qCDebug(QT_BT_OPENHARMONY) << "Found UUID for" << address.toString() -+ << "\ncount: " << uuids.count(); -+ -+ QString result; -+ for (int i = 0; i > pair = sdpCache.take(address); -+ -+ //prefer second uuid set over first -+ populateDiscoveredServices(pair.first, uuids); -+ -+ if (discoveredDevices.count() == 1 && sdpCache.isEmpty()) { -+ //last regular uuid data set from OS -> we finish here -+ _q_serviceDiscoveryFinished(); -+ } -+ } else { -+ //first event -+ QPair > pair; -+ pair.first = discoveredDevices.at(0); -+ pair.second = uuids; -+ -+ if (pair.first.address() != address) -+ return; -+ -+ sdpCache.insert(address, pair); -+ -+ //the discovery on the last device cannot immediately finish -+ //we have to grant the 2 seconds timeout delay -+ if (discoveredDevices.count() == 1) { -+ Q_Q(QBluetoothServiceDiscoveryAgent); -+ QTimer::singleShot(4000, q, [this]() { -+ this->_q_fetchUuidsTimeout(); -+ }); -+ return; -+ } -+ -+ _q_serviceDiscoveryFinished(); -+ } -+} -+ -+void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QBluetoothDeviceInfo &remoteDevice, const QList &uuids) -+{ -+ /* Android doesn't provide decent SDP data. A flat list of uuids is all we get. -+ * -+ * The following approach is chosen: -+ * - If we see an SPP service class and we see -+ * one or more custom uuids we match them up. Such services will always -+ * be SPP services. There is the chance that a custom uuid is eronously -+ * mapped as being an SPP service. In addition, the SPP uuid will be mapped as -+ * standalone SPP service. -+ * - If we see a custom uuid but no SPP uuid then we return -+ * BluetoothServiceInfo instance with just a serviceUuid (no service class set) -+ * - If we don't find any custom uuid but the SPP uuid, we return a -+ * BluetoothServiceInfo instance where classId and serviceUuid() are set to SPP. -+ * - Any other service uuid will stand on its own. -+ * */ -+ -+ Q_Q(QBluetoothServiceDiscoveryAgent); -+ -+ //find SPP and custom uuid -+ bool haveSppClass = false; -+ QVector customUuids; -+ -+ for (int i = 0; i < uuids.count(); i++) { -+ const QBluetoothUuid uuid = uuids.at(i); -+ -+ if (uuid.isNull()) -+ continue; -+ -+ //check for SPP protocol -+ bool ok = false; -+ auto uuid16 = uuid.toUInt16(&ok); -+ haveSppClass |= ok && uuid16 == QBluetoothUuid::SerialPort; -+ -+ //check for custom uuid -+ if (uuid.minimumSize() == 16) -+ customUuids.append(i); -+ } -+ -+ auto rfcommProtocolDescriptorList = []() -> QBluetoothServiceInfo::Sequence { -+ QBluetoothServiceInfo::Sequence protocol; -+ protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm)) -+ << QVariant::fromValue(0); -+ return protocol; -+ }; -+ -+ auto sppProfileDescriptorList = []() -> QBluetoothServiceInfo::Sequence { -+ QBluetoothServiceInfo::Sequence profileSequence; -+ QBluetoothServiceInfo::Sequence classId; -+ classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort)); -+ classId << QVariant::fromValue(quint16(0x100)); -+ profileSequence.append(QVariant::fromValue(classId)); -+ return profileSequence; -+ }; -+ -+ for (int i = 0; i < uuids.count(); i++) { -+ const QBluetoothUuid &uuid = uuids.at(i); -+ if (uuid.isNull()) -+ continue; -+ -+ QBluetoothServiceInfo serviceInfo; -+ serviceInfo.setDevice(remoteDevice); -+ -+ QBluetoothServiceInfo::Sequence protocolDescriptorList; -+ { -+ QBluetoothServiceInfo::Sequence protocol; -+ protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::L2cap)); -+ protocolDescriptorList.append(QVariant::fromValue(protocol)); -+ } -+ -+ if (customUuids.contains(i) && haveSppClass) { -+ //we have a custom uuid of service class type SPP -+ -+ //set rfcomm protocol -+ protocolDescriptorList.append(QVariant::fromValue(rfcommProtocolDescriptorList())); -+ -+ //set SPP profile descriptor list -+ serviceInfo.setAttribute(QBluetoothServiceInfo::BluetoothProfileDescriptorList, -+ sppProfileDescriptorList()); -+ -+ QBluetoothServiceInfo::Sequence classId; -+ //set SPP service class uuid -+ classId << QVariant::fromValue(uuid); -+ classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort)); -+ serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId); -+ -+ serviceInfo.setServiceName(QBluetoothServiceDiscoveryAgent::tr("Serial Port Profile")); -+ serviceInfo.setServiceUuid(uuid); -+ } else if (uuid == QBluetoothUuid{QBluetoothUuid::SerialPort}) { -+ //set rfcomm protocol -+ protocolDescriptorList.append(QVariant::fromValue(rfcommProtocolDescriptorList())); -+ -+ //set SPP profile descriptor list -+ serviceInfo.setAttribute(QBluetoothServiceInfo::BluetoothProfileDescriptorList, -+ sppProfileDescriptorList()); -+ -+ //also we need to set the custom uuid to the SPP uuid -+ //otherwise QBluetoothSocket::connectToService() would fail due to a missing service uuid -+ serviceInfo.setServiceUuid(uuid); -+ } else if (customUuids.contains(i)) { -+ //custom uuid but no serial port -+ serviceInfo.setServiceUuid(uuid); -+ } -+ -+ serviceInfo.setAttribute(QBluetoothServiceInfo::ProtocolDescriptorList, protocolDescriptorList); -+ QBluetoothServiceInfo::Sequence publicBrowse; -+ publicBrowse << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::PublicBrowseGroup)); -+ serviceInfo.setAttribute(QBluetoothServiceInfo::BrowseGroupList, publicBrowse); -+ -+ if (!customUuids.contains(i)) { -+ //if we don't have custom uuid use it as class id as well -+ QBluetoothServiceInfo::Sequence classId; -+ classId << QVariant::fromValue(uuid); -+ serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId); -+ auto clsId = QBluetoothUuid::ServiceClassUuid(uuid.toUInt16()); -+ serviceInfo.setServiceName(QBluetoothUuid::serviceClassToString(clsId)); -+ } -+ -+ //Check if the service is in the uuidFilter -+ if (!uuidFilter.isEmpty()) { -+ bool match = uuidFilter.contains(serviceInfo.serviceUuid()); -+ match |= uuidFilter.contains(QBluetoothSocketPrivateOpenHarmony::reverseUuid(serviceInfo.serviceUuid())); -+ for (const auto &uuid : qAsConst(uuidFilter)) { -+ match |= serviceInfo.serviceClassUuids().contains(uuid); -+ match |= serviceInfo.serviceClassUuids().contains(QBluetoothSocketPrivateOpenHarmony::reverseUuid(uuid)); -+ } -+ -+ if (!match) -+ continue; -+ } -+ -+ //don't include the service if we already discovered it before -+ if (!isDuplicatedService(serviceInfo)) { -+ discoveredServices << serviceInfo; -+ //qCDebug(QT_BT_OPENHARMONY) << serviceInfo; -+ emit q->serviceDiscovered(serviceInfo); -+ } -+ } -+} -+ -+void QBluetoothServiceDiscoveryAgentPrivate::_q_fetchUuidsTimeout() -+{ -+ if (sdpCache.isEmpty()) -+ return; -+ -+ QPair > pair; -+ const QList keys = sdpCache.keys(); -+ for (const QBluetoothAddress &key : keys) { -+ pair = sdpCache.take(key); -+ populateDiscoveredServices(pair.first, pair.second); -+ } -+ -+ Q_ASSERT(sdpCache.isEmpty()); -+ -+ //kill receiver to limit load of signals -+ //receiver->unregisterReceiver(); -+ receiver->deleteLater(); -+ receiver = nullptr; -+ _q_serviceDiscoveryFinished(); -+} -+ -+void QBluetoothServiceDiscoveryAgentPrivate::_q_hostModeStateChanged(QBluetoothLocalDevice::HostMode state) -+{ -+ if (discoveryState() == QBluetoothServiceDiscoveryAgentPrivate::ServiceDiscovery && -+ state == QBluetoothLocalDevice::HostPoweredOff ) { -+ -+ discoveredDevices.clear(); -+ sdpCache.clear(); -+ error = QBluetoothServiceDiscoveryAgent::PoweredOffError; -+ errorString = QBluetoothServiceDiscoveryAgent::tr("Device is powered off"); -+ -+ //kill receiver to limit load of signals -+ //receiver->unregisterReceiver(); -+ receiver->deleteLater(); -+ receiver = nullptr; -+ -+ Q_Q(QBluetoothServiceDiscoveryAgent); -+ emit q->error(error); -+ _q_serviceDiscoveryFinished(); -+ } -+} -+ -+QT_END_NAMESPACE -diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.h b/src/bluetooth/qbluetoothservicediscoveryagent_p.h -index dbf8b1d4..c7dfacde 100644 ---- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h -+++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h -@@ -86,6 +86,12 @@ class LocalDeviceBroadcastReceiver; - #include - #endif - -+#ifdef QT_OPENHARMONY_BLUETOOTH -+class QOpenHarmonyJsObject; -+class OPenHarmonyServiceDiscoveryReceiver; -+class OPenHarmonyLocalDeviceBroadcastReceiver; -+#endif -+ - #ifdef QT_WINRT_BLUETOOTH - class QWinRTBluetoothServiceDiscoveryWorker; - #endif -@@ -150,6 +156,15 @@ public: - void _q_hostModeStateChanged(QBluetoothLocalDevice::HostMode state); - #endif - -+#ifdef QT_OPENHARMONY_BLUETOOTH -+ void _q_processFetchedUuids(const QBluetoothAddress &address, const QList &uuids); -+ -+ void populateDiscoveredServices(const QBluetoothDeviceInfo &remoteDevice, -+ const QList &uuids); -+ void _q_fetchUuidsTimeout(); -+ void _q_hostModeStateChanged(QBluetoothLocalDevice::HostMode state); -+#endif -+ - private: - void start(const QBluetoothAddress &address); - void stop(); -@@ -200,6 +215,15 @@ private: - QMap > > sdpCache; - #endif - -+#ifdef QT_OPENHARMONY_BLUETOOTH -+ OPenHarmonyServiceDiscoveryReceiver *receiver = nullptr; -+ OPenHarmonyLocalDeviceBroadcastReceiver *localDeviceReceiver = nullptr; -+ -+ QSharedPointer m_jsBtMgr; -+ QSharedPointer m_jsAdapter; -+ QMap > > sdpCache; -+#endif -+ - #ifdef QT_WINRT_BLUETOOTH - private slots: - void processFoundService(quint64 deviceAddress, const QBluetoothServiceInfo &info); -diff --git a/src/bluetooth/qbluetoothserviceinfo_ohos.cpp b/src/bluetooth/qbluetoothserviceinfo_ohos.cpp -new file mode 100644 -index 00000000..1c59c713 ---- /dev/null -+++ b/src/bluetooth/qbluetoothserviceinfo_ohos.cpp -@@ -0,0 +1,100 @@ -+#include -+ -+#include "qbluetoothhostinfo.h" -+#include "qbluetoothlocaldevice.h" -+#include "qbluetoothserviceinfo.h" -+#include "qbluetoothserviceinfo_p.h" -+#include "qbluetoothserver_p.h" -+#include "qbluetoothserver.h" -+ -+QT_BEGIN_NAMESPACE -+ -+Q_DECLARE_LOGGING_CATEGORY(QT_BT_OPENHARMONY) -+ -+extern QHash __fakeServerPorts; -+ -+QBluetoothServiceInfoPrivate::QBluetoothServiceInfoPrivate() -+ : registered(false) -+{ -+} -+ -+QBluetoothServiceInfoPrivate::~QBluetoothServiceInfoPrivate() -+{ -+} -+ -+bool QBluetoothServiceInfoPrivate::isRegistered() const -+{ -+ return registered; -+} -+ -+bool QBluetoothServiceInfoPrivate::unregisterService() -+{ -+ if (!registered) -+ return false; -+ -+ QBluetoothServerPrivate *sPriv = __fakeServerPorts.key(serverChannel()); -+ if (!sPriv) { -+ //QBluetoothServer::close() was called without prior call to unregisterService(). -+ //Now it is unregistered anyway. -+ registered = false; -+ return true; -+ } -+ -+ bool result = sPriv->deactivateActiveListening(); -+ if (!result) -+ return false; -+ -+ registered = false; -+ return true; -+} -+ -+bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress& localAdapter) -+{ -+ const QList localDevices = QBluetoothLocalDevice::allDevices(); -+ if (!localDevices.count()) -+ return false; //no Bluetooth device -+ -+ if (!localAdapter.isNull()) { -+ bool found = false; -+ for (const QBluetoothHostInfo &hostInfo : localDevices) { -+ if (hostInfo.address() == localAdapter) { -+ found = true; -+ break; -+ } -+ } -+ -+ if (!found) { -+ qCWarning(QT_BT_OPENHARMONY) << localAdapter.toString() << "is not a valid local Bt adapter"; -+ return false; -+ } -+ } -+ -+ //already registered on local adapter => nothing to do -+ if (registered) -+ return false; -+ -+ if (protocolDescriptor(QBluetoothUuid::Rfcomm).isEmpty()) { -+ qCWarning(QT_BT_OPENHARMONY) << Q_FUNC_INFO << "Only RFCOMM services can be registered on Android"; -+ return false; -+ } -+ -+ QBluetoothServerPrivate *sPriv = __fakeServerPorts.key(serverChannel()); -+ if (!sPriv) -+ return false; -+ -+ //tell the server what service name and uuid our listener should have -+ //and start the real listener -+ bool result = sPriv->initiateActiveListening( -+ attributes.value(QBluetoothServiceInfo::ServiceId).value(), -+ attributes.value(QBluetoothServiceInfo::ServiceName).toString()); -+ if (!result) { -+ return false; -+ } -+ -+ -+ registered = true; -+ return true; -+} -+ -+ -+QT_END_NAMESPACE -diff --git a/src/bluetooth/qbluetoothsocket.cpp b/src/bluetooth/qbluetoothsocket.cpp -index daa589bb..2abd1067 100644 ---- a/src/bluetooth/qbluetoothsocket.cpp -+++ b/src/bluetooth/qbluetoothsocket.cpp -@@ -45,6 +45,8 @@ - #include "bluez/bluez5_helper_p.h" - #elif defined(QT_ANDROID_BLUETOOTH) - #include "qbluetoothsocket_android_p.h" -+#elif defined(QT_OPENHARMONY_BLUETOOTH) -+#include "qbluetoothsocket_ohos_p.h" - #elif defined(QT_WINRT_BLUETOOTH) - #include "qbluetoothsocket_winrt_p.h" - #else -@@ -52,7 +54,6 @@ - #endif - - #include "qbluetoothservicediscoveryagent.h" -- - #include - #include - -@@ -267,6 +268,8 @@ static QBluetoothSocketBasePrivate *createSocketPrivate() - return new QBluetoothSocketPrivateAndroid(); - #elif defined(QT_WINRT_BLUETOOTH) - return new QBluetoothSocketPrivateWinRT(); -+#elif defined(QT_OPENHARMONY_BLUETOOTH) -+ return new QBluetoothSocketPrivateOpenHarmony(); - #else - return new QBluetoothSocketPrivateDummy(); - #endif -diff --git a/src/bluetooth/qbluetoothsocket.h b/src/bluetooth/qbluetoothsocket.h -index d2535544..e4ca30e4 100644 ---- a/src/bluetooth/qbluetoothsocket.h -+++ b/src/bluetooth/qbluetoothsocket.h -@@ -72,10 +72,12 @@ class Q_BLUETOOTH_EXPORT QBluetoothSocket : public QIODevice - friend class QBluetoothServerPrivate; - friend class QBluetoothSocketPrivate; - friend class QBluetoothSocketPrivateAndroid; -+ friend class QBluetoothSocketPrivateOpenHarmony; - friend class QBluetoothSocketPrivateBluez; - friend class QBluetoothSocketPrivateBluezDBus; - friend class QBluetoothSocketPrivateDummy; - friend class QBluetoothSocketPrivateWinRT; -+ friend class QBluetoothSocketPrivateOPenharmony; - - public: - -diff --git a/src/bluetooth/qbluetoothsocket_ohos.cpp b/src/bluetooth/qbluetoothsocket_ohos.cpp -new file mode 100644 -index 00000000..6f301fdb ---- /dev/null -+++ b/src/bluetooth/qbluetoothsocket_ohos.cpp -@@ -0,0 +1,501 @@ -+#include "qbluetoothsocket.h" -+#include "qbluetoothsocket_ohos_p.h" -+#include "qbluetoothaddress.h" -+#include "qbluetoothdeviceinfo.h" -+#include "qbluetoothserviceinfo.h" -+#include "openharmony/bluetoothdatareceiver_p.h" -+#include -+#include -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+Q_DECLARE_LOGGING_CATEGORY(QT_BT_OPENHARMONY) -+ -+ -+class SocketConnectWorker : public QObject -+{ -+ Q_OBJECT -+public: -+ SocketConnectWorker(QSharedPointer jsSocket) -+ : QObject(), -+ mSocketObject(jsSocket) -+ { -+ } -+ -+signals: -+ void socketConnectDone(QSharedPointer jsSocket); -+ void socketConnectFailed(QSharedPointer jsSocket); -+public slots: -+ void connectSocket() -+ { -+ qCDebug(QT_BT_OPENHARMONY) << "Connecting socket"; -+ if (!mSocketObject->call("connect")) { -+ emit socketConnectFailed(mSocketObject); -+ QThread::currentThread()->quit(); -+ return; -+ } -+ qCDebug(QT_BT_OPENHARMONY) << "Socket connection established"; -+ emit socketConnectDone(mSocketObject); -+ } -+ -+ void closeSocket() -+ { -+ qCDebug(QT_BT_OPENHARMONY) << "Executing queued closeSocket()"; -+ -+ if (!mSocketObject->call("close")) { -+ qCWarning(QT_BT_OPENHARMONY) << "Error during closure of abandoned socket"; -+ } -+ -+ QThread::currentThread()->quit(); -+ } -+ -+private: -+ QSharedPointer mSocketObject; -+}; -+ -+class WorkerThread: public QThread -+{ -+ Q_OBJECT -+public: -+ WorkerThread() -+ : QThread(), workerPointer(0) -+ { -+ } -+ -+ // Runs in same thread as QBluetoothSocketPrivateOpenHarmony -+ void setupWorker(QBluetoothSocketPrivateOpenHarmony* d_ptr, QSharedPointer jsSocket) -+ { -+ SocketConnectWorker* worker = new SocketConnectWorker(jsSocket); -+ worker->moveToThread(this); -+ -+ connect(this, &QThread::finished, worker, &QObject::deleteLater); -+ connect(this, &QThread::finished, this, &QObject::deleteLater); -+ connect(d_ptr, &QBluetoothSocketPrivateOpenHarmony::connectJsSocket, -+ worker, &SocketConnectWorker::connectSocket); -+ connect(d_ptr, &QBluetoothSocketPrivateOpenHarmony::closeJsSocket, -+ worker, &SocketConnectWorker::closeSocket); -+ connect(worker, &SocketConnectWorker::socketConnectDone, -+ d_ptr, &QBluetoothSocketPrivateOpenHarmony::socketConnectSuccess); -+ connect(worker, &SocketConnectWorker::socketConnectFailed, -+ d_ptr, &QBluetoothSocketPrivateOpenHarmony::defaultSocketConnectFailed); -+ -+ workerPointer = worker; -+ } -+ -+private: -+ QPointer workerPointer; -+}; -+ -+QBluetoothSocketPrivateOpenHarmony::QBluetoothSocketPrivateOpenHarmony() -+ : m_receiver(nullptr) -+{ -+ secFlags = QBluetooth::Secure; -+ m_jsBluetoothManager = qJsObjectLoader->create("JsBluetoothManager"); -+ qRegisterMetaType(); -+ qRegisterMetaType(); -+} -+ -+QBluetoothSocketPrivateOpenHarmony::~QBluetoothSocketPrivateOpenHarmony() -+{ -+ if (state != QBluetoothSocket::UnconnectedState) -+ emit closeJsSocket(); -+} -+ -+bool QBluetoothSocketPrivateOpenHarmony::ensureNativeSocket(QBluetoothServiceInfo::Protocol type) -+{ -+ socketType = type; -+ if (socketType == QBluetoothServiceInfo::RfcommProtocol) -+ return true; -+ -+ return false; -+} -+ -+void QBluetoothSocketPrivateOpenHarmony::connectToServiceHelper(const QBluetoothAddress &address, -+ const QBluetoothUuid &uuid, -+ QIODevice::OpenMode openMode) -+{ -+ Q_Q(QBluetoothSocket); -+ Q_UNUSED(openMode); -+ -+ qCDebug(QT_BT_OPENHARMONY) << "connectToServiceHelper()" << address.toString() << uuid.toString(); -+ -+ q->setSocketState(QBluetoothSocket::ConnectingState); -+ -+ if (!m_jsBluetoothManager->call("isSupport")) { -+ qCWarning(QT_BT_OPENHARMONY) << "Device does not support Bluetooth"; -+ errorString = QBluetoothSocket::tr("Device does not support Bluetooth"); -+ q->setSocketError(QBluetoothSocket::NetworkError); -+ q->setSocketState(QBluetoothSocket::UnconnectedState); -+ return; -+ } -+ -+ const int state = m_jsBluetoothManager->call("getState"); -+ if (state != 2 ) { -+ qCWarning(QT_BT_OPENHARMONY) << "Bt device offline"; -+ errorString = QBluetoothSocket::tr("Device is powered off"); -+ q->setSocketError(QBluetoothSocket::NetworkError); -+ q->setSocketState(QBluetoothSocket::UnconnectedState); -+ return; -+ } -+ -+ static int index = 0; -+ QString name = QString("QBluetoothSocketPrivateOpenHarmony_%1_%2").arg("device").arg(index); -+ bool createResult = m_jsBluetoothManager->call("createBluetoothDevice", name, address.toString()); -+ if (!createResult) { -+ errorString = QBluetoothSocket::tr("Cannot access address %1", "%1 = Bt address e.g. 11:22:33:44:55:66").arg(address.toString()); -+ q->setSocketError(QBluetoothSocket::HostNotFoundError); -+ q->setSocketState(QBluetoothSocket::UnconnectedState); -+ } -+ -+ m_jsBluetoothDevice = qJsObjectLoader->create(name); -+ m_names.insert(m_jsBluetoothDevice.data(), name); -+ -+ name = QString("QBluetoothSocketPrivateOpenHarmony_%1_%2").arg("socket").arg(index); -+ index++; -+ createResult = m_jsBluetoothDevice->call("createSocket", name, QVariant(qlonglong(static_cast(this)))); -+ if (!createResult) { -+ m_jsBluetoothDevice.clear(); -+ errorString = QBluetoothSocket::tr("Cannot connect to %1 on %2", -+ "%1 = uuid, %2 = Bt address").arg(uuid.toString()).arg(address.toString()); -+ q->setSocketError(QBluetoothSocket::ServiceNotFoundError); -+ q->setSocketState(QBluetoothSocket::UnconnectedState); -+ } -+ -+ m_jsBluetoothSocket = qJsObjectLoader->create(name); -+ m_names.insert(m_jsBluetoothSocket.data(), name); -+ -+ //cut leading { and trailing } {xxx-xxx} -+ QString tempUuid = uuid.toString(); -+ tempUuid.chop(1); //remove trailing '}' -+ tempUuid.remove(0, 1); //remove first '{' -+ bool result = m_jsBluetoothSocket->call("connect", uuid, secFlags != QBluetooth::NoSecurity); -+ -+ WorkerThread *workerThread = new WorkerThread(); -+ workerThread->setupWorker(this, m_jsBluetoothSocket); -+ workerThread->start(); -+ emit connectJsSocket(); -+} -+ -+void QBluetoothSocketPrivateOpenHarmony::connectToService( -+ const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode) -+{ -+ Q_Q(QBluetoothSocket); -+ -+ if (q->state() != QBluetoothSocket::UnconnectedState -+ && q->state() != QBluetoothSocket::ServiceLookupState) { -+ qCWarning(QT_BT_OPENHARMONY) << "QBluetoothSocketPrivateOpenHarmony::connectToService called on busy socket"; -+ errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress"); -+ q->setSocketError(QBluetoothSocket::OperationError); -+ return; -+ } -+ -+ auto protocol = service.socketProtocol(); -+ switch (protocol) { -+ case QBluetoothServiceInfo::L2capProtocol: -+ case QBluetoothServiceInfo::UnknownProtocol: -+ qCWarning(QT_BT_OPENHARMONY) << "Changing socket protocol to RFCOMM"; -+ protocol = QBluetoothServiceInfo::RfcommProtocol; -+ break; -+ case QBluetoothServiceInfo::RfcommProtocol: -+ break; -+ } -+ -+ if (!ensureNativeSocket(protocol)) { -+ errorString = QBluetoothSocket::tr("Socket type not supported"); -+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); -+ return; -+ } -+ connectToServiceHelper(service.device().address(), service.serviceUuid(), openMode); -+} -+ -+void QBluetoothSocketPrivateOpenHarmony::connectToService( -+ const QBluetoothAddress &address, const QBluetoothUuid &uuid, -+ QIODevice::OpenMode openMode) -+{ -+ Q_Q(QBluetoothSocket); -+ -+ if (q->state() != QBluetoothSocket::UnconnectedState) { -+ qCWarning(QT_BT_OPENHARMONY) << "QBluetoothSocketPrivateOpenHarmony::connectToService called on busy socket"; -+ errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress"); -+ q->setSocketError(QBluetoothSocket::OperationError); -+ return; -+ } -+ -+ if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) { -+ qCWarning(QT_BT_OPENHARMONY) << "QBluetoothSocketPrivateOpenHarmony::connectToService cannot " -+ "connect with 'UnknownProtocol' (type provided by given service)"; -+ errorString = QBluetoothSocket::tr("Socket type not supported"); -+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); -+ return; -+ } -+ -+ if (!ensureNativeSocket(q->socketType())) { -+ errorString = QBluetoothSocket::tr("Socket type not supported"); -+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); -+ return; -+ } -+ connectToServiceHelper(address, uuid, openMode); -+} -+ -+void QBluetoothSocketPrivateOpenHarmony::connectToService( -+ const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode) -+{ -+ Q_UNUSED(port); -+ Q_UNUSED(openMode); -+ Q_UNUSED(address); -+ -+ Q_Q(QBluetoothSocket); -+ -+ errorString = tr("Connecting to port is not supported"); -+ q->setSocketError(QBluetoothSocket::ServiceNotFoundError); -+ qCWarning(QT_BT_OPENHARMONY) << "Connecting to port is not supported"; -+} -+ -+void QBluetoothSocketPrivateOpenHarmony::socketConnectSuccess(QSharedPointer jsSocket) -+{ -+ Q_Q(QBluetoothSocket); -+ -+ // test we didn't get a success from a previous connect -+ // which was cleaned up late -+ if (jsSocket != m_jsBluetoothSocket) -+ return; -+ -+ -+ m_receiver = new BluetoothDataReceiver(this); -+ QObject::connect(m_receiver, SIGNAL(dataAvailable()), -+ q, SIGNAL(readyRead()), Qt::QueuedConnection); -+ -+ q->setOpenMode(QIODevice::ReadWrite|QIODevice::Unbuffered); -+ -+ q->setSocketState(QBluetoothSocket::ConnectedState); -+} -+ -+void QBluetoothSocketPrivateOpenHarmony::defaultSocketConnectFailed(QSharedPointer jsSocket) -+{ -+ Q_Q(QBluetoothSocket); -+ -+ // test we didn't get a fail from a previous connect -+ // which was cleaned up late - should be same socket -+ if (m_jsBluetoothSocket != jsSocket) -+ return; -+ -+ errorString = QBluetoothSocket::tr("Connection to service failed"); -+ m_jsBluetoothDevice->call("closeSocket", m_names.value(m_jsBluetoothSocket.data())); -+ m_jsBluetoothManager->call("destroyBluetoothDevice", m_names.value(m_jsBluetoothDevice.data())); -+ m_jsBluetoothSocket.clear(); -+ m_jsBluetoothDevice.clear(); -+ q->setSocketError(QBluetoothSocket::ServiceNotFoundError); -+ q->setSocketState(QBluetoothSocket::UnconnectedState); -+ qCWarning(QT_BT_OPENHARMONY) << "Workaround failed"; -+} -+ -+void QBluetoothSocketPrivateOpenHarmony::abort() -+{ -+ if (state == QBluetoothSocket::UnconnectedState) -+ return; -+ -+ if (m_jsBluetoothSocket->isValid()) { -+ -+ emit closeJsSocket(); -+ m_jsBluetoothDevice->call("closeSocket", m_names.value(m_jsBluetoothSocket.data())); -+ m_jsBluetoothManager->call("destroyBluetoothDevice", m_names.value(m_jsBluetoothDevice.data())); -+ m_jsBluetoothSocket.clear(); -+ m_jsBluetoothDevice.clear(); -+ -+ if (m_receiver) { -+ m_receiver = 0; -+ m_receiver->deleteLater(); -+ } else { -+ Q_Q(QBluetoothSocket); -+ q->setOpenMode(QIODevice::NotOpen); -+ q->setSocketState(QBluetoothSocket::UnconnectedState); -+ emit q->readChannelFinished(); -+ } -+ } -+} -+ -+QString QBluetoothSocketPrivateOpenHarmony::localName() const -+{ -+ return m_jsBluetoothManager->call("getLocalName"); -+} -+ -+QBluetoothAddress QBluetoothSocketPrivateOpenHarmony::localAddress() const -+{ -+ // Todo harmony 没有提供接口 -+ QString result; -+ -+ return QBluetoothAddress(result); -+} -+ -+quint16 QBluetoothSocketPrivateOpenHarmony::localPort() const -+{ -+ return 0; -+} -+ -+QString QBluetoothSocketPrivateOpenHarmony::peerName() const -+{ -+ if (!m_jsBluetoothDevice->isValid()) -+ return QString(); -+ -+ return m_jsBluetoothDevice->call("getName"); -+} -+ -+QBluetoothAddress QBluetoothSocketPrivateOpenHarmony::peerAddress() const -+{ -+ if (!m_jsBluetoothDevice->isValid()) -+ return QBluetoothAddress(); -+ -+ const QString address = m_jsBluetoothDevice->call("getAddress"); -+ -+ return QBluetoothAddress(address); -+} -+ -+quint16 QBluetoothSocketPrivateOpenHarmony::peerPort() const -+{ -+ // Impossible to get channel number with current openharmony API -+ return 0; -+} -+ -+qint64 QBluetoothSocketPrivateOpenHarmony::writeData(const char *data, qint64 maxSize) -+{ -+ //TODO implement buffered behavior (so far only unbuffered) -+ Q_Q(QBluetoothSocket); -+ if (state != QBluetoothSocket::ConnectedState || !m_jsBluetoothSocket->isValid()) { -+ qCWarning(QT_BT_OPENHARMONY) << "Socket::writeData: " << state; -+ errorString = QBluetoothSocket::tr("Cannot write while not connected"); -+ q->setSocketError(QBluetoothSocket::OperationError); -+ return -1; -+ } -+ -+ bool result = m_jsBluetoothSocket->call("write", QByteArray(data)); -+ -+ if (!result) { -+ qCWarning(QT_BT_OPENHARMONY) << "Error while writing"; -+ errorString = QBluetoothSocket::tr("Error during write on socket."); -+ q->setSocketError(QBluetoothSocket::NetworkError); -+ return -1; -+ } -+ -+ emit q->bytesWritten(maxSize); -+ return maxSize; -+} -+ -+qint64 QBluetoothSocketPrivateOpenHarmony::readData(char *data, qint64 maxSize) -+{ -+ Q_Q(QBluetoothSocket); -+ if (state != QBluetoothSocket::ConnectedState || m_receiver == nullptr) { -+ qCWarning(QT_BT_OPENHARMONY) << "Socket::readData: " << state ; -+ errorString = QBluetoothSocket::tr("Cannot read while not connected"); -+ q->setSocketError(QBluetoothSocket::OperationError); -+ return -1; -+ } -+ -+ return m_receiver->readData(data, maxSize); -+} -+ -+void QBluetoothSocketPrivateOpenHarmony::close() -+{ -+ abort(); -+} -+ -+bool QBluetoothSocketPrivateOpenHarmony::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType, -+ QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode) -+{ -+#if 0 -+ Q_UNUSED(socketDescriptor); -+ Q_UNUSED(socketType) -+ Q_UNUSED(socketState); -+ Q_UNUSED(openMode); -+ qCWarning(QT_BT_OPENHARMONY) << "No socket descriptor support on OPENHARMONY."; -+#endif -+ -+ Q_Q(QBluetoothSocket); -+ -+ if (q->state() != QBluetoothSocket::UnconnectedState || -1 == socketDescriptor) -+ return false; -+ -+ if (!ensureNativeSocket(socketType)) -+ return false; -+ -+ QString name = QString::number(socketDescriptor); -+ m_jsBluetoothSocket = qJsObjectLoader->create(name); -+ if (!m_jsBluetoothSocket->isValid()) { -+ errorString = QBluetoothSocket::tr("Obtaining ts object for service failed"); -+ q->setSocketError(QBluetoothSocket::NetworkError); -+ q->setSocketState(QBluetoothSocket::UnconnectedState); -+ return false; -+ } -+ -+ m_jsBluetoothSocket->call("setSocketNumber", socketDescriptor); -+ m_jsBluetoothSocket->call("setId", QVariant(qlonglong(static_cast(this)))); -+ -+ m_names.insert(m_jsBluetoothSocket.data(), name); -+ WorkerThread *workerThread = new WorkerThread(); -+ workerThread->setupWorker(this, m_jsBluetoothSocket); -+ workerThread->start(); -+ -+ q->setOpenMode(openMode | QIODevice::Unbuffered); -+ q->setSocketState(socketState); -+ -+ return true; -+} -+ -+qint64 QBluetoothSocketPrivateOpenHarmony::bytesAvailable() const -+{ -+ //We cannot access buffer directly as it is part of different thread -+ if (m_receiver) -+ return m_receiver->bytesAvailable(); -+ -+ return 0; -+} -+ -+qint64 QBluetoothSocketPrivateOpenHarmony::bytesToWrite() const -+{ -+ return 0; // nothing because always unbuffered -+} -+ -+bool QBluetoothSocketPrivateOpenHarmony::start() -+{ -+ if (!m_jsBluetoothSocket->isValid()) -+ return false; -+ return !m_jsBluetoothSocket->call("start"); -+} -+ -+/* -+ * This function is part of a workaround for QTBUG-61392 -+ * -+ * Returns null uuid if the given \a serviceUuid is not a uuid -+ * derived from the Bluetooth base uuid. -+ */ -+QBluetoothUuid QBluetoothSocketPrivateOpenHarmony::reverseUuid(const QBluetoothUuid &serviceUuid) -+{ -+ if (serviceUuid.isNull()) -+ return QBluetoothUuid(); -+ -+ bool isBaseUuid = false; -+ serviceUuid.toUInt32(&isBaseUuid); -+ if (isBaseUuid) -+ return serviceUuid; -+ -+ const quint128 original = serviceUuid.toUInt128(); -+ quint128 reversed; -+ for (int i = 0; i < 16; i++) -+ reversed.data[15-i] = original.data[i]; -+ return QBluetoothUuid{reversed}; -+} -+ -+bool QBluetoothSocketPrivateOpenHarmony::canReadLine() const -+{ -+ // We cannot access buffer directly as it is part of different thread -+ if (m_receiver) -+ return m_receiver->canReadLine(); -+ -+ return false; -+} -+ -+QT_END_NAMESPACE -+ -+#include -diff --git a/src/bluetooth/qbluetoothsocket_ohos_p.h b/src/bluetooth/qbluetoothsocket_ohos_p.h -new file mode 100644 -index 00000000..e29cd19f ---- /dev/null -+++ b/src/bluetooth/qbluetoothsocket_ohos_p.h -@@ -0,0 +1,78 @@ -+#ifndef QBLUETOOTHSOCKET_OPENHARMONY_P_H -+#define QBLUETOOTHSOCKET_OPENHARMONY_P_H -+ -+#include "qbluetoothsocketbase_p.h" -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+class QOpenHarmonyJsObject; -+class BluetoothDataReceiver; -+class QBluetoothSocketPrivateOpenHarmony final : public QBluetoothSocketBasePrivate -+{ -+ Q_OBJECT -+ friend class QBluetoothServerPrivate; -+ -+public: -+ QBluetoothSocketPrivateOpenHarmony(); -+ ~QBluetoothSocketPrivateOpenHarmony() override; -+ -+ //On openharmony we connect using the uuid not the port -+ void connectToServiceHelper(const QBluetoothAddress &address, const QBluetoothUuid &uuid, -+ QIODevice::OpenMode openMode) override; -+ -+ void connectToService(const QBluetoothServiceInfo &service, -+ QIODevice::OpenMode openMode) override; -+ void connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid, -+ QIODevice::OpenMode openMode) override; -+ void connectToService(const QBluetoothAddress &address, quint16 port, -+ QIODevice::OpenMode openMode) override; -+ -+ -+ bool ensureNativeSocket(QBluetoothServiceInfo::Protocol type) override; -+ -+ QString localName() const override; -+ QBluetoothAddress localAddress() const override; -+ quint16 localPort() const override; -+ -+ QString peerName() const override; -+ QBluetoothAddress peerAddress() const override; -+ quint16 peerPort() const override; -+ -+ void abort() override; -+ void close() override; -+ -+ qint64 writeData(const char *data, qint64 maxSize) override; -+ qint64 readData(char *data, qint64 maxSize) override; -+ -+ bool setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType, -+ QBluetoothSocket::SocketState socketState = QBluetoothSocket::ConnectedState, -+ QBluetoothSocket::OpenMode openMode = QBluetoothSocket::ReadWrite) override; -+ -+ qint64 bytesAvailable() const override; -+ bool canReadLine() const override; -+ qint64 bytesToWrite() const override; -+ -+ bool start(); -+ -+ static QBluetoothUuid reverseUuid(const QBluetoothUuid &serviceUuid); -+ -+ BluetoothDataReceiver *m_receiver; -+ QSharedPointer m_jsBluetoothManager; -+ QSharedPointer m_jsBluetoothDevice; -+ QSharedPointer m_jsBluetoothSocket; -+ QHash m_names; -+public slots: -+ void socketConnectSuccess(QSharedPointer jsSocket); -+ void defaultSocketConnectFailed(QSharedPointer jsSocket); -+ -+signals: -+ void connectJsSocket(); -+ void closeJsSocket(); -+ -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QBLUETOOTHSOCKET_ANDROID_P_H -diff --git a/src/bluetooth/qbluetoothsocketbase_p.h b/src/bluetooth/qbluetoothsocketbase_p.h -index 410dcbbd..6c96d474 100644 ---- a/src/bluetooth/qbluetoothsocketbase_p.h -+++ b/src/bluetooth/qbluetoothsocketbase_p.h -@@ -123,7 +123,7 @@ public: - QBluetoothSocket::OpenMode openMode = QBluetoothSocket::ReadWrite) = 0; - - --#if defined(QT_ANDROID_BLUETOOTH) -+#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_OPENHARMONY_BLUETOOTH) - virtual void connectToServiceHelper(const QBluetoothAddress &address, const QBluetoothUuid &uuid, - QIODevice::OpenMode openMode) = 0; - #else -diff --git a/src/bluetooth/qbluetoothtransfermanager.cpp b/src/bluetooth/qbluetoothtransfermanager.cpp -index 165faceb..f00ac1c7 100644 ---- a/src/bluetooth/qbluetoothtransfermanager.cpp -+++ b/src/bluetooth/qbluetoothtransfermanager.cpp -@@ -122,7 +122,7 @@ QBluetoothTransferReply *QBluetoothTransferManager::put(const QBluetoothTransfer - return reply; - #else - // Android, iOS, and WinRT have no implementation --#if !defined(QT_ANDROID_BLUETOOTH) && !defined(QT_IOS_BLUETOOTH) && !defined(QT_WINRT_BLUETOOTH) -+#if !defined(QT_ANDROID_BLUETOOTH) && !defined(QT_IOS_BLUETOOTH) && !defined(QT_WINRT_BLUETOOTH) && !defined(QT_OPENHARMONY_BLUETOOTH) - printDummyWarning(); - #endif - Q_UNUSED(request); -diff --git a/src/bluetooth/qlowenergycharacteristic.h b/src/bluetooth/qlowenergycharacteristic.h -index cb747e32..a43ef1df 100644 ---- a/src/bluetooth/qlowenergycharacteristic.h -+++ b/src/bluetooth/qlowenergycharacteristic.h -@@ -98,6 +98,7 @@ protected: - friend class QLowEnergyService; - friend class QLowEnergyControllerPrivate; - friend class QLowEnergyControllerPrivateAndroid; -+ friend class QLowEnergyControllerPrivateOpenHarmony; - friend class QLowEnergyControllerPrivateBluez; - friend class QLowEnergyControllerPrivateBluezDBus; - friend class QLowEnergyControllerPrivateCommon; -diff --git a/src/bluetooth/qlowenergycontroller.cpp b/src/bluetooth/qlowenergycontroller.cpp -index 6b4c17d6..0115700f 100644 ---- a/src/bluetooth/qlowenergycontroller.cpp -+++ b/src/bluetooth/qlowenergycontroller.cpp -@@ -54,6 +54,8 @@ - #include "qlowenergycontroller_bluez_p.h" - #elif defined(QT_ANDROID_BLUETOOTH) - #include "qlowenergycontroller_android_p.h" -+#elif defined(QT_OPENHARMONY_BLUETOOTH) -+#include "qlowenergycontroller_ohos_p.h" - #elif defined(QT_WINRT_BLUETOOTH) - #include "qlowenergycontroller_winrt_p.h" - #else -@@ -308,6 +310,8 @@ static QLowEnergyControllerPrivate *privateController(QLowEnergyController::Role - #elif defined(QT_ANDROID_BLUETOOTH) - Q_UNUSED(role); - return new QLowEnergyControllerPrivateAndroid(); -+#elif defined(QT_OPENHARMONY_BLUETOOTH) -+ return new QLowEnergyControllerPrivateOpenHarmony(); - #elif defined(QT_WINRT_BLUETOOTH) - Q_UNUSED(role); - return new QLowEnergyControllerPrivateWinRT(); -@@ -733,6 +737,7 @@ void QLowEnergyController::startAdvertising(const QLowEnergyAdvertisingParameter - qCWarning(QT_BT) << "Cannot start advertising in central role" << state(); - return; - } -+ qDebug() << "ooooooooooooooooooooooooo" << state(); - if (state() != UnconnectedState) { - qCWarning(QT_BT) << "Cannot start advertising in state" << state(); - return; -diff --git a/src/bluetooth/qlowenergycontroller_ohos.cpp b/src/bluetooth/qlowenergycontroller_ohos.cpp -new file mode 100644 -index 00000000..efca9358 ---- /dev/null -+++ b/src/bluetooth/qlowenergycontroller_ohos.cpp -@@ -0,0 +1,871 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include "qlowenergycontroller_ohos_p.h" -+ -+QT_BEGIN_NAMESPACE -+ -+Q_DECLARE_LOGGING_CATEGORY(QT_BT_OPENHARMONY) -+ -+static QString jsUuidfromQtUuid(const QBluetoothUuid& uuid) -+{ -+ QString output = uuid.toString(); -+ // cut off leading and trailing brackets -+ output = output.mid(1, output.size()-2); -+ -+ return output; -+} -+ -+ -+QLowEnergyControllerPrivateOpenHarmony::QLowEnergyControllerPrivateOpenHarmony() -+ : QLowEnergyControllerPrivate(), -+ hub(0) -+{ -+ registerQLowEnergyControllerMetaType(); -+} -+ -+QLowEnergyControllerPrivateOpenHarmony::~QLowEnergyControllerPrivateOpenHarmony() -+{ -+ if (role == QLowEnergyController::PeripheralRole) { -+ if (hub) -+ hub->jsObject()->callWithoutReturn("disconnectServer"); -+ } -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::init() -+{ -+ const bool isPeripheral = (role == QLowEnergyController::PeripheralRole); -+ -+ if (isPeripheral) { -+ -+ hub = new LowEnergyNotificationHub(remoteDevice, isPeripheral, this); -+ // we only connect to the peripheral role specific signals -+ // TODO add connections as they get added later on -+ connect(hub, &LowEnergyNotificationHub::connectionUpdated, -+ this, &QLowEnergyControllerPrivateOpenHarmony::connectionUpdated); -+ connect(hub, &LowEnergyNotificationHub::advertisementError, -+ this, &QLowEnergyControllerPrivateOpenHarmony::advertisementError); -+ connect(hub, &LowEnergyNotificationHub::serverCharacteristicChanged, -+ this, &QLowEnergyControllerPrivateOpenHarmony::serverCharacteristicChanged); -+ connect(hub, &LowEnergyNotificationHub::serverDescriptorWritten, -+ this, &QLowEnergyControllerPrivateOpenHarmony::serverDescriptorWritten); -+ } else { -+ hub = new LowEnergyNotificationHub(remoteDevice, isPeripheral, this); -+ // we only connect to the central role specific signals -+ connect(hub, &LowEnergyNotificationHub::connectionUpdated, -+ this, &QLowEnergyControllerPrivateOpenHarmony::connectionUpdated); -+ connect(hub, &LowEnergyNotificationHub::servicesDiscovered, -+ this, &QLowEnergyControllerPrivateOpenHarmony::servicesDiscovered); -+ connect(hub, &LowEnergyNotificationHub::serviceDetailsDiscoveryFinished, -+ this, &QLowEnergyControllerPrivateOpenHarmony::serviceDetailsDiscoveryFinished); -+ connect(hub, &LowEnergyNotificationHub::characteristicRead, -+ this, &QLowEnergyControllerPrivateOpenHarmony::characteristicRead); -+ connect(hub, &LowEnergyNotificationHub::descriptorRead, -+ this, &QLowEnergyControllerPrivateOpenHarmony::descriptorRead); -+ connect(hub, &LowEnergyNotificationHub::characteristicWritten, -+ this, &QLowEnergyControllerPrivateOpenHarmony::characteristicWritten); -+ connect(hub, &LowEnergyNotificationHub::descriptorWritten, -+ this, &QLowEnergyControllerPrivateOpenHarmony::descriptorWritten); -+ connect(hub, &LowEnergyNotificationHub::characteristicChanged, -+ this, &QLowEnergyControllerPrivateOpenHarmony::characteristicChanged); -+ } -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::connectToDevice() -+{ -+ if (!hub) -+ return; -+ -+ // required to pass unit test on default backend -+ if (remoteDevice.isNull()) { -+ qWarning() << "Invalid/null remote device address"; -+ setError(QLowEnergyController::UnknownRemoteDeviceError); -+ return; -+ } -+ -+ setState(QLowEnergyController::ConnectingState); -+ -+ if (!hub->jsObject()->isValid()) { -+ qCWarning(QT_BT_OPENHARMONY) << "Cannot initiate QtBluetoothLE"; -+ setError(QLowEnergyController::ConnectionError); -+ setState(QLowEnergyController::UnconnectedState); -+ return; -+ } -+ -+ bool result = hub->jsObject()->call("connect"); -+ if (!result) { -+ setError(QLowEnergyController::ConnectionError); -+ setState(QLowEnergyController::UnconnectedState); -+ return; -+ } -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::disconnectFromDevice() -+{ -+ QLowEnergyController::ControllerState oldState = state; -+ setState(QLowEnergyController::ClosingState); -+ -+ if (hub) { -+ if (role == QLowEnergyController::PeripheralRole) -+ hub->jsObject()->callWithoutReturn("disconnectServer"); -+ else -+ hub->jsObject()->callWithoutReturn("disconnect"); -+ } -+ -+ if (oldState == QLowEnergyController::ConnectingState) -+ setState(QLowEnergyController::UnconnectedState); -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::discoverServices() -+{ -+ if (hub && hub->jsObject()->call("discoverServices")) { -+ qCDebug(QT_BT_OPENHARMONY) << "Service discovery initiated"; -+ } else { -+ //revert to connected state -+ setError(QLowEnergyController::NetworkError); -+ setState(QLowEnergyController::ConnectedState); -+ } -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::discoverServiceDetails(const QBluetoothUuid &service) -+{ -+ if (!serviceList.contains(service)) { -+ qCWarning(QT_BT_OPENHARMONY) << "Discovery of unknown service" << service.toString() -+ << "not possible"; -+ return; -+ } -+ -+ if (!hub) -+ return; -+ -+ //cut leading { and trailing } {xxx-xxx} -+ QString tempUuid = service.toString(); -+ tempUuid.chop(1); //remove trailing '}' -+ tempUuid.remove(0, 1); //remove first '{' -+ -+ bool result = hub->jsObject()->call("discoverServiceDetails", tempUuid); -+ if (!result) { -+ QSharedPointer servicePrivate = -+ serviceList.value(service); -+ if (!servicePrivate.isNull()) { -+ servicePrivate->setError(QLowEnergyService::UnknownError); -+ servicePrivate->setState(QLowEnergyService::DiscoveryRequired); -+ } -+ qCWarning(QT_BT_OPENHARMONY) << "Cannot discover details for" << service.toString(); -+ return; -+ } -+ -+ qCDebug(QT_BT_OPENHARMONY) << "Discovery of" << service << "started"; -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::writeCharacteristic( -+ const QSharedPointer service, -+ const QLowEnergyHandle charHandle, -+ const QByteArray &newValue, -+ QLowEnergyService::WriteMode mode) -+{ -+ //TODO don't ignore WriteWithResponse, right now we assume responses -+ Q_ASSERT(!service.isNull()); -+ -+ if (!service->characteristicList.contains(charHandle)) -+ return; -+ -+ bool result = false; -+ if (hub) { -+ if (role == QLowEnergyController::CentralRole) { -+ qCDebug(QT_BT_OPENHARMONY) << "Write characteristic with handle " << charHandle -+ << newValue.toHex() << "(service:" << service->uuid -+ << ", writeWithResponse:" << (mode == QLowEnergyService::WriteWithResponse) -+ << ", signed:" << (mode == QLowEnergyService::WriteSigned) << ")"; -+ result = hub->jsObject()->call("writeCharacteristic", charHandle, newValue, (int)mode); -+ } else { // peripheral mode -+ qCDebug(QT_BT_OPENHARMONY) << "Write server characteristic with handle " << charHandle -+ << newValue.toHex() << "(service:" << service->uuid; -+ -+ const auto &characteristic = characteristicForHandle(charHandle); -+ if (characteristic.isValid()) { -+ result = hub->jsObject()->call( -+ "writeCharacteristic", -+ service->m_jsService, jsUuidfromQtUuid(characteristic.uuid()), newValue); -+ } -+ } -+ } -+ -+ if (!result) -+ service->setError(QLowEnergyService::CharacteristicWriteError); -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::writeDescriptor( -+ const QSharedPointer service, -+ const QLowEnergyHandle charHandle, -+ const QLowEnergyHandle descHandle, -+ const QByteArray &newValue) -+{ -+ Q_ASSERT(!service.isNull()); -+ -+ bool result = false; -+ if (hub) { -+ if (role == QLowEnergyController::CentralRole) { -+ qCDebug(QT_BT_OPENHARMONY) << "Write descriptor with handle " << descHandle -+ << newValue.toHex() << "(service:" << service->uuid << ")"; -+ result = hub->jsObject()->call("writeDescriptor", (int)descHandle, newValue); -+ } else { -+ const auto &characteristic = characteristicForHandle(charHandle); -+ const auto &descriptor = descriptorForHandle(descHandle); -+ if (characteristic.isValid() && descriptor.isValid()) { -+ qCDebug(QT_BT_OPENHARMONY) << "Write descriptor" << descriptor.uuid() -+ << "(service:" << service->uuid -+ << "char: " << characteristic.uuid() << ")"; -+ result = hub->jsObject()->call( -+ "writeDescriptor", service->m_jsService, jsUuidfromQtUuid(characteristic.uuid()), jsUuidfromQtUuid(descriptor.uuid()), newValue); -+ } -+ } -+ } -+ -+ if (!result) -+ service->setError(QLowEnergyService::DescriptorWriteError); -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::readCharacteristic( -+ const QSharedPointer service, -+ const QLowEnergyHandle charHandle) -+{ -+ Q_ASSERT(!service.isNull()); -+ -+ if (!service->characteristicList.contains(charHandle)) -+ return; -+ -+ bool result = false; -+ if (hub) { -+ qCDebug(QT_BT_OPENHARMONY) << "Read characteristic with handle" -+ << charHandle << service->uuid; -+ result = hub->jsObject()->call("readCharacteristic", charHandle); -+ } -+ -+ if (!result) -+ service->setError(QLowEnergyService::CharacteristicReadError); -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::readDescriptor( -+ const QSharedPointer service, -+ const QLowEnergyHandle /*charHandle*/, -+ const QLowEnergyHandle descriptorHandle) -+{ -+ Q_ASSERT(!service.isNull()); -+ -+ bool result = false; -+ if (hub) { -+ qCDebug(QT_BT_OPENHARMONY) << "Read descriptor with handle" -+ << descriptorHandle << service->uuid; -+ result = hub->jsObject()->call("readDescriptor", descriptorHandle); -+ } -+ -+ if (!result) -+ service->setError(QLowEnergyService::DescriptorReadError); -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::connectionUpdated( -+ QLowEnergyController::ControllerState newState, -+ QLowEnergyController::Error errorCode) -+{ -+ qCDebug(QT_BT_OPENHARMONY) << "Connection updated:" -+ << "error:" << errorCode -+ << "oldState:" << state -+ << "newState:" << newState; -+ -+ if (role == QLowEnergyController::PeripheralRole) -+ peripheralConnectionUpdated(newState, errorCode); -+ else -+ centralConnectionUpdated(newState, errorCode); -+} -+ -+// called if server/peripheral -+void QLowEnergyControllerPrivateOpenHarmony::peripheralConnectionUpdated( -+ QLowEnergyController::ControllerState newState, -+ QLowEnergyController::Error errorCode) -+{ -+ // Java errorCode can be larger than max QLowEnergyController::Error -+ if (errorCode > QLowEnergyController::AdvertisingError) -+ errorCode = QLowEnergyController::UnknownError; -+ -+ if (errorCode != QLowEnergyController::NoError) -+ setError(errorCode); -+ -+ const QLowEnergyController::ControllerState oldState = state; -+ setState(newState); -+ -+ // disconnect implies stop of advertisement -+ if (newState == QLowEnergyController::UnconnectedState) -+ stopAdvertising(); -+ -+ -+ Q_Q(QLowEnergyController); -+ if (oldState == QLowEnergyController::ConnectedState -+ && newState != QLowEnergyController::ConnectedState) { -+ remoteDevice.clear(); -+ remoteName.clear(); -+ emit q->disconnected(); -+ } else if (newState == QLowEnergyController::ConnectedState -+ && oldState != QLowEnergyController::ConnectedState) { -+ if (hub) { -+ remoteDevice = QBluetoothAddress(hub->jsObject()->call("remoteAddress")); -+ remoteName = hub->jsObject()->call("remoteName"); -+ } -+ emit q->connected(); -+ } -+} -+ -+// called if client/central -+void QLowEnergyControllerPrivateOpenHarmony::centralConnectionUpdated( -+ QLowEnergyController::ControllerState newState, -+ QLowEnergyController::Error errorCode) -+{ -+ Q_Q(QLowEnergyController); -+ -+ const QLowEnergyController::ControllerState oldState = state; -+ -+ if (errorCode != QLowEnergyController::NoError) { -+ // ConnectionError if transition from Connecting to Connected -+ if (oldState == QLowEnergyController::ConnectingState) { -+ setError(QLowEnergyController::ConnectionError); -+ /* There is a bug in Android, when connecting to an unconnectable -+ * device. The connection times out and Android sends error code -+ * 133 (doesn't exist) and STATE_CONNECTED. A subsequent disconnect() -+ * call never sends a STATE_DISCONNECTED either. -+ * As workaround we will trigger disconnect when we encounter -+ * error during connect attempt. This leaves the controller -+ * in a cleaner state. -+ * */ -+ newState = QLowEnergyController::UnconnectedState; -+ } -+ else -+ setError(errorCode); -+ } -+ -+ setState(newState); -+ if (newState == QLowEnergyController::UnconnectedState -+ && !(oldState == QLowEnergyController::UnconnectedState -+ || oldState == QLowEnergyController::ConnectingState)) { -+ -+ // Invalidate the services if the disconnect came from the remote end. -+ // Qtherwise we disconnected via QLowEnergyController::disconnectDevice() which -+ // triggered invalidation already -+ if (!serviceList.isEmpty()) { -+ Q_ASSERT(oldState != QLowEnergyController::ClosingState); -+ invalidateServices(); -+ } -+ emit q->disconnected(); -+ } else if (newState == QLowEnergyController::ConnectedState -+ && oldState != QLowEnergyController::ConnectedState ) { -+ emit q->connected(); -+ } -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::servicesDiscovered( -+ QLowEnergyController::Error errorCode, const QString &foundServices) -+{ -+ Q_Q(QLowEnergyController); -+ -+ if (errorCode == QLowEnergyController::NoError) { -+ const QStringList list = foundServices.split(QStringLiteral(" "), QString::SkipEmptyParts); -+ for (const QString &entry : list) { -+ const QBluetoothUuid service(entry); -+ if (service.isNull()) -+ return; -+ -+ QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate(); -+ priv->uuid = service; -+ priv->setController(this); -+ -+ QSharedPointer pointer(priv); -+ serviceList.insert(service, pointer); -+ -+ emit q->serviceDiscovered(QBluetoothUuid(entry)); -+ } -+ -+ setState(QLowEnergyController::DiscoveredState); -+ emit q->discoveryFinished(); -+ } else { -+ setError(errorCode); -+ setState(QLowEnergyController::ConnectedState); -+ } -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::serviceDetailsDiscoveryFinished( -+ const QString &serviceUuid, int startHandle, int endHandle) -+{ -+ const QBluetoothUuid service(serviceUuid); -+ if (!serviceList.contains(service)) { -+ qCWarning(QT_BT_OPENHARMONY) << "Discovery done of unknown service:" -+ << service.toString(); -+ return; -+ } -+ -+ //update service data -+ QSharedPointer pointer = -+ serviceList.value(service); -+ pointer->startHandle = startHandle; -+ pointer->endHandle = endHandle; -+ -+ if (hub && hub->jsObject()->isValid()) { -+ QString jsIncludes = hub->jsObject()->call("includedServices", serviceUuid); -+ if (!jsIncludes.isEmpty()) { -+ const QStringList list = jsIncludes.split(QStringLiteral(" "), QString::SkipEmptyParts); -+ for (const QString &entry : list) { -+ const QBluetoothUuid service(entry); -+ if (service.isNull()) -+ return; -+ -+ pointer->includedServices.append(service); -+ -+ // update the type of the included service -+ QSharedPointer otherService = -+ serviceList.value(service); -+ if (!otherService.isNull()) -+ otherService->type |= QLowEnergyService::IncludedService; -+ } -+ } -+ } -+ -+ qCDebug(QT_BT_OPENHARMONY) << "Service" << serviceUuid << "discovered (start:" -+ << startHandle << "end:" << endHandle << ")" << pointer.data(); -+ -+ pointer->setState(QLowEnergyService::ServiceDiscovered); -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::characteristicRead( -+ const QBluetoothUuid &serviceUuid, int handle, -+ const QBluetoothUuid &charUuid, int properties, const QByteArray &data) -+{ -+ if (!serviceList.contains(serviceUuid)) -+ return; -+ -+ QSharedPointer service = -+ serviceList.value(serviceUuid); -+ QLowEnergyHandle charHandle = handle; -+ -+ QLowEnergyServicePrivate::CharData &charDetails = -+ service->characteristicList[charHandle]; -+ -+ //Android uses same property value as Qt which is the Bluetooth LE standard -+ charDetails.properties = QLowEnergyCharacteristic::PropertyType(properties); -+ charDetails.uuid = charUuid; -+ charDetails.value = data; -+ //value handle always one larger than characteristics value handle -+ charDetails.valueHandle = charHandle + 1; -+ -+ if (service->state == QLowEnergyService::ServiceDiscovered) { -+ QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle); -+ if (!characteristic.isValid()) { -+ qCWarning(QT_BT_OPENHARMONY) << "characteristicRead: Cannot find characteristic"; -+ return; -+ } -+ emit service->characteristicRead(characteristic, data); -+ } -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::descriptorRead( -+ const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid, -+ int descHandle, const QBluetoothUuid &descUuid, const QByteArray &data) -+{ -+ if (!serviceList.contains(serviceUuid)) -+ return; -+ -+ QSharedPointer service = -+ serviceList.value(serviceUuid); -+ -+ bool entryUpdated = false; -+ -+ CharacteristicDataMap::iterator charIt = service->characteristicList.begin(); -+ for ( ; charIt != service->characteristicList.end(); ++charIt) { -+ QLowEnergyServicePrivate::CharData &charDetails = charIt.value(); -+ -+ if (charDetails.uuid != charUuid) -+ continue; -+ -+ // new entry created if it doesn't exist -+ QLowEnergyServicePrivate::DescData &descDetails = -+ charDetails.descriptorList[descHandle]; -+ descDetails.uuid = descUuid; -+ descDetails.value = data; -+ entryUpdated = true; -+ break; -+ } -+ -+ if (!entryUpdated) { -+ qCWarning(QT_BT_OPENHARMONY) << "Cannot find/update descriptor" -+ << descUuid << charUuid << serviceUuid; -+ } else if (service->state == QLowEnergyService::ServiceDiscovered){ -+ QLowEnergyDescriptor descriptor = descriptorForHandle(descHandle); -+ if (!descriptor.isValid()) { -+ qCWarning(QT_BT_OPENHARMONY) << "descriptorRead: Cannot find descriptor"; -+ return; -+ } -+ emit service->descriptorRead(descriptor, data); -+ } -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::characteristicWritten( -+ int charHandle, const QByteArray &data, QLowEnergyService::ServiceError errorCode) -+{ -+ QSharedPointer service = -+ serviceForHandle(charHandle); -+ if (service.isNull()) -+ return; -+ -+ qCDebug(QT_BT_OPENHARMONY) << "Characteristic write confirmation" << service->uuid -+ << charHandle << data.toHex() << errorCode; -+ -+ if (errorCode != QLowEnergyService::NoError) { -+ service->setError(errorCode); -+ return; -+ } -+ -+ QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle); -+ if (!characteristic.isValid()) { -+ qCWarning(QT_BT_OPENHARMONY) << "characteristicWritten: Cannot find characteristic"; -+ return; -+ } -+ -+ // only update cache when property is readable. Otherwise it remains -+ // empty. -+ if (characteristic.properties() & QLowEnergyCharacteristic::Read) -+ updateValueOfCharacteristic(charHandle, data, false); -+ emit service->characteristicWritten(characteristic, data); -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::descriptorWritten( -+ int descHandle, const QByteArray &data, QLowEnergyService::ServiceError errorCode) -+{ -+ QSharedPointer service = -+ serviceForHandle(descHandle); -+ if (service.isNull()) -+ return; -+ -+ qCDebug(QT_BT_OPENHARMONY) << "Descriptor write confirmation" << service->uuid -+ << descHandle << data.toHex() << errorCode; -+ -+ if (errorCode != QLowEnergyService::NoError) { -+ service->setError(errorCode); -+ return; -+ } -+ -+ QLowEnergyDescriptor descriptor = descriptorForHandle(descHandle); -+ if (!descriptor.isValid()) { -+ qCWarning(QT_BT_OPENHARMONY) << "descriptorWritten: Cannot find descriptor"; -+ return; -+ } -+ -+ updateValueOfDescriptor(descriptor.characteristicHandle(), -+ descHandle, data, false); -+ emit service->descriptorWritten(descriptor, data); -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::serverDescriptorWritten( -+ const QBluetoothUuid &serviceUuid, const QBluetoothUuid &characteristicUuid, const QBluetoothUuid &descriptorUuid, const QByteArray &newValue) -+{ -+ qCDebug(QT_BT_OPENHARMONY) << "Server descriptor change notification" << newValue.toHex(); -+ -+ if (serviceUuid.isNull() || characteristicUuid.isNull() || descriptorUuid.isNull()) -+ return; -+ -+ if (!localServices.contains(serviceUuid)) -+ return; -+ -+ // find matching QLEDescriptor -+ auto servicePrivate = localServices.value(serviceUuid); -+ // TODO test if service contains two characteristics with same uuid -+ // or characteristic contains two descriptors with same uuid -+ const auto handleList = servicePrivate->characteristicList.keys(); -+ for (const auto charHandle: handleList) { -+ const auto &charData = servicePrivate->characteristicList.value(charHandle); -+ if (charData.uuid != characteristicUuid) -+ continue; -+ -+ const auto &descHandleList = charData.descriptorList.keys(); -+ for (const auto descHandle: descHandleList) { -+ const auto &descData = charData.descriptorList.value(descHandle); -+ if (descData.uuid != descriptorUuid) -+ continue; -+ -+ qCDebug(QT_BT_OPENHARMONY) << "serverDescriptorChanged: Matching descriptor" -+ << descriptorUuid << "in char" << characteristicUuid -+ << "of service" << serviceUuid; -+ -+ servicePrivate->characteristicList[charHandle].descriptorList[descHandle].value = newValue; -+ -+ emit servicePrivate->descriptorWritten( -+ QLowEnergyDescriptor(servicePrivate, charHandle, descHandle), -+ newValue); -+ return; -+ } -+ } -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::characteristicChanged( -+ int charHandle, const QByteArray &data) -+{ -+ QSharedPointer service = -+ serviceForHandle(charHandle); -+ if (service.isNull()) -+ return; -+ -+ qCDebug(QT_BT_OPENHARMONY) << "Characteristic change notification" << service->uuid -+ << charHandle << data.toHex(); -+ -+ QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle); -+ if (!characteristic.isValid()) { -+ qCWarning(QT_BT_OPENHARMONY) << "characteristicChanged: Cannot find characteristic"; -+ return; -+ } -+ -+ // only update cache when property is readable. Otherwise it remains -+ // empty. -+ if (characteristic.properties() & QLowEnergyCharacteristic::Read) -+ updateValueOfCharacteristic(characteristic.attributeHandle(), -+ data, false); -+ emit service->characteristicChanged(characteristic, data); -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::serverCharacteristicChanged( -+ const QBluetoothUuid &serviceUuid, const QBluetoothUuid &characteristicUuid, const QByteArray &newValue) -+{ -+ qCDebug(QT_BT_OPENHARMONY) << "Server characteristic change notification" << newValue.toHex(); -+ -+ if (serviceUuid.isNull()) -+ return; -+ -+ // TODO test if two service with same uuid exist -+ if (!localServices.contains(serviceUuid)) -+ return; -+ -+ auto servicePrivate = localServices.value(serviceUuid); -+ if (characteristicUuid.isNull()) -+ return; -+ -+ QLowEnergyHandle foundHandle = 0; -+ const auto handleList = servicePrivate->characteristicList.keys(); -+ // TODO test if service contains two characteristics with same uuid -+ for (const auto handle: handleList) { -+ QLowEnergyServicePrivate::CharData &charData = servicePrivate->characteristicList[handle]; -+ if (charData.uuid != characteristicUuid) -+ continue; -+ -+ qCDebug(QT_BT_OPENHARMONY) << "serverCharacteristicChanged: Matching characteristic" -+ << characteristicUuid << " on " << serviceUuid; -+ charData.value = newValue; -+ foundHandle = handle; -+ break; -+ } -+ -+ if (!foundHandle) -+ return; -+ -+ emit servicePrivate->characteristicChanged( -+ QLowEnergyCharacteristic(servicePrivate, foundHandle), newValue); -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::serviceError( -+ int attributeHandle, QLowEnergyService::ServiceError errorCode) -+{ -+ // ignore call if it isn't really an error -+ if (errorCode == QLowEnergyService::NoError) -+ return; -+ -+ QSharedPointer service = -+ serviceForHandle(attributeHandle); -+ Q_ASSERT(!service.isNull()); -+ -+ // ATM we don't really use attributeHandle but later on we might -+ // want to associate the error code with a char or desc -+ service->setError(errorCode); -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::advertisementError(const QString &errorString) -+{ -+ Q_Q(QLowEnergyController); -+ -+ this->errorString = errorString; -+ -+ error = QLowEnergyController::AdvertisingError; -+ emit q->error(error); -+ -+ // not relevant states in peripheral mode -+ Q_ASSERT(state != QLowEnergyController::DiscoveredState); -+ Q_ASSERT(state != QLowEnergyController::DiscoveringState); -+ -+ switch (state) -+ { -+ case QLowEnergyController::UnconnectedState: -+ case QLowEnergyController::ConnectingState: -+ case QLowEnergyController::ConnectedState: -+ case QLowEnergyController::ClosingState: -+ // noop as remote is already connected or about to disconnect. -+ // when connection drops we reset to unconnected anyway -+ break; -+ -+ case QLowEnergyController::AdvertisingState: -+ setState(QLowEnergyController::UnconnectedState); -+ break; -+ default: -+ break; -+ } -+} -+ -+static QVariantMap createJsAdvertiseData(const QLowEnergyAdvertisingData &data) -+{ -+ QVariantMap result; -+ result.insert("includeDeviceName", !data.localName().isEmpty()); -+ result.insert("includeTxPowerLevel", data.includePowerLevel()); -+ -+ QStringList uuids; -+ for (const auto service: data.services()) -+ { -+ uuids << jsUuidfromQtUuid(service); -+ } -+ result.insert("serviceUuids", uuids); -+ -+ if (!data.manufacturerData().isEmpty()) { -+ result.insert("manufacturerId", (int)data.manufacturerId()); -+ result.insert("manufacturerData", data.manufacturerData()); -+ -+ } -+ -+ return result; -+} -+ -+static QVariantMap createJsAdvertiseSettings(const QLowEnergyAdvertisingParameters ¶ms) -+{ -+ QVariantMap result; -+ -+ bool connectable = false; -+ switch (params.mode()) -+ { -+ case QLowEnergyAdvertisingParameters::AdvInd: -+ connectable = true; -+ break; -+ case QLowEnergyAdvertisingParameters::AdvScanInd: -+ case QLowEnergyAdvertisingParameters::AdvNonConnInd: -+ connectable = false; -+ break; -+ // intentionally no default case -+ } -+ result.insert("connectable", connectable); -+ result.insert("maximumInterval", params.maximumInterval()); -+ -+ return result; -+} -+ -+ -+void QLowEnergyControllerPrivateOpenHarmony::startAdvertising(const QLowEnergyAdvertisingParameters ¶ms, -+ const QLowEnergyAdvertisingData &advertisingData, -+ const QLowEnergyAdvertisingData &scanResponseData) -+{ -+ setState(QLowEnergyController::AdvertisingState); -+ -+ qDebug() << "ddddddddddddddddddddddddddddd" << hub->jsObject()->isValid(); -+ if (!hub->jsObject()->isValid()) { -+ qCWarning(QT_BT_OPENHARMONY) << "Cannot initiate QtBluetoothLEServer"; -+ setError(QLowEnergyController::AdvertisingError); -+ setState(QLowEnergyController::UnconnectedState); -+ return; -+ } -+ -+// Pass on advertisingData, scanResponse & AdvertiseSettings -+ QVariantMap jAdvertiseData = createJsAdvertiseData(advertisingData); -+ QVariantMap jScanResponse = createJsAdvertiseData(scanResponseData); -+ QVariantMap jAdvertiseSettings = createJsAdvertiseSettings(params); -+ -+ const bool result = hub->jsObject()->call("startAdvertising", -+ jAdvertiseData, jScanResponse, jAdvertiseSettings); -+ if (!result) { -+ setError(QLowEnergyController::AdvertisingError); -+ setState(QLowEnergyController::UnconnectedState); -+ } -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::stopAdvertising() -+{ -+ setState(QLowEnergyController::UnconnectedState); -+ hub->jsObject()->callWithoutReturn("stopAdvertising"); -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::requestConnectionUpdate(const QLowEnergyConnectionParameters ¶ms) -+{ -+ if (role != QLowEnergyController::CentralRole) { -+ qCWarning(QT_BT_OPENHARMONY) << "On Openharmony, connection requests only work for central role"; -+ return; -+ } -+ -+ const bool result = hub->jsObject()->call("requestConnectionUpdatePriority", params.minimumInterval()); -+ if (!result) -+ qCWarning(QT_BT_OPENHARMONY) << "Cannot set connection update priority"; -+} -+ -+void QLowEnergyControllerPrivateOpenHarmony::addToGenericAttributeList(const QLowEnergyServiceData &serviceData, -+ QLowEnergyHandle startHandle) -+{ -+ QSharedPointer service = serviceForHandle(startHandle); -+ if (hub == nullptr || service.isNull()) -+ return; -+ -+ // create BluetoothGattService object -+ bool isPrimary = (serviceData.type() == QLowEnergyServiceData::ServiceTypePrimary); -+ -+ -+ static int index = 0; -+ QString name = QString("QLowEnergyControllerPrivateOpenHarmonyService%1").arg(index++); -+ bool result = hub->jsObject()->call("createService", name, jsUuidfromQtUuid(service->uuid), isPrimary); -+ QSharedPointer jsService = qJsObjectLoader->create(name); -+ if (result) { -+ if (!jsService->isValid()) { -+ qWarning(QT_BT_OPENHARMONY) << "Cannot init js gatt server "; -+ return; -+ } -+ } else { -+ qWarning(QT_BT_OPENHARMONY) << "Cannot init js gatt server "; -+ return; -+ } -+ service->m_jsService = name; -+ -+// add included services, which must have been added earlier already -+ const QList includedServices = serviceData.includedServices(); -+ for (const auto includedServiceEntry: includedServices) { -+ //TODO test this end-to-end -+ result = hub->jsObject()->call( -+ "addIncludeService", includedServiceEntry->d_ptr->m_jsService); -+ if (!result) -+ qWarning(QT_BT_OPENHARMONY) << "Cannot add included service " << includedServiceEntry->serviceUuid() -+ << "to current service" << service->uuid; -+ } -+ -+ // add characteristics -+ const QList serviceCharsData = serviceData.characteristics(); -+ for (const auto &charData: serviceCharsData) { -+ result = jsService->call("addCharacteristic", jsUuidfromQtUuid(charData.uuid()), int(charData.properties()), charData.value()); -+ if (!result) -+ qCWarning(QT_BT_OPENHARMONY) << "Cannot setup initial characteristic value for " << charData.uuid(); -+ -+ const QList descriptorList = charData.descriptors(); -+ for (const auto &descData: descriptorList) { -+ // setupDescPermissions(descData), -+ result = jsService->call("addDescriptor", jsUuidfromQtUuid(charData.uuid()), jsUuidfromQtUuid(descData.uuid()) -+ , descData.value()); -+ -+ if (!result) { -+ qCWarning(QT_BT_OPENHARMONY) << "Cannot add descriptor" << descData.uuid() -+ << "to service" << service->uuid << "(char:" -+ << charData.uuid() << ")"; -+ } -+ } -+ } -+} -+ -+QT_END_NAMESPACE -diff --git a/src/bluetooth/qlowenergycontroller_ohos_p.h b/src/bluetooth/qlowenergycontroller_ohos_p.h -new file mode 100644 -index 00000000..0f18ecd1 ---- /dev/null -+++ b/src/bluetooth/qlowenergycontroller_ohos_p.h -@@ -0,0 +1,103 @@ -+#ifndef QLOWENERGYCONTROLLERPRIVATEOHOS_P_H -+#define QLOWENERGYCONTROLLERPRIVATEOHOS_P_H -+ -+#include -+#include -+#include -+#include -+#include -+#include "qlowenergycontroller.h" -+#include "qlowenergycontrollerbase_p.h" -+ -+#include "openharmony/lowenergynotificationhub_p.h" -+ -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QLowEnergyServiceData; -+class QTimer; -+ -+class LowEnergyNotificationHub; -+ -+extern void registerQLowEnergyControllerMetaType(); -+ -+class QLeAdvertiser; -+ -+class QLowEnergyControllerPrivateOpenHarmony final : public QLowEnergyControllerPrivate -+{ -+ Q_OBJECT -+public: -+ QLowEnergyControllerPrivateOpenHarmony(); -+ ~QLowEnergyControllerPrivateOpenHarmony() override; -+ -+ void init() override; -+ -+ void connectToDevice() override; -+ void disconnectFromDevice() override; -+ -+ void discoverServices() override; -+ void discoverServiceDetails(const QBluetoothUuid &service) override; -+ -+ void startAdvertising(const QLowEnergyAdvertisingParameters ¶ms, -+ const QLowEnergyAdvertisingData &advertisingData, -+ const QLowEnergyAdvertisingData &scanResponseData) override; -+ void stopAdvertising() override; -+ -+ void requestConnectionUpdate(const QLowEnergyConnectionParameters ¶ms) override; -+ -+ // read data -+ void readCharacteristic(const QSharedPointer service, -+ const QLowEnergyHandle charHandle) override; -+ void readDescriptor(const QSharedPointer service, -+ const QLowEnergyHandle charHandle, -+ const QLowEnergyHandle descriptorHandle) override; -+ -+ // write data -+ void writeCharacteristic(const QSharedPointer service, -+ const QLowEnergyHandle charHandle, -+ const QByteArray &newValue, QLowEnergyService::WriteMode mode) override; -+ void writeDescriptor(const QSharedPointer service, -+ const QLowEnergyHandle charHandle, -+ const QLowEnergyHandle descriptorHandle, -+ const QByteArray &newValue) override; -+ -+ void addToGenericAttributeList(const QLowEnergyServiceData &service, -+ QLowEnergyHandle startHandle) override; -+ -+private: -+ -+ LowEnergyNotificationHub *hub; -+ -+private slots: -+ void connectionUpdated(QLowEnergyController::ControllerState newState, -+ QLowEnergyController::Error errorCode); -+ void servicesDiscovered(QLowEnergyController::Error errorCode, -+ const QString &foundServices); -+ void serviceDetailsDiscoveryFinished(const QString& serviceUuid, -+ int startHandle, int endHandle); -+ void characteristicRead(const QBluetoothUuid &serviceUuid, int handle, -+ const QBluetoothUuid &charUuid, int properties, -+ const QByteArray& data); -+ void descriptorRead(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid, -+ int handle, const QBluetoothUuid &descUuid, const QByteArray &data); -+ void characteristicWritten(int charHandle, const QByteArray &data, -+ QLowEnergyService::ServiceError errorCode); -+ void descriptorWritten(int descHandle, const QByteArray &data, -+ QLowEnergyService::ServiceError errorCode); -+ void serverDescriptorWritten(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &characteristicUuid, const QBluetoothUuid &descriptorUuid, const QByteArray &newValue); -+ void characteristicChanged(int charHandle, const QByteArray &data); -+ void serverCharacteristicChanged(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &characteristicUuid, const QByteArray &newValue); -+ void serviceError(int attributeHandle, QLowEnergyService::ServiceError errorCode); -+ void advertisementError(const QString &errorString); -+ -+private: -+ void peripheralConnectionUpdated(QLowEnergyController::ControllerState newState, -+ QLowEnergyController::Error errorCode); -+ void centralConnectionUpdated(QLowEnergyController::ControllerState newState, -+ QLowEnergyController::Error errorCode); -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QLOWENERGYCONTROLLERPRIVATEOHOS_P_H -diff --git a/src/bluetooth/qlowenergydescriptor.h b/src/bluetooth/qlowenergydescriptor.h -index 9dd0cc20..6a78e500 100644 ---- a/src/bluetooth/qlowenergydescriptor.h -+++ b/src/bluetooth/qlowenergydescriptor.h -@@ -81,6 +81,7 @@ protected: - friend class QLowEnergyControllerPrivate; - friend class QLowEnergyControllerPrivateAndroid; - friend class QLowEnergyControllerPrivateBluez; -+ friend class QLowEnergyControllerPrivateOpenHarmony; - friend class QLowEnergyControllerPrivateBluezDBus; - friend class QLowEnergyControllerPrivateCommon; - friend class QLowEnergyControllerPrivateOSX; -diff --git a/src/bluetooth/qlowenergyservice.h b/src/bluetooth/qlowenergyservice.h -index 9de65a84..811db534 100644 ---- a/src/bluetooth/qlowenergyservice.h -+++ b/src/bluetooth/qlowenergyservice.h -@@ -136,6 +136,7 @@ private: - friend class QLowEnergyControllerPrivate; - friend class QLowEnergyControllerPrivateBluez; - friend class QLowEnergyControllerPrivateAndroid; -+ friend class QLowEnergyControllerPrivateOpenHarmony; - QLowEnergyService(QSharedPointer p, - QObject *parent = nullptr); - }; -diff --git a/src/bluetooth/qlowenergyserviceprivate_p.h b/src/bluetooth/qlowenergyserviceprivate_p.h -index fb4163a0..41456e79 100644 ---- a/src/bluetooth/qlowenergyserviceprivate_p.h -+++ b/src/bluetooth/qlowenergyserviceprivate_p.h -@@ -128,7 +128,9 @@ public: - // reference to the BluetoothGattService object - QAndroidJniObject androidService; - #endif -- -+#if defined (QT_OPENHARMONY_BLUETOOTH) -+ QString m_jsService; -+#endif - }; - - typedef QHash CharacteristicDataMap; -diff --git a/src/nfc/nfc.pro b/src/nfc/nfc.pro -index ed88b79a..c10a7900 100644 ---- a/src/nfc/nfc.pro -+++ b/src/nfc/nfc.pro -@@ -56,7 +56,7 @@ SOURCES += \ - qnearfieldsharetarget.cpp \ - qnfc.cpp - --linux:!android:qtHaveModule(dbus) { -+linux:!android:!openharmony:qtHaveModule(dbus) { - NFC_BACKEND_AVAILABLE = yes - - QT_PRIVATE += dbus -@@ -112,6 +112,33 @@ linux:!android:qtHaveModule(dbus) { - qnearfieldsharemanagerimpl_p.cpp \ - qnearfieldsharetargetimpl_p.cpp \ - android/androidmainnewintentlistener.cpp -+} else:openharmony { -+ NFC_BACKEND_AVAILABLE = yes -+ DEFINES += QT_OPENHARMONY_NFC -+ DEFINES += OPENHARMONY_NFC -+ QT_PRIVATE += core-private gui -+ -+ PRIVATE_HEADERS += \ -+ qllcpserver_openharmony_p.h \ -+ qllcpsocket_openharmony_p.h \ -+ qnearfieldmanager_openharmony_p.h \ -+ qnearfieldtarget_openharmony_p.h \ -+ qnearfieldsharemanagerimpl_p.h \ -+ qnearfieldsharetargetimpl_p.h \ -+ openharmony/openharmonynfc_p.h \ -+ openharmony/openharmonynfclistener_p.h -+ -+ SOURCES += \ -+ qllcpserver_openharmony_p.cpp \ -+ qllcpsocket_openharmony_p.cpp \ -+ qnearfieldmanager_openharmony.cpp \ -+ qnearfieldtarget_openharmony.cpp \ -+ qnearfieldsharemanagerimpl_p.cpp \ -+ qnearfieldsharetargetimpl_p.cpp \ -+ qnearfieldtarget_openharmony_p.cpp \ -+ openharmony/openharmonynfc.cpp \ -+ openharmony/openharmonynfclistener.cpp -+ LIBS += -lace_napi.z - } - - isEmpty(NFC_BACKEND_AVAILABLE) { -diff --git a/src/nfc/openharmony/openharmonynfc.cpp b/src/nfc/openharmony/openharmonynfc.cpp -new file mode 100644 -index 00000000..5c7360fd ---- /dev/null -+++ b/src/nfc/openharmony/openharmonynfc.cpp -@@ -0,0 +1,94 @@ -+#include -+#include -+ -+#include "openharmonynfclistener_p.h" -+#include "qopenharmonydefines.h" -+#include -+#include -+#include -+ -+namespace QtHarmonyPrivate { -+ -+static OpenHarmonyNfcListener listeners; -+ -+bool startDiscovery() -+{ -+ QSharedPointer jsNfc = qJsObjectLoader->create("JsNfc"); -+ return jsNfc->call("start"); -+} -+ -+bool isAvailable() -+{ -+ QSharedPointer jsNfc = qJsObjectLoader->create("JsNfc"); -+ return jsNfc->call("isAvailable"); -+} -+ -+bool isSupported() -+{ -+ QSharedPointer jsNfc = qJsObjectLoader->create("JsNfc"); -+ return jsNfc->call("isSupported"); -+} -+ -+bool stopDiscovery() -+{ -+ QSharedPointer jsNfc = qJsObjectLoader->create("JsNfc"); -+ return jsNfc->call("stop"); -+} -+ -+bool registerNfcListener(OpenHarmonyNfcListenerInterface *listener) -+{ -+ return listeners.registerNfcListener(listener); -+} -+ -+bool unregisterNfcListener(OpenHarmonyNfcListenerInterface *listener) -+{ -+ return listeners.unregisterNfcListener(listener); -+} -+ -+bool registerNfcStateListener(OpenHarmonyNfcStateListenerInterface *listener) -+{ -+ return listeners.registerNfcStateListener(listener); -+} -+ -+bool unregisterNfcStateListener(OpenHarmonyNfcStateListenerInterface *listener) -+{ -+ return listeners.unregisterNfcStateListener(listener); -+} -+ -+} -+/* -+ * function for module exports -+ */ -+EXTERN_C_START -+static napi_value Init(napi_env env, napi_value exports) -+{ -+ static bool inited = false; -+ if (!inited) { -+ -+ OpenHarmonyNfcListener::init(env, exports); -+ LOGI("init nfc module"); -+ inited = true; -+ } -+ return exports; -+} -+EXTERN_C_END -+ -+/* -+ * Napi Module define -+ */ -+static napi_module openharmonyQtNfcModule = { -+ .nm_version = 1, -+ .nm_flags = 0, -+ .nm_filename = nullptr, -+ .nm_register_func = Init, -+ .nm_modname = "openharmony_qt_nfc", -+ .nm_priv = ((void*)0), -+ .reserved = { 0 }, -+}; -+/* -+ * Module register function -+ */ -+extern "C" __attribute__((constructor)) void RegisterModule(void) -+{ -+ napi_module_register(&openharmonyQtNfcModule); -+} -diff --git a/src/nfc/openharmony/openharmonynfc_p.h b/src/nfc/openharmony/openharmonynfc_p.h -new file mode 100644 -index 00000000..121e285a ---- /dev/null -+++ b/src/nfc/openharmony/openharmonynfc_p.h -@@ -0,0 +1,31 @@ -+#ifndef OPENHARMONYNFC_P_H -+#define OPENHARMONYNFC_P_H -+ -+namespace QtHarmonyPrivate { -+ -+class OpenHarmonyNfcListenerInterface -+{ -+public: -+ virtual ~OpenHarmonyNfcListenerInterface(){} -+ virtual void newNfc(const QString &tagInfo) = 0; -+}; -+ -+class OpenHarmonyNfcStateListenerInterface -+{ -+public: -+ virtual ~OpenHarmonyNfcStateListenerInterface(){} -+ virtual void handleStateChanged(int state) = 0; -+}; -+ -+bool startDiscovery(); -+bool stopDiscovery(); -+bool isAvailable(); -+bool isSupported(); -+ -+bool registerNfcListener(OpenHarmonyNfcListenerInterface *listener); -+bool unregisterNfcListener(OpenHarmonyNfcListenerInterface *listener); -+ -+bool registerNfcStateListener(OpenHarmonyNfcStateListenerInterface *listener); -+bool unregisterNfcStateListener(OpenHarmonyNfcStateListenerInterface *listener); -+} -+#endif // OPENHARMONYNFC_P_H -diff --git a/src/nfc/openharmony/openharmonynfclistener.cpp b/src/nfc/openharmony/openharmonynfclistener.cpp -new file mode 100644 -index 00000000..f623fbb6 ---- /dev/null -+++ b/src/nfc/openharmony/openharmonynfclistener.cpp -@@ -0,0 +1,155 @@ -+#include "openharmonynfclistener_p.h" -+#include "qopenharmonydefines.h" -+#include -+#include "qdebug.h" -+#include -+ -+static OpenHarmonyNfcListener *g_listener = nullptr; -+ -+static napi_value nfcStateChanged(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 1; -+ napi_value args[1]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 1) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments"); -+ return nullptr; -+ } -+ -+ -+ int32_t value0 = qJs::getInt32(args[0]); -+ if (g_listener != nullptr) -+ g_listener->handleStateChanged(value0); -+ -+ return nullptr; -+} -+ -+static napi_value nfcTargetDetected(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 1; -+ napi_value args[1]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ -+ if (argc != 1) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments"); -+ return nullptr; -+ } -+ -+ QString value0 = qJs::getString(args[0]); -+ if (g_listener != nullptr) -+ g_listener->newNfc(value0); -+ -+ return nullptr; -+} -+ -+napi_value OpenHarmonyNfcListener::init(napi_env env, napi_value exports) -+{ -+ napi_property_descriptor desc[] ={ -+ DECLARE_NAPI_FUNCTION("nfcStateChanged", nfcStateChanged), -+ DECLARE_NAPI_FUNCTION("nfcTargetDetected", nfcTargetDetected), -+ }; -+ NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); -+ return nullptr; -+} -+ -+ -+OpenHarmonyNfcListener::OpenHarmonyNfcListener() -+ : nfcListeners(), nfcStateListeners(), listenersLock(), paused(true), receiving(false) -+{ -+ QtHarmonyPrivate::registerResumePauseListener(this); -+ g_listener = this; -+} -+ -+OpenHarmonyNfcListener::~OpenHarmonyNfcListener() -+{ -+ QtHarmonyPrivate::unregisterResumePauseListener(this); -+ g_listener = nullptr; -+} -+ -+bool OpenHarmonyNfcListener::registerNfcStateListener(QtHarmonyPrivate::OpenHarmonyNfcStateListenerInterface *listener) -+{ -+ listenersLock.lockForWrite(); -+ if (!nfcStateListeners.contains(listener)) -+ nfcStateListeners.push_back(listener); -+ listenersLock.unlock(); -+ return true; -+} -+ -+bool OpenHarmonyNfcListener::unregisterNfcStateListener(QtHarmonyPrivate::OpenHarmonyNfcStateListenerInterface *listener) -+{ -+ listenersLock.lockForWrite(); -+ nfcStateListeners.removeOne(listener); -+ listenersLock.unlock(); -+ return true; -+} -+ -+bool OpenHarmonyNfcListener::registerNfcListener(QtHarmonyPrivate::OpenHarmonyNfcListenerInterface *listener) -+{ -+ listenersLock.lockForWrite(); -+ if (!nfcListeners.contains(listener)) -+ nfcListeners.push_back(listener); -+ listenersLock.unlock(); -+ updateReceiveState(); -+ return true; -+} -+ -+bool OpenHarmonyNfcListener::unregisterNfcListener(QtHarmonyPrivate::OpenHarmonyNfcListenerInterface *listener) -+{ -+ listenersLock.lockForWrite(); -+ nfcListeners.removeOne(listener); -+ listenersLock.unlock(); -+ updateReceiveState(); -+ return true; -+} -+ -+void OpenHarmonyNfcListener::handleResume() -+{ -+ paused = false; -+ updateReceiveState(); -+} -+ -+void OpenHarmonyNfcListener::handlePause() -+{ -+ paused = true; -+ updateReceiveState(); -+} -+ -+void OpenHarmonyNfcListener::newNfc(const QString &tagInfo) -+{ -+ for (const auto listener : qAsConst(nfcListeners)) { -+ listener->newNfc(tagInfo); -+ } -+} -+ -+void OpenHarmonyNfcListener::handleStateChanged(int state) -+{ -+ for (const auto listener : qAsConst(nfcStateListeners)) { -+ listener->handleStateChanged(state); -+ } -+} -+ -+void OpenHarmonyNfcListener::updateReceiveState() -+{ -+ if (paused) { -+ // We were paused while receiving, so we stop receiving. -+ if (receiving) { -+ QtHarmonyPrivate::stopDiscovery(); -+ receiving = false; -+ } -+ return; -+ } -+ -+ // We reach here, so we are not paused. -+ listenersLock.lockForRead(); -+ // We have nfc listeners and do not receive. Switch on. -+ if (nfcListeners.count() && !receiving) -+ receiving = QtHarmonyPrivate::startDiscovery(); -+ -+ // we have no nfc listeners and do receive. Switch off. -+ if (!nfcListeners.count() && receiving) { -+ QtHarmonyPrivate::stopDiscovery(); -+ receiving = false; -+ } -+ listenersLock.unlock(); -+} -diff --git a/src/nfc/openharmony/openharmonynfclistener_p.h b/src/nfc/openharmony/openharmonynfclistener_p.h -new file mode 100644 -index 00000000..87a80328 ---- /dev/null -+++ b/src/nfc/openharmony/openharmonynfclistener_p.h -@@ -0,0 +1,41 @@ -+#ifndef OPENHARMONYNFCLISTENER_P_H_ -+#define OPENHARMONYNFCLISTENER_P_H_ -+ -+ -+#include -+#include -+ -+#include "qlist.h" -+#include "qreadwritelock.h" -+#include "openharmonynfc_p.h" -+ -+class OpenHarmonyNfcListener : QtHarmonyPrivate::ResumePauseListener, QtHarmonyPrivate::OpenHarmonyNfcListenerInterface, QtHarmonyPrivate::OpenHarmonyNfcStateListenerInterface -+{ -+public: -+ OpenHarmonyNfcListener(); -+ ~OpenHarmonyNfcListener(); -+ -+ bool registerNfcListener(QtHarmonyPrivate::OpenHarmonyNfcListenerInterface *listener); -+ bool unregisterNfcListener(QtHarmonyPrivate::OpenHarmonyNfcListenerInterface *listener); -+ -+ bool registerNfcStateListener(QtHarmonyPrivate::OpenHarmonyNfcStateListenerInterface *listener); -+ bool unregisterNfcStateListener(QtHarmonyPrivate::OpenHarmonyNfcStateListenerInterface *listener); -+ -+ void handleResume(); -+ void handlePause(); -+ -+ void newNfc(const QString &tagInfo); -+ void handleStateChanged(int state); -+ -+ static napi_value init(napi_env env, napi_value exports); -+private: -+ void updateReceiveState(); -+protected: -+ QList nfcListeners; -+ QList nfcStateListeners; -+ QReadWriteLock listenersLock; -+ bool paused; -+ bool receiving; -+}; -+ -+#endif /* OPENHARMONYNFCLISTENER_P_H_ */ -diff --git a/src/nfc/qllcpserver_openharmony_p.cpp b/src/nfc/qllcpserver_openharmony_p.cpp -new file mode 100644 -index 00000000..108210c6 ---- /dev/null -+++ b/src/nfc/qllcpserver_openharmony_p.cpp -@@ -0,0 +1,92 @@ -+#include "qllcpserver_openharmony_p.h" -+//#include "qnx/qnxnfcmanager_p.h" -+ -+QT_BEGIN_NAMESPACE -+ -+QLlcpServerPrivate::QLlcpServerPrivate(QLlcpServer *q) -+ : q_ptr(q), m_llcpSocket(0), m_connected(false) -+{ -+} -+ -+QLlcpServerPrivate::~QLlcpServerPrivate() -+{ -+} -+ -+bool QLlcpServerPrivate::listen(const QString &/*serviceUri*/) -+{ -+ /*//The server is already listening -+ if (isListening()) -+ return false; -+ -+ nfc_result_t result = nfc_llcp_register_connection_listener(NFC_LLCP_SERVER, 0, serviceUri.toStdString().c_str(), &m_conListener); -+ m_connected = true; -+ if (result == NFC_RESULT_SUCCESS) { -+ m_serviceUri = serviceUri; -+ qQNXNFCDebug() << "LLCP server registered" << serviceUri; -+ } else { -+ qWarning() << Q_FUNC_INFO << "Could not register for llcp connection listener"; -+ return false; -+ } -+ QNXNFCManager::instance()->registerLLCPConnection(m_conListener, this);*/ -+ return true; -+} -+ -+bool QLlcpServerPrivate::isListening() const -+{ -+ return m_connected; -+} -+ -+void QLlcpServerPrivate::close() -+{ -+ /*nfc_llcp_unregister_connection_listener(m_conListener); -+ QNXNFCManager::instance()->unregisterLLCPConnection(m_conListener); -+ m_serviceUri = QString(); -+ m_connected = false;*/ -+} -+ -+QString QLlcpServerPrivate::serviceUri() const -+{ -+ return m_serviceUri; -+} -+ -+quint8 QLlcpServerPrivate::serverPort() const -+{ -+ /*unsigned int sap; -+ if (nfc_llcp_get_local_sap(m_target, &sap) == NFC_RESULT_SUCCESS) { -+ return sap; -+ }*/ -+ return -1; -+} -+ -+bool QLlcpServerPrivate::hasPendingConnections() const -+{ -+ return m_llcpSocket != 0; -+} -+ -+QLlcpSocket *QLlcpServerPrivate::nextPendingConnection() -+{ -+ /*QLlcpSocket *socket = m_llcpSocket; -+ m_llcpSocket = 0; -+ return socket;*/ -+ return 0; -+} -+ -+QLlcpSocket::SocketError QLlcpServerPrivate::serverError() const -+{ -+ return QLlcpSocket::UnknownSocketError; -+} -+ -+/*void QLlcpServerPrivate::connected(nfc_target_t *target) -+{ -+ m_target = target; -+ if (m_llcpSocket != 0) { -+ qWarning() << Q_FUNC_INFO << "LLCP socket not cloesed properly"; -+ return; -+ } -+ m_llcpSocket = new QLlcpSocket(); -+ m_llcpSocket->bind(serverPort()); -+}*/ -+ -+QT_END_NAMESPACE -+ -+ -diff --git a/src/nfc/qllcpserver_openharmony_p.h b/src/nfc/qllcpserver_openharmony_p.h -new file mode 100644 -index 00000000..cbb4b957 ---- /dev/null -+++ b/src/nfc/qllcpserver_openharmony_p.h -@@ -0,0 +1,44 @@ -+#ifndef QLLCPSERVER_OPENHARMONY_P_H -+#define QLLCPSERVER_OPENHARMONY_P_H -+ -+ -+#include "qllcpserver_p.h" -+//#include "nfc/nfc.h" -+ -+QT_BEGIN_NAMESPACE -+ -+class QLlcpServerPrivate : public QObject -+{ -+ Q_OBJECT -+public: -+ QLlcpServerPrivate(QLlcpServer *q); -+ ~QLlcpServerPrivate(); -+ -+ bool listen(const QString &serviceUri); -+ bool isListening() const; -+ -+ void close(); -+ -+ QString serviceUri() const; -+ quint8 serverPort() const; -+ -+ bool hasPendingConnections() const; -+ QLlcpSocket *nextPendingConnection(); -+ -+ QLlcpSocket::SocketError serverError() const; -+ -+ //Q_INVOKABLE void connected(nfc_target_t *); -+ -+private: -+ QLlcpServer *q_ptr; -+ QLlcpSocket *m_llcpSocket; -+ //We can not use m_conListener for the connection state -+ bool m_connected; -+ //nfc_llcp_connection_listener_t m_conListener; -+ QString m_serviceUri; -+ //nfc_target_t *m_target; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QLLCPSERVER_OPENHARMONY_P_H -diff --git a/src/nfc/qllcpsocket_openharmony_p.cpp b/src/nfc/qllcpsocket_openharmony_p.cpp -new file mode 100644 -index 00000000..77c41ea1 ---- /dev/null -+++ b/src/nfc/qllcpsocket_openharmony_p.cpp -@@ -0,0 +1,285 @@ -+#include "qllcpsocket_android_p.h" -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+QLlcpSocketPrivate::QLlcpSocketPrivate(QLlcpSocket *q) -+ : q_ptr(q), m_state(QLlcpSocket::UnconnectedState), m_server(false) -+{ -+} -+ -+QLlcpSocketPrivate::~QLlcpSocketPrivate() -+{ -+ disconnectFromService(); -+} -+ -+void QLlcpSocketPrivate::connectToService(QNearFieldTarget *target, const QString &serviceUri) -+{ -+ Q_UNUSED(target) -+ Q_UNUSED(serviceUri) -+ /*if (m_state != QLlcpSocket::UnconnectedState) { -+ qWarning() << Q_FUNC_INFO << "socket is already connected"; -+ return; -+ } -+ -+ m_state = QLlcpSocket::ConnectingState; -+ if (nfc_llcp_register_connection_listener(NFC_LLCP_CLIENT, 0, serviceUri.toLocal8Bit().constData(), -+ &m_conListener) != NFC_RESULT_SUCCESS) { -+ qWarning() << Q_FUNC_INFO << "could not register for connection listener"; -+ return; -+ } -+ -+ QNXNFCManager::instance()->registerLLCPConnection(m_conListener, this); -+ -+ qQNXNFCDebug() << "Connecting client socket" << serviceUri << m_conListener; -+ connect(QNXNFCManager::instance(), &QNXNFCManager::llcpDisconnected, this, &QLlcpSocketPrivate::disconnectFromService);*/ -+} -+ -+void QLlcpSocketPrivate::disconnectFromService() -+{ -+ /*Q_Q(QLlcpSocket); -+ QNXNFCManager::instance()->unregisterTargetLost(this); -+ qQNXNFCDebug() << "Shutting down LLCP socket"; -+ if (!m_server && nfc_llcp_unregister_connection_listener(m_conListener) != NFC_RESULT_SUCCESS) { -+ qWarning() << Q_FUNC_INFO << "Error when trying to close LLCP socket"; -+ } -+ QNXNFCManager::instance()->unregisterLLCPConnection(m_conListener); -+ disconnect(QNXNFCManager::instance(), &QNXNFCManager::llcpDisconnected, this, &QLlcpSocketPrivate::disconnectFromService); -+ -+ q->disconnected(); -+ m_conListener = 0; -+ m_state = QLlcpSocket::UnconnectedState;*/ -+} -+ -+bool QLlcpSocketPrivate::bind(quint8 port) -+{ -+ Q_UNUSED(port); -+ -+ /*m_state = QLlcpSocket::ConnectedState; -+ m_server = true; -+ connect(QNXNFCManager::instance(), &QNXNFCManager::llcpDisconnected, this, &QLlcpSocketPrivate::disconnectFromService); -+ connected(QNXNFCManager::instance()->getLastTarget());*/ -+ -+ return true; -+} -+ -+bool QLlcpSocketPrivate::hasPendingDatagrams() const -+{ -+ return !m_receivedDatagrams.isEmpty(); -+} -+ -+qint64 QLlcpSocketPrivate::pendingDatagramSize() const -+{ -+ if (m_receivedDatagrams.isEmpty()) -+ return -1; -+ -+ return m_receivedDatagrams.first().length(); -+} -+ -+qint64 QLlcpSocketPrivate::writeDatagram(const char *data, qint64 size) -+{ -+ if (m_state == QLlcpSocket::ConnectedState) -+ return writeData(data, size); -+ -+ return -1; -+} -+ -+qint64 QLlcpSocketPrivate::writeDatagram(const QByteArray &datagram) -+{ -+ return writeDatagram(datagram.constData(), datagram.size()); -+} -+ -+qint64 QLlcpSocketPrivate::readDatagram(char *data, qint64 maxSize, -+ QNearFieldTarget **target, quint8 *port) -+{ -+ Q_UNUSED(target); -+ Q_UNUSED(port); -+ -+ if (m_state == QLlcpSocket::ConnectedState) -+ return readData(data, maxSize); -+ -+ return -1; -+} -+ -+qint64 QLlcpSocketPrivate::writeDatagram(const char *data, qint64 size, -+ QNearFieldTarget *target, quint8 port) -+{ -+ Q_UNUSED(target); -+ Q_UNUSED(port); -+ -+ return writeDatagram(data, size); -+} -+ -+qint64 QLlcpSocketPrivate::writeDatagram(const QByteArray &datagram, -+ QNearFieldTarget *target, quint8 port) -+{ -+ Q_UNUSED(datagram); -+ Q_UNUSED(target); -+ Q_UNUSED(port); -+ -+ return writeDatagram(datagram.constData(), datagram.size()-1); -+} -+ -+QLlcpSocket::SocketError QLlcpSocketPrivate::error() const -+{ -+ return QLlcpSocket::UnknownSocketError; -+} -+ -+QLlcpSocket::SocketState QLlcpSocketPrivate::state() const -+{ -+ return m_state; -+} -+ -+qint64 QLlcpSocketPrivate::readData(char *data, qint64 maxlen) -+{ -+ Q_UNUSED(data); -+ Q_UNUSED(maxlen); -+ if (m_receivedDatagrams.isEmpty()) -+ return 0; -+ -+ /*const QByteArray datagram = m_receivedDatagrams.takeFirst(); -+ qint64 size = qMin(maxlen, qint64(datagram.length())); -+ memcpy(data, datagram.constData(), size); -+ return size;*/ -+ return 0; -+} -+ -+qint64 QLlcpSocketPrivate::writeData(const char *data, qint64 len) -+{ -+ Q_UNUSED(data); -+ Q_UNUSED(len); -+ /*if (socketState != Idle) { -+ m_writeQueue.append(QByteArray(data, len)); -+ return len; -+ } else { -+ socketState = Writing; -+ qQNXNFCDebug() << "LLCP write"; -+ nfc_result_t res = nfc_llcp_write(m_target, (uchar_t*)data, (size_t)len); -+ if (res == NFC_RESULT_SUCCESS) { -+ return len; -+ } else { -+ qWarning() << Q_FUNC_INFO << "Error writing to LLCP socket. Error" << res; -+ enteringIdle(); -+ return -1; -+ } -+ }*/ -+ return -1; -+} -+ -+qint64 QLlcpSocketPrivate::bytesAvailable() const -+{ -+ /*qint64 available = 0; -+ for (const QByteArray &datagram : qAsConst(m_receivedDatagrams)) -+ available += datagram.length(); -+ -+ return available;*/ -+ return 0; -+} -+ -+bool QLlcpSocketPrivate::canReadLine() const -+{ -+ /*for (const QByteArray &datagram : qAsConst(m_receivedDatagrams)) { -+ if (datagram.contains('\n')) -+ return true; -+ }*/ -+ -+ return false; -+} -+ -+bool QLlcpSocketPrivate::waitForReadyRead(int msecs) -+{ -+ Q_UNUSED(msecs); -+ -+ return false; -+} -+ -+bool QLlcpSocketPrivate::waitForBytesWritten(int msecs) -+{ -+ Q_UNUSED(msecs); -+ -+ return false; -+} -+ -+bool QLlcpSocketPrivate::waitForConnected(int msecs) -+{ -+ Q_UNUSED(msecs); -+ -+ return false; -+} -+ -+bool QLlcpSocketPrivate::waitForDisconnected(int msecs) -+{ -+ Q_UNUSED(msecs); -+ -+ return false; -+} -+ -+/*void QLlcpSocketPrivate::connected(nfc_target_t *target) -+{ -+ Q_Q(QLlcpSocket); -+ m_target = target; -+ -+ m_state = QLlcpSocket::ConnectedState; -+ emit q->connected(); -+ qQNXNFCDebug() << "Socket connected"; -+ -+ unsigned int targetId; -+ nfc_get_target_connection_id(target, &targetId); -+ QNXNFCManager::instance()->requestTargetLost(this, targetId); -+ enteringIdle(); -+}*/ -+ -+void QLlcpSocketPrivate::targetLost() -+{ -+ disconnectFromService(); -+ //qQNXNFCDebug() << "LLCP target lost...socket disconnected"; -+} -+ -+void QLlcpSocketPrivate::dataRead(QByteArray& data) -+{ -+ Q_UNUSED(data); -+ /*Q_Q(QLlcpSocket); -+ if (!data.isEmpty()) { -+ m_receivedDatagrams.append(data); -+ emit q->readyRead(); -+ } -+ socketState = Idle; -+ enteringIdle();*/ -+} -+ -+void QLlcpSocketPrivate::dataWritten() -+{ -+ //enteringIdle(); -+} -+ -+void QLlcpSocketPrivate::read() -+{ -+ /*if (socketState != Idle) { -+ qQNXNFCDebug() << "Trying to read but socket state not in idle..abort"; -+ return; -+ } -+ socketState = Reading; -+ qQNXNFCDebug() << "LLCP read"; -+ if (nfc_llcp_read(m_target, 128) != NFC_RESULT_SUCCESS) { -+ qWarning() << Q_FUNC_INFO << "Could not register for reading"; -+ socketState = Idle; -+ }*/ -+} -+ -+void QLlcpSocketPrivate::enteringIdle() -+{ -+ /*qQNXNFCDebug() << "entering idle; Socket state:" << socketState; -+ socketState = Idle; -+ if (m_state == QLlcpSocket::ConnectedState) { -+ if (m_writeQueue.isEmpty()) { -+ qQNXNFCDebug() << "Write queue empty, reading in 50ms"; -+ QTimer::singleShot(50, this, SLOT(read())); -+ } else { -+ qQNXNFCDebug() << "Write first package in queue"; -+ writeDatagram(m_writeQueue.takeFirst()); -+ } -+ }*/ -+} -+ -+QT_END_NAMESPACE -+ -diff --git a/src/nfc/qllcpsocket_openharmony_p.h b/src/nfc/qllcpsocket_openharmony_p.h -new file mode 100644 -index 00000000..0be026c0 ---- /dev/null -+++ b/src/nfc/qllcpsocket_openharmony_p.h -@@ -0,0 +1,83 @@ -+#ifndef QLLCPSOCKET_OPENHARMONY_P_H -+#define QLLCPSOCKET_OPENHARMONY_P_H -+ -+#include "qllcpsocket_p.h" -+ -+ -+QT_BEGIN_NAMESPACE -+ -+class QLlcpSocketPrivate : public QObject -+{ -+ Q_OBJECT -+ Q_DECLARE_PUBLIC(QLlcpSocket) -+ -+public: -+ QLlcpSocketPrivate(QLlcpSocket *q); -+ -+ ~QLlcpSocketPrivate(); -+ -+ void connectToService(QNearFieldTarget *target, const QString &serviceUri); -+ -+ bool bind(quint8 port); -+ -+ bool hasPendingDatagrams() const; -+ qint64 pendingDatagramSize() const; -+ -+ qint64 writeDatagram(const char *data, qint64 size); -+ qint64 writeDatagram(const QByteArray &datagram); -+ -+ qint64 readDatagram(char *data, qint64 maxSize, -+ QNearFieldTarget **target = 0, quint8 *port = 0); -+ qint64 writeDatagram(const char *data, qint64 size, -+ QNearFieldTarget *target, quint8 port); -+ qint64 writeDatagram(const QByteArray &datagram, QNearFieldTarget *target, quint8 port); -+ -+ QLlcpSocket::SocketError error() const; -+ QLlcpSocket::SocketState state() const; -+ -+ qint64 readData(char *data, qint64 maxlen); -+ qint64 writeData(const char *data, qint64 len); -+ -+ qint64 bytesAvailable() const; -+ bool canReadLine() const; -+ -+ bool waitForReadyRead(int msecs); -+ bool waitForBytesWritten(int msecs); -+ bool waitForConnected(int msecs); -+ bool waitForDisconnected(int msecs); -+ -+ //Q_INVOKABLE void connected(nfc_target_t *); -+ Q_INVOKABLE void targetLost(); -+ -+ void dataRead(QByteArray&); -+ void dataWritten(); -+ -+public Q_SLOTS: -+ void disconnectFromService(); -+ -+private: -+ QLlcpSocket *q_ptr; -+ unsigned int m_sap; -+ //nfc_llcp_connection_listener_t m_conListener; -+ //NearFieldTarget *m_target; -+ //nfc_target_t *m_target; -+ -+ QLlcpSocket::SocketState m_state; -+ -+ QList m_receivedDatagrams; -+ QList m_writeQueue; -+ -+ bool m_server; -+ -+ enum llcpState { -+ Idle, Reading, Writing -+ } socketState; -+ -+private Q_SLOTS: -+ void read(); -+ void enteringIdle(); -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QLLCPSOCKET_OPENHARMONY_P_H -diff --git a/src/nfc/qnearfieldmanager.cpp b/src/nfc/qnearfieldmanager.cpp -index 02e4d918..2da5f74a 100644 ---- a/src/nfc/qnearfieldmanager.cpp -+++ b/src/nfc/qnearfieldmanager.cpp -@@ -46,6 +46,8 @@ - #include "qnearfieldmanager_neard_p.h" - #elif defined(ANDROID_NFC) - #include "qnearfieldmanager_android_p.h" -+#elif defined(OPENHARMONY_NFC) -+#include "qnearfieldmanager_openharmony_p.h" - #else - #include "qnearfieldmanagerimpl_p.h" - #endif -diff --git a/src/nfc/qnearfieldmanager_openharmony.cpp b/src/nfc/qnearfieldmanager_openharmony.cpp -new file mode 100644 -index 00000000..e1087e98 ---- /dev/null -+++ b/src/nfc/qnearfieldmanager_openharmony.cpp -@@ -0,0 +1,279 @@ -+#include "qnearfieldmanager_openharmony_p.h" -+#include "qnearfieldtarget_openharmony_p.h" -+ -+#include "qndeffilter.h" -+#include "qndefmessage.h" -+#include "qndefrecord.h" -+#include "qbytearray.h" -+#include "qcoreapplication.h" -+#include "qdebug.h" -+#include "qlist.h" -+ -+#include -+#include -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+ -+QNearFieldManagerPrivateImpl::QNearFieldManagerPrivateImpl() : -+ m_detecting(false), m_handlerID(0) -+{ -+ QtHarmonyPrivate::registerNfcStateListener(this); -+ connect(this, &QNearFieldManagerPrivateImpl::targetDetected, this, &QNearFieldManagerPrivateImpl::handlerTargetDetected); -+ connect(this, &QNearFieldManagerPrivateImpl::targetLost, this, &QNearFieldManagerPrivateImpl::handlerTargetLost); -+} -+ -+QNearFieldManagerPrivateImpl::~QNearFieldManagerPrivateImpl() -+{ -+ QtHarmonyPrivate::unregisterNfcStateListener(this); -+} -+ -+void QNearFieldManagerPrivateImpl::handlerTargetDetected(QNearFieldTarget *target) -+{ -+ if (ndefMessageHandlers.count() == 0 && ndefFilterHandlers.count() == 0) // if no handler is registered -+ return; -+ if (target->hasNdefMessage()) { -+ connect(reinterpret_cast(target), &NearFieldTarget::ndefMessageRead, -+ this, &QNearFieldManagerPrivateImpl::handlerNdefMessageRead); -+ connect(target, &QNearFieldTarget::requestCompleted, -+ this, &QNearFieldManagerPrivateImpl::handlerRequestCompleted); -+ connect(target, &QNearFieldTarget::error, -+ this, &QNearFieldManagerPrivateImpl::handlerError); -+ -+ QNearFieldTarget::RequestId id = target->readNdefMessages(); -+ m_idToTarget.insert(id, target); -+ } -+} -+ -+void QNearFieldManagerPrivateImpl::handlerTargetLost(QNearFieldTarget *target) -+{ -+ disconnect(reinterpret_cast(target), &NearFieldTarget::ndefMessageRead, -+ this, &QNearFieldManagerPrivateImpl::handlerNdefMessageRead); -+ disconnect(target, &QNearFieldTarget::requestCompleted, -+ this, &QNearFieldManagerPrivateImpl::handlerRequestCompleted); -+ disconnect(target, &QNearFieldTarget::error, -+ this, &QNearFieldManagerPrivateImpl::handlerError); -+ m_idToTarget.remove(m_idToTarget.key(target)); -+} -+ -+struct VerifyRecord -+{ -+ QNdefFilter::Record filterRecord; -+ unsigned int count; -+}; -+ -+void QNearFieldManagerPrivateImpl::handlerNdefMessageRead(const QNdefMessage &message, const QNearFieldTarget::RequestId &id) -+{ -+ QNearFieldTarget *target = m_idToTarget.value(id); -+ //For message handlers without filters -+ for (int i = 0; i < ndefMessageHandlers.count(); i++) { -+ ndefMessageHandlers.at(i).second.invoke(ndefMessageHandlers.at(i).first.second, Q_ARG(QNdefMessage, message), Q_ARG(QNearFieldTarget*, target)); -+ } -+ -+ //For message handlers that specified a filter -+ for (int i = 0; i < ndefFilterHandlers.count(); ++i) { -+ bool matched = true; -+ -+ QNdefFilter filter = ndefFilterHandlers.at(i).second.first; -+ -+ QList filterRecords; -+ for (int j = 0; j < filter.recordCount(); ++j) { -+ VerifyRecord vr; -+ vr.count = 0; -+ vr.filterRecord = filter.recordAt(j); -+ -+ filterRecords.append(vr); -+ } -+ -+ for (const QNdefRecord &record : message) { -+ for (int j = 0; matched && (j < filterRecords.count()); ++j) { -+ VerifyRecord &vr = filterRecords[j]; -+ -+ if (vr.filterRecord.typeNameFormat == record.typeNameFormat() && -+ ( vr.filterRecord.type == record.type() || -+ vr.filterRecord.type.isEmpty()) ) { -+ ++vr.count; -+ break; -+ } else { -+ if (filter.orderMatch()) { -+ if (vr.filterRecord.minimum <= vr.count && -+ vr.count <= vr.filterRecord.maximum) { -+ continue; -+ } else { -+ matched = false; -+ } -+ } -+ } -+ } -+ } -+ -+ for (int j = 0; matched && (j < filterRecords.count()); ++j) { -+ const VerifyRecord &vr = filterRecords.at(j); -+ -+ if (vr.filterRecord.minimum <= vr.count && vr.count <= vr.filterRecord.maximum) -+ continue; -+ else -+ matched = false; -+ } -+ -+ if (matched) { -+ ndefFilterHandlers.at(i).second.second.invoke(ndefFilterHandlers.at(i).first.second, Q_ARG(QNdefMessage, message), Q_ARG(QNearFieldTarget*, target)); -+ } -+ } -+} -+ -+void QNearFieldManagerPrivateImpl::handlerRequestCompleted(const QNearFieldTarget::RequestId &id) -+{ -+ m_idToTarget.remove(id); -+} -+ -+void QNearFieldManagerPrivateImpl::handlerError(QNearFieldTarget::Error error, const QNearFieldTarget::RequestId &id) -+{ -+ Q_UNUSED(error); -+ m_idToTarget.remove(id); -+} -+ -+bool QNearFieldManagerPrivateImpl::isAvailable() const -+{ -+ return QtHarmonyPrivate::isAvailable(); -+} -+ -+bool QNearFieldManagerPrivateImpl::isSupported() const -+{ -+ return QtHarmonyPrivate::isSupported(); -+} -+ -+bool QNearFieldManagerPrivateImpl::startTargetDetection() -+{ -+ if (m_detecting) -+ return false; // Already detecting targets -+ -+ m_detecting = true; -+ updateReceiveState(); -+ return true; -+} -+ -+void QNearFieldManagerPrivateImpl::stopTargetDetection() -+{ -+ m_detecting = false; -+ updateReceiveState(); -+} -+ -+// FIXME This is supposed to be a platform registration. A message that -+// matches the given NDEF filter should restart the current application. -+// The implementation below only works as long as the current application -+// is running. It is not a platform wide registration on Android. -+int QNearFieldManagerPrivateImpl::registerNdefMessageHandler(QObject *object, const QMetaMethod &method) -+{ -+ ndefMessageHandlers.append(QPair, QMetaMethod>(QPair(m_handlerID, object), method)); -+ updateReceiveState(); -+ //Returns the handler ID and increments it afterwards -+ return m_handlerID++; -+} -+ -+// FIXME see above -+int QNearFieldManagerPrivateImpl::registerNdefMessageHandler(const QNdefFilter &filter, -+ QObject *object, const QMetaMethod &method) -+{ -+ //If no record is set in the filter, we ignore the filter -+ if (filter.recordCount()==0) -+ return registerNdefMessageHandler(object, method); -+ -+ ndefFilterHandlers.append(QPair, QPair > -+ (QPair(m_handlerID, object), QPair(filter, method))); -+ -+ updateReceiveState(); -+ -+ return m_handlerID++; -+} -+ -+bool QNearFieldManagerPrivateImpl::unregisterNdefMessageHandler(int handlerId) -+{ -+ for (int i=0; i tag = qJsObjectLoader->create(tagInfo); -+ return tag->call("uid"); -+} -+ -+void QNearFieldManagerPrivateImpl::newNfc(const QString &tagInfo) -+{ -+ // This function is called from different thread and is used to move intent to main thread. -+ QMetaObject::invokeMethod(this, "onTargetDiscovered", Qt::QueuedConnection, Q_ARG(QString, tagInfo)); -+} -+ -+void QNearFieldManagerPrivateImpl::handleStateChanged(int state) -+{ -+ QNearFieldManager::AdapterState adapterState = static_cast((int) state); -+ emit adapterStateChanged(adapterState); -+} -+ -+void QNearFieldManagerPrivateImpl::onTargetDiscovered(const QString &tagInfo) -+{ -+ // Getting UID -+ QByteArray uid = getUid(tagInfo); -+ if (uid.isEmpty()) -+ return; -+ -+ // Accepting all targets but only sending signal of requested types. -+ NearFieldTarget *&target = m_detectedTargets[uid]; -+ if (target) { -+ target->setIndex(tagInfo); // Updating existing target -+ } else { -+ target = new NearFieldTarget(tagInfo, uid, this); -+ connect(target, &NearFieldTarget::targetDestroyed, this, &QNearFieldManagerPrivateImpl::onTargetDestroyed); -+ connect(target, &NearFieldTarget::targetLost, this, &QNearFieldManagerPrivateImpl::targetLost); -+ } -+ emit targetDetected(target); -+} -+ -+void QNearFieldManagerPrivateImpl::onTargetDestroyed(const QByteArray &uid) -+{ -+ m_detectedTargets.remove(uid); -+} -+ -+void QNearFieldManagerPrivateImpl::updateReceiveState() -+{ -+ if (m_detecting) { -+ QtHarmonyPrivate::registerNfcListener(this); -+ } else { -+ if (ndefMessageHandlers.count() || ndefFilterHandlers.count()) { -+ QtHarmonyPrivate::registerNfcListener(this); -+ } else { -+ QtHarmonyPrivate::unregisterNfcListener(this); -+ } -+ } -+} -+ -+QT_END_NAMESPACE -diff --git a/src/nfc/qnearfieldmanager_openharmony_p.h b/src/nfc/qnearfieldmanager_openharmony_p.h -new file mode 100644 -index 00000000..bd9cc9a5 ---- /dev/null -+++ b/src/nfc/qnearfieldmanager_openharmony_p.h -@@ -0,0 +1,68 @@ -+#ifndef QNEARFIELDMANAGER_OPENHARMONY_P_H -+#define QNEARFIELDMANAGER_OPENHARMONY_P_H -+ -+#include "qnearfieldmanager_p.h" -+#include "qnearfieldmanager.h" -+#include "qnearfieldtarget.h" -+#include "openharmony/openharmonynfc_p.h" -+ -+#include -+#include -+#include -+class QOpenHarmonyJsObject; -+ -+QT_BEGIN_NAMESPACE -+ -+typedef QList QNdefMessageList; -+ -+class NearFieldTarget; -+class QByteArray; -+class QNearFieldManagerPrivateImpl : public QNearFieldManagerPrivate -+ , public QtHarmonyPrivate::OpenHarmonyNfcListenerInterface -+ , public QtHarmonyPrivate::OpenHarmonyNfcStateListenerInterface -+{ -+ Q_OBJECT -+ -+public: -+ QNearFieldManagerPrivateImpl(); -+ ~QNearFieldManagerPrivateImpl() override; -+ -+ bool isAvailable() const override; -+ bool isSupported() const override; -+ bool startTargetDetection() override; -+ void stopTargetDetection() override; -+ int registerNdefMessageHandler(QObject *object, const QMetaMethod &method) override; -+ int registerNdefMessageHandler(const QNdefFilter &filter, QObject *object, const QMetaMethod &method) override; -+ bool unregisterNdefMessageHandler(int handlerId) override; -+ void requestAccess(QNearFieldManager::TargetAccessModes accessModes) override; -+ void releaseAccess(QNearFieldManager::TargetAccessModes accessModes) override; -+ QByteArray getUid(const QString &tagInfo); -+ -+ virtual void newNfc(const QString &tagInfo); -+ virtual void handleStateChanged(int state); -+ -+public slots: -+ void onTargetDiscovered(const QString &tagInfo); -+ void onTargetDestroyed(const QByteArray &uid); -+ void handlerTargetDetected(QNearFieldTarget *target); -+ void handlerTargetLost(QNearFieldTarget *target); -+ void handlerNdefMessageRead(const QNdefMessage &message, const QNearFieldTarget::RequestId &id); -+ void handlerRequestCompleted(const QNearFieldTarget::RequestId &id); -+ void handlerError(QNearFieldTarget::Error error, const QNearFieldTarget::RequestId &id); -+ -+protected: -+ void updateReceiveState(); -+ -+private: -+ bool m_detecting; -+ QHash m_detectedTargets; -+ QMap m_idToTarget; -+ -+ int m_handlerID; -+ QList< QPair, QMetaMethod> > ndefMessageHandlers; -+ QList< QPair, QPair > > ndefFilterHandlers; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QNEARFIELDMANAGER_OPENHARMONY_P_H -diff --git a/src/nfc/qnearfieldtarget_openharmony.cpp b/src/nfc/qnearfieldtarget_openharmony.cpp -new file mode 100644 -index 00000000..6de0a783 ---- /dev/null -+++ b/src/nfc/qnearfieldtarget_openharmony.cpp -@@ -0,0 +1,366 @@ -+#include "qnearfieldtarget_openharmony_p.h" -+#include "qdebug.h" -+ -+#include "qopenharmonyjsobjectloader.h" -+#include -+ -+// from openharmony document https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/reference/apis/js-apis-nfcTag.md/#taginfo -+#define NDEFTECHNOLOGY QStringLiteral("6") -+#define NDEFFORMATABLETECHNOLOGY QStringLiteral("7") -+#define ISODEPTECHNOLOGY QStringLiteral("3") -+#define NFCATECHNOLOGY QStringLiteral("1") -+#define NFCBTECHNOLOGY QStringLiteral("2") -+#define NFCFTECHNOLOGY QStringLiteral("4") -+#define NFCVTECHNOLOGY QStringLiteral("5") -+#define MIFARECLASSICTECHNOLOGY QStringLiteral("8") -+#define MIFARECULTRALIGHTTECHNOLOGY QStringLiteral("9") -+ -+#define MIFARETAG QStringLiteral("com.nxp.ndef.mifareclassic") -+#define NFCTAGTYPE1 QStringLiteral("org.nfcforum.ndef.type1") -+#define NFCTAGTYPE2 QStringLiteral("org.nfcforum.ndef.type2") -+#define NFCTAGTYPE3 QStringLiteral("org.nfcforum.ndef.type3") -+#define NFCTAGTYPE4 QStringLiteral("org.nfcforum.ndef.type4") -+ -+NearFieldTarget::NearFieldTarget(const QString &tag, const QByteArray &uid, QObject *parent) : -+ QNearFieldTarget(parent), -+ m_tag(tag), -+ m_uid(uid), -+ m_keepConnection(false) -+{ -+ m_jsTag = qJsObjectLoader->create(m_tag); -+ updateTechList(); -+ updateType(); -+ setupTargetCheckTimer(); -+} -+ -+NearFieldTarget::~NearFieldTarget() -+{ -+ releaseIntent(); -+ emit targetDestroyed(m_uid); -+} -+ -+QByteArray NearFieldTarget::uid() const -+{ -+ return m_uid; -+} -+ -+QNearFieldTarget::Type NearFieldTarget::type() const -+{ -+ return m_type; -+} -+ -+QNearFieldTarget::AccessMethods NearFieldTarget::accessMethods() const -+{ -+ AccessMethods result = UnknownAccess; -+ -+ if (m_techList.contains(NDEFTECHNOLOGY) -+ || m_techList.contains(NDEFFORMATABLETECHNOLOGY)) -+ result |= NdefAccess; -+ -+ if (m_techList.contains(ISODEPTECHNOLOGY) -+ || m_techList.contains(NFCATECHNOLOGY) -+ || m_techList.contains(NFCBTECHNOLOGY) -+ || m_techList.contains(NFCFTECHNOLOGY) -+ || m_techList.contains(NFCVTECHNOLOGY)) -+ result |= TagTypeSpecificAccess; -+ -+ return result; -+} -+ -+bool NearFieldTarget::keepConnection() const -+{ -+ return m_keepConnection; -+} -+ -+bool NearFieldTarget::setKeepConnection(bool isPersistent) -+{ -+ m_keepConnection = isPersistent; -+ -+ if (!m_keepConnection) -+ disconnect(); -+ -+ return true; -+} -+ -+bool NearFieldTarget::disconnect() -+{ -+ bool connected = m_jsTag->call("isConnected"); -+ -+ if (!connected) -+ return false; -+ -+ return m_jsTag->call("close"); -+} -+ -+bool NearFieldTarget::hasNdefMessage() -+{ -+ return m_techList.contains(NDEFTECHNOLOGY); -+} -+ -+QNearFieldTarget::RequestId NearFieldTarget::readNdefMessages() -+{ -+ // Making sure that target has NDEF messages -+ if (!hasNdefMessage()) -+ return QNearFieldTarget::RequestId(); -+ -+ // Making sure that target is still in range -+ QNearFieldTarget::RequestId requestId(new QNearFieldTarget::RequestIdPrivate); -+ bool valid = m_jsTag->call("isInRange"); -+ if (!valid) { -+ reportError(QNearFieldTarget::TargetOutOfRangeError, requestId); -+ return requestId; -+ } -+ -+ QByteArray result = m_jsTag->call("getNdefMessage"); -+ if (result.isEmpty()) -+ return requestId; -+ -+ if (!m_keepConnection) { -+ // Closing connection -+ disconnect(); // IOException at this point does not matter anymore. -+ } -+ -+ // Sending QNdefMessage, requestCompleted and exit. -+ QNdefMessage qNdefMessage = QNdefMessage::fromByteArray(result); -+ QMetaObject::invokeMethod(this, [this, qNdefMessage]() { -+ Q_EMIT this->QNearFieldTarget::ndefMessageRead(qNdefMessage); -+ }, Qt::QueuedConnection); -+ QMetaObject::invokeMethod(this, [this, requestId]() { -+ Q_EMIT this->requestCompleted(requestId); -+ }, Qt::QueuedConnection); -+ QMetaObject::invokeMethod(this, [this, qNdefMessage, requestId]() { -+ //TODO This is an Android specific signal in NearFieldTarget. -+ // We need to check if it is still necessary. -+ Q_EMIT this->ndefMessageRead(qNdefMessage, requestId); -+ }, Qt::QueuedConnection); -+ return requestId; -+} -+ -+int NearFieldTarget::maxCommandLength() const -+{ -+ return m_jsTag->call("maxCommandLength"); -+} -+ -+QNearFieldTarget::RequestId NearFieldTarget::sendCommand(const QByteArray &command) -+{ -+ if (command.size() == 0 || command.size() > maxCommandLength()) { -+ Q_EMIT QNearFieldTarget::error(QNearFieldTarget::InvalidParametersError, QNearFieldTarget::RequestId()); -+ return QNearFieldTarget::RequestId(); -+ } -+ -+ // Making sure that target has commands -+ if (!(accessMethods() & TagTypeSpecificAccess)) -+ return QNearFieldTarget::RequestId(); -+ -+ -+ if (!setTagTechnology({ISODEPTECHNOLOGY, NFCATECHNOLOGY, NFCBTECHNOLOGY, NFCFTECHNOLOGY, NFCVTECHNOLOGY})) { -+ Q_EMIT QNearFieldTarget::error(QNearFieldTarget::UnsupportedError, QNearFieldTarget::RequestId()); -+ return QNearFieldTarget::RequestId(); -+ } -+ -+ // Connecting -+ QNearFieldTarget::RequestId requestId = QNearFieldTarget::RequestId(new QNearFieldTarget::RequestIdPrivate()); -+ if (!connect()) { -+ reportError(QNearFieldTarget::TargetOutOfRangeError, requestId); -+ return requestId; -+ } -+ -+ // Writing -+ QByteArray result = m_jsTag->call("transceive", command); -+ -+ handleResponse(requestId, result); -+ -+ if (!m_keepConnection) { -+ // Closing connection -+ disconnect(); // IOException at this point does not matter anymore. -+ } -+ QMetaObject::invokeMethod(this, [this, requestId]() { -+ Q_EMIT this->requestCompleted(requestId); -+ }, Qt::QueuedConnection); -+ -+ return requestId; -+} -+ -+QNearFieldTarget::RequestId NearFieldTarget::sendCommands(const QList &commands) -+{ -+ QNearFieldTarget::RequestId requestId; -+ for (int i=0; i < commands.size(); i++) -+ requestId = sendCommand(commands.at(i)); -+ return requestId; -+} -+ -+QNearFieldTarget::RequestId NearFieldTarget::writeNdefMessages(const QList &messages) -+{ -+ if (messages.size() == 0) -+ return QNearFieldTarget::RequestId(); -+ -+ if (messages.size() > 1) -+ qWarning("QNearFieldTarget::writeNdefMessages: harmony supports writing only one NDEF message per tag."); -+ -+ QString writeMethod; -+ // Getting write method -+ if (m_tech == NDEFFORMATABLETECHNOLOGY) -+ writeMethod = "format"; -+ else -+ writeMethod = "writeNdefMessage"; -+ -+ if (!setTagTechnology({NDEFFORMATABLETECHNOLOGY, NDEFTECHNOLOGY})) -+ return QNearFieldTarget::RequestId(); -+ -+ // Connecting -+ QNearFieldTarget::RequestId requestId = QNearFieldTarget::RequestId(new QNearFieldTarget::RequestIdPrivate()); -+ if (!connect()) { -+ reportError(QNearFieldTarget::TargetOutOfRangeError, requestId); -+ return requestId; -+ } -+ -+ const QNdefMessage &message = messages.first(); -+ QByteArray ba = message.toByteArray(); -+ m_jsTag->callWithoutReturn(writeMethod, ba); -+ -+ if (!m_keepConnection) -+ disconnect(); // IOException at this point does not matter anymore. -+ QMetaObject::invokeMethod(this, "ndefMessagesWritten", Qt::QueuedConnection); -+ return requestId; -+} -+ -+void NearFieldTarget::setIndex(const QString &tag) -+{ -+ if (m_tag == tag) -+ return; -+ -+ releaseIntent(); -+ m_tag = tag; -+ updateTechList(); -+ updateType(); -+ m_targetCheckTimer->start(); -+} -+ -+void NearFieldTarget::checkIsTargetLost() -+{ -+// if (!m_intent.isValid() || !setTagTechnology(m_techList)) { -+// handleTargetLost(); -+// return; -+// } -+ -+// bool connected = m_tagTech.callMethod("isConnected"); -+// if (catchJavaExceptions()) { -+// handleTargetLost(); -+// return; -+// } -+ -+// if (connected) -+// return; -+ -+// m_tagTech.callMethod("connect"); -+// if (catchJavaExceptions(false)) { -+// handleTargetLost(); -+// return; -+// } -+// m_tagTech.callMethod("close"); -+// if (catchJavaExceptions(false)) -+// handleTargetLost(); -+} -+ -+void NearFieldTarget::releaseIntent() -+{ -+ m_targetCheckTimer->stop(); -+} -+ -+void NearFieldTarget::updateTechList() -+{ -+ QString str = m_jsTag->call("technologies"); -+ if (str.isEmpty()) { -+ return; -+ } -+ m_techList = str.split(","); -+} -+ -+void NearFieldTarget::updateType() -+{ -+ m_type = getTagType(); -+} -+ -+QNearFieldTarget::Type NearFieldTarget::getTagType() const -+{ -+ if (m_techList.contains(NDEFTECHNOLOGY)) { -+ QString qtype = m_jsTag->call("getType"); -+ -+ if (qtype.compare(MIFARETAG) == 0) -+ return MifareTag; -+ if (qtype.compare(NFCTAGTYPE1) == 0) -+ return NfcTagType1; -+ if (qtype.compare(NFCTAGTYPE2) == 0) -+ return NfcTagType2; -+ if (qtype.compare(NFCTAGTYPE3) == 0) -+ return NfcTagType3; -+ if (qtype.compare(NFCTAGTYPE4) == 0) -+ return NfcTagType4; -+ return ProprietaryTag; -+ } else if (m_techList.contains(NFCATECHNOLOGY)) { -+ if (m_techList.contains(MIFARECLASSICTECHNOLOGY)) -+ return MifareTag; -+ -+ // Checking ATQA/SENS_RES -+ // xxx0 0000 xxxx xxxx: Identifies tag Type 1 platform -+ QByteArray atqaQBA = m_jsTag->call("getAtqa"); -+ if (atqaQBA.isEmpty()) -+ return ProprietaryTag; -+ if ((atqaQBA[0] & 0x1F) == 0x00) -+ return NfcTagType1; -+ -+ // Checking SAK/SEL_RES -+ // xxxx xxxx x00x x0xx: Identifies tag Type 2 platform -+ // xxxx xxxx x01x x0xx: Identifies tag Type 4 platform -+ int sakS = m_jsTag->call("getSak"); -+ if ((sakS & 0x0064) == 0x0000) -+ return NfcTagType2; -+ else if ((sakS & 0x0064) == 0x0020) -+ return NfcTagType4; -+ return ProprietaryTag; -+ } else if (m_techList.contains(NFCBTECHNOLOGY)) { -+ return NfcTagType4; -+ } else if (m_techList.contains(NFCFTECHNOLOGY)) { -+ return NfcTagType3; -+ } -+ -+ return ProprietaryTag; -+} -+ -+void NearFieldTarget::setupTargetCheckTimer() -+{ -+ m_targetCheckTimer = new QTimer(this); -+ m_targetCheckTimer->setInterval(1000); -+ QObject::connect(m_targetCheckTimer, &QTimer::timeout, this, &NearFieldTarget::checkIsTargetLost); -+ m_targetCheckTimer->start(); -+} -+ -+void NearFieldTarget::handleTargetLost() -+{ -+ releaseIntent(); -+ emit targetLost(this); -+} -+ -+bool NearFieldTarget::setTagTechnology(const QStringList &techList) -+{ -+ for (const QString &tech : techList) { -+ if (m_techList.contains(tech)) { -+ if (m_tech == tech) { -+ return true; -+ } -+ m_tech = tech; -+ return true; -+ } -+ } -+ return false; -+} -+ -+bool NearFieldTarget::connect() -+{ -+ bool connected = m_jsTag->call("isConnected"); -+ -+ if (connected) -+ return true; -+ -+ return m_jsTag->call("connect"); -+} -diff --git a/src/nfc/qnearfieldtarget_openharmony_p.cpp b/src/nfc/qnearfieldtarget_openharmony_p.cpp -new file mode 100644 -index 00000000..f7bc0390 ---- /dev/null -+++ b/src/nfc/qnearfieldtarget_openharmony_p.cpp -@@ -0,0 +1,32 @@ -+#include -+ -+#include "qnearfieldtarget_p.h" -+#include "qnearfieldtarget_openharmony_p.h" -+ -+QT_BEGIN_NAMESPACE -+ -+bool QNearFieldTargetPrivate::keepConnection() const -+{ -+ NEARFIELDTARGET_Q(); -+ return q->keepConnection(); -+} -+ -+bool QNearFieldTargetPrivate::setKeepConnection(bool isPersistent) -+{ -+ NEARFIELDTARGET_Q(); -+ return q->setKeepConnection(isPersistent); -+} -+ -+bool QNearFieldTargetPrivate::disconnect() -+{ -+ NEARFIELDTARGET_Q(); -+ return q->disconnect(); -+} -+ -+int QNearFieldTargetPrivate::maxCommandLength() const -+{ -+ NEARFIELDTARGET_Q(); -+ return q->maxCommandLength(); -+} -+ -+QT_END_NAMESPACE -diff --git a/src/nfc/qnearfieldtarget_openharmony_p.h b/src/nfc/qnearfieldtarget_openharmony_p.h -new file mode 100644 -index 00000000..cb8a815b ---- /dev/null -+++ b/src/nfc/qnearfieldtarget_openharmony_p.h -@@ -0,0 +1,68 @@ -+#ifndef QNEARFIELDTARGET_OPENHARMONY_P_H -+#define QNEARFIELDTARGET_OPENHARMONY_P_H -+ -+ -+#include "qnearfieldtarget.h" -+#include "qnearfieldtarget_p.h" -+#include "qndefmessage.h" -+#include "qlist.h" -+#include "qstringlist.h" -+#include -+#include -+class QOpenHarmonyJsObject; -+ -+QT_BEGIN_NAMESPACE -+ -+class NearFieldTarget : public QNearFieldTarget -+{ -+ Q_OBJECT -+public: -+ NearFieldTarget(const QString &tag, const QByteArray &uid, -+ QObject *parent = 0); -+ virtual ~NearFieldTarget(); -+ virtual QByteArray uid() const; -+ virtual Type type() const; -+ virtual AccessMethods accessMethods() const; -+ bool keepConnection() const; -+ bool setKeepConnection(bool isPersistent); -+ bool disconnect(); -+ virtual bool hasNdefMessage(); -+ virtual RequestId readNdefMessages(); -+ int maxCommandLength() const; -+ virtual RequestId sendCommand(const QByteArray &command); -+ virtual RequestId sendCommands(const QList &commands); -+ virtual RequestId writeNdefMessages(const QList &messages); -+ void setIndex(const QString &tag); -+ -+signals: -+ void targetDestroyed(const QByteArray &tagId); -+ void targetLost(QNearFieldTarget *target); -+ void ndefMessageRead(const QNdefMessage &message, const QNearFieldTarget::RequestId &id); -+ -+protected slots: -+ void checkIsTargetLost(); -+ -+protected: -+ void releaseIntent(); -+ void updateTechList(); -+ void updateType(); -+ Type getTagType() const; -+ void setupTargetCheckTimer(); -+ void handleTargetLost(); -+ bool setTagTechnology(const QStringList &techList); -+ bool connect(); -+ -+protected: -+ QString m_tag; -+ QSharedPointer m_jsTag; -+ QByteArray m_uid; -+ QStringList m_techList; -+ Type m_type; -+ QTimer *m_targetCheckTimer; -+ QString m_tech; -+ bool m_keepConnection; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QNEARFIELDTARGET_OPENHARMONY_P_H -diff --git a/src/openharmony/bluetooth/bluetooth.pro b/src/openharmony/bluetooth/bluetooth.pro -new file mode 100644 -index 00000000..f063639d ---- /dev/null -+++ b/src/openharmony/bluetooth/bluetooth.pro -@@ -0,0 +1,9 @@ -+TEMPLATE = aux -+ -+CONFIG -= qt -+ -+templates.files += $$files($$PWD/native/QtBluetooth/*, true) -+templates.path = $$[QT_INSTALL_PREFIX]/openharmony/qtconnectivity -+templates.base = $$PWD -+ -+INSTALLS += templates -diff --git a/src/openharmony/bluetooth/native/QtBluetooth/QtBluetooth.ts b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetooth.ts -new file mode 100644 -index 00000000..aa65a203 ---- /dev/null -+++ b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetooth.ts -@@ -0,0 +1,25 @@ -+import bluetoothManager from '@ohos.bluetoothManager'; -+ -+export class QtBluetooth { -+ -+ private deviceId = '' -+ private clientSocket = null -+ private ble : boolean = false; -+ -+ constructor(deviceId : string, isBle: boolean) { -+ this.deviceId = deviceId; -+ this.ble = isBle; -+ } -+ -+ getClass() { -+ return bluetoothManager.getRemoteDeviceClass(this.deviceId).classOfDevice; -+ } -+ -+ getName() { -+ return bluetoothManager.getRemoteDeviceName(this.deviceId); -+ } -+ -+ isBLE() : boolean { -+ return this.ble -+ } -+} -\ No newline at end of file -diff --git a/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothBroadcastReceiver.ts b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothBroadcastReceiver.ts -new file mode 100644 -index 00000000..7c15cf3a ---- /dev/null -+++ b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothBroadcastReceiver.ts -@@ -0,0 +1,6 @@ -+import bluetoothManager from '@ohos.bluetoothManager'; -+ -+export class QtBluetoothBroadcastReceiver { -+ // bluetoothManager.on(‘pinRequired’) -+ -+} -diff --git a/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothDevice.ts b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothDevice.ts -new file mode 100644 -index 00000000..7d09433d ---- /dev/null -+++ b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothDevice.ts -@@ -0,0 +1,31 @@ -+import bluetoothManager from '@ohos.bluetoothManager'; -+import { QtBluetoothSocket } from './QtBluetoothSocket'; -+ -+export class QtBluetoothDevice { -+ -+ private deviceId = '' -+ private clientSocket = null -+ -+ constructor(deviceId) { -+ this.deviceId = deviceId; -+ } -+ -+ getClass() { -+ return bluetoothManager.getRemoteDeviceClass(this.deviceId).classOfDevice; -+ } -+ -+ getName() { -+ return bluetoothManager.getRemoteDeviceName(this.deviceId); -+ } -+ -+ createSocket(name, id) { -+ let socket = new QtBluetoothSocket(id); -+ Reflect.defineProperty(globalThis, name, {value: socket}); -+ return true; -+ } -+ -+ closeSocket(name) { -+ Reflect.deleteProperty(globalThis, name); -+ return true; -+ } -+} -\ No newline at end of file -diff --git a/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothGattServer.ts b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothGattServer.ts -new file mode 100644 -index 00000000..ea05870c ---- /dev/null -+++ b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothGattServer.ts -@@ -0,0 +1,257 @@ -+import bluetoothManager from '@ohos.bluetoothManager'; -+import HashMap from '@ohos.util.HashMap'; -+import { QtBluetoothGattService } from './QtBluetoothGattService' -+import { QtBluetoothGattServerCallback } from './QtBluetoothGattServerCallback' -+ -+export class QtBluetoothGattServer { -+ private gattServer : bluetoothManager.GattServer; -+ private qtObject : number; -+ private services : HashMap; -+ private devices : Array = []; -+ private deviceId : string = ''; -+ private callback : QtBluetoothGattServerCallback; -+ -+ constructor() { -+ this.services = new HashMap(); -+ } -+ -+ setQtObject(qtObject: number) { -+ this.qtObject = qtObject; -+ } -+ -+ getQtObject() : number { -+ return this.qtObject; -+ } -+ -+ server() : bluetoothManager.GattServer { -+ return this.gattServer; -+ } -+ -+ addDevice(deviceId: string) { -+ if (this.devices.indexOf(deviceId) == -1) -+ this.devices.push(deviceId) -+ this.deviceId = deviceId; -+ } -+ -+ connectServer() : boolean { -+ if (this.gattServer) -+ return true; -+ this.gattServer = bluetoothManager.BLE.createGattServer(); -+ return true; -+ } -+ -+ disconnectServer() : boolean { -+ if (!this.gattServer) -+ return; -+ -+ this.callback.stop(); -+ this.gattServer.close(); -+ this.callback = null; -+ this.gattServer = null; -+ globalThis.qtbluetooth.connectChanged(this.qtObject, 0 /*NoError*/, 0 /*QLowEnergyController::UnconnectedState*/); -+ return true; -+ } -+ -+ startAdvertising(advertiseData : object, scanResponse : object, advertiseSettings : object) : boolean { -+ if (!this.connectServer()) { -+ console.error("Server::startAdvertising: Cannot open GATT server"); -+ return false; -+ } -+ this.callback = new QtBluetoothGattServerCallback(this); -+ this.callback.start(); -+ -+ if (!this.gattServer) -+ return false; -+ -+ console.log("Starting to advertise.", JSON.stringify(advertiseData), JSON.stringify(scanResponse), JSON.stringify(advertiseSettings)); -+ try { -+ -+ let _settings : bluetoothManager.AdvertiseSetting = { -+ interval:advertiseSettings["maximumInterval"], -+ connectable:advertiseSettings["connectable"], -+ } -+ -+ let _advertiseData : bluetoothManager.AdvertiseData = { -+ serviceUuids: advertiseData["serviceUuids"], -+ manufactureData:[], -+ serviceData:[], -+ includeDeviceName: advertiseData["includeDeviceName"] -+ } -+ -+ if (advertiseData.hasOwnProperty("manufacturerId") && advertiseData.hasOwnProperty("manufacturerData")) { -+ let manufacturerData : bluetoothManager.ManufactureData; -+ manufacturerData.manufactureId = advertiseData["manufacturerId"]; -+ manufacturerData.manufactureValue = advertiseData["manufacturerData"]; -+ _advertiseData.manufactureData.push(manufacturerData) -+ } -+ -+ let _scanResponse : bluetoothManager.AdvertiseData = { -+ serviceUuids: scanResponse["serviceUuids"], -+ manufactureData:[], -+ serviceData:[] -+ } -+ if (scanResponse.hasOwnProperty("manufacturerId") && scanResponse.hasOwnProperty("manufacturerData")) { -+ let manufacturerData : bluetoothManager.ManufactureData; -+ manufacturerData.manufactureId = scanResponse["manufacturerId"]; -+ manufacturerData.manufactureValue = scanResponse["manufacturerData"]; -+ _scanResponse.manufactureData.push(manufacturerData) -+ } -+ this.gattServer.startAdvertising(_settings, _advertiseData, _scanResponse); -+ return true; -+ } catch (err) { -+ console.error("startAdvertising errCode:" + err.code + ",errMessage:" + err.message); -+ globalThis.qtbluetooth.advertisementError(this.qtObject, err.message); -+ return false; -+ } -+ } -+ -+ stopAdvertising() : void -+ { -+ if (!this.gattServer) -+ return; -+ -+ try { -+ this.gattServer.stopAdvertising(); -+ console.log("Advertisement stopped."); -+ } catch (err) { -+ console.error("stopAdvertising errCode:" + err.code + ",errMessage:" + err.message); -+ } -+ } -+ -+ createService(service: string, uuid: string, isPrimary: boolean) : boolean { -+ if (this.services.hasKey(service)) -+ return false; -+ let s : QtBluetoothGattService = new QtBluetoothGattService(uuid, isPrimary); -+ this.services.set(service, s); -+ Reflect.defineProperty(globalThis, service, {value: s}); -+ this.addIncludeService(service); -+ return true; -+ } -+ -+ addIncludeService(service: string) : boolean { -+ if (!this.connectServer()) { -+ console.log("Server::addService: Cannot open GATT server"); -+ return false; -+ } -+ if (!this.services.hasKey(service)) -+ return false; -+ this.gattServer.addService(this.services.get(service).service()); -+ return true; -+ } -+ -+ getCharacteristic(serviceUuid: string, charUuid: string) : bluetoothManager.BLECharacteristic { -+ for (let item of this.services) { -+ let c = item[1].service().characteristics; -+ for (let i = 0 ; i < c.length; ++i) { -+ if (c[i].serviceUuid == serviceUuid && c[i].characteristicUuid == charUuid) -+ return c[i]; -+ } -+ } -+ return null -+ } -+ -+ getDescriptor(serviceUuid: string, charUuid: string, descUuid: string) : bluetoothManager.BLEDescriptor { -+ for (let item of this.services) { -+ let c = item[1].service().characteristics; -+ for (let i = 0 ; i < c.length; ++i) { -+ let d = c[i].descriptors; -+ for (let j = 0; j < d.length; ++j) { -+ if (d[j].serviceUuid == serviceUuid -+ && d[j].characteristicUuid == charUuid -+ && d[j].descriptorUuid == descUuid) { -+ return d[j]; -+ } -+ } -+ } -+ } -+ return null -+ } -+ -+ writeCharacteristic(service: string, uuid: string, value: ArrayBuffer) : boolean { -+ console.log("writeCharacteristic", service, uuid); -+ if (!this.services.hasKey(service)) -+ return false; -+ let s = this.services.get(service); -+ let gattService = s.service(); -+ let foundChar : bluetoothManager.BLECharacteristic = null; -+ let cArray : Array = gattService.characteristics; -+ for (let i = 0; i < cArray.length; ++i) { -+ let c = cArray[i]; -+ if (c.characteristicUuid == uuid && foundChar == null) { -+ foundChar = c; -+ // don't break here since we want to check next condition below on next iteration -+ } else if (c.characteristicUuid == uuid) { -+ console.error("Found second char with same UUID. Wrong char may have been selected."); -+ break; -+ } -+ } -+ -+ if (foundChar == null) { -+ console.error("writeCharacteristic: update for unknown characteristic failed"); -+ return false; -+ } -+ -+ foundChar.characteristicValue = value; -+ this.sendNotificationsOrIndications(foundChar); -+ -+ return true; -+ } -+ -+ sendNotificationsOrIndications(characteristic: bluetoothManager.BLECharacteristic) : void { -+ if (!this.gattServer) -+ return; -+ for (let i = 0; i < this.devices.length; ++i) { -+ let notifyCharacteristic = { -+ serviceUuid: characteristic.serviceUuid, -+ characteristicUuid: characteristic.characteristicUuid, -+ characteristicValue: characteristic.characteristicValue, -+ confirm: false -+ }; -+ this.gattServer.notifyCharacteristicChanged(this.devices[i], notifyCharacteristic); -+ } -+ } -+ -+ writeDescriptor(service: string, charUuid: string, descUuid: string, value: ArrayBuffer) : boolean { -+ if (!this.services.hasKey(service)) -+ return false; -+ let s = this.services.get(service); -+ let gattService = s.service(); -+ let foundDesc : bluetoothManager.BLEDescriptor = null; -+ let foundChar : bluetoothManager.BLECharacteristic = null; -+ let cArray : Array = gattService.characteristics; -+ for (let i = 0; i < cArray.length; ++i) { -+ let c = cArray[i]; -+ if (c.characteristicUuid == charUuid && foundChar == null) { -+ foundChar = c; -+ // don't break here since we want to check next condition below on next iteration -+ } else if (c.characteristicUuid == charUuid) { -+ console.error("Found second char with same UUID. Wrong char may have been selected."); -+ break; -+ } -+ } -+ -+ for (let i = 0; i < foundChar.descriptors.length; ++i) { -+ let d = foundChar.descriptors[i]; -+ if (d.descriptorUuid == descUuid) { -+ foundDesc = d; -+ break; -+ } -+ } -+ -+ if (foundChar == null || foundDesc == null) { -+ console.error("writeDescriptor: update for unknown char or desc failed (" + foundChar + ")"); -+ return false; -+ } -+ -+ foundDesc.descriptorValue = value; -+ return true; -+ } -+ -+ remoteName() : string { -+ return bluetoothManager.getRemoteDeviceName(this.deviceId); -+ } -+ -+ remoteAddress() : string { -+ return this.deviceId; -+ } -+} -\ No newline at end of file -diff --git a/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothGattServerCallback.ts b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothGattServerCallback.ts -new file mode 100644 -index 00000000..a4dbe238 ---- /dev/null -+++ b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothGattServerCallback.ts -@@ -0,0 +1,140 @@ -+import bluetoothManager from '@ohos.bluetoothManager'; -+import { QtBluetoothGattServer } from './QtBluetoothGattServer' -+ -+export class QtBluetoothGattServerCallback { -+ -+ private gattServer : QtBluetoothGattServer; -+ -+ constructor(gattServer : QtBluetoothGattServer) { -+ this.gattServer = gattServer; -+ -+ } -+ -+ start() { -+ let s = this.gattServer.server(); -+ -+ s.on("connectStateChange", (state : bluetoothManager.BLEConnectChangedState) => { -+ let deviceId : string = state.deviceId; -+ let s : bluetoothManager.ProfileConnectionState = state.state; -+ console.log("Our gatt server connection state changed, new state: " + deviceId + bluetoothManager.getRemoteDeviceName(deviceId)); -+ let qtControllerState : number = 0; -+ if (s == bluetoothManager.ProfileConnectionState.STATE_DISCONNECTED) -+ qtControllerState = 0;// QLowEnergyController::UnconnectedState -+ else if (s = bluetoothManager.ProfileConnectionState.STATE_CONNECTED) -+ qtControllerState = 2; -+ -+ this.gattServer.addDevice(deviceId); -+ globalThis.qtbluetooth.connectionChanged(this.gattServer.getQtObject(), 0 /*NoError*/, qtControllerState /*QLowEnergyController::UnconnectedState*/); -+ }); -+ -+ s.on("characteristicRead", (request: bluetoothManager.CharacteristicReadRequest) => { -+ let deviceId = request.deviceId; -+ let transId = request.transId; -+ let offset = request.offset; -+ let characteristicUuid = request.characteristicUuid; -+ console.log("onCharacteristicRead", deviceId, transId, offset, characteristicUuid); -+ let c = this.gattServer.getCharacteristic(request.serviceUuid, request.characteristicUuid); -+ let serverResponse = {deviceId: deviceId, transId: transId, status: 0, offset: offset, value: null}; -+ if (c != null) { -+ serverResponse.value = c.characteristicValue.slice(offset); -+ } -+ -+ try { -+ this.gattServer.server().sendResponse(serverResponse); -+ } catch (err) { -+ console.error("onCharacteristicRead errCode:" + err.code + ",errMessage:" + err.message); -+ this.gattServer.server().sendResponse({deviceId: deviceId, transId: transId, status: -1, offset: offset, value: null}); -+ } -+ }); -+ -+ s.on("characteristicWrite", (request: bluetoothManager.CharacteristicWriteRequest) => { -+ let deviceId = request.deviceId; -+ let transId = request.transId; -+ let offset = request.offset; -+ let isPrep = request.isPrep; -+ let needRsp = request.needRsp; -+ console.log("onCharacteristicWrite", deviceId, transId, offset, isPrep, needRsp); -+ let sendNotificationOrIndication :boolean = false; -+ let characteristic : bluetoothManager.BLECharacteristic = { -+ characteristicUuid: request.characteristicUuid, -+ characteristicValue: request.value, -+ serviceUuid: request.serviceUuid, -+ descriptors: [] -+ }; -+ let status = 0 -+ if (!isPrep) { // regular write -+ if (offset == 0) { -+ globalThis.qtbluetooth.serverCharacteristicChanged(this.gattServer.getQtObject(), request.serviceUuid, request.characteristicUuid, request.value); -+ sendNotificationOrIndication = true; -+ } else { -+ console.warn("onCharacteristicWriteRequest: !preparedWrite, offset " + offset + ", Not supported"); -+ status = -1; -+ } -+ } else { -+ console.warn("onCharacteristicWriteRequest: preparedWrite, offset " + offset + ", Not supported"); -+ status = -1; -+ } -+ -+ if (needRsp) -+ this.gattServer.server().sendResponse({deviceId: deviceId, transId: transId, status: status, offset: offset, value: request.value}); -+ if (sendNotificationOrIndication) -+ this.gattServer.sendNotificationsOrIndications(characteristic); -+ }); -+ -+ s.on("descriptorRead", (request: bluetoothManager.DescriptorReadRequest) => { -+ let deviceId = request.deviceId; -+ let transId = request.transId; -+ let offset = request.offset; -+ let descriptorUuid = request.descriptorUuid; -+ -+ console.log("onDescriptorRead", deviceId, transId, offset, descriptorUuid); -+ let d = this.gattServer.getDescriptor(request.serviceUuid, request.characteristicUuid, request.descriptorUuid); -+ let serverResponse = {deviceId: deviceId, transId: transId, status: 0, offset: offset, value: null}; -+ if (d != null) { -+ serverResponse.value = d.descriptorValue.slice(offset); -+ } -+ -+ try { -+ this.gattServer.server().sendResponse(serverResponse); -+ } catch (err) { -+ console.error("onDescriptorRead errCode:" + err.code + ",errMessage:" + err.message); -+ this.gattServer.server().sendResponse({deviceId: deviceId, transId: transId, status: -1, offset: offset, value: null}); -+ } -+ }); -+ -+ s.on("descriptorWrite", (request: bluetoothManager.DescriptorWriteRequest) => { -+ let deviceId = request.deviceId; -+ let transId = request.transId; -+ let offset = request.offset; -+ let isPrep = request.isPrep; -+ let needRsp = request.needRsp; -+ let status = 0; -+ console.log("onDescriptorWrite", deviceId, transId, offset, isPrep, needRsp); -+ if (!isPrep) { // regular write -+ if (offset == 0) { -+ globalThis.qtbluetooth.serverDescriptorWritten(this.gattServer.getQtObject(), request.serviceUuid, request.characteristicUuid, request.descriptorUuid, request.value); -+ status = -1; -+ } else { -+ console.warn("onDescriptorWriteRequest: !preparedWrite, offset " + offset + ", Not supported"); -+ } -+ } else { -+ console.warn("onDescriptorWriteRequest: preparedWrite, offset " + offset + ", Not supported"); -+ status = -1; -+ } -+ -+ -+ if (needRsp) -+ this.gattServer.server().sendResponse({deviceId: deviceId, transId: transId, status: status, offset: offset, value: request.value}); -+ }); -+ } -+ -+ stop() { -+ let s = this.gattServer.server(); -+ s.off("connectStateChange"); -+ s.off("characteristicRead"); -+ s.off("characteristicWrite"); -+ s.off("descriptorRead"); -+ s.off("descriptorWrite"); -+ } -+ -+} -\ No newline at end of file -diff --git a/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothGattService.ts b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothGattService.ts -new file mode 100644 -index 00000000..3e3c81c7 ---- /dev/null -+++ b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothGattService.ts -@@ -0,0 +1,65 @@ -+import bluetoothManager from '@ohos.bluetoothManager'; -+ -+export class QtBluetoothGattService { -+ -+ private uuid : string = '' -+ private gattService : bluetoothManager.GattService = null; -+ -+ constructor(uuid: string, isPrimary: boolean) { -+ this.uuid = uuid; -+ this.gattService = { -+ serviceUuid: uuid, -+ isPrimary: isPrimary, -+ characteristics: [] -+ }; -+ } -+ -+ service() : bluetoothManager.GattService { -+ return this.gattService; -+ } -+ -+ addCharacteristic(uuid, p, data) : boolean { -+ if (!this.gattService) -+ return; -+ -+ let pro : bluetoothManager.GattProperties = { -+ write: Boolean(p & 0x08), -+ writeNoResponse: Boolean(p & 0x04), -+ read: Boolean(p & 0x02), -+ notify: Boolean(p & 0x10), -+ indicate: Boolean(p & 0x20) -+ } -+ -+ let characteristic : bluetoothManager.BLECharacteristic = { -+ serviceUuid : this.uuid, -+ characteristicUuid : uuid, -+ characteristicValue: data, -+ descriptors: [], -+ properties: pro -+ } -+ -+ this.gattService.characteristics.push(characteristic); -+ return true; -+ } -+ -+ addDescriptor(cuuid, duuid, data) { -+ if (!this.gattService) -+ return false; -+ -+ for (let i = 0; i < this.gattService.characteristics.length; ++i) { -+ let characteristic : bluetoothManager.BLECharacteristic = this.gattService.characteristics[i]; -+ if (characteristic.characteristicUuid === cuuid) { -+ let descriptor : bluetoothManager.BLEDescriptor = { -+ serviceUuid : this.uuid, -+ characteristicUuid : cuuid, -+ descriptorUuid: duuid, -+ descriptorValue: data -+ } -+ characteristic.descriptors.push(descriptor); -+ return true; -+ } -+ } -+ return false; -+ } -+ -+} -\ No newline at end of file -diff --git a/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothLE.ts b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothLE.ts -new file mode 100644 -index 00000000..f81405e2 ---- /dev/null -+++ b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothLE.ts -@@ -0,0 +1,658 @@ -+import bluetoothManager from '@ohos.bluetoothManager'; -+import HashMap from '@ohos.util.HashMap'; -+import LinkedList from '@ohos.util.LinkedList'; -+ -+export class ConnectionState { -+ static STATE_CONNECTING = 1; -+ static STATE_CONNECTED = 2; -+ static STATE_DISCONNECTING = 3; -+ static STATE_DISCONNECTED = 4; -+} -+ -+enum GattEntryType -+{ -+ Service, Characteristic, CharacteristicValue, Descriptor -+}; -+ -+class GattEntry -+{ -+ type : GattEntryType; -+ valueKnown : boolean = false; -+ service: bluetoothManager.GattService = null; -+ characteristic : bluetoothManager.BLECharacteristic = null; -+ descriptor : bluetoothManager.BLEDescriptor = null; -+ endHandle : number = -1; -+ associatedServiceHandle : number; -+} -+ -+export class QtBluetoothLE { -+ -+ private address = null; -+ private qtObject; -+ private gattClient : bluetoothManager.GattClientDevice = null; -+ private state : ConnectionState = ConnectionState.STATE_DISCONNECTED; -+ private entries : GattEntry[] = []; -+ private uuidToEntry : HashMap>; -+ private servicesToBeDiscovered : LinkedList; -+ -+ constructor(address) { -+ this.address = address; -+ this.uuidToEntry = new HashMap(); -+ this.servicesToBeDiscovered = new LinkedList(); -+ } -+ -+ setQtObject(qtObject) { -+ this.qtObject = qtObject; -+ } -+ -+ private onBLEConnectionStateChange() { -+ if (!this.gattClient) { -+ return; -+ } -+ try { -+ this.gattClient.on('BLEConnectionStateChange', async (data: bluetoothManager.BLEConnectChangedState) => { -+ let deviceId: string = data.deviceId; -+ let state: bluetoothManager.ProfileConnectionState = data.state; -+ if (data) { -+ if (state === bluetoothManager.ProfileConnectionState.STATE_CONNECTED) { -+ try { -+ // Starts discovering services. -+ this.state = ConnectionState.STATE_CONNECTED; -+ globalThis.qtbluetooth.connectChanged(this.qtObject, 3, 0); -+ } catch (err) { -+ globalThis.qtbluetooth.connectChanged(this.qtObject, 0, 5); -+ console.error("onBLEConnectionStateChange: err =", err); -+ } -+ } else if (state === bluetoothManager.ProfileConnectionState.STATE_DISCONNECTED) { -+ this.resetData(); -+ if (this.state === ConnectionState.STATE_CONNECTED || this.state === ConnectionState.STATE_DISCONNECTING) { -+ console.log("onBLEConnectionStateChange: Disconnected from GATT server."); -+ globalThis.qtbluetooth.connectChanged(this.qtObject, 0, 0); -+ this.close(); -+ } -+ } -+ } -+ }) -+ } catch (err) { -+ globalThis.qtbluetooth.connectChanged(this.qtObject, 0, 1); -+ } -+ } -+ -+ connect() : boolean { -+ try { -+ this.gattClient = bluetoothManager.BLE.createGattClientDevice(this.address); -+ this.state = ConnectionState.STATE_CONNECTING; -+ this.onBLEConnectionStateChange(); -+ this.onBLECharacteristicChange(); -+ this.gattClient.connect(); -+ return true; -+ } catch (error) { -+ console.error("connect errCode:" + error.code + ",errMessage:" + error.message); -+ return false; -+ } -+ } -+ -+ private offBLEConnectionStateChange() { -+ if (!this.gattClient) { -+ return; -+ } -+ try { -+ this.gattClient.off('BLEConnectionStateChange'); -+ } catch (err) { -+ console.error("offBLEConnectionStateChange: err =", err); -+ } -+ } -+ -+ private onBLECharacteristicChange() { -+ if (!this.gattClient) { -+ return; -+ } -+ try { -+ this.gattClient.on('BLECharacteristicChange', async (data: bluetoothManager.BLECharacteristic) => { -+ let handle = -1; -+ for (let i = 0; i < this.entries.length; ++i) { -+ let e = this.entries[i]; -+ if (e.type != GattEntryType.Characteristic) -+ continue; -+ if (e.characteristic.characteristicUuid == data.characteristicUuid) { -+ handle = i; -+ break; -+ } -+ } -+ if (handle == -1) { -+ console.warn("onCharacteristicChanged: cannot find handle"); -+ return; -+ } -+ globalThis.qtbluetooth.characteristicChanged(this.qtObject, handle+1, data.characteristicValue); -+ }) -+ } catch (err) { -+ console.error("error in BLECharacteristicChange"); -+ } -+ } -+ -+ private offBLECharacteristicChange() { -+ if (!this.gattClient) { -+ return; -+ } -+ -+ this.gattClient.off('BLECharacteristicChange'); -+ } -+ -+ disconnectToServer() { -+ if (this.gattClient == null) -+ return; -+ try { -+ this.offBLEConnectionStateChange(); -+ this.offBLECharacteristicChange(); -+ if (this.state === ConnectionState.STATE_CONNECTING) { -+ this.close(); -+ } else if (this.state === ConnectionState.STATE_CONNECTED) { -+ this.disconnect(); -+ this.state = ConnectionState.STATE_DISCONNECTING; -+ this.close(); -+ } -+ } catch (err) { -+ console.error("disconnectToServer errCode:" + err.code + ",errMessage:" + err.message); -+ } -+ } -+ -+ private close() { -+ try { -+ if (!this.gattClient) { -+ return; -+ } -+ -+ this.gattClient.close(); -+ this.state = ConnectionState.STATE_DISCONNECTED; -+ } catch (err) { -+ console.error("close: err =", err); -+ } -+ } -+ -+ disconnect() { -+ this.disconnectToServer(); -+ } -+ -+ discoverServices() : boolean { -+ if (this.gattClient == null) -+ return false; -+ try { -+ this.gattClient.getServices().then(services => { -+ let uuids = []; -+ for (var i = 0; i < services.length; ++i) { -+ let service: bluetoothManager.GattService = services[i]; -+ uuids.push(service.serviceUuid); -+ } -+ globalThis.qtbluetooth.servicesDiscovered(this.qtObject, uuids.join(" "), 0); -+ }); -+ return true; -+ } -+ catch (err) { -+ console.error("errCode:" + err.code + ", errMessage:" + err.message); -+ return false; -+ } -+ } -+ -+ private handleForCharacteristic(characteristic: bluetoothManager.BLECharacteristic) : number -+ { -+ if (characteristic == null) -+ return -1; -+ -+ let handles : Array = this.uuidToEntry.get(characteristic.serviceUuid); -+ if (handles == null || handles.length == 0) -+ return -1; -+ -+ //TODO for now we assume we always want the first service in case of uuid collision -+ let serviceHandle = handles[0]; -+ -+ try { -+ let entry : GattEntry = null; -+ for (let i = serviceHandle+1; i < this.entries.length; i++) { -+ entry = this.entries[i]; -+ if (entry == null) -+ continue; -+ -+ switch (entry.type) { -+ case GattEntryType.Descriptor: -+ case GattEntryType.CharacteristicValue: -+ continue; -+ case GattEntryType.Service: -+ break; -+ case GattEntryType.Characteristic: -+ if (entry.characteristic == characteristic) -+ return i; -+ break; -+ } -+ } -+ } catch (error) { /*nothing*/ } -+ return -1; -+ } -+ -+ private handleForDescriptor(descriptor : bluetoothManager.BLEDescriptor) : number { -+ if (descriptor == null) -+ return -1; -+ -+ let handles : Array = this.uuidToEntry[descriptor.serviceUuid]; -+ if (handles == null || handles.length == 0) -+ return -1; -+ -+ //TODO for now we assume we always want the first service in case of uuid collision -+ let serviceHandle = handles[0]; -+ -+ try { -+ let entry : GattEntry; -+ for (let i = serviceHandle+1; i < this.entries.length; i++) { -+ entry = this.entries[i]; -+ if (entry == null) -+ continue; -+ -+ switch (entry.type) { -+ case GattEntryType.Characteristic: -+ case GattEntryType.CharacteristicValue: -+ continue; -+ case GattEntryType.Service: -+ break; -+ case GattEntryType.Descriptor: -+ if (entry.descriptor == descriptor) -+ return i; -+ break; -+ } -+ } -+ } catch (error) { } -+ return -1; -+ } -+ -+ private async populateHandles() { -+ let uuids = []; -+ let services : Array = await this.gattClient.getServices(); -+ for (var i = 0; i < services.length; ++i) { -+ let service: bluetoothManager.GattService = services[i]; -+ -+ let serviceEntry : GattEntry = new GattEntry; -+ serviceEntry.type = GattEntryType.Service; -+ serviceEntry.service = service; -+ this.entries.push(serviceEntry); -+ let serviceHandle : number = this.entries.length - 1; -+ -+ let old : Array = this.uuidToEntry.get(service.serviceUuid); -+ if (old == null) -+ old = new Array(); -+ old.push(this.entries.length - 1); -+ this.uuidToEntry.set(service.serviceUuid, old); -+ -+ let characteristics: Array = service.characteristics; -+ for (var j = 0; j < characteristics.length; ++j) { -+ let characteristic: bluetoothManager.BLECharacteristic = characteristics[j]; -+ let entry: GattEntry = new GattEntry; -+ entry.type = GattEntryType.Characteristic; -+ entry.characteristic = characteristic; -+ entry.associatedServiceHandle = serviceHandle; -+ //entry.endHandle = .. undefined -+ this.entries.push(entry); -+ -+ // this emulates GATT value attributes -+ entry = new GattEntry(); -+ entry.type = GattEntryType.CharacteristicValue; -+ entry.associatedServiceHandle = serviceHandle; -+ entry.endHandle = this.entries.length; // special case -> current index in entries list -+ this.entries.push(entry); -+ // add all descriptors -+ let descList : Array = characteristic.descriptors; -+ for (var k =0; k < descList.length; ++k) { -+ entry = new GattEntry(); -+ entry.type = GattEntryType.Descriptor; -+ entry.descriptor = descList[k]; -+ entry.associatedServiceHandle = serviceHandle; -+ //entry.endHandle = .. undefined -+ this.entries.push(entry); -+ } -+ this.gattClient.setNotifyCharacteristicChanged(characteristic, true); -+ } -+ serviceEntry.endHandle = this.entries.length - 1; -+ uuids.push(service.serviceUuid); -+ } -+ globalThis.qtbluetooth.servicesDiscovered(this.qtObject, uuids.join(" "), 0); -+ } -+ -+ async discoverServiceDetails(serviceUuid: string) { -+ if (this.gattClient == null) -+ return false; -+ -+ if (this.entries.length == 0) -+ await this.populateHandles(); -+ -+ let entry : GattEntry; -+ let serviceHandle = 0; -+ try { -+ let handles : Array = this.uuidToEntry.get(serviceUuid); -+ if (handles == null || handles.length == 0) { -+ console.warn("Unknown service uuid for current device: " + serviceUuid); -+ return false; -+ } -+ -+ serviceHandle = handles[0]; -+ entry = this.entries[serviceHandle]; -+ if (entry == null) { -+ console.warn("Service with UUID " + serviceUuid + " not found"); -+ return false; -+ } -+ } catch (error) { -+ //invalid UUID string passed -+ console.warn("Cannot parse given UUID"); -+ return false; -+ } -+ -+ if (entry.type != GattEntryType.Service) { -+ console.warn("Given UUID is not a service UUID: " + serviceUuid); -+ return false; -+ } -+ -+ // current service already discovered or under investigation -+ if (entry.valueKnown || this.servicesToBeDiscovered.has(serviceHandle)) { -+ console.warn("Service already known or to be discovered"); -+ return true; -+ } -+ -+ this.servicesToBeDiscovered.add(serviceHandle); -+ this.scheduleServiceDetailDiscovery(serviceHandle); -+ -+ return true; -+ } -+ -+ propertyToInt(property : bluetoothManager.GattProperties) : number { -+ // Qt Property Type -+ // Unknown = 0x00, -+ // Broadcasting = 0x01, -+ // Read = 0x02, -+ // WriteNoResponse = 0x04, -+ // Write = 0x08, -+ // Notify = 0x10, -+ // Indicate = 0x20, -+ // WriteSigned = 0x40, -+ // ExtendedProperty = 0x80 -+ let result : number = 0x00; -+ if (property.write) { -+ result = 0x08; -+ } -+ if (property.writeNoResponse) { -+ result = result | 0x04; -+ } -+ if (property.read) { -+ result = result | 0x02; -+ } -+ if (property.notify) { -+ result = result | 0x10; -+ } -+ if (property.indicate) { -+ result = result | 0x20; -+ } -+ return result; -+ } -+ -+ async scheduleServiceDetailDiscovery(handler : number) { -+ let serviceEntry : GattEntry = this.entries[handler]; -+ let endHandle : number = serviceEntry.endHandle; -+ -+ if (handler == endHandle) { -+ console.warn("scheduleServiceDetailDiscovery: service is empty; nothing to discover"); -+ this.finishCurrentServiceDiscovery(handler); -+ return; -+ } -+ -+ // serviceHandle + 1 -> ignore service handle itself -+ for (let i = handler + 1; i <= endHandle; i++) { -+ let entry: GattEntry = this.entries[i]; -+ -+ switch (entry.type) { -+ case GattEntryType.Characteristic: -+ case GattEntryType.Descriptor: -+ // we schedule CharacteristicValue for initial discovery to simplify -+ // detection of the end of service discovery process -+ // performNextIO() ignores CharacteristicValue GATT entries -+ case GattEntryType.CharacteristicValue: -+ break; -+ case GattEntryType.Service: -+ // should not really happen unless endHandle is wrong -+ console.warn("scheduleServiceDetailDiscovery: wrong endHandle"); -+ return; -+ } -+ let handle : number = -1; -+ -+ switch (entry.type) { -+ case GattEntryType.Characteristic: -+ handle = this.handleForCharacteristic(entry.characteristic); -+ break; -+ case GattEntryType.Descriptor: -+ handle = this.handleForDescriptor(entry.descriptor); -+ break; -+ case GattEntryType.CharacteristicValue: -+ handle = entry.endHandle; -+ default: -+ break; -+ } -+ let skip = await this.readGattEntry(entry); -+ if (skip) { -+ if (handle > -1) { -+ let isServiceDiscovery : boolean = !entry.valueKnown; -+ -+ if (isServiceDiscovery) { -+ entry.valueKnown = true; -+ switch (entry.type) { -+ case GattEntryType.Characteristic: -+ console.debug("Non-readable characteristic " + entry.characteristic.characteristicUuid + -+ " for service " + entry.characteristic.serviceUuid); -+ globalThis.qtbluetooth.characteristicRead(this.qtObject, entry.characteristic.serviceUuid, -+ handle + 1, entry.characteristic.characteristicUuid, -+ this.propertyToInt(entry.characteristic.properties), entry.characteristic.characteristicValue); -+ break; -+ case GattEntryType.Descriptor: -+ // atm all descriptor types are readable -+ console.debug("Non-readable descriptor " + entry.descriptor.descriptorUuid + -+ " for service/char" + entry.descriptor.serviceUuid + -+ "/" + entry.descriptor.characteristicUuid); -+ globalThis.qtbluetooth.descriptorRead(this.qtObject, -+ entry.descriptor.serviceUuid, -+ entry.descriptor.characteristicUuid, -+ handle + 1, entry.descriptor.descriptorUuid, -+ entry.descriptor.descriptorValue); -+ break; -+ case GattEntryType.CharacteristicValue: -+ // for more details see scheduleServiceDetailDiscovery(int) -+ break; -+ default : -+ console.log("Scheduling of Service Gatt entry for service discovery should never happen."); -+ break; -+ } -+ -+ let serviceEntry: GattEntry = this.entries[entry.associatedServiceHandle]; -+ if (serviceEntry.endHandle == handle) -+ this.finishCurrentServiceDiscovery(entry.associatedServiceHandle); -+ } else { -+ let errorCode = 0; -+ errorCode = (entry.type == GattEntryType.Characteristic) ? 5 : 6; -+ globalThis.qtbluetooth.serviceError(this.qtObject, handle + 1, errorCode); -+ } -+ } -+ } -+ } -+ } -+ -+ private async readGattEntry(entry: GattEntry) : Promise -+ { -+ let result: boolean; -+ switch (entry.type) { -+ case GattEntryType.Characteristic: -+ try { -+ await this.gattClient.readCharacteristicValue(entry.characteristic); -+ result = true; -+ } catch (error) { -+ result = false; -+ } -+ if (!result) -+ return true; // skip -+ break; -+ case GattEntryType.Descriptor: -+ try { -+ await this.gattClient.readDescriptorValue(entry.descriptor); -+ result = true; -+ } catch (error) { -+ result = false; -+ } -+ if (!result) -+ return true; // skip -+ break; -+ case GattEntryType.Service: -+ return true; -+ case GattEntryType.CharacteristicValue: -+ return true; //skip -+ } -+ return false; -+ } -+ -+ finishCurrentServiceDiscovery(handler : number) { -+ console.warn("Finished current discovery for service handle " + handler); -+ let discoveredService : GattEntry = this.entries[handler]; -+ discoveredService.valueKnown = true; -+ this.servicesToBeDiscovered.removeFirst(); -+ globalThis.qtbluetooth.serviceDetailDiscoveryFinished(this.qtObject, discoveredService.service.serviceUuid, handler + 1, discoveredService.endHandle + 1); -+ } -+ -+ -+ writeCharacteristic(charHandle, value, mode) : boolean { -+ if (this.gattClient == null) -+ return false; -+ let entry : GattEntry = null; -+ try { -+ entry = this.entries[charHandle-1]; //Qt always uses handles + 1 -+ } catch (err) { -+ console.error("writeCharacteristic error", err); -+ return false; -+ } -+ if (entry == null) -+ return false; -+ entry.characteristic.characteristicValue = value; -+ try { -+ this.gattClient.writeCharacteristicValue(entry.characteristic); -+ return true; -+ } catch (err) { -+ console.error("writeCharacteristic error", err); -+ globalThis.qtbluetooth.serviceError(this.qtObject, charHandle, 2); -+ return false; -+ } -+ } -+ -+ writeDescriptor(charHandle, value) : boolean { -+ if (this.gattClient == null) -+ return false; -+ let entry : GattEntry = null; -+ try { -+ entry = this.entries[charHandle-1]; //Qt always uses handles + 1 -+ } catch (err) { -+ console.error("writeCharacteristic error", err); -+ return false; -+ } -+ if (entry == null) -+ return false; -+ entry.descriptor.descriptorValue = value; -+ try { -+ this.gattClient.writeDescriptorValue(entry.descriptor); -+ return true; -+ } catch (err) { -+ console.error("writeCharacteristic error", err); -+ globalThis.qtbluetooth.serviceError(this.qtObject, charHandle, 3); -+ return false; -+ } -+ } -+ -+ readCharacteristic(charHandle) { -+ if (this.gattClient == null) -+ return false; -+ let entry : GattEntry = null; -+ try { -+ entry = this.entries[charHandle-1]; //Qt always uses handles + 1 -+ } catch (err) { -+ console.error("readDescriptor error", err); -+ return false; -+ } -+ if (entry == null) -+ return false; -+ try { -+ this.gattClient.readCharacteristicValue(entry.characteristic, (code, BLECharacteristic) => { -+ if (code.code != 0) { -+ return; -+ } -+ console.log('bluetooth descriptor uuid: ' + BLECharacteristic.characteristicUuid); -+ let value = BLECharacteristic.characteristicValue; -+ globalThis.qtbluetooth.characteristicRead(this.qtObject, entry.characteristic.serviceUuid, charHandle + 1, -+ entry.characteristic.characteristicUuid, this.propertyToInt(entry.characteristic.properties), value); -+ }); -+ return true; -+ } catch (err) { -+ console.error("readDescriptor error", err); -+ return false; -+ } -+ } -+ -+ readDescriptor(descriptorHandle) { -+ if (this.gattClient == null) -+ return false; -+ let entry : GattEntry = null; -+ try { -+ entry = this.entries[descriptorHandle-1]; //Qt always uses handles + 1 -+ } catch (err) { -+ console.error("readDescriptor error", err); -+ return false; -+ } -+ if (entry == null) -+ return false; -+ try { -+ this.gattClient.readDescriptorValue(entry.descriptor, (code, BLEDescriptor) => { -+ if (code.code != 0) { -+ return; -+ } -+ console.log('bluetooth descriptor uuid: ' + BLEDescriptor.descriptorUuid); -+ let value = BLEDescriptor.descriptorValue; -+ globalThis.qtbluetooth.descriptorRead(this.qtObject, entry.characteristic.serviceUuid, entry.characteristic.characteristicUuid, descriptorHandle + 1, BLEDescriptor.descriptorUuid, value); -+ }); -+ return true; -+ } catch (err) { -+ console.error("readDescriptor error", err); -+ return false; -+ } -+ } -+ -+ async includedServices(serviceUuid : string) : Promise -+ { -+ if (this.gattClient == null) -+ return ""; -+ let services = await this.gattClient.getServices(); -+ let target : bluetoothManager.GattService = null; -+ for (let i = 0; i < services.length; ++i) { -+ let service: bluetoothManager.GattService = services[i]; -+ if (service == null) -+ continue; -+ if (service.serviceUuid == serviceUuid) { -+ target = service -+ break; -+ } -+ } -+ if (target == null) -+ return "" -+ -+ let includes : Array = target.includeServices; -+ if (includes.length == 0) -+ return ""; -+ -+ let uuids: Array = []; -+ for (let i = 0; i < includes.length; ++i) { -+ uuids.push(includes[i].serviceUuid) -+ } -+ return uuids.join(" "); //space is separator -+ } -+ -+ resetData() { -+ this.uuidToEntry.clear(); -+ this.entries = []; -+ this.servicesToBeDiscovered.clear(); -+ } -+} -\ No newline at end of file -diff --git a/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothManager.ts b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothManager.ts -new file mode 100644 -index 00000000..66006f68 ---- /dev/null -+++ b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothManager.ts -@@ -0,0 +1,236 @@ -+import bluetoothManager from '@ohos.bluetoothManager'; -+import List from '@ohos.util.List'; -+import { QtBluetooth } from './QtBluetooth'; -+import { QtBluetoothDevice } from './QtBluetoothDevice' -+import { QtBluetoothServer } from './QtBluetoothServer' -+import { QtBluetoothLE } from './QtBluetoothLE' -+import { QtBluetoothGattServer } from './QtBluetoothGattServer' -+import { QtBluetoothServiceAdapter } from './QtBluetoothServiceAdapter' -+import qtbluetooth from 'libQt5Bluetooth.so'; -+ -+function onReceiveEvent(data) { -+ for (var i = 0; i < data.length; ++i) { -+ let target = new QtBluetooth(data[i], false); -+ let address = data[i]; -+ globalThis.qtBluetoothManager.add(address); -+ Reflect.defineProperty(globalThis, address, { value: target }); -+ qtbluetooth.discoveryResult(address); -+ } -+} -+ -+function onBLEReceiveEvent(data) { -+ for (var i = 0; i < data.length; ++i) { -+ let target = new QtBluetooth(data[i], true); -+ let address = data[i]; -+ globalThis.qtBluetoothManager.add(address); -+ Reflect.defineProperty(globalThis, address, { value: target }); -+ qtbluetooth.discoveryResult(address); -+ } -+} -+ -+export class QtBluetoothManager { -+ private targets = null; -+ -+ constructor() { -+ this.targets = new List(); -+ globalThis.qtBluetoothManager = this; -+ globalThis.qtbluetooth = qtbluetooth; -+ } -+ -+ isSupport() { -+ return canIUse("SystemCapability.Communication.Bluetooth.Core"); -+ } -+ -+ enable() { -+ try { -+ if (bluetoothManager.getState() === bluetoothManager.BluetoothState.STATE_ON -+ || bluetoothManager.getState() === bluetoothManager.BluetoothState.STATE_TURNING_ON) -+ return true; -+ bluetoothManager.enableBluetooth(); -+ return true; -+ } catch (err) { -+ console.error("enableBluetooth errCode:" + err.code + ",errMessage:" + err.message); -+ return false; -+ } -+ } -+ -+ disable() { -+ try { -+ bluetoothManager.disableBluetooth(); -+ return true; -+ } catch (err) { -+ console.error("disableBluetooth errCode:" + err.code + ",errMessage:" + err.message); -+ return false; -+ } -+ } -+ -+ scanMode() { -+ try { -+ let mode = bluetoothManager.getBluetoothScanMode(); -+ return mode; -+ } catch (err) { -+ console.error("getBluetoothScanMode errCode:" + err.code + ",errMessage:" + err.message); -+ return -1; -+ } -+ } -+ -+ setScanMode(mode) { -+ try { -+ bluetoothManager.setBluetoothScanMode(mode, 0); -+ } catch (err) { -+ console.error("setBluetoothScanMode errCode:" + err.code + ",errMessage:" + err.message); -+ } -+ } -+ -+ getLocalName() { -+ return bluetoothManager.getLocalName(); -+ } -+ -+ getAddress() { -+ return bluetoothManager.getLocalName(); -+ } -+ -+ pair(address, isPairing) { -+ try { -+ if (isPairing) -+ bluetoothManager.pairDevice(address); -+ // else -+ // bluetoothManager.cancelPairedDevice(address); //没有这个接口 -+ } catch (err) { -+ console.error("pair errCode:" + err.code + ",errMessage:" + err.message); -+ } -+ } -+ -+ pairedDevices() { -+ try { -+ let devices = bluetoothManager.getPairedDevices(); -+ return devices; -+ } catch (err) { -+ console.error("errCode:" + err.code + ",errMessage:" + err.message); -+ return []; -+ } -+ } -+ -+ getBondState(address) { -+ if (this.pairedDevices().indexOf(address) == -1) -+ return bluetoothManager.BondState.BOND_STATE_INVALID; -+ return bluetoothManager.BondState.BOND_STATE_BONDED; -+ } -+ -+ getState() { -+ return bluetoothManager.getState(); -+ } -+ -+ add(deviceId) { -+ if (this.targets.has(deviceId)) -+ return; -+ this.targets.add(deviceId); -+ } -+ -+ startBluetoothDiscovery() { -+ try { -+ bluetoothManager.on('bluetoothDeviceFind', onReceiveEvent); -+ bluetoothManager.startBluetoothDiscovery(); -+ return true; -+ } catch (err) { -+ console.error("startBluetoothDiscovery errCode:" + err.code + ",errMessage:" + err.message); -+ return false; -+ } -+ } -+ -+ stopBluetoothDiscovery() { -+ try { -+ bluetoothManager.off('bluetoothDeviceFind'); -+ bluetoothManager.stopBluetoothDiscovery(); -+ return true; -+ } catch (err) { -+ console.error("stopBluetoothDiscovery errCode:" + err.code + ",errMessage:" + err.message); -+ return false; -+ } -+ } -+ -+ startBLEScan() { -+ try { -+ bluetoothManager.BLE.on("BLEDeviceFind", onBLEReceiveEvent); -+ bluetoothManager.BLE.startBLEScan( -+ null, -+ { -+ interval: 500, -+ dutyMode: bluetoothManager.ScanDuty.SCAN_MODE_LOW_POWER, -+ matchMode: bluetoothManager.MatchMode.MATCH_MODE_AGGRESSIVE, -+ } -+ ); -+ return true; -+ } catch (err) { -+ console.error(" startBLEScan errCode:" + err.code + ",errMessage:" + err.message); -+ return false; -+ } -+ } -+ -+ stopBLEScan() { -+ try { -+ bluetoothManager.BLE.off("BLEDeviceFind"); -+ bluetoothManager.BLE.stopBLEScan(); -+ return true; -+ } catch (err) { -+ console.error("stopBLEScan errCode:" + err.code + ",errMessage:" + err.message); -+ return false; -+ } -+ } -+ -+ createBluetoothDevice(name, address) { -+ if (!this.targets.has(address)) -+ return false; -+ let device = new QtBluetoothDevice(address); -+ Reflect.defineProperty(globalThis, name, { value: device }); -+ return true; -+ } -+ -+ destroyBluetoothDevice(name) { -+ console.log("delete js bluetooth device object for: ", name) -+ Reflect.deleteProperty(globalThis, name); -+ return true; -+ } -+ -+ createServer(name, p) { -+ let server = new QtBluetoothServer(name, p); -+ Reflect.defineProperty(globalThis, name, { value: server }); -+ return true; -+ } -+ -+ createBluetoothLE(name, address) { -+ let target = new QtBluetoothLE(address); -+ Reflect.defineProperty(globalThis, name, { value: target }); -+ return true; -+ } -+ -+ destroyBluetoothLE(name) { -+ console.log("delete js bluetooth le object for: ", name) -+ Reflect.deleteProperty(globalThis, name); -+ return true; -+ } -+ -+ createBluetoothGattServer(name) { -+ let target = new QtBluetoothGattServer; -+ Reflect.defineProperty(globalThis, name, { value: target }); -+ return true; -+ } -+ -+ destroyBluetoothGattServer(name) { -+ console.log("delete js bluetooth le object for: ", name) -+ Reflect.deleteProperty(globalThis, name); -+ return true; -+ } -+ -+ createServiceAdapter(name, pointer) { -+ let adapter = new QtBluetoothServiceAdapter(pointer); -+ Reflect.defineProperty(globalThis, name, { value: adapter }); -+ return true; -+ } -+ -+ destroyServiceAdapter(name) { -+ console.log("delete js blue device object for: ", name) -+ Reflect.deleteProperty(globalThis, name); -+ return true; -+ } -+} -\ No newline at end of file -diff --git a/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothServer.ts b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothServer.ts -new file mode 100644 -index 00000000..ca70df73 ---- /dev/null -+++ b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothServer.ts -@@ -0,0 +1,84 @@ -+import qtbluetooth from 'libQt5Bluetooth.so'; -+import bluetoothManager from '@ohos.bluetoothManager'; -+import { QtBluetoothSocket } from '../native/QtBluetoothSocket' -+ -+export class QtBluetoothServer { -+ private alive = false; -+ private pointerId = 0; -+ private serverNumber = -1; -+ private clientNumber = -1; -+ private name: string = ''; -+ -+ public constructor(name: string, p) { -+ this.name = name; -+ this.pointerId = p; -+ } -+ -+ isAlive() { -+ return this.alive; -+ } -+ -+ createServer(name: string, p) { -+ let server = new QtBluetoothServer(name, p); -+ Reflect.defineProperty(globalThis, name, { value: server }); -+ return true; -+ } -+ -+ destroyServer(name: string) { -+ Reflect.deleteProperty(globalThis, name); -+ this.name = ''; -+ this.pointerId = 0; -+ this.alive = false; -+ return true; -+ } -+ -+ closeClient(socket: number) { -+ try { -+ bluetoothManager.sppCloseClientSocket(socket); -+ } catch (err) { -+ console.error("errCode:" + err.code + ",errMessage:" + err.message); -+ } -+ } -+ -+ close() { -+ try { -+ bluetoothManager.sppCloseServerSocket(this.serverNumber); -+ this.alive = false; -+ } catch (err) { -+ qtbluetooth.occurError(this.pointerId, err); -+ console.error("errCode:" + err.code + ",errMessage:" + err.message); -+ } -+ } -+ -+ listen(srvName, uid, isSecure) { -+ try { -+ let sppOption = { uuid: uid, secure: isSecure, type: bluetoothManager.SppType.SPP_RFCOMM }; -+ -+ bluetoothManager.sppListen(srvName, sppOption, (code, number) => { -+ console.log('bluetooth error code: ' + code.code); -+ if (code.code == 0) { -+ this.serverNumber = number; -+ this.alive = true; -+ console.log('bluetooth serverSocket Number: ' + number); -+ try { -+ bluetoothManager.sppAccept(this.serverNumber, (code, number) => { -+ console.log('bluetooth clientSocket Number: ' + number); -+ // 获取的clientNumber用作服务端后续读/写操作socket的id。 -+ this.clientNumber = number; -+ let server = new QtBluetoothSocket(this.clientNumber); -+ Reflect.defineProperty(globalThis, this.clientNumber, { value: server }); -+ qtbluetooth.acceptClientSocket(this.pointerId, this.clientNumber); -+ }); -+ } catch (err) { -+ qtbluetooth.occurError(this.pointerId, err); -+ console.error("errCode:" + err.code + ",errMessage:" + err.message); -+ } -+ } -+ }); -+ } catch (err) { -+ this.alive = false; -+ qtbluetooth.occurError(this.pointerId, err); -+ console.error("errCode:" + err.code + ",errMessage:" + err.message); -+ } -+ } -+} -\ No newline at end of file -diff --git a/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothServiceAdapter.ts b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothServiceAdapter.ts -new file mode 100644 -index 00000000..e4667274 ---- /dev/null -+++ b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothServiceAdapter.ts -@@ -0,0 +1,39 @@ -+import bluetoothManager from '@ohos.bluetoothManager'; -+import qtbluetooth from 'libQt5Bluetooth.so'; -+ -+export class QtBluetoothServiceAdapter { -+ private pointerId = 0; -+ private device = null; -+ -+ constructor(p) { -+ this.pointerId = p; -+ } -+ -+ createAdapter(adapter) { -+ try { -+ this.device = bluetoothManager.BLE.createGattClientDevice(adapter); -+ } catch (err) { -+ console.error("errCode:" + err.code + ",errMessage:" + err.message); -+ return false; -+ } -+ return true; -+ } -+ -+ fetchService() { -+ console.warn("<----------------fetchService---1"); -+ this.device.connect(); -+ console.warn("<----------------fetchService---3"); -+ this.device.getServices((code, gattServices) => { -+ if (code.code == 0) { -+ let services = gattServices; -+ console.log('bluetooth code is ' + code.code); -+ console.log("bluetooth services size is ", services.length); -+ -+ for (let i = 0; i < services.length; i++) { -+ console.log('bluetooth services is ' + JSON.stringify(services[i])); -+ qtbluetooth.servicesReceiver(this.pointerId, JSON.stringify(services[i])); -+ } -+ } -+ }); -+ } -+} -diff --git a/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothSocket.ts b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothSocket.ts -new file mode 100644 -index 00000000..be9b65ab ---- /dev/null -+++ b/src/openharmony/bluetooth/native/QtBluetooth/QtBluetoothSocket.ts -@@ -0,0 +1,65 @@ -+import bluetoothManager from '@ohos.bluetoothManager'; -+ -+export class QtBluetoothSocket { -+ private clientSocket = null -+ -+ private id = 0; -+ -+ constructor(id) { -+ this.id = id; -+ } -+ -+ setId(id) { -+ this.id = id; -+ return true; -+ } -+ -+ setSocketNumber(number) { -+ this.clientSocket = number; -+ return true; -+ } -+ -+ connect(address, uuid, sec) { -+ try { -+ let sppOption = { uuid: uuid, secure: sec, type: 0 }; -+ bluetoothManager.sppConnect(address, sppOption, (code, client) => { -+ if (code.code != 0) { -+ return; -+ } -+ console.log('bluetooth serverSocket Number: ' + client); -+ // 获取的clientNumber用作客户端后续读/写操作socket的id。 -+ this.clientSocket = client; -+ }); -+ return true; -+ } catch (err) { -+ console.error("errCode:" + err.code + ",errMessage:" + err.message); -+ return false; -+ } -+ } -+ -+ start() { -+ try { -+ bluetoothManager.on('sppRead', this.clientSocket, (dataBuffer) => { -+ globalThis.qtbluetooth.socketDataAvailable(this.id, dataBuffer); -+ }); -+ } catch (err) { -+ console.error("sppRead errCode:" + err.code + ",errMessage:" + err.message); -+ return false; -+ } -+ } -+ -+ write(buffer) { -+ try { -+ bluetoothManager.sppWrite(this.clientSocket, buffer); -+ return true; -+ } catch (err) { -+ console.error("errCode:" + err.code + ",errMessage:" + err.message); -+ return false; -+ } -+ } -+ -+ close() { -+ bluetoothManager.off('sppRead', this.clientSocket); -+ bluetoothManager.sppCloseClientSocket(this.clientSocket); -+ } -+} -\ No newline at end of file -diff --git a/src/openharmony/nfc/native/QtNfc/JsNfc.ts b/src/openharmony/nfc/native/QtNfc/JsNfc.ts -new file mode 100644 -index 00000000..baf23923 ---- /dev/null -+++ b/src/openharmony/nfc/native/QtNfc/JsNfc.ts -@@ -0,0 +1,60 @@ -+import controller from '@ohos.nfc.controller'; -+import tag from '@ohos.nfc.tag'; -+import HashMap from '@ohos.util.HashMap'; -+import { JsNfcTag } from './JsNfcTag'; -+import { BusinessError } from '@ohos.base'; -+import JsDataStore from '../QtCore/JsDataStore' -+import JsLogger from '../QtCore/JsLogger' -+ -+export class JsNfc { -+ private tagsIndex: number = 0; -+ -+ constructor() { -+ -+ } -+ -+ createTag(tagInfo: tag.TagInfo) { -+ if (tagInfo == null || tagInfo == undefined) { -+ JsLogger.info("no TagInfo to be created, ignore it."); -+ return; -+ } -+ this.tagsIndex++; -+ let name = "tagInfo" + this.tagsIndex; -+ JsDataStore.getJsObjectLoader().createObject("JsNfcTag", name, tagInfo); -+ JsDataStore.getQtNativeModule("QtNfc").nfcTargetDetected(name) -+ } -+ -+ start() { -+ controller.on("nfcStateChange", (data) => { -+ JsLogger.info("controller on callback nfcState: %{public}s" + JSON.stringify(data)); -+ JsDataStore.getQtNativeModule("QtNfc").nfcStateChanged(data); -+ }); -+ -+ try { -+ let discTech = [tag.NFC_A, tag.NFC_B]; -+ tag.registerForegroundDispatch(JsDataStore.getElementName(), discTech, (err : BusinessError, tagInfo : tag.TagInfo) =>{ -+ JsLogger.info("foreground callback: tag found tagInfo = %{public}s", JSON.stringify(tagInfo)); -+ this.createTag(tagInfo); -+ }); -+ } catch (e) { -+ JsLogger.error("registerForegroundDispatch error: " + e.message); -+ } -+ } -+ -+ stop() { -+ try { -+ tag.unregisterForegroundDispatch(JsDataStore.getElementName()); -+ } catch (e) { -+ JsLogger.info("registerForegroundDispatch error: " + e.message); -+ } -+ controller.off("nfcStateChange"); -+ } -+ -+ isAvailable() : boolean { -+ return this.isSupported() && controller.isNfcOpen(); -+ } -+ -+ isSupported() : boolean { -+ return canIUse("SystemCapability.Communication.NFC.Core") -+ } -+} -\ No newline at end of file -diff --git a/src/openharmony/nfc/native/QtNfc/JsNfcModule.ts b/src/openharmony/nfc/native/QtNfc/JsNfcModule.ts -new file mode 100644 -index 00000000..f2f58169 ---- /dev/null -+++ b/src/openharmony/nfc/native/QtNfc/JsNfcModule.ts -@@ -0,0 +1,37 @@ -+import { JsQtModule, ObjectBuilder } from '../QtCore/JsQtModule'; -+import JsDataStore from '../QtCore/JsDataStore'; -+import JsLogger from '../QtCore/JsLogger'; -+import { JsNfc } from './JsNfc'; -+import { JsNfcTag } from './JsNfcTag'; -+import tag from '@ohos.nfc.tag'; -+ -+class JsNfcModule extends JsQtModule { -+ -+ public constructor() { -+ super() -+ this.moduleJsObjects.set("JsNfc", new ObjectBuilder<[]>(() =>{ -+ return new JsNfc(); -+ })); -+ this.moduleJsObjects.set("JsNfc", new ObjectBuilder<[tag.TagInfo]>((tagInfo: tag.TagInfo) =>{ -+ return new JsNfcTag(tagInfo); -+ })); -+ this.loadQtModule(); -+ } -+ -+ async loadQtModule(): Promise { -+ let qtMajorVersion = JsDataStore.getQtMajorVersion(); -+ let QtNfcModule: any = null; -+ if (qtMajorVersion == 5) -+ QtNfcModule = await import ("libQt5Nfc.so"); -+ else if (qtMajorVersion == 6) -+ QtNfcModule = await import ("libQt6Nfc.so"); -+ if (QtNfcModule == null) { -+ JsLogger.fatal("Cannot load QtNfc module"); -+ return; -+ } -+ let QtNfc = QtNfcModule.default; -+ JsDataStore.addQtNativeModule("QtNfc", QtNfc); -+ } -+} -+ -+export default new JsNfcModule; -\ No newline at end of file -diff --git a/src/openharmony/nfc/native/QtNfc/JsNfcTag.ts b/src/openharmony/nfc/native/QtNfc/JsNfcTag.ts -new file mode 100644 -index 00000000..ed1834b7 ---- /dev/null -+++ b/src/openharmony/nfc/native/QtNfc/JsNfcTag.ts -@@ -0,0 +1,171 @@ -+import tag from '@ohos.nfc.tag'; -+import JsLogger from '../QtCore/JsLogger' -+ -+export class JsNfcTag { -+ private tagInfo: tag.TagInfo = null; -+ private target = null; -+ -+ constructor(tagInfo: tag.TagInfo) { -+ this.tagInfo = tagInfo; -+ let target = null; -+ for (let i = 0; i < this.tagInfo.technology.length; i++) { -+ if (this.tagInfo.technology[i] == tag.NFC_A) { -+ target = tag.getNfcA(this.tagInfo); -+ break; -+ } -+ if (this.tagInfo.technology[i] == tag.NFC_B) { -+ target = tag.getNfcB(this.tagInfo); -+ break; -+ } -+ if (this.tagInfo.technology[i] == tag.ISO_DEP) { -+ target = tag.getIsoDep(this.tagInfo); -+ break; -+ } -+ if (this.tagInfo.technology[i] == tag.NFC_F) { -+ target = tag.getNfcF(this.tagInfo); -+ break; -+ } -+ if (this.tagInfo.technology[i] == tag.NFC_V) { -+ target = tag.getNfcV(this.tagInfo); -+ break; -+ } -+ if (this.tagInfo.technology[i] == tag.NDEF) { -+ target = tag.getNdef(this.tagInfo); -+ break; -+ } -+ if (this.tagInfo.technology[i] == tag.NDEF_FORMATABLE) { -+ target = tag.getNdefFormatable(this.tagInfo); -+ break; -+ } -+ if (this.tagInfo.technology[i] == tag.MIFARE_CLASSIC) { -+ target = tag.getMifareClassic(this.tagInfo); -+ break; -+ } -+ if (this.tagInfo.technology[i] == tag.MIFARE_ULTRALIGHT) { -+ target = tag.getMifareUltralight(this.tagInfo); -+ break; -+ } -+ this.target = target; -+ } -+ } -+ -+ uid(): ArrayBuffer { -+ let arrayBuffer = new Uint8Array(this.tagInfo.uid).buffer; -+ return arrayBuffer; -+ } -+ -+ technologies(): string { -+ return this.tagInfo.technology.join(","); -+ } -+ -+ getNdefMessage(): string { -+ let isNdef = false; -+ for (let i = 0; i < this.tagInfo.technology.length; i++) { -+ if (this.tagInfo.technology[i] == tag.NDEF) { -+ isNdef = true; -+ break; -+ } -+ } -+ if (!isNdef) -+ return null; -+ try { -+ let ndefTag = tag.getNdef(this.tagInfo); -+ let message = ndefTag.getNdefMessage(); -+ return JSON.stringify(message); -+ } catch (error) { -+ JsLogger.info("tag.getNdefTag catched error: " + error); -+ return null; -+ } -+ } -+ -+ isInRange(): boolean { -+ return true; -+ } -+ -+ maxCommandLength(): number { -+ if (this.target != null) -+ return this.target.getMaxTransmitSize(); -+ return 0; -+ } -+ -+ connect(): boolean { -+ if (this.target != null) { -+ try { -+ this.target.connect(); -+ return true; -+ } catch (error) { -+ JsLogger.error("tag connect busiError: " + error); -+ return false; -+ } -+ } -+ return false; -+ } -+ -+ isConnected(): boolean { -+ if (this.target != null) -+ return this.target.isConnected(); -+ return false; -+ } -+ -+ async transceive(data): Promise { -+ if (this.target == null) -+ return null; -+ try { -+ let result = await this.target.sendData(data); -+ let arrayBuffer = new Uint8Array(result).buffer; -+ return arrayBuffer; -+ } catch(err) { -+ JsLogger.error("tagSession sendData Promise err: " + err); -+ return null; -+ } -+ } -+ -+ format(data) { -+ let message = tag.ndef.createNdefMessage(data); -+ this.target?.format(message); -+ } -+ -+ writeNdefMessage(data: number[]) { -+ let message: tag.NdefMessage = tag.ndef.createNdefMessage(data); -+ try { -+ this.target?.writeNdef(message); -+ } catch (error) { -+ JsLogger.info("ndef writeNdef Promise catch busiError Code: ${(busiError as Businsess).code}, " + -+ "message: ${(busiError as Businsess).message}"); -+ } -+ } -+ -+ getType(): string { -+ if (this.target == null) -+ return ""; -+ let type = this.target.getNdefTagType(); -+ try { -+ return this.target.getNdefTagTypeString(type); -+ } catch (err) { -+ JsLogger.error("ndef getNdefTagTypeString catch busiError Code: ${(busiError as Businsess).code}, " + -+ "message: ${(busiError as Businsess).message}"); -+ } -+ } -+ -+ getAtqa(): ArrayBuffer { -+ if (this.target == null) -+ return null; -+ let result = this.target.getAtqa(); -+ let arrayBuffer = new Uint8Array(result).buffer; -+ return arrayBuffer; -+ } -+ -+ getSak(): number { -+ if (this.target == null) -+ return 0; -+ return this.target.getSak(); -+ } -+ -+ close(): boolean { -+ if (this.target == null) -+ return true; -+ //Todo close -+ return true; -+ } -+ -+} -\ No newline at end of file -diff --git a/src/openharmony/nfc/nfc.pro b/src/openharmony/nfc/nfc.pro -new file mode 100644 -index 00000000..cbbfd2f8 ---- /dev/null -+++ b/src/openharmony/nfc/nfc.pro -@@ -0,0 +1,11 @@ -+TEMPLATE = aux -+ -+CONFIG -= qt -+ -+templates.files += $$files($$PWD/native/QtNfc/*, true) -+templates.path = $$[QT_INSTALL_PREFIX]/openharmony/qtconnectivity -+templates.base = $$PWD -+ -+INSTALLS += templates -+ -+OTHER_FILES += $$templates.files -diff --git a/src/openharmony/openharmony.pro b/src/openharmony/openharmony.pro -new file mode 100644 -index 00000000..f8f5c05e ---- /dev/null -+++ b/src/openharmony/openharmony.pro -@@ -0,0 +1,3 @@ -+TEMPLATE = subdirs -+qtHaveModule(bluetooth): SUBDIRS += bluetooth -+qtHaveModule(nfc): SUBDIRS += nfc -diff --git a/src/src.pro b/src/src.pro -index dba9de4b..53b11bb7 100644 ---- a/src/src.pro -+++ b/src/src.pro -@@ -6,6 +6,11 @@ android { - android.depends += bluetooth nfc - } - -+openharmony { -+ SUBDIRS += openharmony -+ openharmony.depends += bluetooth nfc -+} -+ - contains(QT_CONFIG, private_tests) { - bluetooth_doc_snippets.subdir = bluetooth/doc/snippets - bluetooth_doc_snippets.depends = bluetooth diff --git a/patch/v5.12.12/qtdeclarative.patch b/patch/v5.12.12/qtdeclarative.patch deleted file mode 100644 index 88fa7a6ea478b6b5f8a1e4ca7003090871876a9c..0000000000000000000000000000000000000000 --- a/patch/v5.12.12/qtdeclarative.patch +++ /dev/null @@ -1,227 +0,0 @@ -diff --git a/src/qml/qml/qqmlfile.cpp b/src/qml/qml/qqmlfile.cpp -index 99031e1e74..dcfc473da2 100644 ---- a/src/qml/qml/qqmlfile.cpp -+++ b/src/qml/qml/qqmlfile.cpp -@@ -65,6 +65,9 @@ static char file_string[] = "file"; - #if defined(Q_OS_ANDROID) - static char assets_string[] = "assets"; - #endif -+#if defined(Q_OS_OPENHARMONY) -+static char rawfile_string[] = "rawfile"; -+#endif - - class QQmlFilePrivate; - -@@ -453,6 +456,10 @@ bool QQmlFile::isSynchronous(const QUrl &url) - } else if (scheme.length() == 6 && 0 == scheme.compare(QLatin1String(assets_string), Qt::CaseInsensitive)) { - return true; - #endif -+#if defined(Q_OS_OPENHARMONY) -+ } else if (scheme.length() == 7 && 0 == scheme.compare(QLatin1String(rawfile_string), Qt::CaseInsensitive)) { -+ return true; -+#endif - - } else { - return false; -@@ -495,6 +502,14 @@ bool QQmlFile::isSynchronous(const QString &url) - - } - #endif -+#if defined(Q_OS_OPENHARMONY) -+ else if (f == QLatin1Char('r') || f == QLatin1Char('R')) { -+ return url.length() >= 9 /* rawfile:/ */ && -+ url.startsWith(QLatin1String(rawfile_string), Qt::CaseInsensitive) && -+ url[7] == QLatin1Char(':') && url[8] == QLatin1Char('/'); -+ -+ } -+#endif - - return false; - } -@@ -518,6 +533,10 @@ bool QQmlFile::isLocalFile(const QUrl &url) - } else if (scheme.length() == 6 && 0 == scheme.compare(QLatin1String(assets_string), Qt::CaseInsensitive)) { - return true; - #endif -+#if defined(Q_OS_OPENHARMONY) -+ } else if (scheme.length() == 7 && 0 == scheme.compare(QLatin1String(rawfile_string), Qt::CaseInsensitive)) { -+ return true; -+#endif - - } else { - return false; -@@ -559,6 +578,14 @@ bool QQmlFile::isLocalFile(const QString &url) - - } - #endif -+#if defined(Q_OS_OPENHARMONY) -+ else if (f == QLatin1Char('r') || f == QLatin1Char('R')) { -+ return url.length() >= 9 /* assets:/ */ && -+ url.startsWith(QLatin1String(rawfile_string), Qt::CaseInsensitive) && -+ url[7] == QLatin1Char(':') && url[8] == QLatin1Char('/'); -+ -+ } -+#endif - - return false; - } -@@ -582,6 +609,13 @@ QString QQmlFile::urlToLocalFileOrQrc(const QUrl& url) - return QString(); - } - #endif -+#if defined(Q_OS_OPENHARMONY) -+ else if (url.scheme().compare(QLatin1String("rawfile"), Qt::CaseInsensitive) == 0) { -+ if (url.authority().isEmpty()) -+ return url.toString(); -+ return QString(); -+ } -+#endif - - return url.toLocalFile(); - } -@@ -620,6 +654,11 @@ QString QQmlFile::urlToLocalFileOrQrc(const QString& url) - return url; - } - #endif -+#if defined(Q_OS_OPENHARMONY) -+ else if (url.startsWith(QLatin1String("rawfile:"), Qt::CaseInsensitive)) { -+ return url; -+ } -+#endif - - return toLocalFile(url); - } -diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp -index ba8dce4b6e..aece49ed34 100644 ---- a/src/qml/qml/qqmlimport.cpp -+++ b/src/qml/qml/qqmlimport.cpp -@@ -1823,6 +1823,19 @@ QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e) - - addImportPath(QStringLiteral("qrc:/qt-project.org/imports")); - addImportPath(QCoreApplication::applicationDirPath()); -+/* FIXME 因为鸿蒙权限限制 -+ * 将QML插件的加载和qml脚本的加载进行了区分 -+ */ -+#if defined(Q_OS_OPENHARMONY) -+ addImportPath(QStringLiteral("qrc:/harmony_rcc_bundle/qml")); -+ if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QT_HARMONY_BUNDLED_LIBS_PATH"))) { -+ const QString envImportPath = qEnvironmentVariable("QT_HARMONY_BUNDLED_LIBS_PATH"); -+ QLatin1Char pathSep(':'); -+ QStringList paths = envImportPath.split(pathSep, QString::SkipEmptyParts); -+ for (int ii = paths.count() - 1; ii >= 0; --ii) -+ addPluginPath(paths.at(ii)); -+ } -+#endif - } - - QQmlImportDatabase::~QQmlImportDatabase() -@@ -1862,8 +1875,17 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader, - else - resolvedPath = pluginPath; - } -- -- // hack for resources, should probably go away -+/* FIXME 因为鸿蒙安全限制,QML脚本和扩展插件加载路径有区别 -+ * 使用环境变量QT_HARMONY_BUNDLED_LIBS_PATH的值替换鸿蒙rawfile资源文件的路径 -+ * 用以qml扩展插件的加载 -+ */ -+#if defined(Q_OS_OPENHARMONY) -+ if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QT_HARMONY_CACHE_DIR"))) { -+ QString cacheQmlStr = qEnvironmentVariable("QT_HARMONY_CACHE_DIR"); -+ resolvedPath = QString(qmldirPath).replace(cacheQmlStr, pluginPath); -+ } -+#endif -+ // hack for resources, should probably go away - if (resolvedPath.startsWith(Colon)) - resolvedPath = QCoreApplication::applicationDirPath(); - -diff --git a/src/qml/qml/qqmlplatform.cpp b/src/qml/qml/qqmlplatform.cpp -index 0acf20bbb4..27f22c19a3 100644 ---- a/src/qml/qml/qqmlplatform.cpp -+++ b/src/qml/qml/qqmlplatform.cpp -@@ -60,6 +60,8 @@ QString QQmlPlatform::os() - { - #if defined(Q_OS_ANDROID) - return QStringLiteral("android"); -+#elif defined(Q_OS_OPENHARMONY) -+ return QStringLiteral("openharmony"); - #elif defined(Q_OS_IOS) - return QStringLiteral("ios"); - #elif defined(Q_OS_TVOS) -diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp -index 9e5bc0b021..e12c80a777 100644 ---- a/src/qml/qml/qqmltypeloader.cpp -+++ b/src/qml/qml/qqmltypeloader.cpp -@@ -1827,6 +1827,14 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path) - return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString(); - } - #endif -+#if defined(Q_OS_OPENHARMONY) -+ else if (path.count() > 8 && path.at(7) == QLatin1Char(':') && path.at(8) == QLatin1Char('/') && -+ path.startsWith(QLatin1String("rawfile"), Qt::CaseInsensitive)) { -+ // assets resource url -+ QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path)); -+ return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString(); -+ } -+#endif - - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - QString dirPath(path.left(lastSlash)); -@@ -1884,6 +1892,14 @@ bool QQmlTypeLoader::fileExists(const QString &path, const QString &file) - return fileInfo.isFile(); - } - #endif -+#if defined(Q_OS_OPENHARMONY) -+ else if (path.count() > 8 && path.at(7) == QLatin1Char(':') && path.at(8) == QLatin1Char('/') && -+ path.startsWith(QLatin1String("rawfile"), Qt::CaseInsensitive)) { -+ // assets resource url -+ QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path + file)); -+ return fileInfo.isFile(); -+ } -+#endif - - LockHolder holder(this); - if (!m_importDirCache.contains(path)) { -@@ -1921,6 +1937,9 @@ bool QQmlTypeLoader::directoryExists(const QString &path) - #if defined(Q_OS_ANDROID) - isResource = isResource || path.startsWith(QLatin1String("assets:/")); - #endif -+#if defined(Q_OS_OPENHARMONY) -+ isResource = isResource || path.startsWith(QLatin1String("rawfile:/")); -+#endif - - if (isResource) { - // qrc resource -diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp -index 3c97475e86..e6041d7aa5 100644 ---- a/src/quick/items/qquickwindow.cpp -+++ b/src/quick/items/qquickwindow.cpp -@@ -45,6 +45,12 @@ - #include "qquickitem_p.h" - #include "qquickevents_p_p.h" - -+#ifdef USE_MASK -+#include -+#include "qquicktext_p.h" -+#include -+#endif -+ - #include - #include - #include -@@ -627,6 +633,16 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control) - contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope; - contentItem->setSize(q->size()); - -+#ifdef USE_MASK -+ QQuickText *mask = new QQuickText(contentItem); -+ mask->setColor(QColor(Qt::red)); -+ mask->setText("managed by isoftstone"); -+ mask->setZ(std::numeric_limits::max()); -+ QQuickTextPrivate *maskPrivate = QQuickTextPrivate::get(mask); -+ maskPrivate->anchors()->setRight(contentItemPrivate->right()); -+ maskPrivate->anchors()->setBottom(contentItemPrivate->bottom()); -+#endif -+ - customRenderMode = qgetenv("QSG_VISUALIZE"); - renderControl = control; - if (renderControl) diff --git a/patch/v5.12.12/qtmultimedia.patch b/patch/v5.12.12/qtmultimedia.patch deleted file mode 100644 index f487b999cd89af97fb41c0b9ef3533c4d07bb648..0000000000000000000000000000000000000000 --- a/patch/v5.12.12/qtmultimedia.patch +++ /dev/null @@ -1,3172 +0,0 @@ -diff --git a/src/multimedia/audio/qsoundeffect_qaudio_p.cpp b/src/multimedia/audio/qsoundeffect_qaudio_p.cpp -index 13df416f3..f247bc79a 100644 ---- a/src/multimedia/audio/qsoundeffect_qaudio_p.cpp -+++ b/src/multimedia/audio/qsoundeffect_qaudio_p.cpp -@@ -333,13 +333,14 @@ void PrivateSoundSource::sampleReady() - if (!m_muted) - m_audioOutput->setVolume(m_volume); - else -- m_audioOutput->setVolume(0); -+ m_audioOutput->setVolume(0); - } - m_sampleReady = true; - soundeffect->setStatus(QSoundEffect::Ready); - -- if (m_playing && m_audioOutput->state() == QAudio::StoppedState) -+ if (m_playing && m_audioOutput->state() == QAudio::StoppedState) { - m_audioOutput->start(this); -+ } - } - - void PrivateSoundSource::decoderError() -@@ -362,9 +363,8 @@ void PrivateSoundSource::stateChanged(QAudio::State state) - } - - qint64 PrivateSoundSource::readData(char *data, qint64 len) --{ -+{ - if ((m_runningCount > 0 || m_runningCount == QSoundEffect::Infinite) && m_playing) { -- - if (m_sample->state() != QSample::Ready) - return 0; - -diff --git a/src/openharmony/native/QtMultiMedia/JsAudioManager.ts b/src/openharmony/native/QtMultiMedia/JsAudioManager.ts -new file mode 100644 -index 000000000..d484e3a7c ---- /dev/null -+++ b/src/openharmony/native/QtMultiMedia/JsAudioManager.ts -@@ -0,0 +1,91 @@ -+import audio from '@ohos.multimedia.audio'; -+ -+export class JsAudioManager { -+ private audioManager: audio.AudioManager = audio.getAudioManager(); -+ private routingManager: audio.AudioRoutingManager = this.audioManager.getRoutingManager(); -+ private deviceDescript = new Map([ -+ [0, "INVALID"], [1, "EARPIECE"], [2, "SPEAKER"], [3, "WIRED_HEADSET"], [4, "WIRED_HEADPHONES"], -+ [7, "BLUETOOTH_SCO"], [8, "BLUETOOTH_A2DP"], [15, "MIC"], [22, "USB_HEADSET"], [1000, "DEFAULT"] -+ ]); -+ -+ public constructor() { -+ } -+ -+ /* 获取可用的输入设备描述 */ -+ private async availableInputDevicesDes() { -+ let devicesDes = await this.routingManager.getDevices(audio.DeviceFlag.INPUT_DEVICES_FLAG); -+ return devicesDes; -+ } -+ -+ /* 获取可用的输出设备描述 */ -+ private async availableOutputDevicesDes() { -+ let devices = await this.routingManager.getDevices(audio.DeviceFlag.OUTPUT_DEVICES_FLAG); -+ return devices; -+ } -+ -+ /* 获取可用的输入设备id值 */ -+ async availableInputDevices() { -+ let ids:Array = new Array(); -+ let devDes = await this.availableInputDevicesDes(); -+ for (let des of devDes) { -+ if (audio.DeviceType.INVALID != des.deviceType) { -+ ids.push(String(des.id)); -+ } -+ } -+ return ids; -+ } -+ /* 获取可用的输出设备id值 */ -+ async availableOutputDevices() { -+ let ids:Array = new Array(); -+ let devDes = await this.availableOutputDevicesDes(); -+ for (let des of devDes) { -+ if (audio.DeviceType.INVALID != des.deviceType) { -+ ids.push(String(des.id)); -+ } -+ } -+ return ids; -+ } -+ -+ /* 获取指定输入设备支持的通道数 param id---设备id */ -+ async inputChannelCounts(id: string) { -+ let devices = await this.availableInputDevicesDes(); -+ for (let dev of devices) { -+ if (Number(dev.id) === dev.id) { -+ return dev.channelCounts; -+ } -+ } -+ return undefined; -+ } -+ -+ /* 获取指定输出设备支持的通道数 param id---设备id */ -+ async outputChannelCounts(id: string) { -+ let devices = await this.availableOutputDevicesDes(); -+ for (let dev of devices) { -+ if (Number(dev.id) === dev.id) { -+ return dev.channelCounts; -+ } -+ } -+ return undefined; -+ } -+ -+ /* 返回指定输入设备支持的采样率列表 param id---设备id */ -+ async inputSupportedSampleRates(id: string) { -+ let devices = await this.availableInputDevicesDes(); -+ for (let dev of devices) { -+ if (Number(dev.id) === dev.id) { -+ return dev.sampleRates; -+ } -+ } -+ return undefined; -+ } -+ /* 返回输出设备支持的采样率列表 param id---设备id */ -+ async outputSupportedSampleRates(id: string) { -+ let devices = await this.availableOutputDevicesDes(); -+ for (let dev of devices) { -+ if (Number(dev.id) === dev.id) { -+ return dev.sampleRates; -+ } -+ } -+ return undefined; -+ } -+} -diff --git a/src/openharmony/native/QtMultiMedia/JsCameraManager.ts b/src/openharmony/native/QtMultiMedia/JsCameraManager.ts -new file mode 100644 -index 000000000..252d2f64e ---- /dev/null -+++ b/src/openharmony/native/QtMultiMedia/JsCameraManager.ts -@@ -0,0 +1,56 @@ -+import camera from '@ohos.multimedia.camera'; -+import JsDataStore from '../QtCore/JsDataStore'; -+ -+export class JsCameraManager { -+ private cameraDevs = new Map(); -+ -+ constructor() { -+ } -+ -+ private interfaceToObject(data:camera.CameraDevice):any { -+ var cameraInfo = { -+ "cameraId" : data.cameraId, -+ "cameraType" : data.cameraType, -+ "cameraPosition" : data.cameraPosition, -+ "connectionType" : data.connectionType, -+ } -+ return cameraInfo; -+ } -+ -+ /* 获取相机设备列表 Array */ -+ private async cameraDevices() { -+ let cameraManager = await camera.getCameraManager(JsDataStore.getContext()); -+ let cameras = await cameraManager.getSupportedCameras(); -+ for (let dev of cameras) { -+ this.cameraDevs.set(dev.cameraId, dev); -+ } -+ return cameras; -+ } -+ -+ /* 获取指定id的相机设备信息 */ -+ cameraInfo(id: string): string { -+ if (this.cameraDevs.has(id)) { -+ let info = this.cameraDevs.get(id); -+ var cameraInfo = this.interfaceToObject(info); -+ return JSON.stringify(cameraInfo); -+ } -+ return null; -+ } -+ -+ /* 获取相机设备数量 */ -+ async idOfCameras() { -+ await this.cameraDevices(); -+ return [...this.cameraDevs.keys()]; -+ } -+ -+ /* 获取相机设备信息的以JSON字符串格式表示 */ -+ async jsonOfCameras() { -+ let cameras = await this.cameraDevices(); -+ let devices = new Array(); -+ for (let dev of cameras) { -+ var cameraInfo = this.interfaceToObject(dev); -+ devices.push(cameraInfo); -+ } -+ return JSON.stringify(devices); -+ } -+} -diff --git a/src/openharmony/native/QtMultiMedia/JsMediaRecorder.ts b/src/openharmony/native/QtMultiMedia/JsMediaRecorder.ts -new file mode 100644 -index 000000000..7a3fc0e63 ---- /dev/null -+++ b/src/openharmony/native/QtMultiMedia/JsMediaRecorder.ts -@@ -0,0 +1,90 @@ -+import media from '@ohos.multimedia.media'; -+import { to } from './JsMultimediaUtils'; -+ -+export class JsMediaRecorder { -+ private mRecorder: media.AVRecorder = null; -+ -+ constructor() { -+ } -+ -+ private async hasRecorder() { -+ if (null != this.mRecorder) { -+ return true; -+ } -+ -+ let [error, record] = await to(media.createAVRecorder()); -+ if (null != error) { -+ console.error(`createAVRecorder catchCallback, error:${error}`); -+ return false; -+ } -+ this.mRecorder = record; -+ return true; -+ } -+ -+ async release() { -+ if (this.hasRecorder()) { -+ let [error, placeholder] = await to(this.mRecorder.release()); -+ if (null != error) { -+ console.error('release AVRecorder failed and catch error is ' + error.message); -+ } -+ } -+ } -+ -+ /* NOTE 配置项数据,C++端调用时传入json字符串 */ -+ async prepare(configs:string) { -+ if (this.hasRecorder()) { -+ //TODO -+ var obj = JSON.parse(configs); -+ let AVRecorderProfile = { -+ audioBitrate : 48000, -+ audioChannels : 2, -+ audioCodec : media.CodecMimeType.AUDIO_AAC, -+ audioSampleRate : 48000, -+ fileFormat : media.ContainerFormatType.CFT_MPEG_4, -+ videoBitrate : 48000, -+ videoCodec : media.CodecMimeType.VIDEO_MPEG4, -+ videoFrameWidth : 640, -+ videoFrameHeight : 480, -+ videoFrameRate : 30 -+ } -+ let AVRecorderConfig = { -+ audioSourceType : media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, -+ videoSourceType : media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV, -+ profile : AVRecorderProfile, -+ url : 'fd://', // 文件需先由调用者创建,赋予读写权限,将文件fd传给此参数,eg.fd://45 -+ rotation : 0, // 合理值0、90、180、270,非合理值prepare接口将报错 -+ location : { latitude : 30, longitude : 130 } -+ } -+ -+ this.mRecorder.prepare(AVRecorderConfig); -+ } -+ } -+ -+ async reset() { -+ if (this.hasRecorder()) { -+ let [error, placeholder] = await to(this.mRecorder.reset()); -+ if (null != error) { -+ console.error('reset AVRecorder failed and catch error is ' + error.message); -+ } -+ } -+ } -+ -+ async start() { -+ if (this.hasRecorder()) { -+ let [error, placeholder] = await to(this.mRecorder.start()); -+ if (null != error) { -+ console.info('start AVRecorder failed and catch error is ' + error.message); -+ } -+ } -+ } -+ -+ async stop() { -+ if (this.hasRecorder()) { -+ let [error, placeholder] = await to(this.mRecorder.stop()); -+ if (null != error) { -+ console.info('stop AVRecorder failed and error is ' + error.message); -+ } -+ } -+ } -+} -+ -diff --git a/src/openharmony/native/QtMultiMedia/JsMultiMediaModule.ts b/src/openharmony/native/QtMultiMedia/JsMultiMediaModule.ts -new file mode 100644 -index 000000000..cd08a2070 ---- /dev/null -+++ b/src/openharmony/native/QtMultiMedia/JsMultiMediaModule.ts -@@ -0,0 +1,44 @@ -+import { JsQtModule, ObjectBuilder } from '../QtCore/JsQtModule'; -+import JsDataStore from '../QtCore/JsDataStore'; -+import JsLogger from '../QtCore/JsLogger'; -+import { JsAudioManager } from './JsAudioManager' -+import { JsMediaRecorder } from './JsMediaRecorder' -+import { JsMultimediaUtils } from './JsMultimediaUtils' -+import { JsCameraManager } from './JsCameraManager' -+ -+class JsMultiMediaModule extends JsQtModule { -+ -+ public constructor() { -+ super() -+ this.moduleJsObjects.set("JsAudioManager", new ObjectBuilder<[]>(() =>{ -+ return new JsAudioManager(); -+ })); -+ this.moduleJsObjects.set("JsMediaRecorder", new ObjectBuilder<[]>(() =>{ -+ return new JsMediaRecorder(); -+ })); -+ this.moduleJsObjects.set("JsMultimediaUtils", new ObjectBuilder<[]>(() =>{ -+ return new JsMultimediaUtils(); -+ })); -+ this.moduleJsObjects.set("JsCameraManager", new ObjectBuilder<[]>(() =>{ -+ return new JsCameraManager(); -+ })); -+ this.loadQtModule(); -+ } -+ -+ async loadQtModule(): Promise { -+ let qtMajorVersion = JsDataStore.getQtMajorVersion(); -+ let QtMultimediaModule: any = null; -+ if (qtMajorVersion == 5) -+ QtMultimediaModule = await import ("libQt5Multimedia.so"); -+ else if (qtMajorVersion == 6) -+ QtMultimediaModule = await import ("libQt5Multimedia.so"); -+ if (QtMultimediaModule == null) { -+ JsLogger.fatal("Cannot load QtMultimedia module"); -+ return; -+ } -+ let QtMultimedia = QtMultimediaModule.default; -+ JsDataStore.addQtNativeModule("QtMultimedia", QtMultimedia); -+ } -+} -+ -+export default new JsMultiMediaModule; -\ No newline at end of file -diff --git a/src/openharmony/native/QtMultiMedia/JsMultimediaUtils.ts b/src/openharmony/native/QtMultiMedia/JsMultimediaUtils.ts -new file mode 100644 -index 000000000..3adf5fe55 ---- /dev/null -+++ b/src/openharmony/native/QtMultiMedia/JsMultimediaUtils.ts -@@ -0,0 +1,20 @@ -+import media from '@ohos.multimedia.media'; -+import mediaLibrary from '@ohos.multimedia.mediaLibrary'; -+import JsDataStore from '../QtCore/JsDataStore'; -+ -+export class JsMultimediaUtils { -+ private media = mediaLibrary.getMediaLibrary(JsDataStore.getContext()); -+ -+ constructor(){} -+ -+ async getMediaDirectory(type:number){ -+ const dictResult = await this.media.getPublicDirectory(type); -+ return dictResult; -+ } -+} -+ -+export function to(promise) { -+ return promise.then(data => { -+ return [null, data]; -+ }).catch(err => [err]); -+} -diff --git a/src/openharmony/openharmony.pro b/src/openharmony/openharmony.pro -new file mode 100644 -index 000000000..6f97b7df9 ---- /dev/null -+++ b/src/openharmony/openharmony.pro -@@ -0,0 +1,9 @@ -+TEMPLATE = aux -+ -+CONFIG -= qt -+ -+templates.files += $$files($$PWD/native/QtMultiMedia/*, true) -+templates.path = $$[QT_INSTALL_PREFIX]/openharmony/qtmultimedia -+templates.base = $$PWD -+ -+INSTALLS += templates -diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp -index 2688f3550..f74f6a1d4 100644 ---- a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp -+++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp -@@ -38,7 +38,6 @@ - ****************************************************************************/ - - #include "qandroidmediaservice.h" -- - #include "qandroidmediaplayercontrol.h" - #include "qandroidmetadatareadercontrol.h" - #include "qandroidmediaplayervideorenderercontrol.h" -diff --git a/src/plugins/openharmony/openharmony.pro b/src/plugins/openharmony/openharmony.pro -new file mode 100644 -index 000000000..a3841d703 ---- /dev/null -+++ b/src/plugins/openharmony/openharmony.pro -@@ -0,0 +1,7 @@ -+TEMPLATE = subdirs -+ -+SUBDIRS += src -+ -+qtHaveModule(quick) { -+ SUBDIRS += videonode -+} -diff --git a/src/plugins/openharmony/src/mediacapture/mediacapture.pri b/src/plugins/openharmony/src/mediacapture/mediacapture.pri -new file mode 100644 -index 000000000..566b8733d ---- /dev/null -+++ b/src/plugins/openharmony/src/mediacapture/mediacapture.pri -@@ -0,0 +1,18 @@ -+INCLUDEPATH += $$PWD -+ -+HEADERS += \ -+ $$PWD/qopenharmonycameracontrol.h \ -+ $$PWD/qopenharmonycamerainfocontrol.h \ -+ $$PWD/qopenharmonycamerasession.h \ -+ $$PWD/qopenharmonycaptureservice.h \ -+ $$PWD/qopenharmonycapturesession.h \ -+ $$PWD/qopenharmonymediacontainercontrol.h -+ -+SOURCES += \ -+ $$PWD/qopenharmonycameracontrol.cpp \ -+ $$PWD/qopenharmonycamerainfocontrol.cpp \ -+ $$PWD/qopenharmonycamerasession.cpp \ -+ $$PWD/qopenharmonycaptureservice.cpp \ -+ $$PWD/qopenharmonycapturesession.cpp \ -+ $$PWD/qopenharmonymediacontainercontrol.cpp -+ -diff --git a/src/plugins/openharmony/src/mediacapture/qopenharmonycameracontrol.cpp b/src/plugins/openharmony/src/mediacapture/qopenharmonycameracontrol.cpp -new file mode 100644 -index 000000000..6bb8d7954 ---- /dev/null -+++ b/src/plugins/openharmony/src/mediacapture/qopenharmonycameracontrol.cpp -@@ -0,0 +1,72 @@ -+#include "qopenharmonycamerasession.h" -+#include "qopenharmonycameracontrol.h" -+ -+QT_BEGIN_NAMESPACE -+ -+QOPenHarmonyCameraControl::QOPenHarmonyCameraControl(QOPenHarmonyCameraSession *session) -+ : QCameraControl(Q_NULLPTR) -+ , m_cameraSession(session) -+{ -+ connect(m_cameraSession, SIGNAL(statusChanged(QCamera::Status)), -+ this, SIGNAL(statusChanged(QCamera::Status))); -+ -+ connect(m_cameraSession, SIGNAL(stateChanged(QCamera::State)), -+ this, SIGNAL(stateChanged(QCamera::State))); -+ -+ connect(m_cameraSession, SIGNAL(error(int,QString)), this, SIGNAL(error(int,QString))); -+ -+ connect(m_cameraSession, SIGNAL(captureModeChanged(QCamera::CaptureModes)), -+ this, SIGNAL(captureModeChanged(QCamera::CaptureModes))); -+} -+ -+QOPenHarmonyCameraControl::~QOPenHarmonyCameraControl() -+{ -+ -+} -+ -+QCamera::CaptureModes QOPenHarmonyCameraControl::captureMode() const -+{ -+ return m_cameraSession->captureMode(); -+} -+ -+void QOPenHarmonyCameraControl::setCaptureMode(QCamera::CaptureModes mode) -+{ -+ m_cameraSession->setCaptureMode(mode); -+} -+ -+bool QOPenHarmonyCameraControl::isCaptureModeSupported(QCamera::CaptureModes mode) const -+{ -+ return m_cameraSession->isCaptureModeSupported(mode); -+} -+ -+void QOPenHarmonyCameraControl::setState(QCamera::State state) -+{ -+ m_cameraSession->setState(state); -+} -+ -+QCamera::State QOPenHarmonyCameraControl::state() const -+{ -+ return m_cameraSession->state(); -+} -+ -+QCamera::Status QOPenHarmonyCameraControl::status() const -+{ -+ return m_cameraSession->status(); -+} -+ -+bool QOPenHarmonyCameraControl::canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const -+{ -+ Q_UNUSED(status); -+ -+ switch (changeType) { -+ case QCameraControl::CaptureMode: -+ case QCameraControl::ImageEncodingSettings: -+ case QCameraControl::VideoEncodingSettings: -+ case QCameraControl::Viewfinder: -+ return true; -+ default: -+ return false; -+ } -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/openharmony/src/mediacapture/qopenharmonycameracontrol.h b/src/plugins/openharmony/src/mediacapture/qopenharmonycameracontrol.h -new file mode 100644 -index 000000000..1ad328351 ---- /dev/null -+++ b/src/plugins/openharmony/src/mediacapture/qopenharmonycameracontrol.h -@@ -0,0 +1,35 @@ -+#ifndef QOPENHARMONYCAMERACONTROL_H -+#define QOPENHARMONYCAMERACONTROL_H -+ -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOPenHarmonyCameraSession; -+ -+class QOPenHarmonyCameraControl : public QCameraControl -+{ -+ Q_OBJECT -+ -+public: -+ explicit QOPenHarmonyCameraControl(QOPenHarmonyCameraSession *session); -+ virtual ~QOPenHarmonyCameraControl(); -+ -+ QCamera::State state() const; -+ void setState(QCamera::State state); -+ -+ QCamera::Status status() const; -+ -+ QCamera::CaptureModes captureMode() const; -+ void setCaptureMode(QCamera::CaptureModes mode); -+ bool isCaptureModeSupported(QCamera::CaptureModes mode) const; -+ -+ bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const; -+ -+private: -+ QOPenHarmonyCameraSession *m_cameraSession; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYCAMERACONTROL_H -diff --git a/src/plugins/openharmony/src/mediacapture/qopenharmonycamerainfocontrol.cpp b/src/plugins/openharmony/src/mediacapture/qopenharmonycamerainfocontrol.cpp -new file mode 100644 -index 000000000..68ee24365 ---- /dev/null -+++ b/src/plugins/openharmony/src/mediacapture/qopenharmonycamerainfocontrol.cpp -@@ -0,0 +1,40 @@ -+#include "qopenharmonycamerasession.h" -+#include "qopenharmonycamerainfocontrol.h" -+ -+QT_BEGIN_NAMESPACE -+ -+QCamera::Position QOPenHarmonyCameraInfoControl::cameraPosition(const QString &deviceName) const -+{ -+ return position(deviceName); -+} -+ -+int QOPenHarmonyCameraInfoControl::cameraOrientation(const QString &deviceName) const -+{ -+ return orientation(deviceName); -+} -+ -+QCamera::Position QOPenHarmonyCameraInfoControl::position(const QString &deviceName) -+{ -+ const QList &cameras = QOPenHarmonyCameraSession::availableCameras(); -+ for (int i = 0; i < cameras.count(); ++i) { -+ const OPenHarmonyCameraInfo &info = cameras.at(i); -+ if (QString::fromLatin1(info.name) == deviceName) -+ return info.position; -+ } -+ -+ return QCamera::UnspecifiedPosition; -+} -+ -+int QOPenHarmonyCameraInfoControl::orientation(const QString &deviceName) -+{ -+ const QList &cameras = QOPenHarmonyCameraSession::availableCameras(); -+ for (int i = 0; i < cameras.count(); ++i) { -+ const OPenHarmonyCameraInfo &info = cameras.at(i); -+ if (QString::fromLatin1(info.name) == deviceName) -+ return info.orientation; -+ } -+ -+ return 0; -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/openharmony/src/mediacapture/qopenharmonycamerainfocontrol.h b/src/plugins/openharmony/src/mediacapture/qopenharmonycamerainfocontrol.h -new file mode 100644 -index 000000000..f58accd1c ---- /dev/null -+++ b/src/plugins/openharmony/src/mediacapture/qopenharmonycamerainfocontrol.h -@@ -0,0 +1,22 @@ -+#ifndef QOPENHARMONYCAMERAINFOCONTROL_H -+#define QOPENHARMONYCAMERAINFOCONTROL_H -+ -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOPenHarmonyCameraInfoControl : public QCameraInfoControl -+{ -+ Q_OBJECT -+ -+public: -+ QCamera::Position cameraPosition(const QString &deviceName) const; -+ int cameraOrientation(const QString &deviceName) const; -+ -+ static QCamera::Position position(const QString &deviceName); -+ static int orientation(const QString &deviceName); -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYCAMERAINFOCONTROL_H -diff --git a/src/plugins/openharmony/src/mediacapture/qopenharmonycamerasession.cpp b/src/plugins/openharmony/src/mediacapture/qopenharmonycamerasession.cpp -new file mode 100644 -index 000000000..d64ab47d9 ---- /dev/null -+++ b/src/plugins/openharmony/src/mediacapture/qopenharmonycamerasession.cpp -@@ -0,0 +1,124 @@ -+#include "qopenharmonycamerasession.h" -+ -+QT_BEGIN_NAMESPACE -+ -+Q_GLOBAL_STATIC(QList, g_availableCameras) -+ -+QOPenHarmonyCameraSession::QOPenHarmonyCameraSession(QObject *parent) : QObject{ parent } -+ , m_camera(Q_NULLPTR) -+ , m_captureMode(QCamera::CaptureStillImage) -+ , m_state(QCamera::UnloadedState) -+{ -+ -+} -+ -+QOPenHarmonyCameraSession::~QOPenHarmonyCameraSession() -+{ -+ -+} -+ -+const QList &QOPenHarmonyCameraSession::availableCameras() -+{ -+ if (g_availableCameras->isEmpty()) -+ updateAvailableCameras(); -+ -+ return *g_availableCameras; -+} -+ -+void QOPenHarmonyCameraSession::setState(QCamera::State state) -+{ -+ -+} -+ -+void QOPenHarmonyCameraSession::setStateHelper(QCamera::State state) -+{ -+ switch (state) { -+ case QCamera::UnloadedState: -+ close(); -+ break; -+ case QCamera::LoadedState: -+ case QCamera::ActiveState: -+ if (!m_camera && !open()) { -+ m_state = QCamera::UnloadedState; -+ emit stateChanged(m_state); -+ emit error(QCamera::CameraError, QStringLiteral("Failed to open camera")); -+ m_status = QCamera::UnloadedStatus; -+ emit statusChanged(m_status); -+ return; -+ } -+ if (state == QCamera::ActiveState) -+ startPreview(); -+ else if (state == QCamera::LoadedState) -+ stopPreview(); -+ break; -+ } -+} -+ -+ -+void QOPenHarmonyCameraSession::setCaptureMode(QCamera::CaptureModes mode) -+{ -+ -+} -+ -+bool QOPenHarmonyCameraSession::isCaptureModeSupported(QCamera::CaptureModes mode) const -+{ -+ -+} -+ -+void QOPenHarmonyCameraSession::onApplicationStateChanged(Qt::ApplicationState state) -+{ -+ switch (state) { -+ case Qt::ApplicationInactive: -+ if (m_state != QCamera::UnloadedState) { -+ m_savedState = m_state; -+ //close(); TODO -+ m_state = QCamera::UnloadedState; -+ emit stateChanged(m_state); -+ } -+ break; -+ case Qt::ApplicationActive: -+ if (m_savedState != -1) { -+ setStateHelper(QCamera::State(m_savedState)); -+ m_savedState = -1; -+ } -+ break; -+ default: -+ break; -+ } -+} -+ -+void QOPenHarmonyCameraSession::updateAvailableCameras() -+{ -+ g_availableCameras->clear(); -+ -+ const QStringList &ids = OPenHarmonyCamera::getIdOfCameras(); -+ for (const QString &id : qAsConst(ids)) { -+ OPenHarmonyCameraInfo info; -+ OPenHarmonyCamera::getCameraInfo(id, &info); -+ -+ if (!info.name.isNull()) -+ g_availableCameras->append(info); -+ } -+} -+ -+bool QOPenHarmonyCameraSession::open() -+{ -+ return false; -+} -+ -+void QOPenHarmonyCameraSession::close() -+{ -+ -+} -+ -+bool QOPenHarmonyCameraSession::startPreview() -+{ -+ return false; -+} -+ -+void QOPenHarmonyCameraSession::stopPreview() -+{ -+ -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/openharmony/src/mediacapture/qopenharmonycamerasession.h b/src/plugins/openharmony/src/mediacapture/qopenharmonycamerasession.h -new file mode 100644 -index 000000000..756c91c45 ---- /dev/null -+++ b/src/plugins/openharmony/src/mediacapture/qopenharmonycamerasession.h -@@ -0,0 +1,55 @@ -+#ifndef QOPENHARMONYCAMERASESSION_H -+#define QOPENHARMONYCAMERASESSION_H -+ -+#include -+#include -+ -+#include "openharmonycamera.h" -+ -+QT_BEGIN_NAMESPACE -+ -+class QOPenHarmonyCameraSession : public QObject -+{ -+ Q_OBJECT -+public: -+ explicit QOPenHarmonyCameraSession(QObject *parent = nullptr); -+ ~QOPenHarmonyCameraSession(); -+ static const QList &availableCameras(); -+ -+ QCamera::State state() const { return m_state; } -+ void setState(QCamera::State state); -+ -+ QCamera::Status status() const { return m_status; } -+ -+ QCamera::CaptureModes captureMode() const { return m_captureMode; } -+ void setCaptureMode(QCamera::CaptureModes mode); -+ bool isCaptureModeSupported(QCamera::CaptureModes mode) const; -+ -+Q_SIGNALS: -+ void statusChanged(QCamera::Status status); -+ void stateChanged(QCamera::State); -+ void error(int error, const QString &errorString); -+ void captureModeChanged(QCamera::CaptureModes); -+ -+private Q_SLOTS: -+ void onApplicationStateChanged(Qt::ApplicationState state); -+ -+private: -+ static void updateAvailableCameras(); -+ bool open(); -+ void close(); -+ -+ bool startPreview(); -+ void stopPreview(); -+ -+ void setStateHelper(QCamera::State state); -+ -+ OPenHarmonyCamera *m_camera; -+ QCamera::State m_state; -+ int m_savedState; -+ QCamera::Status m_status; -+ QCamera::CaptureModes m_captureMode; -+}; -+ -+QT_END_NAMESPACE -+#endif // QOPENHARMONYCAMERASESSION_H -diff --git a/src/plugins/openharmony/src/mediacapture/qopenharmonycaptureservice.cpp b/src/plugins/openharmony/src/mediacapture/qopenharmonycaptureservice.cpp -new file mode 100644 -index 000000000..e0734d35b ---- /dev/null -+++ b/src/plugins/openharmony/src/mediacapture/qopenharmonycaptureservice.cpp -@@ -0,0 +1,38 @@ -+#include "qopenharmonycamerasession.h" -+#include "qopenharmonycameracontrol.h" -+#include "qopenharmonycaptureservice.h" -+#include "qmediaserviceproviderplugin.h" -+#include "qopenharmonycamerainfocontrol.h" -+ -+QOPenHarmonyCaptureService::QOPenHarmonyCaptureService(const QString &service, QObject *parent) -+ : QMediaService{ parent } -+ , m_service(service) -+{ -+ if (m_service == QLatin1String(Q_MEDIASERVICE_CAMERA)) { -+ m_cameraSession = new QOPenHarmonyCameraSession(); -+ m_cameraControl = new QOPenHarmonyCameraControl(m_cameraSession); -+ m_cameraInfoControl = new QOPenHarmonyCameraInfoControl(); -+ } else { -+ m_cameraSession = Q_NULLPTR; -+ m_cameraControl = Q_NULLPTR; -+ m_cameraInfoControl = Q_NULLPTR; -+ } -+} -+ -+QOPenHarmonyCaptureService::~QOPenHarmonyCaptureService() -+{ -+ delete m_cameraInfoControl; -+ delete m_cameraControl; -+ delete m_cameraSession; -+} -+ -+QMediaControl *QOPenHarmonyCaptureService::requestControl(const char *name) -+{ -+ if (qstrcmp(name, QCameraInfoControl_iid) == 0) -+ return m_cameraInfoControl; -+} -+ -+void QOPenHarmonyCaptureService::releaseControl(QMediaControl *) -+{ -+ -+} -diff --git a/src/plugins/openharmony/src/mediacapture/qopenharmonycaptureservice.h b/src/plugins/openharmony/src/mediacapture/qopenharmonycaptureservice.h -new file mode 100644 -index 000000000..2856fc62e ---- /dev/null -+++ b/src/plugins/openharmony/src/mediacapture/qopenharmonycaptureservice.h -@@ -0,0 +1,34 @@ -+#ifndef QOPENHARMONYCAPTURESERVICE_H -+#define QOPENHARMONYCAPTURESERVICE_H -+ -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOPenHarmonyCameraControl; -+class QOPenHarmonyCameraSession; -+class QOPenHarmonyCameraInfoControl; -+ -+class QOPenHarmonyCaptureService : public QMediaService -+{ -+ Q_OBJECT -+ -+public: -+ explicit QOPenHarmonyCaptureService(const QString &service, QObject *parent = nullptr); -+ virtual ~QOPenHarmonyCaptureService(); -+ -+ QMediaControl *requestControl(const char *name); -+ void releaseControl(QMediaControl *); -+ -+private: -+ QString m_service; -+ QOPenHarmonyCameraSession *m_cameraSession; -+ -+ QOPenHarmonyCameraControl *m_cameraControl; -+ QOPenHarmonyCameraInfoControl *m_cameraInfoControl; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYCAPTURESERVICE_H -diff --git a/src/plugins/openharmony/src/mediacapture/qopenharmonycapturesession.cpp b/src/plugins/openharmony/src/mediacapture/qopenharmonycapturesession.cpp -new file mode 100644 -index 000000000..f2807b676 ---- /dev/null -+++ b/src/plugins/openharmony/src/mediacapture/qopenharmonycapturesession.cpp -@@ -0,0 +1,135 @@ -+#include "qopenharmonycapturesession.h" -+ -+QT_BEGIN_NAMESPACE -+ -+QOPenHarmonyCaptureSession::QOPenHarmonyCaptureSession(QOPenHarmonyCameraSession *cameraSession) -+ : QObject() -+ , m_cameraSession(cameraSession) -+ , m_duration(0) -+ , m_state(QMediaRecorder::StoppedState) -+ , m_status(QMediaRecorder::UnloadedStatus) -+ , m_containerFormat() -+ , m_containerFormatDirty(true) -+{ -+ -+} -+ -+QOPenHarmonyCaptureSession::~QOPenHarmonyCaptureSession() -+{ -+ stop(); -+} -+ -+qint64 QOPenHarmonyCaptureSession::duration() const -+{ -+ return m_duration; -+} -+ -+QMediaRecorder::State QOPenHarmonyCaptureSession::state() const -+{ -+ return m_state; -+} -+ -+QMediaRecorder::Status QOPenHarmonyCaptureSession::status() const -+{ -+ return m_status; -+} -+ -+void QOPenHarmonyCaptureSession::setState(QMediaRecorder::State state) -+{ -+ if (m_state == state) -+ return; -+ -+ switch (state) { -+ case QMediaRecorder::StoppedState: -+ stop(); -+ break; -+ case QMediaRecorder::RecordingState: -+ start(); -+ break; -+ case QMediaRecorder::PausedState: -+ // Not supported by Android API -+ qWarning("QMediaRecorder::PausedState is not supported on Android"); -+ break; -+ } -+} -+ -+void QOPenHarmonyCaptureSession::setContainerFormat(const QString &format) -+{ -+ if (m_containerFormat == format) -+ return; -+ -+ m_containerFormat = format; -+ m_containerFormatDirty = true; -+} -+ -+void QOPenHarmonyCaptureSession::updateDuration() -+{ -+ if (m_elapsedTime.isValid()) -+ m_duration = m_elapsedTime.elapsed(); -+ -+ emit durationChanged(m_duration); -+} -+ -+void QOPenHarmonyCaptureSession::onCameraOpened() -+{ -+ -+} -+ -+void QOPenHarmonyCaptureSession::onError(int what, int extra) -+{ -+ Q_UNUSED(what) -+ Q_UNUSED(extra) -+ stop(true); -+ emit error(QMediaRecorder::ResourceError, QLatin1String("Unknown error.")); -+} -+ -+void QOPenHarmonyCaptureSession::onInfo(int what, int extra) -+{ -+ Q_UNUSED(extra) -+ if (what == 800) { -+ // MEDIA_RECORDER_INFO_MAX_DURATION_REACHED -+ setState(QMediaRecorder::StoppedState); -+ emit error(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum duration reached.")); -+ } else if (what == 801) { -+ // MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED -+ setState(QMediaRecorder::StoppedState); -+ emit error(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum file size reached.")); -+ } -+} -+ -+void QOPenHarmonyCaptureSession::start() -+{ -+ m_elapsedTime.start(); -+ m_notifyTimer.start(); -+ updateDuration(); -+ -+ m_state = QMediaRecorder::RecordingState; -+ emit stateChanged(m_state); -+ setStatus(QMediaRecorder::RecordingStatus); -+} -+ -+void QOPenHarmonyCaptureSession::stop(bool error) -+{ -+ -+} -+ -+void QOPenHarmonyCaptureSession::setStatus(QMediaRecorder::Status status) -+{ -+ if (m_status == status) -+ return; -+ -+ m_status = status; -+ emit statusChanged(m_status); -+} -+ -+void QOPenHarmonyCaptureSession::updateViewfinder() -+{ -+ -+} -+ -+void QOPenHarmonyCaptureSession::restartViewfinder() -+{ -+ -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/openharmony/src/mediacapture/qopenharmonycapturesession.h b/src/plugins/openharmony/src/mediacapture/qopenharmonycapturesession.h -new file mode 100644 -index 000000000..cc33781d7 ---- /dev/null -+++ b/src/plugins/openharmony/src/mediacapture/qopenharmonycapturesession.h -@@ -0,0 +1,70 @@ -+#ifndef QOPENHARMONYCAPTURESESSION_H -+#define QOPENHARMONYCAPTURESESSION_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOPenHarmonyCameraSession; -+ -+class QOPenHarmonyCaptureSession : public QObject -+{ -+ Q_OBJECT -+ -+public: -+ QOPenHarmonyCaptureSession(QOPenHarmonyCameraSession *cameraSession = Q_NULLPTR); -+ ~QOPenHarmonyCaptureSession(); -+ -+ qint64 duration() const; -+ QMediaRecorder::State state() const; -+ QMediaRecorder::Status status() const; -+ void setState(QMediaRecorder::State state); -+ -+ QString containerFormat() const { return m_containerFormat; } -+ void setContainerFormat(const QString &format); -+ -+Q_SIGNALS: -+ void durationChanged(qint64 position); -+ void audioInputChanged(const QString& name); -+ void stateChanged(QMediaRecorder::State state); -+ void actualLocationChanged(const QUrl &location); -+ void statusChanged(QMediaRecorder::Status status); -+ void error(int error, const QString &errorString); -+ -+private Q_SLOTS: -+ void updateDuration(); -+ void onCameraOpened(); -+ -+ void onError(int what, int extra); -+ void onInfo(int what, int extra); -+ -+private: -+ void start(); -+ void stop(bool error = false); -+ -+ void setStatus(QMediaRecorder::Status status); -+ -+ void updateViewfinder(); -+ void restartViewfinder(); -+ -+ QOPenHarmonyCameraSession *m_cameraSession; -+ -+ QElapsedTimer m_elapsedTime; -+ QTimer m_notifyTimer; -+ qint64 m_duration; -+ -+ QMediaRecorder::State m_state; -+ QMediaRecorder::Status m_status; -+ -+ QString m_containerFormat; -+ bool m_containerFormatDirty; -+}; -+ -+ -+QT_END_NAMESPACE -+#endif // QOPENHARMONYCAPTURESESSION_H -diff --git a/src/plugins/openharmony/src/mediacapture/qopenharmonymediacontainercontrol.cpp b/src/plugins/openharmony/src/mediacapture/qopenharmonymediacontainercontrol.cpp -new file mode 100644 -index 000000000..3017fc913 ---- /dev/null -+++ b/src/plugins/openharmony/src/mediacapture/qopenharmonymediacontainercontrol.cpp -@@ -0,0 +1,64 @@ -+#include "qopenharmonycamerasession.h" -+#include "qopenharmonymediacontainercontrol.h" -+ -+ -+QT_BEGIN_NAMESPACE -+ -+QOPenHarmonyMediaContainerControl::QOPenHarmonyMediaContainerControl(QOPenHarmonyCameraSession *session) -+ : QMediaContainerControl() -+ , m_session(session) -+{ -+ -+} -+ -+QString QOPenHarmonyMediaContainerControl::containerFormat() const -+{ -+ //TODO -+ return QString(); -+} -+ -+QStringList QOPenHarmonyMediaContainerControl::supportedContainers() const -+{ -+ return QStringList() << QLatin1String("ts") -+ << QLatin1String("mp4") -+ << QLatin1String("mkv") -+ << QLatin1String("m4a") -+ << QLatin1String("aac") -+ << QLatin1String("mp3") -+ << QLatin1String("ogg") -+ << QLatin1String("wav") -+ << QLatin1String("webm"); -+ -+} -+ -+void QOPenHarmonyMediaContainerControl::setContainerFormat(const QString &format) -+{ -+ //TODO -+ Q_UNUSED(format); -+} -+ -+QString QOPenHarmonyMediaContainerControl::containerDescription(const QString &formatMimeType) const -+{ -+ if (formatMimeType == QLatin1String("ts")) -+ return tr("TS media file format"); -+ else if (formatMimeType == QLatin1String("mp4")) -+ return tr("MPEG4 media file format"); -+ else if (formatMimeType == QLatin1String("mkv")) -+ return tr("MKV media file format"); -+ else if (formatMimeType == QLatin1String("m4a")) -+ return tr("AAC file format"); -+ else if (formatMimeType == QLatin1String("aac")) -+ return tr("AAC file format"); -+ else if (formatMimeType == QLatin1String("mp3")) -+ return tr("MP3 file format"); -+ else if (formatMimeType == QLatin1String("ogg")) -+ return tr("VORBIS file format"); -+ else if (formatMimeType == QLatin1String("wav")) -+ return tr("PCM file format"); -+ else if (formatMimeType == QLatin1String("webm")) -+ return tr("WEBM file format"); -+ -+ return QString(); -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/openharmony/src/mediacapture/qopenharmonymediacontainercontrol.h b/src/plugins/openharmony/src/mediacapture/qopenharmonymediacontainercontrol.h -new file mode 100644 -index 000000000..8fca536e5 ---- /dev/null -+++ b/src/plugins/openharmony/src/mediacapture/qopenharmonymediacontainercontrol.h -@@ -0,0 +1,27 @@ -+#ifndef QOPENHARMONYMEDIACONTAINERCONTROL_H -+#define QOPENHARMONYMEDIACONTAINERCONTROL_H -+ -+#include -+ -+QT_BEGIN_NAMESPACE -+class QOPenHarmonyCameraSession; -+ -+class QOPenHarmonyMediaContainerControl : public QMediaContainerControl -+{ -+ Q_OBJECT -+ -+public: -+ QOPenHarmonyMediaContainerControl(QOPenHarmonyCameraSession *session); -+ -+ QString containerFormat() const override; -+ QStringList supportedContainers() const override; -+ void setContainerFormat(const QString &format) override; -+ QString containerDescription(const QString &formatMimeType) const override; -+ -+private: -+ QOPenHarmonyCameraSession *m_session; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYMEDIACONTAINERCONTROL_H -diff --git a/src/plugins/openharmony/src/openharmony_mediaservice.json b/src/plugins/openharmony/src/openharmony_mediaservice.json -new file mode 100644 -index 000000000..9f188ebfb ---- /dev/null -+++ b/src/plugins/openharmony/src/openharmony_mediaservice.json -@@ -0,0 +1,4 @@ -+{ -+ "Keys": ["openharmonymultimedia"], -+ "Services": ["org.qt-project.qt.camera", "org.qt-project.qt.mediaplayer", "org.qt-project.qt.audiosource"] -+} -diff --git a/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.cpp b/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.cpp -new file mode 100644 -index 000000000..f7cc1314d ---- /dev/null -+++ b/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.cpp -@@ -0,0 +1,101 @@ -+#include -+ -+#include "qcamera.h" -+#include "qmediaserviceproviderplugin.h" -+#include "qopenharmonymediaserviceplugin.h" -+#include "mediacapture/qopenharmonycamerasession.h" -+#include "mediacapture/qopenharmonycaptureservice.h" -+#include "mediacapture/qopenharmonycamerainfocontrol.h" -+ -+QT_BEGIN_NAMESPACE -+ -+Q_LOGGING_CATEGORY(qtOPenHaronyMediaPlugin, "qt.multimedia.plugins.openharmony") -+ -+QOPenHarmonyMediaServicePlugin::QOPenHarmonyMediaServicePlugin() -+{ -+} -+ -+QOPenHarmonyMediaServicePlugin::~QOPenHarmonyMediaServicePlugin() -+{ -+} -+ -+QMediaService *QOPenHarmonyMediaServicePlugin::create(const QString &key) -+{ -+ if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) -+ return nullptr; -+ -+ if (key == QLatin1String(Q_MEDIASERVICE_CAMERA) -+ || key == QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE)) { -+ return new QOPenHarmonyCaptureService(key); -+ } -+ -+ qCWarning(qtOPenHaronyMediaPlugin) << "OPenHarony service plugin: unsupported key:" << key; -+ return 0; -+} -+ -+void QOPenHarmonyMediaServicePlugin::release(QMediaService *service) -+{ -+ delete service; -+} -+ -+QMediaServiceProviderHint::Features QOPenHarmonyMediaServicePlugin::supportedFeatures(const QByteArray &service) const -+{ -+ if (service == Q_MEDIASERVICE_MEDIAPLAYER) -+ return QMediaServiceProviderHint::VideoSurface; -+ -+ if (service == Q_MEDIASERVICE_CAMERA) -+ return QMediaServiceProviderHint::VideoSurface | QMediaServiceProviderHint::RecordingSupport; -+ -+ if (service == Q_MEDIASERVICE_AUDIOSOURCE) -+ return QMediaServiceProviderHint::RecordingSupport; -+ -+ return QMediaServiceProviderHint::Features(); -+} -+ -+QByteArray QOPenHarmonyMediaServicePlugin::defaultDevice(const QByteArray &service) const -+{ -+ if (service == Q_MEDIASERVICE_CAMERA && !QOPenHarmonyCameraSession::availableCameras().isEmpty()) -+ return QOPenHarmonyCameraSession::availableCameras().first().name; -+ -+ return QByteArray(); -+} -+ -+QList QOPenHarmonyMediaServicePlugin::devices(const QByteArray &service) const -+{ -+ Q_UNUSED(service); -+ if (service == Q_MEDIASERVICE_CAMERA) { -+ QList devices; -+ const QList &cameras = QOPenHarmonyCameraSession::availableCameras(); -+ for (int i = 0; i < cameras.count(); ++i) -+ devices.append(cameras.at(i).name); -+ return devices; -+ } -+ -+ return QList(); -+} -+ -+QString QOPenHarmonyMediaServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device) -+{ -+ if (service == Q_MEDIASERVICE_CAMERA) { -+ const QList &cameras = QOPenHarmonyCameraSession::availableCameras(); -+ for (int i = 0; i < cameras.count(); ++i) { -+ const OPenHarmonyCameraInfo &info = cameras.at(i); -+ if (info.name == device) -+ return info.description; -+ } -+ } -+ -+ return QString(); -+} -+ -+QCamera::Position QOPenHarmonyMediaServicePlugin::cameraPosition(const QByteArray &device) const -+{ -+return QOPenHarmonyCameraInfoControl::position(device); -+} -+ -+int QOPenHarmonyMediaServicePlugin::cameraOrientation(const QByteArray &device) const -+{ -+ return QOPenHarmonyCameraInfoControl::orientation(device); -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.h b/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.h -new file mode 100644 -index 000000000..340e0b7ba ---- /dev/null -+++ b/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.h -@@ -0,0 +1,42 @@ -+#ifndef QOPENHARMONYMEDIASERVICEPLUGIN_H -+#define QOPENHARMONYMEDIASERVICEPLUGIN_H -+ -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOPenHarmonyMediaServicePlugin -+ : public QMediaServiceProviderPlugin -+ , public QMediaServiceSupportedDevicesInterface -+ , public QMediaServiceDefaultDeviceInterface -+ , public QMediaServiceCameraInfoInterface -+ , public QMediaServiceFeaturesInterface -+{ -+ Q_OBJECT -+ Q_INTERFACES(QMediaServiceSupportedDevicesInterface) -+ Q_INTERFACES(QMediaServiceDefaultDeviceInterface) -+ Q_INTERFACES(QMediaServiceCameraInfoInterface) -+ Q_INTERFACES(QMediaServiceFeaturesInterface) -+ Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" -+ FILE "openharmony_mediaservice.json") -+ -+public: -+ QOPenHarmonyMediaServicePlugin(); -+ ~QOPenHarmonyMediaServicePlugin(); -+ -+ QMediaService* create(QString const& key) override; -+ void release(QMediaService *service) override; -+ -+ QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const override; -+ -+ QByteArray defaultDevice(const QByteArray &service) const override; -+ QList devices(const QByteArray &service) const override; -+ QString deviceDescription(const QByteArray &service, const QByteArray &device) override; -+ -+ QCamera::Position cameraPosition(const QByteArray &device) const override; -+ int cameraOrientation(const QByteArray &device) const override; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYMEDIASERVICEPLUGIN_H -diff --git a/src/plugins/openharmony/src/src.pro b/src/plugins/openharmony/src/src.pro -new file mode 100644 -index 000000000..2ff2a0020 ---- /dev/null -+++ b/src/plugins/openharmony/src/src.pro -@@ -0,0 +1,20 @@ -+include (wrappers/napi/napi.pri) -+include (mediacapture/mediacapture.pri) -+ -+TARGET = qtmedia_openharmony -+ -+QT += multimedia-private core-private network -+ -+HEADERS += \ -+ qopenharmonymediaserviceplugin.h -+ -+SOURCES += \ -+ qopenharmonymediaserviceplugin.cpp -+ -+ -+OTHER_FILES += \ -+ openharmony_mediaservice.json -+ -+PLUGIN_TYPE = mediaservice -+PLUGIN_CLASS_NAME = QOPenHarmonyMediaServicePlugin -+load(qt_plugin) -diff --git a/src/plugins/openharmony/src/wrappers/napi/napi.pri b/src/plugins/openharmony/src/wrappers/napi/napi.pri -new file mode 100644 -index 000000000..c71f579b6 ---- /dev/null -+++ b/src/plugins/openharmony/src/wrappers/napi/napi.pri -@@ -0,0 +1,11 @@ -+INCLUDEPATH += $$PWD -+ -+HEADERS += \ -+ $$PWD/openharmonycamera.h \ -+ $$PWD/openharmonymediarecorder.h \ -+ $$PWD/openharmonymultimediautils.h -+ -+SOURCES += \ -+ $$PWD/openharmonycamera.cpp \ -+ $$PWD/openharmonymediarecorder.cpp \ -+ $$PWD/openharmonymultimediautils.cpp -diff --git a/src/plugins/openharmony/src/wrappers/napi/openharmonycamera.cpp b/src/plugins/openharmony/src/wrappers/napi/openharmonycamera.cpp -new file mode 100644 -index 000000000..a0dd71aab ---- /dev/null -+++ b/src/plugins/openharmony/src/wrappers/napi/openharmonycamera.cpp -@@ -0,0 +1,168 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "openharmonycamera.h" -+#include "QtCore/QOpenHarmonyJsObject" -+#include "QtCore/QOpenHarmonyJsEnvironment" -+#include "QtCore/QOpenHarmonyJsObjectLoader" -+ -+QT_BEGIN_NAMESPACE -+ -+Q_LOGGING_CATEGORY(qtCameraInfo, "qt.multimedia.plugins.openharmony.camerainfo") -+ -+using CameraMap = QHash; -+ -+Q_GLOBAL_STATIC(CameraMap, cameras) -+Q_GLOBAL_STATIC(QReadWriteLock, rwLock) -+ -+class OPenHarmonyCameraPrivate : public QObject -+{ -+ Q_OBJECT -+ -+public: -+ OPenHarmonyCameraPrivate(); -+ ~OPenHarmonyCameraPrivate(); -+ -+ Q_INVOKABLE bool init(const QString &cameraId); -+ -+ static QStringList getIdOfCameras(); -+ static void getCameraInfo(const QString &id, OPenHarmonyCameraInfo *info); -+public: -+ QString m_cameraId; -+ static QSharedPointer m_jsCamera; -+}; -+QSharedPointer OPenHarmonyCameraPrivate::m_jsCamera(Q_NULLPTR); -+ -+OPenHarmonyCameraPrivate::OPenHarmonyCameraPrivate() -+ : QObject() -+{ -+ m_jsCamera = qJsObjectLoader->create("JsCameraManager"); -+} -+ -+OPenHarmonyCameraPrivate::~OPenHarmonyCameraPrivate() -+{ -+ -+} -+ -+bool OPenHarmonyCameraPrivate::init(const QString &cameraId) -+{ -+ Q_UNUSED(cameraId); -+ return false; -+} -+ -+QStringList OPenHarmonyCameraPrivate::getIdOfCameras() -+{ -+ if (m_jsCamera.isNull()){ -+ m_jsCamera = qJsObjectLoader->create("JsCameraManager"); -+ } -+ QStringList ids = m_jsCamera->call("idOfCameras"); -+ return ids; -+} -+ -+void OPenHarmonyCameraPrivate::getCameraInfo(const QString &id, OPenHarmonyCameraInfo *info) -+{ -+ Q_ASSERT(info); -+ if (m_jsCamera.isNull()){ -+ m_jsCamera = qJsObjectLoader->create("JsCameraManager"); -+ } -+ -+ QString json = m_jsCamera->call("cameraInfo", id); -+ if (json.isEmpty()) -+ return; -+ -+ QJsonParseError error; -+ QJsonDocument doc = QJsonDocument::fromJson(json.toLocal8Bit(), &error); -+ if (QJsonParseError::NoError != error.error) { -+ qCWarning(qtCameraInfo) << "parase camera info from openharmony: " << error.errorString(); -+ return; -+ } -+ -+ QJsonObject obj = doc.object(); -+ QString cameraId = obj.value(QStringLiteral("cameraId")).toString(); -+ info->name = cameraId.toLocal8Bit(); -+ int pos = obj.value(QStringLiteral("cameraPosition")).toInt(); -+ info->position = QCamera::Position(pos); -+ -+ static QHash sCameraTypeDes{ -+ { 3, QStringLiteral("Telephoto camera") }, -+ { 1, QStringLiteral("Wide Angle Len Camera") }, -+ { 2, QStringLiteral("Ultra wide Angle camera") }, -+ { 0, QStringLiteral("The camera type was not specified") }, -+ { 4, QStringLiteral("Camera with depth of field information") } -+ }; -+ -+ static QHash sConnectionTypeDes{ -+ { 0, QStringLiteral(" Built-in Camera") }, -+ { 1, QStringLiteral(" Usb-connected camera") }, -+ { 2, QStringLiteral(" Remotely connected camera") }, -+ }; -+ -+ int cameraType = obj.value("cameraType").toInt(); -+ int connectType = obj.value("connectionType").toInt(); -+ info->description = sCameraTypeDes.value(cameraType) + sConnectionTypeDes.value(connectType); -+ info->orientation = 0; //TODO Returns the physical orientation of the camera sensor. -+} -+ -+OPenHarmonyCamera::~OPenHarmonyCamera() -+{ -+ -+} -+ -+QString OPenHarmonyCamera::cameraId() const -+{ -+ Q_D(const OPenHarmonyCamera); -+ return d->m_cameraId; -+} -+ -+QStringList OPenHarmonyCamera::getIdOfCameras() -+{ -+ return std::move(OPenHarmonyCameraPrivate::getIdOfCameras()); -+} -+ -+OPenHarmonyCamera *OPenHarmonyCamera::open(const QString &cameraId) -+{ -+ OPenHarmonyCameraPrivate *d = new OPenHarmonyCameraPrivate(); -+ QThread *worker = new QThread; -+ worker->start(); -+ d->moveToThread(worker); -+ connect(worker, &QThread::finished, d, &OPenHarmonyCameraPrivate::deleteLater); -+ bool ok = true; -+ QMetaObject::invokeMethod(d, "init", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok), Q_ARG(const QString&, cameraId)); -+ if (!ok) { -+ worker->quit(); -+ worker->wait(5000); -+ delete worker; -+ return 0; -+ } -+ -+ OPenHarmonyCamera *q = new OPenHarmonyCamera(d, worker); -+ QWriteLocker locker(rwLock); -+ cameras->insert(cameraId, q); -+ -+ return q; -+} -+ -+void OPenHarmonyCamera::getCameraInfo(const QString &id, OPenHarmonyCameraInfo *info) -+{ -+ OPenHarmonyCameraPrivate::getCameraInfo(id, info); -+} -+ -+OPenHarmonyCamera::OPenHarmonyCamera(OPenHarmonyCameraPrivate *d, QThread *worker) : QObject() -+ , d_ptr(d) -+ , m_worker(worker) -+{ -+ -+} -+ -+QT_END_NAMESPACE -+ -+#include "openharmonycamera.moc" -diff --git a/src/plugins/openharmony/src/wrappers/napi/openharmonycamera.h b/src/plugins/openharmony/src/wrappers/napi/openharmonycamera.h -new file mode 100644 -index 000000000..61baab6d8 ---- /dev/null -+++ b/src/plugins/openharmony/src/wrappers/napi/openharmonycamera.h -@@ -0,0 +1,43 @@ -+#ifndef OPENHARMONYCAMERA_H -+#define OPENHARMONYCAMERA_H -+ -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QThread; -+class OPenHarmonyCameraPrivate; -+ -+struct OPenHarmonyCameraInfo -+{ -+ QByteArray name; -+ QString description; -+ QCamera::Position position; -+ int orientation; -+}; -+Q_DECLARE_TYPEINFO(OPenHarmonyCameraInfo, Q_MOVABLE_TYPE); -+ -+class OPenHarmonyCamera : public QObject -+{ -+ Q_OBJECT -+ -+public: -+ ~OPenHarmonyCamera(); -+ -+ QString cameraId() const; -+ static QStringList getIdOfCameras(); -+ static OPenHarmonyCamera *open(const QString &cameraId); -+ static void getCameraInfo(const QString &id, OPenHarmonyCameraInfo *info); -+ -+Q_SIGNALS: -+ -+private: -+ OPenHarmonyCameraPrivate *d_ptr; -+ QScopedPointer m_worker; -+ Q_DECLARE_PRIVATE(OPenHarmonyCamera) -+ OPenHarmonyCamera(OPenHarmonyCameraPrivate *d, QThread *worker); -+}; -+ -+QT_END_NAMESPACE -+#endif // OPENHARMONYCAMERA_H -diff --git a/src/plugins/openharmony/src/wrappers/napi/openharmonymediarecorder.cpp b/src/plugins/openharmony/src/wrappers/napi/openharmonymediarecorder.cpp -new file mode 100644 -index 000000000..1b3882c3b ---- /dev/null -+++ b/src/plugins/openharmony/src/wrappers/napi/openharmonymediarecorder.cpp -@@ -0,0 +1,64 @@ -+#include "openharmonymediarecorder.h" -+#include "QtCore/QOpenHarmonyJsObject" -+#include "QtCore/QOpenHarmonyJsEnvironment" -+#include "QtCore/QOpenHarmonyJsObjectLoader" -+ -+OPenHarmonyMediaRecorder::OPenHarmonyMediaRecorder() -+{ -+ m_mediaRecorder = qJsObjectLoader->create("JsMediaRecorder"); -+} -+ -+OPenHarmonyMediaRecorder::~OPenHarmonyMediaRecorder() -+{ -+ -+} -+ -+void OPenHarmonyMediaRecorder::release() -+{ -+ -+} -+ -+bool OPenHarmonyMediaRecorder::prepare() -+{ -+ -+} -+ -+void OPenHarmonyMediaRecorder::reset() -+{ -+ -+} -+ -+bool OPenHarmonyMediaRecorder::start() -+{ -+ -+} -+ -+void OPenHarmonyMediaRecorder::stop() -+{ -+ -+} -+ -+void OPenHarmonyMediaRecorder::setAudioChannels(int numChannels) -+{ -+ -+} -+ -+void OPenHarmonyMediaRecorder::setAudioEncoder(AudioEncoder encoder) -+{ -+ -+} -+ -+void OPenHarmonyMediaRecorder::setAudioEncodingBitRate(int bitRate) -+{ -+ -+} -+ -+void OPenHarmonyMediaRecorder::setAudioSamplingRate(int samplingRate) -+{ -+ -+} -+ -+void OPenHarmonyMediaRecorder::setAudioSource(AudioSource source) -+{ -+ -+} -diff --git a/src/plugins/openharmony/src/wrappers/napi/openharmonymediarecorder.h b/src/plugins/openharmony/src/wrappers/napi/openharmonymediarecorder.h -new file mode 100644 -index 000000000..453332321 ---- /dev/null -+++ b/src/plugins/openharmony/src/wrappers/napi/openharmonymediarecorder.h -@@ -0,0 +1,68 @@ -+#ifndef OPENHARMONYMEDIARECORDER_H -+#define OPENHARMONYMEDIARECORDER_H -+ -+#include "qobject.h" -+#include "qsharedpointer.h" -+ -+QT_BEGIN_NAMESPACE -+ -+class QOpenHarmonyJsObject; -+class OPenHarmonyMediaRecorder : public QObject -+{ -+ Q_OBJECT -+public: -+ enum AudioEncoder { -+ AAC = 1, -+ VORBIS = 2, -+ FLAC = 3 -+ }; -+ -+ enum AudioSource { -+ Mic = 0 -+ }; -+ -+ enum VideoEncoder { -+ H263 = 0, -+ AVC = 1, -+ MPEG_2 = 2, -+ MPEG_4 = 3, -+ VP8 = 4 -+ }; -+ -+ enum VideoSource { -+ YUV = 0, -+ ES = 1 -+ }; -+ -+ enum OutputFormat { -+ CFT_MPEG_4 = 0, -+ CFT_MPEG_4A = 1, -+ }; -+ -+ OPenHarmonyMediaRecorder(); -+ ~OPenHarmonyMediaRecorder(); -+ -+ void release(); -+ bool prepare(); -+ void reset(); -+ -+ bool start(); -+ void stop(); -+ -+ void setAudioChannels(int numChannels); -+ void setAudioEncoder(AudioEncoder encoder); -+ void setAudioEncodingBitRate(int bitRate); -+ void setAudioSamplingRate(int samplingRate); -+ void setAudioSource(AudioSource source); -+ -+Q_SIGNALS: -+ void error(int what, int extra); -+ void info(int what, int extra); -+ -+private: -+ qptrdiff m_id; -+ QSharedPointer m_mediaRecorder; -+}; -+ -+QT_END_NAMESPACE -+#endif // OPENHARMONYMEDIARECORDER_H -diff --git a/src/plugins/openharmony/src/wrappers/napi/openharmonymultimediautils.cpp b/src/plugins/openharmony/src/wrappers/napi/openharmonymultimediautils.cpp -new file mode 100644 -index 000000000..17d5c7165 ---- /dev/null -+++ b/src/plugins/openharmony/src/wrappers/napi/openharmonymultimediautils.cpp -@@ -0,0 +1,56 @@ -+#include -+#include -+#include -+ -+#include -+#include "openharmonymultimediautils.h" -+ -+QT_BEGIN_NAMESPACE -+ -+class QtMultimediaUtils -+{ -+ QSharedPointer m_jsUtils; -+ -+public: -+ QtMultimediaUtils(); -+ QString getDefaultMediaDirectory(OPenHarmonyMultimediaUtils::MediaType type); -+}; -+ -+QtMultimediaUtils::QtMultimediaUtils() -+{ -+ m_jsUtils = qJsObjectLoader->create("JsMultimediaUtils"); -+} -+ -+QString QtMultimediaUtils::getDefaultMediaDirectory(OPenHarmonyMultimediaUtils::MediaType type) -+{ -+ return QString(); -+// QString dir = m_jsUtils->call("getMediaDirectory", type); -+// return std::move(dir); -+} -+ -+Q_GLOBAL_STATIC(QtMultimediaUtils, gsUtils) -+int OPenHarmonyMultimediaUtils::getDeviceOrientation() -+{ -+ //TODO -+ return 0; -+} -+ -+void OPenHarmonyMultimediaUtils::enableOrientationListener(bool enable) -+{ -+ //TODO -+ Q_UNUSED(enable); -+} -+ -+void OPenHarmonyMultimediaUtils::registerMediaFile(const QString &file) -+{ -+ //TODO -+ Q_UNUSED(file); -+} -+ -+QString OPenHarmonyMultimediaUtils::getDefaultMediaDirectory(MediaType type) -+{ -+ const QString &dir = gsUtils->getDefaultMediaDirectory(type); -+ return std::move(dir); -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/openharmony/src/wrappers/napi/openharmonymultimediautils.h b/src/plugins/openharmony/src/wrappers/napi/openharmonymultimediautils.h -new file mode 100644 -index 000000000..f667b9898 ---- /dev/null -+++ b/src/plugins/openharmony/src/wrappers/napi/openharmonymultimediautils.h -@@ -0,0 +1,27 @@ -+#ifndef OPENHARMONYMULTIMEDIAUTILS_H -+#define OPENHARMONYMULTIMEDIAUTILS_H -+ -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class OPenHarmonyMultimediaUtils -+{ -+public: -+ enum MediaType { -+ Camera = 0, -+ Video = 1, -+ Image = 2, -+ Audio = 3, -+ Documents = 4, -+ Download = 5 -+ }; -+ -+ static int getDeviceOrientation(); -+ static void enableOrientationListener(bool enable); -+ static void registerMediaFile(const QString &file); -+ static QString getDefaultMediaDirectory(MediaType type); -+}; -+ -+QT_END_NAMESPACE -+#endif // OPENHARMONYMULTIMEDIAUTILS_H -diff --git a/src/plugins/openharmony/videonode/openharmony_videonode.json b/src/plugins/openharmony/videonode/openharmony_videonode.json -new file mode 100644 -index 000000000..c82757cb0 ---- /dev/null -+++ b/src/plugins/openharmony/videonode/openharmony_videonode.json -@@ -0,0 +1,3 @@ -+{ -+ "Keys": ["openharmony"] -+} -diff --git a/src/plugins/openharmony/videonode/qopenharmonysgvideonode.cpp b/src/plugins/openharmony/videonode/qopenharmonysgvideonode.cpp -new file mode 100644 -index 000000000..e329ae8ee ---- /dev/null -+++ b/src/plugins/openharmony/videonode/qopenharmonysgvideonode.cpp -@@ -0,0 +1,212 @@ -+/**************************************************************************** -+** -+** Copyright (C) 2016 The Qt Company Ltd. -+** Contact: https://www.qt.io/licensing/ -+** -+** This file is part 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 "qopenharmonysgvideonode.h" -+ -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOPenHarmonySGVideoNodeMaterialShader : public QSGMaterialShader -+{ -+public: -+ void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial); -+ -+ char const *const *attributeNames() const { -+ static const char *names[] = { -+ "qt_VertexPosition", -+ "qt_VertexTexCoord", -+ 0 -+ }; -+ return names; -+ } -+ -+protected: -+ -+ const char *vertexShader() const { -+ const char *shader = -+ "uniform highp mat4 qt_Matrix; \n" -+ "attribute highp vec4 qt_VertexPosition; \n" -+ "attribute highp vec2 qt_VertexTexCoord; \n" -+ "varying highp vec2 qt_TexCoord; \n" -+ "void main() { \n" -+ " qt_TexCoord = qt_VertexTexCoord; \n" -+ " gl_Position = qt_Matrix * qt_VertexPosition; \n" -+ "}"; -+ return shader; -+ } -+ -+ const char *fragmentShader() const { -+ static const char *shader = -+ "uniform sampler2D rgbTexture;" -+ "uniform lowp float opacity;" -+ "" -+ "varying highp vec2 qt_TexCoord;" -+ "" -+ "void main()" -+ "{" -+ " gl_FragColor = texture2D(rgbTexture, qt_TexCoord) * opacity;" -+ "}"; -+ return shader; -+ } -+ -+ void initialize() { -+ m_id_matrix = program()->uniformLocation("qt_Matrix"); -+ m_id_Texture = program()->uniformLocation("rgbTexture"); -+ m_id_opacity = program()->uniformLocation("opacity"); -+ } -+ -+ int m_id_matrix; -+ int m_id_Texture; -+ int m_id_opacity; -+}; -+ -+class QOPenHarmonySGVideoNodeMaterial : public QSGMaterial -+{ -+public: -+ QOPenHarmonySGVideoNodeMaterial() -+ : m_textureId(0) -+ , m_textureUpdated(false) -+ , m_opacity(1.0) -+ { -+ setFlag(Blending, false); -+ } -+ -+ QSGMaterialType *type() const { -+ static QSGMaterialType theType; -+ return &theType; -+ } -+ -+ QSGMaterialShader *createShader() const { -+ return new QOPenHarmonySGVideoNodeMaterialShader; -+ } -+ -+ int compare(const QSGMaterial *other) const { -+ const QOPenHarmonySGVideoNodeMaterial *m = static_cast(other); -+ int diff = m_textureId - m->m_textureId; -+ if (diff) -+ return diff; -+ -+ return (m_opacity > m->m_opacity) ? 1 : -1; -+ } -+ -+ void updateBlending() { -+ setFlag(Blending, qFuzzyCompare(m_opacity, qreal(1.0)) ? false : true); -+ } -+ -+ void updateTexture(GLuint id, const QSize &size) { -+ if (m_textureId != id || m_textureSize != size) { -+ m_textureId = id; -+ m_textureSize = size; -+ m_textureUpdated = true; -+ } -+ } -+ -+ void bind() -+ { -+ glBindTexture(GL_TEXTURE_2D, m_textureId); -+ if (m_textureUpdated) { -+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); -+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); -+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); -+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -+ m_textureUpdated = false; -+ } -+ } -+ -+ QSize m_textureSize; -+ GLuint m_textureId; -+ bool m_textureUpdated; -+ qreal m_opacity; -+}; -+ -+ -+QOPenHarmonySGVideoNode::QOPenHarmonySGVideoNode(const QVideoSurfaceFormat &format) -+ : m_format(format) -+{ -+ setFlags(OwnsMaterial | UsePreprocess); -+ m_material = new QOPenHarmonySGVideoNodeMaterial; -+ setMaterial(m_material); -+} -+ -+QOPenHarmonySGVideoNode::~QOPenHarmonySGVideoNode() -+{ -+ m_frame = QVideoFrame(); -+} -+ -+void QOPenHarmonySGVideoNode::setCurrentFrame(const QVideoFrame &frame, FrameFlags) -+{ -+ QMutexLocker lock(&m_frameMutex); -+ m_frame = frame; -+ markDirty(DirtyMaterial); -+} -+ -+void QOPenHarmonySGVideoNodeMaterialShader::updateState(const RenderState &state, -+ QSGMaterial *newMaterial, -+ QSGMaterial *oldMaterial) -+{ -+ Q_UNUSED(oldMaterial); -+ QOPenHarmonySGVideoNodeMaterial *mat = static_cast(newMaterial); -+ program()->setUniformValue(m_id_Texture, 0); -+ -+ mat->bind(); -+ -+ if (state.isOpacityDirty()) { -+ mat->m_opacity = state.opacity(); -+ mat->updateBlending(); -+ program()->setUniformValue(m_id_opacity, GLfloat(mat->m_opacity)); -+ } -+ -+ if (state.isMatrixDirty()) -+ program()->setUniformValue(m_id_matrix, state.combinedMatrix()); -+} -+ -+void QOPenHarmonySGVideoNode::preprocess() -+{ -+ QMutexLocker lock(&m_frameMutex); -+ -+ GLuint texId = 0; -+ if (m_frame.isValid()) -+ texId = m_frame.handle().toUInt(); -+ -+ m_material->updateTexture(texId, m_frame.size()); -+} -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/openharmony/videonode/qopenharmonysgvideonode.h b/src/plugins/openharmony/videonode/qopenharmonysgvideonode.h -new file mode 100644 -index 000000000..fa731c5af ---- /dev/null -+++ b/src/plugins/openharmony/videonode/qopenharmonysgvideonode.h -@@ -0,0 +1,71 @@ -+/**************************************************************************** -+** -+** Copyright (C) 2016 The Qt Company Ltd. -+** Contact: https://www.qt.io/licensing/ -+** -+** This file is part 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 QOPENHARMONYSGVIDEONODE_H -+#define QOPENHARMONYSGVIDEONODE_H -+ -+#include -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOPenHarmonySGVideoNodeMaterial; -+ -+class QOPenHarmonySGVideoNode : public QSGVideoNode -+{ -+public: -+ QOPenHarmonySGVideoNode(const QVideoSurfaceFormat &format); -+ ~QOPenHarmonySGVideoNode(); -+ -+ void setCurrentFrame(const QVideoFrame &frame, FrameFlags flags); -+ QVideoFrame::PixelFormat pixelFormat() const { return m_format.pixelFormat(); } -+ QAbstractVideoBuffer::HandleType handleType() const { return QAbstractVideoBuffer::GLTextureHandle; } -+ -+ void preprocess(); -+ -+private: -+ QOPenHarmonySGVideoNodeMaterial *m_material; -+ QMutex m_frameMutex; -+ QVideoFrame m_frame; -+ QVideoSurfaceFormat m_format; -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYSGVIDEONODE_H -diff --git a/src/plugins/openharmony/videonode/qopenharmonysgvideonodeplugin.cpp b/src/plugins/openharmony/videonode/qopenharmonysgvideonodeplugin.cpp -new file mode 100644 -index 000000000..ae79b8627 ---- /dev/null -+++ b/src/plugins/openharmony/videonode/qopenharmonysgvideonodeplugin.cpp -@@ -0,0 +1,65 @@ -+/**************************************************************************** -+** -+** Copyright (C) 2016 The Qt Company Ltd. -+** Contact: https://www.qt.io/licensing/ -+** -+** This file is part 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 "qopenharmonysgvideonodeplugin.h" -+#include "qopenharmonysgvideonode.h" -+ -+QT_BEGIN_NAMESPACE -+ -+QList QOPenHarmonySGVideoNodeFactoryPlugin::supportedPixelFormats( -+ QAbstractVideoBuffer::HandleType handleType) const -+{ -+ QList pixelFormats; -+ -+ if (handleType == QAbstractVideoBuffer::GLTextureHandle) -+ pixelFormats.append(QVideoFrame::Format_BGR32); -+ -+ return pixelFormats; -+} -+ -+QSGVideoNode *QOPenHarmonySGVideoNodeFactoryPlugin::createNode(const QVideoSurfaceFormat &format) -+{ -+ if (supportedPixelFormats(format.handleType()).contains(format.pixelFormat())) -+ return new QOPenHarmonySGVideoNode(format); -+ -+ return 0; -+} -+ -+ -+QT_END_NAMESPACE -diff --git a/src/plugins/openharmony/videonode/qopenharmonysgvideonodeplugin.h b/src/plugins/openharmony/videonode/qopenharmonysgvideonodeplugin.h -new file mode 100644 -index 000000000..d2cc4359f ---- /dev/null -+++ b/src/plugins/openharmony/videonode/qopenharmonysgvideonodeplugin.h -@@ -0,0 +1,60 @@ -+/**************************************************************************** -+** -+** Copyright (C) 2016 The Qt Company Ltd. -+** Contact: https://www.qt.io/licensing/ -+** -+** This file is part 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 QOPENHARMONYSGVIDEONODEPLUGIN_H -+#define QOPENHARMONYSGVIDEONODEPLUGIN_H -+ -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+class QOPenHarmonySGVideoNodeFactoryPlugin : public QSGVideoNodeFactoryPlugin -+{ -+ Q_OBJECT -+ Q_PLUGIN_METADATA(IID QSGVideoNodeFactoryInterface_iid -+ FILE "openharmony_videonode.json") -+ -+public: -+ QList supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const; -+ QSGVideoNode *createNode(const QVideoSurfaceFormat &format); -+}; -+ -+QT_END_NAMESPACE -+ -+#endif // QOPENHARMONYSGVIDEONODEPLUGIN_H -diff --git a/src/plugins/openharmony/videonode/videonode.pro b/src/plugins/openharmony/videonode/videonode.pro -new file mode 100644 -index 000000000..e4aa287f2 ---- /dev/null -+++ b/src/plugins/openharmony/videonode/videonode.pro -@@ -0,0 +1,18 @@ -+TARGET = qtsgvideonode_openharmony -+ -+QT += quick multimedia-private qtmultimediaquicktools-private -+ -+HEADERS += \ -+ qopenharmonysgvideonode.h \ -+ qopenharmonysgvideonodeplugin.h -+ -+SOURCES += \ -+ qopenharmonysgvideonode.cpp \ -+ qopenharmonysgvideonodeplugin.cpp -+ -+OTHER_FILES += openharmony_videonode.json -+ -+PLUGIN_TYPE = video/videonode -+PLUGIN_EXTENDS = quick -+PLUGIN_CLASS_NAME = QOPenharmonySGVideoNodeFactoryPlugin -+load(qt_plugin) -diff --git a/src/plugins/opensles/qopenslesaudioinput.cpp b/src/plugins/opensles/qopenslesaudioinput.cpp -index ad87cb057..79bcb04e7 100644 ---- a/src/plugins/opensles/qopenslesaudioinput.cpp -+++ b/src/plugins/opensles/qopenslesaudioinput.cpp -@@ -52,7 +52,11 @@ - - QT_BEGIN_NAMESPACE - -+#if defined(Q_OS_OPENHARMONY) -+#define NUM_BUFFERS 3 -+#else - #define NUM_BUFFERS 2 -+#endif - #define DEFAULT_PERIOD_TIME_MS 50 - #define MINIMUM_PERIOD_TIME_MS 5 - -@@ -84,13 +88,22 @@ static bool hasRecordingPermission() - } - - static void bufferQueueCallback(SLAndroidSimpleBufferQueueItf, void *context) -+{ -+ // Process buffer in main thread -+ QMetaObject::invokeMethod(reinterpret_cast(context), "processBuffer"); -+} -+#elif defined(Q_OS_OPENHARMONY) -+static void bufferQueueCallback(SLOHBufferQueueItf, void *context, SLuint32 size) -+{ -+ qInfo() << "<--------------bufferQueueCallback::::" << size; -+} - #else - static void bufferQueueCallback(SLBufferQueueItf, void *context) --#endif - { - // Process buffer in main thread - QMetaObject::invokeMethod(reinterpret_cast(context), "processBuffer"); - } -+#endif - - QOpenSLESAudioInput::QOpenSLESAudioInput(const QByteArray &device) - : m_device(device) -@@ -207,9 +220,11 @@ QIODevice *QOpenSLESAudioInput::start() - - bool QOpenSLESAudioInput::startRecording() - { -+ qWarning() << "Audio Input Test startRecording<----------000"; -+#ifdef ANDROID - if (!hasRecordingPermission()) - return false; -- -+#endif - m_processedBytes = 0; - m_clockStamp.restart(); - m_lastNotifyTime = 0; -@@ -229,27 +244,40 @@ bool QOpenSLESAudioInput::startRecording() - SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE, NUM_BUFFERS }; - #endif - -+ qWarning() << "Audio Input Test startRecording<----------1111" << m_format; - SLDataFormat_PCM format_pcm = QOpenSLESEngine::audioFormatToSLFormatPCM(m_format); - SLDataSink audioSnk = { &loc_bq, &format_pcm }; -+ qWarning() << "Audio Input Test startRecording<----------1111------->"; - - // create audio recorder - // (requires the RECORD_AUDIO permission) - #ifdef ANDROID - const SLInterfaceID id[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }; - const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; -+#elif defined(Q_OS_OPENHARMONY) -+ const SLInterfaceID id[1] = { SL_IID_OH_BUFFERQUEUE }; -+ const SLboolean req[1] = { SL_BOOLEAN_TRUE }; - #else - const SLInterfaceID id[1] = { SL_IID_BUFFERQUEUE }; - const SLboolean req[1] = { SL_BOOLEAN_TRUE }; - #endif - -+#if defined(Q_OS_OPENHARMONY) -+ qWarning() << "Audio Input Test startRecording<----------1111------->openharmony"; -+ result = (*m_engine->slEngine())->CreateAudioRecorder(m_engine->slEngine(), &m_recorderObject, -+ &audioSrc, &audioSnk, -+ 0, nullptr, nullptr); -+ -+#else - result = (*m_engine->slEngine())->CreateAudioRecorder(m_engine->slEngine(), &m_recorderObject, - &audioSrc, &audioSnk, - sizeof(req) / sizeof(SLboolean), id, req); -+#endif -+ qWarning() << "Audio Input Test startRecording<----------1111------->111"; - if (result != SL_RESULT_SUCCESS) { - m_errorState = QAudio::OpenError; - return false; - } -- - #ifdef ANDROID - // configure recorder source - SLAndroidConfigurationItf configItf; -@@ -291,6 +319,8 @@ bool QOpenSLESAudioInput::startRecording() - // get the buffer queue interface - #ifdef ANDROID - SLInterfaceID bufferqueueItfID = SL_IID_ANDROIDSIMPLEBUFFERQUEUE; -+#elif defined(Q_OS_OPENHARMONY) -+ SLInterfaceID bufferqueueItfID = SL_IID_OH_BUFFERQUEUE; - #else - SLInterfaceID bufferqueueItfID = SL_IID_BUFFERQUEUE; - #endif -@@ -306,7 +336,7 @@ bool QOpenSLESAudioInput::startRecording() - m_errorState = QAudio::FatalError; - return false; - } -- -+ qWarning() << "Audio Input Test startRecording<----------2222"; - if (m_bufferSize <= 0) { - m_bufferSize = m_format.bytesForDuration(DEFAULT_PERIOD_TIME_MS * 1000); - } else { -@@ -336,7 +366,7 @@ bool QOpenSLESAudioInput::startRecording() - } - - m_errorState = QAudio::NoError; -- -+ qWarning() << "Audio Input Test startRecording<----------3333"; - return true; - } - -@@ -419,6 +449,8 @@ void QOpenSLESAudioInput::processBuffer() - // If the buffer queue is empty (shouldn't happen), stop recording. - #ifdef ANDROID - SLAndroidSimpleBufferQueueState state; -+#elif defined(Q_OS_OPENHARMONY) -+ SLOHBufferQueueState state; - #else - SLBufferQueueState state; - #endif -diff --git a/src/plugins/opensles/qopenslesaudioinput.h b/src/plugins/opensles/qopenslesaudioinput.h -index ad84db0cd..e722d7957 100644 ---- a/src/plugins/opensles/qopenslesaudioinput.h -+++ b/src/plugins/opensles/qopenslesaudioinput.h -@@ -44,6 +44,11 @@ - #include - #include - -+#if defined(Q_OS_OPENHARMONY) -+#include -+#include -+#endif -+ - #ifdef ANDROID - #include - -@@ -53,6 +58,8 @@ - - #endif - -+ -+ - QT_BEGIN_NAMESPACE - - class QOpenSLESEngine; -@@ -105,6 +112,9 @@ private: - #ifdef ANDROID - SLuint32 m_recorderPreset; - SLAndroidSimpleBufferQueueItf m_bufferQueue; -+ -+#elif defined(Q_OS_OPENHARMONY) -+ SLOHBufferQueueItf m_bufferQueue; - #else - SLBufferQueueItf m_bufferQueue; - #endif -diff --git a/src/plugins/opensles/qopenslesaudiooutput.cpp b/src/plugins/opensles/qopenslesaudiooutput.cpp -index 381ce0ec2..a916b455a 100644 ---- a/src/plugins/opensles/qopenslesaudiooutput.cpp -+++ b/src/plugins/opensles/qopenslesaudiooutput.cpp -@@ -89,10 +89,10 @@ QOpenSLESAudioOutput::QOpenSLESAudioOutput(const QByteArray &device) - m_startRequiresInit(true) - { - #ifndef ANDROID -- m_streamType = -1; -+ m_streamType = -1; - #else -- m_streamType = SL_ANDROID_STREAM_MEDIA; -- m_category = QLatin1String("media"); -+ m_streamType = SL_ANDROID_STREAM_MEDIA; -+ m_category = QLatin1String("media"); - #endif // ANDROID - } - -@@ -114,22 +114,20 @@ QAudio::State QOpenSLESAudioOutput::state() const - void QOpenSLESAudioOutput::start(QIODevice *device) - { - Q_ASSERT(device); -- - if (m_state != QAudio::StoppedState) - stop(); -- - if (!preparePlayer()) - return; - - m_pullMode = true; -- m_audioSource = device; -+ m_audioSource = device; - m_nextBuffer = 0; - m_processedBytes = 0; - m_availableBuffers = BUFFER_COUNT; - setState(QAudio::ActiveState); - setError(QAudio::NoError); - -- // Attempt to fill buffers first. -+ // Attempt to fill buffers first. - for (int i = 0; i != BUFFER_COUNT; ++i) { - const int index = i * m_bufferSize; - const qint64 readSize = m_audioSource->read(m_buffers + index, m_bufferSize); -@@ -146,8 +144,8 @@ void QOpenSLESAudioOutput::start(QIODevice *device) - if (m_processedBytes < 1) - onEOSEvent(); - -- // Change the state to playing. -- // We need to do this after filling the buffers or processedBytes might get corrupted. -+ // Change the state to playing. -+ // We need to do this after filling the buffers or processedBytes might get corrupted. - startPlayer(); - } - -@@ -165,7 +163,7 @@ QIODevice *QOpenSLESAudioOutput::start() - m_audioSource = new SLIODevicePrivate(this); - m_audioSource->open(QIODevice::WriteOnly | QIODevice::Unbuffered); - -- // Change the state to playing -+ // Change the state to playing - startPlayer(); - - setState(QAudio::IdleState); -@@ -361,10 +359,13 @@ void QOpenSLESAudioOutput::onEOSEvent() - if (m_state != QAudio::ActiveState) - return; - -+#if defined(Q_OS_OPENHARMONY) -+ SLOHBufferQueueState state; -+#else - SLBufferQueueState state; -+#endif - if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->GetState(m_bufferQueueItf, &state)) - return; -- - if (state.count > 0) - return; - -@@ -381,7 +382,6 @@ void QOpenSLESAudioOutput::bufferAvailable(quint32 count, quint32 playIndex) - { - Q_UNUSED(count); - Q_UNUSED(playIndex); -- - if (m_state == QAudio::StoppedState) - return; - -@@ -394,7 +394,7 @@ void QOpenSLESAudioOutput::bufferAvailable(quint32 count, quint32 playIndex) - return; - } - -- // We're in pull mode. -+ // We're in pull mode. - const int index = m_nextBuffer * m_bufferSize; - const qint64 readSize = m_audioSource->read(m_buffers + index, m_bufferSize); - -@@ -427,6 +427,15 @@ void QOpenSLESAudioOutput::playCallback(SLPlayItf player, void *ctx, SLuint32 ev - - } - -+#if defined(Q_OS_OPENHARMONY) -+void QOpenSLESAudioOutput::bufferQueueCallback (SLOHBufferQueueItf bufferQueue, void *ctx, SLuint32 size) -+{ -+ SLOHBufferQueueState state; -+ (*bufferQueue)->GetState(bufferQueue, &state); -+ QOpenSLESAudioOutput *audioOutput = reinterpret_cast(ctx); -+ audioOutput->bufferAvailable(state.count, state.index); -+} -+#else - void QOpenSLESAudioOutput::bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx) - { - SLBufferQueueState state; -@@ -434,7 +443,7 @@ void QOpenSLESAudioOutput::bufferQueueCallback(SLBufferQueueItf bufferQueue, voi - QOpenSLESAudioOutput *audioOutput = reinterpret_cast(ctx); - audioOutput->bufferAvailable(state.count, state.playIndex); - } -- -+#endif - bool QOpenSLESAudioOutput::preparePlayer() - { - if (m_startRequiresInit) -@@ -454,7 +463,7 @@ bool QOpenSLESAudioOutput::preparePlayer() - - SLDataSource audioSrc = { &bufferQueueLocator, &pcmFormat }; - -- // OutputMix -+ // OutputMix - if (SL_RESULT_SUCCESS != (*engine)->CreateOutputMix(engine, - &m_outputMixObject, - 0, -@@ -476,7 +485,11 @@ bool QOpenSLESAudioOutput::preparePlayer() - - #ifndef ANDROID - const int iids = 2; -+#if defined(Q_OS_OPENHARMONY) -+ const SLInterfaceID ids[iids] = { SL_IID_OH_BUFFERQUEUE, SL_IID_VOLUME }; -+#else - const SLInterfaceID ids[iids] = { SL_IID_BUFFERQUEUE, SL_IID_VOLUME }; -+#endif - const SLboolean req[iids] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; - #else - const int iids = 3; -@@ -486,14 +499,22 @@ bool QOpenSLESAudioOutput::preparePlayer() - const SLboolean req[iids] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; - #endif // ANDROID - -- // AudioPlayer -+ // AudioPlayer - if (SL_RESULT_SUCCESS != (*engine)->CreateAudioPlayer(engine, - &m_playerObject, - &audioSrc, -+#if defined(Q_OS_OPENHARMONY) -+ nullptr, -+ 0, -+ nullptr, -+ nullptr -+#else - &audioSink, - iids, - ids, -- req)) { -+ req -+#endif -+ )) { - qWarning() << "Unable to create AudioPlayer"; - setError(QAudio::OpenError); - return false; -@@ -520,8 +541,13 @@ bool QOpenSLESAudioOutput::preparePlayer() - - // Buffer interface - if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject, -+#if defined(Q_OS_OPENHARMONY) -+ SL_IID_OH_BUFFERQUEUE, -+#else - SL_IID_BUFFERQUEUE, -+#endif - &m_bufferQueueItf)) { -+ - setError(QAudio::FatalError); - return false; - } -@@ -531,16 +557,16 @@ bool QOpenSLESAudioOutput::preparePlayer() - this)) { - setError(QAudio::FatalError); - return false; -- } -- -- // Play interface -+ } -+ // Play interface - if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject, - SL_IID_PLAY, - &m_playItf)) { - setError(QAudio::FatalError); - return false; - } -- -+#ifndef Q_OS_OPENHARMONY -+ /* TODO for openharmony*/ - if (SL_RESULT_SUCCESS != (*m_playItf)->RegisterCallback(m_playItf, playCallback, this)) { - setError(QAudio::FatalError); - return false; -@@ -555,8 +581,8 @@ bool QOpenSLESAudioOutput::preparePlayer() - setError(QAudio::FatalError); - return false; - } -- -- // Volume interface -+#endif -+ // Volume interface - if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject, - SL_IID_VOLUME, - &m_volumeItf)) { -@@ -575,7 +601,7 @@ bool QOpenSLESAudioOutput::preparePlayer() - return false; - } - -- // Buffer size -+ // Buffer size - if (m_bufferSize <= 0) { - m_bufferSize = defaultBufferSize; - } else if (QOpenSLESEngine::supportsLowLatency()) { -@@ -639,7 +665,7 @@ void QOpenSLESAudioOutput::stopPlayer() - m_audioSource = nullptr; - } - -- // We need to change the state manually... -+ // We need to change the state manually... - if (m_playItf) - (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_STOPPED); - -@@ -666,10 +692,10 @@ qint64 QOpenSLESAudioOutput::writeData(const char *data, qint64 len) - if (len > m_bufferSize) - len = m_bufferSize; - -- // Acquire one slot in the buffer -+ // Acquire one slot in the buffer - const int before = m_availableBuffers.fetchAndAddAcquire(-1); - -- // If there where no vacant slots, then we just overdrew the buffer account... -+ // If there where no vacant slots, then we just overdrew the buffer account... - if (before < 1) { - m_availableBuffers.fetchAndAddRelease(1); - return 0; -@@ -681,7 +707,7 @@ qint64 QOpenSLESAudioOutput::writeData(const char *data, qint64 len) - m_buffers + index, - len); - -- // If we where unable to enqueue a new buffer, give back the acquired slot. -+ // If we where unable to enqueue a new buffer, give back the acquired slot. - if (res == SL_RESULT_BUFFER_INSUFFICIENT) { - m_availableBuffers.fetchAndAddRelease(1); - return 0; -diff --git a/src/plugins/opensles/qopenslesaudiooutput.h b/src/plugins/opensles/qopenslesaudiooutput.h -index b480f00e6..b8c561aa0 100644 ---- a/src/plugins/opensles/qopenslesaudiooutput.h -+++ b/src/plugins/opensles/qopenslesaudiooutput.h -@@ -42,6 +42,10 @@ - - #include - #include -+#if defined(Q_OS_OPENHARMONY) -+#include -+#include -+#endif - #include - #include - #include -@@ -90,8 +94,12 @@ private: - void bufferAvailable(quint32 count, quint32 playIndex); - - static void playCallback(SLPlayItf playItf, void *ctx, SLuint32 event); -- static void bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx); - -+#if defined(Q_OS_OPENHARMONY) -+ static void bufferQueueCallback(SLOHBufferQueueItf bufferQueue, void *ctx, SLuint32 size); -+#else -+ static void bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx); -+#endif - bool preparePlayer(); - void destroyPlayer(); - void stopPlayer(); -@@ -110,7 +118,11 @@ private: - SLObjectItf m_playerObject; - SLPlayItf m_playItf; - SLVolumeItf m_volumeItf; -+#if defined(Q_OS_OPENHARMONY) -+ SLOHBufferQueueItf m_bufferQueueItf; -+#else - SLBufferQueueItf m_bufferQueueItf; -+#endif - QIODevice *m_audioSource; - char *m_buffers; - qreal m_volume; -diff --git a/src/plugins/opensles/qopenslesdeviceinfo.cpp b/src/plugins/opensles/qopenslesdeviceinfo.cpp -index 8aaf67c76..d9700e572 100644 ---- a/src/plugins/opensles/qopenslesdeviceinfo.cpp -+++ b/src/plugins/opensles/qopenslesdeviceinfo.cpp -@@ -41,13 +41,26 @@ - - #include "qopenslesengine.h" - -+#if defined(Q_OS_OPENHARMONY) -+#include -+#include "QtCore/QOpenHarmonyJsObject" -+#include "QtCore/QOpenHarmonyJsEnvironment" -+#include "QtCore/QOpenHarmonyJsObjectLoader" -+#endif -+ - QT_BEGIN_NAMESPACE - - QOpenSLESDeviceInfo::QOpenSLESDeviceInfo(const QByteArray &device, QAudio::Mode mode) - : m_engine(QOpenSLESEngine::instance()) -- , m_device(device) -- , m_mode(mode) -+ , m_device(device) -+ , m_mode(mode) -+#if defined(Q_OS_OPENHARMONY) -+ , m_jsAudioMgr(Q_NULLPTR) -+#endif - { -+#if defined(Q_OS_OPENHARMONY) -+ m_jsAudioMgr = qJsObjectLoader->create("JsAudioManager"); -+#endif - } - - bool QOpenSLESDeviceInfo::isFormatSupported(const QAudioFormat &format) const -@@ -67,8 +80,14 @@ QAudioFormat QOpenSLESDeviceInfo::preferredFormat() const - format.setCodec(QStringLiteral("audio/pcm")); - format.setSampleSize(16); - format.setSampleType(QAudioFormat::SignedInt); -+ -+#if defined(Q_OS_OPENHARMONY) -+ format.setSampleRate(QOpenSLESEngine::getOutputValue(QOpenSLESEngine::SampleRate, 44100)); -+ format.setChannelCount(2); -+#else - format.setSampleRate(QOpenSLESEngine::getOutputValue(QOpenSLESEngine::SampleRate, 48000)); - format.setChannelCount(m_mode == QAudio::AudioInput ? 1 : 2); -+#endif - return format; - } - -@@ -84,20 +103,39 @@ QStringList QOpenSLESDeviceInfo::supportedCodecs() - - QList QOpenSLESDeviceInfo::supportedSampleRates() - { -+#if defined(Q_OS_OPENHARMONY) -+ if (QAudio::AudioInput == m_mode) { -+ return m_jsAudioMgr->call>("inputSupportedSampleRates", m_device); -+ } else { -+ return m_jsAudioMgr->call>("outputSupportedSampleRates", m_device); -+ } -+#else - return m_engine->supportedSampleRates(m_mode); -+#endif - } - - QList QOpenSLESDeviceInfo::supportedChannelCounts() - { -+#if defined(Q_OS_OPENHARMONY) -+ if (QAudio::AudioInput == m_mode) -+ return m_jsAudioMgr->call>("inputChannelCounts", m_device); -+ else -+ return m_jsAudioMgr->call>("outputChannelCounts", m_device); -+#else - return m_engine->supportedChannelCounts(m_mode); -+#endif - } - - QList QOpenSLESDeviceInfo::supportedSampleSizes() - { -+#if defined(Q_OS_OPENHARMONY) -+ return QList() << 8 << 16 << 24 << 32; -+#else - if (m_mode == QAudio::AudioInput) - return QList() << 16; - else - return QList() << 8 << 16; -+#endif - } - - QList QOpenSLESDeviceInfo::supportedByteOrders() -@@ -107,7 +145,13 @@ QList QOpenSLESDeviceInfo::supportedByteOrders() - - QList QOpenSLESDeviceInfo::supportedSampleTypes() - { -+#if defined(Q_OS_OPENHARMONY) -+ return QList() << QAudioFormat::Float -+ << QAudioFormat::SignedInt -+ << QAudioFormat::UnSignedInt; -+#else - return QList() << QAudioFormat::SignedInt; -+#endif - } - - QT_END_NAMESPACE -diff --git a/src/plugins/opensles/qopenslesdeviceinfo.h b/src/plugins/opensles/qopenslesdeviceinfo.h -index ebeb6b4b2..f7ee584ec 100644 ---- a/src/plugins/opensles/qopenslesdeviceinfo.h -+++ b/src/plugins/opensles/qopenslesdeviceinfo.h -@@ -41,11 +41,18 @@ - #define QOPENSLESDEVICEINFO_H - - #include -+#if defined(Q_OS_OPENHARMONY) -+#include -+#endif - - QT_BEGIN_NAMESPACE - - class QOpenSLESEngine; - -+#if defined(Q_OS_OPENHARMONY) -+class QOpenHarmonyJsObject; -+#endif -+ - class QOpenSLESDeviceInfo : public QAbstractAudioDeviceInfo - { - Q_OBJECT -@@ -68,6 +75,9 @@ private: - QOpenSLESEngine *m_engine; - QByteArray m_device; - QAudio::Mode m_mode; -+#if defined(Q_OS_OPENHARMONY) -+ QSharedPointer m_jsAudioMgr; -+#endif - }; - - QT_END_NAMESPACE -diff --git a/src/plugins/opensles/qopenslesengine.cpp b/src/plugins/opensles/qopenslesengine.cpp -index 43cdcb276..0386fa7b0 100644 ---- a/src/plugins/opensles/qopenslesengine.cpp -+++ b/src/plugins/opensles/qopenslesengine.cpp -@@ -38,7 +38,11 @@ - ****************************************************************************/ - - #include "qopenslesengine.h" -- -+#if defined(Q_OS_OPENHARMONY) -+#include "QtCore/QOpenHarmonyJsObject" -+#include "QtCore/QOpenHarmonyJsEnvironment" -+#include "QtCore/QOpenHarmonyJsObjectLoader" -+#endif - #include "qopenslesaudioinput.h" - #include - -@@ -48,6 +52,9 @@ - #include - #endif - -+#if defined(Q_OS_OPENHARMONY) -+#define OHOS_OPENSLES_BUFFER 16384 -+#endif - #define MINIMUM_PERIOD_TIME_MS 5 - #define DEFAULT_PERIOD_TIME_MS 50 - -@@ -59,6 +66,9 @@ QOpenSLESEngine::QOpenSLESEngine() - : m_engineObject(0) - , m_engine(0) - , m_checkedInputFormats(false) -+#if defined(Q_OS_OPENHARMONY) -+ , m_jsAudioMgr(Q_NULLPTR) -+#endif - { - SLresult result; - -@@ -70,6 +80,10 @@ QOpenSLESEngine::QOpenSLESEngine() - - result = (*m_engineObject)->GetInterface(m_engineObject, SL_IID_ENGINE, &m_engine); - CheckError("Failed to get engine interface"); -+ -+#if defined(Q_OS_OPENHARMONY) -+ m_jsAudioMgr = qJsObjectLoader->create("JsAudioManager"); -+#endif - } - - QOpenSLESEngine::~QOpenSLESEngine() -@@ -92,11 +106,11 @@ SLDataFormat_PCM QOpenSLESEngine::audioFormatToSLFormatPCM(const QAudioFormat &f - format_pcm.bitsPerSample = format.sampleSize(); - format_pcm.containerSize = format.sampleSize(); - format_pcm.channelMask = (format.channelCount() == 1 ? -- SL_SPEAKER_FRONT_CENTER : -- SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT); -+ SL_SPEAKER_FRONT_CENTER : -+ SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT); - format_pcm.endianness = (format.byteOrder() == QAudioFormat::LittleEndian ? -- SL_BYTEORDER_LITTLEENDIAN : -- SL_BYTEORDER_BIGENDIAN); -+ SL_BYTEORDER_LITTLEENDIAN : -+ SL_BYTEORDER_BIGENDIAN); - return format_pcm; - - } -@@ -108,18 +122,24 @@ QByteArray QOpenSLESEngine::defaultDevice(QAudio::Mode mode) const - } - - QList QOpenSLESEngine::availableDevices(QAudio::Mode mode) const --{ -+{ - QList devices; - if (mode == QAudio::AudioInput) { - #ifdef ANDROID - devices << QT_ANDROID_PRESET_MIC - << QT_ANDROID_PRESET_CAMCORDER - << QT_ANDROID_PRESET_VOICE_RECOGNITION; -+#elif defined(Q_OS_OPENHARMONY) -+ devices << m_jsAudioMgr->call("availableInputDevices"); - #else - devices << "default"; - #endif - } else { -+#if defined(Q_OS_OPENHARMONY) -+ devices << m_jsAudioMgr->call("availableOutputDevices"); -+#else - devices << "default"; -+#endif - } - return devices; - } -@@ -204,7 +224,6 @@ int QOpenSLESEngine::getOutputValue(QOpenSLESEngine::OutputValue type, int defau - return sampleRate; - - #endif // Q_OS_ANDROID -- - return defaultValue; - } - -@@ -246,6 +265,8 @@ int QOpenSLESEngine::getDefaultBufferSize(const QAudioFormat &format) - channelConfig, - audioFormat); - return minBufferSize > 0 ? minBufferSize : format.bytesForDuration(DEFAULT_PERIOD_TIME_MS); -+#elif defined(Q_OS_OPENHARMONY) -+ return OHOS_OPENSLES_BUFFER; - #else - return format.bytesForDuration(DEFAULT_PERIOD_TIME_MS); - #endif // Q_OS_ANDROID -@@ -253,8 +274,12 @@ int QOpenSLESEngine::getDefaultBufferSize(const QAudioFormat &format) - - int QOpenSLESEngine::getLowLatencyBufferSize(const QAudioFormat &format) - { -+#if defined(Q_OS_OPENHARMONY) -+ return OHOS_OPENSLES_BUFFER; -+#else - return format.bytesForFrames(QOpenSLESEngine::getOutputValue(QOpenSLESEngine::FramesPerBuffer, - format.framesForDuration(MINIMUM_PERIOD_TIME_MS))); -+#endif - } - - bool QOpenSLESEngine::supportsLowLatency() -@@ -318,7 +343,7 @@ void QOpenSLESEngine::checkSupportedInputFormats() - SL_SAMPLINGRATE_48 }; - - -- // Test sampling rates -+ // Test sampling rates - for (int i = 0 ; i < 9; ++i) { - SLDataFormat_PCM format = defaultFormat; - format.samplesPerSec = rates[i]; -@@ -328,7 +353,7 @@ void QOpenSLESEngine::checkSupportedInputFormats() - - } - -- // Test if stereo is supported -+ // Test if stereo is supported - { - SLDataFormat_PCM format = defaultFormat; - format.numChannels = 2; -@@ -350,8 +375,12 @@ bool QOpenSLESEngine::inputFormatIsSupported(SLDataFormat_PCM format) - - #ifdef ANDROID - SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 }; -+#else -+#if defined(Q_OS_OPENHARMONY) -+ SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE, 0 }; - #else - SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE, 1 }; -+#endif - #endif - SLDataSink audioSnk = { &loc_bq, &format }; - -diff --git a/src/plugins/opensles/qopenslesengine.h b/src/plugins/opensles/qopenslesengine.h -index c36b21488..e45d0524a 100644 ---- a/src/plugins/opensles/qopenslesengine.h -+++ b/src/plugins/opensles/qopenslesengine.h -@@ -44,10 +44,17 @@ - #include - #include - #include -+#if defined(Q_OS_OPENHARMONY) -+#include -+#endif - #include - - QT_BEGIN_NAMESPACE - -+#if defined(Q_OS_OPENHARMONY) -+class QOpenHarmonyJsObject; -+#endif -+ - class QOpenSLESEngine - { - public: -@@ -83,6 +90,9 @@ private: - QList m_supportedInputChannelCounts; - QList m_supportedInputSampleRates; - bool m_checkedInputFormats; -+#if defined(Q_OS_OPENHARMONY) -+ QSharedPointer m_jsAudioMgr; -+#endif - }; - - QT_END_NAMESPACE -diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro -index 71b9ec6e1..c7cfe9970 100644 ---- a/src/plugins/plugins.pro -+++ b/src/plugins/plugins.pro -@@ -17,6 +17,10 @@ android { - SUBDIRS += android opensles - } - -+openharmony { -+ SUBDIRS += openharmony opensles -+} -+ - qnx { - qtConfig(mmrenderer): SUBDIRS += qnx - SUBDIRS += audiocapture -diff --git a/src/plugins/videonode/egl/qsgvideonode_egl.cpp b/src/plugins/videonode/egl/qsgvideonode_egl.cpp -index c3694f533..fbb6dc609 100644 ---- a/src/plugins/videonode/egl/qsgvideonode_egl.cpp -+++ b/src/plugins/videonode/egl/qsgvideonode_egl.cpp -@@ -40,8 +40,11 @@ - #include "qsgvideonode_egl.h" - - #include -- -+#ifdef Q_OS_OPENHARMONY -+#include -+#else - #include -+#endif - - QT_BEGIN_NAMESPACE - -diff --git a/src/src.pro b/src/src.pro -index 97a053379..501514fa0 100644 ---- a/src/src.pro -+++ b/src/src.pro -@@ -1,6 +1,7 @@ - TEMPLATE = subdirs - - SUBDIRS += multimedia -+SUBDIRS += openharmony - - include($$OUT_PWD/multimedia/qtmultimedia-config.pri) - QT_FOR_CONFIG += multimedia-private diff --git a/patch/v5.12.12/qtohextras.patch b/patch/v5.12.12/qtohextras.patch deleted file mode 100644 index f06ab8ee4ae3e95e6dd78374157024479f04d834..0000000000000000000000000000000000000000 --- a/patch/v5.12.12/qtohextras.patch +++ /dev/null @@ -1,3580 +0,0 @@ -diff --git a/.gitattributes b/.gitattributes -new file mode 100644 -index 0000000..1a045fa ---- /dev/null -+++ b/.gitattributes -@@ -0,0 +1,4 @@ -+.tag export-subst -+.gitignore export-ignore -+.gitattributes export-ignore -+.commit-template export-ignore -diff --git a/.gitignore b/.gitignore -new file mode 100644 -index 0000000..4a5200a ---- /dev/null -+++ b/.gitignore -@@ -0,0 +1,126 @@ -+# This file is used to ignore files which are generated in the Qt build system -+# ---------------------------------------------------------------------------- -+ -+callgrind.out.* -+pcviewer.cfg -+*~ -+*.a -+*.la -+*.core -+*.moc -+*.o -+*.obj -+*.orig -+*.swp -+*.rej -+*.so -+*.pbxuser -+*.mode1 -+*.mode1v3 -+*_pch.h.cpp -+*_resource.rc -+.#* -+*.*# -+core -+.qmake.cache -+.qmake.vars -+*.prl -+tags -+.DS_Store -+*.debug -+Makefile* -+*.prl -+*.app -+*.pro.user -+*.qmlproject.user -+*.gcov -+moc_*.cpp -+ui_*.h -+qrc_*.cpp -+ -+# Test generated files -+QObject.log -+tst_* -+!tst_*.* -+tst_*.log -+tst_*.debug -+tst_*~ -+ -+# xemacs temporary files -+*.flc -+ -+# Vim temporary files -+.*.swp -+ -+# Visual Studio generated files -+*.ib_pdb_index -+*.idb -+*.ilk -+*.pdb -+*.sln -+*.suo -+*.vcproj -+*vcproj.*.*.user -+*.ncb -+*.vcxproj -+*.vcxproj.filters -+*.vcxproj.user -+ -+# MinGW generated files -+*.Debug -+*.Release -+ -+# Symlinks generated by configure -+.DS_Store -+.pch -+.rcc -+*.app -+ -+ -+# Directories to ignore -+# --------------------- -+ -+debug -+include/* -+include/*/* -+lib/* -+!lib/fonts -+!lib/README -+plugins/*/* -+release -+tmp -+doc-build -+doc/html/* -+doc/qch -+doc-build -+.rcc -+.pch -+.metadata -+ -+# runonphone crash dumps -+d_exc_*.txt -+d_exc_*.stk -+ -+# Generated by abldfast.bat from devtools. -+.abldsteps.* -+ -+# Carbide project files -+# --------------------- -+.project -+.cproject -+.make.cache -+*.d -+ -+qtc-debugging-helper -+ -+.pc/ -+ -+# INTEGRITY generated files -+*.gpj -+*.int -+*.ael -+*.dla -+*.dnm -+*.dep -+*.map -+work -diff --git a/.qmake.conf b/.qmake.conf -new file mode 100644 -index 0000000..3c57407 ---- /dev/null -+++ b/.qmake.conf -@@ -0,0 +1,5 @@ -+load(qt_build_config) -+ -+DEFINES += QT_NO_FOREACH -+ -+MODULE_VERSION = 5.12.12 -diff --git a/.qmake.stash b/.qmake.stash -new file mode 100644 -index 0000000..f3fcc3d ---- /dev/null -+++ b/.qmake.stash -@@ -0,0 +1,23 @@ -+QMAKE_CXX.INCDIRS = \ -+ C:/Qt/Qt5.9.8/Tools/mingw530_32/lib/gcc/i686-w64-mingw32/5.3.0/include \ -+ C:/Qt/Qt5.9.8/Tools/mingw530_32/lib/gcc/i686-w64-mingw32/5.3.0/include-fixed \ -+ C:/Qt/Qt5.9.8/Tools/mingw530_32/i686-w64-mingw32/include \ -+ C:/Qt/Qt5.9.8/Tools/mingw530_32/i686-w64-mingw32/include/c++ \ -+ C:/Qt/Qt5.9.8/Tools/mingw530_32/i686-w64-mingw32/include/c++/i686-w64-mingw32 \ -+ C:/Qt/Qt5.9.8/Tools/mingw530_32/i686-w64-mingw32/include/c++/backward -+QMAKE_CXX.LIBDIRS = \ -+ C \ -+ /Qt/Qt5.9.8/Tools/mingw530_32/lib/gcc/i686-w64-mingw32/5.3.0/;C \ -+ /Qt/Qt5.9.8/Tools/mingw530_32/lib/gcc/;C \ -+ /Qt/Qt5.9.8/Tools/mingw530_32/i686-w64-mingw32/lib/;C \ -+ /Qt/Qt5.9.8/Tools/mingw530_32/lib/;C \ -+ /Qt/Qt5.9.8/Tools/mingw530_32/lib -+QMAKE_CXX.QT_COMPILER_STDCXX = 199711L -+QMAKE_CXX.QMAKE_GCC_MAJOR_VERSION = 5 -+QMAKE_CXX.QMAKE_GCC_MINOR_VERSION = 3 -+QMAKE_CXX.QMAKE_GCC_PATCH_VERSION = 0 -+QMAKE_CXX.COMPILER_MACROS = \ -+ QT_COMPILER_STDCXX \ -+ QMAKE_GCC_MAJOR_VERSION \ -+ QMAKE_GCC_MINOR_VERSION \ -+ QMAKE_GCC_PATCH_VERSION -diff --git a/.tag b/.tag -new file mode 100644 -index 0000000..6828f88 ---- /dev/null -+++ b/.tag -@@ -0,0 +1 @@ -+$Format:%H$ -diff --git a/LICENSE.FDL b/LICENSE.FDL -new file mode 100644 -index 0000000..938bb8d ---- /dev/null -+++ b/LICENSE.FDL -@@ -0,0 +1,450 @@ -+ GNU Free Documentation License -+ Version 1.3, 3 November 2008 -+ -+ -+ Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. -+ -+ Everyone is permitted to copy and distribute verbatim copies -+ of this license document, but changing it is not allowed. -+ -+0. PREAMBLE -+ -+The purpose of this License is to make a manual, textbook, or other -+functional and useful document "free" in the sense of freedom: to -+assure everyone the effective freedom to copy and redistribute it, -+with or without modifying it, either commercially or noncommercially. -+Secondarily, this License preserves for the author and publisher a way -+to get credit for their work, while not being considered responsible -+for modifications made by others. -+ -+This License is a kind of "copyleft", which means that derivative -+works of the document must themselves be free in the same sense. It -+complements the GNU General Public License, which is a copyleft -+license designed for free software. -+ -+We have designed this License in order to use it for manuals for free -+software, because free software needs free documentation: a free -+program should come with manuals providing the same freedoms that the -+software does. But this License is not limited to software manuals; -+it can be used for any textual work, regardless of subject matter or -+whether it is published as a printed book. We recommend this License -+principally for works whose purpose is instruction or reference. -+ -+ -+1. APPLICABILITY AND DEFINITIONS -+ -+This License applies to any manual or other work, in any medium, that -+contains a notice placed by the copyright holder saying it can be -+distributed under the terms of this License. Such a notice grants a -+world-wide, royalty-free license, unlimited in duration, to use that -+work under the conditions stated herein. The "Document", below, -+refers to any such manual or work. Any member of the public is a -+licensee, and is addressed as "you". You accept the license if you -+copy, modify or distribute the work in a way requiring permission -+under copyright law. -+ -+A "Modified Version" of the Document means any work containing the -+Document or a portion of it, either copied verbatim, or with -+modifications and/or translated into another language. -+ -+A "Secondary Section" is a named appendix or a front-matter section of -+the Document that deals exclusively with the relationship of the -+publishers or authors of the Document to the Document's overall -+subject (or to related matters) and contains nothing that could fall -+directly within that overall subject. (Thus, if the Document is in -+part a textbook of mathematics, a Secondary Section may not explain -+any mathematics.) The relationship could be a matter of historical -+connection with the subject or with related matters, or of legal, -+commercial, philosophical, ethical or political position regarding -+them. -+ -+The "Invariant Sections" are certain Secondary Sections whose titles -+are designated, as being those of Invariant Sections, in the notice -+that says that the Document is released under this License. If a -+section does not fit the above definition of Secondary then it is not -+allowed to be designated as Invariant. The Document may contain zero -+Invariant Sections. If the Document does not identify any Invariant -+Sections then there are none. -+ -+The "Cover Texts" are certain short passages of text that are listed, -+as Front-Cover Texts or Back-Cover Texts, in the notice that says that -+the Document is released under this License. A Front-Cover Text may -+be at most 5 words, and a Back-Cover Text may be at most 25 words. -+ -+A "Transparent" copy of the Document means a machine-readable copy, -+represented in a format whose specification is available to the -+general public, that is suitable for revising the document -+straightforwardly with generic text editors or (for images composed of -+pixels) generic paint programs or (for drawings) some widely available -+drawing editor, and that is suitable for input to text formatters or -+for automatic translation to a variety of formats suitable for input -+to text formatters. A copy made in an otherwise Transparent file -+format whose markup, or absence of markup, has been arranged to thwart -+or discourage subsequent modification by readers is not Transparent. -+An image format is not Transparent if used for any substantial amount -+of text. A copy that is not "Transparent" is called "Opaque". -+ -+Examples of suitable formats for Transparent copies include plain -+ASCII without markup, Texinfo input format, LaTeX input format, SGML -+or XML using a publicly available DTD, and standard-conforming simple -+HTML, PostScript or PDF designed for human modification. Examples of -+transparent image formats include PNG, XCF and JPG. Opaque formats -+include proprietary formats that can be read and edited only by -+proprietary word processors, SGML or XML for which the DTD and/or -+processing tools are not generally available, and the -+machine-generated HTML, PostScript or PDF produced by some word -+processors for output purposes only. -+ -+The "Title Page" means, for a printed book, the title page itself, -+plus such following pages as are needed to hold, legibly, the material -+this License requires to appear in the title page. For works in -+formats which do not have any title page as such, "Title Page" means -+the text near the most prominent appearance of the work's title, -+preceding the beginning of the body of the text. -+ -+The "publisher" means any person or entity that distributes copies of -+the Document to the public. -+ -+A section "Entitled XYZ" means a named subunit of the Document whose -+title either is precisely XYZ or contains XYZ in parentheses following -+text that translates XYZ in another language. (Here XYZ stands for a -+specific section name mentioned below, such as "Acknowledgements", -+"Dedications", "Endorsements", or "History".) To "Preserve the Title" -+of such a section when you modify the Document means that it remains a -+section "Entitled XYZ" according to this definition. -+ -+The Document may include Warranty Disclaimers next to the notice which -+states that this License applies to the Document. These Warranty -+Disclaimers are considered to be included by reference in this -+License, but only as regards disclaiming warranties: any other -+implication that these Warranty Disclaimers may have is void and has -+no effect on the meaning of this License. -+ -+2. VERBATIM COPYING -+ -+You may copy and distribute the Document in any medium, either -+commercially or noncommercially, provided that this License, the -+copyright notices, and the license notice saying this License applies -+to the Document are reproduced in all copies, and that you add no -+other conditions whatsoever to those of this License. You may not use -+technical measures to obstruct or control the reading or further -+copying of the copies you make or distribute. However, you may accept -+compensation in exchange for copies. If you distribute a large enough -+number of copies you must also follow the conditions in section 3. -+ -+You may also lend copies, under the same conditions stated above, and -+you may publicly display copies. -+ -+ -+3. COPYING IN QUANTITY -+ -+If you publish printed copies (or copies in media that commonly have -+printed covers) of the Document, numbering more than 100, and the -+Document's license notice requires Cover Texts, you must enclose the -+copies in covers that carry, clearly and legibly, all these Cover -+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on -+the back cover. Both covers must also clearly and legibly identify -+you as the publisher of these copies. The front cover must present -+the full title with all words of the title equally prominent and -+visible. You may add other material on the covers in addition. -+Copying with changes limited to the covers, as long as they preserve -+the title of the Document and satisfy these conditions, can be treated -+as verbatim copying in other respects. -+ -+If the required texts for either cover are too voluminous to fit -+legibly, you should put the first ones listed (as many as fit -+reasonably) on the actual cover, and continue the rest onto adjacent -+pages. -+ -+If you publish or distribute Opaque copies of the Document numbering -+more than 100, you must either include a machine-readable Transparent -+copy along with each Opaque copy, or state in or with each Opaque copy -+a computer-network location from which the general network-using -+public has access to download using public-standard network protocols -+a complete Transparent copy of the Document, free of added material. -+If you use the latter option, you must take reasonably prudent steps, -+when you begin distribution of Opaque copies in quantity, to ensure -+that this Transparent copy will remain thus accessible at the stated -+location until at least one year after the last time you distribute an -+Opaque copy (directly or through your agents or retailers) of that -+edition to the public. -+ -+It is requested, but not required, that you contact the authors of the -+Document well before redistributing any large number of copies, to -+give them a chance to provide you with an updated version of the -+Document. -+ -+ -+4. MODIFICATIONS -+ -+You may copy and distribute a Modified Version of the Document under -+the conditions of sections 2 and 3 above, provided that you release -+the Modified Version under precisely this License, with the Modified -+Version filling the role of the Document, thus licensing distribution -+and modification of the Modified Version to whoever possesses a copy -+of it. In addition, you must do these things in the Modified Version: -+ -+A. Use in the Title Page (and on the covers, if any) a title distinct -+ from that of the Document, and from those of previous versions -+ (which should, if there were any, be listed in the History section -+ of the Document). You may use the same title as a previous version -+ if the original publisher of that version gives permission. -+B. List on the Title Page, as authors, one or more persons or entities -+ responsible for authorship of the modifications in the Modified -+ Version, together with at least five of the principal authors of the -+ Document (all of its principal authors, if it has fewer than five), -+ unless they release you from this requirement. -+C. State on the Title page the name of the publisher of the -+ Modified Version, as the publisher. -+D. Preserve all the copyright notices of the Document. -+E. Add an appropriate copyright notice for your modifications -+ adjacent to the other copyright notices. -+F. Include, immediately after the copyright notices, a license notice -+ giving the public permission to use the Modified Version under the -+ terms of this License, in the form shown in the Addendum below. -+G. Preserve in that license notice the full lists of Invariant Sections -+ and required Cover Texts given in the Document's license notice. -+H. Include an unaltered copy of this License. -+I. Preserve the section Entitled "History", Preserve its Title, and add -+ to it an item stating at least the title, year, new authors, and -+ publisher of the Modified Version as given on the Title Page. If -+ there is no section Entitled "History" in the Document, create one -+ stating the title, year, authors, and publisher of the Document as -+ given on its Title Page, then add an item describing the Modified -+ Version as stated in the previous sentence. -+J. Preserve the network location, if any, given in the Document for -+ public access to a Transparent copy of the Document, and likewise -+ the network locations given in the Document for previous versions -+ it was based on. These may be placed in the "History" section. -+ You may omit a network location for a work that was published at -+ least four years before the Document itself, or if the original -+ publisher of the version it refers to gives permission. -+K. For any section Entitled "Acknowledgements" or "Dedications", -+ Preserve the Title of the section, and preserve in the section all -+ the substance and tone of each of the contributor acknowledgements -+ and/or dedications given therein. -+L. Preserve all the Invariant Sections of the Document, -+ unaltered in their text and in their titles. Section numbers -+ or the equivalent are not considered part of the section titles. -+M. Delete any section Entitled "Endorsements". Such a section -+ may not be included in the Modified Version. -+N. Do not retitle any existing section to be Entitled "Endorsements" -+ or to conflict in title with any Invariant Section. -+O. Preserve any Warranty Disclaimers. -+ -+If the Modified Version includes new front-matter sections or -+appendices that qualify as Secondary Sections and contain no material -+copied from the Document, you may at your option designate some or all -+of these sections as invariant. To do this, add their titles to the -+list of Invariant Sections in the Modified Version's license notice. -+These titles must be distinct from any other section titles. -+ -+You may add a section Entitled "Endorsements", provided it contains -+nothing but endorsements of your Modified Version by various -+parties--for example, statements of peer review or that the text has -+been approved by an organization as the authoritative definition of a -+standard. -+ -+You may add a passage of up to five words as a Front-Cover Text, and a -+passage of up to 25 words as a Back-Cover Text, to the end of the list -+of Cover Texts in the Modified Version. Only one passage of -+Front-Cover Text and one of Back-Cover Text may be added by (or -+through arrangements made by) any one entity. If the Document already -+includes a cover text for the same cover, previously added by you or -+by arrangement made by the same entity you are acting on behalf of, -+you may not add another; but you may replace the old one, on explicit -+permission from the previous publisher that added the old one. -+ -+The author(s) and publisher(s) of the Document do not by this License -+give permission to use their names for publicity for or to assert or -+imply endorsement of any Modified Version. -+ -+ -+5. COMBINING DOCUMENTS -+ -+You may combine the Document with other documents released under this -+License, under the terms defined in section 4 above for modified -+versions, provided that you include in the combination all of the -+Invariant Sections of all of the original documents, unmodified, and -+list them all as Invariant Sections of your combined work in its -+license notice, and that you preserve all their Warranty Disclaimers. -+ -+The combined work need only contain one copy of this License, and -+multiple identical Invariant Sections may be replaced with a single -+copy. If there are multiple Invariant Sections with the same name but -+different contents, make the title of each such section unique by -+adding at the end of it, in parentheses, the name of the original -+author or publisher of that section if known, or else a unique number. -+Make the same adjustment to the section titles in the list of -+Invariant Sections in the license notice of the combined work. -+ -+In the combination, you must combine any sections Entitled "History" -+in the various original documents, forming one section Entitled -+"History"; likewise combine any sections Entitled "Acknowledgements", -+and any sections Entitled "Dedications". You must delete all sections -+Entitled "Endorsements". -+ -+ -+6. COLLECTIONS OF DOCUMENTS -+ -+You may make a collection consisting of the Document and other -+documents released under this License, and replace the individual -+copies of this License in the various documents with a single copy -+that is included in the collection, provided that you follow the rules -+of this License for verbatim copying of each of the documents in all -+other respects. -+ -+You may extract a single document from such a collection, and -+distribute it individually under this License, provided you insert a -+copy of this License into the extracted document, and follow this -+License in all other respects regarding verbatim copying of that -+document. -+ -+ -+7. AGGREGATION WITH INDEPENDENT WORKS -+ -+A compilation of the Document or its derivatives with other separate -+and independent documents or works, in or on a volume of a storage or -+distribution medium, is called an "aggregate" if the copyright -+resulting from the compilation is not used to limit the legal rights -+of the compilation's users beyond what the individual works permit. -+When the Document is included in an aggregate, this License does not -+apply to the other works in the aggregate which are not themselves -+derivative works of the Document. -+ -+If the Cover Text requirement of section 3 is applicable to these -+copies of the Document, then if the Document is less than one half of -+the entire aggregate, the Document's Cover Texts may be placed on -+covers that bracket the Document within the aggregate, or the -+electronic equivalent of covers if the Document is in electronic form. -+Otherwise they must appear on printed covers that bracket the whole -+aggregate. -+ -+ -+8. TRANSLATION -+ -+Translation is considered a kind of modification, so you may -+distribute translations of the Document under the terms of section 4. -+Replacing Invariant Sections with translations requires special -+permission from their copyright holders, but you may include -+translations of some or all Invariant Sections in addition to the -+original versions of these Invariant Sections. You may include a -+translation of this License, and all the license notices in the -+Document, and any Warranty Disclaimers, provided that you also include -+the original English version of this License and the original versions -+of those notices and disclaimers. In case of a disagreement between -+the translation and the original version of this License or a notice -+or disclaimer, the original version will prevail. -+ -+If a section in the Document is Entitled "Acknowledgements", -+"Dedications", or "History", the requirement (section 4) to Preserve -+its Title (section 1) will typically require changing the actual -+title. -+ -+ -+9. TERMINATION -+ -+You may not copy, modify, sublicense, or distribute the Document -+except as expressly provided under this License. Any attempt -+otherwise to copy, modify, sublicense, or distribute it is void, and -+will automatically terminate your rights under this License. -+ -+However, if you cease all violation of this License, then your license -+from a particular copyright holder is reinstated (a) provisionally, -+unless and until the copyright holder explicitly and finally -+terminates your license, and (b) permanently, if the copyright holder -+fails to notify you of the violation by some reasonable means prior to -+60 days after the cessation. -+ -+Moreover, your license from a particular copyright holder is -+reinstated permanently if the copyright holder notifies you of the -+violation by some reasonable means, this is the first time you have -+received notice of violation of this License (for any work) from that -+copyright holder, and you cure the violation prior to 30 days after -+your receipt of the notice. -+ -+Termination of your rights under this section does not terminate the -+licenses of parties who have received copies or rights from you under -+this License. If your rights have been terminated and not permanently -+reinstated, receipt of a copy of some or all of the same material does -+not give you any rights to use it. -+ -+ -+10. FUTURE REVISIONS OF THIS LICENSE -+ -+The Free Software Foundation may publish new, revised versions of the -+GNU Free Documentation License from time to time. Such new versions -+will be similar in spirit to the present version, but may differ in -+detail to address new problems or concerns. See -+http://www.gnu.org/copyleft/. -+ -+Each version of the License is given a distinguishing version number. -+If the Document specifies that a particular numbered version of this -+License "or any later version" applies to it, you have the option of -+following the terms and conditions either of that specified version or -+of any later version that has been published (not as a draft) by the -+Free Software Foundation. If the Document does not specify a version -+number of this License, you may choose any version ever published (not -+as a draft) by the Free Software Foundation. If the Document -+specifies that a proxy can decide which future versions of this -+License can be used, that proxy's public statement of acceptance of a -+version permanently authorizes you to choose that version for the -+Document. -+ -+11. RELICENSING -+ -+"Massive Multiauthor Collaboration Site" (or "MMC Site") means any -+World Wide Web server that publishes copyrightable works and also -+provides prominent facilities for anybody to edit those works. A -+public wiki that anybody can edit is an example of such a server. A -+"Massive Multiauthor Collaboration" (or "MMC") contained in the site -+means any set of copyrightable works thus published on the MMC site. -+ -+"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 -+license published by Creative Commons Corporation, a not-for-profit -+corporation with a principal place of business in San Francisco, -+California, as well as future copyleft versions of that license -+published by that same organization. -+ -+"Incorporate" means to publish or republish a Document, in whole or in -+part, as part of another Document. -+ -+An MMC is "eligible for relicensing" if it is licensed under this -+License, and if all works that were first published under this License -+somewhere other than this MMC, and subsequently incorporated in whole or -+in part into the MMC, (1) had no cover texts or invariant sections, and -+(2) were thus incorporated prior to November 1, 2008. -+ -+The operator of an MMC Site may republish an MMC contained in the site -+under CC-BY-SA on the same site at any time before August 1, 2009, -+provided the MMC is eligible for relicensing. -+ -+ -+ADDENDUM: How to use this License for your documents -+ -+To use this License in a document you have written, include a copy of -+the License in the document and put the following copyright and -+license notices just after the title page: -+ -+ Copyright (c) YEAR YOUR NAME. -+ Permission is granted to copy, distribute and/or modify this document -+ under the terms of the GNU Free Documentation License, Version 1.3 -+ or any later version published by the Free Software Foundation; -+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -+ A copy of the license is included in the section entitled "GNU -+ Free Documentation License". -+ -+If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, -+replace the "with...Texts." line with this: -+ -+ with the Invariant Sections being LIST THEIR TITLES, with the -+ Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. -+ -+If you have Invariant Sections without Cover Texts, or some other -+combination of the three, merge those two alternatives to suit the -+situation. -+ -+If your document contains nontrivial examples of program code, we -+recommend releasing these examples in parallel under your choice of -+free software license, such as the GNU General Public License, -+to permit their use in free software. -diff --git a/LICENSE.GPL2 b/LICENSE.GPL2 -new file mode 100644 -index 0000000..d159169 ---- /dev/null -+++ b/LICENSE.GPL2 -@@ -0,0 +1,339 @@ -+ GNU GENERAL PUBLIC LICENSE -+ Version 2, June 1991 -+ -+ Copyright (C) 1989, 1991 Free Software Foundation, Inc., -+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -+ Everyone is permitted to copy and distribute verbatim copies -+ of this license document, but changing it is not allowed. -+ -+ Preamble -+ -+ The licenses for most software are designed to take away your -+freedom to share and change it. By contrast, the GNU General Public -+License is intended to guarantee your freedom to share and change free -+software--to make sure the software is free for all its users. This -+General Public License applies to most of the Free Software -+Foundation's software and to any other program whose authors commit to -+using it. (Some other Free Software Foundation software is covered by -+the GNU Lesser General Public License instead.) You can apply it to -+your programs, too. -+ -+ When we speak of free software, we are referring to freedom, not -+price. Our General Public Licenses are designed to make sure that you -+have the freedom to distribute copies of free software (and charge for -+this service if you wish), that you receive source code or can get it -+if you want it, that you can change the software or use pieces of it -+in new free programs; and that you know you can do these things. -+ -+ To protect your rights, we need to make restrictions that forbid -+anyone to deny you these rights or to ask you to surrender the rights. -+These restrictions translate to certain responsibilities for you if you -+distribute copies of the software, or if you modify it. -+ -+ For example, if you distribute copies of such a program, whether -+gratis or for a fee, you must give the recipients all the rights that -+you have. You must make sure that they, too, receive or can get the -+source code. And you must show them these terms so they know their -+rights. -+ -+ We protect your rights with two steps: (1) copyright the software, and -+(2) offer you this license which gives you legal permission to copy, -+distribute and/or modify the software. -+ -+ Also, for each author's protection and ours, we want to make certain -+that everyone understands that there is no warranty for this free -+software. If the software is modified by someone else and passed on, we -+want its recipients to know that what they have is not the original, so -+that any problems introduced by others will not reflect on the original -+authors' reputations. -+ -+ Finally, any free program is threatened constantly by software -+patents. We wish to avoid the danger that redistributors of a free -+program will individually obtain patent licenses, in effect making the -+program proprietary. To prevent this, we have made it clear that any -+patent must be licensed for everyone's free use or not licensed at all. -+ -+ The precise terms and conditions for copying, distribution and -+modification follow. -+ -+ GNU GENERAL PUBLIC LICENSE -+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -+ -+ 0. This License applies to any program or other work which contains -+a notice placed by the copyright holder saying it may be distributed -+under the terms of this General Public License. The "Program", below, -+refers to any such program or work, and a "work based on the Program" -+means either the Program or any derivative work under copyright law: -+that is to say, a work containing the Program or a portion of it, -+either verbatim or with modifications and/or translated into another -+language. (Hereinafter, translation is included without limitation in -+the term "modification".) Each licensee is addressed as "you". -+ -+Activities other than copying, distribution and modification are not -+covered by this License; they are outside its scope. The act of -+running the Program is not restricted, and the output from the Program -+is covered only if its contents constitute a work based on the -+Program (independent of having been made by running the Program). -+Whether that is true depends on what the Program does. -+ -+ 1. You may copy and distribute verbatim copies of the Program's -+source code as you receive it, in any medium, provided that you -+conspicuously and appropriately publish on each copy an appropriate -+copyright notice and disclaimer of warranty; keep intact all the -+notices that refer to this License and to the absence of any warranty; -+and give any other recipients of the Program a copy of this License -+along with the Program. -+ -+You may charge a fee for the physical act of transferring a copy, and -+you may at your option offer warranty protection in exchange for a fee. -+ -+ 2. You may modify your copy or copies of the Program or any portion -+of it, thus forming a work based on the Program, and copy and -+distribute such modifications or work under the terms of Section 1 -+above, provided that you also meet all of these conditions: -+ -+ a) You must cause the modified files to carry prominent notices -+ stating that you changed the files and the date of any change. -+ -+ b) You must cause any work that you distribute or publish, that in -+ whole or in part contains or is derived from the Program or any -+ part thereof, to be licensed as a whole at no charge to all third -+ parties under the terms of this License. -+ -+ c) If the modified program normally reads commands interactively -+ when run, you must cause it, when started running for such -+ interactive use in the most ordinary way, to print or display an -+ announcement including an appropriate copyright notice and a -+ notice that there is no warranty (or else, saying that you provide -+ a warranty) and that users may redistribute the program under -+ these conditions, and telling the user how to view a copy of this -+ License. (Exception: if the Program itself is interactive but -+ does not normally print such an announcement, your work based on -+ the Program is not required to print an announcement.) -+ -+These requirements apply to the modified work as a whole. If -+identifiable sections of that work are not derived from the Program, -+and can be reasonably considered independent and separate works in -+themselves, then this License, and its terms, do not apply to those -+sections when you distribute them as separate works. But when you -+distribute the same sections as part of a whole which is a work based -+on the Program, the distribution of the whole must be on the terms of -+this License, whose permissions for other licensees extend to the -+entire whole, and thus to each and every part regardless of who wrote it. -+ -+Thus, it is not the intent of this section to claim rights or contest -+your rights to work written entirely by you; rather, the intent is to -+exercise the right to control the distribution of derivative or -+collective works based on the Program. -+ -+In addition, mere aggregation of another work not based on the Program -+with the Program (or with a work based on the Program) on a volume of -+a storage or distribution medium does not bring the other work under -+the scope of this License. -+ -+ 3. You may copy and distribute the Program (or a work based on it, -+under Section 2) in object code or executable form under the terms of -+Sections 1 and 2 above provided that you also do one of the following: -+ -+ a) Accompany it with the complete corresponding machine-readable -+ source code, which must be distributed under the terms of Sections -+ 1 and 2 above on a medium customarily used for software interchange; or, -+ -+ b) Accompany it with a written offer, valid for at least three -+ years, to give any third party, for a charge no more than your -+ cost of physically performing source distribution, a complete -+ machine-readable copy of the corresponding source code, to be -+ distributed under the terms of Sections 1 and 2 above on a medium -+ customarily used for software interchange; or, -+ -+ c) Accompany it with the information you received as to the offer -+ to distribute corresponding source code. (This alternative is -+ allowed only for noncommercial distribution and only if you -+ received the program in object code or executable form with such -+ an offer, in accord with Subsection b above.) -+ -+The source code for a work means the preferred form of the work for -+making modifications to it. For an executable work, complete source -+code means all the source code for all modules it contains, plus any -+associated interface definition files, plus the scripts used to -+control compilation and installation of the executable. However, as a -+special exception, the source code distributed need not include -+anything that is normally distributed (in either source or binary -+form) with the major components (compiler, kernel, and so on) of the -+operating system on which the executable runs, unless that component -+itself accompanies the executable. -+ -+If distribution of executable or object code is made by offering -+access to copy from a designated place, then offering equivalent -+access to copy the source code from the same place counts as -+distribution of the source code, even though third parties are not -+compelled to copy the source along with the object code. -+ -+ 4. You may not copy, modify, sublicense, or distribute the Program -+except as expressly provided under this License. Any attempt -+otherwise to copy, modify, sublicense or distribute the Program is -+void, and will automatically terminate your rights under this License. -+However, parties who have received copies, or rights, from you under -+this License will not have their licenses terminated so long as such -+parties remain in full compliance. -+ -+ 5. You are not required to accept this License, since you have not -+signed it. However, nothing else grants you permission to modify or -+distribute the Program or its derivative works. These actions are -+prohibited by law if you do not accept this License. Therefore, by -+modifying or distributing the Program (or any work based on the -+Program), you indicate your acceptance of this License to do so, and -+all its terms and conditions for copying, distributing or modifying -+the Program or works based on it. -+ -+ 6. Each time you redistribute the Program (or any work based on the -+Program), the recipient automatically receives a license from the -+original licensor to copy, distribute or modify the Program subject to -+these terms and conditions. You may not impose any further -+restrictions on the recipients' exercise of the rights granted herein. -+You are not responsible for enforcing compliance by third parties to -+this License. -+ -+ 7. If, as a consequence of a court judgment or allegation of patent -+infringement or for any other reason (not limited to patent issues), -+conditions are imposed on you (whether by court order, agreement or -+otherwise) that contradict the conditions of this License, they do not -+excuse you from the conditions of this License. If you cannot -+distribute so as to satisfy simultaneously your obligations under this -+License and any other pertinent obligations, then as a consequence you -+may not distribute the Program at all. For example, if a patent -+license would not permit royalty-free redistribution of the Program by -+all those who receive copies directly or indirectly through you, then -+the only way you could satisfy both it and this License would be to -+refrain entirely from distribution of the Program. -+ -+If any portion of this section is held invalid or unenforceable under -+any particular circumstance, the balance of the section is intended to -+apply and the section as a whole is intended to apply in other -+circumstances. -+ -+It is not the purpose of this section to induce you to infringe any -+patents or other property right claims or to contest validity of any -+such claims; this section has the sole purpose of protecting the -+integrity of the free software distribution system, which is -+implemented by public license practices. Many people have made -+generous contributions to the wide range of software distributed -+through that system in reliance on consistent application of that -+system; it is up to the author/donor to decide if he or she is willing -+to distribute software through any other system and a licensee cannot -+impose that choice. -+ -+This section is intended to make thoroughly clear what is believed to -+be a consequence of the rest of this License. -+ -+ 8. If the distribution and/or use of the Program is restricted in -+certain countries either by patents or by copyrighted interfaces, the -+original copyright holder who places the Program under this License -+may add an explicit geographical distribution limitation excluding -+those countries, so that distribution is permitted only in or among -+countries not thus excluded. In such case, this License incorporates -+the limitation as if written in the body of this License. -+ -+ 9. The Free Software Foundation may publish revised and/or new versions -+of the General Public License from time to time. Such new versions will -+be similar in spirit to the present version, but may differ in detail to -+address new problems or concerns. -+ -+Each version is given a distinguishing version number. If the Program -+specifies a version number of this License which applies to it and "any -+later version", you have the option of following the terms and conditions -+either of that version or of any later version published by the Free -+Software Foundation. If the Program does not specify a version number of -+this License, you may choose any version ever published by the Free Software -+Foundation. -+ -+ 10. If you wish to incorporate parts of the Program into other free -+programs whose distribution conditions are different, write to the author -+to ask for permission. For software which is copyrighted by the Free -+Software Foundation, write to the Free Software Foundation; we sometimes -+make exceptions for this. Our decision will be guided by the two goals -+of preserving the free status of all derivatives of our free software and -+of promoting the sharing and reuse of software generally. -+ -+ NO WARRANTY -+ -+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -+REPAIR OR CORRECTION. -+ -+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -+POSSIBILITY OF SUCH DAMAGES. -+ -+ END OF TERMS AND CONDITIONS -+ -+ How to Apply These Terms to Your New Programs -+ -+ If you develop a new program, and you want it to be of the greatest -+possible use to the public, the best way to achieve this is to make it -+free software which everyone can redistribute and change under these terms. -+ -+ To do so, attach the following notices to the program. It is safest -+to attach them to the start of each source file to most effectively -+convey the exclusion of warranty; and each file should have at least -+the "copyright" line and a pointer to where the full notice is found. -+ -+ -+ Copyright (C) -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation; either version 2 of the License, or -+ (at your option) any later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License along -+ with this program; if not, write to the Free Software Foundation, Inc., -+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -+ -+Also add information on how to contact you by electronic and paper mail. -+ -+If the program is interactive, make it output a short notice like this -+when it starts in an interactive mode: -+ -+ Gnomovision version 69, Copyright (C) year name of author -+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. -+ This is free software, and you are welcome to redistribute it -+ under certain conditions; type `show c' for details. -+ -+The hypothetical commands `show w' and `show c' should show the appropriate -+parts of the General Public License. Of course, the commands you use may -+be called something other than `show w' and `show c'; they could even be -+mouse-clicks or menu items--whatever suits your program. -+ -+You should also get your employer (if you work as a programmer) or your -+school, if any, to sign a "copyright disclaimer" for the program, if -+necessary. Here is a sample; alter the names: -+ -+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program -+ `Gnomovision' (which makes passes at compilers) written by James Hacker. -+ -+ , 1 April 1989 -+ Ty Coon, President of Vice -+ -+This General Public License does not permit incorporating your program into -+proprietary programs. If your program is a subroutine library, you may -+consider it more useful to permit linking proprietary applications with the -+library. If this is what you want to do, use the GNU Lesser General -+Public License instead of this License. -diff --git a/LICENSE.GPL3 b/LICENSE.GPL3 -new file mode 100644 -index 0000000..94a9ed0 ---- /dev/null -+++ b/LICENSE.GPL3 -@@ -0,0 +1,674 @@ -+ GNU GENERAL PUBLIC LICENSE -+ Version 3, 29 June 2007 -+ -+ Copyright (C) 2007 Free Software Foundation, Inc. -+ Everyone is permitted to copy and distribute verbatim copies -+ of this license document, but changing it is not allowed. -+ -+ Preamble -+ -+ The GNU General Public License is a free, copyleft license for -+software and other kinds of works. -+ -+ The licenses for most software and other practical works are designed -+to take away your freedom to share and change the works. By contrast, -+the GNU General Public License is intended to guarantee your freedom to -+share and change all versions of a program--to make sure it remains free -+software for all its users. We, the Free Software Foundation, use the -+GNU General Public License for most of our software; it applies also to -+any other work released this way by its authors. You can apply it to -+your programs, too. -+ -+ When we speak of free software, we are referring to freedom, not -+price. Our General Public Licenses are designed to make sure that you -+have the freedom to distribute copies of free software (and charge for -+them if you wish), that you receive source code or can get it if you -+want it, that you can change the software or use pieces of it in new -+free programs, and that you know you can do these things. -+ -+ To protect your rights, we need to prevent others from denying you -+these rights or asking you to surrender the rights. Therefore, you have -+certain responsibilities if you distribute copies of the software, or if -+you modify it: responsibilities to respect the freedom of others. -+ -+ For example, if you distribute copies of such a program, whether -+gratis or for a fee, you must pass on to the recipients the same -+freedoms that you received. You must make sure that they, too, receive -+or can get the source code. And you must show them these terms so they -+know their rights. -+ -+ Developers that use the GNU GPL protect your rights with two steps: -+(1) assert copyright on the software, and (2) offer you this License -+giving you legal permission to copy, distribute and/or modify it. -+ -+ For the developers' and authors' protection, the GPL clearly explains -+that there is no warranty for this free software. For both users' and -+authors' sake, the GPL requires that modified versions be marked as -+changed, so that their problems will not be attributed erroneously to -+authors of previous versions. -+ -+ Some devices are designed to deny users access to install or run -+modified versions of the software inside them, although the manufacturer -+can do so. This is fundamentally incompatible with the aim of -+protecting users' freedom to change the software. The systematic -+pattern of such abuse occurs in the area of products for individuals to -+use, which is precisely where it is most unacceptable. Therefore, we -+have designed this version of the GPL to prohibit the practice for those -+products. If such problems arise substantially in other domains, we -+stand ready to extend this provision to those domains in future versions -+of the GPL, as needed to protect the freedom of users. -+ -+ Finally, every program is threatened constantly by software patents. -+States should not allow patents to restrict development and use of -+software on general-purpose computers, but in those that do, we wish to -+avoid the special danger that patents applied to a free program could -+make it effectively proprietary. To prevent this, the GPL assures that -+patents cannot be used to render the program non-free. -+ -+ The precise terms and conditions for copying, distribution and -+modification follow. -+ -+ TERMS AND CONDITIONS -+ -+ 0. Definitions. -+ -+ "This License" refers to version 3 of the GNU General Public License. -+ -+ "Copyright" also means copyright-like laws that apply to other kinds of -+works, such as semiconductor masks. -+ -+ "The Program" refers to any copyrightable work licensed under this -+License. Each licensee is addressed as "you". "Licensees" and -+"recipients" may be individuals or organizations. -+ -+ To "modify" a work means to copy from or adapt all or part of the work -+in a fashion requiring copyright permission, other than the making of an -+exact copy. The resulting work is called a "modified version" of the -+earlier work or a work "based on" the earlier work. -+ -+ A "covered work" means either the unmodified Program or a work based -+on the Program. -+ -+ To "propagate" a work means to do anything with it that, without -+permission, would make you directly or secondarily liable for -+infringement under applicable copyright law, except executing it on a -+computer or modifying a private copy. Propagation includes copying, -+distribution (with or without modification), making available to the -+public, and in some countries other activities as well. -+ -+ To "convey" a work means any kind of propagation that enables other -+parties to make or receive copies. Mere interaction with a user through -+a computer network, with no transfer of a copy, is not conveying. -+ -+ An interactive user interface displays "Appropriate Legal Notices" -+to the extent that it includes a convenient and prominently visible -+feature that (1) displays an appropriate copyright notice, and (2) -+tells the user that there is no warranty for the work (except to the -+extent that warranties are provided), that licensees may convey the -+work under this License, and how to view a copy of this License. If -+the interface presents a list of user commands or options, such as a -+menu, a prominent item in the list meets this criterion. -+ -+ 1. Source Code. -+ -+ The "source code" for a work means the preferred form of the work -+for making modifications to it. "Object code" means any non-source -+form of a work. -+ -+ A "Standard Interface" means an interface that either is an official -+standard defined by a recognized standards body, or, in the case of -+interfaces specified for a particular programming language, one that -+is widely used among developers working in that language. -+ -+ The "System Libraries" of an executable work include anything, other -+than the work as a whole, that (a) is included in the normal form of -+packaging a Major Component, but which is not part of that Major -+Component, and (b) serves only to enable use of the work with that -+Major Component, or to implement a Standard Interface for which an -+implementation is available to the public in source code form. A -+"Major Component", in this context, means a major essential component -+(kernel, window system, and so on) of the specific operating system -+(if any) on which the executable work runs, or a compiler used to -+produce the work, or an object code interpreter used to run it. -+ -+ The "Corresponding Source" for a work in object code form means all -+the source code needed to generate, install, and (for an executable -+work) run the object code and to modify the work, including scripts to -+control those activities. However, it does not include the work's -+System Libraries, or general-purpose tools or generally available free -+programs which are used unmodified in performing those activities but -+which are not part of the work. For example, Corresponding Source -+includes interface definition files associated with source files for -+the work, and the source code for shared libraries and dynamically -+linked subprograms that the work is specifically designed to require, -+such as by intimate data communication or control flow between those -+subprograms and other parts of the work. -+ -+ The Corresponding Source need not include anything that users -+can regenerate automatically from other parts of the Corresponding -+Source. -+ -+ The Corresponding Source for a work in source code form is that -+same work. -+ -+ 2. Basic Permissions. -+ -+ All rights granted under this License are granted for the term of -+copyright on the Program, and are irrevocable provided the stated -+conditions are met. This License explicitly affirms your unlimited -+permission to run the unmodified Program. The output from running a -+covered work is covered by this License only if the output, given its -+content, constitutes a covered work. This License acknowledges your -+rights of fair use or other equivalent, as provided by copyright law. -+ -+ You may make, run and propagate covered works that you do not -+convey, without conditions so long as your license otherwise remains -+in force. You may convey covered works to others for the sole purpose -+of having them make modifications exclusively for you, or provide you -+with facilities for running those works, provided that you comply with -+the terms of this License in conveying all material for which you do -+not control copyright. Those thus making or running the covered works -+for you must do so exclusively on your behalf, under your direction -+and control, on terms that prohibit them from making any copies of -+your copyrighted material outside their relationship with you. -+ -+ Conveying under any other circumstances is permitted solely under -+the conditions stated below. Sublicensing is not allowed; section 10 -+makes it unnecessary. -+ -+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law. -+ -+ No covered work shall be deemed part of an effective technological -+measure under any applicable law fulfilling obligations under article -+11 of the WIPO copyright treaty adopted on 20 December 1996, or -+similar laws prohibiting or restricting circumvention of such -+measures. -+ -+ When you convey a covered work, you waive any legal power to forbid -+circumvention of technological measures to the extent such circumvention -+is effected by exercising rights under this License with respect to -+the covered work, and you disclaim any intention to limit operation or -+modification of the work as a means of enforcing, against the work's -+users, your or third parties' legal rights to forbid circumvention of -+technological measures. -+ -+ 4. Conveying Verbatim Copies. -+ -+ You may convey verbatim copies of the Program's source code as you -+receive it, in any medium, provided that you conspicuously and -+appropriately publish on each copy an appropriate copyright notice; -+keep intact all notices stating that this License and any -+non-permissive terms added in accord with section 7 apply to the code; -+keep intact all notices of the absence of any warranty; and give all -+recipients a copy of this License along with the Program. -+ -+ You may charge any price or no price for each copy that you convey, -+and you may offer support or warranty protection for a fee. -+ -+ 5. Conveying Modified Source Versions. -+ -+ You may convey a work based on the Program, or the modifications to -+produce it from the Program, in the form of source code under the -+terms of section 4, provided that you also meet all of these conditions: -+ -+ a) The work must carry prominent notices stating that you modified -+ it, and giving a relevant date. -+ -+ b) The work must carry prominent notices stating that it is -+ released under this License and any conditions added under section -+ 7. This requirement modifies the requirement in section 4 to -+ "keep intact all notices". -+ -+ c) You must license the entire work, as a whole, under this -+ License to anyone who comes into possession of a copy. This -+ License will therefore apply, along with any applicable section 7 -+ additional terms, to the whole of the work, and all its parts, -+ regardless of how they are packaged. This License gives no -+ permission to license the work in any other way, but it does not -+ invalidate such permission if you have separately received it. -+ -+ d) If the work has interactive user interfaces, each must display -+ Appropriate Legal Notices; however, if the Program has interactive -+ interfaces that do not display Appropriate Legal Notices, your -+ work need not make them do so. -+ -+ A compilation of a covered work with other separate and independent -+works, which are not by their nature extensions of the covered work, -+and which are not combined with it such as to form a larger program, -+in or on a volume of a storage or distribution medium, is called an -+"aggregate" if the compilation and its resulting copyright are not -+used to limit the access or legal rights of the compilation's users -+beyond what the individual works permit. Inclusion of a covered work -+in an aggregate does not cause this License to apply to the other -+parts of the aggregate. -+ -+ 6. Conveying Non-Source Forms. -+ -+ You may convey a covered work in object code form under the terms -+of sections 4 and 5, provided that you also convey the -+machine-readable Corresponding Source under the terms of this License, -+in one of these ways: -+ -+ a) Convey the object code in, or embodied in, a physical product -+ (including a physical distribution medium), accompanied by the -+ Corresponding Source fixed on a durable physical medium -+ customarily used for software interchange. -+ -+ b) Convey the object code in, or embodied in, a physical product -+ (including a physical distribution medium), accompanied by a -+ written offer, valid for at least three years and valid for as -+ long as you offer spare parts or customer support for that product -+ model, to give anyone who possesses the object code either (1) a -+ copy of the Corresponding Source for all the software in the -+ product that is covered by this License, on a durable physical -+ medium customarily used for software interchange, for a price no -+ more than your reasonable cost of physically performing this -+ conveying of source, or (2) access to copy the -+ Corresponding Source from a network server at no charge. -+ -+ c) Convey individual copies of the object code with a copy of the -+ written offer to provide the Corresponding Source. This -+ alternative is allowed only occasionally and noncommercially, and -+ only if you received the object code with such an offer, in accord -+ with subsection 6b. -+ -+ d) Convey the object code by offering access from a designated -+ place (gratis or for a charge), and offer equivalent access to the -+ Corresponding Source in the same way through the same place at no -+ further charge. You need not require recipients to copy the -+ Corresponding Source along with the object code. If the place to -+ copy the object code is a network server, the Corresponding Source -+ may be on a different server (operated by you or a third party) -+ that supports equivalent copying facilities, provided you maintain -+ clear directions next to the object code saying where to find the -+ Corresponding Source. Regardless of what server hosts the -+ Corresponding Source, you remain obligated to ensure that it is -+ available for as long as needed to satisfy these requirements. -+ -+ e) Convey the object code using peer-to-peer transmission, provided -+ you inform other peers where the object code and Corresponding -+ Source of the work are being offered to the general public at no -+ charge under subsection 6d. -+ -+ A separable portion of the object code, whose source code is excluded -+from the Corresponding Source as a System Library, need not be -+included in conveying the object code work. -+ -+ A "User Product" is either (1) a "consumer product", which means any -+tangible personal property which is normally used for personal, family, -+or household purposes, or (2) anything designed or sold for incorporation -+into a dwelling. In determining whether a product is a consumer product, -+doubtful cases shall be resolved in favor of coverage. For a particular -+product received by a particular user, "normally used" refers to a -+typical or common use of that class of product, regardless of the status -+of the particular user or of the way in which the particular user -+actually uses, or expects or is expected to use, the product. A product -+is a consumer product regardless of whether the product has substantial -+commercial, industrial or non-consumer uses, unless such uses represent -+the only significant mode of use of the product. -+ -+ "Installation Information" for a User Product means any methods, -+procedures, authorization keys, or other information required to install -+and execute modified versions of a covered work in that User Product from -+a modified version of its Corresponding Source. The information must -+suffice to ensure that the continued functioning of the modified object -+code is in no case prevented or interfered with solely because -+modification has been made. -+ -+ If you convey an object code work under this section in, or with, or -+specifically for use in, a User Product, and the conveying occurs as -+part of a transaction in which the right of possession and use of the -+User Product is transferred to the recipient in perpetuity or for a -+fixed term (regardless of how the transaction is characterized), the -+Corresponding Source conveyed under this section must be accompanied -+by the Installation Information. But this requirement does not apply -+if neither you nor any third party retains the ability to install -+modified object code on the User Product (for example, the work has -+been installed in ROM). -+ -+ The requirement to provide Installation Information does not include a -+requirement to continue to provide support service, warranty, or updates -+for a work that has been modified or installed by the recipient, or for -+the User Product in which it has been modified or installed. Access to a -+network may be denied when the modification itself materially and -+adversely affects the operation of the network or violates the rules and -+protocols for communication across the network. -+ -+ Corresponding Source conveyed, and Installation Information provided, -+in accord with this section must be in a format that is publicly -+documented (and with an implementation available to the public in -+source code form), and must require no special password or key for -+unpacking, reading or copying. -+ -+ 7. Additional Terms. -+ -+ "Additional permissions" are terms that supplement the terms of this -+License by making exceptions from one or more of its conditions. -+Additional permissions that are applicable to the entire Program shall -+be treated as though they were included in this License, to the extent -+that they are valid under applicable law. If additional permissions -+apply only to part of the Program, that part may be used separately -+under those permissions, but the entire Program remains governed by -+this License without regard to the additional permissions. -+ -+ When you convey a copy of a covered work, you may at your option -+remove any additional permissions from that copy, or from any part of -+it. (Additional permissions may be written to require their own -+removal in certain cases when you modify the work.) You may place -+additional permissions on material, added by you to a covered work, -+for which you have or can give appropriate copyright permission. -+ -+ Notwithstanding any other provision of this License, for material you -+add to a covered work, you may (if authorized by the copyright holders of -+that material) supplement the terms of this License with terms: -+ -+ a) Disclaiming warranty or limiting liability differently from the -+ terms of sections 15 and 16 of this License; or -+ -+ b) Requiring preservation of specified reasonable legal notices or -+ author attributions in that material or in the Appropriate Legal -+ Notices displayed by works containing it; or -+ -+ c) Prohibiting misrepresentation of the origin of that material, or -+ requiring that modified versions of such material be marked in -+ reasonable ways as different from the original version; or -+ -+ d) Limiting the use for publicity purposes of names of licensors or -+ authors of the material; or -+ -+ e) Declining to grant rights under trademark law for use of some -+ trade names, trademarks, or service marks; or -+ -+ f) Requiring indemnification of licensors and authors of that -+ material by anyone who conveys the material (or modified versions of -+ it) with contractual assumptions of liability to the recipient, for -+ any liability that these contractual assumptions directly impose on -+ those licensors and authors. -+ -+ All other non-permissive additional terms are considered "further -+restrictions" within the meaning of section 10. If the Program as you -+received it, or any part of it, contains a notice stating that it is -+governed by this License along with a term that is a further -+restriction, you may remove that term. If a license document contains -+a further restriction but permits relicensing or conveying under this -+License, you may add to a covered work material governed by the terms -+of that license document, provided that the further restriction does -+not survive such relicensing or conveying. -+ -+ If you add terms to a covered work in accord with this section, you -+must place, in the relevant source files, a statement of the -+additional terms that apply to those files, or a notice indicating -+where to find the applicable terms. -+ -+ Additional terms, permissive or non-permissive, may be stated in the -+form of a separately written license, or stated as exceptions; -+the above requirements apply either way. -+ -+ 8. Termination. -+ -+ You may not propagate or modify a covered work except as expressly -+provided under this License. Any attempt otherwise to propagate or -+modify it is void, and will automatically terminate your rights under -+this License (including any patent licenses granted under the third -+paragraph of section 11). -+ -+ However, if you cease all violation of this License, then your -+license from a particular copyright holder is reinstated (a) -+provisionally, unless and until the copyright holder explicitly and -+finally terminates your license, and (b) permanently, if the copyright -+holder fails to notify you of the violation by some reasonable means -+prior to 60 days after the cessation. -+ -+ Moreover, your license from a particular copyright holder is -+reinstated permanently if the copyright holder notifies you of the -+violation by some reasonable means, this is the first time you have -+received notice of violation of this License (for any work) from that -+copyright holder, and you cure the violation prior to 30 days after -+your receipt of the notice. -+ -+ Termination of your rights under this section does not terminate the -+licenses of parties who have received copies or rights from you under -+this License. If your rights have been terminated and not permanently -+reinstated, you do not qualify to receive new licenses for the same -+material under section 10. -+ -+ 9. Acceptance Not Required for Having Copies. -+ -+ You are not required to accept this License in order to receive or -+run a copy of the Program. Ancillary propagation of a covered work -+occurring solely as a consequence of using peer-to-peer transmission -+to receive a copy likewise does not require acceptance. However, -+nothing other than this License grants you permission to propagate or -+modify any covered work. These actions infringe copyright if you do -+not accept this License. Therefore, by modifying or propagating a -+covered work, you indicate your acceptance of this License to do so. -+ -+ 10. Automatic Licensing of Downstream Recipients. -+ -+ Each time you convey a covered work, the recipient automatically -+receives a license from the original licensors, to run, modify and -+propagate that work, subject to this License. You are not responsible -+for enforcing compliance by third parties with this License. -+ -+ An "entity transaction" is a transaction transferring control of an -+organization, or substantially all assets of one, or subdividing an -+organization, or merging organizations. If propagation of a covered -+work results from an entity transaction, each party to that -+transaction who receives a copy of the work also receives whatever -+licenses to the work the party's predecessor in interest had or could -+give under the previous paragraph, plus a right to possession of the -+Corresponding Source of the work from the predecessor in interest, if -+the predecessor has it or can get it with reasonable efforts. -+ -+ You may not impose any further restrictions on the exercise of the -+rights granted or affirmed under this License. For example, you may -+not impose a license fee, royalty, or other charge for exercise of -+rights granted under this License, and you may not initiate litigation -+(including a cross-claim or counterclaim in a lawsuit) alleging that -+any patent claim is infringed by making, using, selling, offering for -+sale, or importing the Program or any portion of it. -+ -+ 11. Patents. -+ -+ A "contributor" is a copyright holder who authorizes use under this -+License of the Program or a work on which the Program is based. The -+work thus licensed is called the contributor's "contributor version". -+ -+ A contributor's "essential patent claims" are all patent claims -+owned or controlled by the contributor, whether already acquired or -+hereafter acquired, that would be infringed by some manner, permitted -+by this License, of making, using, or selling its contributor version, -+but do not include claims that would be infringed only as a -+consequence of further modification of the contributor version. For -+purposes of this definition, "control" includes the right to grant -+patent sublicenses in a manner consistent with the requirements of -+this License. -+ -+ Each contributor grants you a non-exclusive, worldwide, royalty-free -+patent license under the contributor's essential patent claims, to -+make, use, sell, offer for sale, import and otherwise run, modify and -+propagate the contents of its contributor version. -+ -+ In the following three paragraphs, a "patent license" is any express -+agreement or commitment, however denominated, not to enforce a patent -+(such as an express permission to practice a patent or covenant not to -+sue for patent infringement). To "grant" such a patent license to a -+party means to make such an agreement or commitment not to enforce a -+patent against the party. -+ -+ If you convey a covered work, knowingly relying on a patent license, -+and the Corresponding Source of the work is not available for anyone -+to copy, free of charge and under the terms of this License, through a -+publicly available network server or other readily accessible means, -+then you must either (1) cause the Corresponding Source to be so -+available, or (2) arrange to deprive yourself of the benefit of the -+patent license for this particular work, or (3) arrange, in a manner -+consistent with the requirements of this License, to extend the patent -+license to downstream recipients. "Knowingly relying" means you have -+actual knowledge that, but for the patent license, your conveying the -+covered work in a country, or your recipient's use of the covered work -+in a country, would infringe one or more identifiable patents in that -+country that you have reason to believe are valid. -+ -+ If, pursuant to or in connection with a single transaction or -+arrangement, you convey, or propagate by procuring conveyance of, a -+covered work, and grant a patent license to some of the parties -+receiving the covered work authorizing them to use, propagate, modify -+or convey a specific copy of the covered work, then the patent license -+you grant is automatically extended to all recipients of the covered -+work and works based on it. -+ -+ A patent license is "discriminatory" if it does not include within -+the scope of its coverage, prohibits the exercise of, or is -+conditioned on the non-exercise of one or more of the rights that are -+specifically granted under this License. You may not convey a covered -+work if you are a party to an arrangement with a third party that is -+in the business of distributing software, under which you make payment -+to the third party based on the extent of your activity of conveying -+the work, and under which the third party grants, to any of the -+parties who would receive the covered work from you, a discriminatory -+patent license (a) in connection with copies of the covered work -+conveyed by you (or copies made from those copies), or (b) primarily -+for and in connection with specific products or compilations that -+contain the covered work, unless you entered into that arrangement, -+or that patent license was granted, prior to 28 March 2007. -+ -+ Nothing in this License shall be construed as excluding or limiting -+any implied license or other defenses to infringement that may -+otherwise be available to you under applicable patent law. -+ -+ 12. No Surrender of Others' Freedom. -+ -+ If conditions are imposed on you (whether by court order, agreement or -+otherwise) that contradict the conditions of this License, they do not -+excuse you from the conditions of this License. If you cannot convey a -+covered work so as to satisfy simultaneously your obligations under this -+License and any other pertinent obligations, then as a consequence you may -+not convey it at all. For example, if you agree to terms that obligate you -+to collect a royalty for further conveying from those to whom you convey -+the Program, the only way you could satisfy both those terms and this -+License would be to refrain entirely from conveying the Program. -+ -+ 13. Use with the GNU Affero General Public License. -+ -+ Notwithstanding any other provision of this License, you have -+permission to link or combine any covered work with a work licensed -+under version 3 of the GNU Affero General Public License into a single -+combined work, and to convey the resulting work. The terms of this -+License will continue to apply to the part which is the covered work, -+but the special requirements of the GNU Affero General Public License, -+section 13, concerning interaction through a network will apply to the -+combination as such. -+ -+ 14. Revised Versions of this License. -+ -+ The Free Software Foundation may publish revised and/or new versions of -+the GNU General Public License from time to time. Such new versions will -+be similar in spirit to the present version, but may differ in detail to -+address new problems or concerns. -+ -+ Each version is given a distinguishing version number. If the -+Program specifies that a certain numbered version of the GNU General -+Public License "or any later version" applies to it, you have the -+option of following the terms and conditions either of that numbered -+version or of any later version published by the Free Software -+Foundation. If the Program does not specify a version number of the -+GNU General Public License, you may choose any version ever published -+by the Free Software Foundation. -+ -+ If the Program specifies that a proxy can decide which future -+versions of the GNU General Public License can be used, that proxy's -+public statement of acceptance of a version permanently authorizes you -+to choose that version for the Program. -+ -+ Later license versions may give you additional or different -+permissions. However, no additional obligations are imposed on any -+author or copyright holder as a result of your choosing to follow a -+later version. -+ -+ 15. Disclaimer of Warranty. -+ -+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -+ALL NECESSARY SERVICING, REPAIR OR CORRECTION. -+ -+ 16. Limitation of Liability. -+ -+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -+SUCH DAMAGES. -+ -+ 17. Interpretation of Sections 15 and 16. -+ -+ If the disclaimer of warranty and limitation of liability provided -+above cannot be given local legal effect according to their terms, -+reviewing courts shall apply local law that most closely approximates -+an absolute waiver of all civil liability in connection with the -+Program, unless a warranty or assumption of liability accompanies a -+copy of the Program in return for a fee. -+ -+ END OF TERMS AND CONDITIONS -+ -+ How to Apply These Terms to Your New Programs -+ -+ If you develop a new program, and you want it to be of the greatest -+possible use to the public, the best way to achieve this is to make it -+free software which everyone can redistribute and change under these terms. -+ -+ To do so, attach the following notices to the program. It is safest -+to attach them to the start of each source file to most effectively -+state the exclusion of warranty; and each file should have at least -+the "copyright" line and a pointer to where the full notice is found. -+ -+ -+ Copyright (C) -+ -+ This program is free software: you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation, either version 3 of the License, or -+ (at your option) any later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program. If not, see . -+ -+Also add information on how to contact you by electronic and paper mail. -+ -+ If the program does terminal interaction, make it output a short -+notice like this when it starts in an interactive mode: -+ -+ Copyright (C) -+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. -+ This is free software, and you are welcome to redistribute it -+ under certain conditions; type `show c' for details. -+ -+The hypothetical commands `show w' and `show c' should show the appropriate -+parts of the General Public License. Of course, your program's commands -+might be different; for a GUI interface, you would use an "about box". -+ -+ You should also get your employer (if you work as a programmer) or school, -+if any, to sign a "copyright disclaimer" for the program, if necessary. -+For more information on this, and how to apply and follow the GNU GPL, see -+. -+ -+ The GNU General Public License does not permit incorporating your program -+into proprietary programs. If your program is a subroutine library, you -+may consider it more useful to permit linking proprietary applications with -+the library. If this is what you want to do, use the GNU Lesser General -+Public License instead of this License. But first, please read -+. -diff --git a/LICENSE.GPL3-EXCEPT b/LICENSE.GPL3-EXCEPT -new file mode 100644 -index 0000000..b1cb1be ---- /dev/null -+++ b/LICENSE.GPL3-EXCEPT -@@ -0,0 +1,704 @@ -+This is the GNU General Public License version 3, annotated with The -+Qt Company GPL Exception 1.0: -+ -+------------------------------------------------------------------------- -+ -+The Qt Company GPL Exception 1.0 -+ -+Exception 1: -+ -+As a special exception you may create a larger work which contains the -+output of this application and distribute that work under terms of your -+choice, so long as the work is not otherwise derived from or based on -+this application and so long as the work does not in itself generate -+output that contains the output from this application in its original -+or modified form. -+ -+Exception 2: -+ -+As a special exception, you have permission to combine this application -+with Plugins licensed under the terms of your choice, to produce an -+executable, and to copy and distribute the resulting executable under -+the terms of your choice. However, the executable must be accompanied -+by a prominent notice offering all users of the executable the entire -+source code to this application, excluding the source code of the -+independent modules, but including any changes you have made to this -+application, under the terms of this license. -+ -+ -+------------------------------------------------------------------------- -+ -+ GNU GENERAL PUBLIC LICENSE -+ Version 3, 29 June 2007 -+ -+ Copyright (C) 2007 Free Software Foundation, Inc. -+ Everyone is permitted to copy and distribute verbatim copies -+ of this license document, but changing it is not allowed. -+ -+ Preamble -+ -+ The GNU General Public License is a free, copyleft license for -+software and other kinds of works. -+ -+ The licenses for most software and other practical works are designed -+to take away your freedom to share and change the works. By contrast, -+the GNU General Public License is intended to guarantee your freedom to -+share and change all versions of a program--to make sure it remains free -+software for all its users. We, the Free Software Foundation, use the -+GNU General Public License for most of our software; it applies also to -+any other work released this way by its authors. You can apply it to -+your programs, too. -+ -+ When we speak of free software, we are referring to freedom, not -+price. Our General Public Licenses are designed to make sure that you -+have the freedom to distribute copies of free software (and charge for -+them if you wish), that you receive source code or can get it if you -+want it, that you can change the software or use pieces of it in new -+free programs, and that you know you can do these things. -+ -+ To protect your rights, we need to prevent others from denying you -+these rights or asking you to surrender the rights. Therefore, you have -+certain responsibilities if you distribute copies of the software, or if -+you modify it: responsibilities to respect the freedom of others. -+ -+ For example, if you distribute copies of such a program, whether -+gratis or for a fee, you must pass on to the recipients the same -+freedoms that you received. You must make sure that they, too, receive -+or can get the source code. And you must show them these terms so they -+know their rights. -+ -+ Developers that use the GNU GPL protect your rights with two steps: -+(1) assert copyright on the software, and (2) offer you this License -+giving you legal permission to copy, distribute and/or modify it. -+ -+ For the developers' and authors' protection, the GPL clearly explains -+that there is no warranty for this free software. For both users' and -+authors' sake, the GPL requires that modified versions be marked as -+changed, so that their problems will not be attributed erroneously to -+authors of previous versions. -+ -+ Some devices are designed to deny users access to install or run -+modified versions of the software inside them, although the manufacturer -+can do so. This is fundamentally incompatible with the aim of -+protecting users' freedom to change the software. The systematic -+pattern of such abuse occurs in the area of products for individuals to -+use, which is precisely where it is most unacceptable. Therefore, we -+have designed this version of the GPL to prohibit the practice for those -+products. If such problems arise substantially in other domains, we -+stand ready to extend this provision to those domains in future versions -+of the GPL, as needed to protect the freedom of users. -+ -+ Finally, every program is threatened constantly by software patents. -+States should not allow patents to restrict development and use of -+software on general-purpose computers, but in those that do, we wish to -+avoid the special danger that patents applied to a free program could -+make it effectively proprietary. To prevent this, the GPL assures that -+patents cannot be used to render the program non-free. -+ -+ The precise terms and conditions for copying, distribution and -+modification follow. -+ -+ TERMS AND CONDITIONS -+ -+ 0. Definitions. -+ -+ "This License" refers to version 3 of the GNU General Public License. -+ -+ "Copyright" also means copyright-like laws that apply to other kinds of -+works, such as semiconductor masks. -+ -+ "The Program" refers to any copyrightable work licensed under this -+License. Each licensee is addressed as "you". "Licensees" and -+"recipients" may be individuals or organizations. -+ -+ To "modify" a work means to copy from or adapt all or part of the work -+in a fashion requiring copyright permission, other than the making of an -+exact copy. The resulting work is called a "modified version" of the -+earlier work or a work "based on" the earlier work. -+ -+ A "covered work" means either the unmodified Program or a work based -+on the Program. -+ -+ To "propagate" a work means to do anything with it that, without -+permission, would make you directly or secondarily liable for -+infringement under applicable copyright law, except executing it on a -+computer or modifying a private copy. Propagation includes copying, -+distribution (with or without modification), making available to the -+public, and in some countries other activities as well. -+ -+ To "convey" a work means any kind of propagation that enables other -+parties to make or receive copies. Mere interaction with a user through -+a computer network, with no transfer of a copy, is not conveying. -+ -+ An interactive user interface displays "Appropriate Legal Notices" -+to the extent that it includes a convenient and prominently visible -+feature that (1) displays an appropriate copyright notice, and (2) -+tells the user that there is no warranty for the work (except to the -+extent that warranties are provided), that licensees may convey the -+work under this License, and how to view a copy of this License. If -+the interface presents a list of user commands or options, such as a -+menu, a prominent item in the list meets this criterion. -+ -+ 1. Source Code. -+ -+ The "source code" for a work means the preferred form of the work -+for making modifications to it. "Object code" means any non-source -+form of a work. -+ -+ A "Standard Interface" means an interface that either is an official -+standard defined by a recognized standards body, or, in the case of -+interfaces specified for a particular programming language, one that -+is widely used among developers working in that language. -+ -+ The "System Libraries" of an executable work include anything, other -+than the work as a whole, that (a) is included in the normal form of -+packaging a Major Component, but which is not part of that Major -+Component, and (b) serves only to enable use of the work with that -+Major Component, or to implement a Standard Interface for which an -+implementation is available to the public in source code form. A -+"Major Component", in this context, means a major essential component -+(kernel, window system, and so on) of the specific operating system -+(if any) on which the executable work runs, or a compiler used to -+produce the work, or an object code interpreter used to run it. -+ -+ The "Corresponding Source" for a work in object code form means all -+the source code needed to generate, install, and (for an executable -+work) run the object code and to modify the work, including scripts to -+control those activities. However, it does not include the work's -+System Libraries, or general-purpose tools or generally available free -+programs which are used unmodified in performing those activities but -+which are not part of the work. For example, Corresponding Source -+includes interface definition files associated with source files for -+the work, and the source code for shared libraries and dynamically -+linked subprograms that the work is specifically designed to require, -+such as by intimate data communication or control flow between those -+subprograms and other parts of the work. -+ -+ The Corresponding Source need not include anything that users -+can regenerate automatically from other parts of the Corresponding -+Source. -+ -+ The Corresponding Source for a work in source code form is that -+same work. -+ -+ 2. Basic Permissions. -+ -+ All rights granted under this License are granted for the term of -+copyright on the Program, and are irrevocable provided the stated -+conditions are met. This License explicitly affirms your unlimited -+permission to run the unmodified Program. The output from running a -+covered work is covered by this License only if the output, given its -+content, constitutes a covered work. This License acknowledges your -+rights of fair use or other equivalent, as provided by copyright law. -+ -+ You may make, run and propagate covered works that you do not -+convey, without conditions so long as your license otherwise remains -+in force. You may convey covered works to others for the sole purpose -+of having them make modifications exclusively for you, or provide you -+with facilities for running those works, provided that you comply with -+the terms of this License in conveying all material for which you do -+not control copyright. Those thus making or running the covered works -+for you must do so exclusively on your behalf, under your direction -+and control, on terms that prohibit them from making any copies of -+your copyrighted material outside their relationship with you. -+ -+ Conveying under any other circumstances is permitted solely under -+the conditions stated below. Sublicensing is not allowed; section 10 -+makes it unnecessary. -+ -+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law. -+ -+ No covered work shall be deemed part of an effective technological -+measure under any applicable law fulfilling obligations under article -+11 of the WIPO copyright treaty adopted on 20 December 1996, or -+similar laws prohibiting or restricting circumvention of such -+measures. -+ -+ When you convey a covered work, you waive any legal power to forbid -+circumvention of technological measures to the extent such circumvention -+is effected by exercising rights under this License with respect to -+the covered work, and you disclaim any intention to limit operation or -+modification of the work as a means of enforcing, against the work's -+users, your or third parties' legal rights to forbid circumvention of -+technological measures. -+ -+ 4. Conveying Verbatim Copies. -+ -+ You may convey verbatim copies of the Program's source code as you -+receive it, in any medium, provided that you conspicuously and -+appropriately publish on each copy an appropriate copyright notice; -+keep intact all notices stating that this License and any -+non-permissive terms added in accord with section 7 apply to the code; -+keep intact all notices of the absence of any warranty; and give all -+recipients a copy of this License along with the Program. -+ -+ You may charge any price or no price for each copy that you convey, -+and you may offer support or warranty protection for a fee. -+ -+ 5. Conveying Modified Source Versions. -+ -+ You may convey a work based on the Program, or the modifications to -+produce it from the Program, in the form of source code under the -+terms of section 4, provided that you also meet all of these conditions: -+ -+ a) The work must carry prominent notices stating that you modified -+ it, and giving a relevant date. -+ -+ b) The work must carry prominent notices stating that it is -+ released under this License and any conditions added under section -+ 7. This requirement modifies the requirement in section 4 to -+ "keep intact all notices". -+ -+ c) You must license the entire work, as a whole, under this -+ License to anyone who comes into possession of a copy. This -+ License will therefore apply, along with any applicable section 7 -+ additional terms, to the whole of the work, and all its parts, -+ regardless of how they are packaged. This License gives no -+ permission to license the work in any other way, but it does not -+ invalidate such permission if you have separately received it. -+ -+ d) If the work has interactive user interfaces, each must display -+ Appropriate Legal Notices; however, if the Program has interactive -+ interfaces that do not display Appropriate Legal Notices, your -+ work need not make them do so. -+ -+ A compilation of a covered work with other separate and independent -+works, which are not by their nature extensions of the covered work, -+and which are not combined with it such as to form a larger program, -+in or on a volume of a storage or distribution medium, is called an -+"aggregate" if the compilation and its resulting copyright are not -+used to limit the access or legal rights of the compilation's users -+beyond what the individual works permit. Inclusion of a covered work -+in an aggregate does not cause this License to apply to the other -+parts of the aggregate. -+ -+ 6. Conveying Non-Source Forms. -+ -+ You may convey a covered work in object code form under the terms -+of sections 4 and 5, provided that you also convey the -+machine-readable Corresponding Source under the terms of this License, -+in one of these ways: -+ -+ a) Convey the object code in, or embodied in, a physical product -+ (including a physical distribution medium), accompanied by the -+ Corresponding Source fixed on a durable physical medium -+ customarily used for software interchange. -+ -+ b) Convey the object code in, or embodied in, a physical product -+ (including a physical distribution medium), accompanied by a -+ written offer, valid for at least three years and valid for as -+ long as you offer spare parts or customer support for that product -+ model, to give anyone who possesses the object code either (1) a -+ copy of the Corresponding Source for all the software in the -+ product that is covered by this License, on a durable physical -+ medium customarily used for software interchange, for a price no -+ more than your reasonable cost of physically performing this -+ conveying of source, or (2) access to copy the -+ Corresponding Source from a network server at no charge. -+ -+ c) Convey individual copies of the object code with a copy of the -+ written offer to provide the Corresponding Source. This -+ alternative is allowed only occasionally and noncommercially, and -+ only if you received the object code with such an offer, in accord -+ with subsection 6b. -+ -+ d) Convey the object code by offering access from a designated -+ place (gratis or for a charge), and offer equivalent access to the -+ Corresponding Source in the same way through the same place at no -+ further charge. You need not require recipients to copy the -+ Corresponding Source along with the object code. If the place to -+ copy the object code is a network server, the Corresponding Source -+ may be on a different server (operated by you or a third party) -+ that supports equivalent copying facilities, provided you maintain -+ clear directions next to the object code saying where to find the -+ Corresponding Source. Regardless of what server hosts the -+ Corresponding Source, you remain obligated to ensure that it is -+ available for as long as needed to satisfy these requirements. -+ -+ e) Convey the object code using peer-to-peer transmission, provided -+ you inform other peers where the object code and Corresponding -+ Source of the work are being offered to the general public at no -+ charge under subsection 6d. -+ -+ A separable portion of the object code, whose source code is excluded -+from the Corresponding Source as a System Library, need not be -+included in conveying the object code work. -+ -+ A "User Product" is either (1) a "consumer product", which means any -+tangible personal property which is normally used for personal, family, -+or household purposes, or (2) anything designed or sold for incorporation -+into a dwelling. In determining whether a product is a consumer product, -+doubtful cases shall be resolved in favor of coverage. For a particular -+product received by a particular user, "normally used" refers to a -+typical or common use of that class of product, regardless of the status -+of the particular user or of the way in which the particular user -+actually uses, or expects or is expected to use, the product. A product -+is a consumer product regardless of whether the product has substantial -+commercial, industrial or non-consumer uses, unless such uses represent -+the only significant mode of use of the product. -+ -+ "Installation Information" for a User Product means any methods, -+procedures, authorization keys, or other information required to install -+and execute modified versions of a covered work in that User Product from -+a modified version of its Corresponding Source. The information must -+suffice to ensure that the continued functioning of the modified object -+code is in no case prevented or interfered with solely because -+modification has been made. -+ -+ If you convey an object code work under this section in, or with, or -+specifically for use in, a User Product, and the conveying occurs as -+part of a transaction in which the right of possession and use of the -+User Product is transferred to the recipient in perpetuity or for a -+fixed term (regardless of how the transaction is characterized), the -+Corresponding Source conveyed under this section must be accompanied -+by the Installation Information. But this requirement does not apply -+if neither you nor any third party retains the ability to install -+modified object code on the User Product (for example, the work has -+been installed in ROM). -+ -+ The requirement to provide Installation Information does not include a -+requirement to continue to provide support service, warranty, or updates -+for a work that has been modified or installed by the recipient, or for -+the User Product in which it has been modified or installed. Access to a -+network may be denied when the modification itself materially and -+adversely affects the operation of the network or violates the rules and -+protocols for communication across the network. -+ -+ Corresponding Source conveyed, and Installation Information provided, -+in accord with this section must be in a format that is publicly -+documented (and with an implementation available to the public in -+source code form), and must require no special password or key for -+unpacking, reading or copying. -+ -+ 7. Additional Terms. -+ -+ "Additional permissions" are terms that supplement the terms of this -+License by making exceptions from one or more of its conditions. -+Additional permissions that are applicable to the entire Program shall -+be treated as though they were included in this License, to the extent -+that they are valid under applicable law. If additional permissions -+apply only to part of the Program, that part may be used separately -+under those permissions, but the entire Program remains governed by -+this License without regard to the additional permissions. -+ -+ When you convey a copy of a covered work, you may at your option -+remove any additional permissions from that copy, or from any part of -+it. (Additional permissions may be written to require their own -+removal in certain cases when you modify the work.) You may place -+additional permissions on material, added by you to a covered work, -+for which you have or can give appropriate copyright permission. -+ -+ Notwithstanding any other provision of this License, for material you -+add to a covered work, you may (if authorized by the copyright holders of -+that material) supplement the terms of this License with terms: -+ -+ a) Disclaiming warranty or limiting liability differently from the -+ terms of sections 15 and 16 of this License; or -+ -+ b) Requiring preservation of specified reasonable legal notices or -+ author attributions in that material or in the Appropriate Legal -+ Notices displayed by works containing it; or -+ -+ c) Prohibiting misrepresentation of the origin of that material, or -+ requiring that modified versions of such material be marked in -+ reasonable ways as different from the original version; or -+ -+ d) Limiting the use for publicity purposes of names of licensors or -+ authors of the material; or -+ -+ e) Declining to grant rights under trademark law for use of some -+ trade names, trademarks, or service marks; or -+ -+ f) Requiring indemnification of licensors and authors of that -+ material by anyone who conveys the material (or modified versions of -+ it) with contractual assumptions of liability to the recipient, for -+ any liability that these contractual assumptions directly impose on -+ those licensors and authors. -+ -+ All other non-permissive additional terms are considered "further -+restrictions" within the meaning of section 10. If the Program as you -+received it, or any part of it, contains a notice stating that it is -+governed by this License along with a term that is a further -+restriction, you may remove that term. If a license document contains -+a further restriction but permits relicensing or conveying under this -+License, you may add to a covered work material governed by the terms -+of that license document, provided that the further restriction does -+not survive such relicensing or conveying. -+ -+ If you add terms to a covered work in accord with this section, you -+must place, in the relevant source files, a statement of the -+additional terms that apply to those files, or a notice indicating -+where to find the applicable terms. -+ -+ Additional terms, permissive or non-permissive, may be stated in the -+form of a separately written license, or stated as exceptions; -+the above requirements apply either way. -+ -+ 8. Termination. -+ -+ You may not propagate or modify a covered work except as expressly -+provided under this License. Any attempt otherwise to propagate or -+modify it is void, and will automatically terminate your rights under -+this License (including any patent licenses granted under the third -+paragraph of section 11). -+ -+ However, if you cease all violation of this License, then your -+license from a particular copyright holder is reinstated (a) -+provisionally, unless and until the copyright holder explicitly and -+finally terminates your license, and (b) permanently, if the copyright -+holder fails to notify you of the violation by some reasonable means -+prior to 60 days after the cessation. -+ -+ Moreover, your license from a particular copyright holder is -+reinstated permanently if the copyright holder notifies you of the -+violation by some reasonable means, this is the first time you have -+received notice of violation of this License (for any work) from that -+copyright holder, and you cure the violation prior to 30 days after -+your receipt of the notice. -+ -+ Termination of your rights under this section does not terminate the -+licenses of parties who have received copies or rights from you under -+this License. If your rights have been terminated and not permanently -+reinstated, you do not qualify to receive new licenses for the same -+material under section 10. -+ -+ 9. Acceptance Not Required for Having Copies. -+ -+ You are not required to accept this License in order to receive or -+run a copy of the Program. Ancillary propagation of a covered work -+occurring solely as a consequence of using peer-to-peer transmission -+to receive a copy likewise does not require acceptance. However, -+nothing other than this License grants you permission to propagate or -+modify any covered work. These actions infringe copyright if you do -+not accept this License. Therefore, by modifying or propagating a -+covered work, you indicate your acceptance of this License to do so. -+ -+ 10. Automatic Licensing of Downstream Recipients. -+ -+ Each time you convey a covered work, the recipient automatically -+receives a license from the original licensors, to run, modify and -+propagate that work, subject to this License. You are not responsible -+for enforcing compliance by third parties with this License. -+ -+ An "entity transaction" is a transaction transferring control of an -+organization, or substantially all assets of one, or subdividing an -+organization, or merging organizations. If propagation of a covered -+work results from an entity transaction, each party to that -+transaction who receives a copy of the work also receives whatever -+licenses to the work the party's predecessor in interest had or could -+give under the previous paragraph, plus a right to possession of the -+Corresponding Source of the work from the predecessor in interest, if -+the predecessor has it or can get it with reasonable efforts. -+ -+ You may not impose any further restrictions on the exercise of the -+rights granted or affirmed under this License. For example, you may -+not impose a license fee, royalty, or other charge for exercise of -+rights granted under this License, and you may not initiate litigation -+(including a cross-claim or counterclaim in a lawsuit) alleging that -+any patent claim is infringed by making, using, selling, offering for -+sale, or importing the Program or any portion of it. -+ -+ 11. Patents. -+ -+ A "contributor" is a copyright holder who authorizes use under this -+License of the Program or a work on which the Program is based. The -+work thus licensed is called the contributor's "contributor version". -+ -+ A contributor's "essential patent claims" are all patent claims -+owned or controlled by the contributor, whether already acquired or -+hereafter acquired, that would be infringed by some manner, permitted -+by this License, of making, using, or selling its contributor version, -+but do not include claims that would be infringed only as a -+consequence of further modification of the contributor version. For -+purposes of this definition, "control" includes the right to grant -+patent sublicenses in a manner consistent with the requirements of -+this License. -+ -+ Each contributor grants you a non-exclusive, worldwide, royalty-free -+patent license under the contributor's essential patent claims, to -+make, use, sell, offer for sale, import and otherwise run, modify and -+propagate the contents of its contributor version. -+ -+ In the following three paragraphs, a "patent license" is any express -+agreement or commitment, however denominated, not to enforce a patent -+(such as an express permission to practice a patent or covenant not to -+sue for patent infringement). To "grant" such a patent license to a -+party means to make such an agreement or commitment not to enforce a -+patent against the party. -+ -+ If you convey a covered work, knowingly relying on a patent license, -+and the Corresponding Source of the work is not available for anyone -+to copy, free of charge and under the terms of this License, through a -+publicly available network server or other readily accessible means, -+then you must either (1) cause the Corresponding Source to be so -+available, or (2) arrange to deprive yourself of the benefit of the -+patent license for this particular work, or (3) arrange, in a manner -+consistent with the requirements of this License, to extend the patent -+license to downstream recipients. "Knowingly relying" means you have -+actual knowledge that, but for the patent license, your conveying the -+covered work in a country, or your recipient's use of the covered work -+in a country, would infringe one or more identifiable patents in that -+country that you have reason to believe are valid. -+ -+ If, pursuant to or in connection with a single transaction or -+arrangement, you convey, or propagate by procuring conveyance of, a -+covered work, and grant a patent license to some of the parties -+receiving the covered work authorizing them to use, propagate, modify -+or convey a specific copy of the covered work, then the patent license -+you grant is automatically extended to all recipients of the covered -+work and works based on it. -+ -+ A patent license is "discriminatory" if it does not include within -+the scope of its coverage, prohibits the exercise of, or is -+conditioned on the non-exercise of one or more of the rights that are -+specifically granted under this License. You may not convey a covered -+work if you are a party to an arrangement with a third party that is -+in the business of distributing software, under which you make payment -+to the third party based on the extent of your activity of conveying -+the work, and under which the third party grants, to any of the -+parties who would receive the covered work from you, a discriminatory -+patent license (a) in connection with copies of the covered work -+conveyed by you (or copies made from those copies), or (b) primarily -+for and in connection with specific products or compilations that -+contain the covered work, unless you entered into that arrangement, -+or that patent license was granted, prior to 28 March 2007. -+ -+ Nothing in this License shall be construed as excluding or limiting -+any implied license or other defenses to infringement that may -+otherwise be available to you under applicable patent law. -+ -+ 12. No Surrender of Others' Freedom. -+ -+ If conditions are imposed on you (whether by court order, agreement or -+otherwise) that contradict the conditions of this License, they do not -+excuse you from the conditions of this License. If you cannot convey a -+covered work so as to satisfy simultaneously your obligations under this -+License and any other pertinent obligations, then as a consequence you may -+not convey it at all. For example, if you agree to terms that obligate you -+to collect a royalty for further conveying from those to whom you convey -+the Program, the only way you could satisfy both those terms and this -+License would be to refrain entirely from conveying the Program. -+ -+ 13. Use with the GNU Affero General Public License. -+ -+ Notwithstanding any other provision of this License, you have -+permission to link or combine any covered work with a work licensed -+under version 3 of the GNU Affero General Public License into a single -+combined work, and to convey the resulting work. The terms of this -+License will continue to apply to the part which is the covered work, -+but the special requirements of the GNU Affero General Public License, -+section 13, concerning interaction through a network will apply to the -+combination as such. -+ -+ 14. Revised Versions of this License. -+ -+ The Free Software Foundation may publish revised and/or new versions of -+the GNU General Public License from time to time. Such new versions will -+be similar in spirit to the present version, but may differ in detail to -+address new problems or concerns. -+ -+ Each version is given a distinguishing version number. If the -+Program specifies that a certain numbered version of the GNU General -+Public License "or any later version" applies to it, you have the -+option of following the terms and conditions either of that numbered -+version or of any later version published by the Free Software -+Foundation. If the Program does not specify a version number of the -+GNU General Public License, you may choose any version ever published -+by the Free Software Foundation. -+ -+ If the Program specifies that a proxy can decide which future -+versions of the GNU General Public License can be used, that proxy's -+public statement of acceptance of a version permanently authorizes you -+to choose that version for the Program. -+ -+ Later license versions may give you additional or different -+permissions. However, no additional obligations are imposed on any -+author or copyright holder as a result of your choosing to follow a -+later version. -+ -+ 15. Disclaimer of Warranty. -+ -+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -+ALL NECESSARY SERVICING, REPAIR OR CORRECTION. -+ -+ 16. Limitation of Liability. -+ -+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -+SUCH DAMAGES. -+ -+ 17. Interpretation of Sections 15 and 16. -+ -+ If the disclaimer of warranty and limitation of liability provided -+above cannot be given local legal effect according to their terms, -+reviewing courts shall apply local law that most closely approximates -+an absolute waiver of all civil liability in connection with the -+Program, unless a warranty or assumption of liability accompanies a -+copy of the Program in return for a fee. -+ -+ END OF TERMS AND CONDITIONS -+ -+ How to Apply These Terms to Your New Programs -+ -+ If you develop a new program, and you want it to be of the greatest -+possible use to the public, the best way to achieve this is to make it -+free software which everyone can redistribute and change under these terms. -+ -+ To do so, attach the following notices to the program. It is safest -+to attach them to the start of each source file to most effectively -+state the exclusion of warranty; and each file should have at least -+the "copyright" line and a pointer to where the full notice is found. -+ -+ -+ Copyright (C) -+ -+ This program is free software: you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation, either version 3 of the License, or -+ (at your option) any later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program. If not, see . -+ -+Also add information on how to contact you by electronic and paper mail. -+ -+ If the program does terminal interaction, make it output a short -+notice like this when it starts in an interactive mode: -+ -+ Copyright (C) -+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. -+ This is free software, and you are welcome to redistribute it -+ under certain conditions; type `show c' for details. -+ -+The hypothetical commands `show w' and `show c' should show the appropriate -+parts of the General Public License. Of course, your program's commands -+might be different; for a GUI interface, you would use an "about box". -+ -+ You should also get your employer (if you work as a programmer) or school, -+if any, to sign a "copyright disclaimer" for the program, if necessary. -+For more information on this, and how to apply and follow the GNU GPL, see -+. -+ -+ The GNU General Public License does not permit incorporating your program -+into proprietary programs. If your program is a subroutine library, you -+may consider it more useful to permit linking proprietary applications with -+the library. If this is what you want to do, use the GNU Lesser General -+Public License instead of this License. But first, please read -+. -diff --git a/LICENSE.LGPL3 b/LICENSE.LGPL3 -new file mode 100644 -index 0000000..65c5ca8 ---- /dev/null -+++ b/LICENSE.LGPL3 -@@ -0,0 +1,165 @@ -+ GNU LESSER GENERAL PUBLIC LICENSE -+ Version 3, 29 June 2007 -+ -+ Copyright (C) 2007 Free Software Foundation, Inc. -+ Everyone is permitted to copy and distribute verbatim copies -+ of this license document, but changing it is not allowed. -+ -+ -+ This version of the GNU Lesser General Public License incorporates -+the terms and conditions of version 3 of the GNU General Public -+License, supplemented by the additional permissions listed below. -+ -+ 0. Additional Definitions. -+ -+ As used herein, "this License" refers to version 3 of the GNU Lesser -+General Public License, and the "GNU GPL" refers to version 3 of the GNU -+General Public License. -+ -+ "The Library" refers to a covered work governed by this License, -+other than an Application or a Combined Work as defined below. -+ -+ An "Application" is any work that makes use of an interface provided -+by the Library, but which is not otherwise based on the Library. -+Defining a subclass of a class defined by the Library is deemed a mode -+of using an interface provided by the Library. -+ -+ A "Combined Work" is a work produced by combining or linking an -+Application with the Library. The particular version of the Library -+with which the Combined Work was made is also called the "Linked -+Version". -+ -+ The "Minimal Corresponding Source" for a Combined Work means the -+Corresponding Source for the Combined Work, excluding any source code -+for portions of the Combined Work that, considered in isolation, are -+based on the Application, and not on the Linked Version. -+ -+ The "Corresponding Application Code" for a Combined Work means the -+object code and/or source code for the Application, including any data -+and utility programs needed for reproducing the Combined Work from the -+Application, but excluding the System Libraries of the Combined Work. -+ -+ 1. Exception to Section 3 of the GNU GPL. -+ -+ You may convey a covered work under sections 3 and 4 of this License -+without being bound by section 3 of the GNU GPL. -+ -+ 2. Conveying Modified Versions. -+ -+ If you modify a copy of the Library, and, in your modifications, a -+facility refers to a function or data to be supplied by an Application -+that uses the facility (other than as an argument passed when the -+facility is invoked), then you may convey a copy of the modified -+version: -+ -+ a) under this License, provided that you make a good faith effort to -+ ensure that, in the event an Application does not supply the -+ function or data, the facility still operates, and performs -+ whatever part of its purpose remains meaningful, or -+ -+ b) under the GNU GPL, with none of the additional permissions of -+ this License applicable to that copy. -+ -+ 3. Object Code Incorporating Material from Library Header Files. -+ -+ The object code form of an Application may incorporate material from -+a header file that is part of the Library. You may convey such object -+code under terms of your choice, provided that, if the incorporated -+material is not limited to numerical parameters, data structure -+layouts and accessors, or small macros, inline functions and templates -+(ten or fewer lines in length), you do both of the following: -+ -+ a) Give prominent notice with each copy of the object code that the -+ Library is used in it and that the Library and its use are -+ covered by this License. -+ -+ b) Accompany the object code with a copy of the GNU GPL and this license -+ document. -+ -+ 4. Combined Works. -+ -+ You may convey a Combined Work under terms of your choice that, -+taken together, effectively do not restrict modification of the -+portions of the Library contained in the Combined Work and reverse -+engineering for debugging such modifications, if you also do each of -+the following: -+ -+ a) Give prominent notice with each copy of the Combined Work that -+ the Library is used in it and that the Library and its use are -+ covered by this License. -+ -+ b) Accompany the Combined Work with a copy of the GNU GPL and this license -+ document. -+ -+ c) For a Combined Work that displays copyright notices during -+ execution, include the copyright notice for the Library among -+ these notices, as well as a reference directing the user to the -+ copies of the GNU GPL and this license document. -+ -+ d) Do one of the following: -+ -+ 0) Convey the Minimal Corresponding Source under the terms of this -+ License, and the Corresponding Application Code in a form -+ suitable for, and under terms that permit, the user to -+ recombine or relink the Application with a modified version of -+ the Linked Version to produce a modified Combined Work, in the -+ manner specified by section 6 of the GNU GPL for conveying -+ Corresponding Source. -+ -+ 1) Use a suitable shared library mechanism for linking with the -+ Library. A suitable mechanism is one that (a) uses at run time -+ a copy of the Library already present on the user's computer -+ system, and (b) will operate properly with a modified version -+ of the Library that is interface-compatible with the Linked -+ Version. -+ -+ e) Provide Installation Information, but only if you would otherwise -+ be required to provide such information under section 6 of the -+ GNU GPL, and only to the extent that such information is -+ necessary to install and execute a modified version of the -+ Combined Work produced by recombining or relinking the -+ Application with a modified version of the Linked Version. (If -+ you use option 4d0, the Installation Information must accompany -+ the Minimal Corresponding Source and Corresponding Application -+ Code. If you use option 4d1, you must provide the Installation -+ Information in the manner specified by section 6 of the GNU GPL -+ for conveying Corresponding Source.) -+ -+ 5. Combined Libraries. -+ -+ You may place library facilities that are a work based on the -+Library side by side in a single library together with other library -+facilities that are not Applications and are not covered by this -+License, and convey such a combined library under terms of your -+choice, if you do both of the following: -+ -+ a) Accompany the combined library with a copy of the same work based -+ on the Library, uncombined with any other library facilities, -+ conveyed under the terms of this License. -+ -+ b) Give prominent notice with the combined library that part of it -+ is a work based on the Library, and explaining where to find the -+ accompanying uncombined form of the same work. -+ -+ 6. Revised Versions of the GNU Lesser General Public License. -+ -+ The Free Software Foundation may publish revised and/or new versions -+of the GNU Lesser General Public License from time to time. Such new -+versions will be similar in spirit to the present version, but may -+differ in detail to address new problems or concerns. -+ -+ Each version is given a distinguishing version number. If the -+Library as you received it specifies that a certain numbered version -+of the GNU Lesser General Public License "or any later version" -+applies to it, you have the option of following the terms and -+conditions either of that published version or of any later version -+published by the Free Software Foundation. If the Library as you -+received it does not specify a version number of the GNU Lesser -+General Public License, you may choose any version of the GNU Lesser -+General Public License ever published by the Free Software Foundation. -+ -+ If the Library as you received it specifies that a proxy can decide -+whether future versions of the GNU Lesser General Public License shall -+apply, that proxy's public statement of acceptance of any version is -+permanent authorization for you to choose that version for the -+Library. -diff --git a/README.md b/README.md -deleted file mode 100644 -index 5020bcb..0000000 ---- a/README.md -+++ /dev/null -@@ -1,92 +0,0 @@ --# qtohextras -- -- -- --## Getting started -- --To make it easy for you to get started with GitLab, here's a list of recommended next steps. -- --Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! -- --## Add your files -- --- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files --- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: -- --``` --cd existing_repo --git remote add origin http://qt.isscloud.com:8800/qt5/qtohextras.git --git branch -M main --git push -uf origin main --``` -- --## Integrate with your tools -- --- [ ] [Set up project integrations](http://qt.isscloud.com/qt5/qtohextras/-/settings/integrations) -- --## Collaborate with your team -- --- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) --- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) --- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) --- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) --- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) -- --## Test and Deploy -- --Use the built-in continuous integration in GitLab. -- --- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) --- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) --- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) --- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) --- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) -- --*** -- --# Editing this README -- --When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template. -- --## Suggestions for a good README --Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. -- --## Name --Choose a self-explaining name for your project. -- --## Description --Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. -- --## Badges --On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. -- --## Visuals --Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. -- --## Installation --Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. -- --## Usage --Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. -- --## Support --Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. -- --## Roadmap --If you have ideas for releases in the future, it is a good idea to list them in the README. -- --## Contributing --State if you are open to contributions and what your requirements are for accepting them. -- --For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. -- --You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. -- --## Authors and acknowledgment --Show your appreciation to those who have contributed to the project. -- --## License --For open source projects, say how it is licensed. -- --## Project status --If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. -diff --git a/dist/changes-5.12.12 b/dist/changes-5.12.12 -new file mode 100644 -index 0000000..c0c7ef6 ---- /dev/null -+++ b/dist/changes-5.12.12 -@@ -0,0 +1,5 @@ -+ -+QtOhExtras -+------------- -+ -+ - Add first version -diff --git a/examples/examples.pro b/examples/examples.pro -new file mode 100644 -index 0000000..9e89c01 ---- /dev/null -+++ b/examples/examples.pro -@@ -0,0 +1,2 @@ -+TEMPLATE = subdirs -+openharmony:SUBDIRS += startapp -diff --git a/examples/startapp/main.cpp b/examples/startapp/main.cpp -new file mode 100644 -index 0000000..d9ff859 ---- /dev/null -+++ b/examples/startapp/main.cpp -@@ -0,0 +1,12 @@ -+#include "mainwindow.h" -+#include -+#include -+#include -+ -+int main(int argc, char *argv[]) -+{ -+ QApplication a(argc, argv); -+ MainWindow w; -+ w.show(); -+ return a.exec(); -+} -diff --git a/examples/startapp/mainwindow.cpp b/examples/startapp/mainwindow.cpp -new file mode 100644 -index 0000000..141e13c ---- /dev/null -+++ b/examples/startapp/mainwindow.cpp -@@ -0,0 +1,79 @@ -+#include "mainwindow.h" -+#include "ui_mainwindow.h" -+#include -+#include -+#include -+#include -+#include -+MainWindow::MainWindow(QWidget *parent) : -+ QWidget(parent), -+ ui(new Ui::MainWindow) -+{ -+ ui->setupUi(this); -+ QIntValidator *v = new QIntValidator(this); -+ v->setRange(0, 100); -+ ui->lineEdit_flags->setValidator(v); -+ -+ -+} -+ -+MainWindow::~MainWindow() -+{ -+ delete ui; -+} -+ -+void MainWindow::on_pushButton_add_clicked() -+{ -+ QTableWidgetItem *item = new QTableWidgetItem("key"); -+ ui->tableWidget->insertRow(0); -+ ui->tableWidget->setItem(0, 0, item); -+ item = new QTableWidgetItem("value"); -+ ui->tableWidget->setItem(0, 1, item); -+ QComboBox *box = new QComboBox; -+ box->addItems(QStringList() << "string" << "int" << "float" << "bool"); -+ ui->tableWidget->setCellWidget(0, 2, box); -+} -+ -+void MainWindow::on_pushButton_delete_clicked() -+{ -+ int index = ui->tableWidget->currentRow(); -+ ui->tableWidget->removeRow(index); -+} -+ -+void MainWindow::on_pushButton_ok_clicked() -+{ -+ QOpenHarmonyWant want; -+ want.deviceId = ui->lineEdit_deviceId->text(); -+ want.bundleName = ui->lineEdit_bundleName->text(); -+ want.abilityName = ui->lineEdit_abilityName->text(); -+ want.moduleName = ui->lineEdit_moduleName->text(); -+ want.action = ui->lineEdit_action->text(); -+ want.entities = ui->lineEdit_entities->text().split(";"); -+ want.uri = ui->lineEdit_uri->text(); -+ want.type = ui->lineEdit_type->text(); -+ want.flags = ui->lineEdit_flags->text().toInt(); -+ QVariantMap parameters; -+ for (int i = 0; i < ui->tableWidget->rowCount(); ++i) { -+ QTableWidgetItem *item = ui->tableWidget->item(i, 0); -+ QString key = item->text(); -+ item = ui->tableWidget->item(i, 1); -+ QString value = item->text(); -+ item = ui->tableWidget->item(i, 2); -+ QString type = item->text(); -+ QVariant v; -+ if (type == "int") { -+ v = value.toInt(); -+ } else if (type == "bool") { -+ v = (value == "true") || (value == "1"); -+ } else if (type == "float") { -+ v = value.toFloat(); -+ } else { -+ v = value; -+ } -+ want.parameters.insert(key, v); -+ } -+ bool result = QOpenHarmonyAbility::start(want); -+ if (!result) { -+ qWarning() << "open app failed"; -+ } -+} -diff --git a/examples/startapp/mainwindow.h b/examples/startapp/mainwindow.h -new file mode 100644 -index 0000000..5c88bd9 ---- /dev/null -+++ b/examples/startapp/mainwindow.h -@@ -0,0 +1,29 @@ -+#ifndef MAINWINDOW_H -+#define MAINWINDOW_H -+ -+#include -+ -+namespace Ui { -+class MainWindow; -+} -+ -+class MainWindow : public QWidget -+{ -+ Q_OBJECT -+ -+public: -+ explicit MainWindow(QWidget *parent = nullptr); -+ ~MainWindow(); -+ -+private slots: -+ void on_pushButton_add_clicked(); -+ -+ void on_pushButton_delete_clicked(); -+ -+ void on_pushButton_ok_clicked(); -+ -+private: -+ Ui::MainWindow *ui; -+}; -+ -+#endif // MAINWINDOW_H -diff --git a/examples/startapp/mainwindow.ui b/examples/startapp/mainwindow.ui -new file mode 100644 -index 0000000..5fe2f97 ---- /dev/null -+++ b/examples/startapp/mainwindow.ui -@@ -0,0 +1,187 @@ -+ -+ -+ MainWindow -+ -+ -+ -+ 0 -+ 0 -+ 609 -+ 283 -+ -+ -+ -+ Form -+ -+ -+ -+ -+ -+ -+ -+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter -+ -+ -+ -+ -+ Device Id: -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ Bundle Name: -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ Module Name: -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ Ability Name -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ Action: -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ Entities: -+ -+ -+ -+ -+ -+ -+ string list use ; separate -+ -+ -+ -+ -+ -+ -+ Uri: -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ Type: -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ Flags: -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ Parameters -+ -+ -+ -+ -+ -+ -+ QAbstractItemView::SelectRows -+ -+ -+ true -+ -+ -+ -+ Key -+ -+ -+ -+ -+ Value -+ -+ -+ -+ -+ Type -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ + -+ -+ -+ -+ -+ -+ -+ - -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ Ok -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/examples/startapp/startapp.pro b/examples/startapp/startapp.pro -new file mode 100644 -index 0000000..2c8f411 ---- /dev/null -+++ b/examples/startapp/startapp.pro -@@ -0,0 +1,35 @@ -+#------------------------------------------------- -+# -+# Project created by QtCreator 2024-03-13T13:35:25 -+# -+#------------------------------------------------- -+ -+QT += core gui ohextras -+ -+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets -+ -+TARGET = startapp -+TEMPLATE = app -+ -+# The following define makes your compiler emit warnings if you use -+# any feature of Qt which has been marked as deprecated (the exact warnings -+# depend on your compiler). Please consult the documentation of the -+# deprecated API in order to know how to port your code away from it. -+DEFINES += QT_DEPRECATED_WARNINGS -+ -+# You can also make your code fail to compile if you use deprecated APIs. -+# In order to do so, uncomment the following line. -+# You can also select to disable deprecated APIs only up to a certain version of Qt. -+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 -+ -+CONFIG += c++11 -+ -+SOURCES += \ -+ main.cpp \ -+ mainwindow.cpp -+ -+FORMS += \ -+ mainwindow.ui -+ -+HEADERS += \ -+ mainwindow.h -diff --git a/qtohextras.pro b/qtohextras.pro -new file mode 100644 -index 0000000..fe127be ---- /dev/null -+++ b/qtohextras.pro -@@ -0,0 +1,3 @@ -+requires(qtHaveModule(gui)) -+ -+load(qt_parts) -diff --git a/src/ohextras/doc/qtohextras.qdocconf b/src/ohextras/doc/qtohextras.qdocconf -new file mode 100644 -index 0000000..6ea97ea ---- /dev/null -+++ b/src/ohextras/doc/qtohextras.qdocconf -@@ -0,0 +1,47 @@ -+include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) -+ -+Cpp.ignoretokens += Q_OHEXTRAS_EXPORT -+# dummy module header for clang, found under doc/ -+moduleheader = QtOhExtrasDoc -+ -+# pass include paths to clang -+includepaths += -I . \ -+ -I .. \ -+ -+project = QtOhExtras -+description = Qt Harmony Extras Reference Documentation -+version = $QT_VERSION -+ -+qhp.projects = QtOpenHarmonyExtras -+ -+qhp.QtOpenHarmonyExtras.file = qtohextras.qhp -+qhp.QtOpenHarmonyExtras.namespace = org.qt-project.qtohextras.$QT_VERSION_TAG -+qhp.QtOpenHarmonyExtras.virtualFolder = qtohextras -+qhp.QtOpenHarmonyExtras.indexTitle = Qt Harmony Extras -+qhp.QtOpenHarmonyExtras.indexRoot = -+ -+qhp.QtOpenHarmonyExtras.filterAttributes = qtohextras $QT_VERSION qtrefdoc -+qhp.QtOpenHarmonyExtras.customFilters.Qt.name = QtOpenHarmonyExtras $QT_VERSION -+qhp.QtOpenHarmonyExtras.customFilters.Qt.filterAttributes = qtohextras $QT_VERSION -+ -+qhp.QtOpenHarmonyExtras.subprojects = classes examples -+qhp.QtOpenHarmonyExtras.subprojects.classes.title = C++ Classes and Namespaces -+qhp.QtOpenHarmonyExtras.subprojects.classes.indexTitle = Qt Harmony Extras C++ Classes -+qhp.QtOpenHarmonyExtras.subprojects.classes.selectors = class function namespace fake:headerfile -+qhp.QtOpenHarmonyExtras.subprojects.classes.sortPages = true -+qhp.QtOpenHarmonyExtras.subprojects.examples.title = Examples -+qhp.QtOpenHarmonyExtras.subprojects.examples.indexTitle = Qt Harmony Extras Examples -+qhp.QtOpenHarmonyExtras.subprojects.examples.selectors = fake:example -+ -+depends += qtcore qtdoc -+headerdirs += .. -+sourcedirs += .. -+exampledirs += ../../../examples/ohextras \ -+ snippets -+ -+# Specify example install dir under QT_INSTALL_EXAMPLES -+examplesinstallpath = ohextras -+ -+imagedirs += images -+navigation.landingpage = "Qt Harmony Extras" -+navigation.cppclassespage = "Qt Harmony Extras C++ Classes" -diff --git a/src/ohextras/ohextras.pro b/src/ohextras/ohextras.pro -new file mode 100644 -index 0000000..d284856 ---- /dev/null -+++ b/src/ohextras/ohextras.pro -@@ -0,0 +1,9 @@ -+TARGET = QtOhExtras -+ -+DEFINES += QT_NO_USING_NAMESPACE -+QMAKE_DOCS = \ -+ $$PWD/doc/qtohextras.qdocconf -+ -+include(openharmony/openharmony.pri) -+QT += core core-private -+load(qt_module) -diff --git a/src/ohextras/openharmony/openharmony.pri b/src/ohextras/openharmony/openharmony.pri -new file mode 100644 -index 0000000..b175f34 ---- /dev/null -+++ b/src/ohextras/openharmony/openharmony.pri -@@ -0,0 +1,12 @@ -+DEFINES += QT_BUILD_OPENHARMONYEXTRAS_LIB -+ -+SOURCES += \ -+ $$PWD/qopenharmonywant.cpp \ -+ $$PWD/qopenharmonyextrasload.cpp \ -+ $$PWD/qopenharmonyability.cpp -+ -+ -+HEADERS += \ -+ $$PWD/qopenharmonywant.h \ -+ $$PWD/qopenharmonyextrasglobal.h \ -+ $$PWD/qopenharmonyability.h -diff --git a/src/ohextras/openharmony/qopenharmonyability.cpp b/src/ohextras/openharmony/qopenharmonyability.cpp -new file mode 100644 -index 0000000..1cd5afc ---- /dev/null -+++ b/src/ohextras/openharmony/qopenharmonyability.cpp -@@ -0,0 +1,66 @@ -+#include "qopenharmonyability.h" -+#include "qopenharmonywant.h" -+#include "qopenharmonydefines.h" -+#include -+#include -+ -+namespace QOpenHarmonyAbility { -+ -+static napi_value handleAbilityResult(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 2; -+ napi_value args[2]; -+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); -+ if (argc != 2) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments"); -+ return nullptr; -+ } -+ int64_t code = qJs::getInt64(args[0]); -+ QAbilityResultReceiver *r = reinterpret_cast(code); -+ if (r == nullptr) -+ return nullptr; -+ r->handleResult(args[1]); -+ return nullptr; -+} -+ -+ -+napi_value init(napi_env env, napi_value exports) -+{ -+ napi_property_descriptor desc[] ={ -+ DECLARE_NAPI_FUNCTION("handleAbilityResult", handleAbilityResult), -+ }; -+ NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); -+ return nullptr; -+} -+ -+ -+bool start(const QOpenHarmonyWant &want) -+{ -+ QSharedPointer jsManager = qJsObjectLoader->create("JsOpenHarmonyAbility"); -+ if (jsManager.isNull()) -+ return false; -+ bool result = jsManager->call("start", want); -+ return result; -+} -+ -+bool startForResult(const QOpenHarmonyWant &want, QAbilityResultReceiver *receiver) -+{ -+ QSharedPointer jsManager = qJsObjectLoader->create("JsOpenHarmonyAbility"); -+ if (jsManager.isNull() || receiver == nullptr) -+ return false; -+ qlonglong code = reinterpret_cast(receiver); -+ bool result = jsManager->call("startForResult", want, code); -+ return result; -+} -+ -+QAbilityResultReceiver::QAbilityResultReceiver() -+{ -+ -+} -+ -+QAbilityResultReceiver::~QAbilityResultReceiver() -+{ -+ -+} -+ -+} -diff --git a/src/ohextras/openharmony/qopenharmonyability.h b/src/ohextras/openharmony/qopenharmonyability.h -new file mode 100644 -index 0000000..f0f2e43 ---- /dev/null -+++ b/src/ohextras/openharmony/qopenharmonyability.h -@@ -0,0 +1,27 @@ -+#ifndef QOPENHARMONYABILITY_H -+#define QOPENHARMONYABILITY_H -+ -+#include "qopenharmonyextrasglobal.h" -+#include -+struct QOpenHarmonyWant; -+ -+namespace QOpenHarmonyAbility -+{ -+ class Q_OPENHARMONYEXTRAS_EXPORT QAbilityResultReceiver -+ { -+ public: -+ QAbilityResultReceiver(); -+ virtual ~QAbilityResultReceiver(); -+ virtual void handleResult(napi_value result) = 0; -+ -+ private: -+ Q_DISABLE_COPY(QAbilityResultReceiver) -+ }; -+ -+ napi_value init(napi_env env, napi_value exports); -+ -+ Q_OPENHARMONYEXTRAS_EXPORT bool start(const QOpenHarmonyWant &want); -+ Q_OPENHARMONYEXTRAS_EXPORT bool startForResult(const QOpenHarmonyWant &want, QAbilityResultReceiver* receiver); -+}; -+ -+#endif // QOPENHARMONYABILITY_H -diff --git a/src/ohextras/openharmony/qopenharmonyextrasglobal.h b/src/ohextras/openharmony/qopenharmonyextrasglobal.h -new file mode 100644 -index 0000000..48f5563 ---- /dev/null -+++ b/src/ohextras/openharmony/qopenharmonyextrasglobal.h -@@ -0,0 +1,21 @@ -+#ifndef QOPENHARMONYEXTRASGLOBAL_H -+#define QOPENHARMONYEXTRASGLOBAL_H -+ -+#include -+ -+QT_BEGIN_NAMESPACE -+ -+#ifndef QT_STATIC -+# if defined(QT_BUILD_OPENHARMONYEXTRAS_LIB) -+# define Q_OPENHARMONYEXTRAS_EXPORT Q_DECL_EXPORT -+# else -+# define Q_OPENHARMONYEXTRAS_EXPORT Q_DECL_IMPORT -+# endif -+#else -+# define Q_OPENHARMONYEXTRAS_EXPORT -+#endif -+ -+QT_END_NAMESPACE -+ -+ -+#endif // QOPENHARMONYEXTRASGLOBAL_H -diff --git a/src/ohextras/openharmony/qopenharmonyextrasload.cpp b/src/ohextras/openharmony/qopenharmonyextrasload.cpp -new file mode 100644 -index 0000000..465c5e2 ---- /dev/null -+++ b/src/ohextras/openharmony/qopenharmonyextrasload.cpp -@@ -0,0 +1,48 @@ -+#include -+#include -+ -+#include "qopenharmonywant.h" -+#include "qopenharmonyability.h" -+#include "qopenharmonydefines.h" -+#include "qopenharmonyjsenvironment.h" -+ -+/* -+ * function for module exports -+ */ -+EXTERN_C_START -+static napi_value Init(napi_env env, napi_value exports) -+{ -+ static bool initialized = false; -+ if (initialized) -+ return exports; -+ -+ -+ initialized = true; -+ LOGI("init in qt openharmony extras"); -+ qRegisterMetaType(); -+ std::function wantCreatorFunc = QOpenHarmonyWant2JsWant; -+ qJs::registerCreator(wantCreatorFunc); -+ QOpenHarmonyAbility::init(env, exports); -+ return exports; -+} -+EXTERN_C_END -+ -+/* -+ * Napi Module define -+ */ -+static napi_module openharmonyQtOHExtrasModule = { -+ .nm_version = 1, -+ .nm_flags = 0, -+ .nm_filename = nullptr, -+ .nm_register_func = Init, -+ .nm_modname = "Qt5OhExtras", -+ .nm_priv = ((void*)0), -+ .reserved = { 0 }, -+}; -+/* -+ * Module register function -+ */ -+extern "C" __attribute__((constructor)) void RegisterModule(void) -+{ -+ napi_module_register(&openharmonyQtOHExtrasModule); -+} -diff --git a/src/ohextras/openharmony/qopenharmonywant.cpp b/src/ohextras/openharmony/qopenharmonywant.cpp -new file mode 100644 -index 0000000..fb1af4b ---- /dev/null -+++ b/src/ohextras/openharmony/qopenharmonywant.cpp -@@ -0,0 +1,56 @@ -+#include "qopenharmonywant.h" -+#include "qopenharmonydefines.h" -+#include -+#include -+ -+static void setProperty(napi_value o, const QString &key, const QVariant &value) -+{ -+ napi_value name = qJs::createString(key); -+ napi_value v = QtHarmonyPrivate::variant_to_napi_value(value); -+ napi_set_property(qJs::env(), o, name, v); -+} -+ -+napi_value QOpenHarmonyWant2JsWant(const QOpenHarmonyWant &want) -+{ -+ napi_value result = nullptr; -+ NAPI_CALL_BASE(qJs::env(), napi_create_object(qJs::env(), &result), nullptr); -+ -+ if (!want.deviceId.isEmpty()) { -+ setProperty(result, "deviceId", want.deviceId); -+ } -+ -+ if (!want.bundleName.isEmpty()) { -+ setProperty(result, "bundleName", want.bundleName); -+ } -+ -+ if (!want.moduleName.isEmpty()) { -+ setProperty(result, "moduleName", want.moduleName); -+ } -+ -+ if (!want.abilityName.isEmpty()) { -+ setProperty(result, "abilityName", want.abilityName); -+ } -+ -+ if (!want.action.isEmpty()) { -+ setProperty(result, "action", want.action); -+ } -+ -+ if (!want.uri.isEmpty()) { -+ setProperty(result, "uri", want.uri); -+ } -+ -+ if (!want.type.isEmpty()) { -+ setProperty(result, "type", want.type); -+ } -+ -+ if (!want.entities.isEmpty()) { -+ setProperty(result, "entities", want.entities); -+ } -+ -+ if (!want.parameters.isEmpty()) { -+ setProperty(result, "parameters", want.parameters); -+ } -+ -+ setProperty(result, "flags", want.flags); -+ return result; -+} -diff --git a/src/ohextras/openharmony/qopenharmonywant.h b/src/ohextras/openharmony/qopenharmonywant.h -new file mode 100644 -index 0000000..0d67f7b ---- /dev/null -+++ b/src/ohextras/openharmony/qopenharmonywant.h -@@ -0,0 +1,27 @@ -+#ifndef QOPENHARMONYWANT_H -+#define QOPENHARMONYWANT_H -+ -+#include -+#include -+#include -+#include "qopenharmonyextrasglobal.h" -+ -+struct QOpenHarmonyWant -+{ -+ QString deviceId; -+ QString bundleName; -+ QString moduleName; -+ QString abilityName; -+ QString action; -+ QStringList entities; -+ QString uri; -+ QString type; -+ QVariantMap parameters; -+ int flags; -+}; -+ -+Q_OPENHARMONYEXTRAS_EXPORT napi_value QOpenHarmonyWant2JsWant(const QOpenHarmonyWant &want); -+ -+Q_DECLARE_METATYPE(QOpenHarmonyWant) -+ -+#endif // QOPENHARMONYWANT_H -diff --git a/src/src.pro b/src/src.pro -new file mode 100644 -index 0000000..2bca476 ---- /dev/null -+++ b/src/src.pro -@@ -0,0 +1,8 @@ -+openharmony { -+ TEMPLATE = subdirs -+ SUBDIRS += ohextras ts -+} else { -+ TEMPLATE = aux -+ CONFIG += force_qt -+ QMAKE_DOCS = $$PWD/ohextras/doc/qtohextras.qdocconf -+} -diff --git a/src/ts/native/QtOhExtras/JsOhExtrasModule.ts b/src/ts/native/QtOhExtras/JsOhExtrasModule.ts -new file mode 100644 -index 0000000..ce8f860 ---- /dev/null -+++ b/src/ts/native/QtOhExtras/JsOhExtrasModule.ts -@@ -0,0 +1,31 @@ -+import { JsQtModule, ObjectBuilder } from '../QtCore/JsQtModule'; -+import JsOpenHarmonyAbility from './JsOpenHarmonyAbility'; -+import JsDataStore from '../QtCore/JsDataStore'; -+import JsLogger from '../QtCore/JsLogger'; -+ -+class JsOhExtrasModule extends JsQtModule { -+ -+ public constructor() { -+ super() -+ this.moduleJsObjects.set("JsOpenHarmonyAbility", new ObjectBuilder<[]>(() =>{ -+ return new JsOpenHarmonyAbility(); -+ })); -+ } -+ -+ async loadQtModule(): Promise { -+ let qtMajorVersion = JsDataStore.getQtMajorVersion(); -+ let QtOhExtrasModule: any = null; -+ if (qtMajorVersion == 5) -+ QtOhExtrasModule = await import ("libQt5OhExtras.so"); -+ else if (qtMajorVersion == 6) -+ QtOhExtrasModule = await import ("libQt6OhExtras.so"); -+ if (QtOhExtrasModule == null) { -+ JsLogger.fatal("Cannot load QtOhExtras module"); -+ return; -+ } -+ let QtOhExtras = QtOhExtrasModule.default; -+ JsDataStore.addQtNativeModule("QtOhExtras", QtOhExtras); -+ } -+} -+ -+export default new JsOhExtrasModule; -\ No newline at end of file -diff --git a/src/ts/native/QtOhExtras/JsOpenHarmonyAbility.ts b/src/ts/native/QtOhExtras/JsOpenHarmonyAbility.ts -new file mode 100644 -index 0000000..494873e ---- /dev/null -+++ b/src/ts/native/QtOhExtras/JsOpenHarmonyAbility.ts -@@ -0,0 +1,32 @@ -+import Want from '@ohos.app.ability.Want'; -+import JsDataStore from '../QtCore/JsDataStore'; -+import JsLogger from '../QtCore/JsLogger'; -+ -+export default class JsOpenHarmonyAbility { -+ constructor() { -+ -+ } -+ -+ async start(want: Want) : Promise { -+ try { -+ let context = JsDataStore.getContext() -+ await context.startAbility(want); -+ return true; -+ } catch (error) { -+ JsLogger.info("startAbility error, error.code = %{public}s", error.code) -+ return false; -+ } -+ } -+ -+ async startForResult(want: Want, code: number) : Promise { -+ try { -+ let context = JsDataStore.getContext() -+ let result = await context.startAbilityForResult(want); -+ JsDataStore.getQtNativeModule("QtOhExtras").handleAbilityResult(code, result); -+ return true; -+ } catch (error) { -+ JsLogger.info("startAbilityForResult error, error.code = %{public}s", error.code) -+ return false; -+ } -+ } -+} -\ No newline at end of file -diff --git a/src/ts/ts.pro b/src/ts/ts.pro -new file mode 100644 -index 0000000..2c9a72c ---- /dev/null -+++ b/src/ts/ts.pro -@@ -0,0 +1,10 @@ -+TEMPLATE = aux -+CONFIG -= qt -+ -+templates.files += $$files($$PWD/native/*.ts, true) -+templates.path = $$[QT_INSTALL_PREFIX]/openharmony/qtohextras -+templates.base = $$PWD -+ -+INSTALLS += templates -+ -+OTHER_FILES += $$templates.files -diff --git a/sync.profile b/sync.profile -new file mode 100644 -index 0000000..7bf0fa6 ---- /dev/null -+++ b/sync.profile -@@ -0,0 +1,5 @@ -+%modules = ( # path to module name map -+ "QtOhExtras" => "$basedir/src/ohextras", -+); -+%moduleheaders = ( # restrict the module headers to those found in relative path -+); -diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro -new file mode 100644 -index 0000000..dde9692 ---- /dev/null -+++ b/tests/auto/auto.pro -@@ -0,0 +1,3 @@ -+TEMPLATE = subdirs -+SUBDIRS += \ -+ cmake -diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt -new file mode 100644 -index 0000000..01a35e7 ---- /dev/null -+++ b/tests/auto/cmake/CMakeLists.txt -@@ -0,0 +1,10 @@ -+ -+cmake_minimum_required(VERSION 2.8) -+ -+project(qmake_cmake_files) -+ -+enable_testing() -+ -+find_package(Qt5Core REQUIRED) -+ -+include("${_Qt5CTestMacros}") -\ No newline at end of file -diff --git a/tests/auto/cmake/cmake.pro b/tests/auto/cmake/cmake.pro -new file mode 100644 -index 0000000..d8b4ddb ---- /dev/null -+++ b/tests/auto/cmake/cmake.pro -@@ -0,0 +1,7 @@ -+ -+# Cause make to do nothing. -+TEMPLATE = subdirs -+ -+CMAKE_QT_MODULES_UNDER_TEST = ohextras -+ -+CONFIG += ctest_testcase -diff --git a/tests/tests.pro b/tests/tests.pro -new file mode 100644 -index 0000000..9671085 ---- /dev/null -+++ b/tests/tests.pro -@@ -0,0 +1 @@ -+TEMPLATE = subdirs diff --git a/patch/v5.12.12/qtremoteobjects.patch b/patch/v5.12.12/qtremoteobjects.patch deleted file mode 100644 index 4fa952c6310f10725fef1e96bb59941d67d41984..0000000000000000000000000000000000000000 --- a/patch/v5.12.12/qtremoteobjects.patch +++ /dev/null @@ -1,65 +0,0 @@ -diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro -index 69ca9f1..3fbd89e 100644 ---- a/tests/auto/auto.pro -+++ b/tests/auto/auto.pro -@@ -1,27 +1,32 @@ --TEMPLATE = subdirs -- --sub_localsockettestserver.subdir = localsockettestserver --sub_localsockettestserver.target = sub-localsockettestserver -- --sub_integration.subdir = integration --sub_integration.target = sub-integration --sub_integration.depends = sub-localsockettestserver -- --SUBDIRS += \ -- benchmarks \ -- cmake \ -- modelreplica \ -- modelview \ -- pods \ -- proxy \ -- repc \ -- repcodegenerator \ -- repparser \ -- subclassreplica \ -- sub_integration \ -- sub_localsockettestserver -- --contains(QT_CONFIG, ssl): SUBDIRS += external_IODevice -- --qtHaveModule(qml): SUBDIRS += qml --qtConfig(process): SUBDIRS += integration_multiprocess proxy_multiprocess integration_external restart -+TEMPLATE = subdirs -+ -+sub_localsockettestserver.subdir = localsockettestserver -+sub_localsockettestserver.target = sub-localsockettestserver -+ -+sub_integration.subdir = integration -+sub_integration.target = sub-integration -+sub_integration.depends = sub-localsockettestserver -+ -+SUBDIRS += \ -+ benchmarks \ -+ cmake \ -+ modelreplica \ -+ modelview \ -+ pods \ -+ proxy \ -+ repc \ -+ repcodegenerator \ -+ repparser \ -+ subclassreplica \ -+ sub_integration \ -+ sub_localsockettestserver -+ -+contains(QT_CONFIG, ssl): SUBDIRS += external_IODevice -+ -+qtHaveModule(qml): SUBDIRS += qml -+qtConfig(process): SUBDIRS += integration_multiprocess proxy_multiprocess integration_external restart -+ -+!qtHaveModule(gui): SUBDIRS -= modelview -+ -+#TODO add remove -+SUBDIRS -= integration_external -\ No newline at end of file diff --git a/patch/v5.12.12/qtsensors.patch b/patch/v5.12.12/qtsensors.patch deleted file mode 100644 index 3c6c96f30b627d6e8d249c038fd049d78d7c7f6e..0000000000000000000000000000000000000000 --- a/patch/v5.12.12/qtsensors.patch +++ /dev/null @@ -1,1359 +0,0 @@ -diff --git a/src/openharmony/native/QtSensors/JsSensor.ts b/src/openharmony/native/QtSensors/JsSensor.ts -new file mode 100644 -index 00000000..c4084769 ---- /dev/null -+++ b/src/openharmony/native/QtSensors/JsSensor.ts -@@ -0,0 +1,106 @@ -+import sensor from '@ohos.sensor'; -+import JsDataStore from '../QtCore/JsDataStore' -+ -+export default class JsSensor { -+ private pointerId = 0; -+ -+ public constructor(id) { -+ this.pointerId = id; -+ } -+ -+ /* Hz转ns,周期为1 */ -+ private hertz2ns(rate: Number) { -+ /* f=1/T (其中f是指赫兹,T是指以秒为单位的时间)*/ -+ let ns = (1 / Number(rate)) * 1000000000; -+ return ns; -+ } -+ -+ /* ns转Hz,周期为1 */ -+ private ns2hertz(ns: Number) { -+ /* f=1/T (其中f是指赫兹,T是指以秒为单位的时间)*/ -+ let hz = 1 / (Number(ns) / 1000000000); -+ return hz; -+ } -+ -+ stop(type: sensor.SensorId) { -+ try { -+ sensor.off(Number(type)); -+ } catch (error) { -+ console.error(`Failed to invoke off. Code: ${error.code}, message: ${error.message}`); -+ } -+ } -+ -+ /* 开始订阅传感器数据 */ -+ async start(type: sensor.SensorId, rate: Number) { -+ try { -+ this.stop(type); -+ let ns = this.hertz2ns(rate); -+ let max = await this.maxSamplePeriod(type); -+ let min = await this.minSamplePeriod(type); -+ let iv = Math.max(Math.min(max, ns), min); -+ -+ sensor.on(Number(type), (data) => { -+ JsDataStore.getQtNativeModule("QtSensors").DataAcception(this.pointerId, JSON.stringify(data)); -+ }, { interval: iv }); -+ } catch (error) { -+ console.error(`Failed to invoke on. Code: ${error.code}, message: ${error.message}`); -+ } -+ } -+ -+ /* 获取指定传感器信息 */ -+ async description(type: sensor.SensorId) { -+ try { -+ let info = await sensor.getSingleSensor(type); -+ let des = JSON.stringify(info); -+ console.info('Succeeded in getting sensor: ' + des); -+ return des; -+ } catch (e) { -+ console.error(`Failed to get singleSensor . Code: ${e.code}, message: ${e.message}`); -+ } -+ } -+ -+ async sensorName(type: sensor.SensorId) { -+ let s = await sensor.getSingleSensor(type); -+ return s.sensorName; -+ } -+ -+ async vendorName(type: sensor.SensorId) { -+ let s = await sensor.getSingleSensor(type); -+ return s.vendorName; -+ } -+ -+ async firmwareVersion(type: sensor.SensorId) { -+ let s = await sensor.getSingleSensor(type); -+ return s.firmwareVersion; -+ } -+ -+ async hardwareVersion(type: sensor.SensorId) { -+ let s = await sensor.getSingleSensor(type); -+ return s.hardwareVersion; -+ } -+ -+ async maxRange(type: sensor.SensorId) { -+ let s = await sensor.getSingleSensor(type); -+ return s.maxRange; -+ } -+ -+ async minSamplePeriod(type: sensor.SensorId) { -+ let s = await sensor.getSingleSensor(type); -+ return this.ns2hertz(s.minSamplePeriod); -+ } -+ -+ async maxSamplePeriod(type: sensor.SensorId) { -+ let s = await sensor.getSingleSensor(type); -+ return this.ns2hertz(s.minSamplePeriod); -+ } -+ -+ async precision(type: sensor.SensorId) { -+ let s = await sensor.getSingleSensor(type); -+ return s.precision; -+ } -+ -+ async power(type: sensor.SensorId) { -+ let s = await sensor.getSingleSensor(type); -+ return s.minSamplePeriod; -+ } -+} -diff --git a/src/openharmony/native/QtSensors/JsSensorManager.ts b/src/openharmony/native/QtSensors/JsSensorManager.ts -new file mode 100644 -index 00000000..51919095 ---- /dev/null -+++ b/src/openharmony/native/QtSensors/JsSensorManager.ts -@@ -0,0 +1,18 @@ -+import sensor from '@ohos.sensor'; -+ -+export default class JsSensorManager { -+ -+ /* 获取所有传感器类型Id */ -+ async sensorIds() { -+ let s: Array = new Array(); -+ try { -+ let sr = await sensor.getSensorList(); -+ for (let data of sr) { -+ s.push(String(data.sensorId)); -+ } -+ } catch (e) { -+ console.error(`Failed to get sensorList. Code: ${e.code}, message: ${e.message}`); -+ } -+ return s; -+ } -+} -\ No newline at end of file -diff --git a/src/openharmony/native/QtSensors/JsSensorsModule.ts b/src/openharmony/native/QtSensors/JsSensorsModule.ts -new file mode 100644 -index 00000000..8e328f10 ---- /dev/null -+++ b/src/openharmony/native/QtSensors/JsSensorsModule.ts -@@ -0,0 +1,26 @@ -+import { JsQtModule, ObjectBuilder } from '../QtCore/JsQtModule'; -+import JsDataStore from '../QtCore/JsDataStore'; -+import JsSensor from './JsSensor'; -+import JsSensorManager from './JsSensorManager'; -+ -+class JsNfcModule extends JsQtModule { -+ -+ public constructor() { -+ super() -+ this.moduleJsObjects.set("JsSensor", new ObjectBuilder<[number]>((id: number) =>{ -+ return new JsSensor(id); -+ })); -+ this.moduleJsObjects.set("JsSensorManager", new ObjectBuilder<[]>(() =>{ -+ return new JsSensorManager(); -+ })); -+ this.loadQtModule(); -+ } -+ -+ async loadQtModule(): Promise { -+ let module = await import ("libplugins_sensors_qtsensors_openharmony.so"); -+ let QtSensors = module.default; -+ JsDataStore.addQtNativeModule("QtSensors", QtSensors); -+ } -+} -+ -+export default new JsNfcModule; -\ No newline at end of file -diff --git a/src/openharmony/openharmony.pro b/src/openharmony/openharmony.pro -new file mode 100644 -index 00000000..3fdb3db8 ---- /dev/null -+++ b/src/openharmony/openharmony.pro -@@ -0,0 +1,11 @@ -+TEMPLATE = aux -+ -+CONFIG -= qt -+ -+templates.files += $$files($$PWD/native/QtSensors/*.ts, true) -+templates.path = $$[QT_INSTALL_PREFIX]/openharmony/qtsensors -+templates.base = $$PWD -+ -+INSTALLS += templates -+ -+OTHER_FILES += $$templates.files -diff --git a/src/plugins/sensors/openharmony/main.cpp b/src/plugins/sensors/openharmony/main.cpp -new file mode 100644 -index 00000000..c14f6520 ---- /dev/null -+++ b/src/plugins/sensors/openharmony/main.cpp -@@ -0,0 +1,222 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "sensormanager.h" -+#include "sensorbackend.h" -+#include -+#include "openharmonylight.h" -+#include "openharmonyrotation.h" -+#include "openharmonyhumidity.h" -+#include "openharmonypressure.h" -+#include "openharmonyproximity.h" -+#include "openharmonygyroscope.h" -+#include "openharmonytemperature.h" -+#include "openharmonymagnetometer.h" -+#include "openharmonyaccelerometer.h" -+#include "QtCore/qopenharmonydefines.h" -+#include "QtCore/qopenharmonyjsenvironment.h" -+ -+ -+static napi_value DataAcception(napi_env env, napi_callback_info info) -+{ -+ size_t argc = 2; -+ napi_value args[2] = { Q_NULLPTR }; -+ napi_get_cb_info(env, info, &argc, args , nullptr, nullptr); -+ -+ if (argc != 2) { -+ napi_throw_type_error(env, NULL, "Wrong number of arguments in DataAcception"); -+ return nullptr; -+ } -+ int64_t value0 = qJs::getInt64(args[0]); -+ SensorBackendBase *backend = reinterpret_cast(value0); -+ if (backend == nullptr) -+ return Q_NULLPTR; -+ -+ -+ SensorBackendBase *sensor = reinterpret_cast(value0); -+ if (Q_NULLPTR == sensor) -+ return Q_NULLPTR; -+ -+ QByteArray data = qJs::getString(args[1]).toLocal8Bit(); -+ if (data.isEmpty()) -+ return Q_NULLPTR; -+ -+ QJsonParseError err; -+ const QJsonDocument &doc = QJsonDocument::fromJson(data, &err); -+ if (QJsonParseError::NoError != err.error) -+ return Q_NULLPTR; -+ -+ -+ //sensor->dataReceived(doc.object()); -+ QMetaObject::invokeMethod(sensor, "dataReceived", -+ Qt::QueuedConnection, -+ Q_ARG(QJsonObject, doc.object())); -+ return Q_NULLPTR; -+} -+ -+namespace { -+const char OPenHarmonyCompassId[] = "openharmony.synthetic.compass"; -+} -+ -+class OPenHarmonySensorPlugin : public QObject, public QSensorPluginInterface, -+ public QSensorBackendFactory -+{ -+ Q_OBJECT -+ Q_PLUGIN_METADATA(IID "com.qt-project.Qt.QSensorPluginInterface/1.0" FILE "plugin.json") -+ Q_INTERFACES(QSensorPluginInterface) -+ -+public: -+ void registerSensors() override -+ { -+ bool accelerometer = false; -+ bool magnetometer = false; -+ const QList &ids = SensorManager::instance()->sensorIds(); -+ for (int sensor : qAsConst(ids)) { -+ switch (sensor) { -+ case E_BAROMETER: -+ QSensorManager::registerBackend(QPressureSensor::type, QByteArray::number(sensor), this); -+ break; -+ case E_ACCELEROMETER: -+ m_accelerationModes |= OPenHarmonyAccelerometer::Accelerometer; -+ QSensorManager::registerBackend(QAccelerometer::type, QByteArray::number(sensor), this); -+ accelerometer = true; -+ break; -+ case E_LINEAR_ACCELEROMETER: -+ m_accelerationModes |= OPenHarmonyAccelerometer::LinearAcceleration; -+ //QSensorManager::registerBackend(QAccelerometer::type, QByteArray::number(sensor), this); -+ break; -+ case E_GRAVITY: -+ m_accelerationModes |= OPenHarmonyAccelerometer::Gravity; -+ //QSensorManager::registerBackend(QAccelerometer::type, QByteArray::number(sensor), this); -+ break; -+ case E_HUMIDITY: -+ QSensorManager::registerBackend(QHumiditySensor::type, QByteArray::number(sensor), this); -+ break; -+ case E_PROXIMITY: -+ QSensorManager::registerBackend(QProximitySensor::type, QByteArray::number(sensor), this); -+ break; -+ case E_GYROSCOPE: -+ QSensorManager::registerBackend(QGyroscope::type, QByteArray::number(sensor), this); -+ break; -+ case E_ORIENTATION: -+ /*FIXME 通过加速度传感器实现? */ -+ //QSensorManager::registerBackend(QOrientationSensor::type, QByteArray::number(sensor), this); -+ break; -+ case E_ROTATION_VECTOR: -+ QSensorManager::registerBackend(QRotationSensor::type, QByteArray::number(sensor), this); -+ break; -+ case E_AMBIENT_LIGHT: -+ QSensorManager::registerBackend(QAmbientLightSensor::type, QByteArray::number(sensor), this); -+ break; -+ case E_MAGNETIC_FIELD: -+ QSensorManager::registerBackend(QMagnetometer::type, QByteArray::number(sensor), this); -+ magnetometer = true; -+ break; -+ case E_AMBIENT_TEMPERATURE: -+ QSensorManager::registerBackend(QAmbientTemperatureSensor::type, QByteArray::number(sensor), this); -+ break; -+ default: -+ break; -+ } -+ } -+#if 0 -+ /* NOTE 现在的js回调机制不支持 -+ * 罗盘通过加速度和磁场传感器组合实现 -+ */ -+ if (accelerometer && magnetometer) -+ QSensorManager::registerBackend(QCompass::type, OPenHarmonyCompassId, this); -+#endif -+ } -+ -+ QSensorBackend *createBackend(QSensor *sensor) override -+ { -+ /* TODO QCompass */ -+ int id = sensor->identifier().toInt(); -+ switch (id) { -+ case E_BAROMETER: -+ return new OPenHarmonyPressure(id, sensor); -+ case E_ACCELEROMETER: -+ return new OPenHarmonyAccelerometer(m_accelerationModes, sensor); -+ case E_GYROSCOPE: -+ return new OPenHarmonyGyroscope(id, sensor); -+ case E_PROXIMITY: -+ return new OPenHarmonyProximity(id, sensor); -+ case E_AMBIENT_LIGHT: -+ return new OPenHarmonyLight(id, sensor); -+ case E_MAGNETIC_FIELD: -+ return new OPenHarmonyMagnetometer(id, sensor); -+ case E_HUMIDITY: -+ return new OPenHarmonyHumidity(id, sensor); -+ case E_ROTATION_VECTOR: -+ return new OPenHarmonyRotation(id, sensor); -+ case E_AMBIENT_TEMPERATURE: -+ return new OPenHarmonyTemperature(id, sensor); -+ default: -+ break; -+ } -+ return Q_NULLPTR; -+ } -+ -+private: -+ int m_accelerationModes = 0; -+}; -+ -+/* -+ * function for module exports -+ */ -+EXTERN_C_START -+static napi_value Init(napi_env env, napi_value exports) -+{ -+ static bool inited = false; -+ if (!inited) { -+ -+ LOGI("init sensors module"); -+ napi_property_descriptor desc[] ={ -+ DECLARE_NAPI_FUNCTION("DataAcception", DataAcception), -+ }; -+ NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); -+ inited = true; -+ } -+ return exports; -+} -+EXTERN_C_END -+ -+/* -+ * Napi Module define -+ */ -+static napi_module openharmonyQtSensorsModule = { -+ .nm_version = 1, -+ .nm_flags = 0, -+ .nm_filename = nullptr, -+ .nm_register_func = Init, -+ .nm_modname = "openharmony_qt_sensors", -+ .nm_priv = ((void*)0), -+ .reserved = { 0 }, -+}; -+/* -+ * Module register function -+ */ -+extern "C" __attribute__((constructor)) void RegisterModule(void) -+{ -+ napi_module_register(&openharmonyQtSensorsModule); -+} -+ -+#include "main.moc" -diff --git a/src/plugins/sensors/openharmony/openharmony.pro b/src/plugins/sensors/openharmony/openharmony.pro -new file mode 100644 -index 00000000..390e904c ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmony.pro -@@ -0,0 +1,36 @@ -+TARGET = qtsensors_openharmony -+QT = core sensors -+ -+OTHER_FILES = plugin.json -+ -+SOURCES += \ -+ main.cpp \ -+ openharmonyaccelerometer.cpp \ -+ openharmonygyroscope.cpp \ -+ openharmonyhumidity.cpp \ -+ openharmonylight.cpp \ -+ openharmonymagnetometer.cpp \ -+ openharmonypressure.cpp \ -+ openharmonyproximity.cpp \ -+ openharmonyrotation.cpp \ -+ openharmonytemperature.cpp \ -+ sensormanager.cpp -+ -+ -+PLUGIN_TYPE = sensors -+PLUGIN_CLASS_NAME = OPenHarmonySensorPlugin -+load(qt_plugin) -+ -+HEADERS += \ -+ openharmonyaccelerometer.h \ -+ openharmonygyroscope.h \ -+ openharmonyhumidity.h \ -+ openharmonylight.h \ -+ openharmonymagnetometer.h \ -+ openharmonypressure.h \ -+ openharmonyproximity.h \ -+ openharmonyrotation.h \ -+ openharmonytemperature.h \ -+ sensorbackend.h \ -+ sensormanager.h -+ -diff --git a/src/plugins/sensors/openharmony/openharmonyaccelerometer.cpp b/src/plugins/sensors/openharmony/openharmonyaccelerometer.cpp -new file mode 100644 -index 00000000..84ad1e53 ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonyaccelerometer.cpp -@@ -0,0 +1,71 @@ -+#include -+#include -+#include -+ -+#include "openharmonyaccelerometer.h" -+ -+OPenHarmonyAccelerometer::OPenHarmonyAccelerometer(int accelerationModes, QSensor *sensor, QObject *parent) -+ : SensorBackend(E_ACCELEROMETER, sensor, parent) -+ , m_accelerationModes(accelerationModes) -+{ -+ auto accelerometer = qobject_cast(sensor); -+ if (accelerometer) { -+ connect(accelerometer, &QAccelerometer::accelerationModeChanged, -+ this, &OPenHarmonyAccelerometer::applyAccelerationMode); -+ applyAccelerationMode(accelerometer->accelerationMode()); -+ } -+} -+ -+bool OPenHarmonyAccelerometer::isFeatureSupported(QSensor::Feature feature) const -+{ -+ return (feature == QSensor::AccelerationMode) ? m_accelerationModes == AllModes : SensorBackend::isFeatureSupported(feature); -+} -+ -+void OPenHarmonyAccelerometer::dataReceived(const QJsonObject &json) -+{ -+ double x = json.value("x").toDouble(); -+ double y = json.value("y").toDouble(); -+ double z = json.value("z").toDouble(); -+ -+ if (sensor()->skipDuplicates() && qFuzzyCompare(m_reader.x(), x) && -+ qFuzzyCompare(m_reader.y(), y) && -+ qFuzzyCompare(m_reader.z(), z)) { -+ return; -+ } -+ -+ double timestamp = json.value("timestamp").toDouble(); -+ m_reader.setTimestamp(quint64(timestamp)); -+ m_reader.setX(x); -+ m_reader.setY(y); -+ m_reader.setZ(z); -+ -+ newReadingAvailable(); -+} -+ -+void OPenHarmonyAccelerometer::applyAccelerationMode(QAccelerometer::AccelerationMode accelerationMode) -+{ -+ switch (accelerationMode) { -+ case QAccelerometer::Gravity: -+ if (!(m_accelerationModes & Gravity)) { -+ qWarning() << "Gravity sensor missing"; -+ return; -+ } -+ setSensorType(E_GRAVITY); -+ break; -+ case QAccelerometer::User: -+ if (!(m_accelerationModes & LinearAcceleration)) { -+ qWarning() << "Linear acceleration sensor missing"; -+ return; -+ } -+ setSensorType(E_LINEAR_ACCELEROMETER); -+ break; -+ case QAccelerometer::Combined: -+ if (!(m_accelerationModes & Accelerometer)) { -+ qWarning() << "Accelerometer sensor missing"; -+ return; -+ } -+ setSensorType(E_ACCELEROMETER); -+ break; -+ } -+} -+ -diff --git a/src/plugins/sensors/openharmony/openharmonyaccelerometer.h b/src/plugins/sensors/openharmony/openharmonyaccelerometer.h -new file mode 100644 -index 00000000..a610d25b ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonyaccelerometer.h -@@ -0,0 +1,32 @@ -+#ifndef OPENHARMONYACCELEROMETER_H -+#define OPENHARMONYACCELEROMETER_H -+ -+#include -+ -+#include "sensorbackend.h" -+ -+class OPenHarmonyAccelerometer : public SensorBackend -+{ -+ Q_OBJECT -+ -+public: -+ enum AccelerationModes { -+ Accelerometer = 1, -+ Gravity = 2, -+ LinearAcceleration = 4, -+ AllModes = (Accelerometer | Gravity | LinearAcceleration) -+ }; -+ OPenHarmonyAccelerometer(int accelerationModes, QSensor *sensor, QObject *parent = nullptr); -+ bool isFeatureSupported(QSensor::Feature feature) const override; -+ -+protected Q_SLOTS: -+ void dataReceived(const QJsonObject &json) override; -+ -+private: -+ void applyAccelerationMode(QAccelerometer::AccelerationMode accelerationMode); -+ -+private: -+ int m_accelerationModes; -+}; -+ -+#endif // OPENHARMONYACCELEROMETER_H -diff --git a/src/plugins/sensors/openharmony/openharmonygyroscope.cpp b/src/plugins/sensors/openharmony/openharmonygyroscope.cpp -new file mode 100644 -index 00000000..349b7e2f ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonygyroscope.cpp -@@ -0,0 +1,33 @@ -+#include -+#include -+#include -+ -+#include "openharmonygyroscope.h" -+ -+OPenHarmonyGyroscope::OPenHarmonyGyroscope(int type, QSensor *sensor, QObject *parent) -+: SensorBackend(type, sensor, parent) -+{ -+ -+} -+ -+void OPenHarmonyGyroscope::dataReceived(const QJsonObject &json) -+{ -+ double x = qRadiansToDegrees(json.value("x").toDouble()); -+ double y = qRadiansToDegrees(json.value("y").toDouble()); -+ double z = qRadiansToDegrees(json.value("z").toDouble()); -+ double timestamp = json.value("timestamp").toDouble(); -+ -+ if (sensor()->skipDuplicates() && qFuzzyCompare(m_reader.x(), x) && -+ qFuzzyCompare(m_reader.y(), y) && -+ qFuzzyCompare(m_reader.z(), z)) { -+ return; -+ } -+ -+ m_reader.setTimestamp(quint64(timestamp)); -+ m_reader.setX(x); -+ m_reader.setY(y); -+ m_reader.setZ(z); -+ -+ newReadingAvailable(); -+} -+ -diff --git a/src/plugins/sensors/openharmony/openharmonygyroscope.h b/src/plugins/sensors/openharmony/openharmonygyroscope.h -new file mode 100644 -index 00000000..ff3e6d2a ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonygyroscope.h -@@ -0,0 +1,18 @@ -+#ifndef OPENHARMONYGYROSCOPE_H -+#define OPENHARMONYGYROSCOPE_H -+ -+#include -+ -+#include "sensorbackend.h" -+ -+class OPenHarmonyGyroscope : public SensorBackend -+{ -+ Q_OBJECT -+public: -+ OPenHarmonyGyroscope(int type, QSensor *sensor, QObject *parent = nullptr); -+ -+protected Q_SLOTS: -+ void dataReceived(const QJsonObject &json) override; -+}; -+ -+#endif // OPENHARMONYGYROSCOPE_H -diff --git a/src/plugins/sensors/openharmony/openharmonyhumidity.cpp b/src/plugins/sensors/openharmony/openharmonyhumidity.cpp -new file mode 100644 -index 00000000..e17df8cf ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonyhumidity.cpp -@@ -0,0 +1,25 @@ -+#include -+#include -+ -+#include "openharmonyhumidity.h" -+ -+OPenHarmonyHumidity::OPenHarmonyHumidity(int type, QSensor *sensor, QObject *parent) -+ : SensorBackend(type, sensor, parent) -+{ -+ -+} -+ -+void OPenHarmonyHumidity::dataReceived(const QJsonObject &json) -+{ -+ qreal humidity = json.value("humidity").toDouble(); -+ -+ if (sensor()->skipDuplicates() && qFuzzyCompare(humidity, m_reader.relativeHumidity())) -+ return; -+ -+ double timestamp = json.value("timestamp").toDouble(); -+ m_reader.setTimestamp(quint64(timestamp)); -+ m_reader.setAbsoluteHumidity(0.f); -+ m_reader.setRelativeHumidity(humidity); -+ newReadingAvailable(); -+} -+ -diff --git a/src/plugins/sensors/openharmony/openharmonyhumidity.h b/src/plugins/sensors/openharmony/openharmonyhumidity.h -new file mode 100644 -index 00000000..adbf4c9b ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonyhumidity.h -@@ -0,0 +1,18 @@ -+#ifndef OPENHARMONYHUMIDITY_H -+#define OPENHARMONYHUMIDITY_H -+ -+#include -+ -+#include "sensorbackend.h" -+ -+class OPenHarmonyHumidity : public SensorBackend -+{ -+ Q_OBJECT -+public: -+ OPenHarmonyHumidity(int type, QSensor *sensor, QObject *parent = nullptr); -+ -+protected Q_SLOTS: -+ void dataReceived(const QJsonObject &json) override; -+}; -+ -+#endif // OPENHARMONYHUMIDITY_H -diff --git a/src/plugins/sensors/openharmony/openharmonylight.cpp b/src/plugins/sensors/openharmony/openharmonylight.cpp -new file mode 100644 -index 00000000..7274df7c ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonylight.cpp -@@ -0,0 +1,23 @@ -+#include -+#include -+ -+#include "openharmonylight.h" -+ -+OPenHarmonyLight::OPenHarmonyLight(int type, QSensor *sensor, QObject *parent) -+ : SensorBackend(type, sensor, parent) -+{ -+ -+} -+ -+void OPenHarmonyLight::dataReceived(const QJsonObject &json) -+{ -+ double intensity = json.value("intensity").toDouble(); -+ -+ if (sensor()->skipDuplicates() && qFuzzyCompare(m_reader.lux(), intensity)) -+ return; -+ -+ double timestamp = json.value("timestamp").toDouble(); -+ m_reader.setTimestamp(quint64(timestamp)); -+ m_reader.setLux(qreal(intensity)); -+ newReadingAvailable(); -+} -diff --git a/src/plugins/sensors/openharmony/openharmonylight.h b/src/plugins/sensors/openharmony/openharmonylight.h -new file mode 100644 -index 00000000..f8039a92 ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonylight.h -@@ -0,0 +1,18 @@ -+#ifndef OPENHARMONYLIGHT_H -+#define OPENHARMONYLIGHT_H -+ -+#include -+ -+#include "sensorbackend.h" -+ -+class OPenHarmonyLight : public SensorBackend -+{ -+ Q_OBJECT -+public: -+ OPenHarmonyLight(int type, QSensor *sensor, QObject *parent = nullptr); -+ -+protected Q_SLOTS: -+ void dataReceived(const QJsonObject &json) override; -+}; -+ -+#endif // OPENHARMONYLIGHT_H -diff --git a/src/plugins/sensors/openharmony/openharmonymagnetometer.cpp b/src/plugins/sensors/openharmony/openharmonymagnetometer.cpp -new file mode 100644 -index 00000000..34e34b21 ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonymagnetometer.cpp -@@ -0,0 +1,32 @@ -+#include -+#include -+#include "openharmonymagnetometer.h" -+ -+OPenHarmonyMagnetometer::OPenHarmonyMagnetometer(int type, QSensor *sensor, QObject *parent) -+ : SensorBackend(type, sensor, parent) -+{ -+ -+} -+ -+void OPenHarmonyMagnetometer::dataReceived(const QJsonObject &json) -+{ -+ double x = json.value("x").toDouble(); -+ double y = json.value("y").toDouble(); -+ double z = json.value("z").toDouble(); -+ -+ if (sensor()->skipDuplicates() && qFuzzyCompare(m_accuracy, m_reader.calibrationLevel()) && -+ qFuzzyCompare(x, m_reader.x()) && -+ qFuzzyCompare(y, m_reader.y()) && -+ qFuzzyCompare(z, m_reader.z())) { -+ return; -+ } -+ -+ m_reader.setCalibrationLevel(m_accuracy); -+ double timestamp = json.value("timestamp").toDouble(); -+ m_reader.setTimestamp(quint64(timestamp)); -+ m_reader.setX(x); -+ m_reader.setY(y); -+ m_reader.setZ(z); -+ newReadingAvailable(); -+} -+ -diff --git a/src/plugins/sensors/openharmony/openharmonymagnetometer.h b/src/plugins/sensors/openharmony/openharmonymagnetometer.h -new file mode 100644 -index 00000000..c3662243 ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonymagnetometer.h -@@ -0,0 +1,18 @@ -+#ifndef OPENHARMONYMAGNETOMETER_H -+#define OPENHARMONYMAGNETOMETER_H -+ -+#include -+ -+#include "sensorbackend.h" -+ -+class OPenHarmonyMagnetometer : public SensorBackend -+{ -+ Q_OBJECT -+public: -+ OPenHarmonyMagnetometer(int type, QSensor *sensor, QObject *parent = nullptr); -+ -+protected Q_SLOTS: -+ void dataReceived(const QJsonObject &json) override; -+}; -+ -+#endif // OPENHARMONYMAGNETOMETER_H -diff --git a/src/plugins/sensors/openharmony/openharmonypressure.cpp b/src/plugins/sensors/openharmony/openharmonypressure.cpp -new file mode 100644 -index 00000000..c7d5d3d3 ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonypressure.cpp -@@ -0,0 +1,22 @@ -+#include -+#include -+ -+#include "openharmonypressure.h" -+ -+OPenHarmonyPressure::OPenHarmonyPressure(int type, QSensor *sensor, QObject *parent) -+ : SensorBackend(type, sensor, parent) -+{ -+ -+} -+ -+void OPenHarmonyPressure::dataReceived(const QJsonObject &json) -+{ -+ auto pressurePa = json.value("pressure").toDouble(); -+ if (sensor()->skipDuplicates() && qFuzzyCompare(pressurePa, m_reader.pressure())) -+ return; -+ -+ double timestamp = json.value("timestamp").toDouble(); -+ m_reader.setTimestamp(quint64(timestamp)); -+ m_reader.setPressure(pressurePa); -+ newReadingAvailable(); -+} -diff --git a/src/plugins/sensors/openharmony/openharmonypressure.h b/src/plugins/sensors/openharmony/openharmonypressure.h -new file mode 100644 -index 00000000..8c2db8aa ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonypressure.h -@@ -0,0 +1,18 @@ -+#ifndef OPENHARMONYPRESSURE_H -+#define OPENHARMONYPRESSURE_H -+ -+#include -+ -+#include "sensorbackend.h" -+ -+class OPenHarmonyPressure : public SensorBackend -+{ -+ Q_OBJECT -+public: -+ OPenHarmonyPressure(int type, QSensor *sensor, QObject *parent = nullptr); -+ -+protected Q_SLOTS: -+ void dataReceived(const QJsonObject &json) override; -+}; -+ -+#endif // OPENHARMONYPRESSURE_H -diff --git a/src/plugins/sensors/openharmony/openharmonyproximity.cpp b/src/plugins/sensors/openharmony/openharmonyproximity.cpp -new file mode 100644 -index 00000000..e96a0b24 ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonyproximity.cpp -@@ -0,0 +1,28 @@ -+#include -+#include -+ -+#include "openharmonyproximity.h" -+ -+OPenHarmonyProximity::OPenHarmonyProximity(int type, QSensor *sensor, QObject *parent) -+ : SensorBackend(type, sensor, parent) -+{ -+ m_maximumRange = m_jsSensor->maxRange(m_type); -+ -+ // if we can't get the range, we arbitrarily define anything closer than 10 cm as "close" -+ if (m_maximumRange <= 0) -+ m_maximumRange = 10.0; -+} -+ -+void OPenHarmonyProximity::dataReceived(const QJsonObject &json) -+{ -+ qreal distance = json.value("distance").toDouble(); -+ bool close = distance < m_maximumRange; -+ if (sensor()->skipDuplicates() && close == m_reader.close()) -+ return; -+ -+ double timestamp = json.value("timestamp").toDouble(); -+ m_reader.setTimestamp(quint64(timestamp)); -+ m_reader.setClose(close); -+ newReadingAvailable(); -+} -+ -diff --git a/src/plugins/sensors/openharmony/openharmonyproximity.h b/src/plugins/sensors/openharmony/openharmonyproximity.h -new file mode 100644 -index 00000000..f1ef02f2 ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonyproximity.h -@@ -0,0 +1,21 @@ -+#ifndef OPENHARMONYPROXIMITY_H -+#define OPENHARMONYPROXIMITY_H -+ -+#include -+ -+#include "sensorbackend.h" -+ -+class OPenHarmonyProximity : public SensorBackend -+{ -+ Q_OBJECT -+public: -+ OPenHarmonyProximity(int type, QSensor *sensor, QObject *parent = nullptr); -+ -+protected Q_SLOTS: -+ void dataReceived(const QJsonObject &json) override; -+ -+private: -+ qreal m_maximumRange; -+}; -+ -+#endif // OPENHARMONYPROXIMITY_H -diff --git a/src/plugins/sensors/openharmony/openharmonyrotation.cpp b/src/plugins/sensors/openharmony/openharmonyrotation.cpp -new file mode 100644 -index 00000000..a4d70e6f ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonyrotation.cpp -@@ -0,0 +1,29 @@ -+#include -+#include -+#include -+ -+#include "openharmonyrotation.h" -+ -+OPenHarmonyRotation::OPenHarmonyRotation(int type, QSensor *sensor, QObject *parent) -+ : SensorBackend(type, sensor, parent) -+{ -+ -+} -+ -+void OPenHarmonyRotation::dataReceived(const QJsonObject &json) -+{ -+ qreal rz = -qRadiansToDegrees(json.value("x").toDouble()); //corresponds to x -+ qreal rx = -qRadiansToDegrees(json.value("y").toDouble()); //corresponds to y -+ qreal ry = qRadiansToDegrees(json.value("z").toDouble()); //corresponds to z -+ if (sensor()->skipDuplicates() && qFuzzyCompare(m_reader.x(), rx) && -+ qFuzzyCompare(m_reader.y(), ry) && -+ qFuzzyCompare(m_reader.z(), rz)) { -+ return; -+ } -+ -+ double timestamp = json.value("timestamp").toDouble(); -+ m_reader.setTimestamp(quint64(timestamp)); -+ m_reader.setFromEuler(rx, ry, rz); -+ newReadingAvailable(); -+} -+ -diff --git a/src/plugins/sensors/openharmony/openharmonyrotation.h b/src/plugins/sensors/openharmony/openharmonyrotation.h -new file mode 100644 -index 00000000..03243797 ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonyrotation.h -@@ -0,0 +1,18 @@ -+#ifndef OPENHARMONYROTATION_H -+#define OPENHARMONYROTATION_H -+ -+#include -+ -+#include "sensorbackend.h" -+ -+class OPenHarmonyRotation : public SensorBackend -+{ -+ Q_OBJECT -+public: -+ OPenHarmonyRotation(int type, QSensor *sensor, QObject *parent = nullptr); -+ -+protected Q_SLOTS: -+ void dataReceived(const QJsonObject &json) override; -+}; -+ -+#endif // OPENHARMONYROTATION_H -diff --git a/src/plugins/sensors/openharmony/openharmonytemperature.cpp b/src/plugins/sensors/openharmony/openharmonytemperature.cpp -new file mode 100644 -index 00000000..a3865b03 ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonytemperature.cpp -@@ -0,0 +1,23 @@ -+#include -+#include -+ -+#include "openharmonytemperature.h" -+ -+OPenHarmonyTemperature::OPenHarmonyTemperature(int type, QSensor *sensor, QObject *parent) -+ : SensorBackend(type, sensor, parent) -+{ -+ -+} -+ -+void OPenHarmonyTemperature::dataReceived(const QJsonObject &json) -+{ -+ qreal temperature = json.value("temperature").toDouble(); -+ if (sensor()->skipDuplicates() && qFuzzyCompare(m_reader.temperature(), temperature)) -+ return; -+ -+ double timestamp = json.value("timestamp").toDouble(); -+ m_reader.setTimestamp(quint64(timestamp)); -+ m_reader.setTemperature(temperature); // in degree Celsius -+ newReadingAvailable(); -+} -+ -diff --git a/src/plugins/sensors/openharmony/openharmonytemperature.h b/src/plugins/sensors/openharmony/openharmonytemperature.h -new file mode 100644 -index 00000000..cc881f20 ---- /dev/null -+++ b/src/plugins/sensors/openharmony/openharmonytemperature.h -@@ -0,0 +1,18 @@ -+#ifndef OPENHARMONYTEMPERATURE_H -+#define OPENHARMONYTEMPERATURE_H -+ -+#include -+ -+#include "sensorbackend.h" -+ -+class OPenHarmonyTemperature : public SensorBackend -+{ -+ Q_OBJECT -+public: -+ OPenHarmonyTemperature(int type, QSensor *sensor, QObject *parent = nullptr); -+ -+protected Q_SLOTS: -+ void dataReceived(const QJsonObject &json) override; -+}; -+ -+#endif // OPENHARMONYTEMPERATURE_H -diff --git a/src/plugins/sensors/openharmony/plugin.json b/src/plugins/sensors/openharmony/plugin.json -new file mode 100644 -index 00000000..ff40b670 ---- /dev/null -+++ b/src/plugins/sensors/openharmony/plugin.json -@@ -0,0 +1 @@ -+{ "Keys": [ "openharmony" ] } -diff --git a/src/plugins/sensors/openharmony/sensorbackend.h b/src/plugins/sensors/openharmony/sensorbackend.h -new file mode 100644 -index 00000000..aa0907df ---- /dev/null -+++ b/src/plugins/sensors/openharmony/sensorbackend.h -@@ -0,0 +1,91 @@ -+#ifndef SENSORBACKEND_H -+#define SENSORBACKEND_H -+ -+#include -+#include -+ -+#include "sensormanager.h" -+ -+class SensorBackendBase : public QSensorBackend -+{ -+public: -+ SensorBackendBase(QSensor *sensor, QObject *parent = nullptr) -+ : QSensorBackend(sensor, parent) -+ , m_jsSensor(Q_NULLPTR) -+ { -+ QString tempUuid = QUuid::createUuid().toString(); -+ tempUuid.chop(1); //remove trailing '}' -+ tempUuid.remove(0,1); //remove first '{' -+ -+ m_jsSensor = SensorManager::instance()->createJsSensor(tempUuid, this); -+ } -+ -+public Q_SLOTS: -+ virtual void dataReceived(const QJsonObject &json) = 0; -+ -+protected: -+ QSharedPointer m_jsSensor; -+}; -+ -+template -+class SensorBackend : public SensorBackendBase -+{ -+public: -+ explicit SensorBackend(int type, QSensor *sensor, QObject *parent = nullptr) : m_type(0) -+ , SensorBackendBase{ sensor, parent } -+ { -+ setReading(&m_reader); -+ setSensorType(type); -+ m_accuracy = m_jsSensor->precision(type); -+ addDataRate(m_jsSensor->minSamplePeriod(type), -+ m_jsSensor->maxSamplePeriod(type)); -+ addOutputRange(0, m_jsSensor->maxRange(type), m_accuracy); -+ } -+ -+ ~SensorBackend() override -+ { -+ stop(); -+ } -+ -+ void setSensorType(int type) -+ { -+ m_type = type; -+ bool started = m_started; -+ if (started) -+ stop(); -+ -+ setDescription(m_jsSensor->description(type)); -+ if (started) -+ start(); -+ } -+ -+ virtual void start() override -+ { -+ m_jsSensor->start(m_type, sensor()->dataRate()); -+ m_started = true; -+ } -+ -+ virtual void stop() override -+ { -+ m_jsSensor->stop(m_type); -+ m_started = false; -+ } -+ -+ bool isFeatureSupported(QSensor::Feature feature) const override -+ { -+ switch (feature) { -+ case QSensor::SkipDuplicates: -+ return true; -+ default: -+ return false; -+ } -+ } -+ -+protected: -+ T m_reader; -+ int m_type; -+ qreal m_accuracy = 0.0f; -+ bool m_started = false; -+}; -+ -+#endif // SENSORBACKEND_H -diff --git a/src/plugins/sensors/openharmony/sensormanager.cpp b/src/plugins/sensors/openharmony/sensormanager.cpp -new file mode 100644 -index 00000000..b368d556 ---- /dev/null -+++ b/src/plugins/sensors/openharmony/sensormanager.cpp -@@ -0,0 +1,114 @@ -+#include "sensormanager.h" -+#include "QtCore/qopenharmonyjsobject.h" -+#include "QtCore/qopenharmonyjsenvironment.h" -+#include "QtCore/qopenharmonyjsobjectloader.h" -+ -+JsSensor::JsSensor(const QString &uuid) : m_uuid(uuid) -+ , m_jsSensor(Q_NULLPTR) -+{ -+ -+} -+ -+JsSensor::~JsSensor() -+{ -+ if (m_jsSensor) -+ m_jsSensor->call("destroySensor", m_uuid); -+} -+ -+qreal JsSensor::power(int id) const -+{ -+ qreal p = m_jsSensor->call("power", id); -+ return p; -+} -+ -+QString JsSensor::name(int id) const -+{ -+ QString n = m_jsSensor->call("sensorName", id); -+ return n; -+} -+ -+qreal JsSensor::maxRange(int id) const -+{ -+ qreal r = m_jsSensor->call("maxRange", id); -+ return r; -+} -+ -+qreal JsSensor::precision(int id) const -+{ -+ qreal accuracy = m_jsSensor->call("precision", id); -+ return accuracy; -+} -+ -+QString JsSensor::vendorName(int id) const -+{ -+ QString n = m_jsSensor->call("vendorName", id); -+ return n; -+} -+ -+QString JsSensor::description(int id) const -+{ -+ QString des = m_jsSensor->call("description", id); -+ return des; -+} -+ -+qreal JsSensor::minSamplePeriod(int id) const -+{ -+ qreal p = m_jsSensor->call("minSamplePeriod", id); -+ return p; -+} -+ -+qreal JsSensor::maxSamplePeriod(int id) const -+{ -+ qreal p = m_jsSensor->call("maxSamplePeriod", id); -+ return p; -+} -+ -+QString JsSensor::firmwareVersion(int id) const -+{ -+ QString v = m_jsSensor->call("firmwareVersion", id); -+ return v; -+} -+ -+void JsSensor::stop(int id) -+{ -+ m_jsSensor->callWithoutReturn("stop", id); -+} -+ -+void JsSensor::start(int id, int rate) -+{ -+ m_jsSensor->callWithoutReturn("start", id, rate); -+} -+ -+QString JsSensor::hardwareVersion(int id) const -+{ -+ QString hv = m_jsSensor->call("hardwareVersion", id); -+ return hv; -+} -+ -+SensorManager::SensorManager() { -+} -+ -+QList SensorManager::sensorIds() const -+{ -+ QSharedPointer jsSensorManager = qJsObjectLoader->create("JsSensorManager"); -+ QStringList ids = jsSensorManager->call("sensorIds"); -+ QList ns; -+ ns.reserve(ids.size()); -+ for (const auto &id : qAsConst(ids)) { -+ ns.append(id.toInt()); -+ } -+ return ns; -+} -+ -+QSharedPointer &SensorManager::instance() -+{ -+ static QSharedPointer s{ new SensorManager() }; -+ return s; -+} -+ -+QSharedPointer SensorManager::createJsSensor(const QString &uuid, SensorBackendBase *backend) -+{ -+ JsSensor *s = new JsSensor(uuid); -+ s->m_jsSensor = qJsObjectLoader->create("JsSensor", uuid, QVariantList() << QVariant(qlonglong(backend))); -+ return QSharedPointer(s); -+} -diff --git a/src/plugins/sensors/openharmony/sensormanager.h b/src/plugins/sensors/openharmony/sensormanager.h -new file mode 100644 -index 00000000..47c14df7 ---- /dev/null -+++ b/src/plugins/sensors/openharmony/sensormanager.h -@@ -0,0 +1,65 @@ -+#ifndef SENSORMANAGER_H -+#define SENSORMANAGER_H -+ -+#include -+#include -+ -+/* NOTE 鸿蒙和Qt传感器匹配的项 -+ * Qt对前端传感器模块进行了封装 -+ * 鸿蒙提供的传感器模块接口不是 -+ * 完全匹配,需要转换 -+ */ -+enum { -+ E_GRAVITY = 257, /* 重力传感器<--->QAccelerometer */ -+ E_HUMIDITY = 13, /* 湿度传感器<--->QHumiditySensor */ -+ E_BAROMETER = 8, /* 气压传感器<--->QPressureSensor */ -+ E_GYROSCOPE = 2, /* 陀螺仪传感器<--->QGyroscope */ -+ E_PROXIMITY = 12, /* 接近光传感器<--->QDistanceSensor */ -+ E_ORIENTATION = 256, /* 方向传感器<--->QOrientationSensor */ -+ E_ACCELEROMETER = 1, /* 加速度传感器<--->QAccelerometer */ -+ E_AMBIENT_LIGHT = 5, /* 环境光传感器<--->QAmbientLightSensor */ -+ E_MAGNETIC_FIELD = 6, /* 磁场传感器<--->QMagnetometer */ -+ E_ROTATION_VECTOR = 259, /* 旋转矢量传感器<--->QRotationSensor */ -+ E_AMBIENT_TEMPERATURE = 260, /* 环境温度传感器<--->QAmbientTemperatureSensor */ -+ E_LINEAR_ACCELEROMETER = 258, /* 线性加速度传感器<--->QAccelerometer */ -+}; -+ -+class SensorBackendBase; -+class QOpenHarmonyJsObject; -+ -+class JsSensor -+{ -+ QString m_uuid; -+ QSharedPointer m_jsSensor; -+ -+ friend class SensorManager; -+public: -+ JsSensor(const QString &uuid); -+ ~JsSensor(); -+ -+ qreal power(int id) const; -+ QString name(int id) const; -+ qreal maxRange(int id) const; -+ qreal precision(int id) const; -+ QString vendorName(int id) const; -+ QString description(int id) const; -+ qreal minSamplePeriod(int id) const; -+ qreal maxSamplePeriod(int id) const; -+ QString firmwareVersion(int id) const; -+ QString hardwareVersion(int id) const; -+ -+ void stop(int id); -+ void start(int id, int rate); -+}; -+ -+class SensorManager -+{ -+ SensorManager(); -+ -+public: -+ QList sensorIds() const; -+ static QSharedPointer &instance(); -+ QSharedPointer createJsSensor(const QString &uuid, SensorBackendBase *backend); -+}; -+ -+#endif // SENSORMANAGER_H -diff --git a/src/plugins/sensors/sensors.pro b/src/plugins/sensors/sensors.pro -index 7fce2071..8037ffc4 100644 ---- a/src/plugins/sensors/sensors.pro -+++ b/src/plugins/sensors/sensors.pro -@@ -5,6 +5,10 @@ android { - isEmpty(SENSORS_PLUGINS): SENSORS_PLUGINS = android generic - } - -+openharmony { -+ isEmpty(SENSORS_PLUGINS): SENSORS_PLUGINS = openharmony generic -+} -+ - qtConfig(sensorfw) { - isEmpty(SENSORS_PLUGINS): SENSORS_PLUGINS = sensorfw generic - } -@@ -39,6 +43,7 @@ isEmpty(SENSORS_PLUGINS)|contains(SENSORS_PLUGINS, simulator):qtHaveModule(simul - isEmpty(SENSORS_PLUGINS)|contains(SENSORS_PLUGINS, linux):linux:SUBDIRS += linux - isEmpty(SENSORS_PLUGINS)|contains(SENSORS_PLUGINS, iio-sensor-proxy):linux:qtHaveModule(dbus):SUBDIRS += iio-sensor-proxy - isEmpty(SENSORS_PLUGINS)|contains(SENSORS_PLUGINS, android):android:SUBDIRS += android -+isEmpty(SENSORS_PLUGINS)|contains(SENSORS_PLUGINS, openharmony):openharmony:SUBDIRS += openharmony - isEmpty(SENSORS_PLUGINS)|contains(SENSORS_PLUGINS, sensorfw):sensorfw:SUBDIRS += sensorfw - isEmpty(SENSORS_PLUGINS)|contains(SENSORS_PLUGINS, sensortag):linux:SUBDIRS += sensortag - isEmpty(SENSORS_PLUGINS)|contains(SENSORS_PLUGINS, ios):darwin:SUBDIRS += ios -diff --git a/src/src.pro b/src/src.pro -index b3aa7ba0..df12cad0 100644 ---- a/src/src.pro -+++ b/src/src.pro -@@ -16,3 +16,6 @@ plugins.subdir = plugins - plugins.target = sub-plugins - plugins.depends = sensors - -+SUBDIRS += openharmony -+openharmony.subdir = openharmony -+openharmony.target = sub-openharmony diff --git a/patch/v5.15.12/qt3d.patch b/patch/v5.15.12/qt3d.patch new file mode 100644 index 0000000000000000000000000000000000000000..40a12e6289eadaa89d263491c0056dc35bdb64b6 --- /dev/null +++ b/patch/v5.15.12/qt3d.patch @@ -0,0 +1,28 @@ +diff --git a/src/plugins/renderers/opengl/debug/imguirenderer.cpp b/src/plugins/renderers/opengl/debug/imguirenderer.cpp +index c4b1cf5f6..3d31fd218 100644 +--- a/src/plugins/renderers/opengl/debug/imguirenderer.cpp ++++ b/src/plugins/renderers/opengl/debug/imguirenderer.cpp +@@ -663,9 +663,11 @@ void ImGuiRenderer::onMouseChange(QMouseEvent *event) + + void ImGuiRenderer::onWheel(QWheelEvent *event) + { ++#if QT_CONFIG(wheelevent) + // 5 lines per unit + m_mouseWheelH += event->pixelDelta().x() / (ImGui::GetTextLineHeight()); + m_mouseWheel += event->pixelDelta().y() / (5.f * ImGui::GetTextLineHeight()); ++#endif + } + + void ImGuiRenderer::onKeyPressRelease(QKeyEvent *event) +@@ -701,9 +703,11 @@ void ImGuiRenderer::processEvent(QEvent *event) + case QEvent::MouseButtonRelease: + this->onMouseChange(static_cast(event)); + break; ++#if QT_CONFIG(wheelevent) + case QEvent::Wheel: + this->onWheel(static_cast(event)); + break; ++#endif + case QEvent::KeyPress: + case QEvent::KeyRelease: + this->onKeyPressRelease(static_cast(event)); diff --git a/patch/v5.15.12/qtbase.patch b/patch/v5.15.12/qtbase.patch index f51264abe8fab32652af764f161c48503f4e9d26..19d4ca26392e5c6099e9d09ec6aa1d842df0e115 100644 --- a/patch/v5.15.12/qtbase.patch +++ b/patch/v5.15.12/qtbase.patch @@ -44,10 +44,10 @@ index 0000000000..d7be212137 +QMAKE_CXXFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE diff --git a/mkspecs/common/oh-base-tail.conf b/mkspecs/common/oh-base-tail.conf new file mode 100644 -index 0000000000..5ee5cb4a57 +index 0000000000..4039af1e79 --- /dev/null +++ b/mkspecs/common/oh-base-tail.conf -@@ -0,0 +1,76 @@ +@@ -0,0 +1,78 @@ +equals(OHOS_ARCH, armeabi-v7a): \ + QMAKE_CFLAGS += -march=armv7a -msoft-float -mfpu=vfp -fno-builtin-memmove +else: equals(OHOS_ARCH, arm64-v8a): \ @@ -98,7 +98,9 @@ index 0000000000..5ee5cb4a57 + +QMAKE_RANLIB = $${CROSS_COMPILE}llvm-ranlib + -+QMAKE_INCDIR_POST = $$shell_path($$SDK_ROOT/native/sysroot/usr/include/$${NDK_TOOLCHAIN_PREFIX}) ++QMAKE_INCDIR_POST += $$shell_path($$SDK_ROOT/native/sysroot/usr/include) \ ++ $$shell_path($$SDK_ROOT/native/sysroot/usr/include/$${NDK_TOOLCHAIN_PREFIX}) ++ +QMAKE_LIBDIR_POST = $$shell_path($$SDK_ROOT/native/sysroot/usr/lib/$${NDK_TOOLCHAIN_PREFIX}) +QMAKE_INCDIR_X11 = +QMAKE_LIBDIR_X11 = @@ -107,8 +109,8 @@ index 0000000000..5ee5cb4a57 + +QMAKE_LINK_SHLIB = $$QMAKE_LINK +QMAKE_LFLAGS = --sysroot=$$shell_path($$SDK_ROOT/native/sysroot) -+QMAKE_LFLAGS_APP = -Wl,--no-undefined -Wl,-z,noexecstack -shared -+QMAKE_LFLAGS_SHLIB = -Wl,--no-undefined -Wl,-z,noexecstack -shared ++QMAKE_LFLAGS_APP = -Wl,--no-undefined -Wl,-z,noexecstack -shared ++QMAKE_LFLAGS_SHLIB = -Wl,--no-undefined -Wl,-z,noexecstack -shared +QMAKE_LFLAGS_PLUGIN = $$QMAKE_LFLAGS_SHLIB +QMAKE_LFLAGS_NOUNDEF = -Wl,--no-undefined +QMAKE_LFLAGS_RPATH = -Wl,-rpath= @@ -452,6 +454,43 @@ index ad5d30d544..72a026b182 100644 #elif defined(__CYGWIN__) # define Q_OS_CYGWIN #elif !defined(SAG_COM) && (!defined(WINAPI_FAMILY) || WINAPI_FAMILY==WINAPI_FAMILY_DESKTOP_APP) && (defined(WIN64) || defined(_WIN64) || defined(__WIN64__)) +diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri +index a33ffe75f2..721f07ddcc 100644 +--- a/src/corelib/io/io.pri ++++ b/src/corelib/io/io.pri +@@ -195,6 +195,10 @@ win32 { + SOURCES += \ + io/qstandardpaths_android.cpp \ + io/qstorageinfo_unix.cpp ++ } else:openharmony { ++ SOURCES += \ ++ io/qstandardpaths_openharmony.cpp \ ++ io/qstorageinfo_unix.cpp + } else:haiku { + SOURCES += \ + io/qstandardpaths_haiku.cpp \ +diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp +index 14ce49da56..536a6849e2 100644 +--- a/src/corelib/io/qfile.cpp ++++ b/src/corelib/io/qfile.cpp +@@ -864,7 +864,7 @@ QFile::copy(const QString &newName) + d->setError(QFile::CopyError, tr("Cannot open %1 for input").arg(d->fileName)); + } else { + const auto fileTemplate = QLatin1String("%1/qt_temp.XXXXXX"); +-#ifdef QT_NO_TEMPORARYFILE ++#if defined(QT_NO_TEMPORARYFILE) || defined(Q_OS_OPENHARMONY) + QFile out(fileTemplate.arg(QFileInfo(newName).path())); + if (!out.open(QIODevice::ReadWrite)) + error = true; +@@ -914,7 +914,7 @@ QFile::copy(const QString &newName) + d->setError(QFile::CopyError, tr("Cannot create %1 for output").arg(newName)); + } + } +-#ifdef QT_NO_TEMPORARYFILE ++#if defined(QT_NO_TEMPORARYFILE) || defined(Q_OS_OPENHARMONY) + if (error) + out.remove(); + #else diff --git a/src/corelib/io/qfileselector.cpp b/src/corelib/io/qfileselector.cpp index ee378f6434..1798a905c6 100644 --- a/src/corelib/io/qfileselector.cpp @@ -477,32 +516,190 @@ index ee378f6434..1798a905c6 100644 QString equivalentPath = scheme + filePath.path(); QString selectedPath = d->select(equivalentPath); -diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp -index 50512a4a73..3d6226cd2c 100644 ---- a/src/corelib/io/qurl.cpp -+++ b/src/corelib/io/qurl.cpp -@@ -445,6 +445,11 @@ static inline QString webDavScheme() - return QStringLiteral("webdavs"); - } +diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp +index 46e661094a..ed3a9f0a47 100644 +--- a/src/corelib/io/qsettings.cpp ++++ b/src/corelib/io/qsettings.cpp +@@ -1495,8 +1495,8 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile) + bool ok = false; + ensureAllSectionsParsed(confFile); + ParsedSettingsMap mergedKeys = confFile->mergedKeyMap(); +- +-#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile) ++/* FIXME 鸿蒙创建临时文件的权限问题,暂时使用QFile */ ++#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile) && !defined(Q_OS_OPENHARMONY) + QSaveFile sf(confFile->name); + sf.setDirectWriteFallback(!atomicSyncOnly); + #else +@@ -1525,11 +1525,10 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile) + ok = writeFunc(sf, tempOriginalKeys); + } -+static inline QString harmonyFileScheme() +-#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile) ++#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile) && !defined(Q_OS_OPENHARMONY) + if (ok) + ok = sf.commit(); + #endif +- + if (ok) { + confFile->unparsedIniSections.clear(); + confFile->originalKeys = mergedKeys; +diff --git a/src/corelib/io/qstandardpaths_openharmony.cpp b/src/corelib/io/qstandardpaths_openharmony.cpp +new file mode 100644 +index 0000000000..e4aa2cae6e +--- /dev/null ++++ b/src/corelib/io/qstandardpaths_openharmony.cpp +@@ -0,0 +1,137 @@ ++#include "qstandardpaths.h" ++ ++#ifndef QT_NO_STANDARDPATHS ++ ++#include ++#include ++#include ++#include ++ ++QT_BEGIN_NAMESPACE ++ ++typedef QMap OpenHarmonyDirCache; ++Q_GLOBAL_STATIC(OpenHarmonyDirCache, openHarmonyDirCache) ++ ++static QString testDir() +{ -+ return QStringLiteral("datashare"); ++ return QStandardPaths::isTestModeEnabled() ? QLatin1String("/qttest") ++ : QLatin1String(""); +} + - static inline QString webDavSslTag() - { - return QStringLiteral("@SSL"); -@@ -1018,6 +1023,9 @@ inline bool QUrlPrivate::setScheme(const QString &value, int len, bool doSetErro - if (scheme == fileScheme() - #ifdef Q_OS_WIN - || scheme == webDavScheme() -+#endif -+#ifdef Q_OS_OPENHARMONY -+ || scheme == harmonyFileScheme() - #endif - ) { - flags |= IsLocalFile; ++/* ++ * Locations where applications can place persistent files it owns. ++ * E.g., /storage/org.app/Music ++ */ ++static QString getDir(const char *directoryField = 0) ++{ ++ QString &path = (*openHarmonyDirCache)[QString(QLatin1String("APPNAME_%1")).arg(QLatin1String(directoryField))]; ++ if (!path.isEmpty()) ++ return path; ++ ++ static QSharedPointer jsStanardPaths; ++ if (jsStanardPaths.isNull()) ++ jsStanardPaths = qJsObjectLoader->create("JsStandardPaths"); ++ if (jsStanardPaths.isNull()) ++ return QString(); ++ QString result = jsStanardPaths->call("path", QString::fromLatin1(directoryField)); ++ path = result; ++ return result; ++} ++ ++QString QStandardPaths::writableLocation(StandardLocation type) ++{ ++ switch (type) { ++ case QStandardPaths::MusicLocation: ++ return writableLocation(QStandardPaths::AppLocalDataLocation) + QLatin1String("/Music"); ++// return getDir("DIRECTORY_MUSIC"); ++ case QStandardPaths::MoviesLocation: ++ return writableLocation(QStandardPaths::AppLocalDataLocation) + QLatin1String("/Videos"); ++// return getDir("DIRECTORY_MOVIES"); ++ case QStandardPaths::PicturesLocation: ++ return writableLocation(QStandardPaths::AppLocalDataLocation) + QLatin1String("/Pictures"); ++// return getDir("DIRECTORY_PICTURES"); ++ case QStandardPaths::DocumentsLocation: ++ return writableLocation(QStandardPaths::AppLocalDataLocation) + QLatin1String("/Documents"); ++ return getDir("DIRECTORY_DOCUMENTS"); ++ case QStandardPaths::DownloadLocation: ++ return writableLocation(QStandardPaths::AppLocalDataLocation) + QLatin1String("/Downloads"); ++// return getDir("DIRECTORY_DOWNLOADS"); ++ case QStandardPaths::GenericConfigLocation: ++ case QStandardPaths::ConfigLocation: ++ case QStandardPaths::AppConfigLocation: ++ return getDir("DIRECTORY_CACHE") + testDir() + QLatin1String("/settings"); ++ case QStandardPaths::GenericDataLocation: ++ return getDir("DIRECTORY_CACHE") + testDir(); ++ case QStandardPaths::AppDataLocation: ++ case QStandardPaths::AppLocalDataLocation: ++ return getDir("DIRECTORY_FILES") + testDir(); ++ case QStandardPaths::RuntimeLocation: ++ return getDir("DIRECTORY_CACHE"); ++ case QStandardPaths::TempLocation: ++ return getDir("DIRECTORY_TEMP"); ++ case QStandardPaths::GenericCacheLocation: ++ case QStandardPaths::CacheLocation: ++ return getDir("DIRECTORY_CACHE"); ++ case QStandardPaths::DesktopLocation: ++ case QStandardPaths::HomeLocation: ++ return writableLocation(QStandardPaths::AppLocalDataLocation) + QLatin1String("/Desktop"); ++// return getDir("DIRECTORY_DESKTOP"); ++ case QStandardPaths::ApplicationsLocation: ++ case QStandardPaths::FontsLocation: ++ default: ++ break; ++ } ++ ++ return QString(); ++} ++ ++QStringList QStandardPaths::standardLocations(StandardLocation type) ++{ ++ if (type == MusicLocation) { ++ return QStringList() << writableLocation(type); ++ } ++ ++ if (type == MoviesLocation) { ++ return QStringList() << writableLocation(type); ++ } ++ ++ if (type == PicturesLocation) { ++ return QStringList() << writableLocation(type); ++ } ++ ++ if (type == DocumentsLocation) { ++ return QStringList() << writableLocation(type); ++ } ++ ++ if (type == DownloadLocation) { ++ return QStringList() << writableLocation(type); ++ } ++ ++ if (type == AppDataLocation || type == AppLocalDataLocation) { ++ return QStringList() << writableLocation(type); ++ } ++ ++ if (type == CacheLocation) { ++ return QStringList() << writableLocation(type); ++ } ++ ++ if (type == FontsLocation) { ++ QString &fontLocation = (*openHarmonyDirCache)[QStringLiteral("FONT_LOCATION")]; ++ if (!fontLocation.isEmpty()) ++ return QStringList(fontLocation); ++ ++ const QByteArray ba = qgetenv("QT_OpenHarmony_FONT_LOCATION"); ++ if (!ba.isEmpty()) ++ return QStringList((fontLocation = QDir::cleanPath(QString::fromLocal8Bit(ba)))); ++ ++ // Don't cache the fallback, as we might just have been called before ++ // QT_OpenHarmony_FONT_LOCATION has been set. ++ return QStringList(QLatin1String("/system/fonts")); ++ } ++ ++ return QStringList(writableLocation(type)); ++} ++ ++QT_END_NAMESPACE ++ ++#endif // QT_NO_STANDARDPATHS +diff --git a/src/corelib/io/qtemporaryfile_p.h b/src/corelib/io/qtemporaryfile_p.h +index 6bcff936b4..bec3494a2f 100644 +--- a/src/corelib/io/qtemporaryfile_p.h ++++ b/src/corelib/io/qtemporaryfile_p.h +@@ -58,7 +58,7 @@ + #include "private/qfile_p.h" + #include "qtemporaryfile.h" + +-#if defined(Q_OS_LINUX) && QT_CONFIG(linkat) ++#if defined(Q_OS_LINUX) && QT_CONFIG(linkat) && !defined(Q_OS_OPENHARMONY) + # include + # ifdef O_TMPFILE + // some early libc support had the wrong values for O_TMPFILE diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri index 749672c899..602dce2d9f 100644 --- a/src/corelib/kernel/kernel.pri @@ -660,7 +857,7 @@ index 0000000000..7480dc1c20 +#endif // QOPENHARMONYDEFINES_H diff --git a/src/corelib/kernel/qopenharmonyhelpers.cpp b/src/corelib/kernel/qopenharmonyhelpers.cpp new file mode 100644 -index 0000000000..9d2664cae6 +index 0000000000..8a85fb5ded --- /dev/null +++ b/src/corelib/kernel/qopenharmonyhelpers.cpp @@ -0,0 +1,132 @@ @@ -747,7 +944,7 @@ index 0000000000..9d2664cae6 + ret = qJs::getRect(value); + break; + default: -+ //Todo other type ++ ret = qJs::createQVariantFromNapiValue(t, value); + break; + } + return ret; @@ -847,10 +1044,10 @@ index 0000000000..0c0bd12c64 +#endif // QJNIHELPERS_H diff --git a/src/corelib/kernel/qopenharmonyjsenvironment.cpp b/src/corelib/kernel/qopenharmonyjsenvironment.cpp new file mode 100644 -index 0000000000..9fb36e755e +index 0000000000..31da31e358 --- /dev/null +++ b/src/corelib/kernel/qopenharmonyjsenvironment.cpp -@@ -0,0 +1,425 @@ +@@ -0,0 +1,487 @@ +#include "qopenharmonyjsenvironment.h" +#include "qopenharmonydefines.h" +#include "qopenharmonyhelpers_p.h" @@ -859,14 +1056,30 @@ index 0000000000..9fb36e755e +#include + +static napi_threadsafe_function g_threadsafe_call_js_function = nullptr; ++static napi_threadsafe_function g_threadsafe_runonjs_thread_function = nullptr; +napi_env QOpenHarmonyJsEnvironment::m_env = nullptr; +bool QOpenHarmonyJsEnvironment::m_exit = false; +static QHash g_creators; ++static QHash > g_qvariant_creators; ++static std::thread::id g_jsThreadId; ++struct JsThreadParam ++{ ++ std::function f; ++}; + +static void cleanup_creators() +{ + qDeleteAll(g_creators); + g_creators.clear(); ++ g_qvariant_creators.clear(); ++} ++ ++static void _runOnJsThread(napi_env env, napi_value js_cb, void* context, void* data) ++{ ++ JsThreadParam *js = (JsThreadParam *)data; ++ if (js == nullptr); ++ js->f(); ++ delete js; +} + +static void callJsFunction(napi_env env, napi_value js_cb, void* context, void* data) { @@ -901,7 +1114,15 @@ index 0000000000..9fb36e755e + if (g_threadsafe_call_js_function == nullptr) { + LOGW("init call js function failed"); + } ++ ++ napi_create_string_utf8(env, "RunOnJsThread", NAPI_AUTO_LENGTH, &name); ++ napi_create_threadsafe_function(env, nullptr, nullptr, name, 0, 1, nullptr, ++ nullptr, nullptr, _runOnJsThread, &g_threadsafe_runonjs_thread_function); ++ if (g_threadsafe_runonjs_thread_function == nullptr) { ++ LOGW("init run on js thread function failed"); ++ } + qAddPostRoutine(cleanup_creators); ++ g_jsThreadId = std::this_thread::get_id(); +} + +void QOpenHarmonyJsEnvironment::quit() @@ -942,14 +1163,27 @@ index 0000000000..9fb36e755e + return result; +} + -+napi_value QOpenHarmonyJsEnvironment::createObject(const QVariantMap &map) ++napi_value QOpenHarmonyJsEnvironment::createObject() +{ + napi_value result = nullptr; + NAPI_CALL_BASE(m_env, napi_create_object(m_env, &result), nullptr); ++ return result; ++} ++ ++void QOpenHarmonyJsEnvironment::setProperty(napi_value o, const QString &key, const QVariant &value) ++{ ++ napi_value name = createString(key); ++ napi_value v = QtHarmonyPrivate::variant_to_napi_value(value); ++ napi_set_property(m_env, o, name, v); ++} ++ ++napi_value QOpenHarmonyJsEnvironment::createObject(const QVariantMap &map) ++{ ++ napi_value result = createObject(); ++ if (result == nullptr) ++ return nullptr; + for (auto i = map.cbegin(); i != map.cend(); ++i){ -+ napi_value name = createString(i.key()); -+ napi_value v = QtHarmonyPrivate::variant_to_napi_value(i.value()); -+ napi_set_property(m_env, result, name, v); ++ setProperty(result, i.key(), i.value()); + } + return result; +} @@ -962,11 +1196,32 @@ index 0000000000..9fb36e755e + return c->create(value); +} + ++QVariant QOpenHarmonyJsEnvironment::createQVariantFromNapiValue(int type, napi_value value) ++{ ++ if (!g_qvariant_creators.contains(type)) ++ return QVariant(); ++ return g_qvariant_creators.value(type)(value); ++} ++ +void QOpenHarmonyJsEnvironment::registerCreator(int type, NapiValueCreator *creator) +{ + g_creators.insert(type, creator); +} + ++void QOpenHarmonyJsEnvironment::runOnJsThread(const std::function &f) ++{ ++ if (m_exit || g_threadsafe_runonjs_thread_function == nullptr) ++ return; ++ JsThreadParam *p = new JsThreadParam; ++ p->f = f; ++ napi_call_threadsafe_function(g_threadsafe_runonjs_thread_function, p, napi_tsfn_blocking) == napi_ok; ++} ++ ++void QOpenHarmonyJsEnvironment::registerQVariantCreator(int type, const std::function &f) ++{ ++ g_qvariant_creators.insert(type, f); ++} ++ +napi_value QOpenHarmonyJsEnvironment::globalObject() { + napi_value _global; + NAPI_CALL(m_env, napi_get_global(m_env, &_global)); @@ -984,7 +1239,6 @@ index 0000000000..9fb36e755e +{ + if (m_exit || g_threadsafe_call_js_function == nullptr) + return false; -+ napi_acquire_threadsafe_function(g_threadsafe_call_js_function); + return napi_call_threadsafe_function(g_threadsafe_call_js_function, func, napi_tsfn_blocking) == napi_ok; +} + @@ -1276,19 +1530,24 @@ index 0000000000..9fb36e755e + int64_t height = getInt64(objectPropertyValue(value, "height")); + return QRect(left, top, width, height); +} ++ ++std::thread::id QOpenHarmonyJsEnvironment::jsThreadId() ++{ ++ return g_jsThreadId; ++} diff --git a/src/corelib/kernel/qopenharmonyjsenvironment.h b/src/corelib/kernel/qopenharmonyjsenvironment.h new file mode 100644 -index 0000000000..0718f7ee9a +index 0000000000..11a2c2bd64 --- /dev/null +++ b/src/corelib/kernel/qopenharmonyjsenvironment.h -@@ -0,0 +1,156 @@ +@@ -0,0 +1,175 @@ +#ifndef QOPENHARMONYJSENVIRONMENT_H +#define QOPENHARMONYJSENVIRONMENT_H + +#include +#include +#include -+ ++#include +QT_BEGIN_NAMESPACE +#define qJs QOpenHarmonyJsEnvironment +class QOpenHarmonyJsFunction; @@ -1302,7 +1561,7 @@ index 0000000000..0718f7ee9a +template +struct UserTypeNapiValueCreator : NapiValueCreator +{ -+ UserTypeNapiValueCreator(std::function &f) : _f(f) {} ++ UserTypeNapiValueCreator(const std::function &f) : _f(f) {} + virtual napi_value create(const QVariant &value) + { + if (_f && value.canConvert()) { @@ -1323,6 +1582,8 @@ index 0000000000..0718f7ee9a + + static void quit(); + ++ static std::thread::id jsThreadId(); ++ + static void throwError(const QString &errorMessage); + + static napi_env env() { return m_env; } @@ -1341,20 +1602,37 @@ index 0000000000..0718f7ee9a + + static napi_value createArrayBuffer(const QByteArray &buffer); + ++ static napi_value createObject(); ++ ++ static void setProperty(napi_value o, const QString &key, const QVariant &value); ++ + static napi_value createObject(const QVariantMap &map); + + static napi_value createObjectFromUserType(const QVariant &value); + ++ static QVariant createQVariantFromNapiValue(int type, napi_value value); ++ + template -+ static void registerCreator(std::function &f) ++ static void registerCreator(const std::function &f) + { + int id = qMetaTypeId(); + NapiValueCreator *creator = new UserTypeNapiValueCreator(f); + registerCreator(id, creator); + } + ++ template ++ static void registerQVariantCreator(const std::function &f) ++ { ++ int id = qMetaTypeId(); ++ registerQVariantCreator(id, f); ++ } ++ + static void registerCreator(int type, NapiValueCreator *creator); + ++ static void runOnJsThread(const std::function &f); ++ ++ static void registerQVariantCreator(int type, const std::function &f); ++ + static napi_value globalObject(); + + static napi_value globalThis() @@ -1440,10 +1718,10 @@ index 0000000000..0718f7ee9a +#endif // QOPENHARMONYJSENVIRONMENT_H diff --git a/src/corelib/kernel/qopenharmonyjsfunction.cpp b/src/corelib/kernel/qopenharmonyjsfunction.cpp new file mode 100644 -index 0000000000..dbe216de8f +index 0000000000..3488f9c73e --- /dev/null +++ b/src/corelib/kernel/qopenharmonyjsfunction.cpp -@@ -0,0 +1,207 @@ +@@ -0,0 +1,208 @@ +#include "qopenharmonyjsfunction.h" +#include "qopenharmonyjsenvironment.h" +#include "qopenharmonydefines.h" @@ -1510,6 +1788,7 @@ index 0000000000..dbe216de8f + } + if (!m_bResultReceived.loadAcquire()) { + LOGW("Wait js method %{public}s result failded", qPrintable(m_name)); ++ m_bResultReceived.storeRelease(true); + } +} + @@ -1753,10 +2032,10 @@ index 0000000000..17fb051a7b +#endif // QOPENHARMONYJSFUNCTION_H diff --git a/src/corelib/kernel/qopenharmonyjsobject.cpp b/src/corelib/kernel/qopenharmonyjsobject.cpp new file mode 100644 -index 0000000000..1680e85b69 +index 0000000000..4bc0750e2a --- /dev/null +++ b/src/corelib/kernel/qopenharmonyjsobject.cpp -@@ -0,0 +1,99 @@ +@@ -0,0 +1,102 @@ +#include "qopenharmonyjsobject.h" +#include "qopenharmonydefines.h" +#include "qopenharmonyjsfunction.h" @@ -1794,7 +2073,10 @@ index 0000000000..1680e85b69 + +QOpenHarmonyJsObject::~QOpenHarmonyJsObject() +{ -+ napi_delete_reference(qJs::env(), d_ptr->m_jsObject); ++ napi_ref object = d_ptr->m_jsObject; ++ qJs::runOnJsThread([object]{ ++ napi_delete_reference(qJs::env(), object); ++ }); +} + +bool QOpenHarmonyJsObject::isValid() const @@ -1943,10 +2225,10 @@ index 0000000000..6540813735 +#endif // QOPENHARMONYEGLCORE_H diff --git a/src/corelib/kernel/qopenharmonyjsobjectloader.cpp b/src/corelib/kernel/qopenharmonyjsobjectloader.cpp new file mode 100644 -index 0000000000..ef698869ab +index 0000000000..20ec43c33c --- /dev/null +++ b/src/corelib/kernel/qopenharmonyjsobjectloader.cpp -@@ -0,0 +1,170 @@ +@@ -0,0 +1,181 @@ +#include "qopenharmonyjsobjectloader.h" +#include "qopenharmonyjsobject.h" +#include "qopenharmonyjsobjectpool.h" @@ -1974,7 +2256,9 @@ index 0000000000..ef698869ab +}; + +static napi_threadsafe_function g_threadsafe_new_object_function = nullptr; ++static napi_ref g_new_object_function_ref = nullptr; +static napi_threadsafe_function g_threadsafe_remove_object_function = nullptr; ++static napi_ref g_remove_object_function_ref = nullptr; + +struct ObjectParams { + QOpenHarmonyJsObjectLoaderPrivate *loader; @@ -1983,6 +2267,14 @@ index 0000000000..ef698869ab + QString name; +}; + ++static void cleanup() ++{ ++ napi_release_threadsafe_function(g_threadsafe_new_object_function, napi_tsfn_release); ++ napi_release_threadsafe_function(g_threadsafe_remove_object_function, napi_tsfn_release); ++ napi_delete_reference(qJs::env(), g_new_object_function_ref); ++ napi_delete_reference(qJs::env(), g_remove_object_function_ref); ++} ++ +static void callNewObject(napi_env env, napi_value js_cb, void* context, void* data) { + Q_UNUSED(env); + Q_UNUSED(context); @@ -2029,7 +2321,9 @@ index 0000000000..ef698869ab + } + + napi_value newObject = args[0]; ++ napi_create_reference(env, newObject, 1, &g_new_object_function_ref); + napi_value removeObject = args[1]; ++ napi_create_reference(env, removeObject, 1, &g_remove_object_function_ref); + + napi_value name_newObject; + napi_create_string_utf8(env, "JsObjectLoader", NAPI_AUTO_LENGTH, &name_newObject); @@ -2043,6 +2337,7 @@ index 0000000000..ef698869ab + + if (g_threadsafe_new_object_function == nullptr || g_threadsafe_remove_object_function == nullptr) + return qJs::createBool(false); ++ qAddPostRoutine(cleanup); + return qJs::createBool(true); +} + @@ -2079,7 +2374,6 @@ index 0000000000..ef698869ab + p->name = _objectName; + p->type = objectType; + p->loader = d_ptr.data(); -+ napi_acquire_threadsafe_function(g_threadsafe_new_object_function); + napi_call_threadsafe_function(g_threadsafe_new_object_function, p, napi_tsfn_blocking); + d_ptr->wait(objectType, _objectName, 3000); + } @@ -2089,7 +2383,6 @@ index 0000000000..ef698869ab +void QOpenHarmonyJsObjectLoader::remove(const QString &name) +{ + if (g_threadsafe_remove_object_function != nullptr) { -+ napi_acquire_threadsafe_function(g_threadsafe_remove_object_function); + QByteArray dataArray = name.toLatin1(); + char *_data = dataArray.data(); + napi_call_threadsafe_function(g_threadsafe_remove_object_function, (void *)_data, napi_tsfn_blocking); @@ -2378,6 +2671,303 @@ index 582f48e93e..d9032f61ed 100644 # endif QMutexLocker locker(qt_factoryloader_mutex()); +diff --git a/src/corelib/text/qlocale_harmony.cpp b/src/corelib/text/qlocale_harmony.cpp +new file mode 100644 +index 0000000000..c74e0d4ee5 +--- /dev/null ++++ b/src/corelib/text/qlocale_harmony.cpp +@@ -0,0 +1,274 @@ ++#include "qlocale_p.h" ++#include "qstringbuilder.h" ++#include "qdatetime.h" ++#include "qstringlist.h" ++#include "qvariant.h" ++#include "qreadwritelock.h" ++ ++#include "qopenharmonydefines.h" ++#include "qopenharmonyjsobject.h" ++#include "qopenharmonyjsobjectloader.h" ++ ++QT_BEGIN_NAMESPACE ++ ++#ifndef QT_NO_SYSTEMLOCALE ++struct QSystemLocaleData ++{ ++ QSystemLocaleData() ++ : lc_numeric(QLocale::C) ++ , lc_time(QLocale::C) ++ , lc_monetary(QLocale::C) ++ , lc_messages(QLocale::C) ++ , m_jsLocale(nullptr) ++ { ++ readEnvironment(); ++ } ++ ++ void readEnvironment(); ++ ++ QReadWriteLock lock; ++ ++ QLocale lc_numeric; ++ QLocale lc_time; ++ QLocale lc_monetary; ++ QLocale lc_messages; ++ QByteArray lc_messages_var; ++ QByteArray lc_measurement_var; ++ QByteArray lc_collate_var; ++ QStringList uiLanguages; ++ QSharedPointer m_jsLocale; ++}; ++ ++void QSystemLocaleData::readEnvironment() ++{ ++ QWriteLocker locker(&lock); ++ m_jsLocale = qJsObjectLoader->create("JsLocale"); ++ QString tmpAll = m_jsLocale->call("getSystemLocale"); ++ QString tmpLanguage = m_jsLocale->call("getSystemLanguage"); ++ QByteArray all = tmpAll.toLocal8Bit(); ++ QByteArray language = tmpLanguage.toLocal8Bit(); ++ ++ QByteArray numeric = all.isEmpty() ? language : all; ++ QByteArray time = all.isEmpty() ? language : all; ++ QByteArray monetary = all.isEmpty() ? language : all; ++ lc_messages_var = all.isEmpty() ? language : all; ++ lc_measurement_var = all.isEmpty() ? language : all; ++ lc_collate_var = all.isEmpty() ? language : all; ++ QByteArray lang = language; ++ if (lang.isEmpty()) ++ lang = QByteArray("C"); ++ if (numeric.isEmpty()) ++ numeric = lang; ++ if (time.isEmpty()) ++ time = lang; ++ if (monetary.isEmpty()) ++ monetary = lang; ++ if (lc_messages_var.isEmpty()) ++ lc_messages_var = lang; ++ if (lc_measurement_var.isEmpty()) ++ lc_measurement_var = lang; ++ if (lc_collate_var.isEmpty()) ++ lc_collate_var = lang; ++ lc_numeric = QLocale(QString::fromLatin1(numeric)); ++ lc_time = QLocale(QString::fromLatin1(time)); ++ lc_monetary = QLocale(QString::fromLatin1(monetary)); ++ lc_messages = QLocale(QString::fromLatin1(lc_messages_var)); ++} ++ ++Q_GLOBAL_STATIC(QSystemLocaleData, qSystemLocaleData) ++ ++#endif ++ ++#ifndef QT_NO_SYSTEMLOCALE ++ ++static bool contradicts(const QString &maybe, const QString &known) ++{ ++ if (maybe.isEmpty()) ++ return false; ++ ++ /* ++ If \a known (our current best shot at deciding which language to use) ++ provides more information (e.g. script, country) than \a maybe (a ++ candidate to replace \a known) and \a maybe agrees with \a known in what ++ it does provide, we keep \a known; this happens when \a maybe comes from ++ LANGUAGE (usually a simple language code) and LANG includes script and/or ++ country. A textual comparison won't do because, for example, bn (Bengali) ++ isn't a prefix of ben_IN, but the latter is a refinement of the former. ++ (Meanwhile, bn is a prefix of bnt, Bantu; and a prefix of ben is be, ++ Belarusian. There are many more such prefixings between two- and ++ three-letter codes.) ++ */ ++ QLocale::Language langm, langk; ++ QLocale::Script scriptm, scriptk; ++ QLocale::Country landm, landk; ++ QLocalePrivate::getLangAndCountry(maybe, langm, scriptm, landm); ++ QLocalePrivate::getLangAndCountry(known, langk, scriptk, landk); ++ return (langm != QLocale::AnyLanguage && langm != langk) ++ || (scriptm != QLocale::AnyScript && scriptm != scriptk) ++ || (landm != QLocale::AnyCountry && landm != landk); ++} ++ ++QLocale QSystemLocale::fallbackUiLocale() const ++{ ++ QString lang = qSystemLocaleData()->m_jsLocale->call("getSystemLocale"); ++ if (lang.isEmpty()) ++ lang = qSystemLocaleData()->m_jsLocale->call("getSystemLanguage"); ++ if (lang.isEmpty()) ++ lang = qSystemLocaleData()->m_jsLocale->call("getSystemRegion"); ++ // if the locale is the "C" locale, then we can return the language we found here: ++ if (lang.isEmpty() || lang == QLatin1String("C") || lang == QLatin1String("POSIX")) ++ return QLocale(lang); ++ ++ ++ QString prefer = qSystemLocaleData()->m_jsLocale->call("getFirstPreferredLanguage"); ++ if (!prefer.isEmpty()) ++ return QLocale(prefer); ++ ++ return QLocale(QLocale::C); ++#if 0 ++ QString language = qSystemLocaleData()->m_jsLocale->call("getSystemLocale"); ++ if (!language.isEmpty()) { ++ language = language.split(QLatin1Char(':')).constFirst(); ++ if (contradicts(language, lang)) ++ return QLocale(language); ++ } ++ return QLocale(lang); ++#endif ++} ++ ++QVariant QSystemLocale::query(QueryType type, QVariant in) const ++{ ++ QSystemLocaleData *d = qSystemLocaleData(); ++ ++ if (type == LocaleChanged) { ++ d->readEnvironment(); ++ return QVariant(); ++ } ++ ++ QReadLocker locker(&d->lock); ++ ++ const QLocale &lc_numeric = d->lc_numeric; ++ const QLocale &lc_time = d->lc_time; ++ const QLocale &lc_monetary = d->lc_monetary; ++ const QLocale &lc_messages = d->lc_messages; ++ ++ switch (type) { ++ case DecimalPoint: ++ return lc_numeric.decimalPoint(); ++ case GroupSeparator: ++ return lc_numeric.groupSeparator(); ++ case ZeroDigit: ++ return lc_numeric.zeroDigit(); ++ case NegativeSign: ++ return lc_numeric.negativeSign(); ++ case DateFormatLong: ++ return lc_time.dateFormat(QLocale::LongFormat); ++ case DateFormatShort: ++ return lc_time.dateFormat(QLocale::ShortFormat); ++ case TimeFormatLong: ++ return lc_time.timeFormat(QLocale::LongFormat); ++ case TimeFormatShort: ++ return lc_time.timeFormat(QLocale::ShortFormat); ++ case DayNameLong: ++ return lc_time.dayName(in.toInt(), QLocale::LongFormat); ++ case DayNameShort: ++ return lc_time.dayName(in.toInt(), QLocale::ShortFormat); ++ case MonthNameLong: ++ return lc_time.monthName(in.toInt(), QLocale::LongFormat); ++ case MonthNameShort: ++ return lc_time.monthName(in.toInt(), QLocale::ShortFormat); ++ case StandaloneMonthNameLong: ++ return lc_time.standaloneMonthName(in.toInt(), QLocale::LongFormat); ++ case StandaloneMonthNameShort: ++ return lc_time.standaloneMonthName(in.toInt(), QLocale::ShortFormat); ++ case DateToStringLong: ++ return lc_time.toString(in.toDate(), QLocale::LongFormat); ++ case DateToStringShort: ++ return lc_time.toString(in.toDate(), QLocale::ShortFormat); ++ case TimeToStringLong: ++ return lc_time.toString(in.toTime(), QLocale::LongFormat); ++ case TimeToStringShort: ++ return lc_time.toString(in.toTime(), QLocale::ShortFormat); ++ case DateTimeFormatLong: ++ return lc_time.dateTimeFormat(QLocale::LongFormat); ++ case DateTimeFormatShort: ++ return lc_time.dateTimeFormat(QLocale::ShortFormat); ++ case DateTimeToStringLong: ++ return lc_time.toString(in.toDateTime(), QLocale::LongFormat); ++ case DateTimeToStringShort: ++ return lc_time.toString(in.toDateTime(), QLocale::ShortFormat); ++ case PositiveSign: ++ return lc_numeric.positiveSign(); ++ case AMText: ++ return lc_time.amText(); ++ case PMText: ++ return lc_time.pmText(); ++ case FirstDayOfWeek: ++ return lc_time.firstDayOfWeek(); ++ case CurrencySymbol: ++ return lc_monetary.currencySymbol(QLocale::CurrencySymbolFormat(in.toUInt())); ++ case CurrencyToString: { ++ switch (in.userType()) { ++ case QMetaType::Int: ++ return lc_monetary.toCurrencyString(in.toInt()); ++ case QMetaType::UInt: ++ return lc_monetary.toCurrencyString(in.toUInt()); ++ case QMetaType::Double: ++ return lc_monetary.toCurrencyString(in.toDouble()); ++ case QMetaType::LongLong: ++ return lc_monetary.toCurrencyString(in.toLongLong()); ++ case QMetaType::ULongLong: ++ return lc_monetary.toCurrencyString(in.toULongLong()); ++ default: ++ break; ++ } ++ return QString(); ++ } ++ case MeasurementSystem: { ++ const QString meas_locale = QString::fromLatin1(d->lc_measurement_var); ++ if (meas_locale.compare(QLatin1String("Metric"), Qt::CaseInsensitive) == 0) ++ return QLocale::MetricSystem; ++ if (meas_locale.compare(QLatin1String("Other"), Qt::CaseInsensitive) == 0) ++ return QLocale::MetricSystem; ++ return QVariant((int)QLocale(meas_locale).measurementSystem()); ++ } ++ case Collation: ++ return QString::fromLatin1(d->lc_collate_var); ++ case UILanguages: { ++ if (!d->uiLanguages.isEmpty()) ++ return d->uiLanguages; ++ QString languages = QString::fromLatin1(qSystemLocaleData()->m_jsLocale->call("getSystemLocale")); ++ QStringList lst; ++ if (languages.isEmpty()) ++ lst.append(QString::fromLatin1(d->lc_messages_var)); ++ else ++ lst = languages.split(QLatin1Char(':')); ++ ++ for (int i = 0; i < lst.size(); ++i) { ++ const QString &name = lst.at(i); ++ QString lang, script, cntry; ++ if (qt_splitLocaleName(name, lang, script, cntry)) { ++ if (!cntry.length()) ++ d->uiLanguages.append(lang); ++ else ++ d->uiLanguages.append(lang % QLatin1Char('-') % cntry); ++ } ++ } ++ return d->uiLanguages.isEmpty() ? QVariant() : QVariant(d->uiLanguages); ++ } ++ case StringToStandardQuotation: ++ return lc_messages.quoteString(qvariant_cast(in)); ++ case StringToAlternateQuotation: ++ return lc_messages.quoteString(qvariant_cast(in), QLocale::AlternateQuotation); ++ case ListToSeparatedString: ++ return lc_messages.createSeparatedList(in.toStringList()); ++ case LocaleChanged: ++ Q_ASSERT(false); ++ default: ++ break; ++ } ++ return QVariant(); ++} ++#endif // QT_NO_SYSTEMLOCALE ++ ++QT_END_NAMESPACE +diff --git a/src/corelib/text/text.pri b/src/corelib/text/text.pri +index d2a02059c7..d565a21c0d 100644 +--- a/src/corelib/text/text.pri ++++ b/src/corelib/text/text.pri +@@ -52,7 +52,11 @@ false: SOURCES += $$NO_PCH_SOURCES # Hack for QtCreator + SOURCES += text/qlocale_mac.mm + } + else:unix { +- SOURCES += text/qlocale_unix.cpp ++ openharmony { ++ SOURCES += text/qlocale_harmony.cpp ++ } else { ++ SOURCES += text/qlocale_unix.cpp ++ } + } + else:win32 { + SOURCES += text/qlocale_win.cpp diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index a27782d37c..f900b4eb5c 100644 --- a/src/corelib/thread/qthread_unix.cpp @@ -2419,10 +3009,28 @@ index a27782d37c..f900b4eb5c 100644 #else pthread_setcancelstate(enabled ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE, nullptr); diff --git a/src/gui/configure.json b/src/gui/configure.json -index 1f08795c57..a14a499e4a 100644 +index 1f08795c57..af84bac739 100644 --- a/src/gui/configure.json +++ b/src/gui/configure.json -@@ -1306,7 +1306,7 @@ +@@ -1096,6 +1096,17 @@ + "pkg-config-variable": "prefix", + "value": "/usr", + "log": "value" ++ }, ++ "wheelevent": { ++ "label": "Harmony mouse wheel event", ++ "type": "compile", ++ "test": { ++ "include": "arkui/ui_input_event.h", ++ "main": [ ++ "ArkUI_UIInputEvent *event = nullptr;" ++ ], ++ "qmake": "LIBS += -lace_ndk.z" ++ } + } + }, + +@@ -1306,7 +1317,7 @@ "label": "OpenGL ES 2.0", "enable": "input.opengl == 'es2' || input.angle == 'yes'", "disable": "input.opengl == 'desktop' || input.opengl == 'dynamic' || input.opengl == 'no'", @@ -2431,7 +3039,7 @@ index 1f08795c57..a14a499e4a 100644 "output": [ "publicFeature", "publicQtConfig", -@@ -1316,7 +1316,7 @@ +@@ -1316,7 +1327,7 @@ }, "opengles3": { "label": "OpenGL ES 3.0", @@ -2440,6 +3048,14 @@ index 1f08795c57..a14a499e4a 100644 "output": [ "publicFeature", { "type": "define", "name": "QT_OPENGL_ES_3" } +@@ -1674,6 +1685,7 @@ + "label": "QWheelEvent", + "purpose": "Supports wheel events.", + "section": "Kernel", ++ "condition": "config.openharmony && tests.wheelevent || !config.openharmony", + "output": [ "publicFeature", "feature" ] + }, + "tabletevent": { diff --git a/src/gui/configure.pri b/src/gui/configure.pri index 490ef0df28..537fd3ff69 100644 --- a/src/gui/configure.pri @@ -2468,241 +3084,1554 @@ index 990272b0c2..b1b539bfb3 100644 } /*! -diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp -index 507417f86c..a29678555e 100644 ---- a/src/network/access/qnetworkaccessfilebackend.cpp -+++ b/src/network/access/qnetworkaccessfilebackend.cpp -@@ -57,6 +57,9 @@ QStringList QNetworkAccessFileBackendFactory::supportedSchemes() const - << QStringLiteral("qrc"); - #if defined(Q_OS_ANDROID) - schemes << QStringLiteral("assets"); -+#endif -+#if defined(Q_OS_OPENHARMONY) -+ schemes << QStringLiteral("rawfile"); - #endif - return schemes; - } -@@ -80,6 +83,9 @@ QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op, - if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0 - #if defined(Q_OS_ANDROID) - || url.scheme().compare(QLatin1String("assets"), Qt::CaseInsensitive) == 0 -+#endif -+#if defined(Q_OS_OPENHARMONY) -+ || url.scheme().compare(QLatin1String("rawfile"), Qt::CaseInsensitive) == 0 - #endif - || url.isLocalFile()) { - return new QNetworkAccessFileBackend; -@@ -136,6 +142,11 @@ void QNetworkAccessFileBackend::open() - if (url.scheme() == QLatin1String("assets")) - fileName = QLatin1String("assets:") + url.path(); - else -+#endif -+#if defined(Q_OS_OPENHARMONY) -+ if (url.scheme() == QLatin1String("rawfile")) -+ fileName = QLatin1String("rawfile:") + url.path(); -+ else - #endif - fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery); - } -diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp -index a10fe9e3fe..7c363279a2 100644 ---- a/src/network/access/qnetworkaccessmanager.cpp -+++ b/src/network/access/qnetworkaccessmanager.cpp -@@ -1419,6 +1419,9 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera - if (isLocalFile - #ifdef Q_OS_ANDROID - || scheme == QLatin1String("assets") -+#endif -+#ifdef Q_OS_OPENHARMONY -+ || scheme == QLatin1String("rawfile") - #endif - || scheme == QLatin1String("qrc")) { - return new QNetworkReplyFileImpl(this, req, op); -diff --git a/src/network/access/qnetworkreplyfileimpl.cpp b/src/network/access/qnetworkreplyfileimpl.cpp -index 6e69b4c4d3..ff8ff19361 100644 ---- a/src/network/access/qnetworkreplyfileimpl.cpp -+++ b/src/network/access/qnetworkreplyfileimpl.cpp -@@ -110,6 +110,11 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con - if (scheme == QLatin1String("assets")) - fileName = QLatin1String("assets:") + url.path(); - else -+#endif -+#if defined(Q_OS_OPENHARMONY) -+ if (scheme == QLatin1String("rawfile")) -+ fileName = QLatin1String("rawfile:") + url.path(); -+ else - #endif - fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery); - } -diff --git a/src/openharmony/entryability/EntryAbility.ts b/src/openharmony/entryability/EntryAbility.ts +diff --git a/src/gui/util/adapter_c.cpp b/src/gui/util/adapter_c.cpp new file mode 100644 -index 0000000000..6a4626655c +index 0000000000..18e4736b0b --- /dev/null -+++ b/src/openharmony/entryability/EntryAbility.ts -@@ -0,0 +1,44 @@ -+import hilog from '@ohos.hilog'; -+import UIAbility from '@ohos.app.ability.UIAbility'; -+import Window from '@ohos.window' -+import JsApplication from '../native/QtCore/JsApplication' -+import JsDataStore from '../native/QtCore/JsDataStore' -+import JSLogger from '../native/QtCore/JsLogger' ++++ b/src/gui/util/adapter_c.cpp +@@ -0,0 +1,895 @@ ++// Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. All rights reserved. + -+export default class EntryAbility extends UIAbility { ++#include "adapter_c.h" ++#include + -+ onCreate(want, launchParam) { -+ JsDataStore.setContext(this.context); -+ JsDataStore.setElementName({ -+ bundleName: want.bundleName, -+ abilityName: want.abilityName, -+ moduleName: want.moduleName -+ }); -+ } ++static thread_local std::thread::id main_thread_id; ++static napi_env MainEnv = nullptr; ++thread_local napi_ref AddChildNodeCallback = nullptr; ++thread_local napi_ref RemoveChildNodeCallback = nullptr; ++thread_local napi_ref ResizeNodeCallback = nullptr; ++thread_local napi_ref AdjustNodeZOrderCallback = nullptr; ++thread_local napi_ref SetNodeVisibilityCallback = nullptr; ++thread_local napi_ref GetNodeRectCallback = nullptr; ++thread_local napi_ref GetObjectCallback = nullptr; ++thread_local napi_ref ReParentNodeCallback = nullptr; ++thread_local napi_ref RaiseNodeCallback = nullptr; ++thread_local napi_ref LowerNodeCallback = nullptr; ++thread_local napi_ref MoveNodeCallback = nullptr; ++thread_local napi_ref GetRootNodeCallBack = nullptr; ++ ++napi_value InitFunc(napi_env env, napi_callback_info info) { ++ size_t argc = 2; ++ napi_value args[2] = {nullptr}; + -+ onDestroy() { -+ JSLogger.info('%{public}s', 'Ability onDestroy'); -+ JsApplication.quit(); -+ } ++ napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + -+ onWindowStageCreate(windowStage: Window.WindowStage) { -+ // Main window is created, set main page for this ability -+ JSLogger.info('%{public}s', 'Ability onWindowStageCreate'); -+ JsApplication.run(windowStage); ++ size_t length = 0; ++ if (napi_ok != napi_get_value_string_utf8(env, args[0], nullptr, 0, &length)) { ++ napi_throw_error(env, "-1003", "napi_get_value_string_utf8 error"); ++ return nullptr; + } + -+ onWindowStageDestroy() { -+ // Main window is destroyed, release UI related resources -+ JSLogger.info('%{public}s', 'Ability onWindowStageDestroy'); ++ if (length == 0) { ++ napi_throw_error(env, "-1004", "the param length invalid"); ++ return nullptr; + } + -+ onForeground() { -+ // Ability has brought to foreground -+ JSLogger.info('%{public}s', 'Ability onForeground'); ++ char *funcStrBuffer = new char[length + 1]; ++ if (napi_ok != napi_get_value_string_utf8(env, args[0], funcStrBuffer, length + 1, &length)) { ++ delete[] funcStrBuffer; ++ funcStrBuffer = nullptr; ++ napi_throw_error(env, "-1005", "napi_get_value_string_utf8 error"); ++ return nullptr; + } ++ if (strcmp(funcStrBuffer, "AddChildNode") == 0) { ++ napi_create_reference(env, args[1], 1, &AddChildNodeCallback); ++ } else if (strcmp(funcStrBuffer, "RemoveChildNode") == 0) { ++ napi_create_reference(env, args[1], 1, &RemoveChildNodeCallback); ++ } else if (strcmp(funcStrBuffer, "ResizeNode") == 0) { ++ napi_create_reference(env, args[1], 1, &ResizeNodeCallback); ++ } else if (strcmp(funcStrBuffer, "AdjustNodeZOrder") == 0) { ++ napi_create_reference(env, args[1], 1, &AdjustNodeZOrderCallback); ++ } else if (strcmp(funcStrBuffer, "SetNodeVisibility") == 0) { ++ napi_create_reference(env, args[1], 1, &SetNodeVisibilityCallback); ++ } else if (strcmp(funcStrBuffer, "GetNodeRect") == 0) { ++ napi_create_reference(env, args[1], 1, &GetNodeRectCallback); ++ } else if (strcmp(funcStrBuffer, "GetObject") == 0) { ++ napi_create_reference(env, args[1], 1, &GetObjectCallback); ++ } else if (strcmp(funcStrBuffer, "ReParentNode") == 0) { ++ napi_create_reference(env, args[1], 1, &ReParentNodeCallback); ++ } else if (strcmp(funcStrBuffer, "RaiseNode") == 0) { ++ napi_create_reference(env, args[1], 1, &RaiseNodeCallback); ++ } else if (strcmp(funcStrBuffer, "LowerNode") == 0) { ++ napi_create_reference(env, args[1], 1, &LowerNodeCallback); ++ } else if (strcmp(funcStrBuffer, "MoveNode") == 0) { ++ napi_create_reference(env, args[1], 1, &MoveNodeCallback); ++ } else if (strcmp(funcStrBuffer, "GetRootNode") == 0) { ++ napi_create_reference(env, args[1], 1, &GetRootNodeCallBack); ++ } ++ ++ delete[] funcStrBuffer; ++ return nullptr; ++} ++ ++napi_ref GetRootNode(int windowId) { ++ napi_ref resultRef = nullptr; ++ std::thread::id cur_thread_id = std::this_thread::get_id(); ++ if (cur_thread_id == main_thread_id) { ++ napi_value id; ++ napi_create_int32(MainEnv, windowId, &id); ++ ++ napi_value callback; ++ napi_get_reference_value(MainEnv, GetRootNodeCallBack, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 1, &id, &result); ++ ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call GetRootNode Failed!"); ++ } ++ napi_create_reference(MainEnv, result, 1, &resultRef); ++ } else { ++ WorkParam *workParam = new WorkParam{windowId, std::make_shared()}; ++ uv_after_work_cb afterFunc = [](uv_work_t *work, int _status) { ++ WorkParam *param = (WorkParam *)(work->data); ++ ++ napi_value id; ++ napi_create_int32(MainEnv, param->windowId, &id); ++ ++ napi_value callback; ++ napi_get_reference_value(MainEnv, GetRootNodeCallBack, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 1, &id, &result); ++ ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call GetRootNode Failed!"); ++ param->replyStatus = false; ++ } else { ++ param->replyStatus = true; ++ } ++ napi_create_reference(MainEnv, result, 1, &(param->replyParam)); ++ ++ std::unique_lock lock(param->lockInfo->mutex); ++ param->lockInfo->ready = true; ++ param->lockInfo->condition.notify_all(); ++ }; ++ ++ if (GetThreadWorkResult(workParam, afterFunc)) { ++ resultRef = workParam->replyParam; ++ } ++ delete workParam; ++ } ++ return resultRef; ++} ++ ++void configNode(const NodeParams &nodeParams, napi_value *nodeParamsNapi) { ++ napi_value width; ++ napi_create_string_utf8(MainEnv, nodeParams.width.c_str(), nodeParams.width.length(), &width); ++ napi_value height; ++ napi_create_string_utf8(MainEnv, nodeParams.height.c_str(), nodeParams.height.length(), &height); ++ napi_value x; ++ napi_create_string_utf8(MainEnv, nodeParams.x.c_str(), nodeParams.x.length(), &x); ++ napi_value y; ++ napi_create_string_utf8(MainEnv, nodeParams.y.c_str(), nodeParams.y.length(), &y); ++ napi_value nodeType; ++ napi_create_int32(MainEnv, nodeParams.nodeType, &nodeType); ++ ++ napi_value id; ++ napi_create_string_utf8(MainEnv, nodeParams.componentModel->id.c_str(), nodeParams.componentModel->id.length(), ++ &id); ++ napi_value type; ++ napi_create_int32(MainEnv, nodeParams.componentModel->type, &type); ++ napi_value libraryName; ++ napi_create_string_utf8(MainEnv, nodeParams.componentModel->libraryName.c_str(), ++ nodeParams.componentModel->libraryName.length(), &libraryName); ++ ++ napi_value model; ++ napi_create_object(MainEnv, &model); ++ napi_set_named_property(MainEnv, model, "id", id); ++ napi_set_named_property(MainEnv, model, "type", type); ++ napi_set_named_property(MainEnv, model, "libraryname", libraryName); ++ if (nodeParams.componentModel->onLoad) { ++ napi_value onload; ++ onload = (napi_value)nodeParams.componentModel->onLoad; ++ napi_set_named_property(MainEnv, model, "onLoad", onload); ++ } ++ if (nodeParams.componentModel->onDestroy) { ++ napi_value onDestroy; ++ onDestroy = (napi_value)nodeParams.componentModel->onDestroy; ++ napi_set_named_property(MainEnv, model, "onDestroy", onDestroy); ++ } ++ ++ napi_create_object(MainEnv, nodeParamsNapi); ++ napi_set_named_property(MainEnv, *nodeParamsNapi, "width", width); ++ napi_set_named_property(MainEnv, *nodeParamsNapi, "height", height); ++ napi_set_named_property(MainEnv, *nodeParamsNapi, "position_x", x); ++ napi_set_named_property(MainEnv, *nodeParamsNapi, "position_y", y); ++ napi_set_named_property(MainEnv, *nodeParamsNapi, "node_type", nodeType); ++ napi_set_named_property(MainEnv, *nodeParamsNapi, "node_xcomponent", model); ++} ++ ++bool GetThreadWorkResult(WorkParam *workParam, uv_after_work_cb uvAfterWork) { ++ OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Adapter_c", "RunCallbackType begin"); ++ uv_loop_s *loop = nullptr; ++ napi_get_uv_event_loop(MainEnv, &loop); ++ if (loop == nullptr) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Failed to get_uv_event_loop"); ++ return false; ++ } ++ uv_work_t *work = new uv_work_t; ++ if (work == nullptr) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Failed to new uv_work_t"); ++ delete workParam; ++ return false; ++ } ++ work->data = (void *)(workParam); ++ uv_work_cb uvWork = [](uv_work_t *work) {}; ++ uv_queue_work(loop, work, uvWork, uvAfterWork); ++ ++ OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Adapter_c", "childThread lock and wait"); ++ std::unique_lock lock(workParam->lockInfo->mutex); ++ workParam->lockInfo->condition.wait(lock, [&workParam] { return workParam->lockInfo->ready; }); ++ OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Adapter_c", "wait work end"); ++ ++ delete work; ++ return workParam->replyStatus; ++} ++ ++napi_ref AddChildNode(napi_ref nodeRef, NodeParams *nodeParams) { ++ napi_ref resultRef = nullptr; ++ std::thread::id cur_thread_id = std::this_thread::get_id(); ++ if (cur_thread_id == main_thread_id) { ++ // Invoked in the JS main thread. ++ napi_value nodeParamsNapi; ++ configNode(*nodeParams, &nodeParamsNapi); ++ napi_value callback; ++ napi_get_reference_value(MainEnv, AddChildNodeCallback, &callback); ++ ++ napi_value node; ++ napi_get_reference_value(MainEnv, nodeRef, &node); ++ // 创建ArkTS function的入参。 ++ napi_value args[2]; ++ args[0] = node; ++ args[1] = nodeParamsNapi; ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 2, args, &result); ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call AddChildNode Failed!"); ++ } ++ napi_create_reference(MainEnv, result, 1, &resultRef); ++ } else { ++ // Not invoked in the JS main thread. ++ WorkParam *workParam = new WorkParam(nodeRef, nodeParams, std::make_shared()); ++ uv_after_work_cb afterFunc = [](uv_work_t *work, int _status) { ++ WorkParam *param = (WorkParam *)(work->data); ++ ++ napi_value nodeParamsNapi; ++ configNode(*param->nodeParams, &nodeParamsNapi); ++ ++ napi_value node; ++ if (napi_ok != napi_get_reference_value(MainEnv, param->nodeRef, &node)) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", ++ "AddChildNode napi_get_reference_value Failed!"); ++ } ++ // 创建ArkTS function的入参。 ++ napi_value args[2]; ++ args[0] = node; ++ args[1] = nodeParamsNapi; ++ ++ napi_value callback; ++ napi_get_reference_value(MainEnv, AddChildNodeCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 2, args, &result); ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call AddChildNodeFailed!"); ++ param->replyStatus = false; ++ } else { ++ param->replyStatus = true; ++ } ++ napi_create_reference(MainEnv, result, 1, &(param->replyParam)); ++ ++ std::unique_lock lock(param->lockInfo->mutex); ++ param->lockInfo->ready = true; ++ param->lockInfo->condition.notify_all(); ++ }; ++ ++ if (GetThreadWorkResult(workParam, afterFunc)) { ++ resultRef = workParam->replyParam; ++ } ++ ++ delete workParam; ++ } ++ return resultRef; ++} ++ ++bool RemoveChildNode(napi_ref nodeChildRef) { ++ bool res = false; ++ std::thread::id cur_thread_id = std::this_thread::get_id(); ++ if (cur_thread_id == main_thread_id) { ++ napi_value node; ++ napi_get_reference_value(MainEnv, nodeChildRef, &node); ++ ++ napi_value args[1]; ++ args[0] = node; ++ ++ napi_value callback; ++ napi_get_reference_value(MainEnv, RemoveChildNodeCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 1, args, &result); ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call RemoveChildNodeCallback Failed!"); ++ res = false; ++ } else { ++ res = true; ++ } ++ } else { ++ WorkParam *workParam = new WorkParam(nodeChildRef, std::make_shared()); ++ uv_after_work_cb afterFunc = [](uv_work_t *work, int _status) { ++ WorkParam *param = (WorkParam *)(work->data); ++ ++ napi_value node; ++ napi_get_reference_value(MainEnv, param->nodeRef, &node); ++ ++ napi_value args[1]; ++ args[0] = node; ++ ++ napi_value callback; ++ napi_get_reference_value(MainEnv, RemoveChildNodeCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 1, args, &result); ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call RemoveChildNodeCallback Failed!"); ++ param->replyStatus = false; ++ } else { ++ param->replyStatus = true; ++ } ++ napi_create_reference(MainEnv, result, 1, &(param->replyParam)); ++ ++ std::unique_lock lock(param->lockInfo->mutex); ++ param->lockInfo->ready = true; ++ param->lockInfo->condition.notify_all(); ++ }; ++ ++ res = GetThreadWorkResult(workParam, afterFunc); ++ delete workParam; ++ } ++ return res; ++} ++ ++bool ReleaseNodeRef(napi_ref nodeRef) { ++ bool res = false; ++ std::thread::id cur_thread_id = std::this_thread::get_id(); ++ if (cur_thread_id == main_thread_id) { ++ return napi_delete_reference(MainEnv, nodeRef) == napi_ok; ++ } else { ++ WorkParam *workParam = new WorkParam{nodeRef, std::make_shared()}; ++ uv_after_work_cb afterFunc = [](uv_work_t *work, int _status) { ++ WorkParam *param = (WorkParam *)(work->data); ++ ++ param->replyStatus = napi_delete_reference(MainEnv, param->nodeRef) == napi_ok; ++ ++ std::unique_lock lock(param->lockInfo->mutex); ++ param->lockInfo->ready = true; ++ param->lockInfo->condition.notify_all(); ++ }; ++ ++ res = GetThreadWorkResult(workParam, afterFunc); ++ delete workParam; ++ } ++ return res; ++} ++ ++bool RaiseNode(napi_ref nodeRef) { ++ bool res = false; ++ std::thread::id cur_thread_id = std::this_thread::get_id(); ++ if (cur_thread_id == main_thread_id) { ++ napi_value node; ++ napi_get_reference_value(MainEnv, nodeRef, &node); ++ ++ napi_value args[1]; ++ args[0] = node; ++ ++ napi_value callback; ++ napi_get_reference_value(MainEnv, RaiseNodeCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 1, &node, &result); ++ ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call RaiseNode Failed!"); ++ res = false; ++ } else { ++ res = true; ++ } ++ } else { ++ WorkParam *workParam = new WorkParam{nodeRef, std::make_shared()}; ++ uv_after_work_cb afterFunc = [](uv_work_t *work, int _status) { ++ WorkParam *param = (WorkParam *)(work->data); ++ ++ napi_value node; ++ napi_get_reference_value(MainEnv, param->nodeRef, &node); ++ ++ napi_value args[1]; ++ args[0] = node; ++ ++ napi_value callback; ++ napi_get_reference_value(MainEnv, RaiseNodeCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 1, args, &result); ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call RaiseNode Failed!"); ++ param->replyStatus = false; ++ } else { ++ param->replyStatus = true; ++ } ++ napi_create_reference(MainEnv, result, 1, &(param->replyParam)); ++ ++ std::unique_lock lock(param->lockInfo->mutex); ++ param->lockInfo->ready = true; ++ param->lockInfo->condition.notify_all(); ++ }; ++ ++ res = GetThreadWorkResult(workParam, afterFunc); ++ delete workParam; ++ } ++ return res; ++} ++ ++bool LowerNode(napi_ref nodeRef) { ++ bool res = false; ++ std::thread::id cur_thread_id = std::this_thread::get_id(); ++ if (cur_thread_id == main_thread_id) { ++ napi_value node; ++ napi_get_reference_value(MainEnv, nodeRef, &node); ++ ++ napi_value callback; ++ napi_get_reference_value(MainEnv, LowerNodeCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 1, &node, &result); ++ ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call LowerNode Failed!"); ++ res = false; ++ } else { ++ res = true; ++ } ++ } else { ++ WorkParam *workParam = new WorkParam{nodeRef, std::make_shared()}; ++ uv_after_work_cb afterFunc = [](uv_work_t *work, int _status) { ++ WorkParam *param = (WorkParam *)(work->data); ++ ++ napi_value node; ++ napi_get_reference_value(MainEnv, param->nodeRef, &node); ++ ++ napi_value callback; ++ napi_get_reference_value(MainEnv, LowerNodeCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 1, &node, &result); ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call LowerNode Failed!"); ++ param->replyStatus = false; ++ } else { ++ param->replyStatus = true; ++ } ++ napi_create_reference(MainEnv, result, 1, &(param->replyParam)); ++ ++ std::unique_lock lock(param->lockInfo->mutex); ++ param->lockInfo->ready = true; ++ param->lockInfo->condition.notify_all(); ++ }; ++ ++ res = GetThreadWorkResult(workParam, afterFunc); ++ delete workParam; ++ } ++ return res; ++} ++ ++bool ResizeNode(napi_ref nodeRef, std::string width, std::string height) { ++ bool res = false; ++ std::thread::id cur_thread_id = std::this_thread::get_id(); ++ if (cur_thread_id == main_thread_id) { ++ napi_value callback; ++ napi_value args[3]; ++ ++ napi_value w; ++ napi_create_string_utf8(MainEnv, width.c_str(), NAPI_AUTO_LENGTH, &w); ++ ++ napi_value h; ++ napi_create_string_utf8(MainEnv, height.c_str(), NAPI_AUTO_LENGTH, &h); ++ ++ napi_value node; ++ napi_get_reference_value(MainEnv, nodeRef, &node); ++ ++ args[0] = node; ++ args[1] = w; ++ args[2] = h; ++ ++ napi_get_reference_value(MainEnv, ResizeNodeCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 3, args, &result); ++ ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call ResizeNode Failed!"); ++ res = false; ++ } else { ++ res = true; ++ } ++ } else { ++ WorkParam *workParam = ++ new WorkParam(nodeRef, width, height, WorkParam::SIZE_FLAG, std::make_shared()); ++ uv_after_work_cb afterFunc = [](uv_work_t *work, int _status) { ++ WorkParam *param = (WorkParam *)(work->data); ++ napi_value callback; ++ napi_value args[3]; ++ ++ napi_value w; ++ napi_create_string_utf8(MainEnv, param->width.c_str(), NAPI_AUTO_LENGTH, &w); ++ ++ napi_value h; ++ napi_create_string_utf8(MainEnv, param->height.c_str(), NAPI_AUTO_LENGTH, &h); ++ ++ napi_value node; ++ napi_get_reference_value(MainEnv, param->nodeRef, &node); ++ ++ args[0] = node; ++ args[1] = w; ++ args[2] = h; ++ ++ napi_get_reference_value(MainEnv, ResizeNodeCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 3, args, &result); ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call ResizeNode Failed!"); ++ param->replyStatus = false; ++ } else { ++ param->replyStatus = true; ++ } ++ napi_create_reference(MainEnv, result, 1, &(param->replyParam)); ++ ++ std::unique_lock lock(param->lockInfo->mutex); ++ param->lockInfo->ready = true; ++ param->lockInfo->condition.notify_all(); ++ }; ++ ++ res = GetThreadWorkResult(workParam, afterFunc); ++ delete workParam; ++ } ++ return res; ++} ++ ++bool ReParentNode(napi_ref nodeParentNewRef, napi_ref nodeChildRef) { ++ bool res = false; ++ std::thread::id cur_thread_id = std::this_thread::get_id(); ++ if (cur_thread_id == main_thread_id) { ++ napi_value nodeParentNew; ++ napi_get_reference_value(MainEnv, nodeParentNewRef, &nodeParentNew); ++ ++ napi_value nodeChild; ++ napi_get_reference_value(MainEnv, nodeChildRef, &nodeChild); ++ ++ napi_value args[2]; ++ args[0] = nodeParentNew; ++ args[1] = nodeChild; ++ ++ napi_value callback; ++ napi_get_reference_value(MainEnv, ReParentNodeCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 2, args, &result); ++ ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call ReParentNode Failed!"); ++ res = false; ++ } else { ++ res = true; ++ } ++ } else { ++ WorkParam *workParam = new WorkParam(nodeParentNewRef, nodeChildRef, std::make_shared()); ++ uv_after_work_cb afterFunc = [](uv_work_t *work, int _status) { ++ WorkParam *param = (WorkParam *)(work->data); ++ ++ napi_value nodeParentNew; ++ napi_get_reference_value(MainEnv, param->nodeRef, &nodeParentNew); ++ ++ napi_value nodeChild; ++ napi_get_reference_value(MainEnv, param->nodeChildRef, &nodeChild); ++ ++ napi_value args[2]; ++ args[0] = nodeParentNew; ++ args[1] = nodeChild; ++ ++ napi_value callback; ++ napi_get_reference_value(MainEnv, ReParentNodeCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 2, args, &result); ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call ReParentNode Failed!"); ++ param->replyStatus = false; ++ } else { ++ param->replyStatus = true; ++ } ++ napi_create_reference(MainEnv, result, 1, &(param->replyParam)); ++ ++ std::unique_lock lock(param->lockInfo->mutex); ++ param->lockInfo->ready = true; ++ param->lockInfo->condition.notify_all(); ++ }; ++ ++ res = GetThreadWorkResult(workParam, afterFunc); ++ delete workParam; ++ } ++ return res; ++} ++ ++bool ShowNode(napi_ref nodeRef) { ++ bool res = false; ++ std::thread::id cur_thread_id = std::this_thread::get_id(); ++ if (cur_thread_id == main_thread_id) { ++ napi_value node; ++ napi_get_reference_value(MainEnv, nodeRef, &node); ++ ++ napi_value visibility; ++ napi_create_int32(MainEnv, 0, &visibility); ++ ++ napi_value args[2]; ++ args[0] = node; ++ args[1] = visibility; ++ ++ napi_value callback = nullptr; ++ napi_get_reference_value(MainEnv, SetNodeVisibilityCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 2, args, &result); ++ ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call ShowNode Failed!"); ++ res = false; ++ } else { ++ res = true; ++ } ++ } else { ++ WorkParam *workParam = new WorkParam{nodeRef, std::make_shared()}; ++ uv_after_work_cb afterFunc = [](uv_work_t *work, int _status) { ++ WorkParam *param = (WorkParam *)(work->data); ++ ++ napi_value node; ++ napi_get_reference_value(MainEnv, param->nodeRef, &node); ++ ++ napi_value visibility; ++ napi_create_int32(MainEnv, 0, &visibility); ++ ++ napi_value args[2]; ++ args[0] = node; ++ args[1] = visibility; ++ ++ napi_value callback = nullptr; ++ napi_get_reference_value(MainEnv, SetNodeVisibilityCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 2, args, &result); ++ ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call ShowNode Failed!"); ++ param->replyStatus = false; ++ } else { ++ param->replyStatus = true; ++ } ++ napi_create_reference(MainEnv, result, 1, &(param->replyParam)); ++ ++ std::unique_lock lock(param->lockInfo->mutex); ++ param->lockInfo->ready = true; ++ param->lockInfo->condition.notify_all(); ++ }; ++ ++ res = GetThreadWorkResult(workParam, afterFunc); ++ delete workParam; ++ } ++ return res; ++} ++ ++bool HideNode(napi_ref nodeRef) { ++ bool res = false; ++ std::thread::id cur_thread_id = std::this_thread::get_id(); ++ if (cur_thread_id == main_thread_id) { ++ napi_value node; ++ napi_get_reference_value(MainEnv, nodeRef, &node); ++ ++ napi_value visibility; ++ napi_create_int32(MainEnv, 1, &visibility); ++ ++ napi_value args[2]; ++ args[0] = node; ++ args[1] = visibility; ++ ++ napi_value callback = nullptr; ++ napi_get_reference_value(MainEnv, SetNodeVisibilityCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 2, args, &result); ++ ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call HideNode Failed! %{public}%d", ++ status); ++ res = false; ++ } else { ++ res = true; ++ } ++ } else { ++ WorkParam *workParam = new WorkParam{nodeRef, std::make_shared()}; ++ uv_after_work_cb afterFunc = [](uv_work_t *work, int _status) { ++ WorkParam *param = (WorkParam *)(work->data); ++ ++ napi_value node; ++ napi_get_reference_value(MainEnv, param->nodeRef, &node); ++ ++ napi_value visibility; ++ napi_create_int32(MainEnv, 1, &visibility); ++ ++ napi_value args[2]; ++ args[0] = node; ++ args[1] = visibility; ++ ++ napi_value callback = nullptr; ++ napi_get_reference_value(MainEnv, SetNodeVisibilityCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 2, args, &result); ++ ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call HideNode Failed!"); ++ param->replyStatus = false; ++ } else { ++ param->replyStatus = true; ++ } ++ napi_create_reference(MainEnv, result, 1, &(param->replyParam)); ++ ++ std::unique_lock lock(param->lockInfo->mutex); ++ param->lockInfo->ready = true; ++ param->lockInfo->condition.notify_all(); ++ }; ++ ++ res = GetThreadWorkResult(workParam, afterFunc); ++ delete workParam; ++ } ++ return res; ++} ++ ++napi_ref GetNodeRect(napi_ref nodeRef) { ++ napi_ref resultRef = nullptr; ++ std::thread::id cur_thread_id = std::this_thread::get_id(); ++ if (cur_thread_id == main_thread_id) { ++ napi_value node; ++ napi_get_reference_value(MainEnv, nodeRef, &node); ++ ++ napi_value callback; ++ napi_get_reference_value(MainEnv, GetNodeRectCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 1, &node, &result); ++ ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call GetNodeRect Failed!"); ++ } ++ napi_create_reference(MainEnv, result, 1, &resultRef); ++ } else { ++ WorkParam *workParam = new WorkParam{nodeRef, std::make_shared()}; ++ uv_after_work_cb afterFunc = [](uv_work_t *work, int _status) { ++ WorkParam *param = (WorkParam *)(work->data); ++ ++ napi_value node; ++ napi_get_reference_value(MainEnv, param->nodeRef, &node); ++ ++ napi_value callback; ++ napi_get_reference_value(MainEnv, GetNodeRectCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 1, &node, &result); ++ ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call GetNodeRect Failed!"); ++ } ++ napi_create_reference(MainEnv, result, 1, &(param->replyParam)); ++ ++ std::unique_lock lock(param->lockInfo->mutex); ++ param->lockInfo->ready = true; ++ param->lockInfo->condition.notify_all(); ++ }; ++ ++ if (GetThreadWorkResult(workParam, afterFunc)) { ++ resultRef = workParam->replyParam; ++ } ++ delete workParam; ++ } ++ return resultRef; ++} ++ ++napi_ref GetXComponent(napi_ref nodeRef) { return nullptr; } ++ ++bool DeleteFunc() { ++ if (AddChildNodeCallback != nullptr) ++ napi_delete_reference(MainEnv, AddChildNodeCallback); ++ if (RemoveChildNodeCallback != nullptr) ++ napi_delete_reference(MainEnv, RemoveChildNodeCallback); ++ if (ResizeNodeCallback != nullptr) ++ napi_delete_reference(MainEnv, ResizeNodeCallback); ++ if (AdjustNodeZOrderCallback != nullptr) ++ napi_delete_reference(MainEnv, AdjustNodeZOrderCallback); ++ if (SetNodeVisibilityCallback != nullptr) ++ napi_delete_reference(MainEnv, SetNodeVisibilityCallback); ++ if (GetNodeRectCallback != nullptr) ++ napi_delete_reference(MainEnv, GetNodeRectCallback); ++ if (GetObjectCallback != nullptr) ++ napi_delete_reference(MainEnv, GetObjectCallback); ++ if (ReParentNodeCallback != nullptr) ++ napi_delete_reference(MainEnv, ReParentNodeCallback); ++ if (RaiseNodeCallback != nullptr) ++ napi_delete_reference(MainEnv, RaiseNodeCallback); ++ if (LowerNodeCallback != nullptr) ++ napi_delete_reference(MainEnv, LowerNodeCallback); ++ if (MoveNodeCallback != nullptr) ++ napi_delete_reference(MainEnv, MoveNodeCallback); ++ if (GetRootNodeCallBack != nullptr) ++ napi_delete_reference(MainEnv, GetRootNodeCallBack); ++ ++ return true; ++} ++ ++bool MoveNode(napi_ref nodeRef, std::string x, std::string y) { ++ bool res = false; ++ std::thread::id cur_thread_id = std::this_thread::get_id(); ++ if (cur_thread_id == main_thread_id) { ++ napi_value args[3]; ++ ++ napi_value _x; ++ napi_create_string_utf8(MainEnv, x.c_str(), NAPI_AUTO_LENGTH, &_x); ++ ++ napi_value _y; ++ napi_create_string_utf8(MainEnv, y.c_str(), NAPI_AUTO_LENGTH, &_y); ++ ++ napi_value node; ++ napi_get_reference_value(MainEnv, nodeRef, &node); ++ ++ args[0] = node; ++ args[1] = _x; ++ args[2] = _y; ++ ++ napi_value callback; ++ napi_get_reference_value(MainEnv, MoveNodeCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 3, args, &result); ++ ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call MoveNode Failed!"); ++ res = false; ++ } else { ++ res = true; ++ } ++ } else { ++ WorkParam *workParam = new WorkParam(nodeRef, x, y, WorkParam::OFFSET_FLAG, std::make_shared()); ++ uv_after_work_cb afterFunc = [](uv_work_t *work, int _status) { ++ WorkParam *param = (WorkParam *)(work->data); ++ ++ napi_value args[3]; ++ ++ napi_value _x; ++ napi_create_string_utf8(MainEnv, param->x.c_str(), NAPI_AUTO_LENGTH, &_x); ++ ++ napi_value _y; ++ napi_create_string_utf8(MainEnv, param->y.c_str(), NAPI_AUTO_LENGTH, &_y); ++ ++ napi_value node; ++ napi_get_reference_value(MainEnv, param->nodeRef, &node); ++ ++ args[0] = node; ++ args[1] = _x; ++ args[2] = _y; ++ ++ napi_value callback; ++ napi_get_reference_value(MainEnv, MoveNodeCallback, &callback); ++ ++ napi_value result = nullptr; ++ napi_status status = napi_call_function(MainEnv, nullptr, callback, 3, args, &result); ++ ++ if (status != napi_ok) { ++ OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Adapter_c", "Call MoveNode Failed!"); ++ param->replyStatus = false; ++ } else { ++ param->replyStatus = true; ++ } ++ napi_create_reference(MainEnv, result, 1, &(param->replyParam)); ++ ++ std::unique_lock lock(param->lockInfo->mutex); ++ param->lockInfo->ready = true; ++ param->lockInfo->condition.notify_all(); ++ }; ++ ++ res = GetThreadWorkResult(workParam, afterFunc); ++ delete workParam; ++ } ++ return res; ++} ++ ++napi_env GetAdapterEnv() { return MainEnv; } ++ ++napi_value AdapterInit(napi_env env, napi_value exports) ++{ ++ MainEnv = env; ++ main_thread_id = std::this_thread::get_id(); ++ napi_property_descriptor desc[] = { ++ {"initFunc", nullptr, InitFunc, nullptr, nullptr, nullptr, napi_default, nullptr}}; ++ napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); ++ return exports; ++} +diff --git a/src/gui/util/adapter_c.h b/src/gui/util/adapter_c.h +new file mode 100644 +index 0000000000..8b3295e2cf +--- /dev/null ++++ b/src/gui/util/adapter_c.h +@@ -0,0 +1,276 @@ ++// Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. All rights reserved. ++ ++#ifndef ADAPTER_C_H ++#define ADAPTER_C_H ++ ++#include "napi/native_api.h" ++#include ++#include ++#include ++#include ++#include ++ ++const unsigned int LOG_PRINT_DOMAIN = 0xFF00; ++ ++/** ++ * NodeType: Node Component Type ++ */ ++enum NodeType { ++ XComponent, // xcomponent ++ UIExtension, // uiextension not support now ++ Container // notused ++}; ++ ++/** ++ * XComponentType: The type of XComponent ++ */ ++enum XComponentType { ++ SURFACE, ++ COMPONENT, ++ TEXTURE, ++}; ++ ++/** ++ * XComponent component attribute configuration ++ */ ++class XComponentModel { ++public: ++ XComponentModel(std::string id, XComponentType type, std::string libraryName) { ++ this->id = id; ++ this->type = type; ++ this->libraryName = libraryName; ++ } ++ ++ std::string id = ""; ++ XComponentType type = XComponentType::SURFACE; ++ std::string libraryName = ""; ++ void *onLoad = nullptr; // Not supported ++ void *onDestroy = nullptr; // Not supported ++ bool focusable = true; ++}; ++ ++/** ++ * Node attribute configuration ++ */ ++class NodeParams { ++public: ++ NodeParams(std::string width, std::string height, std::string x, std::string y, NodeType nodeType, ++ XComponentModel *componentModel) { ++ this->width = width; ++ this->height = height; ++ this->x = x; ++ this->y = y; ++ this->nodeType = nodeType; ++ this->componentModel = componentModel; ++ } ++ ++ ~NodeParams() { ++ if (componentModel != nullptr) { ++ delete componentModel; ++ componentModel = nullptr; ++ } ++ } ++ ++ std::string width = "50%"; ++ std::string height = "50%"; ++ std::string x = "0"; ++ std::string y = "0"; ++ XComponentModel *componentModel = nullptr; ++ NodeType nodeType = NodeType::XComponent; ++}; ++ ++// Defines locks and semaphores. ++using ThreadLockInfo = struct { ++ std::mutex mutex; ++ std::condition_variable condition; ++ bool ready = false; ++}; ++ ++// Define the parameter structure in the thread. ++struct WorkParam { ++ napi_env env = nullptr; ++ // input parameter ++ int windowId = 0; ++ std::string inParam = ""; ++ napi_ref nodeRef = nullptr; ++ napi_ref nodeChildRef = nullptr; ++ NodeParams *nodeParams = nullptr; ++ std::string width = "0"; ++ std::string height = "0"; ++ std::string x = "0"; ++ std::string y = "0"; ++ // return ++ napi_ref replyParam = nullptr; ++ bool replyStatus = false; ++ // lock structure ++ std::shared_ptr lockInfo; ++ ++ ++ WorkParam(napi_ref node, NodeParams *nodeParams, std::shared_ptr lockInfo) { ++ this->nodeRef = node; ++ this->nodeParams = nodeParams; ++ this->lockInfo = lockInfo; ++ } ++ ++ enum flag { SIZE_FLAG, OFFSET_FLAG }; ++ ++ WorkParam(int windowId, std::shared_ptr lockInfo) { ++ this->windowId = windowId; ++ this->lockInfo = lockInfo; ++ } ++ ++ /** ++ * WorkParam ++ * @param a width or x ++ * @param b height or y ++ * @param f SIZE or OFFSET ++ */ ++ WorkParam(napi_ref nodeRef, std::string a, std::string b, flag f, std::shared_ptr lockInfo) { ++ this->nodeRef = nodeRef; ++ if (f != SIZE_FLAG) { ++ this->x = a; ++ this->y = b; ++ } else { ++ this->width = a; ++ this->height = b; ++ } ++ this->lockInfo = lockInfo; ++ } ++ ++ WorkParam(napi_ref nodeRef, napi_ref nodeChildRef, std::shared_ptr lockInfo) { ++ this->nodeRef = nodeRef; ++ this->nodeChildRef = nodeChildRef; ++ this->lockInfo = lockInfo; ++ } ++ ++ WorkParam(napi_ref nodeRef, std::shared_ptr lockInfo) { ++ this->nodeRef = nodeRef; ++ this->lockInfo = lockInfo; ++ } ++ ++ WorkParam() {} ++}; ++ ++struct NodeRect { ++ int offsetX = 0; ++ int offsetY = 0; ++ int width = 0; ++ int height = 0; ++}; ++ ++napi_env GetAdapterEnv(); ++ ++/** ++ * This interface is invoked by the JS to initialize the interface on the adapter_c side. ++ * @param env: The environment that the API is invoked under. ++ * @param info ++ * @return ++ */ ++napi_value InitFunc(napi_env env, napi_callback_info info); ++ ++/** ++ * Obtains the root node of a window. ++ * @param windowId: Specifies the ID of the window. ++ * @return ++ */ ++napi_ref GetRootNode(int windowId); ++ ++/** ++ * Add a child node to the parent node and returns the child node. ++ * @param node: Parent node. ++ * @return Child node. ++ */ ++napi_ref AddChildNode(napi_ref nodeRef, NodeParams *nodeParams); ++ ++/** ++ * Remove a child node from a parent node. ++ * @param nodeChild: Child node to be removed. ++ * @return An array containing the elements that were deleted. ++ */ ++bool RemoveChildNode(napi_ref nodeChildRef); ++ ++/** ++ * Place the node at the top layer. ++ * @param node: Indicates the node to be operated ++ * @return null ++ */ ++bool RaiseNode(napi_ref nodeRef); ++ ++/** ++ * Place the node at the bottom layer. ++ * @param node: Indicates the node to be operated ++ * @return null ++ */ ++bool LowerNode(napi_ref nodeRef); ++ ++/** ++ * Adjusting the Width and Height of a Node. ++ * @param node: Indicates the node to be operated ++ * @param width ++ * @param height ++ * @return null ++ */ ++bool ResizeNode(napi_ref nodeRef, std::string width, std::string height); ++ ++/** ++ * Move a child node to another parent node. Cross-window movement is not supported. ++ * @param nodeParentNew: new Parent ++ * @param nodeChild: Node needs to be Moved ++ * @return null ++ */ ++bool ReParentNode(napi_ref nodeParentNewRef, napi_ref nodeChildRef); ++ ++/** ++ * Modifying the Position of a Node in the Parent Window. ++ * @param node ++ * @param x ++ * @param y ++ * @return null ++ */ ++bool MoveNode(napi_ref nodeRef, std::string x, std::string y); ++ ++/** ++ * Set Node to visible. ++ * @param node ++ * @return null ++ */ ++bool ShowNode(napi_ref nodeRef); ++ ++/** ++ * Set the node to invisible. ++ * @param node ++ * @return null ++ */ ++bool HideNode(napi_ref nodeRef); ++ ++/** ++ * Obtaining the Rect of a Node ++ * @param node ++ * @return {x, y, w, h} ++ */ ++napi_ref GetNodeRect(napi_ref nodeRef); ++ ++/** ++ * Returns the XComponent object in the specified node. ++ * This object needs to support OH_NativeXComponent_RegisterCallback invoking on the Native side. ++ * @param node ++ * @return OH_NativeXComponent ++ * @todo ++ */ ++napi_ref GetXComponent(napi_ref nodeRef); ++ ++/** ++ * Release refs ++ * @return ++ */ ++bool DeleteFunc(); ++ ++void configNode(const NodeParams &nodeParams, napi_value *nodeParamsNapi); ++ ++bool ReleaseNodeRef(napi_ref nodeRef); ++ ++bool GetThreadWorkResult(WorkParam *workParam, uv_after_work_cb uvAfterWork); ++ ++napi_value AdapterInit(napi_env env, napi_value exports); ++ ++#endif // ADAPTER_C_H +diff --git a/src/gui/util/qopenharmonywindowadapter.cpp b/src/gui/util/qopenharmonywindowadapter.cpp +new file mode 100644 +index 0000000000..2cb7aeff90 +--- /dev/null ++++ b/src/gui/util/qopenharmonywindowadapter.cpp +@@ -0,0 +1,72 @@ ++#include ++#include ++#include ++#include ++ ++#include "qopenharmonywindowadapter.h" ++ ++QT_BEGIN_NAMESPACE ++ ++namespace QOpenHarmonyWindowAdapter { ++ ++bool ReParentNode(WId parent, WId child) ++{ ++ napi_ref childNodeRef = reinterpret_cast(child); ++ napi_ref parentNodeRef = reinterpret_cast(parent); ++ ++ qWarning() << "<-------------reParent node===111"; ++ if (nullptr == childNodeRef || ++ nullptr == parentNodeRef) ++ return false; ++ ++ bool result = ::ReParentNode(parentNodeRef, parentNodeRef); ++ qWarning() << "<-------------reParent node===222" << result; ++ return result; ++} ++ ++bool AddChildNode(WId parent, NodeParams *nodeParams) ++{ ++ napi_ref parentNodeRef = reinterpret_cast(parent); ++ if (parentNodeRef == nullptr) ++ return false; ++ ++ return ::AddChildNode(parentNodeRef, nodeParams) != 0; ++} ++ ++} ++QT_END_NAMESPACE ++ ++/* ++ * function for module exports ++ */ ++EXTERN_C_START ++static napi_value Init(napi_env env, napi_value exports) ++{ ++ static bool inited = false; ++ if (!inited) { ++ AdapterInit(env, exports); ++ inited = true; ++ } ++ return exports; ++} ++EXTERN_C_END ++ ++/* ++ * Napi Module define ++ */ ++static napi_module Qt5GuiModule = { ++ .nm_version = 1, ++ .nm_flags = 0, ++ .nm_filename = nullptr, ++ .nm_register_func = Init, ++ .nm_modname = "Qt5Gui", ++ .nm_priv = ((void*)0), ++ .reserved = { 0 }, ++}; ++/* ++ * Module register function ++ */ ++extern "C" __attribute__((constructor)) void RegisterModule(void) ++{ ++ napi_module_register(&Qt5GuiModule); ++} +diff --git a/src/gui/util/qopenharmonywindowadapter.h b/src/gui/util/qopenharmonywindowadapter.h +new file mode 100644 +index 0000000000..32f78e8b4e +--- /dev/null ++++ b/src/gui/util/qopenharmonywindowadapter.h +@@ -0,0 +1,14 @@ ++#pragma once ++#include ++#include ++#include "adapter_c.h" ++ ++QT_BEGIN_NAMESPACE ++ ++namespace QOpenHarmonyWindowAdapter ++{ ++ Q_GUI_EXPORT bool AddChildNode(WId parent, NodeParams *nodeParams); ++ Q_GUI_EXPORT bool ReParentNode(WId parent, WId child); ++}; ++ ++QT_END_NAMESPACE +diff --git a/src/gui/util/util.pri b/src/gui/util/util.pri +index d3402133d6..63c99d7771 100644 +--- a/src/gui/util/util.pri ++++ b/src/gui/util/util.pri +@@ -40,6 +40,16 @@ SOURCES += \ + util/qktxhandler.cpp \ + util/qastchandler.cpp + ++openharmony { ++HEADERS += \ ++ util/adapter_c.h \ ++ util/qopenharmonywindowadapter.h ++ ++SOURCES += \ ++ util/adapter_c.cpp \ ++ util/qopenharmonywindowadapter.cpp ++} + -+ onBackground() { -+ // Ability has back to background -+ JSLogger.info('%{public}s', 'Ability onBackground'); -+ } -+}; -diff --git a/src/openharmony/native/QtCore/JsApplication.ts b/src/openharmony/native/QtCore/JsApplication.ts + qtConfig(regularexpression) { + HEADERS += \ + util/qshadergenerator_p.h +diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp +index 507417f86c..a29678555e 100644 +--- a/src/network/access/qnetworkaccessfilebackend.cpp ++++ b/src/network/access/qnetworkaccessfilebackend.cpp +@@ -57,6 +57,9 @@ QStringList QNetworkAccessFileBackendFactory::supportedSchemes() const + << QStringLiteral("qrc"); + #if defined(Q_OS_ANDROID) + schemes << QStringLiteral("assets"); ++#endif ++#if defined(Q_OS_OPENHARMONY) ++ schemes << QStringLiteral("rawfile"); + #endif + return schemes; + } +@@ -80,6 +83,9 @@ QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op, + if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0 + #if defined(Q_OS_ANDROID) + || url.scheme().compare(QLatin1String("assets"), Qt::CaseInsensitive) == 0 ++#endif ++#if defined(Q_OS_OPENHARMONY) ++ || url.scheme().compare(QLatin1String("rawfile"), Qt::CaseInsensitive) == 0 + #endif + || url.isLocalFile()) { + return new QNetworkAccessFileBackend; +@@ -136,6 +142,11 @@ void QNetworkAccessFileBackend::open() + if (url.scheme() == QLatin1String("assets")) + fileName = QLatin1String("assets:") + url.path(); + else ++#endif ++#if defined(Q_OS_OPENHARMONY) ++ if (url.scheme() == QLatin1String("rawfile")) ++ fileName = QLatin1String("rawfile:") + url.path(); ++ else + #endif + fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery); + } +diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp +index a10fe9e3fe..7c363279a2 100644 +--- a/src/network/access/qnetworkaccessmanager.cpp ++++ b/src/network/access/qnetworkaccessmanager.cpp +@@ -1419,6 +1419,9 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera + if (isLocalFile + #ifdef Q_OS_ANDROID + || scheme == QLatin1String("assets") ++#endif ++#ifdef Q_OS_OPENHARMONY ++ || scheme == QLatin1String("rawfile") + #endif + || scheme == QLatin1String("qrc")) { + return new QNetworkReplyFileImpl(this, req, op); +diff --git a/src/network/access/qnetworkreplyfileimpl.cpp b/src/network/access/qnetworkreplyfileimpl.cpp +index 6e69b4c4d3..ff8ff19361 100644 +--- a/src/network/access/qnetworkreplyfileimpl.cpp ++++ b/src/network/access/qnetworkreplyfileimpl.cpp +@@ -110,6 +110,11 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con + if (scheme == QLatin1String("assets")) + fileName = QLatin1String("assets:") + url.path(); + else ++#endif ++#if defined(Q_OS_OPENHARMONY) ++ if (scheme == QLatin1String("rawfile")) ++ fileName = QLatin1String("rawfile:") + url.path(); ++ else + #endif + fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery); + } +diff --git a/src/openharmony/entryability/EntryAbility.ets b/src/openharmony/entryability/EntryAbility.ets new file mode 100644 -index 0000000000..80c3160c50 +index 0000000000..d543b3c6fe --- /dev/null -+++ b/src/openharmony/native/QtCore/JsApplication.ts -@@ -0,0 +1,183 @@ -+import font from '@ohos.font'; -+import fs from '@ohos.file.fs'; -+import window from '@ohos.window'; -+import JsLogger from './JsLogger'; -+import display from '@ohos.display'; -+import { JsWindow } from './JsWindow'; -+import { QtQPA } from './JsQtPlatform'; -+import JsDataStore from './JsDataStore'; -+import { Font, UIContext } from '@ohos.arkui.UIContext'; -+import { JsWindowManager } from './JsWindowManager'; -+import resourceManager from '@ohos.resourceManager'; ++++ b/src/openharmony/entryability/EntryAbility.ets +@@ -0,0 +1,54 @@ ++import AbilityConstant from '@ohos.app.ability.AbilityConstant'; ++import UIAbility from '@ohos.app.ability.UIAbility'; ++import Window from '@ohos.window' ++import Want from '@ohos.app.ability.Want'; ++import JsApplication from '../native/QtCore/JsApplication' ++import JsDataStore from '../native/QtCore/JsDataStore' ++import JSLogger from '../native/QtCore/JsLogger' ++import window from '@ohos.window' + -+class JsApplication { -+ private mainWindow: window.Window = null; -+ private mainWindowName: string = "opemharmony_qt_mainwindow"; -+ private qpa: any = QtQPA; -+ private timerId: number | undefined; ++export default class EntryAbility extends UIAbility { ++ onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { ++ JsDataStore.setContext(this.context); ++ if (want) { ++ JsDataStore.setElementName({ ++ bundleName: want.bundleName, ++ abilityName: want.abilityName, ++ moduleName: want.moduleName ++ }); ++ } ++ } + -+ getMainWindow(): window.Window { -+ return this.mainWindow; ++ onDestroy() { ++ JSLogger.info('%{public}s', 'Ability onDestroy'); ++ JsApplication.quit(); + } + -+ getMainWindowName(): string { -+ return this.mainWindowName; ++ onWindowStageCreate(windowStage: Window.WindowStage) { ++ // Main window is created, set main page for this ability ++ JSLogger.info('%{public}s', 'Ability onWindowStageCreate'); ++ JsApplication.run(windowStage, this.launchWant); + } + -+ addWindow(name: string, window: JsWindow): void { -+ let obj = JsDataStore.getJsObjectLoader().createObject("JsWindowManager", "JsWindowManager"); -+ (obj as JsWindowManager).addWindow(name, window); ++ onWindowStageDestroy() { ++ // Main window is destroyed, release UI related resources ++ JSLogger.info('%{public}s', 'Ability onWindowStageDestroy'); + } + -+ updateQtScreen(size: window.Size): void { -+ let d = display.getDefaultDisplaySync(); -+ let titleHeight: number = JsDataStore.windowTitleHeight(); -+ let borderWidth: number = JsDataStore.windowBorderWidth(); -+ JsDataStore.getQtNativeModule("QPA").setDisplayMetrics(size.width - borderWidth - borderWidth, size.height - titleHeight - borderWidth, d.densityDPI, d.scaledDensity, d.xDPI, d.yDPI, -+ 0, 0, size.width - borderWidth - borderWidth, size.height - titleHeight - borderWidth); ++ onForeground() { ++ // Ability has brought to foreground ++ JSLogger.info('%{public}s', 'Ability onForeground'); + } + -+ async run(windowStage: window.WindowStage) { -+ let qtMajorVersion: number = this.qpa.qtMajorVersion(); -+ let QtCoreModule: any = null; -+ if (qtMajorVersion == 5) -+ QtCoreModule = await import("libQt5Core.so"); -+ else if (qtMajorVersion == 6) -+ QtCoreModule = await import("libQt6Core.so"); -+ if (QtCoreModule == null) { -+ JsLogger.fatal("Cannot load QtCore module"); -+ return; -+ } ++ onBackground() { ++ // Ability has back to background ++ JSLogger.info('%{public}s', 'Ability onBackground'); ++ } + -+ JsDataStore.setQtMajorVersion(qtMajorVersion); -+ let QtCore = QtCoreModule.default; -+ QtCore.initJsObjectLoader((type: string, name: string, ...args: any[]) => { -+ if (JsDataStore.getJsObjectLoader().hasJsObject(name)) -+ return JsDataStore.getJsObjectLoader().getJsObject(name) -+ return JsDataStore.getJsObjectLoader().createObject(type, name, ...args); -+ }, (name: string) => { -+ return JsDataStore.getJsObjectLoader().deleteJsObject(name); -+ }); -+ JsDataStore.addQtNativeModule("QPA", this.qpa); -+ JsDataStore.addQtNativeModule("QtCore", QtCore); -+ this.mainWindow = windowStage.getMainWindowSync(); -+ let window = new JsWindow(this.mainWindowName, this.mainWindow); -+ let localStore = new LocalStorage({ "name": this.mainWindowName }); -+ await windowStage.loadContent('pages/Index', localStore); -+ this.addWindow(this.mainWindowName, window); -+ if (this.qpa != null) { -+ let p = this.mainWindow.getWindowProperties(); -+ this.updateQtScreen({ width: p.windowRect.width, height: p.windowRect.height }); -+ } -+ this.mainWindow.on("windowSizeChange", this.updateQtScreen); -+ windowStage.on("windowStageEvent", (state) => { -+ if (this.qpa != null) -+ this.qpa.updateApplicationState(state); -+ JsLogger.info("window stage changed %{public}d", state); -+ }); ++ onPrepareToTerminate() { ++ //关闭窗体时通知qt ++ JsDataStore.getQtNativeModule("QPA").handleWindowEvent("openharmony_qt_mainwindow", window.WindowEventType.WINDOW_DESTROYED); ++ //阻止ts程序退出,由qt程序确定是否退出 ++ return true; ++ } ++}; +diff --git a/src/openharmony/native/QtCore/JsApplication.ets b/src/openharmony/native/QtCore/JsApplication.ets +new file mode 100644 +index 0000000000..ab4eec2747 +--- /dev/null ++++ b/src/openharmony/native/QtCore/JsApplication.ets +@@ -0,0 +1,194 @@ ++import window from '@ohos.window'; ++import display from '@ohos.display'; ++import process from '@ohos.process'; ++import fs, { ReadOptions } from '@ohos.file.fs'; ++import util from '@ohos.util'; ++import resourceManager from '@ohos.resourceManager'; ++import JsDataStore from './JsDataStore'; ++import JsLogger from './JsLogger'; ++import { JsWindowManager } from './JsWindowManager'; ++import JsIndependentWindow from './JsIndependentWindow'; ++import JsQtCore from './JsQtCore'; ++import JsQtGui from '../QtGui/JsQtGui'; ++import Want from '@ohos.app.ability.Want'; ++import Font from '@ohos.arkui.UIContext'; ++import font from '@ohos.font'; ++ ++interface Files { ++ files: Array; ++} ++ ++class JsApplication { ++ updateQtScreen(): void { ++ let d = display.getDefaultDisplaySync(); ++ // let statusBarHeight: number = JsDataStore.statusBarHeight(); ++ // let navigationBarHeight: number = JsDataStore.navigationBarHeight(); ++ // if (JsDataStore.defaultShowIsMaximized()) { ++ // JsDataStore.getQtNativeModule("QPA") ++ // .setDisplayMetrics(d.width, d.height, d.densityDPI, d.scaledDensity, d.xDPI, d.yDPI, ++ // 0, 0, d.width, d.height); ++ // } else { ++ // JsDataStore.getQtNativeModule("QPA") ++ // .setDisplayMetrics(d.width, d.height, d.densityDPI, d.scaledDensity, d.xDPI, d.yDPI, ++ // 0, statusBarHeight, d.width, d.height - statusBarHeight - navigationBarHeight); ++ // } ++ JsDataStore.getQtNativeModule("QPA") ++ .setDisplayMetrics(d.width, d.height, d.densityDPI, d.scaledDensity, d.xDPI, d.yDPI, ++ 0, 0, d.width, d.height); ++ } + ++ async run(windowStage: window.WindowStage, want: Want) { ++ this.loadQtModules(); ++ let mainWindow = windowStage.getMainWindowSync(); ++ if (JsDataStore.defaultShowIsMaximized()) { ++ mainWindow.setWindowLayoutFullScreen(true); ++ mainWindow.setWindowSystemBarEnable([]); ++ } ++ let name = "openharmony_qt_mainwindow"; ++ let window = new JsIndependentWindow(name, mainWindow); ++ let localStore = new LocalStorage(); ++ let manager = JsDataStore.getJsObjectLoader().createObject("JsWindowManager", "JsWindowManager") as JsWindowManager; ++ manager.addWindow(name, window); ++ localStore.setOrCreate("idName", name); ++ JsDataStore.getJsObjectLoader().addJsObject("JsApplication", this); ++ await windowStage.loadContent('pages/Index', localStore); ++ JsDataStore.getQtNativeModule("QPA").setDeviceType(JsDataStore.deviceType()); ++ this.updateQtScreen(); ++ JsDataStore.getQtNativeModule("QPA").setTouchPad(await JsDataStore.isTouchPad()); ++ JsDataStore.getQtNativeModule("QPA").setDefaultShowIsMaximized(JsDataStore.defaultShowIsMaximized()); + /* NOTE 获取字体信息 */ + let mainClass = await windowStage.getMainWindow(); -+ let font: Font = mainClass.getUIContext().getFont(); -+ let fontList: Array = new Array(); -+ for (let f of font.getSystemFontList()) { -+ let fontInfo: font.FontInfo = font.getFontByName(f); -+ fontList.push(fontInfo.path); -+ } -+ this.qpa.setFontInfos(fontList); ++ let f: Font.Font = mainClass.getUIContext().getFont(); ++ let fonts: Array = new Array(); ++ for (let _f of f.getSystemFontList()) { ++ let fontInfo: font.FontInfo = font.getFontByName(_f); ++ fonts.push(fontInfo.path); ++ } ++ JsDataStore.getQtNativeModule("QPA").setFontInfos(fonts); ++ JsDataStore.getQtNativeModule("QPA").setJsQuitFunction(() => { ++ JsLogger.info("quit ts application"); ++ JsDataStore.getContext().getApplicationContext().killAllProcesses(); ++ }); + JsDataStore.setWindowStage(windowStage); -+ + await this.extractFilesToCache(); -+ this.loadQtApplication(); ++ this.loadQtApplication(want); + } + + quit() { -+ this.qpa.quitQtApplication(); ++ JsDataStore.getQtNativeModule("QPA").quitQtApplication(); + } + -+ loadQtApplication() { -+ this.qpa.setResourceManager(JsDataStore.getResourceManager()); -+ let result : boolean = this.qpa.startQtApplication(JsDataStore.getApplicationDirs(), "libentry.so"); -+ JsLogger.info("load qt application result: %{public}s", result); ++ formatParams(want: Want, params: string[]): string[] { ++ if (want != undefined && want.uri) { ++ params.push(want.uri) ++ } ++ if (want.parameters) { ++ let keys = Object.keys(want.parameters); ++ keys.forEach(key => { ++ if (want.parameters) { ++ let value: Object = want.parameters[key]; ++ if (typeof value == 'string' || typeof value == 'number' || typeof value == 'boolean') { ++ if (value.toString().length != 0) { ++ params.push("--" + key); ++ params.push(value.toString()); ++ } ++ } ++ } ++ }) ++ } ++ return params; ++ } + -+ //周期检测qt应用是否执行完成(1000ms) -+ this.timerId = setInterval(() => { -+ let res = this.qpa.waitForQtAppExit(); -+ if(res == 0) -+ { -+ JsLogger.info("qt application finished"); -+ //鸿蒙程序调用qt程序退出函数 -+ this.quit(); -+ JsDataStore.getContext().getApplicationContext().killAllProcesses(); -+ } -+ }, 1000); ++ loadQtApplication(want: Want) { ++ JsDataStore.getQtNativeModule("QPA").setResourceManager(JsDataStore.getResourceManager()); ++ JsDataStore.getQtNativeModule("QPA").setDisplayVersion(JsDataStore.displayVersion()); ++ let params: string[] = ["libentry.so"]; ++ //todo:默认关闭参数传递 ++ // this.formatParams(want, params); ++ let result: boolean = ++ JsDataStore.getQtNativeModule("QPA").startQtApplication(JsDataStore.getApplicationDirs(), params.join("\t")); ++ JsLogger.info("load qt application result: %{public}s", result); ++ if (false == result) { ++ let pro = new process.ProcessManager(); ++ pro.exit(0); ++ } + } + + private saveFileToCache(file: resourceManager.RawFileDescriptor, des: string) { @@ -2725,7 +4654,7 @@ index 0000000000..80c3160c50 + let buffer = new ArrayBuffer(4096); + let currentOffset = file.offset; + let lengthNeedToReed = file.length; -+ let readOption = { ++ let readOption: ReadOptions = { + offset: currentOffset, + length: lengthNeedToReed > buffer.byteLength ? 4096 : lengthNeedToReed + } @@ -2741,8 +4670,12 @@ index 0000000000..80c3160c50 + break; + } + lengthNeedToReed -= readLength; -+ readOption.offset += readLength; -+ readOption.length = lengthNeedToReed > buffer.byteLength ? 4096 : lengthNeedToReed; ++ if (readOption.offset != undefined) { ++ readOption.offset += readLength; ++ } ++ if (readOption.length != undefined) { ++ readOption.length = lengthNeedToReed > buffer.byteLength ? 4096 : lengthNeedToReed; ++ } + } + fs.close(cacheFile); + } @@ -2761,32 +4694,120 @@ index 0000000000..80c3160c50 + try { + let R = JsDataStore.getResourceManager(); + let rawContent: Uint8Array = await R.getRawFileContent("qt.json"); -+ let str: string = String.fromCharCode.apply(null, rawContent) -+ let files = JSON.parse(str); -+ for (var i = 0; i < files.files.length; ++i) { ++ let textDecoder = util.TextDecoder.create("utf-8", { ignoreBOM: true }); ++ let jsonStr = textDecoder.decodeWithStream(rawContent, { stream: false }); ++ let files = JSON.parse(jsonStr) as Files; ++ for (let i = 0; i < files.files.length; ++i) { + await this.extractFile(files.files[i]); + } + } catch (err) { + JsLogger.error("read file qt.json failed: %{public}s", JSON.stringify(err)); + } + } ++ ++ loadQtModules() { ++ JsQtCore.loadQtCore(); ++ JsQtGui.loadQtGui(); ++ } +} + +export default new JsApplication; +\ No newline at end of file +diff --git a/src/openharmony/native/QtCore/JsCursor.ets b/src/openharmony/native/QtCore/JsCursor.ets +new file mode 100644 +index 0000000000..9c5f79e7a3 +--- /dev/null ++++ b/src/openharmony/native/QtCore/JsCursor.ets +@@ -0,0 +1,72 @@ ++import window from '@ohos.window'; ++import pointer from '@ohos.multimodalInput.pointer'; ++import image from '@ohos.multimedia.image'; ++import { BusinessError } from '@ohos.base'; ++import JsDataStore from './JsDataStore'; ++import JsIndependentWindow from './JsIndependentWindow'; ++import JSLogger from './JsLogger'; ++ ++export class JsCursor { ++ constructor() { ++ } ++ ++ setPointerStyle(pointerStyle: pointer.PointerStyle, windowName: string) { ++ let windowId: number | undefined = this.getWindowId(windowName); ++ if (windowId == undefined || windowId < 0) { ++ JSLogger.info('Invalid windowId'); ++ return; ++ } ++ try { ++ pointer.setPointerStyle(windowId, pointerStyle).then(() => { ++ JSLogger.info('Set pointer style success, pointerStyle:%{public}d', pointerStyle as number); ++ }); ++ } catch (error) { ++ JSLogger.error("Set pointer style failed, error: %{public}s", JSON.stringify(error)); ++ } ++ } ++ ++ getWindowId(windowName: string): number | undefined { ++ let win = JsDataStore.getJsObjectLoader().getJsObject(windowName) as JsIndependentWindow; ++ return win == undefined ? undefined : win.getWindowId(); ++ } ++ ++ setPointerVisible(isVisible: boolean) { ++ try { ++ pointer.setPointerVisible(isVisible, (error: Error) => { ++ if (error) { ++ JSLogger.error("Set pointer visible failed, error: %{public}s", JSON.stringify(error)); ++ return; ++ } ++ }); ++ } catch (error) { ++ JSLogger.error("Set pointer visible failed, error: %{public}s", JSON.stringify(error)); ++ } ++ } ++ ++ async setCustomCursorStyle(styleRes: ArrayBuffer, theWidth: number, theHeight: number, focusX: number, focusY: number) { ++ JSLogger.info("setCustomCursorStyle--->theWidth:%{public}d, theHeight:%{public}d, focusX:%{public}d, " + ++ "focusY:%{public}d", theWidth, theHeight, focusX, focusY); ++ ++ let decodingOptions: image.DecodingOptions = { desiredSize: { width: theWidth, height: theHeight } }; ++ let imageSource: image.ImageSource = image.createImageSource(styleRes); ++ let actualFocusX: number = 0; ++ let actualFocusY: number = 0; ++ //鸿蒙系统自定义光标大小固定为64*64 ++ if (theWidth != 0 && theHeight != 0) { ++ actualFocusX = (64 / theWidth) * focusX; ++ actualFocusY = (64 / theHeight) * focusY; ++ } ++ imageSource.createPixelMap(decodingOptions).then((pixelMap) => { ++ window.getLastWindow(getContext(), (_error: BusinessError, win: window.Window) => { ++ let windowId = win.getWindowProperties().id; ++ try { ++ pointer.setCustomCursor(windowId, pixelMap, actualFocusX, actualFocusY).then(() => { ++ JSLogger.info("setCustomCursor success"); ++ }); ++ } catch (error) { ++ JSLogger.error("setCustomCursor failed, error: %{public}s", JSON.stringify(error)); ++ } ++ }); ++ }); ++ } ++} diff --git a/src/openharmony/native/QtCore/JsDataStore.ts b/src/openharmony/native/QtCore/JsDataStore.ts new file mode 100644 -index 0000000000..be9a25b667 +index 0000000000..92e154cc93 --- /dev/null +++ b/src/openharmony/native/QtCore/JsDataStore.ts -@@ -0,0 +1,136 @@ +@@ -0,0 +1,176 @@ ++import common from '@ohos.app.ability.common'; +import window from '@ohos.window'; ++import bundleManager from '@ohos.bundle.bundleManager'; ++import resourceManager from '@ohos.resourceManager'; +import HashMap from '@ohos.util.HashMap'; -+import { JsQtModule } from './JsQtModule' ++import { JsQtModule } from './JsQtModule'; +import deviceInfo from '@ohos.deviceInfo'; -+import common from '@ohos.app.ability.common'; ++import inputDevice from '@ohos.multimodalInput.inputDevice'; +import QtJsObjectLoader from './JsObjectLoader'; -+import resourceManager from '@ohos.resourceManager'; -+import bundleManager from '@ohos.bundle.bundleManager'; ++import { UIContext } from '@kit.ArkUI'; ++import JsLogger from './JsLogger'; + +interface ApplicationDirs { + tempDir: string, @@ -2800,12 +4821,12 @@ index 0000000000..be9a25b667 +} + +class JsDataStore { -+ private windowName: string; ++ private uicontext: UIContext | null = null; + private context: common.UIAbilityContext = null; + private windowStage: window.WindowStage; + private elementName: bundleManager.ElementName = null; + private jsModules: JsQtModule[] = []; -+ private ApplicationDirs: ApplicationDirs; ++ private applicationDirs: ApplicationDirs; + private qtMajorVersion: number = 0; + private qtNativeModules: HashMap = new HashMap(); + private jsObjectLoader: QtJsObjectLoader = new QtJsObjectLoader; @@ -2813,10 +4834,20 @@ index 0000000000..be9a25b667 + constructor() { + } + ++ getUiContext(): UIContext | null { ++ return this.uicontext; ++ } ++ ++ setUiContext(uicontext: UIContext) { ++ if (null == this.uicontext) { ++ this.uicontext = uicontext; ++ } ++ } ++ + setContext(context: common.UIAbilityContext) { + this.context = context; + let appContext = this.context.getApplicationContext(); -+ let dirs : ApplicationDirs = { ++ let dirs: ApplicationDirs = { + tempDir: appContext.tempDir, + filesDir: appContext.filesDir, + cacheDir: appContext.cacheDir, @@ -2826,15 +4857,15 @@ index 0000000000..be9a25b667 + distributedFilesDir: appContext.distributedFilesDir, + qmlDir: appContext.cacheDir + "/Qt/qml" + }; -+ this.ApplicationDirs = dirs; ++ this.applicationDirs = dirs; + } + + getContext(): common.UIAbilityContext { + return this.context; + } + -+ getApplicationDirs() : ApplicationDirs { -+ return this.ApplicationDirs; ++ getApplicationDirs(): ApplicationDirs { ++ return this.applicationDirs; + } + + setElementName(en: bundleManager.ElementName) { @@ -2854,7 +4885,7 @@ index 0000000000..be9a25b667 + return this.windowStage; + } + -+ getResourceManager() : resourceManager.ResourceManager { ++ getResourceManager(): resourceManager.ResourceManager { + return this.context.resourceManager; + } + @@ -2888,251 +4919,368 @@ index 0000000000..be9a25b667 + + deviceType(): string { + // 现目前在pc、pad上模拟成手机端 -+ return 'default'; -+ // return deviceInfo.deviceType; ++ // return 'default'; ++ return deviceInfo.deviceType; ++ } ++ ++ windowTitleHeight(): number { ++ // Todo read from system ++ return deviceInfo.deviceType == "default" ? 0 : 93; ++ } ++ ++ windowBorderWidth(): number { ++ // Todo read from system ++ return deviceInfo.deviceType == "default" ? 0 : 4; ++ } ++ ++ statusBarHeight(): number { ++ // Todo read from system ++ return deviceInfo.deviceType == "default" ? 0 : 73; ++ } ++ ++ navigationBarHeight(): number { ++ // Todo read from system ++ return deviceInfo.deviceType == "default" ? 0 : 150; ++ } ++ ++ displayVersion(): string { ++ return deviceInfo.displayVersion; ++ } ++ ++ defaultShowIsMaximized(): boolean { ++ return deviceInfo.deviceType == "default" || deviceInfo.deviceType == "tablet"; ++ } ++ ++ isDefaultDevice(): boolean { ++ return 'default' == deviceInfo.deviceType; ++ } ++ ++ async isTouchPad(): Promise { ++ try { ++ let devices = await inputDevice.getDeviceList(); ++ for (let i = 0; i < devices.length; ++i) { ++ let deviceInfo = inputDevice.getDeviceInfoSync(devices[i]); ++ if (deviceInfo.sources.includes('touchpad')) { ++ return true; ++ } ++ } ++ return false; ++ } catch (err) { ++ JsLogger.error('Failed to call getDeviceInfoSync. Cause: %{public}s', JSON.stringify(err)); ++ return false; ++ } ++ } ++} ++ ++export default new JsDataStore; +\ No newline at end of file +diff --git a/src/openharmony/native/QtCore/JsDialog.ts b/src/openharmony/native/QtCore/JsDialog.ts +new file mode 100644 +index 0000000000..830c89a6a8 +--- /dev/null ++++ b/src/openharmony/native/QtCore/JsDialog.ts +@@ -0,0 +1,179 @@ ++import JsLogger from './JsLogger' ++import picker from '@ohos.file.picker' ++import JsDataStore from './JsDataStore' ++import { BusinessError } from '@ohos.base' ++import promptAction from '@ohos.promptAction' ++ ++export class JsDialog { ++ constructor() { ++ } ++ ++ private getSelectMaxSelectNumber(fileMode: number): number { ++ switch (fileMode) { ++ case 3: ++ return 500; ++ case 1: ++ default: ++ break; ++ } ++ return 1; ++ } ++ ++ private getSelectMode(type: number): picker.DocumentSelectMode { ++ switch (type) { ++ case 0: ++ case 1: ++ case 3: ++ break; ++ case 2: ++ return picker.DocumentSelectMode.MIXED; ++ case 4: ++ return picker.DocumentSelectMode.FOLDER; ++ default: ++ break; ++ } ++ return picker.DocumentSelectMode.FILE; ++ } ++ ++ messageBox(handler: number, title: string, text: string, buttons: Array): boolean { ++ JsLogger.info("show message box %{public}s %{public}s %{public}d", title, text, buttons.length) ++ let opt: promptAction.ShowDialogOptions = { ++ title: title, ++ message: text, ++ buttons: [{ ++ text: "OK", ++ color: "#000000" ++ }] ++ }; ++ ++ if (buttons != null && buttons.length > 0) { ++ let first: promptAction.Button = { ++ text: buttons[0], ++ color: '#000000', ++ } ++ opt.buttons[0] = first; ++ for (let i = 1; i < buttons.length; i++) { ++ let button: promptAction.Button = { ++ text: buttons[i], ++ color: '#000000', ++ } ++ opt.buttons.push(button) ++ } ++ } ++ ++ let mainWindow = JsDataStore.getWindowStage().getMainWindowSync(); ++ mainWindow.getUIContext().getPromptAction().showDialog(opt, (err, data) => { ++ if (err) { ++ JsLogger.error("show dialog error: %{public}s", JSON.stringify(err)); ++ } ++ if (buttons.length > 0) { ++ let index = err ? -1 : data.index; ++ JsDataStore.getQtNativeModule("QPA").dialogResult(handler, index); ++ } ++ }); ++ return true; + } + -+ windowTitleHeight(): number { -+ // Todo read from system -+ return deviceInfo.deviceType == "default" ? 0 : 93; ++ isVideo(filter: string): boolean { ++ return filter.includes("mp4") || filter.includes("MPEG") ++ || filter.includes("MPG") || filter.includes("DAT") ++ || filter.includes("MOV") || filter.includes("FLV"); + } + -+ windowBorderWidth(): number { -+ // Todo read from system -+ return deviceInfo.deviceType == "default" ? 0 : 4; ++ isAudio(filter: string): boolean { ++ return filter.includes("mp3") || filter.includes("wma") || filter.includes("ogg") || filter.includes("flac") ++ || filter.includes("wv"); + } + -+ statusBarHeight(): number { -+ // Todo read from system -+ return deviceInfo.deviceType == "default" ? 0 : 73; ++ isImage(filter: string): boolean { ++ return filter.includes("png") || filter.includes("jpeg") ++ || filter.includes("jpg") || filter.includes("bmp") + } + -+ navigationBarHeight(): number { -+ // Todo read from system -+ return deviceInfo.deviceType == "default" ? 0 : 100; ++ async openFileDialog(handler: number, filter: Array, type: number, dir: string): Promise { ++ // let config = { ++ // action: 'ohos.want.action.OPEN_FILE', ++ // parameters: { ++ // startMode: 'choose', ++ // fileSuffixFilters:"*" ++ // } ++ // } ++ // ++ // let context = JsDataStore.getContext(); ++ // ++ // context.startAbilityForResult(config).then((result) => { ++ // // 获取到文档文件的uri ++ // let select_item_list = result.want.parameters.select_item_list; ++ // JsLogger.info("select file: %{public}s", select_item_list.toString()); ++ // // 有的系统是file://xxx/xxx/xx/xx ++ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, [select_item_list.toString()]); ++ // }).catch((error) => { ++ // JsLogger.error("open file dialog result %{public}s", JSON.stringify(error)); ++ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); ++ // }); ++ // return true; ++ if (filter.length >= 100 || 0 == filter.length) { ++ filter = ['.*']; ++ } ++ try { ++ let documentPicker = new picker.DocumentViewPicker(); ++ let DocumentSelectOptions = new picker.DocumentSelectOptions(); ++ DocumentSelectOptions.fileSuffixFilters = filter; ++ DocumentSelectOptions.selectMode = this.getSelectMode(type); ++ DocumentSelectOptions.maxSelectNumber = this.getSelectMaxSelectNumber(type); ++ DocumentSelectOptions.defaultFilePathUri = 'file://docs' + dir; ++ documentPicker.select(DocumentSelectOptions).then((DocumentSelectResult: Array) => { ++ JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, DocumentSelectResult) ++ JsLogger.info('DocumentViewPicker.select successfully, DocumentSelectResult uri: ' + ++ JSON.stringify(DocumentSelectResult)); ++ }).catch((err: BusinessError) => { ++ JsLogger.error('DocumentViewPicker.select failed with err: ' + JSON.stringify(err)); ++ }); ++ } catch (error) { ++ let err: BusinessError = error as BusinessError; ++ JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); ++ JsLogger.error('DocumentViewPicker failed with err: ' + JSON.stringify(err)); ++ } ++ return true; + } + -+} ++ async saveFileDialog(handler: number, fileName: string, filter: Array): Promise { ++ // let config = { ++ // action: 'ohos.want.action.CREATE_FILE', ++ // parameters: { ++ // startMode: 'save', ++ // key_pick_file_name: [fileName], ++ // saveFile: fileName, ++ // } ++ // } ++ // ++ // try { ++ // let context = JsDataStore.getContext() ++ // let result = await context.startAbilityForResult(config); ++ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, [result.want?.parameters?.pick_path_return.toString()]); ++ // } catch (error) { ++ // JsLogger.info("startAbilityForResult Promise.Reject is called, error.code = %{public}s", error.code) ++ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); ++ // } ++ // return true; + -+export default new JsDataStore; -\ No newline at end of file -diff --git a/src/openharmony/native/QtCore/JsDialog.ts b/src/openharmony/native/QtCore/JsDialog.ts ++ try { ++ let DocumentSaveOptions = new picker.DocumentSaveOptions(); ++ DocumentSaveOptions.fileSuffixChoices = filter; ++ DocumentSaveOptions.defaultFilePathUri = fileName; ++ let documentPicker = new picker.DocumentViewPicker(); ++ documentPicker.save(DocumentSaveOptions).then((DocumentSaveResult: Array) => { ++ JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, DocumentSaveResult); ++ console.info('DocumentViewPicker.save successfully, DocumentSaveResult uri: ' + ++ JSON.stringify(DocumentSaveResult)); ++ }).catch((err: BusinessError) => { ++ JsLogger.error('DocumentViewPicker.save failed with err: ' + JSON.stringify(err)); ++ }); ++ } catch (error) { ++ let err: BusinessError = error as BusinessError; ++ JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); ++ JsLogger.error('DocumentViewPicker failed with err: ' + JSON.stringify(err)); ++ } ++ return true; ++ } ++} +diff --git a/src/openharmony/native/QtCore/JsEmbeddedWindow.ets b/src/openharmony/native/QtCore/JsEmbeddedWindow.ets new file mode 100644 -index 0000000000..5994b55e8b +index 0000000000..a124d8c561 --- /dev/null -+++ b/src/openharmony/native/QtCore/JsDialog.ts -@@ -0,0 +1,183 @@ -+import promptAction from '@ohos.promptAction' -+import picker from '@ohos.file.picker'; -+import JsDataStore from './JsDataStore' -+import uri from '@ohos.uri'; -+import fs from '@ohos.file.fs'; -+import JsLogger from './JsLogger' ++++ b/src/openharmony/native/QtCore/JsEmbeddedWindow.ets +@@ -0,0 +1,92 @@ ++import JsWindow from './JsWindow' ++import { ViewNodeController, NodeParams, XComponentModel, NodeType, addChildNode, removeChildNode, raiseNode, lowerNode } from 'adapter_ts' ++import JsLogger from './JsLogger'; ++import window from '@ohos.window'; + -+export class JsDialog { ++export default class JsEmbeddedWindow extends JsWindow { + -+ constructor() { -+ } ++ private jsWindowParent : JsWindow | undefined = undefined; + -+ messageBox(handler : number, title : string, text : string, buttons : Array): boolean { -+ var opt: promptAction.ShowDialogOptions = { -+ title: title, -+ message: text, -+ buttons: [{ -+ text: "OK", -+ color: "#000000" -+ }] -+ }; -+ if (buttons != null && buttons.length > 0) { -+ var first : promptAction.Button = { -+ text: buttons[0], -+ color: '#000000', -+ } -+ opt.buttons[0] = first; -+ for (var i = 1; i < buttons.length; i++) { -+ var button : promptAction.Button = { -+ text: buttons[i], -+ color: '#000000', -+ } -+ opt.buttons.push(button) -+ } -+ } ++ constructor(name: string, jsWindowParent: JsWindow, xComponentNodeController: ViewNodeController) { ++ super(name, xComponentNodeController) ++ this.jsWindowParent = jsWindowParent; ++ } + -+ promptAction.showDialog(opt, (err, data) => { -+ if (err) { -+ JsLogger.error("show dialog error: %{public}s", JSON.stringify(err)); -+ } -+ let index = err ? -1 : data.index; -+ JsDataStore.getQtNativeModule("QPA").dialogResult(handler, index); -+ }); -+ return true; ++ async setGeometry(x: number, y: number, w: number, h: number): Promise { ++ JsLogger.info('set embedded window geometry: %{public}s %{public}d %{public}d %{public}d %{public}d', this.name, x, y, w, h) ++ this.rect = {left: x, top: y, width: w, height: h}; ++ // await this.xComponentController?.setGeometry(x, y, w, h); ++ let params = this.xComponentController?.getParams(); ++ if (params != undefined) { ++ params.position_x = x; ++ params.position_y = y; ++ params.width = w; ++ params.height = h; ++ this.jsWindowParent?.getXComponentController()?.update(); + } ++ return true; ++ } + -+ isVideo(filter: string): boolean { -+ return filter.includes("mp4") || filter.includes("MPEG") -+ || filter.includes("MPG") || filter.includes("DAT") -+ || filter.includes("MOV") || filter.includes("FLV"); -+ } -+ -+ isAudio(filter: string): boolean { -+ return filter.includes("mp3") || filter.includes("wma") || filter.includes("ogg") || filter.includes("flac") -+ || filter.includes("wv"); -+ } -+ -+ isImage(filter: string): boolean { -+ return filter.includes("png") || filter.includes("jpeg") -+ || filter.includes("jpg") || filter.includes("bmp") -+ } -+ -+ async openFileDialog(handler: number, filter: string) : Promise { -+ // let isImage = this.isImage(filter); -+ // let isVideo = this.isVideo(filter); -+ // let isAudio = this.isAudio(filter); -+ // if (isImage || isVideo) { -+ // const photoSelectOptions = new picker.PhotoSelectOptions(); -+ // if (isImage) -+ // photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; -+ // if (isVideo) -+ // photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.VIDEO_TYPE; -+ // if (isVideo && isImage) -+ // photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE; -+ // photoSelectOptions.maxSelectNumber = 5; -+ // const photoViewPicker = new picker.PhotoViewPicker(); -+ // photoViewPicker.select(photoSelectOptions).then((photoSelectResult) => { -+ // JsLogger.info('photoViewPicker.select to file succeed and uri is: %{public}', uri); -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, photoSelectResult.photoUris); -+ // }).catch((err) => { -+ // JsLogger.error(`Invoke photoViewPicker.select failed, code is %{public}d, message is %{public}s`, err.code, err.message); -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); -+ // }) -+ // } else if (isAudio) { -+ // const audioSelectOptions = new picker.AudioSelectOptions(); -+ // const audioViewPicker = new picker.AudioViewPicker(); -+ // audioViewPicker.select(audioSelectOptions).then(audioSelectResult => { -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, audioSelectResult); -+ // JsLogger.info('audioViewPicker.select to file succeed and uri is: %{public}s', JSON.stringify(audioSelectResult)); -+ // }).catch((err) => { -+ // JsLogger.error(`Invoke audioViewPicker.select failed, code is %{public}d, message is %{public}s`, err.code, err.message); -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); -+ // }) -+ // } else { -+ // const documentSelectOptions = new picker.DocumentSelectOptions(); -+ // const documentViewPicker = new picker.DocumentViewPicker(); -+ // documentViewPicker.select(documentSelectOptions).then((documentResult) => { -+ // JsLogger.info('documentViewPicker.select to file succeed and uri is: %{public}s', JSON.stringify(documentResult)); -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, documentResult); -+ // }).catch((error) => { -+ // JsLogger.error(`Invoke documentViewPicker.select failed, code is %{public}d, message is %{public}s`, error.code, error.message); -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); -+ // }); -+ // } -+ -+ let config = { -+ action: 'ohos.want.action.OPEN_FILE', -+ parameters: { -+ startMode: 'choose', -+ } -+ } ++ getGeometry() : window.Rect | undefined { ++ return this.rect; ++ } + -+ let context = JsDataStore.getContext(); -+ -+ context.startAbilityForResult(config).then((result) => { -+ // 获取到文档文件的uri -+ let select_item_list = result.want.parameters.select_item_list; -+ JsLogger.info("select file: %{public}s", select_item_list.toString()); -+ // 有的系统是file://xxx/xxx/xx/xx -+ JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, [select_item_list.toString()]); -+ }).catch((error) => { -+ JsLogger.error("open file dialog result %{public}s", JSON.stringify(error)); -+ JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); -+ }); -+ return true; -+ } + -+ async saveFileDialog(handler : number, fileName: string) : Promise { -+ // let isImage = this.isImage(fileName); -+ // let isVideo = this.isVideo(fileName); -+ // let isAudio = this.isAudio(fileName); -+ // if (isImage || isVideo) { -+ // const photoSaveOptions = new picker.PhotoSaveOptions(); -+ // photoSaveOptions.newFileNames = [fileName]; -+ // const photoViewPicker = new picker.PhotoViewPicker(); -+ // photoViewPicker.save(photoSaveOptions).then((photoResult) => { -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, photoResult); -+ // }).catch((error) => { -+ // JsLogger.error(`Invoke documentViewPicker.select failed, code is %{public}d, message is %{public}s`, error.code, error.message); -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); -+ // }); -+ // } else if (isAudio) { -+ // const audioSaveOptions = new picker.AudioSaveOptions(); -+ // audioSaveOptions.newFileNames = [fileName]; -+ // const audioViewPicker = new picker.AudioViewPicker(); -+ // audioViewPicker.save(audioSaveOptions).then((audioResult) => { -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, audioResult); -+ // }).catch((error) => { -+ // JsLogger.error(`Invoke documentViewPicker.select failed, code is %{public}d, message is %{public}s`, error.code, error.message); -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); -+ // }); -+ // } else { -+ // const documentSaveOptions = new picker.DocumentSaveOptions(); -+ // documentSaveOptions.newFileNames = [fileName]; -+ // const documentViewPicker = new picker.DocumentViewPicker(); -+ // documentViewPicker.save(documentSaveOptions).then((documentResult) => { -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, documentResult); -+ // }).catch((error) => { -+ // JsLogger.error(`Invoke documentViewPicker.select failed, code is %{public}d, message is %{public}s`, error.code, error.message); -+ // JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); -+ // }); -+ // } -+ // return true; -+ let config = { -+ action: 'ohos.want.action.CREATE_FILE', -+ parameters: { -+ startMode: 'save', -+ key_pick_file_name: [fileName], -+ saveFile: fileName, -+ } -+ } ++ async destroyWindow() : Promise { ++ if (this.xComponentController == undefined) ++ return false; ++ let parent: ViewNodeController | null = this.xComponentController.getParent(); ++ if (parent) { ++ removeChildNode(parent); ++ } ++ return true; ++ } + -+ try { -+ let context = JsDataStore.getContext() -+ let result = await context.startAbilityForResult(config); -+ JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, [result.want.parameters.pick_path_return.toString()]); -+ } catch (error) { -+ JsLogger.info("startAbilityForResult Promise.Reject is called, error.code = %{public}s", error.code) -+ JsDataStore.getQtNativeModule("QPA").selectedFilesResult(handler, []); -+ } -+ return true; ++ async setVisible(visible: boolean): Promise { ++ if (this.xComponentController == undefined) ++ return false; ++ if (visible) { ++ this.xComponentController.getParams().visibility = Visibility.Visible; ++ } else { ++ this.xComponentController.getParams().visibility = Visibility.Hidden; + } ++ return false; ++ } ++ ++ async hideWindow() : Promise { ++ await this.setVisible(false); ++ } ++ ++ raise(): boolean { ++ if (this.xComponentController == undefined) ++ return false; ++ raiseNode(this.xComponentController); ++ return true; ++ } ++ ++ lower(): boolean { ++ if (this.xComponentController == undefined) ++ return false; ++ lowerNode(this.xComponentController); ++ return true; ++ } ++ ++ getUIContext(): UIContext | undefined { ++ return this.jsWindowParent?.getUIContext(); ++ } ++ ++ createSubWindow(name: string): JsWindow | undefined{ ++ JsLogger.info("create embedded window for %{public}s", name); ++ let curModel = new XComponentModel(name, XComponentType.SURFACE, "plugins_platforms_qopenharmony"); ++ let param = new NodeParams(0, 0, '50%', '50%', NodeType.XComponent, curModel); ++ param.visibility = Visibility.Visible; ++ param.border_width = 0; ++ let childXComponent: ViewNodeController = addChildNode(this.xComponentController as ViewNodeController, param); ++ let window = new JsEmbeddedWindow(name, this, childXComponent); ++ return window; ++ } ++ ++ isVisible(): boolean { ++ return this.xComponentController?.getParams().visibility == Visibility.Visible; ++ } +} diff --git a/src/openharmony/native/QtCore/JsFile.ts b/src/openharmony/native/QtCore/JsFile.ts new file mode 100644 -index 0000000000..a840215671 +index 0000000000..f6d3f710fa --- /dev/null +++ b/src/openharmony/native/QtCore/JsFile.ts -@@ -0,0 +1,91 @@ +@@ -0,0 +1,143 @@ +import fs from '@ohos.file.fs'; -+import deviceInfo from '@ohos.deviceInfo' +import JSLogger from './JsLogger' + +export class JsFile { -+ -+ private uri : string = ''; -+ private file : fs.File= null; -+ private valid : boolean = false; -+ private offset : number = 0; ++ private uri: string = ''; ++ private file: fs.File = null; ++ private valid: boolean = false; ++ private offset: number = 0; ++ private opened: boolean = false; + + constructor(uriString: string) { -+ let _uri = uriString; -+ const versionRegex = /(\d+\.\d+\.\d+\.\d+)/; -+ const match = deviceInfo.displayVersion.match(versionRegex); -+ let version = ''; -+ if (match) { -+ version = match[0] -+ } -+ if (version != '' && version.localeCompare("4.0.7.5") == -1) -+ _uri = _uri.replace("datashare://", "file:") -+ this.uri = _uri; ++ this.setFile(uriString) ++ } ++ ++ setFile(uri: string): boolean { ++ let u = uri.replace("datashare://", "file:"); ++ this.uri = u; ++ return true; + } + + open(mode: number): boolean { @@ -3140,6 +5288,7 @@ index 0000000000..a840215671 + JSLogger.info("open file: %{public}s %{public}d", this.uri, mode) + this.file = fs.openSync(this.uri, mode); + this.valid = true; ++ this.opened = true; + } catch (error) { + JSLogger.error("file open: %{public}s", JSON.stringify(error)); + this.valid = false; @@ -3148,7 +5297,36 @@ index 0000000000..a840215671 + } + } + -+ write(buffer : ArrayBuffer): number { ++ isExists(): boolean { ++ return fs.accessSync(this.uri); ++ } ++ ++ isFile(): boolean { ++ try { ++ return fs.lstatSync(this.uri).isFile(); ++ } catch (e) { ++ JSLogger.warn("fs.lstatSync failed:" + JSON.stringify(e)); ++ } ++ } ++ ++ isLink(): boolean { ++ try { ++ return fs.lstatSync(this.uri).isSymbolicLink(); ++ } catch (e) { ++ JSLogger.warn("fs.lstatSync failed:" + JSON.stringify(e)); ++ } ++ } ++ ++ isDirectory(): boolean { ++ let p = this.uri.replace("file:/", ""); ++ try { ++ return fs.lstatSync(p).isDirectory(); ++ } catch (e) { ++ JSLogger.warn("fs.lstatSync failed:" + JSON.stringify(e)); ++ } ++ } ++ ++ write(buffer: ArrayBuffer): number { + try { + return fs.writeSync(this.file.fd, buffer); + } catch (error) { @@ -3167,6 +5345,10 @@ index 0000000000..a840215671 + } + + size(): number { ++ if (false == this.opened) { ++ this.open(fs.OpenMode.READ_ONLY) ++ } ++ + return fs.statSync(this.file.fd).size; + } + @@ -3175,12 +5357,17 @@ index 0000000000..a840215671 + } + + read(length: number): ArrayBuffer { -+ let buf = new ArrayBuffer(length); + try { ++ let buf = new ArrayBuffer(length); + let options = { "offset": this.offset, "length": length } + let result = fs.readSync(this.file.fd, buf, options); -+ this.offset = this.offset + result; -+ return buf; ++ if (0 != result) { ++ this.offset = Math.min(this.offset + result, this.size()); ++ return buf; ++ } else { ++ return null; ++ } ++ + } catch (error) { + JSLogger.error("file read error: %{public}s", JSON.stringify(error)); + return null; @@ -3191,6 +5378,7 @@ index 0000000000..a840215671 + if (this.valid) { + try { + fs.closeSync(this.file); ++ this.opened = false; + } catch (error) { + JSLogger.error("file close %{public}s", JSON.stringify(error)); + } finally { @@ -3201,13 +5389,287 @@ index 0000000000..a840215671 + return true; + } + } ++ ++ rename(oldPath: string, newPath: string): boolean { ++ if (this.opened) { ++ this.close(); ++ } ++ ++ let ok = false; ++ try { ++ fs.renameSync(oldPath, newPath); ++ ok = true; ++ } catch (error) { ++ ok = false; ++ JSLogger.error("file rename %{public}s", JSON.stringify(error)); ++ } ++ return ok; ++ } ++} +diff --git a/src/openharmony/native/QtCore/JsIndependentWindow.ets b/src/openharmony/native/QtCore/JsIndependentWindow.ets +new file mode 100644 +index 0000000000..9649912462 +--- /dev/null ++++ b/src/openharmony/native/QtCore/JsIndependentWindow.ets +@@ -0,0 +1,251 @@ ++import window from '@ohos.window' ++import JsLogger from './JsLogger' ++import JsDataStore from './JsDataStore' ++import JsWindow from './JsWindow'; ++import { Margins } from './JsWindow'; ++import { ViewNodeController, NodeParams, XComponentModel, NodeType, addChildNode } from 'adapter_ts' ++import JsEmbeddedWindow from './JsEmbeddedWindow' ++import display from '@ohos.display'; ++ ++export default class JsIndependentWindow extends JsWindow { ++ private jsWindow: window.Window | undefined = undefined; ++ private fullScreen: boolean = false; ++ private maximized: boolean = false; ++ private hasFrame: boolean = true; ++ ++ constructor(name: string, jsWindow: window.Window) { ++ super(name) ++ this.jsWindow = jsWindow; ++ } ++ ++ async startListener(): Promise { ++ this.jsWindow?.on("windowSizeChange", (size) => { ++ let p = this.jsWindow?.getWindowProperties(); ++ if (p == undefined) ++ return; ++ let d = display.getDefaultDisplaySync(); ++ this.fullScreen = (p.drawableRect.width == d.width && p.drawableRect.height == d.height) ++ if (p.windowRect.left != this.rect.left || p.windowRect.top != this.rect.top) { ++ // 此次只有当坐标发生改变之后才传递到Qt中 ++ JsLogger.info("%{public}s window size changed %{public}s", this.name, JSON.stringify(p)); ++ JsDataStore.getQtNativeModule("QPA")?.handleGeometryChange(this.name, p.windowRect); ++ } ++ }); ++ this.jsWindow?.on("windowEvent", (type) => { ++ JsDataStore.getQtNativeModule("QPA").handleWindowEvent(this.name, type); ++ if (type == window.WindowEventType.WINDOW_DESTROYED) { ++ this.jsWindow = undefined ++ } ++ }) ++ if (!JsDataStore.isDefaultDevice()){ ++ this.jsWindow?.on("windowStatusChange", (type) => { ++ if (type == window.WindowStatusType.FULL_SCREEN && this.hasFrame) ++ this.maximized = true; ++ if (type == window.WindowStatusType.FLOATING) ++ this.maximized = false; ++ JsDataStore.getQtNativeModule("QPA").handleWindowStatusEvent(this.name, type); ++ }) ++ } ++ return true; ++ } ++ ++ handleWindowStatusEvent(type: window.WindowStatusType): void { ++ ++ } ++ ++ isFullScreen(): boolean { ++ // let p = this.jsWindow?.getWindowProperties(); ++ // return p == undefined ? false : p.isLayoutFullScreen; ++ return this.fullScreen; ++ } ++ ++ getGeometry(): window.Rect | undefined { ++ let p = this.jsWindow?.getWindowProperties(); ++ return p?.windowRect; ++ } ++ ++ getMargins() : Margins { ++ if (JsDataStore.isDefaultDevice() || JsDataStore.defaultShowIsMaximized()) { ++ return {left: 0, top: 0, right: 0, bottom: 0 }; ++ } ++ let m : Margins = {left: 6, top: 47, right: 6, bottom: 6 }; ++ if (this.jsWindow == undefined) ++ return m; ++ // let top = vp2px(this.jsWindow.getWindowDecorHeight()); ++ // m.top = top; ++ // let p = this.jsWindow.getWindowProperties(); ++ // m.left = p.drawableRect.left; ++ // m.top = p.drawableRect.top; ++ // // m.right = p.windowRect.width - p.drawableRect.width - m.left; ++ // // m.bottom = p.windowRect.height - p.drawableRect.height - m.top; ++ // m.right = m.left; ++ // m.bottom = m.left; ++ if (this.maximized) { ++ m.left = 0; ++ m.right = 0; ++ m.bottom = 0; ++ } ++ if (!this.hasFrame || this.fullScreen) { ++ m.left = 0; ++ m.right = 0; ++ m.bottom = 0; ++ m.top = 0; ++ } ++ // console.log("hhhhhhhhhhhhhhhhhhhhhhhhh", this.jsWindow.isWindowShowing(), JSON.stringify(p)); ++ return m; ++ } ++ ++ async setGeometry(x: number, y: number, w: number, h: number): Promise { ++ JsLogger.info('set window geometry: %{public}s %{public}d %{public}d %{public}d %{public}d', this.name, x, y, w, h) ++ try { ++ if (this.jsWindow != undefined) { ++ await this.jsWindow.moveWindowTo(x, y); ++ await this.jsWindow.resize(w, h); ++ this.rect = { ++ 'width': w, 'height': h, 'left': x, 'top': y ++ } ++ } ++ return true; ++ } catch (exception) { ++ JsLogger.error('Failed to call setGeometry for the Window %{public}s. Cause: %{public}s', this.name, JSON.stringify(exception)); ++ return false; ++ } ++ } ++ ++ async destroyWindow(): Promise { ++ try { ++ await this.jsWindow?.destroyWindow(); ++ return true; ++ } catch (exception) { ++ JsLogger.error('Failed to call destroyWindow for the Window %{public}s. Cause: %{public}s', this.name, JSON.stringify(exception)); ++ return false; ++ } ++ } ++ ++ async setVisible(visible: boolean): Promise { ++ JsLogger.info("set window visible: %{public}s %{public}s", this.name, visible); ++ try { ++ if (this.jsWindow != undefined) { ++ if (visible) { ++ await this.jsWindow.showWindow(); ++ // await this.jsWindow.moveWindowTo(this.rect.left, this.rect.top); ++ // await this.jsWindow.resize(this.rect.width, this.rect.height); ++ } else { ++ await this.hideWindow(); ++ } ++ } ++ return true; ++ } catch (exception) { ++ JsLogger.error('Failed to call showWindow for the Window. Cause: %{public}s', JSON.stringify(exception)); ++ return false; ++ } ++ } ++ ++ raise(): boolean { ++ return true; ++ } ++ ++ lower(): boolean { ++ return true; ++ } ++ ++ async hideWindow(): Promise { ++ /* TODO OpenHarmony 没有隐藏接口 */ ++ if (JsDataStore.isDefaultDevice()) { ++ let d = display.getDefaultDisplaySync(); ++ await this.jsWindow?.moveWindowTo(d.width + 50, d.height + 50); ++ } else { ++ await this.jsWindow?.minimize(); ++ } ++ } ++ ++ async load(): Promise { ++ let store = new LocalStorage(); ++ store.setOrCreate("idName", this.name); ++ await this.jsWindow?.loadContent('pages/Index', store); ++ } ++ ++ public getName(): string { ++ return this.name; ++ } ++ ++ getUIContext(): UIContext | undefined { ++ return this.jsWindow?.getUIContext(); ++ } ++ ++ createSubWindow(name: string): JsWindow | undefined { ++ JsLogger.info("create embedded window for %{public}s", name); ++ let curModel = new XComponentModel(name, XComponentType.SURFACE, "plugins_platforms_qopenharmony"); ++ let param = new NodeParams(0, 0, '100%', '100%', NodeType.XComponent, curModel); ++ param.visibility = Visibility.Visible; ++ param.border_width = 0; ++ let childXComponent: ViewNodeController = addChildNode(this.xComponentController as ViewNodeController, param); ++ let window = new JsEmbeddedWindow(name, this, childXComponent); ++ return window; ++ } ++ ++ getWindowId(): number | undefined { ++ return this.jsWindow?.getWindowProperties().id; ++ } ++ ++ isVisible(): boolean { ++ if (this.jsWindow == undefined) ++ return false; ++ return this.jsWindow.isWindowShowing(); ++ } ++ ++ async showMaximized(): Promise { ++ if (this.jsWindow == undefined) ++ return false; ++ if (JsDataStore.defaultShowIsMaximized()) { ++ let d = display.getDefaultDisplaySync(); ++ await this.setGeometry(0, 0, d.width, d.height); ++ } else { ++ await this.jsWindow.setWindowLayoutFullScreen(false); ++ await this.jsWindow.setWindowSystemBarEnable([]); ++ } ++ this.fullScreen = false; ++ this.maximized = true; ++ return true; ++ } ++ ++ async showFullScreen(): Promise { ++ if (this.jsWindow == undefined) ++ return false; ++ await this.jsWindow.setWindowLayoutFullScreen(true); ++ await this.jsWindow.setWindowSystemBarEnable([]); ++ this.fullScreen = true; ++ this.maximized = false; ++ return true; ++ } ++ ++ async showMinimized(): Promise { ++ if (this.jsWindow == undefined) ++ return false; ++ await this.jsWindow.minimize(); ++ this.fullScreen = false; ++ this.maximized = false; ++ return true; ++ } ++ ++ async showNormal(): Promise { ++ if (this.jsWindow == undefined) ++ return false; ++ await this.jsWindow.showWindow(); ++ this.fullScreen = false; ++ this.maximized = false; ++ return true; ++ } ++ ++ setHasFrame(hasFrame: boolean): void { ++ this.hasFrame = hasFrame; ++ } ++ ++ setWindowLimits(limits: window.WindowLimits): boolean { ++ if (this.jsWindow == undefined) ++ return true; ++ this.jsWindow.setWindowLimits(limits); ++ return true; ++ } ++ +} +\ No newline at end of file diff --git a/src/openharmony/native/QtCore/JsInputMethod.ts b/src/openharmony/native/QtCore/JsInputMethod.ts new file mode 100644 -index 0000000000..97d3cd37a4 +index 0000000000..47a9327d56 --- /dev/null +++ b/src/openharmony/native/QtCore/JsInputMethod.ts -@@ -0,0 +1,286 @@ +@@ -0,0 +1,287 @@ +import JsDataStore from './JsDataStore' +import { BusinessError } from '@ohos.base'; +import inputMethod from '@ohos.inputMethod'; @@ -3234,6 +5696,7 @@ index 0000000000..97d3cd37a4 + + try { + this.inputMethodController.on('sendFunctionKey', (functionKey: inputMethod.FunctionKey) => { ++ JsDataStore.getQtNativeModule("QPA").sendEnterKey(this.handler); + console.warn(`Succeeded in subscribing sendFunctionKey, functionKey.enterKeyType: ${functionKey.enterKeyType}`); + }); + } catch (err) { @@ -3495,6 +5958,81 @@ index 0000000000..97d3cd37a4 + } +} \ No newline at end of file +diff --git a/src/openharmony/native/QtCore/JsLocale.ets b/src/openharmony/native/QtCore/JsLocale.ets +new file mode 100644 +index 0000000000..681c589323 +--- /dev/null ++++ b/src/openharmony/native/QtCore/JsLocale.ets +@@ -0,0 +1,69 @@ ++import I18n from '@ohos.i18n'; ++import JsLogger from './JsLogger'; ++import { BusinessError } from '@ohos.base'; ++ ++export class JsLocale { ++ constructor() { ++ } ++ ++ /* 获取系统支持的语言列表 */ ++ getSystemLanguages(): Array { ++ let systemLanguages: Array = new Array(); ++ try { ++ systemLanguages = I18n.System.getSystemLanguages(); ++ } catch (error) { ++ let err: BusinessError = error as BusinessError; ++ JsLogger.error(`call System.getSystemLanguages failed, error code: ${err.code}, message: ${err.message}.`); ++ } ++ return systemLanguages; ++ } ++ ++ /* 获取系统语言 */ ++ getSystemLanguage(): string { ++ let systemLanguage: string = ''; ++ try { ++ systemLanguage = I18n.System.getSystemLanguage(); ++ } catch (error) { ++ let err: BusinessError = error as BusinessError; ++ console.error(`call System.getSystemLanguage failed, error code: ${err.code}, message: ${err.message}.`); ++ } ++ return systemLanguage; ++ } ++ ++ /* 获取系统地区 */ ++ getSystemRegion(): string { ++ let systemRegion: string = ''; ++ try { ++ systemRegion = I18n.System.getSystemRegion(); // 获取系统当前地区设置 ++ } catch (error) { ++ let err: BusinessError = error as BusinessError; ++ console.error(`call System.getSystemRegion failed, error code: ${err.code}, message: ${err.message}.`); ++ } ++ return systemRegion; ++ } ++ ++ /* 获取系统区域 */ ++ getSystemLocale(): string { ++ let systemLocale: string = ''; ++ try { ++ systemLocale = I18n.System.getSystemLocale(); // 获取系统当前Locale ++ } catch (error) { ++ let err: BusinessError = error as BusinessError; ++ console.error(`call System.getSystemLocale failed, error code: ${err.code}, message: ${err.message}.`); ++ } ++ return systemLocale; ++ } ++ ++ /* 获取系统偏好语言列表中的第一个偏好语言 */ ++ getFirstPreferredLanguage(): string { ++ let firstPreferredLanguage: string = ''; ++ try { ++ firstPreferredLanguage = I18n.System.getFirstPreferredLanguage(); // 获取系统当前偏好语言列表中的第一个偏好语言 ++ } catch (error) { ++ let err: BusinessError = error as BusinessError; ++ console.error(`call System.getFirstPreferredLanguage failed, error code: ${err.code}, message: ${err.message}.`); ++ } ++ return firstPreferredLanguage; ++ } ++} ++ diff --git a/src/openharmony/native/QtCore/JsLogger.ts b/src/openharmony/native/QtCore/JsLogger.ts new file mode 100644 index 0000000000..328a60c965 @@ -3539,9 +6077,44 @@ index 0000000000..328a60c965 + +export default new JsLogger('QtForHarmony'); + +diff --git a/src/openharmony/native/QtCore/JsNodeParam.ets b/src/openharmony/native/QtCore/JsNodeParam.ets +new file mode 100644 +index 0000000000..8947462ec4 +--- /dev/null ++++ b/src/openharmony/native/QtCore/JsNodeParam.ets +@@ -0,0 +1,28 @@ ++import { JsXComponentNodeController } from './JsXCompoentNodeController' ++ ++export enum NodeType { ++ XComponent, // xcomponent ++ UIExtension, // uiextension not support now ++ Container // notused ++} ++ ++export class JsNodeParam { ++ public width: Length = '50%'; ++ public height: Length = '50%'; ++ public x: Length = '20%'; // X-coordinate of the offset relative to the upper left corner of the parent component ++ public y: Length = '20%'; // Y-coordinate of the offset relative to the upper left corner of the parent component ++ public type: NodeType = NodeType.XComponent; // Component type used in a node. ++ public visibility: Visibility = Visibility.Hidden; ++ public id: string; ++ public children: Array = []; ++ ++ constructor(id: string, visibility?: Visibility, x?: Length, y?: Length, w?: Length, h?: Length, type?: NodeType) { ++ this.id = id; ++ if (type != undefined) this.type = type; ++ if (w != undefined) this.width = w; ++ if (h != undefined) this.height = h; ++ if (x != undefined) this.x = x; ++ if (y != undefined) this.y = y; ++ if (visibility != undefined) this.visibility = visibility; ++ } ++} +\ No newline at end of file diff --git a/src/openharmony/native/QtCore/JsObjectLoader.ts b/src/openharmony/native/QtCore/JsObjectLoader.ts new file mode 100644 -index 0000000000..115fa04ef6 +index 0000000000..3a900b07ae --- /dev/null +++ b/src/openharmony/native/QtCore/JsObjectLoader.ts @@ -0,0 +1,52 @@ @@ -3557,7 +6130,7 @@ index 0000000000..115fa04ef6 + + } + -+ createObject(type: string, name: string, ...args: any[]): Object { ++ createObject(type: string, name: string, ...args: Args): Object { + let modules = JsDataStore.getJsModules(); + let module: JsQtModule = null; + for (let i = 0; i < modules.length; ++i) { @@ -3600,296 +6173,830 @@ index 0000000000..115fa04ef6 \ No newline at end of file diff --git a/src/openharmony/native/QtCore/JsPasteBoard.ts b/src/openharmony/native/QtCore/JsPasteBoard.ts new file mode 100644 -index 0000000000..6d1dd690fd +index 0000000000..5399ab139a --- /dev/null +++ b/src/openharmony/native/QtCore/JsPasteBoard.ts -@@ -0,0 +1,34 @@ +@@ -0,0 +1,226 @@ +import pasteboard from '@ohos.pasteboard'; +import JsDataStore from './JsDataStore' ++import image from '@ohos.multimedia.image'; +import JsLogger from './JsLogger' + ++interface OpenHarmonyPixMap { ++ w: number; ++ h: number; ++ buffer?: ArrayBuffer; ++} ++ +export class JsPasteBoard { + -+ constructor() { -+ let systemPasteboard = pasteboard.getSystemPasteboard(); -+ systemPasteboard.on('update', () => { -+ JsDataStore.getQtNativeModule("QPA").pasteChanged(); -+ }); ++ constructor() { ++ let systemPasteboard = pasteboard.getSystemPasteboard(); ++ systemPasteboard.on('update', () => { ++ JsDataStore.getQtNativeModule("QPA").pasteChanged(); ++ }); ++ } ++ ++ async hasData() : Promise { ++ let systemPasteboard = pasteboard.getSystemPasteboard(); ++ let result = await systemPasteboard.hasData() ++ return result; ++ } ++ ++ async hasType(type: string): Promise { ++ let systemPasteboard = pasteboard.getSystemPasteboard(); ++ let data = await systemPasteboard.getData(); ++ let result = data.hasType(type); ++ JsLogger.info("clipboard has type %{public}s: %{public}s", type, result); ++ return result; ++ } ++ ++ async hasClipboardText() : Promise { ++ let result = await this.hasData(); ++ if (result) { ++ return await this.hasType(pasteboard.MIMETYPE_TEXT_PLAIN); ++ } ++ return false; ++ } ++ ++ async hasClipboardHtml() : Promise { ++ let result = await this.hasData(); ++ if (result) { ++ return await this.hasType(pasteboard.MIMETYPE_TEXT_HTML); ++ } ++ return false; ++ } ++ ++ async hasClipboardUri() : Promise { ++ let result = await this.hasData(); ++ if (result) { ++ return await this.hasType(pasteboard.MIMETYPE_TEXT_URI); ++ } ++ return false; ++ } ++ ++ async hasClipboardPixelMap(): Promise { ++ let result = await this.hasData(); ++ if (result) { ++ return await this.hasType(pasteboard.MIMETYPE_PIXELMAP); ++ } ++ return false; ++ } ++ ++ async clipboardText() : Promise { ++ try { ++ let systemPasteboard = pasteboard.getSystemPasteboard(); ++ let pasteData = await systemPasteboard.getData(); ++ return pasteData.getPrimaryText(); ++ } catch (e) { ++ JsLogger.error("get clipboard text failed: %{public}s", JSON.stringify(e)); ++ return ""; ++ } ++ } ++ ++ async clipboardHtml() : Promise { ++ try { ++ let systemPasteboard = pasteboard.getSystemPasteboard(); ++ let pasteData = await systemPasteboard.getData(); ++ return pasteData.getPrimaryHtml(); ++ } catch (e) { ++ JsLogger.error("get clipboard text failed: %{public}s", JSON.stringify(e)); ++ return ""; ++ } ++ } ++ ++ async clipboardUri() : Promise { ++ try { ++ let systemPasteboard = pasteboard.getSystemPasteboard(); ++ let pasteData = await systemPasteboard.getData(); ++ let result: string[] = [] ++ for (let i = 0; i < pasteData.getRecordCount(); ++i) { ++ let r = pasteData.getRecord(i); ++ if (r.mimeType == pasteboard.MIMETYPE_TEXT_URI) ++ result.push(r.uri); ++ } ++ return result; ++ } catch (e) { ++ JsLogger.error("get clipboard text failed: %{public}s", JSON.stringify(e)); ++ return []; ++ } ++ } ++ ++ async clipboardPixelMap(): Promise { ++ let result: OpenHarmonyPixMap = { w: 0, h: 0 } ++ try { ++ let systemPasteboard = pasteboard.getSystemPasteboard(); ++ let pasteData = await systemPasteboard.getData(); ++ let map: image.PixelMap = pasteData.getPrimaryPixelMap(); ++ if (map == undefined) ++ return result; ++ let length: number = map.getPixelBytesNumber(); ++ if (length == 0) ++ return result; ++ let info: image.ImageInfo = await map.getImageInfo(); ++ result.w = info.size.width; ++ result.h = info.size.height; ++ let buffer: ArrayBuffer = new ArrayBuffer(length); ++ await map.readPixelsToBuffer(buffer); ++ result.buffer = buffer; ++ } catch (e) { ++ JsLogger.error("get clipboard text failed: %{public}s", JSON.stringify(e)); ++ } ++ return result; ++ } ++ ++ async setClipboardText(text: string) : Promise { ++ JsLogger.info('set clipboard text %{public}s', text); ++ try { ++ let has = await this.hasData(); ++ if (has) { ++ let pasteData = pasteboard.createRecord(pasteboard.MIMETYPE_TEXT_PLAIN, text); ++ this.updatePasteData(pasteData); ++ } else { ++ let pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text); ++ await this.setPasteData(pasteData); ++ } ++ return true; ++ } catch (e) { ++ JsLogger.info('set clipboard text failed', JSON.stringify(e)); ++ return false; ++ } ++ } ++ ++ async setClipboardHtml(html: string): Promise { ++ JsLogger.info('set clipboard html %{public}s', html); ++ try { ++ let has = await this.hasData(); ++ if (has) { ++ let pasteData = pasteboard.createRecord(pasteboard.MIMETYPE_TEXT_HTML, html); ++ this.updatePasteData(pasteData); ++ } else { ++ var pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_HTML, html); ++ await this.setPasteData(pasteData); ++ } ++ return true; ++ } catch (e) { ++ JsLogger.info('set clipboard html failed', JSON.stringify(e)); ++ return false; ++ } ++ } ++ ++ async setClipboardUri(uri: string): Promise { ++ JsLogger.info('set clipboard uri %{public}s', uri); ++ try { ++ let has = await this.hasData(); ++ if (has) { ++ let pasteData = pasteboard.createRecord(pasteboard.MIMETYPE_TEXT_URI, uri); ++ this.updatePasteData(pasteData); ++ } else { ++ var pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_URI, uri); ++ await this.setPasteData(pasteData); ++ } ++ return true; ++ } catch (e) { ++ JsLogger.info('set clipboard uri failed', JSON.stringify(e)); ++ return false; ++ } ++ } ++ ++ async setClipboardPixelMap(buffer: ArrayBuffer, width: number, height: number): Promise { ++ JsLogger.info('set clipboard pixmap %{public}d %{public}d %{public}d', buffer.byteLength, width, height); ++ try { ++ let opt: image.InitializationOptions = { ++ size: { height: height, width: width }, ++ pixelFormat: image.PixelMapFormat.RGBA_8888, ++ editable: false, ++ alphaType: image.AlphaType.UNPREMUL ++ }; ++ ++ let has = await this.hasData(); ++ let pixelMap: image.PixelMap = await image.createPixelMap(buffer, opt); ++ if (has) { ++ let pasteData = pasteboard.createRecord(pasteboard.MIMETYPE_PIXELMAP, pixelMap); ++ this.updatePasteData(pasteData); ++ } else { ++ var pasteData = pasteboard.createData(pasteboard.MIMETYPE_PIXELMAP, pixelMap); ++ await this.setPasteData(pasteData); ++ } ++ return true; ++ } catch (e) { ++ JsLogger.info('set clipboard pixmap failed', JSON.stringify(e)); ++ return false; ++ } ++ } ++ ++ async setPasteData(data: pasteboard.PasteData): Promise { ++ let systemPasteboard = pasteboard.getSystemPasteboard(); ++ await systemPasteboard.setData(data); ++ } ++ ++ updatePasteData(r: pasteboard.PasteDataRecord) : void { ++ let systemPasteboard = pasteboard.getSystemPasteboard(); ++ let pastedData = systemPasteboard.getDataSync(); ++ pastedData.addRecord(r); ++ systemPasteboard.setData(pastedData); ++ } ++ ++ async clearData(): Promise { ++ let systemPasteboard = pasteboard.getSystemPasteboard(); ++ await systemPasteboard.clearData(); ++ return true; ++ } ++} +diff --git a/src/openharmony/native/QtCore/JsQtCore.ets b/src/openharmony/native/QtCore/JsQtCore.ets +new file mode 100644 +index 0000000000..00cee25d78 +--- /dev/null ++++ b/src/openharmony/native/QtCore/JsQtCore.ets +@@ -0,0 +1,41 @@ ++import { JsQtModule } from './JsQtModule' ++import JsDataStore from './JsDataStore' ++import JsLogger from './JsLogger'; ++import JsQtPlatform from './JsQtPlatform' ++ ++interface QtCore { ++ default: Object; ++} ++ ++function createJsObject(type: string, name: string, ...args: Args) { ++ if (JsDataStore.getJsObjectLoader().hasJsObject(name)) ++ return JsDataStore.getJsObjectLoader().getJsObject(name) ++ return JsDataStore.getJsObjectLoader().createObject(type, name, args); ++} ++ ++export class JsQtCore extends JsQtModule { ++ ++ public constructor() { ++ super(); ++ } ++ async loadQtCore() : Promise { ++ JsQtPlatform.load(); ++ let qtMajorVersion: number = JsDataStore.getQtNativeModule("QPA").qtMajorVersion(); ++ // let name = "libQt" + qtMajorVersion + "Core.so"; import(name)加载失败 ++ let qtCore: QtCore | undefined = undefined; ++ if (qtMajorVersion == 5) ++ qtCore = await import("libQt5Core.so"); ++ else if (qtMajorVersion == 6) ++ qtCore = await import("libQt6Core.so"); ++ if (qtCore == undefined) { ++ JsLogger.fatal("Cannot load QtCore module"); ++ return; ++ } ++ JsDataStore.setQtMajorVersion(qtMajorVersion); ++ JsDataStore.addQtNativeModule("QtCore", qtCore.default); ++ JsDataStore.getQtNativeModule("QtCore").initJsObjectLoader(createJsObject, (name: string)=>{ ++ return JsDataStore.getJsObjectLoader().deleteJsObject(name); ++ }); ++ } ++} ++export default new JsQtCore; +\ No newline at end of file +diff --git a/src/openharmony/native/QtCore/JsQtModule.ts b/src/openharmony/native/QtCore/JsQtModule.ts +new file mode 100644 +index 0000000000..2fb540cb9e +--- /dev/null ++++ b/src/openharmony/native/QtCore/JsQtModule.ts +@@ -0,0 +1,36 @@ ++import JsDataStore from './JsDataStore' ++import { HashMap } from '@kit.ArkTS'; ++ ++export class ObjectBuilder { ++ builder: (...args: Args) => Object; ++ constructor(builder: (...args: Args) => Object) { ++ this.builder = builder; ++ } ++} ++ ++ ++export class JsQtModule { ++ moduleJsObjects: HashMap = new HashMap(); ++ ++ constructor() { ++ JsDataStore.addJsModule(this); ++ } ++ ++ hasJsObjectType(type: string): boolean { ++ if (this.moduleJsObjects == undefined) ++ return false; ++ return this.moduleJsObjects.hasKey(type); ++ } ++ ++ createJsObjectImpl(type: string, ...args: any[]): Object | undefined { ++ if (this.moduleJsObjects == undefined) ++ return undefined; ++ let c = this.moduleJsObjects.get(type); ++ return (c as ObjectBuilder)?.builder(...args); ++ } ++ ++ createJsObject(type: string, name: string, ...args: any[]): Object { ++ let obj = this.createJsObjectImpl(type, ...args); ++ return obj; ++ } ++} +\ No newline at end of file +diff --git a/src/openharmony/native/QtCore/JsQtPlatform.ets b/src/openharmony/native/QtCore/JsQtPlatform.ets +new file mode 100644 +index 0000000000..f9bcc8b727 +--- /dev/null ++++ b/src/openharmony/native/QtCore/JsQtPlatform.ets +@@ -0,0 +1,52 @@ ++import { JsFile } from './JsFile' ++import { JsDialog } from './JsDialog' ++import { JsCursor } from './JsCursor' ++import { JsLocale } from './JsLocale' ++import JsDataStore from './JsDataStore'; ++import { JsPasteBoard } from './JsPasteBoard' ++import { JsInputMethod } from './JsInputMethod' ++import { JsWindowManager } from './JsWindowManager' ++import { JsStandardPaths } from './JsStandardPaths' ++import { JsServices } from './JsServices' ++import qpa from 'libplugins_platforms_qopenharmony.so' ++import { JsQtModule, ObjectBuilder } from './JsQtModule' ++ ++class JsQtPlatform extends JsQtModule { ++ public constructor() { ++ super(); ++ this.moduleJsObjects.set("JsDialog", new ObjectBuilder<[]>(() => { ++ return new JsDialog(); ++ })); ++ this.moduleJsObjects.set("JsPasteBoard", new ObjectBuilder<[]>(() => { ++ return new JsPasteBoard(); ++ })); ++ this.moduleJsObjects.set("JsWindowManager", new ObjectBuilder<[]>(() => { ++ return new JsWindowManager(); ++ })); ++ this.moduleJsObjects.set("JsFile", new ObjectBuilder<[Object]>((uri: Object) => { ++ return new JsFile(uri.toString()); ++ })); ++ this.moduleJsObjects.set("JsServices", new ObjectBuilder<[Object]>(() =>{ ++ return new JsServices(); ++ })); ++ this.moduleJsObjects.set("JsStandardPaths", new ObjectBuilder<[]>(() => { ++ return new JsStandardPaths(); ++ })); ++ this.moduleJsObjects.set("JsInputMethod", new ObjectBuilder<[]>(() => { ++ return new JsInputMethod(); ++ })); ++ this.moduleJsObjects.set("JsCursor", new ObjectBuilder<[]>(() => { ++ return new JsCursor(); ++ })); ++ this.moduleJsObjects.set("JsLocale", new ObjectBuilder<[]>(() => { ++ return new JsLocale(); ++ })); ++ JsDataStore.addQtNativeModule("QPA", qpa); ++ } ++ ++ load(): void { ++ ++ } ++} ++ ++export default new JsQtPlatform; +\ No newline at end of file +diff --git a/src/openharmony/native/QtCore/JsServices.ts b/src/openharmony/native/QtCore/JsServices.ts +new file mode 100644 +index 0000000000..04723b542b +--- /dev/null ++++ b/src/openharmony/native/QtCore/JsServices.ts +@@ -0,0 +1,26 @@ ++import JSLogger from './JsLogger' ++import common from '@ohos.app.ability.common'; ++import Want from '@ohos.app.ability.Want'; ++import { BusinessError } from '@ohos.base'; ++import JsDataStore from './JsDataStore'; ++ ++ ++export class JsServices { ++ constructor() {} ++ ++ async openUrl(inUri: string): Promise { ++ let wantInfo: Want = { ++ action: 'ohos.want.action.viewData', ++ entities: ['entity.system.browsable'], ++ uri: inUri ++ } ++ let context: common.UIAbilityContext = JsDataStore.getContext(); ++ await context.startAbility(wantInfo).then(() => { ++ JSLogger.info("open url:%{public}s success!!!!", inUri) ++ }).catch((err: BusinessError) => { ++ JSLogger.error("open url:%{public}s error!!!!", inUri) ++ return false; ++ }) ++ return true; ++ } ++} +\ No newline at end of file +diff --git a/src/openharmony/native/QtCore/JsStandardPaths.ts b/src/openharmony/native/QtCore/JsStandardPaths.ts +new file mode 100644 +index 0000000000..33a20962b0 +--- /dev/null ++++ b/src/openharmony/native/QtCore/JsStandardPaths.ts +@@ -0,0 +1,34 @@ ++import JsDataStore from './JsDataStore' ++import environment from '@ohos.file.environment'; ++ ++export class JsStandardPaths { ++ ++ path(type: string) { ++ if (type == "DIRECTORY_DOWNLOADS") { ++ return environment.getUserDownloadDir(); ++ } else if (type == "DIRECTORY_DOCUMENTS") { ++ return environment.getUserDocumentDir(); ++ } else if (type == "DIRECTORY_DESKTOP") { ++ return environment.getUserDesktopDir(); ++ } else if (type == "DIRECTORY_CACHE") { ++ let dirs = JsDataStore.getApplicationDirs(); ++ return dirs.cacheDir; ++ } else if (type == "DIRECTORY_TEMP") { ++ let dirs = JsDataStore.getApplicationDirs(); ++ return dirs.tempDir; ++ } else if (type == "DIRECTORY_BUNDLE") { ++ let dirs = JsDataStore.getApplicationDirs(); ++ return dirs.bundleCodeDir; ++ } else if (type == "DIRECTORY_FILES") { ++ let dirs = JsDataStore.getApplicationDirs(); ++ return dirs.filesDir; ++ } else if (type == "DIRECTORY_MUSIC") { ++ ++ } else if (type == "DIRECTORY_MOVIES") { ++ ++ } else if (type == "DIRECTORY_PICTURES") { ++ ++ } ++ return ""; ++ } ++} +diff --git a/src/openharmony/native/QtCore/JsWindow.ets b/src/openharmony/native/QtCore/JsWindow.ets +new file mode 100644 +index 0000000000..90cd9b4c66 +--- /dev/null ++++ b/src/openharmony/native/QtCore/JsWindow.ets +@@ -0,0 +1,99 @@ ++import window from '@ohos.window' ++import { ViewNodeController } from 'adapter_ts' ++ ++export interface Margins { ++ bottom : number; ++ left : number; ++ right : number; ++ top : number; ++} ++ ++export default class JsWindow { ++ name: string; ++ rect: window.Rect = {left: 0, top: 0, width: 200, height: 200}; ++ id: number = 0; ++ xComponentController?: ViewNodeController; ++ ++ constructor(name: string, xComponentController?: ViewNodeController) { ++ this.name = name; ++ if (xComponentController != undefined) ++ this.xComponentController = xComponentController; ++ } ++ ++ setXComponentController(xComponentController: ViewNodeController): void { ++ this.xComponentController = xComponentController; ++ } ++ ++ getXComponentController() : ViewNodeController | undefined { ++ return this.xComponentController; ++ } ++ ++ async setGeometry(x: number, y: number, w: number, h: number): Promise { ++ return false; ++ } ++ ++ async destroyWindow() : Promise { ++ return false; ++ } ++ ++ async setVisible(visible: boolean): Promise { ++ return false; ++ } ++ ++ async hideWindow() : Promise { ++ ++ } ++ ++ createSubWindow(name: string): JsWindow | undefined{ ++ return undefined; ++ } ++ ++ getMargins() : Margins { ++ let m : Margins = {left: 0, top: 0, right: 0, bottom: 0 }; ++ return m; ++ } ++ ++ getUIContext() : UIContext | undefined { ++ return undefined ++ } ++ ++ public getName(): string { ++ return this.name; ++ } ++ ++ getId(): number { ++ return this.id; ++ } ++ ++ setId(id: number): void { ++ this.id = id; ++ } ++ ++ raise(): boolean { ++ return true; ++ } ++ ++ lower(): boolean { ++ return true; ++ } ++ ++ isVisible(): boolean { ++ return false; ++ } ++ ++ async showFullScreen(): Promise { ++ return false; + } + -+ async hasClipboardText() : Promise { -+ let systemPasteboard = pasteboard.getSystemPasteboard(); -+ let result = await systemPasteboard.hasData() -+ return result; ++ async showMaximized(): Promise { ++ return false; + } + -+ async clipboardText() : Promise { -+ let systemPasteboard = pasteboard.getSystemPasteboard(); -+ let pastedata = await systemPasteboard.getData(); -+ let result = pastedata.getPrimaryText(); -+ return result; ++ async showMinimized(): Promise { ++ return false; + } + -+ setClipboardText(text: string) : boolean { -+ JsLogger.info('set clipboard text %{public}s', text); -+ var pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text); -+ let systemPasteboard = pasteboard.getSystemPasteboard(); -+ systemPasteboard.setData(pasteData); -+ return true; ++ async showNormal(): Promise { ++ return false; + } +} -diff --git a/src/openharmony/native/QtCore/JsQtModule.ts b/src/openharmony/native/QtCore/JsQtModule.ts +diff --git a/src/openharmony/native/QtCore/JsWindowManager.ets b/src/openharmony/native/QtCore/JsWindowManager.ets new file mode 100644 -index 0000000000..0a2a0f6b00 +index 0000000000..7a9cd9e06c --- /dev/null -+++ b/src/openharmony/native/QtCore/JsQtModule.ts -@@ -0,0 +1,32 @@ -+import JsDataStore from './JsDataStore' ++++ b/src/openharmony/native/QtCore/JsWindowManager.ets +@@ -0,0 +1,80 @@ ++import window from '@ohos.window' ++import HashMap from '@ohos.util.HashMap' ++import JsDataStore from './JsDataStore' ++import JsLogger from './JsLogger' ++import JsWindow from './JsWindow' ++import JsIndependentWindow from './JsIndependentWindow' + -+export class JsQtModule { + -+ moduleJsObjects : Object = null; ++export class JsWindowManager { ++ private windows: HashMap = new HashMap; + + constructor() { -+ JsDataStore.addJsModule(this); ++ + } + -+ hasJsObjectType(type: string): boolean { -+ if (this.moduleJsObjects == null) -+ return false; -+ const constructor = this.moduleJsObjects[type]; -+ return constructor != null; ++ getWindow(name: string): JsWindow | undefined { ++ if (this.windows.hasKey(name)) ++ return this.windows.get(name); ++ return undefined; + } + -+ createJsObjectImpl(type: string, ...args: any[]): Object { -+ if (this.moduleJsObjects == null) -+ return null; -+ const constructor = this.moduleJsObjects[type]; -+ if (constructor) { -+ return new constructor(...args); -+ } -+ return null; ++ async addWindow(name: string, window: JsWindow): Promise { ++ if (this.windows.hasKey(name)) ++ return; ++ this.windows.set(name, window) ++ JsDataStore.getJsObjectLoader().addJsObject(name, window); ++ let id = this.windows.length; ++ window.setId(id); + } + -+ createJsObject(type: string, name: string, ...args: any[]): Object { -+ let obj = this.createJsObjectImpl(type, ...args); -+ return obj; ++ removeWindow(name: string) { ++ this.windows.remove(name); ++ JsDataStore.getJsObjectLoader().deleteJsObject(name); + } -+} -\ No newline at end of file -diff --git a/src/openharmony/native/QtCore/JsQtPlatform.ts b/src/openharmony/native/QtCore/JsQtPlatform.ts -new file mode 100644 -index 0000000000..f5ae8781bd ---- /dev/null -+++ b/src/openharmony/native/QtCore/JsQtPlatform.ts -@@ -0,0 +1,25 @@ -+import { JsQtModule } from './JsQtModule' -+import { JsDialog } from './JsDialog' -+import { JsPasteBoard } from './JsPasteBoard' -+import { JsInputMethod } from './JsInputMethod' -+import { JsWindowManager } from './JsWindowManager' -+import { JsFile } from './JsFile' -+import { JsUrl } from './JsUrl' -+import qpa from 'libplugins_platforms_qopenharmony.so' + -+class JsQtPlatform extends JsQtModule { ++ async createWindow(name: string, title: string, isTopLevel: boolean, hasFocus: boolean, hasFrame: boolean, parentWindowName?: string): Promise { ++ JsLogger.info("create %{public}s window, window title is: %{public}s, window is top window %{public}s, window has frame: %{public}s, parent window is %{public}s", name, title, isTopLevel, hasFrame, parentWindowName); ++ try { ++ if (this.windows.hasKey(name)) ++ return false; ++ let windowStage = JsDataStore.getWindowStage(); ++ if (isTopLevel) { ++ let windowClass: window.Window | null = null; ++ if (JsDataStore.isDefaultDevice()) { ++ windowClass = await windowStage.createSubWindow(name); ++ } else { ++ windowClass = await windowStage.createSubWindowWithOptions(name, { title: title, decorEnabled: hasFrame }); ++ } ++ let window = new JsIndependentWindow(name, windowClass); ++ this.addWindow(name, window); ++ await window.load(); ++ await windowClass.setWindowFocusable(hasFocus); ++ } ++ if (!isTopLevel && parentWindowName != undefined) { ++ if (parentWindowName != undefined && parentWindowName.length > 0) { ++ let parentWindow = this.windows.get(parentWindowName); ++ if (parentWindow != undefined) { ++ let window = parentWindow.createSubWindow(name); ++ if (window != undefined) { ++ this.addWindow(name, window); ++ } ++ } ++ } ++ } ++ return true; ++ } catch (e) { ++ JsLogger.error("create window failed %{public}s", JSON.stringify(e)); ++ return false; ++ } ++ } + -+ public constructor() { -+ super(); -+ this.moduleJsObjects = { -+ JsDialog, -+ JsPasteBoard, -+ JsInputMethod, -+ JsWindowManager, -+ JsFile, -+ JsUrl -+ }; ++ async destroyWindow(name: string): Promise { ++ if (!this.windows.hasKey(name)) ++ return false; ++ let window = this.windows.remove(name); ++ // let result = window.destroyWindow(); ++ JsDataStore.getJsObjectLoader().deleteJsObject(name); ++ return true; + } +} -+export default new JsQtPlatform; -+export const QtQPA : Object = qpa -diff --git a/src/openharmony/native/QtCore/JsUrl.ts b/src/openharmony/native/QtCore/JsUrl.ts +diff --git a/src/openharmony/native/QtCore/JsXCompoentNodeController.ets b/src/openharmony/native/QtCore/JsXCompoentNodeController.ets new file mode 100644 -index 0000000000..7328ee0659 +index 0000000000..04fc391fb0 --- /dev/null -+++ b/src/openharmony/native/QtCore/JsUrl.ts -@@ -0,0 +1,25 @@ -+import JSLogger from './JsLogger' -+import common from '@ohos.app.ability.common'; -+import Want from '@ohos.app.ability.Want'; -+import { BusinessError } from '@ohos.base'; -+import JsDataStore from './JsDataStore'; ++++ b/src/openharmony/native/QtCore/JsXCompoentNodeController.ets +@@ -0,0 +1,115 @@ ++import { FrameNode, BuilderNode, NodeController, Size } from '@ohos.arkui.node'; ++import { UIContext } from '@ohos.arkui.UIContext'; ++import { JsNodeParam, NodeType } from './JsNodeParam' ++import JsWindow from './JsWindow' ++ ++@Builder ++function XComponentBuilder(param: JsNodeParam) { ++ Stack({ alignContent: Alignment.TopStart }) { ++ if (param?.type == NodeType.XComponent) { ++ // XComponent ++ XComponent({ ++ id: param?.id, ++ type: XComponentType.SURFACE, ++ libraryname: "plugins_platforms_qopenharmony" ++ }).focusable(true) ++ .focusOnTouch(true) ++ } else if (param?.type == NodeType.UIExtension) { ++ // @todo UIExtension is not support now ++ } ++ ForEach(param.children, (item: JsXComponentNodeController) => { ++ NodeContainer(item) ++ .zIndex(0) ++ }, (_item: JsXComponentNodeController, index: number) => index.toString()) ++}.width(Number.isNaN(Number(param.width.toString())) ? param.width : Number(param.width.toString())) ++ .height(Number.isNaN(Number(param.height.toString())) ? param.height : Number(param.height.toString())) ++ .visibility(param.visibility) ++ .position({ ++ x: (Number.isNaN(Number(param.x.toString())) ? param.x : Number(param.x.toString())), ++ y: (Number.isNaN(Number(param.y.toString())) ? param.y : Number(param.y.toString())) ++ }).borderWidth(1).borderColor(Color.Red); ++} + ++export class JsXComponentNodeController extends NodeController { ++ private builderNode: BuilderNode | null = null; ++ private wrapBuilder = new WrappedBuilder<[JsNodeParam]>(XComponentBuilder); ++ private param: JsNodeParam = new JsNodeParam(""); ++ private parentNodeController: JsXComponentNodeController | undefined = undefined; ++ public widthReal: number = 0; ++ public heightReal: number = 0; ++ public window?: JsWindow + -+export class JsUrl { -+ constructor() {} ++ public getCurNode(): BuilderNode | null { ++ return this.builderNode; ++ } + -+ async openUrl(inUri: string): Promise { -+ let wantInfo: Want = { -+ action: 'ohos.want.action.viewData', -+ entities: ['entity.system.browsable'], -+ uri: inUri ++ public getParent() { ++ return this.parentNodeController; + } -+ let context: common.UIAbilityContext = JsDataStore.getContext(); -+ context.startAbility(wantInfo).then(() => { -+ JSLogger.info("open url success!!!!") -+ }).catch((err: BusinessError) => { -+ JSLogger.error("open url error!!!!") -+ }) -+ return true; -+ } -+} -\ No newline at end of file -diff --git a/src/openharmony/native/QtCore/JsWindow.ts b/src/openharmony/native/QtCore/JsWindow.ts -new file mode 100644 -index 0000000000..369eb9fbe0 ---- /dev/null -+++ b/src/openharmony/native/QtCore/JsWindow.ts -@@ -0,0 +1,81 @@ -+import window from '@ohos.window' -+import JsLogger from './JsLogger' -+import display from '@ohos.display'; -+import JsDataStore from './JsDataStore' + -+export class JsWindow { ++ public setParent(parentNodeController: JsXComponentNodeController) { ++ this.parentNodeController = parentNodeController; ++ this.parentNodeController.addChild(this) ++ } + -+ private name: string; -+ private window: window.Window = undefined; -+ private rect: window.Rect; ++ public addChild(childNodeController: JsXComponentNodeController) { ++ this.param.children.push(childNodeController); ++ } + -+ constructor(name: string, window: window.Window) { -+ this.name = name; -+ this.window = window; -+ this.window?.on("windowSizeChange", (size)=>{ -+ JsLogger.info("%{public}s window size changed %{public}d %{public}d", name, size.width, size.height); -+ // JsDataStore.getQtNativeModule("QPA").handleGeometryChange(name, 0, 0, size.width, size.height); -+ }); ++ public getParam(): JsNodeParam { ++ return this.param; + } + -+ async setGeometry(x: number, y: number, w: number, h: number): Promise { -+ JsLogger.info('set window geometry: %{public}s %{public}d %{public}d %{public}d %{public}d', x, y, w, h) -+ try { -+ if (this.window != undefined) { -+ let visible = this.window.isWindowShowing(); -+ if (visible) { -+ await this.window.moveWindowTo(x, y); -+ await this.window.resize(w, h); -+ } -+ this.rect = {'width': w, 'height': h, 'left': x, 'top': y} -+ } -+ return true; -+ } catch (exception) { -+ JsLogger.error('Failed to call setGeometry for the Window %{public}s. Cause: %{public}s', this.name, JSON.stringify(exception)); -+ return false; -+ } ++ public setParams(param: JsNodeParam) { ++ this.param = param; ++ this.update(); + } + -+ async destroyWindow() : Promise { -+ try { -+ await this.window.destroyWindow(); -+ return true; -+ } catch (exception) { -+ JsLogger.error('Failed to call destroyWindow for the Window %{public}s. Cause: %{public}s', this.name, JSON.stringify(exception)); -+ return false; ++ constructor(window?: JsWindow, param?: JsNodeParam) { ++ super(); ++ if (window != undefined) ++ this.window = window; ++ if (param != undefined) { ++ this.param = param as JsNodeParam; ++ } ++ if (this.builderNode == null && this.window != undefined) { ++ this.builderNode = new BuilderNode(this.window.getUIContext() as UIContext); ++ this.builderNode.build(this.wrapBuilder, this.param as JsNodeParam); + } + } + -+ async setVisible(visible: boolean): Promise { -+ JsLogger.error("set window visible: %{public}s %{public}s", this.name, visible); -+ try { -+ if (this.window != undefined) { -+ if (visible) { -+ await this.window.showWindow(); -+ await this.window.moveWindowTo(this.rect.left, this.rect.top); -+ await this.window.resize(this.rect.width, this.rect.height); -+ } else { -+ this.hideWindow(); -+ } -+ } -+ return true; -+ } catch (exception) { -+ JsLogger.error('Failed to call showWindow for the Window. Cause: %{public}s', JSON.stringify(exception)); -+ return false; ++ makeNode(uiContext: UIContext): FrameNode | null { ++ // Check whether the builderNode is initialized and then determine whether to run the build command. ++ if (this.builderNode != null) { ++ this.builderNode.build(this.wrapBuilder, this.param); ++ return this.builderNode.getFrameNode(); + } ++ return null; + } + -+ async hideWindow() : Promise { -+ let d = display.getDefaultDisplaySync(); -+ // ںûжṩhideӿڣѴƵĻ֮ȥ -+ await this.window.moveWindowTo(d.width + 50, d.height + 50); ++ aboutToResize(size: Size) { ++ this.widthReal = size.width; ++ this.heightReal = size.height; ++ } ++ ++ delete() { ++ for (let i = 0; i < this.param.children.length; i++) { ++ this.param.children[i].delete(); ++ } ++ this.builderNode?.build(this.wrapBuilder, new JsNodeParam("")) ++ this.builderNode = null; + } + -+ async load() : Promise { -+ let store = new LocalStorage({"name": this.name}); -+ await this.window.loadContent('pages/Index', store); -+ // ʾڣxcompoentᴴc++˵ȴʧܡ -+ await this.setVisible(true); -+ this.hideWindow(); ++ update() { ++ if (this.builderNode != null) { ++ this.builderNode.update(this.param); ++ } ++ } ++ async setGeometry(x: number, y: number, w: number, h: number): Promise { ++ this.param.x = x; ++ this.param.y = y; ++ this.param.width = w; ++ this.param.height = h; ++ return true; + } +} -diff --git a/src/openharmony/native/QtCore/JsWindowManager.ts b/src/openharmony/native/QtCore/JsWindowManager.ts +diff --git a/src/openharmony/native/QtGui/JsQtGui.ets b/src/openharmony/native/QtGui/JsQtGui.ets new file mode 100644 -index 0000000000..86263ca195 +index 0000000000..640cbe35eb --- /dev/null -+++ b/src/openharmony/native/QtCore/JsWindowManager.ts -@@ -0,0 +1,46 @@ -+import window from '@ohos.window' -+import HashMap from '@ohos.util.HashMap' -+import JsDataStore from './JsDataStore' -+import JsLogger from './JsLogger' -+import { JsWindow } from './JsWindow' ++++ b/src/openharmony/native/QtGui/JsQtGui.ets +@@ -0,0 +1,38 @@ ++import JsLogger from '../QtCore/JsLogger'; ++import JsDataStore from '../QtCore/JsDataStore' ++import { JsQtModule } from '../QtCore/JsQtModule' + -+export class JsWindowManager { -+ private windows: HashMap = new HashMap; ++import { ++ addChildNode, ++ reParentNode ++} from 'adapter_ts'; + -+ constructor() { + -+ } ++interface QtGui { ++ default: Object; ++} + ++export class JsQtGui extends JsQtModule { ++ public constructor() { ++ super(); ++ } + -+ async addWindow(name: string, window: JsWindow) : Promise { -+ if (this.windows.hasKey(name)) -+ return; -+ this.windows.set(name, window) -+ JsDataStore.getJsObjectLoader().addJsObject(name, window); -+ } -+ -+ async createWindow(name: string): Promise { -+ try { -+ if (this.windows.hasKey(name)) -+ return false; -+ let windowStage = JsDataStore.getWindowStage(); -+ let windowClass = await windowStage.createSubWindow(name); -+ let window = new JsWindow(name, windowClass); -+ await window.load(); -+ this.addWindow(name, window); -+ return true; -+ } catch (e) { -+ JsLogger.error("create window failed %{public}s", JSON.stringify(e)); -+ return false; -+ } -+ } -+ -+ async destoryWindow(name: string): Promise { -+ if (!this.windows.hasKey(name)) -+ return false; -+ let window = this.windows.get(name); -+ let result =await window.destroyWindow(); -+ JsDataStore.getJsObjectLoader().deleteJsObject(name); -+ return result; ++ async loadQtGui(): Promise { ++ let qtMajorVersion: number = JsDataStore.getQtNativeModule("QPA").qtMajorVersion(); ++ let qtGui: QtGui | undefined = undefined; ++ if (qtMajorVersion == 5) { ++ qtGui = await import("libQt5Gui.so"); ++ } else if (qtMajorVersion == 6) { ++ qtGui = await import("libQt6Gui.so"); + } ++ if (qtGui == undefined) { ++ JsLogger.fatal("Cannot load QtGui module"); ++ return; ++ } ++ JsDataStore.addQtNativeModule("QtGui", qtGui.default); ++ JsDataStore.getQtNativeModule("QtGui").initFunc("AddChildNode", addChildNode); ++ JsDataStore.getQtNativeModule("QtGui").initFunc("ReParentNode", reParentNode); ++ } +} ++ ++export default new JsQtGui; +\ No newline at end of file diff --git a/src/openharmony/openharmony.pro b/src/openharmony/openharmony.pro new file mode 100644 -index 0000000000..1c9156fde0 +index 0000000000..01944a32fd --- /dev/null +++ b/src/openharmony/openharmony.pro -@@ -0,0 +1,12 @@ +@@ -0,0 +1,15 @@ +TEMPLATE = aux +CONFIG -= qt + +templates.files += $$files($$PWD/entryability/*.ts, true) ++templates.files += $$files($$PWD/entryability/*.ets, true) +templates.files += $$files($$PWD/native/*.ts, true) ++templates.files += $$files($$PWD/native/*.ets, true) ++templates.files += $$files($$PWD/pages/*.ts, true) +templates.files += $$files($$PWD/pages/*.ets, true) +templates.path = $$[QT_INSTALL_PREFIX]/openharmony/qtbase +templates.base = $$PWD @@ -3899,24 +7006,62 @@ index 0000000000..1c9156fde0 +OTHER_FILES += $$templates.files diff --git a/src/openharmony/pages/Index.ets b/src/openharmony/pages/Index.ets new file mode 100644 -index 0000000000..c877cc24ef +index 0000000000..2cb84a3ac3 --- /dev/null +++ b/src/openharmony/pages/Index.ets -@@ -0,0 +1,14 @@ -+@Entry +@@ -0,0 +1,51 @@ ++import JsDataStore from '../native/QtCore/JsDataStore' ++import { JsWindowManager } from '../native/QtCore/JsWindowManager' ++import { NodeParams, ViewNodeController, addRootNode, NodeType, XComponentModel } from 'adapter_ts' ++ ++let storage = LocalStorage.getShared() ++ ++function createRootController(param: NodeParams): ViewNodeController | undefined { ++ let windowManager = JsDataStore.getJsObjectLoader().getJsObject("JsWindowManager") as JsWindowManager; ++ let window = windowManager?.getWindow(param.node_xcomponent.id); ++ if (window != undefined) { ++ param.visibility = Visibility.Visible; ++ param.border_width = 0; ++ let uicontext: UIContext | undefined = window.getUIContext(); ++ JsDataStore.setUiContext(uicontext); ++ let rootNodeController = addRootNode(window.getId(), uicontext as UIContext, param); ++ window.setXComponentController(rootNodeController); ++ return rootNodeController; ++ } ++ return undefined; ++} ++ ++@Entry(storage) +@Component +struct Index { -+ private store: LocalStorage = LocalStorage.GetShared(); -+ private idName = this.store.get("name"); ++ @LocalStorageProp("idName") windowId: string = ""; ++ private model: XComponentModel = ++ new XComponentModel(this.windowId, XComponentType.SURFACE, "plugins_platforms_qopenharmony"); ++ private param: NodeParams = new NodeParams(0, 0, '100%', '100%', NodeType.XComponent, this.model); ++ @State rootXComponentController: ViewNodeController | undefined = createRootController(this.param) + + build() { -+ Row() { -+ XComponent({id: this.idName, type: 'surface', libraryname: 'plugins_platforms_qopenharmony' }).width("100%").height("100%") -+ .focusable(true) -+ .focusOnTouch(true) ++ Stack() { ++ NodeContainer(this.rootXComponentController) + } + } +} ++ ++// @Entry(storage) ++// @Component ++// struct Index { ++// @LocalStorageProp("idName") windowId: string = ""; ++// ++// build() { ++// Row() { ++// XComponent({id: this.windowId, type: 'surface', libraryname: 'plugins_platforms_qopenharmony' }).width("100%").height("100%") ++// .onLoad(()=>{ ++// ++// }) ++// } ++// } ++// } +\ No newline at end of file diff --git a/src/plugins/platforms/openharmony/openharmony.json b/src/plugins/platforms/openharmony/openharmony.json new file mode 100644 index 0000000000..886625295c @@ -3928,13 +7073,14 @@ index 0000000000..886625295c +} diff --git a/src/plugins/platforms/openharmony/openharmony.pro b/src/plugins/platforms/openharmony/openharmony.pro new file mode 100644 -index 0000000000..36259aab21 +index 0000000000..ff9badfecd --- /dev/null +++ b/src/plugins/platforms/openharmony/openharmony.pro -@@ -0,0 +1,73 @@ +@@ -0,0 +1,76 @@ +TARGET = qopenharmony + -+LIBS += -lEGL -lace_napi.z -lace_ndk.z -lrawfile.z ++LIBS += -lEGL -lace_napi.z -lace_ndk.z -lrawfile.z -lnative_window ++contains(DEFINES, OH_URI): LIBS += -lohfileuri + +QT += \ + core-private gui-private egl_support-private \ @@ -3971,7 +7117,8 @@ index 0000000000..36259aab21 + $$PWD/qopenharmonydrag.h \ + $$PWD/qopenharmonyfileenginehandler.h \ + $$PWD/qopenharmonyjswindow.h \ -+ $$PWD/qopenharmonyplatformservices.h ++ $$PWD/qopenharmonyplatformservices.h \ ++ $$PWD/qopenharmonyplatformcursor.h + +SOURCES += $$PWD/qopenharmonyplatformplugin.cpp \ + $$PWD/qopenharmonyplatformwindow.cpp \ @@ -3995,7 +7142,8 @@ index 0000000000..36259aab21 + $$PWD/qopenharmonydrag.cpp \ + $$PWD/qopenharmonyfileenginehandler.cpp \ + $$PWD/qopenharmonyjswindow.cpp \ -+ $$PWD/qopenharmonyplatformservices.cpp ++ $$PWD/qopenharmonyplatformservices.cpp \ ++ $$PWD/qopenharmonyplatformcursor.cpp + + +PLUGIN_TYPE = platforms @@ -4007,20 +7155,19 @@ index 0000000000..36259aab21 + diff --git a/src/plugins/platforms/openharmony/qopenharmonydrag.cpp b/src/plugins/platforms/openharmony/qopenharmonydrag.cpp new file mode 100644 -index 0000000000..1f088afc20 +index 0000000000..e2f803ddc9 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonydrag.cpp -@@ -0,0 +1,138 @@ -+#include +@@ -0,0 +1,134 @@ +#include ++#include + +#include "qguiapplication.h" +#include "qopenharmonydrag.h" +#include "qpa/qplatformscreen.h" +#include "qpa/qplatformwindow.h" -+#include -+#include "qpa/qwindowsysteminterface.h" +#include "private/qguiapplication_p.h" ++#include "qpa/qwindowsysteminterface.h" + +QT_BEGIN_NAMESPACE + @@ -4063,9 +7210,6 @@ index 0000000000..1f088afc20 + setCanDrop(false); + updateCursor(Qt::IgnoreAction); + } -+ -+ qCDebug(ohDnd) << "drag began from" << m_sourceWindow << "cursor pos" << QCursor::pos() << "can drop?" << canDrop(); -+ qInfo() << "<----------------------:::QOpenHarmonyDrag::startDrag()<====================="; +} + +static void sendDragLeave(QWindow *window) @@ -4182,41 +7326,20 @@ index 0000000000..f29ee6fccb +#endif // QOPENHARMONYDRAG_H diff --git a/src/plugins/platforms/openharmony/qopenharmonyeglcore.cpp b/src/plugins/platforms/openharmony/qopenharmonyeglcore.cpp new file mode 100644 -index 0000000000..62afca9740 +index 0000000000..b59241bfbb --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyeglcore.cpp -@@ -0,0 +1,335 @@ -+#include "qopenharmonyeglcore.h" +@@ -0,0 +1,90 @@ +#include +#include -+#include ++ ++#include "qopenharmonyeglcore.h" +#include "qopenharmonylog.h" -+#include ++#include + +EGLDisplay QOpenHarmonyEGLCore::m_eglDisplay = EGL_NO_DISPLAY; +EGLConfig QOpenHarmonyEGLCore::m_eglConfig = nullptr; + -+ -+static char vertextShader[] = -+ "#version 300 es\n" -+ "layout (location = 0) in vec4 a_position;\n" -+ "layout (location = 1) in vec2 a_textCoord;\n" -+ "out vec2 v_texCoord;\n" -+ "void main() { \n" -+ " gl_Position = a_position;\n" -+ " v_texCoord = a_textCoord;\n" -+ "}\n"; -+ -+static char fragmentShader[] = -+ "#version 300 es\n" -+ "precision mediump float;\n" -+ "in vec2 v_texCoord;\n" -+ "layout(location = 0) out vec4 outColor;\n" -+ "uniform sampler2D s_TextureMap;\n" -+ "void main() {\n" -+ " outColor = texture(s_TextureMap,v_texCoord);\n" -+ "}\n"; -+ +QOpenHarmonyEGLCore::QOpenHarmonyEGLCore() +{ + @@ -4224,28 +7347,25 @@ index 0000000000..62afca9740 + +QOpenHarmonyEGLCore::~QOpenHarmonyEGLCore() +{ -+ if (m_eglSurface != EGL_NO_SURFACE) { -+ eglDestroySurface(m_eglDisplay, m_eglSurface); -+ m_eglSurface = EGL_NO_SURFACE; -+ } ++ eglDestroySurface(m_eglDisplay, m_eglSurface); +} + +void QOpenHarmonyEGLCore::init() +{ + m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (Q_UNLIKELY(m_eglDisplay == EGL_NO_DISPLAY)) { -+ LOGE("Could not open egl display"); ++ LOGE("Could not open egl display, error code: %{public}d", eglGetError()); + return; + } + + EGLint major, minor; + if (Q_UNLIKELY(!eglInitialize(m_eglDisplay, &major, &minor))) { + m_eglDisplay = EGL_NO_DISPLAY; -+ LOGE("Could not initialize egl display"); ++ LOGE("Could not initialize egl display, error code: %{public}d", eglGetError()); + } + + if (Q_UNLIKELY(!eglBindAPI(EGL_OPENGL_ES_API))) { -+ LOGE("Could not bind GL_ES API"); ++ LOGE("Could not bind GL_ES API, error code: %{public}d", eglGetError()); + return; + } + @@ -4256,46 +7376,6 @@ index 0000000000..62afca9740 + } +} + -+bool QOpenHarmonyEGLCore::valid() const -+{ -+ return m_valid; -+} -+ -+void QOpenHarmonyEGLCore::createSurface(EGLNativeWindowType window, int w, int h) -+{ -+ if (m_eglSurface != EGL_NO_SURFACE) { -+ eglDestroySurface(m_eglDisplay, m_eglSurface); -+ m_eglSurface = EGL_NO_SURFACE; -+ } -+ LOGI("QOpenHarmonyEGLCore::init window = %{public}p, w = %{public}d, h = %{public}d.", window, w, h); -+ m_width = w; -+ m_height = h; -+ m_eglWindow = window; -+ -+ if (m_eglWindow) { -+ m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, m_eglWindow, NULL); -+ if (m_eglSurface == nullptr) { -+ LOGE("EGLCore::eglCreateWindowSurface eglSurface is null, error code %{public}x", eglGetError()); -+ return; -+ } -+ } -+ m_valid = true; -+} -+ -+void QOpenHarmonyEGLCore::updateSurfaceSize(int w, int h) -+{ -+ m_width = w; -+ m_height = h; -+ if (m_image.isNull()) { -+ QImage::Format format = QImage::Format_RGBA8888_Premultiplied; -+ m_image = QImage(w, h, format); -+ m_image.fill(Qt::white); -+ } else { -+ m_image = m_image.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); -+ } -+// drawImage(m_image); -+} -+ +EGLConfig QOpenHarmonyEGLCore::initConfig(int version) { + int attribList[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, @@ -4312,198 +7392,17 @@ index 0000000000..62afca9740 + return configs; +} + -+void QOpenHarmonyEGLCore::createTexture() -+{ -+ glActiveTexture(GL_TEXTURE0); -+ glGenTextures(1, &m_TextureId); -+ glBindTexture(GL_TEXTURE_2D, m_TextureId); -+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); -+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); -+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); -+} -+ -+EGLSurface QOpenHarmonyEGLCore::eglSurface() const -+{ -+ return m_eglSurface; -+} -+ -+QImage QOpenHarmonyEGLCore::image() const -+{ -+ if (m_image.size() != QSize(m_width, m_height)) -+ m_image = m_image.scaled(m_width, m_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); -+ return m_image; -+} -+ -+void QOpenHarmonyEGLCore::initCore() -+{ -+ int attrib3_list[] = { -+ EGL_CONTEXT_CLIENT_VERSION, 2, -+ EGL_NONE -+ }; -+ -+ m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, attrib3_list); -+ -+ if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)) { -+ LOGE("EGLCore::eglMakeCurrent error = %{public}d", eglGetError()); -+ } -+ m_program = createProgram(vertextShader, fragmentShader); -+ if (!m_program) { -+ LOGE("Could not create createProgram"); -+ return; -+ } else { -+ m_samplerLoc = glGetUniformLocation(m_program, "s_TextureMap"); -+ } -+ -+ createTexture(); -+ m_inited = true; -+} -+ -+void QOpenHarmonyEGLCore::drawImage(const QImage &image) -+{ -+ if (!m_valid) -+ return; -+ if (!m_inited) -+ initCore(); -+ -+ if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)) { -+ LOGE("EGLCore::eglMakeCurrent error = %{public}d", eglGetError()); -+ } -+ glViewport(0, 0, m_width, m_height); -+// glClearColor(1.0, 1.0, 1.0, 1.0); -+// glClear(GL_COLOR_BUFFER_BIT); -+ -+ glActiveTexture(GL_TEXTURE0); -+ glBindTexture(GL_TEXTURE_2D, m_TextureId); -+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.bits()); -+ glBindTexture(GL_TEXTURE_2D, GL_NONE); -+ -+ static GLfloat verticesCoords[] = { -+ -1.0f, 1.0f, 0.0f, // Position 0 -+ -1.0f, -1.0f, 0.0f, // Position 1 -+ 1.0f, -1.0f, 0.0f, // Position 2 -+ 1.0f, 1.0f, 0.0f, // Position 3 -+ }; -+ // 4 texture coord -+ static GLfloat textureCoords[] = { -+ 0.0f, 0.0f, // TexCoord 0 -+ 0.0f, 1.0f, // TexCoord 1 -+ 1.0f, 1.0f, // TexCoord 2 -+ 1.0f, 0.0f // TexCoord 3 -+ }; -+ -+ // render container -+ glUseProgram(m_program); -+ // Load the vertex position -+ glVertexAttribPointer (0, 3, GL_FLOAT, -+ GL_FALSE, 3 * sizeof (GLfloat), verticesCoords); -+ // Load the texture coordinate -+ glVertexAttribPointer (1, 2, GL_FLOAT, -+ GL_FALSE, 2 * sizeof (GLfloat), textureCoords); -+ -+ glEnableVertexAttribArray (0); -+ glEnableVertexAttribArray (1); -+ -+ // Bind the RGBA map -+ glActiveTexture(GL_TEXTURE0); -+ glBindTexture(GL_TEXTURE_2D, m_TextureId); -+ -+ // Set the RGBA map sampler to texture unit to 0 -+ glUniform1i(m_samplerLoc, 0); -+ static GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; -+ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); -+ update(); -+ m_image = image; -+} -+ -+void QOpenHarmonyEGLCore::update() -+{ -+ eglSwapBuffers(m_eglDisplay, m_eglSurface); -+} -+ -+GLuint QOpenHarmonyEGLCore::loadShader(GLenum type, const char *shaderSrc) -+{ -+ GLuint shader; -+ GLint compiled; -+ -+ shader = glCreateShader(type); -+ if (shader == 0) { -+ LOGE("LoadShader shader error"); -+ return 0; -+ } -+ -+ glShaderSource(shader, 1, &shaderSrc, nullptr); -+ glCompileShader(shader); -+ -+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); -+ -+ if (!compiled) { -+ GLint infoLen = 0; -+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); -+ -+ if (infoLen > 1) { -+ char *infoLog = (char*)malloc(sizeof(char) * infoLen); -+ glGetShaderInfoLog(shader, infoLen, nullptr, infoLog); -+ LOGE("Error compiling shader:\n%s\n",infoLog); -+ free(infoLog); -+ } -+ glDeleteShader(shader); -+ return 0; -+ } -+ return shader; -+} -+ -+GLuint QOpenHarmonyEGLCore::createProgram(const char *vertexShader, const char *fragShader) ++EGLSurface QOpenHarmonyEGLCore::eglSurface(EGLNativeWindowType window) +{ -+ GLuint vertex; -+ GLuint fragment; -+ GLuint program; -+ GLint linked; -+ -+ vertex = loadShader(GL_VERTEX_SHADER, vertexShader); -+ if (vertex == 0) { -+ LOGE("createProgram vertex error"); -+ return 0; -+ } -+ -+ fragment = loadShader(GL_FRAGMENT_SHADER, fragShader); -+ if (fragment == 0) { -+ LOGE("createProgram fragment error"); -+ glDeleteShader(vertex); -+ return 0; -+ } -+ -+ program = glCreateProgram(); -+ if (program == 0) { -+ LOGE("createProgram program error"); -+ glDeleteShader(vertex); -+ glDeleteShader(fragment); -+ return 0; -+ } ++ if (m_eglSurface == EGL_NO_SURFACE) { ++ LOGI("QOpenHarmonyEGLCore::init window = %{public}lu.", window); + -+ glAttachShader(program, vertex); -+ glAttachShader(program, fragment); -+ glLinkProgram(program); -+ glGetProgramiv(program, GL_LINK_STATUS, &linked); -+ if (!linked) { -+ LOGE("createProgram linked error"); -+ GLint infoLen = 0; -+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen); -+ if (infoLen > 1) { -+ char *infoLog = (char *)malloc(sizeof(char) * infoLen); -+ glGetProgramInfoLog(program, infoLen, nullptr, infoLog); -+ LOGE("Error linking program:\n%s\n",infoLog); -+ free(infoLog); ++ m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, window, NULL); ++ if (m_eglSurface == EGL_NO_SURFACE) { ++ LOGE("EGLCore::eglCreateWindowSurface eglSurface is null, error code:%{public}d", eglGetError()); + } -+ glDeleteShader(vertex); -+ glDeleteShader(fragment); -+ glDeleteProgram(program); -+ return 0; + } -+ glDeleteShader(vertex); -+ glDeleteShader(fragment); -+ -+ return program; ++ return m_eglSurface; +} + +EGLConfig QOpenHarmonyEGLCore::eglConfig() @@ -4523,17 +7422,16 @@ index 0000000000..62afca9740 +} diff --git a/src/plugins/platforms/openharmony/qopenharmonyeglcore.h b/src/plugins/platforms/openharmony/qopenharmonyeglcore.h new file mode 100644 -index 0000000000..68c17e05e4 +index 0000000000..f8c6e5fe00 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyeglcore.h -@@ -0,0 +1,57 @@ +@@ -0,0 +1,32 @@ +#ifndef QOPENHARMONYEGLCORE_H +#define QOPENHARMONYEGLCORE_H + +#include +#include +#include -+#include + +class QOpenHarmonyEGLCore +{ @@ -4541,55 +7439,32 @@ index 0000000000..68c17e05e4 + QOpenHarmonyEGLCore(); + ~QOpenHarmonyEGLCore(); + ++public: + static void init(); + -+ bool valid() const; -+ -+ void createSurface(EGLNativeWindowType window, int w, int h); -+ void updateSurfaceSize(int w, int h); -+ void drawImage(const QImage &image); -+ -+public: + static EGLDisplay eglDisplay(); + + static EGLConfig eglConfig(); + + static void clear(); + -+ EGLSurface eglSurface() const; -+ -+ QImage image() const; ++ EGLSurface eglSurface(EGLNativeWindowType window); + +private: -+ void initCore(); + static EGLConfig initConfig(int version = 3); -+ void update(); -+ GLuint loadShader(GLenum type, const char *shaderSrc); -+ GLuint createProgram(const char *vertexShader, const char *fragShader); -+ void createTexture(); -+ -+ EGLNativeWindowType m_eglWindow = 0; + static EGLDisplay m_eglDisplay; + static EGLConfig m_eglConfig; -+ EGLContext m_eglContext = EGL_NO_CONTEXT; + EGLSurface m_eglSurface = nullptr; -+ GLuint m_program; -+ GLuint m_TextureId; -+ GLint m_samplerLoc; -+ int m_width; -+ int m_height; -+ bool m_valid = false; -+ bool m_inited = false; -+ mutable QImage m_image; +}; + +#endif // QOPENHARMONYEGLCORE_H diff --git a/src/plugins/platforms/openharmony/qopenharmonyeventdispatcher.cpp b/src/plugins/platforms/openharmony/qopenharmonyeventdispatcher.cpp new file mode 100644 -index 0000000000..d86340340b +index 0000000000..b289ce7664 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyeventdispatcher.cpp -@@ -0,0 +1,127 @@ +@@ -0,0 +1,128 @@ ++#include "qopenharmonylog.h" +#include "qopenharmonyeventdispatcher.h" + +QOpenHarmonyEventDispatcher::QOpenHarmonyEventDispatcher(QObject *parent) : @@ -4612,7 +7487,7 @@ index 0000000000..d86340340b + m_semaphore.release(); + wakeUp(); + } else if (prevState == Running) { -+ qWarning("Error: start without corresponding stop"); ++ LOGW("Error: start without corresponding stop"); + } + //else if prevState == StopRequest, no action needed +} @@ -4622,7 +7497,7 @@ index 0000000000..d86340340b + if (m_stopRequest.testAndSetAcquire(Running, StopRequest)) + wakeUp(); + else -+ qWarning("Error: start/stop out of sync"); ++ LOGW("Error: start/stop out of sync"); +} + +void QOpenHarmonyEventDispatcher::goingToStop(bool stop) @@ -4771,22 +7646,23 @@ index 0000000000..51fe72e96e +#endif // QOPENHARMONYEVENTDISPATCHER_H diff --git a/src/plugins/platforms/openharmony/qopenharmonyfileenginehandler.cpp b/src/plugins/platforms/openharmony/qopenharmonyfileenginehandler.cpp new file mode 100644 -index 0000000000..a2a7dec9d4 +index 0000000000..fb470150b6 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyfileenginehandler.cpp -@@ -0,0 +1,466 @@ -+#include "qopenharmonyfileenginehandler.h" -+#include "qopenharmonymain.h" -+ -+#include -+#include -+#include +@@ -0,0 +1,480 @@ +#include -+#include -+#include ++#include ++#include +#include ++#include ++#include ++#include +#include + ++#include "qopenharmonylog.h" ++#include "qopenharmonymain.h" ++#include "qopenharmonyfileenginehandler.h" ++ +QT_BEGIN_NAMESPACE + +typedef QVector FilesList; @@ -5020,12 +7896,18 @@ index 0000000000..a2a7dec9d4 + bool checkJsFile() const + { + if (m_jsFile.isNull() || !m_jsFile->isValid()) { -+ qWarning() << "js file object is invalid"; ++ LOGE("dlclose failed: %{public}s", "js file object is invalid"); + return false; + } + return true; + } + ++ bool exists() const { ++ if (!checkJsFile()) ++ return false; ++ return m_jsFile->call("isExists"); ++ } ++ + bool open(QIODevice::OpenMode openMode) override + { + if (!checkJsFile()) @@ -5099,6 +7981,7 @@ index 0000000000..a2a7dec9d4 + { + if (!checkJsFile()) + return -1; ++ + QByteArray result = m_jsFile->call("read", maxlen); + qint64 l = result.length(); + memcpy(data, result.constData(), qMin(l, maxlen)); @@ -5122,11 +8005,17 @@ index 0000000000..a2a7dec9d4 + + FileFlags fileFlags(FileFlags type = FileInfoAll) const override + { -+ FileFlags flags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag); -+ if (m_jsFile->isValid()) { -+ flags |= FileType; ++ FileFlags flags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm); ++ if (exists()) { ++ flags |= ExistsFlag; ++ if (m_jsFile->call("isFile")) { ++ flags |= FileType; ++ } else if (m_jsFile->call("isDirectory")) { ++ flags |= DirectoryType; ++ } else if (m_jsFile->call("isLink")) { ++ flags |= LinkType; ++ } + } -+ + return type & flags; + } + @@ -5195,14 +8084,14 @@ index 0000000000..a2a7dec9d4 + if (fileName.isEmpty()) + return 0; + -+ static QLatin1String datasharePrefix("datashare:"); ++// static QLatin1String datasharePrefix("datashare:"); + static QLatin1String rawfilePrefix("rawfile:/"); + -+ if (!fileName.startsWith(datasharePrefix) && !fileName.startsWith(rawfilePrefix)) ++ if (/*!fileName.startsWith(datasharePrefix) && */!fileName.startsWith(rawfilePrefix)) + return 0; + -+ if (fileName.startsWith(datasharePrefix)) -+ return new QOpenHarmonyFileEngine(fileName); ++// if (fileName.startsWith(datasharePrefix)) ++// return new QOpenHarmonyFileEngine(fileName); + + if (fileName.startsWith(rawfilePrefix)) { + QString _fileName = fileName; @@ -5277,31 +8166,96 @@ index 0000000000..b81d93de63 +#endif // QOPENHARMONYFILEENGINEHANDLER_H diff --git a/src/plugins/platforms/openharmony/qopenharmonyjswindow.cpp b/src/plugins/platforms/openharmony/qopenharmonyjswindow.cpp new file mode 100644 -index 0000000000..425c4316ee +index 0000000000..84787bd26a --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyjswindow.cpp -@@ -0,0 +1,132 @@ -+#include "qopenharmonyjswindow.h" -+#include "qopenharmonyplatformwindow.h" -+#include "qopenharmonyxcomponent.h" -+#include -+#include "qopenharmonymain.h" -+#include "qopenharmonylog.h" -+ +@@ -0,0 +1,383 @@ ++#include ++#include ++#include +#include +#include -+#include ++#include ++#include ++ ++#include "qopenharmonylog.h" ++#include "qopenharmonymain.h" ++#include "qopenharmonyjswindow.h" ++#include "qopenharmonyxcomponent.h" ++#include "qopenharmonyplatformwindow.h" + +QT_BEGIN_NAMESPACE ++Q_DECLARE_METATYPE(QMargins) ++ ++static QVariant jsMargins2QMargins(napi_value value) ++{ ++ int64_t left = qJs::getInt64(qJs::objectPropertyValue(value, "left")); ++ int64_t top = qJs::getInt64(qJs::objectPropertyValue(value, "top")); ++ int64_t right = qJs::getInt64(qJs::objectPropertyValue(value, "right")); ++ int64_t bottom = qJs::getInt64(qJs::objectPropertyValue(value, "bottom")); ++ return QVariant::fromValue(QMargins(left, top, right, bottom)); ++} ++ ++struct WindowLimits { ++ int maxWidth; ++ int maxHeight; ++ int minWidth; ++ int minHeight; ++}; ++ ++Q_DECLARE_METATYPE(WindowLimits) ++ ++static napi_value WindowLimits2JsWindowLimits(const WindowLimits &limits) ++{ ++ napi_value result = qJs::createObject(); ++ if (result == nullptr) ++ return nullptr; ++ qJs::setProperty(result, "maxWidth", limits.maxWidth); ++ qJs::setProperty(result, "maxHeight", limits.maxHeight); ++ qJs::setProperty(result, "minWidth", limits.minWidth); ++ qJs::setProperty(result, "minHeight", limits.minHeight); ++ return result; ++} ++ ++struct InitHelper ++{ ++ InitHelper() { ++ qRegisterMetaType(); ++ qRegisterMetaType(); ++ ++ std::function sj2qt = jsMargins2QMargins; ++ qJs::registerQVariantCreator(sj2qt); ++ std::function windowLimitsCreatorFunc = WindowLimits2JsWindowLimits; ++ qJs::registerCreator(windowLimitsCreatorFunc); ++ } ++}; + -+QOpenHarmonyJsWindow::QOpenHarmonyJsWindow(const QString &name) ++InitHelper *QOpenHarmonyJsWindow::m_helper = new InitHelper; ++ ++QOpenHarmonyJsWindow::QOpenHarmonyJsWindow(QOpenHarmonyJsWindow *parent) + : m_window(nullptr) ++ , m_parent(parent) + , m_component(nullptr) -+ , m_name(name) +{ + +} + ++QOpenHarmonyJsWindow::~QOpenHarmonyJsWindow() ++{ ++ if (m_xComponentController != nullptr) { ++ napi_ref c = m_xComponentController; ++ qJs::runOnJsThread([c]{ ++ napi_delete_reference(qJs::env(), c); ++ }); ++ } ++ ++ if (m_component != nullptr) { ++ m_component->setWindow(nullptr); ++ delete m_component; ++ m_component = nullptr; ++ } ++} ++ +void QOpenHarmonyJsWindow::setQtWindow(QOpenHarmonyPlatformWindow *window) +{ + if (window == nullptr) @@ -5309,14 +8263,28 @@ index 0000000000..425c4316ee + m_window = window; +} + ++QWindow *QOpenHarmonyJsWindow::qtWindow() const ++{ ++ if (m_window == nullptr) ++ return nullptr; ++ return m_window->window(); ++} ++ ++void QOpenHarmonyJsWindow::setName(const QString &name) ++{ ++ m_name = name; ++} ++ +QString QOpenHarmonyJsWindow::name() const +{ + return m_name; +} + -+QRect QOpenHarmonyJsWindow::geometry() const ++QRect QOpenHarmonyJsWindow::qtGeometry() const +{ -+ return m_geometry; ++ if (m_window == nullptr) ++ return QRect(); ++ return m_window->geometry(); +} + +void QOpenHarmonyJsWindow::setComponent(QOpenHarmonyXComponent *component) @@ -5324,33 +8292,19 @@ index 0000000000..425c4316ee + if (component == nullptr) + return; + m_component = component; ++ handleRepaintEvent(); +} + -+void QOpenHarmonyJsWindow::setNativeWindow(EGLNativeWindowType nativeWindow) ++void QOpenHarmonyJsWindow::setNativeWindow(OHNativeWindow *nativeWindow) +{ + m_nativeWindow = nativeWindow; -+ updateSurfaceSize(); +} + -+EGLNativeWindowType QOpenHarmonyJsWindow::nativeWindow() const ++OHNativeWindow *QOpenHarmonyJsWindow::nativeWindow() const +{ + return m_nativeWindow; +} + -+void QOpenHarmonyJsWindow::updateSurfaceSize() -+{ -+ uint64_t w; -+ uint64_t h; -+ int32_t ret = OH_NativeXComponent_GetXComponentSize(m_component->nativeComponent(), (void *)m_nativeWindow, &w, &h); -+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { -+ LOGE("Get surface size failed"); -+ return; -+ } -+ -+ LOGI("Get surface size %{public}llu, %{public}llu", w, h); -+ m_component->updateSize(w, h); -+} -+ +OH_NativeXComponent *QOpenHarmonyJsWindow::component() const +{ + if (m_component == nullptr) @@ -5362,45 +8316,124 @@ index 0000000000..425c4316ee +{ + if (m_component == nullptr) + return nullptr; -+ return m_component->eglSurface(); ++ return m_component->eglSurface(reinterpret_cast(m_nativeWindow)); +} + -+QImage QOpenHarmonyJsWindow::image() const ++void QOpenHarmonyJsWindow::paint(const QImage &image, const QRegion ®ion, const QPoint &offset) +{ -+ if (m_component == nullptr) -+ return QImage(); -+ return m_component->image(); ++ if (m_component == nullptr) { ++ m_image = image; ++ m_region = region; ++ m_offset = offset; ++ return; ++ } ++ m_component->paint(image, region, offset); +} + -+void QOpenHarmonyJsWindow::paint(const QImage &image) ++QMargins QOpenHarmonyJsWindow::frameMargins() const +{ -+ if (m_component == nullptr) ++ if (m_jsWindow.isNull()) ++ return QMargins(); ++ if (m_window->hasFrame()) { ++ return m_frameMargins; ++ } ++ return QMargins(); ++} ++ ++/*! ++ * \note 不要在js线程调用该方法 ++ */ ++QMargins QOpenHarmonyJsWindow::readFrameMarginsFromTs() const ++{ ++ return m_jsWindow->call("getMargins"); ++} ++ ++QOpenHarmonyJsWindow *QOpenHarmonyJsWindow::parent() const ++{ ++ return m_parent; ++} ++ ++void QOpenHarmonyJsWindow::setParent(QOpenHarmonyJsWindow *parent) ++{ ++ m_parent = parent; ++} ++ ++void QOpenHarmonyJsWindow::setWindowEvent(int event) ++{ ++ if (m_window == nullptr) + return; -+ return m_component->paint(image); ++ QMetaObject::invokeMethod(m_window->helper(), "handleWindowEvent", Q_ARG(int, event)); +} + -+QMargins QOpenHarmonyJsWindow::frameMargins() const ++void QOpenHarmonyJsWindow::handleWindowStatusEvent(int event) +{ -+ return QtOpenHarmony::frameMargins(); ++ if (m_window == nullptr) ++ return; ++ QMetaObject::invokeMethod(m_window->helper(), "handleWindowStatusEvent", Q_ARG(int, event)); +} + -+void QOpenHarmonyJsWindow::setGeometry(const QRect &rect) ++void QOpenHarmonyJsWindow::handleRepaintEvent() +{ -+ if (m_geometry == rect) ++ if (m_window == nullptr) + return; -+ m_geometry = rect; ++ QMetaObject::invokeMethod(m_window->helper(), "handleRepaintEvent"); ++} ++ ++void QOpenHarmonyJsWindow::startListener() ++{ + if (m_jsWindow.isNull()) + m_jsWindow = qJsObjectLoader->create("JsWindow", m_name); -+ m_jsWindow->call("setGeometry", rect.left(), rect.top(), rect.width(), rect.height()); ++ m_jsWindow->call("startListener"); ++} ++ ++QRect QOpenHarmonyJsWindow::jsGeometry() ++{ ++ if (m_jsWindow.isNull()) ++ return QRect(); ++ QRect jsRect = QRect(); ++ do { ++ jsRect = m_jsWindow->call("getGeometry"); /* FIXME 鸿蒙接口获取绘制矩形大小错误,临时方法-多次获取并校验 */ ++ } while (!jsRect.isValid()); ++ ++ QRect rect = jsRect; ++ m_frameMargins = readFrameMarginsFromTs(); ++ if (parent() == nullptr && !isJsFullScreen()) { ++ if (m_window->hasFrame()) ++ rect = jsRect - m_frameMargins; ++ } ++ m_rect = rect; ++ ++ qDebug() << "jsRect: " << jsRect << "frameMargins" << m_frameMargins << "qtRect" << rect; ++ return rect; ++} ++ ++QPoint QOpenHarmonyJsWindow::mapToGlobal(const QPoint &pos) const ++{ ++ return m_window->mapToGlobal(pos); +} + -+void QOpenHarmonyJsWindow::handleGeometryChange(const QRect &rect) ++void QOpenHarmonyJsWindow::setGeometry(const QRect &rect) +{ -+ if (m_geometry == rect) ++ if (m_rect == rect) + return; -+ m_geometry = rect; ++ m_rect = rect; ++ if (m_jsWindow.isNull()) ++ m_jsWindow = qJsObjectLoader->create("JsWindow", m_name); ++ QRect jsRect = rect; ++ if (m_window->hasFrame()) ++ jsRect = jsRect + readFrameMarginsFromTs(); ++ m_jsWindow->call("setGeometry", jsRect.x(), jsRect.y(), jsRect.width(), jsRect.height()); ++} ++ ++void QOpenHarmonyJsWindow::handlePosChange(const QPoint &pos) ++{ ++ handleSurfaceChanged(); ++} ++ ++void QOpenHarmonyJsWindow::handleSurfaceChanged() ++{ + if (m_window != nullptr) { -+ m_window->setGeometry(rect); ++ QMetaObject::invokeMethod(m_window->helper(), "handleGeometryChange"); + } +} + @@ -5411,14 +8444,121 @@ index 0000000000..425c4316ee + m_jsWindow->call("setVisible", visible); +} + ++bool QOpenHarmonyJsWindow::isVisible() const ++{ ++ if (m_jsWindow.isNull()) ++ return false; ++ return m_jsWindow->call("isVisible"); ++} ++ ++bool QOpenHarmonyJsWindow::isBusy() const ++{ ++ return m_isBusy; ++} ++ ++void QOpenHarmonyJsWindow::setBusy(bool busy) ++{ ++ m_isBusy = busy; ++} ++ ++WId QOpenHarmonyJsWindow::id() ++{ ++ if (m_jsWindow.isNull()) ++ m_jsWindow = qJsObjectLoader->create("JsWindow", m_name); ++ if (m_xComponentController == nullptr) ++ m_xComponentController = m_jsWindow->callReturnRef("getXComponentController"); ++ ++ WId ret = reinterpret_cast(m_xComponentController); ++ return ret; ++} ++ ++void QOpenHarmonyJsWindow::setWindowLimits(const QSize &minimumSize, const QSize &maximumSize) ++{ ++ if (m_jsWindow.isNull()) ++ return; ++ WindowLimits limits; ++ limits.maxWidth = maximumSize.width(); ++ limits.maxHeight = maximumSize.height(); ++ limits.minWidth = minimumSize.width(); ++ limits.minHeight = minimumSize.height(); ++ if (m_window->hasFrame()) { ++ QMargins m = readFrameMarginsFromTs(); ++ int woff = m.left() + m.right(); ++ int hoff = m.top() + m.bottom(); ++ limits.maxWidth += woff; ++ limits.minWidth += woff; ++ limits.maxHeight += hoff; ++ limits.minHeight += hoff; ++ } ++ m_jsWindow->call("setWindowLimits", limits); ++} ++ ++bool QOpenHarmonyJsWindow::isJsFullScreen() ++{ ++ if (m_jsWindow.isNull()) ++ m_jsWindow = qJsObjectLoader->create("JsWindow", m_name); ++ return m_jsWindow->call("isFullScreen"); ++} ++ ++void QOpenHarmonyJsWindow::repaint() ++{ ++ if (!m_image.isNull()) { ++ m_component->paint(m_image, m_region, m_offset); ++ m_image = QImage(); ++ m_region = QRegion(); ++ m_offset = QPoint(); ++ } ++} ++ ++bool QOpenHarmonyJsWindow::raise() ++{ ++ if (m_jsWindow.isNull()) ++ m_jsWindow = qJsObjectLoader->create("JsWindow", m_name); ++ return m_jsWindow->call("raise"); ++} ++ ++bool QOpenHarmonyJsWindow::lower() ++{ ++ if (m_jsWindow.isNull()) ++ m_jsWindow = qJsObjectLoader->create("JsWindow", m_name); ++ return m_jsWindow->call("lower"); ++} ++ ++void QOpenHarmonyJsWindow::showFullScreen() ++{ ++ if (m_jsWindow.isNull()) ++ m_jsWindow = qJsObjectLoader->create("JsWindow", m_name); ++ m_jsWindow->call("showFullScreen"); ++} ++ ++void QOpenHarmonyJsWindow::showMaximized() ++{ ++ if (m_jsWindow.isNull()) ++ m_jsWindow = qJsObjectLoader->create("JsWindow", m_name); ++ m_jsWindow->call("showMaximized"); ++} ++ ++void QOpenHarmonyJsWindow::showMinimized() ++{ ++ if (m_jsWindow.isNull()) ++ m_jsWindow = qJsObjectLoader->create("JsWindow", m_name); ++ m_jsWindow->call("showMinimized"); ++} ++ ++void QOpenHarmonyJsWindow::showNormal() ++{ ++ if (m_jsWindow.isNull()) ++ m_jsWindow = qJsObjectLoader->create("JsWindow", m_name); ++ m_jsWindow->call("showNormal"); ++} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/openharmony/qopenharmonyjswindow.h b/src/plugins/platforms/openharmony/qopenharmonyjswindow.h new file mode 100644 -index 0000000000..fca5cd99c3 +index 0000000000..d8e96c55ff --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyjswindow.h -@@ -0,0 +1,64 @@ +@@ -0,0 +1,112 @@ +#ifndef QOPENHARMONYJSWINDOW_H +#define QOPENHARMONYJSWINDOW_H + @@ -5427,68 +8567,116 @@ index 0000000000..fca5cd99c3 +#include +#include +#include ++#include +#include ++#include ++#include ++#include + -+ ++class QImage; +QT_BEGIN_NAMESPACE + +class QOpenHarmonyJsObject; +class QOpenHarmonyXComponent; +class QOpenHarmonyPlatformWindow; ++class InitHelper; + +class QOpenHarmonyJsWindow +{ +public: -+ explicit QOpenHarmonyJsWindow(const QString &name); ++ explicit QOpenHarmonyJsWindow(QOpenHarmonyJsWindow *parent = nullptr); ++ ~QOpenHarmonyJsWindow(); + + void setQtWindow(QOpenHarmonyPlatformWindow *window); + ++ QWindow *qtWindow() const; ++ ++ void setName(const QString &name); + QString name() const; + + void setVisible(bool visible); ++ bool isVisible() const; + + void setGeometry(const QRect &rect); + -+ void handleGeometryChange(const QRect &rect); ++ void handlePosChange(const QPoint &pos); + -+ QRect geometry() const; ++ void handleSurfaceChanged(); + -+ void setComponent(QOpenHarmonyXComponent *component); ++ QRect qtGeometry() const; + -+ void setNativeWindow(EGLNativeWindowType nativeWindow); ++ void setComponent(QOpenHarmonyXComponent *component); + -+ EGLNativeWindowType nativeWindow() const; ++ void setNativeWindow(OHNativeWindow *nativeWindow); + -+ void updateSurfaceSize(); ++ OHNativeWindow *nativeWindow() const; + ++ bool isBusy() const; ++ void setBusy(bool busy); + + OH_NativeXComponent *component() const; + + EGLSurface eglSurface(); + -+ QImage image() const; -+ -+ void paint(const QImage &image); ++ void paint(const QImage &image, const QRegion ®ion, const QPoint &offset); + ++ void repaint(); + QMargins frameMargins() const; + ++ QMargins readFrameMarginsFromTs() const; ++ ++ QOpenHarmonyJsWindow *parent() const; ++ void setParent(QOpenHarmonyJsWindow *parent); ++ ++ void setWindowEvent(int event); ++ void handleWindowStatusEvent(int event); ++ void handleRepaintEvent(); ++ ++ void startListener(); ++ ++ QRect jsGeometry(); ++ ++ QPoint mapToGlobal(const QPoint &pos) const; ++ ++ WId id(); ++ void setWindowLimits(const QSize &minimumSize, const QSize &maximumSize); ++ ++ bool raise(); ++ bool lower(); ++ ++ void showFullScreen(); ++ void showMaximized(); ++ void showMinimized(); ++ void showNormal(); ++ ++private: ++ bool isJsFullScreen(); +private: + QOpenHarmonyPlatformWindow *m_window; ++ QOpenHarmonyJsWindow *m_parent; + QSharedPointer m_jsWindow; + QOpenHarmonyXComponent *m_component; + QString m_name; -+ QRect m_geometry; -+ EGLNativeWindowType m_nativeWindow = 0; ++ OHNativeWindow *m_nativeWindow = nullptr; ++ bool m_isBusy = false; ++ QRect m_rect; ++ napi_ref m_xComponentController = nullptr; ++ friend class InitHelper; ++ static InitHelper *m_helper; ++ QImage m_image; ++ QRegion m_region; ++ QPoint m_offset; ++ QMargins m_frameMargins; +}; -+ ++Q_DECLARE_METATYPE(QOpenHarmonyJsWindow) +QT_END_NAMESPACE +#endif // QOPENHARMONYJSWINDOW_H diff --git a/src/plugins/platforms/openharmony/qopenharmonyjswindowmanager.cpp b/src/plugins/platforms/openharmony/qopenharmonyjswindowmanager.cpp new file mode 100644 -index 0000000000..bcbb1f55fb +index 0000000000..8e98e27653 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyjswindowmanager.cpp -@@ -0,0 +1,195 @@ +@@ -0,0 +1,319 @@ +#include "qopenharmonyjswindowmanager.h" +#include "qopenharmonyxcomponent.h" +#include "qopenharmonyjsenvironment.h" @@ -5496,21 +8684,21 @@ index 0000000000..bcbb1f55fb +#include "qopenharmonylog.h" +#include "qopenharmonymain.h" +#include "qopenharmonyjswindow.h" ++#include "qopenharmonyplatformopenglwindow.h" + +#include + ++#include ++#include ++#include +#include +#include +#include -+#include -+#include -+#include + + +Q_GLOBAL_STATIC(QOpenHarmonyJsWindowManager, manager) + +QOpenHarmonyJsWindowManager::QOpenHarmonyJsWindowManager() -+ : m_mainwindow(nullptr) +{ + +} @@ -5529,31 +8717,48 @@ index 0000000000..bcbb1f55fb + +QOpenHarmonyJsWindow *QOpenHarmonyJsWindowManager::createWindow(QOpenHarmonyPlatformWindow *platformWindow) +{ -+ QString name = QtOpenHarmony::isPhone() ? "opemharmony_qt_mainwindow" : windowName(); ++ if (platformWindow == nullptr) ++ return nullptr; ++ ++ QMutexLocker locker(&m_mutex); ++ QString name = platformWindow->windowName(); ++ // 查找window是否已经创建,如果已经创建直接返回 + QOpenHarmonyJsWindow *jsWindow = window(name); + if (jsWindow != nullptr) { -+ jsWindow->setQtWindow(platformWindow); ++ setWindow(jsWindow, platformWindow); ++ return jsWindow; ++ } ++ ++ // 查找是否有可用的窗口,如果有返回可用的窗口 ++ jsWindow = availableWindow(); ++ if (jsWindow != nullptr) { ++ setWindow(jsWindow, platformWindow); + return jsWindow; + } -+ m_jsWindowCreated.storeRelease(false); -+ qDebug() << "create open harmony platfrom window for" << name; ++ ++ // 如果没有,通知js端创建窗口,并等待窗口完成创建 ++ name = windowName(); ++ LOGW("create open harmony platfrom window for %{public}s", name.toLocal8Bit().constData()); + checkJsWindowManager(); -+ bool result = m_jsWindowManager->call("createWindow", name); -+ if (!result) { -+ qWarning() << "create open harmony platfrom window failed"; ++ if (m_jsWindowManager.isNull()) { ++ LOGE("get openharmony window manager failed"); + return nullptr; + } -+ if (wait()) { -+ qDebug() << "create open harmony platfrom window succeeded"; -+ } -+ else { -+ qWarning() << "create open harmony platfrom window failed"; ++ ++ bool result = m_jsWindowManager->call("createWindow", name, platformWindow->windowTitle(), platformWindow->isTopLevelWindow(), platformWindow->hasFocus(), platformWindow->hasFrame(), platformWindow->parentWindowName()); ++ if (!result) { ++ LOGE("create open harmony platfrom window failed"); + return nullptr; + } + + jsWindow = window(name); ++ if (jsWindow == nullptr) { ++ jsWindow = new QOpenHarmonyJsWindow(); ++ jsWindow->setName(name); ++ m_windows << jsWindow; ++ } + if (jsWindow != nullptr) { -+ jsWindow->setQtWindow(platformWindow); ++ setWindow(jsWindow, platformWindow); + return jsWindow; + } + return jsWindow; @@ -5561,14 +8766,20 @@ index 0000000000..bcbb1f55fb + +void QOpenHarmonyJsWindowManager::destoryWindow(QOpenHarmonyJsWindow *window) +{ ++ if (QtOpenHarmony::isJsExited()) ++ return; + if (window == nullptr || !m_windows.contains(window)) + return; -+ m_windows.removeOne(window); ++ { ++ QMutexLocker locker(&m_windowsMutex); ++ m_windows.removeOne(window); ++ } + checkJsWindowManager(); -+ bool result = m_jsWindowManager->call("destoryWindow", window->name()); ++ bool result = m_jsWindowManager->call("destroyWindow", window->name()); + if (!result) { -+ qWarning() << "destory open harmony platfrom window failed"; ++ LOGE("destory open harmony platfrom window failed"); + } ++ window->setBusy(false); + delete window; +} + @@ -5592,14 +8803,31 @@ index 0000000000..bcbb1f55fb + }); +} + ++QOpenHarmonyJsWindow *QOpenHarmonyJsWindowManager::availableWindow() const ++{ ++ return find([](QOpenHarmonyJsWindow *w){ ++ return !w->isBusy(); ++ }); ++} ++ ++bool QOpenHarmonyJsWindowManager::hasWindow(QOpenHarmonyJsWindow *window) const ++{ ++ return m_windows.contains(window); ++} ++ +void QOpenHarmonyJsWindowManager::setWindowXComponent(OH_NativeXComponent *component) +{ ++ QMutexLocker locker(&m_mutex); + QOpenHarmonyXComponent *xc = new QOpenHarmonyXComponent(component); + QString name = xc->name(); -+ QOpenHarmonyJsWindow *jsWindow = new QOpenHarmonyJsWindow(name); ++ QOpenHarmonyJsWindow *jsWindow = window(name); ++ if (jsWindow == nullptr) { ++ jsWindow = new QOpenHarmonyJsWindow(); ++ m_windows << jsWindow; ++ } + xc->setWindow(jsWindow); + jsWindow->setComponent(xc); -+ m_windows << jsWindow; ++ jsWindow->setName(name); +} + +void QOpenHarmonyJsWindowManager::checkJsWindowManager() @@ -5608,8 +8836,20 @@ index 0000000000..bcbb1f55fb + m_jsWindowManager = qJsObjectLoader->create("JsWindowManager"); +} + ++void QOpenHarmonyJsWindowManager::setWindow(QOpenHarmonyJsWindow *jsWindow, QOpenHarmonyPlatformWindow *platformWindow) ++{ ++ jsWindow->setQtWindow(platformWindow); ++ jsWindow->setBusy(true); ++ QOpenHarmonyPlatformOpenGLWindow *parent = dynamic_cast(platformWindow->parent()); ++ if (parent != nullptr) { ++ jsWindow->setParent(parent->jsWindow()); ++ } ++ platformWindow->setWindowName(jsWindow->name()); ++} ++ +QOpenHarmonyJsWindow *QOpenHarmonyJsWindowManager::find(std::function function) const +{ ++ QMutexLocker locker(const_cast(&m_windowsMutex)); + auto it = std::find_if(m_windows.constBegin(), m_windows.constEnd(), function); + return it == m_windows.constEnd() ? nullptr : *it; +} @@ -5628,21 +8868,72 @@ index 0000000000..bcbb1f55fb + +static napi_value handleJsGeometryChange(napi_env env, napi_callback_info info) +{ -+ size_t argc = 5; -+ napi_value args[5]; ++ size_t argc = 2; ++ napi_value args[2]; ++ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); ++ ++ if (argc != 2) { ++ qJs::throwError("Wrong number of arguments"); ++ return nullptr; ++ } ++ ++ QString name = qJs::getString(args[0]); ++ QRect rect = qJs::getRect(args[1]); ++ qJsWindowManager->handleGeometryChange(name, rect); ++ return nullptr; ++} ++ ++static napi_value handleJsPosChange(napi_env env, napi_callback_info info) ++{ ++ size_t argc = 3; ++ napi_value args[3]; ++ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); ++ ++ if (argc != 3) { ++ qJs::throwError("Wrong number of arguments"); ++ return nullptr; ++ } ++ ++ QString name = qJs::getString(args[0]); ++ int x = qJs::getInt32(args[1]); ++ int y = qJs::getInt32(args[2]); ++ qJsWindowManager->handlePosChange(name, QPoint(x, y)); ++ return nullptr; ++} ++ ++static napi_value handleJsWindowEvent(napi_env env, napi_callback_info info) ++{ ++ size_t argc = 2; ++ napi_value args[2]; ++ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); ++ ++ if (argc != 2) { ++ qJs::throwError("Wrong number of arguments"); ++ return nullptr; ++ } ++ ++ QString name = qJs::getString(args[0]); ++ int event = qJs::getInt32(args[1]); ++ ++ qJsWindowManager->handleWindowEvent(name, event); ++ return nullptr; ++} ++ ++static napi_value handleJsWindowStatusEvent(napi_env env, napi_callback_info info) ++{ ++ size_t argc = 2; ++ napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + -+ if (argc != 5) { ++ if (argc != 2) { + qJs::throwError("Wrong number of arguments"); + return nullptr; + } + + QString name = qJs::getString(args[0]); -+ int left = qJs::getInt32(args[1]); -+ int top = qJs::getInt32(args[2]); -+ int w = qJs::getInt32(args[3]); -+ int h = qJs::getInt32(args[4]); -+ qJsWindowManager->handleGeometryChange(name, QRect(left, top, w, h)); ++ int event = qJs::getInt32(args[1]); ++ ++ qJsWindowManager->handleWindowStatusEvent(name, event); + return nullptr; +} + @@ -5653,14 +8944,14 @@ index 0000000000..bcbb1f55fb + inited = true; + napi_property_descriptor desc[] ={ + DECLARE_NAPI_FUNCTION("handleGeometryChange", handleJsGeometryChange), ++ DECLARE_NAPI_FUNCTION("handleWindowEvent", handleJsWindowEvent), ++ DECLARE_NAPI_FUNCTION("handlePosChange", handleJsPosChange), ++ DECLARE_NAPI_FUNCTION("handleWindowStatusEvent", handleJsWindowStatusEvent), + }; + napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); + } + napi_value exportInstance = nullptr; + OH_NativeXComponent *nativeXComponent = nullptr; -+ int32_t ret; -+ char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { }; -+ uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + + NAPI_CALL_RETURN_VOID_NO_THROW(env, napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance)); + NAPI_CALL_RETURN_VOID_NO_THROW(env, napi_unwrap(env, exportInstance, reinterpret_cast(&nativeXComponent))); @@ -5670,14 +8961,35 @@ index 0000000000..bcbb1f55fb + +void QOpenHarmonyJsWindowManager::handleGeometryChange(const QString &name, const QRect &rect) +{ -+ if (QtOpenHarmony::isPhone()) { -+ QtOpenHarmony::updateDisplayMetrics(rect.width(), rect.height()); -+ } else { -+ QOpenHarmonyJsWindow *w = window(name); -+ if (w == nullptr) -+ return; -+ w->handleGeometryChange(rect); -+ } ++ QOpenHarmonyJsWindow *w = window(name); ++ if (w == nullptr) ++ return; ++// w->handleGeometryChange(rect); ++ w->handleSurfaceChanged(); ++} ++ ++void QOpenHarmonyJsWindowManager::handlePosChange(const QString &name, const QPoint &pos) ++{ ++ QOpenHarmonyJsWindow *w = window(name); ++ if (w == nullptr) ++ return; ++ w->handlePosChange(pos); ++} ++ ++void QOpenHarmonyJsWindowManager::handleWindowEvent(const QString &name, int event) ++{ ++ QOpenHarmonyJsWindow *w = window(name); ++ if (w == nullptr) ++ return; ++ w->setWindowEvent(event); ++} ++ ++void QOpenHarmonyJsWindowManager::handleWindowStatusEvent(const QString &name, int event) ++{ ++ QOpenHarmonyJsWindow *w = window(name); ++ if (w == nullptr) ++ return; ++ w->handleWindowStatusEvent(event); +} + +void QOpenHarmonyJsWindowManager::windowCreated() @@ -5686,10 +8998,10 @@ index 0000000000..bcbb1f55fb +} diff --git a/src/plugins/platforms/openharmony/qopenharmonyjswindowmanager.h b/src/plugins/platforms/openharmony/qopenharmonyjswindowmanager.h new file mode 100644 -index 0000000000..93fd13de04 +index 0000000000..8cc43869d9 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyjswindowmanager.h -@@ -0,0 +1,53 @@ +@@ -0,0 +1,62 @@ +#ifndef QOPENHARMONYJSWINDOWMANAGER_H +#define QOPENHARMONYJSWINDOWMANAGER_H + @@ -5724,14 +9036,22 @@ index 0000000000..93fd13de04 + + QOpenHarmonyJsWindow *window(const QString &name) const; + ++ QOpenHarmonyJsWindow *availableWindow() const; ++ ++ bool hasWindow(QOpenHarmonyJsWindow *window) const; ++ + static void init(napi_env env, napi_value exports); + + void handleGeometryChange(const QString &name, const QRect &rect); ++ void handlePosChange(const QString &name, const QPoint &pos); + ++ void handleWindowEvent(const QString &name, int event); ++ void handleWindowStatusEvent(const QString &name, int event); + void windowCreated(); ++ void checkJsWindowManager(); +private slots: + void setWindowXComponent(OH_NativeXComponent *component); -+ void checkJsWindowManager(); ++ void setWindow(QOpenHarmonyJsWindow *jsWindow, QOpenHarmonyPlatformWindow *platformWindow); +private: + QOpenHarmonyJsWindow *find(std::function function) const; + bool wait(int timeout = 2000); @@ -5739,7 +9059,8 @@ index 0000000000..93fd13de04 + QList m_windows; + QSharedPointer m_jsWindowManager; + QBasicAtomicInt m_jsWindowCreated; -+ QOpenHarmonyJsWindow *m_mainwindow; ++ QMutex m_mutex; ++ QMutex m_windowsMutex; +}; + +#endif // QOPENHARMONYJSWINDOWMANAGER_H @@ -5857,14 +9178,14 @@ index 0000000000..1a88d88b24 +#endif // QOPENHARMONYDEFINES_H diff --git a/src/plugins/platforms/openharmony/qopenharmonymain.cpp b/src/plugins/platforms/openharmony/qopenharmonymain.cpp new file mode 100644 -index 0000000000..ca8eefffcc +index 0000000000..18becb238f --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonymain.cpp -@@ -0,0 +1,451 @@ +@@ -0,0 +1,552 @@ +#include -+#include +#include +#include ++#include +#include +#include + @@ -5880,7 +9201,8 @@ index 0000000000..ca8eefffcc +#include +#include +#include -+#include ++#include ++#include + +#include "qopenharmonylog.h" +#include "qopenharmonymain.h" @@ -5911,7 +9233,20 @@ index 0000000000..ca8eefffcc +static QOpenHarmonyPlatformIntegration *m_platformIntegration = nullptr; +static QOpenHarmonyFileEngineHandler *m_openHarmonyFileEngineHandler = nullptr; +static QString m_deviceType = "default"; ++static QString m_displayVersion = ""; +static QList m_applicationParams; ++static bool m_jsAppExited = false; ++static bool m_isTouchPad = false; ++static bool m_defaultShowIsMaximized = false; ++static napi_threadsafe_function g_threadsafe_quit_js_function = nullptr; ++ ++static void callQuitJs(napi_env env, napi_value js_cb, void* context, void* data) { ++ Q_UNUSED(env); ++ Q_UNUSED(context); ++ napi_value undefined = nullptr; ++ napi_get_undefined(qJs::env(), &undefined); ++ qJs::callFunction(undefined, js_cb); ++} + +static void *startMainMethod(void *arg) +{ @@ -5926,28 +9261,26 @@ index 0000000000..ca8eefffcc + int res = dlclose(m_mainLibraryHnd); + if (res < 0) + LOGE("dlclose failed: %{public}s", dlerror()); -+ } -+ ++ } ++ if (!m_jsAppExited) { ++ napi_acquire_threadsafe_function(g_threadsafe_quit_js_function); ++ napi_call_threadsafe_function(g_threadsafe_quit_js_function, nullptr, napi_tsfn_blocking); ++ } + sem_post(&m_terminateSemaphore); + sem_wait(&m_exitSemaphore); + sem_destroy(&m_exitSemaphore); -+#if 1 -+ if (0 != ret) -+ pthread_exit((void*)-1); -+ else -+ pthread_exit((void*)0); -+#endif -+ return nullptr; ++ exit(ret); ++ return 0; +} + +static napi_value startQtApplication(napi_env env, napi_callback_info info) +{ + napi_status status; -+ size_t argc = 3; -+ napi_value args[3]; ++ size_t argc = 2; ++ napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + -+ if (argc < 2) { ++ if (argc != 2) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return qJs::createBool(false); + } @@ -5975,25 +9308,33 @@ index 0000000000..ca8eefffcc + LOGE("Can't set environment for QT_HARMONY_CACHE_DIR"); + } + ++ if (::setenv("QT_HARMONY_QML_CACHE_DIR", cacheQmlStr.toLatin1().constData(), 1) != 0) { ++ LOGE("Can't set environment for QT_HARMONY_QML_CACHE_DIR"); ++ } ++ + if (::setenv("QT_QPA_PLATFORM_PLUGIN_PATH", libDir.constData(), 1) != 0) { + LOGE("Can't set environment for QT_QPA_PLATFORM_PLUGIN_PATH"); + } ++ + if (::setenv("QML_DISABLE_DISK_CACHE", "1", 1) != 0) { + LOGE("Can't set environment for QML_DISABLE_DISK_CACHE"); + } ++ + if (::setenv("QT_PLUGIN_PATH", libDir.constData(), 1) != 0) { + LOGE("Can't set environment for QT_PLUGIN_PATH"); + } ++ QString tempDir = qJs::getObjectPropertyValue(args[0], "tempDir"); ++ qputenv("TMPDIR", tempDir.toLatin1()); + + QByteArrayList boundPath = { libDir }; + QByteArray boundImport = boundPath.join(":"); -+ qWarning() << "harmony boundImport import path:" << boundImport; ++ LOGI("harmony boundImport import path: %{public}s", boundImport.data()); + if (::setenv("QT_HARMONY_BUNDLED_LIBS_PATH", boundImport.constData(), 1) != 0) { + LOGE("Can't set environment for QT_HARMONY_BUNDLED_LIBS_PATH"); + } + + QByteArray qmlImportPath = qmls.join(":"); -+ qWarning() << "qml import path:" << qmlImportPath; ++ LOGI("qml import path: %{public}s", qmlImportPath.data()); + if (::setenv("QML2_IMPORT_PATH", qmlImportPath.constData(), 1) != 0) { + LOGE("Can't set environment for QML2_IMPORT_PATH"); + } @@ -6034,13 +9375,34 @@ index 0000000000..ca8eefffcc + if (sem_init(&m_terminateSemaphore, 0, 0) == -1) + return qJs::createBool(false); + -+ ++ /* NOTE 事件全部以异步的方式传递 */ ++// QWindowSystemInterface::setSynchronousWindowSystemEvents(false); + m_openHarmonyFileEngineHandler = new QOpenHarmonyFileEngineHandler; + m_applicationParams[0] = libDir + "/" + fileName; + pthread_create(&m_qtAppThread, nullptr, startMainMethod, nullptr); + return qJs::createBool(true); +} + ++static napi_value setJsQuitFunction(napi_env env, napi_callback_info info) ++{ ++ size_t argc = 1; ++ napi_value args[1]; ++ napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); ++ ++ if (argc != 1) { ++ return qJs::createBool(false); ++ } ++ ++ napi_value quitJs = args[0]; ++ napi_value name_quitJs; ++ napi_create_string_utf8(env, "JsObjectLoader", NAPI_AUTO_LENGTH, &name_quitJs); ++ napi_create_threadsafe_function(qJs::env(), quitJs, nullptr, name_quitJs, 0, 1, nullptr, ++ nullptr, nullptr, callQuitJs, &g_threadsafe_quit_js_function); ++ if (g_threadsafe_quit_js_function != nullptr) ++ return qJs::createBool(true); ++ return qJs::createBool(false); ++} ++ +static napi_value setResourceManager(napi_env env, napi_callback_info info) +{ + size_t argc = 1; @@ -6067,6 +9429,47 @@ index 0000000000..ca8eefffcc + return nullptr; +} + ++static napi_value setDisplayVersion(napi_env env, napi_callback_info info) ++{ ++ size_t argc = 1; ++ napi_value args[1]; ++ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); ++ if (argc != 1) { ++ qJs::throwError("Wrong number of arguments"); ++ return nullptr; ++ } ++ m_displayVersion = qJs::getString(args[0]); ++ return nullptr; ++} ++ ++static napi_value setTouchPad(napi_env env, napi_callback_info info) ++{ ++ size_t argc = 1; ++ napi_value args[1]; ++ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); ++ if (argc != 1) { ++ qJs::throwError("Wrong number of arguments"); ++ return nullptr; ++ } ++ m_isTouchPad = qJs::getBool(args[0]); ++ return nullptr; ++} ++ ++static napi_value setDefaultShowIsMaximized(napi_env env, napi_callback_info info) ++{ ++ size_t argc = 1; ++ napi_value args[1]; ++ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); ++ if (argc != 1) { ++ qJs::throwError("Wrong number of arguments"); ++ return nullptr; ++ } ++ m_defaultShowIsMaximized = qJs::getBool(args[0]); ++ return nullptr; ++} ++ ++ ++ +static napi_value setDisplayMetrics(napi_env env, napi_callback_info info) +{ + size_t argc = 10; @@ -6189,9 +9592,10 @@ index 0000000000..ca8eefffcc + m_openHarmonyFileEngineHandler = nullptr; + } + LOGI("quit qt application"); ++ m_jsAppExited = true; + OH_ResourceManager_ReleaseNativeResourceManager(m_resourceManager); + qJs::quit(); -+ //qApp->quit(); ++ qApp->quit(); + return nullptr; +} + @@ -6200,18 +9604,6 @@ index 0000000000..ca8eefffcc + return qJs::createInt32(QT_VERSION_MAJOR); +} + -+static napi_value waitForQtAppExit(napi_env env, napi_callback_info info) -+{ -+ // sem_wait(&m_terminateSemaphore); -+ if (sem_trywait(&m_terminateSemaphore) == 0) { -+ return qJs::createInt32(0); -+ } -+ else -+ { -+ return qJs::createInt32(1); -+ } -+} -+ +/* + * function for module exports + */ @@ -6229,12 +9621,15 @@ index 0000000000..ca8eefffcc + DECLARE_NAPI_FUNCTION("quitQtApplication", quitQtApplication), + DECLARE_NAPI_FUNCTION("qtMajorVersion", qtMajorVersion), + DECLARE_NAPI_FUNCTION("setDeviceType", setDeviceType), -+ DECLARE_NAPI_FUNCTION("waitForQtAppExit", waitForQtAppExit), ++ DECLARE_NAPI_FUNCTION("setDisplayVersion", setDisplayVersion), ++ DECLARE_NAPI_FUNCTION("setJsQuitFunction", setJsQuitFunction), ++ DECLARE_NAPI_FUNCTION("setTouchPad", setTouchPad), ++ DECLARE_NAPI_FUNCTION("setDefaultShowIsMaximized", setDefaultShowIsMaximized) + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + QOpenHarmonyPlatformInputContext::init(env, exports); + QOpenHarmonyPlatformFontDatabase::init(env, exports); -+ QOpenHarmonyPlatformMessageDialogHelper::init(env, exports); ++// QOpenHarmonyPlatformMessageDialogHelper::init(env, exports); + QOpenHarmonyPlatformFileDialogHelper::init(env, exports); + +#ifndef QT_NO_CLIPBOARD @@ -6310,14 +9705,41 @@ index 0000000000..ca8eefffcc + if (isPhone()) + return QMargins(); + // Todo read from js -+ return QMargins(7, 92, 6, 6); ++ return QMargins(6, 48, 6, 6); ++} ++ ++#include ++QString QtOpenHarmony::displayVersion() ++{ ++ // HYM-W5821 2.0.0.66(SP59DEVC00E66R1P4log) ++ QRegExp regex("(\\d+\\.\\d+\\.\\d+\\.\\d+)"); ++ if (regex.indexIn(m_displayVersion) != -1) { ++ QString version = regex.cap(1); ++ return version; ++ }; ++ return m_displayVersion; ++} ++ ++bool QtOpenHarmony::isTouchPad() ++{ ++ return m_isTouchPad; ++} ++ ++bool QtOpenHarmony::defaultShowIsMaximized() ++{ ++ return m_defaultShowIsMaximized; ++} ++ ++bool QtOpenHarmony::isJsExited() ++{ ++ return m_jsAppExited; +} diff --git a/src/plugins/platforms/openharmony/qopenharmonymain.h b/src/plugins/platforms/openharmony/qopenharmonymain.h new file mode 100644 -index 0000000000..c6209bb941 +index 0000000000..13dc6d0cd2 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonymain.h -@@ -0,0 +1,26 @@ +@@ -0,0 +1,35 @@ +#ifndef QOPENHARMONYMAIN_H +#define QOPENHARMONYMAIN_H + @@ -6339,6 +9761,15 @@ index 0000000000..c6209bb941 + +void updateDisplayMetrics(int w, int h); +bool isPhone(); ++ ++QString displayVersion(); ++ ++bool isTouchPad(); ++ ++bool defaultShowIsMaximized(); ++ ++bool isJsExited(); ++ +NativeResourceManager *resourceManager(); +QOpenHarmonyPlatformIntegration *openharmonyPlatformIntegration(); +} @@ -6346,14 +9777,17 @@ index 0000000000..c6209bb941 +#endif //QOPENHARMONYMAIN_H diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformbackingstore.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformbackingstore.cpp new file mode 100644 -index 0000000000..a6a9c436fe +index 0000000000..29ee0182a6 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformbackingstore.cpp -@@ -0,0 +1,48 @@ +@@ -0,0 +1,65 @@ ++#include ++#include ++ ++#include "qopenharmonylog.h" +#include "qopenharmonyplatformbackingstore.h" +#include "qopenharmonyplatformscreen.h" -+#include "qopenharmonyplatformwindow.h" -+#include ++#include "qopenharmonyplatformopenglwindow.h" + +QT_BEGIN_NAMESPACE + @@ -6376,15 +9810,29 @@ index 0000000000..a6a9c436fe + if (!m_backingStoreSet) + setBackingStore(window); + -+ (static_cast(window->handle()))->repaint(region); ++ (static_cast(window->handle()))->paint(m_image, region, offset); +} + +void QOpenHarmonyPlatformBackingStore::resize(const QSize &size, const QRegion &staticContents) +{ -+ Q_UNUSED(staticContents); ++ if (m_image.isNull() || m_image.size() != size) { ++ QImage::Format format = window()->screen()->handle()->format(); + -+ if (m_image.size() != size) -+ m_image = QImage(size, window()->screen()->handle()->format()); ++ QImage oldImage = m_image; ++ QImage newImage(size.width(), size.height(), format); ++ ++ if (!oldImage.isNull() && !staticContents.isEmpty()) { ++ QRegion staticRegion(staticContents); ++ staticRegion &= QRect(0, 0, oldImage.width(), oldImage.height()); ++ staticRegion &= QRect(0, 0, newImage.width(), newImage.height()); ++ QPainter painter(&newImage); ++ painter.setCompositionMode(QPainter::CompositionMode_Source); ++ for (const QRect &rect : staticRegion) ++ painter.drawImage(rect, oldImage, rect); ++ } ++ ++ m_image = newImage; ++ } +} + +void QOpenHarmonyPlatformBackingStore::setBackingStore(QWindow *window) @@ -6393,7 +9841,7 @@ index 0000000000..a6a9c436fe + (static_cast(window->handle()))->setBackingStore(this); + m_backingStoreSet = true; + } else { -+ qWarning("QOpenHarmonyPlatformBackingStore does not support OpenGL-only windows."); ++ LOGW("QOpenHarmonyPlatformBackingStore does not support OpenGL-only windows."); + } +} + @@ -6431,32 +9879,63 @@ index 0000000000..db75831b39 +#endif // QOPENHARMONYPLATFORMBACKINGSTORE_H diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformclipboard.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformclipboard.cpp new file mode 100644 -index 0000000000..d5504bed0d +index 0000000000..c045275ed0 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformclipboard.cpp -@@ -0,0 +1,86 @@ +@@ -0,0 +1,218 @@ +#include "qopenharmonyplatformclipboard.h" +#include "qopenharmonymain.h" +#include "qopenharmonyjsobjectloader.h" + ++#include +#include +#include +#include + +#include +#include ++#include ++#include ++#include ++#include + +#ifndef QT_NO_CLIPBOARD + +#define qClipBoard QOpenHarmonyPlatformClipboard::instance() + ++ ++struct JsImage { ++ int w; ++ int h; ++ QByteArray buffer; ++}; ++ ++static QVariant jsImage2QVariant(napi_value value) ++{ ++ int64_t w = qJs::getInt64(qJs::objectPropertyValue(value, "w")); ++ int64_t h = qJs::getInt64(qJs::objectPropertyValue(value, "h")); ++ QByteArray buffer = qJs::getByteArray(qJs::objectPropertyValue(value, "buffer")); ++ JsImage result; ++ result.w = w; ++ result.h = h; ++ result.buffer = buffer; ++ return QVariant::fromValue(result); ++} ++ ++Q_DECLARE_METATYPE(JsImage) ++ +QOpenHarmonyPlatformClipboard *QOpenHarmonyPlatformClipboard::m_self = nullptr; + +static napi_value pasteChanged(napi_env env, napi_callback_info info) +{ + Q_UNUSED(env) + Q_UNUSED(info) -+ qClipBoard->emitChanged(QClipboard::Clipboard); ++/* FIXME 会引起崩溃 */ ++#if 0 ++ if (qClipBoard) ++ qClipBoard->emitChanged(QClipboard::Clipboard); ++#endif ++ + return nullptr; +} + @@ -6475,58 +9954,159 @@ index 0000000000..d5504bed0d +{ + m_self = this; + m_jsClipboard = qJsObjectLoader->create("JsPasteBoard"); ++ qRegisterMetaType(); ++ ++ std::function js2qt = jsImage2QVariant; ++ qJs::registerQVariantCreator(js2qt); +} + +QOpenHarmonyPlatformClipboard::~QOpenHarmonyPlatformClipboard() +{ -+ m_self = nullptr; ++ m_self = nullptr; ++} ++ ++QOpenHarmonyPlatformClipboard *QOpenHarmonyPlatformClipboard::instance() ++{ ++ return m_self; ++} ++ ++QMimeData *QOpenHarmonyPlatformClipboard::mimeData(QClipboard::Mode mode) ++{ ++ Q_UNUSED(mode); ++ Q_ASSERT(supportsMode(mode)); ++ m_mimeData.clear(); ++ ++ if (hasClipboardUri()) { ++ QStringList uris = clipboardUri(); ++ QList urls; ++ for (int i = 0; i < uris.count(); ++i) { ++ urls << QUrl(uris.at(i)); ++ } ++ m_mimeData.setUrls(urls); ++ } ++ ++ if (hasClipboardText()) { ++ m_mimeData.setText(clipboardText()); ++ } ++ if (hasClipboardHtml()) { ++ m_mimeData.setHtml(clipboardHtml()); ++ } ++ if (hasClipboardPixelMap()) { ++ JsImage jsImage = clipboardPixelMap(); ++ if (jsImage.w != 0 && jsImage.h != 0 && !jsImage.buffer.isEmpty()) { ++ QImage image(reinterpret_cast(jsImage.buffer.data()), jsImage.w, jsImage.h, QImage::Format_RGBA8888); ++ // openharmony is GBRA ++ image = image.rgbSwapped(); ++ //image.save("/storage/Users/currentUser/Images/wh_copy_test.png"); ++ m_mimeData.setImageData(image); ++ } ++ } ++ return &m_mimeData; ++} ++ ++void QOpenHarmonyPlatformClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) ++{ ++ if (!supportsMode(mode)) { ++ if (data != 0) ++ data->deleteLater(); ++ return; ++ } ++ ++ if (data == nullptr) { ++ m_jsClipboard->call("clearData"); ++ return; ++ } ++ ++ if (supportsMode(mode)) { ++ m_jsClipboard->call("clearData"); ++ if (data->hasUrls()) { ++ QList urls = data->urls(); ++ for (const auto &u : qAsConst(urls)) { ++ m_jsClipboard->call("setClipboardUri", u.toString()); ++ } ++ } ++ if (data->hasText()) { ++ m_jsClipboard->call("setClipboardText", data->text()); ++ } ++ if (data->hasHtml()) { ++ m_jsClipboard->call("setClipboardHtml", data->html()); ++ } ++ if (data->hasImage()) { ++ QImage image = qvariant_cast(data->imageData()); ++ QImage::Format fm = image.format(); ++ if (fm >= QImage::Format_RGB32 && fm != QImage::Format_RGBA8888) ++ image = image.convertToFormat(QImage::Format_RGBA8888); ++ ++ int width = image.width(); ++ int height = image.height(); ++ ++ int bytesPerPixel = image.depth() / 8; ++ int bytesPerLine = image.bytesPerLine(); ++ ++ QByteArray byteArray; ++ byteArray.resize(width * height * bytesPerPixel); ++ ++ for (int y = 0; y < height; ++y) { ++ const uchar* line = image.constScanLine(y); ++ memcpy(byteArray.data() + y * bytesPerLine, line, bytesPerLine); ++ } ++ m_jsClipboard->call("setClipboardPixelMap", byteArray, image.width(), image.height()); ++ } ++ } ++} ++ ++bool QOpenHarmonyPlatformClipboard::supportsMode(QClipboard::Mode mode) const ++{ ++ return QClipboard::Clipboard == mode; ++} ++ ++bool QOpenHarmonyPlatformClipboard::hasClipboardText() ++{ ++ return m_jsClipboard->call("hasClipboardText"); ++} ++ ++QString QOpenHarmonyPlatformClipboard::clipboardText() ++{ ++ return m_jsClipboard->call("clipboardText"); +} + -+QOpenHarmonyPlatformClipboard *QOpenHarmonyPlatformClipboard::instance() ++bool QOpenHarmonyPlatformClipboard::hasClipboardPixelMap() +{ -+ return m_self; ++ return m_jsClipboard->call("hasClipboardPixelMap"); +} + -+QMimeData *QOpenHarmonyPlatformClipboard::mimeData(QClipboard::Mode mode) ++JsImage QOpenHarmonyPlatformClipboard::clipboardPixelMap() +{ -+ Q_UNUSED(mode); -+ Q_ASSERT(supportsMode(mode)); -+ m_mimeData.setText(hasClipboardText() ? clipboardText() : QString()); -+ return &m_mimeData; ++ return m_jsClipboard->call("clipboardPixelMap"); +} + -+void QOpenHarmonyPlatformClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) ++bool QOpenHarmonyPlatformClipboard::hasClipboardUri() +{ -+ if (supportsMode(mode)) { -+ QString text = (data != 0 && data->hasText() ? data->text() : QString()); -+ m_jsClipboard->callWithoutReturn("setClipboardText", text); -+ } -+ if (data != 0) -+ data->deleteLater(); ++ return m_jsClipboard->call("hasClipboardUri"); +} + -+bool QOpenHarmonyPlatformClipboard::supportsMode(QClipboard::Mode mode) const ++QStringList QOpenHarmonyPlatformClipboard::clipboardUri() +{ -+ return QClipboard::Clipboard == mode; ++ return m_jsClipboard->call("clipboardUri"); +} + -+bool QOpenHarmonyPlatformClipboard::hasClipboardText() ++bool QOpenHarmonyPlatformClipboard::hasClipboardHtml() +{ -+ return m_jsClipboard->call("hasClipboardText"); ++ return m_jsClipboard->call("hasClipboardHtml"); +} + -+QString QOpenHarmonyPlatformClipboard::clipboardText() ++QString QOpenHarmonyPlatformClipboard::clipboardHtml() +{ -+ return m_jsClipboard->call("clipboardText"); ++ return m_jsClipboard->call("clipboardHtml"); +} + +#endif // QT_NO_CLIPBOARD diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformclipboard.h b/src/plugins/platforms/openharmony/qopenharmonyplatformclipboard.h new file mode 100644 -index 0000000000..40b3576be4 +index 0000000000..07cfb40c98 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformclipboard.h -@@ -0,0 +1,36 @@ +@@ -0,0 +1,46 @@ +#ifndef QOPENHARMONYPLATFORMCLIPBOARD_H +#define QOPENHARMONYPLATFORMCLIPBOARD_H + @@ -6537,6 +10117,7 @@ index 0000000000..40b3576be4 + +#ifndef QT_NO_CLIPBOARD +class QOpenHarmonyJsObject; ++struct JsImage; + +class QOpenHarmonyPlatformClipboard: public QPlatformClipboard +{ @@ -6550,9 +10131,18 @@ index 0000000000..40b3576be4 + bool supportsMode(QClipboard::Mode mode) const override; + + bool hasClipboardText(); -+ + QString clipboardText(); + ++ bool hasClipboardPixelMap(); ++ JsImage clipboardPixelMap(); ++ ++ bool hasClipboardUri(); ++ ++ QStringList clipboardUri(); ++ ++ bool hasClipboardHtml(); ++ ++ QString clipboardHtml(); + static napi_value init(napi_env env, napi_value exports); +private: + QMimeData m_mimeData; @@ -6563,24 +10153,270 @@ index 0000000000..40b3576be4 +#endif // QT_NO_CLIPBOARD + +#endif // QOPENHARMONYPLATFORMCLIPBOARD_H +diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformcursor.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformcursor.cpp +new file mode 100644 +index 0000000000..ad6dd380b5 +--- /dev/null ++++ b/src/plugins/platforms/openharmony/qopenharmonyplatformcursor.cpp +@@ -0,0 +1,151 @@ ++#include "qopenharmonyplatformcursor.h" ++#include "qopenharmonyplatformwindow.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++QT_BEGIN_NAMESPACE ++ ++ ++QOpenHarmonyPlatformCursor::QOpenHarmonyPlatformCursor(const QPlatformScreen *screen) ++ : m_screen(screen) ++{ ++ m_jsCursor = qJsObjectLoader->create("JsCursor"); ++} ++ ++void QOpenHarmonyPlatformCursor::changeCursor(QCursor *windowCursor, QWindow *window) ++{ ++ if (nullptr == windowCursor) ++ return; ++ ++ if (m_jsCursor.isNull()) ++ { ++ m_jsCursor = qJsObjectLoader->create("JsCursor"); ++ } ++ ++ Qt::CursorShape cursorShape = windowCursor->shape(); ++ LOGI("changeCursor:cursorShape-->%{public}d", cursorShape); ++ int pointerStyle = DEFAULT; ++ switch(cursorShape) ++ { ++ case Qt::ArrowCursor: ++ pointerStyle = DEFAULT; ++ break; ++ case Qt::UpArrowCursor: ++ pointerStyle = NORTH; ++ break; ++ case Qt::CrossCursor: ++ pointerStyle = CROSS; ++ break; ++ case Qt::WaitCursor: ++ pointerStyle = LOADING; ++ break; ++ case Qt::IBeamCursor: ++ pointerStyle = TEXT_CURSOR; ++ break; ++ case Qt::SizeVerCursor: ++ pointerStyle = NORTH_SOUTH; ++ break; ++ case Qt::SizeHorCursor: ++ pointerStyle = WEST_EAST; ++ break; ++ case Qt::SizeBDiagCursor: ++ pointerStyle = NORTH_EAST_SOUTH_WEST; ++ break; ++ case Qt::SizeFDiagCursor: ++ pointerStyle = NORTH_WEST_SOUTH_EAST; ++ break; ++ case Qt::SizeAllCursor: ++ pointerStyle = MOVE; ++ break; ++ //todo:空白光标(隐藏) ++ case Qt::BlankCursor: ++ pointerStyle = DEFAULT; ++ break; ++ case Qt::SplitVCursor: ++ pointerStyle = RESIZE_UP_DOWN; ++ break; ++ case Qt::SplitHCursor: ++ pointerStyle = RESIZE_LEFT_RIGHT; ++ break; ++ case Qt::PointingHandCursor: ++ pointerStyle = HAND_POINTING; ++ break; ++ case Qt::ForbiddenCursor: ++ pointerStyle = CURSOR_FORBID; ++ break; ++ case Qt::WhatsThisCursor: ++ pointerStyle = HELP; ++ break; ++ //todo:鸿蒙没有类型的样式,暂时用”加载中“样式 ++ case Qt::BusyCursor: ++ pointerStyle = LOADING; ++ break; ++ case Qt::OpenHandCursor: ++ pointerStyle = HAND_OPEN; ++ break; ++ case Qt::ClosedHandCursor: ++ pointerStyle = HAND_GRABBING; ++ break; ++ //todo:鸿蒙没有类型的样式,暂时用”默认“样式 ++ case Qt::DragCopyCursor: ++ pointerStyle = DEFAULT; ++ break; ++ //todo:鸿蒙没有类型的样式,暂时用”默认“样式 ++ case Qt::DragMoveCursor: ++ pointerStyle = DEFAULT; ++ break; ++ //todo:鸿蒙没有类型的样式,暂时用”默认“样式 ++ case Qt::DragLinkCursor: ++ pointerStyle = DEFAULT; ++ break; ++ //自定义样式 ++ case Qt::BitmapCursor: ++ case Qt::CustomCursor: ++ pointerStyle = CUSTOM; ++ break; ++ default: ++ pointerStyle = DEFAULT; ++ break; ++ } ++ ++ QOpenHarmonyPlatformWindow *win = QOpenHarmonyPlatformWindow::get(window); ++ if(!win) return; ++ ++ if (pointerStyle == CUSTOM) { ++ QImage image = windowCursor->pixmap().toImage(); ++ QByteArray byteArray; ++ QBuffer buffer(&byteArray); ++ buffer.open(QIODevice::WriteOnly); ++ image.save(&buffer, "PNG"); ++ buffer.close(); ++ m_jsCursor->callWithoutReturn("setCustomCursorStyle", byteArray, image.width(), image.height(), windowCursor->hotSpot().x(), windowCursor->hotSpot().y()); ++ } else { ++ m_jsCursor->callWithoutReturn("setPointerStyle", (int)pointerStyle, win->windowName()); ++ } ++} ++ ++void QOpenHarmonyPlatformCursor::setOverrideCursor(const QCursor &cursor) ++{ ++ LOGI("setOverrideCursor:cursorShape-->%{public}d", cursor.shape()); ++ QImage image = cursor.pixmap().toImage(); ++ QByteArray byteArray; ++ QBuffer buffer(&byteArray); ++ buffer.open(QIODevice::WriteOnly); ++ image.save(&buffer, "PNG"); ++ buffer.close(); ++ m_jsCursor->callWithoutReturn("setCustomCursorStyle", byteArray, image.width(), image.height(), cursor.hotSpot().x(), cursor.hotSpot().y()); ++} ++ ++ ++void QOpenHarmonyPlatformCursor::clearOverrideCursor() ++{ ++ ++} ++ ++ ++QT_END_NAMESPACE +diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformcursor.h b/src/plugins/platforms/openharmony/qopenharmonyplatformcursor.h +new file mode 100644 +index 0000000000..48f5f0144e +--- /dev/null ++++ b/src/plugins/platforms/openharmony/qopenharmonyplatformcursor.h +@@ -0,0 +1,78 @@ ++ ++#ifndef QOPENHARMONYPLATFORMCURSOR_H ++#define QOPENHARMONYPLATFORMCURSOR_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++QT_BEGIN_NAMESPACE ++ ++ ++enum HarmonyPointerStyle{ ++ DEFAULT, ++ EAST, ++ WEST, ++ SOUTH, ++ NORTH, ++ WEST_EAST, ++ NORTH_SOUTH, ++ NORTH_EAST, ++ NORTH_WEST, ++ SOUTH_EAST, ++ SOUTH_WEST, ++ NORTH_EAST_SOUTH_WEST, ++ NORTH_WEST_SOUTH_EAST, ++ CROSS, ++ CURSOR_COPY, ++ CURSOR_FORBID, ++ COLOR_SUCKER, ++ HAND_GRABBING, ++ HAND_OPEN, ++ HAND_POINTING, ++ HELP, ++ MOVE, ++ RESIZE_LEFT_RIGHT, ++ RESIZE_UP_DOWN, ++ SCREENSHOT_CHOOSE, ++ SCREENSHOT_CURSOR, ++ TEXT_CURSOR, ++ ZOOM_IN, ++ ZOOM_OUT, ++ MIDDLE_BTN_EAST, ++ MIDDLE_BTN_WEST, ++ MIDDLE_BTN_SOUTH, ++ MIDDLE_BTN_NORTH, ++ MIDDLE_BTN_NORTH_SOUTH, ++ MIDDLE_BTN_NORTH_EAST, ++ MIDDLE_BTN_NORTH_WEST, ++ MIDDLE_BTN_SOUTH_EAST, ++ MIDDLE_BTN_SOUTH_WEST, ++ MIDDLE_BTN_NORTH_SOUTH_WEST_EAST, ++ HORIZONTAL_TEXT_CURSOR, ++ CURSOR_CROSS, ++ CURSOR_CIRCLE, ++ LOADING, ++ RUNNING, ++ CUSTOM=99 ++}; ++ ++class QOpenHarmonyPlatformCursor : public QPlatformCursor ++{ ++public: ++ explicit QOpenHarmonyPlatformCursor(const QPlatformScreen *screen); ++ ++ void changeCursor(QCursor * widgetCursor, QWindow * widget) override; ++ void setOverrideCursor(const QCursor &cursor) override; ++ void clearOverrideCursor() override; ++ ++private: ++ const QPlatformScreen *const m_screen; ++ QSharedPointer m_jsCursor; ++}; ++ ++QT_END_NAMESPACE ++ ++#endif // QOPENHARMONYPLATFORMCURSOR_H diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformdialoghelpers.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformdialoghelpers.cpp new file mode 100644 -index 0000000000..dbd3897b53 +index 0000000000..e002376aca --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformdialoghelpers.cpp -@@ -0,0 +1,298 @@ -+#include "qopenharmonyplatformdialoghelpers.h" +@@ -0,0 +1,329 @@ +#include "qopenharmonymain.h" +#include "qopenharmonyjsobjectloader.h" ++#include "qopenharmonyplatformdialoghelpers.h" + +#include +#include +#include ++#ifdef OH_URI ++#include ++#endif + -+#include -+#include -+#include +#include ++#include ++#include ++#include ++#include ++#include + +void QWaiter::wait(int timeout) +{ @@ -6626,6 +10462,8 @@ index 0000000000..dbd3897b53 + , Qt::WindowModality windowModality + , QWindow *parent) +{ ++ if (m_shown) ++ return true; + Q_UNUSED(windowFlags) + Q_UNUSED(windowModality) + Q_UNUSED(parent) @@ -6636,7 +10474,7 @@ index 0000000000..dbd3897b53 + return false; + + m_buttons.clear(); -+ m_stanardButton.clear(); ++ m_buttonIds.clear(); + const int * currentLayout = buttonLayout(Qt::Horizontal); + while (*currentLayout != QPlatformDialogHelper::EOL) { + int role = (*currentLayout & ~QPlatformDialogHelper::Reverse); @@ -6663,6 +10501,8 @@ index 0000000000..dbd3897b53 + if (b.role == role) { + QString label = b.label; + label.remove(QChar('&')); ++ m_buttons << label; ++ m_buttonIds << b.id; + } + } + @@ -6671,7 +10511,7 @@ index 0000000000..dbd3897b53 + if (buttonRole(b) == role && (opt->standardButtons() & i)) { + const QString text = QGuiApplicationPrivate::platformTheme()->standardButtonText(b); + m_buttons << text; -+ m_stanardButton << b; ++ m_buttonIds << i; + } + } +} @@ -6690,8 +10530,8 @@ index 0000000000..dbd3897b53 + return; + } + -+// QPlatformDialogHelper::StandardButton standardButton = static_cast(buttonID); -+ QPlatformDialogHelper::StandardButton standardButton = m_stanardButton.at(buttonID); ++ int realId = m_buttonIds.at(m_buttonId); ++ QPlatformDialogHelper::StandardButton standardButton = static_cast(realId); + QPlatformDialogHelper::ButtonRole role = QPlatformDialogHelper::buttonRole(standardButton); + if (buttonID > QPlatformDialogHelper::LastButton) { + const QMessageDialogOptions::CustomButton *custom = options()->customButton(buttonID); @@ -6766,7 +10606,6 @@ index 0000000000..dbd3897b53 + +QOpenHarmonyPlatformFileDialogHelper::~QOpenHarmonyPlatformFileDialogHelper() +{ -+ +} + +bool QOpenHarmonyPlatformFileDialogHelper::defaultNameFilterDisables() const @@ -6797,7 +10636,26 @@ index 0000000000..dbd3897b53 +{ + QList result; + for (int i = 0; i < m_files.count(); ++i) { -+ result << QUrl(m_files.at(i)); ++ const QUrl &url = QUrl(m_files.at(i)); ++#ifdef OH_URI ++ const QString &path = url.toString(); ++ ++ char *data = nullptr; ++ const char *cpath = path.toLocal8Bit().constData(); ++#if 0 ++ bool valid = OH_FileUri_IsValidUri(cpath, path.length()); ++#endif ++ FileManagement_ErrCode errCode = OH_FileUri_GetPathFromUri(cpath, path.length(), &data); ++ if (ERR_OK == errCode && nullptr != data) { ++ result << QUrl::fromLocalFile(QString::fromLocal8Bit(data)); ++ free(data); ++ } else { ++ // file://docs/storage/Users/currentUser/xxx/ 多了一个docs取消 ++ result << QUrl::fromLocalFile(url.path()); ++ } ++#else ++ result << QUrl::fromLocalFile(url.path()); ++#endif + } + QSharedPointer opt = options(); + if (opt.data()) { @@ -6834,6 +10692,8 @@ index 0000000000..dbd3897b53 + +bool QOpenHarmonyPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) +{ ++ if (m_shown) ++ return true; + Q_UNUSED(windowModality) + if (m_jsDialog.isNull()) + return false; @@ -6842,9 +10702,11 @@ index 0000000000..dbd3897b53 + return false; + bool open = (opt->acceptMode() == QFileDialogOptions::AcceptOpen); + if (open) -+ m_jsDialog->callWithoutReturn("openFileDialog", QVariant(qlonglong(static_cast(this))), m_filter); ++ m_jsDialog->callWithoutReturn("openFileDialog", QVariant(qlonglong(static_cast(this))), ++ opt->nameFilters(), int(opt->fileMode()), m_dir.path()); + else -+ m_jsDialog->callWithoutReturn("saveFileDialog", QVariant(qlonglong(static_cast(this))), m_saveFile); ++ m_jsDialog->callWithoutReturn("saveFileDialog", QVariant(qlonglong(static_cast(this))), ++ m_saveFile, opt->nameFilters()); + m_shown = true; + return true; +} @@ -6869,7 +10731,7 @@ index 0000000000..dbd3897b53 + diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformdialoghelpers.h b/src/plugins/platforms/openharmony/qopenharmonyplatformdialoghelpers.h new file mode 100644 -index 0000000000..39762fdc90 +index 0000000000..5f5fd94f92 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformdialoghelpers.h @@ -0,0 +1,87 @@ @@ -6920,7 +10782,7 @@ index 0000000000..39762fdc90 + int m_buttonId; + bool m_shown; + QStringList m_buttons; -+ QList m_stanardButton; ++ QList m_buttonIds; + QSharedPointer m_jsDialog; +}; + @@ -6962,12 +10824,11 @@ index 0000000000..39762fdc90 +#endif // QOPENHARMONYPLATFORMDIALOGHELPERS_H diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformfontdatabase.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformfontdatabase.cpp new file mode 100644 -index 0000000000..dc5b6c998f +index 0000000000..7c2de5f765 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformfontdatabase.cpp -@@ -0,0 +1,83 @@ +@@ -0,0 +1,82 @@ +#include -+#include +#include +#include + @@ -7004,6 +10865,7 @@ index 0000000000..dc5b6c998f + +void QOpenHarmonyPlatformFontDatabase::populateFontDatabase() +{ ++#if 1 + /* FIXME + * 使用鸿蒙ts端查询到的字体路径 + * 否则,扫描fontDir路径中的字体文件 @@ -7016,12 +10878,12 @@ index 0000000000..dc5b6c998f + } + return; + } -+ ++#endif + QString fontpath = fontDir(); + QDir dir(fontpath); + + if (Q_UNLIKELY(!dir.exists())) { -+ qWarning("QFontDatabase: Cannot find font directory %s - is Qt installed correctly?", ++ LOGW("QFontDatabase: Cannot find font directory %{public}s - is Qt installed correctly?", + qPrintable(fontpath)); + //Todo Qt使用_stat函数获取状态信息时返回-1,权限被拒绝.但是使用entryInfoList能够列出目录下的字体 + } @@ -7029,13 +10891,12 @@ index 0000000000..dc5b6c998f + QStringList nameFilters; + nameFilters << QLatin1String("*.ttf") + << QLatin1String("*.otf") -+ << QLatin1String("*.ttc"); -+ -+ const auto entries = dir.entryInfoList(nameFilters, QDir::Files); -+ for (const QFileInfo &fi : entries) { ++ << QLatin1String("*.ttc"); ++ const auto entries = dir.entryInfoList(nameFilters, QDir::Files); ++ for (const QFileInfo &fi : entries) { + const QByteArray file = QFile::encodeName(fi.absoluteFilePath()); + QFreeTypeFontDatabase::addTTFile(QByteArray(), file); -+ } ++ } +} + +napi_value QOpenHarmonyPlatformFontDatabase::init(napi_env env, napi_value exports) @@ -7076,21 +10937,25 @@ index 0000000000..bfad0a18a1 +#endif // QOPENHARMONYPLATFORMFONTDATABASE_H diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformforeignwindow.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformforeignwindow.cpp new file mode 100644 -index 0000000000..38c5c0ae04 +index 0000000000..4f018d4bae --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformforeignwindow.cpp -@@ -0,0 +1,54 @@ -+#include "qopenharmonyplatformforeignwindow.h" +@@ -0,0 +1,65 @@ ++#include +#include ++#include +#include + ++#include "qopenharmonylog.h" ++#include "qopenharmonyplatformforeignwindow.h" ++ +QT_BEGIN_NAMESPACE + +QOpenHarmonyPlatformForeignWindow::QOpenHarmonyPlatformForeignWindow(QWindow *window, WId nativeHandle) + : QOpenHarmonyPlatformWindow(window), + m_surfaceId(-1) +{ -+ ++ setWId(nativeHandle); +} + +QOpenHarmonyPlatformForeignWindow::~QOpenHarmonyPlatformForeignWindow() @@ -7131,6 +10996,13 @@ index 0000000000..38c5c0ae04 +void QOpenHarmonyPlatformForeignWindow::setParent(const QPlatformWindow *window) +{ + Q_UNUSED(window); ++ LOGW("<--------LLLLL:::wanghao"); ++ if (nullptr == window) ++ return; ++ ++ bool result = QOpenHarmonyWindowAdapter::ReParentNode(window->winId(), this->winId()); ++ if (!result) ++ LOGW("set window:%{public}d, parent:%{public}d, fail", this, window); +} + +QT_END_NAMESPACE @@ -7169,15 +11041,17 @@ index 0000000000..e792d1a019 +#endif // QOPENHARMONYPLATFORMFOREIGNWINDOW_H diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatforminputcontext.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatforminputcontext.cpp new file mode 100644 -index 0000000000..d00836a94c +index 0000000000..436f5914ea --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatforminputcontext.cpp -@@ -0,0 +1,1175 @@ +@@ -0,0 +1,1538 @@ +#include "qopenharmonymain.h" +#include "qopenharmonyjsobjectloader.h" +#include "qopenharmonyplatformscreen.h" +#include "qopenharmonyplatformintegration.h" +#include "qopenharmonyplatforminputcontext.h" ++#include "qopenharmonyjswindowmanager.h" ++#include "qopenharmonyjswindow.h" + +#include +#include @@ -7226,6 +11100,78 @@ index 0000000000..d00836a94c + +} // namespace anonymous + ++struct KeyRecord { ++ KeyRecord(int c, int a, int s, const QString &t) : code(c), ascii(a), state(s), text(t) {} ++ KeyRecord() {} ++ ++ int code; ++ int ascii; ++ int state; ++ QString text; ++}; ++ ++// We need to record the pressed keys in order to decide, whether the key event is an autorepeat ++// event. As soon as its state changes, the chain of autorepeat events will be broken. ++static const int QT_MAX_KEY_RECORDINGS = 64; // User has LOTS of fingers... ++struct KeyRecorder ++{ ++ inline KeyRecord *findKey(int code, bool remove); ++ inline void storeKey(int code, int ascii, int state, const QString& text); ++ inline void clearKeys(); ++ ++ int nrecs = 0; ++ KeyRecord deleted_record; // A copy of last entry removed from records[] ++ KeyRecord records[QT_MAX_KEY_RECORDINGS]; ++}; ++static KeyRecorder key_recorder; ++ ++static void clearKeyRecorderOnApplicationInActive(Qt::ApplicationState state) ++{ ++ if (state == Qt::ApplicationInactive) ++ key_recorder.clearKeys(); ++} ++ ++KeyRecord *KeyRecorder::findKey(int code, bool remove) ++{ ++ KeyRecord *result = nullptr; ++ for (int i = 0; i < nrecs; ++i) { ++ if (records[i].code == code) { ++ if (remove) { ++ deleted_record = records[i]; ++ // Move rest down, and decrease count ++ while (i + 1 < nrecs) { ++ records[i] = records[i + 1]; ++ ++i; ++ } ++ --nrecs; ++ result = &deleted_record; ++ } else { ++ result = &records[i]; ++ } ++ break; ++ } ++ } ++ return result; ++} ++ ++void KeyRecorder::storeKey(int code, int ascii, int state, const QString& text) ++{ ++ Q_ASSERT_X(nrecs != QT_MAX_KEY_RECORDINGS, ++ "Internal KeyRecorder", ++ "Keyboard recorder buffer overflow, consider increasing QT_MAX_KEY_RECORDINGS"); ++ ++ if (nrecs == QT_MAX_KEY_RECORDINGS) { ++ qWarning("Qt: Internal keyboard buffer overflow"); ++ return; ++ } ++ records[nrecs++] = KeyRecord(code,ascii,state,text); ++} ++ ++void KeyRecorder::clearKeys() ++{ ++ nrecs = 0; ++} ++ +/*! + * \brief Qt-鸿蒙平台下的输入类型枚举值映射 + * \param hints Qt下的枚举值 @@ -7282,6 +11228,23 @@ index 0000000000..d00836a94c + +static QOpenHarmonyPlatformInputContext *m_openHarmonyInputContext = 0; + ++static napi_value sendEnterKey(napi_env env, napi_callback_info info) ++{ ++ size_t argc = 1; ++ napi_value args[1]; ++ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); ++ if (argc != 1) { ++ napi_throw_type_error(env, NULL, "Wrong number of arguments"); ++ return nullptr; ++ } ++ ++ int64_t value0 = qJs::getInt64(args[0]); ++ QObject *object = reinterpret_cast(value0); ++ ++ QMetaObject::invokeMethod(object, "enterKeyDown", Qt::QueuedConnection); ++ return Q_NULLPTR; ++} ++ +static napi_value insertText(napi_env env, napi_callback_info info) +{ + size_t argc = 2; @@ -7380,18 +11343,117 @@ index 0000000000..d00836a94c + DECLARE_NAPI_FUNCTION("moveCursor", moveCursor), + DECLARE_NAPI_FUNCTION("deleteLeft", deleteLeft), + DECLARE_NAPI_FUNCTION("deleteRight", deleteRight), ++ DECLARE_NAPI_FUNCTION("sendEnterKey", sendEnterKey) + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + return nullptr; +} + -+void QOpenHarmonyPlatformInputContext::dispatchKeyEvent(OH_NativeXComponent_KeyCode code, -+ OH_NativeXComponent_KeyAction action, -+ OH_NativeXComponent_EventSourceType stype, -+ int64_t deviceId, -+ int64_t timeStamp) ++Qt::KeyboardModifiers QOpenHarmonyPlatformInputContext::queryKeyboardModifiers() const ++{ ++ Qt::KeyboardModifiers mods(Qt::NoModifier); ++ for (Qt::KeyboardModifier m : qAsConst(m_mpd)) ++ mods |= m; ++ ++ return mods; ++} ++ ++void QOpenHarmonyPlatformInputContext::updateCursorPosition() ++{ ++ // qWarning() << "<--------------::::" << Q_FUNC_INFO; ++} ++ ++void QOpenHarmonyPlatformInputContext::touchDown(float x, float y) ++{ ++ ++} ++ ++void QOpenHarmonyPlatformInputContext::handleMouseEvent(const OpenHarmonyMouseEvent &event) ++{ ++ QOpenHarmonyJsWindow *window = qJsWindowManager->window(event.component); ++ if (window == nullptr) ++ return; ++ if (event.type == QEvent::MouseMove) { ++ mouseMove(window, event.x, event.y, event.btn); ++ } else if (event.type == QEvent::MouseButtonPress) { ++ mousePress(window, event.x, event.y, event.btn); ++ } else if (event.type == QEvent::MouseButtonRelease) { ++ mouseRelease(window, event.x, event.y, event.btn); ++ } ++ QWindowSystemInterface::flushWindowSystemEvents(); ++} ++ ++void QOpenHarmonyPlatformInputContext::wheelEvent(const OpenHarmonyWheelEvent &event) ++{ ++ QOpenHarmonyJsWindow *window = qJsWindowManager->window(event.component); ++ if (window == nullptr || m_ignoreMouseEvents) ++ return; ++ QPoint global = event.global; ++ QPoint pixelDelta = event.pixelDelta; ++ Qt::KeyboardModifiers mods = event.mods; ++ ++ QWindow *tlw = m_mouseGrabber.data(); ++ if (!tlw) ++ tlw = window->qtWindow(); ++ QPoint localPos = tlw ? tlw->mapFromGlobal(global) : global; ++ QWindowSystemInterface::handleWheelEvent(tlw, localPos, global, pixelDelta, ++ QPoint(pixelDelta.x()*8, pixelDelta.y()*8), mods); ++} ++ ++void QOpenHarmonyPlatformInputContext::mouseMove(QOpenHarmonyJsWindow *window, float x, float y, Qt::MouseButton button) ++{ ++ if (window == nullptr || m_ignoreMouseEvents) ++ return; ++ ++ QPoint globalPos(x,y); ++ QWindow *tlw = m_mouseGrabber.data(); ++ if (!tlw) ++ tlw = window->qtWindow(); ++ QPoint localPos = tlw ? tlw->mapFromGlobal(globalPos) : globalPos; ++ QWindowSystemInterface::handleMouseEvent(tlw, localPos, globalPos, ++ Qt::MouseButtons(m_mouseGrabber ? Qt::LeftButton : Qt::NoButton), ++ button, QEvent::MouseMove, queryKeyboardModifiers()); ++} ++ ++void QOpenHarmonyPlatformInputContext::mousePress(QOpenHarmonyJsWindow *window, float x, float y, Qt::MouseButton button) ++{ ++ if (window == nullptr || m_ignoreMouseEvents) ++ return; ++ ++ QPoint globalPos(x,y); ++ m_mouseGrabber = window->qtWindow(); ++ QPoint localPos = m_mouseGrabber ? m_mouseGrabber->mapFromGlobal(globalPos) : globalPos; ++ QWindowSystemInterface::handleMouseEvent(m_mouseGrabber.data(), localPos, globalPos, ++ Qt::MouseButtons(button), ++ button, QEvent::MouseButtonPress, queryKeyboardModifiers()); ++} ++ ++void QOpenHarmonyPlatformInputContext::mouseRelease(QOpenHarmonyJsWindow *window, float x, float y, Qt::MouseButton button) ++{ ++ if (window == nullptr || m_ignoreMouseEvents) ++ return; ++ ++ QPoint globalPos(x,y); ++ QWindow *tlw = m_mouseGrabber.data(); ++ if (!tlw) ++ tlw = window->qtWindow(); ++ QPoint localPos = tlw ? tlw->mapFromGlobal(globalPos) : globalPos; ++ QWindowSystemInterface::handleMouseEvent(tlw, localPos, globalPos, ++ Qt::MouseButtons(Qt::NoButton), ++ button, QEvent::MouseButtonRelease, queryKeyboardModifiers()); ++ m_mouseGrabber = 0; ++} ++ ++void QOpenHarmonyPlatformInputContext::handleKeyEvent(const OpenHarmonyKeyEvent &event) +{ -+ Q_UNUSED(deviceId);Q_UNUSED(timeStamp); ++ QOpenHarmonyJsWindow *jsWindow = qJsWindowManager->window(event.component); ++ if (jsWindow == nullptr) ++ return; ++ ++ QWindow *window = jsWindow->qtWindow(); ++ OH_NativeXComponent_KeyCode code = event.code; ++ OH_NativeXComponent_KeyAction action = event.action; ++ OH_NativeXComponent_EventSourceType stype = event.stype; + static QHash sKeyType{ + { OH_NATIVEXCOMPONENT_KEY_ACTION_DOWN, QEvent::KeyPress }, + { OH_NATIVEXCOMPONENT_KEY_ACTION_UP, QEvent::KeyRelease }, @@ -7764,60 +11826,60 @@ index 0000000000..d00836a94c + /* 有修饰符按下时记录按下的修饰符 + * 松开时移除修饰符记录 + */ -+ -+ Qt::KeyboardModifiers modifiers(Qt::NoModifier); + QEvent::Type t = sKeyType.value(action, QEvent::None); + Qt::KeyboardModifier m = sKeyModifers.value(code, Qt::NoModifier); ++ ++ /* 长按判断 ++ * 记录按下的字符,判断字符是否已经按下 ++ * 如果字符已经按下则认为是长按状态 ++ */ ++ bool autoRepeat = false; ++ if (OH_NATIVEXCOMPONENT_KEY_ACTION_DOWN == action) { ++ KeyRecord *rec = key_recorder.findKey(int(code), false); ++ if (rec) { ++ autoRepeat = true; ++ } else { ++ key_recorder.storeKey(int(code), 0, 0, QString()); ++ } ++ } else { ++ key_recorder.findKey(int(code), true); ++ } ++ ++ bool send = true; + if (Qt::NoModifier != m && QEvent::KeyPress == t){ -+ modifiers |= m; -+ m_mpd.insert(m); ++ send = !m_mpd.contains(m); ++ if (send) { ++ m_mpd.insert(m); ++ } + } -+ else if (Qt::NoModifier != m && QEvent::KeyRelease == t) { ++ ++ if (Qt::NoModifier != m && QEvent::KeyRelease == t) { + m_mpd.remove(m); + } + -+ Qt::Key k = sKeyMap.value(code, Qt::Key_unknown); ++ if (!send) ++ return; + -+ int nativeModifier = KEY_UNKNOWN; ++ int nativeModifier = 0; + for (Qt::KeyboardModifier mf : m_mpd) -+ nativeModifier |= int(mf); -+ -+ QKeyEvent *kEvent = new QKeyEvent(t, k, modifiers, code, code, nativeModifier); -+ /* NOTE 因为鸿蒙回调在其他线程,这里事件处理只能通过线程队列发送 */ -+ QGuiApplication::postEvent(m_focusObject, kEvent); -+ -+ /* 快捷点组合判断 */ -+ if (m_mpd.contains(Qt::ControlModifier) && KEY_A == code) -+ QMetaObject::invokeMethod(this, "sendShortcut", Qt::QueuedConnection, -+ Q_ARG(int, QKeySequence::SelectAll)); -+ else if (m_mpd.contains(Qt::ControlModifier) && KEY_C == code) -+ QMetaObject::invokeMethod(this, "sendShortcut", Qt::QueuedConnection, -+ Q_ARG(int, QKeySequence::Copy)); -+ else if (m_mpd.contains(Qt::ControlModifier) && KEY_V == code) -+ QMetaObject::invokeMethod(this, "sendShortcut", Qt::QueuedConnection, -+ Q_ARG(int, QKeySequence::Paste)); -+} -+ -+void QOpenHarmonyPlatformInputContext::sendShortcut(int key) -+{ -+ QKeySequence::StandardKey skey = QKeySequence::StandardKey(key); -+ QKeySequence sequence(skey); -+ for (int i = 0; i < sequence.count(); ++i) { -+ const int keys = sequence[i]; -+ Qt::Key key = Qt::Key(keys & ~Qt::KeyboardModifierMask); -+ Qt::KeyboardModifiers mod = Qt::KeyboardModifiers(keys & Qt::KeyboardModifierMask); ++ nativeModifier |= int(mf); + -+ QKeyEvent pressEvent(QEvent::KeyPress, key, mod); -+ QKeyEvent releaseEvent(QEvent::KeyRelease, key, mod); ++ int state = 0; ++ state |= (nativeModifier & Qt::ShiftModifier ? int(Qt::ShiftModifier) : 0); ++ state |= (nativeModifier & Qt::ControlModifier ? int(Qt::ControlModifier) : 0); ++ state |= (nativeModifier & Qt::AltModifier ? int(Qt::AltModifier) : 0); ++ state |= (nativeModifier & Qt::MetaModifier ? int(Qt::MetaModifier) : 0); + -+ QGuiApplication::sendEvent(m_focusObject, &pressEvent); -+ QGuiApplication::sendEvent(m_focusObject, &releaseEvent); -+ } -+} ++ Qt::Key key = sKeyMap.value(code, Qt::Key_unknown); ++ if (key == Qt::Key_Control) ++ state = state ^ Qt::ControlModifier; ++ else if (key == Qt::Key_Shift) ++ state = state ^ Qt::ShiftModifier; ++ else if (key == Qt::Key_Alt) ++ state = state ^ Qt::AltModifier; + -+void QOpenHarmonyPlatformInputContext::updateCursorPosition() -+{ -+ // qWarning() << "<--------------::::" << Q_FUNC_INFO; ++ const Qt::KeyboardModifiers modifiers(state); ++ QWindowSystemInterface::handleExtendedKeyEvent(window, t, key, modifiers, code, code, nativeModifier, QString(), autoRepeat); +} + +QOpenHarmonyPlatformInputContext::QOpenHarmonyPlatformInputContext() @@ -7826,9 +11888,10 @@ index 0000000000..d00836a94c + , m_composingCursor(-1) + , m_batchEditNestingLevel(0) + , m_focusObject(Q_NULLPTR) -+{ ++{ + m_jsInputCtl = qJsObjectLoader->create("JsInputMethod"); + m_openHarmonyInputContext = this; ++ qRegisterMetaType(); +} + +QOpenHarmonyPlatformInputContext::~QOpenHarmonyPlatformInputContext() @@ -7857,10 +11920,10 @@ index 0000000000..d00836a94c + // qWarning() << "<-----------::" << Q_FUNC_INFO; + focusObjectStopComposing(); + m_batchEditNestingLevel = 0; -+ ++#if 0 + if (m_jsInputCtl->call("isAttached")) + m_jsInputCtl->call("detach"); -+ ++#endif + clearInput(); + emitInputPanelVisibleChanged(); +} @@ -7947,11 +12010,18 @@ index 0000000000..d00836a94c + if (query.isNull()) + return; + ++ bool imEnable = query->queries().testFlag(Qt::ImEnabled); ++ if (!imEnable) ++ return; ++ ++/* FIXME QML 控件中没有cursorPositionChanged */ ++#if 0 + disconnect(m_updateCursorPosConnection); + if (qGuiApp->focusObject()->metaObject()->indexOfSignal("cursorPositionChanged(int,int)") >= 0) // QLineEdit breaks the pattern + m_updateCursorPosConnection = connect(qGuiApp->focusObject(), SIGNAL(cursorPositionChanged(int,int)), this, SLOT(updateCursorPosition())); + else + m_updateCursorPosConnection = connect(qGuiApp->focusObject(), SIGNAL(cursorPositionChanged()), this, SLOT(updateCursorPosition())); ++#endif + + Qt::InputMethodHints hints = query->value(Qt::ImHints).value(); + Qt::EnterKeyType enterType = query->value(Qt::ImEnterKeyType).value(); @@ -8037,73 +12107,90 @@ index 0000000000..d00836a94c + +void QOpenHarmonyPlatformInputContext::touchAdd(int id, int action, float force, float x, float y) +{ -+ if (m_integration == nullptr) ++ OpenHarmonyTouchPoint touchPoint; ++ touchPoint.id = id; ++ touchPoint.action = action; ++ touchPoint.force = force; ++ touchPoint.x = x; ++ touchPoint.y = y; ++ m_touchPoints.push_back(touchPoint); ++} ++ ++void QOpenHarmonyPlatformInputContext::touchEnd(const OpenHarmonyTouchEvent &event) ++{ ++ QOpenHarmonyJsWindow *window = qJsWindowManager->window(event.component); ++ if ((window == nullptr) || (m_integration == nullptr) || m_touchPoints.isEmpty()) + return; + -+ Qt::TouchPointState state = Qt::TouchPointStationary; -+ switch (action) { -+ case 0: -+ state = Qt::TouchPointPressed; -+ break; -+ case 1: -+ state = Qt::TouchPointReleased; -+ break; -+ case 2: -+ state = Qt::TouchPointMoved; -+ break; -+ case 3: -+ // ohos touch cancel -+ // state = Qt::TouchPointStationary; -+ break; -+ default: -+ break; ++ ++ QList qtTouchPoints; ++ ++ QOpenHarmonyJsWindow *parent = window->parent(); ++ QOpenHarmonyJsWindow *dest = (parent == nullptr) ? window : parent; ++ while (parent != nullptr) { ++ dest = parent; ++ parent = parent->parent(); + } + ++ QMargins m = dest->frameMargins(); + QRect rc = m_integration->screen()->availableGeometry(); + + const double dw = static_cast(rc.width()); + const double dh = static_cast(rc.height()); -+ QWindowSystemInterface::TouchPoint touchPoint; -+ touchPoint.id = id; -+ touchPoint.pressure = static_cast(force); -+ // touchPoint.rotation = qRadiansToDegrees(rotation); -+ touchPoint.rotation = 0; -+ touchPoint.normalPosition = QPointF(double(x / dw), double(y / dh)); -+ touchPoint.state = state; -+ -+ float minor = 50; -+ float major = 50; -+ touchPoint.area = QRectF(x - minor, -+ y - major, -+ double(minor * 2), -+ double(major * 2)); -+ m_touchPoints.push_back(touchPoint); ++ for (int i = 0; i < m_touchPoints.count(); ++i) { ++ const OpenHarmonyTouchPoint &point = m_touchPoints[i]; ++ int action = point.action; ++ ++ Qt::TouchPointState state = Qt::TouchPointStationary; ++ switch (action) { ++ case 0: ++ state = Qt::TouchPointPressed; ++ break; ++ case 1: ++ state = Qt::TouchPointMoved; ++ break; ++ case 2: ++ state = Qt::TouchPointStationary; ++ break; ++ case 3: ++ state = Qt::TouchPointReleased; ++ break; ++ } + -+ if (state == Qt::TouchPointPressed) { -+ touchDown(x, y); ++ QWindowSystemInterface::TouchPoint touchPoint; ++ touchPoint.id = point.id; ++ touchPoint.pressure = static_cast(point.force); ++ // touchPoint.rotation = qRadiansToDegrees(rotation); ++ touchPoint.rotation = 0; ++ QPoint globalPoint = dest->mapToGlobal(QPoint(point.x - m.left(), point.y - m.top())); ++ int x = globalPoint.x(); ++ int y = globalPoint.y(); ++ touchPoint.normalPosition = QPointF(double(x / dw), double(y / dh)); ++ touchPoint.state = state; ++ ++ float minor = 50; ++ float major = 50; ++ touchPoint.area = QRectF(x - minor, ++ y - major, ++ double(minor * 2), ++ double(major * 2)); ++ qtTouchPoints << touchPoint; + } -+} -+ -+void QOpenHarmonyPlatformInputContext::touchEnd(int id) -+{ -+ Q_UNUSED(id) -+ if ((m_integration == nullptr) || m_touchPoints.isEmpty()) -+ return; + + QTouchDevice *touchDevice = m_integration->touchDevice(); + if (touchDevice == nullptr) { + touchDevice = new QTouchDevice; -+ touchDevice->setType(QTouchDevice::TouchScreen); -+ touchDevice->setCapabilities(QTouchDevice::Position -+ | QTouchDevice::Area -+ | QTouchDevice::Pressure -+ | QTouchDevice::NormalizedPosition); ++ touchDevice->setType(QtOpenHarmony::isTouchPad() ? QTouchDevice::TouchPad : QTouchDevice::TouchScreen); ++ QTouchDevice::Capabilities capabilities = QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition | QTouchDevice::Pressure; ++ if (touchDevice->type() == QTouchDevice::TouchPad) ++ capabilities |= QTouchDevice::MouseEmulation; ++ touchDevice->setCapabilities(capabilities); + QWindowSystemInterface::registerTouchDevice(touchDevice); + m_integration->setTouchDevice(touchDevice); + } + -+ QWindow *window = m_integration->screen()->topLevelAt(m_touchPoints.at(0).area.center().toPoint()); -+ QWindowSystemInterface::handleTouchEvent(window, touchDevice, m_touchPoints); ++ QWindowSystemInterface::handleTouchEvent(window->qtWindow(), touchDevice, qtTouchPoints, queryKeyboardModifiers()); ++ QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); +} + +void QOpenHarmonyPlatformInputContext::setComposingText(const QString &text) @@ -8111,37 +12198,87 @@ index 0000000000..d00836a94c + m_composingText = text; +} + -+void QOpenHarmonyPlatformInputContext::mousePress(float x, float y) ++QString QOpenHarmonyPlatformInputContext::getTextAfterCursor(int length, int flags) +{ -+ if ((m_integration == nullptr) || m_ignoreMouseEvents) -+ return; ++ Q_UNUSED(flags); ++ if (length <= 0) ++ return QString(); + -+ QPoint globalPos(x,y); -+ QWindow *tlw = m_integration->screen()->topLevelAt(globalPos); -+ // m_mouseGrabber = tlw; -+ QPoint localPos = tlw ? (globalPos - tlw->position()) : globalPos; -+ QWindowSystemInterface::handleMouseEvent(tlw, localPos, globalPos, -+ Qt::MouseButtons(Qt::LeftButton), -+ Qt::LeftButton, QEvent::MouseButtonPress); -+} ++ QString text; + -+void QOpenHarmonyPlatformInputContext::mouseRelease(float x, float y) -+{ -+ if ((m_integration == nullptr) || m_ignoreMouseEvents) -+ return; ++ QVariant reportedTextAfter = QInputMethod::queryFocusObject(Qt::ImTextAfterCursor, length); ++ if (reportedTextAfter.isValid()) { ++ text = reportedTextAfter.toString(); ++ } else { ++ // Compatibility code for old controls that do not implement the new API ++ QSharedPointer query = ++ focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImSurroundingText); ++ if (query) { ++ const int cursorPos = query->value(Qt::ImCursorPosition).toInt(); ++ text = query->value(Qt::ImSurroundingText).toString().mid(cursorPos); ++ } ++ } + -+ QPoint globalPos(x,y); -+ QWindow *tlw = m_integration->screen()->topLevelAt(globalPos); -+ // m_mouseGrabber = tlw; -+ QPoint localPos = tlw ? (globalPos - tlw->position()) : globalPos; -+ QWindowSystemInterface::handleMouseEvent(tlw, localPos, globalPos, -+ Qt::MouseButtons(Qt::LeftButton), -+ Qt::LeftButton, QEvent::MouseButtonPress); ++ if (focusObjectIsComposing()) { ++ // Controls do not report preedit text, so we have to add it ++ const int cursorPosInsidePreedit = m_composingCursor - m_composingTextStart; ++ text = m_composingText.midRef(cursorPosInsidePreedit) + text; ++ } else { ++ // We must not return selected text if there is any ++ QSharedPointer query = ++ focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAnchorPosition); ++ if (query) { ++ const int cursorPos = query->value(Qt::ImCursorPosition).toInt(); ++ const int anchorPos = query->value(Qt::ImAnchorPosition).toInt(); ++ if (anchorPos > cursorPos) ++ text.remove(0, anchorPos - cursorPos); ++ } ++ } ++ ++ text.truncate(length); ++ return text; +} + -+void QOpenHarmonyPlatformInputContext::touchDown(float x, float y) ++QString QOpenHarmonyPlatformInputContext::getTextBeforeCursor(int length, int flags) +{ ++ Q_UNUSED(flags); ++ if (length <= 0) ++ return QString(); ++ ++ QString text; ++ ++ QVariant reportedTextBefore = QInputMethod::queryFocusObject(Qt::ImTextBeforeCursor, length); ++ if (reportedTextBefore.isValid()) { ++ text = reportedTextBefore.toString(); ++ } else { ++ // Compatibility code for old controls that do not implement the new API ++ QSharedPointer query = ++ focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImSurroundingText); ++ if (query) { ++ const int cursorPos = query->value(Qt::ImCursorPosition).toInt(); ++ text = query->value(Qt::ImSurroundingText).toString().left(cursorPos); ++ } ++ } ++ ++ if (focusObjectIsComposing()) { ++ // Controls do not report preedit text, so we have to add it ++ const int cursorPosInsidePreedit = m_composingCursor - m_composingTextStart; ++ text += m_composingText.leftRef(cursorPosInsidePreedit); ++ } else { ++ // We must not return selected text if there is any ++ QSharedPointer query = ++ focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAnchorPosition); ++ if (query) { ++ const int cursorPos = query->value(Qt::ImCursorPosition).toInt(); ++ const int anchorPos = query->value(Qt::ImAnchorPosition).toInt(); ++ if (anchorPos < cursorPos) ++ text.chop(cursorPos - anchorPos); ++ } ++ } + ++ if (text.length() > length) ++ text = text.right(length); ++ return text; +} + +void QOpenHarmonyPlatformInputContext::clearInput() @@ -8149,7 +12286,18 @@ index 0000000000..d00836a94c + m_composingText.clear(); + m_composingTextStart = -1; + m_composingCursor = -1; -+ // m_extractedText.clear(); ++} ++/*! ++ * \brief FIXME 临时方法发送EnterKey按键事件 ++ */ ++void QOpenHarmonyPlatformInputContext::enterKeyDown() ++{ ++ LOGW("<--------------------- enterKeyDown ----------------->"); ++ QKeyEvent k1(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier, KEY_ENTER, KEY_ENTER, KEY_ENTER); ++ QGuiApplication::sendEvent(m_focusObject, &k1); ++ ++ QKeyEvent k2(QEvent::KeyRelease, Qt::Key_Enter, Qt::NoModifier, KEY_ENTER, KEY_ENTER, KEY_ENTER); ++ QGuiApplication::sendEvent(m_focusObject, &k2); +} + +void QOpenHarmonyPlatformInputContext::commitText(QString text) @@ -8158,8 +12306,41 @@ index 0000000000..d00836a94c + if (query.isNull()) + return; + ++ BatchEditLock batchEditLock(this); ++ ++ const int absoluteCursorPos = getAbsoluteCursorPosition(query); ++ int absoluteAnchorPos = getBlockPosition(query) + query->value(Qt::ImAnchorPosition).toInt(); ++ ++ auto setCursorPosition = [=]() { ++ const int cursorPos = query->value(Qt::ImCursorPosition).toInt(); ++ QInputMethodEvent event({}, { { QInputMethodEvent::Selection, cursorPos, 0 } }); ++ QGuiApplication::sendEvent(m_focusObject, &event); ++ }; ++ ++ // If we have composing region and selection (and therefore focusObjectIsComposing() == false), ++ // we must clear selection so that we won't delete it when we will be replacing composing text ++ if (!m_composingText.isEmpty() && absoluteCursorPos != absoluteAnchorPos) { ++ setCursorPosition(); ++ absoluteAnchorPos = absoluteCursorPos; ++ } ++ ++ // The value of Qt::ImCursorPosition is not updated at the start ++ // when the first character is added, so we must update it (QTBUG-85090) ++ if (absoluteCursorPos == 0 && text.length() == 1 && getTextAfterCursor(1,1).length() >= 0) { ++ setCursorPosition(); ++ } ++ ++ // If we had no composing region, pretend that we had a zero-length composing region at current ++ // cursor position to simplify code. Also account for that we must delete selected text if there ++ // (still) is any. ++ const int effectiveAbsoluteCursorPos = qMin(absoluteCursorPos, absoluteAnchorPos); ++ if (m_composingTextStart == -1) ++ m_composingTextStart = effectiveAbsoluteCursorPos; ++ ++ const int oldComposingTextLen = m_composingText.length(); + m_composingText = text; + ++ + QInputMethodEvent event; + event.setCommitString(text); + QGuiApplication::sendEvent(m_focusObject, &event); @@ -8203,6 +12384,7 @@ index 0000000000..d00836a94c + +void QOpenHarmonyPlatformInputContext::deleteSurroundingText(int leftLength, int rightLength) +{ ++#if 0 + QSharedPointer query = focusObjectInputMethodQuery(); + if (query.isNull()) + return; @@ -8224,11 +12406,13 @@ index 0000000000..d00836a94c + + if (leftLength > 0) { + beginPos = qMax(absolutecursorPos - leftLength, 0); ++ qWarning() << absolutecursorPos << selectLength << beginPos; ++ + event = QInputMethodEvent ({}, { { QInputMethodEvent::Selection, beginPos, leftLength} }); + QGuiApplication::sendEvent(m_focusObject, &event); + + event.setCommitString({}, beginPos, leftLength); -+ QGuiApplication::sendEvent(m_focusObject, &event); ++ QGuiApplication::sendEvent(m_focusObject, &event); + } + + if (rightLength > 0) { @@ -8242,6 +12426,57 @@ index 0000000000..d00836a94c + + event = QInputMethodEvent({}, { { QInputMethodEvent::Selection, beginPos, 0 } }); + QGuiApplication::sendEvent(m_focusObject, &event); ++#else ++ QSharedPointer query = focusObjectInputMethodQuery(); ++ if (query.isNull()) ++ return; ++ ++ ++ const int absolutecursorPos = getAbsoluteCursorPosition(query); ++ const QString &selectText = query->value(Qt::ImCurrentSelection).toString(); ++ const int selectLength = selectText.length(); ++ int beginPos = absolutecursorPos; ++ ++ QInputMethodEvent event; ++ if (!selectText.isEmpty()) { ++ beginPos = qMax(absolutecursorPos - selectLength, 0); ++ event = QInputMethodEvent({}, { { QInputMethodEvent::Selection, beginPos, selectLength } }); ++ QGuiApplication::sendEvent(m_focusObject, &event); ++ ++ event.setCommitString({}, beginPos, selectLength); ++ QGuiApplication::sendEvent(m_focusObject, &event); ++ } ++ ++ if (leftLength > 0) { ++ QString textBeforeCursor = query->value(Qt::ImTextBeforeCursor).toString(); ++ int deleteStartPos = textBeforeCursor.length() - leftLength; ++ deleteStartPos = qMax(deleteStartPos, 0); ++ ++ QString textToDelete = textBeforeCursor.right(leftLength); ++ event = QInputMethodEvent({}, { { QInputMethodEvent::Selection, deleteStartPos, textToDelete.length() } }); ++ QGuiApplication::sendEvent(m_focusObject, &event); ++ ++ event.setCommitString({}, deleteStartPos, textToDelete.length()); ++ QGuiApplication::sendEvent(m_focusObject, &event); ++ ++ event = QInputMethodEvent({}, { { QInputMethodEvent::Selection, deleteStartPos, 0 } }); ++ QGuiApplication::sendEvent(m_focusObject, &event); ++ } ++ ++ if (rightLength > 0) { ++ QString textAfterCursor = query->value(Qt::ImTextAfterCursor).toString(); ++ QString textToDelete = textAfterCursor.left(rightLength); ++ ++ event = QInputMethodEvent({}, { { QInputMethodEvent::Selection, absolutecursorPos, textToDelete.length() } }); ++ QGuiApplication::sendEvent(m_focusObject, &event); ++ ++ event.setCommitString({}, absolutecursorPos, textToDelete.length()); ++ QGuiApplication::sendEvent(m_focusObject, &event); ++ ++ event = QInputMethodEvent({}, { { QInputMethodEvent::Selection, absolutecursorPos, 0 } }); ++ QGuiApplication::sendEvent(m_focusObject, &event); ++ } ++#endif +} + +void QOpenHarmonyPlatformInputContext::sendInputMethodEvent(QInputMethodEvent *event) @@ -8350,13 +12585,14 @@ index 0000000000..d00836a94c + diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatforminputcontext.h b/src/plugins/platforms/openharmony/qopenharmonyplatforminputcontext.h new file mode 100644 -index 0000000000..62fb0fadfc +index 0000000000..1075711f40 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatforminputcontext.h -@@ -0,0 +1,95 @@ +@@ -0,0 +1,148 @@ +#ifndef QOPENHARMONYPLATFORMINPUTCONTEXT_H +#define QOPENHARMONYPLATFORMINPUTCONTEXT_H + ++#include +#include +#include +#include @@ -8365,9 +12601,54 @@ index 0000000000..62fb0fadfc +#include + +class QOpenHarmonyJsObject; ++class QOpenHarmonyJsWindow; +class QOpenHarmonyPlatformIntegration; + +QT_BEGIN_NAMESPACE ++ ++struct OpenHarmonyMouseEvent{ ++ OH_NativeXComponent *component; ++ float x; ++ float y; ++ QEvent::Type type; ++ Qt::MouseButton btn; ++}; ++ ++struct OpenHarmonyWheelEvent{ ++ OH_NativeXComponent *component; ++ QPoint global; ++ QPoint pixelDelta; ++ Qt::KeyboardModifiers mods; ++}; ++ ++struct OpenHarmonyTouchPoint { ++ int id; ++ int action; ++ float force; ++ float x; ++ float y; ++}; ++ ++struct OpenHarmonyTouchEvent { ++ OH_NativeXComponent *component; ++ int id; ++}; ++ ++struct OpenHarmonyKeyEvent { ++ OH_NativeXComponent *component; ++ OH_NativeXComponent_KeyCode code; ++ OH_NativeXComponent_KeyAction action; ++ OH_NativeXComponent_EventSourceType stype; ++ int64_t deviceId; ++ int64_t timeStamp; ++}; ++ ++ ++Q_DECLARE_METATYPE(OpenHarmonyMouseEvent) ++Q_DECLARE_METATYPE(OpenHarmonyWheelEvent) ++Q_DECLARE_METATYPE(OpenHarmonyTouchEvent) ++Q_DECLARE_METATYPE(OpenHarmonyKeyEvent) ++ +class QOpenHarmonyPlatformInputContext: public QPlatformInputContext +{ + Q_OBJECT @@ -8401,25 +12682,31 @@ index 0000000000..62fb0fadfc + + void clear(); + void touchBegin(); -+ void touchEnd(int id); + void setComposingText(const QString &text); ++ QString getTextAfterCursor(int length, int flags); ++ QString getTextBeforeCursor(int length, int flags); ++ + void touchAdd(int id, int action, float force, float x, float y); + static QOpenHarmonyPlatformInputContext * openHarmonyInputContext(); + void setPlatformIntegration(QOpenHarmonyPlatformIntegration *integration); + static napi_value init(napi_env env, napi_value exports); -+ void dispatchKeyEvent(OH_NativeXComponent_KeyCode code, -+ OH_NativeXComponent_KeyAction action, -+ OH_NativeXComponent_EventSourceType stype, int64_t deviceId, int64_t timeStamp); -+ Q_INVOKABLE void sendShortcut(int key); ++ ++ Qt::KeyboardModifiers queryKeyboardModifiers() const; + +public Q_SLOTS: + void updateCursorPosition(); -+ void mousePress(float x, float y); -+ void mouseRelease(float x, float y); -+ void touchDown(float x, float y); ++ void touchEnd(const OpenHarmonyTouchEvent &event); ++ void handleMouseEvent(const OpenHarmonyMouseEvent &event); ++ void wheelEvent(const OpenHarmonyWheelEvent &event); ++ void handleKeyEvent(const OpenHarmonyKeyEvent &event); + +private Q_SLOTS: ++ void touchDown(float x, float y); ++ void mouseMove(QOpenHarmonyJsWindow *window, float x, float y, Qt::MouseButton button); ++ void mousePress(QOpenHarmonyJsWindow *window, float x, float y, Qt::MouseButton button); ++ void mouseRelease(QOpenHarmonyJsWindow *window, float x, float y, Qt::MouseButton button); + void clearInput(); ++ void enterKeyDown(); + void commitText(QString text); + void commitMoveCursor(int direction); + void showInputPanelLater(Qt::ApplicationState state); @@ -8442,8 +12729,9 @@ index 0000000000..62fb0fadfc + bool m_ignoreMouseEvents = false; + QMetaObject::Connection m_updateCursorPosConnection; + QOpenHarmonyPlatformIntegration *m_integration = nullptr; -+ QList m_touchPoints; ++ QList m_touchPoints; + QSharedPointer m_jsInputCtl; ++ QPointer m_mouseGrabber; +}; + +QT_END_NAMESPACE @@ -8451,10 +12739,10 @@ index 0000000000..62fb0fadfc +#endif // QOPENHARMONYPLATFORMINPUTCONTEXT_H diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformintegration.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformintegration.cpp new file mode 100644 -index 0000000000..dea72d7492 +index 0000000000..fdf77dd58e --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformintegration.cpp -@@ -0,0 +1,304 @@ +@@ -0,0 +1,321 @@ +#include "qopenharmonyplatformintegration.h" + +#include @@ -8493,7 +12781,7 @@ index 0000000000..dea72d7492 + +#include +#include -+ ++#include + +QT_BEGIN_NAMESPACE + @@ -8520,6 +12808,7 @@ index 0000000000..dea72d7492 + m_eglDisplay = QOpenHarmonyEGLCore::eglDisplay(); + m_eglConfig = QOpenHarmonyEGLCore::eglConfig(); + ++// QWindowSystemInterface::setSynchronousWindowSystemEvents(true); + m_harmonyFontDatabase.reset(new QOpenHarmonyPlatformFontDatabase); + + m_primaryScreen.reset(new QOpenHarmonyPlatformScreen); @@ -8528,6 +12817,8 @@ index 0000000000..dea72d7492 + m_primaryScreen->setAvailableGeometry(QRect(m_defaultLeft, m_defaultTop, m_defaultGeometryWidth, m_defaultGeometryHeight)); + QWindowSystemInterface::handleScreenAdded(m_primaryScreen.data()); + ++ QPlatformCursor::setCapability(QPlatformCursor::OverrideCursor); ++ + m_mainThread = QThread::currentThread(); + +#ifndef QT_NO_CLIPBOARD @@ -8618,6 +12909,20 @@ index 0000000000..dea72d7492 + m_platformInputContext.reset(QPlatformInputContextFactory::create(icStr)); +} + ++Qt::KeyboardModifiers QOpenHarmonyPlatformIntegration::queryKeyboardModifiers() const ++{ ++ if (m_platformInputContext.isNull()) ++ return QPlatformIntegration::queryKeyboardModifiers(); ++ ++ ++ if (QOpenHarmonyPlatformInputContext *context = ++ static_cast(m_platformInputContext.data())){ ++ return context->queryKeyboardModifiers(); ++ } ++ ++ return Qt::KeyboardModifiers(Qt::NoModifier); ++} ++ +QPlatformInputContext *QOpenHarmonyPlatformIntegration::inputContext() const +{ + return m_platformInputContext.data(); @@ -8639,7 +12944,7 @@ index 0000000000..dea72d7492 + case PasswordMaskDelay: + return m_showPasswordEnabled ? 1500 : 0; + case ShowIsMaximized: -+ return QtOpenHarmony::isPhone(); ++ return QtOpenHarmony::defaultShowIsMaximized(); + default: + return QPlatformIntegration::styleHint(hint); + } @@ -8761,10 +13066,10 @@ index 0000000000..dea72d7492 + diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformintegration.h b/src/plugins/platforms/openharmony/qopenharmonyplatformintegration.h new file mode 100644 -index 0000000000..966cdbdeb5 +index 0000000000..3058c7010f --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformintegration.h -@@ -0,0 +1,135 @@ +@@ -0,0 +1,134 @@ +#ifndef QOPENHARMONYPLATFORMINTERATION_H +#define QOPENHARMONYPLATFORMINTERATION_H + @@ -8806,9 +13111,8 @@ index 0000000000..966cdbdeb5 + ~QOpenHarmonyPlatformIntegration() override; + + void initialize() override; -+ ++ Qt::KeyboardModifiers queryKeyboardModifiers() const override; + bool hasCapability(QPlatformIntegration::Capability cap) const override; -+ + QPlatformWindow *createPlatformWindow(QWindow *window) const override; + QPlatformWindow *createForeignWindow(QWindow *window, WId nativeHandle) const override; + QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override; @@ -9057,10 +13361,10 @@ index 0000000000..c885aa5fec +#endif // QOPENHARMONYPLATFORMOPENGLCONTEXT_H diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformopenglwindow.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformopenglwindow.cpp new file mode 100644 -index 0000000000..8e80ab7de7 +index 0000000000..e816db99c2 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformopenglwindow.cpp -@@ -0,0 +1,220 @@ +@@ -0,0 +1,267 @@ +#include "qopenharmonyplatformopenglwindow.h" +#include "qopenharmonyxcomponent.h" +#include "qopenharmonyplatformscreen.h" @@ -9072,6 +13376,8 @@ index 0000000000..8e80ab7de7 +#include "qopenharmonyjswindow.h" +#include "qopenharmonymain.h" + ++#include ++#include +#include +#include +#include @@ -9081,115 +13387,49 @@ index 0000000000..8e80ab7de7 +#include +#include + -+#include +QT_BEGIN_NAMESPACE -+ ++enum { ++ defaultWindowWidth = 800, ++ defaultWindowHeight = 600 ++}; + +QOpenHarmonyPlatformOpenGLWindow::QOpenHarmonyPlatformOpenGLWindow(QWindow *window, EGLDisplay display) + :QOpenHarmonyPlatformWindow(window), m_eglDisplay(display), m_jsWindow(nullptr) -+{ -+ ++{ +} + +QOpenHarmonyPlatformOpenGLWindow::~QOpenHarmonyPlatformOpenGLWindow() -+{ -+ if (QtOpenHarmony::isPhone()) -+ return; ++{ + clearEgl(); -+ if (m_jsWindow != nullptr) ++ if (m_jsWindow != nullptr) { ++ m_jsWindow->setQtWindow(nullptr); + qJsWindowManager->destoryWindow(m_jsWindow); ++ m_jsWindow = nullptr; ++ } +} + -+void QOpenHarmonyPlatformOpenGLWindow::repaint(const QRegion ®ion) ++void QOpenHarmonyPlatformOpenGLWindow::initialize() +{ -+ // This is only for real raster top-level windows. Stop in all other cases. -+ if ((window()->surfaceType() == QSurface::RasterGLSurface && qt_window_private(window())->compositing) -+ || window()->surfaceType() == QSurface::OpenGLSurface -+ || QOpenHarmonyPlatformWindow::parent()) -+ return; -+ -+ if (QtOpenHarmony::isPhone()) { -+ // phone draw all windows in a xcomponent -+ QRect dirtyClient = region.boundingRect(); -+ QRect currentGeometry = geometry(); -+ QRect dirtyRegion(currentGeometry.left() + dirtyClient.left(), -+ currentGeometry.top() + dirtyClient.top(), -+ dirtyClient.width(), -+ dirtyClient.height()); -+ QRect mOldGeometryLocal = m_oldGeometry; -+ m_oldGeometry = currentGeometry; -+ // If this is a move, redraw the previous location -+ if (mOldGeometryLocal != currentGeometry) -+ platformScreen()->setDirty(mOldGeometryLocal); -+ platformScreen()->setDirty(dirtyRegion); -+ } else { -+ // pad pc draw window in a independent xcomponent -+ if (m_jsWindow == nullptr) { -+ createJsWindow(); -+ } -+ if (m_jsWindow == nullptr) -+ return; -+ QOpenHarmonyPlatformBackingStore *backingStore = this->backingStore(); -+ if (backingStore) { -+ QImage image = m_jsWindow->image(); -+ QPainter compositePainter(&image); -+ compositePainter.setCompositionMode(QPainter::CompositionMode_Source); -+ QImage backingStoreImage = backingStore->toImage(); -+ compositePainter.drawImage(QPoint(0, 0), backingStoreImage, backingStoreImage.rect()); -+#ifdef USE_MASK -+ compositePainter.save(); -+ compositePainter.setBrush(QBrush(Qt::red)); -+ QString ct("managed by isoftstone"); -+ QStaticText sct(ct); -+ QFontMetrics fm(compositePainter.font()); -+ int height = fm.height(); -+ int width = fm.horizontalAdvance(ct); -+ QPoint tp = image.rect().bottomRight(); -+ tp.rx() -= (width + 2); -+ tp.ry() -= (height + 2); -+ compositePainter.drawStaticText(tp, sct); -+ compositePainter.restore(); -+#endif -+ -+ m_jsWindow->paint(image); -+ } -+ } ++ QOpenHarmonyPlatformWindow::initialize(); ++ QRect rect = initialGeometry(window(), ++ window()->geometry(), defaultWindowWidth, defaultWindowHeight); ++ QOpenHarmonyPlatformWindow::setGeometry(rect); +} + -+QMargins QOpenHarmonyPlatformOpenGLWindow::frameMargins() const ++void QOpenHarmonyPlatformOpenGLWindow::repaint() +{ -+ return QtOpenHarmony::frameMargins(); ++ m_jsWindow->repaint(); +} + +void QOpenHarmonyPlatformOpenGLWindow::setGeometry(const QRect &rect) +{ -+ // 在手机上,所有窗口绘制在一个Xcomponent, -+ if (!QtOpenHarmony::isPhone()) { -+ if (m_jsWindow == nullptr) -+ createJsWindow(); -+ if (m_jsWindow == nullptr) -+ return; -+ // 设置jswindow的尺寸 -+ m_jsWindow->setGeometry(rect); -+ } -+ if (rect == geometry()) -+ return; -+ -+ m_oldGeometry = geometry(); -+ + QOpenHarmonyPlatformWindow::setGeometry(rect); -+ QRect availableGeometry = screen()->availableGeometry(); -+ if (m_oldGeometry.width() == 0 -+ && m_oldGeometry.height() == 0 -+ && rect.width() > 0 -+ && rect.height() > 0 -+ && availableGeometry.width() > 0 -+ && availableGeometry.height() > 0) { -+ QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size())); -+ } -+ -+ if (rect.topLeft() != m_oldGeometry.topLeft()) -+ repaint(QRegion(rect)); ++ if (m_jsWindow == nullptr) ++ return; ++ // 设置jswindow的尺寸 ++ m_jsWindow->setGeometry(rect); ++ if (m_windowState & Qt::WindowNoState) ++ m_normalGeometry = rect; +} + +EGLSurface QOpenHarmonyPlatformOpenGLWindow::surface() @@ -9230,6 +13470,15 @@ index 0000000000..8e80ab7de7 + } +} + ++void QOpenHarmonyPlatformOpenGLWindow::propagateSizeHints() ++{ ++ if (m_jsWindow == nullptr) ++ return; ++ QSize minimumSize = windowMinimumSize(); ++ QSize maximumSize = windowMaximumSize(); ++ m_jsWindow->setWindowLimits(minimumSize, maximumSize); ++} ++ +void QOpenHarmonyPlatformOpenGLWindow::createEgl(EGLConfig config) +{ +// clearEgl(); @@ -9239,7 +13488,7 @@ index 0000000000..8e80ab7de7 + if (Q_UNLIKELY(m_eglSurface == EGL_NO_SURFACE)) { + EGLint error = eglGetError(); + eglTerminate(m_eglDisplay); -+ qWarning("EGL Error : Could not create the egl surface: error = 0x%x\n", error); ++ LOGW("EGL Error : Could not create the egl surface: error = 0x%{public}d\n", error); + } +} + @@ -9251,6 +13500,17 @@ index 0000000000..8e80ab7de7 + return m_format; +} + ++void QOpenHarmonyPlatformOpenGLWindow::setWindowState(Qt::WindowStates state) ++{ ++ QOpenHarmonyPlatformWindow::setWindowState(state); ++ if (state == m_windowState) ++ return; ++ m_windowState = state; ++ if (m_jsWindow == nullptr || !m_jsWindow->isVisible()) ++ return; ++ setWindowState_sys(m_windowState); ++} ++ +void QOpenHarmonyPlatformOpenGLWindow::clearEgl() +{ + if (m_eglSurface != EGL_NO_SURFACE) { @@ -9262,7 +13522,38 @@ index 0000000000..8e80ab7de7 + +void QOpenHarmonyPlatformOpenGLWindow::createJsWindow() const +{ -+ m_jsWindow = qJsWindowManager->createWindow(const_cast(this)); ++ qDebug() << "create open harmony window for" << this; ++ QOpenHarmonyPlatformOpenGLWindow *self = const_cast(this); ++ m_jsWindow = qJsWindowManager->createWindow(self); ++ if (m_jsWindow != nullptr) ++ return; ++ if (isTopLevelWindow()) { ++ m_jsWindow->startListener(); ++ self->propagateSizeHints(); ++ } ++} ++ ++void QOpenHarmonyPlatformOpenGLWindow::paint(const QImage &image, const QRegion ®ion, const QPoint &offset) ++{ ++ if (m_jsWindow == nullptr || !isExposed()) ++ return; ++ QImage windowImage = image; ++#ifdef USE_MASK ++ QPainter compositePainter(&windowImage); ++ compositePainter.save(); ++ compositePainter.setBrush(QBrush(Qt::red)); ++ QString ct("managed by isoftstone"); ++ QStaticText sct(ct); ++ QFontMetrics fm(compositePainter.font()); ++ int height = fm.height(); ++ int width = fm.horizontalAdvance(ct); ++ QPoint tp = windowImage.rect().bottomRight(); ++ tp.rx() -= (width + 2); ++ tp.ry() -= (height + 2); ++ compositePainter.drawStaticText(tp, sct); ++ compositePainter.restore(); ++#endif ++ m_jsWindow->paint(windowImage, region, offset); +} + +EGLDisplay QOpenHarmonyPlatformOpenGLWindow::eglDisplay() const @@ -9271,25 +13562,86 @@ index 0000000000..8e80ab7de7 +} + +void QOpenHarmonyPlatformOpenGLWindow::setVisible(bool visible) -+{ ++{ + QOpenHarmonyPlatformWindow::setVisible(visible); -+ if (!QtOpenHarmony::isPhone()) { -+ if (m_jsWindow == nullptr) -+ createJsWindow(); ++ if (m_jsWindow == nullptr) { ++ createJsWindow(); ++ } ++ ++ if (m_jsWindow == nullptr) { ++ return; ++ } ++ if (visible) { ++ const QWindow *w = window(); ++ const Qt::WindowFlags flags = w->flags(); ++ setWindowState_sys(m_windowState); ++ } else { + m_jsWindow->setVisible(visible); + } +} + ++QOpenHarmonyJsWindow *QOpenHarmonyPlatformOpenGLWindow::jsWindow() const ++{ ++ return m_jsWindow; ++} ++ ++void QOpenHarmonyPlatformOpenGLWindow::handleGeometryChange() ++{ ++ QRect rect = m_jsWindow->jsGeometry(); ++ QOpenHarmonyPlatformWindow::setGeometry(rect); ++ QWindowSystemInterface::handleExposeEvent(window(), rect); ++} ++ ++WId QOpenHarmonyPlatformOpenGLWindow::winId() const ++{ ++ if (m_jsWindow == nullptr) ++ createJsWindow(); ++ if (m_jsWindow == nullptr) ++ return 0; ++ return m_jsWindow->id(); ++} ++ ++QMargins QOpenHarmonyPlatformOpenGLWindow::frameMargins() const ++{ ++ if (m_jsWindow == nullptr) ++ return QMargins(); ++ QMargins m = m_jsWindow->frameMargins(); ++ return m; ++} ++ ++ ++void QOpenHarmonyPlatformOpenGLWindow::setWindowState_sys(Qt::WindowStates state) ++{ ++ if (m_jsWindow == nullptr) ++ return; ++ ++ if (state & Qt::WindowFullScreen) ++ m_jsWindow->showFullScreen(); ++ else if (state & Qt::WindowMaximized) ++ m_jsWindow->showMaximized(); ++ else if (state == Qt::WindowMinimized) { ++ m_jsWindow->showMinimized(); ++ } else if (state == Qt::WindowNoState) { ++ m_jsWindow->setGeometry(geometry()); ++ m_jsWindow->showNormal(); ++ } ++ // 在Pad上,XComponent接收不到SurfaceChanged事件,手动触发 ++ if (QtOpenHarmony::defaultShowIsMaximized) { ++ m_jsWindow->handleSurfaceChanged(); ++ } ++} ++ +QT_END_NAMESPACE diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformopenglwindow.h b/src/plugins/platforms/openharmony/qopenharmonyplatformopenglwindow.h new file mode 100644 -index 0000000000..785ede93ef +index 0000000000..dc096311d9 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformopenglwindow.h -@@ -0,0 +1,49 @@ +@@ -0,0 +1,62 @@ +#ifndef QOPENHARMONYPLATFORMOPENGLWINDOW_H +#define QOPENHARMONYPLATFORMOPENGLWINDOW_H + ++#include +#include +#include "qopenharmonyplatformwindow.h" + @@ -9303,24 +13655,35 @@ index 0000000000..785ede93ef + explicit QOpenHarmonyPlatformOpenGLWindow(QWindow *window, EGLDisplay display); + ~QOpenHarmonyPlatformOpenGLWindow() override; + ++ void initialize() override; ++ + void setGeometry(const QRect &rect) override; + + EGLSurface eglSurface(EGLConfig config); + QSurfaceFormat format() const override; + ++ virtual void setWindowState(Qt::WindowStates state) override; + bool checkNativeSurface(EGLConfig config); + + void applicationStateChanged(Qt::ApplicationState) override; ++ void propagateSizeHints() override; + -+ void repaint(const QRegion ®ion) override; -+ -+ virtual QMargins frameMargins() const; -+ ++ void repaint() override; + EGLDisplay eglDisplay() const; + + void setVisible(bool visible); + ++ QOpenHarmonyJsWindow *jsWindow() const; ++ ++ virtual void handleGeometryChange(); ++ ++ WId winId() const override; ++ ++ QMargins frameMargins() const; ++ ++ void paint(const QImage &image, const QRegion ®ion, const QPoint &offset); +protected: ++ void setWindowState_sys(Qt::WindowStates state); + EGLSurface surface(); + void createEgl(EGLConfig config); + void clearEgl(); @@ -9330,8 +13693,9 @@ index 0000000000..785ede93ef + EGLDisplay m_eglDisplay = EGL_NO_DISPLAY; + EGLSurface m_eglSurface = EGL_NO_SURFACE; + QSurfaceFormat m_format; -+ QRect m_oldGeometry; + mutable QOpenHarmonyJsWindow *m_jsWindow = nullptr; ++ Qt::WindowStates m_windowState = Qt::WindowNoState; ++ QRect m_normalGeometry; +}; + +QT_END_NAMESPACE @@ -9370,11 +13734,10 @@ index 0000000000..c4cb6257c3 + diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformscreen.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformscreen.cpp new file mode 100644 -index 0000000000..10a9b29cf2 +index 0000000000..31773b94a4 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformscreen.cpp -@@ -0,0 +1,329 @@ -+#include +@@ -0,0 +1,169 @@ +#include + +#ifdef USE_MASK @@ -9391,8 +13754,7 @@ index 0000000000..10a9b29cf2 +#include "qopenharmonyplatformwindow.h" +#include "qopenharmonyplatformopenglwindow.h" +#include "qopenharmonydefines.h" -+#include "qopenharmonyjswindow.h" -+#include "qopenharmonyjswindowmanager.h" ++#include "qopenharmonyplatformcursor.h" + +#include + @@ -9407,16 +13769,15 @@ index 0000000000..10a9b29cf2 + + +QOpenHarmonyPlatformScreen::QOpenHarmonyPlatformScreen() -+ : QObject(), QPlatformScreen(), m_stopDraw(false) ++ : QObject(), QPlatformScreen(), m_cursor(new QOpenHarmonyPlatformCursor(this)) +{ + m_availableGeometry = QRect(QOpenHarmonyPlatformIntegration::m_defaultLeft, QOpenHarmonyPlatformIntegration::m_defaultTop, QOpenHarmonyPlatformIntegration::m_defaultGeometryWidth, QOpenHarmonyPlatformIntegration::m_defaultGeometryHeight); + m_size = QSize(QOpenHarmonyPlatformIntegration::m_defaultScreenWidth, QOpenHarmonyPlatformIntegration::m_defaultScreenHeight); + -+ m_format = QImage::Format_ARGB32_Premultiplied; ++ m_format = QImage::Format_RGBA8888_Premultiplied; + m_depth = 32; + m_physicalSize.setHeight(QOpenHarmonyPlatformIntegration::m_defaultPhysicalSizeHeight); + m_physicalSize.setWidth(QOpenHarmonyPlatformIntegration::m_defaultPhysicalSizeWidth); -+ connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QOpenHarmonyPlatformScreen::applicationStateChanged); +} + +QOpenHarmonyPlatformScreen::~QOpenHarmonyPlatformScreen() @@ -9445,97 +13806,47 @@ index 0000000000..10a9b29cf2 + return nullptr; +} + -+bool QOpenHarmonyPlatformScreen::event(QEvent *event) -+{ -+ if (event->type() == QEvent::UpdateRequest) { -+ doRedraw(); -+ m_updatePending = false; -+ return true; -+ } -+ return QObject::event(event); -+} -+ +void QOpenHarmonyPlatformScreen::addWindow(QOpenHarmonyPlatformWindow *window) +{ -+ if (window->parent() && window->isRaster()) ++ if (m_windowStack.contains(window)) + return; -+ -+ Q_ASSERT(!m_windowStack.contains(window)); + m_windowStack.prepend(window); -+ if (window->isRaster() && QtOpenHarmony::isPhone()) { -+ setDirty(window->geometry()); -+ } + + QWindow *w = topWindow(); + QWindowSystemInterface::handleWindowActivated(w); -+ topWindowChanged(w); +} ++ +void QOpenHarmonyPlatformScreen::removeWindow(QOpenHarmonyPlatformWindow *window) +{ -+ if (window->parent() && window->isRaster()) ++ if (!m_windowStack.contains(window)) + return; -+ -+ -+ Q_ASSERT(m_windowStack.contains(window)); + m_windowStack.removeOne(window); -+ Q_ASSERT(!m_windowStack.contains(window)); + -+ if (window->isRaster() && QtOpenHarmony::isPhone()) { -+ setDirty(window->geometry()); -+ } + + QWindow *w = topWindow(); ++ if (w == nullptr) ++ return; + QWindowSystemInterface::handleWindowActivated(w); -+ topWindowChanged(w); +} + +void QOpenHarmonyPlatformScreen::raise(QOpenHarmonyPlatformWindow *window) +{ -+ if (window->parent() && window->isRaster()) -+ return; -+ + int index = m_windowStack.indexOf(window); + if (index <= 0) + return; + m_windowStack.move(index, 0); -+ if (window->isRaster() && QtOpenHarmony::isPhone()) { -+ setDirty(window->geometry()); -+ } + QWindow *w = topWindow(); + QWindowSystemInterface::handleWindowActivated(w); -+ topWindowChanged(w); +} + +void QOpenHarmonyPlatformScreen::lower(QOpenHarmonyPlatformWindow *window) +{ -+ if (window->parent() && window->isRaster()) -+ return; -+ + int index = m_windowStack.indexOf(window); + if (index == -1 || index == (m_windowStack.size() - 1)) + return; + m_windowStack.move(index, m_windowStack.size() - 1); -+ if (window->isRaster() && QtOpenHarmony::isPhone()) { -+ setDirty(window->geometry()); -+ } + QWindow *w = topWindow(); + QWindowSystemInterface::handleWindowActivated(w); -+ topWindowChanged(w); -+} -+ -+void QOpenHarmonyPlatformScreen::scheduleUpdate() -+{ -+ if (!m_updatePending) { -+ m_updatePending = true; -+ QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); -+ } -+} -+ -+void QOpenHarmonyPlatformScreen::setDirty(const QRect &rect) -+{ -+ QRect intersection = rect.intersected(m_availableGeometry); -+ m_dirtyRect |= intersection; -+ scheduleUpdate(); +} + +void QOpenHarmonyPlatformScreen::setPhysicalSize(const QSize &size) @@ -9574,105 +13885,6 @@ index 0000000000..10a9b29cf2 + } +} + -+void QOpenHarmonyPlatformScreen::applicationStateChanged(Qt::ApplicationState state) -+{ -+ for (QOpenHarmonyPlatformWindow *w : qAsConst(m_windowStack)) -+ w->applicationStateChanged(state); -+ -+// if (state <= Qt::ApplicationHidden) { -+// releaseSurface(); -+// } -+ m_stopDraw = (state <= Qt::ApplicationHidden); -+} -+ -+void QOpenHarmonyPlatformScreen::topWindowChanged(QWindow *w) -+{ -+ if (w != 0) { -+ QOpenHarmonyPlatformWindow *platformWindow = static_cast(w->handle()); -+ if (platformWindow != 0) -+ platformWindow->updateStatusBarVisibility(); -+ } -+} -+ -+void QOpenHarmonyPlatformScreen::doRedraw() -+{ -+ if (m_dirtyRect.isEmpty() || m_stopDraw) -+ return; -+ -+ // Stop if there are no visible raster windows. If we only have RasterGLSurface -+ // windows that have renderToTexture children (i.e. they need the OpenGL path) then -+ // we do not need an overlay surface. -+ bool hasVisibleRasterWindows = false; -+ for (QOpenHarmonyPlatformWindow *window : qAsConst(m_windowStack)) { -+ if (window->window()->isVisible() && window->isRaster() && !qt_window_private(window->window())->compositing) { -+ hasVisibleRasterWindows = true; -+ break; -+ } -+ } -+ if (!hasVisibleRasterWindows) { -+ qDebug() << "no visible raster windows"; -+ return; -+ } -+ -+ if (m_window == nullptr || m_windowStack.isEmpty()) { -+ m_window = qJsWindowManager->createWindow(m_windowStack.first()); -+ } -+ if (m_window == nullptr) { -+ return; -+ } -+ -+ QImage screenImage = m_window->image(); -+ QPainter compositePainter(&screenImage); -+ compositePainter.setCompositionMode(QPainter::CompositionMode_Source); -+ -+ QRegion visibleRegion(m_dirtyRect); -+ for (QOpenHarmonyPlatformWindow *window : qAsConst(m_windowStack)) { -+ if (!window->window()->isVisible() -+ || qt_window_private(window->window())->compositing -+ || !window->isRaster()) -+ continue; -+ -+ for (const QRect &rect : std::vector(visibleRegion.begin(), visibleRegion.end())) { -+ QRect targetRect = window->geometry(); -+ targetRect &= rect; -+ -+ if (targetRect.isNull()) -+ continue; -+ -+ visibleRegion -= targetRect; -+ QRect windowRect = targetRect.translated(-window->geometry().topLeft()); -+ QOpenHarmonyPlatformOpenGLWindow *openglWindow = static_cast(window); -+ if (openglWindow != nullptr) { -+ QOpenHarmonyPlatformBackingStore *backingStore = openglWindow->backingStore(); -+ if (backingStore) { -+ compositePainter.drawImage(targetRect.topLeft(), backingStore->toImage(), windowRect); -+ } -+ } -+ } -+ } -+ -+ for (const QRect &rect : visibleRegion) -+ compositePainter.fillRect(rect, QColor(Qt::transparent)); -+ -+#ifdef USE_MASK -+ compositePainter.save(); -+ compositePainter.setBrush(QBrush(Qt::red)); -+ QString ct("managed by isoftstone"); -+ QStaticText sct(ct); -+ QFontMetrics fm(compositePainter.font()); -+ int height = fm.height(); -+ int width = fm.horizontalAdvance(ct); -+ QPoint tp = screenImage.rect().bottomRight(); -+ tp.rx() -= (width + 2); -+ tp.ry() -= (height + 2); -+ compositePainter.drawStaticText(tp, sct); -+ compositePainter.restore(); -+#endif -+ -+ m_window->paint(screenImage); -+ m_dirtyRect = QRect(); -+} -+ +QDpi QOpenHarmonyPlatformScreen::logicalDpi() const +{ + qreal lDpi = QtOpenHarmony::scaledDensity() * 72; @@ -9694,36 +13906,23 @@ index 0000000000..10a9b29cf2 + return QOpenHarmonyPlatformIntegration::m_nativeOrientation; +} + -+void QOpenHarmonyPlatformScreen::releaseSurface() -+{ -+// if (m_component != nullptr) { -+// QOpenHarmonyXComponentManager::instance()->remove(m_component); -+// m_component = nullptr; -+// } -+} -+ +QT_END_NAMESPACE diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformscreen.h b/src/plugins/platforms/openharmony/qopenharmonyplatformscreen.h new file mode 100644 -index 0000000000..20bec988ab +index 0000000000..a9761d58dd --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformscreen.h -@@ -0,0 +1,80 @@ +@@ -0,0 +1,61 @@ +#ifndef QOPENHARMONYPLATFORMSCREEN_H +#define QOPENHARMONYPLATFORMSCREEN_H + +#include -+#include -+#include -+#include -+#include -+ +#include ++#include + +QT_BEGIN_NAMESPACE + +class QOpenHarmonyPlatformWindow; -+class QOpenHarmonyJsWindow; + +class QOpenHarmonyPlatformScreen: public QObject, public QPlatformScreen +{ @@ -9747,59 +13946,45 @@ index 0000000000..20bec988ab + void raise(QOpenHarmonyPlatformWindow *window); + void lower(QOpenHarmonyPlatformWindow *window); + -+ void scheduleUpdate(); -+ void topWindowChanged(QWindow *w); ++ using CursorPtr = QScopedPointer; ++ QPlatformCursor *cursor() const override { return m_cursor.data(); } ++ + +public slots: -+ void setDirty(const QRect &rect); + void setPhysicalSize(const QSize &size); + void setAvailableGeometry(const QRect &rect); + void setSize(const QSize &size); -+ -+protected: -+ bool event(QEvent *event) override; -+ ++private: + typedef QList WindowStackType; + WindowStackType m_windowStack; -+ QRect m_dirtyRect; -+ bool m_updatePending = false; + + QRect m_availableGeometry; + int m_depth; + QImage::Format m_format; + QSizeF m_physicalSize; ++ QSize m_size; + ++ const CursorPtr m_cursor; +private: + QDpi logicalDpi() const override; + qreal pixelDensity() const override; + Qt::ScreenOrientation orientation() const override; + Qt::ScreenOrientation nativeOrientation() const override; -+ void releaseSurface(); -+ void applicationStateChanged(Qt::ApplicationState); -+ -+private slots: -+ void doRedraw(); -+ -+private: -+ QOpenHarmonyJsWindow *m_window = nullptr; -+ QWaitCondition m_surfaceWaitCondition; -+ QSize m_size; -+ bool m_stopDraw; +}; + +QT_END_NAMESPACE +#endif diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformservices.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformservices.cpp new file mode 100644 -index 0000000000..a6527dcd59 +index 0000000000..055b1b7991 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformservices.cpp -@@ -0,0 +1,45 @@ +@@ -0,0 +1,46 @@ +#include "qopenharmonyplatformservices.h" + -+#include -+#include +#include ++#include ++#include + +QT_BEGIN_NAMESPACE + @@ -9811,10 +13996,11 @@ index 0000000000..a6527dcd59 + + if (m_jsService.isNull()) + { -+ m_jsService = qJsObjectLoader->create("JsUrl"); ++ m_jsService = qJsObjectLoader->create("JsServices"); + } -+ + QString urlStr = theUrl.toString(); ++ //短时间重复使用鸿蒙的want打开网址,可能会导致传递的url丢失 ++ QThread::msleep(300); + bool result = m_jsService->call("openUrl", urlStr); + return result; +} @@ -9873,11 +14059,10 @@ index 0000000000..b41060aaa0 +#endif diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformtheme.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformtheme.cpp new file mode 100644 -index 0000000000..84b3ec2649 +index 0000000000..f9fc3685a1 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformtheme.cpp -@@ -0,0 +1,119 @@ -+#include +@@ -0,0 +1,140 @@ +#include +#include +#include "qopenharmonyplatformtheme.h" @@ -9889,18 +14074,40 @@ index 0000000000..84b3ec2649 +static QPalette qt_harmonyPalette() +{ +#if 1 -+ QColor base("#FFFFFF"); ++ QColor base(250, 250, 250); + QColor text("#182431"); -+ QColor window("#FFFFFF"); -+ QColor button("#0C182431"); -+ QColor windowText("#182431"); -+ QColor brightText("#FFFFFF"); -+ QColor dark = button.darker(); -+ QColor light = button.lighter(); -+ QColor mid = button.lighter(130); -+ -+ QPalette harmonyPalette(windowText, button, light, dark, mid, text, brightText, base, window); ++ QColor background(250, 250, 250); ++ QColor light = background.lighter(150); ++ QColor mid(background.darker(130)); ++ QColor midLight = mid.lighter(110); ++ QColor disabledBase(background); ++ QColor dark = background.darker(150); ++ QColor darkDisabled = dark.darker(110); ++ QColor highlightedText = Qt::black; ++ QColor disabledText = QColor(190, 190, 190); ++ QColor button(241, 241, 241); ++ QColor shadow(201, 201, 201); ++ QColor highlight(0, 120, 215); ++ QColor disabledShadow = shadow.lighter(150); + ++ QPalette harmonyPalette = QPalette(Qt::black, background, light, dark, mid, text, base); ++ harmonyPalette.setColor(QPalette::Disabled, QPalette::ButtonText, Qt::gray); ++ harmonyPalette.setColor(QPalette::Disabled, QPalette::Text, Qt::gray); ++ harmonyPalette.setBrush(QPalette::Midlight, midLight); ++ harmonyPalette.setBrush(QPalette::Button, button); ++ harmonyPalette.setBrush(QPalette::Shadow, shadow); ++ harmonyPalette.setBrush(QPalette::HighlightedText, highlightedText); ++ ++ harmonyPalette.setBrush(QPalette::Disabled, QPalette::Text, disabledText); ++ harmonyPalette.setBrush(QPalette::Disabled, QPalette::WindowText, disabledText); ++ harmonyPalette.setBrush(QPalette::Disabled, QPalette::ButtonText, disabledText); ++ harmonyPalette.setBrush(QPalette::Disabled, QPalette::Base, disabledBase); ++ harmonyPalette.setBrush(QPalette::Disabled, QPalette::Dark, darkDisabled); ++ harmonyPalette.setBrush(QPalette::Disabled, QPalette::Shadow, disabledShadow); ++ ++ harmonyPalette.setBrush(QPalette::Active, QPalette::Highlight, highlight); ++ harmonyPalette.setBrush(QPalette::Inactive, QPalette::Highlight, highlight); ++ harmonyPalette.setBrush(QPalette::Disabled, QPalette::Highlight, highlight.lighter(150)); + return harmonyPalette; +#endif + @@ -9978,8 +14185,8 @@ index 0000000000..84b3ec2649 +QPlatformDialogHelper *QOpenHarmonyPlatformTheme::createPlatformDialogHelper(QPlatformTheme::DialogType type) const +{ + switch (type) { -+ case MessageDialog: -+ return new QOpenHarmonyPlatformMessageDialogHelper; ++// case MessageDialog: ++// return new QOpenHarmonyPlatformMessageDialogHelper; + case FileDialog: + return new QOpenHarmonyPlatformFileDialogHelper; + default: @@ -10022,10 +14229,11 @@ index 0000000000..34ee717e33 +#endif // QOPENHARMONYPLATFORMTHEME_H diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformvulkaninstance.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformvulkaninstance.cpp new file mode 100644 -index 0000000000..92e75915d3 +index 0000000000..5c6af22330 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformvulkaninstance.cpp -@@ -0,0 +1,27 @@ +@@ -0,0 +1,28 @@ ++#include "qopenharmonylog.h" +#include "qopenharmonyplatformvulkaninstance.h" + +QT_BEGIN_NAMESPACE @@ -10036,7 +14244,7 @@ index 0000000000..92e75915d3 + m_lib.setFileName(QStringLiteral("vulkan")); + + if (!m_lib.load()) { -+ qWarning("Failed to load %s", qPrintable(m_lib.fileName())); ++ LOGW("Failed to load %{public}s", qPrintable(m_lib.fileName())); + return; + } + @@ -10045,7 +14253,7 @@ index 0000000000..92e75915d3 + +void QOpenHarmonyPlatformVulkanInstance::createOrAdoptInstance() +{ -+ initInstance(m_instance, QByteArrayList() << QByteArrayLiteral("VK_KHR_android_surface")); ++ initInstance(m_instance, QByteArrayList() << QByteArrayLiteral("VK_OHOS_surface") << QByteArrayLiteral("VK_KHR_surface")); +} + +QOpenHarmonyPlatformVulkanInstance::~QOpenHarmonyPlatformVulkanInstance() @@ -10085,36 +14293,42 @@ index 0000000000..27803ba6a1 +#endif // QOPENHARMONYPLATFORMVULKANINSTANCE_H diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformwindow.cpp b/src/plugins/platforms/openharmony/qopenharmonyplatformwindow.cpp new file mode 100644 -index 0000000000..a73f38fba3 +index 0000000000..e426be5da1 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformwindow.cpp -@@ -0,0 +1,145 @@ +@@ -0,0 +1,275 @@ +#include "qopenharmonyplatformwindow.h" +#include "qopenharmonyplatformscreen.h" ++#include "qopenharmonymain.h" + ++#include +#include ++#include +#include +#include +#include -+#include + +QT_BEGIN_NAMESPACE + -+enum { -+ defaultWindowWidth = 160, -+ defaultWindowHeight = 160 -+}; ++ ++static QList allWindows; + +QOpenHarmonyPlatformWindow::QOpenHarmonyPlatformWindow(QWindow *window) -+ : QPlatformWindow(window) ++ : QPlatformWindow(window) ++{ ++} ++ ++QOpenHarmonyPlatformWindow::~QOpenHarmonyPlatformWindow() ++{ ++ allWindows.removeOne(this); ++} ++ ++void QOpenHarmonyPlatformWindow::initialize() +{ -+ m_windowFlags = window->flags(); -+ m_windowState = Qt::WindowNoState; -+ static QAtomicInt winIdGenerator(1); -+ m_windowId = winIdGenerator.fetchAndAddRelaxed(1); -+ setWindowState(window->windowStates()); -+ m_rect = initialGeometry(window, -+ QRect(0, 0, 400, 300), defaultWindowWidth, defaultWindowHeight); ++ setWindowState(window()->windowStates()); ++ m_helper.reset(new QOpenHarmonyPlatformWindowHelper(this)); ++ allWindows.append(this); ++ m_hasFrame = detectionWindowHasFrame(); +} + +void QOpenHarmonyPlatformWindow::lower() @@ -10124,68 +14338,64 @@ index 0000000000..a73f38fba3 + +void QOpenHarmonyPlatformWindow::raise() +{ -+ updateStatusBarVisibility(); + platformScreen()->raise(this); +} + -+void QOpenHarmonyPlatformWindow::setGeometry(const QRect &rect) -+{ -+ QWindowSystemInterface::handleGeometryChange(window(), rect); -+ QPlatformWindow::setGeometry(rect); -+ m_rect = rect; -+} -+ -+QRect QOpenHarmonyPlatformWindow::geometry() const ++void QOpenHarmonyPlatformWindow::setOpacity(qreal level) +{ -+ return m_rect; ++ /* TODO 需要控制整个窗体的透明度 */ ++ Q_UNUSED(level); +} + +void QOpenHarmonyPlatformWindow::setVisible(bool visible) +{ + if (visible) { -+ updateStatusBarVisibility(); -+ if (m_windowState & Qt::WindowFullScreen) -+ setGeometry(platformScreen()->geometry()); -+ else if (m_windowState & Qt::WindowMaximized) -+ setGeometry(platformScreen()->availableGeometry()); -+ else if (m_windowState == Qt::WindowNoState) { -+ QRect rect = initialGeometry(window(), -+ window()->geometry(), defaultWindowWidth, defaultWindowHeight); -+ setGeometry(rect); -+ } + platformScreen()->addWindow(this); + } else { + platformScreen()->removeWindow(this); + } ++ QPlatformWindow::setVisible(visible); ++} + -+ QRect availableGeometry = screen()->availableGeometry(); -+ if (geometry().width() > 0 && geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0) -+ QPlatformWindow::setVisible(visible); ++void QOpenHarmonyPlatformWindow::setGeometry(const QRect &rect) ++{ ++ QPlatformWindow::setGeometry(rect); ++ QWindowSystemInterface::handleGeometryChange(window(), rect); +} + -+void QOpenHarmonyPlatformWindow::setWindowState(Qt::WindowStates state) ++bool QOpenHarmonyPlatformWindow::isTopLevelWindow() const +{ -+ if (m_windowState == state) -+ return; ++ return window()->isTopLevel(); ++} + -+ QPlatformWindow::setWindowState(state); -+ m_windowState = state; ++bool QOpenHarmonyPlatformWindow::hasFrame() const ++{ ++ return m_hasFrame; + -+ if (window()->isVisible()) -+ updateStatusBarVisibility(); +} + -+void QOpenHarmonyPlatformWindow::setWindowFlags(Qt::WindowFlags flags) ++bool QOpenHarmonyPlatformWindow::hasFocus() const +{ -+ if (m_windowFlags == flags) -+ return; ++ Qt::WindowType type = static_cast(int(window()->flags()) & Qt::WindowType_Mask); ++ return !((type == Qt::Popup) || (type == Qt::ToolTip) || (type == Qt::Tool)); ++} ++ ++QMargins QOpenHarmonyPlatformWindow::frameMargins() const ++{ ++ return QMargins(); ++} + -+ m_windowFlags = flags; ++QString QOpenHarmonyPlatformWindow::parentWindowName() const ++{ ++ QOpenHarmonyPlatformWindow *p = dynamic_cast(parent()); ++ if (p == nullptr) ++ return QString(); ++ return p->windowName(); +} + -+Qt::WindowFlags QOpenHarmonyPlatformWindow::windowFlags() const ++QOpenHarmonyPlatformWindowHelper *QOpenHarmonyPlatformWindow::helper() const +{ -+ return m_windowFlags; ++ return m_helper.data(); +} + +void QOpenHarmonyPlatformWindow::setParent(const QPlatformWindow *window) @@ -10193,27 +14403,39 @@ index 0000000000..a73f38fba3 + Q_UNUSED(window); +} + ++void QOpenHarmonyPlatformWindow::setWId(WId nativeHandle) ++{ ++ m_windowId = nativeHandle; ++} ++ +QOpenHarmonyPlatformScreen *QOpenHarmonyPlatformWindow::platformScreen() const +{ + return static_cast(window()->screen()->handle()); +} + -+void QOpenHarmonyPlatformWindow::propagateSizeHints() ++void QOpenHarmonyPlatformWindow::setWindowTitle(const QString &title) +{ -+ //shut up warning from default implementation ++ m_title = title; +} + -+void QOpenHarmonyPlatformWindow::requestActivateWindow() ++QString QOpenHarmonyPlatformWindow::windowTitle() const +{ -+ platformScreen()->topWindowChanged(window()); ++ return formatWindowTitle(m_title, " - "); +} + -+void QOpenHarmonyPlatformWindow::updateStatusBarVisibility() ++void QOpenHarmonyPlatformWindow::setWindowName(const QString &name) +{ -+ Qt::WindowFlags flags = window()->flags(); -+ bool isNonRegularWindow = flags & (Qt::Popup | Qt::Dialog | Qt::Sheet) & ~Qt::Window; -+ if (!isNonRegularWindow) { -+ } ++ m_name = name; ++} ++ ++QString QOpenHarmonyPlatformWindow::windowName() const ++{ ++ return m_name; ++} ++ ++void QOpenHarmonyPlatformWindow::requestActivateWindow() ++{ ++ +} + +bool QOpenHarmonyPlatformWindow::isExposed() const @@ -10227,51 +14449,177 @@ index 0000000000..a73f38fba3 +{ + QRegion region; + if (isExposed()) -+ region = QRect(QPoint(), geometry().size()); ++ region = geometry(); + + QWindowSystemInterface::handleExposeEvent(window(), region); + QWindowSystemInterface::flushWindowSystemEvents(); +} + ++QOpenHarmonyPlatformWindowHelper::QOpenHarmonyPlatformWindowHelper(QOpenHarmonyPlatformWindow *window) ++ : m_window(window) ++{ ++ ++} ++ ++// harmony os state ++#define WINDOW_SHOWN 1 ++#define WINDOW_ACTIVE 2 ++#define WINDOW_INACTIVE 3 ++#define WINDOW_HIDDEN 4 ++#define WINDOW_DESTROYED 7 ++ ++void QOpenHarmonyPlatformWindow::handleWindowEvent(int event) ++{ ++ if (event == WINDOW_DESTROYED) { ++ QWindowSystemInterface::handleCloseEvent(window()); ++ QWindowSystemInterface::flushWindowSystemEvents(); ++ } else if (event == WINDOW_HIDDEN){ ++ lower(); ++ } else if (event == WINDOW_SHOWN) { ++ raise(); ++ } else if (event == WINDOW_ACTIVE) { ++ raise(); ++ } else if (event == WINDOW_INACTIVE) { ++ lower(); ++ } ++} ++ ++// openharmony ++#define STATUS_UNDEFINED 0 ++#define STATUS_FULL_SCREEN 1 ++#define STATUS_MAXIMIZE 2 ++#define STATUS_MINIMIZE 3 ++#define STATUS_FLOATING 4 ++#define STATUS_SPLIT_SCREEN 5 ++ ++void QOpenHarmonyPlatformWindow::handleWindowStatusEvent(int event) ++{ ++ switch (event) { ++ case STATUS_UNDEFINED: ++ return; ++ case STATUS_MINIMIZE: ++ handleWindowStateChange(Qt::WindowMinimized); ++ return; ++ case STATUS_MAXIMIZE: ++ handleWindowStateChange(Qt::WindowMaximized); ++ break; ++ case STATUS_FLOATING: ++ handleWindowStateChange(Qt::WindowNoState); ++ break; ++ case STATUS_FULL_SCREEN: ++ handleWindowStateChange(Qt::WindowMaximized); ++// handleWindowStateChange(Qt::WindowFullScreen); ++ break; ++ } ++} ++ ++QOpenHarmonyPlatformWindow *QOpenHarmonyPlatformWindow::get(QWindow *window) ++{ ++ auto it = std::find_if(allWindows.constBegin(), allWindows.constEnd(), [window](QOpenHarmonyPlatformWindow *pw){ ++ return pw->window() == window; ++ }); ++ return it == allWindows.constEnd() ? nullptr : *it; ++} ++ ++bool QOpenHarmonyPlatformWindow::detectionWindowHasFrame() ++{ ++ bool topLevel = isTopLevelWindow(); ++ Qt::WindowFlags flags = window()->flags(); ++ Qt::WindowType type = static_cast(int(flags) & Qt::WindowType_Mask); ++ bool dialog = false; ++ bool tool = false; ++ bool popup = false; ++ switch (type) { ++ case Qt::Dialog: ++ case Qt::Sheet: ++ dialog = true; ++ break; ++ case Qt::Drawer: ++ case Qt::Tool: ++ tool = true; ++ break; ++ case Qt::Popup: ++ popup = true; ++ break; ++ default: ++ break; ++ } ++ ++ qDebug() << this << window()->objectName() << "window is dialog: " << dialog << "is tool: " << tool << "is popup: " << popup ++ << "type: " << type << "windowFlags: " << flags; ++ ++ if ((flags & Qt::MSWindowsFixedSizeDialogHint)) ++ dialog = true; ++ if (popup || (type == Qt::ToolTip) || (type == Qt::SplashScreen)) { ++ return false; ++ } else if (topLevel) { ++ if (flags & Qt::FramelessWindowHint) ++ return false; // no border ++ else ++ return true; ++ } else { ++ return false; ++ } ++ return false; ++} ++ ++void QOpenHarmonyPlatformWindow::handleWindowStateChange(Qt::WindowStates state) ++{ ++ QWindowSystemInterface::handleWindowStateChanged(window(), state); ++ if (state & Qt::WindowMinimized) { ++ QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); ++ } ++} ++ +QT_END_NAMESPACE diff --git a/src/plugins/platforms/openharmony/qopenharmonyplatformwindow.h b/src/plugins/platforms/openharmony/qopenharmonyplatformwindow.h new file mode 100644 -index 0000000000..02a723f2c0 +index 0000000000..670241e35a --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyplatformwindow.h -@@ -0,0 +1,64 @@ +@@ -0,0 +1,105 @@ +#ifndef QOPENHARMONYPLATFORMWINDOW_H +#define QOPENHARMONYPLATFORMWINDOW_H +#include +#include ++#include +#include + +QT_BEGIN_NAMESPACE + +class QOpenHarmonyPlatformScreen; +class QOpenHarmonyPlatformBackingStore; ++class QOpenHarmonyPlatformWindow; ++class QOpenHarmonyPlatformWindowHelper; + -+class QOpenHarmonyPlatformWindow: public QPlatformWindow ++class QOpenHarmonyPlatformWindow : public QPlatformWindow +{ ++ +public: + explicit QOpenHarmonyPlatformWindow(QWindow *window); ++ ~QOpenHarmonyPlatformWindow(); ++ ++ void initialize() override; + + void lower() override; + void raise() override; + -+ void setVisible(bool visible) override; ++ void setOpacity(qreal level) override; + -+ void setWindowState(Qt::WindowStates state) override; -+ void setWindowFlags(Qt::WindowFlags flags) override; -+ Qt::WindowFlags windowFlags() const; ++ void setVisible(bool visible); + void setParent(const QPlatformWindow *window) override; + WId winId() const override { return m_windowId; } -+ ++ void setWId(WId nativeHandle); + QOpenHarmonyPlatformScreen *platformScreen() const; + -+ void propagateSizeHints() override; ++ void setWindowTitle(const QString &title); ++ QString windowTitle() const; ++ ++ void setWindowName(const QString &name); ++ QString windowName() const; ++ + void requestActivateWindow() override; -+ void updateStatusBarVisibility(); ++ + inline bool isRaster() const { + if (isForeignWindow()) + return false; @@ -10286,32 +14634,68 @@ index 0000000000..02a723f2c0 + void setBackingStore(QOpenHarmonyPlatformBackingStore *store) { m_backingStore = store; } + QOpenHarmonyPlatformBackingStore *backingStore() const { return m_backingStore; } + -+ virtual void repaint(const QRegion &) { } ++ virtual void repaint() { } ++ virtual void handleGeometryChange() {} + + void setGeometry(const QRect &rect) override; + -+ QRect geometry() const; ++ bool isTopLevelWindow() const; ++ ++ bool hasFrame() const; ++ ++ bool hasFocus() const; ++ ++ virtual QMargins frameMargins() const; ++ ++ QString parentWindowName() const; ++ ++ QOpenHarmonyPlatformWindowHelper *helper() const; + ++ void handleWindowEvent(int event); ++ void handleWindowStatusEvent(int event); ++ ++ static QOpenHarmonyPlatformWindow *get(QWindow *window); +protected: -+ Qt::WindowFlags m_windowFlags; -+ Qt::WindowStates m_windowState; ++ bool detectionWindowHasFrame(); ++ void handleWindowStateChange(Qt::WindowStates state); + + WId m_windowId; -+ QRect m_rect; -+ ++ QString m_title; ++ QString m_name; + QOpenHarmonyPlatformBackingStore *m_backingStore = nullptr; ++ QScopedPointer m_helper; ++ bool m_hasFrame; ++}; ++ ++class QOpenHarmonyPlatformWindowHelper : public QObject ++{ ++ Q_OBJECT ++public: ++ QOpenHarmonyPlatformWindowHelper(QOpenHarmonyPlatformWindow *window); ++public slots: ++ void handleGeometryChange() { m_window->handleGeometryChange(); } ++ void handleWindowEvent(int event) { m_window->handleWindowEvent(event); } ++ void handleWindowStatusEvent(int event) { m_window->handleWindowStatusEvent(event); } ++ void handleRepaintEvent() { m_window->repaint(); } ++private: ++ QOpenHarmonyPlatformWindow *m_window; +}; + +QT_END_NAMESPACE +#endif // QOPENHARMONYPLATFORMWINDOW_H diff --git a/src/plugins/platforms/openharmony/qopenharmonyxcomponent.cpp b/src/plugins/platforms/openharmony/qopenharmonyxcomponent.cpp new file mode 100644 -index 0000000000..0d43504bb1 +index 0000000000..698f9892ce --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyxcomponent.cpp -@@ -0,0 +1,257 @@ -+#include +@@ -0,0 +1,515 @@ +#include ++#include ++#include ++#include ++#include ++#include ++#include + +#include "qopenharmonyxcomponent.h" +#include "qopenharmonyplatforminputcontext.h" @@ -10322,6 +14706,50 @@ index 0000000000..0d43504bb1 +#include "qopenharmonylog.h" +#include "qopenharmonymain.h" + ++static OH_NativeXComponent_Callback *g_componentCallback = nullptr; ++static OH_NativeXComponent_MouseEvent_Callback *g_mouseEventCallback = nullptr; ++static int g_ref = 0; ++ ++class RepaintHelper : public QObject ++{ ++ Q_OBJECT ++public: ++ RepaintHelper(QOpenHarmonyXComponent * component): m_component(component) {} ++ virtual ~RepaintHelper() {} ++ ++ void update() { ++ QTimer::singleShot(10, this, [this]{ ++ QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); ++ }); ++ } ++ ++ bool event(QEvent *event); ++private: ++ QOpenHarmonyXComponent *m_component; ++}; ++#include "qopenharmonyxcomponent.moc" ++ ++bool RepaintHelper::event(QEvent *event) ++{ ++ if (event->type() == QEvent::UpdateRequest) { ++ m_component->repaint(); ++ return true; ++ } ++ return QObject::event(event); ++} ++ ++static inline Qt::KeyboardModifiers keyStateToModifiers(OH_NativeXComponent_KeyCode code) ++{ ++ Qt::KeyboardModifiers mods(Qt::NoModifier); ++ if ((code == KEY_CTRL_LEFT) || (code == KEY_CTRL_RIGHT)) ++ mods |= Qt::ControlModifier; ++ else if ((code == KEY_SHIFT_LEFT) || (code == KEY_SHIFT_RIGHT)) ++ mods |= Qt::ShiftModifier; ++ else if ((code == KEY_ALT_LEFT) || (code == KEY_ALT_RIGHT)) ++ mods |= Qt::AltModifier; ++ return mods; ++} ++ +QOpenHarmonyXComponent::QOpenHarmonyXComponent(OH_NativeXComponent *component) + : m_nativeComponent(component) + , m_window(nullptr) @@ -10332,8 +14760,9 @@ index 0000000000..0d43504bb1 + +QOpenHarmonyXComponent::~QOpenHarmonyXComponent() +{ -+ delete m_touchEventCallback; -+ delete m_mouseEventCallback; ++ if (m_helper != nullptr) ++ delete m_helper; ++ m_window = nullptr; +} + +void QOpenHarmonyXComponent::setWindow(QOpenHarmonyJsWindow *jsWindow) @@ -10341,12 +14770,12 @@ index 0000000000..0d43504bb1 + m_window = jsWindow; +} + -+void QOpenHarmonyXComponent::paint(const QImage &image) ++void QOpenHarmonyXComponent::paint(const QImage &image, const QRegion ®ion, const QPoint &offset) +{ -+ if (!m_surfaceCreated) -+ m_surfaceCreated = createEglSurface(); -+ if (m_surfaceCreated) -+ m_egl->drawImage(image); ++ m_image = image; ++ m_region = region; ++ m_offset = offset; ++ repaint(); +} + +QString QOpenHarmonyXComponent::name() const @@ -10359,9 +14788,8 @@ index 0000000000..0d43504bb1 + QOpenHarmonyJsWindow *jsWindow = qJsWindowManager->window(component); + if (jsWindow == nullptr) + return; -+ EGLNativeWindowType nativeWindow = reinterpret_cast(window); ++ OHNativeWindow *nativeWindow = static_cast(window); + jsWindow->setNativeWindow(nativeWindow); -+ qJsWindowManager->windowCreated(); +} + +void QOpenHarmonyXComponent::onSurfaceChanged(OH_NativeXComponent *component, void *window) @@ -10370,19 +14798,28 @@ index 0000000000..0d43504bb1 + QOpenHarmonyJsWindow *w = qJsWindowManager->window(component); + if (w == nullptr) + return; -+ w->updateSurfaceSize(); ++ w->handleSurfaceChanged(); +} + +void QOpenHarmonyXComponent::onSurfaceDestroyed(OH_NativeXComponent *component, void *window) -+{ -+ // QOpenHarmonyEGLCore::instance()->onSurfaceDestroyed(m_nativeWindow); ++{ ++ if (--g_ref == 0) { ++ if (g_mouseEventCallback == nullptr) { ++ delete g_mouseEventCallback; ++ g_mouseEventCallback = nullptr; ++ } ++ if (g_componentCallback) { ++ delete g_componentCallback; ++ g_componentCallback = nullptr; ++ } ++ } +} + +void QOpenHarmonyXComponent::dispatchKeyEvent(OH_NativeXComponent *component, void *window) -+{ ++{ + Q_UNUSED(window); + QOpenHarmonyPlatformInputContext *context = QOpenHarmonyPlatformInputContext::openHarmonyInputContext(); -+ if (Q_NULLPTR == context) ++ if (Q_NULLPTR == component || Q_NULLPTR == context) + return; + + OH_NativeXComponent_KeyEvent *keyEvent = Q_NULLPTR; @@ -10403,76 +14840,144 @@ index 0000000000..0d43504bb1 + int64_t timeStamp; + OH_NativeXComponent_GetKeyEventTimestamp(keyEvent, &timeStamp); /* 获取按键事件的时间戳 */ + -+#if 0 -+ qWarning() << "dispatchKeyEvent:::::" << action << code << sourceType << deviceId << timeStamp; -+ QOpenHarmonyPlatformInputContext *context = QOpenHarmonyPlatformInputContext::openHarmonyInputContext(); -+ if (Q_NULLPTR == context) -+ return; -+#endif -+ context->dispatchKeyEvent(code, action, sourceType, deviceId, timeStamp); -+ -+ //nativeModifier = Qt::NoModifier; -+ //QWindowSystemInterface::handleKeyEvent(Q_NULLPTR, ulong(timeStamp), t, k, modifiers); ++ OpenHarmonyKeyEvent event; ++ event.component = component; ++ event.code = code; ++ event.action = action; ++ event.stype = sourceType; ++ event.deviceId = deviceId; ++ event.timeStamp = timeStamp; ++ QMetaObject::invokeMethod(context, "handleKeyEvent", ++ Qt::QueuedConnection, Q_ARG(OpenHarmonyKeyEvent, event)); + + } else { -+ qWarning() << "Get Key Event Error"; ++ LOGW("Get Key Event Error"); + } +} + -+void QOpenHarmonyXComponent::dispatchTouchEvent(OH_NativeXComponent *component, void *window) ++static int getAction(int id, int index, OH_NativeXComponent_TouchEventType type, int x, int y, int pointsize, OH_NativeXComponent_HistoricalPoint* historicalPoints) +{ -+ QOpenHarmonyJsWindow *w = qJsWindowManager->window(component); -+ if (w == nullptr) ++ OH_NativeXComponent_TouchEventType action = type; ++ if (action == OH_NATIVEXCOMPONENT_MOVE) { ++ if (pointsize > 0) { ++ for (int h = 0; h < pointsize; ++h) { ++ if (id != historicalPoints[h].id) ++ continue; ++ if (historicalPoints[h].x != x || ++ historicalPoints[h].y != y) ++ return 1; ++ } ++ return 2; ++ } ++ return 1; ++ } ++ if (action == OH_NATIVEXCOMPONENT_DOWN) { ++ return 0; ++ } else if (action == OH_NATIVEXCOMPONENT_UP) { ++ return 3; ++ } ++ return 2; ++} ++ ++void QOpenHarmonyXComponent::dispatchTouchEvent(OH_NativeXComponent *component, void *window) ++{ ++ if (nullptr == component || nullptr == window) ++ return; ++ ++ // 触摸板会触发touch事件和mouse事件,导致2次鼠标事件 ++ if (QtOpenHarmony::isTouchPad()) + return; ++ + OH_NativeXComponent_TouchEvent touchEvent; + int32_t ret = OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent); + if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) + return; ++ + QOpenHarmonyPlatformInputContext *context = QOpenHarmonyPlatformInputContext::openHarmonyInputContext(); ++ if (nullptr == context) ++ return; ++ + context->touchBegin(); -+ QMargins margins = w->frameMargins(); -+ QRect g = w->geometry(); + -+ int left = g.left() + margins.left(); -+ int top = g.top() + margins.top(); ++ int32_t size; ++ OH_NativeXComponent_HistoricalPoint* historicalPoints = nullptr; ++ ret = OH_NativeXComponent_GetHistoricalPoints(component, window, &size, &historicalPoints); ++ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) ++ return; + + for (int i = 0; i < touchEvent.numPoints; i++) { + OH_NativeXComponent_TouchPoint &point = touchEvent.touchPoints[i]; -+ float x = point.x + left; -+ float y = point.y + top; -+ context->touchAdd(touchEvent.deviceId, touchEvent.type, point.force, x, y); ++ int action = getAction(point.id, i, touchEvent.type, point.x, point.y, size, historicalPoints); ++ context->touchAdd(point.id, action, point.force, point.screenX, point.screenY); + } -+ context->touchEnd(0); ++ OpenHarmonyTouchEvent event; ++ event.component = component; ++ event.id = 0; ++ QMetaObject::invokeMethod(QOpenHarmonyPlatformInputContext::openHarmonyInputContext(), ++ "touchEnd", Qt::QueuedConnection, Q_ARG(OpenHarmonyTouchEvent, event)); +} + +void QOpenHarmonyXComponent::dispatchMouseEvent(OH_NativeXComponent *component, void *window) +{ -+ QOpenHarmonyJsWindow *w = qJsWindowManager->window(component); -+ if (w == nullptr) ++ if (QOpenHarmonyPlatformInputContext::openHarmonyInputContext() == nullptr) + return; ++ + OH_NativeXComponent_MouseEvent mouseEvent; + int32_t ret = OH_NativeXComponent_GetMouseEvent(component, window, &mouseEvent); + if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) + return; -+ QMargins margins = w->frameMargins(); -+ QRect g = w->geometry(); + -+ int left = g.left() + margins.left(); -+ int top = g.top() + margins.top(); -+ -+ float x = mouseEvent.x + left; -+ float y = mouseEvent.y + top; + OH_NativeXComponent_MouseEventAction action = mouseEvent.action; ++ OH_NativeXComponent_MouseEventButton button = mouseEvent.button; ++ Qt::MouseButton qbtn = Qt::NoButton; ++ switch (button) { ++ case OH_NATIVEXCOMPONENT_LEFT_BUTTON: ++ qbtn = Qt::LeftButton; ++ break; ++ case OH_NATIVEXCOMPONENT_RIGHT_BUTTON: ++ qbtn = Qt::RightButton; ++ break; ++ case OH_NATIVEXCOMPONENT_MIDDLE_BUTTON: ++ qbtn = Qt::MiddleButton; ++ break; ++ case OH_NATIVEXCOMPONENT_BACK_BUTTON: ++ qbtn = Qt::BackButton; ++ break; ++ case OH_NATIVEXCOMPONENT_FORWARD_BUTTON: ++ qbtn = Qt::ForwardButton; ++ break; ++ default: ++ break; ++ } ++ ++// LOGI("%{public}s mouse event %{public}d, button is %{public}d screen point (%{public}f, %{public}f)", w->name().toLatin1().constData(), action, qbtn, mouseEvent.screenX, mouseEvent.screenY); ++ OpenHarmonyMouseEvent event; ++ event.x = mouseEvent.screenX; ++ event.y = mouseEvent.screenY; ++ event.component = component; ++ event.btn = qbtn; ++ + switch (action) { + case OH_NATIVEXCOMPONENT_MOUSE_RELEASE: -+ QOpenHarmonyPlatformInputContext::openHarmonyInputContext()->mouseRelease(x, y); ++ event.type = QEvent::MouseButtonRelease; + break; + case OH_NATIVEXCOMPONENT_MOUSE_PRESS: -+ QOpenHarmonyPlatformInputContext::openHarmonyInputContext()->mousePress(x, y); ++ event.type = QEvent::MouseButtonPress; + break; + case OH_NATIVEXCOMPONENT_MOUSE_MOVE: ++ event.type = QEvent::MouseMove; ++ break; ++ default: ++ event.type = QEvent::None; + break; + } ++ /* FIXME 移动端设备中和touch事件重复 ++ * 导致进入两次鼠标相关事件 */ ++ if (!QtOpenHarmony::isPhone() || QtOpenHarmony::isTouchPad()) ++ return; ++ ++ QMetaObject::invokeMethod(QOpenHarmonyPlatformInputContext::openHarmonyInputContext(), ++ "handleMouseEvent", Qt::QueuedConnection, Q_ARG(OpenHarmonyMouseEvent, event)); +} + +void QOpenHarmonyXComponent::dispatchHoverEvent(OH_NativeXComponent *component, bool isHover) @@ -10480,28 +14985,85 @@ index 0000000000..0d43504bb1 + +} + ++#if QT_CONFIG(wheelevent) ++void QOpenHarmonyXComponent::dispatchArkUIInputEvent(OH_NativeXComponent *component, ArkUI_UIInputEvent *event, ++ ArkUI_UIInputEvent_Type type) ++{ ++ if (nullptr == event || ++ QOpenHarmonyPlatformInputContext::openHarmonyInputContext() == nullptr) ++ return; ++ ++ float gx = OH_ArkUI_PointerEvent_GetDisplayX(event); ++ float gy = OH_ArkUI_PointerEvent_GetDisplayY(event); ++ ++ QPoint global(gx, gy); ++ ++ /* 获取UI输入事件发生的时间 */ ++ int64_t timestamp = OH_ArkUI_UIInputEvent_GetEventTime(event); ++ double vaxis = OH_ArkUI_AxisEvent_GetVerticalAxisValue(event); ++ double haxis = OH_ArkUI_AxisEvent_GetHorizontalAxisValue(event); ++ ++ // float tiltX = OH_ArkUI_PointerEvent_GetTiltX(event, 0); ++ // LOGE(<-----------arkUI event tiltX--:%{public}d", tiltX); ++ ++ // float tiltY = OH_ArkUI_PointerEvent_GetTiltY(event, 0); ++ // LOGE(<-----------arkUI event tiltY--:%{public}d", tiltY); ++ /* FIXME 获取是否有修饰键,按键判断可能无效,是否应该由系统提供按键查询接口? */ ++ Qt::KeyboardModifiers mods(Qt::NoModifier); ++ OH_NativeXComponent_KeyEvent *keyEvent = Q_NULLPTR; ++ if (OH_NativeXComponent_GetKeyEvent(component, &keyEvent) >= 0) { ++ OH_NativeXComponent_KeyCode code; ++ OH_NativeXComponent_GetKeyEventCode(keyEvent, &code); /* 获取按键事件的键码值 */ ++ mods = keyStateToModifiers(code); ++ } ++ QPoint pixelDelta(-haxis, -vaxis); /* NOTE 这里取相反的值,保持和Qt滑轮滚动方向一致 */ ++ OpenHarmonyWheelEvent ohevent; ++ ohevent.component = component; ++ ohevent.global = global; ++ ohevent.pixelDelta = pixelDelta; ++ ohevent.mods = mods; ++ QMetaObject::invokeMethod(QOpenHarmonyPlatformInputContext::openHarmonyInputContext(), ++ "wheelEvent", Qt::QueuedConnection, Q_ARG(OpenHarmonyWheelEvent, ohevent)); ++} ++#endif ++ +void QOpenHarmonyXComponent::initXComponent(OH_NativeXComponent *component) +{ + if (m_nativeComponent != nullptr) { -+ m_touchEventCallback = new OH_NativeXComponent_Callback; -+ m_touchEventCallback->OnSurfaceCreated = &QOpenHarmonyXComponent::onSurfaceCreated; -+ m_touchEventCallback->OnSurfaceChanged = &QOpenHarmonyXComponent::onSurfaceChanged; -+ m_touchEventCallback->OnSurfaceDestroyed = &QOpenHarmonyXComponent::onSurfaceDestroyed; -+ m_touchEventCallback->DispatchTouchEvent = &QOpenHarmonyXComponent::dispatchTouchEvent; -+ int32_t ret = OH_NativeXComponent_RegisterCallback(component, m_touchEventCallback); ++ /* 注册界面渲染事件回调 */ ++ if (g_componentCallback == nullptr) { ++ g_componentCallback = new OH_NativeXComponent_Callback; ++ g_componentCallback->OnSurfaceCreated = &QOpenHarmonyXComponent::onSurfaceCreated; ++ g_componentCallback->OnSurfaceChanged = &QOpenHarmonyXComponent::onSurfaceChanged; ++ g_componentCallback->OnSurfaceDestroyed = &QOpenHarmonyXComponent::onSurfaceDestroyed; ++ g_componentCallback->DispatchTouchEvent = &QOpenHarmonyXComponent::dispatchTouchEvent; ++ } ++ int32_t ret = OH_NativeXComponent_RegisterCallback(component, g_componentCallback); + if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + LOGI("set surface touch callback failed"); + } ++#if QT_CONFIG(wheelevent) ++ /* 注册UI输入事件回调,轴事件 */ ++ ret = OH_NativeXComponent_RegisterUIInputEventCallback(component, &QOpenHarmonyXComponent::dispatchArkUIInputEvent, ++ ARKUI_UIINPUTEVENT_TYPE_AXIS); + -+ m_mouseEventCallback = new OH_NativeXComponent_MouseEvent_Callback; -+ m_mouseEventCallback->DispatchMouseEvent = &QOpenHarmonyXComponent::dispatchMouseEvent; -+ m_mouseEventCallback->DispatchHoverEvent = &QOpenHarmonyXComponent::dispatchHoverEvent; ++ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { ++ LOGI("set axis event callback failed"); ++ } ++#endif ++ /* 注册界鼠标事件回调 */ ++ if (g_mouseEventCallback == nullptr) { ++ g_mouseEventCallback = new OH_NativeXComponent_MouseEvent_Callback; ++ g_mouseEventCallback->DispatchMouseEvent = &QOpenHarmonyXComponent::dispatchMouseEvent; ++ g_mouseEventCallback->DispatchHoverEvent = &QOpenHarmonyXComponent::dispatchHoverEvent; ++ } ++ ret = OH_NativeXComponent_RegisterMouseEventCallback(component, g_mouseEventCallback); + if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + LOGI("set surface mouse callback failed"); + } + ++ /* 注册键盘事件回调 */ + ret = OH_NativeXComponent_RegisterKeyEventCallback(component, &QOpenHarmonyXComponent::dispatchKeyEvent); -+ qWarning() << "OH_NativeXComponent_RegisterKeyEventCallback ret:" << ret; + if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) + LOGI("set surface keybord callback failed"); + @@ -10514,19 +15076,92 @@ index 0000000000..0d43504bb1 + } + LOGI("get xcomponent succeded %{public}s", id); + m_name = QString::fromUtf8(id); ++ g_ref++; + } +} + -+QImage QOpenHarmonyXComponent::image() const ++void QOpenHarmonyXComponent::repaint() +{ -+ QImage image = m_egl->image(); -+ if (image.isNull()) { -+ QImage::Format format = QImage::Format_RGBA8888_Premultiplied; -+ image = QImage(m_size.width(), m_size.height(), format); -+ image.fill(Qt::white); -+ return image; ++ QSize ss = surfaceSize(); ++ QSize qtSize = m_image.size(); ++ QSize diff = ss - qtSize; ++ bool needToRepaint = (qAbs(diff.width()) > 2) || (qAbs(diff.height()) > 2); ++ if (needToRepaint) { ++ qWarning() << "the qt window size is not equal xcomponent size: " << qtSize << ss; ++ if (m_helper == nullptr) ++ m_helper = new RepaintHelper(this); ++ m_helper->update(); ++ return; ++ } ++ ++ if (m_window == nullptr) ++ return; ++ OHNativeWindow *window = m_window->nativeWindow(); ++ int fenceFd = 0; ++ NativeWindowBuffer *buffer = nullptr; ++ int ret = OH_NativeWindow_NativeWindowRequestBuffer(window, &buffer, &fenceFd); ++ if (ret != 0) { ++ qWarning() << "request buffer for native window failed"; ++ return; ++ } ++ BufferHandle *bufferHandle = OH_NativeWindow_GetBufferHandleFromNative(buffer); ++ if (bufferHandle == nullptr) { ++ qWarning() << "request buffer handle from native window failed"; ++ return; ++ } ++ ++ uchar *mappedAddress = ++ static_cast(mmap(bufferHandle->virAddr, bufferHandle->size, PROT_READ | PROT_WRITE, MAP_SHARED, bufferHandle->fd, 0)); ++ if (mappedAddress == MAP_FAILED) { ++ qWarning() << "mmap failed"; ++ return; + } -+ return image; ++ ++#if 0 ++ int count = m_region.rectCount(); ++ Region *dirtyRegion = new Region; ++ dirtyRegion->rects = new Region::Rect[count]; ++ dirtyRegion->rectNumber = count; ++ int index = 0; ++ for (auto it = m_region.begin(); it != m_region.end(); ++it) { ++ QRect dirtyRect = *it; ++ dirtyRegion->rects[index].x = dirtyRect.x(); ++ dirtyRegion->rects[index].y = dirtyRect.y(); ++ dirtyRegion->rects[index].w = dirtyRect.width(); ++ dirtyRegion->rects[index].h = dirtyRect.height(); ++ int width = qMin(bufferHandle->width, dirtyRect.right()); ++ int height = qMin(bufferHandle->height, dirtyRect.bottom()); ++ index++; ++ int bytesPL = (width - dirtyRect.left()) * 4/*m_image.bytesPerLine()*/; ++ for (int h = dirtyRect.top(); h < height; ++h) { ++ uchar *pixel = mappedAddress + bufferHandle->stride * h + dirtyRect.left() * 4; ++ uchar *value = m_image.scanLine(h) + dirtyRect.left() * 4; ++ memcpy(pixel, value, bytesPL); ++ } ++ } ++ OH_NativeWindow_NativeWindowFlushBuffer(window, buffer, fenceFd, *dirtyRegion); ++ ++#else ++ int width = qMin(bufferHandle->width, m_image.width()); ++ int height = qMin(bufferHandle->height, m_image.height()); ++ int bytesPL = width * 4/*m_image.bytesPerLine()*/; ++ for (int h = 0; h < height; ++h) { ++ uchar *pixel = mappedAddress + bufferHandle->stride * h; ++ uchar *value = m_image.scanLine(h); ++ memcpy(pixel, value, bytesPL); ++ } ++ ++ Region reg{nullptr, 0}; ++ OH_NativeWindow_NativeWindowFlushBuffer(window, buffer, fenceFd, reg); ++#endif ++ int result = munmap(mappedAddress, bufferHandle->size); ++ if (result == -1) { ++ qWarning() << "munmap failed!"; ++ } ++#if 0 ++ delete[] dirtyRegion->rects; ++ delete dirtyRegion; ++#endif +} + +OH_NativeXComponent *QOpenHarmonyXComponent::nativeComponent() const @@ -10534,32 +15169,34 @@ index 0000000000..0d43504bb1 + return m_nativeComponent; +} + -+bool QOpenHarmonyXComponent::createEglSurface() ++QSize QOpenHarmonyXComponent::surfaceSize() const +{ -+ if (m_window->nativeWindow() == 0) -+ return false; -+ m_egl->createSurface(m_window->nativeWindow(), m_size.width(), m_size.height()); -+ return true; -+} ++ if (m_window == nullptr || m_window->nativeWindow() == 0 || m_nativeComponent == nullptr) ++ return QSize(); + -+void QOpenHarmonyXComponent::updateEglSurface() -+{ -+ m_egl->updateSurfaceSize(m_size.width(), m_size.height()); ++ uint64_t w; ++ uint64_t h; ++ if (void *nativeW = reinterpret_cast(m_window->nativeWindow())) { ++ int32_t ret = OH_NativeXComponent_GetXComponentSize(m_nativeComponent, nativeW, &w, &h); ++ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { ++ LOGE("Get surface size failed"); ++ return QSize(); ++ } ++ return QSize(w, h); ++ } ++ return QSize(); +} + -+void QOpenHarmonyXComponent::updateSize(int w, int h) ++QSize QOpenHarmonyXComponent::qtWindowSize() const +{ -+ m_size.setWidth(w); -+ m_size.setHeight(h); -+ updateEglSurface(); -+ ++ if (m_window == nullptr) ++ return QSize(); ++ return m_window->qtGeometry().size(); +} + -+EGLSurface QOpenHarmonyXComponent::eglSurface() ++EGLSurface QOpenHarmonyXComponent::eglSurface(EGLNativeWindowType window) +{ -+ if (!m_surfaceCreated) -+ m_surfaceCreated = createEglSurface(); -+ return m_egl->eglSurface(); ++ return m_egl->eglSurface(window); +} + +void QOpenHarmonyXComponent::setNativeXComponent(OH_NativeXComponent *component) @@ -10569,10 +15206,10 @@ index 0000000000..0d43504bb1 +} diff --git a/src/plugins/platforms/openharmony/qopenharmonyxcomponent.h b/src/plugins/platforms/openharmony/qopenharmonyxcomponent.h new file mode 100644 -index 0000000000..f7edc8475e +index 0000000000..8c5e196f12 --- /dev/null +++ b/src/plugins/platforms/openharmony/qopenharmonyxcomponent.h -@@ -0,0 +1,67 @@ +@@ -0,0 +1,77 @@ +#ifndef QOPENHARMONYXCOMPONENT_H +#define QOPENHARMONYXCOMPONENT_H + @@ -10582,11 +15219,16 @@ index 0000000000..f7edc8475e +#include +#include + ++#if QT_CONFIG(wheelevent) ++#include ++#endif ++ +class QOpenHarmonyEGLCore; +struct OH_NativeXComponent; +class QOpenHarmonyJsWindow; +struct OH_NativeXComponent_Callback; +struct OH_NativeXComponent_MouseEvent_Callback; ++class RepaintHelper; + +class QOpenHarmonyXComponent +{ @@ -10596,22 +15238,18 @@ index 0000000000..f7edc8475e + + void setWindow(QOpenHarmonyJsWindow *jsWindow); + -+ void paint(const QImage &image); ++ void paint(const QImage &image, const QRegion ®ion, const QPoint &offset); + + QString name() const; + -+ EGLSurface eglSurface(); ++ EGLSurface eglSurface(EGLNativeWindowType window); + + void setNativeXComponent(OH_NativeXComponent *component); + OH_NativeXComponent *nativeComponent() const; + -+ QImage image() const; -+ -+ void updateSize(int w, int h); ++ QSize surfaceSize() const; ++ QSize qtWindowSize() const; +private: -+ bool createEglSurface(); -+ -+ void updateEglSurface(); + + static void onSurfaceCreated(OH_NativeXComponent* component, void* window); + @@ -10627,17 +15265,26 @@ index 0000000000..f7edc8475e + + static void dispatchHoverEvent(OH_NativeXComponent* component, bool isHover); + ++#if QT_CONFIG(wheelevent) ++ static void dispatchArkUIInputEvent(OH_NativeXComponent* component, ArkUI_UIInputEvent* event, ++ ArkUI_UIInputEvent_Type type); ++#endif ++ +private: + void initXComponent(OH_NativeXComponent *component); ++ void repaint(); +private: ++ friend class QOpenHarmonyEGLCore; + OH_NativeXComponent *m_nativeComponent = nullptr; -+ OH_NativeXComponent_Callback *m_touchEventCallback = nullptr; -+ OH_NativeXComponent_MouseEvent_Callback *m_mouseEventCallback = nullptr; + QScopedPointer m_egl; + QOpenHarmonyJsWindow *m_window = nullptr; -+ QSize m_size; -+ bool m_surfaceCreated = false; + QString m_name; ++ QImage m_image; ++ QRegion m_region; ++ QPoint m_offset; ++ friend class RepaintHelper; ++ RepaintHelper *m_helper = nullptr; ++ bool m_canDraw = false; +}; +#endif // QOPENHARMONYXCOMPONENT_H diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro @@ -10657,6 +15304,175 @@ index 23f838a7fe..f13ef8b9cd 100644 qtConfig(xcb) { SUBDIRS += xcb } +diff --git a/src/plugins/styles/harmonyos/harmonyos.pro b/src/plugins/styles/harmonyos/harmonyos.pro +new file mode 100644 +index 0000000000..b7abe8a40b +--- /dev/null ++++ b/src/plugins/styles/harmonyos/harmonyos.pro +@@ -0,0 +1,18 @@ ++TARGET = qharmonyosstyle ++ ++QT += widgets-private ++ ++SOURCES += \ ++ main.cpp \ ++ qharmonyosstyle.cpp ++ ++HEADERS += \ ++ qharmonyosstyle_p.h ++ ++ ++DISTFILES += \ ++ harmonyosstyle.json ++ ++PLUGIN_TYPE = styles ++PLUGIN_CLASS_NAME = QHarmonyOSStylePlugin ++load(qt_plugin) +diff --git a/src/plugins/styles/harmonyos/harmonyosstyle.json b/src/plugins/styles/harmonyos/harmonyosstyle.json +new file mode 100644 +index 0000000000..18d97293f2 +--- /dev/null ++++ b/src/plugins/styles/harmonyos/harmonyosstyle.json +@@ -0,0 +1,3 @@ ++{ ++ "Keys": [ "harmonyos" ] ++} +diff --git a/src/plugins/styles/harmonyos/main.cpp b/src/plugins/styles/harmonyos/main.cpp +new file mode 100644 +index 0000000000..65d71c3299 +--- /dev/null ++++ b/src/plugins/styles/harmonyos/main.cpp +@@ -0,0 +1,26 @@ ++#include ++ ++#include "qharmonyosstyle_p.h" ++ ++QT_BEGIN_NAMESPACE ++ ++class QHarmonyOSStylePlugin : public QStylePlugin ++{ ++ Q_OBJECT ++ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "harmonyosstyle.json") ++public: ++ QStyle *create(const QString &key); ++}; ++ ++QStyle *QHarmonyOSStylePlugin::create(const QString &key) ++{ ++ if (key.compare(QLatin1String("harmonyos"), Qt::CaseInsensitive) == 0) ++ return new QHarmonyOSStyle(); ++ ++ return Q_NULLPTR; ++} ++ ++QT_END_NAMESPACE ++ ++#include "main.moc" ++ +diff --git a/src/plugins/styles/harmonyos/qharmonyosstyle.cpp b/src/plugins/styles/harmonyos/qharmonyosstyle.cpp +new file mode 100644 +index 0000000000..172d0fd314 +--- /dev/null ++++ b/src/plugins/styles/harmonyos/qharmonyosstyle.cpp +@@ -0,0 +1,54 @@ ++#include ++#include ++#include ++#include ++ ++#include "qharmonyosstyle_p.h" ++ ++QT_BEGIN_NAMESPACE ++ ++QHarmonyOSStyle::QHarmonyOSStyle() : QCommonStyle() ++{ ++} ++ ++QHarmonyOSStyle::~QHarmonyOSStyle() ++{ ++ ++} ++ ++void QHarmonyOSStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p, const QWidget *w) const ++{ ++ switch (pe) { ++ case PE_FrameFocusRect: ++ break; ++ case PE_FrameButtonBevel: ++ break; ++ case PE_PanelButtonCommand: { ++ p->setRenderHint(QPainter::Antialiasing); ++ QBrush oldBrush = p->brush(); ++ if (opt->state.testFlag(QStyle::State_MouseOver)) ++ p->setBrush(QColor("#007DFF")); ++ ++ p->drawRoundedRect(opt->rect.adjusted(1, 1, -1, -1), 14, 14); ++ p->setBrush(oldBrush); ++ } ++ break; ++ case PE_FrameDefaultButton: ++ break; ++ default: ++ QCommonStyle::drawPrimitive(pe, opt, p, w); ++ break; ++ } ++} ++ ++void QHarmonyOSStyle::drawControl(ControlElement element, const QStyleOption *opt, QPainter *p, const QWidget *w) const ++{ ++ QCommonStyle::drawControl(element, opt, p, w); ++} ++ ++int QHarmonyOSStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWidget *widget) const ++{ ++ return QCommonStyle::pixelMetric(m, opt, widget); ++} ++ ++QT_END_NAMESPACE +diff --git a/src/plugins/styles/harmonyos/qharmonyosstyle_p.h b/src/plugins/styles/harmonyos/qharmonyosstyle_p.h +new file mode 100644 +index 0000000000..e81d9490cd +--- /dev/null ++++ b/src/plugins/styles/harmonyos/qharmonyosstyle_p.h +@@ -0,0 +1,38 @@ ++#ifndef QHARMONYOSSTYLE_P_H ++#define QHARMONYOSSTYLE_P_H ++ ++// ++// W A R N I N G ++// ------------- ++// ++// This file is not part of the Qt API. It exists for the convenience ++// of qstylefactory.cpp. This header may change from version to version ++// without notice, or even be removed. ++// ++// We mean it. ++// ++#include ++ ++QT_BEGIN_NAMESPACE ++ ++class Q_WIDGETS_EXPORT QHarmonyOSStyle : public QCommonStyle ++{ ++ Q_OBJECT ++ ++public: ++ QHarmonyOSStyle(); ++ virtual ~QHarmonyOSStyle(); ++ ++ void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, ++ QPainter *p, const QWidget *w) const override; ++ ++ void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p, ++ const QWidget *w = nullptr) const override; ++ ++ int pixelMetric(PixelMetric m, const QStyleOption *opt = nullptr, ++ const QWidget *widget = nullptr) const override; ++}; ++ ++QT_END_NAMESPACE ++ ++#endif // QHARMONYOSSTYLE_P_H diff --git a/src/plugins/styles/styles.pro b/src/plugins/styles/styles.pro index 542ad1329a..fb8d978d83 100644 --- a/src/plugins/styles/styles.pro @@ -10757,10 +15573,20 @@ index 0a68f082a2..b3e6991a50 100644 "label": "QStyleSheetStyle", "purpose": "Provides a widget style which is configurable via CSS.", diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp -index 711effefe2..f6e2ff564a 100644 +index 711effefe2..23832aa70a 100644 --- a/src/widgets/dialogs/qfiledialog.cpp +++ b/src/widgets/dialogs/qfiledialog.cpp -@@ -1311,11 +1311,22 @@ QStringList QFileDialog::selectedFiles() const +@@ -886,7 +886,9 @@ void QFileDialog::setVisible(bool visible) + if (d->setNativeDialogVisible(visible)){ + // Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below + // updates the state correctly, but skips showing the non-native version: ++#ifndef Q_OS_OPENHARMONY + setAttribute(Qt::WA_DontShowOnScreen); ++#endif + #if QT_CONFIG(fscompleter) + // So the completer doesn't try to complete and therefore show a popup + if (!d->nativeDialogInUse) +@@ -1311,6 +1313,9 @@ QStringList QFileDialog::selectedFiles() const QStringList files; const QList userSelectedFiles = d->userSelectedFiles(); files.reserve(userSelectedFiles.size()); @@ -10768,59 +15594,51 @@ index 711effefe2..f6e2ff564a 100644 + + QString fileName; for (const QUrl &file : userSelectedFiles) { -+#ifdef Q_OS_OPENHARMONY -+ fileName = file.toLocalFile(); -+ if (!fileName.isEmpty()) { -+ fileName.prepend("datashare://"); -+ } -+ files.append(fileName); -+#else if (file.isLocalFile() || file.isEmpty()) files.append(file.toLocalFile()); - else - files.append(file.toString()); -+#endif - } - if (files.isEmpty() && d->usingWidgets()) { - const FileMode fm = fileMode(); -@@ -2218,10 +2229,18 @@ QString QFileDialog::getOpenFileName(QWidget *parent, - const QStringList schemes = QStringList(QStringLiteral("file")); - const QUrl selectedUrl = getOpenFileUrl(parent, caption, QUrl::fromLocalFile(dir), filter, - selectedFilter, options, schemes); +@@ -1354,6 +1359,22 @@ QList QFileDialog::selectedUrls() const + */ + QStringList qt_make_filter_list(const QString &filter) + { +#ifdef Q_OS_OPENHARMONY -+ QString fileName = selectedUrl.toLocalFile(); -+ if (!fileName.isEmpty()) { -+ fileName.prepend("datashare://"); ++ QStringList allExtensions; ++ if (filter == QFileDialogOptions::defaultNameFilterString()){ ++ allExtensions << "*"; ++ } else { ++ int pos = 0; ++ QRegExp regExp("\\*\\.\\w+"); ++ while ((pos = regExp.indexIn(filter, pos)) != -1) { ++ QString matchedExtension = regExp.cap(0); /* 获取整个匹配到的字符串 */ ++ QString extension = matchedExtension.mid(1); /* 去掉开头的星号 */ ++ allExtensions << extension; ++ pos += regExp.matchedLength(); ++ } + } -+ return fileName; ++ return allExtensions; +#else - if (selectedUrl.isLocalFile() || selectedUrl.isEmpty()) - return selectedUrl.toLocalFile(); - else - return selectedUrl.toString(); + QString f(filter); + + if (f.isEmpty()) +@@ -1369,6 +1390,7 @@ QStringList qt_make_filter_list(const QString &filter) + } + + return f.split(sep); +#endif } /*! -@@ -2581,10 +2600,18 @@ QString QFileDialog::getSaveFileName(QWidget *parent, - const QStringList schemes = QStringList(QStringLiteral("file")); - const QUrl selectedUrl = getSaveFileUrl(parent, caption, QUrl::fromLocalFile(dir), filter, +@@ -2219,7 +2241,11 @@ QString QFileDialog::getOpenFileName(QWidget *parent, + const QUrl selectedUrl = getOpenFileUrl(parent, caption, QUrl::fromLocalFile(dir), filter, selectedFilter, options, schemes); + if (selectedUrl.isLocalFile() || selectedUrl.isEmpty()) +#ifdef Q_OS_OPENHARMONY -+ QString fileName = selectedUrl.toLocalFile(); -+ if (!fileName.isEmpty()) { -+ fileName.prepend("datashare://"); -+ } -+ return fileName; ++ return selectedUrl.path(); +#else - if (selectedUrl.isLocalFile() || selectedUrl.isEmpty()) return selectedUrl.toLocalFile(); ++#endif else return selectedUrl.toString(); -+#endif } - - /*! diff --git a/src/widgets/widgets/qtextbrowser.cpp b/src/widgets/widgets/qtextbrowser.cpp index 122a8529e8..138a6a5b4e 100644 --- a/src/widgets/widgets/qtextbrowser.cpp diff --git a/patch/v5.15.12/qtdeclarative.patch b/patch/v5.15.12/qtdeclarative.patch index f4e721f898afe5d25832f63c28ad1dba55486ab5..d1ec8a2992f4858871f98a9969d5648741f14822 100644 --- a/patch/v5.15.12/qtdeclarative.patch +++ b/patch/v5.15.12/qtdeclarative.patch @@ -916,7 +916,7 @@ index e7263d1850..1409bcb993 100644 } diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp -index 40300b1fe3..4aff260ac3 100644 +index 40300b1fe3..bfd8d9f1e1 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -466,7 +466,7 @@ void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data) @@ -938,6 +938,15 @@ index 40300b1fe3..4aff260ac3 100644 QString typeName = QString::fromUtf8(type.typeName); bool fileImport = false; if (*(type.uri) == '\0') +@@ -670,7 +669,7 @@ public: + bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePath, + const QString &uri, const QString &typeNamespace, int vmaj, + QList *errors) +-{ ++{ + if (!typeNamespace.isEmpty() && typeNamespace != uri) { + // This is an 'identified' module + // The namespace for type registrations must match the URI for locating the module diff --git a/src/qml/qml/qqmlplatform.cpp b/src/qml/qml/qqmlplatform.cpp index dcd7ca2d46..a03b569641 100644 --- a/src/qml/qml/qqmlplatform.cpp @@ -1030,6 +1039,22 @@ index 01aba47a9a..96bce3548a 100644 if (isResource) { // qrc resource QFileInfo fileInfo(path); +diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp +index 1001b34c3c..46b37a4d3b 100644 +--- a/src/quick/items/qquicklistview.cpp ++++ b/src/quick/items/qquicklistview.cpp +@@ -3920,9 +3920,11 @@ bool QQuickListViewPrivate::wantsPointerEvent(const QEvent *event) + QPointF pos; + // TODO switch not needed in Qt 6: use points().first().position() + switch (event->type()) { ++#if QT_CONFIG(wheelevent) + case QEvent::Wheel: + pos = static_cast(event)->position(); + break; ++#endif + case QEvent::MouseButtonPress: + pos = static_cast(event)->localPos(); + break; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 8ab69603ad..119bcdfef0 100644 --- a/src/quick/items/qquickwindow.cpp diff --git a/patch/v5.15.12/qtmultimedia.patch b/patch/v5.15.12/qtmultimedia.patch index acb598eb28d42ed9c92e3906e0472bf56ed93269..9ffaf87ad3a9eeb5584c372820264b3b0cde4666 100644 --- a/patch/v5.15.12/qtmultimedia.patch +++ b/patch/v5.15.12/qtmultimedia.patch @@ -253,19 +253,19 @@ index 000000000..7a3fc0e63 + } +} + -diff --git a/src/openharmony/native/QtMultiMedia/JsMultiMediaModule.ts b/src/openharmony/native/QtMultiMedia/JsMultiMediaModule.ts +diff --git a/src/openharmony/native/QtMultiMedia/JsMultiMediaModule.ets b/src/openharmony/native/QtMultiMedia/JsMultiMediaModule.ets new file mode 100644 -index 000000000..cd08a2070 +index 000000000..b8c8618cd --- /dev/null -+++ b/src/openharmony/native/QtMultiMedia/JsMultiMediaModule.ts -@@ -0,0 +1,44 @@ ++++ b/src/openharmony/native/QtMultiMedia/JsMultiMediaModule.ets +@@ -0,0 +1,33 @@ +import { JsQtModule, ObjectBuilder } from '../QtCore/JsQtModule'; +import JsDataStore from '../QtCore/JsDataStore'; -+import JsLogger from '../QtCore/JsLogger'; +import { JsAudioManager } from './JsAudioManager' +import { JsMediaRecorder } from './JsMediaRecorder' +import { JsMultimediaUtils } from './JsMultimediaUtils' +import { JsCameraManager } from './JsCameraManager' ++import QtMultimedia from 'libplugins_mediaservice_qtmedia_openharmony.so' + +class JsMultiMediaModule extends JsQtModule { + @@ -283,22 +283,11 @@ index 000000000..cd08a2070 + this.moduleJsObjects.set("JsCameraManager", new ObjectBuilder<[]>(() =>{ + return new JsCameraManager(); + })); -+ this.loadQtModule(); + } + + async loadQtModule(): Promise { -+ let qtMajorVersion = JsDataStore.getQtMajorVersion(); -+ let QtMultimediaModule: any = null; -+ if (qtMajorVersion == 5) -+ QtMultimediaModule = await import ("libQt5Multimedia.so"); -+ else if (qtMajorVersion == 6) -+ QtMultimediaModule = await import ("libQt5Multimedia.so"); -+ if (QtMultimediaModule == null) { -+ JsLogger.fatal("Cannot load QtMultimedia module"); -+ return; -+ } -+ let QtMultimedia = QtMultimediaModule.default; + JsDataStore.addQtNativeModule("QtMultimedia", QtMultimedia); ++ JsDataStore.getQtNativeModule("QtMultimedia").setResourceManager(JsDataStore.getResourceManager()); + } +} + @@ -332,15 +321,16 @@ index 000000000..3adf5fe55 +} diff --git a/src/openharmony/openharmony.pro b/src/openharmony/openharmony.pro new file mode 100644 -index 000000000..6f97b7df9 +index 000000000..597a116d0 --- /dev/null +++ b/src/openharmony/openharmony.pro -@@ -0,0 +1,9 @@ +@@ -0,0 +1,10 @@ +TEMPLATE = aux + +CONFIG -= qt + -+templates.files += $$files($$PWD/native/QtMultiMedia/*, true) ++templates.files += $$files($$PWD/native/QtMultiMedia/*.ts, true) ++templates.files += $$files($$PWD/native/QtMultiMedia/*.ets, true) +templates.path = $$[QT_INSTALL_PREFIX]/openharmony/qtmultimedia +templates.base = $$PWD + @@ -1180,171 +1170,1517 @@ index 000000000..9f188ebfb + "Keys": ["openharmonymultimedia"], + "Services": ["org.qt-project.qt.camera", "org.qt-project.qt.mediaplayer", "org.qt-project.qt.audiosource"] +} -diff --git a/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.cpp b/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.cpp +diff --git a/src/plugins/openharmony/src/player/player.pri b/src/plugins/openharmony/src/player/player.pri new file mode 100644 -index 000000000..f7cc1314d +index 000000000..0abf1d2b5 --- /dev/null -+++ b/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.cpp -@@ -0,0 +1,101 @@ -+#include ++++ b/src/plugins/openharmony/src/player/player.pri +@@ -0,0 +1,15 @@ ++INCLUDEPATH += $$PWD + -+#include "qcamera.h" -+#include "qmediaserviceproviderplugin.h" -+#include "qopenharmonymediaserviceplugin.h" -+#include "mediacapture/qopenharmonycamerasession.h" -+#include "mediacapture/qopenharmonycaptureservice.h" -+#include "mediacapture/qopenharmonycamerainfocontrol.h" ++qtHaveModule(widgets): QT += widgets ++ ++HEADERS += \ ++ $$PWD/qopenharmonyplayerservice.h \ ++ $$PWD/qopenharmonyplayercontrol.h \ ++ $$PWD/qopenharmonywindowcontrol.h \ ++ $$PWD/qopenharmonyplayer.h ++ ++SOURCES += \ ++ $$PWD/qopenharmonyplayerservice.cpp \ ++ $$PWD/qopenharmonyplayercontrol.cpp \ ++ $$PWD/qopenharmonywindowcontrol.cpp \ ++ $$PWD/qopenharmonyplayer.cpp +diff --git a/src/plugins/openharmony/src/player/qopenharmonyplayer.cpp b/src/plugins/openharmony/src/player/qopenharmonyplayer.cpp +new file mode 100644 +index 000000000..21a50b78d +--- /dev/null ++++ b/src/plugins/openharmony/src/player/qopenharmonyplayer.cpp +@@ -0,0 +1,378 @@ ++#include "qopenharmonyplayer.h" ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++typedef QHash MediaPlayers; ++Q_GLOBAL_STATIC(MediaPlayers, mediaPlayers) ++Q_GLOBAL_STATIC(QReadWriteLock, rwLock) + +QT_BEGIN_NAMESPACE + -+Q_LOGGING_CATEGORY(qtOPenHaronyMediaPlugin, "qt.multimedia.plugins.openharmony") ++NativeResourceManager *QOpenHarmonyPlayer::m_resourceManager = nullptr; + -+QOPenHarmonyMediaServicePlugin::QOPenHarmonyMediaServicePlugin() ++QOpenHarmonyPlayer::QOpenHarmonyPlayer() ++ : QObject() +{ ++ QWriteLocker locker(rwLock); ++ m_player = OH_AVPlayer_Create(); ++ if (m_player != nullptr) { ++ m_playerCallback.onInfo = &QOpenHarmonyPlayer::onPlayerInfo; ++ m_playerCallback.onError = &QOpenHarmonyPlayer::onPlayerError; ++ OH_AVPlayer_SetPlayerCallback(m_player, m_playerCallback); ++ mediaPlayers->insert(m_player, this); ++ } +} + -+QOPenHarmonyMediaServicePlugin::~QOPenHarmonyMediaServicePlugin() ++QOpenHarmonyPlayer::~QOpenHarmonyPlayer() +{ ++ if (m_player != nullptr) { ++ QWriteLocker locker(rwLock); ++ mediaPlayers->remove(m_player); ++ OH_AVPlayer_Release(m_player); ++ } +} + -+QMediaService *QOPenHarmonyMediaServicePlugin::create(const QString &key) ++void QOpenHarmonyPlayer::release() +{ -+ if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) -+ return nullptr; ++ OH_AVPlayer_Release(m_player); ++} + -+ if (key == QLatin1String(Q_MEDIASERVICE_CAMERA) -+ || key == QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE)) { -+ return new QOPenHarmonyCaptureService(key); -+ } ++void QOpenHarmonyPlayer::reset() ++{ ++ OH_AVPlayer_Reset(m_player); ++} + -+ qCWarning(qtOPenHaronyMediaPlugin) << "OPenHarony service plugin: unsupported key:" << key; ++int QOpenHarmonyPlayer::getCurrentPosition() ++{ ++ if ((m_state == AV_PLAYING) ++ || (m_state == AV_PAUSED) ++ || (m_state == AV_COMPLETED)) { ++ int time = 0; ++ OH_AVPlayer_GetCurrentTime(m_player, &time); ++ return time; ++ } + return 0; +} + -+void QOPenHarmonyMediaServicePlugin::release(QMediaService *service) ++int QOpenHarmonyPlayer::getDuration() +{ -+ delete service; ++ if (m_state < AV_PREPARED) ++ return 0; ++ int duration = 0; ++ OH_AVPlayer_GetDuration(m_player, &duration); ++ return duration; +} + -+QMediaServiceProviderHint::Features QOPenHarmonyMediaServicePlugin::supportedFeatures(const QByteArray &service) const ++bool QOpenHarmonyPlayer::isPlaying() +{ -+ if (service == Q_MEDIASERVICE_MEDIAPLAYER) -+ return QMediaServiceProviderHint::VideoSurface; ++ return m_state == AV_PLAYING; ++} + -+ if (service == Q_MEDIASERVICE_CAMERA) -+ return QMediaServiceProviderHint::VideoSurface | QMediaServiceProviderHint::RecordingSupport; ++int QOpenHarmonyPlayer::volume() ++{ ++ return m_volume * 100; ++} + -+ if (service == Q_MEDIASERVICE_AUDIOSOURCE) -+ return QMediaServiceProviderHint::RecordingSupport; ++bool QOpenHarmonyPlayer::isMuted() ++{ ++ return qFuzzyCompare(m_volume, 0.0); ++} + -+ return QMediaServiceProviderHint::Features(); ++qreal QOpenHarmonyPlayer::playbackRate() ++{ ++ qreal rate(1.0); ++ ++ AVPlaybackSpeed speed; ++ OH_AVPlayer_GetPlaybackSpeed(m_player, &speed); ++ switch (speed) { ++ case AV_SPEED_FORWARD_0_75_X: ++ rate = 0.75; ++ break; ++ case AV_SPEED_FORWARD_1_00_X: ++ rate = 1.0; ++ break; ++ case AV_SPEED_FORWARD_1_25_X: ++ rate = 1.25; ++ break; ++ case AV_SPEED_FORWARD_1_75_X: ++ rate = 1.75; ++ break; ++ case AV_SPEED_FORWARD_2_00_X: ++ rate = 2.0; ++ break; ++ } ++ return rate; +} + -+QByteArray QOPenHarmonyMediaServicePlugin::defaultDevice(const QByteArray &service) const ++void QOpenHarmonyPlayer::play() +{ -+ if (service == Q_MEDIASERVICE_CAMERA && !QOPenHarmonyCameraSession::availableCameras().isEmpty()) -+ return QOPenHarmonyCameraSession::availableCameras().first().name; ++ if (m_state == AV_PLAYING) ++ return; ++ if ((m_state == AV_PREPARED) ++ || (m_state == AV_PAUSED) ++ || (m_state == AV_COMPLETED)) { ++ OH_AVPlayer_Play(m_player); ++ } else if (m_state == AV_STOPPED) { ++ m_isPendingPlaying = true; ++ prepare(); ++ } ++ else { ++ m_isPendingPlaying = true; ++ } ++} + -+ return QByteArray(); ++void QOpenHarmonyPlayer::pause() ++{ ++ if (m_state == AV_PLAYING) ++ OH_AVPlayer_Pause(m_player); +} + -+QList QOPenHarmonyMediaServicePlugin::devices(const QByteArray &service) const ++void QOpenHarmonyPlayer::stop() +{ -+ Q_UNUSED(service); -+ if (service == Q_MEDIASERVICE_CAMERA) { -+ QList devices; -+ const QList &cameras = QOPenHarmonyCameraSession::availableCameras(); -+ for (int i = 0; i < cameras.count(); ++i) -+ devices.append(cameras.at(i).name); -+ return devices; ++ if ((m_state == AV_PLAYING) || ++ (m_state == AV_COMPLETED) || ++ (m_state == AV_PREPARED) || ++ (m_state == AV_PAUSED)) { ++ OH_AVPlayer_Stop(m_player); + } ++} + -+ return QList(); ++void QOpenHarmonyPlayer::seekTo(qint32 msec) ++{ ++ if ((m_state == AV_PLAYING) || ++ (m_state == AV_PAUSED) || ++ (m_state == AV_COMPLETED)) { ++ OH_AVPlayer_Seek(m_player, msec, AV_SEEK_NEXT_SYNC); ++ } +} + -+QString QOPenHarmonyMediaServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device) ++void QOpenHarmonyPlayer::setMuted(bool mute) +{ -+ if (service == Q_MEDIASERVICE_CAMERA) { -+ const QList &cameras = QOPenHarmonyCameraSession::availableCameras(); -+ for (int i = 0; i < cameras.count(); ++i) { -+ const OPenHarmonyCameraInfo &info = cameras.at(i); -+ if (info.name == device) -+ return info.description; ++ if (mute) { ++ m_volumeBeforeMute = m_volume; ++ setVolume(0); ++ } else { ++ setVolume(m_volumeBeforeMute); ++ } ++} ++ ++void QOpenHarmonyPlayer::setDataSource(const QNetworkRequest &request) ++{ ++ if (m_state != AV_IDLE) ++ reset(); ++ ++ QUrl url = request.url(); ++ if (url.isEmpty()) ++ return; ++ QString scheme = url.scheme(); ++ if (scheme == "rawfile") { ++ QString _fileName = url.path(); ++ if (_fileName.startsWith("/")) ++ _fileName.remove(0, 1); ++ QByteArray byteArray = _fileName.toLatin1(); ++ RawFile *file = OH_ResourceManager_OpenRawFile(m_resourceManager, byteArray.constData()); ++ if (file != nullptr) { ++ RawFileDescriptor desc; ++ if (OH_ResourceManager_GetRawFileDescriptor(file, desc)) { ++ OH_AVPlayer_SetFDSource(m_player, desc.fd, desc.start, desc.length); ++ OH_ResourceManager_ReleaseRawFileDescriptor(desc); ++ } ++ OH_ResourceManager_CloseRawFile(file); + } ++ } else if (scheme == "file") { ++ QFile file(url.path()); ++ if (file.open(QFile::ReadOnly)) { ++ int length = file.size(); ++ int fd = file.handle(); ++ OH_AVPlayer_SetFDSource(m_player, fd, 0, length); ++ file.close(); ++ } else { ++ qWarning() << "Could not open media:" << file.errorString(); ++ emit mediaStatusChanged(QMediaPlayer::InvalidMedia); ++ } ++ } else { ++ QString urlString = url.toString(); ++ QByteArray dataArray = urlString.toLatin1(); ++ OH_AVPlayer_SetURLSource(m_player, dataArray.constData()); + } ++} + -+ return QString(); ++void QOpenHarmonyPlayer::prepare() ++{ ++ OH_AVPlayer_Prepare(m_player); +} + -+QCamera::Position QOPenHarmonyMediaServicePlugin::cameraPosition(const QByteArray &device) const ++void QOpenHarmonyPlayer::setVolume(int volume) +{ -+return QOPenHarmonyCameraInfoControl::position(device); ++ m_volume = volume; ++ OH_AVPlayer_SetVolume(m_player, m_volume / 100.0, m_volume / 100.0); +} + -+int QOPenHarmonyMediaServicePlugin::cameraOrientation(const QByteArray &device) const ++bool QOpenHarmonyPlayer::setPlaybackRate(qreal rate) +{ -+ return QOPenHarmonyCameraInfoControl::orientation(device); ++ AVPlaybackSpeed speed; ++ if (rate < 0.75) ++ speed = AV_SPEED_FORWARD_0_75_X; ++ else if (rate < 1.0) ++ speed = AV_SPEED_FORWARD_1_00_X; ++ else if (rate < 1.25) ++ speed = AV_SPEED_FORWARD_1_25_X; ++ else if (rate < 1.75) ++ speed = AV_SPEED_FORWARD_1_75_X; ++ else ++ speed = AV_SPEED_FORWARD_2_00_X; ++ return OH_AVPlayer_SetPlaybackSpeed(m_player, speed) == AV_ERR_OK; ++} ++ ++void QOpenHarmonyPlayer::setDisplay(OHNativeWindow *window) ++{ ++ if (window == nullptr) ++ return; ++ m_window = window; ++ if (m_state == AV_INITIALIZED) { ++ OH_AVPlayer_SetVideoSurface(m_player, m_window); ++ m_surfaceSetted = true; ++ OH_AVPlayer_Prepare(m_player); ++ } ++} ++ ++OHNativeWindow *QOpenHarmonyPlayer::display() const ++{ ++ return m_window; ++} ++ ++void QOpenHarmonyPlayer::setErrorString(int code, const QString &errorString) ++{ ++ emit error(code, errorString); ++} ++ ++void QOpenHarmonyPlayer::setPlayerInfo(AVPlayerOnInfoType type, int32_t extra) ++{ ++ switch(type) { ++ case AV_INFO_TYPE_STATE_CHANGE: ++ setState(extra); ++ break; ++ case AV_INFO_TYPE_RESOLUTION_CHANGE: ++ obtainVideoSize(); ++ emit videoAvailableChanged(true); ++ break; ++ case AV_INFO_TYPE_DURATION_UPDATE: ++ emit durationChanged(extra); ++ break; ++ case AV_INFO_TYPE_POSITION_UPDATE: ++ emit progressChanged(extra); ++ break; ++ case AV_INFO_TYPE_BUFFERING_UPDATE: ++ emit bufferingChanged(extra); ++ break; ++ default: ++ break; ++ } ++} ++ ++void QOpenHarmonyPlayer::setState(int32_t state) ++{ ++ m_state = AVPlayerState (state); ++ switch(state) { ++ case AV_IDLE: ++ release(); ++ break; ++ case AV_INITIALIZED: // avplayer 设置播放源后触发该状态上报 ++ if (!m_surfaceSetted && m_window != nullptr) { ++ OH_AVPlayer_SetVideoSurface(m_player, m_window); ++ m_surfaceSetted = true; ++ prepare(); ++ } ++ break; ++ case AV_PREPARED: // prepare调用成功后上报该状态机 ++ obtainVideoSize(); ++ mediaStatusChanged(QMediaPlayer::LoadedMedia); ++ if (m_isPendingPlaying) { ++ OH_AVPlayer_Play(m_player); ++ } ++ emit audioAvailableChanged(true); ++ emit seekableChanged(true); ++ break; ++ case AV_STOPPED: ++ emit seekableChanged(false); ++ break; ++ case AV_COMPLETED: ++ emit mediaStatusChanged(QMediaPlayer::EndOfMedia); ++ break; ++ case AV_RELEASED: ++ emit durationChanged(0); ++ emit progressChanged(0); ++ ++ emit audioAvailableChanged(false); ++ emit videoAvailableChanged(false); ++ emit seekableChanged(true); ++ break; ++ default: ++ break; ++ } ++ stateChanged(state); ++} ++ ++static napi_value setResourceManager(napi_env env, napi_callback_info info) ++{ ++ size_t argc = 1; ++ napi_value args[1]; ++ napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); ++ if (argc != 1) { ++ return nullptr; ++ } ++ napi_valuetype type; ++ napi_typeof(env, args[0], &type); ++ QOpenHarmonyPlayer::m_resourceManager = OH_ResourceManager_InitNativeResourceManager(env, args[0]); ++ return nullptr; ++} ++ ++void QOpenHarmonyPlayer::init(napi_env env, napi_value exports) ++{ ++ napi_property_descriptor desc[] ={ ++ {"setResourceManager", nullptr, setResourceManager, nullptr, nullptr, nullptr, napi_default, nullptr} ++ }; ++ napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); ++} ++ ++void QOpenHarmonyPlayer::obtainVideoSize() ++{ ++ int32_t w, h = 0; ++ OH_AVErrCode result = OH_AVPlayer_GetVideoWidth(m_player, &w); ++ if (result != AV_ERR_OK) ++ return; ++ result = OH_AVPlayer_GetVideoHeight(m_player, &h); ++ if (result != AV_ERR_OK) ++ return; ++ emit videoSizeChanged(w, h); ++} ++ ++void QOpenHarmonyPlayer::onPlayerInfo(OH_AVPlayer *player, AVPlayerOnInfoType type, int32_t extra) ++{ ++ QOpenHarmonyPlayer *p = mediaPlayers->value(player); ++ if (p == nullptr) ++ return; ++ p->setPlayerInfo(type, extra); ++} ++ ++void QOpenHarmonyPlayer::onPlayerError(OH_AVPlayer *player, int32_t errorCode, const char *errorMsg) ++{ ++ QOpenHarmonyPlayer *p = mediaPlayers->value(player); ++ if (p == nullptr) ++ return; ++ p->setErrorString(errorCode, QString::fromLatin1(errorMsg)); ++} ++ ++AVPlayerState QOpenHarmonyPlayer::state() const ++{ ++ return m_state; +} + +QT_END_NAMESPACE -diff --git a/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.h b/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.h +diff --git a/src/plugins/openharmony/src/player/qopenharmonyplayer.h b/src/plugins/openharmony/src/player/qopenharmonyplayer.h new file mode 100644 -index 000000000..340e0b7ba +index 000000000..39257ea80 --- /dev/null -+++ b/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.h -@@ -0,0 +1,42 @@ -+#ifndef QOPENHARMONYMEDIASERVICEPLUGIN_H -+#define QOPENHARMONYMEDIASERVICEPLUGIN_H ++++ b/src/plugins/openharmony/src/player/qopenharmonyplayer.h +@@ -0,0 +1,78 @@ ++#ifndef QOPENHARMONYPLAYER_H ++#define QOPENHARMONYPLAYER_H + -+#include ++#include ++#include ++#include ++#include + -+QT_BEGIN_NAMESPACE ++class QNetworkRequest; ++struct NativeResourceManager; + -+class QOPenHarmonyMediaServicePlugin -+ : public QMediaServiceProviderPlugin -+ , public QMediaServiceSupportedDevicesInterface -+ , public QMediaServiceDefaultDeviceInterface -+ , public QMediaServiceCameraInfoInterface -+ , public QMediaServiceFeaturesInterface ++class QOpenHarmonyPlayer : public QObject +{ + Q_OBJECT -+ Q_INTERFACES(QMediaServiceSupportedDevicesInterface) -+ Q_INTERFACES(QMediaServiceDefaultDeviceInterface) -+ Q_INTERFACES(QMediaServiceCameraInfoInterface) -+ Q_INTERFACES(QMediaServiceFeaturesInterface) -+ Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" -+ FILE "openharmony_mediaservice.json") -+ +public: -+ QOPenHarmonyMediaServicePlugin(); -+ ~QOPenHarmonyMediaServicePlugin(); ++ QOpenHarmonyPlayer(); ++ ~QOpenHarmonyPlayer(); + -+ QMediaService* create(QString const& key) override; -+ void release(QMediaService *service) override; ++ void release(); ++ void reset(); + -+ QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const override; ++ int getCurrentPosition(); ++ int getDuration(); ++ bool isPlaying(); ++ int volume(); ++ bool isMuted(); ++ qreal playbackRate(); + -+ QByteArray defaultDevice(const QByteArray &service) const override; -+ QList devices(const QByteArray &service) const override; -+ QString deviceDescription(const QByteArray &service, const QByteArray &device) override; ++ void play(); ++ void pause(); ++ void stop(); ++ void seekTo(qint32 msec); ++ void setMuted(bool mute); ++ void setDataSource(const QNetworkRequest &request); ++ void prepare(); ++ void setVolume(int volume); ++ bool setPlaybackRate(qreal rate); ++ void setDisplay(OHNativeWindow *window); ++ OHNativeWindow *display() const; + -+ QCamera::Position cameraPosition(const QByteArray &device) const override; -+ int cameraOrientation(const QByteArray &device) const override; -+}; + -+QT_END_NAMESPACE ++ void setErrorString(int code, const QString &errorString); ++ void setPlayerInfo(AVPlayerOnInfoType type, int32_t extra); + -+#endif // QOPENHARMONYMEDIASERVICEPLUGIN_H -diff --git a/src/plugins/openharmony/src/src.pro b/src/plugins/openharmony/src/src.pro ++ AVPlayerState state() const; ++ void setState(int32_t state); ++ ++ static void init(napi_env env, napi_value exports); ++ static NativeResourceManager *m_resourceManager; ++ ++Q_SIGNALS: ++ void error(int code, const QString &errorString); ++ void bufferingChanged(qint32 percent); ++ void durationChanged(qint64 duration); ++ void progressChanged(qint64 progress); ++ void stateChanged(qint32 state); ++ void videoSizeChanged(qint32 width, qint32 height); ++ void audioAvailableChanged(bool available); ++ void videoAvailableChanged(bool available); ++ void seekableChanged(bool seekable); ++ void mediaStatusChanged(QMediaPlayer::MediaStatus status); ++private: ++ void obtainVideoSize(); ++private: ++ static void onPlayerInfo(OH_AVPlayer *player, AVPlayerOnInfoType type, int32_t extra); ++ static void onPlayerError(OH_AVPlayer *player, int32_t errorCode, const char *errorMsg); ++private: ++ OH_AVPlayer *m_player = nullptr; ++ AVPlayerCallback m_playerCallback; ++ int m_volume = 0; ++ int m_volumeBeforeMute = 0; ++ OHNativeWindow *m_window = nullptr; ++ bool m_surfaceSetted = false; ++ AVPlayerState m_state; ++ bool m_isPendingPlaying = false; ++}; ++ ++#endif // QOPENHARMONYPLAYER_H +diff --git a/src/plugins/openharmony/src/player/qopenharmonyplayercontrol.cpp b/src/plugins/openharmony/src/player/qopenharmonyplayercontrol.cpp new file mode 100644 -index 000000000..2ff2a0020 +index 000000000..4b043e2da --- /dev/null -+++ b/src/plugins/openharmony/src/src.pro -@@ -0,0 +1,20 @@ -+include (wrappers/napi/napi.pri) -+include (mediacapture/mediacapture.pri) ++++ b/src/plugins/openharmony/src/player/qopenharmonyplayercontrol.cpp +@@ -0,0 +1,339 @@ ++#include "qopenharmonyplayercontrol.h" ++#include "qopenharmonyplayerservice.h" ++#include "qopenharmonyplayer.h" ++#include "qopenharmonywindowcontrol.h" + -+TARGET = qtmedia_openharmony ++#include ++#include ++#include ++ ++ ++QOpenHarmonyPlayerControl::QOpenHarmonyPlayerControl(QObject *parent) ++ : QMediaPlayerControl(parent) ++{ ++ m_player = new QOpenHarmonyPlayer(); ++ connect(m_player,SIGNAL(bufferingChanged(qint32)), ++ this,SLOT(onBufferingChanged(qint32))); ++ connect(m_player,SIGNAL(error(int,QString)), ++ this,SLOT(onError(int,QString))); ++ connect(m_player,SIGNAL(stateChanged(qint32)), ++ this,SLOT(onStateChanged(qint32))); ++ connect(m_player,SIGNAL(videoSizeChanged(qint32,qint32)), ++ this,SLOT(onVideoSizeChanged(qint32,qint32))); ++ connect(m_player,SIGNAL(progressChanged(qint64)), ++ this,SIGNAL(positionChanged(qint64))); ++ connect(m_player,SIGNAL(durationChanged(qint64)), ++ this,SIGNAL(durationChanged(qint64))); ++ connect(m_player,SIGNAL(audioAvailableChanged(bool)), ++ this,SLOT(setAudioAvailable(bool))); ++ connect(m_player,SIGNAL(videoAvailableChanged(bool)), ++ this,SLOT(setVideoAvailable(bool))); ++ connect(m_player,SIGNAL(seekableChanged(bool)), ++ this,SLOT(setSeekable(bool))); ++ connect(m_player,SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), ++ this,SLOT(setMediaStatus(QMediaPlayer::MediaStatus))); ++ ++} ++ ++QOpenHarmonyPlayerControl::~QOpenHarmonyPlayerControl() ++{ ++ m_player->release(); ++ delete m_player; ++} ++ ++void QOpenHarmonyPlayerControl::setVideoOutput(QOpenHarmonyWindowControl *control) ++{ ++ if (control == nullptr) ++ return; ++ m_control = control; ++ connect(m_control, &QOpenHarmonyWindowControl::windowCreated, this, &QOpenHarmonyPlayerControl::onWindowCreated); ++} ++ ++QMediaPlayer::State QOpenHarmonyPlayerControl::state() const ++{ ++ AVPlayerState state = m_player->state(); ++ if (state == AV_PLAYING) ++ return QMediaPlayer::PlayingState; ++ else if (state == AV_PAUSED) ++ return QMediaPlayer::PausedState; ++ else ++ return QMediaPlayer::StoppedState; ++} ++ ++QMediaPlayer::MediaStatus QOpenHarmonyPlayerControl::mediaStatus() const ++{ ++ return m_status; ++} ++ ++qint64 QOpenHarmonyPlayerControl::duration() const ++{ ++ return m_player->getDuration(); ++} ++ ++qint64 QOpenHarmonyPlayerControl::position() const ++{ ++ if (m_status == QMediaPlayer::EndOfMedia) ++ return duration(); ++ return m_player->getCurrentPosition(); ++} ++ ++void QOpenHarmonyPlayerControl::setPosition(qint64 position) ++{ ++ if (m_status == QMediaPlayer::EndOfMedia) { ++ m_status = QMediaPlayer::LoadedMedia; ++ emit mediaStatusChanged(m_status); ++ } ++ const int seekPosition = (position > INT_MAX) ? INT_MAX : position; ++ ++ if (seekPosition == this->position()) ++ return; ++ ++ m_player->seekTo(seekPosition); ++ ++ Q_EMIT positionChanged(seekPosition); ++} ++ ++int QOpenHarmonyPlayerControl::volume() const ++{ ++ return m_volume; ++} ++ ++void QOpenHarmonyPlayerControl::setVolume(int volume) ++{ ++ if (m_volume == volume) ++ return; ++ m_player->setVolume(volume); ++ ++ Q_EMIT volumeChanged(volume); ++} ++ ++bool QOpenHarmonyPlayerControl::isMuted() const ++{ ++ return m_muted; ++} ++ ++void QOpenHarmonyPlayerControl::setMuted(bool muted) ++{ ++ if (m_muted == muted) ++ return; ++ ++ m_player->setMuted(muted); ++ ++ Q_EMIT mutedChanged(muted); ++} ++ ++int QOpenHarmonyPlayerControl::bufferStatus() const ++{ ++ return m_bufferFilled ? 100 : 0; ++} ++ ++bool QOpenHarmonyPlayerControl::isAudioAvailable() const ++{ ++ return m_audioAvailable; ++} ++ ++bool QOpenHarmonyPlayerControl::isVideoAvailable() const ++{ ++ return m_videoAvailable; ++} ++ ++bool QOpenHarmonyPlayerControl::isSeekable() const ++{ ++ return m_seekable; ++} ++ ++QMediaTimeRange QOpenHarmonyPlayerControl::availablePlaybackRanges() const ++{ ++ return m_availablePlaybackRange; ++} ++ ++void QOpenHarmonyPlayerControl::updateAvailablePlaybackRanges() ++{ ++ if (m_buffering) { ++ const qint64 pos = position(); ++ const qint64 end = (duration() / 100) * m_bufferPercent; ++ m_availablePlaybackRange.addInterval(pos, end); ++ } else if (m_seekable) { ++ m_availablePlaybackRange = QMediaTimeRange(0, duration()); ++ } else { ++ m_availablePlaybackRange = QMediaTimeRange(); ++ } ++ ++ Q_EMIT availablePlaybackRangesChanged(m_availablePlaybackRange); ++} ++ ++qreal QOpenHarmonyPlayerControl::playbackRate() const ++{ ++ return m_player->playbackRate(); ++} ++ ++void QOpenHarmonyPlayerControl::setPlaybackRate(qreal rate) ++{ ++ if (m_player->setPlaybackRate(rate)) { ++ emit playbackRateChanged(rate); ++ } ++} ++ ++QMediaContent QOpenHarmonyPlayerControl::media() const ++{ ++ return m_media; ++} ++ ++const QIODevice *QOpenHarmonyPlayerControl::mediaStream() const ++{ ++ return m_stream; ++} ++ ++void QOpenHarmonyPlayerControl::setMedia(const QMediaContent &media, QIODevice *stream) ++{ ++ if (m_media == media && m_stream == stream) ++ return; ++ ++ m_media = media; ++ m_stream = stream; ++ if (media.isNull()) { ++ m_status = QMediaPlayer::NoMedia; ++ } else { ++ m_status = QMediaPlayer::LoadingMedia; ++ m_player->setDataSource(media.request()); ++ m_status = QMediaPlayer::LoadedMedia; ++ } ++ ++ Q_EMIT mediaChanged(media); ++} ++ ++void QOpenHarmonyPlayerControl::play() ++{ ++ playOrPause(QMediaPlayer::PlayingState); ++} ++ ++void QOpenHarmonyPlayerControl::pause() ++{ ++ playOrPause(QMediaPlayer::PausedState); ++} ++ ++void QOpenHarmonyPlayerControl::playOrPause(QMediaPlayer::State state) ++{ ++ if (m_status == QMediaPlayer::NoMedia || state == QMediaPlayer::StoppedState) ++ return; ++ if (m_status == QMediaPlayer::InvalidMedia) { ++ setMedia(m_media, m_stream); ++ if (m_error != QMediaPlayer::NoError) ++ return; ++ } ++ ++ if (state == QMediaPlayer::PausedState) ++ m_player->pause(); ++ else ++ m_player->play(); ++ ++ emit stateChanged(state); ++} ++ ++void QOpenHarmonyPlayerControl::stop() ++{ ++ m_player->stop(); ++ emit stateChanged(QMediaPlayer::StoppedState); ++} ++ ++void QOpenHarmonyPlayerControl::setDisplay() ++{ ++ m_player->setDisplay(m_control->nativeWindow()); ++} ++ ++QSize QOpenHarmonyPlayerControl::videoSize() const ++{ ++ return m_videoSize; ++} ++ ++void QOpenHarmonyPlayerControl::setSeekable(bool seekable) ++{ ++ if (m_seekable == seekable) ++ return; ++ ++ m_seekable = seekable; ++ Q_EMIT seekableChanged(m_seekable); ++} ++ ++void QOpenHarmonyPlayerControl::setAudioAvailable(bool available) ++{ ++ if (m_audioAvailable == available) ++ return; ++ ++ m_audioAvailable = available; ++ Q_EMIT videoAvailableChanged(m_audioAvailable); ++} ++ ++void QOpenHarmonyPlayerControl::setVideoAvailable(bool available) ++{ ++ if (m_videoAvailable == available) ++ return; ++ ++ m_videoAvailable = available; ++ Q_EMIT videoAvailableChanged(m_videoAvailable); ++} ++ ++void QOpenHarmonyPlayerControl::onWindowCreated() ++{ ++ setDisplay(); ++} ++ ++void QOpenHarmonyPlayerControl::onStateChanged(qint32) ++{ ++ emit stateChanged(state()); ++} ++ ++void QOpenHarmonyPlayerControl::onVideoSizeChanged(qint32 width, qint32 height) ++{ ++ QSize newSize(width, height); ++ ++ if (width == 0 || height == 0 || newSize == m_videoSize) ++ return; ++ ++ m_videoSize = newSize; ++} ++ ++void QOpenHarmonyPlayerControl::setMediaStatus(QMediaPlayer::MediaStatus status) ++{ ++ if (m_status == status) ++ return; ++ ++ m_status = status; ++ ++ if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia) ++ Q_EMIT durationChanged(0); ++ ++ if (status == QMediaPlayer::EndOfMedia) ++ Q_EMIT positionChanged(position()); ++ ++ updateBufferStatus(); ++} ++ ++void QOpenHarmonyPlayerControl::updateBufferStatus() ++{ ++ bool bufferFilled = (m_status == QMediaPlayer::BufferedMedia ++ || m_status == QMediaPlayer::BufferingMedia); ++ ++ if (m_bufferFilled != bufferFilled) { ++ m_bufferFilled = bufferFilled; ++ Q_EMIT bufferStatusChanged(bufferStatus()); ++ } ++} ++ ++void QOpenHarmonyPlayerControl::onBufferingChanged(qint32 percent) ++{ ++ m_buffering = percent != 100; ++ m_bufferPercent = percent; ++ ++ updateAvailablePlaybackRanges(); ++ ++ if (state() != QMediaPlayer::StoppedState) ++ setMediaStatus(m_buffering ? QMediaPlayer::BufferingMedia : QMediaPlayer::BufferedMedia); ++} ++ ++void QOpenHarmonyPlayerControl::onError(int code, const QString &errorString) ++{ ++ qWarning() <<"open harmony player error: " << code << errorString; ++ // Todo 鸿蒙errorCode和QMediaPlayer::Error对应 ++ emit error(code, errorString); ++} +diff --git a/src/plugins/openharmony/src/player/qopenharmonyplayercontrol.h b/src/plugins/openharmony/src/player/qopenharmonyplayercontrol.h +new file mode 100644 +index 000000000..dedee7a0d +--- /dev/null ++++ b/src/plugins/openharmony/src/player/qopenharmonyplayercontrol.h +@@ -0,0 +1,97 @@ ++#ifndef QOPENHARMONYPLAYERCONTROL_H ++#define QOPENHARMONYPLAYERCONTROL_H ++ ++#include "qmediacontent.h" ++#include "qmediaplayercontrol.h" ++ ++#include ++#include ++ ++QT_BEGIN_NAMESPACE ++class QOpenHarmonyWindowControl; ++class QOpenHarmonyPlayer; ++ ++class QOpenHarmonyPlayerControl : public QMediaPlayerControl ++{ ++ Q_OBJECT ++public: ++ QOpenHarmonyPlayerControl(QObject *parent = nullptr); ++ ~QOpenHarmonyPlayerControl() override; ++ ++ void setVideoOutput(QOpenHarmonyWindowControl *control); ++ ++ QMediaPlayer::State state() const override; ++ ++ QMediaPlayer::MediaStatus mediaStatus() const override; ++ ++ qint64 duration() const override; ++ ++ qint64 position() const override; ++ void setPosition(qint64 position) override; ++ ++ int volume() const override; ++ void setVolume(int volume) override; ++ ++ bool isMuted() const override; ++ void setMuted(bool muted) override; ++ ++ int bufferStatus() const override; ++ ++ bool isAudioAvailable() const override; ++ bool isVideoAvailable() const override; ++ ++ bool isSeekable() const override; ++ ++ QMediaTimeRange availablePlaybackRanges() const override; ++ ++ qreal playbackRate() const override; ++ void setPlaybackRate(qreal rate) override; ++ ++ QMediaContent media() const override; ++ const QIODevice *mediaStream() const override; ++ void setMedia(const QMediaContent &media, QIODevice *stream) override; ++ ++ void play() override; ++ void pause() override; ++ void stop() override; ++ ++ void setDisplay(); ++ ++ QSize videoSize() const; ++ ++private slots: ++ void setSeekable(bool seekable); ++ void setAudioAvailable(bool available); ++ void setVideoAvailable(bool available); ++ void onWindowCreated(); ++ void onStateChanged(qint32); ++ void onVideoSizeChanged(qint32, qint32 height); ++ void setMediaStatus(QMediaPlayer::MediaStatus status); ++ void onBufferingChanged(qint32 percent); ++ void onError(int code, const QString &errorString); ++private: ++ void updateAvailablePlaybackRanges(); ++ void updateBufferStatus(); ++ void playOrPause(QMediaPlayer::State state); ++ QOpenHarmonyWindowControl *m_control = nullptr; ++ QOpenHarmonyPlayer *m_player = nullptr; ++ QMediaPlayer::MediaStatus m_status = QMediaPlayer::NoMedia; ++ QMediaPlayer::Error m_error = QMediaPlayer::NoError; ++ QMediaTimeRange m_availablePlaybackRange; ++ QIODevice *m_stream = nullptr; ++ int m_volume = 100; ++ bool m_muted = false; ++ QMediaContent m_media; ++ QString m_errorString; ++ QSize m_videoSize; ++ bool m_seekable = true; ++ bool m_audioAvailable = false; ++ bool m_videoAvailable = false; ++ bool m_buffering = false; ++ bool m_bufferPercent = 0; ++ bool m_bufferFilled = false; ++}; ++ ++QT_END_NAMESPACE ++ ++#endif +diff --git a/src/plugins/openharmony/src/player/qopenharmonyplayerservice.cpp b/src/plugins/openharmony/src/player/qopenharmonyplayerservice.cpp +new file mode 100644 +index 000000000..f2d1ed621 +--- /dev/null ++++ b/src/plugins/openharmony/src/player/qopenharmonyplayerservice.cpp +@@ -0,0 +1,44 @@ ++#include "qopenharmonyplayerservice.h" ++#include "qopenharmonyplayercontrol.h" ++#include "qopenharmonywindowcontrol.h" ++ ++ ++QT_BEGIN_NAMESPACE ++ ++QOpenHarmonyPlayerService::QOpenHarmonyPlayerService(QObject *parent) ++ : QMediaService(parent) ++{ ++ m_playerControl = new QOpenHarmonyPlayerControl(this); ++ m_videoWindowControl = new QOpenHarmonyWindowControl(this); ++ m_playerControl->setVideoOutput(m_videoWindowControl); ++ m_videoWindowControl->setPlayerControl(m_playerControl); ++} ++ ++QOpenHarmonyPlayerService::~QOpenHarmonyPlayerService() ++{ ++ delete m_playerControl; ++} ++ ++QMediaControl *QOpenHarmonyPlayerService::requestControl(const char *name) ++{ ++ if (qstrcmp(name, QMediaPlayerControl_iid) == 0) ++ return m_playerControl; ++ if (qstrcmp(name, QVideoWindowControl_iid) == 0) { ++ return m_videoWindowControl; ++ } ++ return nullptr; ++} ++ ++void QOpenHarmonyPlayerService::releaseControl(QMediaControl *control) ++{ ++ if (!control) { ++ qWarning("QMediaService::releaseControl():" ++ " Attempted release of null control"); ++ } else if (control == m_videoWindowControl) { ++ delete m_videoWindowControl; ++ ++ m_videoWindowControl = nullptr; ++ } ++} ++ ++QT_END_NAMESPACE +diff --git a/src/plugins/openharmony/src/player/qopenharmonyplayerservice.h b/src/plugins/openharmony/src/player/qopenharmonyplayerservice.h +new file mode 100644 +index 000000000..ba322d56e +--- /dev/null ++++ b/src/plugins/openharmony/src/player/qopenharmonyplayerservice.h +@@ -0,0 +1,28 @@ ++#ifndef QOPENHARMONYPLAYERSERVICE_H ++#define QOPENHARMONYPLAYERSERVICE_H ++ ++#include "qmediaservice.h" ++ ++QT_BEGIN_NAMESPACE ++ ++class QOpenHarmonyPlayerControl; ++class QOpenHarmonyWindowControl; ++ ++class QOpenHarmonyPlayerService : public QMediaService ++{ ++ Q_OBJECT ++public: ++ QOpenHarmonyPlayerService(QObject *parent = nullptr); ++ ~QOpenHarmonyPlayerService() override; ++ ++ QMediaControl *requestControl(const char *name) override; ++ void releaseControl(QMediaControl *control) override; ++ ++private: ++ QOpenHarmonyPlayerControl *m_playerControl = nullptr; ++ QOpenHarmonyWindowControl *m_videoWindowControl = nullptr; ++}; ++ ++QT_END_NAMESPACE ++ ++#endif +diff --git a/src/plugins/openharmony/src/player/qopenharmonywindowcontrol.cpp b/src/plugins/openharmony/src/player/qopenharmonywindowcontrol.cpp +new file mode 100644 +index 000000000..b2c998e61 +--- /dev/null ++++ b/src/plugins/openharmony/src/player/qopenharmonywindowcontrol.cpp +@@ -0,0 +1,208 @@ ++#include "qopenharmonywindowcontrol.h" ++#include ++#include ++#include "qopenharmonyplayercontrol.h" ++#include "qopenharmonywindowadapter.h" ++ ++#ifndef QT_NO_WIDGETS ++#include ++#include ++#endif ++typedef QHash PlayerControls; ++Q_GLOBAL_STATIC(PlayerControls, g_controls) ++ ++static QOpenHarmonyWindowControl *getControl(OH_NativeXComponent *component) ++{ ++ char id[OH_XCOMPONENT_ID_LEN_MAX + 1] = { }; ++ uint64_t id_length = OH_XCOMPONENT_ID_LEN_MAX + 1; ++ ++ int32_t ret = OH_NativeXComponent_GetXComponentId(component, id, &id_length); ++ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { ++ return nullptr; ++ } ++ LOGI("get player xcomponent succeded %{public}s", id); ++ QString name = QString::fromUtf8(id); ++ return g_controls->value(name); ++} ++ ++QOpenHarmonyWindowControl::QOpenHarmonyWindowControl(QObject *parent) ++ : QVideoWindowControl(parent) ++{ ++ ++} ++ ++QOpenHarmonyWindowControl::~QOpenHarmonyWindowControl() ++{ ++ if (m_callback != nullptr) ++ delete m_callback; ++ g_controls->remove(m_name); ++} ++ ++ ++WId QOpenHarmonyWindowControl::winId() const ++{ ++ return m_windowId; ++} ++ ++void QOpenHarmonyWindowControl::setWinId(WId id) ++{ ++ if (m_windowId == id) ++ return; ++ m_windowId = id; ++ ++ static int componentId = 0; ++ QString componentIdStr = QString("player_component_id%1").arg(componentId++); ++ QString libName = "plugins_mediaservice_qtmedia_openharmony"; ++ XComponentModel *model = new XComponentModel(componentIdStr.toStdString(), XComponentType::SURFACE, libName.toStdString()); ++ NodeParams nodeParams("100%", "100%", "0", "0", NodeType::XComponent, model); ++ QOpenHarmonyWindowAdapter::AddChildNode(id, &nodeParams); ++ g_controls->insert(componentIdStr, this); ++ m_name = componentIdStr; ++} ++ ++QRect QOpenHarmonyWindowControl::displayRect() const ++{ ++ return m_displayRect; ++} ++ ++void QOpenHarmonyWindowControl::setDisplayRect(const QRect &rect) ++{ ++ m_displayRect = rect; ++} ++ ++bool QOpenHarmonyWindowControl::isFullScreen() const ++{ ++ return m_fullScreen; ++} ++ ++void QOpenHarmonyWindowControl::setFullScreen(bool fullScreen) ++{ ++ emit fullScreenChanged(m_fullScreen = fullScreen); ++} ++ ++void QOpenHarmonyWindowControl::repaint() ++{ ++ ++} ++ ++QSize QOpenHarmonyWindowControl::nativeSize() const ++{ ++ return m_playerControl == nullptr ? QSize() : m_playerControl->videoSize(); ++} ++ ++Qt::AspectRatioMode QOpenHarmonyWindowControl::aspectRatioMode() const ++{ ++ return m_aspectRatioMode; ++} ++ ++void QOpenHarmonyWindowControl::setAspectRatioMode(Qt::AspectRatioMode mode) ++{ ++ m_aspectRatioMode = mode; ++} ++ ++int QOpenHarmonyWindowControl::brightness() const ++{ ++ return m_brightness; ++} ++ ++void QOpenHarmonyWindowControl::setBrightness(int brightness) ++{ ++ m_brightness = brightness; ++ ++ emit brightnessChanged(brightness); ++} ++ ++int QOpenHarmonyWindowControl::contrast() const ++{ ++ return m_contrast; ++} ++ ++void QOpenHarmonyWindowControl::setContrast(int contrast) ++{ ++ m_contrast = contrast; ++ ++ emit contrastChanged(contrast); ++} ++ ++int QOpenHarmonyWindowControl::hue() const ++{ ++ return m_hue; ++} ++ ++void QOpenHarmonyWindowControl::setHue(int hue) ++{ ++ m_hue = hue; ++ ++ emit hueChanged(hue); ++} ++ ++int QOpenHarmonyWindowControl::saturation() const ++{ ++ return m_saturation; ++} ++ ++void QOpenHarmonyWindowControl::setSaturation(int saturation) ++{ ++ m_saturation = saturation; ++ ++ emit saturationChanged(saturation); ++} ++ ++void QOpenHarmonyWindowControl::setNativeXComponent(OH_NativeXComponent *nativeXComponent) ++{ ++ m_callback = new OH_NativeXComponent_Callback; ++ m_callback->OnSurfaceCreated = &QOpenHarmonyWindowControl::onSurfaceCreated; ++ int32_t ret = OH_NativeXComponent_RegisterCallback(nativeXComponent, m_callback); ++ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { ++ LOGI("set surface touch callback failed"); ++ } ++} ++ ++void QOpenHarmonyWindowControl::setNativeWindow(OHNativeWindow *nativeWindow) ++{ ++ m_nativeWindow = nativeWindow; ++ QMetaObject::invokeMethod(this, "windowCreated"); ++} ++ ++OHNativeWindow *QOpenHarmonyWindowControl::nativeWindow() const ++{ ++ return m_nativeWindow; ++} ++ ++void QOpenHarmonyWindowControl::setPlayerControl(QOpenHarmonyPlayerControl *control) ++{ ++ if (control == nullptr) ++ return; ++ m_playerControl = control; ++} ++ ++void QOpenHarmonyWindowControl::onSurfaceCreated(OH_NativeXComponent *component, void *window) ++{ ++ if (component != nullptr) { ++ QOpenHarmonyWindowControl *control = getControl(component); ++ if (control == nullptr) ++ return; ++ OHNativeWindow *nativeWindow = static_cast(window); ++ control->setNativeWindow(nativeWindow); ++ } ++} ++ ++void QOpenHarmonyWindowControl::init(napi_env env, napi_value exports) ++{ ++ napi_value exportInstance = nullptr; ++ OH_NativeXComponent *nativeXComponent = nullptr; ++ int32_t ret; ++ char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { }; ++ uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; ++ ++ if (napi_ok != napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance)) ++ return; ++ if (napi_ok != napi_unwrap(env, exportInstance, reinterpret_cast(&nativeXComponent))) ++ return; ++ if (nativeXComponent != nullptr) { ++ QOpenHarmonyWindowControl *control = getControl(nativeXComponent); ++ if (control == nullptr) ++ return; ++ control->setNativeXComponent(nativeXComponent); ++ } ++} +diff --git a/src/plugins/openharmony/src/player/qopenharmonywindowcontrol.h b/src/plugins/openharmony/src/player/qopenharmonywindowcontrol.h +new file mode 100644 +index 000000000..cf1bfbadd +--- /dev/null ++++ b/src/plugins/openharmony/src/player/qopenharmonywindowcontrol.h +@@ -0,0 +1,75 @@ ++#ifndef QOPENHARMONYWINDOWCONTROL_H ++#define QOPENHARMONYWINDOWCONTROL_H ++ ++#include "qvideowindowcontrol.h" ++#include ++#include ++#include ++ ++struct OH_NativeXComponent; ++struct OH_NativeXComponent_Callback; ++QT_BEGIN_NAMESPACE ++class QOpenHarmonyPlayerControl; ++class QOpenHarmonyWindowControl : public QVideoWindowControl ++{ ++ Q_OBJECT ++public: ++ QOpenHarmonyWindowControl(QObject *parent = nullptr); ++ ~QOpenHarmonyWindowControl() override; ++ ++ WId winId() const override; ++ void setWinId(WId id) override; ++ ++ QRect displayRect() const override; ++ void setDisplayRect(const QRect &rect) override; ++ ++ bool isFullScreen() const override; ++ void setFullScreen(bool fullScreen) override; ++ ++ void repaint() override; ++ ++ QSize nativeSize() const override; ++ ++ Qt::AspectRatioMode aspectRatioMode() const override; ++ void setAspectRatioMode(Qt::AspectRatioMode mode) override; ++ ++ int brightness() const override; ++ void setBrightness(int brightness) override; ++ ++ int contrast() const override; ++ void setContrast(int contrast) override; ++ ++ int hue() const override; ++ void setHue(int hue) override; ++ ++ int saturation() const override; ++ void setSaturation(int saturation) override; ++ ++ void setNativeXComponent(OH_NativeXComponent *nativeXComponent); ++ void setNativeWindow(OHNativeWindow *nativeWindow); ++ OHNativeWindow *nativeWindow() const; ++ ++ void setPlayerControl(QOpenHarmonyPlayerControl *control); ++ static void onSurfaceCreated(OH_NativeXComponent* component, void* window); ++ static void init(napi_env env, napi_value exports); ++ ++signals: ++ void windowCreated(); ++private: ++ WId m_windowId = 0; ++ OH_NativeXComponent_Callback *m_callback; ++ OHNativeWindow *m_nativeWindow = nullptr; ++ QString m_name; ++ Qt::AspectRatioMode m_aspectRatioMode = Qt::KeepAspectRatio; ++ QRect m_displayRect; ++ int m_brightness = 0; ++ int m_contrast = 0; ++ int m_hue = 0; ++ int m_saturation = 0; ++ bool m_fullScreen = false; ++ QOpenHarmonyPlayerControl *m_playerControl; ++}; ++ ++QT_END_NAMESPACE ++ ++#endif +diff --git a/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.cpp b/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.cpp +new file mode 100644 +index 000000000..e164db15b +--- /dev/null ++++ b/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.cpp +@@ -0,0 +1,128 @@ ++#include ++ ++#include "qcamera.h" ++#include "qmediaserviceproviderplugin.h" ++#include "qopenharmonymediaserviceplugin.h" ++#include "mediacapture/qopenharmonycamerasession.h" ++#include "mediacapture/qopenharmonycaptureservice.h" ++#include "mediacapture/qopenharmonycamerainfocontrol.h" ++#include "qopenharmonyplayerservice.h" ++#include "player/qopenharmonyplayer.h" ++#include "player/qopenharmonywindowcontrol.h" ++ ++QT_BEGIN_NAMESPACE ++ ++Q_LOGGING_CATEGORY(qtOPenHaronyMediaPlugin, "qt.multimedia.plugins.openharmony") ++ ++QOPenHarmonyMediaServicePlugin::QOPenHarmonyMediaServicePlugin() ++{ ++} ++ ++QOPenHarmonyMediaServicePlugin::~QOPenHarmonyMediaServicePlugin() ++{ ++} ++ ++QMediaService *QOPenHarmonyMediaServicePlugin::create(const QString &key) ++{ ++ if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) ++ return new QOpenHarmonyPlayerService; ++ ++ if (key == QLatin1String(Q_MEDIASERVICE_CAMERA) ++ || key == QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE)) { ++ return new QOPenHarmonyCaptureService(key); ++ } ++ ++ qCWarning(qtOPenHaronyMediaPlugin) << "OPenHarony service plugin: unsupported key:" << key; ++ return 0; ++} ++ ++void QOPenHarmonyMediaServicePlugin::release(QMediaService *service) ++{ ++ delete service; ++} ++ ++QMediaServiceProviderHint::Features QOPenHarmonyMediaServicePlugin::supportedFeatures(const QByteArray &service) const ++{ ++ if (service == Q_MEDIASERVICE_MEDIAPLAYER) ++ return QMediaServiceProviderHint::VideoSurface; ++ ++ if (service == Q_MEDIASERVICE_CAMERA) ++ return QMediaServiceProviderHint::VideoSurface | QMediaServiceProviderHint::RecordingSupport; ++ ++ if (service == Q_MEDIASERVICE_AUDIOSOURCE) ++ return QMediaServiceProviderHint::RecordingSupport; ++ ++ return QMediaServiceProviderHint::Features(); ++} ++ ++QByteArray QOPenHarmonyMediaServicePlugin::defaultDevice(const QByteArray &service) const ++{ ++ if (service == Q_MEDIASERVICE_CAMERA && !QOPenHarmonyCameraSession::availableCameras().isEmpty()) ++ return QOPenHarmonyCameraSession::availableCameras().first().name; ++ ++ return QByteArray(); ++} ++ ++QList QOPenHarmonyMediaServicePlugin::devices(const QByteArray &service) const ++{ ++ Q_UNUSED(service); ++ if (service == Q_MEDIASERVICE_CAMERA) { ++ QList devices; ++ const QList &cameras = QOPenHarmonyCameraSession::availableCameras(); ++ for (int i = 0; i < cameras.count(); ++i) ++ devices.append(cameras.at(i).name); ++ return devices; ++ } ++ ++ return QList(); ++} ++ ++QString QOPenHarmonyMediaServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device) ++{ ++ if (service == Q_MEDIASERVICE_CAMERA) { ++ const QList &cameras = QOPenHarmonyCameraSession::availableCameras(); ++ for (int i = 0; i < cameras.count(); ++i) { ++ const OPenHarmonyCameraInfo &info = cameras.at(i); ++ if (info.name == device) ++ return info.description; ++ } ++ } ++ ++ return QString(); ++} ++ ++QCamera::Position QOPenHarmonyMediaServicePlugin::cameraPosition(const QByteArray &device) const ++{ ++ return QOPenHarmonyCameraInfoControl::position(device); ++} ++ ++int QOPenHarmonyMediaServicePlugin::cameraOrientation(const QByteArray &device) const ++{ ++ return QOPenHarmonyCameraInfoControl::orientation(device); ++} ++ ++QT_END_NAMESPACE ++ ++ ++EXTERN_C_START static napi_value Init(napi_env env, napi_value exports) { ++ static bool inited = false; ++ if (!inited) { ++ inited = true; ++ QOpenHarmonyPlayer::init(env, exports); ++ } ++ QOpenHarmonyWindowControl::init(env, exports); ++ return exports; ++} ++EXTERN_C_END ++ ++static napi_module multiMediaModule = { ++ .nm_version = 1, ++ .nm_flags = 0, ++ .nm_filename = nullptr, ++ .nm_register_func = Init, ++ .nm_modname = "plugins_mediaservice_qtmedia_openharmony", ++ .nm_priv = ((void *)0), ++ .reserved = {0}, ++}; ++ ++extern "C" __attribute__((constructor)) void RegisterModule(void) { napi_module_register(&multiMediaModule); } +diff --git a/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.h b/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.h +new file mode 100644 +index 000000000..340e0b7ba +--- /dev/null ++++ b/src/plugins/openharmony/src/qopenharmonymediaserviceplugin.h +@@ -0,0 +1,42 @@ ++#ifndef QOPENHARMONYMEDIASERVICEPLUGIN_H ++#define QOPENHARMONYMEDIASERVICEPLUGIN_H ++ ++#include ++ ++QT_BEGIN_NAMESPACE ++ ++class QOPenHarmonyMediaServicePlugin ++ : public QMediaServiceProviderPlugin ++ , public QMediaServiceSupportedDevicesInterface ++ , public QMediaServiceDefaultDeviceInterface ++ , public QMediaServiceCameraInfoInterface ++ , public QMediaServiceFeaturesInterface ++{ ++ Q_OBJECT ++ Q_INTERFACES(QMediaServiceSupportedDevicesInterface) ++ Q_INTERFACES(QMediaServiceDefaultDeviceInterface) ++ Q_INTERFACES(QMediaServiceCameraInfoInterface) ++ Q_INTERFACES(QMediaServiceFeaturesInterface) ++ Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" ++ FILE "openharmony_mediaservice.json") ++ ++public: ++ QOPenHarmonyMediaServicePlugin(); ++ ~QOPenHarmonyMediaServicePlugin(); ++ ++ QMediaService* create(QString const& key) override; ++ void release(QMediaService *service) override; ++ ++ QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const override; ++ ++ QByteArray defaultDevice(const QByteArray &service) const override; ++ QList devices(const QByteArray &service) const override; ++ QString deviceDescription(const QByteArray &service, const QByteArray &device) override; ++ ++ QCamera::Position cameraPosition(const QByteArray &device) const override; ++ int cameraOrientation(const QByteArray &device) const override; ++}; ++ ++QT_END_NAMESPACE ++ ++#endif // QOPENHARMONYMEDIASERVICEPLUGIN_H +diff --git a/src/plugins/openharmony/src/src.pro b/src/plugins/openharmony/src/src.pro +new file mode 100644 +index 000000000..fe23c3c9d +--- /dev/null ++++ b/src/plugins/openharmony/src/src.pro +@@ -0,0 +1,23 @@ ++include (wrappers/napi/napi.pri) ++include (mediacapture/mediacapture.pri) ++include (player/player.pri) ++ ++TARGET = qtmedia_openharmony ++ ++LIBS += -lace_ndk.z -lavplayer -lrawfile.z + +QT += multimedia-private core-private network + diff --git a/patch/v5.15.12/qtquickcontrols.patch b/patch/v5.15.12/qtquickcontrols.patch new file mode 100644 index 0000000000000000000000000000000000000000..c60a82cffad9c5c137e36069a203d5ba98b3840c --- /dev/null +++ b/patch/v5.15.12/qtquickcontrols.patch @@ -0,0 +1,85 @@ +diff --git a/src/controls/plugin.cpp b/src/controls/plugin.cpp +index 446357aa..94e7298a 100644 +--- a/src/controls/plugin.cpp ++++ b/src/controls/plugin.cpp +@@ -241,6 +241,11 @@ QString QtQuickControls1Plugin::fileLocation() const + { + #ifdef Q_OS_ANDROID + return "qrc:/android_rcc_bundle/qml/QtQuick/Controls"; ++#elif defined(Q_OS_OPENHARMONY) /* FIXME 鸿蒙对文件读取的路径做了限制 */ ++ if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QT_HARMONY_CACHE_DIR"))) { ++ const QString envImportPath = qEnvironmentVariable("QT_HARMONY_CACHE_DIR"); ++ return "file:" + envImportPath + "/Qt/qml/QtQuick/Controls"; ++ } + #else + # ifndef QT_STATIC + if (isLoadedFromResource()) +@@ -254,7 +259,7 @@ QString QtQuickControls1Plugin::fileLocation() const + + bool QtQuickControls1Plugin::isLoadedFromResource() const + { +-#ifdef Q_OS_ANDROID ++#if defined(Q_OS_ANDROID) || defined(Q_OS_OPENHARMONY) + return true; + #else + # ifdef QT_STATIC +diff --git a/src/dialogs/dialogs.pro b/src/dialogs/dialogs.pro +index ef0878ce..5302d154 100644 +--- a/src/dialogs/dialogs.pro ++++ b/src/dialogs/dialogs.pro +@@ -72,7 +72,7 @@ DIALOGS_QML_FILES += \ + images/window_border.png \ + $$WIDGET_DIALOGS_QML_FILES + +-ios|android|blackberry { ++ios|android|blackberry|openharmony { + DIALOGS_QML_FILES -= $$WIDGET_DIALOGS_QML_FILES + } + +diff --git a/src/dialogs/plugin.cpp b/src/dialogs/plugin.cpp +index 4bc7d386..60c65ab9 100644 +--- a/src/dialogs/plugin.cpp ++++ b/src/dialogs/plugin.cpp +@@ -98,7 +98,7 @@ public: + #ifndef QT_STATIC + widgetsDir.cd("../PrivateWidgets"); + #endif +-#ifdef QT_STATIC ++#if defined(QT_STATIC) || defined(Q_OS_OPENHARMONY) + m_useResources = false; + #else + #ifndef ALWAYS_LOAD_FROM_RESOURCES +@@ -109,7 +109,6 @@ public: + m_useResources = false; + #endif + #endif +- + QQuickAbstractDialog::m_decorationComponentUrl = fileLocation("qml/DefaultWindowDecoration"); + // Prefer the QPA dialog helpers if the platform supports them. + // Else if there is a QWidget-based implementation, check whether it's +@@ -229,14 +228,25 @@ protected: + + QUrl fileLocation(const QString &moduleName) const + { ++#ifdef Q_OS_OPENHARMONY ++ ++ QString temp = baseUrl().toLocalFile(); ++ temp.replace(qEnvironmentVariable("QT_PLUGIN_PATH"), qEnvironmentVariable("QT_HARMONY_CACHE_DIR")); ++#endif + return m_useResources ? + #ifdef Q_OS_ANDROID + QUrl(QString("qrc:/android_rcc_bundle/qml/QtQuick/Dialogs/%1.qml").arg(moduleName)) : ++#elif defined(Q_OS_OPENHARMONY) ++ QUrl(QString("qrc:/harmony_rcc_bundle/qml/QtQuick/Dialogs/%1.qml").arg(moduleName)) : + #else + QUrl(QString("qrc:/QtQuick/Dialogs/%1.qml").arg(moduleName)) : + #endif + #ifndef QT_STATIC ++#ifdef Q_OS_OPENHARMONY ++ QUrl::fromLocalFile(QDir(temp).filePath(moduleName + ".qml")); ++#else + QUrl::fromLocalFile(QDir(baseUrl().toLocalFile()).filePath(moduleName + ".qml")); ++#endif + #else + QUrl(QString("qrc:/qt-project.org/imports/QtQuick/Dialogs/%1.qml").arg(moduleName)); + #endif diff --git a/patch/v5.15.12/qtremoteobjects.patch b/patch/v5.15.12/qtremoteobjects.patch deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/patch/v5.15.12/qtwebview.patch b/patch/v5.15.12/qtwebview.patch new file mode 100644 index 0000000000000000000000000000000000000000..c548773b70e37e12c166d8ccdee75ef703efa8db --- /dev/null +++ b/patch/v5.15.12/qtwebview.patch @@ -0,0 +1,865 @@ +diff --git a/src/openharmony/native/QtWebView/JsWebView.ets b/src/openharmony/native/QtWebView/JsWebView.ets +new file mode 100644 +index 0000000..a503613 +--- /dev/null ++++ b/src/openharmony/native/QtWebView/JsWebView.ets +@@ -0,0 +1,179 @@ ++import { UIContext } from '@kit.ArkUI'; ++import { NodeParams } from 'adapter_ts' ++import web_webview from '@ohos.web.webview' ++import JsDataStore from '../QtCore/JsDataStore'; ++import { ViewNodeController } from 'adapter_ts' ++import { BuilderNode, FrameNode } from '@ohos.arkui.node'; ++import qtwebview from 'libplugins_webview_qtwebview_harmony.so' ++ ++class WebParams extends NodeParams { ++ url: string = "" ++ controller: web_webview.WebviewController | undefined = undefined; ++ ++ constructor(url: string, controller: web_webview.WebviewController) { ++ super() ++ this.url = url; ++ this.controller = this.controller; ++ } ++} ++ ++@Builder ++function webBuilder(params: WebParams) { ++ Column() { ++ Web({ src: params.url, controller: params.controller }) ++ } ++} ++ ++class WebViewNodeController extends ViewNodeController { ++ private webParams: WebParams | undefined = undefined; ++ private buildWebNode: BuilderNode<[WebParams]> | null = null; ++ private webBuilder = new WrappedBuilder<[WebParams]>(webBuilder); ++ public webViewController: web_webview.WebviewController | null = null; ++ ++ constructor(uiContextP: UIContext, params?: WebParams) { ++ super(uiContextP) ++ this.webParams = params; ++ if (null == this.buildWebNode) { ++ this.buildWebNode = new BuilderNode(uiContextP); ++ } ++ ++ if (null != params?.controller) { ++ this.webViewController = params?.controller; ++ } else { ++ this.webViewController = new web_webview.WebviewController(); ++ } ++ } ++ ++ public getCurNode(): BuilderNode<[WebParams]> | null { ++ return this.buildWebNode; ++ } ++ ++ makeNode(uiContext: UIContext): FrameNode | null { ++ if (this.buildWebNode != null) { ++ this.buildWebNode.build(this.webBuilder, this.webParams); ++ return this.buildWebNode.getFrameNode(); ++ } ++ return null; ++ } ++ ++ delete() { ++ for (let i = 0; i < super.params.node_list.length; i++) { ++ super.params.node_list[i].delete(); ++ } ++ this.buildWebNode?.build(this.webBuilder, new NodeParams) ++ this.buildWebNode = null; ++ } ++ ++ update() { ++ if (this.buildWebNode != null) { ++ this.buildWebNode.update(this.webParams); ++ } ++ } ++} ++ ++const gWebControllers = new Map(); ++ ++export class JsWebView { ++ constructor() { ++ } ++ ++ async getWebView(pointerId: number): Promise { ++ if (gWebControllers.has(pointerId)) { ++ return gWebControllers.get(pointerId); ++ } ++ ++ let webNode: WebViewNodeController = new WebViewNodeController(JsDataStore.getUiContext() as UIContext); ++ gWebControllers.set(pointerId, webNode); ++ return webNode; ++ } ++ ++ destroy(pointerId: number) { ++ let ct: WebViewNodeController | undefined = gWebControllers.get(pointerId); ++ if (undefined != ct) { ++ gWebControllers.delete(pointerId); ++ ct.delete(); ++ } ++ } ++ ++ getUserAgent(pointerId: number): string { ++ let ct: WebViewNodeController | undefined = gWebControllers.get(pointerId); ++ if (undefined != ct) { ++ return ct.webViewController?.getUserAgent() as string; ++ } ++ return ''; ++ } ++ ++ setCustomUserAgent(pointerId: number, userAgent: string) { ++ let ct: WebViewNodeController | undefined = gWebControllers.get(pointerId); ++ if (undefined != ct) { ++ ct.webViewController?.setCustomUserAgent(userAgent); ++ } ++ } ++ ++ getUrl(pointerId: number): string { ++ let ct: WebViewNodeController | undefined = gWebControllers.get(pointerId); ++ if (undefined != ct) { ++ return ct.webViewController?.getUrl() as string; ++ } ++ return ''; ++ } ++ ++ loadUrl(pointerId: number, url: string) { ++ let ct: WebViewNodeController | undefined = gWebControllers.get(pointerId); ++ if (undefined != ct) { ++ ct.webViewController?.loadUrl(url); ++ } ++ } ++ ++ accessBackward(pointerId: number): boolean { ++ let ct: WebViewNodeController | undefined = gWebControllers.get(pointerId); ++ if (undefined != ct) { ++ return ct.webViewController?.accessBackward() as boolean; ++ } ++ return false; ++ } ++ ++ backward(pointerId: number) { ++ let ct: WebViewNodeController | undefined = gWebControllers.get(pointerId); ++ if (undefined != ct) { ++ return ct.webViewController?.backward(); ++ } ++ } ++ ++ accessForward(pointerId: number): boolean { ++ let ct: WebViewNodeController | undefined = gWebControllers.get(pointerId); ++ if (undefined != ct) { ++ return ct.webViewController?.accessForward() as boolean; ++ } ++ return false; ++ } ++ ++ forward(pointerId: number) { ++ let ct: WebViewNodeController | undefined = gWebControllers.get(pointerId); ++ if (undefined != ct) { ++ return ct.webViewController?.forward(); ++ } ++ } ++ ++ refresh(pointerId: number) { ++ let ct: WebViewNodeController | undefined = gWebControllers.get(pointerId); ++ if (undefined != ct) { ++ ct.webViewController?.refresh(); ++ } ++ } ++ ++ getTitle(pointerId: number): string { ++ let ct: WebViewNodeController | undefined = gWebControllers.get(pointerId); ++ if (undefined != ct) { ++ return ct.webViewController?.getTitle() as string; ++ } ++ return ''; ++ } ++ ++ stop(pointerId: number) { ++ let ct: WebViewNodeController | undefined = gWebControllers.get(pointerId); ++ if (undefined != ct) { ++ ct.webViewController?.stop(); ++ } ++ } ++} +\ No newline at end of file +diff --git a/src/openharmony/openharmony.pro b/src/openharmony/openharmony.pro +new file mode 100644 +index 0000000..a94bf94 +--- /dev/null ++++ b/src/openharmony/openharmony.pro +@@ -0,0 +1,10 @@ ++TEMPLATE = aux ++ ++CONFIG -= qt ++ ++templates.files += $$files($$PWD/native/QtWebView/*.ts, true) ++templates.files += $$files($$PWD/native/QtWebView/*.ets, true) ++templates.path = $$[QT_INSTALL_PREFIX]/openharmony/qtwebview ++templates.base = $$PWD ++ ++INSTALLS += templates +diff --git a/src/plugins/harmony/harmony.json b/src/plugins/harmony/harmony.json +new file mode 100644 +index 0000000..9f65fd4 +--- /dev/null ++++ b/src/plugins/harmony/harmony.json +@@ -0,0 +1,3 @@ ++{ ++ "Keys": ["native"] ++} +diff --git a/src/plugins/harmony/harmony.pro b/src/plugins/harmony/harmony.pro +new file mode 100644 +index 0000000..6c93b4f +--- /dev/null ++++ b/src/plugins/harmony/harmony.pro +@@ -0,0 +1,20 @@ ++TARGET = qtwebview_harmony ++ ++PLUGIN_TYPE = webview ++PLUGIN_CLASS_NAME = QHarmonyWebViewPlugin ++load(qt_plugin) ++ ++QT += core gui webview-private ++ ++HEADERS += \ ++ qharmonywebview_p.h ++ ++SOURCES += \ ++ qharmonywebviewplugin.cpp \ ++ qharmonywebview.cpp ++ ++OTHER_FILES += ++ ++DISTFILES += \ ++ harmony.json ++ +diff --git a/src/plugins/harmony/qharmonywebview.cpp b/src/plugins/harmony/qharmonywebview.cpp +new file mode 100644 +index 0000000..21f20d4 +--- /dev/null ++++ b/src/plugins/harmony/qharmonywebview.cpp +@@ -0,0 +1,420 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "qharmonywebview_p.h" ++#include "QtCore/qopenharmonydefines.h" ++#include "QtCore/qopenharmonyjsobjectloader.h" ++ ++QT_BEGIN_NAMESPACE ++ ++typedef QMap WebViews; ++Q_GLOBAL_STATIC(WebViews, g_webViews) ++ ++QHarmonyWebViewPrivate::QHarmonyWebViewPrivate(QObject *p) ++ : QAbstractWebView(p) ++ , m_id(reinterpret_cast(this)) ++ , m_callbackId(0) ++ , m_window(0) ++{ ++ m_jsWebView = qJsObjectLoader->create("JsWebView"); ++ ++ // m_viewController = QJNIObjectPrivate(qtAndroidWebViewControllerClass, ++ // "(Landroid/app/Activity;J)V", ++ // QtAndroidPrivate::activity(), ++ // m_id); ++ // m_webView = m_viewController.callObjectMethod("getWebView", ++ // "()Landroid/webkit/WebView;"); ++ ++ // m_window = QWindow::fromWinId(reinterpret_cast(m_webView.object())); ++ ++ napi_ref webController = m_jsWebView->callReturnRef("getWebView", m_id); ++ WId webViewId = reinterpret_cast(webController); ++ m_window = QWindow::fromWinId(webViewId); ++ ++ g_webViews->insert(m_id, this); ++ connect(qApp, &QGuiApplication::applicationStateChanged, ++ this, &QHarmonyWebViewPrivate::onApplicationStateChanged); ++ qWarning() << m_window << "<+++++++++++++++++++++:::" << webViewId; ++} ++ ++QHarmonyWebViewPrivate::~QHarmonyWebViewPrivate() ++{ ++ g_webViews->take(m_id); ++ if (m_window != Q_NULLPTR) { ++ m_window->setVisible(false); ++ m_window->setParent(Q_NULLPTR); ++ delete m_window; ++ } ++ m_jsWebView->callWithoutReturn("destroy", m_id); ++} ++ ++QString QHarmonyWebViewPrivate::httpUserAgent() const ++{ ++ return QString(m_jsWebView->call("getUserAgent", m_id)); ++} ++ ++void QHarmonyWebViewPrivate::setHttpUserAgent(const QString &userAgent) ++{ ++ m_jsWebView->callWithoutReturn("setCustomUserAgent", m_id, userAgent); ++ Q_EMIT httpUserAgentChanged(userAgent); ++} ++ ++QUrl QHarmonyWebViewPrivate::url() const ++{ ++ return QUrl::fromUserInput(m_jsWebView->call("getUrl", m_id)); ++} ++ ++void QHarmonyWebViewPrivate::setUrl(const QUrl &url) ++{ ++ m_jsWebView->callWithoutReturn("loadUrl", m_id, url); ++} ++ ++void QHarmonyWebViewPrivate::loadHtml(const QString &html, const QUrl &baseUrl) ++{ ++ // const QJNIObjectPrivate &htmlString = QJNIObjectPrivate::fromString(html); ++ // const QJNIObjectPrivate &mimeTypeString = QJNIObjectPrivate::fromString(QLatin1String("text/html;charset=UTF-8")); ++ ++ // baseUrl.isEmpty() ? m_viewController.callMethod("loadData", ++ // "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", ++ // htmlString.object(), ++ // mimeTypeString.object(), ++ // 0) ++ ++ // : m_viewController.callMethod("loadDataWithBaseURL", ++ // "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", ++ // QJNIObjectPrivate::fromString(baseUrl.toString()).object(), ++ // htmlString.object(), ++ // mimeTypeString.object(), ++ // 0, ++ // 0); ++} ++ ++bool QHarmonyWebViewPrivate::canGoBack() const ++{ ++ return m_jsWebView->call("accessBackward", m_id); ++} ++ ++void QHarmonyWebViewPrivate::goBack() ++{ ++ return m_jsWebView->callWithoutReturn("backward", m_id); ++} ++ ++bool QHarmonyWebViewPrivate::canGoForward() const ++{ ++ return m_jsWebView->call("accessForward", m_id); ++} ++ ++void QHarmonyWebViewPrivate::goForward() ++{ ++ return m_jsWebView->callWithoutReturn("forward", m_id); ++} ++ ++void QHarmonyWebViewPrivate::reload() ++{ ++ m_jsWebView->callWithoutReturn("refresh", m_id); ++} ++ ++QString QHarmonyWebViewPrivate::title() const ++{ ++ return QString(m_jsWebView->call("getTitle", m_id)); ++} ++ ++void QHarmonyWebViewPrivate::setGeometry(const QRect &geometry) ++{ ++ if (m_window == 0) ++ return; ++ ++ m_window->setGeometry(geometry); ++} ++ ++void QHarmonyWebViewPrivate::setVisibility(QWindow::Visibility visibility) ++{ ++ m_window->setVisibility(visibility); ++} ++ ++void QHarmonyWebViewPrivate::runJavaScriptPrivate(const QString &script, ++ int callbackId) ++{ ++#if 0 ++ if (QtAndroidPrivate::androidSdkVersion() < 19) { ++ qWarning("runJavaScript() requires API level 19 or higher."); ++ if (callbackId == -1) ++ return; ++ ++ // Emit signal here to remove the callback. ++ Q_EMIT javaScriptResult(callbackId, QVariant()); ++ } ++ ++ m_viewController.callMethod("runJavaScript", ++ "(Ljava/lang/String;J)V", ++ static_cast(QJNIObjectPrivate::fromString(script).object()), ++ callbackId); ++#endif ++} ++ ++void QHarmonyWebViewPrivate::setVisible(bool visible) ++{ ++ if (nullptr == m_window) ++ return; ++ ++ m_window->setVisible(visible); ++} ++ ++int QHarmonyWebViewPrivate::loadProgress() const ++{ ++ return 100; ++ // return m_viewController.callMethod("getProgress"); ++} ++ ++bool QHarmonyWebViewPrivate::isLoading() const ++{ ++ return true; ++ // return m_viewController.callMethod("isLoading"); ++} ++ ++void QHarmonyWebViewPrivate::setParentView(QObject *view) ++{ ++ if (nullptr == m_window) ++ return; ++ ++ m_window->setParent(qobject_cast(view)); ++} ++ ++QObject *QHarmonyWebViewPrivate::parentView() const ++{ ++ if (m_window) ++ return m_window->parent(); ++ ++ return nullptr; ++} ++ ++void QHarmonyWebViewPrivate::stop() ++{ ++ m_jsWebView->callWithoutReturn("stop", m_id); ++} ++ ++//void QHarmonyWebViewPrivate::initialize() ++//{ ++// // TODO: ++//} ++ ++void QHarmonyWebViewPrivate::onApplicationStateChanged(Qt::ApplicationState state) ++{ ++#if 0 ++ if (QtAndroidPrivate::androidSdkVersion() < 11) ++ return; ++ ++ if (state == Qt::ApplicationActive) ++ m_viewController.callMethod("onResume"); ++ else ++ m_viewController.callMethod("onPause"); ++#endif ++} ++ ++QT_END_NAMESPACE ++#if 0 ++static void c_onRunJavaScriptResult(JNIEnv *env, ++ jobject thiz, ++ jlong id, ++ jlong callbackId, ++ jstring result) ++{ ++ Q_UNUSED(env) ++ Q_UNUSED(thiz) ++ ++ const WebViews &wv = (*g_webViews); ++ QHarmonyWebViewPrivate *wc = static_cast(wv[id]); ++ if (!wc) ++ return; ++ ++ const QString &resultString = QJNIObjectPrivate(result).toString(); ++ ++ // The result string is in JSON format, lets parse it to see what we got. ++ QJsonValue jsonValue; ++ const QByteArray &jsonData = "{ \"data\": " + resultString.toUtf8() + " }"; ++ QJsonParseError error; ++ const QJsonDocument &jsonDoc = QJsonDocument::fromJson(jsonData, &error); ++ if (error.error == QJsonParseError::NoError && jsonDoc.isObject()) { ++ const QJsonObject &object = jsonDoc.object(); ++ jsonValue = object.value(QStringLiteral("data")); ++ } ++ ++ Q_EMIT wc->javaScriptResult(int(callbackId), ++ jsonValue.isNull() ? resultString ++ : jsonValue.toVariant()); ++} ++ ++static void c_onPageFinished(JNIEnv *env, ++ jobject thiz, ++ jlong id, ++ jstring url) ++{ ++ Q_UNUSED(env) ++ Q_UNUSED(thiz) ++ const WebViews &wv = (*g_webViews); ++ QHarmonyWebViewPrivate *wc = wv[id]; ++ if (!wc) ++ return; ++ ++ QWebViewLoadRequestPrivate loadRequest(QUrl(QJNIObjectPrivate(url).toString()), ++ QWebView::LoadSucceededStatus, ++ QString()); ++ Q_EMIT wc->loadingChanged(loadRequest); ++} ++ ++static void c_onPageStarted(JNIEnv *env, ++ jobject thiz, ++ jlong id, ++ jstring url, ++ jobject icon) ++{ ++ Q_UNUSED(env) ++ Q_UNUSED(thiz) ++ Q_UNUSED(icon) ++ const WebViews &wv = (*g_webViews); ++ QHarmonyWebViewPrivate *wc = wv[id]; ++ if (!wc) ++ return; ++ QWebViewLoadRequestPrivate loadRequest(QUrl(QJNIObjectPrivate(url).toString()), ++ QWebView::LoadStartedStatus, ++ QString()); ++ Q_EMIT wc->loadingChanged(loadRequest); ++ ++// if (!icon) ++// return; ++ ++// QImage image; ++// if (favIcon(env, icon, &image)) ++// Q_EMIT wc->iconChanged(image); ++} ++ ++static void c_onProgressChanged(JNIEnv *env, ++ jobject thiz, ++ jlong id, ++ jint newProgress) ++{ ++ Q_UNUSED(env) ++ Q_UNUSED(thiz) ++ const WebViews &wv = (*g_webViews); ++ QHarmonyWebViewPrivate *wc = wv[id]; ++ if (!wc) ++ return; ++ ++ Q_EMIT wc->loadProgressChanged(newProgress); ++} ++ ++static void c_onReceivedIcon(JNIEnv *env, ++ jobject thiz, ++ jlong id, ++ jobject icon) ++{ ++ Q_UNUSED(env) ++ Q_UNUSED(thiz) ++ Q_UNUSED(id) ++ Q_UNUSED(icon) ++ ++ const WebViews &wv = (*g_webViews); ++ QHarmonyWebViewPrivate *wc = wv[id]; ++ if (!wc) ++ return; ++ ++ if (!icon) ++ return; ++ ++// QImage image; ++// if (favIcon(env, icon, &image)) ++// Q_EMIT wc->iconChanged(image); ++} ++ ++static void c_onReceivedTitle(JNIEnv *env, ++ jobject thiz, ++ jlong id, ++ jstring title) ++{ ++ Q_UNUSED(env) ++ Q_UNUSED(thiz) ++ const WebViews &wv = (*g_webViews); ++ QHarmonyWebViewPrivate *wc = wv[id]; ++ if (!wc) ++ return; ++ ++ const QString &qTitle = QJNIObjectPrivate(title).toString(); ++ Q_EMIT wc->titleChanged(qTitle); ++} ++ ++static void c_onReceivedError(JNIEnv *env, ++ jobject thiz, ++ jlong id, ++ jint errorCode, ++ jstring description, ++ jstring url) ++{ ++ Q_UNUSED(env) ++ Q_UNUSED(thiz) ++ Q_UNUSED(errorCode) ++ ++ const WebViews &wv = (*g_webViews); ++ QHarmonyWebViewPrivate *wc = wv[id]; ++ if (!wc) ++ return; ++ QWebViewLoadRequestPrivate loadRequest(QUrl(QJNIObjectPrivate(url).toString()), ++ QWebView::LoadFailedStatus, ++ QJNIObjectPrivate(description).toString()); ++ Q_EMIT wc->loadingChanged(loadRequest); ++} ++#endif ++ ++/* ++ * function for module exports ++ */ ++EXTERN_C_START ++static napi_value Init(napi_env env, napi_value exports) ++{ ++ static bool inited = false; ++ ++ if (!inited) { ++ napi_property_descriptor desc[] ={ ++ // DECLARE_NAPI_FUNCTION("startQtApplication", startQtApplication), ++ // DECLARE_NAPI_FUNCTION("setDisplayMetrics", setDisplayMetrics), ++ // DECLARE_NAPI_FUNCTION("updateApplicationState", updateApplicationState), ++ // DECLARE_NAPI_FUNCTION("setResourceManager", setResourceManager), ++ // DECLARE_NAPI_FUNCTION("quitQtApplication", quitQtApplication), ++ // DECLARE_NAPI_FUNCTION("qtMajorVersion", qtMajorVersion), ++ // DECLARE_NAPI_FUNCTION("setDeviceType", setDeviceType), ++ // DECLARE_NAPI_FUNCTION("setDisplayVersion", setDisplayVersion), ++ }; ++ NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); ++ ++ qJs::init(env); ++ inited = true; ++ } ++ return exports; ++} ++EXTERN_C_END ++ ++/* ++ * Napi Module define ++ */ ++static napi_module webviewPluginModule = { ++ .nm_version = 1, ++ .nm_flags = 0, ++ .nm_filename = nullptr, ++ .nm_register_func = Init, ++ .nm_modname = "plugins_webview_qtwebview_harmony", ++ .nm_priv = ((void*)0), ++ .reserved = { 0 }, ++}; ++/* ++ * Module register function ++ */ ++extern "C" __attribute__((constructor)) void RegisterModule(void) ++{ ++ napi_module_register(&webviewPluginModule); ++} +diff --git a/src/plugins/harmony/qharmonywebview_p.h b/src/plugins/harmony/qharmonywebview_p.h +new file mode 100644 +index 0000000..5238dc8 +--- /dev/null ++++ b/src/plugins/harmony/qharmonywebview_p.h +@@ -0,0 +1,109 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2015 The Qt Company Ltd. ++** Contact: http://www.qt.io/licensing/ ++** ++** This file is part of the QtWebView module of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:LGPL3$ ++** 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 http://www.qt.io/terms-conditions. For further ++** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free ++** Software Foundation and appearing in the file LICENSE.GPL included in ++** the packaging of this file. Please review the following information to ++** ensure the GNU General Public License version 2.0 requirements will be ++** met: http://www.gnu.org/licenses/gpl-2.0.html. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#ifndef QHARMONYWEBVIEW_P_H ++#define QHARMONYWEBVIEW_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. ++// ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "QtCore/qopenharmonyjsobject.h" ++ ++QT_BEGIN_NAMESPACE ++ ++class QHarmonyWebViewPrivate : public QAbstractWebView ++{ ++ Q_OBJECT ++public: ++ explicit QHarmonyWebViewPrivate(QObject *p = 0); ++ ~QHarmonyWebViewPrivate() Q_DECL_OVERRIDE; ++ ++ QString httpUserAgent() const Q_DECL_OVERRIDE; ++ void setHttpUserAgent(const QString &httpUserAgent) Q_DECL_OVERRIDE; ++ QUrl url() const Q_DECL_OVERRIDE; ++ void setUrl(const QUrl &url) Q_DECL_OVERRIDE; ++ bool canGoBack() const Q_DECL_OVERRIDE; ++ bool canGoForward() const Q_DECL_OVERRIDE; ++ QString title() const Q_DECL_OVERRIDE; ++ int loadProgress() const Q_DECL_OVERRIDE; ++ bool isLoading() const Q_DECL_OVERRIDE; ++ ++ void setParentView(QObject *view) Q_DECL_OVERRIDE; ++ QObject *parentView() const Q_DECL_OVERRIDE; ++ void setGeometry(const QRect &geometry) Q_DECL_OVERRIDE; ++ void setVisibility(QWindow::Visibility visibility) Q_DECL_OVERRIDE; ++ void setVisible(bool visible) Q_DECL_OVERRIDE; ++ ++public Q_SLOTS: ++ void goBack() Q_DECL_OVERRIDE; ++ void goForward() Q_DECL_OVERRIDE; ++ void reload() Q_DECL_OVERRIDE; ++ void stop() Q_DECL_OVERRIDE; ++ void loadHtml(const QString &html, const QUrl &baseUrl = QUrl()) Q_DECL_OVERRIDE; ++ ++protected: ++ void runJavaScriptPrivate(const QString& script, ++ int callbackId) Q_DECL_OVERRIDE; ++ ++private Q_SLOTS: ++ void onApplicationStateChanged(Qt::ApplicationState state); ++ ++private: ++ quintptr m_id; ++ quint64 m_callbackId; ++ QWindow *m_window; ++ QSharedPointer m_jsWebView; ++ // QJNIObjectPrivate m_viewController; ++ // QJNIObjectPrivate m_webView; ++}; ++ ++QT_END_NAMESPACE ++ ++#endif // QHARMONYWEBVIEW_P_H +diff --git a/src/plugins/harmony/qharmonywebviewplugin.cpp b/src/plugins/harmony/qharmonywebviewplugin.cpp +new file mode 100644 +index 0000000..e9bfdc1 +--- /dev/null ++++ b/src/plugins/harmony/qharmonywebviewplugin.cpp +@@ -0,0 +1,56 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2016 The Qt Company Ltd. ++** Contact: http://www.qt.io/licensing/ ++** ++** This file is part of the QtWebView module of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:LGPL3$ ++** 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 http://www.qt.io/terms-conditions. For further ++** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free ++** Software Foundation and appearing in the file LICENSE.GPL included in ++** the packaging of this file. Please review the following information to ++** ensure the GNU General Public License version 2.0 requirements will be ++** met: http://www.gnu.org/licenses/gpl-2.0.html. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#include "qharmonywebview_p.h" ++#include ++ ++QT_BEGIN_NAMESPACE ++ ++class QAndroidWebViewPlugin : public QWebViewPlugin ++{ ++ Q_OBJECT ++ Q_PLUGIN_METADATA(IID QWebViewPluginInterface_iid FILE "harmony.json") ++ ++public: ++ QAbstractWebView *create(const QString &key) const override ++ { ++ return (key == QLatin1String("webview")) ? new QHarmonyWebViewPrivate() : nullptr; ++ } ++}; ++ ++QT_END_NAMESPACE ++ ++#include "qharmonywebviewplugin.moc" +diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro +index 60c5078..c3a8fae 100644 +--- a/src/plugins/plugins.pro ++++ b/src/plugins/plugins.pro +@@ -4,6 +4,8 @@ android { + SUBDIRS += android + } else:if(ios|macos) { + SUBDIRS += darwin ++} else:if(openharmony) { ++ SUBDIRS += harmony + } else:winrt { + SUBDIRS += winrt + } +diff --git a/src/src.pro b/src/src.pro +index 7bc2ef7..62580d4 100644 +--- a/src/src.pro ++++ b/src/src.pro +@@ -5,3 +5,7 @@ plugins.depends = webview + imports.depends = webview + + android: SUBDIRS += jar ++ ++openharmony { ++ SUBDIRS += openharmony ++}