Add reboot option when downloads complete

This commit implements a new "Reboot System" option that allows users to automatically reboot the computer when all downloads are complete, similar to the existing shutdown, suspend, and hibernate options.

Closes #10774.
PR #23525.
This commit is contained in:
Vasiliy Kostin
2025-11-30 14:16:48 +03:00
committed by GitHub
parent 8b9064a33c
commit f2f4676824
9 changed files with 90 additions and 16 deletions

View File

@@ -776,8 +776,9 @@ void Application::allTorrentsFinished()
bool isShutdown = pref->shutdownWhenDownloadsComplete(); bool isShutdown = pref->shutdownWhenDownloadsComplete();
bool isSuspend = pref->suspendWhenDownloadsComplete(); bool isSuspend = pref->suspendWhenDownloadsComplete();
bool isHibernate = pref->hibernateWhenDownloadsComplete(); bool isHibernate = pref->hibernateWhenDownloadsComplete();
bool isReboot = pref->rebootWhenDownloadsComplete();
bool haveAction = isExit || isShutdown || isSuspend || isHibernate; const bool haveAction = isExit || isShutdown || isSuspend || isHibernate || isReboot;
if (!haveAction) return; if (!haveAction) return;
ShutdownDialogAction action = ShutdownDialogAction::Exit; ShutdownDialogAction action = ShutdownDialogAction::Exit;
@@ -787,6 +788,8 @@ void Application::allTorrentsFinished()
action = ShutdownDialogAction::Hibernate; action = ShutdownDialogAction::Hibernate;
else if (isShutdown) else if (isShutdown)
action = ShutdownDialogAction::Shutdown; action = ShutdownDialogAction::Shutdown;
else if (isReboot)
action = ShutdownDialogAction::Reboot;
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
// ask confirm // ask confirm
@@ -808,6 +811,7 @@ void Application::allTorrentsFinished()
pref->setShutdownWhenDownloadsComplete(false); pref->setShutdownWhenDownloadsComplete(false);
pref->setSuspendWhenDownloadsComplete(false); pref->setSuspendWhenDownloadsComplete(false);
pref->setHibernateWhenDownloadsComplete(false); pref->setHibernateWhenDownloadsComplete(false);
pref->setRebootWhenDownloadsComplete(false);
// Make sure preferences are synced before exiting // Make sure preferences are synced before exiting
m_shutdownAct = action; m_shutdownAct = action;
} }

View File

@@ -1297,6 +1297,19 @@ void Preferences::setShutdownWhenDownloadsComplete(const bool shutdown)
setValue(u"Preferences/Downloads/AutoShutDownOnCompletion"_s, shutdown); setValue(u"Preferences/Downloads/AutoShutDownOnCompletion"_s, shutdown);
} }
bool Preferences::rebootWhenDownloadsComplete() const
{
return value(u"Preferences/Downloads/AutoRebootOnCompletion"_s, false);
}
void Preferences::setRebootWhenDownloadsComplete(const bool reboot)
{
if (reboot == rebootWhenDownloadsComplete())
return;
setValue(u"Preferences/Downloads/AutoRebootOnCompletion"_s, reboot);
}
bool Preferences::suspendWhenDownloadsComplete() const bool Preferences::suspendWhenDownloadsComplete() const
{ {
return value(u"Preferences/Downloads/AutoSuspendOnCompletion"_s, false); return value(u"Preferences/Downloads/AutoSuspendOnCompletion"_s, false);

View File

@@ -287,6 +287,8 @@ public:
bool shutdownWhenDownloadsComplete() const; bool shutdownWhenDownloadsComplete() const;
void setShutdownWhenDownloadsComplete(bool shutdown); void setShutdownWhenDownloadsComplete(bool shutdown);
bool rebootWhenDownloadsComplete() const;
void setRebootWhenDownloadsComplete(bool reboot);
bool suspendWhenDownloadsComplete() const; bool suspendWhenDownloadsComplete() const;
void setSuspendWhenDownloadsComplete(bool suspend); void setSuspendWhenDownloadsComplete(bool suspend);
bool hibernateWhenDownloadsComplete() const; bool hibernateWhenDownloadsComplete() const;

View File

@@ -37,7 +37,8 @@ enum class ShutdownDialogAction
Exit, Exit,
Shutdown, Shutdown,
Suspend, Suspend,
Hibernate Hibernate,
Reboot
}; };
using QStringMap = QMap<QString, QString>; using QStringMap = QMap<QString, QString>;

View File

@@ -87,22 +87,30 @@ void Utils::OS::shutdownComputer([[maybe_unused]] const ShutdownDialogAction &ac
{ {
::SetSuspendState(TRUE, FALSE, FALSE); ::SetSuspendState(TRUE, FALSE, FALSE);
} }
else else if (action == ShutdownDialogAction::Shutdown)
{ {
std::wstring msg = QCoreApplication::translate("misc" std::wstring msg = QCoreApplication::translate("misc"
, "qBittorrent will shutdown the computer now because all downloads are complete.").toStdWString(); , "qBittorrent will shutdown the computer now because all downloads are complete.").toStdWString();
::InitiateSystemShutdownW(nullptr, msg.data(), 10, TRUE, FALSE); ::InitiateSystemShutdownW(nullptr, msg.data(), 10, TRUE, FALSE);
} }
else if (action == ShutdownDialogAction::Reboot)
{
std::wstring msg = QCoreApplication::translate("misc"
, "qBittorrent will reboot the computer now because all downloads are complete.").toStdWString();
::InitiateSystemShutdownW(nullptr, msg.data(), 10, TRUE, TRUE);
}
// Disable shutdown privilege. // Disable shutdown privilege.
tkp.Privileges[0].Attributes = 0; tkp.Privileges[0].Attributes = 0;
::AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0); ::AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);
#elif defined(Q_OS_MACOS) #elif defined(Q_OS_MACOS)
AEEventID EventToSend; AEEventID EventToSend {};
if (action != ShutdownDialogAction::Shutdown) if (action == ShutdownDialogAction::Suspend)
EventToSend = kAESleep; EventToSend = kAESleep;
else else if (action == ShutdownDialogAction::Reboot)
EventToSend = kAERestart;
else if (action == ShutdownDialogAction::Shutdown)
EventToSend = kAEShutDown; EventToSend = kAEShutDown;
AEAddressDesc targetDesc; AEAddressDesc targetDesc;
const ProcessSerialNumber kPSNOfSystemProcess = {0, kSystemProcess}; const ProcessSerialNumber kPSNOfSystemProcess = {0, kSystemProcess};
@@ -133,7 +141,7 @@ void Utils::OS::shutdownComputer([[maybe_unused]] const ShutdownDialogAction &ac
#elif defined(QBT_USES_DBUS) #elif defined(QBT_USES_DBUS)
// Use dbus to power off / suspend the system // Use dbus to power off / suspend the system
if (action != ShutdownDialogAction::Shutdown) if ((action == ShutdownDialogAction::Suspend) || (action == ShutdownDialogAction::Hibernate))
{ {
// Some recent systems use systemd's logind // Some recent systems use systemd's logind
QDBusInterface login1Iface(u"org.freedesktop.login1"_s, u"/org/freedesktop/login1"_s, QDBusInterface login1Iface(u"org.freedesktop.login1"_s, u"/org/freedesktop/login1"_s,
@@ -166,7 +174,7 @@ void Utils::OS::shutdownComputer([[maybe_unused]] const ShutdownDialogAction &ac
else else
halIface.call(u"Hibernate"_s); halIface.call(u"Hibernate"_s);
} }
else else if (action == ShutdownDialogAction::Shutdown)
{ {
// Some recent systems use systemd's logind // Some recent systems use systemd's logind
QDBusInterface login1Iface(u"org.freedesktop.login1"_s, u"/org/freedesktop/login1"_s, QDBusInterface login1Iface(u"org.freedesktop.login1"_s, u"/org/freedesktop/login1"_s,
@@ -190,6 +198,30 @@ void Utils::OS::shutdownComputer([[maybe_unused]] const ShutdownDialogAction &ac
QDBusConnection::systemBus()); QDBusConnection::systemBus());
halIface.call(u"Shutdown"_s); halIface.call(u"Shutdown"_s);
} }
else if (action == ShutdownDialogAction::Reboot)
{
// Some recent systems use systemd's logind
QDBusInterface login1Iface(u"org.freedesktop.login1"_s, u"/org/freedesktop/login1"_s,
u"org.freedesktop.login1.Manager"_s, QDBusConnection::systemBus());
if (login1Iface.isValid())
{
login1Iface.call(u"Reboot"_s, false);
return;
}
// Else, other recent systems use ConsoleKit
QDBusInterface consolekitIface(u"org.freedesktop.ConsoleKit"_s, u"/org/freedesktop/ConsoleKit/Manager"_s,
u"org.freedesktop.ConsoleKit.Manager"_s, QDBusConnection::systemBus());
if (consolekitIface.isValid())
{
consolekitIface.call(u"Restart"_s);
return;
}
// HAL (older systems)
QDBusInterface halIface(u"org.freedesktop.Hal"_s, u"/org/freedesktop/Hal/devices/computer"_s,
u"org.freedesktop.Hal.Device.SystemPowerManagement"_s,
QDBusConnection::systemBus());
halIface.call(u"Reboot"_s);
}
#endif #endif
} }

View File

@@ -388,10 +388,16 @@ MainWindow::MainWindow(IGUIApplication *app, const WindowState initialState, con
autoShutdownGroup->addAction(m_ui->actionAutoShutdown); autoShutdownGroup->addAction(m_ui->actionAutoShutdown);
autoShutdownGroup->addAction(m_ui->actionAutoSuspend); autoShutdownGroup->addAction(m_ui->actionAutoSuspend);
autoShutdownGroup->addAction(m_ui->actionAutoHibernate); autoShutdownGroup->addAction(m_ui->actionAutoHibernate);
autoShutdownGroup->addAction(m_ui->actionAutoReboot);
#if (!defined(Q_OS_UNIX) || defined(Q_OS_MACOS)) || defined(QBT_USES_DBUS) #if (!defined(Q_OS_UNIX) || defined(Q_OS_MACOS)) || defined(QBT_USES_DBUS)
m_ui->actionAutoShutdown->setChecked(pref->shutdownWhenDownloadsComplete()); m_ui->actionAutoShutdown->setChecked(pref->shutdownWhenDownloadsComplete());
m_ui->actionAutoReboot->setChecked(pref->rebootWhenDownloadsComplete());
m_ui->actionAutoSuspend->setChecked(pref->suspendWhenDownloadsComplete()); m_ui->actionAutoSuspend->setChecked(pref->suspendWhenDownloadsComplete());
m_ui->actionAutoHibernate->setChecked(pref->hibernateWhenDownloadsComplete()); m_ui->actionAutoHibernate->setChecked(pref->hibernateWhenDownloadsComplete());
#ifdef Q_OS_MACOS
// macOS doesn't support Hibernate via Apple Events API
m_ui->actionAutoHibernate->setDisabled(true);
#endif
#else #else
m_ui->actionAutoShutdown->setDisabled(true); m_ui->actionAutoShutdown->setDisabled(true);
m_ui->actionAutoSuspend->setDisabled(true); m_ui->actionAutoSuspend->setDisabled(true);
@@ -1790,30 +1796,31 @@ void MainWindow::on_actionCriticalMessages_triggered(const bool checked)
setExecutionLogMsgTypes(flags); setExecutionLogMsgTypes(flags);
} }
void MainWindow::on_actionAutoExit_toggled(bool enabled) void MainWindow::on_actionAutoExit_toggled(const bool enabled)
{ {
qDebug() << Q_FUNC_INFO << enabled;
Preferences::instance()->setShutdownqBTWhenDownloadsComplete(enabled); Preferences::instance()->setShutdownqBTWhenDownloadsComplete(enabled);
} }
void MainWindow::on_actionAutoSuspend_toggled(bool enabled) void MainWindow::on_actionAutoSuspend_toggled(const bool enabled)
{ {
qDebug() << Q_FUNC_INFO << enabled;
Preferences::instance()->setSuspendWhenDownloadsComplete(enabled); Preferences::instance()->setSuspendWhenDownloadsComplete(enabled);
} }
void MainWindow::on_actionAutoHibernate_toggled(bool enabled) void MainWindow::on_actionAutoHibernate_toggled(const bool enabled)
{ {
qDebug() << Q_FUNC_INFO << enabled;
Preferences::instance()->setHibernateWhenDownloadsComplete(enabled); Preferences::instance()->setHibernateWhenDownloadsComplete(enabled);
} }
void MainWindow::on_actionAutoShutdown_toggled(bool enabled) void MainWindow::on_actionAutoShutdown_toggled(const bool enabled)
{ {
qDebug() << Q_FUNC_INFO << enabled;
Preferences::instance()->setShutdownWhenDownloadsComplete(enabled); Preferences::instance()->setShutdownWhenDownloadsComplete(enabled);
} }
void MainWindow::on_actionAutoReboot_toggled(const bool enabled)
{
Preferences::instance()->setRebootWhenDownloadsComplete(enabled);
}
void MainWindow::updatePowerManagementState() const void MainWindow::updatePowerManagementState() const
{ {
const auto *pref = Preferences::instance(); const auto *pref = Preferences::instance();

View File

@@ -158,6 +158,7 @@ private slots:
void on_actionAutoSuspend_toggled(bool); void on_actionAutoSuspend_toggled(bool);
void on_actionAutoHibernate_toggled(bool); void on_actionAutoHibernate_toggled(bool);
void on_actionAutoShutdown_toggled(bool); void on_actionAutoShutdown_toggled(bool);
void on_actionAutoReboot_toggled(bool);
void on_actionAbout_triggered(); void on_actionAbout_triggered();
void on_actionStatistics_triggered(); void on_actionStatistics_triggered();
void on_actionCreateTorrent_triggered(); void on_actionCreateTorrent_triggered();

View File

@@ -76,6 +76,7 @@
<addaction name="actionAutoExit"/> <addaction name="actionAutoExit"/>
<addaction name="actionAutoSuspend"/> <addaction name="actionAutoSuspend"/>
<addaction name="actionAutoHibernate"/> <addaction name="actionAutoHibernate"/>
<addaction name="actionAutoReboot"/>
<addaction name="actionAutoShutdown"/> <addaction name="actionAutoShutdown"/>
</widget> </widget>
<addaction name="actionCreateTorrent"/> <addaction name="actionCreateTorrent"/>
@@ -397,6 +398,14 @@
<string>Sh&amp;utdown System</string> <string>Sh&amp;utdown System</string>
</property> </property>
</action> </action>
<action name="actionAutoReboot">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Reboot System</string>
</property>
</action>
<action name="actionAutoShutdownDisabled"> <action name="actionAutoShutdownDisabled">
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>

View File

@@ -134,6 +134,11 @@ void ShutdownConfirmDialog::initText()
okButton->setText(tr("&Hibernate Now")); okButton->setText(tr("&Hibernate Now"));
setWindowTitle(tr("Hibernate confirmation")); setWindowTitle(tr("Hibernate confirmation"));
break; break;
case ShutdownDialogAction::Reboot:
m_msg = tr("The computer is going to reboot.");
okButton->setText(tr("&Reboot Now"));
setWindowTitle(tr("Reboot confirmation"));
break;
} }
m_msg += u'\n'; m_msg += u'\n';