mirror of
https://github.com/thejerf/baijiSteamVR.git
synced 2026-10-17 23:01:32 +02:00
This fixes #14 and fixes #8 to my satisfaction. At least now, since the game starts in the user's visual field, it is obvious that they need to dismiss the Baiji UI to get to the game. This works like everything else in the Steam Frame windowing environment. Now the emulation never freezes, no matter how hard I focus off of Baiji. May still be some problems here. But this is the closest to correct it has been working yet.
367 lines
9.9 KiB
C++
367 lines
9.9 KiB
C++
// Copyright 2015 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include "DolphinQt/Host.h"
|
|
|
|
#include <functional>
|
|
|
|
#include <QAbstractEventDispatcher>
|
|
#include <QApplication>
|
|
#include <QGuiApplication>
|
|
#include <QLocale>
|
|
#include <QThread>
|
|
|
|
#include <imgui.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#include "Core/Config/MainSettings.h"
|
|
#include "Core/ConfigManager.h"
|
|
#include "Core/Core.h"
|
|
#include "Core/Debugger/PPCDebugInterface.h"
|
|
#include "Core/Host.h"
|
|
#include "Core/NetPlayProto.h"
|
|
#include "Core/State.h"
|
|
#include "Core/System.h"
|
|
|
|
#ifdef HAS_LIBMGBA
|
|
#include "DolphinQt/GBAWidget.h"
|
|
#endif
|
|
#include "DolphinQt/QtUtils/QueueOnObject.h"
|
|
#include "DolphinQt/Settings.h"
|
|
|
|
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
|
|
|
#include "UICommon/DiscordPresence.h"
|
|
|
|
#include "VideoCommon/AbstractGfx.h"
|
|
#include "VideoCommon/CullingCodeFinder.h"
|
|
#include "VideoCommon/Fifo.h"
|
|
#include "VideoCommon/Present.h"
|
|
#include "VideoCommon/VideoConfig.h"
|
|
|
|
Host::Host()
|
|
{
|
|
#ifdef BAIJI_STEAMVR
|
|
m_application_focused =
|
|
QGuiApplication::applicationState() == Qt::ApplicationState::ApplicationActive;
|
|
connect(qApp, &QGuiApplication::applicationStateChanged, this,
|
|
[this](Qt::ApplicationState state) {
|
|
m_application_focused.store(state == Qt::ApplicationState::ApplicationActive,
|
|
std::memory_order_release);
|
|
});
|
|
#endif
|
|
|
|
State::SetOnAfterLoadCallback([] {
|
|
auto& finder = CullingCodeFinder::GetInstance();
|
|
finder.NotifySavestateLoaded();
|
|
finder.AdvanceScan(Core::System::GetInstance());
|
|
Host_UpdateDisasmDialog();
|
|
});
|
|
}
|
|
|
|
Host::~Host()
|
|
{
|
|
State::SetOnAfterLoadCallback(nullptr);
|
|
}
|
|
|
|
Host* Host::GetInstance()
|
|
{
|
|
static Host* s_instance = new Host();
|
|
return s_instance;
|
|
}
|
|
|
|
bool Host::IsRenderToMain()
|
|
{
|
|
#ifdef BAIJI_STEAMVR
|
|
// Keep the main Qt interface available while emulating on Steam Frame. Batch mode
|
|
// intentionally uses a separate render window because its main window is hidden.
|
|
return !Settings::Instance().IsBatchModeEnabled();
|
|
#else
|
|
return Config::Get(Config::MAIN_RENDER_TO_MAIN);
|
|
#endif
|
|
}
|
|
|
|
void Host::SetRenderHandle(void* handle)
|
|
{
|
|
m_render_to_main = IsRenderToMain();
|
|
|
|
if (m_render_handle == handle)
|
|
return;
|
|
|
|
m_render_handle = handle;
|
|
if (g_presenter)
|
|
{
|
|
g_presenter->ChangeSurface(handle);
|
|
g_controller_interface.ChangeWindow(handle);
|
|
}
|
|
}
|
|
|
|
void Host::SetMainWindowHandle(void* handle)
|
|
{
|
|
m_main_window_handle = handle;
|
|
}
|
|
|
|
static void RunWithGPUThreadInactive(std::function<void()> f)
|
|
{
|
|
// Potentially any thread which shows panic alerts can be blocked on this returning.
|
|
// This means that, in order to avoid deadlocks, we need to be careful with how we
|
|
// synchronize with other threads. Note that the panic alert handler temporarily declares
|
|
// us as the CPU and/or GPU thread if the panic alert was requested by that thread.
|
|
|
|
// TODO: What about the unlikely case where the GPU thread calls the panic alert handler
|
|
// while the panic alert handler is processing a panic alert from the CPU thread?
|
|
|
|
if (Core::IsGPUThread())
|
|
{
|
|
// If we are the GPU thread, we can't call Core::PauseAndLock without getting a deadlock,
|
|
// since it would try to pause the GPU thread while that thread is waiting for us.
|
|
// However, since we know that the GPU thread is inactive, we can just run f directly.
|
|
|
|
f();
|
|
}
|
|
else if (Core::IsCPUThread())
|
|
{
|
|
// If we are the CPU thread in dual core mode, we can't call Core::PauseAndLock, for the
|
|
// same reason as above. Instead, we use Fifo::PauseAndLock to pause the GPU thread only.
|
|
// (Note that this case cannot be reached in single core mode, because in single core mode,
|
|
// the CPU and GPU threads are the same thread, and we already checked for the GPU thread.)
|
|
|
|
auto& system = Core::System::GetInstance();
|
|
const bool was_running = Core::GetState(system) == Core::State::Running;
|
|
auto& fifo = system.GetFifo();
|
|
fifo.PauseAndLock();
|
|
f();
|
|
fifo.RestoreState(was_running);
|
|
}
|
|
else
|
|
{
|
|
// If we reach here, we can call Core::PauseAndLock (which we do using a CPUThreadGuard).
|
|
const Core::CPUThreadGuard guard(Core::System::GetInstance());
|
|
f();
|
|
}
|
|
}
|
|
|
|
bool Host::GetRenderFocus()
|
|
{
|
|
#ifdef BAIJI_STEAMVR
|
|
// The main window, settings dialogs, and the render widget all belong to the same Qt
|
|
// application. Keep focus while switching among them; only lose it when Baiji itself
|
|
// becomes inactive.
|
|
return m_application_focused.load(std::memory_order_acquire);
|
|
#elif defined(_WIN32)
|
|
// Unfortunately Qt calls SetRenderFocus() with a slight delay compared to what we actually need
|
|
// to avoid inputs that cause a focus loss to be processed by the emulation
|
|
if (m_render_to_main && !m_render_fullscreen)
|
|
return GetForegroundWindow() == (HWND)m_main_window_handle.load();
|
|
return GetForegroundWindow() == (HWND)m_render_handle.load();
|
|
#else
|
|
return m_render_focus;
|
|
#endif
|
|
}
|
|
|
|
bool Host::GetRenderFullFocus()
|
|
{
|
|
return m_render_full_focus;
|
|
}
|
|
|
|
void Host::SetRenderFocus(bool focus)
|
|
{
|
|
m_render_focus = focus;
|
|
if (g_gfx && m_render_fullscreen && g_ActiveConfig.ExclusiveFullscreenEnabled())
|
|
{
|
|
RunWithGPUThreadInactive([focus] {
|
|
if (!Host::IsRenderToMain())
|
|
g_gfx->SetFullscreen(focus);
|
|
});
|
|
}
|
|
}
|
|
|
|
void Host::SetRenderFullFocus(bool focus)
|
|
{
|
|
m_render_full_focus = focus;
|
|
}
|
|
|
|
bool Host::GetGBAFocus()
|
|
{
|
|
#ifdef HAS_LIBMGBA
|
|
return qobject_cast<GBAWidget*>(QApplication::activeWindow()) != nullptr;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool Host::GetTASInputFocus() const
|
|
{
|
|
return m_tas_input_focus;
|
|
}
|
|
|
|
bool Host::GetRenderFullscreen()
|
|
{
|
|
return m_render_fullscreen;
|
|
}
|
|
|
|
void Host::SetRenderFullscreen(bool fullscreen)
|
|
{
|
|
m_render_fullscreen = fullscreen;
|
|
|
|
if (g_gfx && g_gfx->IsFullscreen() != fullscreen && g_ActiveConfig.ExclusiveFullscreenEnabled())
|
|
{
|
|
RunWithGPUThreadInactive([fullscreen] { g_gfx->SetFullscreen(fullscreen); });
|
|
}
|
|
}
|
|
|
|
void Host::SetTASInputFocus(const bool focus)
|
|
{
|
|
m_tas_input_focus = focus;
|
|
}
|
|
|
|
void Host::ResizeSurface(int new_width, int new_height)
|
|
{
|
|
if (g_presenter)
|
|
g_presenter->ResizeSurface();
|
|
}
|
|
|
|
std::vector<std::string> Host_GetPreferredLocales()
|
|
{
|
|
const QStringList ui_languages = QLocale::system().uiLanguages();
|
|
std::vector<std::string> converted_languages(ui_languages.size());
|
|
|
|
for (int i = 0; i < ui_languages.size(); ++i)
|
|
converted_languages[i] = ui_languages[i].toStdString();
|
|
|
|
return converted_languages;
|
|
}
|
|
|
|
void Host_Message(HostMessageID id)
|
|
{
|
|
if (id == HostMessageID::WMUserStop)
|
|
{
|
|
emit Host::GetInstance()->RequestStop();
|
|
}
|
|
else if (id == HostMessageID::WMUserJobDispatch)
|
|
{
|
|
// Just poke the main thread to get it to wake up, job dispatch
|
|
// will happen automatically before it goes back to sleep again.
|
|
QAbstractEventDispatcher::instance(qApp->thread())->wakeUp();
|
|
}
|
|
}
|
|
|
|
void Host_UpdateTitle(const std::string& title)
|
|
{
|
|
emit Host::GetInstance()->RequestTitle(QString::fromStdString(title));
|
|
}
|
|
|
|
bool Host_RendererHasFocus()
|
|
{
|
|
#ifdef BAIJI_STEAMVR
|
|
return Host::GetInstance()->GetRenderFocus();
|
|
#else
|
|
return Host::GetInstance()->GetRenderFocus() || Host::GetInstance()->GetGBAFocus();
|
|
#endif
|
|
}
|
|
|
|
bool Host_RendererHasFullFocus()
|
|
{
|
|
return Host::GetInstance()->GetRenderFullFocus();
|
|
}
|
|
|
|
bool Host_RendererIsFullscreen()
|
|
{
|
|
return Host::GetInstance()->GetRenderFullscreen();
|
|
}
|
|
|
|
bool Host_TASInputHasFocus()
|
|
{
|
|
return Host::GetInstance()->GetTASInputFocus();
|
|
}
|
|
|
|
void Host_YieldToUI()
|
|
{
|
|
if (qApp->thread() == QThread::currentThread())
|
|
qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
|
|
}
|
|
|
|
void Host_UpdateDisasmDialog()
|
|
{
|
|
if (Settings::Instance().GetIsContinuouslyFrameStepping())
|
|
return;
|
|
|
|
QueueOnObject(QApplication::instance(), [] { emit Host::GetInstance()->UpdateDisasmDialog(); });
|
|
}
|
|
|
|
void Host_JitCacheInvalidation()
|
|
{
|
|
QueueOnObject(QApplication::instance(), [] { emit Host::GetInstance()->JitCacheInvalidation(); });
|
|
}
|
|
|
|
void Host_JitProfileDataWiped()
|
|
{
|
|
QueueOnObject(QApplication::instance(), [] { emit Host::GetInstance()->JitProfileDataWiped(); });
|
|
}
|
|
|
|
void Host_PPCSymbolsChanged()
|
|
{
|
|
QueueOnObject(QApplication::instance(), [] { emit Host::GetInstance()->PPCSymbolsChanged(); });
|
|
}
|
|
|
|
void Host_PPCBreakpointsChanged()
|
|
{
|
|
QueueOnObject(QApplication::instance(),
|
|
[] { emit Host::GetInstance()->PPCBreakpointsChanged(); });
|
|
}
|
|
|
|
void Host_RequestRenderWindowSize(int w, int h)
|
|
{
|
|
emit Host::GetInstance()->RequestRenderSize(w, h);
|
|
}
|
|
|
|
bool Host_UIBlocksControllerState()
|
|
{
|
|
// TODO: Remove the Paused check once async presentation is implemented.
|
|
return ImGui::GetCurrentContext() && ImGui::GetIO().WantCaptureKeyboard &&
|
|
Core::GetState(Core::System::GetInstance()) != Core::State::Paused;
|
|
}
|
|
|
|
void Host_TitleChanged()
|
|
{
|
|
#ifdef USE_DISCORD_PRESENCE
|
|
// TODO: Not sure if the NetPlay check is needed.
|
|
if (!NetPlay::IsNetPlayRunning())
|
|
Discord::UpdateDiscordPresence();
|
|
#endif
|
|
}
|
|
|
|
void Host_UpdateDiscordClientID(const std::string& client_id)
|
|
{
|
|
#ifdef USE_DISCORD_PRESENCE
|
|
Discord::UpdateClientID(client_id);
|
|
#endif
|
|
}
|
|
|
|
bool Host_UpdateDiscordPresenceRaw(const std::string& details, const std::string& state,
|
|
const std::string& large_image_key,
|
|
const std::string& large_image_text,
|
|
const std::string& small_image_key,
|
|
const std::string& small_image_text,
|
|
const int64_t start_timestamp, const int64_t end_timestamp,
|
|
const int party_size, const int party_max)
|
|
{
|
|
#ifdef USE_DISCORD_PRESENCE
|
|
return Discord::UpdateDiscordPresenceRaw(details, state, large_image_key, large_image_text,
|
|
small_image_key, small_image_text, start_timestamp,
|
|
end_timestamp, party_size, party_max);
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
#ifndef HAS_LIBMGBA
|
|
std::unique_ptr<GBAHostInterface> Host_CreateGBAHost(std::weak_ptr<HW::GBA::Core> core)
|
|
{
|
|
return nullptr;
|
|
}
|
|
#endif
|