Merge branch 'idevice-rust' into dev

This commit is contained in:
uncor3
2026-02-01 10:18:41 +00:00
108 changed files with 7389 additions and 5326 deletions
+335 -185
View File
@@ -26,22 +26,26 @@
#include "ifusediskunmountbutton.h"
#include "ifusemanager.h"
#include "jailbrokenwidget.h"
#include "releasechangelogdialog.h"
#include "settingswidget.h"
#ifdef ENABLE_RECOVERY_DEVICE_SUPPORT
#include "libirecovery.h"
#endif
// #ifdef ENABLE_RECOVERY_DEVICE_SUPPORT
// #include "libirecovery.h"
// #endif
#include "toolboxwidget.h"
#include "welcomewidget.h"
#include <QHBoxLayout>
#include <QStack>
#include <QStackedWidget>
#include <QTimer>
#include <QVBoxLayout>
#include <QWidget>
#include <unistd.h>
#include "appcontext.h"
#include "settingsmanager.h"
// #include "devicemonitor.h"
// #include "Toast.h"
#include "networkdevicemanager.h"
#include "networkdeviceswidget.h"
#include "statusballoon.h"
#include <QApplication>
#include <QDesktopServices>
#include <QMenu>
@@ -52,79 +56,85 @@
#include "platform/windows/check_deps.h"
#endif
void handleCallback(const idevice_event_t *event, void *userData)
{
qDebug() << "Device event received";
using namespace IdeviceFFI;
switch (event->event) {
case IDEVICE_DEVICE_ADD: {
/* this should never happen iDescriptor does not support network devices
but for some reason even though we are only listening for USB devices,
we still get network devices on macOS*/
if (event->conn_type == CONNECTION_NETWORK) {
return;
}
qDebug() << "Device added: " << QString::fromUtf8(event->udid);
// void handleCallback(const idevice_event_t *event, void *userData)
// {
// printf("Device event received: ");
QMetaObject::invokeMethod(
AppContext::sharedInstance(), "addDevice", Qt::QueuedConnection,
Q_ARG(QString, QString::fromUtf8(event->udid)),
Q_ARG(idevice_connection_type, event->conn_type),
Q_ARG(AddType, AddType::Regular));
break;
}
// switch (event->event) {
// case IDEVICE_DEVICE_ADD: {
// /* this should never happen iDescriptor does not support network
// devices but for some reason even though we are only listening for USB
// devices, we still get network devices on macOS*/ if (event->conn_type
// == CONNECTION_NETWORK) {
// return;
// }
// qDebug() << "Device added: " << QString::fromUtf8(event->udid);
case IDEVICE_DEVICE_REMOVE: {
QMetaObject::invokeMethod(AppContext::sharedInstance(), "removeDevice",
Qt::QueuedConnection,
Q_ARG(QString, QString(event->udid)));
break;
}
// QMetaObject::invokeMethod(
// AppContext::sharedInstance(), "addDevice", Qt::QueuedConnection,
// Q_ARG(QString, QString::fromUtf8(event->udid)),
// Q_ARG(idevice_connection_type, event->conn_type),
// Q_ARG(AddType, AddType::Regular));
// break;
// }
case IDEVICE_DEVICE_PAIRED: {
if (event->conn_type == CONNECTION_NETWORK) {
qDebug()
<< "Network devices are not supported but a network device was "
"received in event listener. Please report this issue.";
return;
}
qDebug() << "Device paired: " << QString::fromUtf8(event->udid);
// case IDEVICE_DEVICE_REMOVE: {
// QMetaObject::invokeMethod(AppContext::sharedInstance(),
// "removeDevice",
// Qt::QueuedConnection,
// Q_ARG(QString, QString(event->udid)));
// break;
// }
QMetaObject::invokeMethod(
AppContext::sharedInstance(), "addDevice", Qt::QueuedConnection,
Q_ARG(QString, QString::fromUtf8(event->udid)),
Q_ARG(idevice_connection_type, event->conn_type),
Q_ARG(AddType, AddType::Pairing));
break;
}
default:
qDebug() << "Unhandled event: " << event->event;
}
}
// case IDEVICE_DEVICE_PAIRED: {
// if (event->conn_type == CONNECTION_NETWORK) {
// qDebug()
// << "Network devices are not supported but a network device
// was "
// "received in event listener. Please report this issue.";
// return;
// }
// qDebug() << "Device paired: " << QString::fromUtf8(event->udid);
#ifdef ENABLE_RECOVERY_DEVICE_SUPPORT
void handleCallbackRecovery(const irecv_device_event_t *event, void *userData)
{
// QMetaObject::invokeMethod(
// AppContext::sharedInstance(), "addDevice", Qt::QueuedConnection,
// Q_ARG(QString, QString::fromUtf8(event->udid)),
// Q_ARG(idevice_connection_type, event->conn_type),
// Q_ARG(AddType, AddType::Pairing));
// break;
// }
// default:
// qDebug() << "Unhandled event: " << event->event;
// }
// }
switch (event->type) {
case IRECV_DEVICE_ADD:
qDebug() << "Recovery device added: ";
QMetaObject::invokeMethod(AppContext::sharedInstance(),
"addRecoveryDevice", Qt::QueuedConnection,
Q_ARG(uint64_t, event->device_info->ecid));
break;
case IRECV_DEVICE_REMOVE:
qDebug() << "Recovery device removed: ";
QMetaObject::invokeMethod(AppContext::sharedInstance(),
"removeRecoveryDevice", Qt::QueuedConnection,
Q_ARG(uint64_t, event->device_info->ecid));
break;
default:
printf("Unhandled recovery event: %d\n", event->type);
}
}
irecv_device_event_context_t context;
#endif
// #ifdef ENABLE_RECOVERY_DEVICE_SUPPORT
// void handleCallbackRecovery(const irecv_device_event_t *event, void
// *userData)
// {
// switch (event->type) {
// case IRECV_DEVICE_ADD:
// qDebug() << "Recovery device added: ";
// QMetaObject::invokeMethod(AppContext::sharedInstance(),
// "addRecoveryDevice", Qt::QueuedConnection,
// Q_ARG(uint64_t, event->device_info->ecid));
// break;
// case IRECV_DEVICE_REMOVE:
// qDebug() << "Recovery device removed: ";
// QMetaObject::invokeMethod(AppContext::sharedInstance(),
// "removeRecoveryDevice",
// Qt::QueuedConnection, Q_ARG(uint64_t,
// event->device_info->ecid));
// break;
// default:
// printf("Unhandled recovery event: %d\n", event->type);
// }
// }
// irecv_device_event_context_t context;
// #endif
MainWindow *MainWindow::sharedInstance()
{
@@ -161,8 +171,7 @@ MainWindow::MainWindow(QWidget *parent)
m_ZTabWidget->addTab(m_mainStackedWidget, "iDevice");
auto *appsWidgetTab =
m_ZTabWidget->addTab(AppsWidget::sharedInstance(), "Apps");
m_ZTabWidget->addTab(ToolboxWidget::sharedInstance(), "Toolbox");
m_ZTabWidget->addTab(new ToolboxWidget(this), "Toolbox");
auto *jailbrokenWidget = new JailbrokenWidget(this);
m_ZTabWidget->addTab(jailbrokenWidget, "Jailbroken");
m_ZTabWidget->finalizeStyles();
@@ -192,6 +201,12 @@ MainWindow::MainWindow(QWidget *parent)
"QLabel:hover { background-color : #13131319; }");
ui->statusbar->addWidget(m_connectedDeviceCountLabel);
// TODO: implement downloads/uploads progress stuff
StatusBalloon *statusBalloon = StatusBalloon::sharedInstance();
ui->statusbar->addWidget(statusBalloon->getButton());
ui->statusbar->setContentsMargins(0, 0, 0, 0);
QLabel *appVersionLabel = new QLabel(QString("v%1").arg(APP_VERSION));
appVersionLabel->setContentsMargins(5, 0, 5, 0);
@@ -226,34 +241,37 @@ MainWindow::MainWindow(QWidget *parent)
}
#endif
#ifdef ENABLE_RECOVERY_DEVICE_SUPPORT
irecv_error_t res_recovery =
irecv_device_event_subscribe(&context, handleCallbackRecovery, nullptr);
// #ifdef ENABLE_RECOVERY_DEVICE_SUPPORT
// irecv_error_t res_recovery =
// irecv_device_event_subscribe(&context, handleCallbackRecovery,
// nullptr);
if (res_recovery != IRECV_E_SUCCESS) {
qDebug() << "ERROR: Unable to subscribe to recovery device events. "
"Error code:"
<< res_recovery;
}
qDebug() << "Subscribed to recovery device events successfully.";
#endif
// if (res_recovery != IRECV_E_SUCCESS) {
// qDebug() << "ERROR: Unable to subscribe to recovery device
// events. "
// "Error code:"
// << res_recovery;
// }
// qDebug() << "Subscribed to recovery device events successfully.";
// #endif
idevice_error_t res = idevice_event_subscribe(handleCallback, nullptr);
if (res != IDEVICE_E_SUCCESS) {
qDebug() << "ERROR: Unable to subscribe to device events. Error code:"
<< res;
}
qDebug() << "Subscribed to device events successfully.";
createMenus();
// idevice_error_t res = idevice_event_subscribe(handleCallback,
// nullptr); if (res != IDEVICE_E_SUCCESS) {
// qDebug() << "ERROR: Unable to subscribe to device events. Error
// code:"
// << res;
// }
// qDebug() << "Subscribed to device events successfully.";
// createMenus();
UpdateProcedure updateProcedure;
bool packageManagerManaged = false;
bool isPortable = false;
bool skipPrerelease = true;
#ifdef WIN32
isPortable = !is_iDescriptorInstalled();
qDebug() << "isPortable=" << isPortable;
#endif
// UpdateProcedure updateProcedure;
// bool packageManagerManaged = false;
// bool isPortable = false;
// bool skipPrerelease = true;
// #ifdef WIN32
// isPortable = !is_iDescriptorInstalled();
// qDebug() << "isPortable=" << isPortable;
// #endif
/*
struct UpdateProcedure {
@@ -264,87 +282,215 @@ MainWindow::MainWindow(QWidget *parent)
QString boxText;
};
*/
switch (ZUpdater::detectPlatform()) {
case Platform::Windows:
updateProcedure = UpdateProcedure{
!isPortable,
isPortable,
!isPortable,
isPortable ? "New portable version downloaded, app location will "
"be shown after this message"
: "The application will now quit to install the update.",
isPortable ? "New portable version downloaded"
: "Do you want to install the downloaded update now?",
};
break;
// todo: adjust for pkg managers
case Platform::MacOS:
updateProcedure = UpdateProcedure{
true,
false,
true,
"The application will now quit and open .dmg file downloaded to "
"\"Downloads\" from there you can drag it to Applications to "
"install.",
"Update downloaded would you like to quit and install the update?",
};
break;
case Platform::Linux:
// currently only on linux (arch aur) is enabled
#ifdef PACKAGE_MANAGER_MANAGED
packageManagerManaged = true;
#endif
updateProcedure = UpdateProcedure{
true,
false,
true,
"AppImages we ship are not updateable. New version is downloaded "
"to "
"\"Downloads\". You can start using the new version by launching "
"it "
"from there. You can delete this AppImage version if you like.",
"Update downloaded would you like to quit and open the new "
"version?",
};
break;
default:
updateProcedure = UpdateProcedure{
false, false, false, "", "",
};
}
// switch (ZUpdater::detectPlatform()) {
// case Platform::Windows:
// updateProcedure = UpdateProcedure{
// !isPortable,
// isPortable,
// !isPortable,
// isPortable ? "New portable version downloaded, app location
// will "
// "be shown after this message"
// : "The application will now quit to install the
// update.",
// isPortable ? "New portable version downloaded"
// : "Do you want to install the downloaded update
// now?",
// };
// break;
// // todo: adjust for pkg managers
// case Platform::MacOS:
// updateProcedure = UpdateProcedure{
// true,
// false,
// true,
// "The application will now quit and open .dmg file downloaded
// to "
// "\"Downloads\" from there you can drag it to Applications to
// " "install.", "Update downloaded would you like to quit and
// install the update?",
// };
// break;
// case Platform::Linux:
// // currently only on linux (arch aur) is enabled
// #ifdef PACKAGE_MANAGER_MANAGED
// packageManagerManaged = true;
// #endif
// updateProcedure = UpdateProcedure{
// true,
// false,
// true,
// "AppImages we ship are not updateable. New version is
// downloaded " "to "
// "\"Downloads\". You can start using the new version by
// launching " "it " "from there. You can delete this AppImage
// version if you like.", "Update downloaded would you like to
// quit and open the new " "version?",
// };
// break;
// default:
// updateProcedure = UpdateProcedure{
// false, false, false, "", "",
// };
// }
m_updater = new ZUpdater("iDescriptor/iDescriptor", APP_VERSION,
"iDescriptor", updateProcedure, isPortable,
packageManagerManaged, skipPrerelease, this);
#if defined(PACKAGE_MANAGER_MANAGED) && defined(__linux__)
m_updater->setPackageManagerManagedMessage(
QString(
"You seem to have installed iDescriptor using a package manager. "
"Please use %1 to update it.")
.arg(PACKAGE_MANAGER_HINT));
#endif
// m_updater = new ZUpdater("iDescriptor/iDescriptor", APP_VERSION,
// "iDescriptor", updateProcedure, isPortable,
// packageManagerManaged, skipPrerelease,
// this);
// #if defined(PACKAGE_MANAGER_MANAGED) && defined(__linux__)
// m_updater->setPackageManagerManagedMessage(
// QString(
// "You seem to have installed iDescriptor using a package
// manager. " "Please use %1 to update it.")
// .arg(PACKAGE_MANAGER_HINT));
// #endif
QString lastAppVersion = SettingsManager::sharedInstance()->appVersion();
bool shouldShowReleaseChangelog = lastAppVersion != APP_VERSION;
SettingsManager::sharedInstance()->setAppVersion(APP_VERSION);
// SettingsManager::sharedInstance()->doIfEnabled(
// SettingsManager::Setting::AutoCheckUpdates, [this]() {
// qDebug() << "Checking for updates...";
// m_updater->checkForUpdates();
// });
if (shouldShowReleaseChangelog) {
connect(
m_updater, &ZUpdater::dataAvailable, this,
[this](const QJsonDocument data, bool isUpdateAvailable) {
if (!isUpdateAvailable) {
ReleaseChangelogDialog dialog(data, this);
dialog.exec();
// Usage in main thread:
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: {
/* this should never happen iDescriptor does not
support network devices but for some reason even
though we are only listening for USB devices, we
still get network devices on macOS*/
if (conn_type == DeviceMonitorThread::CONNECTION_NETWORK) {
return;
}
},
Qt::SingleShotConnection);
}
qDebug() << "Device added: " << udid;
SettingsManager::sharedInstance()->doIfEnabled(
SettingsManager::Setting::AutoCheckUpdates, [this]() {
qDebug() << "Checking for updates...";
m_updater->checkForUpdates();
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::Regular));
break;
}
case DeviceMonitorThread::IDEVICE_DEVICE_REMOVE: {
QMetaObject::invokeMethod(AppContext::sharedInstance(),
"removeDevice", Qt::QueuedConnection,
Q_ARG(QString, udid));
break;
}
case DeviceMonitorThread::IDEVICE_DEVICE_PAIRED: {
if (conn_type == DeviceMonitorThread::CONNECTION_NETWORK) {
qDebug() << "Network devices are not supported but a "
"network device was "
"received in event listener. Please report "
"this issue.";
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(); });
connect(AppContext::sharedInstance(), &AppContext::deviceRemoved, this,
[](const std::string &udid, const std::string &wifiMacAddress) {
qDebug() << "Upgrading device to wireless connection for UDID"
<< QString::fromStdString(udid);
QMetaObject::invokeMethod(
AppContext::sharedInstance(), "addDevice",
Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(udid)),
Q_ARG(DeviceMonitorThread::IdeviceConnectionType,
DeviceMonitorThread::CONNECTION_NETWORK),
Q_ARG(AddType, AddType::UpgradeToWireless),
Q_ARG(QString, QString::fromStdString(wifiMacAddress)));
});
connect(NetworkDeviceManager::sharedInstance(),
&NetworkDeviceManager::deviceAdded, this,
[this](const NetworkDevice &device) {
// return; // FIXME: disable for now
const iDescriptorDevice *idevice =
AppContext::sharedInstance()->getDeviceByMacAddress(
device.macAddress);
if (idevice) {
qDebug() << "Network device matched to connected device:"
<< QString::fromStdString(
idevice->deviceInfo.deviceName)
<< "MAC:" << device.macAddress;
// You can now use 'idevice' as needed
}
// FIXME: both macAddress and udid can be used to get pairing
// file
if (AppContext::sharedInstance()->getDeviceByMacAddress(
device.macAddress)) {
qDebug() << "Prefering wired connection on device MAC:"
<< device.macAddress;
return;
}
qDebug() << "Trying to add network device with MAC:"
<< device.macAddress;
QMetaObject::invokeMethod(
AppContext::sharedInstance(), "addDevice",
Qt::QueuedConnection, Q_ARG(QString, device.macAddress),
Q_ARG(DeviceMonitorThread::IdeviceConnectionType,
DeviceMonitorThread::CONNECTION_NETWORK),
Q_ARG(AddType, AddType::Wireless),
Q_ARG(QString, device.macAddress));
// Handle network device addition if needed
});
connect(AppContext::sharedInstance(), &AppContext::deviceHeartbeatFailed,
this, [this](const QString &macAddress, int tries) {
// Toast *toast = new Toast(this);
// toast->setAttribute(Qt::WA_DeleteOnClose);
// toast->setDuration(8000); // Hide after 8 seconds
// toast->setTitle("Heartbeat failed");
// toast->setText(
// QString("Heartbeat failed for device with MAC %1. "
// "Number of failed attempts: %2")
// .arg(macAddress)
// .arg(tries));
// toast->setPosition(ToastPosition::BOTTOM_MIDDLE);
// toast->show();
});
// NetworkDevicesWidget *m_networkDevicesWidget = new
// NetworkDevicesWidget();
// m_networkDevicesWidget->setAttribute(Qt::WA_DeleteOnClose);
// m_networkDevicesWidget->setWindowFlag(Qt::Window);
// m_networkDevicesWidget->resize(500, 600);
// // connect(m_networkDevicesWidget, &QObject::destroyed, this,
// // [this]() { m_networkDevicesWidget = nullptr; });
// m_networkDevicesWidget->show();
}
void MainWindow::createMenus()
@@ -354,9 +500,9 @@ void MainWindow::createMenus()
QAction *aboutAct = new QAction("&About iDescriptor", this);
connect(aboutAct, &QAction::triggered, this, [this]() {
QMessageBox::about(
this, "iDescriptor",
"A free, open-source, and cross-platform iDevice management tool.");
QMessageBox::about(this, "iDescriptor",
"A free, open-source, and cross-platform "
"iDevice management tool.");
});
actionsMenu->addAction(aboutAct);
#endif
@@ -372,19 +518,23 @@ void MainWindow::updateNoDevicesConnected()
return m_mainStackedWidget->setCurrentIndex(0); // Show Welcome page
}
int deviceCount = AppContext::sharedInstance()->getConnectedDeviceCount();
m_connectedDeviceCountLabel->setText(
"iDescriptor: " + QString::number(deviceCount) +
(deviceCount == 1 ? " device" : " devices") + " connected");
// m_connectedDeviceCountLabel->setText(
// "iDescriptor: " + QString::number(deviceCount) +
// (deviceCount == 1 ? " device" : " devices") + " connected");
m_mainStackedWidget->setCurrentIndex(1); // Show device list page
}
MainWindow::~MainWindow()
{
idevice_event_unsubscribe();
#ifdef ENABLE_RECOVERY_DEVICE_SUPPORT
irecv_device_event_unsubscribe(context);
#endif
// idevice_event_unsubscribe();
// #ifdef ENABLE_RECOVERY_DEVICE_SUPPORT
// irecv_device_event_unsubscribe(context);
// #endif
delete ui;
delete m_updater;
sleep(2);
m_deviceMonitor->requestInterruption();
// FIXME:QThread: Destroyed while thread '' is still running
// m_deviceMonitor->wait();
delete m_deviceMonitor;
// delete m_updater;
// sleep(2);
}