From 40ee5fab0731b34dda4aa96b6b950d700fb68730 Mon Sep 17 00:00:00 2001 From: Michael Fisher Date: Tue, 20 Jan 2026 11:44:32 -0500 Subject: [PATCH] feat: start WinSparkle integration for Windows updater functionality --- .github/workflows/build.yml | 7 +++++- CMakeLists.txt | 24 +++++++++++++++---- cmake/FindWinSparkle.cmake | 31 ++++++++++++++++++++++++ src/winsparkle.cc | 48 +++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 cmake/FindWinSparkle.cmake create mode 100644 src/winsparkle.cc diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0610d11c4..1bdc3cbc4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -144,13 +144,18 @@ jobs: -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl ` -DCMAKE_BUILD_TYPE=Release ` -DELEMENT_ENABLE_ASIO=ON ` + -DELEMENT_ENABLE_UPDATER=ON ` -DELEMENT_BUILD_PLUGINS=ON env: BOOST_ROOT: ${{ steps.install-boost-windows.outputs.BOOST_ROOT }} - name: Configure CMake (Unix) if: runner.os != 'Windows' - run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DELEMENT_BUILD_PLUGINS=ON + run: | + cmake -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DELEMENT_ENABLE_UPDATER=${{ runner.os == 'macOS' && 'ON' || 'OFF' }} \ + -DELEMENT_BUILD_PLUGINS=ON env: BOOST_ROOT: ${{ steps.install-boost-linux.outputs.BOOST_ROOT || steps.install-boost-macos.outputs.BOOST_ROOT }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c4da1b05..a9d077011 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,12 @@ set(PROJECT_VERSION_SUFFIX "b2") set(PROJECT_VERSION_FULL "${PROJECT_VERSION}${PROJECT_VERSION_SUFFIX}") include(cmake/Element.cmake) -if(APPLE AND ELEMENT_ENABLE_UPDATER) - include(cmake/FindSparkle.cmake) +if(ELEMENT_ENABLE_UPDATER) + if(APPLE) + include(cmake/FindSparkle.cmake) + elseif(WIN32) + include(cmake/FindWinSparkle.cmake) + endif() endif() include(cmake/FindJack.cmake) include(cmake/FindSol2.cmake) @@ -74,9 +78,7 @@ juce_add_gui_app(element_app target_sources(element_app PRIVATE src/main.cc) target_link_libraries(element_app PRIVATE kv::element) -if(NOT APPLE) - install(TARGETS element_app DESTINATION ${CMAKE_INSTALL_BINDIR}) -else() +if(APPLE) if(ELEMENT_ENABLE_UPDATER) target_compile_options(element_app PRIVATE "-F${sparkle_SOURCE_DIR}") target_link_options(element_app PRIVATE "-F${sparkle_SOURCE_DIR}" -framework Sparkle) @@ -96,6 +98,18 @@ else() install(TARGETS element_app BUNDLE DESTINATION "Applications" COMPONENT "Standalone") +elseif(WIN32) + if(ELEMENT_ENABLE_UPDATER) + target_link_libraries(element_app PRIVATE WinSparkle::WinSparkle) + target_sources(element_app PRIVATE src/winsparkle.cc) + add_custom_command(TARGET element_app POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${WIN_SPARKLE_DLL}" "$") + install(FILES "${WIN_SPARKLE_DLL}" DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() + install(TARGETS element_app DESTINATION ${CMAKE_INSTALL_BINDIR}) +else() + install(TARGETS element_app DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() if(ELEMENT_BUILD_PLUGINS) diff --git a/cmake/FindWinSparkle.cmake b/cmake/FindWinSparkle.cmake new file mode 100644 index 000000000..9152926dc --- /dev/null +++ b/cmake/FindWinSparkle.cmake @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2026 Kushview, LLC +# SPDX-License-Identifier: GPL-3.0-or-later + +set(WIN_SPARKLE_VERSION "0.9.2" CACHE STRING "WinSparkle version to download") + +set(WIN_SPARKLE_FEED_URL "http://localhost:8000/appcast.xml" + CACHE STRING "The WinSparkle feed url to use") +set(WIN_SPARKLE_PUBLIC_KEY "+3pPKr274O3KL4lTWaEs/ukVk3Ayvnc7BkdS45zOnE4=" + CACHE STRING "The WinSparkle eDSA public key to use") + +FetchContent_Declare( + winsparkle + URL https://github.com/vslavik/winsparkle/releases/download/v${WIN_SPARKLE_VERSION}/WinSparkle-${WIN_SPARKLE_VERSION}.zip + DOWNLOAD_EXTRACT_TIMESTAMP TRUE +) + +FetchContent_MakeAvailable(winsparkle) + +message(STATUS "Searching for Sparkle in: ${winsparkle_SOURCE_DIR}") + +set(WIN_SPARKLE_DLL "${winsparkle_SOURCE_DIR}/x64/Release/WinSparkle.dll") + +add_library(WinSparkle::WinSparkle SHARED IMPORTED) +set_target_properties(WinSparkle::WinSparkle PROPERTIES + IMPORTED_LOCATION "${winsparkle_SOURCE_DIR}/x64/Release/WinSparkle.dll" + IMPORTED_IMPLIB "${winsparkle_SOURCE_DIR}/x64/Release/WinSparkle.lib" + INTERFACE_INCLUDE_DIRECTORIES "${winsparkle_SOURCE_DIR}/include" +) + +message(STATUS "WinSparkle library: ${winsparkle_SOURCE_DIR}/x64/Release/WinSparkle.lib") +message(STATUS "WinSparkle include: ${winsparkle_SOURCE_DIR}/include") diff --git a/src/winsparkle.cc b/src/winsparkle.cc new file mode 100644 index 000000000..fdbf6d755 --- /dev/null +++ b/src/winsparkle.cc @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2026 Kushview, LLC +// SPDX-License-Identifier: ISC + +#include + +#include + +#include +#include +#include + +namespace element { + +class WinSparkleUpdater : public Updater +{ +public: + WinSparkleUpdater() + { + std::string ver = ELEMENT_VERSION_STRING; + std::wstring wver (ver.begin(), ver.end()); + win_sparkle_set_app_details (L"Kushview", L"Element", wver.c_str()); + win_sparkle_set_app_build_version (L"100"); + win_sparkle_set_appcast_url ("http://localhost:8080/appcast.xml"); + win_sparkle_set_automatic_check_for_updates (0); + win_sparkle_set_eddsa_public_key ("pubkey"); + } + + void check (bool async) override + { + juce::ignoreUnused (async); + if (! _initialized) { + win_sparkle_init(); + _initialized = true; + } + + // win_sparkle_check_update_without_ui(); + win_sparkle_check_update_with_ui(); + } +private: + bool _initialized { false }; +}; + +std::unique_ptr Updater::create() +{ + return std::make_unique(); +} + +} // namespace element \ No newline at end of file