diff --git a/lib/uxplay b/lib/uxplay index acdd9ba..bd5e2de 160000 --- a/lib/uxplay +++ b/lib/uxplay @@ -1 +1 @@ -Subproject commit acdd9bafe0b299f77fe4d1a75068a50dbd270e16 +Subproject commit bd5e2de5c2ff2d9dc1f0905549d145deacf1f86c diff --git a/resources.qrc b/resources.qrc index 7bc83ba..fc960e4 100644 --- a/resources.qrc +++ b/resources.qrc @@ -61,7 +61,7 @@ resources/iphone-mockups/iphone-15.png resources/iphone-mockups/iphone-16.png resources/connect.png - resources/airplayer-tutorial.mp4 + resources/airplay-tutorial.mp4 resources/ipad-mockups/ipad.png DeveloperDiskImages.json resources/keychain.mp4 diff --git a/resources/airplay-tutorial.mp4 b/resources/airplay-tutorial.mp4 new file mode 100644 index 0000000..edbf38e Binary files /dev/null and b/resources/airplay-tutorial.mp4 differ diff --git a/resources/airplayer-tutorial.mp4 b/resources/airplayer-tutorial.mp4 deleted file mode 100755 index 16d39f0..0000000 Binary files a/resources/airplayer-tutorial.mp4 and /dev/null differ diff --git a/src/airplaywindow.cpp b/src/airplaywindow.cpp index c885c27..010a0a0 100644 --- a/src/airplaywindow.cpp +++ b/src/airplaywindow.cpp @@ -55,6 +55,8 @@ #include #include +#include "toolboxwidget.h" + AirPlaySettings::AirPlaySettings() : fps(SettingsManager::sharedInstance()->airplayFps()) { @@ -67,6 +69,9 @@ QStringList AirPlaySettings::toArgs() const // FPS args << "-fps" << QString::number(fps); + // Allow new connections to take over + args << "-nohold"; + return args; } @@ -86,12 +91,23 @@ void AirPlaySettingsDialog::setupUI() QGroupBox *videoGroup = new QGroupBox("Video Settings"); QFormLayout *videoLayout = new QFormLayout(videoGroup); - m_fpsSpinBox = new QSpinBox(); - m_fpsSpinBox->setRange(1, 255); - m_fpsSpinBox->setValue(SettingsManager::sharedInstance()->airplayFps()); - m_fpsSpinBox->setToolTip("Set maximum allowed streaming framerate"); - videoLayout->addRow("Max FPS:", m_fpsSpinBox); + // FPS Layout + QVBoxLayout *fpsLayout = new QVBoxLayout(); + m_fpsComboBox = new QComboBox(); + m_fpsComboBox->addItems({"24", "30", "60", "120"}); + m_fpsComboBox->setCurrentText( + QString::number(SettingsManager::sharedInstance()->airplayFps())); + m_fpsComboBox->setToolTip("Set maximum allowed streaming framerate"); + QLabel *fpsFootnote = + new QLabel("Note: Older devices may not support higher framerates. If " + "you are experiencing issues, set this to 30 FPS or lower."); + fpsFootnote->setWordWrap(true); + fpsFootnote->setStyleSheet("color: #666; font-size: 12px;"); + fpsLayout->addWidget(m_fpsComboBox); + fpsLayout->addWidget(fpsFootnote); + + videoLayout->addRow("Max FPS:", fpsLayout); mainLayout->addWidget(videoGroup); // Buttons @@ -105,7 +121,7 @@ void AirPlaySettingsDialog::setupUI() AirPlaySettings AirPlaySettingsDialog::getSettings() const { AirPlaySettings settings; - settings.fps = m_fpsSpinBox->value(); + settings.fps = m_fpsComboBox->currentText().toInt(); return settings; } @@ -218,7 +234,7 @@ void AirPlayWindow::setupTutorialVideo() QSizePolicy::Expanding); m_tutorialPlayer->setVideoOutput(m_tutorialVideoWidget); - m_tutorialPlayer->setSource(QUrl("qrc:/resources/airplayer-tutorial.mp4")); + m_tutorialPlayer->setSource(QUrl("qrc:/resources/airplay-tutorial.mp4")); m_tutorialVideoWidget->setAspectRatioMode( Qt::AspectRatioMode::KeepAspectRatioByExpanding); m_tutorialVideoWidget->setStyleSheet( @@ -273,7 +289,7 @@ void AirPlayWindow::showSettingsDialog() QMessageBox::information(this, "Settings Saved", "AirPlay will be restarted to apply the new " "settings."); - emit restartRequested(); + ToolboxWidget::sharedInstance()->restartAirPlayWindow(); } } diff --git a/src/airplaywindow.h b/src/airplaywindow.h index b1fcc0d..c7e5ffc 100644 --- a/src/airplaywindow.h +++ b/src/airplaywindow.h @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -34,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -89,7 +87,7 @@ public: private: void setupUI(); - QSpinBox *m_fpsSpinBox; + QComboBox *m_fpsComboBox; AirPlaySettings m_settings; }; @@ -110,9 +108,6 @@ private slots: void onV4L2CheckboxToggled(bool enabled); #endif -signals: - void restartRequested(); - private: void setupUI(); void setupTutorialVideo(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c82d111..9a36264 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -162,7 +162,7 @@ MainWindow::MainWindow(QWidget *parent) m_ZTabWidget->addTab(m_mainStackedWidget, "iDevice"); auto *appsWidgetTab = m_ZTabWidget->addTab(AppsWidget::sharedInstance(), "Apps"); - m_ZTabWidget->addTab(new ToolboxWidget(this), "Toolbox"); + m_ZTabWidget->addTab(ToolboxWidget::sharedInstance(), "Toolbox"); auto *jailbrokenWidget = new JailbrokenWidget(this); m_ZTabWidget->addTab(jailbrokenWidget, "Jailbroken"); diff --git a/src/toolboxwidget.cpp b/src/toolboxwidget.cpp index 8d8ccee..abc35c6 100644 --- a/src/toolboxwidget.cpp +++ b/src/toolboxwidget.cpp @@ -79,6 +79,12 @@ bool enterRecoveryMode(iDescriptorDevice *device) } } +ToolboxWidget *ToolboxWidget::sharedInstance() +{ + static ToolboxWidget *instance = new ToolboxWidget(); + return instance; +} + ToolboxWidget::ToolboxWidget(QWidget *parent) : QWidget{parent} { setupUI(); @@ -428,17 +434,6 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool, bool requiresDevice) m_airplayWindow = new AirPlayWindow(); connect(m_airplayWindow, &QObject::destroyed, this, [this]() { m_airplayWindow = nullptr; }); - connect(m_airplayWindow, &AirPlayWindow::restartRequested, this, - [this]() { - if (m_airplayWindow) { - m_airplayWindow->close(); - } - m_airplayWindow = new AirPlayWindow(); - m_airplayWindow->setAttribute(Qt::WA_DeleteOnClose); - m_airplayWindow->setWindowFlag(Qt::Window); - m_airplayWindow->resize(400, 300); - m_airplayWindow->show(); - }); m_airplayWindow->setAttribute(Qt::WA_DeleteOnClose); m_airplayWindow->setWindowFlag(Qt::Window); m_airplayWindow->resize(400, 300); @@ -645,4 +640,24 @@ void ToolboxWidget::_enterRecoveryMode(iDescriptorDevice *device) _msgBox.setText("Failed to enter recovery mode."); } _msgBox.exec(); +} + +void ToolboxWidget::restartAirPlayWindow() +{ + if (!m_airplayWindow) { + onToolboxClicked(iDescriptorTool::Airplayer, false); + return; + } + + connect( + m_airplayWindow, &QObject::destroyed, this, + [this]() { + // give some time for cleanup + QTimer::singleShot(100, this, [this]() { + onToolboxClicked(iDescriptorTool::Airplayer, false); + }); + }, + Qt::SingleShotConnection); + + m_airplayWindow->close(); } \ No newline at end of file diff --git a/src/toolboxwidget.h b/src/toolboxwidget.h index 245969c..4320564 100644 --- a/src/toolboxwidget.h +++ b/src/toolboxwidget.h @@ -47,6 +47,8 @@ public: static void restartDevice(iDescriptorDevice *device); static void shutdownDevice(iDescriptorDevice *device); static void _enterRecoveryMode(iDescriptorDevice *device); + static ToolboxWidget *sharedInstance(); + void restartAirPlayWindow(); private slots: void onDeviceSelectionChanged(); void onToolboxClicked(iDescriptorTool tool, bool requiresDevice);