diff --git a/src/app/application.cpp b/src/app/application.cpp index 8955ceed5..1dde0513d 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -776,8 +776,9 @@ void Application::allTorrentsFinished() bool isShutdown = pref->shutdownWhenDownloadsComplete(); bool isSuspend = pref->suspendWhenDownloadsComplete(); 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; ShutdownDialogAction action = ShutdownDialogAction::Exit; @@ -787,6 +788,8 @@ void Application::allTorrentsFinished() action = ShutdownDialogAction::Hibernate; else if (isShutdown) action = ShutdownDialogAction::Shutdown; + else if (isReboot) + action = ShutdownDialogAction::Reboot; #ifndef DISABLE_GUI // ask confirm @@ -808,6 +811,7 @@ void Application::allTorrentsFinished() pref->setShutdownWhenDownloadsComplete(false); pref->setSuspendWhenDownloadsComplete(false); pref->setHibernateWhenDownloadsComplete(false); + pref->setRebootWhenDownloadsComplete(false); // Make sure preferences are synced before exiting m_shutdownAct = action; } diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index 2a80a4be7..0760184ce 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -1297,6 +1297,19 @@ void Preferences::setShutdownWhenDownloadsComplete(const bool 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 { return value(u"Preferences/Downloads/AutoSuspendOnCompletion"_s, false); diff --git a/src/base/preferences.h b/src/base/preferences.h index 398c81455..dc22f26b5 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -287,6 +287,8 @@ public: bool shutdownWhenDownloadsComplete() const; void setShutdownWhenDownloadsComplete(bool shutdown); + bool rebootWhenDownloadsComplete() const; + void setRebootWhenDownloadsComplete(bool reboot); bool suspendWhenDownloadsComplete() const; void setSuspendWhenDownloadsComplete(bool suspend); bool hibernateWhenDownloadsComplete() const; diff --git a/src/base/types.h b/src/base/types.h index 7bb8a29f7..daf9d77cc 100644 --- a/src/base/types.h +++ b/src/base/types.h @@ -37,7 +37,8 @@ enum class ShutdownDialogAction Exit, Shutdown, Suspend, - Hibernate + Hibernate, + Reboot }; using QStringMap = QMap; diff --git a/src/base/utils/os.cpp b/src/base/utils/os.cpp index ad1cd4057..6d3157700 100644 --- a/src/base/utils/os.cpp +++ b/src/base/utils/os.cpp @@ -87,22 +87,30 @@ void Utils::OS::shutdownComputer([[maybe_unused]] const ShutdownDialogAction &ac { ::SetSuspendState(TRUE, FALSE, FALSE); } - else + else if (action == ShutdownDialogAction::Shutdown) { std::wstring msg = QCoreApplication::translate("misc" , "qBittorrent will shutdown the computer now because all downloads are complete.").toStdWString(); ::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. tkp.Privileges[0].Attributes = 0; ::AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0); #elif defined(Q_OS_MACOS) - AEEventID EventToSend; - if (action != ShutdownDialogAction::Shutdown) + AEEventID EventToSend {}; + if (action == ShutdownDialogAction::Suspend) EventToSend = kAESleep; - else + else if (action == ShutdownDialogAction::Reboot) + EventToSend = kAERestart; + else if (action == ShutdownDialogAction::Shutdown) EventToSend = kAEShutDown; AEAddressDesc targetDesc; const ProcessSerialNumber kPSNOfSystemProcess = {0, kSystemProcess}; @@ -133,7 +141,7 @@ void Utils::OS::shutdownComputer([[maybe_unused]] const ShutdownDialogAction &ac #elif defined(QBT_USES_DBUS) // 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 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 halIface.call(u"Hibernate"_s); } - else + else if (action == ShutdownDialogAction::Shutdown) { // Some recent systems use systemd's logind 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()); 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 } diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index a68ff1893..0310fedcd 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -388,10 +388,16 @@ MainWindow::MainWindow(IGUIApplication *app, const WindowState initialState, con autoShutdownGroup->addAction(m_ui->actionAutoShutdown); autoShutdownGroup->addAction(m_ui->actionAutoSuspend); autoShutdownGroup->addAction(m_ui->actionAutoHibernate); + autoShutdownGroup->addAction(m_ui->actionAutoReboot); #if (!defined(Q_OS_UNIX) || defined(Q_OS_MACOS)) || defined(QBT_USES_DBUS) m_ui->actionAutoShutdown->setChecked(pref->shutdownWhenDownloadsComplete()); + m_ui->actionAutoReboot->setChecked(pref->rebootWhenDownloadsComplete()); m_ui->actionAutoSuspend->setChecked(pref->suspendWhenDownloadsComplete()); 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 m_ui->actionAutoShutdown->setDisabled(true); m_ui->actionAutoSuspend->setDisabled(true); @@ -1790,30 +1796,31 @@ void MainWindow::on_actionCriticalMessages_triggered(const bool checked) 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); } -void MainWindow::on_actionAutoSuspend_toggled(bool enabled) +void MainWindow::on_actionAutoSuspend_toggled(const bool enabled) { - qDebug() << Q_FUNC_INFO << 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); } -void MainWindow::on_actionAutoShutdown_toggled(bool enabled) +void MainWindow::on_actionAutoShutdown_toggled(const bool enabled) { - qDebug() << Q_FUNC_INFO << enabled; Preferences::instance()->setShutdownWhenDownloadsComplete(enabled); } +void MainWindow::on_actionAutoReboot_toggled(const bool enabled) +{ + Preferences::instance()->setRebootWhenDownloadsComplete(enabled); +} + void MainWindow::updatePowerManagementState() const { const auto *pref = Preferences::instance(); diff --git a/src/gui/mainwindow.h b/src/gui/mainwindow.h index ee4a620fa..de2da3b66 100644 --- a/src/gui/mainwindow.h +++ b/src/gui/mainwindow.h @@ -158,6 +158,7 @@ private slots: void on_actionAutoSuspend_toggled(bool); void on_actionAutoHibernate_toggled(bool); void on_actionAutoShutdown_toggled(bool); + void on_actionAutoReboot_toggled(bool); void on_actionAbout_triggered(); void on_actionStatistics_triggered(); void on_actionCreateTorrent_triggered(); diff --git a/src/gui/mainwindow.ui b/src/gui/mainwindow.ui index 202867775..d68ade0e6 100644 --- a/src/gui/mainwindow.ui +++ b/src/gui/mainwindow.ui @@ -76,6 +76,7 @@ + @@ -397,6 +398,14 @@ Sh&utdown System + + + true + + + &Reboot System + + true diff --git a/src/gui/shutdownconfirmdialog.cpp b/src/gui/shutdownconfirmdialog.cpp index 0a787ae1d..b2e83b8c3 100644 --- a/src/gui/shutdownconfirmdialog.cpp +++ b/src/gui/shutdownconfirmdialog.cpp @@ -134,6 +134,11 @@ void ShutdownConfirmDialog::initText() okButton->setText(tr("&Hibernate Now")); setWindowTitle(tr("Hibernate confirmation")); 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';