diff --git a/.github/workflows/android-linux.yml b/.github/workflows/android-linux.yml new file mode 100644 index 000000000000..6b7b44d7777e --- /dev/null +++ b/.github/workflows/android-linux.yml @@ -0,0 +1,114 @@ +name: Android-Linux + +on: + push: + branches: + - master + - 'Stable*' + tags: + - 'v*' + paths-ignore: + - 'docs/**' # Do not trigger for any changes under docs + pull_request: + paths: + - '.github/workflows/android-linux.yml' + - 'deploy/android/**' + - 'src/**' + - 'android/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'translations/*' + +# concurrency: +# group: ${{ github.workflow }}-${{ github.ref }} +# cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + BuildType: [Release] + + defaults: + run: + shell: bash + + env: + ARTIFACT: QGroundControl.apk + PACKAGE: QGroundControl + QT_VERSION: 6.8.3 + QT_ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/deploy/android/android_release.keystore + QT_ANDROID_KEYSTORE_ALIAS: QGCAndroidKeyStore + QT_ANDROID_KEYSTORE_STORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + QT_ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + QT_ANDROID_ABIS: ${{ matrix.BuildType == 'Release' && 'armeabi-v7a;arm64-v8a' || 'arm64-v8a' }} + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 1 + fetch-tags: false + + - name: Get all tags for correct version determination + working-directory: ${{ github.workspace }} + run: | + git fetch --all --tags -f --depth 1 + + - name: Initial Setup + uses: ./.github/actions/common + + - name: Free Disk Space + if: runner.os == 'Linux' + uses: jlumbroso/free-disk-space@main + with: + tool-cache: false + android: false + large-packages: false + + - name: Install Qt for Android + uses: ./.github/actions/qt-android + with: + host: linux + arch: linux_gcc_64 + version: ${{ env.QT_VERSION }} + abis: ${{ env.QT_ANDROID_ABIS }} + cpm-modules: ${{ runner.temp }}/shadow_build_dir/cpm_modules + + - name: Configure + working-directory: ${{ runner.temp }}/shadow_build_dir + run: ${{ env.QT_ROOT_DIR }}/bin/qt-cmake -S ${{ github.workspace }} -B . -G Ninja + -DCMAKE_BUILD_TYPE=${{ matrix.BuildType }} + -DCMAKE_WARN_DEPRECATED=FALSE + -DQT_ANDROID_ABIS="${{ env.QT_ANDROID_ABIS }}" + -DQT_ANDROID_BUILD_ALL_ABIS=OFF + -DQT_HOST_PATH="${{ env.QT_ROOT_DIR }}/../gcc_64" + -DQT_ANDROID_SIGN_APK=${{ env.QT_ANDROID_KEYSTORE_STORE_PASS != '' && 'ON' || 'OFF' }} + -DQT_DEBUG_FIND_PACKAGE=ON + -DQGC_STABLE_BUILD=${{ github.ref_type == 'tag' || contains(github.ref, 'Stable') && 'ON' || 'OFF' }} + + - name: Build + working-directory: ${{ runner.temp }}/shadow_build_dir + run: cmake --build . --target all --config ${{ matrix.BuildType }} + + - run: cp ${{ runner.temp }}/shadow_build_dir/android-build/*.apk ${{ runner.temp }}/shadow_build_dir/${{ env.ARTIFACT }} + + - name: Upload Build File + if: matrix.BuildType == 'Release' + uses: ./.github/actions/upload + with: + artifact_name: ${{ env.ARTIFACT }} + package_name: ${{ env.PACKAGE }} + aws_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }} + + # - name: Deploy to Play Store + # if: matrix.BuildType == 'Release' + # uses: ./.github/actions/playstore + # with: + # artifact_name: ${{ runner.temp }}/shadow_build_dir/${{ env.ARTIFACT }} + # service_account_json: ${{ secrets.SERVICE_ACCOUNT }} diff --git a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java index 8427e496d2f3..bbf624732e32 100644 --- a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java +++ b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java @@ -148,6 +148,67 @@ public boolean dispatchKeyEvent(KeyEvent event) { return super.dispatchKeyEvent(event); } + /** + * Checks and requests storage permissions for SD card access. + * For Android 11+ (API 30+), this requires MANAGE_EXTERNAL_STORAGE permission. + * + * @return true if permissions are granted, false otherwise + */ + public static boolean checkStoragePermissions() { + if (m_instance == null) { + Log.e(TAG, "Activity instance is null"); + return false; + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + // Android 11+ (API 30+) requires MANAGE_EXTERNAL_STORAGE for full SD card access + if (!Environment.isExternalStorageManager()) { + Log.i(TAG, "MANAGE_EXTERNAL_STORAGE not granted, requesting..."); + try { + Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); + intent.setData(Uri.parse("package:" + m_instance.getPackageName())); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + m_instance.startActivity(intent); + } catch (Exception e) { + Log.e(TAG, "Failed to open storage permission settings", e); + // Fallback to general settings + Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + m_instance.startActivity(intent); + } + return false; + } + Log.i(TAG, "MANAGE_EXTERNAL_STORAGE already granted"); + return true; + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + // Android 6.0+ (API 23+) requires runtime permissions + String[] permissions = { + android.Manifest.permission.READ_EXTERNAL_STORAGE, + android.Manifest.permission.WRITE_EXTERNAL_STORAGE + }; + + boolean allGranted = true; + for (String permission : permissions) { + if (ContextCompat.checkSelfPermission(m_instance, permission) != PackageManager.PERMISSION_GRANTED) { + allGranted = false; + break; + } + } + + if (!allGranted) { + Log.i(TAG, "Storage permissions not granted, requesting..."); + ActivityCompat.requestPermissions(m_instance, permissions, 1); + return false; + } + + Log.i(TAG, "Storage permissions already granted"); + return true; + } else { + // Below Android 6.0, permissions are granted at install time + return true; + } + } + // Native C++ functions public native boolean nativeInit(); public native void qgcLogDebug(final String message); diff --git a/cmake/CreateMacDMG.cmake b/cmake/CreateMacDMG.cmake new file mode 100644 index 000000000000..722819d370f0 --- /dev/null +++ b/cmake/CreateMacDMG.cmake @@ -0,0 +1,12 @@ + +set(STAGING_BUNDLE_PATH ${CMAKE_BINARY_DIR}/staging/${TARGET_APP_NAME}.app) + +file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/package) +file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/package) +file(COPY ${STAGING_BUNDLE_PATH} DESTINATION ${CMAKE_BINARY_DIR}/package) + +message(STATUS "Creating DMG: ${TARGET_APP_NAME}.dmg") +execute_process( + COMMAND ${CREATE_DMG_PROGRAM} --volname "${TARGET_APP_NAME}" --filesystem "APFS" "${TARGET_APP_NAME}.dmg" "${CMAKE_BINARY_DIR}/package/" + COMMAND_ERROR_IS_FATAL ANY +) diff --git a/cmake/CreateWinInstaller.cmake b/cmake/CreateWinInstaller.cmake new file mode 100644 index 000000000000..fb6be7a62908 --- /dev/null +++ b/cmake/CreateWinInstaller.cmake @@ -0,0 +1,36 @@ +message(STATUS "Creating Windows NSIS Installer") + +file(TO_NATIVE_PATH "${QGC_WINDOWS_INSTALLER_SCRIPT}" QGC_NSIS_INSTALLER_SCRIPT) +set(_PF86 "PROGRAMFILES(x86)") +find_program(QGC_NSIS_INSTALLER_CMD makensis + PATHS "$ENV{PROGRAMFILES}/NSIS" "$ENV{${_PF86}}/NSIS" "$ENV{PROGRAMW6432}/NSIS" + DOC "Path to the makensis utility." + REQUIRED +) + +file(TO_NATIVE_PATH "${QGC_WINDOWS_ICON_PATH}" QGC_INSTALLER_ICON) +file(TO_NATIVE_PATH "${QGC_WINDOWS_INSTALL_HEADER_PATH}" QGC_INSTALLER_HEADER_BITMAP) +file(TO_NATIVE_PATH "${QGC_WINDOWS_DRIVER_MSI}" QGC_INSTALLER_DRIVER_MSI) +file(TO_NATIVE_PATH "${QGC_WINDOWS_OUT}" QGC_INSTALLER_OUT) + +set(QGC_NSIS_INSTALLER_PARAMETERS + /DDRIVER_MSI=${QGC_INSTALLER_DRIVER_MSI} + /DINSTALLER_ICON=${QGC_INSTALLER_ICON} + /DHEADER_BITMAP=${QGC_INSTALLER_HEADER_BITMAP} + /DAPPNAME=${CMAKE_PROJECT_NAME} + /DEXENAME=${CMAKE_PROJECT_NAME} + /DORGNAME=${QGC_ORG_NAME} + /DDESTDIR=${CMAKE_INSTALL_PREFIX} + /NOCD + "/XOutFile ${QGC_INSTALLER_OUT}" + ${QGC_NSIS_INSTALLER_SCRIPT} +) + +execute_process( + COMMAND ${QGC_NSIS_INSTALLER_CMD} ${QGC_NSIS_INSTALLER_PARAMETERS} + COMMAND_ECHO STDOUT + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE + ECHO_OUTPUT_VARIABLE + ECHO_ERROR_VARIABLE +) diff --git a/cmake/Git.cmake b/cmake/Git.cmake new file mode 100644 index 000000000000..89dd307cb340 --- /dev/null +++ b/cmake/Git.cmake @@ -0,0 +1,128 @@ +find_package(Git) + +# Initialize default values +set(QGC_GIT_BRANCH "unknown") +set(QGC_GIT_HASH "unknown") +set(QGC_APP_VERSION_STR "v5.0.8") +set(QGC_APP_VERSION "5.0.8") +set(QGC_APP_DATE "unknown") + +if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git") + option(GIT_SUBMODULE "Check submodules during build" OFF) + if(GIT_SUBMODULE) + message(STATUS "Submodule update") + execute_process( + COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + RESULT_VARIABLE GIT_SUBMODULE_RESULT + OUTPUT_VARIABLE GIT_SUBMODULE_OUTPUT + ERROR_VARIABLE GIT_SUBMODULE_ERROR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT GIT_SUBMODULE_RESULT EQUAL 0) + cmake_print_variables(GIT_SUBMODULE_RESULT GIT_SUBMODULE_OUTPUT GIT_SUBMODULE_ERROR) + message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMODULE_RESULT}, please checkout submodules") + endif() + endif() + + include(CMakePrintHelpers) + + # Get git branch with error handling + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + RESULT_VARIABLE GIT_BRANCH_RESULT + OUTPUT_VARIABLE QGC_GIT_BRANCH + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT GIT_BRANCH_RESULT EQUAL 0) + set(QGC_GIT_BRANCH "unknown") + endif() + + # Get git hash with error handling + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + RESULT_VARIABLE GIT_HASH_RESULT + OUTPUT_VARIABLE QGC_GIT_HASH + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT GIT_HASH_RESULT EQUAL 0) + set(QGC_GIT_HASH "unknown") + endif() + + # Get version string with error handling + execute_process( + COMMAND ${GIT_EXECUTABLE} describe --always --tags + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + RESULT_VARIABLE GIT_DESCRIBE_RESULT + OUTPUT_VARIABLE QGC_APP_VERSION_STR + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT GIT_DESCRIBE_RESULT EQUAL 0) + set(QGC_APP_VERSION_STR "v5.0.8") + endif() + + # Get version tag with error handling + execute_process( + COMMAND ${GIT_EXECUTABLE} describe --always --abbrev=0 + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + RESULT_VARIABLE GIT_VERSION_RESULT + OUTPUT_VARIABLE QGC_APP_VERSION + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT GIT_VERSION_RESULT EQUAL 0) + set(QGC_APP_VERSION "v5.0.8") + endif() + + # Get commit date with error handling + execute_process( + COMMAND ${GIT_EXECUTABLE} log -1 --format=%aI HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + RESULT_VARIABLE GIT_DATE_RESULT + OUTPUT_VARIABLE QGC_APP_DATE + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT GIT_DATE_RESULT EQUAL 0) + set(QGC_APP_DATE "unknown") + endif() + +else() + # Not a git repository, use hardcoded values + message(STATUS "Not a git repository, using hardcoded version 5.0.7") + set(QGC_GIT_BRANCH "release") + set(QGC_GIT_HASH "unknown") + set(QGC_APP_VERSION_STR "v5.0.8") + set(QGC_APP_VERSION "v5.0.8") + set(QGC_APP_DATE "2025-01-01") +endif() + +# Safe version parsing +string(FIND "${QGC_APP_VERSION}" "v" QGC_APP_VERSION_VALID) +if(QGC_APP_VERSION_VALID GREATER -1) + string(REPLACE "v" "" QGC_APP_VERSION ${QGC_APP_VERSION}) +else() + set(QGC_APP_VERSION "5.0.8") +endif() + +# Safe regex matching +string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" QGC_APP_VERSION_MATCH "${QGC_APP_VERSION}") +if(QGC_APP_VERSION_MATCH) + set(QGC_APP_VERSION_MAJOR ${CMAKE_MATCH_1}) + set(QGC_APP_VERSION_MINOR ${CMAKE_MATCH_2}) + set(QGC_APP_VERSION_PATCH ${CMAKE_MATCH_3}) +else() + # Fallback if regex fails + set(QGC_APP_VERSION_MAJOR "5") + set(QGC_APP_VERSION_MINOR "0") + set(QGC_APP_VERSION_PATCH "8") +endif() + +message(STATUS "QGC Version: ${QGC_APP_VERSION_MAJOR}.${QGC_APP_VERSION_MINOR}.${QGC_APP_VERSION_PATCH}") +message(STATUS "Git Branch: ${QGC_GIT_BRANCH}") +message(STATUS "Git Hash: ${QGC_GIT_HASH}") \ No newline at end of file diff --git a/cmake/Install.cmake b/cmake/Install.cmake new file mode 100644 index 000000000000..419d55366bbf --- /dev/null +++ b/cmake/Install.cmake @@ -0,0 +1,100 @@ +include(InstallRequiredSystemLibraries) + +install( + TARGETS ${CMAKE_PROJECT_NAME} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + BUNDLE DESTINATION . +) + +set(deploy_tool_options_arg "") +if(MACOS OR WIN32) + set(deploy_tool_options_arg "-qmldir=${CMAKE_SOURCE_DIR}") + if(MACOS) + list(APPEND deploy_tool_options_arg "-appstore-compliant") + endif() +endif() + +# Set extra deploy QML app script options for Qt 6.7.0 and above +set(EXTRA_DEPLOY_QML_APP_SCRIPT_OPTIONS) +if(Qt6_VERSION VERSION_GREATER_EQUAL 6.7.0) + list(APPEND EXTRA_DEPLOY_QML_APP_SCRIPT_OPTIONS DEPLOY_TOOL_OPTIONS ${deploy_tool_options_arg}) +endif() + +qt_generate_deploy_qml_app_script( + TARGET ${CMAKE_PROJECT_NAME} + OUTPUT_SCRIPT deploy_script + ${EXTRA_DEPLOY_QML_APP_SCRIPT_OPTIONS} + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM +) +install(SCRIPT ${deploy_script}) + +if(ANDROID) + # get_target_property(QGC_ANDROID_DEPLOY_FILE ${CMAKE_PROJECT_NAME} QT_ANDROID_DEPLOYMENT_SETTINGS_FILE) + # cmake_print_variables(QGC_ANDROID_DEPLOY_FILE) +elseif(LINUX) + configure_file( + ${CMAKE_SOURCE_DIR}/deploy/linux/org.mavlink.qgroundcontrol.desktop.in + ${CMAKE_BINARY_DIR}/org.mavlink.qgroundcontrol.desktop + @ONLY + ) + install( + FILES ${CMAKE_BINARY_DIR}/org.mavlink.qgroundcontrol.desktop + DESTINATION ${CMAKE_INSTALL_DATADIR}/applications + ) + install( + FILES ${QGC_APPIMAGE_ICON_PATH} + DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps/ + RENAME ${CMAKE_PROJECT_NAME}.png + ) + configure_file( + ${CMAKE_SOURCE_DIR}/deploy/linux/org.mavlink.qgroundcontrol.metainfo.xml.in + ${CMAKE_BINARY_DIR}/metainfo/org.mavlink.qgroundcontrol.metainfo.xml + @ONLY + ) + install( + FILES ${CMAKE_BINARY_DIR}/metainfo/org.mavlink.qgroundcontrol.metainfo.xml + DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo/ + ) + install( + FILES ${CMAKE_SOURCE_DIR}/deploy/linux/AppRun + DESTINATION ${CMAKE_BINARY_DIR} + ) + install(SCRIPT "${CMAKE_SOURCE_DIR}/cmake/CreateAppImage.cmake") +elseif(WIN32) + install(CODE "set(CMAKE_PROJECT_NAME ${CMAKE_PROJECT_NAME})") + install(CODE "set(QGC_ORG_NAME ${QGC_ORG_NAME})") + install(CODE "set(QGC_WINDOWS_ICON_PATH ${QGC_WINDOWS_ICON_PATH})") + install(CODE "set(QGC_WINDOWS_INSTALL_HEADER_PATH ${QGC_WINDOWS_INSTALL_HEADER_PATH})") + install(CODE "set(QGC_WINDOWS_DRIVER_MSI ${CMAKE_SOURCE_DIR}/deploy/windows/driver.msi)") + install(CODE "set(QGC_WINDOWS_OUT ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}-installer.exe)") + install(CODE "set(QGC_WINDOWS_INSTALLER_SCRIPT ${CMAKE_SOURCE_DIR}/deploy/windows/nullsoft_installer.nsi)") + install(SCRIPT "${CMAKE_SOURCE_DIR}/cmake/CreateWinInstaller.cmake") +elseif(MACOS) + install(CODE "set(QGC_STAGING_BUNDLE_PATH \"${CMAKE_BINARY_DIR}/staging/${CMAKE_PROJECT_NAME}.app\")") + if(QGC_MACOS_SIGN_WITH_IDENTITY) + message(STATUS "QGC: Signing Bundle using signing identity") + install(SCRIPT "${CMAKE_SOURCE_DIR}/cmake/SignMacBundle.cmake") + else() + message(STATUS "QGC: Signing Bundle using Ad-Hoc signing") + install(CODE " + message(STATUS \"QGC: Signing Bundle using Ad-Hoc signing\") + execute_process( + COMMAND codesign --force --deep -s - \"\${QGC_STAGING_BUNDLE_PATH}\" + COMMAND_ERROR_IS_FATAL ANY + ) + ") + endif() + + install(CODE "set(TARGET_APP_NAME ${QGC_APP_NAME})") + find_program(CREATE_DMG_PROGRAM create-dmg) + if(NOT CREATE_DMG_PROGRAM) + message(FATAL_ERROR "create-dmg not found. Please install it using `sh qgroundcontrol/tools/setup/install-dependencies-osx.sh`") + endif() + install(CODE "set(CREATE_DMG_PROGRAM \"${CREATE_DMG_PROGRAM}\")") + install(CODE "set(MACDEPLOYQT ${Qt6_DIR}/../../../bin/macdeployqt)") + install(SCRIPT "${CMAKE_SOURCE_DIR}/cmake/CreateMacDMG.cmake") +endif() diff --git a/cmake/SignMacBundle.cmake b/cmake/SignMacBundle.cmake new file mode 100644 index 000000000000..d9720d6ab1d7 --- /dev/null +++ b/cmake/SignMacBundle.cmake @@ -0,0 +1,70 @@ +message(STATUS "QGC: Signing Bundle using signing identity") +if(NOT DEFINED ENV{QGC_MACOS_SIGNING_IDENTITY} OR "$ENV{QGC_MACOS_SIGNING_IDENTITY}" STREQUAL "") + message(FATAL_ERROR "QGC: QGC_MACOS_SIGNING_IDENTITY environment variable must be set to sign MacOS bundle") +endif() +if(NOT DEFINED ENV{QGC_MACOS_NOTARIZATION_USERNAME} OR "$ENV{QGC_MACOS_NOTARIZATION_USERNAME}" STREQUAL "") + message(FATAL_ERROR "QGC: QGC_MACOS_NOTARIZATION_USERNAME environment variable must be set to notarize MacOS bundle") +endif() +if(NOT DEFINED ENV{QGC_MACOS_NOTARIZATION_TEAM_ID} OR "$ENV{QGC_MACOS_NOTARIZATION_TEAM_ID}" STREQUAL "") + message(FATAL_ERROR "QGC: QGC_MACOS_NOTARIZATION_TEAM_ID environment variable must be set to notarize MacOS bundle") +endif() +if(NOT DEFINED ENV{QGC_MACOS_NOTARIZATION_PASSWORD} OR "$ENV{QGC_MACOS_NOTARIZATION_PASSWORD}" STREQUAL "") + message(FATAL_ERROR "QGC: QGC_MACOS_NOTARIZATION_PASSWORD environment variable must be set to notarize MacOS bundle") +endif() +file(REMOVE ${QGC_STAGING_BUNDLE_PATH}/Contents/Frameworks/GStreamer.framework/Commands) +file(REMOVE ${QGC_STAGING_BUNDLE_PATH}/Contents/Frameworks/GStreamer.framework/Versions/1.0/Commands) +execute_process( + COMMAND find ${QGC_STAGING_BUNDLE_PATH}/Contents -type f -name "*.dylib" -exec codesign --timestamp --options=runtime --force -s "$ENV{QGC_MACOS_SIGNING_IDENTITY}" "{}" \\; + COMMAND_ERROR_IS_FATAL ANY +) +execute_process( + COMMAND find ${QGC_STAGING_BUNDLE_PATH}/Contents -type f -name "*.so" -exec codesign --timestamp --options=runtime --force -s "$ENV{QGC_MACOS_SIGNING_IDENTITY}" "{}" \\; + COMMAND_ERROR_IS_FATAL ANY +) +execute_process( + COMMAND find ${QGC_STAGING_BUNDLE_PATH}/Contents/Frameworks/GStreamer.framework/Versions/1.0/libexec/gstreamer-1.0 -type f -name "*" -exec codesign --timestamp --options=runtime --force -s "$ENV{QGC_MACOS_SIGNING_IDENTITY}" "{}" \\; + COMMAND_ERROR_IS_FATAL ANY +) +execute_process( + COMMAND codesign --timestamp --options=runtime --force -s "$ENV{QGC_MACOS_SIGNING_IDENTITY}" ${QGC_STAGING_BUNDLE_PATH}/Contents/Frameworks/GStreamer.framework/Versions/1.0/lib/GStreamer + COMMAND_ERROR_IS_FATAL ANY +) +execute_process( + COMMAND codesign --timestamp --options=runtime --force -s "$ENV{QGC_MACOS_SIGNING_IDENTITY}" ${QGC_STAGING_BUNDLE_PATH}/Contents/Frameworks/GStreamer.framework/Versions/1.0/GStreamer + COMMAND_ERROR_IS_FATAL ANY +) +file(GLOB FRAMEWORK_DIRS ${QGC_STAGING_BUNDLE_PATH}/Contents/Frameworks/*.framework) +foreach(FRAMEWORK_DIR ${FRAMEWORK_DIRS}) + if (EXISTS "${FRAMEWORK_DIR}/Versions/1.0") + execute_process( + COMMAND find ${FRAMEWORK_DIR}/Versions/1.0 -type f -exec codesign --timestamp --options=runtime --force -s "$ENV{QGC_MACOS_SIGNING_IDENTITY}" "{}" \\; + COMMAND_ERROR_IS_FATAL ANY + ) + endif() + if (EXISTS "${FRAMEWORK_DIR}/Versions/A") + execute_process( + COMMAND find ${FRAMEWORK_DIR}/Versions/A -type f -exec codesign --timestamp --options=runtime --force -s "$ENV{QGC_MACOS_SIGNING_IDENTITY}" "{}" \\; + COMMAND_ERROR_IS_FATAL ANY + ) + endif() +endforeach() +execute_process( + COMMAND codesign --timestamp --options=runtime --force -s "$ENV{QGC_MACOS_SIGNING_IDENTITY}" ${QGC_STAGING_BUNDLE_PATH} + COMMAND_ERROR_IS_FATAL ANY +) +message(STATUS "QGC: Archiving Bundle for Notarization upload") +file(REMOVE qgc_notarization_upload.zip) +execute_process( + COMMAND ditto -c -k --keepParent ${QGC_STAGING_BUNDLE_PATH} qgc_notarization_upload.zip + COMMAND_ERROR_IS_FATAL ANY +) +message(STATUS "QGC: Notarizing app bundle. This may take a while...") +execute_process( + COMMAND xcrun notarytool submit qgc_notarization_upload.zip --apple-id "$ENV{QGC_MACOS_NOTARIZATION_USERNAME}" --team-id "$ENV{QGC_MACOS_NOTARIZATION_TEAM_ID}" --password "$ENV{QGC_MACOS_NOTARIZATION_PASSWORD}" --wait + COMMAND_ERROR_IS_FATAL ANY +) +message(STATUS "QGC: Stapling notarization ticket to app bundle") +execute_process( + COMMAND xcrun stapler staple ${QGC_STAGING_BUNDLE_PATH} + COMMAND_ERROR_IS_FATAL ANY +) diff --git a/cmake/find-modules/FindGStreamer.cmake b/cmake/find-modules/FindGStreamer.cmake index 200da8569dd9..7eb74eca2cb6 100644 --- a/cmake/find-modules/FindGStreamer.cmake +++ b/cmake/find-modules/FindGStreamer.cmake @@ -118,12 +118,10 @@ elseif(LINUX) _gst_normalize_and_validate_root() - if((EXISTS "${GStreamer_ROOT_DIR}/lib/${CMAKE_SYSTEM_PROCESSOR}-linux-gnu") AND (EXISTS "${GStreamer_ROOT_DIR}/lib/${CMAKE_SYSTEM_PROCESSOR}-linux-gnu/gstreamer-1.0")) - set(GSTREAMER_LIB_PATH "${GStreamer_ROOT_DIR}/lib/${CMAKE_SYSTEM_PROCESSOR}-linux-gnu") - elseif((EXISTS "${GStreamer_ROOT_DIR}/lib64") AND (EXISTS "${GStreamer_ROOT_DIR}/lib64/gstreamer-1.0")) - set(GSTREAMER_LIB_PATH "${GStreamer_ROOT_DIR}/lib64") - elseif((EXISTS "${GStreamer_ROOT_DIR}/lib") AND (EXISTS "${GStreamer_ROOT_DIR}/lib/gstreamer-1.0")) - set(GSTREAMER_LIB_PATH "${GStreamer_ROOT_DIR}/lib") + if((EXISTS "${GStreamer_ROOT_DIR}/${LIB_DIR_NAME}/${CMAKE_SYSTEM_PROCESSOR}-linux-gnu" ) AND (EXISTS "${GStreamer_ROOT_DIR}/${LIB_DIR_NAME}/${CMAKE_SYSTEM_PROCESSOR}-linux-gnu/gstreamer-1.0")) + set(GSTREAMER_LIB_PATH "${GStreamer_ROOT_DIR}/${LIB_DIR_NAME}/${CMAKE_SYSTEM_PROCESSOR}-linux-gnu") + elseif(EXISTS "${GStreamer_ROOT_DIR}/${LIB_DIR_NAME}") + set(GSTREAMER_LIB_PATH "${GStreamer_ROOT_DIR}/${LIB_DIR_NAME}") else() message(FATAL_ERROR "Could not locate GStreamer libraries - check installation or set environment/cmake variables") endif() diff --git a/fix-qml-plugins.cmake b/fix-qml-plugins.cmake new file mode 100644 index 000000000000..c4bf684b9d28 --- /dev/null +++ b/fix-qml-plugins.cmake @@ -0,0 +1,123 @@ +function(create_qml_plugin_targets) + set(QML_PLUGINS + qtquick2plugin + qmlplugin + modelsplugin + workscriptplugin + qtquickcontrols2plugin + qtquickcontrols2fusionstyleplugin + qtquickcontrols2materialstyleplugin + qtquickcontrols2imaginestyleplugin + qtquickcontrols2universalstyleplugin + qtquickcontrols2fluentwinui3styleplugin + qtquickcontrols2basicstyleplugin + qtquicktemplates2plugin + qtquickcontrols2implplugin + qtquickcontrols2fusionstyleimplplugin + quickwindowplugin + qtquickcontrols2materialstyleimplplugin + qtquickcontrols2imaginestyleimplplugin + qtquickcontrols2universalstyleimplplugin + qtquickcontrols2fluentwinui3styleimplplugin + effectsplugin + qquicklayoutsplugin + qmlshapesplugin + qtquickcontrols2basicstyleimplplugin + labsmodelsplugin + qtquickdialogsplugin + labsplatformplugin + qtchartsqml2plugin + declarative_locationplugin + positioningquickplugin + labsanimationplugin + qtgraphicaleffectsplugin + qtqmlcoreplugin + qtquickdialogs2quickimplplugin + qtgraphicaleffectsprivateplugin + quickmultimediaplugin + qmlfolderlistmodelplugin + qquick3dplugin + workerscriptplugin + ) + + foreach(plugin ${QML_PLUGINS}) + if(NOT TARGET Qt6::${plugin}) + add_library(Qt6::${plugin} INTERFACE IMPORTED) + set_target_properties(Qt6::${plugin} PROPERTIES + INTERFACE_QT_QML_PLUGIN_TYPE "${plugin}" + ) + message(STATUS "Created interface target: Qt6::${plugin}") + endif() + endforeach() +endfunction() + +function(create_special_qml_targets) + if(NOT TARGET Qt6::quickwindow) + add_library(Qt6::quickwindow INTERFACE IMPORTED) + set_target_properties(Qt6::quickwindow PROPERTIES + INTERFACE_QT_QML_PLUGIN_TYPE "quickwindow" + ) + endif() + + if(NOT TARGET Qt6::LabsPlatformplugin) + add_library(Qt6::LabsPlatformplugin INTERFACE IMPORTED) + set_target_properties(Qt6::LabsPlatformplugin PROPERTIES + INTERFACE_QT_QML_PLUGIN_TYPE "labsplatform" + ) + endif() + + if(NOT TARGET Qt6::qtchartsqml2) + add_library(Qt6::qtchartsqml2 INTERFACE IMPORTED) + set_target_properties(Qt6::qtchartsqml2 PROPERTIES + INTERFACE_QT_QML_PLUGIN_TYPE "qtchartsqml2" + ) + endif() + + if(NOT TARGET Qt6::declarative_location) + add_library(Qt6::declarative_location INTERFACE IMPORTED) + set_target_properties(Qt6::declarative_location PROPERTIES + INTERFACE_QT_QML_PLUGIN_TYPE "declarative_location" + ) + endif() + + if(NOT TARGET Qt6::qtgraphicaleffectsprivate) + add_library(Qt6::qtgraphicaleffectsprivate INTERFACE IMPORTED) + set_target_properties(Qt6::qtgraphicaleffectsprivate PROPERTIES + INTERFACE_QT_QML_PLUGIN_TYPE "qtgraphicaleffectsprivate" + ) + endif() + + if(NOT TARGET Qt6::quickmultimedia) + add_library(Qt6::quickmultimedia INTERFACE IMPORTED) + set_target_properties(Qt6::quickmultimedia PROPERTIES + INTERFACE_QT_QML_PLUGIN_TYPE "quickmultimedia" + ) + endif() +endfunction() + +function(link_all_qml_plugins target_name) + if(NOT TARGET ${target_name}) + message(WARNING "Target ${target_name} not found") + return() + endif() + + set(ESSENTIAL_PLUGINS + Qt6::qtqmlcoreplugin + Qt6::qtquick2plugin + Qt6::qmlplugin + Qt6::qtquickcontrols2plugin + Qt6::qtquicktemplates2plugin + Qt6::qtgraphicaleffectsplugin + Qt6::qquicklayoutsplugin + ) + + target_link_libraries(${target_name} + PRIVATE + ${ESSENTIAL_PLUGINS} + ) + + message(STATUS "Linked QML plugins to target: ${target_name}") +endfunction() + +create_qml_plugin_targets() +create_special_qml_targets() diff --git a/src/AnalyzeView/CMakeLists.txt b/src/AnalyzeView/CMakeLists.txt index 1f5bed0434bb..48c5f593fa89 100644 --- a/src/AnalyzeView/CMakeLists.txt +++ b/src/AnalyzeView/CMakeLists.txt @@ -44,3 +44,22 @@ qt_add_qml_module(AnalyzeViewModule VibrationPage.qml NO_PLUGIN ) + +#===========================================================================# + +if(NOT USE_SYSTEM_ULOG_CPP) +CPMAddPackage( + NAME ulog_cpp + GITHUB_REPOSITORY PX4/ulog_cpp + GIT_TAG main +) + +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(ulog_cpp PRIVATE -Wno-unknown-warning-option) +endif() + +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE ulog_cpp::ulog_cpp) +else() +find_package(ulog_cpp REQUIRED) +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE ulog_cpp) +endif() diff --git a/src/Comms/CMakeLists.txt b/src/Comms/CMakeLists.txt index 8cbd6baa4d3b..543b7399b6fc 100644 --- a/src/Comms/CMakeLists.txt +++ b/src/Comms/CMakeLists.txt @@ -72,6 +72,7 @@ add_subdirectory(Bluetooth) # ZeroConf/mDNS Service Discovery # ---------------------------------------------------------------------------- if(QGC_ZEROCONF_ENABLED) + if(NOT USE_SYSTEM_QMDNSENGINE) CPMAddPackage( NAME qmdnsengine GITHUB_REPOSITORY nitroshare/qmdnsengine @@ -89,6 +90,11 @@ if(QGC_ZEROCONF_ENABLED) else() message(WARNING "QGC: qmdnsengine target not created, ZeroConf will be disabled") endif() + else() + find_package(qmdnsengine REQUIRED) + target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE qmdnsengine) + target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE QGC_ZEROCONF_ENABLED) + endif() endif() # ============================================================================ diff --git a/src/Comms/MockLink/MockLink.cc b/src/Comms/MockLink/MockLink.cc index 16c2349c9c38..fcd6e3565aff 100644 --- a/src/Comms/MockLink/MockLink.cc +++ b/src/Comms/MockLink/MockLink.cc @@ -42,6 +42,26 @@ QList MockLink::_availableFlightModes = { { "MockLink Mode (delayed)",0, PX4CustomMode::AUTO_FOLLOW_TARGET, true, false}, }; +QList MockLink::_availableFlightModes = { + // Mode Name Standard Mode Custom Mode CanBeSet adv + { "Manual", 0, PX4CustomMode::MANUAL, true, true }, + { "Stabilized", 0, PX4CustomMode::STABILIZED, true, true }, + { "Acro", 0, PX4CustomMode::ACRO, true, true }, + { "Altitude", 0, PX4CustomMode::ALTCTL, true, false}, + { "Offboard", 0, PX4CustomMode::OFFBOARD, true, true }, + { "Position", 0, PX4CustomMode::POSCTL_POSCTL, true, false}, + { "Orbit", 0, PX4CustomMode::POSCTL_ORBIT, false, true }, + { "Hold", 0, PX4CustomMode::AUTO_LOITER, true, true }, + { "Mission", 0, PX4CustomMode::AUTO_MISSION, true, true }, + { "Return", 0, PX4CustomMode::AUTO_RTL, true, true }, + { "Land", MAV_STANDARD_MODE_LAND, PX4CustomMode::AUTO_LAND, false, true }, + { "Precision Landing", 0, PX4CustomMode::AUTO_PRECLAND, true, true }, + { "Takeoff", MAV_STANDARD_MODE_TAKEOFF, PX4CustomMode::AUTO_TAKEOFF, false, false}, + { "MockLink Mode", 0, PX4CustomMode::RATTITUDE, true, false}, + { "(Mode not available)", 0, PX4CustomMode::AUTO_RTGS, false, false}, + { "MockLink Mode (delayed)",0, PX4CustomMode::AUTO_FOLLOW_TARGET, true, false}, +}; + MockLink::MockLink(SharedLinkConfigurationPtr &config, QObject *parent) : LinkInterface(config, parent) , _mockConfig(qobject_cast(_config.get())) diff --git a/src/Comms/MockLink/MockLink.h b/src/Comms/MockLink/MockLink.h index 6775137fdfe8..ff012fa2f323 100644 --- a/src/Comms/MockLink/MockLink.h +++ b/src/Comms/MockLink/MockLink.h @@ -290,6 +290,10 @@ private slots: QMutex _availableModesWorkerMutex; uint8_t _availableModesMonitorSeqNumber = 0; ///< Sequence number for the next available mode message to send + // Mavlink standard modes worker information + int _availableModesWorkerNextModeIndex = 0; ///< 0: not active, +index: next mode the send in sequence, -index: send a single mode (indices are 1-based) + uint8_t _availableModesMonitorSeqNumber = 0; ///< Sequence number for the next available mode message to send + QString _logDownloadFilename; ///< Filename for log download which is in progress uint32_t _logDownloadCurrentOffset = 0; ///< Current offset we are sending from uint32_t _logDownloadBytesRemaining = 0; ///< Number of bytes still to send, 0 = send inactive diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc index 96ab48cb1052..93f6b38fdd93 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc @@ -40,6 +40,25 @@ PX4FirmwarePlugin::PX4FirmwarePlugin() const QString simpleFlightModeName = tr("Simple"); const QString orbitFlightModeName = tr("Orbit"); + const QString manualFlightModeName = tr("Manual"); + const QString acroFlightModeName = tr("Acro"); + const QString stabilizedFlightModeName = tr("Stabilized"); + const QString rattitudeFlightModeName = tr("Rattitude"); + const QString altCtlFlightModeName = tr("Altitude"); + const QString posCtlFlightModeName = tr("Position"); + const QString offboardFlightModeName = tr("Offboard"); + const QString readyFlightModeName = tr("Ready"); + const QString takeoffFlightModeName = tr("Takeoff"); + const QString holdFlightModeName = tr("Hold"); + const QString missionFlightModeName = tr("Mission"); + const QString rtlFlightModeName = tr("Return"); + const QString landingFlightModeName = tr("Land"); + const QString preclandFlightModeName = tr("Precision Land"); + const QString rtgsFlightModeName = tr("Return to Groundstation"); + const QString followMeFlightModeName = tr("Follow Me"); + const QString simpleFlightModeName = tr("Simple"); + const QString orbitFlightModeName = tr("Orbit"); + _setModeEnumToModeStringMapping({ { PX4CustomMode::MANUAL, manualFlightModeName }, { PX4CustomMode::STABILIZED, stabilizedFlightModeName }, diff --git a/src/GPS/CMakeLists.txt b/src/GPS/CMakeLists.txt index 7a3aba76de22..a2d26dad389b 100644 --- a/src/GPS/CMakeLists.txt +++ b/src/GPS/CMakeLists.txt @@ -35,6 +35,7 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_ # PX4 GPS Drivers Integration # ============================================================================ +if(NOT USE_SYSTEM_PX4-GPSDRIVERS) CPMAddPackage( NAME px4-gpsdrivers GITHUB_REPOSITORY PX4/PX4-GPSDrivers @@ -42,21 +43,10 @@ CPMAddPackage( SOURCE_SUBDIR src ) -# NOTE: Using file(GLOB) for external dependency sources -# CONFIGURE_DEPENDS ensures CMake re-runs if files are added/removed -file(GLOB GPS_DRIVERS_SOURCES - CONFIGURE_DEPENDS - "${px4-gpsdrivers_SOURCE_DIR}/src/*.c" - "${px4-gpsdrivers_SOURCE_DIR}/src/*.cpp" - "${px4-gpsdrivers_SOURCE_DIR}/src/*.h" -) - -add_library(px4-gpsdrivers OBJECT ${GPS_DRIVERS_SOURCES}) -target_include_directories(px4-gpsdrivers SYSTEM PRIVATE ${px4-gpsdrivers_SOURCE_DIR}/src) -target_compile_definitions(px4-gpsdrivers PRIVATE GPS_DEFINITIONS_HEADER=<${CMAKE_CURRENT_SOURCE_DIR}/definitions.h>) -target_link_libraries(px4-gpsdrivers PRIVATE Qt6::Core) -qgc_disable_dependency_warnings(px4-gpsdrivers) - +else() +set(px4-gpsdrivers_SOURCE_DIR /usr/src/PX4-GPSDrivers) +endif() +file(GLOB GPS_DRIVERS_SOURCES "${px4-gpsdrivers_SOURCE_DIR}/src/*") target_sources(${CMAKE_PROJECT_NAME} PRIVATE definitions.h diff --git a/src/Joystick/CMakeLists.txt b/src/Joystick/CMakeLists.txt index 25368778686a..3d6af6350061 100644 --- a/src/Joystick/CMakeLists.txt +++ b/src/Joystick/CMakeLists.txt @@ -21,3 +21,82 @@ target_sources(${CMAKE_PROJECT_NAME} JoystickSDL.cc JoystickSDL.h ) + +target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE QGC_SDL_JOYSTICK) + +#===========================================================================# + +if(NOT USE_SYSTEM_SDL_GAMECONTROLLERDB) +CPMAddPackage( + NAME sdl_gamecontrollerdb + GITHUB_REPOSITORY mdqinc/SDL_GameControllerDB + GIT_TAG master +) + +else() +set(sdl_gamecontrollerdb_SOURCE_DIR /usr/share/SDL_GameControllerDB) +endif() +set(SDL_GAMECONTROLLERDB_PATH "${sdl_gamecontrollerdb_SOURCE_DIR}/gamecontrollerdb.txt" CACHE FILEPATH "SDL GameControllerDB Path") + +#===========================================================================# + +set(SDL2_EXTRA_OPTIONS) +if(WIN32) + set(SDL2_EXTRA_OPTIONS + "SDL_DIRECTX OFF" + "SDL_XINPUT ON" + ) +endif() + +if(NOT USE_SYSTEM_SDL2) +CPMAddPackage( + NAME SDL2 + VERSION 2.32.4 + GITHUB_REPOSITORY libsdl-org/SDL + GIT_TAG release-2.32.4 + OPTIONS + "SDL2_DISABLE_INSTALL ON" + "SDL2_DISABLE_INSTALL ON" + "SDL2_DISABLE_UNINSTALL ON" + "SDL2_DISABLE_SDL2MAIN ON" + "SDL_SHARED OFF" + "SDL_STATIC ON" + "SDL_TEST OFF" + "SDL_ATOMIC ON" + "SDL_AUDIO OFF" + "SDL_CPUINFO ON" + "SDL_EVENTS ON" + "SDL_FILE OFF" + "SDL_FILESYSTEM OFF" + "SDL_HAPTIC ON" + "SDL_HIDAPI ON" + "SDL_JOYSTICK ON" + "SDL_LOADSO ON" + "SDL_LOCALE OFF" + "SDL_MISC OFF" + "SDL_POWER ON" + "SDL_RENDER OFF" + "SDL_SENSOR OFF" + "SDL_THREADS ON" + "SDL_TIMERS OFF" + "SDL_VIDEO OFF" + "SDL_3DNOW OFF" + "SDL_DBUS OFF" + "SDL_IBUS OFF" + "SDL_MMX OFF" + "SDL_VIRTUAL_JOYSTICK ON" + "${SDL2_EXTRA_OPTIONS}" +) + +target_compile_definitions(SDL2-static PRIVATE SDL_MAIN_HANDLED) +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE SDL2::SDL2-static) +else() +find_package(SDL2 2.32.4 REQUIRED CONFIG) +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE SDL2) +target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${SDL2_INCLUDE_DIRS}) +endif() + +# Default list of config overrides - TODO: Add to Custom Build Options +# set(ENV{SDL_GAMECONTROLLERCONFIG} +# "0300000009120000544f000011010000,OpenTX Radiomaster TX16S Joystick,leftx:a3,lefty:a2,rightx:a0,righty:a1,platform:Linux" +# ) diff --git a/src/Joystick/Joystick.cc b/src/Joystick/Joystick.cc index b7e996aa7769..352d98f54bc6 100644 --- a/src/Joystick/Joystick.cc +++ b/src/Joystick/Joystick.cc @@ -1,3 +1,15 @@ +/**************************************************************************** + * + * (c) 2009-2024 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + + +#define GRIPPER_ACTION_RELEASE GRIPPER_ACTION_OPEN +#define GRIPPER_ACTION_GRAB GRIPPER_ACTION_CLOSE #include "Joystick.h" #include "MavlinkAction.h" #include "MavlinkActionManager.h" diff --git a/src/MAVLink/CMakeLists.txt b/src/MAVLink/CMakeLists.txt index c31b7b55196d..18383078a9a4 100644 --- a/src/MAVLink/CMakeLists.txt +++ b/src/MAVLink/CMakeLists.txt @@ -30,6 +30,7 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_ message(STATUS "QGC: Building MAVLink") +if(NOT USE_SYSTEM_MAVLINK) CPMAddPackage( NAME mavlink GIT_REPOSITORY ${QGC_MAVLINK_GIT_REPO} @@ -38,15 +39,33 @@ CPMAddPackage( "MAVLINK_DIALECT ${QGC_MAVLINK_DIALECT}" "MAVLINK_VERSION ${QGC_MAVLINK_VERSION}" ) +else() +set(mavlink_SOURCE_DIR /usr/include/c_library_v2) +file(GLOB MAVLINK_SOURCES1 ${mavlink_SOURCE_DIR}/*) +file(GLOB MAVLINK_SOURCES2 ${mavlink_SOURCE_DIR}/all/*) +file(GLOB MAVLINK_SOURCES3 ${mavlink_SOURCE_DIR}/common/*) +file(GLOB MAVLINK_SOURCES4 ${mavlink_SOURCE_DIR}/development/*) -if(mavlink_ADDED) - add_dependencies(${CMAKE_PROJECT_NAME} mavlink) - target_include_directories(${CMAKE_PROJECT_NAME} - PRIVATE - ${mavlink_BINARY_DIR}/include/mavlink - ${mavlink_BINARY_DIR}/include/mavlink/${QGC_MAVLINK_DIALECT} - ) +target_sources(${CMAKE_PROJECT_NAME} + PRIVATE + ${MAVLINK_SOURCES1} + ${MAVLINK_SOURCES2} + ${MAVLINK_SOURCES3} + ${MAVLINK_SOURCES4} +) endif() +# For QGC all dialects means common and development. Though use of development mavlink code should be restricted to debug builds only. +target_include_directories(${CMAKE_PROJECT_NAME} + PRIVATE + ${mavlink_SOURCE_DIR} + ${mavlink_SOURCE_DIR}/all + ${mavlink_SOURCE_DIR}/common + ${mavlink_SOURCE_DIR}/development +) + +# if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") +# target_compile_options(comm PRIVATE -Wno-address-of-packed-member) +# endif() # ---------------------------------------------------------------------------- # LibEvents - MAVLink event handling diff --git a/src/MAVLink/LibEvents/CMakeLists.txt b/src/MAVLink/LibEvents/CMakeLists.txt index 29a027d21fb7..65f54842d90a 100644 --- a/src/MAVLink/LibEvents/CMakeLists.txt +++ b/src/MAVLink/LibEvents/CMakeLists.txt @@ -1,32 +1,47 @@ -# ============================================================================ -# LibEvents - MAVLink Event Handling -# Event logging, health reporting, and arming checks -# ============================================================================ - -target_sources(${CMAKE_PROJECT_NAME} - PRIVATE - EventHandler.cc - EventHandler.h - HealthAndArmingCheckReport.cc - HealthAndArmingCheckReport.h - logging.cpp -) - -target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) - -# ============================================================================ -# LibEvents Library Integration -# ============================================================================ - -CPMAddPackage( - NAME libevents - GITHUB_REPOSITORY mavlink/libevents - GIT_TAG main - SOURCE_SUBDIR libs/cpp -) - -# libevents is a header-only (INTERFACE) library, so we cannot suppress warnings -# on its target. Instead, mark its include directories as SYSTEM to suppress -# warnings when its headers are compiled as part of QGC source code. -target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE libevents) -target_include_directories(${CMAKE_PROJECT_NAME} SYSTEM PRIVATE ${libevents_SOURCE_DIR}/libs/cpp) +target_sources(${CMAKE_PROJECT_NAME} + PRIVATE + EventHandler.cc + EventHandler.h + HealthAndArmingCheckReport.cc + HealthAndArmingCheckReport.h + logging.cpp +) + +target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +#===========================================================================# + +if(NOT USE_SYSTEM_LIBEVENTS) +CPMAddPackage( + NAME libevents + GITHUB_REPOSITORY mavlink/libevents + GIT_TAG main + SOURCE_SUBDIR libs/cpp +) + +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE libevents) +target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${libevents_SOURCE_DIR}/libs/cpp) +else() +find_package(libevents REQUIRED) +set(LIBEVENTS_INCLUDE_DIRS /usr/include/libevents) +target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${LIBEVENTS_INCLUDE_DIRS}) +find_library(EVENTS_HEALTH_LIB events_health_and_arming_checks) +find_library(EVENTS_PARSER_LIB events_parser) +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE ${EVENTS_HEALTH_LIB}) +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE ${EVENTS_PARSER_LIB}) +find_package(PkgConfig) + +if(TARGET libevents) + if(TARGET events_health_and_arming_checks) + if(TARGET events_parser) + target_link_libraries(events_health_and_arming_checks PRIVATE events_parser) + message(STATUS "Fixed: events_health_and_arming_checks -> events_parser") + endif() + endif() + + if(TARGET events::health_and_arming_checks AND TARGET events::parser) + target_link_libraries(events::health_and_arming_checks PRIVATE events::parser) + message(STATUS "Fixed: events::health_and_arming_checks -> events::parser") + endif() +endif() +endif() diff --git a/src/MAVLink/QGCMAVLink.h b/src/MAVLink/QGCMAVLink.h index d681fe3b26ea..068933317adc 100644 --- a/src/MAVLink/QGCMAVLink.h +++ b/src/MAVLink/QGCMAVLink.h @@ -1,3 +1,14 @@ +/**************************************************************************** + * + * (c) 2009-2024 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#define GRIPPER_ACTION_RELEASE GRIPPER_ACTION_OPEN +#define GRIPPER_ACTION_GRAB GRIPPER_ACTION_CLOSE #pragma once #include diff --git a/src/QtLocationPlugin/QGCTileCacheWorker.h b/src/QtLocationPlugin/QGCTileCacheWorker.h index 59c6857889ab..fe89509d4b96 100644 --- a/src/QtLocationPlugin/QGCTileCacheWorker.h +++ b/src/QtLocationPlugin/QGCTileCacheWorker.h @@ -7,6 +7,7 @@ #include #include #include +#include #include diff --git a/src/Utilities/Compression/CMakeLists.txt b/src/Utilities/Compression/CMakeLists.txt index 3288c42bd9da..aeb281d23fee 100644 --- a/src/Utilities/Compression/CMakeLists.txt +++ b/src/Utilities/Compression/CMakeLists.txt @@ -43,6 +43,7 @@ if(WIN32) ) endif() +if(NOT USE_SYSTEM_ZLIB) CPMAddPackage( NAME zlib GITHUB_REPOSITORY madler/zlib @@ -65,19 +66,12 @@ endif() target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE ZLIB::ZLIBSTATIC) -# Export ZLIB paths for libarchive to find -set(ZLIB_INCLUDE_DIR "${zlib_SOURCE_DIR};${zlib_BINARY_DIR}" CACHE PATH "" FORCE) -set(ZLIB_LIBRARY "zlibstatic" CACHE STRING "" FORCE) -set(ZLIB_LIBRARIES "zlibstatic" CACHE STRING "" FORCE) -set(ZLIB_FOUND TRUE CACHE BOOL "" FORCE) -if(NOT TARGET ZLIB::ZLIB) - add_library(ZLIB::ZLIB ALIAS zlibstatic) +else() +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE z) endif() +#===========================================================================# -# ============================================================================ -# XZ (LZMA) Integration - liblzma from xz-utils -# ============================================================================ - +if(NOT USE_SYSTEM_XZ-EMBEDDED) CPMAddPackage( NAME xz VERSION 5.8.2 @@ -332,3 +326,24 @@ else() endif() +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE xz) +else() + +add_library(xz STATIC ${LIB_PREFIX}/libxz.a) +set_target_properties(xz PROPERTIES + IMPORTED_LOCATION "${LIB_PREFIX}/libxz.a" + INTERFACE_INCLUDE_DIRECTORIES "/usr/include/xz-embedded" +) + +target_compile_definitions(xz + PUBLIC + XZ_USE_CRC64 + XZ_USE_CRC32 + XZ_DEC_ANY_CHECK +) + + +target_include_directories(xz INTERFACE /usr/include/xz-embedded) +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE xz) +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE "${LIB_PREFIX}/libxz.a") +endif() diff --git a/src/Utilities/Geo/CMakeLists.txt b/src/Utilities/Geo/CMakeLists.txt index 59c96845e685..5b2986d9ce76 100644 --- a/src/Utilities/Geo/CMakeLists.txt +++ b/src/Utilities/Geo/CMakeLists.txt @@ -34,6 +34,7 @@ qt_add_resources(${CMAKE_PROJECT_NAME} "kml_schema" # GeographicLib Integration # ============================================================================ +if(NOT USE_SYSTEM_GEOGRAPHICLIB) CPMAddPackage( NAME geographiclib VERSION 2.7 @@ -59,25 +60,7 @@ CPMAddPackage( ) target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE GeographicLib::GeographicLib) -if(geographiclib_ADDED) - qgc_disable_dependency_warnings(GeographicLib_STATIC) -endif() - -# ============================================================================ -# Shapelib Integration -# ============================================================================ - -CPMAddPackage( - NAME Shapelib - VERSION 1.6.2 - GITHUB_REPOSITORY OSGeo/shapelib - OPTIONS - "BUILD_SHAPELIB_CONTRIB OFF" - "BUILD_APPS OFF" - "BUILD_TESTING OFF" -) - -target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE shp) -if(Shapelib_ADDED) - qgc_disable_dependency_warnings(shp) +else() +find_package(GeographicLib REQUIRED) +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE GeographicLib) endif() diff --git a/src/Utilities/Shape/CMakeLists.txt b/src/Utilities/Shape/CMakeLists.txt new file mode 100644 index 000000000000..850ba1e174d8 --- /dev/null +++ b/src/Utilities/Shape/CMakeLists.txt @@ -0,0 +1,29 @@ +target_sources(${CMAKE_PROJECT_NAME} + PRIVATE + KMLDomDocument.cc + KMLDomDocument.h + KMLHelper.cc + KMLHelper.h + ShapeFileHelper.cc + ShapeFileHelper.h + SHPFileHelper.cc + SHPFileHelper.h +) + +target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +#===========================================================================# + +if(NOT USE_SYSTEM_SHAPE) +CPMAddPackage( + NAME Shapelib + VERSION 1.6.1 + GITHUB_REPOSITORY OSGeo/shapelib + OPTIONS + "BUILD_SHAPELIB_CONTRIB OFF" + "BUILD_APPS OFF" + "BUILD_TESTING OFF" +) +endif() + +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE shp) diff --git a/src/Vehicle/StandardModes.cc b/src/Vehicle/StandardModes.cc index cd8e070e1602..b19dd1419484 100644 --- a/src/Vehicle/StandardModes.cc +++ b/src/Vehicle/StandardModes.cc @@ -49,15 +49,18 @@ void StandardModes::gotMessage(MAV_RESULT result, const mavlink_message_t &messa break; case MAV_STANDARD_MODE_SAFE_RECOVERY: name = "Safe Recovery"; + cannotBeSet = true; // These are exposed in the UI as separate buttons break; case MAV_STANDARD_MODE_MISSION: name = "Mission"; break; case MAV_STANDARD_MODE_LAND: name = "Land"; + cannotBeSet = true; // These are exposed in the UI as separate buttons break; case MAV_STANDARD_MODE_TAKEOFF: name = "Takeoff"; + cannotBeSet = true; // These are exposed in the UI as separate buttons break; } diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index cca1b13bf4ea..97e574f9161b 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -1,3 +1,14 @@ +/**************************************************************************** + * + * (c) 2009-2024 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#define GRIPPER_ACTION_RELEASE GRIPPER_ACTION_OPEN +#define GRIPPER_ACTION_GRAB GRIPPER_ACTION_CLOSE #include "Vehicle.h" #include "Actuators.h" #include "ADSBVehicleManager.h" diff --git a/src/VideoManager/VideoReceiver/GStreamer/CMakeLists.txt b/src/VideoManager/VideoReceiver/GStreamer/CMakeLists.txt index 057db82665df..34d638da70a3 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/CMakeLists.txt +++ b/src/VideoManager/VideoReceiver/GStreamer/CMakeLists.txt @@ -20,6 +20,13 @@ if(QGC_ENABLE_GST_VIDEOSTREAMING) OPTIONAL_COMPONENTS GlEgl GlWayland GlX11 ) endif() + if(USE_SYSTEM_GSTQML6) + target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE "${LIB_PREFIX}/gstreamer-1.0/libgstqml6.so") + else() + add_subdirectory(gstqml6gl) + endif() + + # Build GStreamer Qt6 QML GL plugin add_subdirectory(gstqml6gl) diff --git a/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc b/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc index 8e3c18c98cad..6fe0b7e55139 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc +++ b/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc @@ -114,9 +114,9 @@ void _registerPlugins() #endif #endif -// #if !defined(GST_PLUGIN_qml6_FOUND) && defined(QGC_GST_STATIC_BUILD) +#if !defined(GST_PLUGIN_qml6_FOUND) && defined(QGC_GST_STATIC_BUILD) GST_PLUGIN_STATIC_REGISTER(qml6); -// #endif +#endif GST_PLUGIN_STATIC_REGISTER(qgc); }