mirror of
https://github.com/iDescriptor/iDescriptor.git
synced 2026-06-21 19:35:49 +08:00
fix Windows build
- Updated README.md to include CMake command for installation. - Improved win-deploy.cmake to handle executable path issues and added detailed logging for deployment steps. - Introduced checks for runtime dependencies and optimized DLL copying logic to avoid unnecessary copies. - Added additional MinGW runtime DLLs required for GStreamer and FFmpeg. - Created idescriptor.rc for application versioning and resource management. - Updated resources.qrc to include application icon. - Modified AppsWidget to improve UI for install and download actions. - Adjusted dnssd_service.h to conditionally include headers based on platform. - Enhanced install_ipa.cpp with additional includes for better compatibility. - Updated main.cpp to set up environment variables for GStreamer on Windows. - Improved mainwindow.cpp to add a no devices detected page and integrate dependency checks. - Cleaned up mainwindow.ui by removing unnecessary layout elements. - Implemented check_deps.cpp and check_deps.h for verifying required dependencies on Windows. - Created diagnose_dialog.cpp and diagnose_dialog.h for a dialog to display dependency checks. - Developed diagnose_widget.cpp and diagnose_widget.h to manage and display dependency items. - Enhanced sshterminalwidget.cpp to improve terminal handling on Windows. - Updated welcomewidget.cpp to refine UI layout and spacing for better aesthetics.
This commit is contained in:
@@ -0,0 +1,173 @@
|
|||||||
|
name: Build Windows
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
env:
|
||||||
|
QT_VERSION: "6.7.2"
|
||||||
|
GO_VERSION: "1.23.0"
|
||||||
|
LIBPLIST_VER: "2.7.0"
|
||||||
|
LIBTATSU_VER: "1.0.5"
|
||||||
|
LIBIMOBILEDEVICE_GLUE_VER: "1.3.2"
|
||||||
|
LIBIMOBILEDEVICE_VER: "1.4.0"
|
||||||
|
LIBIRECOVERY_VER: "1.3.1"
|
||||||
|
LIBUSBMUXD_VER: "2.1.1"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-windows:
|
||||||
|
runs-on: windows-2022
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: msys2 {0}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: "recursive"
|
||||||
|
token: ${{ secrets.PAT }}
|
||||||
|
|
||||||
|
- name: Setup MSYS2
|
||||||
|
uses: msys2/setup-msys2@v2
|
||||||
|
with:
|
||||||
|
msystem: mingw64
|
||||||
|
release: false
|
||||||
|
update: false
|
||||||
|
install: >-
|
||||||
|
coreutils
|
||||||
|
base-devel
|
||||||
|
git
|
||||||
|
mingw-w64-x86_64-gcc
|
||||||
|
make
|
||||||
|
libtool
|
||||||
|
autoconf
|
||||||
|
automake-wrapper
|
||||||
|
mingw-w64-x86_64-cmake
|
||||||
|
mingw-w64-x86_64-qt6-base
|
||||||
|
mingw-w64-x86_64-qt6-svg
|
||||||
|
mingw-w64-x86_64-qt6-multimedia
|
||||||
|
mingw-w64-x86_64-qt6-location
|
||||||
|
mingw-w64-x86_64-qt6-positioning
|
||||||
|
mingw-w64-x86_64-qt6-serialport
|
||||||
|
mingw-w64-x86_64-pugixml
|
||||||
|
mingw-w64-x86_64-libusb
|
||||||
|
mingw-w64-x86_64-qrencode
|
||||||
|
mingw-w64-x86_64-curl
|
||||||
|
mingw-w64-x86_64-openssl
|
||||||
|
mingw-w64-x86_64-libzip
|
||||||
|
mingw-w64-x86_64-go
|
||||||
|
mingw-w64-x86_64-nsis
|
||||||
|
p7zip
|
||||||
|
mingw-w64-x86_64-gstreamer
|
||||||
|
mingw-w64-x86_64-gst-plugins-base
|
||||||
|
mingw-w64-x86_64-gst-plugins-good
|
||||||
|
mingw-w64-x86_64-gst-plugins-bad
|
||||||
|
mingw-w64-x86_64-gst-plugins-ugly
|
||||||
|
mingw-w64-x86_64-gst-libav
|
||||||
|
|
||||||
|
- name: Setup .NET SDK
|
||||||
|
uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
dotnet-version: "8.x"
|
||||||
|
|
||||||
|
- name: Install WiX Toolset
|
||||||
|
run: dotnet tool install --global wix
|
||||||
|
|
||||||
|
- name: Download and Extract Bonjour SDK
|
||||||
|
run: |
|
||||||
|
wget "https://gsf-fl.softonic.com/b18/d44/cbdcd43ad683cf6760d45ce891c3035044/bonjoursdksetup.exe?Expires=1757906571&Signature=af8dfce8a002130f34c0e93e17b8dd96d547ff69&url=https://bonjour.en.softonic.com/&Filename=bonjoursdksetup.exe" -O bonjour-sdk.exe
|
||||||
|
|
||||||
|
EXPECTED_HASH="4ff2aae8205aec31b06743782cfcadce"
|
||||||
|
ACTUAL_HASH=$(md5sum bonjour-sdk.exe | awk '{print $1}')
|
||||||
|
|
||||||
|
echo "Expected MD5: $EXPECTED_HASH"
|
||||||
|
echo "Actual MD5: $ACTUAL_HASH"
|
||||||
|
|
||||||
|
if [ "$ACTUAL_HASH" != "$EXPECTED_HASH" ]; then
|
||||||
|
echo "::error::Checksum mismatch!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Checksum verified. Extracting installer..."
|
||||||
|
7z x bonjour-sdk.exe -oBonjourInstaller
|
||||||
|
|
||||||
|
- name: Install Bonjour Runtime
|
||||||
|
shell: powershell
|
||||||
|
run: |
|
||||||
|
$currentPath = Get-Location
|
||||||
|
$msiFilePath = Join-Path -Path $currentPath -ChildPath "BonjourInstaller\Bonjour64.msi"
|
||||||
|
$msiexecArguments = "/i `"$msiFilePath`" /qn"
|
||||||
|
Write-Host "Installing Bonjour Runtime..."
|
||||||
|
Start-Process msiexec -ArgumentList $msiexecArguments -NoNewWindow -Wait
|
||||||
|
|
||||||
|
- name: Install Bonjour SDK
|
||||||
|
shell: powershell
|
||||||
|
run: |
|
||||||
|
$currentPath = Get-Location
|
||||||
|
$msiFilePath = Join-Path -Path $currentPath -ChildPath "BonjourInstaller\BonjourSDK64.msi"
|
||||||
|
$msiexecArguments = "/i `"$msiFilePath`" /qn"
|
||||||
|
Write-Host "Installing Bonjour SDK..."
|
||||||
|
Start-Process msiexec -ArgumentList $msiexecArguments -NoNewWindow -Wait
|
||||||
|
|
||||||
|
"BONJOUR_SDK_HOME=C:/Program Files/Bonjour SDK" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||||
|
|
||||||
|
- name: Build libimobiledevice suite (versioned tarballs)
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
workspace="$PWD"
|
||||||
|
tmp="$PWD/_tmp_libs"
|
||||||
|
mkdir -p "$tmp"
|
||||||
|
cd "$tmp"
|
||||||
|
|
||||||
|
base_url="https://github.com/libimobiledevice"
|
||||||
|
|
||||||
|
libs=( "libplist" "libtatsu" "libimobiledevice-glue" "libimobiledevice" "libirecovery" "libusbmuxd" )
|
||||||
|
|
||||||
|
for name in "${libs[@]}"; do
|
||||||
|
ver_var=$(echo "${name^^}_VER" | sed 's/-/_/g')
|
||||||
|
ver="${!ver_var:-}"
|
||||||
|
if [ -z "$ver" ]; then
|
||||||
|
echo "Version for $name not set (env var $ver_var)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
archive="${name}-${ver}.tar.bz2"
|
||||||
|
url="${base_url}/${name}/releases/download/${ver}/${archive}"
|
||||||
|
echo "=== Processing $name $ver ==="
|
||||||
|
echo "URL: $url"
|
||||||
|
curl -L -o "$archive" "$url"
|
||||||
|
|
||||||
|
echo "Extracting $archive"
|
||||||
|
tar xjf "$archive"
|
||||||
|
|
||||||
|
srcdir="${name}-${ver}"
|
||||||
|
pushd "$srcdir"
|
||||||
|
|
||||||
|
./configure --without-cython
|
||||||
|
make -j"$(nproc)"
|
||||||
|
make install
|
||||||
|
popd
|
||||||
|
echo "Installed $name $ver"
|
||||||
|
done
|
||||||
|
|
||||||
|
# cleanup
|
||||||
|
cd "$workspace"
|
||||||
|
rm -rf "$tmp"
|
||||||
|
|
||||||
|
- name: Configure CMake
|
||||||
|
run: |
|
||||||
|
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
|
||||||
|
|
||||||
|
- name: Build with CMake
|
||||||
|
run: cmake --build build --config Release
|
||||||
|
|
||||||
|
- name: Install & CPack
|
||||||
|
working-directory: build
|
||||||
|
run: |
|
||||||
|
cmake --install .
|
||||||
|
cpack .
|
||||||
|
|
||||||
|
- name: Upload Artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: iDescriptor-Windows-Installer
|
||||||
|
path: build/artifacts/*
|
||||||
+123
-87
@@ -9,6 +9,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
|
|
||||||
# Platform-specific paths for libraries built from source
|
# Platform-specific paths for libraries built from source
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
|
include_directories("C:/msys64/mingw64/include")
|
||||||
|
link_directories("C:/msys64/mingw64/lib")
|
||||||
|
set(PKG_CONFIG_EXECUTABLE "C:/msys64/mingw64/bin/pkg-config.exe")
|
||||||
|
list(APPEND CMAKE_PREFIX_PATH "C:/lxqt")
|
||||||
set(CUSTOM_LIB_PATH "C:/msys64/mingw64/lib")
|
set(CUSTOM_LIB_PATH "C:/msys64/mingw64/lib")
|
||||||
set(CUSTOM_BIN_PATH "C:/msys64/mingw64/bin")
|
set(CUSTOM_BIN_PATH "C:/msys64/mingw64/bin")
|
||||||
set(CUSTOM_INCLUDE_PATH "C:/msys64/mingw64/include")
|
set(CUSTOM_INCLUDE_PATH "C:/msys64/mingw64/include")
|
||||||
@@ -31,25 +35,19 @@ find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia MultimediaWidgets Networ
|
|||||||
# Add QTermWidget
|
# Add QTermWidget
|
||||||
pkg_check_modules(QTERMWIDGET REQUIRED IMPORTED_TARGET qtermwidget6)
|
pkg_check_modules(QTERMWIDGET REQUIRED IMPORTED_TARGET qtermwidget6)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
# Add Avahi for network device discovery
|
# Get the path to the Qt bin directory
|
||||||
if (APPLE)
|
get_target_property(QT_BIN_PATH Qt${QT_VERSION_MAJOR}::Core IMPORTED_LOCATION_RELEASE)
|
||||||
# On macOS, use the built-in Bonjour framework instead of Avahi
|
if(NOT QT_BIN_PATH)
|
||||||
find_library(CORE_SERVICES_FRAMEWORK CoreServices REQUIRED)
|
get_target_property(QT_BIN_PATH Qt${QT_VERSION_MAJOR}::Core IMPORTED_LOCATION_DEBUG)
|
||||||
add_definitions(-DUSE_DNS_SD)
|
endif()
|
||||||
message(STATUS "Using macOS Bonjour framework for network service discovery")
|
if(NOT QT_BIN_PATH)
|
||||||
else()
|
get_target_property(QT_BIN_PATH Qt${QT_VERSION_MAJOR}::Core IMPORTED_LOCATION)
|
||||||
# On Linux and Windows, use Avahi
|
endif()
|
||||||
add_definitions(-DUSE_AVAHI)
|
get_filename_component(QT_BIN_PATH ${QT_BIN_PATH} DIRECTORY)
|
||||||
message(STATUS "Using Avahi for network service discovery")
|
message(STATUS "Found Qt bin directory: ${QT_BIN_PATH}")
|
||||||
pkg_check_modules(AVAHI_CLIENT REQUIRED IMPORTED_TARGET avahi-client)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# pkg_check_modules(AVAHI_CLIENT REQUIRED IMPORTED_TARGET avahi-client)
|
|
||||||
# pkg_check_modules(AVAHI_COMMON REQUIRED IMPORTED_TARGET avahi-common)
|
|
||||||
|
|
||||||
# Link directly to libraries in /usr/local/lib instead of using pkg-config
|
|
||||||
# Force NO_DEFAULT_PATH to only search in /usr/local/lib
|
|
||||||
find_library(IMOBILEDEVICE_LIBRARY
|
find_library(IMOBILEDEVICE_LIBRARY
|
||||||
NAMES imobiledevice-1.0
|
NAMES imobiledevice-1.0
|
||||||
PATHS ${CUSTOM_LIB_PATH}
|
PATHS ${CUSTOM_LIB_PATH}
|
||||||
@@ -64,13 +62,6 @@ find_library(IMOBILEDEVICE_GLUE_LIBRARY
|
|||||||
REQUIRED
|
REQUIRED
|
||||||
)
|
)
|
||||||
|
|
||||||
# find_library(PLIST_LIBRARY
|
|
||||||
# NAMES plist-2.0
|
|
||||||
# PATHS /usr/local/lib
|
|
||||||
# NO_DEFAULT_PATH
|
|
||||||
# REQUIRED
|
|
||||||
# )
|
|
||||||
|
|
||||||
find_library(TATSU_LIBRARY
|
find_library(TATSU_LIBRARY
|
||||||
NAMES tatsu
|
NAMES tatsu
|
||||||
PATHS ${CUSTOM_LIB_PATH}
|
PATHS ${CUSTOM_LIB_PATH}
|
||||||
@@ -89,18 +80,10 @@ find_library(IRECOVERY_LIBRARY
|
|||||||
REQUIRED
|
REQUIRED
|
||||||
)
|
)
|
||||||
|
|
||||||
# # Add missing libraries
|
|
||||||
# find_library(USBMUXD_LIBRARY
|
|
||||||
# NAMES usbmuxd-2.0
|
|
||||||
# PATHS /usr/local/lib
|
|
||||||
# NO_DEFAULT_PATH
|
|
||||||
# REQUIRED
|
|
||||||
# )
|
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
# On MSYS2, these are found in the standard mingw64 prefix
|
# On MSYS2, these are found in the standard mingw64 prefix
|
||||||
find_library(SSL_LIBRARY NAMES ssl REQUIRED)
|
find_library(SSL_LIBRARY NAMES ssl PATHS C:/msys64/mingw64/lib REQUIRED)
|
||||||
find_library(CRYPTO_LIBRARY NAMES crypto REQUIRED)
|
find_library(CRYPTO_LIBRARY NAMES crypto PATHS C:/msys64/mingw64/lib REQUIRED)
|
||||||
else()
|
else()
|
||||||
find_library(SSL_LIBRARY
|
find_library(SSL_LIBRARY
|
||||||
NAMES ssl
|
NAMES ssl
|
||||||
@@ -116,11 +99,7 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Add libssh for SSH connections
|
# Add libssh for SSH connections
|
||||||
find_library(SSH_LIBRARY
|
pkg_check_modules(SSH REQUIRED IMPORTED_TARGET libssh)
|
||||||
NAMES ssh
|
|
||||||
PATHS ${CUSTOM_LIB_PATH} /usr/lib /usr/lib/x86_64-linux-gnu
|
|
||||||
REQUIRED
|
|
||||||
)
|
|
||||||
|
|
||||||
# Apple-specific crypto libraries for SSH
|
# Apple-specific crypto libraries for SSH
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
@@ -128,37 +107,11 @@ if(APPLE)
|
|||||||
find_library(COREFOUNDATION_FRAMEWORK CoreFoundation REQUIRED)
|
find_library(COREFOUNDATION_FRAMEWORK CoreFoundation REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Remove frida support for now
|
|
||||||
# find_library(FRIDA_LIBRARY
|
|
||||||
# NAMES frida-core
|
|
||||||
# PATHS /usr/local/lib /usr/lib /usr/lib/x86_64-linux-gnu
|
|
||||||
# REQUIRED
|
|
||||||
# )
|
|
||||||
|
|
||||||
# find_library(ZIP_LIBRARY
|
|
||||||
# NAMES zip
|
|
||||||
# PATHS /usr/lib /usr/lib/x86_64-linux-gnu
|
|
||||||
# REQUIRED
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
pkg_check_modules(PUGIXML REQUIRED IMPORTED_TARGET pugixml)
|
pkg_check_modules(PUGIXML REQUIRED IMPORTED_TARGET pugixml)
|
||||||
pkg_check_modules(USB REQUIRED IMPORTED_TARGET libusb-1.0)
|
pkg_check_modules(USB REQUIRED IMPORTED_TARGET libusb-1.0)
|
||||||
pkg_check_modules(PLIST REQUIRED IMPORTED_TARGET libplist-2.0)
|
pkg_check_modules(PLIST REQUIRED IMPORTED_TARGET libplist-2.0)
|
||||||
# Use system plist library instead of manually built one - exclude /usr/local/lib
|
|
||||||
# find_library(PLIST_LIBRARY
|
|
||||||
# NAMES plist-2.0
|
|
||||||
# PATHS /usr/lib
|
|
||||||
# NO_DEFAULT_PATH
|
|
||||||
# REQUIRED
|
|
||||||
# )
|
|
||||||
|
|
||||||
# set(PROJECT_SOURCES
|
|
||||||
# src/*.cpp
|
|
||||||
# src/*.h
|
|
||||||
# src/*.ui
|
|
||||||
# resources.qrc
|
|
||||||
# )
|
|
||||||
|
|
||||||
file(GLOB PROJECT_SOURCES
|
file(GLOB PROJECT_SOURCES
|
||||||
src/*.cpp
|
src/*.cpp
|
||||||
@@ -177,6 +130,16 @@ if(APPLE)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
list(APPEND PROJECT_SOURCES
|
||||||
|
src/core/services/dnssd/dnssd_service.cpp
|
||||||
|
src/core/services/dnssd/dnssd_service.h
|
||||||
|
)
|
||||||
|
|
||||||
|
file(GLOB WINDOWS_PLATFORM_SOURCES src/platform/windows/*.cpp src/platform/windows/*.h)
|
||||||
|
list(APPEND PROJECT_SOURCES ${WINDOWS_PLATFORM_SOURCES})
|
||||||
|
endif()
|
||||||
|
|
||||||
if(LINUX)
|
if(LINUX)
|
||||||
list(APPEND PROJECT_SOURCES
|
list(APPEND PROJECT_SOURCES
|
||||||
src/core/services/avahi/avahi_service.cpp
|
src/core/services/avahi/avahi_service.cpp
|
||||||
@@ -189,9 +152,9 @@ endif()
|
|||||||
add_subdirectory(lib/airplay)
|
add_subdirectory(lib/airplay)
|
||||||
add_subdirectory(lib/ipatool-go)
|
add_subdirectory(lib/ipatool-go)
|
||||||
|
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
# todo
|
set(app_icon_resource_windows "${CMAKE_CURRENT_SOURCE_DIR}/idescriptor.rc")
|
||||||
set(app_icon_resource_windows "${CMAKE_CURRENT_SOURCE_DIR}/resources/todo.rc")
|
|
||||||
qt_add_executable(iDescriptor
|
qt_add_executable(iDescriptor
|
||||||
MANUAL_FINALIZATION
|
MANUAL_FINALIZATION
|
||||||
${PROJECT_SOURCES}
|
${PROJECT_SOURCES}
|
||||||
@@ -228,14 +191,11 @@ target_link_libraries(iDescriptor PRIVATE
|
|||||||
Qt6::QuickControls2
|
Qt6::QuickControls2
|
||||||
${IMOBILEDEVICE_LIBRARY}
|
${IMOBILEDEVICE_LIBRARY}
|
||||||
${IMOBILEDEVICE_GLUE_LIBRARY}
|
${IMOBILEDEVICE_GLUE_LIBRARY}
|
||||||
# ${PLIST_LIBRARY}
|
|
||||||
${TATSU_LIBRARY}
|
${TATSU_LIBRARY}
|
||||||
${IRECOVERY_LIBRARY}
|
${IRECOVERY_LIBRARY}
|
||||||
${SSL_LIBRARY}
|
${SSL_LIBRARY}
|
||||||
${CRYPTO_LIBRARY}
|
${CRYPTO_LIBRARY}
|
||||||
${SSH_LIBRARY}
|
PkgConfig::SSH
|
||||||
# ${FRIDA_LIBRARY}
|
|
||||||
# ${ZIP_LIBRARY}
|
|
||||||
PkgConfig::PUGIXML
|
PkgConfig::PUGIXML
|
||||||
PkgConfig::USB
|
PkgConfig::USB
|
||||||
PkgConfig::PLIST
|
PkgConfig::PLIST
|
||||||
@@ -246,16 +206,24 @@ target_link_libraries(iDescriptor PRIVATE
|
|||||||
airplay
|
airplay
|
||||||
ipatool-go
|
ipatool-go
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT APPLE)
|
if(APPLE)
|
||||||
|
find_library(CORE_SERVICES_FRAMEWORK CoreServices REQUIRED)
|
||||||
target_link_libraries(iDescriptor PRIVATE
|
target_link_libraries(iDescriptor PRIVATE
|
||||||
PkgConfig::AVAHI_CLIENT
|
${CORE_SERVICES_FRAMEWORK})
|
||||||
# PkgConfig::AVAHI_COMMON
|
message(STATUS "Using macOS Bonjour framework for network service discovery")
|
||||||
)
|
endif()
|
||||||
|
elseif (WIN32)
|
||||||
|
find_path(DNSSD_INCLUDE_DIR dns_sd.h HINTS ${BONJOUR_SDK}/Include )
|
||||||
|
target_include_directories( iDescriptor PRIVATE ${DNSSD_INCLUDE_DIR} )
|
||||||
|
message( STATUS "Using Bonjour SDK for network service discovery" )
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
target_link_libraries(iDescriptor PRIVATE
|
target_link_libraries(iDescriptor PRIVATE
|
||||||
${CORE_SERVICES_FRAMEWORK}
|
PkgConfig::AVAHI_CLIENT
|
||||||
|
# PkgConfig::AVAHI_COMMON
|
||||||
)
|
)
|
||||||
|
message(STATUS "Using Avahi for network service discovery")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
@@ -285,26 +253,94 @@ set_target_properties(iDescriptor PROPERTIES
|
|||||||
)
|
)
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
install(TARGETS iDescriptor
|
|
||||||
BUNDLE DESTINATION .
|
# Set the installation directory to be within the build folder
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/dist")
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
if(QT_VERSION_MAJOR EQUAL 6)
|
if(QT_VERSION_MAJOR EQUAL 6)
|
||||||
qt_finalize_executable(iDescriptor)
|
qt_finalize_executable(iDescriptor)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Copy runtime DLLs to build directory after building
|
# Copy runtime DLLs to build directory after building
|
||||||
if(WIN32)
|
if(WIN32 AND NOT DEFINED NO_DEPLOY)
|
||||||
add_custom_command(TARGET iDescriptor POST_BUILD
|
add_custom_command(TARGET iDescriptor POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E echo "Starting Windows deployment..."
|
COMMAND ${CMAKE_COMMAND} -E echo "Starting Windows deployment..."
|
||||||
COMMAND ${CMAKE_COMMAND}
|
COMMAND ${CMAKE_COMMAND}
|
||||||
-DEXECUTABLE_PATH=${CMAKE_CURRENT_BINARY_DIR}/iDescriptor.exe
|
-DEXECUTABLE_PATH=$<TARGET_FILE:iDescriptor>
|
||||||
-DMSYS2_BIN_PATH=${CUSTOM_BIN_PATH}
|
-DQT_BIN_PATH=${QT_BIN_PATH}
|
||||||
-DOUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR}
|
-DMSYS2_BIN_PATH=C:/msys64/mingw64/bin
|
||||||
|
-DOUTPUT_DIR=$<TARGET_FILE_DIR:iDescriptor>
|
||||||
|
-DQML_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/qml
|
||||||
-P ${CMAKE_CURRENT_LIST_DIR}/cmake/win-deploy.cmake
|
-P ${CMAKE_CURRENT_LIST_DIR}/cmake/win-deploy.cmake
|
||||||
COMMENT "Deploying Windows application with all dependencies"
|
COMMENT "Deploying Windows application with all dependencies"
|
||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
install(CODE "
|
||||||
|
message(STATUS \"Deploying dependencies to installation directory for packaging...\")
|
||||||
|
message(STATUS \"CMAKE_INSTALL_PREFIX: \${CMAKE_INSTALL_PREFIX}\")
|
||||||
|
# copy executable to dist dir
|
||||||
|
file(MAKE_DIRECTORY \"\${CMAKE_INSTALL_PREFIX}\")
|
||||||
|
# file(COPY <TARGET_FILE:iDescriptor> DESTINATION CMAKE_INSTALL_PREFIX)
|
||||||
|
file(COPY ${CMAKE_CURRENT_BINARY_DIR}/iDescriptor.exe DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||||
|
# Check if file exists before deployment
|
||||||
|
set(EXECUTABLE_PATH \"\${CMAKE_INSTALL_PREFIX}/iDescriptor.exe\")
|
||||||
|
message(STATUS \"Looking for executable at: \${EXECUTABLE_PATH}\")
|
||||||
|
|
||||||
|
if(EXISTS \"\${EXECUTABLE_PATH}\")
|
||||||
|
message(STATUS \"SUCCESS: Executable found at \${EXECUTABLE_PATH}\")
|
||||||
|
else()
|
||||||
|
message(STATUS \"ERROR: Executable NOT found at \${EXECUTABLE_PATH}\")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND \"${CMAKE_COMMAND}\"
|
||||||
|
-DEXECUTABLE_PATH=\${EXECUTABLE_PATH}
|
||||||
|
-DQT_BIN_PATH=\"${QT_BIN_PATH}\"
|
||||||
|
-DMSYS2_BIN_PATH=\"C:/msys64/mingw64/bin\"
|
||||||
|
-DOUTPUT_DIR=\"\${CMAKE_INSTALL_PREFIX}\"
|
||||||
|
-DQML_SOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/qml\"
|
||||||
|
-P \"${CMAKE_CURRENT_LIST_DIR}/cmake/win-deploy.cmake\"
|
||||||
|
)
|
||||||
|
")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Packaging Configuration (CPack)
|
||||||
|
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
|
||||||
|
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
|
||||||
|
set(CPACK_WIX_VERSION 4)
|
||||||
|
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "iDescriptor Application")
|
||||||
|
set(CPACK_PACKAGE_VENDOR "iDescriptor")
|
||||||
|
set(CPACK_PACKAGE_CONTACT "support@idescriptor.com")
|
||||||
|
|
||||||
|
set(CPACK_OUTPUT_FILE_PREFIX "${CMAKE_BINARY_DIR}/artifacts")
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set(CPACK_GENERATOR "WIX;ZIP")
|
||||||
|
|
||||||
|
string(UUID CPACK_WIX_PRODUCT_GUID NAMESPACE "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d" NAME "${PROJECT_NAME}" TYPE MD5)
|
||||||
|
string(UUID CPACK_WIX_UPGRADE_GUID NAMESPACE "d6c5b4a3-f2e1-d0c9-b8a7-f6e5d4c3b2a1" NAME "${PROJECT_NAME}" TYPE MD5)
|
||||||
|
set(CPACK_WIX_UI_REF "WixUI_InstallDir")
|
||||||
|
set(CPACK_WIX_INSTALL_SCOPE "perMachine")
|
||||||
|
set(CPACK_WIX_PROGRAM_MENU_FOLDER "${PROJECT_NAME}")
|
||||||
|
set(CPACK_PACKAGE_EXECUTABLES "iDescriptor" "iDescriptor")
|
||||||
|
set(CPACK_WIX_CREATE_DESKTOP_SHORTCUT "iDescriptor")
|
||||||
|
set(CPACK_WIX_PRODUCT_ICON "${CMAKE_CURRENT_SOURCE_DIR}/resources/icons/app-icon/icon.ico")
|
||||||
|
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.rtf")
|
||||||
|
set(CPACK_WIX_LICENSE_RTF "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.rtf")
|
||||||
|
endif()
|
||||||
|
set(CPACK_WIX_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-win64-setup")
|
||||||
|
|
||||||
|
set(CPACK_ARCHIVE_COMPONENT_INSTALL TRUE)
|
||||||
|
set(CPACK_ARCHIVE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-win64-portable")
|
||||||
|
|
||||||
|
# Tell CPack to use the pre-built dist directory
|
||||||
|
set(CPACK_INSTALLED_DIRECTORIES "${CMAKE_INSTALL_PREFIX};.")
|
||||||
|
|
||||||
|
# Prevent CPack from running install again
|
||||||
|
set(CPACK_INSTALL_CMAKE_PROJECTS "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(CPack)
|
||||||
|
|||||||
+135
-14
@@ -1,16 +1,57 @@
|
|||||||
# Windows deployment script for Qt applications with MinGW/MSYS2
|
# Windows deployment script for Qt applications with MinGW/MSYS2
|
||||||
# This script handles Qt deployment, runtime DLL copying, and GStreamer plugins
|
# This script handles Qt deployment, runtime DLL copying, and GStreamer plugins
|
||||||
|
|
||||||
if(NOT EXISTS ${EXECUTABLE_PATH})
|
# Strip quotes from all path variables if they exist
|
||||||
message(FATAL_ERROR "Executable not found: ${EXECUTABLE_PATH}")
|
string(REPLACE "\"" "" EXECUTABLE_PATH "${EXECUTABLE_PATH}")
|
||||||
|
string(REPLACE "\"" "" OUTPUT_DIR "${OUTPUT_DIR}")
|
||||||
|
string(REPLACE "\"" "" QT_BIN_PATH "${QT_BIN_PATH}")
|
||||||
|
string(REPLACE "\"" "" MSYS2_BIN_PATH "${MSYS2_BIN_PATH}")
|
||||||
|
if(QML_SOURCE_DIR)
|
||||||
|
string(REPLACE "\"" "" QML_SOURCE_DIR "${QML_SOURCE_DIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
message("=== Starting Windows deployment for: ${EXECUTABLE_PATH} ===")
|
message("=== Starting Windows deployment for: ${EXECUTABLE_PATH} ===")
|
||||||
|
message("Debug info:")
|
||||||
|
message(" EXECUTABLE_PATH: ${EXECUTABLE_PATH}")
|
||||||
|
message(" OUTPUT_DIR: ${OUTPUT_DIR}")
|
||||||
|
message(" QT_BIN_PATH: ${QT_BIN_PATH}")
|
||||||
|
message(" MSYS2_BIN_PATH: ${MSYS2_BIN_PATH}")
|
||||||
|
|
||||||
# Step 1: Run windeployqt6 to copy Qt-related DLLs and plugins
|
if(NOT EXISTS ${EXECUTABLE_PATH})
|
||||||
message("Running windeployqt6 to deploy Qt dependencies...")
|
message(STATUS "Executable not found: ${EXECUTABLE_PATH}")
|
||||||
|
message(STATUS "Checking if it's a path issue...")
|
||||||
|
|
||||||
|
# Try to find the executable with different path formats
|
||||||
|
get_filename_component(DIR_PATH ${EXECUTABLE_PATH} DIRECTORY)
|
||||||
|
get_filename_component(FILE_NAME ${EXECUTABLE_PATH} NAME)
|
||||||
|
|
||||||
|
message(STATUS "Directory path: ${DIR_PATH}")
|
||||||
|
message(STATUS "File name: ${FILE_NAME}")
|
||||||
|
|
||||||
|
# List contents of the directory
|
||||||
|
if(EXISTS ${DIR_PATH})
|
||||||
|
message(STATUS "Directory exists, listing contents:")
|
||||||
|
file(GLOB DIR_CONTENTS "${DIR_PATH}/*")
|
||||||
|
foreach(ITEM ${DIR_CONTENTS})
|
||||||
|
message(STATUS " Found: ${ITEM}")
|
||||||
|
endforeach()
|
||||||
|
else()
|
||||||
|
message(STATUS "Directory does not exist: ${DIR_PATH}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(FATAL_ERROR "Executable not found: ${EXECUTABLE_PATH}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message("SUCCESS: Executable found at: ${EXECUTABLE_PATH}")
|
||||||
|
|
||||||
|
message("Running windeployqt6 to deploy Qt dependencies (without compiler runtime)...")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
message("Executing: ${QT_BIN_PATH}/windeployqt6.exe --dir ${OUTPUT_DIR} --plugindir ${OUTPUT_DIR}/plugins ${EXECUTABLE_PATH}")
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND ${MSYS2_BIN_PATH}/windeployqt6.exe --dir ${OUTPUT_DIR} --plugindir ${OUTPUT_DIR}/plugins ${EXECUTABLE_PATH}
|
COMMAND ${QT_BIN_PATH}/windeployqt6.exe --dir ${OUTPUT_DIR} --plugindir ${OUTPUT_DIR}/plugins ${EXECUTABLE_PATH}
|
||||||
RESULT_VARIABLE WINDEPLOYQT_RESULT
|
RESULT_VARIABLE WINDEPLOYQT_RESULT
|
||||||
OUTPUT_VARIABLE WINDEPLOYQT_OUTPUT
|
OUTPUT_VARIABLE WINDEPLOYQT_OUTPUT
|
||||||
ERROR_VARIABLE WINDEPLOYQT_ERROR
|
ERROR_VARIABLE WINDEPLOYQT_ERROR
|
||||||
@@ -25,22 +66,56 @@ endif()
|
|||||||
# Step 2: Find and copy runtime dependencies using GET_RUNTIME_DEPENDENCIES
|
# Step 2: Find and copy runtime dependencies using GET_RUNTIME_DEPENDENCIES
|
||||||
message("Analyzing runtime dependencies for: ${EXECUTABLE_PATH}")
|
message("Analyzing runtime dependencies for: ${EXECUTABLE_PATH}")
|
||||||
|
|
||||||
|
# Get the build directory to exclude DLLs already there
|
||||||
|
get_filename_component(BUILD_DIR ${EXECUTABLE_PATH} DIRECTORY)
|
||||||
|
|
||||||
file(GET_RUNTIME_DEPENDENCIES
|
file(GET_RUNTIME_DEPENDENCIES
|
||||||
EXECUTABLES ${EXECUTABLE_PATH}
|
EXECUTABLES ${EXECUTABLE_PATH}
|
||||||
RESOLVED_DEPENDENCIES_VAR DLLS
|
RESOLVED_DEPENDENCIES_VAR DLLS
|
||||||
PRE_EXCLUDE_REGEXES "^api-ms-" "^ext-ms-" "^AVRT" "^avrt" "^MSVCP" "^VCRUNTIME" "^ucrtbase"
|
PRE_EXCLUDE_REGEXES "^api-ms-" "^ext-ms-" "^AVRT" "^avrt" "^MSVCP" "^VCRUNTIME" "^ucrtbase" "^libgcc_s_seh-1\\.dll$" "^libstdc\\+\\+-6\\.dll$" "^libwinpthread-1\\.dll$" "^Qt.*\\.dll$" "^libgstreamer-1\\.0-0\\.dll$" "^libgstbase-1\\.0-0\\.dll$" "^libgobject-2\\.0-0\\.dll$" "^libglib-2\\.0-0\\.dll$" "^libintl-8\\.dll$" "^libiconv-2\\.dll$"
|
||||||
POST_EXCLUDE_REGEXES ".*system32/.*\\.dll" ".*SysWOW64/.*\\.dll" ".*Windows/.*\\.dll" ".*Microsoft.VC.*"
|
#PRE_EXCLUDE_REGEXES "^api-ms-" "^ext-ms-" "^AVRT" "^avrt" "^MSVCP" "^VCRUNTIME" "^ucrtbase"
|
||||||
DIRECTORIES ${MSYS2_BIN_PATH} $ENV{PATH}
|
POST_EXCLUDE_REGEXES ".*system32/.*\\.dll" ".*SysWOW64/.*\\.dll" ".*Windows/.*\\.dll" ".*Microsoft.VC.*" ".*Qt.*\\.dll$"
|
||||||
|
DIRECTORIES ${QT_BIN_PATH} ${MSYS2_BIN_PATH} "C:/lxqt/lib" $ENV{PATH}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(COPIED_DLLS 0)
|
||||||
foreach(DLL ${DLLS})
|
foreach(DLL ${DLLS})
|
||||||
get_filename_component(DLL_NAME ${DLL} NAME)
|
get_filename_component(DLL_NAME ${DLL} NAME)
|
||||||
message("Copying dependency: ${DLL_NAME}")
|
get_filename_component(DLL_DIR ${DLL} DIRECTORY)
|
||||||
file(COPY ${DLL} DESTINATION ${OUTPUT_DIR})
|
|
||||||
|
# Skip if DLL is from the build directory (avoid copying to itself)
|
||||||
|
if("${DLL_DIR}" STREQUAL "${BUILD_DIR}")
|
||||||
|
message(" Skipping ${DLL_NAME} (already in build directory)")
|
||||||
|
continue()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(DEST_FILE "${OUTPUT_DIR}/${DLL_NAME}")
|
||||||
|
|
||||||
|
# Check if we need to copy (file doesn't exist or is different)
|
||||||
|
set(SHOULD_COPY TRUE)
|
||||||
|
if(EXISTS ${DEST_FILE})
|
||||||
|
file(SIZE ${DLL} SOURCE_SIZE)
|
||||||
|
file(SIZE ${DEST_FILE} DEST_SIZE)
|
||||||
|
if(SOURCE_SIZE EQUAL DEST_SIZE)
|
||||||
|
file(TIMESTAMP ${DLL} SOURCE_TIME)
|
||||||
|
file(TIMESTAMP ${DEST_FILE} DEST_TIME)
|
||||||
|
if(NOT SOURCE_TIME IS_NEWER_THAN DEST_TIME)
|
||||||
|
set(SHOULD_COPY FALSE)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SHOULD_COPY)
|
||||||
|
message(" Copying dependency: ${DLL_NAME}")
|
||||||
|
file(COPY ${DLL} DESTINATION ${OUTPUT_DIR})
|
||||||
|
math(EXPR COPIED_DLLS "${COPIED_DLLS} + 1")
|
||||||
|
else()
|
||||||
|
message(" Skipping ${DLL_NAME} (already up to date)")
|
||||||
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
list(LENGTH DLLS DLL_COUNT)
|
list(LENGTH DLLS TOTAL_DLLS)
|
||||||
message("Successfully copied ${DLL_COUNT} runtime DLL dependencies")
|
message("Processed ${TOTAL_DLLS} runtime dependencies, copied ${COPIED_DLLS} files")
|
||||||
|
|
||||||
# Step 3: Copy GStreamer plugins
|
# Step 3: Copy GStreamer plugins
|
||||||
message("Copying GStreamer plugins...")
|
message("Copying GStreamer plugins...")
|
||||||
@@ -62,7 +137,8 @@ else()
|
|||||||
message(WARNING "No GStreamer plugins found in ${MSYS2_BIN_PATH}/../lib/gstreamer-1.0/")
|
message(WARNING "No GStreamer plugins found in ${MSYS2_BIN_PATH}/../lib/gstreamer-1.0/")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Step 4: Copy additional MinGW runtime DLLs that might be missed
|
# Step 4: Manually copy the correct MSYS2 MinGW runtime DLLs.
|
||||||
|
# This ensures the versions required by GStreamer/FFmpeg are used.
|
||||||
set(ADDITIONAL_DLLS
|
set(ADDITIONAL_DLLS
|
||||||
"libgcc_s_seh-1.dll"
|
"libgcc_s_seh-1.dll"
|
||||||
"libstdc++-6.dll"
|
"libstdc++-6.dll"
|
||||||
@@ -73,9 +149,45 @@ set(ADDITIONAL_DLLS
|
|||||||
"libglib-2.0-0.dll"
|
"libglib-2.0-0.dll"
|
||||||
"libintl-8.dll"
|
"libintl-8.dll"
|
||||||
"libiconv-2.dll"
|
"libiconv-2.dll"
|
||||||
|
"libfdk-aac-2.dll"
|
||||||
|
"libfaad-2.dll"
|
||||||
|
"avfilter-10.dll"
|
||||||
|
"libopenal-1.dll"
|
||||||
|
"libgstaudio-1.0-0.dll"
|
||||||
|
"libgstvideo-1.0-0.dll"
|
||||||
|
"liborc-0.4-0.dll"
|
||||||
|
"libgstpbutils-1.0-0.dll"
|
||||||
|
"libgsttag-1.0-0.dll"
|
||||||
|
"libgstlibav.dll"
|
||||||
|
"libass-9.dll"
|
||||||
|
"libfontconfig-1.dll"
|
||||||
|
"libharfbuzz-0.dll"
|
||||||
|
"libexpat-1.dll"
|
||||||
|
"libfreetype-6.dll"
|
||||||
|
"libpng16-16.dll"
|
||||||
|
"libgraphite2.dll"
|
||||||
|
"libfribidi-0.dll"
|
||||||
|
"libunibreak-6.dll"
|
||||||
|
"liblcms2-2.dll"
|
||||||
|
"libvpl-2.dll"
|
||||||
|
"libzimg-2.dll"
|
||||||
|
"libdovi.dll"
|
||||||
|
"libshaderc_shared.dll"
|
||||||
|
"vulkan-1.dll"
|
||||||
|
"libvidstab.dll"
|
||||||
|
"libgomp-1.dll"
|
||||||
|
"postproc-58.dll"
|
||||||
|
"libplacebo-351.dll"
|
||||||
|
"libspirv-cross-c-shared.dll"
|
||||||
|
"libva.dll"
|
||||||
|
"libxml2-16.dll"
|
||||||
|
"libva_win32.dll"
|
||||||
|
"libpcre2-8-0.dll"
|
||||||
|
"libffi-8.dll"
|
||||||
|
"libgmodule-2.0-0.dll"
|
||||||
)
|
)
|
||||||
|
|
||||||
message("Copying additional MinGW runtime DLLs...")
|
message("Copying additional MinGW runtime DLLs from MSYS2...")
|
||||||
foreach(DLL_NAME ${ADDITIONAL_DLLS})
|
foreach(DLL_NAME ${ADDITIONAL_DLLS})
|
||||||
set(DLL_PATH "${MSYS2_BIN_PATH}/${DLL_NAME}")
|
set(DLL_PATH "${MSYS2_BIN_PATH}/${DLL_NAME}")
|
||||||
if(EXISTS ${DLL_PATH})
|
if(EXISTS ${DLL_PATH})
|
||||||
@@ -84,4 +196,13 @@ foreach(DLL_NAME ${ADDITIONAL_DLLS})
|
|||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
message("Copying GStreamer helper executables...")
|
||||||
|
set(GST_LIBEXEC_PATH "${MSYS2_BIN_PATH}/../libexec/gstreamer-1.0")
|
||||||
|
file(COPY "${GST_LIBEXEC_PATH}/gst-plugin-scanner.exe" DESTINATION "${OUTPUT_DIR}/libexec/gstreamer-1.0")
|
||||||
|
file(COPY "${GST_LIBEXEC_PATH}/gst-ptp-helper.exe" DESTINATION "${OUTPUT_DIR}/libexec/gstreamer-1.0")
|
||||||
|
|
||||||
|
message("Copying executables")
|
||||||
|
file(COPY C:/msys64/mingw64/bin/iproxy.exe DESTINATION ${OUTPUT_DIR})
|
||||||
|
|
||||||
|
|
||||||
message("=== Windows deployment completed ===")
|
message("=== Windows deployment completed ===")
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#include "winver.h"
|
||||||
|
|
||||||
|
IDI_ICON1 ICON "resources/icons/app-icon/icon.ico"
|
||||||
|
|
||||||
|
VS_VERSION_INFO VERSIONINFO
|
||||||
|
FILEVERSION 1,0,0,0
|
||||||
|
PRODUCTVERSION 1,0,0,0
|
||||||
|
FILEFLAGS 0x0L
|
||||||
|
FILEFLAGSMASK 0x3fL
|
||||||
|
FILEOS 0x00040004L
|
||||||
|
FILETYPE 0x1L
|
||||||
|
FILESUBTYPE 0x0L
|
||||||
|
BEGIN
|
||||||
|
BLOCK "StringFileInfo"
|
||||||
|
BEGIN
|
||||||
|
BLOCK "000004b0"
|
||||||
|
BEGIN
|
||||||
|
VALUE "CompanyName", "iDescriptor"
|
||||||
|
VALUE "FileDescription", "iDescriptor - iDevice Management Tool"
|
||||||
|
VALUE "FileVersion", "1.0.0.0"
|
||||||
|
VALUE "LegalCopyright", "Copyright (C) 2025 iDescriptor"
|
||||||
|
VALUE "InternalName", "idescriptor"
|
||||||
|
VALUE "OriginalFilename", "idescriptor.exe"
|
||||||
|
VALUE "ProductName", "iDescriptor"
|
||||||
|
VALUE "ProductVersion", "1.0.0.0"
|
||||||
|
END
|
||||||
|
END
|
||||||
|
BLOCK "VarFileInfo"
|
||||||
|
BEGIN
|
||||||
|
VALUE "Translation", 0x0, 1200
|
||||||
|
END
|
||||||
|
END
|
||||||
+1
-1
@@ -17,7 +17,7 @@
|
|||||||
<file>resources/icons/MdiLightMagnify.png</file>
|
<file>resources/icons/MdiLightMagnify.png</file>
|
||||||
<file>resources/icons/IcBaselineInsertDriveFile.png</file>
|
<file>resources/icons/IcBaselineInsertDriveFile.png</file>
|
||||||
<file>resources/icons/MaterialSymbolsLightHome.png</file>
|
<file>resources/icons/MaterialSymbolsLightHome.png</file>
|
||||||
<!-- <file>resources/icons/icon.png</file> -->
|
<file>resources/icons/app-icon/icon.ico</file>
|
||||||
<file>qml/MapView.qml</file>
|
<file>qml/MapView.qml</file>
|
||||||
<!-- TODO: -->
|
<!-- TODO: -->
|
||||||
<!-- <file>resources/dump.js</file> -->
|
<!-- <file>resources/dump.js</file> -->
|
||||||
|
|||||||
+5
-4
@@ -349,14 +349,15 @@ void AppsWidget::createAppCard(const QString &name, const QString &bundleId,
|
|||||||
// Install button placeholder
|
// Install button placeholder
|
||||||
ZLabel *installLabel = new ZLabel("Install");
|
ZLabel *installLabel = new ZLabel("Install");
|
||||||
installLabel->setAlignment(Qt::AlignCenter);
|
installLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
ZLabel *downloadIpaLabel = new ZLabel("Download IPA");
|
|
||||||
downloadIpaLabel->setAlignment(Qt::AlignCenter);
|
|
||||||
|
|
||||||
installLabel->setStyleSheet("font-size: 12px; color: #007AFF; font-weight: "
|
installLabel->setStyleSheet("font-size: 12px; color: #007AFF; font-weight: "
|
||||||
"bold; background-color: transparent;");
|
"bold; background-color: transparent;");
|
||||||
installLabel->setCursor(Qt::PointingHandCursor);
|
installLabel->setCursor(Qt::PointingHandCursor);
|
||||||
installLabel->setFixedHeight(30);
|
installLabel->setFixedHeight(30);
|
||||||
|
|
||||||
|
ZLabel *downloadIpaLabel = new ZLabel("Download IPA");
|
||||||
|
downloadIpaLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
downloadIpaLabel->setCursor(Qt::PointingHandCursor);
|
||||||
|
|
||||||
connect(installLabel, &ZLabel::clicked, this,
|
connect(installLabel, &ZLabel::clicked, this,
|
||||||
[this, name, bundleId, description]() {
|
[this, name, bundleId, description]() {
|
||||||
onAppCardClicked(name, bundleId, description);
|
onAppCardClicked(name, bundleId, description);
|
||||||
|
|||||||
@@ -11,7 +11,11 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include "dns_sd.h"
|
||||||
|
#else
|
||||||
#include <dns_sd.h>
|
#include <dns_sd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
class DnssdService : public QObject
|
class DnssdService : public QObject
|
||||||
{
|
{
|
||||||
@@ -69,4 +73,4 @@ private:
|
|||||||
QMap<QString, PendingDevice> m_pendingDevices;
|
QMap<QString, PendingDevice> m_pendingDevices;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DNSSD_SERVICE_H
|
#endif // DNSSD_SERVICE_H
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#endif
|
#endif
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define _GNU_SOURCE 1
|
||||||
|
#define __USE_GNU 1
|
||||||
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <getopt.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@@ -14,11 +18,16 @@
|
|||||||
#ifdef HAVE_UNISTD_H
|
#ifdef HAVE_UNISTD_H
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef WIN32
|
||||||
|
#include <signal.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <libimobiledevice/afc.h>
|
#include <libimobiledevice/afc.h>
|
||||||
#include <libimobiledevice/installation_proxy.h>
|
#include <libimobiledevice/installation_proxy.h>
|
||||||
#include <libimobiledevice/libimobiledevice.h>
|
#include <libimobiledevice/libimobiledevice.h>
|
||||||
#include <libimobiledevice/lockdown.h>
|
#include <libimobiledevice/lockdown.h>
|
||||||
|
#include <libimobiledevice/notification_proxy.h>
|
||||||
|
|
||||||
|
|
||||||
#include <plist/plist.h>
|
#include <plist/plist.h>
|
||||||
|
|
||||||
|
|||||||
+40
-2
@@ -1,17 +1,55 @@
|
|||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
#include <QStyleFactory>
|
#include <QStyleFactory>
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <stdlib.h> // For getenv
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include "platform/windows/check_deps.h"
|
||||||
|
#endif
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
|
#ifdef WIN32
|
||||||
|
// This setup MUST be done before the QApplication object is created.
|
||||||
|
QString appPath = QCoreApplication::applicationDirPath();
|
||||||
|
QString gstPluginPath =
|
||||||
|
QDir::toNativeSeparators(appPath + "/gstreamer-1.0");
|
||||||
|
QString gstPluginScannerPath =
|
||||||
|
QDir::toNativeSeparators(appPath + "/gstreamer-1.0/gst-plugin-scanner");
|
||||||
|
|
||||||
|
// Add the application's directory to the PATH so GStreamer plugins can find
|
||||||
|
// their dependencies (e.g., FFmpeg DLLs).
|
||||||
|
const char *oldPath = getenv("PATH");
|
||||||
|
QString newPath = appPath + ";" + QString(oldPath);
|
||||||
|
qputenv("PATH", newPath.toUtf8());
|
||||||
|
|
||||||
|
qputenv("GST_PLUGIN_PATH", gstPluginPath.toUtf8());
|
||||||
|
qDebug() << "GST_PLUGIN_PATH=" << gstPluginPath;
|
||||||
|
printf("GST_PLUGIN_PATH=%s\n", gstPluginPath.toUtf8().data());
|
||||||
|
qputenv("GST_REGISTRY_REUSE_PLUGIN_SCANNER", "yes");
|
||||||
|
qDebug() << "GST_REGISTRY_REUSE_PLUGIN_SCANNER=yes";
|
||||||
|
printf("GST_REGISTRY_REUSE_PLUGIN_SCANNER=yes\n");
|
||||||
|
qputenv("GST_PLUGIN_SYSTEM_PATH", gstPluginPath.toUtf8());
|
||||||
|
qDebug() << "GST_PLUGIN_SYSTEM_PATH=" << gstPluginPath;
|
||||||
|
printf("GST_PLUGIN_SYSTEM_PATH=%s\n", gstPluginPath.toUtf8().data());
|
||||||
|
qputenv("GST_DEBUG", "GST_PLUGIN_LOADING:5");
|
||||||
|
qDebug() << "GST_DEBUG=GST_PLUGIN_LOADING:5";
|
||||||
|
printf("GST_DEBUG=GST_PLUGIN_LOADING:5\n");
|
||||||
|
qputenv("GST_PLUGIN_SCANNER_1_0", gstPluginScannerPath.toUtf8());
|
||||||
|
qDebug() << "GST_PLUGIN_SCANNER_1_0=" << gstPluginScannerPath;
|
||||||
|
printf("GST_PLUGIN_SCANNER_1_0=%s\n", gstPluginScannerPath.toUtf8().data());
|
||||||
|
#endif
|
||||||
QCoreApplication::setOrganizationName("iDescriptor");
|
QCoreApplication::setOrganizationName("iDescriptor");
|
||||||
// QCoreApplication::setOrganizationDomain("iDescriptor.com");
|
// QCoreApplication::setOrganizationDomain("iDescriptor.com");
|
||||||
QCoreApplication::setApplicationName("iDescriptor");
|
QCoreApplication::setApplicationName("iDescriptor");
|
||||||
// QCoreApplication::setApplicationVersion(IDESCRIPTOR_VERSION);
|
// QCoreApplication::setApplicationVersion(IDESCRIPTOR_VERSION);
|
||||||
|
// QApplication::setStyle(QStyleFactory::create("Windows"));
|
||||||
QApplication::setStyle(QStyleFactory::create("macOS"));
|
#ifndef __APPLE__
|
||||||
|
QApplication::setStyle(QStyleFactory::create("Fusion"));
|
||||||
|
#endif
|
||||||
|
|
||||||
MainWindow *w = MainWindow::sharedInstance();
|
MainWindow *w = MainWindow::sharedInstance();
|
||||||
w->show();
|
w->show();
|
||||||
|
|||||||
@@ -29,6 +29,10 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <libusb-1.0/libusb.h>
|
#include <libusb-1.0/libusb.h>
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include "platform/windows/diagnose_widget.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
void handleCallback(const idevice_event_t *event, void *userData)
|
void handleCallback(const idevice_event_t *event, void *userData)
|
||||||
{
|
{
|
||||||
printf("Device event received: ");
|
printf("Device event received: ");
|
||||||
@@ -134,6 +138,25 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
|
|
||||||
// Welcome page (shown when no devices are connected)
|
// Welcome page (shown when no devices are connected)
|
||||||
WelcomeWidget *welcomePage = new WelcomeWidget(this);
|
WelcomeWidget *welcomePage = new WelcomeWidget(this);
|
||||||
|
// No devices page
|
||||||
|
QWidget *noDevicesPage = new QWidget();
|
||||||
|
QVBoxLayout *noDeviceLayout = new QVBoxLayout(noDevicesPage);
|
||||||
|
noDeviceLayout->addStretch();
|
||||||
|
QHBoxLayout *labelLayout = new QHBoxLayout();
|
||||||
|
labelLayout->addStretch();
|
||||||
|
QLabel *noDeviceLabel = new QLabel("No devices detected");
|
||||||
|
noDeviceLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
labelLayout->addWidget(noDeviceLabel);
|
||||||
|
labelLayout->addStretch();
|
||||||
|
noDeviceLayout->addLayout(labelLayout);
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
// Add diagnose widget to check dependencies
|
||||||
|
// DiagnoseWidget *diagnoseWidget = new DiagnoseWidget();
|
||||||
|
// noDeviceLayout->addWidget(diagnoseWidget);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
noDeviceLayout->addStretch();
|
||||||
|
|
||||||
m_deviceManager = new DeviceManagerWidget(this);
|
m_deviceManager = new DeviceManagerWidget(this);
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QStatusBar" name="statusbar"/>
|
<widget class="QStatusBar" name="statusbar"/>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
</layout>
|
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
bool CheckRegistry(HKEY hKeyRoot, LPCSTR subKey, LPCSTR displayNameToFind)
|
||||||
|
{
|
||||||
|
HKEY hKey;
|
||||||
|
if (RegOpenKeyExA(hKeyRoot, subKey, 0, KEY_READ, &hKey) != ERROR_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char keyName[256];
|
||||||
|
DWORD keyNameSize = sizeof(keyName);
|
||||||
|
DWORD index = 0;
|
||||||
|
|
||||||
|
while (RegEnumKeyExA(hKey, index++, keyName, &keyNameSize, NULL, NULL, NULL,
|
||||||
|
NULL) == ERROR_SUCCESS) {
|
||||||
|
HKEY appKey;
|
||||||
|
if (RegOpenKeyExA(hKey, keyName, 0, KEY_READ, &appKey) ==
|
||||||
|
ERROR_SUCCESS) {
|
||||||
|
char displayName[256];
|
||||||
|
DWORD displayNameSize = sizeof(displayName);
|
||||||
|
if (RegQueryValueExA(appKey, "DisplayName", NULL, NULL,
|
||||||
|
(LPBYTE)displayName,
|
||||||
|
&displayNameSize) == ERROR_SUCCESS) {
|
||||||
|
if (strcmp(displayName, displayNameToFind) == 0) {
|
||||||
|
RegCloseKey(appKey);
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RegCloseKey(appKey);
|
||||||
|
}
|
||||||
|
keyNameSize = sizeof(keyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsAppleMobileDeviceSupportInstalled()
|
||||||
|
{
|
||||||
|
if (CheckRegistry(HKEY_LOCAL_MACHINE,
|
||||||
|
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
|
||||||
|
"Apple Mobile Device Support")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (CheckRegistry(HKEY_LOCAL_MACHINE,
|
||||||
|
"SOFTWARE\\WOW6432Node\\Microsoft\\Wi"
|
||||||
|
"ndows\\CurrentVersion\\Uninstall",
|
||||||
|
"Apple Mobile Device Support")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsWinFspInstalled()
|
||||||
|
{
|
||||||
|
if (CheckRegistry(HKEY_LOCAL_MACHINE,
|
||||||
|
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
|
||||||
|
"WinFsp 2025")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (CheckRegistry(HKEY_LOCAL_MACHINE,
|
||||||
|
"SOFTWARE\\WOW6432Node\\Microsoft\\Wi"
|
||||||
|
"ndows\\CurrentVersion\\Uninstall",
|
||||||
|
"WinFsp 2025")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
#ifndef CHECK_DEPS_H
|
||||||
|
#define CHECK_DEPS_H
|
||||||
|
|
||||||
|
bool IsAppleMobileDeviceSupportInstalled();
|
||||||
|
bool IsWinFspInstalled();
|
||||||
|
|
||||||
|
#endif // CHECK_DEPS_H
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#include "diagnose_dialog.h"
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
DiagnoseDialog::DiagnoseDialog(QWidget *parent) : QDialog(parent)
|
||||||
|
{
|
||||||
|
setupUI();
|
||||||
|
|
||||||
|
setWindowTitle("System Dependencies");
|
||||||
|
setModal(true);
|
||||||
|
resize(500, 400);
|
||||||
|
|
||||||
|
// Set clean close behavior
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiagnoseDialog::setupUI()
|
||||||
|
{
|
||||||
|
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
||||||
|
mainLayout->setContentsMargins(10, 10, 10, 10);
|
||||||
|
|
||||||
|
// Add the main diagnose widget
|
||||||
|
m_diagnoseWidget = new DiagnoseWidget();
|
||||||
|
|
||||||
|
// Close button
|
||||||
|
QHBoxLayout *buttonLayout = new QHBoxLayout();
|
||||||
|
buttonLayout->addStretch();
|
||||||
|
|
||||||
|
m_closeButton = new QPushButton("Close");
|
||||||
|
m_closeButton->setMinimumWidth(80);
|
||||||
|
connect(m_closeButton, &QPushButton::clicked, this,
|
||||||
|
&DiagnoseDialog::onCloseClicked);
|
||||||
|
|
||||||
|
buttonLayout->addWidget(m_closeButton);
|
||||||
|
|
||||||
|
// Layout assembly
|
||||||
|
mainLayout->addWidget(m_diagnoseWidget, 1);
|
||||||
|
mainLayout->addLayout(buttonLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiagnoseDialog::onCloseClicked() { accept(); }
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#ifndef DIAGNOSE_DIALOG_H
|
||||||
|
#define DIAGNOSE_DIALOG_H
|
||||||
|
|
||||||
|
#include "diagnose_widget.h"
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
|
||||||
|
class DiagnoseDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DiagnoseDialog(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onCloseClicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupUI();
|
||||||
|
|
||||||
|
DiagnoseWidget *m_diagnoseWidget;
|
||||||
|
QPushButton *m_closeButton;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DIAGNOSE_DIALOG_H
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
#include "diagnose_widget.h"
|
||||||
|
#include "check_deps.h"
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
|
||||||
|
DependencyItem::DependencyItem(const QString &name, const QString &description,
|
||||||
|
QWidget *parent)
|
||||||
|
: QWidget(parent), m_name(name)
|
||||||
|
{
|
||||||
|
setFixedHeight(80);
|
||||||
|
|
||||||
|
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||||
|
layout->setContentsMargins(10, 5, 10, 5);
|
||||||
|
|
||||||
|
// Left side - info
|
||||||
|
QVBoxLayout *infoLayout = new QVBoxLayout();
|
||||||
|
|
||||||
|
m_nameLabel = new QLabel(name);
|
||||||
|
QFont nameFont = m_nameLabel->font();
|
||||||
|
nameFont.setBold(true);
|
||||||
|
nameFont.setPointSize(nameFont.pointSize() + 1);
|
||||||
|
m_nameLabel->setFont(nameFont);
|
||||||
|
|
||||||
|
m_descriptionLabel = new QLabel(description);
|
||||||
|
m_descriptionLabel->setWordWrap(true);
|
||||||
|
|
||||||
|
infoLayout->addWidget(m_nameLabel);
|
||||||
|
infoLayout->addWidget(m_descriptionLabel);
|
||||||
|
|
||||||
|
// Middle - status
|
||||||
|
m_statusLabel = new QLabel("Checking...");
|
||||||
|
m_statusLabel->setMinimumWidth(100);
|
||||||
|
m_statusLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
|
// Right side - actions
|
||||||
|
QVBoxLayout *actionLayout = new QVBoxLayout();
|
||||||
|
|
||||||
|
m_installButton = new QPushButton("Install");
|
||||||
|
m_installButton->setMinimumWidth(80);
|
||||||
|
m_installButton->setVisible(false);
|
||||||
|
connect(m_installButton, &QPushButton::clicked, this,
|
||||||
|
&DependencyItem::onInstallClicked);
|
||||||
|
|
||||||
|
m_progressBar = new QProgressBar();
|
||||||
|
m_progressBar->setRange(0, 0); // Indeterminate
|
||||||
|
m_progressBar->setVisible(false);
|
||||||
|
m_progressBar->setMaximumHeight(20);
|
||||||
|
|
||||||
|
actionLayout->addWidget(m_installButton);
|
||||||
|
actionLayout->addWidget(m_progressBar);
|
||||||
|
actionLayout->addStretch();
|
||||||
|
|
||||||
|
layout->addLayout(infoLayout, 1);
|
||||||
|
layout->addWidget(m_statusLabel);
|
||||||
|
layout->addLayout(actionLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DependencyItem::setInstalled(bool installed)
|
||||||
|
{
|
||||||
|
setChecking(false);
|
||||||
|
|
||||||
|
if (installed) {
|
||||||
|
m_statusLabel->setText("✓ Installed");
|
||||||
|
m_statusLabel->setStyleSheet("color: green; font-weight: bold;");
|
||||||
|
m_installButton->setVisible(false);
|
||||||
|
} else {
|
||||||
|
m_statusLabel->setText("✗ Not Installed");
|
||||||
|
m_statusLabel->setStyleSheet("color: red; font-weight: bold;");
|
||||||
|
m_installButton->setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DependencyItem::setChecking(bool checking)
|
||||||
|
{
|
||||||
|
if (checking) {
|
||||||
|
m_statusLabel->setText("Checking...");
|
||||||
|
m_statusLabel->setStyleSheet("color: gray;");
|
||||||
|
m_installButton->setVisible(false);
|
||||||
|
m_progressBar->setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DependencyItem::onInstallClicked() { emit installRequested(m_name); }
|
||||||
|
|
||||||
|
DiagnoseWidget::DiagnoseWidget(QWidget *parent) : QWidget(parent)
|
||||||
|
{
|
||||||
|
setupUI();
|
||||||
|
|
||||||
|
// Add dependency items
|
||||||
|
addDependencyItem("Apple Mobile Device Support",
|
||||||
|
"Required for iOS device communication");
|
||||||
|
addDependencyItem("WinFsp",
|
||||||
|
"Required for filesystem operations and mounting");
|
||||||
|
|
||||||
|
// Auto-check on startup
|
||||||
|
QTimer::singleShot(100, this, &DiagnoseWidget::checkDependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiagnoseWidget::setupUI()
|
||||||
|
{
|
||||||
|
m_mainLayout = new QVBoxLayout(this);
|
||||||
|
m_mainLayout->setSpacing(10);
|
||||||
|
|
||||||
|
// Title and summary
|
||||||
|
QLabel *titleLabel = new QLabel("Dependency Check");
|
||||||
|
QFont titleFont = titleLabel->font();
|
||||||
|
titleFont.setBold(true);
|
||||||
|
titleFont.setPointSize(titleFont.pointSize() + 2);
|
||||||
|
titleLabel->setFont(titleFont);
|
||||||
|
|
||||||
|
m_summaryLabel = new QLabel("Checking system dependencies...");
|
||||||
|
|
||||||
|
// Check button
|
||||||
|
m_checkButton = new QPushButton("Refresh Check");
|
||||||
|
m_checkButton->setMaximumWidth(150);
|
||||||
|
connect(m_checkButton, &QPushButton::clicked, this,
|
||||||
|
&DiagnoseWidget::checkDependencies);
|
||||||
|
|
||||||
|
// Scroll area for dependency items
|
||||||
|
m_scrollArea = new QScrollArea();
|
||||||
|
m_scrollArea->setWidgetResizable(true);
|
||||||
|
// m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarNever);
|
||||||
|
m_scrollArea->setMinimumHeight(200);
|
||||||
|
|
||||||
|
m_itemsWidget = new QWidget();
|
||||||
|
m_itemsLayout = new QVBoxLayout(m_itemsWidget);
|
||||||
|
m_itemsLayout->setSpacing(5);
|
||||||
|
m_itemsLayout->addStretch();
|
||||||
|
|
||||||
|
m_scrollArea->setWidget(m_itemsWidget);
|
||||||
|
|
||||||
|
// Layout assembly
|
||||||
|
QHBoxLayout *headerLayout = new QHBoxLayout();
|
||||||
|
headerLayout->addWidget(titleLabel);
|
||||||
|
headerLayout->addStretch();
|
||||||
|
headerLayout->addWidget(m_checkButton);
|
||||||
|
|
||||||
|
m_mainLayout->addLayout(headerLayout);
|
||||||
|
m_mainLayout->addWidget(m_summaryLabel);
|
||||||
|
m_mainLayout->addWidget(m_scrollArea, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiagnoseWidget::addDependencyItem(const QString &name,
|
||||||
|
const QString &description)
|
||||||
|
{
|
||||||
|
DependencyItem *item = new DependencyItem(name, description);
|
||||||
|
item->setProperty("name", name); // Set the name property for identification
|
||||||
|
connect(item, &DependencyItem::installRequested, this,
|
||||||
|
&DiagnoseWidget::onInstallRequested);
|
||||||
|
|
||||||
|
m_dependencyItems.append(item);
|
||||||
|
|
||||||
|
// Insert before the stretch
|
||||||
|
m_itemsLayout->insertWidget(m_itemsLayout->count() - 1, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiagnoseWidget::checkDependencies()
|
||||||
|
{
|
||||||
|
m_summaryLabel->setText("Checking system dependencies...");
|
||||||
|
m_checkButton->setEnabled(false);
|
||||||
|
|
||||||
|
// Reset all items to checking state
|
||||||
|
for (DependencyItem *item : m_dependencyItems) {
|
||||||
|
item->setChecking(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate async checking with timer
|
||||||
|
QTimer::singleShot(500, [this]() {
|
||||||
|
int installedCount = 0;
|
||||||
|
int totalCount = m_dependencyItems.size();
|
||||||
|
|
||||||
|
for (DependencyItem *item : m_dependencyItems) {
|
||||||
|
bool installed = false;
|
||||||
|
QString itemName = item->property("name").toString();
|
||||||
|
|
||||||
|
if (itemName == "Apple Mobile Device Support") {
|
||||||
|
installed = IsAppleMobileDeviceSupportInstalled();
|
||||||
|
} else if (itemName == "WinFsp") {
|
||||||
|
installed = IsWinFspInstalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
item->setInstalled(installed);
|
||||||
|
if (installed)
|
||||||
|
installedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update summary
|
||||||
|
if (installedCount == totalCount) {
|
||||||
|
m_summaryLabel->setText(
|
||||||
|
QString("All dependencies are installed (%1/%2)")
|
||||||
|
.arg(installedCount)
|
||||||
|
.arg(totalCount));
|
||||||
|
m_summaryLabel->setStyleSheet("color: green; font-weight: bold;");
|
||||||
|
} else {
|
||||||
|
m_summaryLabel->setText(
|
||||||
|
QString("Missing dependencies (%1/%2 installed)")
|
||||||
|
.arg(installedCount)
|
||||||
|
.arg(totalCount));
|
||||||
|
m_summaryLabel->setStyleSheet("color: red; font-weight: bold;");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_checkButton->setEnabled(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiagnoseWidget::onInstallRequested(const QString &name)
|
||||||
|
{
|
||||||
|
QString url;
|
||||||
|
QString message;
|
||||||
|
|
||||||
|
if (name == "Apple Mobile Device Support") {
|
||||||
|
url = "https://support.apple.com/downloads/itunes";
|
||||||
|
message = "Apple Mobile Device Support is typically installed with "
|
||||||
|
"iTunes.\n\n"
|
||||||
|
"Would you like to open the iTunes download page?";
|
||||||
|
} else if (name == "WinFsp") {
|
||||||
|
url = "https://winfsp.dev/rel/";
|
||||||
|
message = "WinFsp can be downloaded from the official website.\n\n"
|
||||||
|
"Would you like to open the WinFsp download page?";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!url.isEmpty()) {
|
||||||
|
int ret = QMessageBox::question(this, "Install " + name, message,
|
||||||
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
|
if (ret == QMessageBox::Yes) {
|
||||||
|
QDesktopServices::openUrl(QUrl(url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
#ifndef DIAGNOSE_WIDGET_H
|
||||||
|
#define DIAGNOSE_WIDGET_H
|
||||||
|
|
||||||
|
#include <QGroupBox>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QProgressBar>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QScrollArea>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
|
||||||
|
class DependencyItem : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DependencyItem(const QString &name, const QString &description,
|
||||||
|
QWidget *parent = nullptr);
|
||||||
|
void setInstalled(bool installed);
|
||||||
|
void setChecking(bool checking);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void installRequested(const QString &name);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onInstallClicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_name;
|
||||||
|
QLabel *m_nameLabel;
|
||||||
|
QLabel *m_descriptionLabel;
|
||||||
|
QLabel *m_statusLabel;
|
||||||
|
QPushButton *m_installButton;
|
||||||
|
QProgressBar *m_progressBar;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DiagnoseWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DiagnoseWidget(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void checkDependencies();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onInstallRequested(const QString &name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupUI();
|
||||||
|
void addDependencyItem(const QString &name, const QString &description);
|
||||||
|
|
||||||
|
QVBoxLayout *m_mainLayout;
|
||||||
|
QVBoxLayout *m_itemsLayout;
|
||||||
|
QPushButton *m_checkButton;
|
||||||
|
QLabel *m_summaryLabel;
|
||||||
|
QScrollArea *m_scrollArea;
|
||||||
|
QWidget *m_itemsWidget;
|
||||||
|
|
||||||
|
QList<DependencyItem *> m_dependencyItems;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DIAGNOSE_WIDGET_H
|
||||||
@@ -24,8 +24,8 @@ SSHTerminalWidget::SSHTerminalWidget(const ConnectionInfo &connectionInfo,
|
|||||||
m_sshChannel(nullptr), m_iproxyProcess(nullptr), m_sshConnected(false),
|
m_sshChannel(nullptr), m_iproxyProcess(nullptr), m_sshConnected(false),
|
||||||
m_isInitialized(false), m_currentState(TerminalState::Loading)
|
m_isInitialized(false), m_currentState(TerminalState::Loading)
|
||||||
{
|
{
|
||||||
setWindowTitle(
|
setWindowTitle(QString("SSH Terminal / %1 - iDescriptor")
|
||||||
QString("SSH Terminal - %1").arg(m_connectionInfo.deviceName));
|
.arg(m_connectionInfo.deviceName));
|
||||||
setMinimumSize(800, 600);
|
setMinimumSize(800, 600);
|
||||||
|
|
||||||
setupUI();
|
setupUI();
|
||||||
@@ -130,8 +130,11 @@ void SSHTerminalWidget::setupActionState()
|
|||||||
menu.exec(m_terminal->mapToGlobal(pos));
|
menu.exec(m_terminal->mapToGlobal(pos));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
#ifdef WIN32
|
||||||
|
m_terminal->startTerminalEmulation();
|
||||||
|
#else
|
||||||
m_terminal->startTerminalTeletype();
|
m_terminal->startTerminalTeletype();
|
||||||
|
#endif
|
||||||
m_terminal->setStyleSheet("padding: 5px;");
|
m_terminal->setStyleSheet("padding: 5px;");
|
||||||
|
|
||||||
actionLayout->addWidget(m_terminal);
|
actionLayout->addWidget(m_terminal);
|
||||||
@@ -459,8 +462,12 @@ void SSHTerminalWidget::checkSshData()
|
|||||||
int nbytes = ssh_channel_read_nonblocking(m_sshChannel, buffer,
|
int nbytes = ssh_channel_read_nonblocking(m_sshChannel, buffer,
|
||||||
sizeof(buffer), 0);
|
sizeof(buffer), 0);
|
||||||
if (nbytes > 0) {
|
if (nbytes > 0) {
|
||||||
|
#ifdef WIN32
|
||||||
|
m_terminal->receiveData(buffer, nbytes);
|
||||||
|
#else
|
||||||
// Write data to terminal's PTY
|
// Write data to terminal's PTY
|
||||||
write(m_terminal->getPtySlaveFd(), buffer, nbytes);
|
write(m_terminal->getPtySlaveFd(), buffer, nbytes);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,7 +478,11 @@ void SSHTerminalWidget::checkSshData()
|
|||||||
sizeof(buffer), 1);
|
sizeof(buffer), 1);
|
||||||
if (nbytes > 0) {
|
if (nbytes > 0) {
|
||||||
// Write stderr data to terminal's PTY
|
// Write stderr data to terminal's PTY
|
||||||
|
#ifdef WIN32
|
||||||
|
m_terminal->receiveData(buffer, nbytes);
|
||||||
|
#else
|
||||||
write(m_terminal->getPtySlaveFd(), buffer, nbytes);
|
write(m_terminal->getPtySlaveFd(), buffer, nbytes);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ void WelcomeWidget::setupUI()
|
|||||||
{
|
{
|
||||||
// Main layout with proper spacing and margins
|
// Main layout with proper spacing and margins
|
||||||
m_mainLayout = new QVBoxLayout(this);
|
m_mainLayout = new QVBoxLayout(this);
|
||||||
m_mainLayout->setContentsMargins(40, 60, 40, 60);
|
m_mainLayout->setContentsMargins(5, 5, 5, 5);
|
||||||
m_mainLayout->setSpacing(0);
|
m_mainLayout->setSpacing(0);
|
||||||
|
|
||||||
// Add top stretch
|
// Add top stretch
|
||||||
@@ -34,21 +34,22 @@ void WelcomeWidget::setupUI()
|
|||||||
palette.color(QPalette::WindowText).lighter(140));
|
palette.color(QPalette::WindowText).lighter(140));
|
||||||
m_subtitleLabel->setPalette(palette);
|
m_subtitleLabel->setPalette(palette);
|
||||||
m_mainLayout->addWidget(m_subtitleLabel);
|
m_mainLayout->addWidget(m_subtitleLabel);
|
||||||
m_mainLayout->addSpacing(40);
|
m_mainLayout->addSpacing(10);
|
||||||
|
|
||||||
m_imageLabel = new ResponsiveQLabel();
|
m_imageLabel = new ResponsiveQLabel();
|
||||||
m_imageLabel->setPixmap(QPixmap(":/resources/connect.png"));
|
m_imageLabel->setPixmap(QPixmap(":/resources/connect.png"));
|
||||||
// Let the pixmap scale while preserving aspect ratio
|
// Let the pixmap scale while preserving aspect ratio
|
||||||
m_imageLabel->setScaledContents(true);
|
m_imageLabel->setScaledContents(true);
|
||||||
// Prefer centered, not full-width expansion
|
// Prefer centered, not full-width expansion
|
||||||
m_imageLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
m_imageLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
// Cap size so it stays nicely centered on large windows
|
// Cap size so it stays nicely centered on large windows
|
||||||
m_imageLabel->setMaximumSize(480, 320);
|
// m_imageLabel->setMaximumSize(480, 320);
|
||||||
|
|
||||||
m_imageLabel->setStyleSheet("background: transparent; border: none;");
|
m_imageLabel->setStyleSheet("background: transparent; border: none;");
|
||||||
|
|
||||||
m_imageLabel->setAlignment(Qt::AlignCenter);
|
m_imageLabel->setAlignment(Qt::AlignCenter);
|
||||||
m_mainLayout->addWidget(m_imageLabel, 0, Qt::AlignHCenter);
|
m_mainLayout->addWidget(m_imageLabel, 0, Qt::AlignHCenter);
|
||||||
m_mainLayout->addSpacing(32);
|
m_mainLayout->addSpacing(10);
|
||||||
|
|
||||||
// Instruction text
|
// Instruction text
|
||||||
m_instructionLabel = createStyledLabel(
|
m_instructionLabel = createStyledLabel(
|
||||||
@@ -60,7 +61,7 @@ void WelcomeWidget::setupUI()
|
|||||||
instructionPalette.color(QPalette::WindowText).lighter(120));
|
instructionPalette.color(QPalette::WindowText).lighter(120));
|
||||||
m_instructionLabel->setPalette(instructionPalette);
|
m_instructionLabel->setPalette(instructionPalette);
|
||||||
m_mainLayout->addWidget(m_instructionLabel);
|
m_mainLayout->addWidget(m_instructionLabel);
|
||||||
m_mainLayout->addSpacing(24);
|
m_mainLayout->addSpacing(10);
|
||||||
|
|
||||||
// GitHub link
|
// GitHub link
|
||||||
m_githubLabel =
|
m_githubLabel =
|
||||||
|
|||||||
Reference in New Issue
Block a user