FrontendCommon: Add PlaySoundAsync()

This commit is contained in:
Connor McLaughlin 2022-09-21 22:44:52 +10:00
parent cba4bb4ab2
commit a4eb5f1d5d
10 changed files with 195 additions and 86 deletions

View file

@ -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()

View file

@ -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)

View file

@ -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"

View file

@ -23,12 +23,12 @@
</ClCompile>
<ClCompile Include="imgui_manager.cpp" />
<ClCompile Include="imgui_overlays.cpp" />
<ClCompile Include="inhibit_screensaver.cpp" />
<ClCompile Include="input_manager.cpp" />
<ClCompile Include="input_source.cpp" />
<ClCompile Include="opengl_host_display.cpp">
<ExcludedFromBuild Condition="'$(Platform)'=='ARM64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="platform_misc_win32.cpp" />
<ClCompile Include="postprocessing_chain.cpp" />
<ClCompile Include="postprocessing_shader.cpp" />
<ClCompile Include="postprocessing_shadergen.cpp" />
@ -61,7 +61,7 @@
</ClInclude>
<ClInclude Include="imgui_manager.h" />
<ClInclude Include="imgui_overlays.h" />
<ClInclude Include="inhibit_screensaver.h" />
<ClInclude Include="platform_misc.h" />
<ClInclude Include="input_manager.h" />
<ClInclude Include="input_source.h" />
<ClInclude Include="opengl_host_display.h">
@ -87,4 +87,4 @@
<Import Project="..\..\dep\msvc\vsprops\StaticLibrary.props" />
<Import Project="frontend-common.props" />
<Import Project="..\..\dep\msvc\vsprops\Targets.props" />
</Project>
</Project>

View file

@ -15,7 +15,6 @@
<ClCompile Include="postprocessing_chain.cpp" />
<ClCompile Include="cubeb_audio_stream.cpp" />
<ClCompile Include="fullscreen_ui.cpp" />
<ClCompile Include="inhibit_screensaver.cpp" />
<ClCompile Include="xaudio2_audio_stream.cpp" />
<ClCompile Include="d3d12_host_display.cpp" />
<ClCompile Include="imgui_impl_dx12.cpp" />
@ -30,6 +29,7 @@
<ClCompile Include="win32_raw_input_source.cpp" />
<ClCompile Include="dinput_source.cpp" />
<ClCompile Include="imgui_overlays.cpp" />
<ClCompile Include="platform_misc_win32.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="icon.h" />
@ -46,7 +46,7 @@
<ClInclude Include="postprocessing_chain.h" />
<ClInclude Include="cubeb_audio_stream.h" />
<ClInclude Include="fullscreen_ui.h" />
<ClInclude Include="inhibit_screensaver.h" />
<ClInclude Include="platform_misc.h" />
<ClInclude Include="xaudio2_audio_stream.h" />
<ClInclude Include="d3d12_host_display.h" />
<ClInclude Include="imgui_impl_dx12.h" />

View file

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

View file

@ -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

View file

@ -0,0 +1,77 @@
#include "platform_misc.h"
#include "common/log.h"
#include "common/string.h"
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <cinttypes>
Log_SetChannel(FrontendCommon);
#import <AppKit/AppKit.h>
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<u64>(reinterpret_cast<uintptr_t>(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;
}

View file

@ -1,54 +1,9 @@
#include "inhibit_screensaver.h"
#include "common/log.h"
#include "common/string.h"
#include "platform_misc.h"
#include <cinttypes>
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 <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>
@ -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<char**>(argv), environ);
return (res == 0);
#else
return false;
#endif
}

View file

@ -0,0 +1,61 @@
#include "common/log.h"
#include "common/string.h"
#include "common/string_util.h"
#include "platform_misc.h"
#include <cinttypes>
Log_SetChannel(FrontendCommon);
#include "common/windows_headers.h"
#include <mmsystem.h>
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<u64>(reinterpret_cast<uintptr_t>(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);
}