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);