mirror of
https://github.com/iDescriptor/iDescriptor.git
synced 2026-06-22 03:45:51 +08:00
Refactor and enhance service management
- Updated CMakeLists.txt to set CPACK_WIX_UPGRADE_GUID directly. - Enhanced DependencyItem and DiagnoseWidget to manage service availability states. - Implemented service management functions for Windows, including starting services and checking their status. - Improved UI responsiveness in WelcomeWidget and NetworkDevicesToConnectWidget. - Introduced SERVICE_AVAILABILITY enum to standardize service state representation. - Cleaned up unnecessary code and comments across various files.
This commit is contained in:
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* iDescriptor: A free and open-source idevice management tool.
|
||||
*
|
||||
* Copyright (C) 2025 Uncore <https://github.com/uncor3>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// #include "../../iDescriptor.h"
|
||||
// #include <string>
|
||||
// #include <vector>
|
||||
|
||||
// struct JailbreakDetectionResult {
|
||||
// bool is_jailbroken;
|
||||
// std::vector<std::string> found_folders;
|
||||
// };
|
||||
|
||||
// // char *possible_jailbreak_paths[] = {
|
||||
// // "/Applications/Cydia.app",
|
||||
// // "/Library/MobileSubstrate/MobileSubstrate.dylib",
|
||||
// // "/bin/bash",
|
||||
// // "/usr/sbin/sshd",
|
||||
// // "/etc/apt",
|
||||
// // NULL
|
||||
// // };
|
||||
|
||||
// JailbreakDetectionResult detect_has_jailbroken_before(AfcClientHandle *afc)
|
||||
// {
|
||||
// // std::vector<std::string> jailbreak_folders = {".installed_palera1n",
|
||||
// // ".procursus_strapped"};
|
||||
|
||||
// JailbreakDetectionResult result = {false, {}};
|
||||
|
||||
// // char **dirs = NULL;
|
||||
// // size_t count = 0;
|
||||
// // if (!afc_list_directory(afc, (std::string(POSSIBLE_ROOT) +
|
||||
// // "bin").c_str(),
|
||||
// // &dirs, &count)) {
|
||||
// // free(dirs);
|
||||
// // }
|
||||
// // afc_dictionary_free(dirs);
|
||||
// return result;
|
||||
// }
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* iDescriptor: A free and open-source idevice management tool.
|
||||
*
|
||||
* Copyright (C) 2025 Uncore <https://github.com/uncor3>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// parse boot args from idevice info
|
||||
// NonVolatileRAM: auto-boot: dHJ1ZQ==
|
||||
// backlight-level: MTUyNg==
|
||||
// boot-args:
|
||||
// com.apple.System.tz0-size: MHg2MDAwMDA=
|
||||
// oblit-begins:
|
||||
// T2JsaXRUeXBlOiBPYmxpdGVyYXRlRGF0YVBhcnRpdGlvbi4gUmVhc29uOiB1bmtub3du
|
||||
// obliteration: aGFuZGxlX21lc3NhZ2U6IE9ibGl0ZXJhdGlvbiBDb21wbGV0ZQo=
|
||||
// PartitionType: GUID_partition_scheme
|
||||
@@ -4,6 +4,9 @@ DevModeWidget::DevModeWidget(std::shared_ptr<iDescriptorDevice> device,
|
||||
QWidget *parent)
|
||||
: QDialog{parent}
|
||||
{
|
||||
#ifdef WIN32
|
||||
setupWinWindow(this);
|
||||
#endif
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->setContentsMargins(0, 20, 0, 0);
|
||||
mainLayout->setSpacing(0);
|
||||
|
||||
+157
-110
@@ -23,27 +23,9 @@
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
#endif
|
||||
#include "iDescriptor-ui.h"
|
||||
#include <QApplication>
|
||||
#include <QCoreApplication>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFrame>
|
||||
#include <QMessageBox>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QProcess>
|
||||
#include <QRegularExpression>
|
||||
#include <QStandardPaths>
|
||||
#include <QTextStream>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
|
||||
DependencyItem::DependencyItem(const QString &name, const QString &description,
|
||||
QWidget *parent)
|
||||
bool optional, QWidget *parent)
|
||||
: QWidget(parent), m_name(name)
|
||||
{
|
||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||
@@ -58,6 +40,9 @@ DependencyItem::DependencyItem(const QString &name, const QString &description,
|
||||
m_nameLabel->setFont(nameFont);
|
||||
|
||||
m_descriptionLabel = new QLabel(QString("(%1)").arg(description));
|
||||
if (optional) {
|
||||
m_descriptionLabel->setText(m_descriptionLabel->text() + " (Optional)");
|
||||
}
|
||||
m_descriptionLabel->setWordWrap(false);
|
||||
|
||||
infoLayout->addWidget(m_nameLabel);
|
||||
@@ -92,12 +77,17 @@ DependencyItem::DependencyItem(const QString &name, const QString &description,
|
||||
layout->addLayout(actionLayout);
|
||||
}
|
||||
|
||||
void DependencyItem::setInstalled(bool installed, bool isRequired)
|
||||
void DependencyItem::setInstalled(SERVICE_AVAILABILITY availability,
|
||||
bool isRequired)
|
||||
{
|
||||
setChecking(false);
|
||||
m_availability = availability;
|
||||
|
||||
if (installed) {
|
||||
if (m_name == "Avahi Daemon") {
|
||||
const bool isInstalled = (availability != SERVICE_UNAVAILABLE);
|
||||
const bool isRunning = (availability == SERVICE_AVAILABLE);
|
||||
|
||||
if (isRunning) {
|
||||
if (m_name == "Avahi Daemon" || m_name == "Bonjour Service") {
|
||||
m_statusLabel->setText("Activated");
|
||||
} else {
|
||||
m_statusLabel->setText("Installed");
|
||||
@@ -112,6 +102,14 @@ void DependencyItem::setInstalled(bool installed, bool isRequired)
|
||||
QString("QLabel { color: %1; }").arg(COLOR_GREEN.name())));
|
||||
#endif
|
||||
m_installButton->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Not running or not installed
|
||||
if (isInstalled) {
|
||||
// Installed but not running (only meaningful for services)
|
||||
m_statusLabel->setText("Installed but not running");
|
||||
m_installButton->setText("Enable");
|
||||
} else {
|
||||
if (m_name == "Avahi Daemon") {
|
||||
m_statusLabel->setText("Not activated");
|
||||
@@ -124,19 +122,19 @@ void DependencyItem::setInstalled(bool installed, bool isRequired)
|
||||
}
|
||||
m_installButton->setText("Install");
|
||||
}
|
||||
#ifndef WIN32
|
||||
if (isRequired) {
|
||||
m_statusLabel->setStyleSheet("color: red;");
|
||||
}
|
||||
#else
|
||||
// FIXME: if we call this multiple times, the styles will keep stacking
|
||||
// and become a mess, need a better way to handle this
|
||||
m_statusLabel->setStyleSheet(mergeStyles(
|
||||
m_statusLabel,
|
||||
QString("QLabel { color: %1; }").arg(COLOR_RED.name())));
|
||||
#endif
|
||||
m_installButton->setVisible(true);
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
if (isRequired) {
|
||||
m_statusLabel->setStyleSheet("color: red;");
|
||||
}
|
||||
#else
|
||||
// FIXME: if we call this multiple times, the styles will keep stacking
|
||||
// and become a mess, need a better way to handle this
|
||||
m_statusLabel->setStyleSheet(mergeStyles(
|
||||
m_statusLabel, QString("QLabel { color: %1; }").arg(COLOR_RED.name())));
|
||||
#endif
|
||||
m_installButton->setVisible(true);
|
||||
}
|
||||
|
||||
void DependencyItem::setChecking(bool checking)
|
||||
@@ -164,13 +162,30 @@ void DependencyItem::setInstalling(bool installing)
|
||||
}
|
||||
}
|
||||
|
||||
void DependencyItem::setActivating(bool activating)
|
||||
{
|
||||
if (activating) {
|
||||
m_statusLabel->setText("Activating...");
|
||||
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::setProgress(const QString &message)
|
||||
{
|
||||
m_statusLabel->setText(message);
|
||||
m_statusLabel->setStyleSheet("color: gray;");
|
||||
}
|
||||
|
||||
void DependencyItem::onInstallClicked() { emit installRequested(m_name); }
|
||||
void DependencyItem::onInstallClicked()
|
||||
{
|
||||
emit installRequested(m_name, m_availability);
|
||||
}
|
||||
|
||||
DiagnoseWidget::DiagnoseWidget(QWidget *parent)
|
||||
: QWidget(parent), m_isExpanded(false)
|
||||
@@ -183,14 +198,15 @@ DiagnoseWidget::DiagnoseWidget(QWidget *parent)
|
||||
"Required for AirPlay, wireless devices and network service discovery");
|
||||
addDependencyItem("Apple Mobile Device Support",
|
||||
"Required for iOS device communication");
|
||||
addDependencyItem("WinFsp", "Required for mounting your device as a drive");
|
||||
addDependencyItem("WinFsp", "Required for mounting your device as a drive",
|
||||
true);
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#ifdef ENABLE_RECOVERY_DEVICE_SUPPORT
|
||||
addDependencyItem(
|
||||
"UDEV rules",
|
||||
"Required for recovery devices requires manual setup (optional)");
|
||||
addDependencyItem("UDEV rules",
|
||||
"Required for recovery devices requires manual setup",
|
||||
true);
|
||||
#endif
|
||||
addDependencyItem(
|
||||
"Avahi Daemon",
|
||||
@@ -225,8 +241,10 @@ void DiagnoseWidget::setupUI()
|
||||
[this]() { checkDependencies(false); });
|
||||
|
||||
// Toggle button
|
||||
m_toggleButton = new QPushButton("▼");
|
||||
m_toggleButton->setFixedSize(24, 24);
|
||||
m_toggleButton = new ZIconWidget(
|
||||
QIcon(":/resources/icons/MaterialSymbolsLightKeyboardArrowDown.png"),
|
||||
"Expand/Collapse");
|
||||
// m_toggleButton->setFixedSize(24, 24);
|
||||
m_toggleButton->setCheckable(true);
|
||||
connect(m_toggleButton, &QPushButton::clicked, this,
|
||||
&DiagnoseWidget::onToggleExpand);
|
||||
@@ -249,14 +267,16 @@ void DiagnoseWidget::setupUI()
|
||||
}
|
||||
|
||||
void DiagnoseWidget::addDependencyItem(const QString &name,
|
||||
const QString &description)
|
||||
const QString &description,
|
||||
bool optional)
|
||||
{
|
||||
DependencyItem *item = new DependencyItem(name, description);
|
||||
DependencyItem *item = new DependencyItem(name, description, optional);
|
||||
item->setProperty("name", name);
|
||||
item->setProperty("optional", optional);
|
||||
connect(item, &DependencyItem::installRequested, this,
|
||||
&DiagnoseWidget::onInstallRequested);
|
||||
|
||||
m_dependencyItems.append(item);
|
||||
m_dependencyItems[name] = item;
|
||||
|
||||
// Insert before the stretch
|
||||
m_itemsLayout->insertWidget(m_itemsLayout->count() - 1, item);
|
||||
@@ -274,9 +294,11 @@ void DiagnoseWidget::checkDependencies(bool autoExpand)
|
||||
QTimer::singleShot(500, [this, autoExpand]() {
|
||||
int installedCount = 0;
|
||||
int totalCount = 0;
|
||||
int optionalInstalledCount = 0;
|
||||
int optionalTotalCount = 0;
|
||||
|
||||
for (DependencyItem *item : m_dependencyItems) {
|
||||
bool installed = false;
|
||||
SERVICE_AVAILABILITY installed = SERVICE_UNAVAILABLE;
|
||||
QString itemName = item->property("name").toString();
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -297,27 +319,36 @@ void DiagnoseWidget::checkDependencies(bool autoExpand)
|
||||
}
|
||||
#endif
|
||||
|
||||
bool isRequired = true;
|
||||
#ifdef __linux__
|
||||
if (itemName == "UDEV rules") {
|
||||
isRequired = false;
|
||||
}
|
||||
#endif
|
||||
item->setInstalled(installed, isRequired);
|
||||
|
||||
if (isRequired) {
|
||||
bool isRequired = item->property("optional").toBool() == false;
|
||||
if (!isRequired) {
|
||||
++optionalTotalCount;
|
||||
if (installed == SERVICE_AVAILABILITY::SERVICE_AVAILABLE) {
|
||||
++optionalInstalledCount;
|
||||
}
|
||||
} else {
|
||||
++totalCount;
|
||||
if (installed)
|
||||
if (installed == SERVICE_AVAILABILITY::SERVICE_AVAILABLE) {
|
||||
++installedCount;
|
||||
}
|
||||
}
|
||||
|
||||
item->setInstalled(installed, isRequired);
|
||||
}
|
||||
|
||||
if (installedCount == totalCount) {
|
||||
m_summaryLabel->setText(
|
||||
QString(
|
||||
"All required dependencies are installed/activated (%1/%2)")
|
||||
.arg(installedCount)
|
||||
.arg(totalCount));
|
||||
if (optionalTotalCount != optionalInstalledCount) {
|
||||
int optionalMissingCount =
|
||||
optionalTotalCount - optionalInstalledCount;
|
||||
QString optionalText = optionalMissingCount == 1
|
||||
? "optional capability is"
|
||||
: "optional capabilities are";
|
||||
m_summaryLabel->setText(QString("%1 %2 available")
|
||||
.arg(optionalMissingCount)
|
||||
.arg(optionalText));
|
||||
} else {
|
||||
m_summaryLabel->setText(
|
||||
"All required dependencies are installed");
|
||||
}
|
||||
m_summaryLabel->setStyleSheet(
|
||||
QString("color: %1; font-weight: bold;")
|
||||
.arg(COLOR_GREEN.name()));
|
||||
@@ -342,23 +373,55 @@ void DiagnoseWidget::checkDependencies(bool autoExpand)
|
||||
|
||||
void DiagnoseWidget::onInstallRequested(const QString &name)
|
||||
{
|
||||
DependencyItem *itemToInstall = m_dependencyItems.value(name);
|
||||
if (!itemToInstall) {
|
||||
QMessageBox::warning(this, "Error",
|
||||
"Dependency item not found: " + name);
|
||||
return;
|
||||
}
|
||||
SERVICE_AVAILABILITY availability = itemToInstall->availability();
|
||||
#ifdef WIN32
|
||||
if (name == "Bonjour Service") {
|
||||
if (availability == SERVICE_AVAILABLE_BUT_NOT_RUNNING) {
|
||||
qDebug() << "Attempting to start Bonjour Service...";
|
||||
itemToInstall->setActivating(true);
|
||||
QTimer::singleShot(100, [this, itemToInstall, name]() {
|
||||
bool success = StartBonjourService();
|
||||
itemToInstall->setActivating(false);
|
||||
|
||||
if (!success) {
|
||||
QMessageBox::warning(
|
||||
this, "Activation Failed",
|
||||
"Failed to start the service. Please try to start it "
|
||||
"manually from the Services app (services.msc)");
|
||||
}
|
||||
checkDependencies(false);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
installBonjourRuntime();
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Apple Mobile Device Support") {
|
||||
DependencyItem *itemToInstall = nullptr;
|
||||
for (DependencyItem *item : m_dependencyItems) {
|
||||
if (item->property("name").toString() == name) {
|
||||
itemToInstall = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (availability == SERVICE_AVAILABLE_BUT_NOT_RUNNING) {
|
||||
qDebug() << "Attempting to start Apple Mobile Device Service...";
|
||||
itemToInstall->setActivating(true);
|
||||
QTimer::singleShot(100, [this, itemToInstall, name]() {
|
||||
bool success = StartWinFspService();
|
||||
itemToInstall->setActivating(false);
|
||||
|
||||
if (!itemToInstall)
|
||||
if (!success) {
|
||||
QMessageBox::warning(
|
||||
this, "Activation Failed",
|
||||
"Failed to start the service. Please try to start it "
|
||||
"manually from the Services app (services.msc)");
|
||||
}
|
||||
checkDependencies(false);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
itemToInstall->setInstalling(true);
|
||||
|
||||
@@ -366,45 +429,31 @@ void DiagnoseWidget::onInstallRequested(const QString &name)
|
||||
"/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();
|
||||
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",
|
||||
"This might be a "
|
||||
"permissions issue or an internal error.\n\n"
|
||||
"Details: " +
|
||||
errorOutput.trimmed());
|
||||
}
|
||||
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");
|
||||
checkDependencies(false);
|
||||
installProcess->deleteLater();
|
||||
});
|
||||
|
||||
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\"'")
|
||||
"\"%1\"' -Wait")
|
||||
.arg(scriptPath);
|
||||
|
||||
QStringList args;
|
||||
@@ -606,7 +655,11 @@ void DiagnoseWidget::onToggleExpand()
|
||||
{
|
||||
m_isExpanded = !m_isExpanded;
|
||||
m_itemsWidget->setVisible(m_isExpanded);
|
||||
m_toggleButton->setText(m_isExpanded ? "▲" : "▼");
|
||||
m_toggleButton->setIcon(
|
||||
m_isExpanded
|
||||
? QIcon(":/resources/icons/MaterialSymbolsLightKeyboardArrowUp.png")
|
||||
: QIcon(":/resources/icons/"
|
||||
"MaterialSymbolsLightKeyboardArrowDown.png"));
|
||||
m_itemsWidget->updateGeometry();
|
||||
adjustSize();
|
||||
}
|
||||
@@ -614,13 +667,7 @@ void DiagnoseWidget::onToggleExpand()
|
||||
#ifdef WIN32
|
||||
void DiagnoseWidget::installBonjourRuntime()
|
||||
{
|
||||
DependencyItem *itemToInstall = nullptr;
|
||||
for (DependencyItem *item : m_dependencyItems) {
|
||||
if (item->property("name").toString() == "Bonjour Service") {
|
||||
itemToInstall = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
DependencyItem *itemToInstall = m_dependencyItems.value("Bonjour Service");
|
||||
|
||||
if (!itemToInstall)
|
||||
return;
|
||||
|
||||
+32
-7
@@ -20,16 +20,36 @@
|
||||
#ifndef DIAGNOSE_WIDGET_H
|
||||
#define DIAGNOSE_WIDGET_H
|
||||
|
||||
#include "iDescriptor-ui.h"
|
||||
#include "iDescriptor.h"
|
||||
#include "qprocessindicator.h"
|
||||
#include <QApplication>
|
||||
#include <QCoreApplication>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFrame>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QProcess>
|
||||
#include <QProgressBar>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QScrollArea>
|
||||
#include <QStandardPaths>
|
||||
#include <QTextStream>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include "qprocessindicator.h"
|
||||
#include "service.h"
|
||||
|
||||
class DependencyItem : public QWidget
|
||||
{
|
||||
@@ -37,14 +57,17 @@ class DependencyItem : public QWidget
|
||||
|
||||
public:
|
||||
explicit DependencyItem(const QString &name, const QString &description,
|
||||
QWidget *parent = nullptr);
|
||||
void setInstalled(bool installed, bool isRequired);
|
||||
bool optional = false, QWidget *parent = nullptr);
|
||||
void setInstalled(SERVICE_AVAILABILITY availability, bool isRequired);
|
||||
void setChecking(bool checking);
|
||||
void setInstalling(bool installing);
|
||||
void setActivating(bool activating);
|
||||
void setProgress(const QString &message);
|
||||
SERVICE_AVAILABILITY availability() const { return m_availability; }
|
||||
|
||||
signals:
|
||||
void installRequested(const QString &name);
|
||||
void installRequested(const QString &name,
|
||||
SERVICE_AVAILABILITY availability);
|
||||
|
||||
private slots:
|
||||
void onInstallClicked();
|
||||
@@ -56,6 +79,7 @@ private:
|
||||
QLabel *m_statusLabel;
|
||||
QPushButton *m_installButton;
|
||||
QProcessIndicator *m_processIndicator;
|
||||
SERVICE_AVAILABILITY m_availability = SERVICE_UNAVAILABLE;
|
||||
};
|
||||
|
||||
class DiagnoseWidget : public QWidget
|
||||
@@ -74,7 +98,8 @@ private slots:
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
void addDependencyItem(const QString &name, const QString &description);
|
||||
void addDependencyItem(const QString &name, const QString &description,
|
||||
bool optional = false);
|
||||
|
||||
#ifdef WIN32
|
||||
void installBonjourRuntime();
|
||||
@@ -88,12 +113,12 @@ private:
|
||||
QVBoxLayout *m_mainLayout;
|
||||
QVBoxLayout *m_itemsLayout;
|
||||
QPushButton *m_checkButton;
|
||||
QPushButton *m_toggleButton;
|
||||
ZIconWidget *m_toggleButton;
|
||||
QLabel *m_summaryLabel;
|
||||
QWidget *m_itemsWidget;
|
||||
bool m_isExpanded;
|
||||
|
||||
QList<DependencyItem *> m_dependencyItems;
|
||||
QMap<QString, DependencyItem *> m_dependencyItems;
|
||||
};
|
||||
|
||||
#endif // DIAGNOSE_WIDGET_H
|
||||
|
||||
@@ -172,7 +172,7 @@ void DiskUsageWidget::setupUI()
|
||||
"border: none; padding: 0; margin: 0; }");
|
||||
m_galleryBar->setStyleSheet(
|
||||
"QWidget#galleryBar { background-color: #9b59b6; border: 1px solid "
|
||||
"#8e44ad; border-radius:0px; padding: 0; margin: 0; }");
|
||||
"#b36cd1; border-radius:0px; padding: 0; margin: 0; }");
|
||||
m_othersBar->setStyleSheet(
|
||||
"QWidget#othersBar { background-color: #a28729; border: 1px solid "
|
||||
"#c4a32d; border-radius:0px; padding: 0; margin: 0; }");
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
HowToConnectDialog::HowToConnectDialog(QWidget *parent) : QDialog{parent}
|
||||
{
|
||||
#ifdef WIN32
|
||||
setupWinWindow(this);
|
||||
#endif
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->setContentsMargins(0, 20, 0, 0);
|
||||
mainLayout->setSpacing(0);
|
||||
|
||||
+2
-159
@@ -34,6 +34,7 @@ using u_int64_t = uint64_t;
|
||||
#include <QThread>
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include "service.h"
|
||||
#include <mutex>
|
||||
#include <pugixml.hpp>
|
||||
#include <string>
|
||||
@@ -43,8 +44,7 @@ using u_int64_t = uint64_t;
|
||||
#define TOOL_NAME "iDescriptor"
|
||||
#define APP_LABEL "iDescriptor"
|
||||
#define APP_COPYRIGHT \
|
||||
"© 2025 The iDescriptor Project contributors. See AUTHORS for details."
|
||||
#define AFC2_SERVICE_NAME "com.apple.afc2"
|
||||
"© 2026 The iDescriptor Project contributors. See AUTHORS for details."
|
||||
#define RECOVERY_CLIENT_CONNECTION_TRIES 3
|
||||
#define APPLE_VENDOR_ID 0x05ac
|
||||
#define REPO_URL "https://github.com/iDescriptor/iDescriptor"
|
||||
@@ -264,14 +264,6 @@ void init_idescriptor_recovery_device(uint64_t ecid,
|
||||
iDescriptorInitDeviceResultRecovery &res);
|
||||
#endif
|
||||
|
||||
struct TakeScreenshotResult {
|
||||
bool success = false;
|
||||
QImage img;
|
||||
};
|
||||
|
||||
void warn(const QString &message, const QString &title = "Warning",
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
enum class AddType {
|
||||
Regular,
|
||||
Pairing,
|
||||
@@ -282,22 +274,6 @@ enum class AddType {
|
||||
|
||||
std::string parse_product_type(const std::string &productType);
|
||||
|
||||
struct MediaEntry {
|
||||
std::string name;
|
||||
bool isDir;
|
||||
};
|
||||
|
||||
struct AFCFileTree {
|
||||
std::vector<MediaEntry> entries;
|
||||
bool success;
|
||||
std::string currentPath;
|
||||
};
|
||||
|
||||
struct WirelessInitArgs {
|
||||
const QString ip;
|
||||
const QString pairing_file;
|
||||
};
|
||||
|
||||
enum class ImageCompatibility {
|
||||
Compatible, // Exact match or known compatible version
|
||||
MaybeCompatible, // Major version matches but minor doesn't
|
||||
@@ -411,139 +387,6 @@ inline QJsonObject getVersionedConfig(const QJsonObject &rootObj)
|
||||
return QJsonObject();
|
||||
}
|
||||
|
||||
inline void free_directory_listing(char **entries, size_t count)
|
||||
{
|
||||
if (!entries)
|
||||
return;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (entries[i]) {
|
||||
// FIXME: crashes on Windows
|
||||
// free(entries[i]);
|
||||
}
|
||||
}
|
||||
// FIXME: crashes on Windows
|
||||
// free(entries);
|
||||
}
|
||||
|
||||
inline int read_file(const char *filename, uint8_t **data, size_t *length)
|
||||
{
|
||||
FILE *file = fopen(filename, "rb");
|
||||
if (!file) {
|
||||
perror("Failed to open file");
|
||||
return 0;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
*length = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
*data = (uint8_t *)malloc(*length);
|
||||
if (!*data) {
|
||||
perror("Failed to allocate memory");
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fread(*data, 1, *length, file) != *length) {
|
||||
perror("Failed to read file");
|
||||
free(*data);
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct ExportResult {
|
||||
QString sourceFilePath;
|
||||
QString outputFilePath;
|
||||
bool success = false;
|
||||
bool cancelled = false;
|
||||
QString errorMessage;
|
||||
qint64 bytesTransferred = 0;
|
||||
};
|
||||
|
||||
struct ExportJobSummary {
|
||||
QUuid jobId;
|
||||
int totalItems = 0;
|
||||
int successfulItems = 0;
|
||||
int failedItems = 0;
|
||||
qint64 totalBytesTransferred = 0;
|
||||
QString destinationPath;
|
||||
bool wasCancelled = false;
|
||||
};
|
||||
|
||||
struct ImportResult;
|
||||
|
||||
template <typename ResultT> class PItem
|
||||
{
|
||||
public:
|
||||
QString sourcePathOnDevice;
|
||||
QString suggestedFileName;
|
||||
QString d_udid;
|
||||
std::function<void(const ResultT &)> callback;
|
||||
|
||||
PItem() = default;
|
||||
PItem(const QString &sourcePath, const QString &fileName,
|
||||
const QString &d_udid,
|
||||
std::function<void(const ResultT &)> callback = nullptr)
|
||||
: sourcePathOnDevice(sourcePath), suggestedFileName(fileName),
|
||||
d_udid(std::move(d_udid)), callback(std::move(callback))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct ExportItem : public PItem<ExportResult> {
|
||||
using PItem<ExportResult>::PItem;
|
||||
};
|
||||
|
||||
struct ImportItem : public PItem<ImportResult> {
|
||||
using PItem<ImportResult>::PItem;
|
||||
};
|
||||
|
||||
class JobBase
|
||||
{
|
||||
public:
|
||||
QUuid jobId;
|
||||
QString destinationPath;
|
||||
std::optional<bool> altAfc;
|
||||
std::atomic<bool> cancelRequested{false};
|
||||
QUuid statusBalloonProcessId;
|
||||
// device udid
|
||||
QString d_udid;
|
||||
|
||||
virtual ~JobBase() = default;
|
||||
};
|
||||
|
||||
template <typename ItemT> class Job : public JobBase
|
||||
{
|
||||
public:
|
||||
QList<QString> items;
|
||||
};
|
||||
|
||||
// Concrete aliases
|
||||
using ExportJob = Job<ExportItem>;
|
||||
using ImportJob = Job<ImportItem>;
|
||||
|
||||
struct ImportResult {
|
||||
QString sourceFilePath;
|
||||
QString outputFilePath;
|
||||
bool success = false;
|
||||
QString errorMessage;
|
||||
qint64 bytesTransferred = 0;
|
||||
};
|
||||
|
||||
struct ImportJobSummary {
|
||||
QUuid jobId;
|
||||
int totalItems = 0;
|
||||
int successfulItems = 0;
|
||||
int failedItems = 0;
|
||||
qint64 totalBytesTransferred = 0;
|
||||
QString destinationPath;
|
||||
bool wasCancelled = false;
|
||||
};
|
||||
|
||||
struct XmlPlistDict {
|
||||
pugi::xml_node current_node;
|
||||
|
||||
|
||||
@@ -83,7 +83,6 @@ public:
|
||||
|
||||
private:
|
||||
explicit IOManagerClient(QObject *parent = nullptr);
|
||||
void executeExportJob(ExportJob *job);
|
||||
};
|
||||
|
||||
#endif // IOMANAGERCLIENT_H
|
||||
#endif // IOMANAGERCLIENT_H
|
||||
|
||||
@@ -215,10 +215,9 @@ void NetworkDevicesToConnectWidget::setupUI()
|
||||
// Scroll area
|
||||
m_scrollArea = new QScrollArea();
|
||||
m_scrollArea->setWidgetResizable(true);
|
||||
m_scrollArea->setMinimumHeight(400);
|
||||
m_scrollArea->setMaximumHeight(400);
|
||||
m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
m_scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
m_scrollArea->setStyleSheet(
|
||||
"QScrollArea { background: transparent; border: none; }");
|
||||
/* FIXME: We need a better approach to theme awareness */
|
||||
@@ -236,10 +235,9 @@ void NetworkDevicesToConnectWidget::setupUI()
|
||||
m_deviceLayout->addStretch();
|
||||
|
||||
m_scrollArea->setWidget(m_scrollContent);
|
||||
groupLayout->addWidget(m_scrollArea);
|
||||
groupLayout->addWidget(m_scrollArea, 1);
|
||||
|
||||
mainLayout->addWidget(m_deviceGroup);
|
||||
mainLayout->addStretch();
|
||||
mainLayout->addWidget(m_deviceGroup, 1);
|
||||
}
|
||||
|
||||
void NetworkDevicesToConnectWidget::createDeviceCard(
|
||||
|
||||
@@ -19,6 +19,36 @@
|
||||
|
||||
#include "win_common.h"
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
#include <winsvc.h>
|
||||
|
||||
bool IsServiceRunning(LPCSTR serviceName)
|
||||
{
|
||||
SC_HANDLE scm = OpenSCManagerA(nullptr, nullptr, SC_MANAGER_CONNECT);
|
||||
if (!scm) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SC_HANDLE svc = OpenServiceA(scm, serviceName, SERVICE_QUERY_STATUS);
|
||||
if (!svc) {
|
||||
CloseServiceHandle(scm);
|
||||
return false;
|
||||
}
|
||||
|
||||
SERVICE_STATUS_PROCESS status;
|
||||
DWORD bytesNeeded = 0;
|
||||
bool isRunning = false;
|
||||
|
||||
if (QueryServiceStatusEx(svc, SC_STATUS_PROCESS_INFO,
|
||||
reinterpret_cast<LPBYTE>(&status), sizeof(status),
|
||||
&bytesNeeded)) {
|
||||
isRunning = (status.dwCurrentState == SERVICE_RUNNING);
|
||||
}
|
||||
|
||||
CloseServiceHandle(svc);
|
||||
CloseServiceHandle(scm);
|
||||
return isRunning;
|
||||
}
|
||||
|
||||
bool CheckRegistry(HKEY hKeyRoot, LPCSTR subKey, LPCSTR displayNameToFind)
|
||||
{
|
||||
@@ -67,36 +97,56 @@ bool CheckRegistryKeyExists(HKEY hKeyRoot, LPCSTR subKey)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsAppleMobileDeviceSupportInstalled()
|
||||
SERVICE_AVAILABILITY IsAppleMobileDeviceSupportInstalled()
|
||||
{
|
||||
bool installed = false;
|
||||
|
||||
if (CheckRegistry(HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
|
||||
"Apple Mobile Device Support")) {
|
||||
return true;
|
||||
installed = true;
|
||||
} else if (CheckRegistry(HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\WOW6432Node\\Microsoft\\Wi"
|
||||
"ndows\\CurrentVersion\\Uninstall",
|
||||
"Apple Mobile Device Support")) {
|
||||
installed = true;
|
||||
}
|
||||
if (CheckRegistry(HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\WOW6432Node\\Microsoft\\Wi"
|
||||
"ndows\\CurrentVersion\\Uninstall",
|
||||
"Apple Mobile Device Support")) {
|
||||
return true;
|
||||
|
||||
if (!installed) {
|
||||
return SERVICE_UNAVAILABLE;
|
||||
}
|
||||
return false;
|
||||
|
||||
if (IsServiceRunning("Apple Mobile Device Service")) {
|
||||
return SERVICE_AVAILABLE;
|
||||
}
|
||||
|
||||
return SERVICE_AVAILABLE_BUT_NOT_RUNNING;
|
||||
}
|
||||
|
||||
bool IsWinFspInstalled()
|
||||
SERVICE_AVAILABILITY IsWinFspInstalled()
|
||||
{
|
||||
bool installed = false;
|
||||
|
||||
if (CheckRegistry(HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
|
||||
"WinFsp 2025")) {
|
||||
return true;
|
||||
installed = true;
|
||||
} else if (CheckRegistry(HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\WOW6432Node\\Microsoft\\Wi"
|
||||
"ndows\\CurrentVersion\\Uninstall",
|
||||
"WinFsp 2025")) {
|
||||
installed = true;
|
||||
}
|
||||
if (CheckRegistry(HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\WOW6432Node\\Microsoft\\Wi"
|
||||
"ndows\\CurrentVersion\\Uninstall",
|
||||
"WinFsp 2025")) {
|
||||
return true;
|
||||
|
||||
if (!installed) {
|
||||
return SERVICE_UNAVAILABLE;
|
||||
}
|
||||
return false;
|
||||
|
||||
if (IsServiceRunning("WinFsp.Launcher")) {
|
||||
return SERVICE_AVAILABLE;
|
||||
}
|
||||
|
||||
return SERVICE_AVAILABLE_BUT_NOT_RUNNING;
|
||||
}
|
||||
|
||||
bool is_iDescriptorInstalled()
|
||||
@@ -115,12 +165,130 @@ bool is_iDescriptorInstalled()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsBonjourServiceInstalled()
|
||||
bool StartServiceByName(LPCSTR serviceName, DWORD timeoutMs = 30000)
|
||||
{
|
||||
if (CheckRegistryKeyExists(HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Apple Inc.\\Bonjour")) {
|
||||
SC_HANDLE scm = OpenSCManagerA(nullptr, nullptr, SC_MANAGER_CONNECT);
|
||||
if (!scm) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SC_HANDLE svc = OpenServiceA(scm, serviceName,
|
||||
SERVICE_START | SERVICE_QUERY_STATUS |
|
||||
SERVICE_CHANGE_CONFIG);
|
||||
if (!svc) {
|
||||
CloseServiceHandle(scm);
|
||||
return false;
|
||||
}
|
||||
|
||||
// set startup type to Automatic
|
||||
ChangeServiceConfigA(svc,
|
||||
SERVICE_NO_CHANGE, // service type
|
||||
SERVICE_AUTO_START, // start type
|
||||
SERVICE_NO_CHANGE, // error control
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr);
|
||||
|
||||
SERVICE_STATUS_PROCESS status;
|
||||
DWORD bytesNeeded = 0;
|
||||
|
||||
if (!QueryServiceStatusEx(svc, SC_STATUS_PROCESS_INFO,
|
||||
reinterpret_cast<LPBYTE>(&status), sizeof(status),
|
||||
&bytesNeeded)) {
|
||||
CloseServiceHandle(svc);
|
||||
CloseServiceHandle(scm);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (status.dwCurrentState == SERVICE_RUNNING) {
|
||||
CloseServiceHandle(svc);
|
||||
CloseServiceHandle(scm);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!StartServiceA(svc, 0, nullptr)) {
|
||||
CloseServiceHandle(svc);
|
||||
CloseServiceHandle(scm);
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD startTick = GetTickCount();
|
||||
do {
|
||||
Sleep(500);
|
||||
|
||||
if (!QueryServiceStatusEx(svc, SC_STATUS_PROCESS_INFO,
|
||||
reinterpret_cast<LPBYTE>(&status),
|
||||
sizeof(status), &bytesNeeded)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (status.dwCurrentState == SERVICE_RUNNING) {
|
||||
CloseServiceHandle(svc);
|
||||
CloseServiceHandle(scm);
|
||||
return true;
|
||||
}
|
||||
|
||||
} while (GetTickCount() - startTick < timeoutMs &&
|
||||
(status.dwCurrentState == SERVICE_START_PENDING));
|
||||
|
||||
CloseServiceHandle(svc);
|
||||
CloseServiceHandle(scm);
|
||||
return false;
|
||||
}
|
||||
|
||||
SERVICE_AVAILABILITY IsBonjourServiceInstalled()
|
||||
{
|
||||
bool installed = CheckRegistryKeyExists(HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Apple Inc.\\Bonjour");
|
||||
if (!installed) {
|
||||
return SERVICE_UNAVAILABLE;
|
||||
}
|
||||
|
||||
// Modern Bonjour service name
|
||||
if (IsServiceRunning("Bonjour Service")) {
|
||||
return SERVICE_AVAILABLE;
|
||||
}
|
||||
|
||||
// Fallback for older installs
|
||||
if (IsServiceRunning("mDNSResponder")) {
|
||||
return SERVICE_AVAILABLE;
|
||||
}
|
||||
|
||||
return SERVICE_AVAILABLE_BUT_NOT_RUNNING;
|
||||
}
|
||||
|
||||
bool StartBonjourService()
|
||||
{
|
||||
// Modern Bonjour service name
|
||||
if (IsServiceRunning("Bonjour Service")) {
|
||||
return true;
|
||||
}
|
||||
if (StartServiceByName("Bonjour Service")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Apparently it used to be called mDNSResponder
|
||||
if (IsServiceRunning("mDNSResponder")) {
|
||||
return true;
|
||||
}
|
||||
if (StartServiceByName("mDNSResponder")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StartAppleMobileDeviceService()
|
||||
{
|
||||
if (IsServiceRunning("Apple Mobile Device Service")) {
|
||||
return true;
|
||||
}
|
||||
return StartServiceByName("Apple Mobile Device Service");
|
||||
}
|
||||
|
||||
bool StartWinFspService()
|
||||
{
|
||||
if (IsServiceRunning("WinFsp.Launcher")) {
|
||||
return true;
|
||||
}
|
||||
return StartServiceByName("WinFsp.Launcher");
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#include "win_common.h"
|
||||
|
||||
// TODO: remove
|
||||
void setupTitleBar(HWND hwnd)
|
||||
{
|
||||
if (!hwnd)
|
||||
return;
|
||||
|
||||
LONG_PTR style = GetWindowLongPtr(hwnd, GWL_STYLE);
|
||||
if (style) {
|
||||
style &= ~WS_THICKFRAME; // disable resizing
|
||||
// Keep WS_CAPTION, WS_SYSMENU, WS_MINIMIZEBOX, WS_MAXIMIZEBOX
|
||||
SetWindowLongPtr(hwnd, GWL_STYLE, style);
|
||||
}
|
||||
|
||||
SetWindowPos(hwnd, nullptr, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
||||
|
||||
// Clear title text and icon
|
||||
SetWindowTextW(hwnd, L"");
|
||||
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM) nullptr);
|
||||
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM) nullptr);
|
||||
|
||||
// Make the title bar transparent (extend frame into client area)
|
||||
MARGINS margins = {0, 0, 0, 0};
|
||||
DwmExtendFrameIntoClientArea(hwnd, &margins);
|
||||
|
||||
// Disable the DWM caption drawing so the title bar has no background
|
||||
BOOL value = TRUE;
|
||||
const DWORD DWMWA_USE_IMMERSIVE_DARK_MODE_ATTR = 20;
|
||||
DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE_ATTR, &value,
|
||||
sizeof(value));
|
||||
}
|
||||
@@ -28,15 +28,20 @@
|
||||
#include <winternl.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include "../../service.h"
|
||||
|
||||
enum WIN_BACKDROP { MICA = 2, ACRYLIC = 3, MICA_ALT = 4 };
|
||||
|
||||
bool IsAppleMobileDeviceSupportInstalled();
|
||||
bool IsWinFspInstalled();
|
||||
SERVICE_AVAILABILITY IsAppleMobileDeviceSupportInstalled();
|
||||
SERVICE_AVAILABILITY IsWinFspInstalled();
|
||||
bool is_iDescriptorInstalled();
|
||||
bool IsBonjourServiceInstalled();
|
||||
SERVICE_AVAILABILITY IsBonjourServiceInstalled();
|
||||
|
||||
bool StartAppleMobileDeviceService();
|
||||
bool StartWinFspService();
|
||||
bool StartBonjourService();
|
||||
|
||||
void enableAcrylic(HWND hwnd);
|
||||
void setupTitleBar(HWND hwnd);
|
||||
void enableMica(HWND hwnd);
|
||||
|
||||
inline QString getWindowsAccentColor()
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
enum SERVICE_AVAILABILITY {
|
||||
SERVICE_AVAILABLE,
|
||||
SERVICE_AVAILABLE_BUT_NOT_RUNNING,
|
||||
SERVICE_UNAVAILABLE
|
||||
};
|
||||
@@ -56,14 +56,19 @@ void WelcomeWidget::setupUI()
|
||||
|
||||
QHBoxLayout *imageAndWirelessDevicesLayout = new QHBoxLayout();
|
||||
|
||||
m_imageLabel = new QLabel();
|
||||
m_imageLabel = new ResponsiveQLabel();
|
||||
m_imageLabel->setPixmap(QPixmap(":/resources/connect.png"));
|
||||
m_imageLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
m_imageLabel->setMinimumSize(QSize(400, 0));
|
||||
m_imageLabel->setMinimumWidth(200);
|
||||
|
||||
m_imageLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
m_imageLabel->setStyleSheet("background: transparent; border: none;");
|
||||
m_imageLabel->setAlignment(Qt::AlignCenter);
|
||||
|
||||
imageAndWirelessDevicesLayout->addSpacing(40);
|
||||
imageAndWirelessDevicesLayout->addWidget(m_imageLabel, 0, Qt::AlignHCenter);
|
||||
imageAndWirelessDevicesLayout->addSpacing(40);
|
||||
|
||||
QVBoxLayout *explorerWithIntructionLayout = new QVBoxLayout();
|
||||
NetworkDevicesToConnectWidget *networkDevicesWidget =
|
||||
new NetworkDevicesToConnectWidget();
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ private:
|
||||
QVBoxLayout *m_mainLayout;
|
||||
ZLabel *m_titleLabel;
|
||||
ZLabel *m_subtitleLabel;
|
||||
QLabel *m_imageLabel;
|
||||
ResponsiveQLabel *m_imageLabel;
|
||||
ZLabel *m_instructionLabel;
|
||||
ZLabel *m_githubLabel;
|
||||
ZLabel *m_howToConnectLabel;
|
||||
|
||||
Reference in New Issue
Block a user