diff --git a/CMakeLists.txt b/CMakeLists.txt index c095d48..be42aa5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 ) diff --git a/src/appcontext.cpp b/src/appcontext.cpp index 4c7bad2..f133556 100644 --- a/src/appcontext.cpp +++ b/src/appcontext.cpp @@ -18,16 +18,6 @@ */ #include "appcontext.h" -#include "devicemonitor.h" -#include "iDescriptor.h" -#include "mainwindow.h" -#include "settingsmanager.h" -#include -#include -#include -#include -#include -#include 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; diff --git a/src/appcontext.h b/src/appcontext.h index 8567268..3656c74 100644 --- a/src/appcontext.h +++ b/src/appcontext.h @@ -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 +#include #include +#include +#include +#include +#include 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); diff --git a/src/devicemanagerwidget.cpp b/src/devicemanagerwidget.cpp index 2ef142c..989d3f5 100644 --- a/src/devicemanagerwidget.cpp +++ b/src/devicemanagerwidget.cpp @@ -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; } -} \ No newline at end of file +} + +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); +} diff --git a/src/devicemanagerwidget.h b/src/devicemanagerwidget.h index 830715e..a0e174a 100644 --- a/src/devicemanagerwidget.h +++ b/src/devicemanagerwidget.h @@ -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 \ No newline at end of file diff --git a/src/devicemonitor.h b/src/devicemonitor.h deleted file mode 100644 index c2898aa..0000000 --- a/src/devicemonitor.h +++ /dev/null @@ -1,126 +0,0 @@ -#ifndef DEVICEMONITOR_H -#define DEVICEMONITOR_H -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 deviceMap; -}; -#endif // DEVICEMONITOR_H \ No newline at end of file diff --git a/src/gallerywidget.cpp b/src/gallerywidget.cpp index cdb9f85..7200b93 100644 --- a/src/gallerywidget.cpp +++ b/src/gallerywidget.cpp @@ -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"; diff --git a/src/gallerywidget.h b/src/gallerywidget.h index b45ba48..0adf7f7 100644 --- a/src/gallerywidget.h +++ b/src/gallerywidget.h @@ -23,8 +23,10 @@ #include "exportalbum.h" #include "iDescriptor-ui.h" #include "iDescriptor.h" +#include "photoimportdialog.h" #include "photomodel.h" #include "zloadingwidget.h" +#include #include 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; diff --git a/src/iDescriptor.h b/src/iDescriptor.h index 5700261..88e34b1 100644 --- a/src/iDescriptor.h +++ b/src/iDescriptor.h @@ -26,7 +26,6 @@ #include #include -// #include "idevice.h" #include #include #include @@ -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 @@ -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; diff --git a/src/installedappswidget.cpp b/src/installedappswidget.cpp index e56d4a8..7c5b29f 100644 --- a/src/installedappswidget.cpp +++ b/src/installedappswidget.cpp @@ -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 future = QtConcurrent::run([this]() -> QVariantMap { QVariantMap result; + + if (QCoreApplication::closingDown() || !m_device) { + return result; + } + std::lock_guard lock(m_device->mutex); + QVariantList apps; // fetch icon from springboard service IdeviceFfiError *err = nullptr; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 482a6af..4af17a3 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -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::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( - 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( - 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); } \ No newline at end of file diff --git a/src/mainwindow.h b/src/mainwindow.h index 77aac02..50b4a94 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -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; diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock new file mode 100644 index 0000000..bb7361f --- /dev/null +++ b/src/rust/Cargo.lock @@ -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" diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml new file mode 100644 index 0000000..750ace7 --- /dev/null +++ b/src/rust/Cargo.toml @@ -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"] } diff --git a/src/rust/include/idevice_monitor.h b/src/rust/include/idevice_monitor.h new file mode 100644 index 0000000..ade0a13 --- /dev/null +++ b/src/rust/include/idevice_monitor.h @@ -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 \ No newline at end of file diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs new file mode 100644 index 0000000..ca3521e --- /dev/null +++ b/src/rust/src/lib.rs @@ -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 = 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; + } + }); + }); +} diff --git a/src/servicemanager.h b/src/servicemanager.h index bc84d33..2d90aef 100644 --- a/src/servicemanager.h +++ b/src/servicemanager.h @@ -21,6 +21,7 @@ #define SERVICEMANAGER_H #include "iDescriptor.h" +#include #include #include #include @@ -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 operation, std::optional 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 operation, std::optional 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 operation, T failureValue, std::optional altAfc = std::nullopt) { - if (!device) { + if (QCoreApplication::closingDown() || !device) { return failureValue; } @@ -126,7 +125,7 @@ public: std::function operation, std::optional altAfc = std::nullopt) { - if (!device) { + if (QCoreApplication::closingDown() || !device) { return; } @@ -151,7 +150,7 @@ public: std::function operation, std::optional 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 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 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 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 altAfc = std::nullopt) { try { - if (!device) { + if (QCoreApplication::closingDown() || !device) { return T{}; } diff --git a/src/settingsmanager.cpp b/src/settingsmanager.cpp index 3abdaba..03336c6 100644 --- a/src/settingsmanager.cpp +++ b/src/settingsmanager.cpp @@ -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 diff --git a/src/settingsmanager.h b/src/settingsmanager.h index 8ed2104..3e7898d 100644 --- a/src/settingsmanager.h +++ b/src/settingsmanager.h @@ -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); diff --git a/src/settingswidget.cpp b/src/settingswidget.cpp index 6bb9292..701fe29 100644 --- a/src/settingswidget.cpp +++ b/src/settingswidget.cpp @@ -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__ diff --git a/src/settingswidget.h b/src/settingswidget.h index c385ff0..b6f8611 100644 --- a/src/settingswidget.h +++ b/src/settingswidget.h @@ -62,6 +62,7 @@ private: #endif QCheckBox *m_useUnsecureBackend; // Device Connection + QCheckBox *m_autoConnectWirelessDevices; QSpinBox *m_connectionTimeout; // Jailbroken