Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions runtime/include/aurora_events.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "friend_code_keyboard.h"
#include "settings_overlay.h"
#include "steam_input_layout.h"
#include "runtime_config.h"
#include "gx_worker.h"
#include "perf_stats.h"
Expand Down Expand Up @@ -63,6 +64,7 @@ inline void Flush(bool force = false) {
// process directly and do not run the crash/atexit paths.
[[noreturn]] inline void ExitForAuroraWindowClose() noexcept {
settings_overlay::ReleaseControllers();
SteamInputLayout::Release();
WindowPlacementPersistence::Flush(true);
#if defined(_WIN32)
::ExitProcess(0);
Expand Down Expand Up @@ -120,6 +122,7 @@ inline void ProcessAuroraEvents(const AuroraEvent* events) {
}
settings_overlay::HandleEvents(events);
FriendCodeKeyboard::HandleEvents(events);
SteamInputLayout::HandleEvents(events);
}

inline void ApplyPendingMkwDynamicAspectSurface() {
Expand Down
23 changes: 23 additions & 0 deletions runtime/include/runtime_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ struct RuntimeUserConfig {
std::optional<int32_t> wiiIrSensitivity;
std::optional<int32_t> wiiSpeakerVolume;
std::optional<bool> wiiRumble;
// Outside a Steam launch, ask a running Steam client for a game controller layout while
// the window has focus instead of its desktop one (buttons bound to mouse and keyboard).
std::optional<bool> steamInputOverride;
std::optional<uint32_t> steamInputAppId;
std::optional<bool> networkEnabled;
std::optional<bool> discordPresenceEnabled;
// The application ID of the WiiCompiled Discord application. This is only
Expand Down Expand Up @@ -308,6 +312,12 @@ inline void EnsureConfigFile() {
"mix_worker = true\n\n"
"[network]\n"
"enabled = true\n\n"
"[controller]\n"
"# Outside a Steam launch, Steam's desktop layout binds controller buttons\n"
"# to mouse and keyboard. While the game has focus, ask a running Steam\n"
"# client to use the game layout of steam_input_app_id instead.\n"
"steam_input_override = true\n"
"# steam_input_app_id = 480\n\n"
"[discord]\n"
"# Rich Presence talks only to a locally-running Discord client.\n"
"# Retro Rewind supplies its official app ID automatically. Set this\n"
Expand Down Expand Up @@ -489,6 +499,10 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
config.wiiSpeakerVolume = *value;
}
config.wiiRumble = FindConfigValue<bool>(document, "controller", "wii_rumble");
config.steamInputOverride = FindConfigValue<bool>(document, "controller", "steam_input_override");
if (const auto value = FindConfigUint(document, "controller", "steam_input_app_id"); value && *value != 0) {
config.steamInputAppId = *value;
}
config.networkEnabled = FindConfigValue<bool>(document, "network", "enabled");
config.discordPresenceEnabled = FindConfigValue<bool>(document, "discord", "enabled");
config.discordClientId = FindConfigValue<std::string>(document, "discord", "client_id");
Expand Down Expand Up @@ -895,6 +909,15 @@ inline bool SetWiiRemotesEnabled(bool value) {
return true;
}

inline bool SteamInputOverrideEnabled(bool fallback = true) {
return Get().steamInputOverride.value_or(fallback);
}

// Spacewar, Valve's Steamworks sample app, available to every Steam account.
inline uint32_t SteamInputAppId(uint32_t fallback = 480) {
return Get().steamInputAppId.value_or(fallback);
}

inline bool WiiAutoPairEnabled(bool fallback = true) {
return Get().wiiAutoPair.value_or(fallback);
}
Expand Down
17 changes: 17 additions & 0 deletions runtime/include/steam_input_layout.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#pragma once

#include <aurora/event.h>

#include <cstdint>

// Outside a Steam launch, Steam applies its desktop controller layout, which binds
// controller buttons to mouse and keyboard actions. While the game window has focus,
// a running Steam client is asked to apply the given app's game layout instead.
namespace SteamInputLayout {

void Initialize(bool enabled, uint32_t appId);
void HandleEvents(const AuroraEvent* events);
// Restores the desktop layout. Idempotent; safe to call from any exit path.
void Release() noexcept;

} // namespace SteamInputLayout
17 changes: 17 additions & 0 deletions runtime/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include <mmsystem.h>
#include <dbghelp.h>
#else
#include <fcntl.h>
#include <signal.h>
#if defined(__x86_64__)
// Only the x86 POSIX fault path inspects ucontext_t to recover the page-fault
Expand Down Expand Up @@ -68,6 +69,7 @@
#include "aurora_events.h"
#include "wiimote_host.h"
#include "discord_presence.h"
#include "steam_input_layout.h"
#include "fiber_manager.h"
#include "gx_worker.h"
#include "hle_stubs.h"
Expand Down Expand Up @@ -337,6 +339,9 @@ bool InstallTranscriptPipe(int& outReadFd, int& outWriteFd, int targetFd) {
if (::pipe(pipeFds) != 0) {
return false;
}
// Spawned helpers must not keep the transcript open; its reader waits for EOF on shutdown.
::fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC);
::fcntl(pipeFds[1], F_SETFD, FD_CLOEXEC);
#endif

outReadFd = pipeFds[0];
Expand Down Expand Up @@ -1098,6 +1103,7 @@ LONG ReportFatalSehAndExit(EXCEPTION_POINTERS* info) {
<< " at host address 0x" << record->ExceptionInformation[1];
}
popupDetails << ".\n\nThe process transcript and crash log contain the full CPU and stack diagnostics.";
SteamInputLayout::Release();
ShowRuntimeFatalPopup("a native crash occurred", popupDetails.str());
DumpHostStackTrace();

Expand Down Expand Up @@ -1198,6 +1204,7 @@ void PosixMemoryFaultHandler(int sig, siginfo_t* info, void* ucontextVoid) {
}
popupDetails << ".\n\nThe process transcript and crash log contain the full CPU and stack "
"diagnostics.";
SteamInputLayout::Release();
ShowRuntimeFatalPopup("a native crash occurred", popupDetails.str());
DumpHostStackTrace();
WriteFatalLogImpl(sig == SIGBUS ? "sigbus" : "sigsegv");
Expand Down Expand Up @@ -1231,6 +1238,7 @@ void AbortSignalHandler(int signum) {
// abort() bypasses atexit, so the guest-memory fault summary has to be
// emitted here too. It is idempotent, so a later AtExitHandler is a no-op.
GuestFlat::LogFaultSummary();
SteamInputLayout::Release();

ShowRuntimeFatalPopup("a fatal internal error occurred",
"The process called abort while running the game or Aurora renderer.\n\n"
Expand Down Expand Up @@ -1266,6 +1274,7 @@ void SetRuntimeExitCode(int code) {

// Global handler called via atexit() to flush buffers before any exit
static void AtExitHandler() {
SteamInputLayout::Release();
// End-of-run guest memory report. This runs before the fatal-report check
// below because the counters describe the whole session and are just as
// interesting after a crash as after a clean exit.
Expand All @@ -1290,6 +1299,7 @@ static void AtExitHandler() {

// Global terminate handler for uncaught exceptions
static void TerminateHandler() {
SteamInputLayout::Release();
// Skip detailed dump if already reported
if (g_fatalErrorReported.load(std::memory_order_acquire)) {
std::fflush(stderr);
Expand Down Expand Up @@ -1480,6 +1490,10 @@ int RuntimeMain(int argc, char** argv) {
UpdateMkwDynamicAspectSurface(auroraInfo.windowSize.native_fb_width,
auroraInfo.windowSize.native_fb_height);
settings_overlay::InitializeRuntimeSettings();
if (!g_headless) {
SteamInputLayout::Initialize(RuntimeConfigFile::SteamInputOverrideEnabled(),
RuntimeConfigFile::SteamInputAppId());
}
if (BootRace::Requested()) {
aurora_set_skip_unready_pipelines(false);
}
Expand Down Expand Up @@ -1525,6 +1539,7 @@ int RuntimeMain(int argc, char** argv) {
Fiber::GuestFiberManager::Shutdown();
WindowPlacementPersistence::Flush(true);
Wiimote::Shutdown();
SteamInputLayout::Release();
GxWorker::Stop();
aurora_shutdown();
DiscordPresence::Shutdown();
Expand All @@ -1539,6 +1554,7 @@ int RuntimeMain(int argc, char** argv) {
<< " len=0x" << ex.length()
<< std::dec << std::nouppercase
<< " reason=" << ex.reason();
SteamInputLayout::Release();
ShowRuntimeFatalPopup("a guest memory access was out of bounds", details.str());
WriteFatalLogImpl("access_violation", details.str());
SetRuntimeExitCodeImpl(1);
Expand All @@ -1553,6 +1569,7 @@ int RuntimeMain(int argc, char** argv) {
} catch (const std::exception& ex) {
std::cerr << "Runtime error: " << ex.what() << std::endl;
SystemBridge::DumpCpuState(TryGetCpuContext());
SteamInputLayout::Release();
ShowRuntimeFatalPopup("a runtime exception occurred", ex.what());
WriteFatalLogImpl("exception");
SetRuntimeExitCodeImpl(1);
Expand Down
Loading
Loading