diff --git a/src/mumble/CMakeLists.txt b/src/mumble/CMakeLists.txt index 1f3daa534c2..2799c4e4116 100644 --- a/src/mumble/CMakeLists.txt +++ b/src/mumble/CMakeLists.txt @@ -230,6 +230,8 @@ set(MUMBLE_SOURCES "SettingsKeys.h" "Settings.cpp" "Settings.h" + "SleepInhibitor.cpp" + "SleepInhibitor.h" "SharedMemory.cpp" "SharedMemory.h" "SocketRPC.cpp" diff --git a/src/mumble/LookConfig.cpp b/src/mumble/LookConfig.cpp index 24032a6df8f..ffeb4c05343 100644 --- a/src/mumble/LookConfig.cpp +++ b/src/mumble/LookConfig.cpp @@ -234,6 +234,7 @@ void LookConfig::load(const Settings &r) { loadCheckBox(qcbEnableDeveloperMenu, r.bEnableDeveloperMenu); loadCheckBox(qcbLockLayout, (r.wlWindowLayout == Settings::LayoutCustom) && r.bLockLayout); + loadCheckBox(qcbInhibitSleep, r.bInhibitSleep); loadCheckBox(qcbHideTray, r.bHideInTray); loadCheckBox(qcbStateInTray, r.bStateInTray); loadCheckBox(qcbShowUserCount, r.bShowUserCount); @@ -309,6 +310,7 @@ void LookConfig::save() const { s.quitBehavior = static_cast< QuitBehavior >(qcbQuitBehavior->currentIndex()); s.bEnableDeveloperMenu = qcbEnableDeveloperMenu->isChecked(); s.bLockLayout = qcbLockLayout->isChecked(); + s.bInhibitSleep = qcbInhibitSleep->isChecked(); s.bHideInTray = qcbHideTray->isChecked(); s.bStateInTray = qcbStateInTray->isChecked(); s.bShowUserCount = qcbShowUserCount->isChecked(); diff --git a/src/mumble/LookConfig.ui b/src/mumble/LookConfig.ui index 5ddc48e22e3..3ba18ba73ba 100644 --- a/src/mumble/LookConfig.ui +++ b/src/mumble/LookConfig.ui @@ -141,6 +141,16 @@ + + + + Prevents the computer from sleeping while connected to a server + + + Inhibit system sleep while connected + + + @@ -1288,6 +1298,7 @@ qcbQuitBehavior qcbEnableDeveloperMenu qcbLockLayout + qcbInhibitSleep qcbHideTray qcbStateInTray qcbSearchUserAction diff --git a/src/mumble/MainWindow.cpp b/src/mumble/MainWindow.cpp index 057005fd85f..a2d4db7d482 100644 --- a/src/mumble/MainWindow.cpp +++ b/src/mumble/MainWindow.cpp @@ -43,6 +43,7 @@ #include "ServerHandler.h" #include "ServerInformation.h" #include "Settings.h" +#include "SleepInhibitor.h" #include "SvgIcon.h" #include "TalkingUI.h" #include "TextMessage.h" @@ -208,6 +209,8 @@ MainWindow::MainWindow(QWidget *p) QObject::connect(this, &MainWindow::channelStateChanged, this, &MainWindow::on_channelStateChanged); QAccessible::installFactory(AccessibleSlider::semanticSliderFactory); + + m_sleepInhibitor = new SleepInhibitor(this); } // Loading a state that was stored by a different version of Qt can lead to a crash. @@ -3511,6 +3514,10 @@ void MainWindow::serverConnected() { MUSuppressAppNap(true); #endif + if (Global::get().s.bInhibitSleep) { + m_sleepInhibitor->setInhibit(true); + } + Global::get().l->clearIgnore(); Global::get().l->setIgnore(Log::UserJoin); Global::get().l->setIgnore(Log::OtherSelfMute); @@ -3579,6 +3586,8 @@ void MainWindow::serverDisconnected(QAbstractSocket::SocketError err, QString re MUSuppressAppNap(false); #endif + m_sleepInhibitor->setInhibit(false); + QString uname, pw, host; unsigned short port; Global::get().sh->getConnectionInfo(host, port, uname, pw); @@ -4222,6 +4231,10 @@ void MainWindow::openConfigDialog() { close(); } } + + if (Global::get().uiSession != 0) { + m_sleepInhibitor->setInhibit(Global::get().s.bInhibitSleep); + } } Global::get().inConfigUI = false; diff --git a/src/mumble/MainWindow.h b/src/mumble/MainWindow.h index 462630a48c6..4417060c5b6 100644 --- a/src/mumble/MainWindow.h +++ b/src/mumble/MainWindow.h @@ -42,6 +42,7 @@ class UserInformation; class VoiceRecorderDialog; class PositionalAudioViewer; class PTTButtonWidget; +class SleepInhibitor; namespace Search { class SearchDialog; @@ -206,6 +207,8 @@ class MainWindow : public QMainWindow, public Ui::MainWindow { std::stack< unsigned int > m_previousChannels; std::optional< unsigned int > m_movedBackFromChannel; + SleepInhibitor *m_sleepInhibitor; + static constexpr int stateVersion(); void createActions(); diff --git a/src/mumble/Settings.h b/src/mumble/Settings.h index 232f987e03e..5d10d4280f3 100644 --- a/src/mumble/Settings.h +++ b/src/mumble/Settings.h @@ -432,6 +432,7 @@ struct Settings { bool bLockLayout = false; bool bHideInTray = false; bool bStateInTray = true; + bool bInhibitSleep = false; bool bUsage = true; bool bShowUserCount = false; bool bShowVolumeAdjustments = true; diff --git a/src/mumble/SettingsKeys.h b/src/mumble/SettingsKeys.h index 6ff85bec573..d554033f989 100644 --- a/src/mumble/SettingsKeys.h +++ b/src/mumble/SettingsKeys.h @@ -193,6 +193,7 @@ const SettingsKey OVERLAY_HEADER_STATE = { "overlay_header_state const SettingsKey SERVER_FILTER_MODE_KEY = { "server_filter_mode" }; const SettingsKey HIDE_IN_TRAY_KEY = { "hide_in_tray" }; const SettingsKey DISPLAY_TALKING_STATE_IN_TRAY_KEY = { "display_talking_state_in_tray" }; +const SettingsKey INHIBIT_SLEEP_KEY = { "inhibit_sleep" }; const SettingsKey SEND_USAGE_STATISTICS_KEY = { "send_usage_statistics" }; const SettingsKey DISPLAY_USER_COUNT_KEY = { "display_user_count" }; const SettingsKey DISPLAY_VOLUME_ADJUSTMENTS_KEY = { "display_volume_adjustments" }; diff --git a/src/mumble/SettingsMacros.h b/src/mumble/SettingsMacros.h index 293e206cdda..939474c7a4f 100644 --- a/src/mumble/SettingsMacros.h +++ b/src/mumble/SettingsMacros.h @@ -152,6 +152,7 @@ PROCESS(ui, USER_DRAG_MODE_KEY, ceUserDrag) \ PROCESS(ui, ALWAYS_ON_TOP_KEY, aotbAlwaysOnTop) \ PROCESS(ui, QUIT_BEHAVIOR_KEY, quitBehavior) \ + PROCESS(ui, INHIBIT_SLEEP_KEY, bInhibitSleep) \ PROCESS(ui, SHOW_DEVELOPER_MENU_KEY, bEnableDeveloperMenu) \ PROCESS(ui, LOCK_LAYOUT_KEY, bLockLayout) \ PROCESS(ui, MINIMAL_VIEW_KEY, bMinimalView) \ diff --git a/src/mumble/SleepInhibitor.cpp b/src/mumble/SleepInhibitor.cpp new file mode 100644 index 00000000000..4fc15fa1c1b --- /dev/null +++ b/src/mumble/SleepInhibitor.cpp @@ -0,0 +1,94 @@ +// Copyright The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +#include "SleepInhibitor.h" + +#if defined(Q_OS_WIN) +# include +#elif defined(Q_OS_MAC) +# include +#elif defined(USE_DBUS) +# include +#endif + +SleepInhibitor::SleepInhibitor(QObject *parent) : QObject(parent) { +} + +SleepInhibitor::~SleepInhibitor() { + setInhibit(false); +} + +void SleepInhibitor::setInhibit(bool inhibit) { + if (inhibit == m_inhibited) { + return; + } + + if (inhibit) { + platformInhibit(); + } else { + platformDeinhibit(); + } + + m_inhibited = inhibit; +} + +void SleepInhibitor::platformInhibit() { +#if defined(Q_OS_WIN) + SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); +#elif defined(Q_OS_MAC) + CFStringRef reasonForActivity = CFStringCreateWithCString(kCFAllocatorDefault, "Mumble is in a call", kCFStringEncodingUTF8); + IOReturn success = IOPMAssertionCreateWithDescription(kIOPMAssertionTypeNoDisplaySleep, + reasonForActivity, + NULL, NULL, NULL, 0, NULL, + &m_assertionID); + CFRelease(reasonForActivity); + if (success != kIOReturnSuccess) { + qWarning("SleepInhibitor: Failed to create IOPMAssertion"); + m_assertionID = 0; + } +#elif defined(USE_DBUS) + QDBusInterface screensaver("org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver", QDBusConnection::sessionBus()); + if (screensaver.isValid()) { + QDBusReply reply = screensaver.call("Inhibit", "Mumble", "In a call"); + if (reply.isValid()) { + m_cookie = reply.value(); + } else { + qWarning() << "SleepInhibitor: ScreenSaver Inhibit call failed:" << reply.error().message(); + } + } + + QDBusInterface logind("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", QDBusConnection::systemBus()); + if (logind.isValid()) { + QDBusReply reply = logind.call("Inhibit", "sleep:idle", "Mumble", "In a call", "block"); + if (reply.isValid()) { + m_logindFD = reply.value(); + } else { + qWarning() << "SleepInhibitor: Logind Inhibit call failed:" << reply.error().message(); + } + } +#endif +} + +void SleepInhibitor::platformDeinhibit() { +#if defined(Q_OS_WIN) + SetThreadExecutionState(ES_CONTINUOUS); +#elif defined(Q_OS_MAC) + if (m_assertionID != 0) { + IOPMAssertionRelease(m_assertionID); + m_assertionID = 0; + } +#elif defined(USE_DBUS) + if (m_cookie != 0) { + QDBusInterface screensaver("org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver", QDBusConnection::sessionBus()); + if (screensaver.isValid()) { + screensaver.call("UnInhibit", m_cookie); + } + m_cookie = 0; + } + if (m_logindFD.isValid()) { + m_logindFD = QDBusUnixFileDescriptor(); + } +#endif +} diff --git a/src/mumble/SleepInhibitor.h b/src/mumble/SleepInhibitor.h new file mode 100644 index 00000000000..4fd6f9b9679 --- /dev/null +++ b/src/mumble/SleepInhibitor.h @@ -0,0 +1,41 @@ +// Copyright The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +#ifndef MUMBLE_MUMBLE_SLEEPINHIBITOR_H_ +#define MUMBLE_MUMBLE_SLEEPINHIBITOR_H_ + +#include + +#include +#if defined(USE_DBUS) +# include +#endif + +class SleepInhibitor : public QObject { + Q_OBJECT + Q_DISABLE_COPY(SleepInhibitor) + +public: + explicit SleepInhibitor(QObject *parent = nullptr); + ~SleepInhibitor() override; + + void setInhibit(bool inhibit); + +private: + bool m_inhibited = false; +#if defined(Q_OS_WIN) + // No extra state needed for Windows (ES_CONTINUOUS) +#elif defined(Q_OS_MAC) + uint32_t m_assertionID = 0; +#elif defined(USE_DBUS) + uint32_t m_cookie = 0; + QDBusUnixFileDescriptor m_logindFD; +#endif + + void platformInhibit(); + void platformDeinhibit(); +}; + +#endif