diff --git a/.clang-format-ignore b/.clang-format-ignore new file mode 100644 index 00000000..5e71f5bf --- /dev/null +++ b/.clang-format-ignore @@ -0,0 +1 @@ +CMakePresets.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 60b6a15b..6e63e8d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,8 @@ jobs: env_cc: '' env_cxx: '' compiler: msvc + cppflags: '' + ldflags: '' msvc_arch: x64 msvc_version: 2022 qt_version: 6.8.3 @@ -43,6 +45,8 @@ jobs: env_cc: gcc-11 env_cxx: g++-11 compiler: gcc + cppflags: '' + ldflags: '' qt_version: 6.8.3 qt_arch_aqt: linux_gcc_64 qt_arch_dir: gcc_64 @@ -59,6 +63,8 @@ jobs: env_cc: clang-17 env_cxx: clang++-17 compiler: clang + cppflags: '' + ldflags: '' qt_version: 6.8.3 qt_arch_aqt: linux_gcc_64 qt_arch_dir: gcc_64 @@ -75,6 +81,8 @@ jobs: env_cc: gcc-11 env_cxx: g++-11 compiler: gcc + cppflags: '' + ldflags: '' qt_version: 6.8.3 qt_arch_aqt: linux_gcc_arm64 qt_arch_dir: gcc_arm64 @@ -85,6 +93,36 @@ jobs: appimage_arch: aarch64 artifact_suffix: linux-arm64 compiler_packages: g++-11 + - name: macos_clang18_x64 + os: macos-13 + build_type: Release + env_cc: clang + env_cxx: clang++ + compiler: clang + qt_version: 6.8.3 + qt_arch_aqt: clang_64 + qt_arch_dir: macos + qt_modules: qtimageformats qtmultimedia qtpositioning qtserialport + qt_tools: '' + conan_package_manager: '' + conan_profile: scwx-macos_clang-18 + appimage_arch: '' + artifact_suffix: macos-x64 + - name: macos_clang18_arm64 + os: macos-15 + build_type: Release + env_cc: clang + env_cxx: clang++ + compiler: clang + qt_version: 6.8.3 + qt_arch_aqt: clang_64 + qt_arch_dir: macos + qt_modules: qtimageformats qtmultimedia qtpositioning qtserialport + qt_tools: '' + conan_package_manager: '' + conan_profile: scwx-macos_clang-18_armv8 + appimage_arch: '' + artifact_suffix: macos-arm64 name: ${{ matrix.name }} env: CC: ${{ matrix.env_cc }} @@ -133,6 +171,17 @@ jobs: flatpak-builder \ ${{ matrix.compiler_packages }} + - name: Setup macOS Environment + if: ${{ startsWith(matrix.os, 'macos') }} + shell: bash + run: | + brew install llvm@18 + LLVM_PATH=$(brew --prefix llvm@18) + echo "CC=${LLVM_PATH}/bin/clang" >> $GITHUB_ENV + echo "CXX=${LLVM_PATH}/bin/clang++" >> $GITHUB_ENV + echo "CPPFLAGS=-I${LLVM_PATH}/include" >> $GITHUB_ENV + echo "LDFLAGS=-L${LLVM_PATH}/lib -L${LLVM_PATH}/lib/c++" >> $GITHUB_ENV + - name: Setup Python Environment shell: pwsh run: | @@ -325,12 +374,26 @@ jobs: name: supercell-wx-flatpak-${{ matrix.artifact_suffix }} path: ${{ github.workspace }}/supercell-wx.flatpak + - name: Build Disk Image (macOS) + if: ${{ startsWith(matrix.os, 'macos') }} + shell: pwsh + run: | + cd build + cpack + + - name: Upload Disk Image (macOS) + if: ${{ startsWith(matrix.os, 'macos') }} + uses: actions/upload-artifact@v4 + with: + name: supercell-wx-${{ matrix.artifact_suffix }} + path: ${{ github.workspace }}/build/supercell-wx-*.dmg* + - name: Test Supercell Wx working-directory: ${{ github.workspace }}/build env: MAPBOX_API_KEY: ${{ secrets.MAPBOX_API_KEY }} MAPTILER_API_KEY: ${{ secrets.MAPTILER_API_KEY }} - run: ctest -C ${{ matrix.build_type }} --exclude-regex test_mln.* + run: ctest -C ${{ matrix.build_type }} --exclude-regex "test_mln.*|UpdateManager.*" - name: Upload Test Logs if: ${{ !cancelled() }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a2da515..d04b97fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,8 @@ set(PROJECT_NAME supercell-wx) include(tools/scwx_config.cmake) +set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0) + scwx_python_setup() project(${PROJECT_NAME} diff --git a/CMakePresets.json b/CMakePresets.json index 575be130..056a32b9 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -210,6 +210,88 @@ "CC": "gcc-11", "CXX": "g++-11" } + }, + { + "name": "macos-base", + "inherits": "base", + "hidden": true, + "cacheVariables": { + "CMAKE_PREFIX_PATH": "$env{HOME}/Qt/6.8.3/macos" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + } + }, + { + "name": "macos-clang18-base", + "inherits": "macos-base", + "hidden": true + }, + { + "name": "macos-clang18-x64-base", + "inherits": "macos-clang18-base", + "hidden": true, + "cacheVariables": { + "CONAN_HOST_PROFILE": "scwx-macos_clang-18", + "CONAN_BUILD_PROFILE": "scwx-macos_clang-18" + }, + "environment": { + "CC": "/usr/local/opt/llvm@18/bin/clang", + "CXX": "/usr/local/opt/llvm@18/bin/clang++", + "PATH": "/usr/local/opt/llvm@18/bin:$penv{PATH}", + "CPPFLAGS": "-I/usr/local/opt/llvm@18/include", + "LDFLAGS": "-L/usr/local/opt/llvm@18/lib -L/usr/local/opt/llvm@18/lib/c++" + } + }, + { + "name": "macos-clang18-arm64-base", + "inherits": "macos-clang18-base", + "hidden": true, + "cacheVariables": { + "CONAN_HOST_PROFILE": "scwx-macos_clang-18_armv8", + "CONAN_BUILD_PROFILE": "scwx-macos_clang-18_armv8" + }, + "environment": { + "CC": "/opt/homebrew/opt/llvm@18/bin/clang", + "CXX": "/opt/homebrew/opt/llvm@18/bin/clang++", + "PATH": "/opt/homebrew/opt/llvm@18/bin:$penv{PATH}", + "CPPFLAGS": "-I/opt/homebrew/opt/llvm@18/include", + "LDFLAGS": "-L/opt/homebrew/opt/llvm@18/lib -L/opt/homebrew/opt/llvm@18/lib/c++" + } + }, + { + "name": "macos-clang18-x64-debug", + "inherits": "macos-clang18-x64-base", + "displayName": "macOS Clang 18 x64 Debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "macos-clang18-x64-release", + "inherits": "macos-clang18-x64-base", + "displayName": "macOS Clang 18 x64 Release", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "macos-clang18-arm64-debug", + "inherits": "macos-clang18-arm64-base", + "displayName": "macOS Clang 18 Arm64 Debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "macos-clang18-arm64-release", + "inherits": "macos-clang18-arm64-base", + "displayName": "macOS Clang 18 Arm64 Release", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } } ], "buildPresets": [ @@ -236,6 +318,30 @@ "configurePreset": "linux-gcc-release", "displayName": "Linux GCC Release", "configuration": "Release" + }, + { + "name": "macos-clang18-x64-debug", + "configurePreset": "macos-clang18-x64-debug", + "displayName": "macOS Clang 18 x64 Debug", + "configuration": "Debug" + }, + { + "name": "macos-clang18-x64-release", + "configurePreset": "macos-clang18-x64-release", + "displayName": "macOS Clang 18 x64 Release", + "configuration": "Release" + }, + { + "name": "macos-clang18-arm64-debug", + "configurePreset": "macos-clang18-arm64-debug", + "displayName": "macOS Clang 18 Arm64 Debug", + "configuration": "Debug" + }, + { + "name": "macos-clang18-arm64-release", + "configurePreset": "macos-clang18-arm64-release", + "displayName": "macOS Clang 18 Arm64 Release", + "configuration": "Release" } ], "testPresets": [ diff --git a/README.md b/README.md index 01f2ced6..d5995d2f 100644 --- a/README.md +++ b/README.md @@ -28,31 +28,32 @@ Supercell Wx supports the following 64-bit operating systems: - Ubuntu 22.04+ - NixOS 25.05+ - Most distributions supporting the GCC Standard C++ Library 11+ - +- macOS 12 (Monterey) or later + ## Linux Dependencies Supercell Wx requires the following Linux dependencies: -- Linux/X11 (Wayland works too) with support for GCC 11 and OpenGL 3.3 +- Linux/X11 (Wayland works too) with support for GCC 11, OpenGL 3.3 and OpenGL ES 3.0 - X11/XCB libraries including xcb-cursor - + ## FAQ Frequently asked questions: - Q: Why is the map black when loading for the first time? - + - A. You must obtain a free API key from either (or both) [MapTiler](https://cloud.maptiler.com/auth/widget?next=https://cloud.maptiler.com/maps/) which currently does not require a credit/debit card, or [Mapbox](https://account.mapbox.com/) which ***does*** require a credit/debit card, but as of writing, you will receive 200K free requests per month, which should be sufficient for an individual user. - Q: Why is it that when I change my color table, API key, grid width/height settings, nothing happens after hitting apply? - A. As of right now, you must restart Supercell Wx in order to apply these changes. In future iterations, this will no longer be an issue. - + - Q. Is it possible to get dark mode? - + - A. In Windows, make sure to set the flag `-style fusion` at the end of the target path of the .exe - Example: `C:\Users\Administrator\Desktop\Supercell-Wx\bin\supercell-wx.exe -style fusion` - A. In Linux, if you're using KDE, Supercell Wx should automatically follow your theme settings. - + - Q: How can I contribute? - A. Head to [Developer Setup](https://supercell-wx.readthedocs.io/en/stable/development/developer-setup.html) and [Contributing](CONTRIBUTING.md) to configure the Supercell Wx development environment for your IDE. Currently Visual Studio and Visual Studio Code are recommended, with other IDEs remaining untested at this time. diff --git a/conanfile.py b/conanfile.py index 13a978ca..ea880677 100644 --- a/conanfile.py +++ b/conanfile.py @@ -35,10 +35,16 @@ class SupercellWxConan(ConanFile): self.options["openssl"].shared = True self.options["libcurl"].ca_bundle = "none" self.options["libcurl"].ca_path = "none" + elif self.settings.os == "Macos": + self.options["openssl"].shared = True + self.options["libcurl"].ca_bundle = "none" + self.options["libcurl"].ca_path = "none" def requirements(self): if self.settings.os == "Linux": self.requires("onetbb/2022.0.0") + elif self.settings.os == "Macos": + self.requires("onetbb/2022.0.0") def generate(self): build_folder = os.path.join(self.build_folder, diff --git a/external/maplibre-native b/external/maplibre-native index 554e6e9a..3654f5fa 160000 --- a/external/maplibre-native +++ b/external/maplibre-native @@ -1 +1 @@ -Subproject commit 554e6e9ac46b6eaf5970a219c88e3df11f1cee30 +Subproject commit 3654f5fa9f06534d7fd2d95b810049a82e5953ef diff --git a/external/maplibre-native-qt.cmake b/external/maplibre-native-qt.cmake index 736c6049..7db42c10 100644 --- a/external/maplibre-native-qt.cmake +++ b/external/maplibre-native-qt.cmake @@ -35,6 +35,12 @@ else() target_compile_options(MLNQtCore PRIVATE "$<$:-g>") endif() +if (APPLE) + # Enable GL check error debug + target_compile_definitions(mbgl-core PRIVATE MLN_GL_CHECK_ERRORS=1) + target_compile_definitions(MLNQtCore PRIVATE MLN_GL_CHECK_ERRORS=1) +endif() + set(MLN_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/maplibre-native/include ${CMAKE_CURRENT_SOURCE_DIR}/maplibre-native-qt/src/core/include ${CMAKE_CURRENT_BINARY_DIR}/maplibre-native-qt/src/core/include PARENT_SCOPE) diff --git a/scwx-qt/res/icons/scwx.icns b/scwx-qt/res/icons/scwx.icns new file mode 100644 index 00000000..bda4ea32 Binary files /dev/null and b/scwx-qt/res/icons/scwx.icns differ diff --git a/scwx-qt/res/scwx-qt.plist.in b/scwx-qt/res/scwx-qt.plist.in new file mode 100644 index 00000000..f9db57c0 --- /dev/null +++ b/scwx-qt/res/scwx-qt.plist.in @@ -0,0 +1,42 @@ + + + + + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + + LSMinimumSystemVersion + ${CMAKE_OSX_DEPLOYMENT_TARGET} + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + + CFBundleDevelopmentRegion + en + CFBundleAllowMixedLocalizations + + + NSPrincipalClass + NSApplication + + NSSupportsAutomaticGraphicsSwitching + + + diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index aff16705..4bffdc2e 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -626,6 +626,26 @@ if (WIN32) if (SCWX_DISABLE_CONSOLE) set_target_properties(supercell-wx PROPERTIES WIN32_EXECUTABLE $,TRUE,FALSE>) endif() +elseif (APPLE) + set(SCWX_ICON "${scwx-qt_SOURCE_DIR}/res/icons/scwx.icns") + + set_source_files_properties(${SCWX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") + + qt_add_executable(supercell-wx ${EXECUTABLE_SOURCES} ${SCWX_ICON}) + + string(TIMESTAMP CURRENT_YEAR "%Y") + + set_target_properties(supercell-wx PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_INFO_PLIST "${scwx-qt_SOURCE_DIR}/res/scwx-qt.plist.in" + MACOSX_BUNDLE_GUI_IDENTIFIER "net.supercellwx.app" + MACOSX_BUNDLE_BUNDLE_NAME "Supercell Wx" + MACOSX_BUNDLE_BUNDLE_VERSION "${SCWX_VERSION}" + MACOSX_BUNDLE_SHORT_VERSION_STRING "${SCWX_VERSION}" + MACOSX_BUNDLE_COPYRIGHT "Copyright ${CURRENT_YEAR} Dan Paulat" + MACOSX_BUNDLE_ICON_FILE "scwx.icns" + MACOSX_BUNDLE_INFO_STRING "Free and open source advanced weather radar" + RESOURCE ${SCWX_ICON}) else() qt_add_executable(supercell-wx ${EXECUTABLE_SOURCES}) endif() @@ -635,7 +655,7 @@ if (WIN32) target_compile_definitions(supercell-wx PUBLIC WIN32_LEAN_AND_MEAN) endif() -if (NOT MSVC) +if (LINUX) # Qt emit keyword is incompatible with TBB target_compile_definitions(scwx-qt PRIVATE QT_NO_EMIT) target_compile_definitions(supercell-wx PRIVATE QT_NO_EMIT) @@ -694,7 +714,9 @@ if (MSVC) else() target_compile_options(scwx-qt PRIVATE "$<$:-g>") target_compile_options(supercell-wx PRIVATE "$<$:-g>") +endif() +if (LINUX) # Add wayland client packages find_package(QT NAMES Qt6 COMPONENTS WaylandClient @@ -733,9 +755,11 @@ target_link_libraries(scwx-qt PUBLIC Qt${QT_VERSION_MAJOR}::Widgets target_link_libraries(supercell-wx PRIVATE scwx-qt wxdata) -# Set DT_RUNPATH for Linux targets -set_target_properties(MLNQtCore PROPERTIES INSTALL_RPATH "\$ORIGIN/../lib") # QMapLibre::Core -set_target_properties(supercell-wx PROPERTIES INSTALL_RPATH "\$ORIGIN/../lib") +if (LINUX) + # Set DT_RUNPATH for Linux targets + set_target_properties(MLNQtCore PROPERTIES INSTALL_RPATH "\$ORIGIN/../lib") # QMapLibre::Core + set_target_properties(supercell-wx PROPERTIES INSTALL_RPATH "\$ORIGIN/../lib") +endif() install(TARGETS supercell-wx MLNQtCore # QMapLibre::Core @@ -745,7 +769,15 @@ install(TARGETS supercell-wx "^(/usr)?/lib/.*\\.so(\\..*)?" RUNTIME COMPONENT supercell-wx + BUNDLE + DESTINATION . + COMPONENT supercell-wx + OPTIONAL LIBRARY + COMPONENT supercell-wx + OPTIONAL + FRAMEWORK + DESTINATION Frameworks COMPONENT supercell-wx OPTIONAL) @@ -767,14 +799,64 @@ install(SCRIPT ${deploy_script_qmaplibre_core} install(SCRIPT ${deploy_script_scwx} COMPONENT supercell-wx) +if (APPLE) + # Install additional script to fix up the bundle + install(CODE [[ + include (BundleUtilities) + + # Define the bundle path + set(BUNDLE_PATH "${CMAKE_INSTALL_PREFIX}/supercell-wx.app") + + file(GLOB_RECURSE PLUGIN_DYLIBS "${BUNDLE_PATH}/Contents/PlugIns/**/*.dylib") + + # Add the correct rpath for plugins to find bundled frameworks + foreach(PLUGIN_DYLIB ${PLUGIN_DYLIBS}) + execute_process( + COMMAND install_name_tool -add_rpath "@loader_path/../../Frameworks" + ${PLUGIN_DYLIB} + ) + endforeach() + + # Fix up the bundle with all dependencies + fixup_bundle( + "${BUNDLE_PATH}" + "" + "${CMAKE_INSTALL_PREFIX}/lib;${CMAKE_INSTALL_PREFIX}/Frameworks" + ) + + # Re-sign the bundle + execute_process( + COMMAND codesign --force --deep --sign - "${BUNDLE_PATH}" + ) + + # Verify the bundle + verify_app("${BUNDLE_PATH}") + + # Rename to "Supercell Wx.app" + file(REMOVE_RECURSE + "${CMAKE_INSTALL_PREFIX}/Supercell Wx.app") + file(RENAME + "${BUNDLE_PATH}" + "${CMAKE_INSTALL_PREFIX}/Supercell Wx.app") + + # Remove extra directories + file(REMOVE_RECURSE + "${CMAKE_INSTALL_PREFIX}/Frameworks") + file(REMOVE_RECURSE + "${CMAKE_INSTALL_PREFIX}/lib") + ]] + COMPONENT supercell-wx) +endif() + +set(CPACK_PACKAGE_NAME "Supercell Wx") +set(CPACK_PACKAGE_VENDOR "Dan Paulat") +set(CPACK_PACKAGE_CHECKSUM SHA256) +set(CPACK_RESOURCE_FILE_LICENSE "${SCWX_DIR}/LICENSE.txt") + if (MSVC) - set(CPACK_PACKAGE_NAME "Supercell Wx") - set(CPACK_PACKAGE_VENDOR "Dan Paulat") set(CPACK_PACKAGE_FILE_NAME "supercell-wx-v${SCWX_VERSION}-windows-x64") set(CPACK_PACKAGE_INSTALL_DIRECTORY "Supercell Wx") set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/res/icons/scwx-256.ico") - set(CPACK_PACKAGE_CHECKSUM SHA256) - set(CPACK_RESOURCE_FILE_LICENSE "${SCWX_DIR}/LICENSE.txt") set(CPACK_GENERATOR WIX) set(CPACK_PACKAGE_EXECUTABLES "supercell-wx;Supercell Wx") set(CPACK_WIX_UPGRADE_GUID 36AD0F51-4D4F-4B5D-AB61-94C6B4E4FE1C) @@ -786,5 +868,15 @@ if (MSVC) set(CPACK_INSTALL_CMAKE_PROJECTS "${CMAKE_CURRENT_BINARY_DIR};${CMAKE_PROJECT_NAME};supercell-wx;/") + include(CPack) +elseif(APPLE) + set(CPACK_PACKAGE_FILE_NAME "supercell-wx-v${SCWX_VERSION}-macos") + set(CPACK_PACKAGE_ICON "${SCWX_ICON}") + set(CPACK_PACKAGE_VERSION "${SCWX_VERSION}") + + set(CPACK_GENERATOR DragNDrop) + + set(CPACK_COMPONENTS_ALL supercell-wx) + include(CPack) endif() diff --git a/scwx-qt/source/scwx/qt/gl/draw/icons.cpp b/scwx-qt/source/scwx/qt/gl/draw/icons.cpp index d7f6b64c..50e30a82 100644 --- a/scwx-qt/source/scwx/qt/gl/draw/icons.cpp +++ b/scwx-qt/source/scwx/qt/gl/draw/icons.cpp @@ -740,7 +740,7 @@ bool Icons::RunMousePicking( // For each pickable icon auto it = std::find_if( // - std::execution::par_unseq, + std::execution::par, p->currentHoverIcons_.crbegin(), p->currentHoverIcons_.crend(), [&mouseLocalCoords](const auto& icon) diff --git a/scwx-qt/source/scwx/qt/gl/draw/placefile_polygons.cpp b/scwx-qt/source/scwx/qt/gl/draw/placefile_polygons.cpp index 4f7f2c81..e5c8addd 100644 --- a/scwx-qt/source/scwx/qt/gl/draw/placefile_polygons.cpp +++ b/scwx-qt/source/scwx/qt/gl/draw/placefile_polygons.cpp @@ -4,10 +4,15 @@ #include -#include #include -#if defined(_WIN32) +#if !defined(__APPLE__) +# include +#else +# include +#endif + +#if defined(_WIN32) || defined(__APPLE__) typedef void (*_GLUfuncptr)(void); #endif diff --git a/scwx-qt/source/scwx/qt/gl/gl_context.cpp b/scwx-qt/source/scwx/qt/gl/gl_context.cpp index 4cc42879..9fd6bd85 100644 --- a/scwx-qt/source/scwx/qt/gl/gl_context.cpp +++ b/scwx-qt/source/scwx/qt/gl/gl_context.cpp @@ -12,6 +12,7 @@ namespace gl { static const std::string logPrefix_ = "scwx::qt::gl::gl_context"; +static const auto logger_ = scwx::util::Logger::Create(logPrefix_); class GlContext::Impl { @@ -84,6 +85,13 @@ void GlContext::Impl::InitializeGL() gl_->initializeOpenGLFunctions(); gl30_->initializeOpenGLFunctions(); + logger_->info("OpenGL Version: {}", + reinterpret_cast(gl_->glGetString(GL_VERSION))); + logger_->info("OpenGL Vendor: {}", + reinterpret_cast(gl_->glGetString(GL_VENDOR))); + logger_->info("OpenGL Renderer: {}", + reinterpret_cast(gl_->glGetString(GL_RENDERER))); + gl_->glGenTextures(1, &textureAtlas_); glInitialized_ = true; diff --git a/scwx-qt/source/scwx/qt/main/main.cpp b/scwx-qt/source/scwx/qt/main/main.cpp index 904ff3b3..7a1993f7 100644 --- a/scwx-qt/source/scwx/qt/main/main.cpp +++ b/scwx-qt/source/scwx/qt/main/main.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,16 @@ int main(int argc, char* argv[]) QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); +#if defined(__APPLE__) + // For macOS, we must choose between OpenGL 4.1 Core and OpenGL 2.1 + // Compatibility. OpenGL 2.1 does not meet requirements for shaders used by + // Supercell Wx. + QSurfaceFormat surfaceFormat = QSurfaceFormat::defaultFormat(); + surfaceFormat.setVersion(4, 1); + surfaceFormat.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); + QSurfaceFormat::setDefaultFormat(surfaceFormat); +#endif + QApplication a(argc, argv); QCoreApplication::setApplicationName("Supercell Wx"); diff --git a/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp b/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp index 18af3b02..d2bc15d0 100644 --- a/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp @@ -175,7 +175,7 @@ public: level2ChunksProviderManager_->Disable(); std::shared_lock lock(level3ProviderManagerMutex_); - std::for_each(std::execution::par_unseq, + std::for_each(std::execution::par, level3ProviderManagerMap_.begin(), level3ProviderManagerMap_.end(), [](auto& p) @@ -693,7 +693,7 @@ void RadarProductManager::EnableRefresh(common::RadarProductGroup group, auto availableProducts = providerManager->provider_->GetAvailableProducts(); - if (std::find(std::execution::par_unseq, + if (std::find(std::execution::par, availableProducts.cbegin(), availableProducts.cend(), product) != availableProducts.cend()) @@ -920,13 +920,13 @@ RadarProductManager::GetActiveVolumeTimes( // For each provider (in parallel) std::for_each( - std::execution::par_unseq, + std::execution::par, providers.begin(), providers.end(), [&](const std::shared_ptr& provider) { // For yesterday, today and tomorrow (in parallel) - std::for_each(std::execution::par_unseq, + std::for_each(std::execution::par, dates.begin(), dates.end(), [&](const auto& date) @@ -1246,7 +1246,7 @@ void RadarProductManagerImpl::PopulateProductTimes( std::mutex volumeTimesMutex {}; // For yesterday, today and tomorrow (in parallel) - std::for_each(std::execution::par_unseq, + std::for_each(std::execution::par, dates.begin(), dates.end(), [&](const auto& date) diff --git a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp index 41558378..0c8cecef 100644 --- a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp @@ -62,7 +62,7 @@ LoadImageResources(const std::vector& urlStrings) std::mutex m {}; std::vector> images {}; - std::for_each(std::execution::par_unseq, + std::for_each(std::execution::par, urlStrings.begin(), urlStrings.end(), [&](auto& urlString) diff --git a/scwx-qt/source/scwx/qt/manager/text_event_manager.cpp b/scwx-qt/source/scwx/qt/manager/text_event_manager.cpp index 8aa4c611..d7759275 100644 --- a/scwx-qt/source/scwx/qt/manager/text_event_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/text_event_manager.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -488,7 +487,7 @@ void TextEventManager::Impl::LoadArchives( std::vector loadListEntries {}; - for (auto date : boost::irange(startDate, endDate)) + for (auto date = startDate; date < endDate; date += std::chrono::days {1}) { auto mapIt = unloadedProductMap_.find(date); if (mapIt == unloadedProductMap_.cend()) diff --git a/scwx-qt/source/scwx/qt/map/alert_layer.cpp b/scwx-qt/source/scwx/qt/map/alert_layer.cpp index 93a6d2d1..156b1a6d 100644 --- a/scwx-qt/source/scwx/qt/map/alert_layer.cpp +++ b/scwx-qt/source/scwx/qt/map/alert_layer.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/scwx-qt/source/scwx/qt/ui/setup/audio_codec_page.cpp b/scwx-qt/source/scwx/qt/ui/setup/audio_codec_page.cpp index dd86e1f6..364c9959 100644 --- a/scwx-qt/source/scwx/qt/ui/setup/audio_codec_page.cpp +++ b/scwx-qt/source/scwx/qt/ui/setup/audio_codec_page.cpp @@ -124,6 +124,9 @@ void AudioCodecPage::Impl::SetInstructionsLabelText() self_, [](const QString& link) { QDesktopServices::openUrl(QUrl {link}); }); +#elif defined(__APPLE__) + instructionsLabel_->setText(tr( + "Please see the instructions for your Mac for installing media codecs.")); #else instructionsLabel_->setText( tr("Please see the instructions for your Linux distribution for " diff --git a/scwx-qt/source/scwx/qt/util/color.cpp b/scwx-qt/source/scwx/qt/util/color.cpp index 16060bb9..bdd3f6c2 100644 --- a/scwx-qt/source/scwx/qt/util/color.cpp +++ b/scwx-qt/source/scwx/qt/util/color.cpp @@ -13,8 +13,6 @@ namespace util namespace color { -static const std::string logPrefix_ = "scwx::qt::util::color"; - std::string ToArgbString(const boost::gil::rgba8_pixel_t& color) { return fmt::format( diff --git a/scwx-qt/source/scwx/qt/util/file.cpp b/scwx-qt/source/scwx/qt/util/file.cpp index b129e6ce..7e126345 100644 --- a/scwx-qt/source/scwx/qt/util/file.cpp +++ b/scwx-qt/source/scwx/qt/util/file.cpp @@ -12,8 +12,6 @@ namespace qt namespace util { -static const std::string logPrefix_ = "scwx::qt::util::file"; - std::unique_ptr OpenFile(const std::string& filename, std::ios_base::openmode mode) { diff --git a/scwx-qt/source/scwx/qt/util/texture_atlas.cpp b/scwx-qt/source/scwx/qt/util/texture_atlas.cpp index c069bfb3..b8b5bd67 100644 --- a/scwx-qt/source/scwx/qt/util/texture_atlas.cpp +++ b/scwx-qt/source/scwx/qt/util/texture_atlas.cpp @@ -390,11 +390,10 @@ TextureAtlas::Impl::LoadImage(const std::string& imagePath, double scale) QUrl url = QUrl::fromUserInput(qImagePath); - if (url.isLocalFile()) { - QString suffix = QFileInfo(qImagePath).suffix().toLower(); - QString qLocalImagePath = url.toString(QUrl::PreferLocalFile); + const QString suffix = QFileInfo(qImagePath).suffix().toLower(); + const QString qLocalImagePath = url.toString(QUrl::PreferLocalFile); if (suffix == "svg") { @@ -448,18 +447,18 @@ TextureAtlas::Impl::LoadImage(const std::string& imagePath, double scale) // If no alpha channel, replace black with transparent if (numChannels == 3) { - std::for_each( - std::execution::par_unseq, - view.begin(), - view.end(), - [](boost::gil::rgba8_pixel_t& pixel) - { - static const boost::gil::rgba8_pixel_t kBlack {0, 0, 0, 255}; - if (pixel == kBlack) - { - pixel[3] = 0; - } - }); + std::for_each(std::execution::par, + view.begin(), + view.end(), + [](boost::gil::rgba8_pixel_t& pixel) + { + static const boost::gil::rgba8_pixel_t kBlack { + 0, 0, 0, 255}; + if (pixel == kBlack) + { + pixel[3] = 0; + } + }); } stbi_image_free(pixelData); diff --git a/tools/conan/profiles/scwx-macos_clang-18 b/tools/conan/profiles/scwx-macos_clang-18 new file mode 100644 index 00000000..f7a5b0ce --- /dev/null +++ b/tools/conan/profiles/scwx-macos_clang-18 @@ -0,0 +1,9 @@ +[settings] +arch=x86_64 +build_type=Release +compiler=clang +compiler.cppstd=20 +compiler.libcxx=libc++ +compiler.version=18 +os=Macos +os.version=12.0 diff --git a/tools/conan/profiles/scwx-macos_clang-18_armv8 b/tools/conan/profiles/scwx-macos_clang-18_armv8 new file mode 100644 index 00000000..65d9afd4 --- /dev/null +++ b/tools/conan/profiles/scwx-macos_clang-18_armv8 @@ -0,0 +1,9 @@ +[settings] +arch=armv8 +build_type=Release +compiler=clang +compiler.cppstd=20 +compiler.libcxx=libc++ +compiler.version=18 +os=Macos +os.version=12.0 diff --git a/tools/configure-environment.sh b/tools/configure-environment.sh index 0da64217..1dfc714c 100755 --- a/tools/configure-environment.sh +++ b/tools/configure-environment.sh @@ -1,8 +1,19 @@ #!/bin/bash -script_dir="$(dirname "$(readlink -f "$0")")" +script_source="${BASH_SOURCE[0]:-$0}" +script_dir="$(cd "$(dirname "${script_source}")" && pwd)" # Assign user-specified Python Virtual Environment -[ "${1:-}" = "none" ] && unset venv_path || export venv_path="$(readlink -f "${1:-${script_dir}/../.venv}")" +if [ "${1:-}" = "none" ]; then + unset venv_path +else + venv_arg="${1:-${script_dir}/../.venv}" + # Portable way to get absolute path without requiring the directory to exist + case "${venv_arg}" in + /*) venv_path="${venv_arg}" ;; + *) venv_path="$(cd "$(dirname "${venv_arg}")" && pwd)/$(basename "${venv_arg}")" ;; + esac + export venv_path +fi # Load custom build settings if [ -f "${script_dir}/lib/user-setup.sh" ]; then @@ -11,12 +22,12 @@ fi # Activate Python Virtual Environment if [ -n "${venv_path:-}" ]; then - python -m venv "${venv_path}" + python3 -m venv "${venv_path}" source "${venv_path}/bin/activate" fi # Detect if a Python Virtual Environment was specified above, or elsewhere -IN_VENV=$(python -c 'import sys; print(sys.prefix != getattr(sys, "base_prefix", sys.prefix))') +IN_VENV=$(python3 -c 'import sys; print(sys.prefix != getattr(sys, "base_prefix", sys.prefix))') if [ "${IN_VENV}" = "True" ]; then # In a virtual environment, don't use --user @@ -27,27 +38,36 @@ else fi # Install Python packages -python -m pip install ${PIP_FLAGS} --upgrade pip -pip install ${PIP_FLAGS} -r "${script_dir}/../requirements.txt" +python3 -m pip install ${PIP_FLAGS} --upgrade pip +python3 -m pip install ${PIP_FLAGS} -r "${script_dir}/../requirements.txt" # Configure default Conan profile conan profile detect -e # Conan profiles -conan_profiles=( - "scwx-linux_clang-17" - "scwx-linux_clang-17_armv8" - "scwx-linux_clang-18" - "scwx-linux_clang-18_armv8" - "scwx-linux_gcc-11" - "scwx-linux_gcc-11_armv8" - "scwx-linux_gcc-12" - "scwx-linux_gcc-12_armv8" - "scwx-linux_gcc-13" - "scwx-linux_gcc-13_armv8" - "scwx-linux_gcc-14" - "scwx-linux_gcc-14_armv8" +if [[ "$(uname)" == "Darwin" ]]; then + # macOS profiles + conan_profiles=( + "scwx-macos_clang-18" + "scwx-macos_clang-18_armv8" ) +else + # Linux profiles + conan_profiles=( + "scwx-linux_clang-17" + "scwx-linux_clang-17_armv8" + "scwx-linux_clang-18" + "scwx-linux_clang-18_armv8" + "scwx-linux_gcc-11" + "scwx-linux_gcc-11_armv8" + "scwx-linux_gcc-12" + "scwx-linux_gcc-12_armv8" + "scwx-linux_gcc-13" + "scwx-linux_gcc-13_armv8" + "scwx-linux_gcc-14" + "scwx-linux_gcc-14_armv8" + ) +fi # Install Conan profiles for profile_name in "${conan_profiles[@]}"; do diff --git a/tools/lib/run-cmake-configure.sh b/tools/lib/run-cmake-configure.sh index 5e1a3bbf..03bbcdb1 100755 --- a/tools/lib/run-cmake-configure.sh +++ b/tools/lib/run-cmake-configure.sh @@ -1,5 +1,6 @@ #!/bin/bash -script_dir="$(dirname "$(readlink -f "$0")")" +script_source="${BASH_SOURCE[0]:-$0}" +script_dir="$(cd "$(dirname "${script_source}")" && pwd)" cmake_args=( -B "${build_dir}" diff --git a/tools/lib/setup-common.sh b/tools/lib/setup-common.sh index e28978e9..b11270e8 100755 --- a/tools/lib/setup-common.sh +++ b/tools/lib/setup-common.sh @@ -1,5 +1,6 @@ #!/bin/bash -script_dir="$(dirname "$(readlink -f "$0")")" +script_source="${BASH_SOURCE[0]:-$0}" +script_dir="$(cd "$(dirname "${script_source}")" && pwd)" # Import common paths source "${script_dir}/common-paths.sh" @@ -9,14 +10,14 @@ if [ -f "${script_dir}/user-setup.sh" ]; then source "${script_dir}/user-setup.sh" fi -# Activate Python Virtual Environment +# Activate python3 Virtual Environment if [ -n "${venv_path:-}" ]; then - python -m venv "${venv_path}" + python3 -m venv "${venv_path}" source "${venv_path}/bin/activate" fi -# Detect if a Python Virtual Environment was specified above, or elsewhere -IN_VENV=$(python -c 'import sys; print(sys.prefix != getattr(sys, "base_prefix", sys.prefix))') +# Detect if a python3 Virtual Environment was specified above, or elsewhere +IN_VENV=$(python3 -c 'import sys; print(sys.prefix != getattr(sys, "base_prefix", sys.prefix))') if [ "${IN_VENV}" = "True" ]; then # In a virtual environment, don't use --user @@ -26,9 +27,9 @@ else PIP_FLAGS="--upgrade --user" fi -# Install Python packages -python -m pip install ${PIP_FLAGS} pip -pip install ${PIP_FLAGS} -r "${script_dir}/../../requirements.txt" +# Install python3 packages +python3 -m pip install ${PIP_FLAGS} pip +python3 -m pip install ${PIP_FLAGS} -r "${script_dir}/../../requirements.txt" if [[ -n "${build_type}" ]]; then # Install Conan profile and packages @@ -49,7 +50,7 @@ fi # Run CMake Configure "${script_dir}/run-cmake-configure.sh" -# Deactivate Python Virtual Environment +# Deactivate python3 Virtual Environment if [ -n "${venv_path:-}" ]; then deactivate fi diff --git a/tools/lib/setup-conan.sh b/tools/lib/setup-conan.sh index 2ac38ee7..0b6c5004 100755 --- a/tools/lib/setup-conan.sh +++ b/tools/lib/setup-conan.sh @@ -1,5 +1,6 @@ #!/bin/bash -script_dir="$(dirname "$(readlink -f "$0")")" +script_source="${BASH_SOURCE[0]:-$0}" +script_dir="$(cd "$(dirname "${script_source}")" && pwd)" # Configure default Conan profile conan profile detect -e diff --git a/tools/scwx_config.cmake b/tools/scwx_config.cmake index 8373418b..93205f90 100644 --- a/tools/scwx_config.cmake +++ b/tools/scwx_config.cmake @@ -31,6 +31,9 @@ macro(scwx_python_setup) set(Python3_EXECUTABLE "$ENV{VIRTUAL_ENV}/bin/python") endif() + # Add virtual environment to program search paths + set(CMAKE_PROGRAM_PATH "$ENV{VIRTUAL_ENV}/bin" ${CMAKE_PROGRAM_PATH}) + message(STATUS "Using virtual environment: $ENV{VIRTUAL_ENV}") else() message(STATUS "Python virtual environment undefined") diff --git a/tools/setup-macos-debug.sh b/tools/setup-macos-debug.sh new file mode 100755 index 00000000..ec24dfe2 --- /dev/null +++ b/tools/setup-macos-debug.sh @@ -0,0 +1,30 @@ +#!/bin/bash +script_source="${BASH_SOURCE[0]:-$0}" +script_dir="$(cd "$(dirname "${script_source}")" && pwd)" + +export build_dir="$(python3 -c 'import os,sys;print(os.path.realpath(sys.argv[1]))' "${1:-${script_dir}/../build-debug}")" +export build_type=Debug +export conan_profile=${2:-scwx-macos_clang-18_armv8} +export generator=Ninja +export qt_base="/Users/${USER}/Qt" +export qt_arch=macos +export address_sanitizer=${4:-disabled} + +# Set explicit compiler paths +export CC=$(brew --prefix llvm@18)/bin/clang +export CXX=$(brew --prefix llvm@18)/bin/clang++ +export PATH="$(brew --prefix llvm@18)/bin:$PATH" + +export LDFLAGS="-L$(brew --prefix llvm@18)/lib -L$(brew --prefix llvm@18)/lib/c++" +export CPPFLAGS="-I$(brew --prefix llvm@18)/include" + +# Assign user-specified Python Virtual Environment +if [ "${3:-}" = "none" ]; then + unset venv_path +else + # macOS does not have 'readlink -f', use python for realpath + export venv_path="$(python3 -c 'import os,sys;print(os.path.realpath(sys.argv[1]))' "${3:-${script_dir}/../.venv}")" +fi + +# Perform common setup +"${script_dir}/lib/setup-common.sh" diff --git a/tools/setup-macos-release.sh b/tools/setup-macos-release.sh new file mode 100755 index 00000000..870dd841 --- /dev/null +++ b/tools/setup-macos-release.sh @@ -0,0 +1,30 @@ +#!/bin/bash +script_source="${BASH_SOURCE[0]:-$0}" +script_dir="$(cd "$(dirname "${script_source}")" && pwd)" + +export build_dir="$(python3 -c 'import os,sys;print(os.path.realpath(sys.argv[1]))' "${1:-${script_dir}/../build-release}")" +export build_type=Release +export conan_profile=${2:-scwx-macos_clang-18_armv8} +export generator=Ninja +export qt_base="/Users/${USER}/Qt" +export qt_arch=macos +export address_sanitizer=${4:-disabled} + +# Set explicit compiler paths +export CC=$(brew --prefix llvm@18)/bin/clang +export CXX=$(brew --prefix llvm@18)/bin/clang++ +export PATH="$(brew --prefix llvm@18)/bin:$PATH" + +export LDFLAGS="-L$(brew --prefix llvm@18)/lib -L$(brew --prefix llvm@18)/lib/c++" +export CPPFLAGS="-I$(brew --prefix llvm@18)/include" + +# Assign user-specified Python Virtual Environment +if [ "${3:-}" = "none" ]; then + unset venv_path +else + # macOS does not have 'readlink -f', use python for realpath + export venv_path="$(python3 -c 'import os,sys;print(os.path.realpath(sys.argv[1]))' "${3:-${script_dir}/../.venv}")" +fi + +# Perform common setup +"${script_dir}/lib/setup-common.sh" diff --git a/wxdata/source/scwx/network/cpr.cpp b/wxdata/source/scwx/network/cpr.cpp index 81dea5ad..0f8fb956 100644 --- a/wxdata/source/scwx/network/cpr.cpp +++ b/wxdata/source/scwx/network/cpr.cpp @@ -7,8 +7,6 @@ namespace network namespace cpr { -static const std::string logPrefix_ = "scwx::network::cpr"; - static ::cpr::Header header_ {}; ::cpr::Header GetHeader() diff --git a/wxdata/source/scwx/wsr88d/rpg/raster_data_packet.cpp b/wxdata/source/scwx/wsr88d/rpg/raster_data_packet.cpp index c0bf5557..03b00220 100644 --- a/wxdata/source/scwx/wsr88d/rpg/raster_data_packet.cpp +++ b/wxdata/source/scwx/wsr88d/rpg/raster_data_packet.cpp @@ -2,6 +2,7 @@ #include #include +#include #include namespace scwx diff --git a/wxdata/wxdata.cmake b/wxdata/wxdata.cmake index a30398a0..ab23a4e7 100644 --- a/wxdata/wxdata.cmake +++ b/wxdata/wxdata.cmake @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.24) project(scwx-data) +include(CheckCXXSymbolExists) + find_package(Boost) find_package(cpr) find_package(LibXml2) @@ -10,7 +12,9 @@ find_package(range-v3) find_package(re2) find_package(spdlog) -if (NOT MSVC) +check_cxx_symbol_exists(_LIBCPP_VERSION version LIBCPP) + +if (LINUX) find_package(TBB) endif() @@ -327,10 +331,16 @@ if (NOT CHRONO_HAS_TIMEZONES_AND_CALENDERS) target_link_libraries(wxdata PUBLIC date::date-tz) endif() -if (NOT MSVC) +if (LINUX) target_link_libraries(wxdata PUBLIC TBB::tbb) endif() +if (LIBCPP) + # Enable support for parallel algorithms + target_compile_options(wxdata PUBLIC -fexperimental-library) + target_link_libraries(wxdata INTERFACE c++experimental) +endif() + set_target_properties(wxdata PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF)