feat: introduce Rust into the codebase

- Rewrite DeviceMonitorThread in Rust (idevice_monitor)
- Enhanced ServiceManager to check for application shutdown before executing operations.
- Introduced auto-connect feature for wireless devices in SettingsManager.
- Updated SettingsWidget to include UI elements for auto-connecting wireless devices.
- Make import button work in gallerywidget
This commit is contained in:
uncor3
2026-03-21 01:50:37 +00:00
parent 6cd2c5e5ba
commit 101febb632
21 changed files with 1051 additions and 241 deletions
+23
View File
@@ -317,6 +317,27 @@ endif()
# Make sure idevice_cpp depends on the Rust library being built
add_dependencies(idevice_cpp idevice_rs_build)
set(RUST_MONITOR_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/rust)
set(RUST_MONITOR_LIB_PATH ${RUST_MONITOR_SOURCE_DIR}/target/debug/libidevice_monitor.a)
add_custom_command(
OUTPUT ${RUST_MONITOR_LIB_PATH}
COMMAND ${CARGO_EXECUTABLE} build --manifest-path ${RUST_MONITOR_SOURCE_DIR}/Cargo.toml
WORKING_DIRECTORY ${RUST_MONITOR_SOURCE_DIR}
COMMENT "Building Rust device monitor library"
VERBATIM
)
add_custom_target(rust_device_monitor_build DEPENDS ${RUST_MONITOR_LIB_PATH})
add_library(idevice_monitor STATIC IMPORTED GLOBAL)
set_target_properties(idevice_monitor PROPERTIES
IMPORTED_LOCATION "${RUST_MONITOR_LIB_PATH}"
)
add_dependencies(idevice_monitor rust_device_monitor_build)
target_link_libraries(iDescriptor PRIVATE
Qt6::Widgets
Qt6::Multimedia
@@ -347,6 +368,7 @@ target_link_libraries(iDescriptor PRIVATE
# ZToast
${IDEVICE_IMPLEMENTATION_LIBS}
SQLite::SQLite3
idevice_monitor
)
if(ENABLE_RECOVERY_DEVICE_SUPPORT)
@@ -363,6 +385,7 @@ target_include_directories(iDescriptor PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/lib/zupdater/src
# System includes last
${IDEVICE_IMPLEMENTATION_INCLUDES}
${RUST_MONITOR_SOURCE_DIR}/include
)
+16 -19
View File
@@ -18,16 +18,6 @@
*/
#include "appcontext.h"
#include "devicemonitor.h"
#include "iDescriptor.h"
#include "mainwindow.h"
#include "settingsmanager.h"
#include <QDebug>
#include <QMessageBox>
#include <QThreadPool>
#include <QTimer>
#include <QUuid>
#include <thread>
AppContext *AppContext::sharedInstance()
{
@@ -186,7 +176,7 @@ void AppContext::cachePairedDevices()
}
void AppContext::addDevice(iDescriptor::Uniq uniq,
DeviceMonitorThread::IdeviceConnectionType conn_type,
iDescriptor::IdeviceConnectionType conn_type,
AddType addType, QString wifiMacAddress,
QString ipAddress)
{
@@ -524,8 +514,8 @@ void AppContext::tryToConnectToNetworkDevice(const NetworkDevice &device)
QMetaObject::invokeMethod(
AppContext::sharedInstance(), "addDevice", Qt::QueuedConnection,
Q_ARG(iDescriptor::Uniq, iDescriptor::Uniq(device.macAddress, true)),
Q_ARG(DeviceMonitorThread::IdeviceConnectionType,
DeviceMonitorThread::CONNECTION_NETWORK),
Q_ARG(iDescriptor::IdeviceConnectionType,
iDescriptor::CONNECTION_NETWORK),
Q_ARG(AddType, AddType::Wireless), Q_ARG(QString, device.macAddress),
Q_ARG(QString, device.address));
}
@@ -552,6 +542,7 @@ void AppContext::freeDevice(iDescriptorDevice *device)
lockdownd_client_free(device->lockdown);
idevice_provider_free(device->provider);
delete device;
device = nullptr;
}
void AppContext::handlePairing(iDescriptorInitDeviceResult *initResult,
@@ -559,10 +550,16 @@ void AppContext::handlePairing(iDescriptorInitDeviceResult *initResult,
{
qDebug() << "[handlePairing] for device" << uniq;
emit initFailed(uniq);
if (initResult->error &&
initResult->error->code == PairingDialogResponsePending ||
initResult->error->code == InvalidHostID ||
initResult->error->code == PasswordProtected) {
if (!initResult || !initResult->error) {
qDebug() << "[handlePairing] initResult->error is null for" << uniq
<< "- skipping pairing handling";
return;
}
const auto code = initResult->error->code;
if (code == PairingDialogResponsePending || code == InvalidHostID ||
code == PasswordProtected) {
if (addType == AddType::Regular) {
m_pendingDevices.append(uniq);
emit devicePasswordProtected(uniq);
@@ -687,8 +684,8 @@ void AppContext::handlePairing(iDescriptorInitDeviceResult *initResult,
AppContext::sharedInstance(), "addDevice",
Qt::QueuedConnection,
Q_ARG(iDescriptor::Uniq, uniq),
Q_ARG(DeviceMonitorThread::IdeviceConnectionType,
DeviceMonitorThread::IdeviceConnectionType::
Q_ARG(iDescriptor::IdeviceConnectionType,
iDescriptor::IdeviceConnectionType::
CONNECTION_NETWORK),
Q_ARG(AddType, AddType::Regular));
ok = true;
+10 -3
View File
@@ -20,11 +20,18 @@
#ifndef APPCONTEXT_H
#define APPCONTEXT_H
#include "devicemonitor.h"
#include "devicesidebarwidget.h"
#include "heartbeat.h"
#include "iDescriptor.h"
#include "mainwindow.h"
#include "settingsmanager.h"
#include <QDebug>
#include <QMessageBox>
#include <QObject>
#include <QThreadPool>
#include <QTimer>
#include <QUuid>
#include <thread>
class AppContext : public QObject
{
@@ -96,8 +103,8 @@ signals:
public slots:
void removeDevice(iDescriptor::Uniq uniq);
void addDevice(iDescriptor::Uniq udid,
DeviceMonitorThread::IdeviceConnectionType connType,
AddType addType, QString wifiMacAddress = QString(),
iDescriptor::IdeviceConnectionType connType, AddType addType,
QString wifiMacAddress = QString(),
QString ipAddress = QString());
void heartbeatFailed(const QString &macAddress, int tries);
// void heartbeatThreadExited(const QString &macAddress);
+41 -9
View File
@@ -42,7 +42,7 @@ DeviceManagerWidget::DeviceManagerWidget(QWidget *parent)
DeviceSelection(device->udid));
});
emit updateNoDevicesConnected();
updateUI();
});
connect(AppContext::sharedInstance(), &AppContext::deviceRemoved, this,
@@ -52,19 +52,19 @@ DeviceManagerWidget::DeviceManagerWidget(QWidget *parent)
if (!devices.isEmpty())
AppContext::sharedInstance()->setCurrentDeviceSelection(
DeviceSelection(devices.first()->udid));
emit updateNoDevicesConnected();
updateUI();
});
connect(AppContext::sharedInstance(), &AppContext::devicePairPending, this,
[this](const QString &udid) {
addPendingDevice(udid, false);
emit updateNoDevicesConnected();
updateUI();
});
connect(AppContext::sharedInstance(), &AppContext::devicePasswordProtected,
this, [this](const QString &udid) {
addPendingDevice(udid, true);
emit updateNoDevicesConnected();
updateUI();
});
connect(AppContext::sharedInstance(), &AppContext::devicePaired, this,
@@ -77,27 +77,27 @@ DeviceManagerWidget::DeviceManagerWidget(QWidget *parent)
// DeviceSelection(device->udid));
// });
emit updateNoDevicesConnected();
updateUI();
});
#ifdef ENABLE_RECOVERY_DEVICE_SUPPORT
connect(AppContext::sharedInstance(), &AppContext::recoveryDeviceAdded,
this, [this](const iDescriptorRecoveryDevice *recoveryDeviceInfo) {
addRecoveryDevice(recoveryDeviceInfo);
emit updateNoDevicesConnected();
updateUI();
});
connect(AppContext::sharedInstance(), &AppContext::recoveryDeviceRemoved,
this, [this](uint64_t ecid) {
removeRecoveryDevice(ecid);
emit updateNoDevicesConnected();
updateUI();
});
#endif
connect(AppContext::sharedInstance(), &AppContext::devicePairingExpired,
this, [this](const QString &udid) {
removePendingDevice(udid);
emit updateNoDevicesConnected();
updateUI();
});
onDeviceSelectionChanged(
AppContext::sharedInstance()->getCurrentDeviceSelection());
@@ -109,6 +109,11 @@ void DeviceManagerWidget::setupUI()
m_mainLayout->setContentsMargins(0, 0, 0, 0);
m_mainLayout->setSpacing(0);
m_noDevicesLabel = new QLabel("This is where devices will appear", this);
m_noDevicesLabel->setFont(QFont("", 20, QFont::Bold));
m_noDevicesLabel->setAlignment(Qt::AlignCenter);
m_noDevicesLabel->setWordWrap(true);
// Create sidebar
m_sidebar = new DeviceSidebarWidget();
@@ -353,4 +358,31 @@ void DeviceManagerWidget::onDeviceSelectionChanged(
}
break;
}
}
}
void DeviceManagerWidget::updateUI()
{
emit updateNoDevicesConnected();
m_noDevicesLabel->setVisible(
AppContext::sharedInstance()->noDevicesConnected());
}
void DeviceManagerWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
if (!m_noDevicesLabel)
return;
const int margin = 10;
int maxWidth = qMax(0, width() - 2 * margin);
m_noDevicesLabel->setMaximumWidth(maxWidth);
m_noDevicesLabel->adjustSize();
int x = (width() - m_noDevicesLabel->width()) / 2;
int y = (height() - m_noDevicesLabel->height()) / 2;
x = qMax(margin, x);
y = qMax(margin, y);
m_noDevicesLabel->move(x, y);
}
+7 -1
View File
@@ -20,6 +20,8 @@
#ifndef DEVICEMANAGERWIDGET_H
#define DEVICEMANAGERWIDGET_H
class DeviceMenuWidget;
#include "devicemenuwidget.h"
#include "devicependingwidget.h"
#include "devicesidebarwidget.h"
@@ -52,9 +54,12 @@ signals:
private slots:
void onDeviceSelectionChanged(const DeviceSelection &selection);
protected:
void resizeEvent(QResizeEvent *event) override;
private:
void setupUI();
void updateUI();
void addDevice(const iDescriptorDevice *device);
void removeDevice(const std::string &uuid);
#ifdef ENABLE_RECOVERY_DEVICE_SUPPORT
@@ -83,6 +88,7 @@ private:
#endif
std::string m_currentDeviceUuid;
QLabel *m_noDevicesLabel = nullptr;
};
#endif // DEVICEMANAGERWIDGET_H
-126
View File
@@ -1,126 +0,0 @@
#ifndef DEVICEMONITOR_H
#define DEVICEMONITOR_H
#include <QDebug>
#include <QThread>
#include <idevice++/bindings.hpp>
#include <idevice++/core_device_proxy.hpp>
#include <idevice++/dvt/remote_server.hpp>
#include <idevice++/dvt/screenshot.hpp>
#include <idevice++/ffi.hpp>
#include <idevice++/provider.hpp>
#include <idevice++/readwrite.hpp>
#include <idevice++/rsd.hpp>
#include <idevice++/usbmuxd.hpp>
using namespace IdeviceFFI;
class DeviceMonitorThread : public QThread
{
Q_OBJECT
public:
DeviceMonitorThread(QObject *parent = nullptr) : QThread(parent) {}
void run() override
{
while (!isInterruptionRequested()) {
// Create connection
UsbmuxdConnectionHandle *usbmuxd_conn;
IdeviceFfiError *err =
idevice_usbmuxd_new_default_connection(0, &usbmuxd_conn);
if (err) {
idevice_error_free(err);
QThread::msleep(1000); // Wait before retry
continue;
}
UsbmuxdListenerHandle *listener;
err = idevice_usbmuxd_listen(usbmuxd_conn, &listener);
if (err) {
idevice_usbmuxd_connection_free(usbmuxd_conn);
idevice_error_free(err);
QThread::msleep(1000);
continue;
}
// Monitor loop
while (!isInterruptionRequested()) {
bool connect = false;
UsbmuxdDeviceHandle *device;
uint32_t disconnect_id;
err = idevice_usbmuxd_listener_next(listener, &connect, &device,
&disconnect_id);
qDebug() << "Listening for device connections..." << err
<< connect;
if (err) {
// Check if it's just the stream ending (no devices)
if (err->code == -1) { // Socket error code
qDebug() << "Listener stream ended, reconnecting...";
idevice_error_free(err);
break; // Break inner loop to reconnect
} else {
qDebug() << "Real error:" << err->message;
idevice_error_free(err);
break;
}
}
if (connect) {
const char *udid = idevice_usbmuxd_device_get_udid(device);
uint32_t device_id =
idevice_usbmuxd_device_get_device_id(device);
uint8_t conn_type =
idevice_usbmuxd_device_get_connection_type(device);
// Skip network devices (same as original callback)
if (conn_type == CONNECTION_NETWORK) {
qDebug() << "Skipping network device:" << QString(udid);
idevice_usbmuxd_device_free(device);
continue;
}
deviceMap[device_id] = QString(udid);
qDebug()
<< "[DeviceMonitor] Device connected:" << QString(udid);
emit deviceEvent(IDEVICE_DEVICE_ADD, QString(udid),
conn_type, AddType::Regular);
idevice_usbmuxd_device_free(device);
} else {
QString udid = deviceMap.value(disconnect_id, QString());
if (!udid.isEmpty()) {
emit deviceEvent(IDEVICE_DEVICE_REMOVE, udid,
CONNECTION_USB, AddType::Regular);
deviceMap.remove(disconnect_id);
} else {
qDebug() << "Unknown device disconnected please report "
"this issue: "
<< disconnect_id;
}
}
}
// Cleanup before reconnecting
idevice_usbmuxd_listener_handle_free(listener);
idevice_usbmuxd_connection_free(usbmuxd_conn);
}
}
enum IdeviceEventType {
IDEVICE_DEVICE_ADD = 1,
IDEVICE_DEVICE_REMOVE = 2,
IDEVICE_DEVICE_PAIRED = 3
};
enum IdeviceConnectionType { CONNECTION_USB = 1, CONNECTION_NETWORK = 2 };
enum AddType { Regular = 0, Pairing = 1 };
signals:
void deviceEvent(int event, const QString &udid, int conn_type,
int addType);
private:
QMap<uint32_t, QString> deviceMap;
};
#endif // DEVICEMONITOR_H
+20
View File
@@ -174,6 +174,9 @@ void GalleryWidget::setupControlsLayout()
connect(m_backButton, &ZIconWidget::clicked, this,
&GalleryWidget::onBackToAlbums);
connect(m_importButton, &QPushButton::clicked, this,
&GalleryWidget::handleImport);
// Add widgets to layout
m_controlsLayout->addWidget(m_backButton);
m_controlsLayout->addWidget(m_importButton);
@@ -700,6 +703,23 @@ void GalleryWidget::onPhotoContextMenu(const QPoint &pos)
contextMenu.exec(m_listView->viewport()->mapToGlobal(pos));
}
void GalleryWidget::handleImport()
{
QStringList filePaths = QFileDialog::getOpenFileNames(
this, "Select Photos to Import",
QStandardPaths::writableLocation(QStandardPaths::PicturesLocation),
"Images (*.jpg *.jpeg *.png *.heic);;All Files (*)");
if (filePaths.isEmpty()) {
return;
}
qDebug() << "Selected files for import:" << filePaths;
PhotoImportDialog dialog(filePaths, this);
dialog.exec();
}
GalleryWidget::~GalleryWidget()
{
qDebug() << "GalleryWidget destructor called";
+3
View File
@@ -23,8 +23,10 @@
#include "exportalbum.h"
#include "iDescriptor-ui.h"
#include "iDescriptor.h"
#include "photoimportdialog.h"
#include "photomodel.h"
#include "zloadingwidget.h"
#include <QStringList>
#include <QWidget>
QT_BEGIN_NAMESPACE
@@ -72,6 +74,7 @@ private:
void loadAlbumThumbnailAsync(const QString &albumPath, QStandardItem *item);
void onPhotoContextMenu(const QPoint &pos);
PhotoModel::FilterType getCurrentFilterType() const;
void handleImport();
const iDescriptorDevice *m_device;
bool m_loaded = false;
+11 -3
View File
@@ -26,7 +26,6 @@
#include <QThread>
#include <QtCore/QObject>
// #include "idevice.h"
#include <idevice++/bindings.hpp>
#include <idevice++/core_device_proxy.hpp>
#include <idevice++/diagnostics_relay.hpp>
@@ -67,7 +66,6 @@
#define POSSIBLE_ROOT "../../../../"
#define IDEVICE_DEVICE_VERSION(maj, min, patch) \
((((maj) & 0xFF) << 16) | (((min) & 0xFF) << 8) | ((patch) & 0xFF))
#include "devicemonitor.h"
#include "iDescriptor-utils.h"
#ifdef ENABLE_RECOVERY_DEVICE_SUPPORT
#include <libirecovery.h>
@@ -98,6 +96,16 @@
#define LOCKDOWN_PATH qgetenv("PROGRAMDATA") + "/Apple/Lockdown"
#endif
// idevice_monitor (rust ffi)
extern "C" {
#include "idevice_monitor.h"
}
namespace iDescriptor
{
enum IdeviceConnectionType { CONNECTION_USB = 1, CONNECTION_NETWORK = 2 };
}
using namespace IdeviceFFI;
struct BatteryInfo {
QString health;
uint64_t cycleCount;
@@ -231,7 +239,7 @@ class HeartbeatThread;
struct iDescriptorDevice {
std::string udid;
DeviceMonitorThread::IdeviceConnectionType conn_type;
iDescriptor::IdeviceConnectionType conn_type;
IdeviceProviderHandle *provider;
DeviceInfo deviceInfo;
AfcClientHandle *afcClient;
+14
View File
@@ -166,6 +166,14 @@ InstalledAppsWidget::InstalledAppsWidget(const iDescriptorDevice *device,
InstalledAppsWidget::~InstalledAppsWidget()
{
if (m_watcher) {
m_watcher->cancel();
m_watcher->waitForFinished();
}
if (m_containerWatcher) {
m_containerWatcher->cancel();
m_containerWatcher->waitForFinished();
}
cleanupHouseArrestClients();
if (m_springboardClient) {
springboard_services_free(m_springboardClient);
@@ -290,6 +298,12 @@ void InstalledAppsWidget::fetchInstalledApps()
// todo maybe clear m_watcher ?
QFuture<QVariantMap> future = QtConcurrent::run([this]() -> QVariantMap {
QVariantMap result;
if (QCoreApplication::closingDown() || !m_device) {
return result;
}
std::lock_guard<std::recursive_mutex> lock(m_device->mutex);
QVariantList apps;
// fetch icon from springboard service
IdeviceFfiError *err = nullptr;
+33 -63
View File
@@ -73,6 +73,30 @@ void handleCallbackRecovery(const irecv_device_event_t *event, void *userData)
irecv_device_event_context_t context;
#endif
void handleCallback(const IdeviceEvent *e)
{
QString udid = QString::fromUtf8(e->udid);
qDebug() << "Device event: "
<< (e->kind == 1 ? "Connected" : "Disconnected")
<< ", UDID: " << udid;
free(e->udid);
bool isConnected = (e->kind == 1);
if (isConnected) {
QMetaObject::invokeMethod(
AppContext::sharedInstance(), "addDevice", Qt::QueuedConnection,
Q_ARG(iDescriptor::Uniq, iDescriptor::Uniq(udid)),
Q_ARG(iDescriptor::IdeviceConnectionType,
static_cast<iDescriptor::IdeviceConnectionType>(
iDescriptor::CONNECTION_USB)),
Q_ARG(AddType, AddType::Regular));
} else {
QMetaObject::invokeMethod(
AppContext::sharedInstance(), "removeDevice", Qt::QueuedConnection,
Q_ARG(iDescriptor::Uniq, iDescriptor::Uniq(udid)));
}
}
MainWindow *MainWindow::sharedInstance()
{
static MainWindow instance;
@@ -333,65 +357,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
m_updater->checkForUpdates();
});
m_deviceMonitor = new DeviceMonitorThread(this);
connect(
m_deviceMonitor, &DeviceMonitorThread::deviceEvent, this,
[this](int event, const QString &udid, int conn_type, int addType) {
// Handle device connection
switch (event) {
case DeviceMonitorThread::IDEVICE_DEVICE_ADD: {
/* never gets fired on any platform */
if (conn_type == DeviceMonitorThread::CONNECTION_NETWORK) {
return;
}
qDebug() << "Device event received: " << udid;
QMetaObject::invokeMethod(
AppContext::sharedInstance(), "addDevice",
Qt::QueuedConnection,
Q_ARG(iDescriptor::Uniq, iDescriptor::Uniq(udid)),
Q_ARG(
DeviceMonitorThread::IdeviceConnectionType,
static_cast<DeviceMonitorThread::IdeviceConnectionType>(
conn_type)),
Q_ARG(AddType, AddType::Regular));
break;
}
case DeviceMonitorThread::IDEVICE_DEVICE_REMOVE: {
QMetaObject::invokeMethod(
AppContext::sharedInstance(), "removeDevice",
Qt::QueuedConnection,
Q_ARG(iDescriptor::Uniq, iDescriptor::Uniq(udid)));
break;
}
case DeviceMonitorThread::IDEVICE_DEVICE_PAIRED: {
/* never gets fired on any platform */
if (conn_type == DeviceMonitorThread::CONNECTION_NETWORK) {
return;
}
qDebug() << "Device paired: " << udid;
QMetaObject::invokeMethod(
AppContext::sharedInstance(), "addDevice",
Qt::QueuedConnection, Q_ARG(QString, udid),
Q_ARG(
DeviceMonitorThread::IdeviceConnectionType,
static_cast<DeviceMonitorThread::IdeviceConnectionType>(
conn_type)),
Q_ARG(AddType, AddType::Pairing));
break;
}
default:
qDebug() << "Unhandled event: " << event;
}
});
/* If a device is connected before starting the app on slower machines ui
* takes a lot of time to render so delay the monitoring a bit */
QTimer::singleShot(std::chrono::seconds(1), this,
[this]() { m_deviceMonitor->start(); });
[this]() { idevice_event_subscribe(handleCallback); });
// ═══════════════════════════════════════════════════════════════════════
// Upgrade to wireless when a "WIRED" device is removed
@@ -404,11 +373,12 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
return;
qDebug() << "Upgrading device to wireless connection for UDID"
<< QString::fromStdString(udid);
// FIXME: ignore iOS 15 and lower
QMetaObject::invokeMethod(
AppContext::sharedInstance(), "addDevice", Qt::QueuedConnection,
Q_ARG(iDescriptor::Uniq, iDescriptor::Uniq(udid, wasWireless)),
Q_ARG(DeviceMonitorThread::IdeviceConnectionType,
DeviceMonitorThread::CONNECTION_NETWORK),
Q_ARG(iDescriptor::IdeviceConnectionType,
iDescriptor::CONNECTION_NETWORK),
Q_ARG(AddType, AddType::UpgradeToWireless),
Q_ARG(QString, QString::fromStdString(wifiMacAddress)),
Q_ARG(QString, QString::fromStdString(ipAddress)));
@@ -420,6 +390,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
connect(NetworkDeviceProvider::sharedInstance(),
&NetworkDeviceProvider::deviceAdded, this,
[this](const NetworkDevice &device) {
if (!SettingsManager::sharedInstance()
->autoConnectWirelessDevices())
return;
if (auto existingDevice =
AppContext::sharedInstance()->getDeviceByMacAddress(
device.macAddress)) {
@@ -442,8 +415,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
AppContext::sharedInstance(), "addDevice",
Q_ARG(iDescriptor::Uniq,
iDescriptor::Uniq(device.macAddress, true)),
Q_ARG(DeviceMonitorThread::IdeviceConnectionType,
DeviceMonitorThread::CONNECTION_NETWORK),
Q_ARG(iDescriptor::IdeviceConnectionType,
iDescriptor::CONNECTION_NETWORK),
Q_ARG(AddType, AddType::Wireless),
Q_ARG(QString, device.macAddress),
Q_ARG(QString, device.address));
@@ -528,9 +501,6 @@ MainWindow::~MainWindow()
#ifdef ENABLE_RECOVERY_DEVICE_SUPPORT
irecv_device_event_unsubscribe(context);
#endif
m_deviceMonitor->requestInterruption();
m_deviceMonitor->wait();
delete m_deviceMonitor;
delete m_updater;
// sleep(2);
}
-2
View File
@@ -21,7 +21,6 @@
#define MAINWINDOW_H
#include "ZDownloader.h"
#include "ZUpdater.h"
#include "devicemonitor.h"
#include "iDescriptor-ui.h"
#include "iDescriptor.h"
#include "ztabwidget.h"
@@ -51,7 +50,6 @@ private:
DeviceManagerWidget *m_deviceManager;
QStackedWidget *m_mainStackedWidget;
QLabel *m_connectedDeviceCountLabel;
DeviceMonitorThread *m_deviceMonitor;
QLabel *m_titleLabel;
QPushButton *m_minBtn;
QPushButton *m_maxBtn;
+730
View File
@@ -0,0 +1,730 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "aws-lc-rs"
version = "1.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc"
dependencies = [
"aws-lc-sys",
"zeroize",
]
[[package]]
name = "aws-lc-sys"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa7e52a4c5c547c741610a2c6f123f3881e409b714cd27e6798ef020c514f0a"
dependencies = [
"cc",
"cmake",
"dunce",
"fs_extra",
]
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bytes"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "cc"
version = "1.2.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cmake"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d"
dependencies = [
"cc",
]
[[package]]
name = "deranged"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
dependencies = [
"powerfmt",
]
[[package]]
name = "dunce"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "fs_extra"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
name = "futures"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-executor"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
[[package]]
name = "futures-macro"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
[[package]]
name = "futures-task"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
[[package]]
name = "futures-util"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"slab",
]
[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "idevice"
version = "0.1.53"
dependencies = [
"base64",
"futures",
"plist",
"plist-macro",
"rustls",
"serde",
"thiserror",
"tokio",
"tokio-rustls",
"tracing",
]
[[package]]
name = "idevice_monitor"
version = "0.1.0"
dependencies = [
"futures",
"idevice",
"tokio",
]
[[package]]
name = "indexmap"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "jobserver"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [
"getrandom 0.3.4",
"libc",
]
[[package]]
name = "libc"
version = "0.2.183"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
[[package]]
name = "memchr"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "mio"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
dependencies = [
"libc",
"wasi",
"windows-sys 0.61.2",
]
[[package]]
name = "num-conv"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "plist"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07"
dependencies = [
"base64",
"indexmap",
"quick-xml",
"serde",
"time",
]
[[package]]
name = "plist-macro"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82c39e020f2d8d361e91a434cfe26bc7c3e21ae54414379816d0110f3f7ca121"
dependencies = [
"plist",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quick-xml"
version = "0.38.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.17",
"libc",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "rustls"
version = "0.23.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4"
dependencies = [
"aws-lc-rs",
"once_cell",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pki-types"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
dependencies = [
"zeroize",
]
[[package]]
name = "rustls-webpki"
version = "0.103.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
dependencies = [
"aws-lc-rs",
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "socket2"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
dependencies = [
"libc",
"windows-sys 0.61.2",
]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "time"
version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde_core",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "time-macros"
version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "tokio"
version = "1.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
dependencies = [
"bytes",
"libc",
"mio",
"pin-project-lite",
"socket2",
"tokio-macros",
"windows-sys 0.61.2",
]
[[package]]
name = "tokio-macros"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-rustls"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
dependencies = [
"rustls",
"tokio",
]
[[package]]
name = "tracing"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
[[package]]
name = "zeroize"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "idevice_monitor"
version = "0.1.0"
edition = "2024"
[lib]
name = "idevice_monitor"
crate-type = ["staticlib"]
[dependencies]
tokio = { version = "1", features = ["rt", "time", "sync", "macros"] }
futures = "0.3"
idevice = { path = "../../lib/idevice-rs/idevice", features = ["usbmuxd"] }
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
int kind; // 1 = connected, 2 = disconnected
char *udid;
} IdeviceEvent;
typedef void (*IdeviceEventCallback)(const IdeviceEvent *event);
void idevice_event_subscribe(IdeviceEventCallback cb);
#ifdef __cplusplus
}
#endif
+77
View File
@@ -0,0 +1,77 @@
use futures::StreamExt;
use idevice::usbmuxd::{Connection, UsbmuxdConnection, UsbmuxdListenEvent};
use std::collections::HashMap;
use std::os::raw::c_char;
use std::{ffi::CString, thread};
use tokio::runtime::Builder;
#[repr(C)]
pub struct IdeviceEvent {
pub kind: i32, // 1 = connected, 2 = disconnected
pub udid: *mut c_char,
}
pub type IdeviceEventCallback = extern "C" fn(event: *const IdeviceEvent);
fn make_event(kind: i32, udid: &str) -> IdeviceEvent {
let c_udid = CString::new(udid).unwrap_or_default();
IdeviceEvent {
kind,
udid: c_udid.into_raw(),
}
}
#[unsafe(no_mangle)]
pub extern "C" fn idevice_event_subscribe(cb: IdeviceEventCallback) {
thread::spawn(move || {
let rt = Builder::new_current_thread().enable_all().build().unwrap();
rt.block_on(async move {
let mut device_map: HashMap<u32, String> = HashMap::new();
loop {
match UsbmuxdConnection::default().await {
Ok(mut uc) => match uc.listen().await {
Ok(mut stream) => {
while let Some(evt) = stream.next().await {
match evt {
Ok(UsbmuxdListenEvent::Connected(d)) => {
// ignore non-USB connections
if d.connection_type != Connection::Usb {
continue;
}
let udid = d.udid.clone();
let device_id = d.device_id;
device_map.insert(device_id, udid.clone());
let ev = make_event(1, &udid);
cb(&ev);
}
Ok(UsbmuxdListenEvent::Disconnected(device_id)) => {
if let Some(udid) = device_map.remove(&device_id) {
let ev = make_event(2, &udid);
cb(&ev);
} else {
eprintln!("Unknown device disconnected: {device_id}");
}
}
Err(e) => {
eprintln!("usbmuxd listen error: {e:?}");
break;
}
}
}
}
Err(e) => eprintln!("Failed to start usbmuxd listen: {e:?}"),
},
// Safe to ignore as it likely means usbmuxd isn't running
// usbmuxd is killed when the last device disconnects
Err(_) => {}
}
tokio::time::sleep(std::time::Duration::from_millis(2000)).await;
}
});
});
}
+10 -15
View File
@@ -21,6 +21,7 @@
#define SERVICEMANAGER_H
#include "iDescriptor.h"
#include <QCoreApplication>
#include <QDebug>
#include <QFuture>
#include <functional>
@@ -31,9 +32,7 @@
* @brief Centralized manager for device service operations with thread safety
*
* This class provides thread-safe wrappers for all device operations to prevent
* crashes when devices are unplugged during active operations. It uses a
* per-device recursive mutex to ensure that device cleanup waits for all
* operations to complete.
* crashes when devices are unplugged during active operations
*/
class ServiceManager
{
@@ -44,7 +43,7 @@ public:
std::function<T(AfcClientHandle *)> operation,
std::optional<AfcClientHandle *> altAfc = std::nullopt)
{
if (!device) {
if (QCoreApplication::closingDown() || !device) {
return T{}; // Return default-constructed value for the type
}
@@ -73,7 +72,7 @@ public:
std::function<T()> operation,
std::optional<AfcClientHandle *> altAfc = std::nullopt)
{
if (!device) {
if (QCoreApplication::closingDown() || !device) {
qDebug() << "[executeOperation] Device or mutex is null";
return T{}; // Return default-constructed value for the type
}
@@ -101,7 +100,7 @@ public:
std::function<T()> operation, T failureValue,
std::optional<AfcClientHandle *> altAfc = std::nullopt)
{
if (!device) {
if (QCoreApplication::closingDown() || !device) {
return failureValue;
}
@@ -126,7 +125,7 @@ public:
std::function<void()> operation,
std::optional<AfcClientHandle *> altAfc = std::nullopt)
{
if (!device) {
if (QCoreApplication::closingDown() || !device) {
return;
}
@@ -151,7 +150,7 @@ public:
std::function<void()> operation,
std::optional<AfcClientHandle *> altAfc = std::nullopt)
{
if (!device) {
if (QCoreApplication::closingDown() || !device) {
return;
}
@@ -177,14 +176,12 @@ public:
AfcFileHandle *handle)
{
try {
if (!device) {
// FIXME: we have to free error
if (QCoreApplication::closingDown() || !device) {
return new IdeviceFfiError{1, "DEVICE_OR_MUTEX_IS_NULL"};
}
std::lock_guard<std::recursive_mutex> lock(device->mutex);
// Double-check device is still valid after acquiring lock
if (!device->afcClient) {
return new IdeviceFfiError{1, "AFC_CLIENT_IS_NULL"};
}
@@ -206,8 +203,7 @@ public:
std::optional<AfcClientHandle *> altAfc = std::nullopt)
{
try {
if (!device) {
// FIXME: we have to free error
if (QCoreApplication::closingDown() || !device) {
qDebug()
<< "[executeAfcClientOperation] Device or mutex is null";
return new IdeviceFfiError{1, "DEVICE_OR_MUTEX_IS_NULL"};
@@ -215,7 +211,6 @@ public:
std::lock_guard<std::recursive_mutex> lock(device->mutex);
// Double-check device is still valid after acquiring lock
if (!device->afcClient) {
qDebug() << "[executeAfcClientOperation] AFC client is null";
return new IdeviceFfiError{1, "AFC_CLIENT_IS_NULL"};
@@ -245,7 +240,7 @@ public:
std::optional<AfcClientHandle *> altAfc = std::nullopt)
{
try {
if (!device) {
if (QCoreApplication::closingDown() || !device) {
return T{};
}
+12
View File
@@ -122,6 +122,17 @@ void SettingsManager::setSwitchToNewDevice(bool enabled)
m_settings->sync();
}
bool SettingsManager::autoConnectWirelessDevices() const
{
return m_settings->value("autoConnectWirelessDevices", true).toBool();
}
void SettingsManager::setAutoConnectWirelessDevices(bool enabled)
{
m_settings->setValue("autoConnectWirelessDevices", enabled);
m_settings->sync();
}
#ifndef __APPLE__
bool SettingsManager::unmountiFuseOnExit() const
{
@@ -238,6 +249,7 @@ void SettingsManager::resetToDefaults()
setAutoCheckUpdates(true);
setAutoRaiseWindow(true);
setSwitchToNewDevice(true);
setAutoConnectWirelessDevices(true);
#ifndef __APPLE__
setUnmountiFuseOnExit(false);
#endif
+3
View File
@@ -77,6 +77,9 @@ public:
bool switchToNewDevice() const;
void setSwitchToNewDevice(bool enabled);
bool autoConnectWirelessDevices() const;
void setAutoConnectWirelessDevices(bool enabled);
#ifndef __APPLE__
bool unmountiFuseOnExit() const;
void setUnmountiFuseOnExit(bool enabled);
+9
View File
@@ -156,6 +156,10 @@ void SettingsWidget::setupUI()
m_switchToNewDevice = new QCheckBox("Switch to newly connected device");
deviceLayout->addWidget(m_switchToNewDevice);
m_autoConnectWirelessDevices =
new QCheckBox("Automatically connect to wireless devices");
deviceLayout->addWidget(m_autoConnectWirelessDevices);
// Connection timeout
auto *timeoutLayout = new QHBoxLayout();
timeoutLayout->addWidget(new QLabel("Connection Timeout:"));
@@ -302,6 +306,7 @@ void SettingsWidget::loadSettings()
m_autoUpdateCheck->setChecked(sm->autoCheckUpdates());
m_autoRaiseWindow->setChecked(sm->autoRaiseWindow());
m_switchToNewDevice->setChecked(sm->switchToNewDevice());
m_autoConnectWirelessDevices->setChecked(sm->autoConnectWirelessDevices());
m_wirelessFileServerPort->setValue(sm->wirelessFileServerPort());
#ifndef __APPLE__
@@ -352,6 +357,8 @@ void SettingsWidget::connectSignals()
&SettingsWidget::onSettingChanged);
connect(m_switchToNewDevice, &QCheckBox::toggled, this,
&SettingsWidget::onSettingChanged);
connect(m_autoConnectWirelessDevices, &QCheckBox::toggled, this,
&SettingsWidget::onSettingChanged);
#ifndef __APPLE__
connect(m_unmount_iFuseDrives, &QCheckBox::toggled, this,
&SettingsWidget::onSettingChanged);
@@ -486,6 +493,8 @@ void SettingsWidget::saveSettings()
sm->setAutoCheckUpdates(m_autoUpdateCheck->isChecked());
sm->setAutoRaiseWindow(m_autoRaiseWindow->isChecked());
sm->setSwitchToNewDevice(m_switchToNewDevice->isChecked());
sm->setAutoConnectWirelessDevices(
m_autoConnectWirelessDevices->isChecked());
sm->setWirelessFileServerPort(m_wirelessFileServerPort->value());
#ifndef __APPLE__
+1
View File
@@ -62,6 +62,7 @@ private:
#endif
QCheckBox *m_useUnsecureBackend;
// Device Connection
QCheckBox *m_autoConnectWirelessDevices;
QSpinBox *m_connectionTimeout;
// Jailbroken