mirror of
https://github.com/iDescriptor/iDescriptor.git
synced 2026-06-21 19:35:49 +08:00
copy just enough gstreamer plugins and handle deps for Windows build
- Added custom command to copy libZUpdater.dll for deployment - Updated win-deploy.cmake to copy specific GStreamer plugins - Introduced install scripts for Apple drivers and WinFsp - Improved DiagnoseWidget UI with scroll area and process indicators - Adjusted WelcomeWidget to include DiagnoseWidget on non-Apple platforms
This commit is contained in:
@@ -387,3 +387,15 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
include(CPack)
|
||||
|
||||
|
||||
# FIXME: move to win-deploy.cmake
|
||||
if(WIN32)
|
||||
# Ensure libZUpdater.dll is copied next to the main executable for deployment
|
||||
add_custom_command(TARGET iDescriptor POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
$<TARGET_FILE:ZUpdater>
|
||||
$<TARGET_FILE_DIR:iDescriptor>
|
||||
COMMENT "Copying libZUpdater.dll to executable directory"
|
||||
)
|
||||
endif()
|
||||
|
||||
+49
-18
@@ -19,7 +19,6 @@ message(" MSYS2_BIN_PATH: ${MSYS2_BIN_PATH}")
|
||||
|
||||
if(NOT EXISTS ${EXECUTABLE_PATH})
|
||||
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)
|
||||
@@ -75,7 +74,7 @@ file(GET_RUNTIME_DEPENDENCIES
|
||||
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$"
|
||||
#PRE_EXCLUDE_REGEXES "^api-ms-" "^ext-ms-" "^AVRT" "^avrt" "^MSVCP" "^VCRUNTIME" "^ucrtbase"
|
||||
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}
|
||||
DIRECTORIES ${BUILD_DIR} ${QT_BIN_PATH} ${MSYS2_BIN_PATH} "C:/lxqt/lib" $ENV{PATH}
|
||||
)
|
||||
|
||||
set(COPIED_DLLS 0)
|
||||
@@ -119,23 +118,52 @@ message("Processed ${TOTAL_DLLS} runtime dependencies, copied ${COPIED_DLLS} fil
|
||||
|
||||
# Step 3: Copy GStreamer plugins
|
||||
message("Copying GStreamer plugins...")
|
||||
file(GLOB GSTREAMER_PLUGINS "${MSYS2_BIN_PATH}/../lib/gstreamer-1.0/*.dll")
|
||||
# OLD: file(GLOB GSTREAMER_PLUGINS "${MSYS2_BIN_PATH}/../lib/gstreamer-1.0/*.dll")
|
||||
# Replace broad copy with targeted list (matches versioned names via glob)
|
||||
set(GSTREAMER_PLUGIN_DIR "${MSYS2_BIN_PATH}/../lib/gstreamer-1.0")
|
||||
|
||||
if(GSTREAMER_PLUGINS)
|
||||
# Create gstreamer-1.0 directory in output
|
||||
file(MAKE_DIRECTORY ${OUTPUT_DIR}/gstreamer-1.0)
|
||||
|
||||
foreach(PLUGIN ${GSTREAMER_PLUGINS})
|
||||
get_filename_component(PLUGIN_NAME ${PLUGIN} NAME)
|
||||
message("Copying GStreamer plugin: ${PLUGIN_NAME}")
|
||||
file(COPY ${PLUGIN} DESTINATION ${OUTPUT_DIR}/gstreamer-1.0)
|
||||
endforeach()
|
||||
|
||||
list(LENGTH GSTREAMER_PLUGINS PLUGIN_COUNT)
|
||||
message("Successfully copied ${PLUGIN_COUNT} GStreamer plugins")
|
||||
else()
|
||||
message(WARNING "No GStreamer plugins found in ${MSYS2_BIN_PATH}/../lib/gstreamer-1.0/")
|
||||
endif()
|
||||
# List of plugin basenames to copy (no extension or version). Add items as needed.
|
||||
set(WANTED_PLUGINS
|
||||
"libgstaudioconvert"
|
||||
"libgstvolume"
|
||||
"libgstcoreelements"
|
||||
"libgstautodetect"
|
||||
"libgstdirectsound"
|
||||
"libgstlibav"
|
||||
"libgstapp"
|
||||
"libgstlevel"
|
||||
"libgstwasapi"
|
||||
"libgstplayback"
|
||||
"libgstaudioresample"
|
||||
"libgstaudiomixer"
|
||||
"libgstaudiotestsrc"
|
||||
"libgstmediafoundation"
|
||||
"libgstdecodebin"
|
||||
"libgsttypefindfunctions"
|
||||
"libgstvideoscale"
|
||||
"libgstvideoconvert"
|
||||
"libgstvideorate"
|
||||
"libgstoverlaycomposition"
|
||||
)
|
||||
|
||||
file(MAKE_DIRECTORY "${OUTPUT_DIR}/gstreamer-1.0")
|
||||
set(COPIED_PLUGIN_COUNT 0)
|
||||
foreach(BASENAME ${WANTED_PLUGINS})
|
||||
# match any versioned filename starting with the basename
|
||||
file(GLOB MATCHES "${GSTREAMER_PLUGIN_DIR}/${BASENAME}*.dll")
|
||||
if(MATCHES)
|
||||
foreach(PLUGIN_PATH ${MATCHES})
|
||||
get_filename_component(PLUGIN_NAME ${PLUGIN_PATH} NAME)
|
||||
message("Copying GStreamer plugin: ${PLUGIN_NAME}")
|
||||
file(COPY "${PLUGIN_PATH}" DESTINATION "${OUTPUT_DIR}/gstreamer-1.0")
|
||||
math(EXPR COPIED_PLUGIN_COUNT "${COPIED_PLUGIN_COUNT} + 1")
|
||||
endforeach()
|
||||
else()
|
||||
message(WARNING "Requested GStreamer plugin not found: ${BASENAME} (searched ${GSTREAMER_PLUGIN_DIR})")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
message("Successfully copied ${COPIED_PLUGIN_COUNT} requested GStreamer plugins")
|
||||
|
||||
# Step 4: Manually copy the correct MSYS2 MinGW runtime DLLs.
|
||||
# This ensures the versions required by GStreamer/FFmpeg are used.
|
||||
@@ -204,5 +232,8 @@ file(COPY "${GST_LIBEXEC_PATH}/gst-ptp-helper.exe" DESTINATION "${OUTPUT_DIR}/li
|
||||
message("Copying executables")
|
||||
file(COPY C:/msys64/mingw64/bin/iproxy.exe DESTINATION ${OUTPUT_DIR})
|
||||
|
||||
message("Copying required scripts")
|
||||
file(COPY "${CMAKE_SOURCE_DIR}/install-apple-drivers.ps1" DESTINATION ${OUTPUT_DIR})
|
||||
file(COPY "${CMAKE_SOURCE_DIR}/install-win-fsp.silent.bat" DESTINATION ${OUTPUT_DIR})
|
||||
|
||||
message("=== Windows deployment completed ===")
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
## Taken from https://github.com/NelloKudo/Apple-Mobile-Drivers-Installer
|
||||
## Apple USB and Mobile Device Ethernet drivers installer!
|
||||
## Please report any issues at GitHub: https://github.com/NelloKudo/Apple-Mobile-Drivers-Installer
|
||||
|
||||
## Download links for Apple USB Drivers and Apple Mobile Ethernet USB Drivers respectively.
|
||||
## All of these are downloaded from Microsoft's Update Catalog, which you can browse yourself at here: https://www.catalog.update.microsoft.com/
|
||||
|
||||
$AppleDri1 = "https://catalog.s.download.windowsupdate.com/d/msdownload/update/driver/drvs/2020/11/01d96dfd-2f6f-46f7-8bc3-fd82088996d2_a31ff7000e504855b3fa124bf27b3fe5bc4d0893.cab"
|
||||
$AppleDri2 = "https://catalog.s.download.windowsupdate.com/c/msdownload/update/driver/drvs/2017/11/netaapl_7503681835e08ce761c52858949731761e1fa5a1.cab"
|
||||
$AppleITunesLink = "https://www.apple.com/itunes/download/win64"
|
||||
|
||||
Write-Host ""
|
||||
Write-Host -ForegroundColor Cyan "Welcome to Apple USB and Mobile Device Ethernet drivers installer!!"
|
||||
Write-Host ""
|
||||
|
||||
## Checking if the script is being run as admin..
|
||||
if (-not ([Security.Principal.WindowsIdentity]::GetCurrent().Groups -contains 'S-1-5-32-544')) {
|
||||
Write-Host -ForegroundColor Yellow "This script requires administrative privileges!"
|
||||
Write-Host -ForegroundColor Yellow "Please run the script as an administrator if you want to install drivers."
|
||||
pause
|
||||
exit 1
|
||||
}
|
||||
|
||||
## Preparing the system to actually download drivers..
|
||||
$destinationFolder = [System.IO.Path]::Combine($env:TEMP, "AppleDriTemp")
|
||||
if (-not (Test-Path $destinationFolder)) {
|
||||
New-Item -ItemType Directory -Path $destinationFolder | Out-Null
|
||||
}
|
||||
|
||||
try {
|
||||
$currentPath = $PWD.Path
|
||||
Write-Host -ForegroundColor Yellow "Downloading Apple iTunes and installing AppleMobileDeviceSupport64.msi.."
|
||||
Write-Host -ForegroundColor Yellow "(It might take a while! Script is not frozen!)"
|
||||
(New-Object System.Net.WebClient).DownloadFile($AppleITunesLink, [System.IO.Path]::Combine($destinationFolder, "iTunes64Setup.exe"))
|
||||
cd "$destinationFolder"
|
||||
Start-Process -FilePath "$destinationFolder\iTunes64Setup.exe" -ArgumentList "/extract" -Wait
|
||||
cd "$currentPath"
|
||||
Start-Process -FilePath "$destinationFolder\AppleMobileDeviceSupport64.msi" -ArgumentList "/qn" -Wait
|
||||
|
||||
Write-Host -ForegroundColor Yellow "Downloading Apple USB and Mobile Device Ethernet drivers from Microsoft..."
|
||||
Invoke-WebRequest -Uri $AppleDri1 -OutFile ([System.IO.Path]::Combine($destinationFolder, "AppleUSB-486.0.0.0-driv.cab"))
|
||||
Invoke-WebRequest -Uri $AppleDri2 -OutFile ([System.IO.Path]::Combine($destinationFolder, "AppleNet-1.8.5.1-driv.cab"))
|
||||
|
||||
Write-Host -ForegroundColor Yellow "Extracting drivers..."
|
||||
& expand.exe -F:* "$destinationFolder\AppleUSB-486.0.0.0-driv.cab" "$destinationFolder" >$null 2>&1
|
||||
& expand.exe -F:* "$destinationFolder\AppleNet-1.8.5.1-driv.cab" "$destinationFolder" >$null 2>&1
|
||||
|
||||
## Installing drivers..
|
||||
Write-Host -ForegroundColor Yellow "Installing Apple USB and Mobile Device Ethernet drivers!"
|
||||
Write-Host -ForegroundColor Yellow "If any of your peripherals stop working for a few seconds that's due to Apple stuff installing."
|
||||
Write-Host ""
|
||||
Get-ChildItem -Path "$destinationFolder\*.inf" | ForEach-Object {
|
||||
pnputil /add-driver $_.FullName /install
|
||||
Write-Host ""
|
||||
Write-Host -ForegroundColor Yellow "Driver installed.."
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
## Cleaning..
|
||||
Remove-Item -Path $destinationFolder -Recurse -Force
|
||||
|
||||
} catch {
|
||||
Write-Host -ForegroundColor Red "Failed to complete installation. Error: $_"
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host -ForegroundColor Cyan "Installation complete! Enjoy your Apple devices!!"
|
||||
Write-Host -ForegroundColor Yellow "(If devices are still not working, a reboot might be needed!!)"
|
||||
@@ -0,0 +1,29 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
net session >nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo Installing WinFsp...
|
||||
|
||||
set "DOWNLOAD_URL=https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi"
|
||||
set "CACHE_DIR=%TEMP%\winfsp-install"
|
||||
set "MSI_FILE=%CACHE_DIR%\winfsp-2.1.25156.msi"
|
||||
|
||||
if not exist "%CACHE_DIR%" mkdir "%CACHE_DIR%"
|
||||
|
||||
curl -L -o "%MSI_FILE%" "%DOWNLOAD_URL%"
|
||||
if %errorlevel% neq 0 (
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
msiexec /i "%MSI_FILE%" /quiet /norestart
|
||||
if %errorlevel% neq 0 (
|
||||
rmdir /s /q "%CACHE_DIR%"
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
rmdir /s /q "%CACHE_DIR%"
|
||||
exit /b 0
|
||||
@@ -20,8 +20,6 @@
|
||||
<file>resources/icons/MdiGithub.png</file>
|
||||
<file>resources/icons/app-icon/icon.ico</file>
|
||||
<file>qml/MapView.qml</file>
|
||||
<!-- TODO: -->
|
||||
<!-- <file>resources/dump.js</file> -->
|
||||
<file>resources/iphone.png</file>
|
||||
<file>resources/ios-wallpapers/iphone-ios4.png</file>
|
||||
<file>resources/ios-wallpapers/iphone-ios5.png</file>
|
||||
|
||||
@@ -28,10 +28,6 @@
|
||||
#include <QMenuBar>
|
||||
#include <QMessageBox>
|
||||
|
||||
#ifdef WIN32
|
||||
#include "platform/windows/diagnose_widget.h"
|
||||
#endif
|
||||
|
||||
void handleCallback(const idevice_event_t *event, void *userData)
|
||||
{
|
||||
printf("Device event received: ");
|
||||
@@ -79,7 +75,6 @@ void handleCallback(const idevice_event_t *event, void *userData)
|
||||
default:
|
||||
qDebug() << "Unhandled event: " << event->event;
|
||||
}
|
||||
// return;
|
||||
}
|
||||
|
||||
void handleCallbackRecovery(const irecv_device_event_t *event, void *userData)
|
||||
@@ -117,10 +112,7 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
const QSize minSize(900, 600);
|
||||
setMinimumSize(minSize);
|
||||
resize(minSize);
|
||||
// TODO
|
||||
// setWindowIcon(QIcon(":/resources/icons/icon.png"));
|
||||
|
||||
// Create custom tab widget
|
||||
m_ZTabWidget = new ZTabWidget(this);
|
||||
m_ZTabWidget->setAttribute(Qt::WA_ContentsMarginsRespectsSafeArea, false);
|
||||
|
||||
@@ -131,31 +123,8 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
#endif
|
||||
setCentralWidget(m_ZTabWidget);
|
||||
|
||||
// Create device manager and stacked widget for main tab
|
||||
m_mainStackedWidget = new QStackedWidget();
|
||||
|
||||
// Welcome page (shown when no devices are connected)
|
||||
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_mainStackedWidget->addWidget(welcomePage);
|
||||
|
||||
@@ -16,10 +16,15 @@ DiagnoseDialog::DiagnoseDialog(QWidget *parent) : QDialog(parent)
|
||||
void DiagnoseDialog::setupUI()
|
||||
{
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
||||
QScrollArea *scrollArea = new QScrollArea(this);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
mainLayout->setContentsMargins(10, 10, 10, 10);
|
||||
|
||||
// Add the main diagnose widget
|
||||
m_diagnoseWidget = new DiagnoseWidget();
|
||||
scrollArea->setWidget(m_diagnoseWidget);
|
||||
|
||||
// Close button
|
||||
QHBoxLayout *buttonLayout = new QHBoxLayout();
|
||||
@@ -33,7 +38,7 @@ void DiagnoseDialog::setupUI()
|
||||
buttonLayout->addWidget(m_closeButton);
|
||||
|
||||
// Layout assembly
|
||||
mainLayout->addWidget(m_diagnoseWidget, 1);
|
||||
mainLayout->addWidget(scrollArea);
|
||||
mainLayout->addLayout(buttonLayout);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
#include "diagnose_widget.h"
|
||||
#ifdef WIN32
|
||||
#include "check_deps.h"
|
||||
#endif
|
||||
#include <QApplication>
|
||||
#include <QCoreApplication>
|
||||
#include <QDesktopServices>
|
||||
#include <QMessageBox>
|
||||
#include <QProcess>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
|
||||
|
||||
DependencyItem::DependencyItem(const QString &name, const QString &description,
|
||||
QWidget *parent)
|
||||
: QWidget(parent), m_name(name)
|
||||
@@ -37,7 +40,8 @@ DependencyItem::DependencyItem(const QString &name, const QString &description,
|
||||
m_statusLabel->setAlignment(Qt::AlignCenter);
|
||||
|
||||
// Right side - actions
|
||||
QVBoxLayout *actionLayout = new QVBoxLayout();
|
||||
QHBoxLayout *actionLayout = new QHBoxLayout();
|
||||
actionLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_installButton = new QPushButton("Install");
|
||||
m_installButton->setMinimumWidth(80);
|
||||
@@ -45,13 +49,13 @@ DependencyItem::DependencyItem(const QString &name, const QString &description,
|
||||
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);
|
||||
m_processIndicator = new QProcessIndicator();
|
||||
m_processIndicator->setType(QProcessIndicator::line_rotate);
|
||||
m_processIndicator->setFixedSize(24, 24);
|
||||
m_processIndicator->setVisible(false);
|
||||
|
||||
actionLayout->addWidget(m_processIndicator);
|
||||
actionLayout->addWidget(m_installButton);
|
||||
actionLayout->addWidget(m_progressBar);
|
||||
actionLayout->addStretch();
|
||||
|
||||
layout->addLayout(infoLayout, 1);
|
||||
@@ -80,28 +84,47 @@ void DependencyItem::setChecking(bool checking)
|
||||
m_statusLabel->setText("Checking...");
|
||||
m_statusLabel->setStyleSheet("color: gray;");
|
||||
m_installButton->setVisible(false);
|
||||
m_progressBar->setVisible(false);
|
||||
m_processIndicator->setVisible(false);
|
||||
m_processIndicator->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void DependencyItem::setInstalling(bool installing)
|
||||
{
|
||||
if (installing) {
|
||||
m_statusLabel->setText("Installing...");
|
||||
m_statusLabel->setStyleSheet("color: gray;");
|
||||
m_installButton->setVisible(false);
|
||||
m_processIndicator->setVisible(true);
|
||||
m_processIndicator->start();
|
||||
} else {
|
||||
m_processIndicator->stop();
|
||||
m_processIndicator->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
void DependencyItem::onInstallClicked() { emit installRequested(m_name); }
|
||||
|
||||
DiagnoseWidget::DiagnoseWidget(QWidget *parent) : QWidget(parent)
|
||||
DiagnoseWidget::DiagnoseWidget(QWidget *parent)
|
||||
: QWidget(parent), m_isExpanded(false)
|
||||
{
|
||||
setupUI();
|
||||
|
||||
#ifdef WIN32
|
||||
// Add dependency items
|
||||
addDependencyItem("Apple Mobile Device Support",
|
||||
"Required for iOS device communication");
|
||||
addDependencyItem("WinFsp",
|
||||
"Required for filesystem operations and mounting");
|
||||
#endif
|
||||
|
||||
// Auto-check on startup
|
||||
QTimer::singleShot(100, this, &DiagnoseWidget::checkDependencies);
|
||||
QTimer::singleShot(100, this, [this]() { checkDependencies(); });
|
||||
}
|
||||
|
||||
void DiagnoseWidget::setupUI()
|
||||
{
|
||||
setAutoFillBackground(true);
|
||||
m_mainLayout = new QVBoxLayout(this);
|
||||
m_mainLayout->setSpacing(10);
|
||||
|
||||
@@ -115,40 +138,43 @@ void DiagnoseWidget::setupUI()
|
||||
m_summaryLabel = new QLabel("Checking system dependencies...");
|
||||
|
||||
// Check button
|
||||
m_checkButton = new QPushButton("Refresh Check");
|
||||
m_checkButton = new QPushButton("Refresh Check(s)");
|
||||
m_checkButton->setMaximumWidth(150);
|
||||
connect(m_checkButton, &QPushButton::clicked, this,
|
||||
&DiagnoseWidget::checkDependencies);
|
||||
[this]() { checkDependencies(false); });
|
||||
|
||||
// Scroll area for dependency items
|
||||
m_scrollArea = new QScrollArea();
|
||||
m_scrollArea->setWidgetResizable(true);
|
||||
// m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarNever);
|
||||
m_scrollArea->setMinimumHeight(200);
|
||||
// Toggle button
|
||||
m_toggleButton = new QPushButton("▼");
|
||||
m_toggleButton->setFixedSize(24, 24);
|
||||
m_toggleButton->setCheckable(true);
|
||||
connect(m_toggleButton, &QPushButton::clicked, this,
|
||||
&DiagnoseWidget::onToggleExpand);
|
||||
|
||||
m_itemsWidget = new QWidget();
|
||||
// m_itemsWidget->setSizePolicy(QSizePolicy::Expanding,
|
||||
// QSizePolicy::Preferred);
|
||||
m_itemsWidget->setFixedHeight(400);
|
||||
m_itemsLayout = new QVBoxLayout(m_itemsWidget);
|
||||
m_itemsLayout->setSpacing(5);
|
||||
m_itemsLayout->addStretch();
|
||||
|
||||
m_scrollArea->setWidget(m_itemsWidget);
|
||||
m_itemsWidget->setVisible(m_isExpanded);
|
||||
|
||||
// Layout assembly
|
||||
QHBoxLayout *headerLayout = new QHBoxLayout();
|
||||
headerLayout->addWidget(titleLabel);
|
||||
headerLayout->addStretch();
|
||||
headerLayout->addWidget(m_checkButton);
|
||||
headerLayout->addWidget(m_toggleButton);
|
||||
|
||||
m_mainLayout->addLayout(headerLayout);
|
||||
m_mainLayout->addWidget(m_summaryLabel);
|
||||
m_mainLayout->addWidget(m_scrollArea, 1);
|
||||
m_mainLayout->addWidget(m_itemsWidget);
|
||||
}
|
||||
|
||||
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
|
||||
item->setProperty("name", name);
|
||||
connect(item, &DependencyItem::installRequested, this,
|
||||
&DiagnoseWidget::onInstallRequested);
|
||||
|
||||
@@ -158,18 +184,16 @@ void DiagnoseWidget::addDependencyItem(const QString &name,
|
||||
m_itemsLayout->insertWidget(m_itemsLayout->count() - 1, item);
|
||||
}
|
||||
|
||||
void DiagnoseWidget::checkDependencies()
|
||||
void DiagnoseWidget::checkDependencies(bool autoExpand)
|
||||
{
|
||||
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]() {
|
||||
QTimer::singleShot(500, [this, autoExpand]() {
|
||||
int installedCount = 0;
|
||||
int totalCount = m_dependencyItems.size();
|
||||
|
||||
@@ -177,30 +201,37 @@ void DiagnoseWidget::checkDependencies()
|
||||
bool installed = false;
|
||||
QString itemName = item->property("name").toString();
|
||||
|
||||
#ifdef WIN32
|
||||
if (itemName == "Apple Mobile Device Support") {
|
||||
installed = IsAppleMobileDeviceSupportInstalled();
|
||||
} else if (itemName == "WinFsp") {
|
||||
installed = IsWinFspInstalled();
|
||||
}
|
||||
#endif
|
||||
|
||||
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;");
|
||||
if (m_isExpanded && autoExpand) {
|
||||
onToggleExpand();
|
||||
}
|
||||
} else {
|
||||
m_summaryLabel->setText(
|
||||
QString("Missing dependencies (%1/%2 installed)")
|
||||
.arg(installedCount)
|
||||
.arg(totalCount));
|
||||
m_summaryLabel->setStyleSheet("color: red; font-weight: bold;");
|
||||
if (!m_isExpanded && autoExpand) {
|
||||
onToggleExpand();
|
||||
}
|
||||
}
|
||||
|
||||
m_checkButton->setEnabled(true);
|
||||
@@ -209,26 +240,125 @@ void DiagnoseWidget::checkDependencies()
|
||||
|
||||
void DiagnoseWidget::onInstallRequested(const QString &name)
|
||||
{
|
||||
QString url;
|
||||
QString message;
|
||||
|
||||
#ifdef WIN32
|
||||
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));
|
||||
DependencyItem *itemToInstall = nullptr;
|
||||
for (DependencyItem *item : m_dependencyItems) {
|
||||
if (item->property("name").toString() == name) {
|
||||
itemToInstall = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!itemToInstall)
|
||||
return;
|
||||
|
||||
itemToInstall->setInstalling(true);
|
||||
|
||||
QString scriptPath = QCoreApplication::applicationDirPath() +
|
||||
"/install-apple-drivers.ps1";
|
||||
|
||||
QProcess *installProcess = new QProcess(this);
|
||||
connect(
|
||||
installProcess, &QProcess::finished, this,
|
||||
[this, installProcess,
|
||||
itemToInstall](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
if (exitStatus != QProcess::NormalExit || exitCode != 0) {
|
||||
QString errorOutput =
|
||||
installProcess->readAllStandardError();
|
||||
if (errorOutput.isEmpty()) {
|
||||
errorOutput = installProcess->readAllStandardOutput();
|
||||
}
|
||||
QMessageBox::warning(
|
||||
this, "Installation Failed",
|
||||
"Failed to launch the installation script. This "
|
||||
"might be a "
|
||||
"permissions issue or an internal error.\n\n"
|
||||
"Details: " +
|
||||
errorOutput.trimmed());
|
||||
checkDependencies(false); // Revert UI on failure
|
||||
} else {
|
||||
// FIXME: we need to track process completion
|
||||
QMessageBox::information(
|
||||
this, "Installation Started",
|
||||
"The installation process has been started.\n"
|
||||
"Please approve the administrator prompt (UAC) if it "
|
||||
"appears.\n"
|
||||
"After installation, please re-run the dependency "
|
||||
"check");
|
||||
|
||||
itemToInstall->setInstalling(false);
|
||||
}
|
||||
installProcess->deleteLater();
|
||||
});
|
||||
|
||||
// Correctly launch powershell.exe elevated, and pass the script to it.
|
||||
// The -Wait parameter is removed as it does not work with -Verb RunAs.
|
||||
QString command =
|
||||
QString("Start-Process -FilePath powershell.exe -Verb RunAs "
|
||||
"-ArgumentList '-NoProfile -ExecutionPolicy Bypass -File "
|
||||
"\"%1\"'")
|
||||
.arg(scriptPath);
|
||||
|
||||
QStringList args;
|
||||
args << "-NoProfile"
|
||||
<< "-ExecutionPolicy"
|
||||
<< "Bypass"
|
||||
<< "-Command" << command;
|
||||
|
||||
installProcess->start("powershell.exe", args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "WinFsp") {
|
||||
DependencyItem *itemToInstall = nullptr;
|
||||
for (DependencyItem *item : m_dependencyItems) {
|
||||
if (item->property("name").toString() == name) {
|
||||
itemToInstall = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!itemToInstall)
|
||||
return;
|
||||
|
||||
itemToInstall->setInstalling(true);
|
||||
|
||||
QString scriptPath = QCoreApplication::applicationDirPath() +
|
||||
"/install-win-fsp.silent.bat";
|
||||
|
||||
QProcess *installProcess = new QProcess(this);
|
||||
connect(
|
||||
installProcess, &QProcess::finished, this,
|
||||
[this, installProcess](int exitCode,
|
||||
QProcess::ExitStatus exitStatus) {
|
||||
if (exitStatus != QProcess::NormalExit || exitCode != 0) {
|
||||
QMessageBox::warning(
|
||||
this, "Installation Failed",
|
||||
"The installation script failed to run correctly. "
|
||||
"This might be because the action was cancelled or an "
|
||||
"error occurred.\n\nPlease try again.");
|
||||
}
|
||||
checkDependencies(false);
|
||||
installProcess->deleteLater();
|
||||
});
|
||||
|
||||
QStringList args;
|
||||
args << "-NoProfile"
|
||||
<< "-ExecutionPolicy"
|
||||
<< "Bypass"
|
||||
<< "-Command"
|
||||
<< QString("Start-Process -FilePath \"%1\" -Verb RunAs -Wait;")
|
||||
.arg(scriptPath);
|
||||
|
||||
installProcess->start("powershell.exe", args);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DiagnoseWidget::onToggleExpand()
|
||||
{
|
||||
m_isExpanded = !m_isExpanded;
|
||||
m_itemsWidget->setVisible(m_isExpanded);
|
||||
m_toggleButton->setText(m_isExpanded ? "▲" : "▼");
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include "../../qprocessindicator.h"
|
||||
|
||||
class DependencyItem : public QWidget
|
||||
{
|
||||
@@ -20,6 +21,7 @@ public:
|
||||
QWidget *parent = nullptr);
|
||||
void setInstalled(bool installed);
|
||||
void setChecking(bool checking);
|
||||
void setInstalling(bool installing);
|
||||
|
||||
signals:
|
||||
void installRequested(const QString &name);
|
||||
@@ -33,7 +35,7 @@ private:
|
||||
QLabel *m_descriptionLabel;
|
||||
QLabel *m_statusLabel;
|
||||
QPushButton *m_installButton;
|
||||
QProgressBar *m_progressBar;
|
||||
QProcessIndicator *m_processIndicator;
|
||||
};
|
||||
|
||||
class DiagnoseWidget : public QWidget
|
||||
@@ -44,10 +46,11 @@ public:
|
||||
explicit DiagnoseWidget(QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void checkDependencies();
|
||||
void checkDependencies(bool autoExpand = true);
|
||||
|
||||
private slots:
|
||||
void onInstallRequested(const QString &name);
|
||||
void onToggleExpand();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
@@ -56,9 +59,10 @@ private:
|
||||
QVBoxLayout *m_mainLayout;
|
||||
QVBoxLayout *m_itemsLayout;
|
||||
QPushButton *m_checkButton;
|
||||
QPushButton *m_toggleButton;
|
||||
QLabel *m_summaryLabel;
|
||||
QScrollArea *m_scrollArea;
|
||||
QWidget *m_itemsWidget;
|
||||
bool m_isExpanded;
|
||||
|
||||
QList<DependencyItem *> m_dependencyItems;
|
||||
};
|
||||
|
||||
+11
-5
@@ -9,13 +9,17 @@
|
||||
#include <QPalette>
|
||||
#include <QUrl>
|
||||
|
||||
#ifdef WIN32
|
||||
#include "platform/windows/diagnose_widget.h"
|
||||
#endif
|
||||
|
||||
WelcomeWidget::WelcomeWidget(QWidget *parent) : QWidget(parent) { setupUI(); }
|
||||
|
||||
void WelcomeWidget::setupUI()
|
||||
{
|
||||
// Main layout with proper spacing and margins
|
||||
m_mainLayout = new QVBoxLayout(this);
|
||||
m_mainLayout->setContentsMargins(5, 5, 5, 5);
|
||||
m_mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_mainLayout->setSpacing(0);
|
||||
|
||||
// Add top stretch
|
||||
@@ -85,11 +89,13 @@ void WelcomeWidget::setupUI()
|
||||
|
||||
m_mainLayout->addWidget(m_githubLabel);
|
||||
|
||||
// Add bottom stretch
|
||||
m_mainLayout->addStretch(1);
|
||||
// no additional deps needed on macOS
|
||||
#ifndef __APPLE__
|
||||
DiagnoseWidget *diagnoseWidget = new DiagnoseWidget();
|
||||
m_mainLayout->addWidget(diagnoseWidget);
|
||||
#endif
|
||||
|
||||
// Set minimum size
|
||||
// setMinimumSize(600, 500);
|
||||
m_mainLayout->addStretch(1);
|
||||
}
|
||||
|
||||
ZLabel *WelcomeWidget::createStyledLabel(const QString &text, int fontSize,
|
||||
|
||||
@@ -9,7 +9,12 @@
|
||||
ZTab::ZTab(const QString &text, QWidget *parent) : QPushButton(text, parent)
|
||||
{
|
||||
setCheckable(true);
|
||||
#ifndef WIN32
|
||||
setFixedHeight(50);
|
||||
#else
|
||||
setFixedHeight(40);
|
||||
#endif
|
||||
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
}
|
||||
|
||||
@@ -21,7 +26,11 @@ ZTabWidget::ZTabWidget(QWidget *parent) : QWidget(parent), m_currentIndex(0)
|
||||
|
||||
// Create tab bar container
|
||||
m_tabBar = new QWidget();
|
||||
#ifndef WIN32
|
||||
m_tabBar->setFixedHeight(50);
|
||||
#else
|
||||
m_tabBar->setFixedHeight(40);
|
||||
#endif
|
||||
m_tabBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
m_tabLayout = new QHBoxLayout(m_tabBar);
|
||||
m_tabLayout->setSpacing(0);
|
||||
|
||||
Reference in New Issue
Block a user