diff --git a/src/cableinfowidget.cpp b/src/cableinfowidget.cpp new file mode 100644 index 0000000..f340af4 --- /dev/null +++ b/src/cableinfowidget.cpp @@ -0,0 +1,310 @@ +#include "cableinfowidget.h" +#include +#include +#include +#include + +CableInfoWidget::CableInfoWidget(iDescriptorDevice *device, QWidget *parent) + : QWidget(parent), m_device(device), m_response(nullptr) +{ + setupUI(); + initCableInfo(); + + // Auto-refresh cable info after UI is set up + // QTimer::singleShot(100, this, &CableInfoWidget::refreshCableInfo); +} + +void CableInfoWidget::setupUI() +{ + setWindowTitle("Cable Information"); + m_mainLayout = new QVBoxLayout(this); + m_mainLayout->setSpacing(20); + m_mainLayout->setContentsMargins(20, 20, 20, 20); + + // Header section + QHBoxLayout *headerLayout = new QHBoxLayout(); + + m_iconLabel = new QLabel(); + m_iconLabel->setFixedSize(48, 48); + m_iconLabel->setScaledContents(true); + m_iconLabel->setAlignment(Qt::AlignCenter); + + m_statusLabel = new QLabel("Analyzing cable..."); + m_statusLabel->setStyleSheet("QLabel { " + "font-size: 18px; " + "font-weight: bold; " + "color: #333; " + "}"); + + headerLayout->addWidget(m_iconLabel); + headerLayout->addWidget(m_statusLabel, 1); + + m_mainLayout->addLayout(headerLayout); + + // Scroll area to make the info section scrollable + QScrollArea *scrollArea = new QScrollArea(); + scrollArea->setWidgetResizable(true); + scrollArea->setFrameShape(QFrame::NoFrame); + scrollArea->setStyleSheet("QScrollArea { " + "background-color: #f8f9fa; " + "border: 1px solid #dee2e6; " + "border-radius: 8px; " + "}"); + + // Info widget that goes inside the scroll area + m_infoWidget = new QWidget(); + m_infoWidget->setStyleSheet("QWidget { " + "background: transparent; " + "padding: 16px; " + "color: #333; " + "}"); + m_infoLayout = new QGridLayout(m_infoWidget); + m_infoLayout->setSpacing(12); + m_infoLayout->setColumnStretch(1, 1); + + scrollArea->setWidget(m_infoWidget); + + m_mainLayout->addWidget(scrollArea); + m_mainLayout->addStretch(); +} + +void CableInfoWidget::initCableInfo() +{ + if (!m_device || !m_device->device) { + m_statusLabel->setText("❌ Device not available"); + m_statusLabel->setStyleSheet( + "QLabel { color: #dc3545; font-size: 18px; font-weight: bold; }"); + return; + } + + m_statusLabel->setText("Analyzing cable..."); + m_statusLabel->setStyleSheet( + "QLabel { color: #6c757d; font-size: 18px; font-weight: bold; }"); + + // Get cable info + get_cable_info(m_device->device, m_response); + + analyzeCableInfo(); + updateUI(); +} + +void CableInfoWidget::analyzeCableInfo() +{ + qDebug() << "Analyzing cable info..."; + m_cableInfo = CableInfo(); + + if (!m_response) { + return; + } + + PlistNavigator nav(m_response); + PlistNavigator ioreg = nav["IORegistry"]; + + if (!ioreg.valid()) { + return; + } + + m_cableInfo.isConnected = true; + + // Check if genuine (Apple manufacturer and valid model info) + m_cableInfo.manufacturer = QString::fromStdString( + ioreg["IOAccessoryAccessoryManufacturer"].getString()); + m_cableInfo.modelNumber = QString::fromStdString( + ioreg["IOAccessoryAccessoryModelNumber"].getString()); + m_cableInfo.accessoryName = + QString::fromStdString(ioreg["IOAccessoryAccessoryName"].getString()); + m_cableInfo.serialNumber = QString::fromStdString( + ioreg["IOAccessoryAccessorySerialNumber"].getString()); + m_cableInfo.interfaceModuleSerial = QString::fromStdString( + ioreg["IOAccessoryInterfaceModuleSerialNumber"].getString()); + + // Determine if genuine based on manufacturer and presence of detailed info + m_cableInfo.isGenuine = + (m_cableInfo.manufacturer.contains("Apple", Qt::CaseInsensitive) && + !m_cableInfo.modelNumber.isEmpty() && + !m_cableInfo.accessoryName.isEmpty()); + + // Check if Type-C (based on accessory name or TriStar class) + m_cableInfo.triStarClass = + QString::fromStdString(ioreg["TriStarICClass"].getString()); + m_cableInfo.isTypeC = + (m_cableInfo.accessoryName.contains("USB-C", Qt::CaseInsensitive) || + m_cableInfo.triStarClass.contains("1612")); // CBTL1612 is Type-C + + // Power information + m_cableInfo.currentLimit = ioreg["IOAccessoryUSBCurrentLimit"].getUInt(); + m_cableInfo.chargingVoltage = + ioreg["IOAccessoryUSBChargingVoltage"].getUInt(); + + // Connection type + QString connectString = QString::fromStdString( + ioreg["IOAccessoryUSBConnectString"].getString()); + int connectType = + static_cast(ioreg["IOAccessoryUSBConnectType"].getUInt()); + m_cableInfo.connectionType = + QString("%1 (Type %2)").arg(connectString).arg(connectType); + + // Supported and active transports + PlistNavigator supportedTransports = ioreg["TransportsSupported"]; + if (supportedTransports.valid() && + plist_get_node_type(supportedTransports) == PLIST_ARRAY) { + uint32_t count = plist_array_get_size(supportedTransports); + for (uint32_t i = 0; i < count; i++) { + PlistNavigator transport = supportedTransports[static_cast(i)]; + if (transport.valid()) { + m_cableInfo.supportedTransports.append( + QString::fromStdString(transport.getString())); + } + } + } + + PlistNavigator activeTransports = ioreg["TransportsActive"]; + if (activeTransports.valid() && + plist_get_node_type(activeTransports) == PLIST_ARRAY) { + uint32_t count = plist_array_get_size(activeTransports); + for (uint32_t i = 0; i < count; i++) { + PlistNavigator transport = activeTransports[static_cast(i)]; + if (transport.valid()) { + m_cableInfo.activeTransports.append( + QString::fromStdString(transport.getString())); + } + } + } +} + +void CableInfoWidget::updateUI() +{ + // Clear existing info + QLayoutItem *item; + while ((item = m_infoLayout->takeAt(0)) != nullptr) { + delete item->widget(); + delete item; + } + + // if (!m_cableInfo.isConnected) { + // m_statusLabel->setText("❌ No cable detected"); + // m_statusLabel->setStyleSheet( + // "QLabel { color: #dc3545; font-size: 18px; font-weight: bold; + // }"); + // m_iconLabel->setText("🔌"); + // m_iconLabel->setStyleSheet("QLabel { font-size: 32px; }"); + + // QLabel *noDataLabel = new QLabel("No cable information available"); + // noDataLabel->setStyleSheet( + // "QLabel { color: #6c757d; font-size: 14px; text-align: center; + // }"); + // m_infoLayout->addWidget(noDataLabel, 0, 0, 1, 2, Qt::AlignCenter); + // return; + // } + + // Update status and icon based on cable type + QString statusText; + QString statusStyle; + QString iconText; + + if (m_cableInfo.isGenuine) { + statusText = QString("Genuine %1") + .arg(m_cableInfo.isTypeC ? "USB-C to Lightning Cable" + : "Lightning Cable"); + statusStyle = + "QLabel { color: #28a745; font-size: 18px; font-weight: bold; }"; + iconText = m_cableInfo.isTypeC ? "Type-C" : "Lightning"; + } else { + statusText = "⚠️ Third-party Cable"; + statusStyle = + "QLabel { color: #ffc107; font-size: 18px; font-weight: bold; }"; + iconText = "❓"; + } + + m_statusLabel->setText(statusText); + m_statusLabel->setStyleSheet(statusStyle); + m_iconLabel->setText(iconText); + m_iconLabel->setStyleSheet("QLabel { font-size: 32px; }"); + + int row = 0; + + // Basic information + if (!m_cableInfo.accessoryName.isEmpty()) { + createInfoRow(m_infoLayout, row++, "Name:", m_cableInfo.accessoryName); + } + + if (!m_cableInfo.manufacturer.isEmpty()) { + createInfoRow(m_infoLayout, row++, + "Manufacturer:", m_cableInfo.manufacturer); + } + + if (!m_cableInfo.modelNumber.isEmpty()) { + createInfoRow(m_infoLayout, row++, "Model:", m_cableInfo.modelNumber); + } + + if (!m_cableInfo.serialNumber.isEmpty()) { + createInfoRow(m_infoLayout, row++, + "Serial Number:", m_cableInfo.serialNumber); + } + + if (!m_cableInfo.interfaceModuleSerial.isEmpty()) { + createInfoRow(m_infoLayout, row++, + "Interface Module:", m_cableInfo.interfaceModuleSerial); + } + + // Technical information + createInfoRow(m_infoLayout, row++, "Cable Type:", + m_cableInfo.isTypeC ? "USB-C to Lightning" + : "Lightning to USB-A"); + + if (m_cableInfo.currentLimit > 0) { + createInfoRow(m_infoLayout, row++, "Current Limit:", + QString("%1 mA").arg(m_cableInfo.currentLimit)); + } + + if (m_cableInfo.chargingVoltage > 0) { + createInfoRow(m_infoLayout, row++, "Charging Voltage:", + QString("%1 mV").arg(m_cableInfo.chargingVoltage)); + } + + if (!m_cableInfo.connectionType.isEmpty()) { + createInfoRow(m_infoLayout, row++, + "Connection:", m_cableInfo.connectionType); + } + + if (!m_cableInfo.triStarClass.isEmpty()) { + createInfoRow(m_infoLayout, row++, + "Controller:", m_cableInfo.triStarClass); + } + + // Transport information + if (!m_cableInfo.activeTransports.isEmpty()) { + createInfoRow(m_infoLayout, row++, "Active Transports:", + m_cableInfo.activeTransports.join(", ")); + } + + if (!m_cableInfo.supportedTransports.isEmpty()) { + createInfoRow(m_infoLayout, row++, "Supported Transports:", + m_cableInfo.supportedTransports.join(", ")); + } +} + +void CableInfoWidget::createInfoRow(QGridLayout *layout, int row, + const QString &label, const QString &value, + const QString &style) +{ + qDebug() << "Creating info row:" << label << value; + QLabel *labelWidget = new QLabel(label); + labelWidget->setStyleSheet("QLabel { " + "font-weight: bold; " + "color: #495057; " + "font-size: 13px; " + "}"); + + QLabel *valueWidget = new QLabel(value); + QString valueStyle = style.isEmpty() ? "QLabel { " + "color: #212529; " + "font-size: 13px; " + "}" + : style; + valueWidget->setStyleSheet(valueStyle); + valueWidget->setWordWrap(true); + + layout->addWidget(labelWidget, row, 0, Qt::AlignTop); + layout->addWidget(valueWidget, row, 1, Qt::AlignTop); +} \ No newline at end of file diff --git a/src/cableinfowidget.h b/src/cableinfowidget.h new file mode 100644 index 0000000..e87f952 --- /dev/null +++ b/src/cableinfowidget.h @@ -0,0 +1,63 @@ +#ifndef CABLEINFOWIDGET_H +#define CABLEINFOWIDGET_H + +#include "iDescriptor.h" +#include +#include +#include +#include +#include +#include +#include +#include + +class CableInfoWidget : public QWidget +{ + Q_OBJECT + +public: + explicit CableInfoWidget(iDescriptorDevice *device, + QWidget *parent = nullptr); + +private slots: + void initCableInfo(); + +private: + void setupUI(); + void analyzeCableInfo(); + void updateUI(); + void createInfoRow(QGridLayout *layout, int row, const QString &label, + const QString &value, const QString &style = ""); + + // Cable information structure + struct CableInfo { + bool isConnected = false; + bool isGenuine = false; + bool isTypeC = false; + QString manufacturer; + QString modelNumber; + QString accessoryName; + QString serialNumber; + QString interfaceModuleSerial; + uint64_t currentLimit = 0; + uint64_t chargingVoltage = 0; + QString connectionType; + QString triStarClass; + QStringList supportedTransports; + QStringList activeTransports; + }; + + // UI components + QVBoxLayout *m_mainLayout; + QLabel *m_statusLabel; + QLabel *m_iconLabel; + QWidget *m_infoWidget; + QGridLayout *m_infoLayout; + + // Data + iDescriptorDevice *m_device; + CableInfo m_cableInfo; + plist_t m_response; +}; + +#endif // CABLEINFOWIDGET_H \ No newline at end of file diff --git a/src/clickablewidget.h b/src/clickablewidget.h deleted file mode 100644 index 756e16c..0000000 --- a/src/clickablewidget.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef CLICKABLEWIDGET_H -#define CLICKABLEWIDGET_H - -#include -#include - -class ClickableWidget : public QWidget -{ - Q_OBJECT - -public: - explicit ClickableWidget(QWidget *parent = nullptr) : QWidget(parent) {} - -signals: - void clicked(); - -protected: - // Override the protected event handler - void mousePressEvent(QMouseEvent *event) override - { - // When the event happens, emit our public signal - emit clicked(); - // Call the base class implementation if needed - QWidget::mousePressEvent(event); - } -}; - -#endif // CLICKABLEWIDGET_H \ No newline at end of file diff --git a/src/core/services/get_cable_info.cpp b/src/core/services/get_cable_info.cpp new file mode 100644 index 0000000..79f952c --- /dev/null +++ b/src/core/services/get_cable_info.cpp @@ -0,0 +1,49 @@ +#include "../../iDescriptor.h" +#include +#include +#include + +void get_cable_info(idevice_t device, plist_t &response) +{ + lockdownd_client_t lockdown_client = NULL; + diagnostics_relay_client_t diagnostics_client = NULL; + lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; + lockdownd_service_descriptor_t service = NULL; + int use_network = 0; + + if (LOCKDOWN_E_SUCCESS != (ret = lockdownd_client_new_with_handshake( + device, &lockdown_client, TOOL_NAME))) { + idevice_free(device); + printf("ERROR: Could not connect to lockdownd, error code %d\n", ret); + } + + /* attempt to use newer diagnostics service available on iOS 5 and later */ + ret = lockdownd_start_service( + lockdown_client, "com.apple.mobile.diagnostics_relay", &service); + if (ret == LOCKDOWN_E_INVALID_SERVICE) { + /* attempt to use older diagnostics service */ + ret = lockdownd_start_service( + lockdown_client, "com.apple.iosdiagnostics.relay", &service); + } + lockdownd_client_free(lockdown_client); + + if (ret != LOCKDOWN_E_SUCCESS) { + idevice_free(device); + printf("ERROR: Could not start diagnostics relay service: %s\n", + lockdownd_strerror(ret)); + } + + if ((ret == LOCKDOWN_E_SUCCESS) && service && (service->port > 0)) { + if (diagnostics_relay_client_new(device, service, + &diagnostics_client) != + DIAGNOSTICS_RELAY_E_SUCCESS) { + printf("ERROR: Could not connect to diagnostics_relay!\n"); + } + } + + diagnostics_relay_error_t err = diagnostics_relay_query_ioregistry_entry( + diagnostics_client, NULL, "AppleTriStarBuiltIn", &response); + + if (diagnostics_client) + diagnostics_relay_client_free(diagnostics_client); +} \ No newline at end of file diff --git a/src/devicesidebarwidget.cpp b/src/devicesidebarwidget.cpp index 4cd70d5..400bfb9 100644 --- a/src/devicesidebarwidget.cpp +++ b/src/devicesidebarwidget.cpp @@ -1,5 +1,5 @@ #include "devicesidebarwidget.h" -#include "clickablewidget.h" +#include "iDescriptor-ui.h" #include "loadingspinnerwidget.h" #include #include diff --git a/src/devicesidebarwidget.h b/src/devicesidebarwidget.h index d1f8d8b..41d7781 100644 --- a/src/devicesidebarwidget.h +++ b/src/devicesidebarwidget.h @@ -1,7 +1,7 @@ #ifndef DEVICESIDEBARWIDGET_H #define DEVICESIDEBARWIDGET_H -#include "clickablewidget.h" +#include "iDescriptor-ui.h" #include #include #include diff --git a/src/iDescriptor-ui.h b/src/iDescriptor-ui.h index f10fbbf..5ee2f8c 100644 --- a/src/iDescriptor-ui.h +++ b/src/iDescriptor-ui.h @@ -1,6 +1,8 @@ #pragma once #include #include +#include +#include #define COLOR_GREEN QColor(0, 180, 0) // Green #define COLOR_ORANGE QColor(255, 140, 0) // Orange @@ -26,6 +28,49 @@ protected: } }; +class ClickableWidget : public QWidget +{ + Q_OBJECT +public: + using QWidget::QWidget; + +signals: + void clicked(); + +protected: + // On mouse release, if the click is inside the widget, emit the clicked + // signal + void mouseReleaseEvent(QMouseEvent *event) override + { + if (event->button() == Qt::LeftButton && + rect().contains(event->pos())) { + emit clicked(); + } + QWidget::mouseReleaseEvent(event); + } +}; + #ifdef Q_OS_MAC void setupMacOSWindow(QMainWindow *window); #endif + +enum class iDescriptorTool { + Airplayer, + RealtimeScreen, + EnterRecoveryMode, + MountDevImage, + VirtualLocation, + Restart, + Shutdown, + RecoveryMode, + QueryMobileGestalt, + DeveloperDiskImages, + WirelessFileImport, + MountIphone, + CableInfoWidget, + TouchIdTest, + FaceIdTest, + UnmountDevImage, + Unknown, + iFuse +}; \ No newline at end of file diff --git a/src/iDescriptor.h b/src/iDescriptor.h index f63cfe7..b7e37c2 100644 --- a/src/iDescriptor.h +++ b/src/iDescriptor.h @@ -449,3 +449,5 @@ void fetchAppIconFromApple(const QString &bundleId, QObject *context); afc_error_t afc2_client_new(idevice_t device, afc_client_t *afc); + +void get_cable_info(idevice_t device, plist_t &response); diff --git a/src/toolboxwidget.cpp b/src/toolboxwidget.cpp index 1159ebd..5a7a7fd 100644 --- a/src/toolboxwidget.cpp +++ b/src/toolboxwidget.cpp @@ -1,7 +1,9 @@ #include "toolboxwidget.h" #include "airplaywindow.h" #include "appcontext.h" +#include "cableinfowidget.h" #include "devdiskimageswidget.h" +#include "iDescriptor-ui.h" #include "iDescriptor.h" #include "ifusewidget.h" #include "pcfileexplorerwidget.h" @@ -13,6 +15,13 @@ #include #include +struct iDescriptorToolWidget { + iDescriptorTool tool; + QString description; + bool requiresDevice; + QString iconName; +}; + bool enterRecoveryMode(iDescriptorDevice *device) { lockdownd_client_t client = NULL; @@ -100,70 +109,50 @@ void ToolboxWidget::setupUI() m_gridLayout = new QGridLayout(m_contentWidget); m_gridLayout->setSpacing(10); - // Create toolboxes - QWidget *airplayerBox = - createToolbox("Airplayer", - "Start an airplayer service to cast your device screen " - "(does not require a device to be connected)", - "SP_MediaPlay", false); - QWidget *virtualLocationBox = createToolbox( - "Virtual Location", "Simulate GPS location on your device", - "SP_DriveNetIcon", true); - QWidget *realtimeScreenBox = createToolbox( - "Realtime Screen", - "View device screen in real-time (wired connection required)", - "SP_ComputerIcon", true); - QWidget *restartBox = createToolbox("Restart", "Restart device services", - "SP_MediaStop", true); - QWidget *shutdownBox = createToolbox("Shutdown", "Shut down the device", - "SP_MessageBoxWarning", true); - QWidget *recoveryBox = - createToolbox("Recovery Mode", "Enter device recovery mode", - "SP_MessageBoxWarning", true); - QWidget *mobileGestaltBox = createToolbox( - "Query MobileGestalt", "Query device hardware information", - "SP_FileDialogDetailedView", true); - QWidget *touchIdBox = - createToolbox("Touch ID Test", "Test Touch ID functionality", - "SP_DialogOkButton", true); - QWidget *faceIdBox = - createToolbox("Face ID Test", "Test Face ID functionality", - "SP_DialogOkButton", true); - QWidget *unmountDevImage = - createToolbox("Unmount Dev Image", "Unmount a developer image", - "SP_DialogOkButton", true); - QWidget *enterRecoveryMode = - createToolbox("Enter Recovery Mode", "Enter device recovery mode", - "SP_DialogOkButton", true); + QList toolWidgets; + toolWidgets.append({iDescriptorTool::Airplayer, + "Start an airplayer service to cast your device screen " + "(does not require a device to be connected)", + false, ""}); + toolWidgets.append({iDescriptorTool::VirtualLocation, + "Simulate GPS location on your device", true, ""}); + toolWidgets.append( + {iDescriptorTool::RealtimeScreen, + "View device screen in real-time (wired connection required)", true, + ""}); + toolWidgets.append( + {iDescriptorTool::Restart, "Restart device services", true, ""}); + toolWidgets.append( + {iDescriptorTool::Shutdown, "Shut down the device", true, ""}); + toolWidgets.append({iDescriptorTool::RecoveryMode, + "Enter device recovery mode", true, ""}); + toolWidgets.append({iDescriptorTool::QueryMobileGestalt, + "Query device hardware information", true, ""}); + toolWidgets.append({iDescriptorTool::TouchIdTest, + "Test Touch ID functionality", true, ""}); + toolWidgets.append( + {iDescriptorTool::FaceIdTest, "Test Face ID functionality", true, ""}); + toolWidgets.append({iDescriptorTool::UnmountDevImage, + "Unmount a developer image", true, ""}); + toolWidgets.append({iDescriptorTool::EnterRecoveryMode, + "Enter device recovery mode", true, ""}); + toolWidgets.append({iDescriptorTool::DeveloperDiskImages, + "Manage developer disk images", false, ""}); + toolWidgets.append({iDescriptorTool::WirelessFileImport, + "Import files wirelessly to your iDevice", false, ""}); + toolWidgets.append({iDescriptorTool::MountIphone, + "Mount your iPhone's filesystem on your PC", true, ""}); + toolWidgets.append({iDescriptorTool::CableInfoWidget, + "View detailed cable and connection info", true, ""}); - QWidget *devDiskImages = - createToolbox("Developer Disk Images", "Manage developer disk images", - "SP_DialogOkButton", false); - - QWidget *wirelessImport = createToolbox( - "Wireless File Import", "Import files wirelessly to your iDevice", - "SP_DialogOkButton", false); - - QWidget *mount_iPhone = createToolbox( - "Mount iPhone", "Mount your iPhone's filesystem on your PC", - "SP_DialogOkButton", false); - - // Add toolboxes to grid (3 columns) - m_gridLayout->addWidget(airplayerBox, 0, 0); - m_gridLayout->addWidget(virtualLocationBox, 0, 1); - m_gridLayout->addWidget(realtimeScreenBox, 0, 2); - m_gridLayout->addWidget(restartBox, 1, 0); - m_gridLayout->addWidget(shutdownBox, 1, 1); - m_gridLayout->addWidget(recoveryBox, 1, 2); - m_gridLayout->addWidget(mobileGestaltBox, 2, 0); - m_gridLayout->addWidget(touchIdBox, 2, 1); - m_gridLayout->addWidget(faceIdBox, 2, 2); - m_gridLayout->addWidget(enterRecoveryMode, 3, 0); - m_gridLayout->addWidget(unmountDevImage, 3, 1); - m_gridLayout->addWidget(devDiskImages, 3, 2); - m_gridLayout->addWidget(wirelessImport, 4, 0); - m_gridLayout->addWidget(mount_iPhone, 4, 1); - m_gridLayout->setRowStretch(3, 1); + for (int i = 0; i < toolWidgets.size(); ++i) { + const auto &tool = toolWidgets[i]; + ClickableWidget *toolbox = + createToolbox(tool.tool, tool.description, tool.requiresDevice); + int row = i / 3; + int col = i % 3; + m_gridLayout->addWidget(toolbox, row, col); + } m_scrollArea->setWidget(m_contentWidget); mainLayout->addWidget(m_scrollArea); @@ -172,36 +161,78 @@ void ToolboxWidget::setupUI() this, &ToolboxWidget::onDeviceSelectionChanged); } -QWidget *ToolboxWidget::createToolbox(const QString &title, - const QString &description, - const QString &iconName, - bool requiresDevice) +ClickableWidget *ToolboxWidget::createToolbox(iDescriptorTool tool, + const QString &description, + bool requiresDevice) { - QFrame *frame = new QFrame(); - frame->setObjectName("toolboxFrame"); - frame->setFrameStyle(QFrame::Box); - frame->setStyleSheet("#toolboxFrame { " - "border-radius: 5px; padding: 5px; }"); - frame->setFixedSize(200, 120); + ClickableWidget *b = new ClickableWidget(); + b->setStyleSheet("padding: 5px; border: none; outline: none;"); + b->setFixedSize(200, 120); - QVBoxLayout *layout = new QVBoxLayout(frame); + QVBoxLayout *layout = new QVBoxLayout(b); // Icon QLabel *iconLabel = new QLabel(); QIcon icon = - this->style()->standardIcon(static_cast( - iconName == "SP_MediaPlay" ? QStyle::SP_MediaPlay - : iconName == "SP_DriveNetIcon" ? QStyle::SP_DriveNetIcon - : iconName == "SP_ComputerIcon" ? QStyle::SP_ComputerIcon - : iconName == "SP_BrowserReload" ? QStyle::SP_BrowserReload - : iconName == "SP_MediaStop" ? QStyle::SP_MediaStop - : iconName == "SP_MessageBoxWarning" ? QStyle::SP_MessageBoxWarning - : iconName == "SP_FileDialogDetailedView" - ? QStyle::SP_FileDialogDetailedView - : QStyle::SP_DialogOkButton)); + // TODO:icons + this->style()->standardIcon( + static_cast(QStyle::SP_DialogOkButton)); iconLabel->setPixmap(icon.pixmap(32, 32)); iconLabel->setAlignment(Qt::AlignCenter); - + QString title; + switch (tool) { + case iDescriptorTool::Airplayer: + title = "Airplayer"; + break; + case iDescriptorTool::RealtimeScreen: + title = "Realtime Screen"; + break; + case iDescriptorTool::EnterRecoveryMode: + title = "Enter Recovery Mode"; + break; + case iDescriptorTool::MountDevImage: + title = "Mount Dev Image"; + break; + case iDescriptorTool::VirtualLocation: + title = "Virtual Location"; + break; + case iDescriptorTool::Restart: + title = "Restart"; + break; + case iDescriptorTool::Shutdown: + title = "Shutdown"; + break; + case iDescriptorTool::RecoveryMode: + title = "Recovery Mode"; + break; + case iDescriptorTool::QueryMobileGestalt: + title = "Query Mobile Gestalt"; + break; + case iDescriptorTool::DeveloperDiskImages: + title = "Dev Disk Images"; + break; + case iDescriptorTool::WirelessFileImport: + title = "Wireless File Import"; + break; + case iDescriptorTool::MountIphone: + title = "iFuse Mount"; + break; + case iDescriptorTool::CableInfoWidget: + title = "Cable Info"; + break; + case iDescriptorTool::TouchIdTest: + title = "Touch ID Test"; + break; + case iDescriptorTool::FaceIdTest: + title = "Face ID Test"; + break; + case iDescriptorTool::UnmountDevImage: + title = "Unmount Dev Image"; + break; + default: + title = "Unknown Tool"; + break; + } // Title QLabel *titleLabel = new QLabel(title); titleLabel->setFont(QFont("Arial", 10, QFont::Bold)); @@ -218,28 +249,13 @@ QWidget *ToolboxWidget::createToolbox(const QString &title, layout->addWidget(titleLabel); layout->addWidget(descLabel); - // Make frame clickable - frame->setProperty("toolName", title); - frame->installEventFilter(this); - frame->setCursor(Qt::PointingHandCursor); + b->setCursor(Qt::PointingHandCursor); - m_toolboxes.append(frame); + m_toolboxes.append(b); m_requiresDevice.append(requiresDevice); - - return frame; -} - -bool ToolboxWidget::eventFilter(QObject *obj, QEvent *event) -{ - if (event->type() == QEvent::MouseButtonPress) { - QFrame *frame = qobject_cast(obj); - if (frame && frame->isEnabled()) { - QString toolName = frame->property("toolName").toString(); - onToolboxClicked(toolName); - return true; - } - } - return QWidget::eventFilter(obj, event); + connect(b, &ClickableWidget::clicked, + [this, tool]() { onToolboxClicked(tool); }); + return b; } void ToolboxWidget::updateDeviceList() @@ -258,8 +274,7 @@ void ToolboxWidget::updateDeviceList() for (iDescriptorDevice *device : devices) { m_deviceCombo->addItem(QString::fromStdString(device->udid)); } - // DO NOT USE AI HARD TODO: - // Set m_uuid to first device if available + // TODO: m_uuid = devices.first()->udid; m_currentDevice = devices.first(); // Set current device to first one } @@ -317,18 +332,24 @@ void ToolboxWidget::onDeviceSelectionChanged() m_uuid.clear(); // No valid device selected } -void ToolboxWidget::onToolboxClicked(const QString &toolName) +void ToolboxWidget::onToolboxClicked(iDescriptorTool tool) { - qDebug() << "Toolbox clicked:" << toolName; - if (toolName == "Airplayer") { + switch (tool) { + case iDescriptorTool::Airplayer: { AirPlayWindow *airplayWindow = new AirPlayWindow(); + airplayWindow->setAttribute(Qt::WA_DeleteOnClose); + airplayWindow->setWindowFlag(Qt::Window); + airplayWindow->resize(400, 300); airplayWindow->show(); - } else if (toolName == "Realtime Screen") { + } break; + + case iDescriptorTool::RealtimeScreen: { RealtimeScreen *realtimeScreen = new RealtimeScreen(QString::fromStdString(m_uuid)); realtimeScreen->show(); - } else if (toolName == "Enter Recovery Mode") { + } break; + case iDescriptorTool::EnterRecoveryMode: { // Handle entering recovery mode bool success = enterRecoveryMode(m_currentDevice); QMessageBox msgBox; @@ -339,8 +360,9 @@ void ToolboxWidget::onToolboxClicked(const QString &toolName) msgBox.setText("Failed to enter recovery mode."); } msgBox.exec(); - } else if (toolName == "Mount Dev Image") { - // Handle mounting device image + } break; + case iDescriptorTool::MountDevImage: { + // TODO: Handle mounting device image // bool success = // mount_dev_image(const_cast(m_currentDevice->udid.c_str())); @@ -351,8 +373,8 @@ void ToolboxWidget::onToolboxClicked(const QString &toolName) // } else { // msgBox.setText("Failed to mount device image."); // } - // msgBox.exec(); - } else if (toolName == "Virtual Location") { + } break; + case iDescriptorTool::VirtualLocation: { // Handle virtual location functionality VirtualLocation *virtualLocation = new VirtualLocation(m_currentDevice); virtualLocation->setAttribute( @@ -360,7 +382,8 @@ void ToolboxWidget::onToolboxClicked(const QString &toolName) virtualLocation->setWindowFlag(Qt::Window); // Make it a true window virtualLocation->resize(800, 600); // Optional: default size virtualLocation->show(); - } else if (toolName == "Restart") { + } break; + case iDescriptorTool::Restart: { // TODO:WIP std::string udid = m_currentDevice->udid; AppContext::sharedInstance()->instanceRemoveDevice( @@ -375,32 +398,35 @@ void ToolboxWidget::onToolboxClicked(const QString &toolName) warn("Device services restarted successfully", "Success"); qDebug() << "Restarting device"; } - } else if (toolName == "Shutdown") { + } break; + case iDescriptorTool::Shutdown: { // TODO // if (!(shutdown(m_currentDevice->device))) // warn("Failed to shutdown device"); - qDebug() << "Shutting down device..."; - } else if (toolName == "Recovery Mode") { + } break; + case iDescriptorTool::RecoveryMode: { // Handle entering recovery mode - // bool success = enterRecoveryMode(m_currentDevice); - // QMessageBox msgBox; - // msgBox.setWindowTitle("Recovery Mode"); - // if (success) - // { - // msgBox.setText("Successfully entered recovery mode."); - // } - // else - // { - // msgBox.setText("Failed to enter recovery mode."); - // } - // msgBox.exec(); - } else if (toolName == "Query MobileGestalt") { + bool success = enterRecoveryMode(m_currentDevice); + QMessageBox msgBox; + msgBox.setWindowTitle("Recovery Mode"); + if (success) { + msgBox.setText("Successfully entered recovery mode."); + } else { + msgBox.setText("Failed to enter recovery mode."); + } + msgBox.exec(); + } break; + case iDescriptorTool::QueryMobileGestalt: { // Handle querying MobileGestalt QueryMobileGestaltWidget *queryMobileGestaltWidget = new QueryMobileGestaltWidget(m_currentDevice); + queryMobileGestaltWidget->setAttribute(Qt::WA_DeleteOnClose); + queryMobileGestaltWidget->setWindowFlag(Qt::Window); + queryMobileGestaltWidget->resize(800, 600); queryMobileGestaltWidget->show(); - } else if (toolName == "Developer Disk Images") { - // Handle developer disk images + } break; + case iDescriptorTool::DeveloperDiskImages: { + // single instance lock if (!m_devDiskImagesWidget) { m_devDiskImagesWidget = new DevDiskImagesWidget(m_currentDevice); m_devDiskImagesWidget->setAttribute(Qt::WA_DeleteOnClose); @@ -413,19 +439,31 @@ void ToolboxWidget::onToolboxClicked(const QString &toolName) m_devDiskImagesWidget->raise(); m_devDiskImagesWidget->activateWindow(); } - } else if (toolName == "Wireless File Import") { + } break; + case iDescriptorTool::WirelessFileImport: { // Handle wireless file import PCFileExplorerWidget *fileExplorer = new PCFileExplorerWidget(); fileExplorer->setAttribute(Qt::WA_DeleteOnClose); fileExplorer->setWindowFlag(Qt::Window); fileExplorer->resize(800, 600); fileExplorer->show(); - } else if (toolName == "Mount iPhone") { + } break; + case iDescriptorTool::MountIphone: { iFuseWidget *ifuseWidget = new iFuseWidget(m_currentDevice); ifuseWidget->setAttribute(Qt::WA_DeleteOnClose); ifuseWidget->setWindowFlag(Qt::Window); ifuseWidget->resize(600, 400); ifuseWidget->show(); + } break; + case iDescriptorTool::CableInfoWidget: { + CableInfoWidget *cableInfoWidget = new CableInfoWidget(m_currentDevice); + cableInfoWidget->setAttribute(Qt::WA_DeleteOnClose); + cableInfoWidget->setWindowFlag(Qt::Window); + cableInfoWidget->resize(600, 400); + cableInfoWidget->show(); + } break; + default: + qDebug() << "Clicked on unimplemented tool"; + break; } - // Implement specific tool functionality here } diff --git a/src/toolboxwidget.h b/src/toolboxwidget.h index 901bff4..8c772a3 100644 --- a/src/toolboxwidget.h +++ b/src/toolboxwidget.h @@ -2,9 +2,9 @@ #define TOOLBOXWIDGET_H #include "devdiskimageswidget.h" +#include "iDescriptor-ui.h" #include "iDescriptor.h" #include -#include #include #include #include @@ -23,15 +23,15 @@ private slots: void onDeviceAdded(); void onDeviceRemoved(); void onDeviceSelectionChanged(); - void onToolboxClicked(const QString &toolName); + void onToolboxClicked(iDescriptorTool tool); private: void setupUI(); void updateDeviceList(); void updateToolboxStates(); - QWidget *createToolbox(const QString &title, const QString &description, - const QString &iconName, bool requiresDevice); - bool eventFilter(QObject *obj, QEvent *event) override; + ClickableWidget *createToolbox(iDescriptorTool tool, + const QString &description, + bool requiresDevice); QComboBox *m_deviceCombo; QLabel *m_deviceLabel; QScrollArea *m_scrollArea;