From a4eb5f1d5d9ff69058126a1aea8a147e7da2d301 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Wed, 21 Sep 2022 22:44:52 +1000 Subject: [PATCH] FrontendCommon: Add PlaySoundAsync() --- src/duckstation-qt/qthost.cpp | 8 ++ src/frontend-common/CMakeLists.txt | 17 +++- src/frontend-common/common_host.cpp | 2 +- src/frontend-common/frontend-common.vcxproj | 6 +- .../frontend-common.vcxproj.filters | 4 +- src/frontend-common/inhibit_screensaver.h | 7 -- src/frontend-common/platform_misc.h | 10 +++ src/frontend-common/platform_misc_mac.mm | 77 ++++++++++++++++ ...screensaver.cpp => platform_misc_unix.cpp} | 89 ++++--------------- src/frontend-common/platform_misc_win32.cpp | 61 +++++++++++++ 10 files changed, 195 insertions(+), 86 deletions(-) delete mode 100644 src/frontend-common/inhibit_screensaver.h create mode 100644 src/frontend-common/platform_misc.h create mode 100644 src/frontend-common/platform_misc_mac.mm rename src/frontend-common/{inhibit_screensaver.cpp => platform_misc_unix.cpp} (55%) create mode 100644 src/frontend-common/platform_misc_win32.cpp diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index d3196be44..a2eab153c 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -1809,6 +1809,14 @@ void QtHost::HookSignals() { std::signal(SIGINT, SignalHandler); std::signal(SIGTERM, SignalHandler); + +#ifdef __linux__ + // Ignore SIGCHLD by default on Linux, since we kick off aplay asynchronously. + struct sigaction sa_chld = {}; + sigemptyset(&sa_chld.sa_mask); + sa_chld.sa_flags = SA_SIGINFO | SA_RESTART | SA_NOCLDSTOP | SA_NOCLDWAIT; + sigaction(SIGCHLD, &sa_chld, nullptr); +#endif } void QtHost::InitializeEarlyConsole() diff --git a/src/frontend-common/CMakeLists.txt b/src/frontend-common/CMakeLists.txt index 7476cfc34..4e87affdf 100644 --- a/src/frontend-common/CMakeLists.txt +++ b/src/frontend-common/CMakeLists.txt @@ -10,8 +10,6 @@ add_library(frontend-common host_settings.cpp icon.cpp icon.h - inhibit_screensaver.cpp - inhibit_screensaver.h input_manager.cpp input_manager.h input_source.cpp @@ -22,6 +20,7 @@ add_library(frontend-common imgui_manager.h imgui_overlays.cpp imgui_overlays.h + platform_misc.h postprocessing_chain.cpp postprocessing_chain.h postprocessing_shader.cpp @@ -81,9 +80,21 @@ if(ENABLE_VULKAN) ) endif() -if(APPLE) +if(WIN32) + target_sources(frontend-common PRIVATE + platform_misc_win32.cpp + ) + target_link_libraries(frontend-common PRIVATE winmm.lib) +elseif(APPLE) find_library(IOK_LIBRARY IOKit REQUIRED) target_link_libraries(frontend-common PRIVATE "${IOK_LIBRARY}") + target_sources(frontend-common PRIVATE + platform_misc_mac.mm + ) +elseif(NOT ANDROID) + target_sources(frontend-common PRIVATE + platform_misc_unix.cpp + ) endif() if(SDL2_FOUND) diff --git a/src/frontend-common/common_host.cpp b/src/frontend-common/common_host.cpp index fbad4a136..8541ba393 100644 --- a/src/frontend-common/common_host.cpp +++ b/src/frontend-common/common_host.cpp @@ -32,7 +32,7 @@ #include "imgui_fullscreen.h" #include "imgui_manager.h" #include "imgui_overlays.h" -#include "inhibit_screensaver.h" +#include "platform_misc.h" #include "input_manager.h" #include "scmversion/scmversion.h" #include "util/audio_stream.h" diff --git a/src/frontend-common/frontend-common.vcxproj b/src/frontend-common/frontend-common.vcxproj index b54c008f9..8fa7f260b 100644 --- a/src/frontend-common/frontend-common.vcxproj +++ b/src/frontend-common/frontend-common.vcxproj @@ -23,12 +23,12 @@ - true + @@ -61,7 +61,7 @@ - + @@ -87,4 +87,4 @@ - + \ No newline at end of file diff --git a/src/frontend-common/frontend-common.vcxproj.filters b/src/frontend-common/frontend-common.vcxproj.filters index b7d37a9ff..c01c27fab 100644 --- a/src/frontend-common/frontend-common.vcxproj.filters +++ b/src/frontend-common/frontend-common.vcxproj.filters @@ -15,7 +15,6 @@ - @@ -30,6 +29,7 @@ + @@ -46,7 +46,7 @@ - + diff --git a/src/frontend-common/inhibit_screensaver.h b/src/frontend-common/inhibit_screensaver.h deleted file mode 100644 index 1fef23887..000000000 --- a/src/frontend-common/inhibit_screensaver.h +++ /dev/null @@ -1,7 +0,0 @@ -#include "common/window_info.h" - -namespace FrontendCommon -{ -void SuspendScreensaver(const WindowInfo& wi); -void ResumeScreensaver(); -} diff --git a/src/frontend-common/platform_misc.h b/src/frontend-common/platform_misc.h new file mode 100644 index 000000000..2c1a6ee58 --- /dev/null +++ b/src/frontend-common/platform_misc.h @@ -0,0 +1,10 @@ +#include "common/window_info.h" + +namespace FrontendCommon { +void SuspendScreensaver(const WindowInfo& wi); +void ResumeScreensaver(); + +/// Abstracts platform-specific code for asynchronously playing a sound. +/// On Windows, this will use PlaySound(). On Linux, it will shell out to aplay. On MacOS, it uses NSSound. +bool PlaySoundAsync(const char* path); +} // namespace FrontendCommon diff --git a/src/frontend-common/platform_misc_mac.mm b/src/frontend-common/platform_misc_mac.mm new file mode 100644 index 000000000..2cb34725e --- /dev/null +++ b/src/frontend-common/platform_misc_mac.mm @@ -0,0 +1,77 @@ +#include "platform_misc.h" +#include "common/log.h" +#include "common/string.h" +#include +#include +Log_SetChannel(FrontendCommon); + +#import + +static IOPMAssertionID s_prevent_idle_assertion = kIOPMNullAssertionID; + +static bool SetScreensaverInhibitMacOS(bool inhibit) +{ + 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; + } +} + +static bool s_screensaver_suspended; +static WindowInfo s_screensaver_suspender; + +void FrontendCommon::SuspendScreensaver(const WindowInfo& wi) +{ + if (s_screensaver_suspended && + (s_screensaver_suspender.type != wi.type || s_screensaver_suspender.window_handle != wi.window_handle)) + ResumeScreensaver(); + + if (!SetScreensaverInhibitMacOS(true)) + { + Log_ErrorPrintf("Failed to suspend screensaver."); + return; + } + + Log_InfoPrintf("Screensaver suspended by 0x%" PRIx64 ".", + static_cast(reinterpret_cast(wi.window_handle))); + s_screensaver_suspended = true; + s_screensaver_suspender = wi; +} + +void FrontendCommon::ResumeScreensaver() +{ + if (!s_screensaver_suspended) + return; + + if (!SetScreensaverInhibitMacOS(false)) + Log_ErrorPrint("Failed to resume screensaver."); + else + Log_InfoPrint("Screensaver resumed."); + + s_screensaver_suspended = false; + s_screensaver_suspender = {}; +} + +bool FrontendCommon::PlaySoundAsync(const char* path) +{ + NSString* nspath = [[NSString alloc] initWithUTF8String:path]; + NSSound* sound = [[NSSound alloc] initWithContentsOfFile:nspath byReference:YES]; + const bool result = [sound play]; + [sound release]; + [nspath release]; + return result; +} diff --git a/src/frontend-common/inhibit_screensaver.cpp b/src/frontend-common/platform_misc_unix.cpp similarity index 55% rename from src/frontend-common/inhibit_screensaver.cpp rename to src/frontend-common/platform_misc_unix.cpp index 9a0c07b15..1f7ae9ecb 100644 --- a/src/frontend-common/inhibit_screensaver.cpp +++ b/src/frontend-common/platform_misc_unix.cpp @@ -1,54 +1,9 @@ -#include "inhibit_screensaver.h" #include "common/log.h" #include "common/string.h" +#include "platform_misc.h" #include Log_SetChannel(FrontendCommon); -#if defined(_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 - -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 #include @@ -76,15 +31,7 @@ static bool SetScreensaverInhibitX11(bool inhibit, const WindowInfo& wi) 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; + return true; } #endif // USE_X11 @@ -93,16 +40,6 @@ static bool SetScreensaverInhibit(bool inhibit, const WindowInfo& wi) { switch (wi.type) { -#if defined(_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); @@ -114,12 +51,10 @@ static bool SetScreensaverInhibit(bool inhibit, const WindowInfo& wi) } } -namespace FrontendCommon { - static bool s_screensaver_suspended; static WindowInfo s_screensaver_suspender; -void SuspendScreensaver(const WindowInfo& wi) +void FrontendCommon::SuspendScreensaver(const WindowInfo& wi) { if (s_screensaver_suspended && (s_screensaver_suspender.type != wi.type || s_screensaver_suspender.window_handle != wi.window_handle)) @@ -137,7 +72,7 @@ void SuspendScreensaver(const WindowInfo& wi) s_screensaver_suspender = wi; } -void ResumeScreensaver() +void FrontendCommon::ResumeScreensaver() { if (!s_screensaver_suspended) return; @@ -151,4 +86,18 @@ void ResumeScreensaver() s_screensaver_suspender = {}; } -} // namespace FrontendCommon +bool FrontendCommon::PlaySoundAsync(const char* path) +{ +#ifdef __linux__ + // This is... pretty awful. But I can't think of a better way without linking to e.g. gstreamer. + const char* cmdname = "aplay"; + const char* argv[] = {cmdname, path, nullptr}; + pid_t pid; + + // Since we set SA_NOCLDWAIT in Qt, we don't need to wait here. + int res = posix_spawnp(&pid, cmdname, nullptr, nullptr, const_cast(argv), environ); + return (res == 0); +#else + return false; +#endif +} diff --git a/src/frontend-common/platform_misc_win32.cpp b/src/frontend-common/platform_misc_win32.cpp new file mode 100644 index 000000000..e3179b326 --- /dev/null +++ b/src/frontend-common/platform_misc_win32.cpp @@ -0,0 +1,61 @@ +#include "common/log.h" +#include "common/string.h" +#include "common/string_util.h" +#include "platform_misc.h" +#include +Log_SetChannel(FrontendCommon); + +#include "common/windows_headers.h" +#include + +static bool SetScreensaverInhibitWin32(bool inhibit) +{ + if (SetThreadExecutionState(ES_CONTINUOUS | (inhibit ? (ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED) : 0)) == NULL) + { + Log_ErrorPrintf("SetThreadExecutionState() failed: %d", GetLastError()); + return false; + } + + return true; +} + +static bool s_screensaver_suspended; +static WindowInfo s_screensaver_suspender; + +void FrontendCommon::SuspendScreensaver(const WindowInfo& wi) +{ + if (s_screensaver_suspended && + (s_screensaver_suspender.type != wi.type || s_screensaver_suspender.window_handle != wi.window_handle)) + ResumeScreensaver(); + + if (!SetScreensaverInhibitWin32(true)) + { + Log_ErrorPrintf("Failed to suspend screensaver."); + return; + } + + Log_InfoPrintf("Screensaver suspended by 0x%" PRIx64 ".", + static_cast(reinterpret_cast(wi.window_handle))); + s_screensaver_suspended = true; + s_screensaver_suspender = wi; +} + +void FrontendCommon::ResumeScreensaver() +{ + if (!s_screensaver_suspended) + return; + + if (!SetScreensaverInhibitWin32(false)) + Log_ErrorPrint("Failed to resume screensaver."); + else + Log_InfoPrint("Screensaver resumed."); + + s_screensaver_suspended = false; + s_screensaver_suspender = {}; +} + +bool FrontendCommon::PlaySoundAsync(const char* path) +{ + const std::wstring wpath(StringUtil::UTF8StringToWideString(path)); + return PlaySoundW(wpath.c_str(), NULL, SND_ASYNC | SND_NODEFAULT); +}