FrontendCommon: Add option to inhibit screensaver

This commit is contained in:
Connor McLaughlin 2021-06-20 15:41:01 +10:00
parent 531845a0c7
commit d9412f9fcd
13 changed files with 218 additions and 6 deletions

View File

@ -61,6 +61,7 @@ public:
virtual ~HostDisplay(); virtual ~HostDisplay();
ALWAYS_INLINE const WindowInfo& GetWindowInfo() const { return m_window_info; }
ALWAYS_INLINE s32 GetWindowWidth() const { return static_cast<s32>(m_window_info.surface_width); } ALWAYS_INLINE s32 GetWindowWidth() const { return static_cast<s32>(m_window_info.surface_width); }
ALWAYS_INLINE s32 GetWindowHeight() const { return static_cast<s32>(m_window_info.surface_height); } ALWAYS_INLINE s32 GetWindowHeight() const { return static_cast<s32>(m_window_info.surface_height); }
ALWAYS_INLINE float GetWindowScale() const { return m_window_info.surface_scale; } ALWAYS_INLINE float GetWindowScale() const { return m_window_info.surface_scale; }

View File

@ -487,6 +487,7 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
si.SetFloatValue("Main", "TurboSpeed", 0.0f); si.SetFloatValue("Main", "TurboSpeed", 0.0f);
si.SetBoolValue("Main", "SyncToHostRefreshRate", false); si.SetBoolValue("Main", "SyncToHostRefreshRate", false);
si.SetBoolValue("Main", "IncreaseTimerResolution", true); si.SetBoolValue("Main", "IncreaseTimerResolution", true);
si.SetBoolValue("Main", "InhibitScreensaver", true);
si.SetBoolValue("Main", "StartPaused", false); si.SetBoolValue("Main", "StartPaused", false);
si.SetBoolValue("Main", "StartFullscreen", false); si.SetBoolValue("Main", "StartFullscreen", false);
si.SetBoolValue("Main", "PauseOnFocusLoss", false); si.SetBoolValue("Main", "PauseOnFocusLoss", false);

View File

@ -147,6 +147,7 @@ void Settings::Load(SettingsInterface& si)
turbo_speed = si.GetFloatValue("Main", "TurboSpeed", 0.0f); turbo_speed = si.GetFloatValue("Main", "TurboSpeed", 0.0f);
sync_to_host_refresh_rate = si.GetBoolValue("Main", "SyncToHostRefreshRate", false); sync_to_host_refresh_rate = si.GetBoolValue("Main", "SyncToHostRefreshRate", false);
increase_timer_resolution = si.GetBoolValue("Main", "IncreaseTimerResolution", true); increase_timer_resolution = si.GetBoolValue("Main", "IncreaseTimerResolution", true);
inhibit_screensaver = si.GetBoolValue("Main", "InhibitScreensaver", true);
start_paused = si.GetBoolValue("Main", "StartPaused", false); start_paused = si.GetBoolValue("Main", "StartPaused", false);
start_fullscreen = si.GetBoolValue("Main", "StartFullscreen", false); start_fullscreen = si.GetBoolValue("Main", "StartFullscreen", false);
pause_on_focus_loss = si.GetBoolValue("Main", "PauseOnFocusLoss", false); pause_on_focus_loss = si.GetBoolValue("Main", "PauseOnFocusLoss", false);
@ -341,6 +342,7 @@ void Settings::Save(SettingsInterface& si) const
si.SetFloatValue("Main", "TurboSpeed", turbo_speed); si.SetFloatValue("Main", "TurboSpeed", turbo_speed);
si.SetBoolValue("Main", "SyncToHostRefreshRate", sync_to_host_refresh_rate); si.SetBoolValue("Main", "SyncToHostRefreshRate", sync_to_host_refresh_rate);
si.SetBoolValue("Main", "IncreaseTimerResolution", increase_timer_resolution); si.SetBoolValue("Main", "IncreaseTimerResolution", increase_timer_resolution);
si.SetBoolValue("Main", "InhibitScreensaver", inhibit_screensaver);
si.SetBoolValue("Main", "StartPaused", start_paused); si.SetBoolValue("Main", "StartPaused", start_paused);
si.SetBoolValue("Main", "StartFullscreen", start_fullscreen); si.SetBoolValue("Main", "StartFullscreen", start_fullscreen);
si.SetBoolValue("Main", "PauseOnFocusLoss", pause_on_focus_loss); si.SetBoolValue("Main", "PauseOnFocusLoss", pause_on_focus_loss);

View File

@ -86,6 +86,7 @@ struct Settings
float turbo_speed = 0.0f; float turbo_speed = 0.0f;
bool sync_to_host_refresh_rate = true; bool sync_to_host_refresh_rate = true;
bool increase_timer_resolution = true; bool increase_timer_resolution = true;
bool inhibit_screensaver = false;
bool start_paused = false; bool start_paused = false;
bool start_fullscreen = false; bool start_fullscreen = false;
bool pause_on_focus_loss = false; bool pause_on_focus_loss = false;

View File

@ -25,6 +25,8 @@ GeneralSettingsWidget::GeneralSettingsWidget(QtHostInterface* host_interface, QW
false); false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.hideCursorInFullscreen, "Main", SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.hideCursorInFullscreen, "Main",
"HideCursorInFullscreen", true); "HideCursorInFullscreen", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.inhibitScreensaver, "Main", "InhibitScreensaver",
true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.renderToMain, "Main", "RenderToMainWindow", true); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.renderToMain, "Main", "RenderToMainWindow", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.saveStateOnExit, "Main", "SaveStateOnExit", true); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.saveStateOnExit, "Main", "SaveStateOnExit", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.confirmPowerOff, "Main", "ConfirmPowerOff", true); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.confirmPowerOff, "Main", "ConfirmPowerOff", true);
@ -51,6 +53,9 @@ GeneralSettingsWidget::GeneralSettingsWidget(QtHostInterface* host_interface, QW
tr("Automatically switches to fullscreen mode when a game is started.")); tr("Automatically switches to fullscreen mode when a game is started."));
dialog->registerWidgetHelp(m_ui.hideCursorInFullscreen, tr("Hide Cursor In Fullscreen"), tr("Checked"), dialog->registerWidgetHelp(m_ui.hideCursorInFullscreen, tr("Hide Cursor In Fullscreen"), tr("Checked"),
tr("Hides the mouse pointer/cursor when the emulator is in fullscreen mode.")); tr("Hides the mouse pointer/cursor when the emulator is in fullscreen mode."));
dialog->registerWidgetHelp(
m_ui.inhibitScreensaver, tr("Inhibit Screensaver"), tr("Checked"),
tr("Prevents the screen saver from activating and the host from sleeping while emulation is running."));
dialog->registerWidgetHelp( dialog->registerWidgetHelp(
m_ui.renderToMain, tr("Render To Main Window"), tr("Checked"), m_ui.renderToMain, tr("Render To Main Window"), tr("Checked"),
tr("Renders the display of the simulated console to the main window of the application, over " tr("Renders the display of the simulated console to the main window of the application, over "
@ -81,7 +86,7 @@ GeneralSettingsWidget::GeneralSettingsWidget(QtHostInterface* host_interface, QW
tr("Enables the fullscreen UI mode, suitable for controller operation which is used in the NoGUI frontend.")); tr("Enables the fullscreen UI mode, suitable for controller operation which is used in the NoGUI frontend."));
// Since this one is compile-time selected, we don't put it in the .ui file. // Since this one is compile-time selected, we don't put it in the .ui file.
int current_col = 1; int current_col = 0;
int current_row = m_ui.formLayout_4->rowCount() - current_col; int current_row = m_ui.formLayout_4->rowCount() - current_col;
#ifdef WITH_DISCORD_PRESENCE #ifdef WITH_DISCORD_PRESENCE
{ {

View File

@ -39,7 +39,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="3" column="1">
<widget class="QCheckBox" name="inhibitScreensaver">
<property name="text">
<string>Inhibit Screensaver</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="renderToMain"> <widget class="QCheckBox" name="renderToMain">
<property name="text"> <property name="text">
<string>Render To Main Window</string> <string>Render To Main Window</string>
@ -74,21 +81,21 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="6" column="0">
<widget class="QCheckBox" name="applyGameSettings"> <widget class="QCheckBox" name="applyGameSettings">
<property name="text"> <property name="text">
<string>Apply Per-Game Settings</string> <string>Apply Per-Game Settings</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="6" column="1">
<widget class="QCheckBox" name="autoLoadCheats"> <widget class="QCheckBox" name="autoLoadCheats">
<property name="text"> <property name="text">
<string>Automatically Load Cheats</string> <string>Automatically Load Cheats</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="4" column="1">
<widget class="QCheckBox" name="loadDevicesFromSaveStates"> <widget class="QCheckBox" name="loadDevicesFromSaveStates">
<property name="text"> <property name="text">
<string>Load Devices From Save States</string> <string>Load Devices From Save States</string>
@ -102,7 +109,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="3" column="0">
<widget class="QCheckBox" name="enableFullscreenUI"> <widget class="QCheckBox" name="enableFullscreenUI">
<property name="text"> <property name="text">
<string>Enable Fullscreen UI</string> <string>Enable Fullscreen UI</string>

View File

@ -17,6 +17,8 @@ add_library(frontend-common
game_settings.h game_settings.h
icon.cpp icon.cpp
icon.h icon.h
inhibit_screensaver.cpp
inhibit_screensaver.h
ini_settings_interface.cpp ini_settings_interface.cpp
ini_settings_interface.h ini_settings_interface.h
input_overlay_ui.cpp input_overlay_ui.cpp
@ -89,6 +91,11 @@ if(USE_EVDEV)
) )
endif() endif()
if(USE_X11)
target_compile_definitions(frontend-common PRIVATE "-DUSE_X11=1")
target_include_directories(frontend-common PRIVATE "${X11_INCLUDE_DIR}")
endif()
if(ENABLE_DISCORD_PRESENCE) if(ENABLE_DISCORD_PRESENCE)
target_compile_definitions(frontend-common PUBLIC -DWITH_DISCORD_PRESENCE=1) target_compile_definitions(frontend-common PUBLIC -DWITH_DISCORD_PRESENCE=1)
target_link_libraries(frontend-common PRIVATE discord-rpc) target_link_libraries(frontend-common PRIVATE discord-rpc)

View File

@ -29,6 +29,7 @@
#include "imgui.h" #include "imgui.h"
#include "imgui_fullscreen.h" #include "imgui_fullscreen.h"
#include "imgui_styles.h" #include "imgui_styles.h"
#include "inhibit_screensaver.h"
#include "ini_settings_interface.h" #include "ini_settings_interface.h"
#include "input_overlay_ui.h" #include "input_overlay_ui.h"
#include "save_state_selector_ui.h" #include "save_state_selector_ui.h"
@ -975,6 +976,9 @@ void CommonHostInterface::OnSystemCreated()
if (g_settings.display_post_processing && !m_display->SetPostProcessingChain(g_settings.display_post_process_chain)) if (g_settings.display_post_processing && !m_display->SetPostProcessingChain(g_settings.display_post_process_chain))
AddOSDMessage(TranslateStdString("OSDMessage", "Failed to load post processing shader chain."), 20.0f); AddOSDMessage(TranslateStdString("OSDMessage", "Failed to load post processing shader chain."), 20.0f);
if (g_settings.inhibit_screensaver)
FrontendCommon::SuspendScreensaver(m_display->GetWindowInfo());
} }
void CommonHostInterface::OnSystemPaused(bool paused) void CommonHostInterface::OnSystemPaused(bool paused)
@ -990,6 +994,12 @@ void CommonHostInterface::OnSystemPaused(bool paused)
SetFullscreen(false); SetFullscreen(false);
StopControllerRumble(); StopControllerRumble();
FrontendCommon::ResumeScreensaver();
}
else
{
if (g_settings.inhibit_screensaver)
FrontendCommon::SuspendScreensaver(m_display->GetWindowInfo());
} }
UpdateSpeedLimiterState(); UpdateSpeedLimiterState();
@ -1007,6 +1017,7 @@ void CommonHostInterface::OnSystemDestroyed()
FullscreenUI::SystemDestroyed(); FullscreenUI::SystemDestroyed();
StopControllerRumble(); StopControllerRumble();
FrontendCommon::ResumeScreensaver();
} }
void CommonHostInterface::OnRunningGameChanged(const std::string& path, CDImage* image, const std::string& game_code, void CommonHostInterface::OnRunningGameChanged(const std::string& path, CDImage* image, const std::string& game_code,
@ -3021,6 +3032,14 @@ void CommonHostInterface::CheckForSettingsChanges(const Settings& old_settings)
m_display->SetPostProcessingChain({}); m_display->SetPostProcessingChain({});
} }
} }
if (g_settings.inhibit_screensaver != old_settings.inhibit_screensaver)
{
if (g_settings.inhibit_screensaver)
FrontendCommon::SuspendScreensaver(m_display->GetWindowInfo());
else
FrontendCommon::ResumeScreensaver();
}
} }
if (g_settings.log_level != old_settings.log_level || g_settings.log_filter != old_settings.log_filter || if (g_settings.log_level != old_settings.log_level || g_settings.log_filter != old_settings.log_filter ||

View File

@ -101,6 +101,7 @@
<ClCompile Include="imgui_impl_opengl3.cpp" /> <ClCompile Include="imgui_impl_opengl3.cpp" />
<ClCompile Include="imgui_impl_vulkan.cpp" /> <ClCompile Include="imgui_impl_vulkan.cpp" />
<ClCompile Include="imgui_styles.cpp" /> <ClCompile Include="imgui_styles.cpp" />
<ClCompile Include="inhibit_screensaver.cpp" />
<ClCompile Include="ini_settings_interface.cpp" /> <ClCompile Include="ini_settings_interface.cpp" />
<ClCompile Include="input_overlay_ui.cpp" /> <ClCompile Include="input_overlay_ui.cpp" />
<ClCompile Include="opengl_host_display.cpp" /> <ClCompile Include="opengl_host_display.cpp" />
@ -134,6 +135,7 @@
<ClInclude Include="imgui_impl_opengl3.h" /> <ClInclude Include="imgui_impl_opengl3.h" />
<ClInclude Include="imgui_impl_vulkan.h" /> <ClInclude Include="imgui_impl_vulkan.h" />
<ClInclude Include="imgui_styles.h" /> <ClInclude Include="imgui_styles.h" />
<ClInclude Include="inhibit_screensaver.h" />
<ClInclude Include="ini_settings_interface.h" /> <ClInclude Include="ini_settings_interface.h" />
<ClInclude Include="input_overlay_ui.h" /> <ClInclude Include="input_overlay_ui.h" />
<ClInclude Include="opengl_host_display.h" /> <ClInclude Include="opengl_host_display.h" />

View File

@ -32,6 +32,7 @@
<ClCompile Include="http_downloader_winhttp.cpp" /> <ClCompile Include="http_downloader_winhttp.cpp" />
<ClCompile Include="input_overlay_ui.cpp" /> <ClCompile Include="input_overlay_ui.cpp" />
<ClCompile Include="game_database.cpp" /> <ClCompile Include="game_database.cpp" />
<ClCompile Include="inhibit_screensaver.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="icon.h" /> <ClInclude Include="icon.h" />
@ -65,6 +66,7 @@
<ClInclude Include="http_downloader_winhttp.h" /> <ClInclude Include="http_downloader_winhttp.h" />
<ClInclude Include="input_overlay_ui.h" /> <ClInclude Include="input_overlay_ui.h" />
<ClInclude Include="game_database.h" /> <ClInclude Include="game_database.h" />
<ClInclude Include="inhibit_screensaver.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="font_roboto_regular.inl" /> <None Include="font_roboto_regular.inl" />

View File

@ -1325,6 +1325,10 @@ void DrawSettingsWindow()
settings_changed |= ToggleButtonForNonSetting( settings_changed |= ToggleButtonForNonSetting(
"Hide Cursor In Fullscreen", "Hides the mouse pointer/cursor when the emulator is in fullscreen mode.", "Hide Cursor In Fullscreen", "Hides the mouse pointer/cursor when the emulator is in fullscreen mode.",
"Main", "HideCursorInFullscreen", true); "Main", "HideCursorInFullscreen", true);
settings_changed |= ToggleButton(
"Inhibit Screensaver",
"Prevents the screen saver from activating and the host from sleeping while emulation is running.",
&s_settings_copy.inhibit_screensaver);
settings_changed |= settings_changed |=
ToggleButton("Load Devices From Save States", ToggleButton("Load Devices From Save States",
"When enabled, memory cards and controllers will be overwritten when save states are loaded.", "When enabled, memory cards and controllers will be overwritten when save states are loaded.",

View File

@ -0,0 +1,154 @@
#include "inhibit_screensaver.h"
#include "common/log.h"
#include "common/string.h"
#include <cinttypes>
Log_SetChannel(FrontendCommon);
#ifdef _WIN32
#include "common/windows_headers.h"
static bool SetScreensaverInhibitWin32(bool inhibit, const WindowInfo& wi)
{
if (SetThreadExecutionState(ES_CONTINUOUS | (inhibit ? (ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED) : 0)) == NULL)
{
Log_ErrorPrintf("SetThreadExecutionState() failed: %d", GetLastError());
return false;
}
return true;
}
#endif // _WIN32
#ifdef __APPLE__
#include <IOKit/pwr_mgt/IOPMLib.h>
static IOPMAssertionID s_prevent_idle_assertion = kIOPMNullAssertionID;
static bool SetScreensaverInhibitMacOS(bool inhibit, const WindowInfo& wi)
{
if (inhibit)
{
const CFStringRef reason = CFSTR("System Running");
if (IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep, kIOPMAssertionLevelOn, reason,
&s_prevent_idle_assertion) != kIOReturnSuccess)
{
Log_ErrorPrintf("IOPMAssertionCreateWithName() failed");
return false;
}
return true;
}
else
{
IOPMAssertionRelease(s_prevent_idle_assertion);
s_prevent_idle_assertion = kIOPMNullAssertionID;
return true;
}
}
#endif // __APPLE__
#ifdef USE_X11
#include <cstdio>
#include <spawn.h>
#include <sys/wait.h>
#include <unistd.h>
static bool SetScreensaverInhibitX11(bool inhibit, const WindowInfo& wi)
{
TinyString command;
command.AppendString("xdg-screensaver");
TinyString operation;
operation.AppendString(inhibit ? "suspend" : "resume");
TinyString id;
id.Format("0x%" PRIx64, static_cast<u64>(reinterpret_cast<uintptr_t>(wi.window_handle)));
char* argv[4] = {command.GetWriteableCharArray(), operation.GetWriteableCharArray(), id.GetWriteableCharArray(),
nullptr};
pid_t pid;
int res = posix_spawnp(&pid, "xdg-screensaver", nullptr, nullptr, argv, environ);
if (res != 0)
{
Log_ErrorPrintf("posix_spawnp() failed: %d", res);
return false;
}
int status = 0;
while (waitpid(pid, &status, 0) == -1)
;
if (WEXITSTATUS(status) == 0)
return true;
Log_ErrorPrintf("xdg-screensaver returned error %d", WEXITSTATUS(status));
return false;
}
#endif // USE_X11
static bool SetScreensaverInhibit(bool inhibit, const WindowInfo& wi)
{
switch (wi.type)
{
#ifdef _WIN32
case WindowInfo::Type::Win32:
return SetScreensaverInhibitWin32(inhibit, wi);
#endif
#ifdef __APPLE__
case WindowInfo::Type::MacOS:
return SetScreensaverInhibitMacOS(inhibit, wi);
#endif
#ifdef USE_X11
case WindowInfo::Type::X11:
return SetScreensaverInhibitX11(inhibit, wi);
#endif
default:
Log_ErrorPrintf("Unknown type: %u", static_cast<unsigned>(wi.type));
return false;
}
}
namespace FrontendCommon {
static bool s_screensaver_suspended;
static WindowInfo s_screensaver_suspender;
void SuspendScreensaver(const WindowInfo& wi)
{
if (s_screensaver_suspended &&
(s_screensaver_suspender.type != wi.type || s_screensaver_suspender.window_handle != wi.window_handle))
ResumeScreensaver();
if (!SetScreensaverInhibit(true, wi))
{
Log_ErrorPrintf("Failed to suspend screensaver.");
return;
}
Log_InfoPrintf("Screensaver suspended by 0x%" PRIx64 ".",
static_cast<u64>(reinterpret_cast<uintptr_t>(wi.window_handle)));
s_screensaver_suspended = true;
s_screensaver_suspender = wi;
}
void ResumeScreensaver()
{
if (!s_screensaver_suspended)
return;
if (!SetScreensaverInhibit(false, s_screensaver_suspender))
Log_ErrorPrint("Failed to resume screensaver.");
else
Log_InfoPrint("Screensaver resumed.");
s_screensaver_suspended = false;
s_screensaver_suspender = {};
}
} // namespace FrontendCommon

View File

@ -0,0 +1,7 @@
#include "common/window_info.h"
namespace FrontendCommon
{
void SuspendScreensaver(const WindowInfo& wi);
void ResumeScreensaver();
}