SDL: harmonyos: Port SDL3 to HarmonyOS/OpenHarmony.

From a24429c192cfee5ae92697f12f219cad27635fb4 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Tue, 9 Jun 2026 00:52:31 -0400
Subject: [PATCH] harmonyos: Port SDL3 to HarmonyOS/OpenHarmony.

This is my port of SDL3 to HarmonyOS/OpenHarmony. It has been tested on real
hardware from Huawei, and commercial games are being prepped to ship with it
already.

There is _heavy_ documentation in docs/README-harmonyos.md, including how to
set up a development environment and project, build, and debug, for the
completely uninitiated.

This work was sponsored by Outfit7: https://outfit7.com/

Fixes #9837.
---
 .gitignore                                    |   10 +
 CMakeLists.txt                                |  169 ++-
 build-scripts/harmonyos-tool.sh               |  209 +++
 cmake/PreseedOpenHarmonyCache.cmake           |  188 +++
 cmake/macros.cmake                            |    2 +-
 cmake/sdlcompilers.cmake                      |    5 +
 docs/README-harmonyos.md                      |  872 ++++++++++++
 docs/README-platforms.md                      |    1 +
 include/SDL3/SDL_iostream.h                   |   13 +-
 include/SDL3/SDL_keyboard.h                   |    7 +
 include/SDL3/SDL_main.h                       |   13 +
 include/SDL3/SDL_main_impl.h                  |    7 +
 include/SDL3/SDL_platform_defines.h           |   11 +
 include/SDL3/SDL_system.h                     |   95 +-
 include/SDL3/SDL_video.h                      |   12 +
 include/build_config/SDL_build_config.h.cmake |    5 +
 openharmony-project/.gitignore                |   12 +
 openharmony-project/AppScope/app.json5        |   11 +
 .../resources/base/element/string.json        |    8 +
 .../resources/base/media/background.png       |  Bin 0 -> 91942 bytes
 .../resources/base/media/foreground.png       |  Bin 0 -> 15325 bytes
 .../resources/base/media/layered_image.json   |    7 +
 openharmony-project/build-profile.json5       |   42 +
 openharmony-project/code-linter.json5         |   32 +
 openharmony-project/entry/.gitignore          |    6 +
 openharmony-project/entry/build-profile.json5 |   45 +
 openharmony-project/entry/hvigorfile.ts       |    6 +
 .../entry/obfuscation-rules.txt               |   23 +
 .../entry/oh-package-lock.json5               |   19 +
 openharmony-project/entry/oh-package.json5    |   11 +
 .../entry/src/main/cpp/CMakeLists.txt         |   18 +
 .../src/main/cpp/types/libSDL3/Index.d.ts     |    1 +
 .../main/cpp/types/libSDL3/oh-package.json5   |    7 +
 .../main/ets/entryability/EntryAbility.ets    |   28 +
 .../entrybackupability/EntryBackupAbility.ets |   16 +
 .../entry/src/main/ets/pages/Index.ets        |   15 +
 .../entry/src/main/module.json5               |   67 +
 .../main/resources/base/element/color.json    |    8 +
 .../main/resources/base/element/float.json    |    8 +
 .../main/resources/base/element/string.json   |   33 +
 .../main/resources/base/media/background.png  |  Bin 0 -> 91942 bytes
 .../main/resources/base/media/foreground.png  |  Bin 0 -> 8805 bytes
 .../resources/base/media/layered_image.json   |    7 +
 .../main/resources/base/media/startIcon.png   |  Bin 0 -> 20093 bytes
 .../resources/base/profile/backup_config.json |    3 +
 .../resources/base/profile/main_pages.json    |    5 +
 .../main/resources/dark/element/color.json    |    8 +
 .../hvigor/hvigor-config.json5                |   23 +
 openharmony-project/hvigorfile.ts             |    6 +
 openharmony-project/oh-package-lock.json5     |   28 +
 openharmony-project/oh-package.json5          |   10 +
 src/SDL.c                                     |    7 +
 src/SDL_log.c                                 |   29 +-
 src/audio/SDL_audio.c                         |    3 +
 src/audio/SDL_sysaudio.h                      |    1 +
 src/audio/ohaudio/SDL_ohaudio.c               |  648 +++++++++
 src/audio/ohaudio/SDL_ohaudio.h               |   38 +
 src/audio/ohaudio/SDL_ohaudiofuncs.h          |   62 +
 src/camera/SDL_camera.c                       |    3 +
 src/camera/SDL_syscamera.h                    |    1 +
 .../openharmony/SDL_camera_openharmony.c      |  775 +++++++++++
 src/core/SDL_core_unsupported.c               |   22 +
 src/core/SDL_core_unsupported.h               |    8 +
 src/core/android/SDL_android.h                |    4 +
 src/core/openharmony/SDL_openharmony.c        | 1207 +++++++++++++++++
 src/core/openharmony/SDL_openharmony.h        |   66 +
 src/cpuinfo/SDL_cpuinfo.c                     |    2 +-
 src/dynapi/SDL_dynapi.exports                 |    3 +
 src/dynapi/SDL_dynapi.h                       |    2 +
 src/dynapi/SDL_dynapi.sym                     |    3 +
 src/dynapi/SDL_dynapi_overrides.h             |    3 +
 src/dynapi/SDL_dynapi_procs.h                 |    3 +
 src/events/SDL_keyboard.c                     |    6 +-
 src/filesystem/SDL_filesystem.c               |    2 +-
 .../openharmony/SDL_sysfilesystem.c           |   78 ++
 src/filesystem/posix/SDL_sysfsops.c           |   36 +-
 src/io/SDL_iostream.c                         |   38 +-
 src/locale/openharmony/SDL_syslocale.c        |   36 +
 src/locale/psp/SDL_syslocale.c                |    1 -
 src/main/SDL_main_callbacks.c                 |    9 +-
 src/main/generic/SDL_sysmain_callbacks.c      |    1 +
 src/main/openharmony/SDL_sysmain_callbacks.c  |   60 +
 src/main/openharmony/SDL_sysmain_runapp.c     |  102 ++
 src/misc/android/SDL_sysurl.c                 |    5 +
 src/misc/openharmony/SDL_sysurl.c             |   34 +
 src/power/SDL_power.c                         |    3 +
 src/power/SDL_syspower.h                      |    1 +
 src/power/openharmony/SDL_syspower.c          |   46 +
 src/render/SDL_render.c                       |    2 +-
 src/thread/pthread/SDL_systhread.c            |   16 +-
 src/video/SDL_egl.c                           |    4 +
 src/video/SDL_sysvideo.h                      |    1 +
 src/video/SDL_video.c                         |    3 +
 src/video/SDL_vulkan_internal.h               |    3 +
 .../openharmony/SDL_openharmonyclipboard.c    |  220 +++
 .../openharmony/SDL_openharmonyclipboard.h    |   34 +
 src/video/openharmony/SDL_openharmonyevents.c |  574 ++++++++
 src/video/openharmony/SDL_openharmonyevents.h |   33 +
 .../openharmony/SDL_openharmonykeyboard.c     |   93 ++
 .../openharmony/SDL_openharmonykeyboard.h     |   29 +
 src/video/openharmony/SDL_openharmonymouse.c  |   76 ++
 src/video/openharmony/SDL_openharmonymouse.h  |   30 +
 src/video/openharmony/SDL_openharmonyopengl.c |   79 ++
 src/video/openharmony/SDL_openharmonyopengl.h |   31 +
 src/video/openharmony/SDL_openharmonyvideo.c  |  280 ++++
 src/video/openharmony/SDL_openharmonyvideo.h  |   47 +
 src/video/openharmony/SDL_openharmonyvulkan.c |  174 +++
 src/video/openharmony/SDL_openharmonyvulkan.h |   52 +
 src/video/openharmony/SDL_openharmonywindow.c |  187 +++
 src/video/openharmony/SDL_openharmonywindow.h |   51 +
 test/CMakeLists.txt                           |    4 +-
 test/testclipboard.c                          |    6 +-
 test/testsymbols.c                            |    6 +
 test/testurl.c                                |    4 +-
 test/testvulkan.c                             |   90 +-
 115 files changed, 7518 insertions(+), 91 deletions(-)
 create mode 100755 build-scripts/harmonyos-tool.sh
 create mode 100644 cmake/PreseedOpenHarmonyCache.cmake
 create mode 100644 docs/README-harmonyos.md
 create mode 100644 openharmony-project/.gitignore
 create mode 100644 openharmony-project/AppScope/app.json5
 create mode 100644 openharmony-project/AppScope/resources/base/element/string.json
 create mode 100644 openharmony-project/AppScope/resources/base/media/background.png
 create mode 100644 openharmony-project/AppScope/resources/base/media/foreground.png
 create mode 100644 openharmony-project/AppScope/resources/base/media/layered_image.json
 create mode 100644 openharmony-project/build-profile.json5
 create mode 100644 openharmony-project/code-linter.json5
 create mode 100644 openharmony-project/entry/.gitignore
 create mode 100644 openharmony-project/entry/build-profile.json5
 create mode 100644 openharmony-project/entry/hvigorfile.ts
 create mode 100644 openharmony-project/entry/obfuscation-rules.txt
 create mode 100644 openharmony-project/entry/oh-package-lock.json5
 create mode 100644 openharmony-project/entry/oh-package.json5
 create mode 100644 openharmony-project/entry/src/main/cpp/CMakeLists.txt
 create mode 100644 openharmony-project/entry/src/main/cpp/types/libSDL3/Index.d.ts
 create mode 100644 openharmony-project/entry/src/main/cpp/types/libSDL3/oh-package.json5
 create mode 100644 openharmony-project/entry/src/main/ets/entryability/EntryAbility.ets
 create mode 100644 openharmony-project/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets
 create mode 100644 openharmony-project/entry/src/main/ets/pages/Index.ets
 create mode 100644 openharmony-project/entry/src/main/module.json5
 create mode 100644 openharmony-project/entry/src/main/resources/base/element/color.json
 create mode 100644 openharmony-project/entry/src/main/resources/base/element/float.json
 create mode 100644 openharmony-project/entry/src/main/resources/base/element/string.json
 create mode 100644 openharmony-project/entry/src/main/resources/base/media/background.png
 create mode 100644 openharmony-project/entry/src/main/resources/base/media/foreground.png
 create mode 100644 openharmony-project/entry/src/main/resources/base/media/layered_image.json
 create mode 100644 openharmony-project/entry/src/main/resources/base/media/startIcon.png
 create mode 100644 openharmony-project/entry/src/main/resources/base/profile/backup_config.json
 create mode 100644 openharmony-project/entry/src/main/resources/base/profile/main_pages.json
 create mode 100644 openharmony-project/entry/src/main/resources/dark/element/color.json
 create mode 100644 openharmony-project/hvigor/hvigor-config.json5
 create mode 100644 openharmony-project/hvigorfile.ts
 create mode 100644 openharmony-project/oh-package-lock.json5
 create mode 100644 openharmony-project/oh-package.json5
 create mode 100644 src/audio/ohaudio/SDL_ohaudio.c
 create mode 100644 src/audio/ohaudio/SDL_ohaudio.h
 create mode 100644 src/audio/ohaudio/SDL_ohaudiofuncs.h
 create mode 100644 src/camera/openharmony/SDL_camera_openharmony.c
 create mode 100644 src/core/openharmony/SDL_openharmony.c
 create mode 100644 src/core/openharmony/SDL_openharmony.h
 create mode 100644 src/filesystem/openharmony/SDL_sysfilesystem.c
 create mode 100644 src/locale/openharmony/SDL_syslocale.c
 create mode 100644 src/main/openharmony/SDL_sysmain_callbacks.c
 create mode 100644 src/main/openharmony/SDL_sysmain_runapp.c
 create mode 100644 src/misc/openharmony/SDL_sysurl.c
 create mode 100644 src/power/openharmony/SDL_syspower.c
 create mode 100644 src/video/openharmony/SDL_openharmonyclipboard.c
 create mode 100644 src/video/openharmony/SDL_openharmonyclipboard.h
 create mode 100644 src/video/openharmony/SDL_openharmonyevents.c
 create mode 100644 src/video/openharmony/SDL_openharmonyevents.h
 create mode 100644 src/video/openharmony/SDL_openharmonykeyboard.c
 create mode 100644 src/video/openharmony/SDL_openharmonykeyboard.h
 create mode 100644 src/video/openharmony/SDL_openharmonymouse.c
 create mode 100644 src/video/openharmony/SDL_openharmonymouse.h
 create mode 100644 src/video/openharmony/SDL_openharmonyopengl.c
 create mode 100644 src/video/openharmony/SDL_openharmonyopengl.h
 create mode 100644 src/video/openharmony/SDL_openharmonyvideo.c
 create mode 100644 src/video/openharmony/SDL_openharmonyvideo.h
 create mode 100644 src/video/openharmony/SDL_openharmonyvulkan.c
 create mode 100644 src/video/openharmony/SDL_openharmonyvulkan.h
 create mode 100644 src/video/openharmony/SDL_openharmonywindow.c
 create mode 100644 src/video/openharmony/SDL_openharmonywindow.h

diff --git a/.gitignore b/.gitignore
index 6ae2fd4100812..b419000db2b78 100644
--- a/.gitignore
+++ b/.gitignore
@@ -99,6 +99,16 @@ out/
 android-project/local.properties
 android-project/.gradle/
 
+# for OpenHarmony/HarmonyOS
+openharmony-project/entry/src/main/cpp/CMakeLists.txt
+openharmony-project/entry/src/main/cpp/*.c
+openharmony-project/entry/src/main/cpp/*.h
+openharmony-project/entry/src/main/cpp/SDL
+openharmony-project/entry/src/main/resources/rawfile
+openharmony-project/AppScope/app.json5
+openharmony-project/AppScope/resources/base/element/string.json
+openharmony-project/build-profile.json5
+
 test/checkkeys
 test/checkkeysthreads
 test/gamepadmap
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4cabec1374754..d0095516e16ac 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -78,6 +78,7 @@ include("${SDL3_SOURCE_DIR}/cmake/GetGitRevisionDescription.cmake")
 include("${SDL3_SOURCE_DIR}/cmake/3rdparty.cmake")
 include("${SDL3_SOURCE_DIR}/cmake/PreseedMSVCCache.cmake")
 include("${SDL3_SOURCE_DIR}/cmake/PreseedEmscriptenCache.cmake")
+include("${SDL3_SOURCE_DIR}/cmake/PreseedOpenHarmonyCache.cmake")
 include("${SDL3_SOURCE_DIR}/cmake/PreseedHaikuCache.cmake")
 include("${SDL3_SOURCE_DIR}/cmake/PreseedNokiaNGageCache.cmake")
 include("${SDL3_SOURCE_DIR}/cmake/PreseedDOSCache.cmake")
@@ -322,7 +323,7 @@ dep_option(SDL_SSE4_2              "Use SSE4.2 assembly routines" ON "SDL_ASSEMB
 dep_option(SDL_MMX                 "Use MMX assembly routines" ON "SDL_ASSEMBLY;SDL_CPU_X86 OR SDL_CPU_X64" OFF)
 dep_option(SDL_ALTIVEC             "Use Altivec assembly routines" ON "SDL_ASSEMBLY;SDL_CPU_POWERPC32 OR SDL_CPU_POWERPC64" OFF)
 dep_option(SDL_ARMNEON             "Use NEON assembly routines" ON "SDL_ASSEMBLY;SDL_CPU_ARM32 OR SDL_CPU_ARM64" OFF)
-dep_option(SDL_ARMSVE2             "Use SVE2 assembly routines" ON "SDL_ASSEMBLY;SDL_CPU_ARM64" OFF)
+dep_option(SDL_ARMSVE2             "Use SVE2 assembly routines" ON "SDL_ASSEMBLY;SDL_CPU_ARM64 AND NOT OHOS" OFF)
 dep_option(SDL_LSX                 "Use LSX assembly routines" ON "SDL_ASSEMBLY;SDL_CPU_LOONGARCH64" OFF)
 dep_option(SDL_LASX                "Use LASX assembly routines" ON "SDL_ASSEMBLY;SDL_CPU_LOONGARCH64" OFF)
 
@@ -385,7 +386,7 @@ dep_option(SDL_RENDER_D3D12        "Enable the Direct3D 12 render driver" ON "SD
 dep_option(SDL_RENDER_METAL        "Enable the Metal render driver" ON "SDL_RENDER;APPLE" OFF)
 dep_option(SDL_RENDER_GPU          "Enable the SDL_GPU render driver" ON "SDL_RENDER;SDL_GPU" OFF)
 dep_option(SDL_VIVANTE             "Use Vivante EGL video driver" ON "${UNIX_SYS};SDL_CPU_ARM32" OFF)
-dep_option(SDL_VULKAN              "Enable Vulkan support" "${SDL_VULKAN_DEFAULT}" "SDL_VIDEO;ANDROID OR APPLE OR LINUX OR FREEBSD OR OPENBSD OR WINDOWS OR CYGWIN" OFF)
+dep_option(SDL_VULKAN              "Enable Vulkan support" "${SDL_VULKAN_DEFAULT}" "SDL_VIDEO;ANDROID OR APPLE OR LINUX OR FREEBSD OR OPENBSD OR WINDOWS OR CYGWIN OR OHOS" OFF)
 dep_option(SDL_RENDER_VULKAN       "Enable the Vulkan render driver" ON "SDL_RENDER;SDL_VULKAN" OFF)
 dep_option(SDL_METAL               "Enable Metal support" ON "APPLE" OFF)
 set_option(SDL_OPENVR              "Use OpenVR video driver" OFF)
@@ -1724,6 +1725,170 @@ if(ANDROID)
     target_link_options(SDL3-static INTERFACE "-Wl,-u,JNI_OnLoad")
   endif()
 
+elseif(OHOS)  # OpenHarmony/HarmonyOS
+  sdl_glob_sources(
+    "${SDL3_SOURCE_DIR}/src/core/openharmony/*.c"
+    "${SDL3_SOURCE_DIR}/src/core/openharmony/*.h"
+  )
+
+  sdl_link_dependency(openharmonycore LIBS libace_napi.z.so libace_ndk.z.so deviceinfo_ndk.z rawfile.z hilog_ndk.z ohcommonevent)
+
+  sdl_glob_sources(
+    "${SDL3_SOURCE_DIR}/src/main/openharmony/*.c"
+    "${SDL3_SOURCE_DIR}/src/main/openharmony/*.h"
+  )
+  set(HAVE_SDL_MAIN_CALLBACKS TRUE)
+
+  sdl_glob_sources(
+    "${SDL3_SOURCE_DIR}/src/misc/openharmony/*.c"
+    "${SDL3_SOURCE_DIR}/src/misc/openharmony/*.h"
+  )
+  set(HAVE_SDL_MISC TRUE)
+
+  if(SDL_AUDIO)
+    set(SDL_AUDIO_DRIVER_OHAUDIO 1)
+    sdl_glob_sources(
+      "${SDL3_SOURCE_DIR}/src/audio/ohaudio/*.c"
+      "${SDL3_SOURCE_DIR}/src/audio/ohaudio/*.h"
+    )
+
+    set(HAVE_SDL_AUDIO TRUE)
+  endif()
+
+  set(SDL_FILESYSTEM_OPENHARMONY 1)
+  sdl_glob_sources(
+    "${SDL3_SOURCE_DIR}/src/filesystem/openharmony/*.c"
+    "${SDL3_SOURCE_DIR}/src/filesystem/openharmony/*.h"
+  )
+  sdl_link_dependency(openharmonyfs LIBS ability_runtime)
+  set(HAVE_SDL_FILESYSTEM TRUE)
+
+  set(SDL_FSOPS_POSIX 1)
+  sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
+  set(HAVE_SDL_FSOPS TRUE)
+
+if (False)   # !!! FIXME
+  if(SDL_HAPTIC)
+    set(SDL_HAPTIC_OPENHARMONY 1)
+    sdl_glob_sources(
+      "${SDL3_SOURCE_DIR}/src/haptic/openharmony/*.c"
+      "${SDL3_SOURCE_DIR}/src/haptic/openharmony/*.h"
+    )
+    set(HAVE_SDL_HAPTIC TRUE)
+  endif()
+
+  # !!! FIXME: any chance HIDAPI would work here?
+  CheckHIDAPI()
+
+  if(SDL_JOYSTICK)
+    set(SDL_JOYSTICK_OPENHARMONY 1)
+    sdl_glob_sources(
+      "${SDL3_SOURCE_DIR}/src/joystick/openharmony/*.c"
+      "${SDL3_SOURCE_DIR}/src/joystick/openharmony/*.h"
+    )
+    set(HAVE_SDL_JOYSTICK TRUE)
+  endif()
+endif()
+
+  set(SDL_LOADSO_DLOPEN 1)
+  sdl_glob_sources(
+    "${SDL3_SOURCE_DIR}/src/loadso/dlopen/*.c"
+    "${SDL3_SOURCE_DIR}/src/loadso/dlopen/*.h"
+  )
+  set(HAVE_SDL_LOADSO TRUE)
+
+  if(SDL_POWER)
+    set(SDL_POWER_OPENHARMONY 1)
+    sdl_glob_sources(
+      "${SDL3_SOURCE_DIR}/src/power/openharmony/*.c"
+      "${SDL3_SOURCE_DIR}/src/power/openharmony/*.h"
+    )
+    sdl_link_dependency(openharmonypower LIBS ohbattery_info)
+    set(HAVE_SDL_POWER TRUE)
+  endif()
+
+  sdl_glob_sources(
+    "${SDL3_SOURCE_DIR}/src/locale/openharmony/*.c"
+    "${SDL3_SOURCE_DIR}/src/locale/openharmony/*.h"
+  )
+  set(HAVE_SDL_LOCALE TRUE)
+
+  set(SDL_TIME_UNIX 1)
+  sdl_glob_sources(
+    "${SDL3_SOURCE_DIR}/src/time/unix/*.c"
+    "${SDL3_SOURCE_DIR}/src/time/unix/*.h"
+  )
+  set(HAVE_SDL_TIME TRUE)
+
+  set(SDL_TIMER_UNIX 1)
+  sdl_glob_sources(
+    "${SDL3_SOURCE_DIR}/src/timer/unix/*.c"
+    "${SDL3_SOURCE_DIR}/src/timer/unix/*.h"
+  )
+  set(HAVE_SDL_TIMERS TRUE)
+
+if(False)
+  if(SDL_SENSOR)
+    set(SDL_SENSOR_OPENHARMONY 1)
+    set(HAVE_SDL_SENSORS TRUE)
+    sdl_glob_sources(
+      "${SDL3_SOURCE_DIR}/src/sensor/openharmony/*.c"
+      "${SDL3_SOURCE_DIR}/src/sensor/openharmony/*.h"
+    )
+  endif()
+endif()
+
+  if(SDL_CAMERA)
+    set(SDL_CAMERA_DRIVER_OPENHARMONY 1)
+    set(HAVE_CAMERA TRUE)
+    sdl_glob_sources(
+      "${SDL3_SOURCE_DIR}/src/camera/openharmony/*.c"
+      "${SDL3_SOURCE_DIR}/src/camera/openharmony/*.h"
+    )
+    sdl_link_dependency(openharmonycamera LIBS ohcamera ohimage image_receiver native_image native_buffer)
+  endif()
+
+  if(SDL_VIDEO)
+    set(SDL_VIDEO_DRIVER_OPENHARMONY 1)
+    sdl_glob_sources(
+      "${SDL3_SOURCE_DIR}/src/video/openharmony/*.c"
+      "${SDL3_SOURCE_DIR}/src/video/openharmony/*.h"
+    )
+    set(HAVE_SDL_VIDEO TRUE)
+    sdl_link_dependency(openharmonyvideo LIBS native_display_manager pasteboard udmf)
+
+    # Core stuff
+    # find_library(ANDROID_DL_LIBRARY dl)
+    # FIXME failing dlopen https://github.com/android-ndk/ndk/issues/929
+# !!! FIXME
+#    sdl_link_dependency(android_video LIBS dl log android)
+    sdl_compile_definitions(PRIVATE "GL_GLEXT_PROTOTYPES")
+
+    #enable gles
+    if(SDL_OPENGLES)
+      set(SDL_VIDEO_OPENGL_EGL 1)
+      set(HAVE_OPENGLES TRUE)
+      set(SDL_VIDEO_OPENGL_ES2 1)
+      set(SDL_VIDEO_RENDER_OGL_ES2 1)
+      #sdl_link_dependency(opengles LIBS GLESv3)
+    endif()
+
+    if(SDL_VULKAN)
+      set(SDL_VIDEO_VULKAN 1)
+      set(HAVE_VULKAN TRUE)
+      if(SDL_RENDER_VULKAN)
+        set(SDL_VIDEO_RENDER_VULKAN 1)
+        set(HAVE_RENDER_VULKAN TRUE)
+      endif()
+    endif()
+  endif()
+
+  CheckPTHREAD()
+
+  if(SDL_CLOCK_GETTIME)
+    set(HAVE_CLOCK_GETTIME 1)
+  endif()
+
 elseif(EMSCRIPTEN)
   # Hide noisy warnings that intend to aid mostly during initial stages of porting a new
   # project. Uncomment at will for verbose cross-compiling -I/../ path info.
diff --git a/build-scripts/harmonyos-tool.sh b/build-scripts/harmonyos-tool.sh
new file mode 100755
index 0000000000000..684b7241dbf93
--- /dev/null
+++ b/build-scripts/harmonyos-tool.sh
@@ -0,0 +1,209 @@
+#!/usr/bin/bash
+
+HVIGORWPATH=`which hvigorw 2>/dev/null`
+if [ "x$HVIGORWPATH" == "x" ]; then
+    echo "Couldn't find hvigorw!"
+    echo "Please make sure the HarmonyOS command line tools are in your PATH." 1>&2
+    exit 1
+fi
+
+HDCPATH=`which hdc 2>/dev/null`
+if [ "x$HDCPATH" == "x" ]; then
+    echo "Couldn't find hdc!"
+    echo "Please make sure the HarmonyOS command line tools are in your PATH." 1>&2
+    exit 1
+fi
+
+CLTBINPATH=`dirname "$HVIGORWPATH"`
+CLTPATH=`dirname "$CLTBINPATH"`
+CLT=`realpath "$CLTPATH"`
+#echo "CLT is '$CLT'"
+
+need_usage=0
+
+APPJSON=""
+PROJDIR="$1"
+if [ "x$PROJDIR" == "x" ]; then
+    need_usage=1
+else
+    PROJDIR=`realpath "$PROJDIR"`
+    APPJSON="$PROJDIR/AppScope/app.json5"
+    if [ ! -f "$APPJSON" ]; then
+        echo "Can't find '$APPJSON'. Is project directory correct?" 1>&2
+        echo "" 1>&2
+        need_usage=1
+    fi
+fi
+
+do_clean=0
+do_build=0
+do_uninstall=0
+do_install=0
+do_kill=0
+do_launch=0
+do_log=0
+do_debug=0
+do_anything=0
+
+shift  # dump projectdir
+while [ $# -gt 0 ]; do
+    do_anything=1
+    case $1 in
+        --clean)
+            do_clean=1
+            shift
+            ;;
+        --build)
+            do_build=1
+            shift
+            ;;
+        --install)
+            do_install=1
+            shift
+            ;;
+        --uninstall)
+            do_uninstall=1
+            shift
+            ;;
+        --kill)
+            do_kill=1
+            shift
+            ;;
+        --launch)
+            do_launch=1
+            shift
+            ;;
+        --log)
+            do_log=1
+            shift
+            ;;
+        --debug)
+            do_launch=1
+            do_debug=1
+            shift
+            ;;
+        *)
+            echo "Unknown option '$1'" 1>&2
+            need_usage=1
+            shift
+            ;;
+    esac
+done
+
+if [ "$do_anything" == "0" ]; then
+    need_usage=1
+fi
+
+if [ "$need_usage" == "1" ]; then
+    echo "USAGE: $0 <project_dir> [--kill] [--uninstall] [--build] [--install] [--launch] [--debug] [--log]" 1>&2
+    echo "" 1>&2
+    exit 1
+fi
+
+if [ "$do_build" == "1" ]; then
+    if [ "$do_debug" == "1" ]; then
+        do_install=1
+    elif [ "$do_launch" == "1" ]; then
+        do_install=1
+    fi
+fi
+
+# Here we go!
+
+BUNDLE=`grep -F bundleName "$PROJDIR/AppScope/app.json5" |perl -w -p -e 's/\A\s*\"bundleName\"\s*\:\s*\"(.*?)\".*?\Z/$1/i;'`
+if [ "x$BUNDLE" == "x" ]; then
+    echo "Failed to determine bundle name! Aborting!" 1>&2
+    exit 1
+fi
+
+echo "BUNDLE is '$BUNDLE'"
+
+#set -x
+
+cd "$PROJDIR"
+
+if [ "$do_kill" == "1" ]; then
+    echo "KILLING..."
+    hdc shell aa force-stop $BUNDLE || exit 1
+fi
+
+if [ "$do_uninstall" == "1" ]; then
+    echo "UNINSTALLING..."
+    hdc uninstall $BUNDLE || exit 1
+fi
+
+if [ "$do_clean" == "1" ]; then
+    echo "CLEANING..."
+    hvigorw clean || exit 1
+fi
+
+if [ "$do_build" == "1" ]; then
+    echo "BUILDING..."
+    hvigorw assembleHap || exit 1
+fi
+
+if [ "$do_debug" == "1" ]; then
+    hdc shell mkdir -p data/local/tmp/debugserver/$BUNDLE
+    hdc file send "$CLT/sdk/default/hms/native/lldb/aarch64-linux-ohos/lldb-server" data/local/tmp/debugserver/$BUNDLE
+    hdc shell chmod 755 data/local/tmp/debugserver/$BUNDLE/lldb-server
+fi
+
+if [ "$do_install" == "1" ]; then
+    echo "INSTALLING..."
+    hdc install "$PROJDIR/entry/build/default/outputs/default/entry-default-signed.hap" || exit 1
+fi
+
+# Catch sigint, kill the backgrounded `hdc hilog` then exit.
+trap_sigint() {
+    if [ "$HILOGPID" != "" ]; then
+        kill -9 "$HILOGPID"
+    fi
+    wait "$HILOGPID" 2>/dev/null
+    echo ""
+    #echo "Killed $HILOGPID"
+    exit 0
+}
+
+HILOGPID=""
+if [ "$do_log" == "1" ]; then
+    trap trap_sigint INT
+
+    # This wild bash syntax puts `hdc` in the background, and pipes its output through `grep`, but `$!` will have hdc's process ID.
+    # The ridiculous perl regular expression basically filters out a ton of garbage from each line of the log. You can remove it entirely and just grep for the bundle name, though!
+    hdc hilog > >(grep -F --line-buffered "/$BUNDLE/" |perl -w -p -e 's/\A\d+\-\d+\s+(\d+\:\d+\:\d+)\.\d+\s+\d+\s+\d+\s+(.)\s+.*?\/.*?\/(.*?)\:\s+/$1 $2 $3: /;') & HILOGPID="$!"
+fi
+
+if [ "$do_launch" == "1" ]; then
+    echo "LAUNCHING..."
+    # !!! FIXME: maybe this makes the process wait on launch until debugger is attached?
+    #hdc shell aa appdebug -b $BUNDLE
+    hdc shell aa start -a EntryAbility -b $BUNDLE || exit 1
+fi
+
+if [ "$do_debug" == "1" ]; then
+    echo "DEBUGGING..."
+    hdc shell aa attach -b $BUNDLE
+    hdc shell aa process -a EntryAbility -b $BUNDLE -D "/data/local/tmp/debugserver/$BUNDLE/lldb-server platform --listen unix-abstract:///lldb-server/platform.sock"
+
+    PID=`hdc shell ps -w -o PID,ARGS |grep -F "$BUNDLE" |head -n 1 |awk '{print $1}'`
+    if [ "x$PID" == "x" ]; then
+        echo "Failed to determine PID! Aborting!" 1>&2
+        exit 1
+    fi
+
+    echo "PID is $PID"
+
+    trap - INT
+    "$CLT/sdk/default/openharmony/native/llvm/bin/lldb" \
+        -O "platform select remote-ohos" \
+        -O "platform connect unix-abstract-connect:///lldb-server/platform.sock" \
+        -O "settings append target.exec-search-paths \"$PROJDIR/entry/build/default/intermediates/cmake/default/obj/arm64-v8a\"" \
+        -O "attach $PID"
+    trap trap_sigint INT
+fi
+
+if [ "$HILOGPID" != "" ]; then
+    #echo "$0: WAITING ON $HILOGPID"
+    wait "$HILOGPID"
+fi
+
diff --git a/cmake/PreseedOpenHarmonyCache.cmake b/cmake/PreseedOpenHarmonyCache.cmake
new file mode 100644
index 0000000000000..b78c24aeb9c98
--- /dev/null
+++ b/cmake/PreseedOpenHarmonyCache.cmake
@@ -0,0 +1,188 @@
+if(OHOS)
+  function(SDL_Preseed_CMakeCache)
+    set(COMPILER_SUPPORTS_FDIAGNOSTICS_COLOR_ALWAYS    "1"   CACHE INTERNAL "Test COMPILER_SUPPORTS_FDIAGNOSTICS_COLOR_ALWAYS")
+    set(COMPILER_SUPPORTS_GCC_ATOMICS                  "1"    CACHE INTERNAL "Test COMPILER_SUPPORTS_GCC_ATOMICS")
+    set(COMPILER_SUPPORTS_SYNC_LOCK_TEST_AND_SET       "1"   CACHE INTERNAL "Test COMPILER_SUPPORTS_SYNC_LOCK_TEST_AND_SET")
+    set(HAVE_CLANG_COMMENT_BLOCK_COMMANDS              "1"    CACHE INTERNAL "Test HAVE_CLANG_COMMENT_BLOCK_COMMANDS")
+    set(HAVE_ALLOCA_H                                  "1"   CACHE INTERNAL "Have include alloca.h")
+    set(HAVE_LIBM                                      "1"   CACHE INTERNAL "Have library m")
+    set(HAVE_POSIX_SPAWN                               "1"   CACHE INTERNAL "Have symbol posix_spawn")
+    set(HAVE_FSEEKO                                    "1"   CACHE INTERNAL "Have symbol fseeko")
+    set(HAVE_OFF64_T                                   "1"   CACHE INTERNAL "Have symbol off64_t")
+    set(LIBC_HAS_ABS                                   "1"   CACHE INTERNAL "Have symbol abs")
+    set(LIBC_HAS_ACOS                                  "1"   CACHE INTERNAL "Have symbol acos")
+    set(LIBC_HAS_ACOSF                                 "1"   CACHE INTERNAL "Have symbol acosf")
+    set(LIBC_HAS_ASIN                                  "1"   CACHE INTERNAL "Have symbol asin")
+    set(LIBC_HAS_ASINF                                 "1"   CACHE INTERNAL "Have symbol asinf")
+    set(LIBC_HAS_ATAN                                  "1"   CACHE INTERNAL "Have symbol atan")
+    set(LIBC_HAS_ATAN2                                 "1"   CACHE INTERNAL "Have symbol atan2")
+    set(LIBC_HAS_ATAN2F                                "1"   CACHE INTERNAL "Have symbol atan2f")
+    set(LIBC_HAS_ATANF                                 "1"   CACHE INTERNAL "Have symbol atanf")
+    set(LIBC_HAS_ATOF                                  "1"   CACHE INTERNAL "Have symbol atof")
+    set(LIBC_HAS_ATOI                                  "1"   CACHE INTERNAL "Have symbol atoi")
+    set(LIBC_HAS_BCOPY                                 "1"   CACHE INTERNAL "Have symbol bcopy")
+    set(LIBC_HAS_CALLOC                                "1"   CACHE INTERNAL "Have symbol calloc")
+    set(LIBC_HAS_CEIL                                  "1"   CACHE INTERNAL "Have symbol ceil")
+    set(LIBC_HAS_CEILF                                 "1"   CACHE INTERNAL "Have symbol ceilf")
+    set(LIBC_HAS_COPYSIGN                              "1"   CACHE INTERNAL "Have symbol copysign")
+    set(LIBC_HAS_COPYSIGNF                             "1"   CACHE INTERNAL "Have symbol copysignf")
+    set(LIBC_HAS_COS                                   "1"   CACHE INTERNAL "Have symbol cos")
+    set(LIBC_HAS_COSF                                  "1"   CACHE INTERNAL "Have symbol cosf")
+    set(LIBC_HAS_EXP                                   "1"   CACHE INTERNAL "Have symbol exp")
+    set(LIBC_HAS_EXPF                                  "1"   CACHE INTERNAL "Have symbol expf")
+    set(LIBC_HAS_FABS                                  "1"   CACHE INTERNAL "Have symbol fabs")
+    set(LIBC_HAS_FABSF                                 "1"   CACHE INTERNAL "Have symbol fabsf")
+    set(LIBC_HAS_FLOAT_H                               "1"   CACHE INTERNAL "Have include float.h")
+    set(LIBC_HAS_FLOOR                                 "1"   CACHE INTERNAL "Have symbol floor")
+    set(LIBC_HAS_FLOORF                                "1"   CACHE INTERNAL "Have symbol floorf")
+    set(LIBC_HAS_FMOD                                  "1"   CACHE INTERNAL "Have symbol fmod")
+    set(LIBC_HAS_FMODF                                 "1"   CACHE INTERNAL "Have symbol fmodf")
+    set(LIBC_HAS_FOPEN64                               "1"   CACHE INTERNAL "Have symbol fopen64")
+    set(LIBC_HAS_FREE                                  "1"   CACHE INTERNAL "Have symbol free")
+    set(LIBC_HAS_FSEEKO                                "1"   CACHE INTERNAL "Have symbol fseeko")
+    set(LIBC_HAS_FSEEKO64                              "1"   CACHE INTERNAL "Have symbol fseeko64 (broken in DJGPP)")
+    set(LIBC_HAS_GETENV                                "1"   CACHE INTERNAL "Have symbol getenv")
+    set(LIBC_HAS_ICONV_H                               "1"   CACHE INTERNAL "Have include iconv.h")
+    set(LIBC_HAS_INDEX                                 "1"   CACHE INTERNAL "Have symbol index")
+    set(LIBC_HAS_INTTYPES_H                            "1"   CACHE INTERNAL "Have include inttypes.h")
+    set(LIBC_HAS_ISINF                                 ""    CACHE INTERNAL "Have include isinf(double)")
+    set(LIBC_ISINF_HANDLES_FLOAT                       ""    CACHE INTERNAL "Have include isinf(float)")
+    set(LIBC_HAS_ISINFF                                ""    CACHE INTERNAL "Have include isinff(float)")
+    set(LIBC_HAS_ISNAN                                 ""    CACHE INTERNAL "Have include isnan(double)")
+    set(LIBC_ISNAN_HANDLES_FLOAT                       ""    CACHE INTERNAL "Have include isnan(float)")
+    set(LIBC_HAS_ISNANF                                ""    CACHE INTERNAL "Have include isnanf(float)")
+    set(LIBC_HAS_ITOA                                  ""    CACHE INTERNAL "Have symbol itoa")
+    set(LIBC_HAS_LIMITS_H                              "1"   CACHE INTERNAL "Have include limits.h")
+    set(LIBC_HAS_LOG                                   "1"   CACHE INTERNAL "Have symbol log")
+    set(LIBC_HAS_LOG10                                 "1"   CACHE INTERNAL "Have symbol log10")
+    set(LIBC_HAS_LOG10F                                "1"   CACHE INTERNAL "Have symbol log10f")
+    set(LIBC_HAS_LOGF                                  "1"   CACHE INT

(Patch may be truncated, please check the link at the top of this post.)